instar 1.3.125 → 1.3.126

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.
@@ -28,25 +28,33 @@ export declare function findRolloutFileSync(threadId: string, codexHome?: string
28
28
  /**
29
29
  * List rollout files under $CODEX_HOME/sessions, newest first (by mtime).
30
30
  *
31
- * Codex partitions rollouts by date: `sessions/YYYY/MM/DD/rollout-*.jsonl`. A
32
- * busy account accumulates tens of thousands of these over time (one real
33
- * machine: 14k files / 1.4 GB). The previous implementation walked AND
34
- * `stat`ed EVERY file on every call before slicing to `limit`, which made
35
- * callers like `GET /codex/usage` and the TokenLedger scan take tens of
36
- * seconds (the route timed out) on a large history.
31
+ * Codex partitions rollouts by date: `sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl`.
32
+ * A busy account accumulates tens of thousands of these (one real machine: 14k
33
+ * files / 1.4 GB). The original implementation `stat`ed EVERY file before
34
+ * slicing to `limit`; an interim fix narrowed that to the newest partitions but
35
+ * still `stat`ed every file in them (~3.6k on the same machine) — fast in a
36
+ * healthy process, but catastrophic when the server's event loop is CPU-starved
37
+ * (each `await fs.stat` needs a loop turn), which is exactly when `GET
38
+ * /codex/usage` timed out.
37
39
  *
38
- * This walks the date-partition directories in DESCENDING date order and
39
- * `stat`s only the rollout files in the newest partitions, stopping once it has
40
- * `limit` candidates (after covering >= MIN_PARTITIONS_SCANNED non-empty
41
- * partitions, so a day is never cut mid-way and the cross-midnight boundary is
42
- * covered). Work is bounded to the most-recent partitions instead of the entire
43
- * history. Trade-off: "newest by mtime" becomes "newest among the most-recent
44
- * date partitions, then by mtime" — for the rate-limit reader this is exact
45
- * (the account-wide windows are identical across any recently-active session),
46
- * and for the resume/token-ledger callers a session older than the scanned
47
- * partitions yet still the single most-recently-touched file is the only miss,
48
- * which is vanishingly rare in practice. A non-date-partitioned layout falls
49
- * back to the original full walk (correctness over speed).
40
+ * This avoids the per-file `stat` storm entirely:
41
+ * 1. Enumerate date-partition dirs newest-first; `readdir` (one syscall each,
42
+ * NO per-file stat) the newest partitions to collect candidate PATHS until
43
+ * we have >= `limit * STAT_OVERSCAN` of them AND have covered >=
44
+ * MIN_PARTITIONS_SCANNED non-empty partitions.
45
+ * 2. Sort candidates DESCENDING by path — the zero-padded `YYYY/MM/DD` dir +
46
+ * `rollout-<ISO-ish-ts>` filename make a lexicographic sort chronological
47
+ * by CREATION time, no `stat` needed.
48
+ * 3. `stat` ONLY the top `limit * STAT_OVERSCAN` candidates for the
49
+ * authoritative mtime, sort by mtime, return the newest `limit`.
50
+ *
51
+ * So a `limit`-8 call does ~32 `stat`s instead of ~3.6k. Trade-off: "newest by
52
+ * mtime" becomes "newest by mtime among the newest-created candidates" — exact
53
+ * for the rate-limit reader (account-wide windows are identical across any
54
+ * recently-active session); the only miss for the resume/token-ledger callers is
55
+ * a session whose creation is older than the newest `limit * STAT_OVERSCAN` yet
56
+ * is still the single most-recently-touched file — vanishingly rare. A
57
+ * non-date-partitioned layout falls back to the original full walk.
50
58
  */
51
59
  export declare function listAllRollouts(codexHome?: string, limit?: number): Promise<ReadonlyArray<{
52
60
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"sessionPaths.d.ts","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/sessionPaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIvF;AAyDD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,eAAe,CACnC,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,SAAM,GACV,OAAO,CAAC,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CA0CzD"}
1
+ {"version":3,"file":"sessionPaths.d.ts","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/sessionPaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,4EAA4E;AAC5E,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIvF;AA+DD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,eAAe,CACnC,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,SAAM,GACV,OAAO,CAAC,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAoDzD"}
@@ -100,28 +100,41 @@ async function walkForUuid(root, uuid) {
100
100
  // early break, so the cross-midnight boundary (and a session that started
101
101
  // "yesterday" but is still the most recently active) is not missed.
102
102
  const MIN_PARTITIONS_SCANNED = 2;
103
+ // `stat` only this multiple of `limit` candidate files — the newest by the
104
+ // timestamp embedded in their filename — to resolve the authoritative
105
+ // newest-by-mtime. A long-running session (older filename, newer mtime) is still
106
+ // caught as long as it is among the newest `limit * STAT_OVERSCAN` created.
107
+ const STAT_OVERSCAN = 4;
103
108
  /**
104
109
  * List rollout files under $CODEX_HOME/sessions, newest first (by mtime).
105
110
  *
106
- * Codex partitions rollouts by date: `sessions/YYYY/MM/DD/rollout-*.jsonl`. A
107
- * busy account accumulates tens of thousands of these over time (one real
108
- * machine: 14k files / 1.4 GB). The previous implementation walked AND
109
- * `stat`ed EVERY file on every call before slicing to `limit`, which made
110
- * callers like `GET /codex/usage` and the TokenLedger scan take tens of
111
- * seconds (the route timed out) on a large history.
111
+ * Codex partitions rollouts by date: `sessions/YYYY/MM/DD/rollout-<ts>-<uuid>.jsonl`.
112
+ * A busy account accumulates tens of thousands of these (one real machine: 14k
113
+ * files / 1.4 GB). The original implementation `stat`ed EVERY file before
114
+ * slicing to `limit`; an interim fix narrowed that to the newest partitions but
115
+ * still `stat`ed every file in them (~3.6k on the same machine) — fast in a
116
+ * healthy process, but catastrophic when the server's event loop is CPU-starved
117
+ * (each `await fs.stat` needs a loop turn), which is exactly when `GET
118
+ * /codex/usage` timed out.
112
119
  *
113
- * This walks the date-partition directories in DESCENDING date order and
114
- * `stat`s only the rollout files in the newest partitions, stopping once it has
115
- * `limit` candidates (after covering >= MIN_PARTITIONS_SCANNED non-empty
116
- * partitions, so a day is never cut mid-way and the cross-midnight boundary is
117
- * covered). Work is bounded to the most-recent partitions instead of the entire
118
- * history. Trade-off: "newest by mtime" becomes "newest among the most-recent
119
- * date partitions, then by mtime" — for the rate-limit reader this is exact
120
- * (the account-wide windows are identical across any recently-active session),
121
- * and for the resume/token-ledger callers a session older than the scanned
122
- * partitions yet still the single most-recently-touched file is the only miss,
123
- * which is vanishingly rare in practice. A non-date-partitioned layout falls
124
- * back to the original full walk (correctness over speed).
120
+ * This avoids the per-file `stat` storm entirely:
121
+ * 1. Enumerate date-partition dirs newest-first; `readdir` (one syscall each,
122
+ * NO per-file stat) the newest partitions to collect candidate PATHS until
123
+ * we have >= `limit * STAT_OVERSCAN` of them AND have covered >=
124
+ * MIN_PARTITIONS_SCANNED non-empty partitions.
125
+ * 2. Sort candidates DESCENDING by path — the zero-padded `YYYY/MM/DD` dir +
126
+ * `rollout-<ISO-ish-ts>` filename make a lexicographic sort chronological
127
+ * by CREATION time, no `stat` needed.
128
+ * 3. `stat` ONLY the top `limit * STAT_OVERSCAN` candidates for the
129
+ * authoritative mtime, sort by mtime, return the newest `limit`.
130
+ *
131
+ * So a `limit`-8 call does ~32 `stat`s instead of ~3.6k. Trade-off: "newest by
132
+ * mtime" becomes "newest by mtime among the newest-created candidates" — exact
133
+ * for the rate-limit reader (account-wide windows are identical across any
134
+ * recently-active session); the only miss for the resume/token-ledger callers is
135
+ * a session whose creation is older than the newest `limit * STAT_OVERSCAN` yet
136
+ * is still the single most-recently-touched file — vanishingly rare. A
137
+ * non-date-partitioned layout falls back to the original full walk.
125
138
  */
126
139
  export async function listAllRollouts(codexHome, limit = 100) {
127
140
  const root = path.join(codexHomeFromConfig(codexHome), 'sessions');
@@ -134,7 +147,9 @@ export async function listAllRollouts(codexHome, limit = 100) {
134
147
  all.sort((a, b) => b.mtime - a.mtime);
135
148
  return all.slice(0, limit);
136
149
  }
137
- const out = [];
150
+ const statTarget = Math.max(limit, 1) * STAT_OVERSCAN;
151
+ // Phase 1: collect candidate PATHS (readdir only — no per-file stat).
152
+ const candidates = [];
138
153
  let partitionsWithFiles = 0;
139
154
  for (const dir of dayDirs) {
140
155
  let entries;
@@ -148,24 +163,30 @@ export async function listAllRollouts(codexHome, limit = 100) {
148
163
  for (const entry of entries) {
149
164
  if (!entry.startsWith('rollout-') || !entry.endsWith('.jsonl'))
150
165
  continue;
151
- const full = path.join(dir, entry);
152
- let stat;
153
- try {
154
- stat = await fs.stat(full);
155
- }
156
- catch {
157
- continue;
158
- }
159
- if (stat.isFile()) {
160
- out.push({ path: full, mtime: stat.mtimeMs });
161
- had = true;
162
- }
166
+ candidates.push(path.join(dir, entry));
167
+ had = true;
163
168
  }
164
169
  if (had)
165
170
  partitionsWithFiles++;
166
- if (out.length >= limit && partitionsWithFiles >= MIN_PARTITIONS_SCANNED)
171
+ if (candidates.length >= statTarget && partitionsWithFiles >= MIN_PARTITIONS_SCANNED)
167
172
  break;
168
173
  }
174
+ // Phase 2: sort by path descending (chronological by creation), keep the top.
175
+ candidates.sort((a, b) => (a > b ? -1 : a < b ? 1 : 0));
176
+ const top = candidates.slice(0, statTarget);
177
+ // Phase 3: stat ONLY the top candidates for authoritative mtime.
178
+ const out = [];
179
+ for (const full of top) {
180
+ let stat;
181
+ try {
182
+ stat = await fs.stat(full);
183
+ }
184
+ catch {
185
+ continue;
186
+ }
187
+ if (stat.isFile())
188
+ out.push({ path: full, mtime: stat.mtimeMs });
189
+ }
169
190
  out.sort((a, b) => b.mtime - a.mtime);
170
191
  return out.slice(0, limit);
171
192
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sessionPaths.js","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/sessionPaths.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sessionPaths.js","sourceRoot":"","sources":["../../../../../src/providers/adapters/openai-codex/observability/sessionPaths.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.125",
3
+ "version": "1.3.126",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-30T22:08:54.842Z",
5
- "instarVersion": "1.3.125",
4
+ "generatedAt": "2026-05-30T22:30:07.950Z",
5
+ "instarVersion": "1.3.126",
6
6
  "entryCount": 193,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -4,59 +4,50 @@
4
4
 
5
5
  ## What Changed
6
6
 
7
- **Fixed the ROOT of duplicate agent-to-agent replies: `POST /messages/relay-agent`
8
- now responds at the accept boundary instead of after the session spawn.** The
9
- co-located inbound handler used to AWAIT `handleInboundMessage` — a session
10
- spawn/resume that routinely takes 9-30 s — before responding. But the sender
11
- (`MessageRouter.relay`) uses a 5-second fetch timeout and reads only whether the
12
- request succeeded. So whenever the receiver's spawn outran 5 s, the sender gave
13
- up, retried with a fresh message id, and the receiver spawned a SECOND session →
14
- a duplicate reply. The content-hash dedup shipped earlier is the symptom
15
- backstop; this removes the cause. The handler now responds `{ ok:true,
16
- accepted:true, threadline:{accepted:true, async:true} }` the instant the message
17
- is accepted + gated, and runs the spawn in the background. Everything that must
18
- happen first — the dedup, the reply-waiter resolution, and the warrants-reply
19
- gate (including its suppress short-circuit) — still runs synchronously before the
20
- response. Scoped to the co-located path; the cross-machine relay-funnel path
21
- (which has retry-on-error semantics to preserve) is tracked separately.
7
+ **Fixed `GET /codex/usage` still timing out on a large codex history under server
8
+ load — by not statting thousands of files.** The previous fix (v1.3.124) narrowed
9
+ `listAllRollouts` to the newest date-partitions, but it still `await fs.stat`-ed
10
+ every file in them — ~3,637 on a real account. That is fast in a healthy process,
11
+ but when the server's event loop is CPU-starved (the log showed a ~14 s drift),
12
+ those thousands of sequential awaited stats each wait for a starved loop turn and
13
+ the route hangs for tens of seconds. (Other endpoints were fine — they do only a
14
+ handful of awaits.) `listAllRollouts` now finds the newest rollouts WITHOUT the
15
+ stat storm: it `readdir`s the newest partitions to collect candidate paths (one
16
+ syscall per dir, no per-file stat), sorts them by the creation timestamp embedded
17
+ in the filename, and `stat`s ONLY the newest `limit * 4` candidates (~32 for the
18
+ usage reader) for the authoritative mtime. The returned newest-N is unchanged.
19
+ This fixes all four callers of the helper at once.
22
20
 
23
21
  ## Summary of New Capabilities
24
22
 
25
- - No new capability — a robustness fix. Agent-to-agent messages on the same
26
- machine no longer risk a duplicate reply when the receiver's session spawn is
27
- slower than the sender's 5-second delivery timeout.
23
+ - No new capability — a performance fix. `listAllRollouts` now does ~32 file
24
+ stats instead of ~3,637 on a large account, so codex usage/ledger/resume reads
25
+ stay fast even when the server is under heavy load.
28
26
 
29
27
  ## What to Tell Your User
30
28
 
31
- Agents talking to each other on the same machine no longer occasionally send the
32
- same reply twice. The cause was that a receiving agent waited until it had fully
33
- spun up a session — up to half a minute — before telling the sender the message
34
- arrived, but the sender only waits five seconds and would resend. Now the
35
- receiver acknowledges the moment the message is safely accepted and does the
36
- slow work in the background, so the sender never times out and never resends.
37
- Nothing changes for you; the real reply still arrives as before, just without the
38
- occasional duplicate.
29
+ The codex usage check now answers instantly even on a heavy account AND when the
30
+ machine is under heavy load. The earlier fix already stopped it from reading the
31
+ whole history, but it still checked the timestamp of every recent log file — a
32
+ few thousand of them — one at a time, which stalled when the server was
33
+ overloaded. It now reads just the list of recent filenames, picks out the newest
34
+ by the time in the name, and only checks the real timestamps of the few most
35
+ recent. Nothing else changes.
39
36
 
40
37
  ## Evidence
41
38
 
42
- **Reproduction / root cause.** Confirmed in the code: the co-located sender
43
- `MessageRouter.relayToAgent` (`src/messaging/MessageRouter.ts:486-510`) wraps its
44
- POST to `/messages/relay-agent` in `AbortSignal.timeout(5000)` and returns only
45
- `response.ok`. The receiver's handler awaited `handleInboundMessage` (a 9-30 s
46
- session spawn) before responding, so any spawn over 5 s aborted the sender's
47
- fetch → the sender retried with a fresh `message.id`, which slips past the
48
- id-based relay dedup → a second spawn and a duplicate reply. (The 2026-05-30
49
- duplicate-reply incidents on the echo↔codey loop are this path.)
50
-
51
- **Before / after.** Before: the response is sent only after the full spawn. After:
52
- the response is `{accepted:true, async:true}` sent at the accept boundary, with
53
- the spawn running in the background. The rewritten integration test holds the
54
- background handler open and asserts the HTTP response returns while the handler
55
- is still mid-spawn (`router-start` present, `router-end` not yet) — i.e. the
56
- sender is no longer made to wait. The handler still runs to completion in the
57
- background (not dropped); a background rejection still yields HTTP 200; the
58
- reply-waiter resolution and the warrants-reply gate's suppress short-circuit stay
59
- green; the content-hash dedup suite and the keystone gate-before-spawn wiring
60
- test stay green. An independent second-pass reviewer audited the change (it
61
- touches inbound messaging) and confirmed correctness after one artifact-honesty
62
- correction.
39
+ **Reproduction.** After deploying the first fix (v1.3.124) to a server with a
40
+ 14,277-file codex history, `GET /codex/usage` STILL returned a connection timeout
41
+ (curl status 000 after 25 s) while `/tokens/summary` (0.19 s) and `/capabilities`
42
+ (0.38 s) on the same server were fast. The server log showed the cause:
43
+ `[SleepWakeDetector] Drift ~14s under load ratio 1.72 — CPU starvation`. The
44
+ reader ran in 57 ms in an isolated process and 250 ms for 5 concurrent calls — so
45
+ the slowness was not the reader's logic but its ~3,637 sequential `await fs.stat`
46
+ calls colliding with a starved event loop (each stat callback waits for a loop
47
+ turn).
48
+
49
+ **Before / after.** Old: ~3,637 stats per call. New: against the same live
50
+ 14,277-file / 1.4 GB tree, a `limit`-8 call does **32 stats in 14 ms**, returning
51
+ the same newest rollout. A unit test (`fs.stat` spy) asserts the bounded stat
52
+ count for a 206-file / 42-partition fixture; the reader / TokenLedger / resume /
53
+ canary suites stay green.
@@ -0,0 +1,53 @@
1
+ # Instar Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **Fixed `GET /codex/usage` still timing out on a large codex history under server
8
+ load — by not statting thousands of files.** The previous fix (v1.3.124) narrowed
9
+ `listAllRollouts` to the newest date-partitions, but it still `await fs.stat`-ed
10
+ every file in them — ~3,637 on a real account. That is fast in a healthy process,
11
+ but when the server's event loop is CPU-starved (the log showed a ~14 s drift),
12
+ those thousands of sequential awaited stats each wait for a starved loop turn and
13
+ the route hangs for tens of seconds. (Other endpoints were fine — they do only a
14
+ handful of awaits.) `listAllRollouts` now finds the newest rollouts WITHOUT the
15
+ stat storm: it `readdir`s the newest partitions to collect candidate paths (one
16
+ syscall per dir, no per-file stat), sorts them by the creation timestamp embedded
17
+ in the filename, and `stat`s ONLY the newest `limit * 4` candidates (~32 for the
18
+ usage reader) for the authoritative mtime. The returned newest-N is unchanged.
19
+ This fixes all four callers of the helper at once.
20
+
21
+ ## Summary of New Capabilities
22
+
23
+ - No new capability — a performance fix. `listAllRollouts` now does ~32 file
24
+ stats instead of ~3,637 on a large account, so codex usage/ledger/resume reads
25
+ stay fast even when the server is under heavy load.
26
+
27
+ ## What to Tell Your User
28
+
29
+ The codex usage check now answers instantly even on a heavy account AND when the
30
+ machine is under heavy load. The earlier fix already stopped it from reading the
31
+ whole history, but it still checked the timestamp of every recent log file — a
32
+ few thousand of them — one at a time, which stalled when the server was
33
+ overloaded. It now reads just the list of recent filenames, picks out the newest
34
+ by the time in the name, and only checks the real timestamps of the few most
35
+ recent. Nothing else changes.
36
+
37
+ ## Evidence
38
+
39
+ **Reproduction.** After deploying the first fix (v1.3.124) to a server with a
40
+ 14,277-file codex history, `GET /codex/usage` STILL returned a connection timeout
41
+ (curl status 000 after 25 s) while `/tokens/summary` (0.19 s) and `/capabilities`
42
+ (0.38 s) on the same server were fast. The server log showed the cause:
43
+ `[SleepWakeDetector] Drift ~14s under load ratio 1.72 — CPU starvation`. The
44
+ reader ran in 57 ms in an isolated process and 250 ms for 5 concurrent calls — so
45
+ the slowness was not the reader's logic but its ~3,637 sequential `await fs.stat`
46
+ calls colliding with a starved event loop (each stat callback waits for a loop
47
+ turn).
48
+
49
+ **Before / after.** Old: ~3,637 stats per call. New: against the same live
50
+ 14,277-file / 1.4 GB tree, a `limit`-8 call does **32 stats in 14 ms**, returning
51
+ the same newest rollout. A unit test (`fs.stat` spy) asserts the bounded stat
52
+ count for a 206-file / 42-partition fixture; the reader / TokenLedger / resume /
53
+ canary suites stay green.
@@ -0,0 +1,55 @@
1
+ # Side-Effects Review — stat only the newest-created candidates (perf v2)
2
+
3
+ **Version / slug:** `codex-rollout-scan-perf-v2`
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` no longer `stat`s every file in the newest date-partitions
11
+ (~3,637 on the real machine). It collects candidate paths via `readdir` (no
12
+ per-file stat), sorts them by the creation timestamp embedded in the path, and
13
+ `stat`s ONLY the top `limit * STAT_OVERSCAN` (= 32 for the reader). Fixes
14
+ `GET /codex/usage` still timing out after the v1 fix because ~3,637 sequential
15
+ `await fs.stat`s are pathological under a CPU-starved event loop (server log:
16
+ "Drift ~14s … CPU starvation"). 14,277-file tree: 32 stats / 14 ms (was ~3,637).
17
+
18
+ ## Decision-point inventory
19
+
20
+ - No decision point. A read helper returns a file list; only the disk-scan
21
+ strategy changed.
22
+
23
+ ---
24
+
25
+ ## 1. Over-block / ## 2. Under-block
26
+ No block/allow surface. The only "miss" is the documented trade-off (a session
27
+ created older than the newest `limit*4` yet still the single most-recently-
28
+ touched file) — irrelevant for the rate-limit reader (account-wide windows) and
29
+ vanishingly rare for the other callers.
30
+
31
+ ## 3. Level-of-abstraction fit
32
+ Correct — the shared helper, so all four callers (codex-usage reader, TokenLedger
33
+ scan, resume index, canary) get the speedup from one change. The filename-as-
34
+ creation-timestamp ordering is a property of the codex layout the helper already
35
+ encodes.
36
+
37
+ ## 4. Signal vs authority compliance
38
+ Compliant — no authority; a read-only reporter.
39
+
40
+ ## 5. Interactions
41
+ - **Callers:** all pass a finite `limit` and want newest-N; the bounded-stat set
42
+ preserves that. `readdir` returns all names in one syscall (cheap); the
43
+ in-memory path sort of a few thousand strings is sub-ms.
44
+ - **No writes / no races:** read-only; opens + closes its own handles.
45
+ - **Robust under load:** the whole point — it now makes ~32 awaits instead of
46
+ ~3,637, so a starved event loop no longer makes the call hang.
47
+
48
+ ## 6. External surfaces
49
+ - `GET /codex/usage` becomes responsive on a heavy account even when the server
50
+ is CPU-starved. Response shape/semantics unchanged.
51
+ - No new route, no message.
52
+
53
+ ## 7. Rollback cost
54
+ Trivial — revert the `listAllRollouts` body to the v1 form. No data, no
55
+ migration.