Skip to content

report-lane-outcome: record a key-to-subscription fallback as a lane-record token #806

Closed

Description

@kyle-sexton

Problem

When a caller runs run-claude-cli as two jobs (key job first, subscription-token job only after the key job's credit stop, the v0.54.0 pattern), the only lane record for a run comes from the subscription job. report-lane-outcome has no input saying that the role's API key hit a credit stop, so that fact is not a key=value token in the lane record.

The owner rotates a spare account's API key into a role by hand when that role's key runs out. The record of that event is what they act on, and today it is not machine-readable:

  • claude-code-plugins #8577 works around it by writing the note into the human-readable lane input (pr-verify (key AUTOMATIONS_CLAUDE_API_KEY hit a credit stop)) plus a ::warning::.
  • harness-ops morning brief reads CI lane records as tokens from job annotations (class=, turns=, max-turns=; plugins/harness-ops/skills/morning-brief/scripts/morning-brief.sh:1410-1425 in claude-code-plugins). The lane text is prose, so the credit stop never shows in the brief's lane rows.

Proposal

Add an optional report-lane-outcome input, for example fallback-reason with a fixed vocabulary (credit-stop, no-key), printed as a fallback=<reason> token in the lane record next to credential=. Callers in the two-job pattern pass it from the key job's lane-class output.

Then, in claude-code-plugins:

  • pr-verify.yml sets lane: pr-verify again and passes the new input.
  • The morning brief counts fallback=credit-stop per lane and credential role, so the rotation signal shows in the brief.

Acceptance

  • A run whose key job credit-stops writes one lane record containing credential=<ROLE>_CLAUDE_CODE_OAUTH_TOKEN and fallback=credit-stop.
  • A run with no fallback writes no fallback= token.
  • The input rejects values outside the vocabulary.

Activity

  1. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 20, 2026
  2. kyle-sexton commented on Oct 20, 2026

    @kyle-sexton
    MemberAuthor

    Agent-ready brief (triaged 2026-10-20)

    • Decision: Add an optional fallback-reason input to report-lane-outcome with exactly two values, credit-stop and no-key, printed as a fallback=<reason> token in the lane record. Wire it into ci-workflows' own intake-triage subscription job in the same PR.

    • Current state (origin/main 834f4e1):

    • Scope:

      • report-lane-outcome/action.yml: new optional input fallback-reason, default empty, documented with its two values.
      • classify.cjs recordTokens: when the input is set, push fallback=<reason> immediately after credential=.
      • Reject any other non-empty value: the step fails with a message naming the allowed values.
      • intake-triage subscription job report step: pass fallback-reason: credit-stop when needs.claude.outputs.lane-class == 'credit-stop', else no-key when needs.claude.outputs.credential-present == 'false'.
      • README or action description entry for the new input.
    • Out of scope: an auth value for a dead key; claude-code-plugins changes (pr-verify passing the input, the harness-ops morning brief counting the token) follow in that repo after a repin.

    • Acceptance criteria:

      • fallback-reason: credit-stop with credential: X_CLAUDE_CODE_OAUTH_TOKEN yields one lane record containing both credential=X_CLAUDE_CODE_OAUTH_TOKEN and fallback=credit-stop, with fallback= right after credential= (test in classify.test.cjs or outcome-step.test.cjs).
      • fallback-reason: no-key yields fallback=no-key (test).
      • An empty fallback-reason yields no fallback= token, and existing records are byte-identical (existing tests unchanged and passing).
      • A value outside credit-stop / no-key fails the step (test).
      • The intake-triage subscription job passes fallback-reason derived from needs.claude.outputs.lane-class and needs.claude.outputs.credential-present; the leg parity test .github/scripts/intake-triage-legs.test.cjs is updated to allow this one difference, and node --test .github/scripts/*.test.cjs passes.
    • Sequencing / dependencies: independent; touches the same subscription job as #804, so rebase whichever lands second. Downstream consumer: claude-code-plugins #8577 workaround and the harness-ops morning brief.

    • Evidence: the vocabulary matches the two triggers of the subscription job's if at intake-triage.yml:835. Placement next to credential= follows classify.cjs:320. No external research was needed.

  3. added a commit that references this issue on Oct 20, 2026
    a4e6c20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageNot yet classified. Floor until a type and one priority tier are set.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions