instar 1.3.122 → 1.3.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  2. package/dist/core/PostUpdateMigrator.js +16 -0
  3. package/dist/core/PostUpdateMigrator.js.map +1 -1
  4. package/dist/core/SessionManager.d.ts +10 -0
  5. package/dist/core/SessionManager.d.ts.map +1 -1
  6. package/dist/core/SessionManager.js +34 -2
  7. package/dist/core/SessionManager.js.map +1 -1
  8. package/dist/providers/adapters/openai-codex/observability/codexModelSwapPolicy.d.ts +83 -0
  9. package/dist/providers/adapters/openai-codex/observability/codexModelSwapPolicy.d.ts.map +1 -0
  10. package/dist/providers/adapters/openai-codex/observability/codexModelSwapPolicy.js +93 -0
  11. package/dist/providers/adapters/openai-codex/observability/codexModelSwapPolicy.js.map +1 -0
  12. package/dist/providers/adapters/openai-codex/observability/codexRateLimitReader.d.ts +95 -0
  13. package/dist/providers/adapters/openai-codex/observability/codexRateLimitReader.d.ts.map +1 -0
  14. package/dist/providers/adapters/openai-codex/observability/codexRateLimitReader.js +170 -0
  15. package/dist/providers/adapters/openai-codex/observability/codexRateLimitReader.js.map +1 -0
  16. package/dist/providers/adapters/openai-codex/observability/sessionPaths.d.ts +23 -1
  17. package/dist/providers/adapters/openai-codex/observability/sessionPaths.d.ts.map +1 -1
  18. package/dist/providers/adapters/openai-codex/observability/sessionPaths.js +102 -5
  19. package/dist/providers/adapters/openai-codex/observability/sessionPaths.js.map +1 -1
  20. package/dist/scaffold/templates.d.ts.map +1 -1
  21. package/dist/scaffold/templates.js +5 -0
  22. package/dist/scaffold/templates.js.map +1 -1
  23. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  24. package/dist/server/CapabilityIndex.js +11 -0
  25. package/dist/server/CapabilityIndex.js.map +1 -1
  26. package/dist/server/routes.d.ts.map +1 -1
  27. package/dist/server/routes.js +31 -0
  28. package/dist/server/routes.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/data/builtin-manifest.json +63 -63
  31. package/src/scaffold/templates.ts +5 -0
  32. package/upgrades/1.3.123.md +105 -0
  33. package/upgrades/1.3.124.md +53 -0
  34. package/upgrades/side-effects/codex-model-swap.md +134 -0
  35. package/upgrades/side-effects/codex-rollout-scan-perf.md +64 -0
  36. package/upgrades/side-effects/codex-usage-visibility.md +107 -0
