Skip to content

Record and inspect local Plan usage across subscription Harnesses and User config routes - #2198

Merged
JacobStephens2 merged 3 commits into
issue-583from
issue-2181
Nov 14, 2026
Merged

JacobStephens2 merged 3 commits into
issue-583from
issue-2181

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Nov 14, 2026 •

Copy link
Copy Markdown
Owner

Summary

Ticket 1 of #583: this machine now records Plan usage for the five subscription Harnesses, refreshes it on demand, and shows it. The User config gains ordered routes and utilization ceilings, and headless agy keeps to included usage unless its route allows credits.

src/plan_usage.rs        # record(), credits_allowed(), `plan-usage` with a 60 s read cache
src/plan_usage/
├── observation.rs       # Plan windows and limits from structured wire frames
├── store.rs             # ~/.thirdshift/plan-usage.json, cooldowns and 1h→6h revalidation backoff, --clear
├── read.rs              # codex app-server, muse serve, claude auth status --json (non-billed)
├── route.rs             # [harness] routes / max_utilization, [security] routes, allow_credits
└── view.rs              # rows in route order, active primary route, text and JSON
 Recording.run → follow (every session)
   record_line → Interpretation::decode
+    Evidence::PlanUsage   claude rate_limit_event (every status and %) and API-error rate_limit_info,
+                          codex token_count, each Harness's own terminal usage-limit failure frame
+  observe_limit_records   codex native token_count, for ordinary sessions too (Session trace unchanged)
+  plan_usage::record      → ~/.thirdshift/plan-usage.json
   (Evals keep their 85% Paused Command stop as before)

 agy session args
+  --settings {"useG1Credits":false}   unless the User config route it runs as sets allow_credits = true
$ thirdshift plan-usage [--json] [--clear [<harness>]]
codex (active)
  route: harness.default, ceiling 90%: available
  route: harness.routes 1, ceiling 90%: available, active
  account: signed in, plan pro
  primary: 87% used, resets …
agy
  route: harness.routes 2, gemini-3.8-flash-high, ceiling 80%: not installed

Rows list every Harness the User config names: harness.default, harness.routes, security.harness and security.routes, in route order. Any other subscription Harness appears only when it has a saved observation or a signed-in CLI. opencode and kimi never appear.

Evidence

Execution evidence

Tests and builds ran inside the local node:22 container with the host Rust toolchain, because this host has no C toolchain.

  • Before: cargo test --bin thirdshift console::parity::every_cli_command_has_an_api_twin_or_reasoned_exclusion
    panicked at src/console/parity.rs:320:5: CLI inventory for Command needs updating … left: {…, "PlanUsage", …}
    After: same command → test result: ok. The command adds PlanUsage to the inventory with interim exclusions.
  • Before: the reviewers' six tests, added verbatim (an_unchanged_snapshot_is_not_rewritten, a_failed_tool_task_mentioning_quota_is_not_plan_usage, agent_text_in_an_interrupted_claude_result_records_no_plan_usage, claude_api_error_limits_without_a_status_keep_their_window_reset_and_spend_cap, an_api_key_route_ahead_leaves_no_subscription_route_active, muse_account_state_alone_says_signed_out):
    test result: FAILED. 15 passed; 6 failed
    After: cargo test --bin thirdshift -- plan_usage console::parity harness:: → test result: ok. 235 passed; 0 failed
  • After: cargo test --test plan_usage → test result: ok. 7 passed; 0 failed. The tests use scripted fake Harness CLIs to cover passive capture at several utilization levels on claude and codex, terminal limits on claude, codex, grok, muse and agy, the reads and their 60 s cache, row filtering, JSON output, --clear, User config validation and the agy credit guard.
  • Full Rust suite (cargo test --no-fail-fast on the final head): 107 targets passed and 4 failed.
    • trial_sessions_keep_their_cap_and_clean_detached_work needs a systemctl --user session, which the container lacks.
    • Three tests failed with warning: could not fully sweep session …: could not read process …'s environment: Permission denied in their stderr (architect_run, opencode_sessions, run_notification). That sweep code is untouched here. All three passed when rerun alone, one of them 3/3, and all passed in the first full run.
    • Earlier "owned process survived" cancellation failures were container zombies. They pass with docker run --init.
  • Not run: the Console browser tests (npm test --prefix console). Playwright and Chromium are not installed here. The only Console change is one match arm in src/console/start_controls.rs.

Visual evidence

None: the change is CLI-only, with no rendered UI.

Merge Danger

Door: two-way

The new User config keys default to today's behaviour (routes = [], max_utilization = 100). The snapshot is a new file. Nothing routes on it yet; routing comes in #2183.

Blast Radius: sessions

Every session's stream now also writes ~/.thirdshift/plan-usage.json, best-effort and skipped when unchanged. Headless agy sessions, release summaries included, now pass --settings {"useG1Credits":false}. ADR 0026 gains two interim CLI-only entries for plan-usage, which #2182 removes when it serves /api/v1/plan-usage.

Unaddressed findings

Standards

  • 2. User manual, site pages and README not updated. Declined: Document Plan usage viewing and routing across the user manual, site pages, and code index #2185 is open and owns "Document Plan usage viewing and routing across the user manual, site pages, and code index". This follows the plan's split in docs/grill-with-docs/583-available-usage-routing.md Q3.4, item 5. That includes docs/guide.md's copy of the default User config.

  • 3. cfg!(test) and a User config read inside agy's argument builder. Declined. Choice is built as a struct literal at about 87 sites. Carrying the credit decision on it belongs with the route choice that Route factory session Steps proactively and reactively across configured Plan usage routes #2183 introduces. credits_allowed (src/plan_usage.rs) fails closed, keeping the guard on whenever the config can't be read. cfg!(test) keeps unit tests off the developer's real $HOME. The decision itself is the pure, unit-tested Routing::allows_credits.

  • 4. Shared locked-JSON helper with the GitHub Quota store. The bug is fixed: the write guard now compares like with like, and an_unchanged_snapshot_is_not_rewritten passes. The extraction is declined. The two stores already share lock_exclusive, and their record types and update semantics differ.

  • 7. No Plan usage row in docs/code-navigation.md. Declined: Document Plan usage viewing and routing across the user manual, site pages, and code index #2185 owns the code index.

  • 8. Smells. Fixed:

    • Feature Envy: availability is now State::availability.
    • Hidden clock: View carries its now.
    • Duplicated harness_name: one function in route.rs.

    Declined:

    • Codex window parsing "duplication": two different wire formats, snake_case session token_count and the camelCase app-server answer.
    • Three child runners in read.rs: they need different things. One needs only an exit status, one needs stdout to EOF, and one is a JSON-RPC line stream.
    • BTreeMap<String, State>: the key is the saved file's JSON key, and Harness has no serde impl.

Spec

  • None declined. On finding 4, Muse's real account/read state values are unconfirmed. muse_state_signed_in maps only clearly signed-in or signed-out spellings, and leaves anything else unknown.

Security

  • None. The Security review traced the passive capture from Harness wire frames and Codex's native session file, the non-billed CLI and RPC reads, the saved snapshot, the plan-usage view and --clear, User config route parsing, and the agy credit guard. Saved strings are kept only as short operator tokens or control-free text, the snapshot is written owner-only under its lock, the reads read no private tokens, and the credit guard stays on unless a User config route allows credits. A cooldown forged from a session's text could only change what plan-usage displays, because nothing routes on it until Route factory session Steps proactively and reactively across configured Plan usage routes #2183.

Security review outcome

No unaddressed introduced findings.

Review coverage

Both reviewers read every changed file at review time; nothing was excluded as generated. These files changed after review, while addressing findings, and were not reviewed as changes:

  • docs/adr/0026-each-console-serves-an-api-that-can-do-what-the-cli-can.md
  • src/console/parity.rs
  • src/harness/agy/stream.rs
  • src/harness/muse/stream.rs
  • src/harness/interpretation_tests.rs
  • src/setup.rs
  • tests/agy_sessions.rs

Closes #2181

Built with claude 2.1.322 · claude-opus-5-5 · high

Running sessions save Plan windows and limits from structured wire frames
(Claude rate_limit_event at every status and utilization and API-error
rate-limit facts, Codex token_count, and terminal usage-limit errors on
claude, codex, muse, agy and grok) in ~/.thirdshift/plan-usage.json.
thirdshift plan-usage [--json] [--clear [<harness>]] adds non-billed codex
app-server, muse serve and claude auth status reads at most once a minute,
shows the User config's routes in order with the active one, and forgets
recorded cooldowns. The User config gains harness.routes,
harness.max_utilization and security.routes, and headless agy keeps to
included usage unless its route allows credits.

Closes #2181
…route

Terminal usage-limit facts now come only from each Harness's own top-level
failed-turn frame (never a tool task's failure or an interrupted turn's
final message), Claude API errors keep their window, reset and spend cap
without a status, the snapshot skips unchanged writes, an installed API-key
route ahead of a subscription route leaves none marked active, Muse's
account state can say signed out, and plan-usage gets interim ADR 0026
exclusions until #2182 serves its Console API.
The every-setting fixtures and default-key inventory name harness.routes,
harness.max_utilization and security.routes, the agy session argv expects
the included-usage guard, and plan-usage shares the resources help line so
help stays a compact reference.
@JacobStephens2 JacobStephens2 added the in-progress A Claim: thirdshift has taken this issue label Nov 14, 2026
@JacobStephens2
JacobStephens2 merged commit e5cdbaf into issue-583 Nov 14, 2026
36 of 41 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-2181 branch November 14, 2026 04:01
@JacobStephens2 JacobStephens2 removed the in-progress A Claim: thirdshift has taken this issue label Nov 14, 2026
@JacobStephens2 JacobStephens2 mentioned this pull request Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant