instar 1.3.739 → 1.3.741
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.
- package/dist/core/promptContract.d.ts +114 -0
- package/dist/core/promptContract.d.ts.map +1 -0
- package/dist/core/promptContract.js +120 -0
- package/dist/core/promptContract.js.map +1 -0
- package/dist/data/llmBenchCoverage.d.ts +8 -0
- package/dist/data/llmBenchCoverage.d.ts.map +1 -1
- package/dist/data/llmBenchCoverage.js +117 -0
- package/dist/data/llmBenchCoverage.js.map +1 -1
- package/dist/monitoring/ReapLog.d.ts +44 -2
- package/dist/monitoring/ReapLog.d.ts.map +1 -1
- package/dist/monitoring/ReapLog.js +151 -10
- package/dist/monitoring/ReapLog.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/src/data/llmBenchCoverage.ts +200 -0
- package/upgrades/1.3.740.md +65 -0
- package/upgrades/1.3.741.md +85 -0
- package/upgrades/side-effects/prompt-parser-contract-standard.md +67 -0
- package/upgrades/side-effects/reaper-log-flood-fix.eli16.md +39 -0
- package/upgrades/side-effects/reaper-log-flood-fix.md +90 -0
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
220
|
+
fs.renameSync(this.logPath, `${this.logPath}.1`); // overwrites a prior .1
|
|
95
221
|
}
|
|
96
222
|
catch {
|
|
97
|
-
// @silent-fallback-ok —
|
|
98
|
-
//
|
|
99
|
-
|
|
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":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"ReapLog.js","sourceRoot":"","sources":["../../src/monitoring/ReapLog.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-03T23:25:24.796Z",
|
|
5
|
+
"instarVersion": "1.3.741",
|
|
6
6
|
"entryCount": 202,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -360,3 +360,203 @@ export const LLM_JUDGES_CLAIMS: Readonly<Record<string, JudgesClaimsFlag>> = {
|
|
|
360
360
|
CartographerSweep: false, // authors doc-tree summaries over code
|
|
361
361
|
StandardsCoverageEnrichment: false, // enriches standards-coverage rows
|
|
362
362
|
};
|
|
363
|
+
|
|
364
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
365
|
+
// Prompt↔parser contract standard (defect class 1 — docs/specs/prompt-parser-
|
|
366
|
+
// contract-standard.md §4)
|
|
367
|
+
//
|
|
368
|
+
// The `contract` axis of the program's shared per-callsite metadata record
|
|
369
|
+
// (class-closure-gate.md §"Program-shared machinery"). It co-locates in THIS
|
|
370
|
+
// file with the bench-coverage record it extends, exactly like the sibling
|
|
371
|
+
// `untrustedInput` (authority-clause) and `judgesClaims` (evidence-bar) axes.
|
|
372
|
+
//
|
|
373
|
+
// THE FIELD IS REQUIRED AND EXPLICIT FOR EVERY COMPONENT_CATEGORY KEY — there is
|
|
374
|
+
// NO DEFAULT. A callsite whose output is machine-parsed into a CLOSED, taught
|
|
375
|
+
// verdict/decision vocabulary (the B15 failure surface: prompt teaches "B15",
|
|
376
|
+
// parser accepts only "B15_CONTEXT_DEATH_STOP") either NAMES its contract-test
|
|
377
|
+
// file (`{ contractTest: '<path>' }`, covered) or is queued in the shrink-only
|
|
378
|
+
// pending set (`{ pending: 'contract-wave-1' | 'contract-wave-2' }`). A callsite
|
|
379
|
+
// with NO such closed-vocabulary parse — no live LLM prompt, a fixed canary, or
|
|
380
|
+
// free-text / open-set CONTENT consumed as data (summaries, extractions,
|
|
381
|
+
// syntheses, briefs, reviews, open-set id routing) — is `{ false: '<reason>' }`
|
|
382
|
+
// and pinned shrink-only in
|
|
383
|
+
// tests/unit/parser-contract-classification-ratchet.test.ts. A silent omission
|
|
384
|
+
// is red CI, so the flag can NEVER default toward the un-contracted state.
|
|
385
|
+
//
|
|
386
|
+
// POLARITY (spec §3: "Undeclared content is hazard-scanned by default"): the
|
|
387
|
+
// default pulls TOWARD "needs a contract" — a callsite is `false` only with an
|
|
388
|
+
// argued reason, exactly like the sibling `untrustedInput` axis defaults toward
|
|
389
|
+
// `true`.
|
|
390
|
+
//
|
|
391
|
+
// WAVE-1 is the spec-named SHIPPED-FIX set (rollout §0/§1): the four
|
|
392
|
+
// highest-stakes parsed callsites — MessagingToneGate (the motivating B15
|
|
393
|
+
// defect), ExternalOperationGate, CompletionEvaluator (stop-judge), and
|
|
394
|
+
// InputClassifier. It is pinned as a seed floor in the ratchet so a
|
|
395
|
+
// highest-stakes callsite can never silently slip out of scope. WAVE-2 is every
|
|
396
|
+
// other enumerated-verdict/decision callsite, graduating on the shrink-only
|
|
397
|
+
// schedule (rollout §2).
|
|
398
|
+
//
|
|
399
|
+
// DARK / REPORT-ONLY: this record is build-time metadata read ONLY by the new
|
|
400
|
+
// pinned ratchet. Nothing here is a contract test yet (those render the REAL
|
|
401
|
+
// production prompt and need the live-builder render refactor, deferred to its
|
|
402
|
+
// own A/B-gated increments — spec rollout §0/§1). The pending set IS the report
|
|
403
|
+
// (spec §4: "report-only inventory happens by construction").
|
|
404
|
+
//
|
|
405
|
+
// A cross-check lint flags any GATE/SENTINEL-category callsite marked `false`
|
|
406
|
+
// for review (these categories most often parse a verdict) — see the ratchet's
|
|
407
|
+
// REVIEWED_FALSE_PARSER_GATE pin.
|
|
408
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
409
|
+
|
|
410
|
+
export type ParserContractFlag =
|
|
411
|
+
| { contractTest: string }
|
|
412
|
+
| { pending: 'contract-wave-1' | 'contract-wave-2' }
|
|
413
|
+
| { false: string };
|
|
414
|
+
|
|
415
|
+
export const LLM_PARSER_CONTRACT: Readonly<Record<string, ParserContractFlag>> = {
|
|
416
|
+
// ── WAVE-1: the four spec-named highest-stakes parsed callsites (rollout §0) ──
|
|
417
|
+
MessagingToneGate: { pending: 'contract-wave-1' }, // THE motivating defect: prompt taught "B15", parser accepts only "B15_CONTEXT_DEATH_STOP"
|
|
418
|
+
ExternalOperationGate: { pending: 'contract-wave-1' }, // parses a closed mutability/reversibility classification
|
|
419
|
+
CompletionEvaluator: { pending: 'contract-wave-1' }, // the stop-judge surface parses a closed done/blocked/continue verdict
|
|
420
|
+
InputClassifier: { pending: 'contract-wave-1' }, // parses a closed auto-approve vs relay decision
|
|
421
|
+
|
|
422
|
+
// ── WAVE-2: every other enumerated-verdict / decision callsite (rollout §2) ──
|
|
423
|
+
MessageSentinel: { pending: 'contract-wave-2' }, // closed intent set (pause / emergency / normal)
|
|
424
|
+
LLMSanitizer: { pending: 'contract-wave-2' }, // parses a closed sanitize verdict/decision
|
|
425
|
+
WarrantsReplyGate: { pending: 'contract-wave-2' }, // closed should-reply yes/no verdict
|
|
426
|
+
InputGuard: { pending: 'contract-wave-2' }, // closed input-coherence verdict
|
|
427
|
+
StallTriageNurse: { pending: 'contract-wave-2' }, // closed stall-triage diagnosis label
|
|
428
|
+
CommitmentSentinel: { pending: 'contract-wave-2' }, // closed commitment-detected verdict + structured envelope
|
|
429
|
+
PresenceProxy: { pending: 'contract-wave-2' }, // closed tier-3 stall verdict
|
|
430
|
+
ProjectDriftChecker: { pending: 'contract-wave-2' }, // closed on-project verdict
|
|
431
|
+
TemporalCoherenceChecker: { pending: 'contract-wave-2' }, // closed temporal-coherence verdict
|
|
432
|
+
SessionWatchdog: { pending: 'contract-wave-2' }, // closed stuck verdict
|
|
433
|
+
ResumeQueueDrainer: { pending: 'contract-wave-2' }, // closed resume-sanity verdict
|
|
434
|
+
TopicIntentArcCheck: { pending: 'contract-wave-2' }, // closed arc-check classification label
|
|
435
|
+
TelegramAdapter: { pending: 'contract-wave-2' }, // stall-confirm — closed genuinely-stalled verdict
|
|
436
|
+
SlackAdapter: { pending: 'contract-wave-2' }, // stall-confirm (byte-identical prompt to Telegram's; parity)
|
|
437
|
+
PromptGate: { pending: 'contract-wave-2' }, // closed injection-detected verdict
|
|
438
|
+
UnjustifiedStopGate: { pending: 'contract-wave-2' }, // closed stop-justified verdict
|
|
439
|
+
OverrideDetector: { pending: 'contract-wave-2' }, // closed override-intent verdict
|
|
440
|
+
TaskClassifier: { pending: 'contract-wave-2' }, // closed task-type label set
|
|
441
|
+
ResumeValidator: { pending: 'contract-wave-2' }, // closed resume-UUID match yes/no verdict
|
|
442
|
+
CoherenceReviewer: { pending: 'contract-wave-2' }, // gate-triage — closed coherence verdict
|
|
443
|
+
|
|
444
|
+
// ── Argued false (pinned shrink-only) — no closed-vocabulary verdict parse ──
|
|
445
|
+
// No live LLM callsite, a fixed canary, or free-text / open-set content.
|
|
446
|
+
InputDetector: {
|
|
447
|
+
false:
|
|
448
|
+
'attribution-manifest alias only (a legacy prompt-pattern matcher); the live matcher calls with attribution PromptGate, contracted there',
|
|
449
|
+
},
|
|
450
|
+
SessionActivitySentinel: {
|
|
451
|
+
false:
|
|
452
|
+
'authors a free-text activity DIGEST — the product is prose, not a closed verdict vocabulary a prompt teaches and a parser gates on',
|
|
453
|
+
},
|
|
454
|
+
PromiseBeacon: {
|
|
455
|
+
false:
|
|
456
|
+
'no live LLM prompt — generateStatusLine/classifyProgress hooks are unwired at the construction site; nothing parses a taught vocabulary (matches its bench-coverage exemption)',
|
|
457
|
+
},
|
|
458
|
+
InteractivePoolCanaryJudge: {
|
|
459
|
+
false:
|
|
460
|
+
'judges a FIXED known-answer canary probe — the expected output is a constant, so the canary is its own contract; a prompt↔parser contract test would re-test the same constant',
|
|
461
|
+
},
|
|
462
|
+
SessionSummarySentinel: {
|
|
463
|
+
false:
|
|
464
|
+
'extracts task/phase/files as open-set free-text FIELDS — there is no closed taught verdict vocabulary, so the B15 prompt↔parser drift cannot arise here',
|
|
465
|
+
},
|
|
466
|
+
AutoApprover: {
|
|
467
|
+
false:
|
|
468
|
+
'mechanical key injection + audit logging, no LLM prompt of its own; the upstream parsed decision is InputClassifier.classify(), contracted there',
|
|
469
|
+
},
|
|
470
|
+
IntegrationGate: {
|
|
471
|
+
false:
|
|
472
|
+
'no LLM prompt of its own — delegates to JobReflector.reflect(); zero LLM-provider callsites of its own that parse a taught vocabulary',
|
|
473
|
+
},
|
|
474
|
+
CoherenceGate: {
|
|
475
|
+
false:
|
|
476
|
+
'no callsite carries attribution CoherenceGate — all LLM calls flow through CoherenceReviewer.callApi(), contracted there',
|
|
477
|
+
},
|
|
478
|
+
JobReflector: {
|
|
479
|
+
false:
|
|
480
|
+
'reflection produces free-text content over a job — no closed taught verdict vocabulary a parser gates on (its own bench coverage is wave-3)',
|
|
481
|
+
},
|
|
482
|
+
crossModelReviewer: {
|
|
483
|
+
false:
|
|
484
|
+
'produces a free-text review of a SPEC document — no closed output vocabulary the prompt teaches and a parser must accept',
|
|
485
|
+
},
|
|
486
|
+
SelfKnowledgeTree: {
|
|
487
|
+
false:
|
|
488
|
+
'extracts self-knowledge tree fragments as content that is stored/merged — no closed verdict token gates a branch on a taught vocabulary',
|
|
489
|
+
},
|
|
490
|
+
TreeTriage: {
|
|
491
|
+
false:
|
|
492
|
+
'triages knowledge-tree fragments into content — no closed taught output vocabulary a parser must accept or reject',
|
|
493
|
+
},
|
|
494
|
+
TopicSummarizer: {
|
|
495
|
+
false:
|
|
496
|
+
'produces a free-text topic summary — the prose is the product; there is no closed vocabulary the prompt teaches and a parser gates on',
|
|
497
|
+
},
|
|
498
|
+
ContextualEvaluator: {
|
|
499
|
+
false:
|
|
500
|
+
'evaluates context relevance into content — no closed taught verdict vocabulary that a parser accepts a promised form of',
|
|
501
|
+
},
|
|
502
|
+
RelationshipManager: {
|
|
503
|
+
false:
|
|
504
|
+
'extracts relationship facts as open-set structured content — no closed taught vocabulary a prompt promises and a parser gates on',
|
|
505
|
+
},
|
|
506
|
+
StandardsConformanceReviewer: {
|
|
507
|
+
false:
|
|
508
|
+
'reviews artifact-vs-standard conformance as content — no closed output vocabulary the prompt teaches and a parser must accept',
|
|
509
|
+
},
|
|
510
|
+
DiscoveryEvaluator: {
|
|
511
|
+
false:
|
|
512
|
+
'evaluates serendipity discoveries into content — no closed taught verdict vocabulary a parser accepts a promised form of',
|
|
513
|
+
},
|
|
514
|
+
Usher: {
|
|
515
|
+
false:
|
|
516
|
+
'routes a turn to candidate TOPIC IDS — an OPEN, machine-supplied set, not a closed taught vocabulary the prompt fixes and a parser gates on',
|
|
517
|
+
},
|
|
518
|
+
TopicIntentExtractor: {
|
|
519
|
+
false:
|
|
520
|
+
'extracts a topic-intent description from a turn — free-text content, not a closed taught verdict vocabulary a parser accepts',
|
|
521
|
+
},
|
|
522
|
+
PreCompactionFlush: {
|
|
523
|
+
false:
|
|
524
|
+
'extracts durable facts before compaction as free-text content — no closed taught output vocabulary a parser gates on',
|
|
525
|
+
},
|
|
526
|
+
TreeSynthesis: {
|
|
527
|
+
false:
|
|
528
|
+
'synthesizes knowledge fragments into a free-text answer — the prose is the product, no closed taught verdict vocabulary a parser accepts',
|
|
529
|
+
},
|
|
530
|
+
LLMConflictResolver: {
|
|
531
|
+
false:
|
|
532
|
+
'resolves divergent multi-machine state into a merged value/content — no closed taught verdict vocabulary a prompt promises and a parser gates on',
|
|
533
|
+
},
|
|
534
|
+
openConversationBrief: {
|
|
535
|
+
false:
|
|
536
|
+
'generates a free-text A2A conversation brief — the prose is the product, no closed output vocabulary the prompt teaches and a parser must accept',
|
|
537
|
+
},
|
|
538
|
+
'a2a-checkin': {
|
|
539
|
+
false:
|
|
540
|
+
'summarizes A2A check-in threads into free-text content — no closed taught verdict vocabulary a parser accepts a promised form of',
|
|
541
|
+
},
|
|
542
|
+
'correction-learning': {
|
|
543
|
+
false:
|
|
544
|
+
'distills recurring corrections into a preference (content) — no closed taught output vocabulary a parser gates on',
|
|
545
|
+
},
|
|
546
|
+
'mentor-stage-b': {
|
|
547
|
+
false:
|
|
548
|
+
'classifies mentor signals over mentee output into differential content — no closed taught verdict vocabulary a parser gates on (its own bench coverage is wave-3)',
|
|
549
|
+
},
|
|
550
|
+
PipeSessionSpawner: {
|
|
551
|
+
false:
|
|
552
|
+
'spawns sessions from task descriptions — no LLM output parsed into a closed taught verdict vocabulary',
|
|
553
|
+
},
|
|
554
|
+
CartographerSweep: {
|
|
555
|
+
false:
|
|
556
|
+
'authors doc-tree summaries over code as free text — the prose is the product, no closed taught output vocabulary a parser gates on',
|
|
557
|
+
},
|
|
558
|
+
StandardsCoverageEnrichment: {
|
|
559
|
+
false:
|
|
560
|
+
'enriches standards-coverage rows with content — no closed taught verdict vocabulary a prompt promises and a parser accepts',
|
|
561
|
+
},
|
|
562
|
+
};
|
|
@@ -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,85 @@
|
|
|
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 **"The Prompt and the Parser Are One Contract"** standard
|
|
9
|
+
(`docs/specs/prompt-parser-contract-standard.md`, defect class 1 / `prompt-parser-contract`
|
|
10
|
+
closure), shipped as a self-contained DARK increment — no runtime wiring, no operator-gated
|
|
11
|
+
registry text, no config key, and **no change to any live prompt or parser**. It is the
|
|
12
|
+
structural answer to the 2026-07-02 INSTAR-Bench v2 finding: the tone gate's PROMPT taught
|
|
13
|
+
models the short rule id (`B15`) while its production PARSER accepts only the full identifier
|
|
14
|
+
(`B15_CONTEXT_DEATH_STOP`) and fails closed on the short form — so every model through every
|
|
15
|
+
door obeyed the prompt and "failed." A single CI test comparing what the prompt promises
|
|
16
|
+
against what the parser accepts would have made that defect unrepresentable.
|
|
17
|
+
|
|
18
|
+
This increment ships **the shared contract library + the per-callsite classification + its CI
|
|
19
|
+
ratchet only**:
|
|
20
|
+
|
|
21
|
+
- A **shared contract library** (`src/core/promptContract.ts`): the `PromptContract` manifest
|
|
22
|
+
type (the co-located machine-readable promise a prose-shaped callsite carries next to its
|
|
23
|
+
prompt) and `deriveRejectedForms` — a pure generator for the counter-examples a contract
|
|
24
|
+
test feeds the REAL parser to prove its fail-closed behavior (case-mutation,
|
|
25
|
+
prefix-truncation at each separator — the exact `B15` shape — and separator-stripping, plus
|
|
26
|
+
hand-picked extras, minus any form that collides with a promised token). Machine-derived by
|
|
27
|
+
design: a hand-only reject list invites trivial rejects that prove nothing.
|
|
28
|
+
- The **`contract` classification** (`LLM_PARSER_CONTRACT` in `src/data/llmBenchCoverage.ts`):
|
|
29
|
+
the `contract` axis of the program's ONE shared per-callsite metadata record (sibling of the
|
|
30
|
+
authority-clause `untrustedInput` and evidence-bar `judgesClaims` axes), required-explicit
|
|
31
|
+
for every LLM component — no default, so a silent omission is red CI and the flag can never
|
|
32
|
+
default toward the un-contracted state. The four spec-named highest-stakes parsed callsites
|
|
33
|
+
(tone gate, external-op gate, stop judge, input classifier) seed `contract-wave-1`; every
|
|
34
|
+
other enumerated-verdict callsite is `contract-wave-2`; a callsite with no closed-vocabulary
|
|
35
|
+
parse (no live prompt, a fixed canary, or free-text/open-set content) is an argued `false`.
|
|
36
|
+
- A **classification ratchet** (`tests/unit/parser-contract-classification-ratchet.test.ts`):
|
|
37
|
+
required-explicit + no-dangling + valid-wave + the wave-1 seed pinned as a floor (a
|
|
38
|
+
highest-stakes callsite can never silently slip scope) + the pending set pinned shrink-only
|
|
39
|
+
+ the argued-false set pinned shrink-only with a real-reason floor + a gate/sentinel
|
|
40
|
+
cross-check. Same pinned-baseline family as `llm-bench-coverage-ratchet` and the sibling
|
|
41
|
+
`untrusted-input-classification-ratchet`.
|
|
42
|
+
|
|
43
|
+
Deliberately OUT of scope (not orphan deferrals — see `upgrades/side-effects/`):
|
|
44
|
+
|
|
45
|
+
- The **per-callsite contract tests** and the **live-builder render refactor** (spec rollout
|
|
46
|
+
§0/§1). A contract test renders the REAL production prompt through an exported pure render
|
|
47
|
+
function; several production builders are private instance methods with live deps and need
|
|
48
|
+
that refactor, which TOUCHES live parsing code. It is deferred to its own A/B-gated
|
|
49
|
+
increments, one callsite at a time — never inside this dark inventory increment.
|
|
50
|
+
- The **runtime contract-drift warning** (spec Frontloaded Decision #3) — a prompt-build-time
|
|
51
|
+
signal that also touches the live builders; it lands with the single-sourcing migrations.
|
|
52
|
+
- The **registry / constitution text** (spec §1) — operator-gated; ships ONLY with Justin's
|
|
53
|
+
explicit sign-off. It is DRAFTED in the spec; this run does not edit the standards registry.
|
|
54
|
+
|
|
55
|
+
## What to Tell Your User
|
|
56
|
+
|
|
57
|
+
Nothing changes for you right now — this ships dark, and it is maintainer-only machinery (a
|
|
58
|
+
no-op on your install unless you develop instar itself). What it does is give my own AI checks
|
|
59
|
+
a safety net: the words a check's prompt tells a model to answer with, and the words the code
|
|
60
|
+
behind that check will actually accept, are now recorded together and held in sync by an
|
|
61
|
+
automatic test. That closes a class of bug where a check would quietly reject every correct
|
|
62
|
+
answer because the two halves had drifted apart — a mistake that was entirely mine, never the
|
|
63
|
+
model's. The real per-check tests and any wording changes come later, one at a time, and only
|
|
64
|
+
after careful review.
|
|
65
|
+
|
|
66
|
+
## Summary of New Capabilities
|
|
67
|
+
|
|
68
|
+
None active for end users in this increment — everything ships dark and additive. (For instar
|
|
69
|
+
maintainers: a shared prompt↔parser contract library plus a required per-callsite `contract`
|
|
70
|
+
classification with a shrink-only ratchet and a pinned highest-stakes seed floor, so a
|
|
71
|
+
machine-parsed callsite can never silently ship a taught output vocabulary its own parser
|
|
72
|
+
rejects.)
|
|
73
|
+
|
|
74
|
+
## Evidence
|
|
75
|
+
|
|
76
|
+
- `npx vitest run tests/unit/parser-contract-classification-ratchet.test.ts tests/unit/promptContract.test.ts tests/unit/llm-bench-coverage-ratchet.test.ts`
|
|
77
|
+
→ **3 files, 26 tests, 0 failures** (required-explicit + no-dangling + wave-1 seed floor +
|
|
78
|
+
shrink-only pending + shrink-only argued-false + real-reason floor + gate/sentinel
|
|
79
|
+
cross-check + the derive-rejected-forms mutation logic incl. the B15 prefix shape; the
|
|
80
|
+
pre-existing coverage ratchet still green against the additive record).
|
|
81
|
+
- `npx tsc --noEmit` → exit 0. `npm run lint` → exit 0.
|
|
82
|
+
- Dark-by-construction: `LLM_PARSER_CONTRACT` is build-time metadata read only by the new
|
|
83
|
+
pinned ratchet; `promptContract.ts` has no runtime caller. It changes NO prompt text and
|
|
84
|
+
wires NO runtime parser — verified by the absence of any src import of the new module
|
|
85
|
+
outside tests.
|