instar 1.3.738 → 1.3.740

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.
@@ -35,4 +35,22 @@ export type UntrustedInputFlag = true | {
35
35
  false: string;
36
36
  };
37
37
  export declare const LLM_UNTRUSTED_INPUT: Readonly<Record<string, UntrustedInputFlag>>;
38
+ /** The kind of claim a judge callsite credits/refuses (spec §2). Different kinds
39
+ * take different evidence, so each `judgesClaims: true` entry declares its kind:
40
+ * - completionClaim: proof of asserted work ("the task is done / tests pass");
41
+ * - healthClaim: behavioral-signal sufficiency (stall/stuck/health classifiers);
42
+ * - scoredCredit: rubric evaluators that award credit for claimed work. */
43
+ export type ClaimKind = 'completionClaim' | 'healthClaim' | 'scoredCredit';
44
+ export declare const CLAIM_KINDS: ReadonlyArray<ClaimKind>;
45
+ /** The `judgesClaims` classification of one LLM callsite:
46
+ * - `{ claimKind }` → judges a claim (true), of the declared kind;
47
+ * - `false` → does not judge a completion/progress/health claim;
48
+ * - `{ false: '<reason>' }` → a judge-SHAPED callsite argued OUT of scope
49
+ * (pinned shrink-only + reviewed, like the untrustedInput argued-false set). */
50
+ export type JudgesClaimsFlag = {
51
+ claimKind: ClaimKind;
52
+ } | false | {
53
+ false: string;
54
+ };
55
+ export declare const LLM_JUDGES_CLAIMS: Readonly<Record<string, JudgesClaimsFlag>>;
38
56
  //# sourceMappingURL=llmBenchCoverage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"llmBenchCoverage.d.ts","sourceRoot":"","sources":["../../src/data/llmBenchCoverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,OAAO,EAAE,QAAQ,GAAG,QAAQ,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvB,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAqFtE,CAAC;AA4BF,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAiF5E,CAAC"}
1
+ {"version":3,"file":"llmBenchCoverage.d.ts","sourceRoot":"","sources":["../../src/data/llmBenchCoverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,OAAO,EAAE,QAAQ,GAAG,QAAQ,CAAA;CAAE,GAChC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvB,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAqFtE,CAAC;AA4BF,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAiF5E,CAAC;AA+CF;;;;6EAI6E;AAC7E,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG,aAAa,GAAG,cAAc,CAAC;AAE3E,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,SAAS,CAIhD,CAAC;AAEF;;;;oFAIoF;AACpF,MAAM,MAAM,gBAAgB,GAAG;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,GAAG,KAAK,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpF,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAmExE,CAAC"}
@@ -171,4 +171,72 @@ export const LLM_UNTRUSTED_INPUT = {
171
171
  false: 'attribution-manifest alias only (a legacy prompt-pattern matcher); the live matcher calls with attribution PromptGate, classified true there',
172
172
  },
173
173
  };
174
+ export const CLAIM_KINDS = [
175
+ 'completionClaim',
176
+ 'healthClaim',
177
+ 'scoredCredit',
178
+ ];
179
+ export const LLM_JUDGES_CLAIMS = {
180
+ // ── Judges (judgesClaims: true) ────────────────────────────────────────────
181
+ // Completion evaluators — credit/refuse a claim of asserted work.
182
+ CompletionEvaluator: { claimKind: 'completionClaim' }, // THE motivating callsite: credited a bare "tests pass"
183
+ UnjustifiedStopGate: { claimKind: 'completionClaim' }, // judges whether a stop is justified = is the claimed work actually done/blocked
184
+ JobReflector: { claimKind: 'completionClaim' }, // reflects on job success/completion (spec §2 pending-wave judge; classified now so the asymmetry can't silently reopen)
185
+ // Stall/stuck/health classifiers — credit/refuse a health claim about a session.
186
+ SessionWatchdog: { claimKind: 'healthClaim' }, // stuck-judge
187
+ PresenceProxy: { claimKind: 'healthClaim' }, // tier-3 stall judge
188
+ StallTriageNurse: { claimKind: 'healthClaim' }, // stall-triage diagnosis
189
+ TelegramAdapter: { claimKind: 'healthClaim' }, // confirmStallAlert — judges whether a session is genuinely stalled before alerting (inclusion criteria: stall/health classifier)
190
+ SlackAdapter: { claimKind: 'healthClaim' }, // confirmStallAlert (byte-identical prompt to Telegram's; parity)
191
+ // Scored evaluators — award credit for claimed work.
192
+ 'mentor-stage-b': { claimKind: 'scoredCredit' }, // classifies mentor signals over mentee output (spec §2 pending-wave judge)
193
+ // ── Not a completion/progress/health judge (judgesClaims: false) ────────────
194
+ // Sentinels that classify/summarize but do not credit a completion/health claim.
195
+ InputDetector: false, // legacy prompt-pattern matcher alias
196
+ InputGuard: false, // input-coherence, not a completion/health claim
197
+ SessionActivitySentinel: false, // activity DIGEST — summarizes activity, does not credit a claim (exclusion: pure summarizer)
198
+ CommitmentSentinel: false, // detects commitments in text
199
+ PromiseBeacon: false, // no live LLM prompt (hooks unwired at construction — see its bench exemption)
200
+ MessageSentinel: false, // classifies message intent (pause/emergency), not a completion claim
201
+ TopicIntentArcCheck: false, // arc-check classification of a topic's intent, not a completion/health claim
202
+ InputClassifier: false, // input auto-approve vs relay classification
203
+ SessionSummarySentinel: false, // summarizes tmux output → task/phase/files (exclusion: pure summarizer)
204
+ ProjectDriftChecker: false, // is-work-on-project coherence, not a completion/health claim
205
+ TemporalCoherenceChecker: false, // temporal-coherence classification
206
+ ResumeQueueDrainer: false, // inspects session state before a resume — judges STATE validity, not an agent's claim
207
+ InteractivePoolCanaryJudge: false, // judges a FIXED known-answer canary constant, not an agent claim
208
+ // Gates that classify an action/message but do not credit a completion/health claim.
209
+ PromptGate: false, // detects prompt-injection in content
210
+ AutoApprover: false, // mechanical key injection, no LLM prompt of its own
211
+ IntegrationGate: false, // delegates to JobReflector.reflect(), no own callsite
212
+ ExternalOperationGate: false, // classifies operation mutability/reversibility, not a completion claim
213
+ WarrantsReplyGate: false, // "should I reply?" — not a completion/health claim
214
+ CoherenceGate: false, // no own callsite — flows through CoherenceReviewer
215
+ MessagingToneGate: false, // reviews outbound tone/leaks
216
+ CoherenceReviewer: false, // reviews outbound coherence
217
+ LLMSanitizer: false, // sanitizes untrusted inbound content
218
+ OverrideDetector: false, // detects override intent in a turn
219
+ TaskClassifier: false, // classifies task type
220
+ ResumeValidator: false, // matches a resume UUID against a topic — a state match, not a claim
221
+ // Reflectors/jobs that extract/summarize/route but do not credit a completion/health claim.
222
+ crossModelReviewer: false, // reviews a SPEC document, not a session's completion claim
223
+ SelfKnowledgeTree: false, // extracts self-knowledge
224
+ TreeTriage: false, // triages tree fragments
225
+ TopicSummarizer: false, // summarizes a topic
226
+ ContextualEvaluator: false, // evaluates context relevance
227
+ RelationshipManager: false, // extracts relationship facts
228
+ StandardsConformanceReviewer: false, // reviews artifact-vs-standard conformance, not a session completion claim
229
+ DiscoveryEvaluator: false, // evaluates serendipity discoveries
230
+ Usher: false, // routes a turn to candidate topics
231
+ TopicIntentExtractor: false, // extracts topic intent from a turn
232
+ PreCompactionFlush: false, // extracts durable facts before compaction
233
+ TreeSynthesis: false, // synthesizes knowledge fragments → answer
234
+ LLMConflictResolver: false, // resolves divergent multi-machine state
235
+ openConversationBrief: false, // generates an A2A conversation brief
236
+ 'a2a-checkin': false, // summarizes A2A check-in threads
237
+ 'correction-learning': false, // distills recurring corrections → preference
238
+ PipeSessionSpawner: false, // spawns from task descriptions
239
+ CartographerSweep: false, // authors doc-tree summaries over code
240
+ StandardsCoverageEnrichment: false, // enriches standards-coverage rows
241
+ };
174
242
  //# sourceMappingURL=llmBenchCoverage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"llmBenchCoverage.js","sourceRoot":"","sources":["../../src/data/llmBenchCoverage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"llmBenchCoverage.js","sourceRoot":"","sources":["../../src/data/llmBenchCoverage.ts"],"names":[],"mappings":"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"}
@@ -67,10 +67,34 @@ export interface ReapLogEntry {
67
67
  /** Notify entries: the outcome this record asserts. */
68
68
  outcome?: ReapNotifyOutcome;
69
69
  }
70
+ /** Optional overrides for the self-limiting caps — primarily so tests can
71
+ * trigger rotation without writing 16MB. Production uses the module defaults. */
72
+ export interface ReapLogOptions {
73
+ maxLogBytes?: number;
74
+ tailReadBytes?: number;
75
+ maxSkipState?: number;
76
+ }
70
77
  export declare class ReapLog {
71
78
  private readonly logPath;
72
79
  private readonly machineId?;
73
- constructor(stateDir: string, machineId?: () => string | undefined);
80
+ private readonly maxLogBytes;
81
+ private readonly tailReadBytes;
82
+ private readonly maxSkipState;
83
+ /** session name → last-LOGGED skip signature (`${reason}::${skipped}`).
84
+ * A `recordSkipped` whose signature equals the session's last-logged one is
85
+ * the SAME permanent veto being re-evaluated at tick speed (open-commitment,
86
+ * not-lease-holder, protected …) — it is NOT re-appended. This is the
87
+ * primary cure for the reaper self-inflicted log flood (3218 open-commitment
88
+ * + 1608 not-lease-holder repeat rows ⇒ 142MB, 2026-07-03): mirror the
89
+ * reaper-audit "log on transition, not every tick" pattern. Cleared when the
90
+ * session is reaped so a same-named successor logs its first skip fresh. */
91
+ private readonly skipState;
92
+ /** Cheap running estimate of the current log's byte size, so rotation is
93
+ * decided WITHOUT a statSync on every append. Seeded from the real size on
94
+ * first append, then advanced by each write; re-synced on rotation. -1 =
95
+ * not yet seeded. */
96
+ private approxSize;
97
+ constructor(stateDir: string, machineId?: () => string | undefined, opts?: ReapLogOptions);
74
98
  recordReaped(e: {
75
99
  session: string;
76
100
  tmuxSession: string;
@@ -103,8 +127,26 @@ export declare class ReapLog {
103
127
  skipped: string;
104
128
  origin?: 'operator' | 'autonomous';
105
129
  }): void;
130
+ /** Record a session's last-logged skip signature, enforcing the map ceiling
131
+ * (oldest-first prune — Map preserves insertion order). Re-inserting an
132
+ * existing key does not reorder it, which is fine: churny NEW names are the
133
+ * growth risk, and those get pruned. */
134
+ private rememberSkip;
135
+ /** Forget a session's skip state — call when the session is gone (reaped) so a
136
+ * same-named successor logs its first skip fresh and the map can't leak. */
137
+ private forgetSkip;
106
138
  private append;
107
- /** Read the most-recent `limit` entries (newest last), tolerating partial/corrupt lines. */
139
+ /** Roll the log to `<path>.1` (single retained generation) once it would cross
140
+ * MAX_LOG_BYTES, so the file can never grow unbounded. Rename is O(1) — no
141
+ * data rewrite on the append hot path (rewriting a large file synchronously
142
+ * here would reintroduce the very event-loop stall we're removing). Seeds the
143
+ * cheap size estimate from the real file size on first use. */
144
+ private rotateIfNeeded;
145
+ /** Read the most-recent `limit` entries (newest last), tolerating partial/corrupt lines.
146
+ * Only the last TAIL_READ_BYTES of each file are ever read, so a large log can
147
+ * never be slurped whole into memory (the readFileSync-whole-file freeze). If
148
+ * the current file's tail doesn't yield enough rows and a rotated `.1` backup
149
+ * exists, its tail is merged so recent history survives a rotation boundary. */
108
150
  read(limit?: number): ReapLogEntry[];
109
151
  private normalizeEntry;
110
152
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ReapLog.d.ts","sourceRoot":"","sources":["../../src/monitoring/ReapLog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GACzB,UAAU,GACV,MAAM,GACN,uBAAuB,GACvB,UAAU,GACV,gBAAgB,CAAC;AAUrB,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX;oFACgF;IAChF,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf;+BAC2B;IAC3B,WAAW,EAAE,UAAU,GAAG,iBAAiB,GAAG,WAAW,MAAM,EAAE,GAAG,UAAU,MAAM,EAAE,CAAC;IACvF,MAAM,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACnC;;;6EAGyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oGAAoG;IACpG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;+DAG2D;IAC3D,UAAU,CAAC,EAAE,UAAU,GAAG,sBAAsB,CAAC;IACjD;kEAC8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC;IAC7C,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC7B;AAED,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAA2B;gBAE1C,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS;IAKlE,YAAY,CAAC,CAAC,EAAE;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,UAAU,GAAG,iBAAiB,CAAC;QAC7C,MAAM,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;QACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,UAAU,GAAG,sBAAsB,CAAC;QACjD,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,cAAc,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC;KAC9C,GAAG,IAAI;IAkBR;;;;;OAKG;IACH,YAAY,CAAC,CAAC,EAAE;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,OAAO,EAAE,iBAAiB,CAAC;QAC3B,oEAAoE;QACpE,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,IAAI;IAeR,aAAa,CAAC,CAAC,EAAE;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;KACpC,GAAG,IAAI;IAcR,OAAO,CAAC,MAAM;IASd,4FAA4F;IAC5F,IAAI,CAAC,KAAK,SAAM,GAAG,YAAY,EAAE;IAsBjC,OAAO,CAAC,cAAc;CA+CvB"}
1
+ {"version":3,"file":"ReapLog.d.ts","sourceRoot":"","sources":["../../src/monitoring/ReapLog.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,47 @@
14
14
  */
15
15
  import fs from 'node:fs';
16
16
  import path from 'node:path';
17
+ /**
18
+ * Read only the last `maxBytes` of a file and return its non-empty lines,
19
+ * newest last. Bounded by construction: an arbitrarily large log is never
20
+ * loaded whole (the readFileSync-whole-file pattern that froze the event loop
21
+ * on a 142MB reap-log, 2026-07-03). If the read starts mid-file (the file is
22
+ * larger than `maxBytes`), the first — possibly partial — line is dropped so a
23
+ * torn record is never mis-parsed. Never throws; an absent/unreadable file
24
+ * yields [].
25
+ */
26
+ function tailLines(filePath, maxBytes) {
27
+ let fd;
28
+ try {
29
+ const size = fs.statSync(filePath).size;
30
+ if (size === 0)
31
+ return [];
32
+ const readBytes = Math.min(size, maxBytes);
33
+ const start = size - readBytes;
34
+ const buf = Buffer.allocUnsafe(readBytes);
35
+ fd = fs.openSync(filePath, 'r');
36
+ fs.readSync(fd, buf, 0, readBytes, start);
37
+ const text = buf.toString('utf-8');
38
+ const lines = text.split('\n');
39
+ // Dropped: a leading partial line when we didn't read from byte 0.
40
+ if (start > 0 && lines.length > 0)
41
+ lines.shift();
42
+ return lines.filter((l) => l.trim().length > 0);
43
+ }
44
+ catch {
45
+ return []; // @silent-fallback-ok — absent/unreadable ⇒ no rows.
46
+ }
47
+ finally {
48
+ if (fd !== undefined) {
49
+ try {
50
+ fs.closeSync(fd);
51
+ }
52
+ catch {
53
+ /* fd already gone */
54
+ }
55
+ }
56
+ }
57
+ }
17
58
  const NOTIFY_OUTCOMES = new Set([
18
59
  'enqueued',
19
60
  'sent',
@@ -21,12 +62,47 @@ const NOTIFY_OUTCOMES = new Set([
21
62
  'no-topic',
22
63
  'enqueue-failed',
23
64
  ]);
65
+ /** Rotate the reap-log once it crosses this many bytes. The current file is
66
+ * renamed to `<path>.1` (O(1), no data rewrite) and a fresh file started, so
67
+ * the on-disk log can NEVER grow unbounded — even if some future caller floods
68
+ * it past the transition-dedup below. One backup generation is retained;
69
+ * `read()` merges its tail so recent history survives a rotation boundary. */
70
+ const MAX_LOG_BYTES = 16 * 1024 * 1024;
71
+ /** `read()` only ever pulls the last this-many bytes of a log file, so a large
72
+ * reap-log can never be slurped whole into memory (the 142MB readFileSync +
73
+ * split that blocked the event loop, 2026-07-03). Generous vs the default
74
+ * `limit` of 200 rows (~300 B/row ⇒ ~6.5k rows fit in 2 MB). */
75
+ const TAIL_READ_BYTES = 2 * 1024 * 1024;
76
+ /** Cap on the in-memory transition-dedup map. Live session names are bounded
77
+ * (dozens); this is a safety ceiling so a pathological churn of distinct names
78
+ * can't grow the map without bound. Oldest entries are pruned first. */
79
+ const MAX_SKIP_STATE = 2000;
24
80
  export class ReapLog {
25
81
  logPath;
26
82
  machineId;
27
- constructor(stateDir, machineId) {
83
+ maxLogBytes;
84
+ tailReadBytes;
85
+ maxSkipState;
86
+ /** session name → last-LOGGED skip signature (`${reason}::${skipped}`).
87
+ * A `recordSkipped` whose signature equals the session's last-logged one is
88
+ * the SAME permanent veto being re-evaluated at tick speed (open-commitment,
89
+ * not-lease-holder, protected …) — it is NOT re-appended. This is the
90
+ * primary cure for the reaper self-inflicted log flood (3218 open-commitment
91
+ * + 1608 not-lease-holder repeat rows ⇒ 142MB, 2026-07-03): mirror the
92
+ * reaper-audit "log on transition, not every tick" pattern. Cleared when the
93
+ * session is reaped so a same-named successor logs its first skip fresh. */
94
+ skipState = new Map();
95
+ /** Cheap running estimate of the current log's byte size, so rotation is
96
+ * decided WITHOUT a statSync on every append. Seeded from the real size on
97
+ * first append, then advanced by each write; re-synced on rotation. -1 =
98
+ * not yet seeded. */
99
+ approxSize = -1;
100
+ constructor(stateDir, machineId, opts = {}) {
28
101
  this.logPath = path.join(stateDir, '..', 'logs', 'reap-log.jsonl');
29
102
  this.machineId = machineId;
103
+ this.maxLogBytes = opts.maxLogBytes ?? MAX_LOG_BYTES;
104
+ this.tailReadBytes = opts.tailReadBytes ?? TAIL_READ_BYTES;
105
+ this.maxSkipState = opts.maxSkipState ?? MAX_SKIP_STATE;
30
106
  }
31
107
  recordReaped(e) {
32
108
  this.append({
@@ -44,6 +120,9 @@ export class ReapLog {
44
120
  ...(e.workEvidence && e.workEvidence.length > 0 ? { workEvidence: e.workEvidence } : {}),
45
121
  ...(e.evidenceSource ? { evidenceSource: e.evidenceSource } : {}),
46
122
  });
123
+ // The session is gone — drop its skip-dedup state so a same-named successor
124
+ // logs its first skip fresh and the map never leaks reaped names.
125
+ this.forgetSkip(e.session);
47
126
  }
48
127
  /**
49
128
  * Append one reap-notice delivery outcome record (reap-notify spec R1.3).
@@ -66,6 +145,18 @@ export class ReapLog {
66
145
  });
67
146
  }
68
147
  recordSkipped(e) {
148
+ // Log-on-transition: a reaper evaluates a permanently-vetoed session
149
+ // (open-commitment, not-lease-holder, protected, …) on EVERY tick and would
150
+ // emit an identical `skipped` row each time — 5k+ repeat rows ⇒ a 142MB log
151
+ // that froze the event loop when read (2026-07-03). Only append when the
152
+ // skip STATE changes for this session; a re-evaluation with the same
153
+ // (reason, skipped) is the same veto and is dropped. The reaper keeps
154
+ // evaluating every tick, so the moment the veto lifts (commitment closes,
155
+ // lease moves) the state changes and the next skip — or the reap — logs.
156
+ const sig = `${e.reason}::${e.skipped}`;
157
+ if (this.skipState.get(e.session) === sig)
158
+ return;
159
+ this.rememberSkip(e.session, sig);
69
160
  this.append({
70
161
  ts: new Date().toISOString(),
71
162
  type: 'skipped',
@@ -78,27 +169,77 @@ export class ReapLog {
78
169
  machine: this.machineId?.(),
79
170
  });
80
171
  }
172
+ /** Record a session's last-logged skip signature, enforcing the map ceiling
173
+ * (oldest-first prune — Map preserves insertion order). Re-inserting an
174
+ * existing key does not reorder it, which is fine: churny NEW names are the
175
+ * growth risk, and those get pruned. */
176
+ rememberSkip(session, sig) {
177
+ this.skipState.set(session, sig);
178
+ while (this.skipState.size > this.maxSkipState) {
179
+ const oldest = this.skipState.keys().next().value;
180
+ if (oldest === undefined)
181
+ break;
182
+ this.skipState.delete(oldest);
183
+ }
184
+ }
185
+ /** Forget a session's skip state — call when the session is gone (reaped) so a
186
+ * same-named successor logs its first skip fresh and the map can't leak. */
187
+ forgetSkip(session) {
188
+ this.skipState.delete(session);
189
+ }
81
190
  append(entry) {
82
191
  try {
83
192
  fs.mkdirSync(path.dirname(this.logPath), { recursive: true });
84
- fs.appendFileSync(this.logPath, JSON.stringify(entry) + '\n');
193
+ const line = JSON.stringify(entry) + '\n';
194
+ this.rotateIfNeeded(Buffer.byteLength(line));
195
+ fs.appendFileSync(this.logPath, line);
196
+ if (this.approxSize >= 0)
197
+ this.approxSize += Buffer.byteLength(line);
85
198
  }
86
199
  catch {
87
200
  // never throw from the audit sink
88
201
  }
89
202
  }
90
- /** Read the most-recent `limit` entries (newest last), tolerating partial/corrupt lines. */
91
- read(limit = 200) {
92
- let raw;
203
+ /** Roll the log to `<path>.1` (single retained generation) once it would cross
204
+ * MAX_LOG_BYTES, so the file can never grow unbounded. Rename is O(1) — no
205
+ * data rewrite on the append hot path (rewriting a large file synchronously
206
+ * here would reintroduce the very event-loop stall we're removing). Seeds the
207
+ * cheap size estimate from the real file size on first use. */
208
+ rotateIfNeeded(incomingBytes) {
209
+ if (this.approxSize < 0) {
210
+ try {
211
+ this.approxSize = fs.statSync(this.logPath).size;
212
+ }
213
+ catch {
214
+ this.approxSize = 0; // absent file ⇒ empty
215
+ }
216
+ }
217
+ if (this.approxSize + incomingBytes <= this.maxLogBytes)
218
+ return;
93
219
  try {
94
- raw = fs.readFileSync(this.logPath, 'utf-8');
220
+ fs.renameSync(this.logPath, `${this.logPath}.1`); // overwrites a prior .1
95
221
  }
96
222
  catch {
97
- // @silent-fallback-ok — no log file yet means no reaps recorded; an empty
98
- // list is the correct answer, not a degraded one.
99
- return [];
223
+ // @silent-fallback-ok — if the roll fails the append still proceeds; the
224
+ // read path is tail-bounded so an oversize file is a perf hit, never a
225
+ // correctness failure.
226
+ }
227
+ this.approxSize = 0;
228
+ }
229
+ /** Read the most-recent `limit` entries (newest last), tolerating partial/corrupt lines.
230
+ * Only the last TAIL_READ_BYTES of each file are ever read, so a large log can
231
+ * never be slurped whole into memory (the readFileSync-whole-file freeze). If
232
+ * the current file's tail doesn't yield enough rows and a rotated `.1` backup
233
+ * exists, its tail is merged so recent history survives a rotation boundary. */
234
+ read(limit = 200) {
235
+ const wanted = limit > 0 ? limit : Number.MAX_SAFE_INTEGER;
236
+ let lines = tailLines(this.logPath, this.tailReadBytes);
237
+ if (lines.length < wanted) {
238
+ // Backfill from the rotated generation (older lines first).
239
+ const older = tailLines(`${this.logPath}.1`, this.tailReadBytes);
240
+ if (older.length > 0)
241
+ lines = older.concat(lines);
100
242
  }
101
- const lines = raw.split('\n').filter((l) => l.trim().length > 0);
102
243
  const tail = limit > 0 ? lines.slice(-limit) : lines;
103
244
  const out = [];
104
245
  for (const line of tail) {
@@ -1 +1 @@
1
- {"version":3,"file":"ReapLog.js","sourceRoot":"","sources":["../../src/monitoring/ReapLog.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ReapLog.js","sourceRoot":"","sources":["../../src/monitoring/ReapLog.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.738",
3
+ "version": "1.3.740",
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-07-03T22:30:08.221Z",
5
- "instarVersion": "1.3.738",
4
+ "generatedAt": "2026-07-03T23:17:57.781Z",
5
+ "instarVersion": "1.3.740",
6
6
  "entryCount": 202,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -226,3 +226,137 @@ export const LLM_UNTRUSTED_INPUT: Readonly<Record<string, UntrustedInputFlag>> =
226
226
  'attribution-manifest alias only (a legacy prompt-pattern matcher); the live matcher calls with attribution PromptGate, classified true there',
227
227
  },
228
228
  };
229
+
230
+ // ───────────────────────────────────────────────────────────────────────────
231
+ // Evidence-Bar Extension — Judge Prompts (defect class 3 / claim-vs-evidence —
232
+ // docs/specs/evidence-bar-judge-extension.md §2 "the judge-nature classification")
233
+ //
234
+ // The `judgesClaims` axis of the program's shared per-callsite metadata record
235
+ // (class-closure-gate.md §"Program-shared machinery"; co-located in THIS file
236
+ // with the bench-coverage record it extends, exactly like the sibling
237
+ // `untrustedInput` axis). The consolidated axis ratchet derives the required
238
+ // bench axes from all these per-callsite axes together.
239
+ //
240
+ // WHY (earned): on 2026-07-02 the INSTAR-Bench v2 defect-class review found the
241
+ // completion judge (and four other model routes on the same case) credited a
242
+ // BARE assertion ("tests pass," no output shown) as satisfied evidence — the
243
+ // judge prompt never defined what counts as evidence, so models defaulted to
244
+ // crediting the claim. The agent-facing "Bug-Fix Evidence Bar" holds the
245
+ // CLAIMANT ("verify before you claim"); the prompts that JUDGE such claims were
246
+ // never given the same rule. This classification is the structural record of
247
+ // WHICH callsites judge a claim, so the (deferred) bench-axis ratchet can
248
+ // require both a bare-claim (false-accept) AND a real-evidence (false-reject)
249
+ // case for each — the known over-correction hazard (the first fix rejected REAL
250
+ // evidence on 6 routes) makes the false-reject direction mandatory.
251
+ //
252
+ // INCLUSION CRITERIA (spec §2): the callsite's task is to CREDIT or REFUSE an
253
+ // agent/session claim of completion, progress, or health — verdict gates,
254
+ // completion evaluators, stuck/stall/health classifiers, and scored evaluators
255
+ // that award credit for claimed work. EXCLUSION: pure summarizers and
256
+ // extractors are out unless they score or credit a claim.
257
+ //
258
+ // THE FIELD IS REQUIRED AND EXPLICIT FOR EVERY COMPONENT_CATEGORY KEY — there is
259
+ // NO DEFAULT (same polarity rule as `untrustedInput`). A `{ claimKind }` entry
260
+ // is a judge (true) and declares WHICH kind of claim it judges (the axis cases +
261
+ // accepted evidence classes are authored per kind). Plain `false` is a callsite
262
+ // that does not judge a completion/progress/health claim. A judge-SHAPED
263
+ // callsite that argues it does NOT judge claims is written as
264
+ // `{ false: '<argued reason>' }` and pinned shrink-only in
265
+ // tests/unit/judges-claims-classification-ratchet.test.ts — a silent omission
266
+ // is red CI, so the flag can never default toward the un-benched state.
267
+ //
268
+ // DETERMINISTIC ARM (spec "Honest reach"): the real-check `verification_command`
269
+ // verifier is NAMED in the spec seed but is NOT an LLM callsite (it runs the
270
+ // actual command on a met-verdict) — it has no COMPONENT_CATEGORY key and is
271
+ // therefore out of this LLM classification by construction. A prompt bar governs
272
+ // what is SHOWN; verification of what is TRUE belongs to that deterministic arm.
273
+ // ───────────────────────────────────────────────────────────────────────────
274
+
275
+ /** The kind of claim a judge callsite credits/refuses (spec §2). Different kinds
276
+ * take different evidence, so each `judgesClaims: true` entry declares its kind:
277
+ * - completionClaim: proof of asserted work ("the task is done / tests pass");
278
+ * - healthClaim: behavioral-signal sufficiency (stall/stuck/health classifiers);
279
+ * - scoredCredit: rubric evaluators that award credit for claimed work. */
280
+ export type ClaimKind = 'completionClaim' | 'healthClaim' | 'scoredCredit';
281
+
282
+ export const CLAIM_KINDS: ReadonlyArray<ClaimKind> = [
283
+ 'completionClaim',
284
+ 'healthClaim',
285
+ 'scoredCredit',
286
+ ];
287
+
288
+ /** The `judgesClaims` classification of one LLM callsite:
289
+ * - `{ claimKind }` → judges a claim (true), of the declared kind;
290
+ * - `false` → does not judge a completion/progress/health claim;
291
+ * - `{ false: '<reason>' }` → a judge-SHAPED callsite argued OUT of scope
292
+ * (pinned shrink-only + reviewed, like the untrustedInput argued-false set). */
293
+ export type JudgesClaimsFlag = { claimKind: ClaimKind } | false | { false: string };
294
+
295
+ export const LLM_JUDGES_CLAIMS: Readonly<Record<string, JudgesClaimsFlag>> = {
296
+ // ── Judges (judgesClaims: true) ────────────────────────────────────────────
297
+ // Completion evaluators — credit/refuse a claim of asserted work.
298
+ CompletionEvaluator: { claimKind: 'completionClaim' }, // THE motivating callsite: credited a bare "tests pass"
299
+ UnjustifiedStopGate: { claimKind: 'completionClaim' }, // judges whether a stop is justified = is the claimed work actually done/blocked
300
+ JobReflector: { claimKind: 'completionClaim' }, // reflects on job success/completion (spec §2 pending-wave judge; classified now so the asymmetry can't silently reopen)
301
+
302
+ // Stall/stuck/health classifiers — credit/refuse a health claim about a session.
303
+ SessionWatchdog: { claimKind: 'healthClaim' }, // stuck-judge
304
+ PresenceProxy: { claimKind: 'healthClaim' }, // tier-3 stall judge
305
+ StallTriageNurse: { claimKind: 'healthClaim' }, // stall-triage diagnosis
306
+ TelegramAdapter: { claimKind: 'healthClaim' }, // confirmStallAlert — judges whether a session is genuinely stalled before alerting (inclusion criteria: stall/health classifier)
307
+ SlackAdapter: { claimKind: 'healthClaim' }, // confirmStallAlert (byte-identical prompt to Telegram's; parity)
308
+
309
+ // Scored evaluators — award credit for claimed work.
310
+ 'mentor-stage-b': { claimKind: 'scoredCredit' }, // classifies mentor signals over mentee output (spec §2 pending-wave judge)
311
+
312
+ // ── Not a completion/progress/health judge (judgesClaims: false) ────────────
313
+ // Sentinels that classify/summarize but do not credit a completion/health claim.
314
+ InputDetector: false, // legacy prompt-pattern matcher alias
315
+ InputGuard: false, // input-coherence, not a completion/health claim
316
+ SessionActivitySentinel: false, // activity DIGEST — summarizes activity, does not credit a claim (exclusion: pure summarizer)
317
+ CommitmentSentinel: false, // detects commitments in text
318
+ PromiseBeacon: false, // no live LLM prompt (hooks unwired at construction — see its bench exemption)
319
+ MessageSentinel: false, // classifies message intent (pause/emergency), not a completion claim
320
+ TopicIntentArcCheck: false, // arc-check classification of a topic's intent, not a completion/health claim
321
+ InputClassifier: false, // input auto-approve vs relay classification
322
+ SessionSummarySentinel: false, // summarizes tmux output → task/phase/files (exclusion: pure summarizer)
323
+ ProjectDriftChecker: false, // is-work-on-project coherence, not a completion/health claim
324
+ TemporalCoherenceChecker: false, // temporal-coherence classification
325
+ ResumeQueueDrainer: false, // inspects session state before a resume — judges STATE validity, not an agent's claim
326
+ InteractivePoolCanaryJudge: false, // judges a FIXED known-answer canary constant, not an agent claim
327
+
328
+ // Gates that classify an action/message but do not credit a completion/health claim.
329
+ PromptGate: false, // detects prompt-injection in content
330
+ AutoApprover: false, // mechanical key injection, no LLM prompt of its own
331
+ IntegrationGate: false, // delegates to JobReflector.reflect(), no own callsite
332
+ ExternalOperationGate: false, // classifies operation mutability/reversibility, not a completion claim
333
+ WarrantsReplyGate: false, // "should I reply?" — not a completion/health claim
334
+ CoherenceGate: false, // no own callsite — flows through CoherenceReviewer
335
+ MessagingToneGate: false, // reviews outbound tone/leaks
336
+ CoherenceReviewer: false, // reviews outbound coherence
337
+ LLMSanitizer: false, // sanitizes untrusted inbound content
338
+ OverrideDetector: false, // detects override intent in a turn
339
+ TaskClassifier: false, // classifies task type
340
+ ResumeValidator: false, // matches a resume UUID against a topic — a state match, not a claim
341
+
342
+ // Reflectors/jobs that extract/summarize/route but do not credit a completion/health claim.
343
+ crossModelReviewer: false, // reviews a SPEC document, not a session's completion claim
344
+ SelfKnowledgeTree: false, // extracts self-knowledge
345
+ TreeTriage: false, // triages tree fragments
346
+ TopicSummarizer: false, // summarizes a topic
347
+ ContextualEvaluator: false, // evaluates context relevance
348
+ RelationshipManager: false, // extracts relationship facts
349
+ StandardsConformanceReviewer: false, // reviews artifact-vs-standard conformance, not a session completion claim
350
+ DiscoveryEvaluator: false, // evaluates serendipity discoveries
351
+ Usher: false, // routes a turn to candidate topics
352
+ TopicIntentExtractor: false, // extracts topic intent from a turn
353
+ PreCompactionFlush: false, // extracts durable facts before compaction
354
+ TreeSynthesis: false, // synthesizes knowledge fragments → answer
355
+ LLMConflictResolver: false, // resolves divergent multi-machine state
356
+ openConversationBrief: false, // generates an A2A conversation brief
357
+ 'a2a-checkin': false, // summarizes A2A check-in threads
358
+ 'correction-learning': false, // distills recurring corrections → preference
359
+ PipeSessionSpawner: false, // spawns from task descriptions
360
+ CartographerSweep: false, // authors doc-tree summaries over code
361
+ StandardsCoverageEnrichment: false, // enriches standards-coverage rows
362
+ };
@@ -0,0 +1,77 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: minor -->
5
+
6
+ ## What Changed
7
+
8
+ The mechanical arm of the **Evidence-Bar Extension to Judge Prompts** standard
9
+ (`docs/specs/evidence-bar-judge-extension.md`, defect class 3 / `claim-vs-evidence` closure),
10
+ shipped as a self-contained DARK increment — no runtime wiring, no operator-gated registry
11
+ amendment, no config key, no behavioral prompt change. It is the structural answer to the
12
+ 2026-07-02 INSTAR-Bench v2 finding that the completion judge (and four other model routes on
13
+ the same case) credited a BARE assertion ("tests pass," no output shown) as satisfied evidence:
14
+ the agent-facing **Bug-Fix Evidence Bar** holds the CLAIMANT ("verify before you claim"), but
15
+ the prompts that JUDGE such claims were never given the same rule — an asymmetry where we hold
16
+ the claimer to a bar the judge does not know exists.
17
+
18
+ This increment ships **the judge-nature classification + its CI ratchet only**:
19
+
20
+ - The **`judgesClaims` classification** (`LLM_JUDGES_CLAIMS` in `src/data/llmBenchCoverage.ts`):
21
+ the `judgesClaims` axis of the program's ONE shared per-callsite metadata record (sibling of
22
+ the authority-clause `untrustedInput` axis), required-explicit for every LLM component (no
23
+ default — a silent omission is red CI). A judge is a `{ claimKind }` entry declaring WHICH
24
+ kind of claim it credits/refuses — `completionClaim` (proof of asserted work), `healthClaim`
25
+ (stall/stuck/health signal sufficiency), or `scoredCredit` (rubric evaluators) — because those
26
+ are NOT one evidence problem and their axis cases are authored per kind. Nine callsites classify
27
+ as judges (the five measured completion/health judges — CompletionEvaluator, UnjustifiedStopGate,
28
+ SessionWatchdog, PresenceProxy, StallTriageNurse — plus the two stall-confirm adapters and the
29
+ two spec-named pending-wave judges JobReflector + mentor-stage-b); the deterministic real-check
30
+ `verification_command` verifier is seed-named but is NOT an LLM callsite (it runs the actual
31
+ command), out of the classification by construction.
32
+ - A **classification ratchet** (`tests/unit/judges-claims-classification-ratchet.test.ts`):
33
+ required-explicit + no-dangling + valid-claimKind-per-judge + the spec-named JUDGE SEED pinned
34
+ as a judge (the callsites measured crediting a bare claim can't silently slip out of scope) +
35
+ the argued-false set pinned shrink-only with a real-reason floor. Same pinned-baseline family as
36
+ `llm-bench-coverage-ratchet` and `untrusted-input-classification-ratchet`.
37
+
38
+ Deliberately OUT of scope (not orphan deferrals — see `upgrades/side-effects/`):
39
+ - The **registry amendment** (spec §1) — operator-gated; ships ONLY with Justin's explicit
40
+ sign-off. It is DRAFTED in the spec; this run does not edit the standards registry.
41
+ - The **bench-axis pair ratchet** (spec §3: a bare-claim false-accept case + a real-evidence
42
+ false-reject case per judge) — blocked on the SAME program-wide "batteries readable by CI"
43
+ decision the sibling authority-clause axis ratchet deferred on (`research/` is absent from
44
+ canonical main), owned by `class-closure-gate.md` §"Program-shared machinery" and binding all
45
+ three axis specs.
46
+ - The **`evidenceBar()` prompt clause** (spec §4) — a sibling of the authority clause in
47
+ `src/core/promptClauses.ts`. That shared library is introduced by the still-open sibling PR
48
+ (authority-clause); adding the clause here would collide on the same new file. The clause is
49
+ only consumed via the A/B-gated per-component migrations (spec §4/rollout §2), which are
50
+ behavioral and deferred regardless. It lands as a follow-up once the shared library is on main.
51
+
52
+ ## What to Tell Your User
53
+
54
+ Nothing changes for you right now — this ships **dark**, and it is maintainer-only machinery (a
55
+ no-op on your install unless you develop instar itself). It records WHICH of my AI checks exist to
56
+ JUDGE a completion/health claim, so the same "a claim is not evidence — show the output" bar that
57
+ already binds what I claim can be extended to the checks that judge those claims. The judge-prompt
58
+ wording and the registry text itself change later, and only after an operator sign-off.
59
+
60
+ ## Summary of New Capabilities
61
+
62
+ None active for end users in this increment — everything ships dark and additive. (For instar
63
+ maintainers: a required per-callsite `judgesClaims` classification with a shrink-only ratchet and
64
+ a pinned JUDGE-SEED floor, so a claim-judging callsite can never silently ship un-benched against
65
+ the bare-claim / real-evidence axis pair.)
66
+
67
+ ## Evidence
68
+
69
+ - `npx vitest run tests/unit/judges-claims-classification-ratchet.test.ts tests/unit/llm-bench-coverage-ratchet.test.ts`
70
+ → **2 files, 13 tests, 0 failures** (required-explicit + no-dangling + valid-claimKind + the
71
+ JUDGE-SEED floor + shrink-only argued-false + the grounding canary; the pre-existing coverage
72
+ ratchet still green against the additive record). During authoring the ratchet caught three
73
+ unclassified callsites (TopicIntentArcCheck, InputClassifier, SessionSummarySentinel) and
74
+ failed the build until they were classified — the required-explicit guard doing its job.
75
+ - `npx tsc --noEmit` → exit 0. `npm run lint` → exit 0.
76
+ - Dark-by-construction: `LLM_JUDGES_CLAIMS` is build-time metadata read only by the new pinned
77
+ ratchet; it changes NO prompt text and wires NO runtime gate.
@@ -0,0 +1,65 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Three self-limiting changes to `src/monitoring/ReapLog.ts` (the audit trail behind
9
+ `GET /sessions/reap-log`) that stop the reaper from flooding — and freezing on — its own log.
10
+
11
+ The reaper re-evaluates every session on each tick. When a session **can't** be reaped yet — it
12
+ holds an open commitment, or it belongs to another machine (`not-lease-holder`), or it's
13
+ protected — the reaper skips it and recorded a `skipped` row **every tick**. On a live agent
14
+ that produced 3218 `open-commitment` + 1608 `not-lease-holder` **identical repeat rows** and
15
+ grew `reap-log.jsonl` to **142MB / 463k lines** (observed 2026-07-03). Worse, `read()` did
16
+ `fs.readFileSync(WHOLE file).split('\n')` just to return the last N rows — a 142MB slurp that
17
+ **blocked the event loop** (~90s CPU-bound stalls) whenever the route was queried.
18
+
19
+ - **Write-side transition dedup (primary cure).** `recordSkipped()` now logs on *transition*:
20
+ it keeps an in-memory `session → last-logged skip signature (${reason}::${skipped})` map and
21
+ drops a re-append when the signature is unchanged — mirroring the sibling `reaper-audit.jsonl`
22
+ "log on change, not every tick" discipline. State is cleared when the session is reaped
23
+ (`forgetSkip`) so a same-named successor logs fresh and the map can't leak (hard ceiling
24
+ `MAX_SKIP_STATE`, 2000, oldest-pruned). The reaper still **evaluates** every tick, so the
25
+ moment a veto lifts the next skip — or the reap — is logged.
26
+ - **Read-side bounded tail.** `read()` now reads only the last `TAIL_READ_BYTES` (2MB) via a
27
+ positional `readSync`, drops a torn leading line, and merges the rotated `.1` tail if needed —
28
+ so a large log can never be slurped whole into memory again.
29
+ - **Size-cap rotation (defense-in-depth).** `append()` rolls the file to `<path>.1` (O(1)
30
+ `renameSync`, no hot-path rewrite) once it crosses `MAX_LOG_BYTES` (16MB), so it can never
31
+ grow unbounded even if a future caller floods it.
32
+
33
+ Caps are overridable via an optional `ReapLogOptions` constructor arg (defaults = the module
34
+ constants) purely so tests can trigger rotation cheaply; the production callsite is unchanged.
35
+
36
+ ## What to Tell Your User
37
+
38
+ If your agent's server ever felt like it "froze for a minute" periodically, or your disk was
39
+ filling up with a giant session-audit log, this is a fix for that. The background tidier that
40
+ closes finished sessions was writing the same "couldn't clean this up yet" note thousands of
41
+ times, and reading that log loaded the whole thing at once. Now it writes the note once and
42
+ reads only the end of the file. Nothing you do changes — your "why did my session vanish?"
43
+ history is still complete (the first skip, every change of reason, and every actual close are
44
+ all still recorded); it just stops the duplicate spam and the stall it caused, and a log that's
45
+ already huge stops growing and gets trimmed automatically.
46
+
47
+ ## Summary of New Capabilities
48
+
49
+ None — this is a reliability fix to existing behavior, not a new capability. The session-audit
50
+ history keeps the same shape and meaning; it's just bounded and de-duplicated now.
51
+
52
+ ## Evidence
53
+
54
+ - `npx vitest run tests/unit/reap-log.test.ts` → **16 tests, 0 failures** (transition-dedup:
55
+ identical-skip-suppressed, reason-transition-logs, per-session independence, re-log-after-reap;
56
+ bounded read returns correct last-N; rotation to `.1` + cross-boundary merge + live file
57
+ bounded; absent-file empty read).
58
+ - `npx vitest run tests/unit/reap-log.test.ts tests/unit/session-reaper.test.ts
59
+ tests/unit/reap-notifier.test.ts tests/unit/reap-guard.test.ts` → **115 tests, 0 failures**
60
+ (reaper family regression-clean; `normalizeEntry` whitelist untouched).
61
+ - `npx tsc --noEmit -p tsconfig.json` → **0 errors**.
62
+ - Independent second-pass reviewer: **Concur** — observability-only, touches no kill/keep
63
+ decision; no operator-needed row hidden; read/rotation off-by-one-clean.
64
+ - Side-effects review: `upgrades/side-effects/reaper-log-flood-fix.md` (8 questions +
65
+ signal-vs-authority + multi-machine posture + second-pass concurrence).
@@ -0,0 +1,66 @@
1
+ # Side-Effects Review — Evidence-Bar Extension (defect class 3, dark increment)
2
+
3
+ **Version / slug:** `evidence-bar-judge-extension`
4
+ **Date:** `2026-07-03`
5
+ **Author:** `echo`
6
+ **Tier:** `1 (dark increment — additive classification + one pinned ratchet; no runtime wiring, no operator-gated text)`
7
+
8
+ ## Summary of the change
9
+
10
+ The mechanical arm of the "Evidence-Bar Extension to Judge Prompts" standard (defect class 3 / `claim-vs-evidence` closure; `docs/specs/evidence-bar-judge-extension.md`), shipped as a self-contained DARK increment. It ships:
11
+
12
+ 1. **`src/data/llmBenchCoverage.ts`** (additive) — `LLM_JUDGES_CLAIMS`, the `judgesClaims` axis of the program's ONE shared per-callsite metadata record (sibling of the authority-clause `untrustedInput` axis, co-located in the same file). `ClaimKind` = `completionClaim | healthClaim | scoredCredit`; `JudgesClaimsFlag` = `{ claimKind }` (a judge) | `false` (not a judge) | `{ false: reason }` (a judge-shaped callsite argued out of scope). Required-explicit for every `COMPONENT_CATEGORY` key.
13
+ 2. **`tests/unit/judges-claims-classification-ratchet.test.ts`** — required-explicit + no-dangling + valid-claimKind-per-judge + the spec-named JUDGE-SEED pinned as a judge + argued-false-shrink-only + real-reason floor. Same pinned-baseline family as the sibling ratchets.
14
+ 3. **`docs/specs/evidence-bar-judge-extension.md`** + **`.eli16.md`** — the converged spec + plain-English overview (carried in with the closure so the standard's authority is on canonical main).
15
+
16
+ ## What is DELIBERATELY out of scope (not orphan deferrals)
17
+
18
+ - **The registry amendment (spec §1).** Operator-gated — ships ONLY with Justin's explicit sign-off (spec front-matter `operator-gate`; the amendment is DRAFTED in spec §1). This run does not write `docs/STANDARDS-REGISTRY.md`.
19
+ - **The bench-axis pair ratchet (spec §3).** A bare-claim (false-accept) case + a real-evidence (false-reject) case per `judgesClaims` judge. Blocked on the program-wide "batteries readable by CI" decision — `research/` is absent from canonical main, so main's CI cannot read axis fields (`class-closure-gate.md` §"Program-shared machinery" #2, binding all three sibling axis specs). The sibling authority-clause axis ratchet deferred on the identical blocker; the keystone (#1347) staged its axis-requirements ratchet into its OWN dark increment for the same reason. Landing it under one standard's name would be doing shared cross-program infra as a side effect.
20
+ - **The `evidenceBar()` prompt clause (spec §4).** A sibling of the authority clause, defined by the spec to live in `src/core/promptClauses.ts`. That shared library is introduced by the still-OPEN sibling PR (authority-clause, #1351); creating it here would hard-collide on the same new file and break auto-merge for whichever PR merges second. The clause is consumed only by the A/B-gated per-component migrations (spec §4 / rollout §2), which are behavioral and deferred regardless — so shipping the clause function now would add a caller-less string with a merge hazard and no live value. It lands as a follow-up once the shared library is on main.
21
+ - **Per-component A/B clause migrations (rollout §2).** Behavioral prompt-text changes, each gated by the A/B protocol; the completion judge goes LAST (three wordings have already failed honestly — the named legitimate terminal state is "incumbent stands + routing mitigation + tracked gap"). A downstream task, not this dark increment.
22
+
23
+ ## Decision-point inventory
24
+
25
+ - `LLM_JUDGES_CLAIMS` (`src/data/llmBenchCoverage.ts`) — **add** — build-time metadata only. Read by the new pinned ratchet; no runtime consumer. A SIGNAL producer (which callsites judge a completion/health claim), never a runtime authority.
26
+ - `tests/unit/judges-claims-classification-ratchet.test.ts` — **add** — a CI-only pinned baseline (same family as `llm-bench-coverage-ratchet`). It gates the build (adding an unclassified LLM callsite / mis-shaping a judge entry is red CI), never a runtime path.
27
+
28
+ ---
29
+
30
+ ## 1. Over-block
31
+
32
+ **What legitimate inputs does this change reject that it shouldn't?**
33
+
34
+ None at runtime — nothing is wired to a runtime allow/deny path. The only new red-CI surfaces are build-time and intentional: (a) adding a new LLM component to `COMPONENT_CATEGORY` without an explicit `judgesClaims` classification, (b) shaping a judge entry with an invalid `claimKind`, (c) marking a spec-named JUDGE-SEED callsite as `false`, or (d) drifting the argued-false pinned baseline. Each is a self-describing failure with fix-instructions in the assertion message — the "visible, reviewed act" the standard exists to force, not an over-block of legitimate work.
35
+
36
+ ## 2. Under-block
37
+
38
+ **What failure modes does this still miss?**
39
+
40
+ - The classification records WHICH callsites judge claims; it does NOT yet enforce that each judge carries the bare-claim + real-evidence axis PAIR (that is the deferred bench-axis ratchet, spec §3). So a classified judge can still ship with no false-accept/false-reject coverage until that ratchet lands. This is the known staged-rollout gap, tracked in the spec rollout, not a silent miss.
41
+ - Classification accuracy is author-judged (like the sibling `untrustedInput` axis). A judge mislabeled `false` beyond the spec-named seed would pass — the JUDGE-SEED floor + the argued-false shrink-only pin catch the measured judges and every judge-shaped exemption; the seeding PR IS that review.
42
+ - A prompt bar cannot AUTHENTICATE material — a fabricated transcript showing a fake PASS passes any in-prompt bar (spec "Honest reach"). The authoritative verification arm is the deterministic real-check `verification_command`, which is out of this classification by construction and named in the spec as the true-verification owner.
43
+
44
+ ## 3. Level-of-abstraction fit
45
+
46
+ **Is this at the right layer?**
47
+
48
+ Yes. The classification extends the ONE shared per-callsite metadata record the program mandates (`src/data/llmBenchCoverage.ts`), not a new parallel registry — exactly where the sibling `untrustedInput` axis lives; the ratchet is in the established pinned-baseline vitest family. No higher gate already owns "which callsites judge a completion claim"; no lower primitive is duplicated.
49
+
50
+ ## 4. Signal vs authority compliance
51
+
52
+ `LLM_JUDGES_CLAIMS` and the ratchet are SIGNALS (which callsites judge a claim; is each judge classified and axis-eligible) — they inform the build and the reviewer, never grant or deny a runtime action. No model-produced field is wired to satisfy any check. The (future, deferred) prompt clause, once consumed, would make a model REPORT that a bare claim is unverified rather than CREDIT it — it never becomes the authority; the deterministic real-check verifier owns what is TRUE.
53
+
54
+ ## 5. Interactions with existing systems
55
+
56
+ - **`llm-bench-coverage-ratchet`** — unaffected: `LLM_JUDGES_CLAIMS` is a NEW additive export; the existing `LLM_BENCH_COVERAGE` union and its ratchet are untouched (both green post-change under a bounded run).
57
+ - **`untrusted-input-classification-ratchet` / `promptClauses.ts`** (sibling authority-clause, PR #1351, still open) — this increment adds a PARALLEL axis export in the same file and a parallel ratchet; it does not import or depend on the authority-clause work. Both append to `LLM_BENCH_COVERAGE`'s file region, so a textual rebase against #1351 may be needed depending on merge order — an additive, mechanical resolve, no semantic conflict.
58
+ - **`class-closure-lint.mjs`** — already names `src/data/llmBenchCoverage.ts` as an agent-authored artifact; touching it routes the PR to operator review (the lint is report-only today). The defect class `claim-vs-evidence` already carries `closureStandard: evidence-bar-judge-extension` on master — untouched here.
59
+
60
+ ## 6. Failure modes / rollback
61
+
62
+ Pure additive TypeScript + one test + spec docs. Rollback = revert the commit; nothing persists state, nothing runs at runtime, no migration, no config key (spec §"Decision points touched": none at runtime; no agent-side config key exists or is wanted — repo posture only, so no Migration Parity work). tsc clean, `npm run lint` exit 0, both ratchets green under bounded single-file runs (machine under intermittent external CPU pressure; verification used bounded vitest per the CI-as-gate pattern, full suite gated by CI).
63
+
64
+ ## 7. Second-pass reviewer
65
+
66
+ Self-review (bounded machine-pressure build). The change is additive, dark, and test-pinned; no runtime surface. Key self-checks: (a) confirmed the JUDGE-SEED is exactly the spec §2 named LLM judges (real-check verifier correctly excluded as the deterministic arm); (b) the two stall-confirm adapters (Telegram/Slack) classify true by the spec's stated inclusion criteria (stall/health classifiers), the safe over-inclusive direction; (c) the required-explicit ratchet actually fired during authoring (caught 3 unclassified callsites), proving it is not a no-op; (d) argued-false baseline is empty and the type still supports it for future judge-shaped exemptions.
@@ -0,0 +1,39 @@
1
+ # Reaper log-flood fix — Plain-English Overview
2
+
3
+ > The one-line version: the reaper was scribbling the same "couldn't clean this up" note into an audit file thousands of times a minute until the file grew to 142MB and froze the whole server when anything read it — this makes it write the note once and read the file in small bites.
4
+
5
+ ## The problem in one breath
6
+
7
+ Every agent has a background "reaper" that tidies up finished sessions. When a session CAN'T be tidied yet (it has an open promise to the user, or it belongs to another machine), the reaper politely skips it — and writes a one-line "skipped, here's why" note to an audit log. The bug: it re-checks and re-writes that identical note on every single tick, forever. On a live agent that piled up 3,218 + 1,608 duplicate notes and grew the log to 142MB. Worse, the code that READS that log loaded the entire 142MB into memory at once, which pegged a CPU and froze the server for ~90 seconds each time.
8
+
9
+ ## What already exists
10
+
11
+ - **The reaper** — the background tidier that decides which sessions to close. Its actual close/keep decisions are correct and are NOT changed by this work.
12
+ - **`reap-log.jsonl`** — the "why did my session vanish?" audit trail. One line per close, one line per refused close. This is the file that ballooned.
13
+ - **`reaper-audit.jsonl`** — a SEPARATE, sibling audit file that already does the right thing: it only writes when a decision CHANGES, not on every tick. It stayed tiny (under half a megabyte). We copy its proven habit.
14
+
15
+ ## What this adds
16
+
17
+ The reaper's skip-note now follows the same "only write when something changed" rule its sibling already uses. If a session is skipped for the exact same reason as last time, no new note is written. The moment the reason changes — the promise closes, the session moves machines, or it finally gets reaped — a fresh note is written, so the audit trail still tells the true story with none of the spam.
18
+
19
+ Two more safety belts:
20
+
21
+ - **Small-bite reading** — reading the log now pulls only the last couple of megabytes off the end of the file instead of the whole thing, so even a giant log can never freeze the server again.
22
+ - **Auto-rotation** — if the file ever crosses 16MB anyway, it's instantly rolled aside to a single backup and a fresh file is started, so it can never grow without bound. Reading transparently stitches the backup back in so recent history is never lost.
23
+
24
+ ## The new pieces
25
+
26
+ - **Transition dedup (in-memory)** — a tiny table remembering each session's last-written skip reason. It's per-machine, capped at 2,000 entries (sessions are only ever a few dozen in practice), and forgotten the instant a session is reaped so it can't leak. It is NOT allowed to change any close/keep decision — it only decides whether a LOG LINE is worth writing.
27
+ - **Bounded tail reader** — reads the end of the file only. It carefully drops a half-a-line at the read boundary so a torn record is never mis-parsed.
28
+ - **Size-cap rotation** — an instant rename (no slow file rewrite) when the file gets too big.
29
+
30
+ ## The safeguards
31
+
32
+ - The reaper's real authority — which sessions live or die — is untouched. This is purely about how much it writes and how the file is read.
33
+ - Nothing is lost from the audit story: first-skip, every change-of-reason, and every actual reap are all still recorded. Only the identical-to-last-time repeats are dropped.
34
+ - It's per-machine by design (each machine audits its own reaping), matching the sibling file. No cross-machine syncing to get wrong.
35
+ - Rollback is a one-file revert with no data migration — old fat logs and new backups both read correctly through the new reader.
36
+
37
+ ## What you need to decide
38
+
39
+ Nothing structural — this is a contained, low-risk hygiene fix with full unit-test coverage (16 focused tests; 115 reaper-family tests green). The one judgment call baked in: we accept losing the per-tick "still skipped at 12:00:01, still skipped at 12:00:02…" heartbeat in exchange for a log that doesn't self-destruct. That trade matches what the sibling audit file already chose, so it's a consistent, already-proven decision rather than a new gamble.
@@ -0,0 +1,90 @@
1
+ # Side-Effects Review — Reaper self-inflicted log-flood fix (ReapLog transition-dedup + bounded read + rotation)
2
+
3
+ **Version / slug:** `reaper-log-flood-fix`
4
+ **Date:** `2026-07-03`
5
+ **Author:** `Echo (instar-dev agent)`
6
+ **Second-pass reviewer:** `reviewer subagent (see appended concurrence)`
7
+
8
+ ## Summary of the change
9
+
10
+ Three self-limiting changes to `src/monitoring/ReapLog.ts`, the append-only audit trail behind `GET /sessions/reap-log`:
11
+
12
+ 1. **Write-side transition dedup (primary cure).** `recordSkipped()` now logs on TRANSITION only — it keeps an in-memory `session → last-logged skip signature (${reason}::${skipped})` map and drops a re-append when the signature is unchanged. The reaper re-evaluates a permanently-vetoed session (`open-commitment`, `not-lease-holder`, `protected`) on every tick and previously appended an identical `skipped` row each time — on a live agent this produced 3218 `open-commitment` + 1608 `not-lease-holder` repeat rows and grew the log to 142MB / 463k lines (observed 2026-07-03). The dedup state is cleared when the session is reaped (`recordReaped` → `forgetSkip`) so a same-named successor logs fresh and the map cannot leak. The map has a hard ceiling (`MAX_SKIP_STATE`, default 2000, oldest-pruned).
13
+ 2. **Read-side bounded tail (fixes the freeze-on-read).** `read()` previously did `fs.readFileSync(WHOLE file).split('\n')` just to return the last `limit` rows — a 142MB slurp + split that blocked the event loop when the route was queried. It now reads only the last `TAIL_READ_BYTES` (default 2MB) of the file via a positional `readSync`, drops a leading partial line, and merges the rotated `.1` tail if the live file doesn't yield enough rows.
14
+ 3. **Size-cap rotation (defense-in-depth).** `append()` rolls the file to `<path>.1` (O(1) `renameSync`, no data rewrite on the hot path) once it would cross `MAX_LOG_BYTES` (default 16MB), so the file can never grow unbounded even if a future caller floods it. One backup generation is retained; `read()` merges it.
15
+
16
+ The caps are overridable via an optional `ReapLogOptions` constructor arg (defaults = module constants) purely so tests can trigger rotation cheaply. The production callsite (`src/commands/server.ts`) is unchanged.
17
+
18
+ ## Decision-point inventory
19
+
20
+ This change touches NO block/allow/gate decision point. `ReapLog` is a read-only observability sink — the class docstring states it "never gates or mutates a session — it only records." The reaper's keep/kill authority (`SessionManager` / `SessionReaper`) is entirely untouched; only the LOGGING cadence and READ mechanics change.
21
+
22
+ - `ReapLog.recordSkipped` — modify — logs on transition instead of every tick (observability cadence only).
23
+ - `ReapLog.read` — modify — bounded tail read instead of whole-file slurp (I/O mechanics only).
24
+ - `ReapLog.append` — modify — adds O(1) size-cap rotation (I/O mechanics only).
25
+
26
+ ---
27
+
28
+ ## 1. Over-block
29
+
30
+ No block/allow surface — over-block not applicable. `ReapLog` never blocks a session. The only "suppression" is of DUPLICATE LOG ROWS for an unchanged skip state, which is the intended behavior (mirrors the existing `reaper-audit.jsonl` log-on-transition pattern).
31
+
32
+ ---
33
+
34
+ ## 2. Under-block
35
+
36
+ No block/allow surface — under-block not applicable. In audit-fidelity terms, the analogue is "what does the log no longer record?": it no longer records a fresh row for an UNCHANGED skip state on each tick. It STILL records: the first skip (transition into skipped), every change of skip reason, and every reap. A reviewer asking "is this session still being skipped?" reads the most recent skip row plus the absence of a later reap — the same answer, without 5000 duplicate rows. Loss of the per-tick "still skipped at time T" heartbeat is intentional and matches reaper-audit.
37
+
38
+ ---
39
+
40
+ ## 3. Level-of-abstraction fit
41
+
42
+ Correct layer. The dedup lives INSIDE `ReapLog` (the class that owns the log), so it covers every caller — not bolted onto the single `reapBlocked` event wiring in `server.ts`, which would leave other/future callers un-deduped. The read/rotation are pure file mechanics that belong on the class that owns the file. This is a detector/observability primitive, not an authority — it produces no signal consumed by any gate. It mirrors the already-shipped `reaper-audit.jsonl` transition pattern rather than inventing a new one.
43
+
44
+ ---
45
+
46
+ ## 4. Signal vs authority compliance
47
+
48
+ Compliant. The change adds NO blocking authority and NO brittle decision logic in front of any gate. It is a data-sink optimization: fewer duplicate rows written, bounded bytes read. `docs/signal-vs-authority.md` governs decision points; this change has none.
49
+
50
+ ---
51
+
52
+ ## 5. Interactions
53
+
54
+ - **Mirrors, does not shadow, `reaper-audit.jsonl`.** That sink already logs reaper decisions on transition; this brings `reap-log.jsonl`'s skip rows to the same discipline. They remain separate files with separate purposes.
55
+ - **`recordReaped` → `forgetSkip` ordering.** Reap clears the skip-state so a same-named successor logs fresh. A reap for a session never previously skipped is a harmless no-op delete.
56
+ - **No race with cleanup.** `ReapLog` is synchronous and single-process; the rotation `renameSync` is atomic. A concurrent reader mid-rotation either sees the old file (pre-rename) or the new empty file + `.1` (post-rename) — `read()` merges `.1`, so no window loses the newest rows.
57
+ - **`normalizeEntry` untouched** — the notify/skipped/reaped type + field whitelist that other tests guard is unchanged; all 115 reaper-family unit tests pass.
58
+
59
+ ---
60
+
61
+ ## 6. External surfaces
62
+
63
+ - **`GET /sessions/reap-log`** — same JSON shape, same newest-last ordering, same `limit` semantics. The only observable difference: far fewer duplicate `skipped` rows, and the route no longer blocks the event loop on a large file. No API contract change.
64
+ - No change visible to other agents or users beyond the route above. No dependency on timing or conversation state.
65
+ - **Pre-existing on-disk bloat is not rewritten by this change.** A file already at 142MB stays until the next rotation trims it (or an operator archives it, as was done live on 2026-07-03). The bounded READ means even a still-large file no longer freezes the loop; the write-side dedup means it stops growing.
66
+
67
+ ---
68
+
69
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
70
+
71
+ **Machine-local BY DESIGN.** `reap-log.jsonl` is a per-machine audit of that machine's own reap decisions (the log path is under the machine's own `logs/`, and `GET /sessions/reap-log` already reports only the local machine's entries — pool-scope reap history is not a feature). The in-memory `skipState` dedup map is likewise per-process and correctly scoped to the machine doing the reaping. No replication, no proxied read, no cross-machine state. This matches the existing `reaper-audit.jsonl` posture. No topic-transfer, one-voice, or URL-durability concerns (it is not user-facing and generates no links).
72
+
73
+ ---
74
+
75
+ ## 8. Rollback cost
76
+
77
+ Low. The change is contained to one file (`ReapLog.ts`) plus its unit tests. Back-out is a straight revert — no data migration (the on-disk format is unchanged; older whole-file logs and `.1` backups both read correctly through the new tail reader). If the transition-dedup were ever suspected of hiding a needed row, reverting restores per-tick logging immediately. No agent-state repair, no hot-fix data surgery. The caps are constructor-overridable if a deployment needs different thresholds without a code change.
78
+
79
+ ---
80
+
81
+ ## Second-pass review (independent reviewer subagent)
82
+
83
+ **Concur with the review.** The change is observability-only and touches no kill/keep decision — the reaper still evaluates every tick, so the transition-dedup can never strand an operator's "why did my session vanish / not get reaped?" answer.
84
+
85
+ 1. **Transition-dedup:** correctly keyed on `${reason}::${skipped}`; suppresses only a byte-identical re-evaluation of the same permanent veto. Any genuine change (veto lifts → reap row always appends since `recordReaped` has no dedup; reason changes → new signature → new row) is still logged. `forgetSkip` on reap prevents both the stale-successor bug and unbounded map growth, backed by the `MAX_SKIP_STATE` oldest-first ceiling. No operator-needed row is hidden.
86
+ 2. **Bounded read:** traced the merge/tail logic — older `.1` correctly prepended to newer live, `.slice(-limit)` keeps the newest, leading partial line dropped only when `start > 0`, trailing empty line filtered, `.1` read only when live yields `< wanted`. No off-by-one, no torn record, newest row never dropped, no cross-file duplication (rename→fresh-empty-live).
87
+ 3. **Rotation:** O(1) `renameSync` off the hot path; `approxSize` seeded once then advanced per-write (no per-append stat); a pre-existing 142MB file rotates on the first append. On-disk bounded to `live (≤cap) + .1 (≤cap)` since each rotation overwrites the prior `.1`. Concurrent reader sees a consistent pre/post-rename state, both handled by `read()`.
88
+ 4. **Signal-vs-authority:** no blocking authority added; `SessionReaper`/`SessionManager` kill logic untouched; audit-sink failures swallow silently rather than perturbing behavior.
89
+
90
+ No correctness defect found. Tests exercise both sides of the dedup boundary, the rotation/merge boundary, and the absent-file path. Sound to merge.