fix(chat/runs): abort headless run when platform-api skill is missing
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 → []⇒provisionRunSessionrejects 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 whenrecoup-platform-api-accessisn’t discovered; caller maps to 5xx and revokes the ephemeral key. - Tests: default discovery includes the skill; added missing-skill rejection case.
- Fail-closed guard in