@@ -0,0 +1,105 @@
1
+ # Instar Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **Codex usage is now readable over HTTP — the codex `/status` rate-limit
8
+ windows, without the interactive TUI.** Codex has no public usage API, but the
9
+ codex CLI persists the authoritative account rate-limit windows it gets back
10
+ from OpenAI — primary (5h rolling) and secondary (weekly) — into each session
11
+ rollout's `token_count` events. A new reader
12
+ (`readLatestCodexUsage`) finds the newest rollout, reads only its tail (the
13
+ windows are appended per-turn, so the freshest is near the end), and returns a
14
+ structured snapshot. A new read-only route, `GET /codex/usage`, surfaces it:
15
+
16
+ ```
17
+ { "available": true,
18
+ "usage": {
19
+ "primary": { "usedPercent": 13, "remainingPercent": 87, "windowMinutes": 300, "resetsAtIso": "…", "resetsInSeconds": … },
20
+ "secondary": { "usedPercent": 93, "remainingPercent": 7, "windowMinutes": 10080, "resetsAtIso": "…", "resetsInSeconds": … },
21
+ "model": "gpt-5.5", "planType": "plus", "rateLimitReachedType": null } }
22
+ ```
23
+
24
+ `available:false` (still HTTP 200, never 503 — it is a disk reader) means there
25
+ is simply no codex session data on disk yet (e.g. a pure-Claude agent). This is
26
+ the data an agent needs to answer "how much codex usage is left?" and to drive a
27
+ model-swap when a window is exhausted (`rateLimitReachedType` non-null, or
28
+ `secondary.remainingPercent` low).
29
+
30
+ The route is classified in the capability index (discoverable via
31
+ `GET /capabilities`), surfaced in the CLAUDE.md template for new agents, and
32
+ back-filled into existing agents' CLAUDE.md by the migrator (so deployed agents
33
+ learn it on update). Also restores the feature-delivery-completeness test to
34
+ green by tracking two prior dark/operational migrator sections
35
+ (`Autonomous-fix loop`, `Multi-Machine Session Pool`) that had not been
36
+ registered.
37
+
38
+ **Codex agents can auto-swap to a fallback model when their weekly window is
39
+ exhausted (ships DARK).** Building on the usage reader above: at codex session
40
+ launch, if the main model's weekly window is spent (or codex flags a limit hit),
41
+ the next session launches on a configured fallback model that draws on a separate
42
+ quota bucket — instead of stalling. The decision is a pure policy
43
+ (`resolveCodexLaunchModel`) wired into both codex launch paths in
44
+ `SessionManager` (headless + interactive). Gated behind
45
+ `codex.rateLimitModelSwap.{enabled,fallbackModel,weeklyRemainingThreshold}`;
46
+ off by default with zero spawn-path overhead. The fallback model id is operator
47
+ config — never hardcoded — because the exact id and its subscription
48
+ availability are the account owner's to confirm.
49
+
50
+ ## Summary of New Capabilities
51
+
52
+ - `GET /codex/usage` — the freshest codex account rate-limit snapshot (primary
53
+ 5h + secondary weekly windows, with used/remaining percent, window length,
54
+ absolute + relative reset, plan type, and which window — if any — is
55
+ exhausted). Read-only; never mutates session state.
56
+ - An optional `?codexHome=` query parameter targets a specific `$CODEX_HOME`
57
+ (defaults to `~/.codex`).
58
+ - Agent-awareness: the capability appears in `GET /capabilities`, the CLAUDE.md
59
+ template, and is migrated into existing agents.
60
+ - Codex rate-limit model-swap (dark): `codex.rateLimitModelSwap` —
61
+ auto-launch the next codex session on `fallbackModel` when the weekly window's
62
+ remaining percent is at or below `weeklyRemainingThreshold` (default 10) or
63
+ codex reports a limit hit. Off by default; best-effort and fail-safe (a usage
64
+ read never blocks a launch).
65
+
66
+ ## What to Tell Your User
67
+
68
+ You can now ask me how much codex usage is left and I can answer directly,
69
+ without opening the codex status screen myself — I read the same five-hour and
70
+ weekly limit windows the codex tool reports. I can tell you the percent used and
71
+ remaining on each window, when each one resets, which model is in effect, and
72
+ whether either window is currently maxed out. This also gives me the signal I
73
+ need to switch models before the weekly limit runs out. Nothing changes for you
74
+ on update; the capability is simply there when you need it.
75
+
76
+ And if you want it, I can now do that switch automatically: when a codex agent's
77
+ weekly model quota is nearly spent, the next session it starts can launch on a
78
+ backup model that has its own separate budget, so it keeps working instead of
79
+ stalling. This is off until you turn it on and tell me which backup model to use
80
+ — the exact name and whether it works on your subscription is something only you
81
+ can confirm — so nothing changes by default.
82
+
83
+ ## Evidence
84
+
85
+ **Source of truth.** Inspected a live codex rollout
86
+ (`~/.codex/sessions/2026/05/30/rollout-…jsonl`) and confirmed each
87
+ `token_count` event carries `payload.rate_limits` with `primary`
88
+ (window_minutes 300) and `secondary` (window_minutes 10080) sub-objects, each
89
+ with `used_percent` and `resets_at`, plus `plan_type` and
90
+ `rate_limit_reached_type`. The observed `secondary.used_percent` of 93 (i.e. 7%
91
+ weekly remaining) matched the codex `/status` screen at the same moment —
92
+ verifying the on-disk windows equal the TUI's.
93
+
94
+ **Before:** there was no way for an agent to read codex usage programmatically —
95
+ the only surface was the interactive TUI (`UsageMeterProvider.isAuthoritative()`
96
+ returns false and only does local token accounting), so an agent could not
97
+ answer "where does codex usage sit?" or react to an exhausted window.
98
+
99
+ **After:** `GET /codex/usage` returns the structured snapshot above. Verified by
100
+ a 3-tier test suite (16 tests, all green): unit tests parse the latest
101
+ `token_count` (newest event wins; malformed lines skipped; missing-window
102
+ tolerated; reached-type surfaced), an integration test drives the route over
103
+ HTTP against a rollout fixture (data → `available:true`; none → `available:false`
104
+ + 200), and a Tier-3 lifecycle test boots the real AgentServer and confirms the
105
+ route is alive (200, Bearer-gated, read-only POST→404).
@@ -0,0 +1,53 @@
1
+ # Instar Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **Fixed: `GET /codex/usage` (and the codex TokenLedger / resume scans) timed out
8
+ on a large codex history.** The shared helper `listAllRollouts` walked the ENTIRE
9
+ `$CODEX_HOME/sessions` tree and `stat`-ed every rollout file before slicing to
10
+ the newest few. On a real account that history is large — one machine measured
11
+ 14,277 files / 1.4 GB — so the full walk plus ~14k sequential `stat`s, under
12
+ live server load, made `GET /codex/usage` (shipped in v1.3.123) return a
13
+ connection timeout. It now walks the date-partition directories
14
+ (`sessions/YYYY/MM/DD/`) newest-first and `stat`s only the newest partitions
15
+ (at least two, until it has `limit` candidates), with a full-walk fallback for
16
+ any non-date-partitioned layout. The returned newest-N is unchanged; only the
17
+ scan is bounded. This one change fixes all four callers of the helper (the
18
+ codex-usage reader, the TokenLedger codex scan, the session-resume index, and
19
+ the layout canary).
20
+
21
+ ## Summary of New Capabilities
22
+
23
+ - No new capability — a performance fix. `listAllRollouts` is now bounded to the
24
+ most-recent date partitions (helpers `listDayPartitionsDescending` +
25
+ `readNamesDesc`), so codex usage/ledger/resume reads stay fast regardless of
26
+ how much rollout history has accumulated.
27
+
28
+ ## What to Tell Your User
29
+
30
+ The codex usage check now answers instantly even on a heavy account. Before this
31
+ fix, on an account with a very large codex history, asking how much codex usage
32
+ was left could hang and time out, because the check was reading through the
33
+ entire history to find the most recent record. It now looks only at the most
34
+ recent day or two of records, so it returns right away. Nothing else changes.
35
+
36
+ ## Evidence
37
+
38
+ **Reproduction.** On a real codex account with 14,277 rollout files (1.4 GB)
39
+ under `~/.codex/sessions`, hitting `GET /codex/usage` on the running server
40
+ returned a connection timeout — curl reported status 000 after a 30-second
41
+ `--max-time`, while `GET /health` on the same server responded normally. Root
42
+ cause confirmed by reading the code: `listAllRollouts` recursively walked the
43
+ whole tree and awaited an `fs.stat` on every one of the 14k files before
44
+ sorting and slicing.
45
+
46
+ **Before / after.** The old full-walk timed out at 30 s+ under load. The fixed
47
+ date-partition walk, run against the same live 14,277-file tree, completed in
48
+ **59 ms** (it scanned the two newest day-partitions, found the newest rollout,
49
+ and stopped) — returning the same newest record. A unit test with 206 fixture
50
+ files across 42 partitions uses an `fs.stat` spy to assert fewer than 20 stats
51
+ occur (only the newest partitions), versus 206 before. All three test tiers for
52
+ the codex-usage feature plus the TokenLedger / resume / canary suites stay
53
+ green.
@@ -0,0 +1,134 @@
1
+ # Side-Effects Review — Codex rate-limit model-swap (directive #4b)
2
+
3
+ **Version / slug:** `codex-model-swap`
4
+ **Date:** 2026-05-30
5
+ **Author:** Echo (instar-dev agent)
6
+ **Second-pass reviewer:** REQUIRED (touches session spawn / lifecycle) — see verdict at end
7
+
8
+ ## Summary of the change
9
+
10
+ At codex session launch, if the agent's main model has exhausted its weekly
11
+ window, launch the next session on a configured fallback model (separate quota
12
+ bucket) instead of stalling. New pure policy
13
+ (`src/providers/adapters/openai-codex/observability/codexModelSwapPolicy.ts`) +
14
+ a private `SessionManager.resolveCodexLaunchModel` helper wired into BOTH codex
15
+ launch paths (`spawnSession`→`buildHeadlessLaunch`,
16
+ `spawnInteractiveSession`→`buildInteractiveLaunch`). Consumes the on-disk
17
+ `readLatestCodexUsage` reader (shipped in CODEX-USAGE-VISIBILITY). Ships DARK
18
+ behind `config.codex.rateLimitModelSwap.{enabled,fallbackModel,weeklyRemainingThreshold}`.
19
+
20
+ ## Decision-point inventory
21
+
22
+ - `SessionManager.resolveCodexLaunchModel` (NEW decision) — picks the launch
23
+ model for a codex spawn. Add. It is a model SELECTION, not a block/allow gate:
24
+ the only outcomes are "requested model" or "configured fallback model". Both
25
+ spawn paths consume it (pass-through for non-codex / disabled).
26
+
27
+ ---
28
+
29
+ ## 1. Over-block
30
+
31
+ **No block/allow surface — over-block not applicable.** The policy never rejects
32
+ or cancels a launch. Its worst "wrong" outcome is launching on the fallback
33
+ model when it didn't strictly need to (e.g. weekly at exactly the threshold) —
34
+ which still produces a working session, just on the other quota bucket.
35
+
36
+ ## 2. Under-block
37
+
38
+ **Not a blocker.** The closest "miss": the swap only takes effect at the NEXT
39
+ launch — a session already mid-turn on an exhausted model still fails that turn
40
+ (it can't change model mid-flight). That is inherent to how model selection
41
+ works and is the correct scope; the RateLimitSentinel covers the in-flight
42
+ throttle messaging separately. Also: if usage can't be read, no swap happens
43
+ (fail-safe) — a genuinely-exhausted agent with an unreadable rollout would not
44
+ swap. Acceptable: it degrades to today's behavior (launch on the main model).
45
+
46
+ ## 3. Level-of-abstraction fit
47
+
48
+ Right layer. The decision lives in the openai-codex observability namespace
49
+ (beside the reader it consumes) as a pure function; the WIRING lives in
50
+ SessionManager at the single point where the launch model is finalized for each
51
+ spawn path. It does not belong in the reader (that's a pure read surface) nor in
52
+ a separate poller (a poller would have to persist an override and re-apply it —
53
+ more state, more drift; deciding at launch is stateless and exact).
54
+
55
+ ## 4. Signal vs authority compliance
56
+
57
+ **Compliant.** Pure signal-driven decision, zero blocking authority (ref
58
+ `docs/signal-vs-authority.md`). It consumes the authoritative usage signal and
59
+ selects a model. No brittle gate, no veto. Fails safe (read failure → requested
60
+ model). The signal producer (the reader) and this consumer are cleanly split.
61
+
62
+ ## 5. Interactions
63
+
64
+ - **Shadowing / double-fire:** none. It's the only thing that selects the codex
65
+ launch model; it runs once per spawn. The two call sites are mutually
66
+ exclusive (headless vs interactive spawn of a given session).
67
+ - **RateLimitSentinel:** complementary, not conflicting. The sentinel handles an
68
+ IN-FLIGHT throttle (backoff + notify + verify recovery); this picks the model
69
+ for the NEXT launch. They operate at different moments and don't race.
70
+ - **`/local-model` + `frameworkDefaultModels`:** the swap takes `options.model`
71
+ / `defaultModel` (already resolved from those) as its INPUT and only overrides
72
+ when armed + exhausted, so an explicit per-call/local model still flows through
73
+ untouched when the feature is off (default) or the window is healthy.
74
+ - **Shared `~/.codex/config.toml`:** unaffected — the model flows via the launch
75
+ flag (`-c model=` / `--model`), the per-spawn override that already wins over
76
+ the shared config (same mechanism as `codexThreadlineMcpFlags`). No multi-agent
77
+ config collision.
78
+ - **Spawn-path latency:** when DISABLED (default) the fast-path guards return
79
+ before any disk read — zero added latency. When enabled, the read is the same
80
+ bounded path the shipped `/codex/usage` route uses: a recursive walk of
81
+ `$CODEX_HOME/sessions` (`listAllRollouts`, capped to the newest few) plus a
82
+ tail-read of the newest rollout. All async fs, wrapped best-effort (never
83
+ throws, never blocks the spawn). Only runs when the feature is armed.
84
+
85
+ ## 6. External surfaces
86
+
87
+ - **Behavioral, opt-in:** when an operator enables it with a verified
88
+ `fallbackModel`, a codex session may launch on a different model than the
89
+ configured default once the weekly window is low. That is the intended,
90
+ operator-armed behavior. A `[SessionManager]` log line records every swap with
91
+ the reason.
92
+ - No new HTTP surface, no new message, no change visible to other agents.
93
+ - Off by default → no change for any existing agent on update.
94
+
95
+ ## 7. Rollback cost
96
+
97
+ Low. Pure additive + dark. Back-out = revert the policy module + the
98
+ SessionManager helper/wiring + tests; no data migration, no agent-state repair.
99
+ In the field, neutralise instantly by not setting (or removing)
100
+ `codex.rateLimitModelSwap.enabled` — no redeploy of code required.
101
+
102
+ ---
103
+
104
+ ## Phase 5 — Second-pass reviewer verdict
105
+
106
+ **Concur with the review.** An independent reviewer subagent audited this artifact
107
+ AND the actual code (not just the claims), verifying each risk with file:line
108
+ citations:
109
+
110
+ - **Never blocks/delays/breaks a launch:** the single new
111
+ `await this.resolveCodexLaunchModel(...)` on each spawn path delegates to
112
+ `resolveCodexLaunchModelWithUsage`, whose disk read is `try/catch`-wrapped and
113
+ returns no-swap on any throw (`codexModelSwapPolicy.ts:125-129`); the helper
114
+ always returns `string | undefined`, never throws (`SessionManager.ts`).
115
+ - **Zero disk I/O when disabled:** the three fast-path guards (non-codex /
116
+ disabled-or-no-fallback / already-on-fallback) all return BEFORE the
117
+ `read(...)` call; unit tests assert `read===0` for each.
118
+ - **Disabled/default path is byte-identical:** on every fast-path the helper
119
+ returns the requested model unchanged, so the builder input is unchanged for
120
+ non-codex agents and codex agents with no config block.
121
+ - **Swap-decision correctness:** swaps only on the documented conjunction; uses
122
+ the weekly (`secondary`) window not `primary`; `<=` threshold off-by-one
123
+ covered both ways; never swaps to an empty model.
124
+ - **Both spawn paths covered**, enforced by source-assertion tests.
125
+ - **No conflict** with RateLimitSentinel (different moment), the shared
126
+ `~/.codex/config.toml` (per-spawn flag override wins), or `/local-model`
127
+ (its model is the helper's input, passes through when off).
128
+
129
+ Reviewer notes addressed: §5's spawn-path-latency wording was corrected to
130
+ acknowledge the `listAllRollouts` directory walk (not just a single tail-read).
131
+ One non-blocking nit noted for a future graduation-out-of-dark: the config is
132
+ read via an inline cast rather than a typed `codex?` field on
133
+ `SessionManagerConfig` — intentional to keep the feature dark without a schema
134
+ change; can be promoted to a typed field if/when it ships on by default.
@@ -0,0 +1,64 @@
1
+ # Side-Effects Review — bound the codex rollout scan to the newest partitions
2
+
3
+ **Version / slug:** `codex-rollout-scan-perf`
4
+ **Date:** 2026-05-30
5
+ **Author:** Echo (instar-dev agent)
6
+ **Second-pass reviewer:** not required (pure read-path perf optimization; no block/allow/lifecycle/decision surface)
7
+
8
+ ## Summary of the change
9
+
10
+ `listAllRollouts` (`src/providers/adapters/openai-codex/observability/sessionPaths.ts`)
11
+ no longer walks + `stat`s the ENTIRE `$CODEX_HOME/sessions` tree to return the
12
+ newest-N rollouts. It now walks date-partition dirs newest-first and stats only
13
+ the newest partitions (>= 2, until `limit` candidates), with a full-walk
14
+ fallback for non-date-partitioned layouts. Fixes the `GET /codex/usage` timeout
15
+ (and the shared TokenLedger / resume-index cost) on a large codex history
16
+ (measured 14,277 files / 1.4 GB → route timed out at 30 s; now 59 ms).
17
+
18
+ ## Decision-point inventory
19
+
20
+ - No decision point. `listAllRollouts` returns data (a list of file paths +
21
+ mtimes). The change is purely the disk-scan strategy used to produce that
22
+ list; the returned newest-N is unchanged for the common case.
23
+
24
+ ---
25
+
26
+ ## 1. Over-block
27
+ No block/allow surface — not applicable. It returns a file list; it rejects
28
+ nothing.
29
+
30
+ ## 2. Under-block
31
+ Not applicable. The closest "miss" is the documented trade-off: a session OLDER
32
+ than the scanned partitions yet still the single most-recently-touched file on
33
+ disk would not be in the candidate set. For the rate-limit reader this changes
34
+ nothing (account-wide windows are identical across any recently-active session);
35
+ for the resume/token-ledger callers it is a vanishingly rare miss (sessions are
36
+ not active for days) and `MIN_PARTITIONS_SCANNED = 2` covers the common
37
+ cross-midnight case.
38
+
39
+ ## 3. Level-of-abstraction fit
40
+ Correct layer — the fix is in the shared helper, so all four callers
41
+ (codex-usage reader, TokenLedger scan, resume index, layout canary) get the
42
+ speedup from one change rather than each re-implementing a bounded scan.
43
+
44
+ ## 4. Signal vs authority compliance
45
+ Compliant — no authority at all. A read helper that returns data; it gates,
46
+ blocks, and decides nothing (`docs/signal-vs-authority.md` n/a beyond "this is
47
+ not a decision point").
48
+
49
+ ## 5. Interactions
50
+ - **Shared helper:** all callers benefit; none depend on the *full* unbounded
51
+ list (every caller passes a `limit` and wants newest-N). Verified the four
52
+ call sites pass a finite limit.
53
+ - **No races / no writes:** read-only; opens + closes its own dir/file handles.
54
+ - **Canary:** `codexSessionLayoutCanary` uses date-partitioned fixtures → hits
55
+ the fast path → stays green (verified).
56
+
57
+ ## 6. External surfaces
58
+ - Makes `GET /codex/usage` actually responsive on a heavy account (it returns
59
+ fast instead of timing out). No change to the response shape or semantics.
60
+ - No new route, no message, no change visible to other agents.
61
+
62
+ ## 7. Rollback cost
63
+ Trivial. Revert the `listAllRollouts` rewrite + the two private helpers + the new
64
+ test. No data, no migration, no behavior to repair — only the scan strategy.
@@ -0,0 +1,107 @@
1
+ # Side-Effects Review — Codex usage over HTTP (GET /codex/usage)
2
+
3
+ **Version / slug:** `codex-usage-visibility`
4
+ **Date:** 2026-05-30
5
+ **Author:** Echo (instar-dev agent)
6
+ **Second-pass reviewer:** not required (no block/allow/lifecycle decision surface)
7
+
8
+ ## Summary of the change
9
+
10
+ Adds a read-only way to see codex account rate-limit usage (the codex `/status`
11
+ windows) without the interactive TUI. A new reader
12
+ (`src/providers/adapters/openai-codex/observability/codexRateLimitReader.ts`)
13
+ finds the newest codex rollout, tail-reads its freshest `token_count`
14
+ `rate_limits`, and returns a structured snapshot. A new route
15
+ (`src/server/routes.ts` → `GET /codex/usage`) surfaces it. Discoverability +
16
+ awareness via `src/server/CapabilityIndex.ts` (capabilities entry),
17
+ `src/scaffold/templates.ts` (new-agent CLAUDE.md section), and
18
+ `src/core/PostUpdateMigrator.ts` (existing-agent back-fill). Also registers two
19
+ prior dark/operational migrator sections in
20
+ `tests/unit/feature-delivery-completeness.test.ts` that were untracked
21
+ (pre-existing red on main). No runtime decision point is touched.
22
+
23
+ ## Decision-point inventory
24
+
25
+ - No decision point. The reader returns data; the route returns it over HTTP.
26
+ Nothing is gated, blocked, filtered, or routed based on the result. The route
27
+ is read-only (GET) and Bearer-gated by the existing auth middleware (not new
28
+ logic).
29
+
30
+ ---
31
+
32
+ ## 1. Over-block
33
+
34
+ **No block/allow surface — over-block not applicable.** The route is a read-only
35
+ GET that returns 200 with either the snapshot or `available:false`. It rejects
36
+ nothing.
37
+
38
+ ## 2. Under-block
39
+
40
+ **No block/allow surface — under-block not applicable.** There is no failure
41
+ mode the change is meant to catch-and-stop; it only reports.
42
+
43
+ The closest "miss" is data freshness: the snapshot reflects the last
44
+ `token_count` codex wrote, which can lag the true account state by up to one
45
+ codex turn. This is acceptable and documented — it is the same data the `/status`
46
+ screen shows, which is also turn-stamped. Callers that need to react to a hard
47
+ exhaustion should treat `rateLimitReachedType` as the authoritative "we are
48
+ limited now" flag (codex sets it when a window is actually hit).
49
+
50
+ ## 3. Level-of-abstraction fit
51
+
52
+ Correct layer. It sits in the openai-codex observability namespace alongside
53
+ `sessionPaths`/`usageMeterProvider` (it reuses `listAllRollouts`), and is exposed
54
+ through the same HTTP routes layer as the other read-only observability surfaces
55
+ (`/tokens/*`, `/sessions/reap-log`). It does NOT belong in the
56
+ `UsageMeterProvider` interface, because that interface's `read()` is a generic
57
+ token-accounting contract and this is codex-specific authoritative rate-limit
58
+ data — conflating them would muddy the `isAuthoritative()` semantics (the meter
59
+ is correctly non-authoritative; these windows ARE authoritative). A future
60
+ refactor could expose this through a dedicated authoritative-windows method, but
61
+ the standalone reader is the right minimal surface now.
62
+
63
+ ## 4. Signal vs authority compliance
64
+
65
+ **Compliant.** This is a pure signal producer with zero blocking authority (ref
66
+ `docs/signal-vs-authority.md`). It reads on-disk data and returns it. The
67
+ model-swap policy that will *consume* this signal is a separate change; keeping
68
+ the reader authority-free is exactly the prescribed split (brittle/cheap signal
69
+ at the edge, decisions made by a downstream consumer).
70
+
71
+ ## 5. Interactions
72
+
73
+ - **Shadowing:** none. No other route serves `/codex/usage`; no existing reader
74
+ parses `rate_limits`. It reuses `listAllRollouts` (read-only) but does not
75
+ change it.
76
+ - **Double-fire / race:** none. Stateless per-request disk read; no writes, no
77
+ locks, no shared mutable state. Concurrent requests each open + close their own
78
+ file handles.
79
+ - **Adjacent cleanup:** the codex rollout files it reads are owned/rotated by the
80
+ codex CLI; this reader never deletes or mutates them, so it cannot race
81
+ rollout rotation destructively (a rotated-away file just yields the next
82
+ newest, or `null`).
83
+ - **Capabilities lint:** the new `/codex` prefix is now classified in
84
+ `CapabilityIndex`, satisfying the capabilities-discoverability lint (verified
85
+ green).
86
+
87
+ ## 6. External surfaces
88
+
89
+ - **New HTTP route** visible to any Bearer-authenticated caller: `GET
90
+ /codex/usage`. It exposes the account's codex rate-limit percentages, reset
91
+ times, plan type, and active model — the same information the account owner
92
+ already sees on the codex `/status` screen. No secrets, tokens, or message
93
+ content are exposed.
94
+ - **CLAUDE.md** gains a "Codex Usage" section for new agents (templates.ts) and
95
+ existing agents (migrator back-fill, idempotent content-sniff). This changes
96
+ agent-visible instructions, handled per Migration Parity.
97
+ - **Timing dependence:** only freshness (see §2) — bounded by codex's per-turn
98
+ write cadence. No dependence on conversation state or uncontrolled runtime
99
+ conditions.
100
+
101
+ ## 7. Rollback cost
102
+
103
+ Low. Pure additive feature. Back-out = revert the route + reader + the
104
+ CapabilityIndex/template/migrator/test entries. No data migration, no agent-state
105
+ repair, no release coordination beyond a normal patch. The migrator block is
106
+ idempotent (content-sniffed on `/codex/usage`), so partial application or re-run
107
+ is safe.