fix(chat/runs): abort headless run when platform-api skill is missing

Closed
SW
4mo ago
+29-2

What

After install + discovery in provisionRunSession, fail closed: if recoup-platform-api-access isn't among the discovered skills, throw. The caller (handleStartChatRun) already maps a provisionRunSession throw to a 5xx and revokes any minted ephemeral key — so the run aborts cleanly instead of proceeding.

Why

Installing skills in the headless path (the stacked PR below) is necessary but not sufficient: a best-effort install can still silently fail (network/registry/timeout), leaving the agent with no skill tool. Today that degrades to the agent guessing API endpoints and fabricating a report it then emails to a customer (recoupable/chat#1822).

This makes the failure observable and safe: a missed run is recoverable; a fabricated report sent to a label/manager is not.

Behavior choice: this is fail-closed (abort). The alternative — log+metric and send a degraded "no data" email — is also defensible. Aborting was chosen because a fabricated send is the worst outcome. Happy to switch to warn-only or add a retry if preferred.

Tests (TDD, RED→GREEN)

Extends lib/chat/runs/__tests__/provisionRunSession.test.ts:

  • aborts when the platform API-access skill is missing after discovery — discoverSkills → [] ⇒ provisionRunSession rejects with /recoup-platform-api-access/ (was RED; now GREEN).
  • Existing success/best-effort tests updated so discovery returns the skill.

lib/chat/runs suite green (20 passed), eslint clean, zero new tsc errors in changed files.

Stacking

Stacked on #722 (fix/headless-install-global-skills) — base is that branch so this PR's diff is only the abort check. Retarget to test after #722 merges.

Refs recoupable/chat#1822 · merge order: skills#65 → api#722 → this · base fix/headless-install-global-skills

🤖 Generated with Claude Code


Summary by cubic

Abort headless chat runs if the required recoup-platform-api-access skill isn’t present after install + discovery. This prevents ungrounded runs and avoids sending fabricated reports.

  • Bug Fixes
    • Fail-closed guard in provisionRunSession: throw when recoup-platform-api-access isn’t discovered; caller maps to 5xx and revokes the ephemeral key.
    • Tests: default discovery includes the skill; added missing-skill rejection case.