Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Pick the row that matches what you want to do.
| run a recorded cycle from the command line | [`run_cycle.py`](docs/workflows.md#runtime-driver-run_cyclepy) | via its stages |
| close a cycle whose rereview ran outside the runtime | [`manual_rereview.py`](docs/review-cycle.md#manual-rereview) | publishes one PR comment |
| list or revoke stored sandbox permissions | [`consent_control.py`](docs/routing.md#sensitive-sandbox-grants) | — |
| inspect quota records or attest a legacy handoff | [`quota_state.py`](docs/routing.md#operator-quota-recovery) | — |
| review a diff or branch without a pull request | [`cc-code-review`](docs/skills.md#cc-code-review) | — |
| audit a change for security | [`cc-security-review`](docs/skills.md#cc-security-review) | — |
| prove a change actually works | [`cc-verify`](docs/skills.md#cc-verify) | — |
Expand Down
12 changes: 7 additions & 5 deletions docs/instrumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -766,11 +766,13 @@ resolution only when it names a merged PR in the resolving snapshot.

Schema 23 adds `quota_exhaustions`, keyed by repository, executor, and requested
model, with exhaustion and expiry timestamps, a closed reason token, and whether
the executor reported a reset time. Production `--continue` reads active rows;
expired rows are deleted when read, and a successful production dispatch clears
its matching row. The additive table does not rewrite existing telemetry, and a
code rollback leaves unused rows. The `quota_resume_reason` cycle payload field
records when a continuation routes around a persisted exhaustion.
the executor reported a reset time. All production routes read active rows.
Schema 26 keeps expired records and adds independent `quota_history` and
`quota_handoffs` audit tables; success ends availability blocking without deleting
provenance. Unknown dated resets use `reset_time_ambiguous` and no recovery time.
The `quota_resume_reason` cycle payload field records a route around persisted
exhaustion. The journal records redacted messages and scoped handoff fingerprints,
executors and operator actions. See [quota telemetry](telemetry.md#quota-exhaustion-windows-schema-23).

Schema 24 adds the stop reasons `publication_violation` and
`publication_unverified` of the [publication audit](role-workspace-policy.md#publication-audit).
Expand Down
16 changes: 14 additions & 2 deletions docs/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,20 @@ Resolve keeps the `cheap_coder` / `deep_coder` profile choice. With `code_cycle.
- **Probe once per run.** An executor that is only *installed* isn't dispatchable. Codex and Claude can only prove `authenticated`: remaining quota and session validity are invisible until something is spent. Production uses the `attempt` readiness policy to dispatch from `authenticated`, and records that it did (`dispatched_from`).
- **Fallback (production only).** If the primary is unavailable or not eligible for the role's workspace policy, the profile's `fallback` is used, and the decision says so.
- **Sandbox recovery.** An explicit, complete agent-reported restriction can use the same reroute machinery. See [Recovering sandbox restrictions](#recovering-sandbox-restrictions) for its additional conditions.
- **One reroute per stage.** A `capacity`, `transport`, `timeout`, or `quota` failure can use that profile's distinct, available fallback once, sharing the quota retry budget. The previous process group must have exited; asynchronous launches and missing exit proof stop. A `not_started` attempt can reroute directly. A `started` or `unknown` attempt that could write or publish first reconciles the workspace, local HEAD, remote refs, PR state, comments and reviews, and issue comments. Dirty or changed state, a publication, or unavailable evidence stops with a closed reason token. A publication is what the cycle's own code-host account (`gh api user`) created or edited: pull requests (state, head, title, body), comments and reviews. Other accounts' comments and newly opened pull requests, reactions and timestamps do not stop the reroute; an unreadable account is unavailable evidence. A pull request carries no editor identity, so any edit to an existing in-scope pull request stops conservatively, whoever opened it. A review's read-only workspace does not exempt its publication boundary. Other error classes and human friction never reroute. Both attempts and the parent's `error_code` and `start_state` are recorded. The fallback applies only to this stage; later stages keep the original executor availability. Recovery from published evidence remains a separate operation.
- **Quota across `--continue`.** A quota failure is saved in `telemetry.sqlite` by repository, executor, and requested model. If the dirty-workspace guard stops the same-dispatch fallback, the next `--continue --cwd <checkout>` uses the profile fallback while that exact model's record is active, carrying the partial work as its starting point. Other models and executors remain eligible. A reported time of day expires at its next occurrence in the local timezone; with no parseable reset (or usable local timezone), the record lasts 30 minutes. A successful dispatch on that target clears it early. If recording fails, the cycle continues without persistent evidence. When no configured target remains, the stop names the earliest reset. The stop report directs the operator to wait for reset or continue on the fallback. Calibration never reads or writes these records and never substitutes an arm.
- **One reroute per stage.** A `capacity`, `transport`, `timeout`, or `quota` failure can use that profile's distinct, available fallback once, sharing the quota retry budget. The previous process group must have exited; asynchronous launches and missing exit proof stop. A `not_started` attempt can reroute directly. A `started` or `unknown` attempt that could write or publish first reconciles the workspace, local HEAD, remote refs, PR state, comments and reviews, and issue comments. Dirty or changed state, a publication, or unavailable evidence stops with a closed reason token. A publication is what the cycle's own code-host account (`gh api user`) created or edited: pull requests (state, head, title, body), comments and reviews. Other accounts' comments and newly opened pull requests, reactions and timestamps do not stop the reroute; an unreadable account is unavailable evidence. A pull request carries no editor identity, so any edit to an existing in-scope pull request stops conservatively, whoever opened it. A review's read-only workspace does not exempt its publication boundary. Other error classes and human friction never reroute. Both attempts and the parent's `error_code` and `start_state` are recorded. The fallback applies only to this stage; later stages keep the original executor availability except for recorded quota exhaustion, which they continue to honour. Recovery from published evidence remains a separate operation.
- **Quota across resumes.** Every production route skips an active quota record for the exact repository, executor and requested model, including `--continue` and `--from`. The route reports the substitution and reset or operator-retry requirement. Calibration never reads or writes these records and never substitutes an arm. Complete dates (including ordinal days), years, clocks and explicit UTC offsets or IANA zones (including parenthesized zones) are preserved. Without an explicit zone the runtime's local zone applies, retaining its date-specific DST rules; when no IANA zone is available, including on Windows, the OS converts the local wall time for that date. A repeated hour uses its later interpretation. Uninterpretable dates, weekday-only dates, relative durations and ambiguous zone abbreviations stay unavailable until an explicit operator retry. Missing reset information alone uses a 30-minute minimum. Expiry is retry eligibility, not proof of recovery. Original provider text is redacted into the journal and stop report; telemetry holds closed reason tokens and structured timestamps only.
- **Dirty `--from` resumes.** A dirty resume needs an exact quota worktree provenance record bound to the repository, work item, stage, PR, HEAD, original executor/model, destination executor and complete SHA-256 fingerprint of staged, unstaged and nonignored untracked changes. The runtime records ownership only after a finished quota-exhausted process began on a clean tree (or matched previous provenance), left HEAD unchanged and changed no remote or publication boundary. The fallback must inspect those edits before writing. A missing record, altered diff, wrong scope or HEAD stops without changing the checkout. Ordinary dirty executor changes remain refused; `implement --continue` retains its existing behavior. Provenance survives quota expiry, allowing a matching resume on the original executor after expiry; a new exhaustion records another ownership checkpoint. A successful resume consumes the checkpoint, retaining its audit history. This does not weaken the PR-head guard or any sandbox policy.

### Operator quota recovery

Use `python3 scripts/quota_state.py --database <telemetry.sqlite> --repo <owner/repo> --task <id> <command>`:

- `list` shows exhaustion record IDs, timestamps, ambiguity tokens and retry authorization history, without provider prose.
- `retry --executor <executor> --record <quota-id>` explicitly authorizes retry of that exact current record, records the authenticated `gh api user` identity and journal timestamp, and preserves exhaustion history. It does not claim quota recovered. Another quota failure creates a new record. Superseded or already authorized records are refused.
- `inspect --cwd <worktree> --pr <number> --stage <stage> --original <executor> --model <model> --destination <executor> --head <full-sha>` previews the exact fingerprint and scope for a legacy worktree.
- `attest` takes the same scope plus `--confirm-fingerprint <sha256>`. The operator must inspect and explicitly confirm that exact fingerprint. Only a legacy quota record predating automatic provenance history is eligible. A new unattested worktree cannot use this path. The attestation records the authenticated operator and timestamp as `operator_attested`, never `executor_verified`; it is bound to the exact scope and diff, can be created once for that scope, and is claimed atomically before one dispatch. It is never functional verification.

For interrupted implementation use `run_cycle.py ... --continue --cwd <worktree>`. For an existing PR use `run_cycle.py ... --from resolve --pr <number> --cwd <worktree>` (or the interrupted review stage), without `--continue`. These are alternatives; the CLI rejects combining them. A legacy worktree is never automatically attested, and no WIP commit is needed.

Native POSIX dispatches run in their own process group and stop remaining children before returning exit proof, including on timeout. The group first receives `SIGTERM`, so a sandbox can unmount and remove what it set up, then `SIGKILL` after a bounded grace period (`PROCESS_GROUP_GRACE_SECONDS`, 10 seconds); output pipes a stray descendant keeps open are waited on for at most `OUTPUT_DRAIN_SECONDS`, so cleanup never blocks the cycle indefinitely. Windows currently lacks group exit proof and therefore stops rather than automatically rerouting. Reconciliation reads GitHub through `gh`; unsupported publication boundaries fail closed. Snapshots are transient and never enter telemetry.

Expand Down
25 changes: 18 additions & 7 deletions docs/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -499,13 +499,24 @@ comparison of the two policies on a controlled fake CI.
### Quota exhaustion windows (schema 23)

The `quota_exhaustions` table persists a quota reset window per repository,
executor, and requested model so a later production `--continue` can skip that
exact target and use the configured fallback. A reported time of day expires at
its next occurrence in the local timezone; an absent or unparseable reset uses a
30-minute window. Reading active rows deletes expired entries. A successful
production dispatch clears only its matching row. Calibration does not read,
write, or clear these records. The table is additive; reverting the code leaves
its rows unused.
executor, and requested model so every production route, including `--from`,
can skip that exact target. Schema 26 preserves expired rows: reading availability
never deletes ownership or audit evidence. An empty projection `expires_at`
means `reset_time_ambiguous`, with no invented reset timestamp. History uses SQL
NULL for that unknown time. Only missing reset information uses a 30-minute
window. Success ends matching eligibility without deleting history. Calibration
does not read, write or clear these records.

Schema 26 adds `quota_history` with an immutable exhaustion ID, executor/model,
exhaustion time, nullable reset, closed reason and optional explicit `retry_at`.
Each repeated exhaustion appends a new record. It also adds `quota_handoffs`,
whose repository/work-item/stage/PR/HEAD scope, original and destination executors,
original model, SHA-256 diff fingerprint, provenance, operator and creation time
outlive quota availability. `claimed_at` makes operator attestations single-use;
`used_at` marks successful resumes without removing audit rows. File contents,
diff text and provider prose never enter these tables. The journal holds the
redacted original message and operator actions. See [operator quota recovery](routing.md#operator-quota-recovery).
The additive tables leave existing rows readable; rollback leaves them unused.

### Work-item outcomes (schema 14)

Expand Down
Loading
Loading