@vostride/agent-qa-agent 0.2.2 → 0.2.4

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/index.d.ts CHANGED
@@ -1,13 +1,7 @@
1
- import { ScopeKey, AuthoringBrief, Delta, CanaryOutcome, RecordId, GateReceipt, PromotionOutcome, GateReceiptRecordOutcome, Action, ActionResult, Patch, PatchId, Job, JobStatus, JobId, JobKind, JobEnqueueRequest, JobConclusionKind, DraftReceipt, TestEditPatchSet, RunsStartInput, RunStartAcknowledgement, CompletedRunHandoff, AuthoringVerifyDraftInput, TestFileWrite, QuarantineEntryState, SuiteStabilityAssessment, AuthoringFunnelWindow, AuthoringFunnel, ConfigChange, ConfigWriteRefusal, ConfigWriteIssue, ConfigPathSegment, CatalogRow, ExplainCitation, QuarantineLadderState, Verdict, VerdictSentenceParts, ChainedJobRequest, JobHandlerOutcome, RunAnalysisArtifact, ClauseReceiptStatus, AgentAskInput, AgentAskResult, MaskedResolvedConfig, RunId, StepEvidence, RecordedTargets, KnowledgeGatesListResult, KnowledgeCandidateInjectionResult, KnowledgeBundleSummary, KnowledgeRecordsInspectInput, InspectRecordView, KnowledgeRecordsDiffInput, RecordChange, KnowledgeRecordShowInput, KnowledgeRecordShowResult, EntityResolution, EntityDescriptor, LiveUrlSigner, LiveStreamDeclaration, KnowledgeAcceptInput, KnowledgeRejectInput, KnowledgeForgetInput, KnowledgeChangeOutput, PostProcessingStageDetail, CurationSnapshot, RunNotification, RunDetailRows, EvidenceBlobLookup, EvidenceBlobPresence, QuarantineOperationsPort, FeedbackPort, FeedbackReadsPort, MaskFn, RiskActionDescriptor, FunctionCaller, LiveUrlClaims, LiveViewSubjectRef, LiveUrlVerdict, AuthoringDedupContinuation, VerdictSource, RunEvidenceRef, DraftRef, WrittenTestReceipt, AuthoringDraftWriteOutput, FanOutReport, FanOutBound, ChatSessionEvent, ChatAbortedEvent, RefusedChatSessionLine, ChatTurnStatus, ClauseReceipt, ChatCompactionEvent, SessionLogPort, ScenarioClass, ChatScenario, ScenarioLane, ScenarioExpectationKind, AnalysisGetOutput, MisdirectionRootPosture, ExecutionContext, RunNotificationVerdictSummary, GateVerdict } from '@vostride/agent-qa-schema';
1
+ import { ScopeKey, AuthoringBrief, Delta, CanaryOutcome, RecordId, GateReceipt, PromotionOutcome, GateReceiptRecordOutcome, Patch, PatchId, Job, JobStatus, JobId, JobKind, JobEnqueueRequest, JobConclusionKind, DraftReceipt, TestEditPatchSet, RunsStartInput, RunStartAcknowledgement, CompletedRunHandoff, AuthoringVerifyDraftInput, TestFileWrite, QuarantineEntryState, SuiteStabilityAssessment, AuthoringFunnelWindow, AuthoringFunnel, ConfigChange, ConfigWriteRefusal, ConfigWriteIssue, ConfigPathSegment, CatalogRow, ExplainCitation, QuarantineLadderState, Verdict, VerdictSentenceParts, ChainedJobRequest, JobHandlerOutcome, RunAnalysisArtifact, ClauseReceiptStatus, AgentAskInput, AgentAskResult, MaskedResolvedConfig, RunId, StepEvidence, RecordedTargets, KnowledgeGatesListResult, KnowledgeBundleSummary, KnowledgeRecordsInspectInput, InspectRecordView, KnowledgeRecordsDiffInput, RecordChange, KnowledgeRecordShowInput, KnowledgeRecordShowResult, EntityResolution, EntityDescriptor, LiveUrlSigner, LiveStreamDeclaration, KnowledgeAcceptInput, KnowledgeRejectInput, KnowledgeForgetInput, KnowledgeChangeOutput, PostProcessingStageDetail, CurationSnapshot, RunNotification, RunDetailRows, EvidenceBlobLookup, EvidenceBlobPresence, QuarantineOperationsPort, FeedbackPort, FeedbackReadsPort, MaskFn, RiskActionDescriptor, FunctionCaller, LiveUrlClaims, LiveViewSubjectRef, LiveUrlVerdict, AuthoringDedupContinuation, VerdictSource, RunEvidenceRef, DraftRef, WrittenTestReceipt, AuthoringDraftWriteOutput, FanOutReport, FanOutBound, ChatSessionEvent, ChatAbortedEvent, RefusedChatSessionLine, ChatTurnStatus, ClauseReceipt, ChatCompactionEvent, SessionLogPort, ScenarioClass, ChatScenario, ScenarioLane, ScenarioExpectationKind, AnalysisGetOutput } from '@vostride/agent-qa-schema';
2
2
  import { EventLogPort } from '@vostride/agent-qa-runner';
3
3
  import { ModelMessage, LanguageModel, ToolSet } from 'ai';
4
4
 
5
- /**
6
- * D-13 authoring-read port: the M4 QA agent's screenless seam, minted beside KnowledgePort and
7
- * test-proven in M2 with no CLI surface (382's verb family or M4 adds one). Read-only law: this
8
- * port must survive 382's no-write-symbol module-boundary proof — no Delta/commit/promote symbol
9
- * may ever join it. The runner's six-method KnowledgeHintReader hot-path contract is untouched.
10
- */
11
5
  interface AuthoringReadPort {
12
6
  getAuthoringBrief(intent: string, scope: ScopeKey): Promise<AuthoringBrief>;
13
7
  }
@@ -20,57 +14,6 @@ interface KnowledgePort {
20
14
  recordGateReceipt(receipt: GateReceipt): Promise<GateReceiptRecordOutcome>;
21
15
  }
22
16
 
23
- /**
24
- * 2026-09-06, 416 D-10: replayGateAvailable is true iff a host composition bound this port.
25
- * Phase 416 binds nothing; Phase 417 binds runReplayGate.
26
- *
27
- * 2026-09-11, 422 D-14 — THE ACKNOWLEDGEMENT CONTRACT, stated on the port rather than in the
28
- * adapter that happens to implement it today.
29
- *
30
- * A resolved `runReplayGate` means the returned receipt is DURABLE: the writer capability
31
- * acknowledged persisting it. It does not mean "a receipt was generated". The distinction is
32
- * the whole point — a Gate-1 run costs replay work no later run can recover, and a caller that
33
- * read a generated-but-unwritten receipt as a screening result would report a candidate as
34
- * screened whose evidence nothing can find afterwards, then refuse to screen it again because
35
- * its own memory says it already did.
36
- *
37
- * So the contract is one-sided on purpose: persistence refusal, a stale writer fence, a lease
38
- * held by somebody else and a dry run are all REFUSALS, surfaced through this port's existing
39
- * error path as {@link Gate1ReceiptNotPersistedError}. There is no acknowledgement member on
40
- * the returned receipt, because a receipt that reached this caller at all is already the
41
- * acknowledged one and a flag saying so would be a second answer to the same question.
42
- */
43
- interface ReplayGatePort {
44
- runReplayGate(candidateId: RecordId): Promise<GateReceipt>;
45
- }
46
- /**
47
- * The Gate-1 call produced a receipt that the writer did not acknowledge persisting (422 D-14).
48
- *
49
- * PROVIDER-NEUTRAL, and it lives here rather than in the CLI adapter for the reason every other
50
- * port refusal does: the caller that has to tell "screened" from "spent but unrecorded" is the
51
- * job handler, two packages away from whichever host bound the port, and an adapter-private
52
- * class would make that distinction reachable only by matching on message text.
53
- *
54
- * `recorded` carries the writer's own word for what happened — `dry-run`, `lease-held`,
55
- * `lease-refused`, or whatever a future writer adds — because a caller deciding whether to
56
- * retry wants to know WHICH refusal it met, and a boolean would flatten a transient lease
57
- * contention into the same fact as a deliberate dry run.
58
- *
59
- * What this error does NOT carry is the receipt. Handing back the bytes of a receipt nothing
60
- * stored is exactly the affordance that lets a caller record a screening result the store will
61
- * never agree with.
62
- */
63
- declare class Gate1ReceiptNotPersistedError extends Error {
64
- readonly candidateId: string;
65
- readonly recorded: string;
66
- readonly name = "Gate1ReceiptNotPersistedError";
67
- constructor(candidateId: string, recorded: string);
68
- }
69
-
70
- interface ToolsPort {
71
- execute(action: Action): Promise<ActionResult>;
72
- }
73
-
74
17
  interface PersistencePort {
75
18
  savePatch(patch: Patch): Promise<void>;
76
19
  getPatch(id: PatchId): Promise<Patch | undefined>;
@@ -80,12 +23,6 @@ interface ClockPort {
80
23
  now(): string;
81
24
  }
82
25
 
83
- /**
84
- * The authority token a drain-side verb must present.
85
- *
86
- * Deliberately a plain epoch record, not an opaque handle: 386 binds the v51 writer lease
87
- * as its source, and that lease's authority already IS an integer epoch on disk.
88
- */
89
26
  interface JobStoreFence {
90
27
  readonly epoch: number;
91
28
  }
@@ -103,73 +40,22 @@ interface JobCheckpointRecord {
103
40
  readonly recordedAt: string;
104
41
  readonly value: unknown;
105
42
  }
106
- /**
107
- * Refusal at the mutation boundary, mirroring v51's `WriterAuthorityLostError` shape: a
108
- * named class carrying both the epoch the caller held and the epoch the store observed, so
109
- * a log line says which writer was behind and by how much.
110
- */
111
43
  declare class StaleJobStoreEpochError extends Error {
112
44
  readonly heldEpoch: number;
113
45
  readonly observedEpoch: number;
114
46
  readonly name = "StaleJobStoreEpochError";
115
47
  constructor(heldEpoch: number, observedEpoch: number);
116
48
  }
117
- /**
118
- * One job row to insert: a caller's `JobEnqueueRequest` plus what the engine resolved for it.
119
- *
120
- * 385 plan 02 split this from the request DTO rather than duplicating it. The request is the
121
- * three facts a CALLER knows; the two fields added here are derived at the engine boundary
122
- * and belong to nobody else. The id is derived from `(kind, idempotencyKey)` through an
123
- * injected id source, so the same logical job carries the same id in every process and in
124
- * every replay (D-03 corollary) — a store that minted its own would break that, and a caller
125
- * that supplied its own could break it twice. Timestamps are absent on purpose: the store
126
- * stamps them from ITS injected clock, so no SQL-side default can leak host time into a dump.
127
- *
128
- * There is no separate "chained enqueue" type. A successor differs from a root job in who
129
- * asked for it, not in what it is, and the difference that does matter — a successor is
130
- * inserted inside its predecessor's transaction — is expressed by the verb it arrives at.
131
- */
132
49
  interface JobInsert extends JobEnqueueRequest {
133
50
  readonly id: JobId;
134
51
  readonly summary: string;
135
52
  }
136
- /**
137
- * Refusal when an operator tries to requeue a row this build cannot read (422 D-16).
138
- *
139
- * A requeue is the one operator verb that DESTROYS history — it resets the attempt count, deletes
140
- * every checkpoint row and file the last attempt recorded, and disowns the run's chain-terminal
141
- * notification. For an ordinary terminal row that is the whole point: the row goes back so a fixed
142
- * handler can run it again. For a quarantined row there is no handler to run it again — no kind
143
- * registry contains its kind — so the reset would spend the remedy and buy nothing: the very next
144
- * claim would quarantine the row a second time, and the spent attempt and the fixed reason that
145
- * recorded WHY it was quarantined would be gone.
146
- *
147
- * Raised INSIDE the requeue's transaction and before any of those three media is touched, so a
148
- * refused requeue leaves the store byte-identical.
149
- *
150
- * THE MESSAGE DOES NOT NAME THE KIND, and that is the same rule
151
- * `QUARANTINED_UNREADABLE_KIND_REASON` states for `last_failure_reason`: the unreadable kind is
152
- * a string an EARLIER build -- or anything with write access to that database -- put in a
153
- * column, and this message is rendered on operator terminals and copied into logs.
154
- * Interpolating it here took the one field of the row this build refuses to trust and pasted it
155
- * into the one place an operator is most likely to read without thinking. The value stays on
156
- * `unreadableKind`, where a caller has to ask for it and knows what it is when they do --
157
- * exactly where the `kind` column keeps it.
158
- */
159
53
  declare class QuarantinedJobRequeueError extends Error {
160
54
  readonly jobId: string;
161
55
  readonly unreadableKind: string;
162
56
  readonly name = "QuarantinedJobRequeueError";
163
57
  constructor(jobId: string, unreadableKind: string);
164
58
  }
165
- /**
166
- * The result of an enqueue.
167
- *
168
- * `deduped` reports that the unique index already held this `(kind, idempotencyKey)` pair
169
- * and `job` is the pre-existing row. A duplicate enqueue is a SUCCESS, never an error —
170
- * that is what makes "run the tail twice, get one job" a store guarantee rather than a
171
- * convention every caller has to remember (D-08).
172
- */
173
59
  interface JobEnqueueOutcome {
174
60
  readonly job: Job;
175
61
  readonly deduped: boolean;
@@ -189,17 +75,6 @@ type JobClaimOutcome = {
189
75
  readonly job: Job;
190
76
  readonly payload: unknown;
191
77
  };
192
- /**
193
- * What a claim may be told is not its to take (Phase 388-09, G-388-2 substrate).
194
- *
195
- * The exclusion is an OWNERSHIP declaration: a host names the kinds it may not claim, fixed
196
- * when its queue is composed. A kind on this list must be left exactly as the claim found it
197
- * — never claimed and released — because per D-18 the attempt is spent AT the claim, and
198
- * (388-09 Task 1, M-2) a released row is re-offered ahead of every younger row, so a
199
- * claim-then-release "refusal" either burns the row's bound or starves every other kind
200
- * behind it for as long as the row exists. The only refusal that writes no receipt and
201
- * costs nothing is one where the excluded row is never offered at all.
202
- */
203
78
  interface ClaimNextOptions {
204
79
  readonly excludeKinds?: readonly JobKind[];
205
80
  }
@@ -212,16 +87,6 @@ interface JobFailAndChainOutcome {
212
87
  readonly job: Job;
213
88
  readonly chained: readonly JobEnqueueOutcome[];
214
89
  }
215
- /**
216
- * How a failed attempt should be disposed of. Behaviour lands with the retry plan.
217
- *
218
- * This is NOT a second spelling of the handler's `JobHandlerOutcome`, and the two are kept
219
- * apart deliberately (385 plan 02). The handler REPORTS whether the thing that went wrong
220
- * could go right next time; the engine DECIDES what to record, and only the engine knows how
221
- * many attempts remain. A retryable report on the last permitted attempt becomes a permanent
222
- * disposition here — which is exactly the bound D-18 puts on retries, and it would have
223
- * nowhere to live if the report and the decision were one type.
224
- */
225
90
  type JobFailureDisposition = {
226
91
  readonly kind: 'retry';
227
92
  readonly reason: string;
@@ -236,17 +101,6 @@ type JobCheckpointReadOutcome = {
236
101
  readonly found: true;
237
102
  readonly payload: unknown;
238
103
  };
239
- /**
240
- * What asking to cancel a job actually did (D-19).
241
- *
242
- * Three outcomes, because cancellation genuinely has three cases and collapsing any two of
243
- * them loses a guarantee. A job nobody is running stops at once — there is nothing to be
244
- * cooperative about. A job in flight only records the REQUEST: terminating its row out from
245
- * under a running handler would abandon a half-finished step and leave whatever it was part
246
- * way through with no owner, so the driver honours the request at its next checkpoint boundary
247
- * instead. A job that has already finished, failed or been cancelled reports what it is and
248
- * nothing moves — a cancel is not an eraser.
249
- */
250
104
  type JobCancelOutcome = {
251
105
  readonly outcome: 'cancelled';
252
106
  } | {
@@ -255,38 +109,6 @@ type JobCancelOutcome = {
255
109
  readonly outcome: 'already-terminal';
256
110
  readonly status: JobStatus;
257
111
  };
258
- /**
259
- * What returning a finished job to the queue actually did (413 D-04/D-06 gap closure).
260
- *
261
- * A requeue is an OPERATOR's explicit act under the fence, not a lifecycle transition any
262
- * handler can reach. It exists because Phase 413's pump composed without the post-run
263
- * capabilities, the kind registry selected the STAND-IN analyze-run handler, and nineteen
264
- * rows reached `done` with a checkpoint recorded and nothing authored — a completion the
265
- * queue has no vocabulary to disown.
266
- *
267
- * `requeued` returns the row to `queued` with its attempts reset AND everything its last
268
- * attempt recorded FORGOTTEN in all three durable media, because they are one remedy rather
269
- * than three tidy-ups. The checkpoint rows and the checkpoint files go because `readCheckpoint`
270
- * resolves the LATEST recorded value across attempts, so a reset that kept the stand-in's
271
- * `verdicts-authored` row would hand the real handler the stand-in's value on the next attempt
272
- * and it would report completion having written nothing — the very substitution the requeue
273
- * exists to undo, performed a second time by the recovery path. The chain-terminal notification
274
- * the attempt minted for the run this job's payload names goes for a stronger reason: the
275
- * store's insert dedupes on the run (`ON CONFLICT (run_id) DO NOTHING`), so a receipt that
276
- * survives a requeue can never be replaced by the re-run — it would keep publishing an outcome
277
- * for an attempt whose every other trace the same transaction just deleted.
278
- *
279
- * What the requeue does NOT touch is the row's identity: the id and the
280
- * `(kind, idempotencyKey)` pair survive, so the chain identity `chainJobIdsForRun` derives
281
- * from the run id still addresses this row and a re-chained successor dedupes onto the
282
- * existing slot instead of doubling it. Enqueueing a fresh job under a new key would have
283
- * been the easier remedy and the wrong one.
284
- *
285
- * `not-terminal` is a refusal, and it carries the status it found. A queued or claimed job
286
- * belongs to a drainer: requeueing one would reset the attempt count out from under a
287
- * handler that is running, which is the two-writers-under-one-fence tampering the fence
288
- * exists to exclude.
289
- */
290
112
  type JobRequeueOutcome = {
291
113
  readonly outcome: 'requeued';
292
114
  } | {
@@ -306,52 +128,13 @@ declare class JobCancellationNotRequestedError extends Error {
306
128
  readonly name = "JobCancellationNotRequestedError";
307
129
  constructor(jobId: JobId);
308
130
  }
309
- /**
310
- * What a chain-terminal transition asks the store to record, if it really is terminal
311
- * (Phase 388, D-09 / D-10 / D-12).
312
- *
313
- * The 385 D-05 split, applied to a new decision. POLICY is already resolved by the time this
314
- * record exists: the engine decided which run this job belongs to, which ids the chain could
315
- * have enqueued, and what the notification should say. What the STORE decides is the only
316
- * part that cannot be decided outside a transaction — whether every candidate that exists is
317
- * terminal at the instant of the write, and whether this run has been notified already.
318
- *
319
- * `payloadJson` arrives serialized rather than as a value, and that is deliberate: the record
320
- * crosses a storageNeutral port into a store whose column is text, and canonical
321
- * serialization is a shipped primitive. A store that serialized it would be a second place
322
- * the bytes are decided, and the two would differ on the first key-order question.
323
- *
324
- * The parameter is OPTIONAL on both terminal verbs and defaults absent, so every existing
325
- * caller — the store-contract suite, the kill matrix, every kind that is not a chain member —
326
- * compiles and behaves exactly as before. A record that arrives for a chain that turns out
327
- * NOT to be terminal is not an error: the store simply writes nothing.
328
- */
329
131
  interface ChainTerminalRecord {
330
- /** The UNIQUE key one notification per run rests on. `runId` alone (D-10). */
331
132
  readonly dedupeKey: string;
332
- /**
333
- * Every job the chain COULD have enqueued, whether or not it did.
334
- *
335
- * Candidates rather than members, because a derived chain id is not a promise that a row
336
- * exists. `chainJobIdsForRun` derives EVERY one of the chain's ids from the run id alone, up
337
- * front, while the rows appear one at a time: each successor is enqueued only once its
338
- * predecessor chains, and a deduped or halted chain leaves a derived id no row will ever
339
- * carry. The store's rule is therefore stated over the candidates that EXIST — a successor
340
- * that has not been written yet cannot be read as a finished one, so the terminal cannot fire
341
- * a job early. The head is the one exception and is checked by name: a chain whose analyze-run
342
- * row was never written is not a run's chain at all.
343
- *
344
- * 2026-09-01 (Phase 409): the chain narrowed to two ids.
345
- * 2026-09-11 (Phase 422, D-13): three again, the third being the proposal stage the fold now
346
- * chains to. The candidate/member distinction is what carries that widening for free — a
347
- * chain halted at a permanently failed fold has no proposal row, and the rule reads it as the
348
- * absence it is rather than as an unfinished stage that blocks the record forever.
349
- */
350
133
  readonly candidateJobIds: readonly JobId[];
351
134
  /** Canonical JSON of the `RunNotification` this terminal would record. */
352
135
  readonly payloadJson: string;
353
136
  }
354
- /** One row of the deterministic logical dump (D-03). Column names, not the DTO shape. */
137
+ /** One row of the deterministic logical dump. Column names, not the DTO shape. */
355
138
  interface JobStoreDumpRow {
356
139
  readonly id: string;
357
140
  readonly kind: string;
@@ -361,39 +144,11 @@ interface JobStoreDumpRow {
361
144
  readonly payload: unknown;
362
145
  readonly attempts: number;
363
146
  readonly claimed: boolean;
364
- /**
365
- * Someone has asked for this job to stop (D-19).
366
- *
367
- * Store-owned state, exactly like `claimed`, and for the same reason: `JobStatus` is a
368
- * shipped kernel vocabulary whose C6 break added the two TERMINAL members the queue needed,
369
- * and "a running job that has been asked to stop" is not a terminal state — it is a fact
370
- * about what has been asked of a job that is still queued. Adding a status member for it
371
- * would be a second contract break to describe something no observer of the row needs to
372
- * treat as a lifecycle stage.
373
- */
374
147
  readonly cancelRequested: boolean;
375
- /**
376
- * What this row CONCLUDED, when the store can say it from the row alone (422 D-16).
377
- *
378
- * Present today for exactly one conclusion, `unreadable_kind`: a row written under a kind this
379
- * build has since retired, landed permanently at claim without ever being parsed. It belongs on
380
- * the DIAGNOSTIC projection rather than on `Job` because that is the only projection such a row
381
- * can travel through at all — `Job.kind` is the strict five-kind enum, so a quarantined row is
382
- * precisely the row the typed DTO cannot carry. Naming the column-shaped facts here is what lets
383
- * a listing describe it instead of throwing on it.
384
- *
385
- * ABSENT means "this row concluded nothing the store can name", which includes an ordinary
386
- * `done` row and a retired-kind row still sitting queued. It is not a summary of the status.
387
- */
388
148
  readonly conclusionKind?: JobConclusionKind;
389
149
  readonly createdAt: string;
390
150
  readonly updatedAt: string;
391
151
  }
392
- /**
393
- * A deterministic, ordered, canonical-JSON-comparable projection of everything the store
394
- * holds. The kill matrix compares THIS across an interrupted and an uninterrupted history —
395
- * never raw database bytes, whose page layout is not deterministic (D-03).
396
- */
397
152
  interface JobStoreDump {
398
153
  readonly schemaVersion: number;
399
154
  readonly epoch: number;
@@ -405,7 +160,6 @@ interface JobStorePort {
405
160
  * Adopting an epoch BELOW the recorded one is refused: authority only moves forward.
406
161
  */
407
162
  adoptEpoch(epoch: number): Promise<JobStoreFence>;
408
- /** Fence-free by design — see the D-02/D-07 note at the top of this module. */
409
163
  enqueue(request: JobInsert): Promise<JobEnqueueOutcome>;
410
164
  /**
411
165
  * Offer the next claimable row, honouring an optional kind exclusion in the SELECTION
@@ -418,24 +172,7 @@ interface JobStorePort {
418
172
  * that must BEHAVE differently.
419
173
  */
420
174
  claimNext(fence: JobStoreFence, options?: ClaimNextOptions): Promise<JobClaimOutcome>;
421
- /**
422
- * Terminate one job and enqueue its declared successors in the SAME transaction. Two
423
- * verbs would leave a window where a crash loses the chain; one verb has no window.
424
- *
425
- * `chainTerminal`, when supplied, joins that same transaction (388 D-09): the notification
426
- * a replayed terminal re-attempts dedupes on a unique index rather than on a caller's
427
- * memory of having written it.
428
- */
429
175
  completeAndChain(jobId: JobId, next: readonly JobInsert[], fence: JobStoreFence, chainTerminal?: ChainTerminalRecord): Promise<JobCompleteOutcome>;
430
- /**
431
- * Land one permanent failure and enqueue every declared continuation in the SAME fenced
432
- * transaction. A stale or crashing host therefore exposes neither half or both halves,
433
- * never a terminal parent with a partial chain (or an orphaned chain with a live parent).
434
- *
435
- * Carries the same optional chain-terminal record as its sibling, and that symmetry is
436
- * D-12: a failure is exactly when the user most needs to hear from the chain, so the
437
- * notification cannot live on the success path alone.
438
- */
439
176
  failAndChain(jobId: JobId, disposition: Extract<JobFailureDisposition, {
440
177
  readonly kind: 'permanent';
441
178
  }>, next: readonly JobInsert[], fence: JobStoreFence, chainTerminal?: ChainTerminalRecord): Promise<JobFailAndChainOutcome>;
@@ -447,14 +184,6 @@ interface JobStorePort {
447
184
  * cross-process projection, but that projection never becomes the source of authority.
448
185
  */
449
186
  publishCheckpoint(jobId: JobId, checkpointId: string, payload: JobCheckpointRecord, fence: JobStoreFence): Promise<void>;
450
- /**
451
- * Ask for a job to stop, and report what that meant for the job it found (D-19).
452
- *
453
- * Fenced like every other verb that moves a job, and for the sharper reason: a writer that
454
- * has lost its lease could otherwise cancel work the current writer is actively running.
455
- * One transaction, so the decision between "stop it now" and "ask it to stop" is made
456
- * against the same row state it is then written to.
457
- */
458
187
  requestCancel(jobId: JobId, fence: JobStoreFence): Promise<JobCancelOutcome>;
459
188
  /**
460
189
  * Land a job that was asked to stop, releasing its claim. THE DRIVER'S verb, not a caller's.
@@ -465,19 +194,6 @@ interface JobStorePort {
465
194
  * what keeps `cancelled` unreachable except through the two-step path.
466
195
  */
467
196
  recordCancellation(jobId: JobId, fence: JobStoreFence): Promise<void>;
468
- /**
469
- * Return a FINISHED job to the queue, forgetting what its last attempt recorded.
470
- *
471
- * Terminal is exactly `done | failed | cancelled` — the store's own `TERMINAL_JOB_STATUSES`
472
- * — and anything else answers `not-terminal` having moved nothing. The lifecycle reset and
473
- * the checkpoint forget are ONE fenced transaction, because a row that reads `queued` while
474
- * its checkpoints still exist is precisely the state that replays a stand-in's recorded
475
- * authorship as though a handler had produced it.
476
- *
477
- * An id no row answers to raises the store's existing not-found error rather than gaining a
478
- * third outcome arm (413-02 D-4): the outcome union describes what the store found a JOB
479
- * to be, and a mismatched id is a caller bug, not data.
480
- */
481
197
  requeueTerminal(jobId: JobId, fence: JobStoreFence): Promise<JobRequeueOutcome>;
482
198
  /**
483
199
  * Read-side: no fence, because reading claims no authority.
@@ -500,13 +216,6 @@ interface JobStorePort {
500
216
  interface JobModelRequest {
501
217
  readonly system?: string;
502
218
  readonly prompt: string;
503
- /**
504
- * Optional image parts riding the SAME request (389 D-02). Screenshots are
505
- * capability-gated per the v46 negotiation the host binding surfaces as `vision` below:
506
- * an author attaches evidence bytes here only when the capability advertises vision AND
507
- * a blob reader is bound. Absent means a text-only request — the L9 graceful-degradation
508
- * posture — and every pre-widening caller and stub stays assignable unchanged.
509
- */
510
219
  readonly images?: readonly Uint8Array[];
511
220
  readonly signal?: AbortSignal;
512
221
  }
@@ -514,23 +223,8 @@ interface JobModelRequest {
514
223
  interface JobModelOutput {
515
224
  readonly text: string;
516
225
  }
517
- /**
518
- * The model, as a job may see it (D-11 layer 2).
519
- *
520
- * A TYPE, never an import: a job handler receives a value shaped like this from exactly one
521
- * place — the author callback's parameter — and the barrel publishes no accessor that could
522
- * produce one. `modelId` is here so a checkpoint's provenance is recordable without the engine
523
- * having to know anything else about the binding.
524
- */
525
226
  interface JobModelCapability {
526
227
  readonly modelId: string;
527
- /**
528
- * Whether the bound model accepts image parts (389 D-02) — the v46 capability
529
- * negotiation surfaced as one flag by the host binding. OPTIONAL and absent means
530
- * text-only: a text-only BYOM binding and every existing stub keep compiling and keep
531
- * their L9 posture without declaring anything. Nothing else in the memoization
532
- * contract moves with this flag.
533
- */
534
228
  readonly vision?: boolean;
535
229
  generate(request: JobModelRequest): Promise<JobModelOutput>;
536
230
  }
@@ -597,57 +291,18 @@ interface ParsedDraftLike {
597
291
  readonly filePath: string;
598
292
  /** Identity half two: the parsed test's display name, as `PerTestOutcome.testName`. */
599
293
  readonly testName: string;
600
- /**
601
- * The parsed test's `target` — the name `agent-qa run` resolves against the config.
602
- *
603
- * REQUIRED, exactly as `TestDefinitionSchema` requires the key: a document that carries none is
604
- * not a parsed test at all, so an optional mirror here would be a second, weaker answer to what
605
- * the parser already decided.
606
- *
607
- * IT IS HERE BECAUSE IT IS CHECKED, and unlike `context` it is checked precisely because it is
608
- * NOT editable (394-37, WR-04). `runs.start` types this value into a live application:
609
- * `resolveTarget` throws on a name the config does not declare, inside a real child process, up
610
- * to `DRAFT_STABILITY_RUNS_N` times — and `target` is outside `EDITABLE_POINTER_ROOTS`, so no
611
- * repair patch may correct it. The one place that can refuse it cheaply is the compile stage,
612
- * which already holds the vocabulary it serves the prompt; this field is what lets it compare.
613
- */
614
294
  readonly target: string;
615
- /** Pre-interpolation NL step text. Post-interpolation text can carry resolved secrets (D-19). */
295
+ /** Pre-interpolation NL step text. Post-interpolation text can carry resolved secrets. */
616
296
  readonly steps: readonly string[];
617
- /**
618
- * The parsed test's `context` block, pre-interpolation, optional exactly as
619
- * `TestDefinitionSchema.context` is — post-interpolation text can carry resolved secrets (D-19).
620
- *
621
- * IT IS HERE BECAUSE IT IS EDITABLE. `context` is one of the three `EDITABLE_POINTER_ROOTS`, so
622
- * a patch can write it; a mirror that omitted it made the credential gate blind to a whole root
623
- * by TYPE SHAPE rather than by a missed entry in a locations array.
624
- */
625
297
  readonly context?: string;
626
298
  /** Digest of the draft bytes evaluated, so a later edit cannot inherit this receipt. */
627
299
  readonly contentDigest: string;
628
300
  }
629
- /**
630
- * The run capability, shaped as the `runs.start` / `runs.get` op pair (D-02, op-calls-op).
631
- *
632
- * There is no second in-host marker-stamping site: the evaluator asks for a gate-run through the
633
- * same input contract every other caller uses, and the runner stamps the handoff. What the
634
- * evaluator does with the acknowledgment is CONFIRM — `runs.start` copies both markers off the
635
- * durable record rather than echoing the request, so an acknowledgment that comes back without
636
- * them is the host saying the run was not accepted as a gate-run.
637
- */
638
301
  interface DraftRunPortLike {
639
302
  start(input: RunsStartInput): Promise<RunStartAcknowledgement>;
640
303
  get(runId: string): Promise<CompletedRunHandoff | undefined>;
641
304
  }
642
- /**
643
- * The suite-fingerprint read port (D-15), mirroring `createSuiteFingerprintIndex`'s three verbs.
644
- *
645
- * Synchronous, because the real implementation reads a local SQLite table and is. Partition
646
- * scoping is the binder's job: the real factory REQUIRES a product, so a bound index is already
647
- * scoped and this shape has no scoping knob to get wrong.
648
- */
649
305
  interface SuiteFingerprintIndexLike {
650
- /** The union D-14's novel-element test compares against. Empty table, empty union. */
651
306
  elementUnion(): ReadonlySet<string>;
652
307
  /** The identities that HAVE fingerprint rows — the fingerprint lane's population. */
653
308
  coveredTests(): readonly SuiteTestIdentity[];
@@ -659,49 +314,15 @@ interface SuiteTestIdentity {
659
314
  readonly filePath: string;
660
315
  readonly testName: string;
661
316
  }
662
- /**
663
- * One suite test as TEXT, for D-16's degraded lane.
664
- *
665
- * The population is the authored-test universe MINUS `coveredTests()` — an evidence-less test is
666
- * one the derived index has no rows for, which the index deliberately does not conflate with
667
- * "uncovered". Building that difference is the host's job: the suite is not a fact the derived
668
- * index owns.
669
- *
670
- * Carries no `title` beside `testName`. A parsed test has ONE display name, so a separate title
671
- * field would be two spellings of one fact and a reader would have to guess which of them the
672
- * similarity actually reads.
673
- */
674
317
  interface SuiteTextCorpusEntryLike {
675
318
  readonly filePath: string;
676
319
  readonly testName: string;
677
320
  /** Pre-interpolation step text, same rule as the draft's. */
678
321
  readonly stepsText: readonly string[];
679
322
  }
680
- /**
681
- * The durable receipts store (D-19), mirroring `createDraftReceiptsStore`'s write verb.
682
- *
683
- * Only `put` — this evaluator writes and never reads back. The real store's `get`/`list` exist for
684
- * 394's proposal envelope, and a port that named verbs its holder never calls would invite a
685
- * binder to supply implementations nothing exercises.
686
- */
687
323
  interface DraftReceiptsStoreLike {
688
324
  put(receipt: DraftReceipt): Promise<void>;
689
325
  }
690
- /**
691
- * The READ half of the same store (394-07, D-12 · WTST-05) — the funnel's whole substrate.
692
- *
693
- * A SECOND, NARROWER MIRROR rather than a `list` member added to the write port above, and the
694
- * split is the point: the evaluator writes and never reads back, and the funnel reads and never
695
- * writes. One port carrying both verbs would let a composition hand the funnel projection a
696
- * capability to overwrite the very records it is reporting on — and the whole claim of D-12 is
697
- * that nothing new is persisted, which is easier to keep when the projection is structurally
698
- * unable to persist anything.
699
- *
700
- * `list()` and nothing else. The real store's `get(contentDigest)` exists, and the funnel has no
701
- * use for it: a funnel is about a POPULATION, and a port able to fetch one receipt by address
702
- * would invite a projection that walked digests it learned somewhere else — a second denominator
703
- * beside the one `list()` defines.
704
- */
705
326
  interface DraftReceiptsReadPortLike {
706
327
  list(): Promise<DraftReceipt[]>;
707
328
  }
@@ -723,52 +344,11 @@ interface DraftReceiptsReadPortLike {
723
344
  interface AuthoringReadPortLike {
724
345
  getAuthoringBrief(intent: string, scope: ScopeKey): Promise<AuthoringBrief>;
725
346
  suiteTextCorpus(): Promise<readonly SuiteTextCorpusEntryLike[]>;
726
- /**
727
- * The names a `target` may legally take in this project (394-33, WR-07).
728
- *
729
- * A VOCABULARY, NOT A DERIVATION, and that is the whole of what it fixes. `TestDefinitionSchema`
730
- * requires a `target`, nothing validates one before `runs.start` types it into a live
731
- * application, and `target` is outside `EDITABLE_POINTER_ROOTS` so no repair patch can correct
732
- * it — yet stage 3's prompt asked for the value while serving no list to choose from. The Scope
733
- * block is not that list: `deriveDefaultAuthoringScope` answers a PRODUCT
734
- * (`target.product ?? targetName`), so on any project where a target declares a product under a
735
- * different name, the scope key is not a configured target at all.
736
- *
737
- * A THIRD VERB ON THE READ HALF rather than a port of its own, on this interface's own stated
738
- * reasoning: it is a READ of this project's own state through capabilities the composition root
739
- * already holds, and a host that can serve the brief and the suite corpus but not the names its
740
- * own config declares does not exist. It is deliberately NOT on `AuthoringScopePortLike` — that
741
- * port answers WHICH SCOPE THIS PROJECT AUTHORS FOR and can legitimately refuse; this one
742
- * enumerates and never refuses. An empty array is a legitimate answer (a config declaring no
743
- * targets), and the block a reader is served says so in words rather than rendering nothing.
744
- */
745
347
  configuredTargetNames(): Promise<readonly string[]>;
746
348
  }
747
- /**
748
- * The host's answer to "which scope does this project author for?" when the caller named none.
749
- *
750
- * SEPARATE FROM {@link AuthoringReadPortLike} rather than a third verb on it, because the two are
751
- * different facts about a host. The read half needs a knowledge index; this needs the resolved
752
- * config and the workspace's committed tests, and a host can genuinely have one without the
753
- * other. Bundling them would make a composition that can derive a scope but serve no brief report
754
- * the whole read half absent.
755
- *
756
- * IT EITHER ANSWERS OR IT THROWS, and there is no third arm. A `ScopeKey | undefined` would put
757
- * the never-guess decision back at the op, one layer from the evidence — the host is the only
758
- * side that knows WHICH candidates it saw, and D-19's posture is that a refusal names them.
759
- */
760
349
  interface AuthoringScopePortLike {
761
350
  deriveDefault(): Promise<ScopeKey>;
762
351
  }
763
- /**
764
- * What staging a compiled draft produced — parsed and written, or refused and NOT written.
765
- *
766
- * The refusal arm is the T-394-06 mitigation expressed as a return type. Model-authored YAML is
767
- * the one input on this path that no schema at the op boundary can vet, so it is parsed before it
768
- * becomes a file; a shape that could only answer "here is your path" would have forced the caller
769
- * to discover an unparsable draft one stage later, from the verify op, with the bytes already on
770
- * disk.
771
- */
772
352
  type DraftStageOutcome = {
773
353
  readonly kind: 'staged';
774
354
  readonly draft: ParsedDraftLike;
@@ -796,70 +376,12 @@ type DraftStageOutcome = {
796
376
  * draft's own member.
797
377
  */
798
378
  interface DraftShelfPortLike {
799
- /**
800
- * The model stage 3 authors with — built by the host through `createModel` (389 D-04).
801
- *
802
- * A FACTORY and not an object, on the composition root's own `model` idiom: a model is
803
- * resolved at most once and lazily, when something actually needs one, so a composition can
804
- * bind this capability without constructing a client for a host that never authors a draft.
805
- */
806
379
  model(): Promise<JobModelCapability>;
807
380
  /** Parse, then write, in that order. An unparsable draft leaves nothing on disk. */
808
381
  write(contents: string): Promise<DraftStageOutcome>;
809
- /**
810
- * Apply an RFC-6902 patch set to a staged draft and stage the result (394-06, D-09/D-20).
811
- *
812
- * THE ONE PATCH APPLIER, REACHED THROUGH A PORT RATHER THAN AN IMPORT. `applyPatchSetToTestDoc`
813
- * lives in `core` — it needs the yaml CST so untouched nodes keep their source bytes — and this
814
- * package may not depend on `core` at all (the FUNC-02 firewall stated at the top of this file).
815
- * The host binds this verb over that applier, so the repair loop and the user-facing edit op are
816
- * two consumers of one contract, which is the property a patch vocabulary with a single consumer
817
- * cannot keep.
818
- *
819
- * It answers the SAME `DraftStageOutcome` the whole-document `write` answers, and for the same
820
- * reason: what comes back from a model cannot be trusted to still be a runnable test document
821
- * after it is applied, so the result is parsed before it is anything else, and an unparsable
822
- * result leaves the shelf as it was.
823
- */
824
382
  applyPatches(draft: ParsedDraftLike, patchSet: TestEditPatchSet): Promise<DraftStageOutcome>;
825
- /**
826
- * The run capability stage 5 reads evidence through and the repair arm probes with (394-06).
827
- *
828
- * ON THIS BUNDLE rather than as a fourth loop dep, on the bundle's own stated reasoning: none of
829
- * these capabilities is independently bindable. `verifyDraft` already dispatches an op that
830
- * starts gate runs, so a host that can verify a draft can start and read one by construction —
831
- * a shelf bound without this would be a host that can ask whether a draft is any good and cannot
832
- * look at the runs that answered.
833
- *
834
- * It is the HANDOFF reader (`DraftRunPortLike.get`) and not `runs.get`'s projection, because the
835
- * router decides on per-step execution evidence — terminations, planning failures, cycle flags —
836
- * and none of that survives a `RunDetail`, whose only step-shaped field is a count.
837
- */
838
383
  readonly runs: DraftRunPortLike;
839
- /**
840
- * Stage 4, dispatched as the REGISTERED `authoring.draft.verify` op (D-01, op-calls-op).
841
- *
842
- * A closure the composition root mints over the registry it is building, exactly as
843
- * `createDraftEvaluation`'s `startGateRun` is minted over the same deferred reference — it is
844
- * how a registered op reaches a sibling registered op without this package being able to name a
845
- * registry. It rides THIS bundle rather than a dep of its own because it is not independently
846
- * bindable either: a host that can compile and stage a draft but cannot verify one would author
847
- * a test and then have nothing to say about whether it is worth keeping, which is the one answer
848
- * the loop exists to produce.
849
- */
850
384
  verifyDraft(input: AuthoringVerifyDraftInput): Promise<DraftReceipt>;
851
- /**
852
- * Write a staged draft's bytes to its test path (432 D-06/D-07). The direct final-test writer.
853
- *
854
- * ON THIS BUNDLE because it is not independently bindable either: a host that can stage a draft
855
- * on its shelf is the host that can put those bytes in its suite. The host keeps the guards the
856
- * old accept kept — the target must lie inside the workspace and still hold the bytes the caller
857
- * read (`sourceDigest`, absent for a new test) — writes through a sibling temp and a rename, and
858
- * runs no git. A write that did not land answers `not-written` with its reason, never `written`.
859
- *
860
- * THE LOOP DECIDES WHETHER TO CALL IT, from the disposition (`draftDispositionWritesTarget`); the
861
- * writer never sees one. A rejected draft is simply never handed to it.
862
- */
863
385
  publish(request: TestFilePublishRequestLike): Promise<TestFileWrite>;
864
386
  }
865
387
  /** One final draft to write: the shelf copy, where it goes, and what that path held when read. */
@@ -884,32 +406,10 @@ interface TestFilePublishRequestLike {
884
406
  * that into the honest `no-history` posture rather than into a clean bill of health.
885
407
  */
886
408
  interface SuiteOutcomeHistoryPortLike {
887
- /**
888
- * The stable identities the target suite file has run evidence for, in ONE product.
889
- *
890
- * `product` LEADS BOTH VERBS because the projection these read is keyed
891
- * (product, filePath, testName) and nothing else is a stable identity there. A port shaped
892
- * without it could only be bound by guessing a product or by serving every product's history
893
- * under one file path — and the second would report another application's flakiness as this
894
- * target's, which is the whole failure the message exists to avoid (T-394-68).
895
- */
896
409
  testsIn(product: string, filePath: string): Promise<readonly SuiteTestIdentity[]>;
897
410
  /** One identity's recorded member outcomes, newest last. */
898
411
  outcomesFor(product: string, filePath: string, testName: string): Promise<readonly string[]>;
899
412
  }
900
- /**
901
- * The capability `authoring.funnel.get` binds through — the funnel's reads, bundled.
902
- *
903
- * 2026-10-01 (432-17, D-06): the proposals inbox read is deleted with the inbox. An authored test
904
- * that a keep-gate did not reject is written in its own call, so no person accepts it afterwards
905
- * and nothing records such a decision. The terminal stage reads the receipts alone (see
906
- * {@link projectAuthoringFunnel}).
907
- *
908
- * THE STABILITY HALVES RIDE THE SAME BUNDLE for the same argument, and they are OPTIONAL within
909
- * it: a host may genuinely have receipts and no outcome projection, and D-14's whole point is
910
- * that a missing history reads as `no-history` rather than as a refusal. What is NOT here is any
911
- * way to start a run.
912
- */
913
413
  interface AuthoringFunnelPortLike {
914
414
  readonly receipts: DraftReceiptsReadPortLike;
915
415
  readonly outcomes?: SuiteOutcomeHistoryPortLike;
@@ -926,32 +426,7 @@ interface AuthoringFunnelPortLike {
926
426
  * order, which is why the sort is stable rather than a comparator that reorders equal instants.
927
427
  */
928
428
  declare function scopeReceiptsToWindow(receipts: readonly DraftReceipt[], window: AuthoringFunnelWindow): readonly DraftReceipt[];
929
- /**
930
- * Project the funnel over one window.
931
- *
932
- * `dedupRefusals` is deliberately left ABSENT: a stage-2 refusal ends the invocation with nothing
933
- * authored — no draft, no receipt and no gate-run (D-04) — so nothing on this host durably records
934
- * one, and reporting `0` would say "no author was ever refused" about something nothing counts.
935
- *
936
- * THE TERMINAL STAGE IS READ OFF THE RECEIPTS (2026-10-02, quick 261002-ctr). It was `accepted`,
937
- * applied decisions read off `test-draft` proposal envelopes. Since 432-08 a draft a keep-gate did
938
- * not reject is written to its test path in the call that authored it and nothing is filed, and
939
- * 432-17 deleted the inbox, so `accepted` read 0 on every host. `writeEligible` counts the receipts
940
- * whose stored disposition D-06 writes, which `computeAuthoringFunnel` reads itself, so this
941
- * projection hands it nothing but the window's receipts.
942
- */
943
429
  declare function projectAuthoringFunnel(window: AuthoringFunnelWindow, port: AuthoringFunnelPortLike): Promise<AuthoringFunnel>;
944
- /**
945
- * Gather the target's existing tests and assess them. STARTS ZERO RUNS.
946
- *
947
- * The draft's OWN identity is excluded from its neighbours: a draft under evaluation is not
948
- * evidence about the suite it is trying to join, and counting its own flip as a neighbour's
949
- * would let every mixed verdict explain itself.
950
- *
951
- * An unbound outcome port is `no-history` rather than a refusal, which is D-14's degraded posture
952
- * arriving through the composition instead of through a special case: a host that cannot read the
953
- * projection genuinely has no history to report, and saying so is the honest answer.
954
- */
955
430
  declare function gatherSuiteStability(input: {
956
431
  /**
957
432
  * The partition the outcome projection keys on — `slugBranch` of the run's own scope, as the
@@ -965,39 +440,9 @@ declare function gatherSuiteStability(input: {
965
440
 
966
441
  /** A dotted config path's new value. The three scalar shapes a config field can hold. */
967
442
  type ConfigFieldValue = string | number | boolean;
968
- /**
969
- * A value of any shape the configuration format can hold (395-05).
970
- *
971
- * SCALARS ARE NOT ENOUGH ONCE MORE THAN ONE OP WRITES. `config.field.set` addresses one leaf, so its
972
- * DTO's scalar union was the whole vocabulary. `config.target.create` writes a target ENTRY — a map
973
- * of `platform`, `url` and possibly an auth binding — and `config.hook.add` writes a hook ENTRY into
974
- * a sequence. Composing those out of several scalar writes would mean several parses of the same
975
- * file, each free to disagree with the last about what it was editing, for an edit a person reads as
976
- * one block.
977
- *
978
- * IT IS THE FORMAT'S VOCABULARY, NOT ANY OP'S. Three scalars, sequences and maps is what yaml holds;
979
- * nothing here names a target, a hook or a plugin. A per-op value type would be the mutation fork
980
- * this phase's construction pipeline exists to avoid.
981
- */
982
443
  type ConfigStructuredValue = ConfigFieldValue | readonly ConfigStructuredValue[] | {
983
444
  readonly [key: string]: ConfigStructuredValue;
984
445
  };
985
- /**
986
- * What the document holds at a path, as a STRUCTURAL fact rather than as a value (395-05).
987
- *
988
- * WHY THIS EXISTS AT ALL: three of this phase's ops have to answer a question about the document
989
- * before they may mutate it. Does `registry.targets.shop` already exist (a create is never an
990
- * upsert)? Does it exist at all (an edit of an absent target is a typed not-found, and 387's
991
- * never-guess posture says the refusal lists the names that DO exist)? How long is the hook
992
- * sequence, so an append writes past its end rather than over its last member? `packages/agent`
993
- * cannot parse yaml — its ownership contract pins external dependencies at `['ai', 'zod']` — so the
994
- * document's shape is a host fact exactly as its bytes are.
995
- *
996
- * IT ANSWERS SHAPE, NEVER CONTENT, and the line is drawn there deliberately. `keys` and `length`
997
- * describe the container; the values inside it are not returned. A port that answered "what is at
998
- * this path" would be a second, unmasked config reader — the capability `ConfigReaderPort`'s header
999
- * says must not exist — reached through the back door of a mutation helper.
1000
- */
1001
446
  interface ConfigPathFacts {
1002
447
  /** The dotted path this fact is about, echoed so a caller reading several cannot misalign them. */
1003
448
  readonly path: string;
@@ -1016,123 +461,36 @@ interface ConfigFileBytes {
1016
461
  /** Absent means the file is not there — a fact, never a zero-length stand-in for one. */
1017
462
  readonly bytes?: Uint8Array;
1018
463
  }
1019
- /**
1020
- * The config file SET, enumerated — never matched.
1021
- *
1022
- * A SET AND NOT A GLOB (394-39's population lesson, RESEARCH Pitfall 5). The host derives the
1023
- * members from the config itself — the config yaml, the hooks file it names, the managed gitignore
1024
- * block — so "which files are config" has one answer that a porcelain check, an out-of-set
1025
- * classifier and an apply all read. A pattern would answer differently on the first repository
1026
- * whose layout nobody anticipated, and it would do so silently.
1027
- */
1028
464
  interface ConfigFileSet {
1029
465
  /** Which member of {@link ConfigFileSet.files} is the config yaml a field edit addresses. */
1030
466
  readonly configFile: string;
1031
- /**
1032
- * Which member is the hooks file (395-05).
1033
- *
1034
- * NAMED RATHER THAN GUESSED. `config.hook.add` writes this member and no other, and the two ways
1035
- * to find it without being told are both wrong: matching on `hooks.yaml` ignores the fact that
1036
- * `workspace.hooksFile` can name anything, and "the yaml that is not the config" breaks the day
1037
- * the set grows a third yaml. The host reads the name from the config it just parsed, which is the
1038
- * same read `enumerateConfigFileSet` was given.
1039
- */
1040
467
  readonly hooksFile: string;
1041
468
  readonly files: readonly ConfigFileBytes[];
1042
469
  }
1043
- /**
1044
- * What the comment-preserving edit produced, and what it replaced.
1045
- *
1046
- * `previousValue` RIDES THE SAME PARSE THAT WROTE THE NEW ONE (395-04, D-02). The per-value
1047
- * cross-boundary classifier has to compare the committed value against the proposed one — a base URL
1048
- * pointing at a NEW origin is the case a per-op map cannot see — and the only alternative to
1049
- * returning it here was a second read of the same bytes through a second parse, free to disagree
1050
- * with the one the mutation actually applied. One parse answers both halves of one edit.
1051
- *
1052
- * ABSENT MEANS "there was no scalar there": either the path held nothing, or it held a map or a
1053
- * sequence. Both are true statements about what the edit replaced, and neither is a value an origin
1054
- * comparison can be taken over — a wholesale subtree replacement is flagged on its own grounds
1055
- * (secret-adjacency) rather than by pretending its old value was a string.
1056
- */
1057
470
  interface ConfigFieldSetResult {
1058
471
  readonly bytes: Uint8Array;
1059
472
  readonly previousValue?: ConfigFieldValue;
1060
473
  }
1061
474
  interface ConfigFilesPortLike {
1062
- /** Re-read the committed set. Called at PROPOSAL time, which is CFGM-04's own law. */
1063
475
  read(): Promise<ConfigFileSet>;
1064
- /**
1065
- * The typed field mutation, comment-preserving, over exactly the bytes it was handed.
1066
- *
1067
- * TAKES THE BYTES RATHER THAN RE-READING THEM. The caller has just re-read the file and is about
1068
- * to digest what it read; a mutation that opened the file again would edit a version the digest
1069
- * is not about, which is a freshness hole inside the freshness contract.
1070
- *
1071
- * 395-05 WIDENED `value` from a scalar to {@link ConfigStructuredValue}. See that type's header:
1072
- * the widening is the yaml FORMAT's vocabulary arriving in full, not an op's shape leaking into
1073
- * the port. `previousValue` still answers scalars only, because that is the only case an origin
1074
- * comparison can be taken over.
1075
- */
1076
476
  setField(input: {
1077
477
  readonly bytes?: Uint8Array;
1078
478
  readonly path: string;
1079
479
  readonly value: ConfigStructuredValue;
1080
480
  }): Promise<ConfigFieldSetResult>;
1081
- /**
1082
- * What the document HOLDS at each path, structurally (395-05). See {@link ConfigPathFacts}.
1083
- *
1084
- * EVERY PATH IN ONE CALL, SO EVERY ANSWER COMES FROM ONE PARSE. An op that asked twice could be
1085
- * told the target is absent and then, from a second parse of the same bytes, be given a key list
1086
- * that contains it. The bytes are handed in for `setField`'s reason, one paragraph up.
1087
- */
1088
481
  inspect(input: {
1089
482
  readonly bytes?: Uint8Array;
1090
483
  readonly paths: readonly string[];
1091
484
  }): Promise<readonly ConfigPathFacts[]>;
1092
- /**
1093
- * A fresh canonical hook id (395-05).
1094
- *
1095
- * A HOST FACT FOR THE REASON `mintProposalId` IS ONE. `core`'s `HookDefinitionSchema` refuses any
1096
- * id `isCanonicalHookId` does not accept — `h_` followed by ten id-agent words — and that mint
1097
- * lives in `@vostride/agent-qa-ids`, which is not among this package's internal dependencies. It
1098
- * also draws randomness, and nothing in `packages/agent` reads a clock or an entropy source. A
1099
- * hand-formatted id would be the "don't hand-roll" row of this phase's research arriving as a
1100
- * defect: it would parse as a string, be written into a person's repository, and be refused by
1101
- * the hooks schema at the next run rather than at the proposal.
1102
- */
1103
485
  mintHookId(): string;
1104
486
  }
1105
- /**
1106
- * One thing whole-config validation said, and the file it said it ABOUT (395-10 · WR-11).
1107
- *
1108
- * THE MESSAGE IS THE VALIDATOR'S OWN WORDS; the path is not. This layer neither classifies nor
1109
- * renders the words — a closed issue vocabulary here would be a second, weaker copy of `core`'s
1110
- * schema refinements, out of date the first time one of them is added — but WHICH FILE an issue is
1111
- * about is a fact the validator alone knows, and a string that had already joined the two could
1112
- * only be split again by parsing it back apart.
1113
- *
1114
- * WHY IT IS SPLIT AT ALL. A blocked record's `paths` used to be the DIFFED files, so a proposal
1115
- * that edited the config yaml and was refused because the committed hooks file does not parse named
1116
- * the one file that was fine. A person following that reference finds nothing wrong and re-tries the
1117
- * edit. The pipeline now unions these paths into the record's, which it can only do if the path
1118
- * arrives as a path.
1119
- */
1120
487
  interface ConfigValidationIssue {
1121
488
  /** The config-set member the issue is about — never a dotted config path. */
1122
489
  readonly path: string;
1123
- /**
1124
- * The validator's own sentence. It may contain a dotted config path; it may not contain a VALUE.
1125
- *
1126
- * THE SECOND HALF IS A CONTRACT AND NOT A HOPE, and 395-10 is where it was learned: the vendored
1127
- * `yaml` package pretty-prints a parse error with a verbatim excerpt of the offending source line,
1128
- * and an implementation that interpolated that error text published whatever that line held onto
1129
- * three surfaces. A message derived from a library's error must be reduced to the facts this
1130
- * product chose to publish — for a parse failure, its POSITION.
1131
- */
1132
490
  readonly message: string;
1133
491
  }
1134
492
  /**
1135
- * What whole-config validation said about the candidate config (CFGM-01).
493
+ * What whole-config validation said about the candidate config.
1136
494
  */
1137
495
  type ConfigValidation = {
1138
496
  readonly ok: true;
@@ -1141,21 +499,6 @@ type ConfigValidation = {
1141
499
  readonly issues: readonly ConfigValidationIssue[];
1142
500
  };
1143
501
  interface ConfigValidatePortLike {
1144
- /**
1145
- * The WHOLE CANDIDATE SET as it would stand after the mutation — never the changed field alone,
1146
- * and since 395-05 never the changed FILE alone either.
1147
- *
1148
- * THE SET, BECAUSE THE CONFIG IS MORE THAN ONE FILE. `config.hook.add` writes the hooks file and
1149
- * leaves the config yaml untouched; a validator that only ever saw the config yaml would have
1150
- * declared that proposal valid without reading the only bytes it changed. D-03's law is that the
1151
- * guards here are WHOLE-INPUT by construction, and the whole input is the pinned file set: the
1152
- * host validates each member it has a schema for (the config yaml against `core`'s
1153
- * `AgentQaConfigSchema`, the hooks file against `HooksFileSchema`) and passes over the ones it
1154
- * does not (`.gitignore` is a member of the set and has no schema).
1155
- *
1156
- * MEMBERS WITH NO BYTES ARE ABSENT FILES, and absent is a legal state for every member but the
1157
- * config yaml — a project with no hooks file is an ordinary project, not an invalid one.
1158
- */
1159
502
  validate(input: {
1160
503
  readonly configFile: string;
1161
504
  /** The hooks file member, so the validator picks a schema by NAME rather than by extension. */
@@ -1164,12 +507,6 @@ interface ConfigValidatePortLike {
1164
507
  readonly files: readonly ConfigFileBytes[];
1165
508
  }): Promise<ConfigValidation>;
1166
509
  }
1167
- /**
1168
- * What a direct field change did to the config yaml (2026-09-30, 432-07 D-05/D-07).
1169
- *
1170
- * `applied` names the revision the change merged onto, which is the file as it stood in the
1171
- * change's own turn and so includes any outside edit made meanwhile. A refusal wrote nothing.
1172
- */
1173
510
  type ConfigFieldWriteOutcome = {
1174
511
  readonly kind: 'applied';
1175
512
  /** The config member, workspace-relative. */
@@ -1192,13 +529,6 @@ interface ConfigFilePublication {
1192
529
  readonly baseDigest?: string;
1193
530
  readonly bytes: Uint8Array;
1194
531
  }
1195
- /**
1196
- * What a multi-file publication actually did (432-07 D-05/D-07).
1197
- *
1198
- * `refused` is decided before any write. `interrupted` is a later failure, and `written` names
1199
- * exactly the files that now hold their new bytes. There is no rollback and no journal: the files
1200
- * are plain edits a person reverts with git.
1201
- */
1202
532
  type ConfigFilePublishOutcome = {
1203
533
  readonly kind: 'published';
1204
534
  readonly written: readonly string[];
@@ -1217,16 +547,6 @@ type ConfigFilePublishOutcome = {
1217
547
  };
1218
548
  readonly unwritten: readonly string[];
1219
549
  };
1220
- /**
1221
- * The direct config writer (2026-09-30, 432-07 D-05/D-07): generated changes publish in the call
1222
- * that made them, with no proposal and no git.
1223
- *
1224
- * FIELD CHANGES STAY FIELD CHANGES until the host rereads the file in that path's write turn, the
1225
- * turn the dashboard's own autosave takes, so an outside edit to another field is kept and a stale
1226
- * `expect` refuses. Whole files (a hooks file, the managed `.gitignore` block, a scaffold) are
1227
- * checked against the digest they were built from and written one at a time, each atomically.
1228
- * The queue is per process; nothing here claims a cross-process lock.
1229
- */
1230
550
  interface ConfigWritePortLike {
1231
551
  applyFields(request: {
1232
552
  readonly changes: readonly ConfigChange[];
@@ -1235,35 +555,6 @@ interface ConfigWritePortLike {
1235
555
  readonly files: readonly ConfigFilePublication[];
1236
556
  }): Promise<ConfigFilePublishOutcome>;
1237
557
  }
1238
- /**
1239
- * The scaffold's CONTENT, rendered by the host that owns it (395-08, D-04 · CFGM-05).
1240
- *
1241
- * A PORT BECAUSE THE CONTENT LIVES IN `cli` AND `agent` MAY NOT IMPORT IT. D-04's whole claim is
1242
- * that `agent-qa init` and `config.project.scaffold` write ONE substance; the substance is the
1243
- * default config derivation, the yaml comment injection, the hooks template and the managed
1244
- * `.gitignore` block, and all four live beside `init`'s filesystem machinery in
1245
- * `commands/scaffold-content.ts`. Copying any of it here would mint the second scaffold D-04
1246
- * exists to prevent, and the copy would fail silently — a person's proposal and their `init` would
1247
- * simply produce different projects.
1248
- *
1249
- * IT TAKES THE DERIVATION, NOT THE DOMAIN. `targetName` and `baseUrl` are what
1250
- * `parseHostnameToken` produced; handing the raw token over would put the derivation on both sides
1251
- * of the boundary and let the host answer a different one than the gate pinned.
1252
- *
1253
- * `existingGitignore` IS BYTES, ABSENT WHEN THERE IS NO FILE. The block is an UPSERT: a project
1254
- * with a `.gitignore` of its own gets the managed block appended to what it already has, and the
1255
- * proposal's diff shows exactly that. Absent and empty are deliberately different — the first is
1256
- * a file the apply CREATES, the second a file it rewrites.
1257
- *
1258
- * `existingConfig` IS THE SECOND MODE OF THE ONE PRODUCER (395-12, D-04 · CFGM-05), and it is a
1259
- * MODE rather than a second port because D-04's claim is that there is one substance: a second
1260
- * renderer would be exactly the drift D-04 exists to make impossible. Present when the project HAS
1261
- * a configuration that declares no targets — the class SC-5 has to serve, because a project able to
1262
- * hold a conversation at all is one whose `registry.llms` lives in a config file. In that mode the
1263
- * producer inserts exactly `registry.targets.<targetName>` into the person's own bytes through the
1264
- * comment-preserving Document API and emits no other change to that file. Absent means from zero,
1265
- * and the from-zero render is byte-for-byte what it was before this field existed.
1266
- */
1267
558
  interface ConfigScaffoldContentPortLike {
1268
559
  render(input: {
1269
560
  readonly targetName: string;
@@ -1303,101 +594,38 @@ interface ConfigScaffoldMergeTarget {
1303
594
  readonly hooksPresent: boolean;
1304
595
  }
1305
596
 
1306
- /**
1307
- * The abstention coverage record (D-11).
1308
- *
1309
- * `query` is verbatim, and it is why this record is written to a LOCAL append-only log and to
1310
- * nothing else: it is the one free-text value in the feature. SURF-FUT-02 reads the log later to
1311
- * decide whether docs-RAG has demand. An implementation that forwards this to a network sink is
1312
- * the D-14 no-free-text law broken, not a configuration choice.
1313
- */
1314
597
  interface ExplainAbstentionRecord {
1315
598
  readonly query: string;
1316
599
  /** Canonical row ids the deterministic scorer picked — never a model's suggestion. */
1317
600
  readonly closestRowIds: readonly string[];
1318
601
  }
1319
- /**
1320
- * What the `explain` op needs from its host, and the whole of it.
1321
- *
1322
- * Bound at `composeLocalAgent()` (385 D-15) by plan 07. An unbound composition refuses BY NAME
1323
- * through `FunctionNotBoundError` rather than degrading to a model-only answer — an explain that
1324
- * silently loses its catalog is precisely an explain that answers from model memory.
1325
- */
1326
602
  interface CapabilityCatalogPort {
1327
- /**
1328
- * Every catalog row, as data.
1329
- *
1330
- * A PRODUCED zero-row catalog is a valid answer here and must not be an error (398-01's loader
1331
- * makes the same distinction): the renderer turns zero rows into the abstention arm, which is
1332
- * SC-2's "an empty catalog abstains rather than inventing" at the seam where it is decidable.
1333
- */
1334
603
  loadCatalog(): Promise<readonly CatalogRow[]>;
1335
- /**
1336
- * The model's selection over the rows it was handed — row ids only (D-10).
1337
- *
1338
- * Implementations MUST pass `rows` as the grounding data. Anything cited that is not in `rows`
1339
- * is a ghost id, and the renderer answers ghosts with a whole-output abstention, so a confused
1340
- * or injected model degrades to "I am not certain" rather than to a fabricated command.
1341
- */
1342
604
  citeCapabilities(query: string, rows: readonly CatalogRow[]): Promise<ExplainCitation>;
1343
- /** One counter tick for a grounded answer. No payload — D-14 permits a count and nothing more. */
1344
605
  recordAnswered(): void;
1345
- /** Append the coverage record, locally (D-11). The demand signal for writing the missing doc. */
606
+ /** Append the coverage record, locally. The demand signal for writing the missing doc. */
1346
607
  recordAbstained(record: ExplainAbstentionRecord): void;
1347
608
  }
1348
609
 
1349
610
  interface EvaluateDraftPorts {
1350
611
  readonly runs: DraftRunPortLike;
1351
612
  readonly fingerprints: SuiteFingerprintIndexLike;
1352
- /** D-16's evidence-less population. An empty array is a legitimate answer, not an absent port. */
1353
613
  readonly textCorpus: readonly SuiteTextCorpusEntryLike[];
1354
614
  readonly receipts: DraftReceiptsStoreLike;
1355
615
  /** Injected so a receipt's `evaluatedAt` is pinnable in a test. Defaults to the wall clock. */
1356
616
  readonly now?: () => number;
1357
- /**
1358
- * D-14's read side, consulted ONLY when gate 2 flips (394-17 · WTST-05).
1359
- *
1360
- * ONE BUNDLE OF TWO HALVES that are useless apart, on the `AuthoringFunnelPortLike` reasoning
1361
- * one level down. `port` is the funnel's read bundle — which carries NO run capability at all,
1362
- * so the zero-runs law is structural rather than remembered. `product` is the partition the
1363
- * projection is keyed on, and it is a THUNK because the answer is OBSERVED rather than derived:
1364
- * a composition learns it from the first gate-run acknowledgement, and D-04 forbids
1365
- * short-circuiting, so by the time gate 2 can flip at least one run has been acknowledged. It
1366
- * answers `undefined` when nothing was observed, and the consultation is then SKIPPED —
1367
- * deriving a product from configuration instead would be a second rule for a fact the write
1368
- * side already decides, and the first time the two disagreed this message would report another
1369
- * application's flakiness as this target's (T-394-68).
1370
- */
1371
617
  readonly suiteStability?: {
1372
618
  readonly port: AuthoringFunnelPortLike;
1373
619
  product(): string | undefined;
1374
620
  };
1375
621
  }
1376
622
 
1377
- /**
1378
- * The projection's key, spelled the way `run_test_outcomes` is keyed: stable test identity
1379
- * is `(product, filePath, testName)` — NEVER `testId`, a per-run explicit id.
1380
- *
1381
- * 390 D-07 adds the FAILURE FINGERPRINT beside the identity, and the two legs answer two
1382
- * different questions: recurrence is counted per `(product, fingerprint)` — the same failure
1383
- * recurring, not the same test — while the quarantine ladder is keyed on the identity, which
1384
- * is what the ledger records. A member with no failing step to key from (a zero-step failure)
1385
- * omits the leg entirely rather than carrying a stand-in: a fabricated key would window a
1386
- * failure against occurrences that are not its own.
1387
- */
1388
623
  interface RecurrenceQuery {
1389
624
  readonly product: string;
1390
625
  readonly filePath: string;
1391
626
  readonly testName: string;
1392
627
  readonly fingerprint?: string;
1393
628
  }
1394
- /**
1395
- * The ladder rung this identity currently sits on (390 D-06/D-17), when it sits on one at all.
1396
- *
1397
- * `expiresAt` is the ISO deadline the ledger recorded. It is optional because `flagged` has no
1398
- * deadline — flagging is visibility, not suppression — and because the ledger is committed text
1399
- * a human may edit, so a recorded deadline is not guaranteed to parse.
1400
- */
1401
629
  interface RecurrenceLadder {
1402
630
  readonly state: QuarantineLadderState;
1403
631
  readonly expiresAt?: string;
@@ -1409,34 +637,9 @@ interface RecurrenceAnswer {
1409
637
  /** Absent means this identity is on no rung — the clause is the occurrence count alone. */
1410
638
  readonly ladder?: RecurrenceLadder;
1411
639
  }
1412
- /**
1413
- * The D-08 recurrence seam: recurrence context is a LIVE join at render time, resolved per
1414
- * call through this provider — never baked into authored parts and never stored as prose.
1415
- *
1416
- * 389 bound this seam to v51's derived run-outcome projection (`readTestOutcomeHistory` +
1417
- * `failureRecurrence`); 390-06 REBOUND the same seam at the same composition root to the
1418
- * fingerprint-keyed recurrence window plus a per-call fold of the committed quarantine ledger,
1419
- * which is what the ladder arms render. The seam itself did not change shape — the query gained
1420
- * a fingerprint and the answer gained a rung. The author's prompt separately consumes failure
1421
- * history as input-for-REASONING (plan 03's forced channel); this join is the rendered-CLAUSE
1422
- * — two different things by decision.
1423
- *
1424
- * `undefined` means the provider has no history for this identity; the renderer answers
1425
- * with an explicit no-history clause, so obligation c is always rendered, honestly.
1426
- */
1427
640
  interface RecurrenceProvider {
1428
641
  failureRecurrence(query: RecurrenceQuery): RecurrenceAnswer | undefined;
1429
642
  }
1430
- /**
1431
- * Compose one verdict's sentence from its typed parts (D-05).
1432
- *
1433
- * Fixed order: resolved-class frame, the quoted grounding observation with its step ref,
1434
- * the discriminator, the recurrence clause (joined LIVE through `provider`), the
1435
- * recommended next action, and the `agentqa://` links (rendered verbatim from
1436
- * `parts.links`, already codec-validated at the artifact boundary; an empty list renders
1437
- * no see-section). Byte-deterministic: the same (verdict, parts, provider answer) yields
1438
- * the same string in every process.
1439
- */
1440
643
  declare function renderVerdictSentence(verdict: Verdict, parts: VerdictSentenceParts, provider: RecurrenceProvider | undefined, query: RecurrenceQuery): string;
1441
644
 
1442
645
  /** What a handler is handed. Checkpoint publication is a capability, never an import. */
@@ -1452,38 +655,9 @@ interface JobHandlerContext {
1452
655
  */
1453
656
  readonly payload: unknown;
1454
657
  publishCheckpoint(checkpointId: string, value: unknown): Promise<void>;
1455
- /**
1456
- * The ONE path from a job to a model (D-10 / D-11 layer 2).
1457
- *
1458
- * What is absent from this interface is the contract: there is no model member here, and
1459
- * this member's own signature is the only place a capability appears. A handler therefore
1460
- * cannot reach a model except by asking this primitive to record the asking — which is what
1461
- * makes a resumed job's re-invocation impossible by control flow rather than by discipline.
1462
- * The member set of this interface is pinned for exact equality by a test, so an ambient
1463
- * accessor cannot be added quietly beside it.
1464
- */
1465
658
  readonly recordLlmAuthorship: BoundRecordLlmAuthorship;
1466
659
  }
1467
- /**
1468
- * What a handler reports (D-17/D-18).
1469
- *
1470
- * 385 plan 02 replaced this module's own outcome shape with the schema DTO. The two were the
1471
- * same concept described twice, and the local one could not say a handler had FAILED at all —
1472
- * a bare optional successor list makes "finished, chaining nothing" and "gave up" identical.
1473
- * The TOPOLOGY of what may chain to what is still declared here, on the kind; only the
1474
- * INSTANCES come back from the handler, which is what keeps an outcome-dependent chain
1475
- * ordinary data while leaving the whole graph auditable in one place.
1476
- */
1477
660
  type JobHandler = (context: JobHandlerContext) => Promise<JobHandlerOutcome>;
1478
- /**
1479
- * How a kind's retries are spaced (D-18) — DATA, never a function.
1480
- *
1481
- * A callback would let a kind compute its own delay from whatever it liked, including the
1482
- * wall clock, and the engine could then neither describe a kind's retry behaviour without
1483
- * running it nor keep a dump deterministic. As data, the delay for attempt N is a pure
1484
- * function of the policy and N, which is exactly what the driver computes and hands back to
1485
- * whichever host decides when to pump next.
1486
- */
1487
661
  type JobBackoffPolicy = {
1488
662
  readonly kind: 'none';
1489
663
  } | {
@@ -1495,14 +669,6 @@ type JobBackoffPolicy = {
1495
669
  readonly factor: number;
1496
670
  readonly maxDelayMs: number;
1497
671
  };
1498
- /**
1499
- * Everything a fallback continuation is allowed to know.
1500
- *
1501
- * The absences are the contract (D-20 / D-11 layer 2). There is no model capability here and
1502
- * no place to pass one, so a producer cannot invoke a model however it is written — which is
1503
- * what puts the permanent-failure path on the resume-legal side of the queue by construction
1504
- * rather than by review.
1505
- */
1506
672
  interface FallbackContinuationInput {
1507
673
  readonly job: Job;
1508
674
  readonly payload: unknown;
@@ -1516,21 +682,15 @@ interface FallbackContinuationInput {
1516
682
  * that must return its instances immediately can only compute them from what it was handed.
1517
683
  */
1518
684
  type FallbackContinuationProducer = (input: FallbackContinuationInput) => readonly ChainedJobRequest[];
1519
- /**
1520
- * What a kind declares should happen to its chain when it gives up (D-20).
1521
- *
1522
- * `'halt-chain'` enqueues nothing. A producer names the successors the chain proceeds with
1523
- * instead — the analyze-run posture, whose downstream jobs must never wait on a dead head.
1524
- */
1525
685
  type JobPermanentFailureDisposition = 'halt-chain' | FallbackContinuationProducer;
1526
686
  interface JobKindDeclaration {
1527
687
  readonly kind: JobKind;
1528
- /** `false` means the kind resumes NOTHING after a crash — chat's posture (JOBQ-04). */
688
+ /** `false` means the kind resumes NOTHING after a crash — chat's posture. */
1529
689
  readonly checkpointed: boolean;
1530
690
  /** The complete set of checkpoint ids this kind may publish, in schedule order. */
1531
691
  readonly checkpointSchedule: readonly string[];
1532
692
  readonly allowedSuccessors: readonly JobKind[];
1533
- /** Attempts are spent at CLAIM (D-18), so this bound holds even for a job that never returns. */
693
+ /** Attempts are spent at CLAIM, so this bound holds even for a job that never returns. */
1534
694
  readonly maxAttempts: number;
1535
695
  readonly backoff: JobBackoffPolicy;
1536
696
  readonly onPermanentFailure: JobPermanentFailureDisposition;
@@ -1548,7 +708,7 @@ declare class UnknownJobKindError extends Error {
1548
708
  readonly name = "UnknownJobKindError";
1549
709
  constructor(jobKind: string);
1550
710
  }
1551
- /** Thrown at construction when a declaration omits a field the engine reads (D-09). */
711
+ /** Thrown at construction when a declaration omits a field the engine reads. */
1552
712
  declare class IncompleteJobKindDeclarationError extends Error {
1553
713
  readonly jobKind: string;
1554
714
  readonly field: string;
@@ -1581,18 +741,12 @@ declare function createJobKindRegistry(declarations: readonly JobKindDeclaration
1581
741
  interface JobIdSource {
1582
742
  jobIdFor(kind: JobKind, idempotencyKey: string): JobId;
1583
743
  }
1584
- /**
1585
- * Thrown when an enqueue arrives without a usable idempotency key (D-08).
1586
- *
1587
- * The type already requires the argument; this covers the empty string, which types cannot
1588
- * exclude and which would silently collapse every keyless caller onto one queue slot.
1589
- */
1590
744
  declare class MissingIdempotencyKeyError extends Error {
1591
745
  readonly jobKind: JobKind;
1592
746
  readonly name = "MissingIdempotencyKeyError";
1593
747
  constructor(jobKind: JobKind);
1594
748
  }
1595
- /** Thrown when a handler asks to chain to a successor its kind never declared (D-17). */
749
+ /** Thrown when a handler asks to chain to a successor its kind never declared. */
1596
750
  declare class UndeclaredJobSuccessorError extends Error {
1597
751
  readonly jobKind: JobKind;
1598
752
  readonly successorKind: JobKind;
@@ -1606,16 +760,6 @@ declare class UndeclaredJobCheckpointError extends Error {
1606
760
  readonly name = "UndeclaredJobCheckpointError";
1607
761
  constructor(jobKind: JobKind, checkpointId: string);
1608
762
  }
1609
- /**
1610
- * Raised at a checkpoint boundary when the job being run has been asked to stop (D-19).
1611
- *
1612
- * A thrown sentinel rather than a return value, because the boundary is inside a capability
1613
- * the handler awaits and there is no other way to unwind a handler from underneath. A handler
1614
- * MAY catch it — cooperative cancellation is cooperative — but catching it buys nothing: the
1615
- * next boundary raises it again, and a kind whose handler swallowed every one of them would
1616
- * simply run to the end, which is the same thing that happens to a kind with no boundary at
1617
- * all. The driver catches it, records the cancellation and reports the boundary it stopped at.
1618
- */
1619
763
  declare class JobCancelledError extends Error {
1620
764
  readonly jobId: JobId;
1621
765
  readonly checkpointId: string;
@@ -1627,26 +771,7 @@ interface CreateAgentJobQueueOptions {
1627
771
  readonly registry: JobKindRegistry;
1628
772
  readonly clock: ClockPort;
1629
773
  readonly idSource: JobIdSource;
1630
- /**
1631
- * The model a job may author with, bound by the composition root (D-10).
1632
- *
1633
- * REQUIRED, and deliberately not defaultable. An optional capability would mean a queue
1634
- * could be built with none, and the only sensible behaviour for an author callback that
1635
- * cannot reach a model is to fail somewhere far from the composition that forgot it. Making
1636
- * it an argument of construction puts that decision where the binding is made. The engine
1637
- * never calls it: the only thing that happens to this value is that the authorship
1638
- * primitive hands it to an author callback, carrying the job's signal when a host passed one.
1639
- */
1640
774
  readonly model: JobModelCapability;
1641
- /**
1642
- * The kinds this host may never claim, fixed when its queue is composed (388-09).
1643
- *
1644
- * A CONSTRUCTION option, deliberately not a member of `RunNextJobInput`: whether a host
1645
- * may claim a kind is a fact about the HOST — which capabilities its composition can bind
1646
- * — and putting it on the per-call input would make it a decision every pump site
1647
- * re-makes, with every pump site a place the answers could diverge. Absent means what it
1648
- * means today: every declared kind is this host's to take.
1649
- */
1650
775
  readonly unclaimableKinds?: readonly JobKind[];
1651
776
  }
1652
777
  interface RunNextJobInput {
@@ -1659,18 +784,7 @@ interface RanJobOutcomeBase {
1659
784
  /** The job AS CLAIMED. The store owns what its row says afterwards. */
1660
785
  readonly job: Job;
1661
786
  readonly checkpointsPublished: readonly string[];
1662
- /**
1663
- * `false` in exactly one case: the attempt bound was already spent when the job was
1664
- * claimed, so the driver failed it without handing it to its handler (D-18).
1665
- */
1666
787
  readonly handlerInvoked: boolean;
1667
- /**
1668
- * The payload of the job AS CLAIMED, so a host can attribute this outcome to whatever the
1669
- * payload names — a run, above all — without going back to the store for a row the job has
1670
- * already moved past (406 D-16). Optional because the outcome is the engine's fact and the
1671
- * payload is the caller's: nothing here reads it, and no fabricated outcome must grow a
1672
- * field to keep compiling.
1673
- */
1674
788
  readonly payload?: unknown;
1675
789
  }
1676
790
  type RunNextJobOutcome = {
@@ -1682,15 +796,6 @@ type RunNextJobOutcome = {
1682
796
  }) | (RanJobOutcomeBase & {
1683
797
  readonly disposition: 'retry-scheduled';
1684
798
  readonly reason: string;
1685
- /**
1686
- * How long a host should wait before pumping this job again, computed from the kind's
1687
- * declared backoff policy and the attempt just spent.
1688
- *
1689
- * Returned rather than applied: 385 ships no dispatcher, and a driver that slept on its
1690
- * own would be the timer loop this engine deliberately is not. The same value goes to the
1691
- * store as the disposition's `retryAfterMs`, which stores the row's not-before time from
1692
- * its own clock and keeps the row out of every claim until then (444.1 D-23).
1693
- */
1694
799
  readonly retryDelayMs: number;
1695
800
  readonly attemptsRemaining: number;
1696
801
  }) | (RanJobOutcomeBase & {
@@ -1731,14 +836,6 @@ interface AgentJobQueue {
1731
836
  enqueue(kind: JobKind, payload: unknown, idempotencyKey: string): Promise<JobEnqueueOutcome>;
1732
837
  /** Claim, run, checkpoint and dispose of exactly one job. Returns when that job is done. */
1733
838
  runNextJob(input: RunNextJobInput): Promise<RunNextJobOutcome>;
1734
- /**
1735
- * Ask for one job to stop (D-19).
1736
- *
1737
- * Built with the driver rather than after it. The place a cooperative cancel is honoured is
1738
- * the seam between declared checkpoints, and that seam only exists inside `runNextJob` —
1739
- * adding the check later would mean reopening engine core with handlers already written
1740
- * against it. 385 exposes this on no surface at all; the phases that own job UX do that.
1741
- */
1742
839
  cancel(input: CancelJobInput): Promise<JobCancelOutcome>;
1743
840
  /**
1744
841
  * Return every outstanding claim to the queue, keeping the attempt each one spent.
@@ -1756,11 +853,10 @@ interface AgentJobQueue {
1756
853
  }
1757
854
  declare function createAgentJobQueue(options: CreateAgentJobQueueOptions): AgentJobQueue;
1758
855
 
1759
- /** The three job ids one run's chain addresses, whether or not any of them exists. */
856
+ /** The job ids one run's chain addresses, whether or not any of them exists. */
1760
857
  interface ChainJobIds {
1761
858
  readonly analyzeRun: JobId;
1762
859
  readonly curateMemory: JobId;
1763
- readonly proposeImprovements: JobId;
1764
860
  }
1765
861
  /**
1766
862
  * Every id the chain for `runId` would carry, from `runId` and the id source alone.
@@ -1769,31 +865,10 @@ interface ChainJobIds {
1769
865
  * — because they ARE the same keys: this function predicts what that function will ask for.
1770
866
  * If the two ever disagree, the projection addresses rows that exist under other ids, which
1771
867
  * is the failure the shared derivation exists to make impossible.
1772
- *
1773
- * The third id is addressed from the SECOND, not from the run: chaining is transitive and each
1774
- * key is built from its own predecessor, so a chain whose head id source changed moves all
1775
- * three together rather than leaving the tail addressing a row nobody minted (422 D-13). It is
1776
- * deliberately not keyed by candidate: candidates are discovered by the fold and a chain whose
1777
- * identity depended on them could not be predicted before the fold ran.
1778
868
  */
1779
869
  declare function chainJobIdsForRun(runId: string, idSource: JobIdSource): ChainJobIds;
1780
870
 
1781
- /**
1782
- * How an op returns its result (D-08).
1783
- *
1784
- * `unary` resolves ONE value, validated against the spec's `output` contract.
1785
- * `stream` resolves a {@link FunctionStream} whose elements each honour that same
1786
- * `output` contract — the contract describes one element, never the iterable.
1787
- */
1788
871
  type FunctionResultKind = 'unary' | 'stream';
1789
- /**
1790
- * A stream op's result (D-08): a typed async iterable of seq-stamped elements.
1791
- *
1792
- * Connection-independent by construction — every element carries its own resume token, so
1793
- * a caller that observed token N re-invokes with that token and receives N + 1 onward.
1794
- * Nothing about the iterable is bound to the transport that produced it, which is why
1795
- * 386's live attach and this phase's completed-run replay are the SAME contract.
1796
- */
1797
872
  type FunctionStream<Element = unknown> = AsyncIterable<Element>;
1798
873
  /**
1799
874
  * One replayed run event, defined AS the element type the runner's `EventLogPort.read`
@@ -1801,17 +876,6 @@ type FunctionStream<Element = unknown> = AsyncIterable<Element>;
1801
876
  * layer cannot drift out of step with the log the runner actually writes.
1802
877
  */
1803
878
  type ReplayedRunEvent = ReturnType<EventLogPort['read']>[number];
1804
- /**
1805
- * Whether the op's implementation is bound in THIS composition (D-09). A declared-but-
1806
- * unbound op is a visible state, never a silent gap: coverage checks must treat
1807
- * `unbound` as covered-but-unbound rather than as an absent capability.
1808
- *
1809
- * It is a property of a COMPOSITION, not of a spec, and since 385 (D-16) it is computed
1810
- * that way: the field lives on {@link FunctionDeclaration} — which a registry mints from
1811
- * the deps it was handed — and on no spec. A spec that carried a static `'bound'` would be
1812
- * making a claim about compositions it is not part of, and the first composition that
1813
- * omitted the implementation would report `bound` and lie. There is no literal to flip.
1814
- */
1815
879
  type FunctionBindingStatus = 'bound' | 'unbound';
1816
880
  /**
1817
881
  * The OPTIONAL members of {@link FunctionDeps} — the deps whose presence varies by host.
@@ -1822,88 +886,19 @@ type FunctionBindingStatus = 'bound' | 'unbound';
1822
886
  * compositions can run this op" is answerable by reading two lines.
1823
887
  */
1824
888
  type OptionalFunctionDepKey = 'liveView' | 'runInvocation' | 'knowledgeReview' | 'postProcessing' | 'runDetail' | 'evidenceBlobs' | 'analysis' | 'recurrence' | 'quarantine' | 'chatTurn' | 'draftEvaluation' | 'authoringRead' | 'authoringScope' | 'draftShelf' | 'authoringFunnel' | 'configFiles' | 'configValidate' | 'scaffoldContent' | 'capabilityCatalog' | 'feedback' | 'feedbackReads' | 'configWrite' | 'secretMask';
1825
- /**
1826
- * 419 GATE3-01: the union previously had only a type-level pin, invisible to vitest.
1827
- * Like DELTA_ORIGINS and MARKS, this runtime projection makes removal measurable.
1828
- */
1829
889
  declare const OPTIONAL_FUNCTION_DEP_KEYS: readonly ["liveView", "runInvocation", "knowledgeReview", "postProcessing", "runDetail", "evidenceBlobs", "analysis", "recurrence", "quarantine", "chatTurn", "draftEvaluation", "authoringRead", "authoringScope", "draftShelf", "authoringFunnel", "configFiles", "configValidate", "scaffoldContent", "capabilityCatalog", "feedback", "feedbackReads", "configWrite", "secretMask"];
1830
- /**
1831
- * The capability is declared, but no implementation is bound in THIS composition (D-09).
1832
- *
1833
- * Deliberately distinct from `UnknownFunctionError`: the contract exists, is listed, and
1834
- * projects into a catalog — what is absent is the binding. A caller (or a coverage check)
1835
- * that could not tell those two apart would read a staged capability as a hole in the API,
1836
- * which is the silent gap `bindingStatus` exists to prevent.
1837
- *
1838
- * Declared here beside {@link FunctionBindingStatus} rather than beside the pipeline's
1839
- * other refusals so an unbound op can throw it without an op → invoke → registry → op
1840
- * import cycle; `invoke.ts` re-exports it, so the layer's refusal vocabulary still reads
1841
- * as one set at the call site.
1842
- */
1843
890
  declare class FunctionNotBoundError extends Error {
1844
891
  readonly functionId: string;
1845
892
  constructor(functionId: string);
1846
893
  }
1847
- /**
1848
- * The scalar metadata every capability carries, shared by the public view and the
1849
- * internal spec.
1850
- *
1851
- * `id` is dot-namespaced `domain.verb` (D-15) over ONE canonical domain taxonomy.
1852
- *
1853
- * 2026-08-16 (397 D-18): this docstring used to repeat D-15's original eight domains
1854
- * (tests · runs · memory · analysis · eval · gates · config · agent), and six later phases
1855
- * shipped ids in domains that list does not contain — so the sentence had been false for a
1856
- * month by the time anyone checked it. The taxonomy is now DATA rather than prose, pinned in
1857
- * the conformance suite's `domain-list.ts` with a one-line charter apiece and censused
1858
- * against this registry in both directions. An id in an unpinned domain is a named RED
1859
- * there; widening the taxonomy is a reviewed one-line diff to that table. Ids are never
1860
- * renamed to suit it — they are the shipped, locked contract, and the taxonomy serves them.
1861
- *
1862
- * The two CONTRACT fields are deliberately NOT here. They are the one place the view and
1863
- * the spec must differ in kind — a projection on one, a live validator on the other — and
1864
- * splitting them out is what makes that difference a property of the type system rather
1865
- * than a rule someone has to remember at the seam (BL-01 / T-384-08).
1866
- */
1867
894
  interface FunctionMetadata {
1868
895
  id: string;
1869
896
  /** One-line human description; rendered by catalogs and `agent-qa fn --list`. */
1870
897
  summary: string;
1871
898
  resultKind: FunctionResultKind;
1872
- /** The risk descriptor of this op: listings, MCP tool annotations and the read scope read it (no gate since 432-13). */
1873
899
  risk: RiskActionDescriptor;
1874
900
  }
1875
- /**
1876
- * An INERT description of one declared contract: a JSON-Schema document, deep-frozen.
1877
- *
1878
- * A projection has no `parse` and no `safeParse` — there is nothing on it to run and
1879
- * nothing on it to replace. That is the whole point. The surface-visible view used to
1880
- * carry the live `z.ZodType`, and because ops assign the schema package's own exported
1881
- * validators, a single write reached through a permitted `registry.list()` replaced the
1882
- * contract vocabulary every firewalled surface imports, for the whole process. Publishing
1883
- * a projection removes the object that write needed rather than adding a rule about it.
1884
- *
1885
- * D-14 already named the input contract "also the JSON-Schema projection source"; on the
1886
- * declaration that description is now literal, and it is the shape 396's MCP tool listing
1887
- * consumes.
1888
- */
1889
901
  type FunctionSchemaProjection = Readonly<Record<string, unknown>>;
1890
- /**
1891
- * What one clause's result MEANT, as the op that produced it reports (Phase 399.1, D-01/D-02/D-03).
1892
- *
1893
- * TWO FIELDS AND NO THIRD. `status` is the one bit a surface must not get wrong — whether the thing
1894
- * was carried out — and `outcome` is the sentence that says which road it came in by. Anything
1895
- * larger would be a second payload channel, and the payload has one (`results[]`).
1896
- *
1897
- * `not-run` IS ABSENT FROM THE STATUS SET, AND ITS ABSENCE IS THE POINT. That member describes a
1898
- * clause the ENGINE deliberately never attempted after an earlier one failed — a fact about the
1899
- * turn, which no op can know and none may claim. The three a classifier may return are derived from
1900
- * the schema's own vocabulary rather than re-spelled, so a fifth receipt status cannot appear here
1901
- * without somebody deciding whether an op may return it.
1902
- *
1903
- * THE `failed` MEMBER IS WHAT MAKES THIS FAIL-CLOSED POSSIBLE (D-03). A result the declared fold
1904
- * cannot read reports `failed` with a named outcome, never `executed`: an unreadable result might be
1905
- * a refusal, so success is the one claim a receipt must never default to.
1906
- */
1907
902
  interface ClauseVerdict {
1908
903
  readonly status: Exclude<ClauseReceiptStatus, 'not-run'>;
1909
904
  /**
@@ -1923,52 +918,10 @@ interface ClauseVerdict {
1923
918
  * FunctionSpec} instead, which never leaves this directory.
1924
919
  */
1925
920
  interface FunctionDeclaration extends FunctionMetadata {
1926
- /**
1927
- * Whether THIS composition can run the op (D-09/D-16).
1928
- *
1929
- * Computed by the registry from the deps it was constructed with, never copied off a
1930
- * spec. A listing is therefore a fact about the host a caller is talking to: the same
1931
- * capability reads `bound` from a composition that supplied its implementation and
1932
- * `unbound` from one that did not, and `invokeFunction` refuses on exactly that answer.
1933
- */
1934
921
  bindingStatus: FunctionBindingStatus;
1935
- /**
1936
- * This op folds its OWN result into a clause verdict (2026-08-20, Phase 399.1 D-01).
1937
- *
1938
- * PRESENCE, AS DATA — never the callable: {@link FunctionSpec.classify} stays where it is, and a
1939
- * function-valued field on {@link FunctionMetadata} would put something runnable back on every
1940
- * listing (BL-01).
1941
- *
1942
- * WHAT THE PROJECTION IS FOR, AND WHY IT IS NOT DECORATION. D-01 puts refusal knowledge in a
1943
- * per-op fold rather than in a chat-side rule that pattern-matches values, because a rule that
1944
- * can only refuse what it can NAME cannot be driven to zero by naming more things. The cost of
1945
- * that choice is coverage: an op that never declared a fold reports the legacy `executed` for
1946
- * every result it can return, silently. This boolean is what makes that countable — the census in
1947
- * `packages/cli` cannot reach a spec by construction, so without it the coverage question could
1948
- * only be answered by somebody reading forty-three op files.
1949
- *
1950
- * `true` is the only value: `false` and an absent key would be two spellings of one fact, and a
1951
- * present `undefined` would make a golden column read `null` for an op that simply has no fold.
1952
- */
1953
922
  classifies?: true;
1954
- /**
1955
- * This op composes its own human-facing lines on top of the default payload render (399.1 D-04).
1956
- *
1957
- * PRESENCE, AS DATA, on the two flags above. The fold ADDS legibility and can never suppress: the
1958
- * default render is the complete payload and a declared fold is printed beside it, never instead
1959
- * of it. That is a property of the render path rather than of this flag, and the flag is here so
1960
- * a listing — and the golden ledger — can say which ops carry one.
1961
- */
1962
923
  rendersResult?: true;
1963
- /** Inert JSON-Schema projection of the runtime input contract (D-14). */
1964
924
  input: FunctionSchemaProjection;
1965
- /**
1966
- * Inert JSON-Schema projection of the runtime output contract (D-14).
1967
- *
1968
- * For a `unary` op the underlying contract describes the whole result. For a `stream`
1969
- * op it describes ONE element, never the iterable, and a catalog reads this projection
1970
- * as the element schema its transport (SSE frame, MCP notification) carries.
1971
- */
1972
925
  output: FunctionSchemaProjection;
1973
926
  }
1974
927
  /**
@@ -1981,45 +934,9 @@ interface FunctionDeclaration extends FunctionMetadata {
1981
934
  interface RunsReaderPort {
1982
935
  /** Every completed run this project can load. Missing run storage yields `[]`. */
1983
936
  list(): Promise<readonly CompletedRunHandoff[]>;
1984
- /**
1985
- * One completed run by id, or `undefined` when no such run is recorded.
1986
- *
1987
- * Absence is a RESULT, not an error: the typed refusal is the op's to throw, so the
1988
- * "not found" vocabulary lives in this layer rather than in every composition root.
1989
- * Implementations resolve the id against the runs they already enumerate — never by
1990
- * joining a caller-supplied string onto a filesystem path (T-384-08).
1991
- */
1992
937
  get(runId: RunId): Promise<CompletedRunHandoff | undefined>;
1993
- /**
1994
- * Replay one run's recorded event stream, starting at `fromSeq` and in `seq` order.
1995
- *
1996
- * The exact `EventLogPort.read(fromSeq)` contract the v49 bus already replays against,
1997
- * lifted behind a port so this layer needs no host access. A run with no recorded
1998
- * events replays as an ordinary empty result — nothing was truncated, so nothing is
1999
- * refused. When events below `fromSeq` existed but are no longer available, the
2000
- * implementation rejects with the runner's `ReplayIncompleteError` carrying the lowest
2001
- * seq it can still serve; the op turns that into the layer's typed refusal. Serving a
2002
- * silently truncated prefix is the one thing an implementation may never do.
2003
- */
2004
938
  readEvents(runId: RunId, fromSeq: number): Promise<readonly ReplayedRunEvent[]>;
2005
- /**
2006
- * Read back ONE step's persisted evidence — the substrate `knowledge.brief.why` answers
2007
- * from (Phase 387, KREV-02 / D-15).
2008
- *
2009
- * On the RUNS port and not on the knowledge port, and that placement is the whole honesty
2010
- * argument rather than filing. A step's evidence is a record of what happened during a run;
2011
- * the knowledge index is a record of what is true now. Answering "why did the brief say
2012
- * this" out of the second would answer a different question than the one asked, and the op
2013
- * that answers it is given no serving capability at all, so re-running retrieval is not a
2014
- * temptation it has to resist.
2015
- */
2016
939
  readStepEvidence(runId: RunId, address: StepEvidenceAddress): Promise<StepEvidenceLookup>;
2017
- /**
2018
- * The target names one completed run recorded WHEN IT RAN (431 D-08), never current
2019
- * configuration. `unknown` when the run has no readable snapshot, including one that vanished
2020
- * after `list()` answered. Optional: an unbound reader leaves every run unknown, and an unknown
2021
- * run never matches a requested target. Implementations resolve only runs they enumerate.
2022
- */
2023
940
  getRecordedTargets?(runId: RunId): Promise<RecordedTargets>;
2024
941
  }
2025
942
  /**
@@ -2063,30 +980,11 @@ type StepEvidenceLookup = {
2063
980
  * writer-gated main entry is unreachable from this layer by construction.
2064
981
  */
2065
982
  interface KnowledgeReaderPort {
2066
- /**
2067
- * 2026-09-06, 416 D-17: gate history and the current verdict. Optional so existing
2068
- * implementations and fakes remain type-valid; an unbound read refuses by name.
2069
- */
2070
983
  gateReceipts?(input: {
2071
984
  candidateId: string;
2072
985
  }): Promise<KnowledgeGatesListResult>;
2073
- /**
2074
- * 2026-09-07, 417 D-01: the DATA the Gate-1 bridge carries in. Optional so existing
2075
- * implementations and fakes stay type-valid; an unbound read refuses by name.
2076
- */
2077
- candidateInjection?(input: {
2078
- candidateId: string;
2079
- }): Promise<KnowledgeCandidateInjectionResult>;
2080
986
  /** Every committed bundle, already projected to the schema-owned listing row. */
2081
987
  listBundles(): Promise<readonly KnowledgeBundleSummary[]>;
2082
- /**
2083
- * The maintenance view of every record matching a closed structural filter (KREV-01).
2084
- *
2085
- * Wiring over the reader entry's own `inspectRecords`, and the return type says so: it is
2086
- * that function's view schema and not a paraphrase of it. KREV-01's "no new memidex code"
2087
- * claim survives only while that stays literally true, and the first field of a paraphrase
2088
- * to drift would be the one nobody looked at.
2089
- */
2090
988
  inspectRecords(input: KnowledgeRecordsInspectInput): Promise<readonly InspectRecordView[]>;
2091
989
  /** Every record change since a build ordinal — the reader entry's `diffSince`, wired. */
2092
990
  diffSince(input: KnowledgeRecordsDiffInput): Promise<readonly RecordChange[]>;
@@ -2108,36 +1006,6 @@ interface KnowledgeReaderPort {
2108
1006
  showRecords?(input: {
2109
1007
  recordIds: readonly string[];
2110
1008
  }): Promise<readonly KnowledgeRecordShowResult[]>;
2111
- /**
2112
- * What became of the canary lines THIS run served (2026-08-29, Phase 407 · D-08).
2113
- *
2114
- * ONE ROW PER TUPLE ASKED ABOUT, in the order asked. The caller supplies the served-canary
2115
- * tuples it already holds from the run's own evidence — `candidateId` is the served channel-1
2116
- * line's id, which IS the curation candidate id — and the reader answers with the observation
2117
- * id each tuple's outcome event would carry, plus that outcome when a surviving attributed
2118
- * event exists. `outcome` ABSENT means "no attributed event survives for this tuple", which is
2119
- * an ordinary state (curation has not settled, or it settled and attributed nothing) and NOT
2120
- * an error; `observationId` is always present because it is DERIVED from the tuple —
2121
- * `canaryOutcomeObservationId({ candidateId, runId, testId, testIndex, stepIndex })` — rather
2122
- * than looked up, so a caller can name the event it is waiting for before that event exists.
2123
- *
2124
- * ON THE READER PORT AND NOT THE RUNS PORT, unlike `readStepEvidence` one interface up: the
2125
- * SERVING half of a canary (the marker) is a fact about the run and is read from its evidence,
2126
- * but the ATTRIBUTED half is a fact about the committed knowledge store's outcomes log. Asking
2127
- * the runs port for it would be asking a record of what happened for a record of what is true.
2128
- *
2129
- * OPTIONAL, so every existing port implementation and every test fake stays type-valid. The
2130
- * `/reader` entry implements it in 407-04 and agent-local binds it there; until then a caller
2131
- * sees `undefined` and renders the honest not-yet-attributed state rather than a wrong badge.
2132
- *
2133
- * THE ANSWER CARRIES ITS OWN PARTIALITY (407-19, REVIEW WR-03). `skippedBundles` is the walk's
2134
- * count of bundles it could not load — corrupt, truncated, or failing the integrity check that
2135
- * catches tampering. `> 0` means the answer is PARTIAL, and a caller must not render an absent
2136
- * `outcome` as a positive `not attributed`: on a partial walk that absence means "not found in
2137
- * the part of the store I could read", which is a different sentence. The count rides HERE, on
2138
- * the read that degraded, rather than being inferred from a sibling read failing on the same
2139
- * file — an inference that does not survive the two reads seeing different stores.
2140
- */
2141
1009
  canaryAttributions?(input: {
2142
1010
  runId: string;
2143
1011
  tuples: readonly {
@@ -2157,45 +1025,9 @@ interface KnowledgeReaderPort {
2157
1025
  readonly skippedBundles: number;
2158
1026
  }>;
2159
1027
  }
2160
- /**
2161
- * Injected WRITE capability over knowledge review — accept, reject and forget (Phase 387,
2162
- * KREV-01 / D-17, declared here and implemented in 387-08; direct since 432-12).
2163
- *
2164
- * A SEPARATE port from {@link KnowledgeReaderPort}, and separate is the point. Widening the
2165
- * reader with a write member would have handed a write path to every composition that only
2166
- * ever wanted to list bundles — including the read-only surfaces the physically-separate
2167
- * reader entry exists to keep write-free — and no lint could have told the two apart
2168
- * afterwards, because they would have been one type. The split is what keeps "this host can
2169
- * read knowledge" and "this host can change it" two different, separately answerable
2170
- * questions.
2171
- *
2172
- * OPTIONAL on {@link FunctionDeps} and joined to {@link OptionalFunctionDepKey} for the same
2173
- * reason `runInvocation` is: a host that cannot mutate knowledge — a listing registry, a
2174
- * read-only surface — must report that as a VISIBLE `unbound` on the verbs by name, not as a call
2175
- * that fails somewhere far away.
2176
- *
2177
- * 2026-09-30, 432-12 (D-04/D-05/D-09): `change` applies an explicit accept, reject or forget in the
2178
- * call, through the explicit writer, in every execution context, under the writer lease. It is the
2179
- * member every change op reaches. 2026-10-01 (432-17): `propose` and `apply` are deleted with the
2180
- * proposal inbox; old proposal files are never read again (D-22).
2181
- */
2182
1028
  interface KnowledgeReviewPort {
2183
- /**
2184
- * Apply one explicit knowledge change in the call and answer what it changed (432-12).
2185
- *
2186
- * Validation, target resolution, the writer lease, delegation to a live holder, delta
2187
- * validation, bundle egress and every promotion gate belong to the writer behind this member.
2188
- * A repeated `operationId` is the same change and lands once; an absent one is minted.
2189
- */
2190
1029
  change(request: KnowledgeChangeRequest): Promise<KnowledgeChangeOutput>;
2191
1030
  }
2192
- /**
2193
- * One explicit knowledge change (432-12): the verb, the input that verb takes, and the caller's
2194
- * stable identity for it when the caller has one.
2195
- *
2196
- * Discriminated so `reject`'s required reason cannot be omitted by asking for an accept. A mark is
2197
- * not here: it changes knowledge through `runs.mark`, which owns its own identity and checkpoints.
2198
- */
2199
1031
  type KnowledgeChangeRequest = ({
2200
1032
  readonly verb: 'accept';
2201
1033
  readonly input: KnowledgeAcceptInput;
@@ -2208,90 +1040,29 @@ type KnowledgeChangeRequest = ({
2208
1040
  }) & {
2209
1041
  readonly operationId?: string;
2210
1042
  };
2211
- /**
2212
- * Injected read port over the entity directory (Phase 387, ENTY-01 / D-09).
2213
- *
2214
- * Two verbs, and the split is the never-guess contract expressed as a type. `resolve` takes
2215
- * anything a human or a model might say and answers with a closed three-arm union, so a
2216
- * caller is structurally unable to receive a maybe. `get` takes an address that already
2217
- * exists and answers with the entity or with nothing.
2218
- *
2219
- * Descriptor-level on purpose. The port carries FACTS about an entity and never a card,
2220
- * which is what leaves exactly one card producer in this layer (ENTY-02): a composition
2221
- * cannot render, because it is handed no vocabulary in which to do so.
2222
- *
2223
- * `get` may not mint. `resolve` sights, and sighting is what assigns a permanent address
2224
- * (D-01); a `get` that could write would make every card render on every surface a
2225
- * potential mutation of the one durable store this system never rebuilds.
2226
- */
2227
1043
  interface EntityDirectoryPort {
2228
1044
  resolve(ref: string): Promise<EntityResolution>;
2229
1045
  get(ref: string): Promise<EntityDescriptor | undefined>;
2230
1046
  }
2231
- /**
2232
- * One post-run chain job as the chip projection needs to see it (Phase 388, D-14).
2233
- *
2234
- * Status and claim drive the chips; optional recorded facts drive the stage detail (423 D-12).
2235
- * The claim remains separate from status, so running does not widen the job vocabulary.
2236
- * Missing facts stay absent, including on older reader implementations.
2237
- */
2238
1047
  interface PostProcessingJobRow extends Readonly<PostProcessingStageDetail> {
2239
1048
  readonly status: JobStatus;
2240
1049
  /** Whether a drainer holds this job right now. `false` covers queued and terminal alike. */
2241
1050
  readonly claimed: boolean;
2242
1051
  }
2243
- /**
2244
- * What one run's post-run chain has durably recorded, by member (Phase 388, D-14; narrowed to
2245
- * the two chip stages — Phase 404, D-01, 2026-08-26).
2246
- *
2247
- * Every member is OPTIONAL, and each absence is a real, different situation: no analyze-run row
2248
- * means nothing was ever enqueued for this run; no curate-memory row means the analysis has not
2249
- * chained yet. Padding either of them with a synthetic row would erase exactly the distinction
2250
- * the projection reads.
2251
- *
2252
- * 423 D-12 reports all three durable stage rows. Proposals contributes detail only;
2253
- * the chip set remains analysis and curation.
2254
- *
2255
- * 2026-09-01 (Phase 409): the chain's third derived id left with the Issue-slot cleanup.
2256
- * 2026-09-11 (Phase 422, D-13): a third derived id returns, without a third chip.
2257
- */
2258
1052
  interface PostProcessingChainState {
2259
1053
  readonly analyzeRun?: PostProcessingJobRow;
2260
1054
  readonly curateMemory?: PostProcessingJobRow;
2261
- readonly proposeImprovements?: PostProcessingJobRow;
2262
- /** Completed curation facts, available before the proposals stage reaches terminal. */
1055
+ /** Completed curation facts, available before the chain terminal is written. */
2263
1056
  readonly curationSnapshot?: CurationSnapshot;
2264
- /** The chain-terminal record, once the terminal transaction wrote it (D-09). */
2265
1057
  readonly notification?: RunNotification;
2266
1058
  }
2267
- /**
2268
- * Injected read port over the durable post-run chain state (Phase 388, D-14).
2269
- *
2270
- * OPTIONAL on {@link FunctionDeps}, so a host with no queue storage lists `runs.postProcessing`
2271
- * as `unbound` BY NAME rather than answering a confident all-pending about a chain it has no
2272
- * way to see.
2273
- *
2274
- * The port carries the host's `idSource` rather than resolving ids itself, and that placement
2275
- * is the load-bearing part. Chain identity is DERIVED (RESEARCH Pattern 2), and it must be
2276
- * derived through the same source the host's queue enqueued with — a reader that owned its own
2277
- * derivation would address rows under other ids the moment a composition configured a different
2278
- * source, and would report `pending` about a chain that had already finished. So the function
2279
- * layer derives, once, through `chainJobIdsForRun`, and the port only reads what it is handed.
2280
- *
2281
- * Reads are fence-free by design: reading claims no authority, so a status query neither adopts
2282
- * an epoch nor competes with the drainer that holds one.
2283
- */
2284
1059
  interface PostProcessingReaderPort {
2285
1060
  /** The id source THIS host's queue derives job ids with. */
2286
1061
  readonly idSource: JobIdSource;
2287
1062
  /**
2288
1063
  * Durable state for the ids given, or `undefined` when this project holds no queue database
2289
1064
  * at all. Chain identity is still the job-kind layer's to derive, and this port only reads
2290
- * what it is handed: all three ids, with the proposals row used only for detail (423 D-12).
2291
- * It may not derive an id of its own.
2292
- *
2293
- * 2026-09-01 (Phase 409): the width dropped from three to two.
2294
- * 2026-09-11 (Phase 422, D-13): three again, and no longer equal to the state's member count.
1065
+ * what it is handed, and it may not derive an id of its own.
2295
1066
  *
2296
1067
  * `undefined` is a distinct answer from an empty state and both are honest: the first says
2297
1068
  * nothing has ever been enqueued here, the second says this chain's rows are not among the
@@ -2300,64 +1071,13 @@ interface PostProcessingReaderPort {
2300
1071
  */
2301
1072
  readChain(ids: ChainJobIds, runId: string): Promise<PostProcessingChainState | undefined>;
2302
1073
  }
2303
- /**
2304
- * Injected read port over ONE run's durable dashboard projection (Phase 405, D-03).
2305
- *
2306
- * `packages/agent` is `storageNeutral`: no host builtin is importable here, so every row arrives
2307
- * through this port, built at the composition root over the concrete dashboard SQLite database.
2308
- * The port answers ROWS — members, steps, sub-actions — and nothing derived; the D-06 token sum is
2309
- * the op's, computed once in the layer that owns the contract rather than once per host that
2310
- * happens to hold a database.
2311
- *
2312
- * ABSENCE IS A RESULT, NEVER A PATH JOIN ON CALLER INPUT (the `RunsReaderPort.get` law, T-384-08).
2313
- * A run id that names nothing answers `undefined`, and turning that into the layer's typed refusal
2314
- * is `runs.detail`'s job — so the "not found" vocabulary lives here in one place rather than in
2315
- * every composition root. An implementation resolves the id against the rows it already
2316
- * enumerates; joining a caller-supplied string onto a filesystem path is the one thing it may
2317
- * never do.
2318
- *
2319
- * OPTIONAL on {@link FunctionDeps} for the reason `postProcessing` is: a host with no dashboard
2320
- * database genuinely cannot say what a run's steps were, and the alternative to reporting that by
2321
- * name is answering an empty projection about a run it has never been able to see — a wrong answer
2322
- * wearing a correct one's clothes.
2323
- */
2324
1074
  interface RunDetailReaderPort {
2325
1075
  /** One run's projected rows, or `undefined` when the runs database holds no such run or does not exist yet. */
2326
1076
  read(runId: RunId): Promise<RunDetailRows | undefined>;
2327
- /**
2328
- * 2026-10-18 (444-12, D-14): the run's own row status, or `undefined` when no row is recorded.
2329
- * Optional: a host without it derives no `no-verdict` fact, because absence is not a status.
2330
- */
2331
1077
  readRecord?(runId: RunId): Promise<{
2332
1078
  status: string;
2333
1079
  } | undefined>;
2334
1080
  }
2335
- /**
2336
- * Injected read port over ONE run's content-addressed evidence blobs (Phase 405, D-12/D-13).
2337
- *
2338
- * `packages/agent` is `storageNeutral`: no filesystem is importable here, so the bytes arrive
2339
- * through this port, built at the composition root over the SAME `FsEvidenceArtifactStore` the
2340
- * runner wrote them with. The port answers BYTES and nothing derived — the content-type sniff is
2341
- * `runs.artifact`'s, computed once in the layer that owns the contract rather than once per host
2342
- * that happens to hold a runtime root.
2343
- *
2344
- * ABSENCE IS A RESULT, AND IT IS TWO RESULTS (D-15). See {@link EvidenceBlobLookup}: the concrete
2345
- * store collapses "no such run" and "no such blob" into one `undefined`, and an implementation of
2346
- * this port is where that distinction is recovered — because above it the two are different things
2347
- * a page has to render. Turning either arm into a typed refusal is the op's job, so the "not
2348
- * found" vocabulary lives in one place rather than in every composition root.
2349
- *
2350
- * THE PORT NEVER JOINS CALLER INPUT ONTO A PATH (the `RunsReaderPort.get` law, T-384-08 /
2351
- * T-405-14). Containment is the STORE's own rule — `slugBranch` over the runId and a
2352
- * 64-lowercase-hex check over the sha, both inside `FsEvidenceArtifactStore` — and an
2353
- * implementation that assembled a path itself would be a second containment rule, free to be
2354
- * weaker than the one the runner writes through.
2355
- *
2356
- * OPTIONAL on {@link FunctionDeps} for the reason `postProcessing` is, and for a sharper one: the
2357
- * alternative was widening the REQUIRED {@link RunsReaderPort} with a blob verb, which every
2358
- * daemon, MCP and test implementer of that port would then have to satisfy with storage most of
2359
- * them do not have.
2360
- */
2361
1081
  interface EvidenceBlobReaderPort {
2362
1082
  /** One blob's bytes, or which KIND of absence this host is reporting. */
2363
1083
  read(runId: RunId, sha: string): Promise<EvidenceBlobLookup>;
@@ -2368,66 +1088,15 @@ interface EvidenceBlobReaderPort {
2368
1088
  status: 'unknown_run' | 'blob_absent';
2369
1089
  }>;
2370
1090
  readRange?(runId: RunId, sha: string, start: number, end: number): Promise<EvidenceBlobLookup>;
2371
- /**
2372
- * The same question asked as a STAT — are the bytes still there — for read-time presence
2373
- * annotation on the durable reads (Phase 406, D-17).
2374
- *
2375
- * OPTIONAL SO THAT EVERY EXISTING IMPLEMENTATION AND FAKE KEEPS COMPILING, and for the sharper
2376
- * reason that absence here is a real host condition rather than an oversight: a host that cannot
2377
- * cheaply stat its blobs is not lying about anything, and an op that finds this member missing
2378
- * OMITS presence rather than reporting refs it never looked at as absent. A claimed absence is
2379
- * the one answer this field must never be able to give by accident, because it renders honest
2380
- * evidence as `evidence-incomplete`.
2381
- *
2382
- * IT ANSWERS THE SAME THREE WORDS `read` DOES, so the two cannot disagree about WHICH absence a
2383
- * ref is in — the read is this answer plus the bytes, never a second rule.
2384
- */
2385
1091
  has?(runId: RunId, sha: string): Promise<EvidenceBlobPresence>;
2386
1092
  }
2387
- /**
2388
- * Injected read port over a run's persisted analysis artifact (Phase 389, D-10/D-11).
2389
- *
2390
- * `analysis.json` is the analyze-run job's own receipt; the only legal reader routes its
2391
- * bytes through `guardRunAnalysisArtifact` (the version guard), and an implementation of
2392
- * this port does exactly that at the composition root — the op receives a guarded document
2393
- * or a typed absence, never raw bytes.
2394
- *
2395
- * `undefined` is an ANSWER, not an error: a run whose analysis never ran (budget spent,
2396
- * daemon down, chain still in flight) has no artifact, and the ops answer it through the
2397
- * D-10 accessor's handoff floor. That is why `runs.get` treats an UNBOUND port the same
2398
- * way — artifact-absent — while `analysis.get` refuses by name: a listing that cannot see
2399
- * artifacts still lists truthfully off the floor, but a caller asking for THE analysis of
2400
- * a run must be told this host cannot serve one.
2401
- */
2402
1093
  interface AnalysisArtifactReaderPort {
2403
1094
  /** The guarded artifact for one run, or `undefined` when none is on disk. */
2404
1095
  getArtifact(runId: string): RunAnalysisArtifact | undefined;
2405
1096
  }
2406
- /**
2407
- * Injected read port over the resolved project configuration.
2408
- *
2409
- * The port's name says what its security contract is: it yields a config whose
2410
- * secret-bearing values were dropped while the DTO was CONSTRUCTED. There is deliberately
2411
- * no unmasked variant on this port — a capability that could ask for one would make the
2412
- * masking optional, and optional masking is not masking (T-384-07).
2413
- */
2414
1097
  interface ConfigReaderPort {
2415
1098
  getMasked(): Promise<MaskedResolvedConfig>;
2416
1099
  }
2417
- /**
2418
- * Injected capability that STARTS a run (D-16, Phase 385).
2419
- *
2420
- * Its one verb resolves the identity of the run it started and nothing else. That narrowness
2421
- * is the point: the acknowledgment a caller receives is not assembled from what this port
2422
- * SAYS happened, it is read back off the run's own durable record through
2423
- * {@link RunsReaderPort} — the same port `runs.get` answers from. A host that returned an id
2424
- * without running anything therefore fails at the join rather than at a promise nobody can
2425
- * check.
2426
- *
2427
- * Owning the process model is what makes this a port rather than shared code (the plan-08
2428
- * dissection rule): a terminal host runs a suite one way, a daemon another, and neither
2429
- * would call the other's orchestration as written.
2430
- */
2431
1100
  interface RunInvocationPort {
2432
1101
  /**
2433
1102
  * Start a run for this request and resolve when the run has been recorded.
@@ -2441,51 +1110,6 @@ interface RunInvocationPort {
2441
1110
  interface StartedRun {
2442
1111
  readonly runId: RunId;
2443
1112
  }
2444
- /**
2445
- * The injected-dependency record every op implementation receives. Ops are pure
2446
- * functions of (validated input, deps) — they never reach for a host API.
2447
- *
2448
- * The four READ ports are required and non-partial on purpose: a composition that omits one
2449
- * fails to typecheck. Phase 387 adds `entities` as the fourth, and it joins as REQUIRED
2450
- * rather than optional deliberately — every host that can list a capability can also resolve
2451
- * an address, so a host that omitted it would not be reporting a real absence, it would be
2452
- * reporting that somebody forgot. That is precisely the state the required/optional split
2453
- * exists to distinguish, and widening the optional side to cover a forgetting would have
2454
- * made `unbound` mean two different things.
2455
- *
2456
- * The OPTIONAL members' optionality is not a relaxation of that discipline but the mechanism
2457
- * behind it. Not every host can start a run — a read-only surface, a registry built for a
2458
- * listing, 386's daemon before it wires its own executor — and D-09 requires that state to be
2459
- * VISIBLE rather than a call that fails somewhere far away. Omitting `runInvocation` makes
2460
- * `runs.start` list as `unbound` and refuse by name; supplying it makes the same capability
2461
- * bound. Any further optional member must join {@link OptionalFunctionDepKey} so the same
2462
- * reporting applies to it.
2463
- *
2464
- * `knowledgeReview` (387, KREV-01) is the second, and it is the one that makes the required/
2465
- * optional split earn its keep on the WRITE side: the ability to read committed knowledge and
2466
- * the ability to change it are separate powers, so they are separate members. A host that
2467
- * supplies only the reader lists the mutating verbs as `unbound` by name — which is a true
2468
- * statement about that host, and is exactly what widening the reader port with a write member
2469
- * would have made unsayable.
2470
- */
2471
- /**
2472
- * The live-view seam `runs.liveurl` mints through (403 D-03).
2473
- *
2474
- * OPTIONAL, and its absence is a GENUINE HOST FACT rather than a degraded mode: a CLI run in a
2475
- * terminal serves no origin, so there is no URL it could honestly hand back. A host that binds it
2476
- * supplies the per-process signer, the origin it is actually listening on, and a read of the live
2477
- * run registry — never a token, and never a parse.
2478
- *
2479
- * `capabilityOf` answers `undefined` for a run this host has no live session for (the op turns
2480
- * that into `unknown_run`) and a `LiveStreamDeclaration` — possibly `{ kind: 'none' }` — otherwise.
2481
- * `onMinted` observes every minted token value. 2026-10-14 (441-16, D-01): no shipped host binds it
2482
- * (a minted token is not a declared secret); tests use it to capture what the op really minted.
2483
- *
2484
- * `editorCapabilityOf` is the editor subject's read (428 D-07), through which `editor.liveurl`
2485
- * mints. ABSENT means this host serves no live editor — the standalone MCP host and the MCP App
2486
- * host never bind it, and the op refuses by name. `undefined` means no such live editor session.
2487
- * It is a separate lookup from `capabilityOf` so an editor session can never be addressed as a run.
2488
- */
2489
1113
  interface LiveViewPort {
2490
1114
  signer: LiveUrlSigner;
2491
1115
  origin(): string | undefined;
@@ -2500,230 +1124,30 @@ interface FunctionDeps {
2500
1124
  config: ConfigReaderPort;
2501
1125
  entities: EntityDirectoryPort;
2502
1126
  runInvocation?: RunInvocationPort;
2503
- /**
2504
- * 403 D-03: the live-view mint seam. See {@link LiveViewPort} — absent means this host has no
2505
- * served origin, and `runs.liveurl` refuses by name rather than minting a link to nowhere.
2506
- */
2507
1127
  liveView?: LiveViewPort;
2508
1128
  knowledgeReview?: KnowledgeReviewPort;
2509
- /**
2510
- * 388 D-14: the durable post-run chain read. The third optional member, and it joins for the
2511
- * reason the other two did rather than by analogy — a host with no queue storage genuinely
2512
- * cannot answer "what became of that run's analysis", and the alternative to reporting that
2513
- * by name is answering `pending` about a chain it has never been able to see, which is a
2514
- * wrong answer wearing a correct one's clothes.
2515
- */
2516
1129
  postProcessing?: PostProcessingReaderPort;
2517
- /**
2518
- * 405 D-03: the durable run-detail projection read. See {@link RunDetailReaderPort} — absent
2519
- * means this host owns no dashboard projection database, and `runs.detail` refuses BY NAME
2520
- * rather than answering an empty run about a projection it cannot observe. The CLI host binds it
2521
- * over the configured or default runs database path whenever its config parses (a read before
2522
- * that file exists answers not recorded in the runs database); the dashboard passes its own handle.
2523
- */
2524
1130
  runDetail?: RunDetailReaderPort;
2525
- /**
2526
- * 405 D-12/D-13: the run-scoped evidence-blob read. See {@link EvidenceBlobReaderPort} — absent
2527
- * means this host owns no runtime root, and `runs.artifact` refuses BY NAME rather than
2528
- * answering `blob_absent` about every artifact in the product, which would read to an operator
2529
- * as storage loss. Bound on every LOCAL host, which is exactly the set that has run directories.
2530
- */
2531
1131
  evidenceBlobs?: EvidenceBlobReaderPort;
2532
- /**
2533
- * 389 D-10/D-11: the analysis-artifact read. `analysis.get` binds through it and refuses
2534
- * by name where it is absent; `runs.get` reads it opportunistically and treats an unbound
2535
- * port as artifact-absent (the floor), because the cheapest read must stay cheap and a
2536
- * listing off the deterministic floor is honest, not degraded. Bound in plan 06's
2537
- * composition root over the run-directory `analysis.json` + version guard.
2538
- */
2539
1132
  analysis?: AnalysisArtifactReaderPort;
2540
- /**
2541
- * 389 D-08: the live recurrence seam the one renderer joins through. OPTIONAL because a
2542
- * host without the knowledge index genuinely has no history to answer with — and the
2543
- * renderer's contract turns that absence into the explicit no-history clause rather than
2544
- * a refusal, so `analysis.get` stays bound wherever `analysis` is. Bound in plan 06 to
2545
- * v51's run-outcome projection; 390 rebinds the SAME seam to its fingerprint window.
2546
- */
2547
1133
  recurrence?: RecurrenceProvider;
2548
- /**
2549
- * 390 D-12/D-13: the committed quarantine ledger's three verbs — list, release, add.
2550
- *
2551
- * OPTIONAL for the reason `knowledgeReview` is, and not by analogy: a host with no knowledge
2552
- * root genuinely cannot enumerate quarantine state, and the alternative to saying so by name
2553
- * is answering an empty list about a ledger it has never been able to read — a wrong answer
2554
- * wearing a correct one's clothes. On the write side the stakes are higher still: an unbound
2555
- * `add` that no-opped would report a test as quarantined while it kept failing every build.
2556
- *
2557
- * ONE port for all three verbs rather than a reader/writer split, because the read/write
2558
- * asymmetry is already stated on each op's risk descriptor — `quarantine.list` declares a
2559
- * read-only descriptor, `release` and `add` declare `readOnly: false` — so a second port would
2560
- * restate at the composition root a distinction the descriptors already carry. 2026-10-02
2561
- * (432-39, D-12): no op declares a scope and no gate enforces the split at a call; it lives only
2562
- * on the descriptors. The port TYPE is minted in schema, so knowledge-local can implement it
2563
- * without depending on the function layer.
2564
- */
2565
1134
  quarantine?: QuarantineOperationsPort;
2566
- /**
2567
- * 419 GATE3-01: the mark port — assembled by the cli host over eval-local + the review
2568
- * port; absent on hosts that cannot freeze gold.
2569
- */
1135
+ /** The mark port; absent, `runs.mark` is unbound by name and `runs.detail` lists no marks. */
2570
1136
  feedback?: FeedbackPort;
2571
- /**
2572
- * 423 D-19: read-only feedback observations. Separate from `feedback` so a host can serve
2573
- * history without mark authority; absent means `runs.marks.history` is unbound by name.
2574
- * 423 D-16: the same binding carries `preview`, runs.mark's plan for an exact input with no
2575
- * write; a binding without that verb leaves `runs.mark.preview` unbound by name.
2576
- */
2577
1137
  feedbackReads?: FeedbackReadsPort;
2578
- /**
2579
- * 392 D-24: the assembled chat turn engine, behind which `agent.ask` runs.
2580
- *
2581
- * OPTIONAL for the reason its neighbours are, and the honest absence matters more here than
2582
- * anywhere else in this record: a host with no engine — a remote read surface, a daemon before
2583
- * a chat host exists — genuinely cannot run a turn, and the alternative to reporting that by
2584
- * name is answering an EMPTY turn. A person told "I understood you and did nothing" stops
2585
- * asking, which is the same failure the unbound proposals inbox was reasoned about avoiding.
2586
- *
2587
- * The port is a single verb by design. Anything wider — a session lister, a history reader —
2588
- * would be reach the turn entry does not need and would give a surface holding this dep a way
2589
- * into session state without a registered op declaring it.
2590
- */
2591
1138
  chatTurn?: ChatTurnPort;
2592
- /**
2593
- * 393 D-01 / D-15: the two host halves one draft evaluation needs — resolve a caller's path to a
2594
- * parsed draft, and assemble the ports the four gates run against.
2595
- *
2596
- * ONE BUNDLED CAPABILITY rather than five separate deps, on the `runInvocation` precedent. The
2597
- * receipts store, the suite fingerprint index, the text corpus and the run pair are not
2598
- * independently bindable: an index scoped to a different product than the runs were taken under
2599
- * would compare a draft against another project's suite, and a host that could supply three of
2600
- * the five would list this op `bound` and then fail somewhere far from the composition that
2601
- * forgot one. Bundling makes "can this host verify a draft" a single fact.
2602
- *
2603
- * OPTIONAL for the reason `runInvocation` is, and here it says two things at once: a host that
2604
- * cannot PARSE a test file and a host that cannot START a run are both hosts that cannot verify
2605
- * a draft, and `composeLocalAgent()` binds this only when it has both.
2606
- *
2607
- * WHY RESOLUTION IS A SEPARATE VERB FROM EVALUATION. `packages/agent` is `storageNeutral`: it
2608
- * cannot stat a path, read a file, or know where the workspace ends. So the three refusals that
2609
- * need a filesystem are ANSWERED by the host and NAMED by the op — `resolveDraft` returns a
2610
- * typed outcome and never throws a host error through the layer, and
2611
- * `functions/ops/authoring-verify-draft.ts` maps each arm onto its own caller-facing class. The
2612
- * alternative — a single `evaluate(draftPath)` — would have put the refusal vocabulary in
2613
- * whichever package happened to own the syscall, one layer below where a caller catches it.
2614
- */
2615
1139
  draftEvaluation?: DraftEvaluationPort;
2616
- /**
2617
- * 394 D-01 / WTST-01: the authoring loop's read half — the brief, and the authored suite as
2618
- * text (stages 1-2).
2619
- *
2620
- * OPTIONAL for the reason `draftEvaluation` is, and the honest absence is load-bearing here in
2621
- * a way it is nowhere else in this record. A host with no knowledge index cannot serve an
2622
- * authoring brief, and the alternative to saying so by name is compiling a test from an EMPTY
2623
- * brief — which produces a plausible draft, authored against nothing this project knows, and
2624
- * says so nowhere. That is a wrong answer wearing a correct one's clothes, and it is wearing
2625
- * them into a file the user is asked to commit.
2626
- */
2627
1140
  authoringRead?: AuthoringReadPortLike;
2628
- /**
2629
- * 394-26 / WTST-01: which scope this project authors for, when the caller named none.
2630
- *
2631
- * OPTIONAL and consulted ONLY on absence. `authoring.draft.write`'s `scope` is optional at the
2632
- * registry (the `runs.start` law), so an input carrying just an intent has to be resolved
2633
- * before the loop runs — and the resolution reads the config and the workspace, which
2634
- * `packages/agent` cannot do. A composition that binds the loop's other halves and forgets this
2635
- * one refuses BY NAME on a scope-less call rather than authoring against a default: a guessed
2636
- * product is a draft written for the wrong application, which is the one failure mode a silent
2637
- * fallback here would produce.
2638
- */
2639
1141
  authoringScope?: AuthoringScopePortLike;
2640
- /**
2641
- * 394 D-01 / WTST-01: the authoring loop's compile half — the model, and the draft shelf
2642
- * (stage 3).
2643
- *
2644
- * Bound with `authoringRead` or not at all: `composeLocalAgent()` supplies the pair, because a
2645
- * host that can read but not write is not a degraded author, it is a host that must say
2646
- * `unbound` by name. `authoring.draft.write` names THIS key in `boundThrough` — it is the half
2647
- * that makes the op mutating — and refuses on either half's absence at the invoke, so an
2648
- * in-layer caller holding the spec directly gets the same named error.
2649
- */
2650
1142
  draftShelf?: DraftShelfPortLike;
2651
- /**
2652
- * 394-07 D-12 / WTST-05: the honesty funnel's reads — the receipts store and (optionally within
2653
- * the bundle) the outcome projection and quarantine ledger D-14 consults. 2026-10-01 (432-17): the
2654
- * proposals inbox the `accepted` stage read is deleted; 2026-10-02 (quick 261002-ctr) that stage
2655
- * is `writeEligible`, read off the receipts (see `projectAuthoringFunnel`).
2656
- *
2657
- * OPTIONAL, and the stakes sit on the READ side. A host with no agent-qa runtime root cannot list draft receipts, and the alternative to
2658
- * saying so by name is answering an all-zero funnel — which does not merely under-report, it
2659
- * reports a loop that filtered everything, the one reading of a zero funnel that FLATTERS the
2660
- * loop this measurement exists to keep honest.
2661
- */
2662
1143
  authoringFunnel?: AuthoringFunnelPortLike;
2663
- /**
2664
- * 395 D-03/D-17 · CFGM-01/04: the pinned config file SET as RAW BYTES, plus the comment-preserving
2665
- * field edit over them.
2666
- *
2667
- * DELIBERATELY NOT A WIDENING OF {@link ConfigReaderPort}, which is masked-only by contract and
2668
- * must stay so — a capability that could ask it for unmasked values would make masking optional,
2669
- * and optional masking is not masking. This port answers a different question: what bytes are
2670
- * COMMITTED in the person's repository. Those carry no resolved secrets, because D-11 makes
2671
- * committed config refs-only by law.
2672
- *
2673
- * The EDIT rides the same port as the read because both are host facts. `yaml`'s Document API is
2674
- * an external dependency `packages/agent`'s ownership contract does not admit, and admitting one
2675
- * to bring the edit in-layer would widen a boundary rather than respect it.
2676
- */
2677
1144
  configFiles?: ConfigFilesPortLike;
2678
- /**
2679
- * 395 CFGM-01: whole-config validation, which runs BEFORE a change is written.
2680
- *
2681
- * A PORT because `AgentQaConfigSchema` and `validateProject` live in `@vostride/agent-qa-core`
2682
- * and this package does not depend on it — `validate:arch` reds that edge with no legal repair.
2683
- * The capability is declared here and bound in `cli`.
2684
- */
2685
1145
  configValidate?: ConfigValidatePortLike;
2686
- /**
2687
- * 395-08 D-04 · CFGM-05: the shared scaffold CONTENT, rendered by the host that owns it.
2688
- *
2689
- * THE ONE DEP `config.project.scaffold` HAS AND THE OTHER FIVE CHANGE OPS DO NOT. D-04's claim
2690
- * is that `agent-qa init` and the scaffold change write one substance; that substance lives
2691
- * beside `init`'s filesystem machinery in `packages/cli`, which this package may not import. Its
2692
- * absence makes exactly one op unavailable, and it says so by name.
2693
- */
2694
1146
  scaffoldContent?: ConfigScaffoldContentPortLike;
2695
- /**
2696
- * 2026-09-30, 432-07 D-05/D-07: the direct config writer. Field changes merge in the config
2697
- * path's shared write turn and whole files publish over the digest they were built from. No
2698
- * proposal is filed and no git process runs.
2699
- */
2700
1147
  configWrite?: ConfigWritePortLike;
2701
- /**
2702
- * 2026-08-17 (398 D-09/D-11 · XPLN-02): the four host halves one `explain` needs — the committed
2703
- * catalog as data, the model's citation over it, and the two coverage hooks.
2704
- *
2705
- * ONE PORT AND NOT FOUR DEPS, on the `draftEvaluation` bundle's stated reasoning: a host that
2706
- * could load the catalog but not cite over it would list `agent.explain` as `bound` and then
2707
- * answer every question with a ghost citation, which abstains — safe, and useless, and reported
2708
- * nowhere. "Can this host explain itself" is one fact.
2709
- *
2710
- * OPTIONAL for the reason `analysis` is, and the honest absence is unusually load-bearing here.
2711
- * `packages/agent` is storageNeutral: it cannot read the committed artifact, so a composition
2712
- * that omitted the port and got a degraded answer would be answering FROM MODEL MEMORY, which is
2713
- * the single behaviour XPLN-02 exists to make impossible. The op refuses BY NAME instead.
2714
- */
2715
1148
  capabilityCatalog?: CapabilityCatalogPort;
2716
- /** 441 D-06: the host's declared-secret mask for projected run text; absent or throwing, run reads fail closed. */
2717
1149
  readonly secretMask?: MaskFn;
2718
1150
  }
2719
- /**
2720
- * What a host can tell the layer about a caller-supplied draft path (393, T-393-01).
2721
- *
2722
- * A discriminated union rather than `ParsedDraftLike | undefined`: "there is no file there", "that
2723
- * is a directory", "that is outside this workspace" and "that file is not a test" are four
2724
- * different things to tell a person, and collapsing them into an absence would make the op answer
2725
- * all four with one message. Every arm but `parsed` becomes a NAMED error at the invoke.
2726
- */
2727
1151
  type DraftResolution = {
2728
1152
  readonly kind: 'parsed';
2729
1153
  readonly draft: ParsedDraftLike;
@@ -2737,26 +1161,10 @@ type DraftResolution = {
2737
1161
  readonly kind: 'unparsable';
2738
1162
  readonly reason: string;
2739
1163
  };
2740
- /**
2741
- * The capability `authoring.draft.verify` binds through (393 D-01).
2742
- *
2743
- * `openEvaluation` takes the resolved draft because two of the four ports depend on WHICH draft is
2744
- * being evaluated: the suite comparison must exclude the draft's own identity from both of gate
2745
- * 3's lanes, or a draft that already lives in the authored suite would be measured against itself
2746
- * and could never be shown to add value. It is a separate call from `resolveDraft` so a refused
2747
- * path costs no database handle and no suite walk.
2748
- */
2749
1164
  interface DraftEvaluationPort {
2750
1165
  resolveDraft(draftPath: string): Promise<DraftResolution>;
2751
1166
  openEvaluation(draft: ParsedDraftLike): Promise<EvaluateDraftPorts>;
2752
1167
  }
2753
- /**
2754
- * The one verb `agent.ask` binds through (392 D-24).
2755
- *
2756
- * Typed against the SCHEMA contracts rather than against the engine's own types, which is what
2757
- * lets the composition root hand this layer an engine without the function layer depending on how
2758
- * a turn is assembled — and what lets 396's MCP projection describe the same shape.
2759
- */
2760
1168
  interface ChatTurnPort {
2761
1169
  runTurn(input: AgentAskInput): Promise<AgentAskResult>;
2762
1170
  }
@@ -2771,19 +1179,6 @@ interface CreateFunctionRegistryOptions {
2771
1179
  deps: FunctionDeps;
2772
1180
  }
2773
1181
  interface FunctionRegistry {
2774
- /**
2775
- * Declaration views of every registered op, sorted lexicographically by id.
2776
- *
2777
- * 2026-10-01 (432-20, D-12): the same list for every caller. The safe-mode filter that hid ops
2778
- * whose scopes exceeded `read` is deleted with the scopes themselves, so the caller argument is
2779
- * not read. 2026-10-02 (432-39): the caller contract is narrowed to `surface`/`origin`.
2780
- *
2781
- * KEPT, DATED 2026-10-02 (432-42 census decision): the argument stays and stays unread. Removing
2782
- * it changes no behaviour and touches every listing call site in the workspace (well over a
2783
- * hundred, most of them other plans' fixtures), so the churn buys nothing a reader can observe.
2784
- * It is not a permission seam: C432-TOKENLESS-CATALOG and C432-ALL-OPS-AVAILABLE pin that every
2785
- * caller lists the same set, so a filter returning here reds by name.
2786
- */
2787
1182
  list(caller: FunctionCaller): readonly FunctionDeclaration[];
2788
1183
  }
2789
1184
  /** Build the registry of built-in capabilities over composition-supplied dependencies. */
@@ -2811,26 +1206,11 @@ declare class UnknownFunctionError extends Error {
2811
1206
  readonly functionId: string;
2812
1207
  constructor(functionId: string);
2813
1208
  }
2814
- /** The supplied input failed the op's declared input contract (D-14). */
2815
1209
  declare class FunctionInputError extends Error {
2816
1210
  readonly functionId: string;
2817
1211
  readonly issues: string;
2818
1212
  constructor(functionId: string, issues: string);
2819
1213
  }
2820
- /**
2821
- * Run one registered capability on behalf of one caller.
2822
- *
2823
- * Order is load-bearing and asserted by tests:
2824
- * 1. resolve the spec — unknown id fails before any dependency is touched
2825
- * 2. validate the input — malformed input never reaches an implementation
2826
- * 3. binding check — a declared-but-unbound op refuses by name
2827
- * 4. dispatch — the only place a spec implementation ever runs
2828
- * 5. validate the output — unary results honour their declared contract
2829
- *
2830
- * Stream ops (D-08) run through the SAME steps. Nothing between the refusals and dispatch asks
2831
- * anybody anything (432-13), and nothing reads who is calling (432-20): `caller` is accepted so
2832
- * every door keeps one call shape, and the record it carries is presentation data only.
2833
- */
2834
1214
  declare function invokeFunction(registry: FunctionRegistry, functionId: string, input: unknown, _caller: FunctionCaller): Promise<unknown>;
2835
1215
 
2836
1216
  /**
@@ -2882,14 +1262,6 @@ declare class RunNotRecordedError extends Error {
2882
1262
  }
2883
1263
 
2884
1264
  declare const RUNS_EVENTS_FUNCTION_ID = "runs.events";
2885
- /**
2886
- * The requested resume point is below the lowest seq the log can still serve.
2887
- *
2888
- * A caller-facing refusal, and the ONE alternative to silently serving a gap: the caller
2889
- * learns the exact seq replay can restart at, so it can decide whether to accept the loss
2890
- * or re-read the run in full. `earliestAvailableSeq` is the documented resume floor the
2891
- * v49 bus reports, carried across the layer boundary unchanged.
2892
- */
2893
1265
  declare class StreamReplayFloorError extends Error {
2894
1266
  readonly runId: string;
2895
1267
  readonly requestedSeq: number;
@@ -2899,39 +1271,17 @@ declare class StreamReplayFloorError extends Error {
2899
1271
 
2900
1272
  /** The token's format marker. A FIXED prefix, deliberately — there is no algorithm negotiation. */
2901
1273
  declare const LIVE_URL_TOKEN_VERSION = "v2";
2902
- /** How long a minted `liveUrl` stays valid (D-02). Short by design; the socket renews in place. */
2903
1274
  declare const LIVE_URL_TTL_MS: number;
2904
- /** The served embed page's path prefix (D-06, ratified). Bare — no `/api/` for a host to allowlist. */
2905
1275
  declare const LIVE_EMBED_PATH_PREFIX = "/live/";
2906
1276
  /** base64url-encode an ASCII string. The claims body is JSON of integers + ids, so ASCII holds. */
2907
1277
  declare function encodeBase64Url(text: string): string;
2908
1278
  /** base64url-decode to an ASCII string, or `undefined` when the input is not base64url at all. */
2909
1279
  declare function decodeBase64Url(text: string): string | undefined;
2910
- /**
2911
- * Mint a token for `claims`. The ONLY producer of `liveUrl` tokens in the system — `runs.liveurl`
2912
- * is its only caller, which is what makes the minted-token secret store complete (D-04, Q4(b)).
2913
- */
2914
1280
  declare function mintLiveUrlToken(signer: LiveUrlSigner, claims: LiveUrlClaims): string;
2915
- /**
2916
- * Validate a token against the subject it was presented for: a run by its id, or an editor session
2917
- * by its id. A signed subject of another kind, or of the same kind with another id, is
2918
- * `scope_mismatch` (428 D-07).
2919
- *
2920
- * THE SIGNATURE IS CHECKED BEFORE THE BODY IS PARSED (T-403-01). A validator that parses first
2921
- * reports `malformed` for an attacker-authored body and `bad_signature` only for a well-formed
2922
- * forgery — which hands the attacker a free oracle for "did my claims body parse". Here, anything
2923
- * that survives the shape check is signature-checked immediately, so an unsigned body is
2924
- * `bad_signature` no matter how it is spelled inside.
2925
- */
2926
1281
  declare function validateLiveUrlToken(signer: LiveUrlSigner, token: string, expect: {
2927
1282
  subject: LiveViewSubjectRef;
2928
1283
  now: number;
2929
1284
  }): LiveUrlVerdict;
2930
- /**
2931
- * Compose the viewer-facing URL. The token rides the FRAGMENT and nothing else (D-04): a fragment
2932
- * is never sent to the server on any request, never lands in an access log, and never appears in a
2933
- * `Referer` — the three durable channels a query parameter would leak into by default.
2934
- */
2935
1285
  declare function liveUrlOf(origin: string, runId: string, token: string): string;
2936
1286
 
2937
1287
  declare const RUNS_LIVE_URL_FUNCTION_ID = "runs.liveurl";
@@ -2962,15 +1312,6 @@ declare class UnknownLiveEditorSessionError extends Error {
2962
1312
  constructor(sessionId: string);
2963
1313
  }
2964
1314
 
2965
- /**
2966
- * No entity is addressed by this reference.
2967
- *
2968
- * A typed refusal rather than an `undefined` result, on the `RunNotFoundError` precedent: the
2969
- * absence is the ANSWER to a caller who asked for one specific thing, and a surface needs to
2970
- * be able to phrase it. Note what it does not mean — a permanently minted address whose
2971
- * entity has since been tombstoned still RESOLVES (D-04), to a card whose status says so.
2972
- * This error is for an address nothing ever minted.
2973
- */
2974
1315
  declare class EntityNotFoundError extends Error {
2975
1316
  readonly ref: string;
2976
1317
  constructor(ref: string);
@@ -2996,23 +1337,8 @@ declare class AmbiguousStepAddressError extends Error {
2996
1337
  constructor(runId: string, stepIndex: number, testIds: readonly string[]);
2997
1338
  }
2998
1339
 
2999
- /**
3000
- * All lowercase, and that is a measurement rather than a taste.
3001
- *
3002
- * D-15's id taxonomy is pinned by `parity-coverage.test.ts` as `/^[a-z]+(?:\.[a-z]+)+$/`, so a
3003
- * camelCased verb segment is not a spelling this catalogue admits. The shape follows
3004
- * `runs.events` — a domain and a noun — because what this op returns is a STATE and not an
3005
- * action. The exported constant is what plan 04 and every later surface name; the literal
3006
- * appears here once.
3007
- */
3008
1340
  declare const RUNS_POST_PROCESSING_FUNCTION_ID = "runs.postprocessing";
3009
1341
 
3010
- /**
3011
- * All lowercase and two segments, because D-15's id taxonomy is pinned as
3012
- * `/^[a-z]+(?:\.[a-z]+)+$/` — a camelCased segment is not a spelling this catalogue admits. The
3013
- * exported constant is what the dashboard composition hands its server as DATA;
3014
- * `packages/dashboard-server` may not import this package, so the literal appears here once.
3015
- */
3016
1342
  declare const RUNS_DETAIL_FUNCTION_ID = "runs.detail";
3017
1343
 
3018
1344
  declare const RUNS_EVIDENCE_FUNCTION_ID = "runs.evidence";
@@ -3024,112 +1350,36 @@ declare const RUNS_EVIDENCE_FUNCTION_ID = "runs.evidence";
3024
1350
  * package, so the literal appears here once.
3025
1351
  */
3026
1352
  declare const RUNS_ARTIFACT_FUNCTION_ID = "runs.artifact";
3027
- /**
3028
- * The run exists and holds no blob under that sha (D-15).
3029
- *
3030
- * A SEPARATE REFUSAL FROM `RunNotFoundError`, and separate because the two ask different things of
3031
- * whoever reads them: an unknown run is a link that was never valid, while an absent blob is an
3032
- * artifact that was collected, never written, or declared incomplete by its own ref. The dashboard
3033
- * renders the second as a visible unavailable state (406 upgrades that rendering); rendering it as
3034
- * "no such run" would tell an operator to go look for a run that is right there.
3035
- *
3036
- * Carries both coordinates so a surface can say WHICH artifact of which run is missing. The route
3037
- * adapter does not echo them — a refusal body that quoted the request's own sha would be a
3038
- * reflection this door has no reason to offer — but a CLI or chat caller holding the error can.
3039
- */
3040
1353
  declare class EvidenceBlobAbsentError extends Error {
3041
1354
  readonly runId: string;
3042
1355
  readonly sha: string;
3043
1356
  constructor(runId: string, sha: string);
3044
1357
  }
3045
1358
 
3046
- /**
3047
- * Lowercase dot taxonomy (D-15): the locked `analysis` domain, one noun. The exported
3048
- * constant is what plan 07's CLI and every later surface name; the literal appears here
3049
- * once, and the parity-coverage suite picks the id up from the registry automatically.
3050
- */
3051
1359
  declare const ANALYSIS_GET_FUNCTION_ID = "analysis.get";
3052
1360
 
3053
- /**
3054
- * Lowercase dot taxonomy (384 D-15): the `runs` domain, one noun.
3055
- *
3056
- * `runs.` rather than a new `digest.` domain, because what this returns is a fact ABOUT A RUN —
3057
- * the same reason `runs.postprocessing` is not `postprocessing.get`. The exported constant is
3058
- * what the CLI's `digest` verb, the tail's fetch pointer, 392's chat and M5 all name; the
3059
- * literal appears here once, and the parity suite picks the id up from the registry.
3060
- */
3061
1361
  declare const RUNS_DIGEST_FUNCTION_ID = "runs.digest";
3062
1362
 
3063
- /**
3064
- * Lowercase dot taxonomy (384 D-15): the `runs` domain, one noun.
3065
- *
3066
- * `runs.knowledge` and not `knowledge.run`, for the reason `runs.postprocessing` is not
3067
- * `postprocessing.get` — what this returns is a fact ABOUT A RUN. The literal appears here once;
3068
- * every surface names the exported constant and the parity suite picks the id up from the
3069
- * registry.
3070
- */
3071
1363
  declare const RUNS_KNOWLEDGE_FUNCTION_ID = "runs.knowledge";
3072
1364
 
3073
1365
  declare const RUNS_OVERVIEW_FUNCTION_ID = "runs.overview";
3074
1366
 
3075
1367
  declare const RUNS_LIST_FUNCTION_ID = "runs.list";
3076
1368
 
3077
- /** The literal appears here once; every surface names the constant (384 D-15). */
1369
+ /** The literal appears here once; every surface names the constant. */
3078
1370
  declare const QUARANTINE_ADD_FUNCTION_ID = "quarantine.add";
3079
1371
 
3080
- /**
3081
- * Lowercase dot taxonomy (384 D-15): a new `quarantine` domain, one noun. Quarantine state is
3082
- * neither a run, a memory nor an analysis — filing these verbs under an existing domain would
3083
- * have made the taxonomy decorative, which is the same call 387 made for `entities`.
3084
- *
3085
- * The literal appears here once; the parity-coverage suite picks the id up off the registry.
3086
- */
3087
1372
  declare const QUARANTINE_LIST_FUNCTION_ID = "quarantine.list";
3088
1373
 
3089
- /** The literal appears here once; every surface names the constant (384 D-15). */
1374
+ /** The literal appears here once; every surface names the constant. */
3090
1375
  declare const QUARANTINE_RELEASE_FUNCTION_ID = "quarantine.release";
3091
1376
 
3092
- /**
3093
- * Lowercase dot taxonomy (384 D-15): the `agent.` domain's first verb.
3094
- *
3095
- * The literal appears exactly once, here. Every surface that names it — the `ask` command today,
3096
- * the REPL and the MCP projection later — imports this constant, so the parity suite and the
3097
- * catalogue read the same string as the dispatcher.
3098
- */
3099
1377
  declare const AGENT_ASK_FUNCTION_ID = "agent.ask";
3100
1378
 
3101
- /**
3102
- * Lowercase dot taxonomy (384 D-15): the `agent` domain, already a pinned member of the conformance
3103
- * suite's `domain-list.ts` because `agent.ask` lives there. No taxonomy widening — an explain is a
3104
- * thing the AGENT does about itself, which is exactly what that domain's charter covers.
3105
- *
3106
- * The literal appears here once; the scenario rows, the composition and every later surface name
3107
- * this constant.
3108
- */
3109
1379
  declare const EXPLAIN_FUNCTION_ID = "agent.explain";
3110
1380
 
3111
1381
  declare const AUTHORING_DRAFT_VERIFY_FUNCTION_ID = "authoring.draft.verify";
3112
1382
 
3113
- /**
3114
- * What a refusal NAMES. Every field is evidence a reader can act on, not a summary of a verdict.
3115
- *
3116
- * `matchedRef` is the reference a caller hands to `entities.resolve`, spelled
3117
- * `<filePath>::<testName>` — the same spelling `adds-value.ts`'s gate reason already prints, so
3118
- * the two surfaces that name a suite test name it the same way.
3119
- *
3120
- * IT IS NOT A `TST-###` SHORT-ID, and that is measured rather than an omission. `ENTITY_KINDS`
3121
- * reserves `TST`, but a short-ID exists only once something SIGHTS the entity, and sighting is
3122
- * what mints the permanent address — `resolver/resolve.ts` states that no creation path anywhere
3123
- * calls the ledger, so a refusal could not mint one without becoming a write on a read path.
3124
- * Measured further: `composeLocalAgent()` wires exactly one sighting source, the memory one, so
3125
- * NO suite test has a `TST-###` on any host in this build. Printing one would publish an address
3126
- * that resolves to nothing, which is precisely ENTY-01's never-guess failure. The durable identity
3127
- * is what this build can name truthfully, so it is what the refusal names; when a `TST` sighting
3128
- * source lands, this field is the one place that has to change.
3129
- *
3130
- * `threshold` rides along because a refusal that reports 0.52 without reporting what it was
3131
- * compared to tells an author nothing about whether their draft was borderline.
3132
- */
3133
1383
  interface DedupEvidence {
3134
1384
  readonly score: number;
3135
1385
  readonly matched: SuiteTestIdentity;
@@ -3208,18 +1458,6 @@ interface RouterFailurePoint {
3208
1458
  /** Which path produced the verdict. Absent ⇒ the failure was not definitive. */
3209
1459
  readonly verdictSource?: VerdictSource;
3210
1460
  }
3211
- /**
3212
- * The application looks wrong, or the evidence does not say clearly enough to risk repairing — so
3213
- * the loop STOPS here and reports what the run said.
3214
- *
3215
- * Carries `failurePoint` and `evidenceRefs`, neither of which exists on {@link RepairRoute}.
3216
- * `failurePoint` is nullable because rule 4 admits a member that failed at the member level with
3217
- * no step verdict to point at; `evidenceRefs` is not, because a stop a reader cannot trace is not
3218
- * a report.
3219
- *
3220
- * 2026-09-02 (Phase 411): the proposal arm for a correctly-failing draft left with the Issue-slot
3221
- * cleanup.
3222
- */
3223
1461
  interface StopRoute {
3224
1462
  readonly kind: 'stop';
3225
1463
  readonly signalClass: RouterStopSignalClass;
@@ -3227,20 +1465,6 @@ interface StopRoute {
3227
1465
  readonly evidenceRefs: readonly RunEvidenceRef[];
3228
1466
  }
3229
1467
 
3230
- /**
3231
- * The probe showed a step that used to pass now failing. The dynamic half's refusal.
3232
- *
3233
- * Distinct from the static one by NAME rather than by a flag, because the two are evidence of
3234
- * different things: a static violation says the model asked to touch a protected step, and this
3235
- * says it broke one WITHOUT asking. The second is the more interesting signal and would be lost
3236
- * inside a shared class.
3237
- *
3238
- * ITS FOUR FIELDS ARE ONE COORDINATE, AND THEY NAME THE PROBED MEMBER (CR-01). A run record is
3239
- * addressed by (runId, filePath, testName, stepIndex), so the run id alone is half an address —
3240
- * and the half a reader would otherwise supply from the PRE-repair draft names a file this probe
3241
- * never opened, because every applied patch set re-stages the candidate under its new digest. All
3242
- * four are minted by this package from the candidate it probed; none is model text.
3243
- */
3244
1468
  declare class PassingPrefixRegressedError extends Error {
3245
1469
  readonly stepIndex: number;
3246
1470
  readonly runId: string;
@@ -3279,21 +1503,6 @@ type RepairAbandonment = {
3279
1503
  */
3280
1504
  declare const AUTHORING_LOOP_STAGES: readonly ["brief", "dedup", "compile", "verify", "route", "compound"];
3281
1505
  type AuthoringLoopStage = (typeof AUTHORING_LOOP_STAGES)[number];
3282
- /**
3283
- * How one loop invocation ended.
3284
- *
3285
- * Discriminated on the CLOSED schema vocabulary, and all four arms are declared here in the
3286
- * tracer even though only `drafted` is reachable from it. See the vocabulary's own doc block: a
3287
- * union that grew an arm per plan would make plans 04-06 changes to the shape every consumer
3288
- * branches on rather than new cases behind a discriminant they already read.
3289
- *
3290
- * The `stopped` arm carries the ROUTE and the abandonment facts and writes nothing — the op
3291
- * projects the result's `stop` payload from it (411 D-05).
3292
- *
3293
- * 2026-09-30, 432 D-06: the `drafted` and `repaired` arms carry the WRITTEN-TEST RECEIPT where they
3294
- * used to carry a proposal for a person to accept. The final draft is written to its target unless
3295
- * its final receipt is `rejected`, and the receipt says which file was written or why none was.
3296
- */
3297
1506
  type AuthoringLoopOutcome = {
3298
1507
  readonly kind: 'drafted';
3299
1508
  readonly draftRef: DraftRef;
@@ -3316,15 +1525,6 @@ type AuthoringLoopOutcome = {
3316
1525
  readonly draftRef: DraftRef;
3317
1526
  readonly receipt: DraftReceipt;
3318
1527
  readonly route: StopRoute;
3319
- /**
3320
- * ITS KIND AND ITS SPEND ARE ONE PARAMETER, so `repairAbandonment` and `repairIterations`
3321
- * land on the op's payload together or not at all (394-21, WR-09). Present as one object or
3322
- * absent — never two independent optionals, which could disagree: a kind with no spend, or a
3323
- * spend on a stop repair never touched, which reads as "repair was tried and cost nothing".
3324
- *
3325
- * `iterations` is `RepairOutcome.iterations` INCLUDING iterations a fence refused, which
3326
- * started nothing — which is why it is not derivable from the run ids `fanOut` counts (D-08).
3327
- */
3328
1528
  readonly abandonment?: {
3329
1529
  readonly kind: RepairAbandonment['kind'];
3330
1530
  readonly iterations: number;
@@ -3335,93 +1535,24 @@ interface AuthoringLoopInput {
3335
1535
  readonly scope: ScopeKey;
3336
1536
  /** Handed straight through to stage 4. See the op's input contract for what it costs. */
3337
1537
  readonly draftMode?: true;
3338
- /**
3339
- * The invoking session, threaded to EVERY receipt this invocation mints (394-17, D-12).
3340
- *
3341
- * Both of them: stage 4's own evaluation and the final stability gate a repaired candidate goes
3342
- * through. A thread that stopped at the first would attribute first-try drafts and silently
3343
- * unattribute every draft that needed repairing — the population a keep rate most needs.
3344
- */
3345
1538
  readonly sessionRef?: string;
3346
1539
  }
3347
- /**
3348
- * The fan-out ONE invocation of this loop can cost, in child `agent-qa run` processes (D-03/D-10).
3349
- *
3350
- * THE SINGLE PLACE THE THREE TERMS ARE ASSEMBLED, and the only caller of `computeFanOutBound` in
3351
- * this package. The dispatch confirm renders what this returns and the finished proposal reports
3352
- * against it, so the number a person consents on and the number they are shown afterwards are the
3353
- * same function of the same input rather than two agreeing arithmetics. A surface recomputing
3354
- * `N + K + N` for itself would be free to drift the moment either constant moved — which is what
3355
- * mutation I of the phase's mutation driver (since removed) demonstrated rather than asserted.
3356
- *
3357
- * DRAFT MODE IS THE CASE WHERE THE TWO N TERMS DIFFER. A draft-mode caller asks for ONE cheap gate
3358
- * run (rule 1 of the disposition table), but the FINAL stability gate still runs the pinned N on
3359
- * the repaired candidate — `repairDraft` deliberately drops the draft-mode marker for it. So the
3360
- * bound is `1 + K + N`, not `2 * 1 + K`, and it is the configuration the schema cells use to prove
3361
- * the two terms stay separate: with the shipped constants they are both 3, and a collapse would be
3362
- * invisible under default values.
3363
- */
3364
1540
  declare function authoringFanOutBound(input: {
3365
1541
  readonly draftMode?: true;
3366
1542
  }): FanOutBound;
3367
1543
  interface AuthoringLoopDeps {
3368
1544
  readonly read: AuthoringReadPortLike;
3369
- /**
3370
- * The compile half, plus stage 5's run reader (394-06).
3371
- *
3372
- * The run capability rides this bundle rather than arriving as a third dep: see the port's own
3373
- * `runs` doc. What it buys the loop is that "can this host author" stays ONE question with one
3374
- * answer, which is the property D-01's composition rests on.
3375
- */
3376
1545
  readonly shelf: DraftShelfPortLike;
3377
1546
  }
3378
- /**
3379
- * What one invocation produced, and the stages it actually entered.
3380
- *
3381
- * `stages` is DATA the loop answers with, not a test hook. Plan 04's refusal ends an invocation
3382
- * at stage 2 and plan 05's repair re-enters stages 3-4, so "where did this stop" and "how many
3383
- * times did it iterate" are questions about a real invocation that a caller — and the funnel's
3384
- * side counters (D-15) — has to be able to ask. It is also what makes the ordering claim
3385
- * checkable at all: the trace is pushed by each stage as it is entered, so a reordered loop
3386
- * reports a reordered trace.
3387
- */
3388
1547
  interface AuthoringLoopResult {
3389
1548
  readonly outcome: AuthoringLoopOutcome;
3390
1549
  readonly stages: readonly AuthoringLoopStage[];
3391
1550
  readonly dedup: DedupScreening;
3392
1551
  /** Which named side counters this invocation incremented. See the vocabulary's doc block. */
3393
1552
  readonly sideCounters: readonly AuthoringLoopSideCounter[];
3394
- /**
3395
- * What was previewed at dispatch, beside the runs this invocation actually spent (WTST-08).
3396
- *
3397
- * ON THE RESULT AND NOT ONLY ON THE PROPOSAL ARM, because a stage-2 refusal has no arm — D-04
3398
- * ends it with no draft, no receipt and no envelope — and "this invocation cost nothing" is a
3399
- * fact a caller is entitled to for every outcome, not only the ones that produced something.
3400
- */
3401
1553
  readonly fanOut: FanOutReport;
3402
- /**
3403
- * What stage 3's one refinement round did (D-18).
3404
- *
3405
- * ABSENT exactly when the round never ran — a stage-2 refusal, or a compile refusal for a lost
3406
- * stated value — and present on every other invocation, including the ones where the round
3407
- * changed nothing. The
3408
- * distinction the field exists to keep is between a round that found nothing (`no-op`) and one
3409
- * whose emission could not be used (`not-applied`): both leave the draft alone, and only one of
3410
- * them is the loop working as designed.
3411
- */
3412
1554
  readonly refinement?: RefinementOutcome;
3413
1555
  }
3414
- /**
3415
- * Facts about an invocation that are counted BESIDE the funnel, never as a stage of it (D-15).
3416
- *
3417
- * A dedup refusal is the first member, and the distinction is the whole reason the vocabulary
3418
- * exists. Plan 07 projects the loop as a funnel — how many intents reached compile, verify, a
3419
- * keep-eligible receipt — and a refusal that was folded into a funnel stage would read as an
3420
- * invocation the pipeline FILTERED. It is not: it is an invocation the pipeline never entered,
3421
- * refused for a working-as-intended reason before any cost was incurred. Counting it inside the
3422
- * funnel would inflate the filter rate with successes and break comparison against the reference
3423
- * distribution, which is exactly the number the funnel exists to be compared against.
3424
- */
3425
1556
  declare const AUTHORING_LOOP_SIDE_COUNTERS: readonly ["dedup-refused"];
3426
1557
  type AuthoringLoopSideCounter = (typeof AUTHORING_LOOP_SIDE_COUNTERS)[number];
3427
1558
  declare function runAuthoringLoop(input: AuthoringLoopInput, deps: AuthoringLoopDeps): Promise<AuthoringLoopResult>;
@@ -3440,37 +1571,8 @@ declare const CONFIG_TARGET_CREATE_FUNCTION_ID = "config.target.create";
3440
1571
 
3441
1572
  declare const CONFIG_TARGET_EDIT_FUNCTION_ID = "config.target.edit";
3442
1573
 
3443
- /**
3444
- * Lowercase dot taxonomy (384 D-15), three segments: the shipped `authoring` domain, `funnel` as
3445
- * the subject and `get` as the verb — the `authoring.draft.verify` / `authoring.draft.write`
3446
- * precedent, which is itself the `knowledge.records.inspect` one.
3447
- *
3448
- * NO UNDERSCORE, and that is a constraint rather than a style: `parity-coverage.test.ts` requires
3449
- * every id to match the dot taxonomy, and `chat/tool-names.ts` REFUSES an id already carrying `_`
3450
- * because such a name has two pre-images under the `.` ↔ `_` inverse. Since 392 D-07 binds every
3451
- * registry op into the planner's toolset, an underscore id would raise `ChatToolNameError` for
3452
- * the whole projection rather than merely looking unconventional.
3453
- *
3454
- * `funnel` and not `drafts` because the subject is the MEASUREMENT, not the drafts: a second verb
3455
- * about drafts would file under `authoring.draft.` beside its two siblings, and filing this one
3456
- * there would have made "get the drafts" and "get the funnel over the drafts" indistinguishable at
3457
- * exactly the point a reader is choosing between them.
3458
- *
3459
- * The literal appears here once; the parity-coverage suite picks the id up off the registry.
3460
- */
3461
1574
  declare const AUTHORING_FUNNEL_GET_FUNCTION_ID = "authoring.funnel.get";
3462
1575
 
3463
- /**
3464
- * The session-scoped directives: canon that maps to NO registry op.
3465
- *
3466
- * Closed and spelled once. `/model` changes how THIS SESSION behaves (D-04's model override and its
3467
- * `model_change` event); `/help` and `/quit` are REPL chrome. None of them is a capability, which is
3468
- * why none of them has a function id — and they are in the table anyway, because the table is the
3469
- * vocabulary a person types and the meta-test demands a scenario for every row of it.
3470
- *
3471
- * 432-13 (D-14): `/pin` is gone. It was a session deny overlay over the op gate, and there is no op
3472
- * gate; a clause spelled `/pin …` is now an ordinary clause nothing in the table resolves.
3473
- */
3474
1576
  declare const CHAT_DIRECTIVES: readonly ["model", "help", "quit"];
3475
1577
  type ChatDirectiveName = (typeof CHAT_DIRECTIVES)[number];
3476
1578
  /**
@@ -3518,77 +1620,16 @@ type ChatResolution = {
3518
1620
  } | {
3519
1621
  readonly kind: 'unresolved';
3520
1622
  };
3521
- /**
3522
- * THE CANON. Ordered, and the order is the first-match walk (see `resolveClause`).
3523
- *
3524
- * No two rules overlap today, so the order is documentation rather than semantics — and it is
3525
- * written in D-06's own listing order so it stays readable as one.
3526
- */
3527
1623
  declare const CHAT_RESOLVER_TABLE: ChatResolverTable;
3528
- /**
3529
- * Stage 1 for one clause: the FIRST rule that binds, or `unresolved`.
3530
- *
3531
- * First-match rather than unique-match, and the table is ordered rather than a map, because D-06's
3532
- * canon has forms that could legitimately overlap as it grows, and "the earlier, more specific rule
3533
- * wins" is a rule a reader of the table can see. Precision is 1.0 by construction — every rule is
3534
- * an exact predicate over the clause, so a miss is a miss and never a guess (D-09).
3535
- */
3536
1624
  declare function resolveClause(table: ChatResolverTable, clause: string): ChatResolution;
3537
- /**
3538
- * One line of `/help`, in a shape a census can read (Phase 398 plan 09).
3539
- *
3540
- * WHY THE ENTRIES ARE STRUCTURED AND THE STRING IS DERIVED. `CHAT_HELP_TEXT` used to be ten
3541
- * hand-aligned sentences, and a sentence is not something a test can join to anything: the only
3542
- * assertion available over it was a substring match somebody remembered to write. The fields below
3543
- * are the join keys — `alias` names the row of `CHAT_RESOLVER_TABLE` the line documents, and
3544
- * `syntax` is the FORM A PERSON TYPES, first token and all — so "the help text agrees with what
3545
- * chat understands" became a set comparison and "this line claims an argument is optional" became a
3546
- * probe of the shipped rule.
3547
- *
3548
- * THE CENSUS-TIE DECISION (398 plan 09, planned 2026-08-16, executed 2026-08-17 · D-01/SC-1).
3549
- * The discretion call was whether help re-derives from the committed capability catalog or stays
3550
- * here with a mechanical tie. It stays HERE. `packages/agent` is storage-neutral — it cannot read a
3551
- * committed artifact off disk without acquiring a filesystem dependency the whole package is built
3552
- * to not have — so re-deriving at runtime would move help out of the package that owns the
3553
- * vocabulary and into the one that ships bytes. The tie gives the same guarantee without the move:
3554
- * `packages/cli/src/__tests__/conformance/help-census.test.ts` set-equals these aliases against the
3555
- * catalog's alias rows plus the shipped directives plus the passthrough, in BOTH directions, so a
3556
- * help line with no capability behind it and a capability with no help line are each a named RED.
3557
- * The header's one-list law is unchanged; what changed is that it is now enforced rather than
3558
- * asked for.
3559
- */
3560
1625
  interface ChatHelpEntry {
3561
1626
  /** The `CHAT_RESOLVER_TABLE` row this line documents. The census's join key. */
3562
1627
  readonly alias: string;
3563
- /**
3564
- * What a person types, verbatim, including the leading `/` where there is one.
3565
- *
3566
- * `<angle>` marks a REQUIRED argument and `[square]` an optional one, and the distinction is
3567
- * load-bearing rather than typographic: a conformance cell reads it and drives the shipped rule
3568
- * with the bare verb, so a line promising that a verb works alone when the rule refuses it — the
3569
- * exact drift this plan found on `digest` — is a failure rather than a reading.
3570
- */
3571
1628
  readonly syntax: string;
3572
1629
  /** What the line says the verb does. */
3573
1630
  readonly summary: string;
3574
1631
  }
3575
- /**
3576
- * What `/help` prints, one entry per row of the table above (Phase 392 plan 08, D-06).
3577
- *
3578
- * IT LIVES BESIDE THE TABLE, and that is the point rather than the filing: the canon a person can
3579
- * type is `CHAT_RESOLVER_TABLE`, and help text kept anywhere else would be a second list of what
3580
- * chat understands, free to drift from the one that actually routes. `/fn` is named explicitly
3581
- * because it is the reach lever — every registry op is one deterministic branch away, which is why
3582
- * the alias set stays thin (D-06).
3583
- */
3584
1632
  declare const CHAT_HELP_ENTRIES: readonly ChatHelpEntry[];
3585
- /**
3586
- * What `/help` prints, RENDERED from the entries rather than spelled a second time.
3587
- *
3588
- * The string is still the export `composeLocalAgent`'s directive handler returns, because a person
3589
- * types `/help` and reads sentences. What is gone is the possibility of the sentences and the join
3590
- * keys disagreeing: there is one list, and this is a projection of it.
3591
- */
3592
1633
  declare const CHAT_HELP_TEXT: string;
3593
1634
 
3594
1635
  /** One line off a session file, with the number an editor would show the person reading it. */
@@ -3623,7 +1664,7 @@ interface SessionRepair {
3623
1664
  * refuses a foreign header before this function is called, and it is pinned by a test anyway.
3624
1665
  */
3625
1666
  declare function repairSession(lines: readonly SessionLogLine[], options: RepairSessionOptions): SessionRepair;
3626
- /** One line of rebuilt conversation. Refs and one-liners only — never a payload body (D-18). */
1667
+ /** One line of rebuilt conversation. Refs and one-liners only — never a payload body. */
3627
1668
  interface ChatReplayMessage {
3628
1669
  readonly role: 'user' | 'assistant';
3629
1670
  readonly text: string;
@@ -3634,19 +1675,6 @@ interface SessionReplay {
3634
1675
  /** Every turn the log records as interrupted, however it was interrupted. */
3635
1676
  readonly abortedTurnIds: readonly string[];
3636
1677
  }
3637
- /**
3638
- * Rebuild the live conversation a resumed session continues from, over REPAIRED events.
3639
- *
3640
- * IT RUNS AFTER REPAIR AND BEFORE ANY PRUNE, and the order is not decoration: replaying the raw
3641
- * events of a crashed session yields a conversation in which the interrupted turn looks answered,
3642
- * so a caller that pruned first would trim a conversation that never happened. CHAT-05 asks for
3643
- * the repair on "the next turn, before pruning" for exactly this reason.
3644
- *
3645
- * AN ABORTED TURN IS EXCLUDED WHOLE, not merely truncated. Its user message is a question nobody
3646
- * answered; carrying it forward would put an unanswered request in the model's context and invite
3647
- * the next turn to act on it — which is the silent re-execution CHAT-05's repair exists to
3648
- * prevent. The turn is not lost: it is on the log, with an `aborted` record naming it.
3649
- */
3650
1678
  declare function replaySession(events: readonly ChatSessionEvent[]): SessionReplay;
3651
1679
 
3652
1680
  /** One piece of an utterance, as the splitter produced it. */
@@ -3661,24 +1689,7 @@ interface ResolvedClause {
3661
1689
  readonly functionId: string;
3662
1690
  readonly args: unknown;
3663
1691
  }
3664
- /**
3665
- * WHICH STAGE PROVED A BINDING (RESEARCH Pitfall 1 / 391 D-06).
3666
- *
3667
- * The same word names the caller mint and the action's provenance on purpose: the origin a clause
3668
- * executes under is decided by which stage bound it, and giving the two ideas separate vocabularies
3669
- * would let them drift into disagreeing about the same clause.
3670
- */
3671
1692
  type ChatBindingStage = 'resolver' | 'planner';
3672
- /**
3673
- * One step of an execute verdict: a binding, and the stage that proved it.
3674
- *
3675
- * `source` is carried rather than re-derived, and that is a correctness decision rather than a
3676
- * convenience. The engine has to mint a caller per action, and the only honest input to that choice
3677
- * is which stage produced the binding — a fact reconciliation KNOWS and every later consumer would
3678
- * have to guess at by matching the action back against the mapper set. A guess that matched on
3679
- * `functionId` would mint `human` for a planner-planned call to an op the table also knows, which
3680
- * is exactly the self-grant 391 D-06 withholds.
3681
- */
3682
1693
  interface ClauseAction extends ResolvedClause {
3683
1694
  readonly source: ChatBindingStage;
3684
1695
  }
@@ -3695,159 +1706,43 @@ interface PlannerToolCall {
3695
1706
  readonly toolName: string;
3696
1707
  readonly input: unknown;
3697
1708
  }
3698
- /**
3699
- * A stage-2 failure the engine folds into a failed turn (CHAT-01).
3700
- *
3701
- * A transport error, an auth refusal or a provider outage is a fact about this turn, not a crash of
3702
- * the session — so it is DATA rather than a thrown error, and the turn ends with a receipt instead
3703
- * of a hang or a stack trace. The name is the provider's own refusal name where there is one, which
3704
- * keeps it on the same footing as every other refusal a receipt carries.
3705
- */
3706
1709
  interface PlannerStageFailure {
3707
1710
  readonly name: string;
3708
1711
  readonly message: string;
3709
1712
  }
3710
- /**
3711
- * What stage 2 produces: a PLAN, and never an effect.
3712
- *
3713
- * `toolCalls` carries EVERY call the model made, meta-tools included and in the order it proposed
3714
- * them. Nothing is filtered out here, because stage 3 is the component that decides what a plan
3715
- * means and a stage that pre-digested it would be making that decision one layer early, out of
3716
- * sight of the reconciliation the whole design exists to route through.
3717
- *
3718
- * THERE IS NO `answer` FIELD, and its absence is load-bearing (392-14). This outcome used to carry
3719
- * a convenience projection of the FIRST `answer_only` call's prose, which became a second prose
3720
- * path the moment the tool contract moved to one call per answered clause: reconciliation counted
3721
- * every claimed clause as answered and the person received the first answer only. The prose now
3722
- * has exactly one carrier — the verdict's `answer`, built from the claims the merge actually
3723
- * consumed — and a projection that could disagree with it no longer exists to be read.
3724
- */
3725
1713
  interface PlannerStageOutcome {
3726
1714
  readonly toolCalls: readonly PlannerToolCall[];
3727
1715
  /** Present when the model could not be reached or refused; the turn fails, never hangs. */
3728
1716
  readonly failure?: PlannerStageFailure;
3729
1717
  }
3730
- /**
3731
- * Stage 2 — the tool-bound planner (CHAT-01).
3732
- *
3733
- * OPTIONAL AT THE COMPOSITION, and its absence is a real production posture rather than a stub:
3734
- * a host with no model bound is HEADLESS, and the correct behaviour there is that any clause the
3735
- * table cannot resolve fails the turn closed by name. A stubbed planner that answered "I could not
3736
- * plan that" would say the same words about a completely different fact — that a model was asked
3737
- * and declined — and the two need to stay distinguishable in the log.
3738
- */
3739
1718
  interface PlannerStagePort {
3740
1719
  plan(request: PlannerStageRequest): Promise<PlannerStageOutcome>;
3741
1720
  }
3742
- /**
3743
- * What stage 2 is asked, INCLUDING WHOSE TURN IS ASKING (392 plan 13, CR-02).
3744
- *
3745
- * `sessionId` is not decoration on a prompt. The context a composition builds for this call — the
3746
- * conversation replayed into the request, the model in force — are facts about ONE session, and a
3747
- * composition serving two conversations at once has no other way to know which of them it is
3748
- * assembling a request for. Without it the planner reads whichever conversation was published
3749
- * most recently, which on an interleaved pair is the OTHER person's.
3750
- */
3751
1721
  interface PlannerStageRequest {
3752
1722
  readonly utterance: string;
3753
1723
  readonly clauses: readonly ChatClause[];
3754
1724
  readonly sessionId: string;
3755
1725
  }
3756
- /**
3757
- * What stage 3 is given: what the table proved, and what the model proposed.
3758
- *
3759
- * `planned` is the model's RAW outcome rather than a set of resolved clauses, and the difference is
3760
- * where the safety property lives. Aligning tool calls to clauses IS reconciliation — deciding
3761
- * which proposal answers which part of the request, and whose binding wins where the table already
3762
- * has one (D-12). A stage 2 that handed over pre-aligned clauses would have made that decision
3763
- * already, in the one component whose input is untrusted model output.
3764
- */
3765
1726
  interface ReconcileInput {
3766
1727
  readonly clauses: readonly ChatClause[];
3767
1728
  readonly mapped: readonly ResolvedClause[];
3768
1729
  readonly planned: PlannerStageOutcome;
3769
1730
  }
3770
- /**
3771
- * What stage 3 decides.
3772
- *
3773
- * `unhandled` and `execute` are mutually exclusive by contract rather than by shape, and the
3774
- * asymmetry is D-10: reconciliation completes BEFORE the first side effect, so a verdict naming an
3775
- * unhandled clause carries an empty execution plan and the turn ends with `executed == 0`.
3776
- */
3777
1731
  type ReconcileVerdict = {
3778
1732
  readonly kind: 'execute';
3779
1733
  readonly plan: readonly ClauseAction[];
3780
- /**
3781
- * The prose a turn answers with when the model answered rather than acted (CHAT-01).
3782
- *
3783
- * It rides the EXECUTE verdict, with an empty plan in the pure case, because an answered turn
3784
- * completed — it did what was asked. Giving it a third verdict variant would have made
3785
- * "answered" a sibling of "refused", and every consumer would then have to remember that one
3786
- * of the two non-execute outcomes is a success.
3787
- */
3788
1734
  readonly answer?: string;
3789
1735
  } | {
3790
1736
  readonly kind: 'unhandled';
3791
1737
  readonly clause: ChatClause;
3792
1738
  readonly reason: string;
3793
1739
  };
3794
- /**
3795
- * Stage 3 — deterministic reconciliation (D-09 / D-12 / D-16).
3796
- *
3797
- * A pure function, supplied at engine CONSTRUCTION. The purity is what makes the mutation proof
3798
- * possible at all: the differential composes the real host twice with two strategies and reads the
3799
- * difference off a named scenario, without a single production code path knowing which it got.
3800
- */
3801
1740
  type ReconcileStrategy = (input: ReconcileInput) => ReconcileVerdict;
3802
- /**
3803
- * The deterministic conjunction splitter (D-09).
3804
- *
3805
- * A seam rather than a hard-coded call because plan 02 replaces the tracer's whole-utterance
3806
- * splitter with the quote-aware tokenizer, and the engine must not need editing for that.
3807
- */
3808
1741
  type ClauseSplitter = (utterance: string) => readonly ChatClause[];
3809
- /**
3810
- * Per-stage caller minting (RESEARCH Pitfall 1 / 391 D-06).
3811
- *
3812
- * `FunctionCaller.origin` is a per-CALL fact, not a per-surface one: a person typing a canonical
3813
- * alias IS a human acting, and a model planning a call is not, and both happen inside one turn, so a
3814
- * receipt attributes each clause to the stage that bound it. 2026-10-02 (432-39, D-10/D-12): the
3815
- * origin is attribution only; no op withholds anything by it.
3816
- */
3817
1742
  interface CallerMint {
3818
- /**
3819
- * The caller for one binding stage.
3820
- *
3821
- * 2026-10-02 (432-39, D-02/D-12): no `sessionId` parameter. It only ever stamped the retired
3822
- * `FunctionCaller.sessionId` (392 plan 13), which nothing read after the session deny overlay left
3823
- * (432-13/432-19); the production mint ignored it, and the caller member is deleted.
3824
- */
3825
1743
  forStage(stage: ChatBindingStage): FunctionCaller;
3826
1744
  }
3827
- /**
3828
- * Stage-4's dispatch binding.
3829
- *
3830
- * A closure over `invokeFunction` supplied by the composition root, NOT a registry handle. Two
3831
- * reasons and the second is the structural one: `agent`'s chat subtree would otherwise import the
3832
- * registry it is registered inside, and — more importantly — a seam typed as "call this op for me"
3833
- * cannot become a seam that runs a spec directly (384 FUNC-03).
3834
- */
3835
1745
  type ChatInvokeBinding = (functionId: string, input: unknown, caller: FunctionCaller) => Promise<ChatInvokeAnswer>;
3836
- /**
3837
- * What an op answered, classified — the seam's return type since 399.1 (D-01/D-03/D-04/D-05).
3838
- *
3839
- * WHY THE VERDICT ARRIVES AS DATA RATHER THAN BEING COMPUTED HERE. The engine cannot classify a
3840
- * result: the fold that knows which arm of an op's union is a refusal lives on that op's SPEC, and
3841
- * this seam is deliberately not a registry handle (the paragraph above, 384 FUNC-03). The one place
3842
- * in the system holding both the registry and this call is the composition root that mints the
3843
- * binding, so that is where the fold runs and this is how its answer travels. The engine reads three
3844
- * fields; it reaches for nothing.
3845
- *
3846
- * THIS TYPE IS WHY THE SEAM CHANGED AT ALL. `Promise<unknown>` was a seam that could carry the
3847
- * RESULT and nothing about it, so the engine's only available derivation for a receipt status was
3848
- * throw-versus-return — and every arm of a typed refusal returns. G-399-2 is that gap measured: a
3849
- * credential-blocked config mutation reported `executed` in green.
3850
- */
3851
1746
  interface ChatInvokeAnswer {
3852
1747
  /**
3853
1748
  * The op's own result, masked as the function layer returned it.
@@ -3856,112 +1751,29 @@ interface ChatInvokeAnswer {
3856
1751
  * every `agent.ask` client in this form; each surface's emit masks with the declared secrets.
3857
1752
  */
3858
1753
  readonly payload: unknown;
3859
- /**
3860
- * The classifier's answer, or the legacy verdict when the op declares no classifier.
3861
- *
3862
- * FAIL-CLOSED (D-03): a fold that throws, or answers something that is not a verdict, yields
3863
- * `failed` with a named outcome — never `executed`. An unreadable result might be a refusal, and
3864
- * success is the one claim a receipt must not default to.
3865
- */
3866
1754
  readonly verdict: ClauseVerdict;
3867
1755
  /** The op's declared render fold, or `[]`. ADDITIVE — these lines never replace the payload. */
3868
1756
  readonly foldLines: readonly string[];
3869
1757
  }
3870
- /**
3871
- * CHAT-04's degrade pass, as a seam the tracer leaves empty.
3872
- *
3873
- * Declared now for the reason every other seat here is: plan 06 fills it over the shipped
3874
- * `TokenAccounting` substrate, and an engine that grew a compaction call later would grow it in the
3875
- * middle of the turn contract this file pins.
3876
- *
3877
- * IT IS HANDED THE REPLAY, AND THAT IS WHAT MAKES CHAT-05'S ORDERING STRUCTURAL. The requirement is
3878
- * that the replay-safety repair runs BEFORE pruning, and a hook that received nothing would have to
3879
- * fetch its own messages — at which point nothing in the type stops it fetching them from an
3880
- * unrepaired source. Receiving an already-repaired replay leaves the degrade pass no other input to
3881
- * prune, so "repair first" is a fact about the argument rather than a rule someone has to remember.
3882
- */
3883
1758
  type CompactionHook = (context: {
3884
1759
  readonly turnId: string;
3885
- /**
3886
- * The session this turn belongs to (392 plan 13, CR-02).
3887
- *
3888
- * Beside `append` for the same reason `append` is here: what a pass produces — the pruned
3889
- * conversation — belongs to ONE conversation, and the sink a host publishes it into is one
3890
- * container per session. A hook that knew the turn but not the session could write a pruned
3891
- * history into whichever conversation asked most recently.
3892
- */
3893
1760
  readonly sessionId: string;
3894
1761
  /** The conversation as of this turn's start: repaired, aborted turns already excluded. */
3895
1762
  readonly replay: SessionReplay;
3896
- /**
3897
- * Where this pass's one event goes: the SESSION HANDLE of the turn being compacted (392-12).
3898
- *
3899
- * The hook used to hold an `emit` bound at composition time, which — while the adapter owned a
3900
- * single mutable descriptor — meant "the session whose open ran most recently" rather than "the
3901
- * session this turn belongs to". Passing the append IN makes the destination a fact about the
3902
- * call, so a compaction event cannot land in another conversation however many turns are in
3903
- * flight. The hook still holds no log of its own; it holds one turn's writer for the length of
3904
- * one call.
3905
- */
3906
1763
  readonly append: (event: ChatCompactionEvent) => Promise<void>;
3907
1764
  }) => Promise<void> | void;
3908
- /**
3909
- * What a `/model` directive did (Phase 392 plan 08, D-04).
3910
- *
3911
- * The handler applies the override and REPORTS the two model names, rather than emitting anything.
3912
- * The engine is what turns the pair into a `model_change` event, through the same `diffChatConfig`
3913
- * the per-turn config re-read uses — so there is ONE construction site for that event and the
3914
- * `source` field is what distinguishes a person typing from a file changing underneath them.
3915
- */
3916
1765
  interface ChatModelDirectiveOutcome {
3917
1766
  readonly from: string;
3918
1767
  readonly to: string;
3919
1768
  /** The line the surface prints back. The handler renders it, because it knows what it resolved. */
3920
1769
  readonly line: string;
3921
1770
  }
3922
- /**
3923
- * The session directives, as handlers a composition binds (D-04).
3924
- *
3925
- * EVERY MEMBER IS OPTIONAL, and an absent one is not a gap: the engine's refusal arm names an
3926
- * unbound directive by name, which is the honest answer for a build that recognises `/model` and
3927
- * cannot carry it out. A host that binds none behaves exactly as every host did before this seam
3928
- * existed.
3929
- *
3930
- * A HANDLER MAY THROW, AND THROWING IS AN ANSWER. That refusal travels the engine's ONE refusal
3931
- * path, so it leaves the same `mark_unhandled` row every other refused clause does.
3932
- *
3933
- * 432-13 (D-14): `/pin` and its handler are gone with the op gate they tightened.
3934
- */
3935
1771
  interface ChatDirectiveHandlers {
3936
1772
  readonly model?: (name: string) => ChatModelDirectiveOutcome | Promise<ChatModelDirectiveOutcome>;
3937
1773
  readonly help?: () => string;
3938
1774
  readonly quit?: () => string;
3939
- /**
3940
- * THE BATCH PRE-FLIGHT: state the refusal before ANY directive of this turn is applied (392-14).
3941
- *
3942
- * The engine runs this over EVERY collected directive and only then applies the first one, so a
3943
- * throw here refuses the whole turn with zero effects. That is D-10's own shape — reconciliation
3944
- * completes before the first side effect — applied to a directive BATCH.
3945
- *
3946
- * IT MUTATES NOTHING, and that is the member's whole contract. Asking whether a directive CAN be
3947
- * carried out is a different act from carrying it out, exactly as `directiveBindingRefusal` is.
3948
- * Resolving and caching a value the apply will reuse is permitted, because a cache is not a
3949
- * session's state.
3950
- *
3951
- * OPTIONAL, so a host that publishes none is exactly as safe as it was: `runDirective`'s catch
3952
- * remains the belt, and the only thing such a host loses is the batch guarantee.
3953
- */
3954
1775
  readonly validate?: (directive: ChatDirectiveName, argument?: string) => void | Promise<void>;
3955
1776
  }
3956
- /**
3957
- * Where the engine publishes the live turn state a surface renders from (RESEARCH Open Q2).
3958
- *
3959
- * A SINK rather than the container, so the engine can write the two facts only it holds — the
3960
- * minted turn id and the turn's outcome — without acquiring the ability to read or reshape the
3961
- * rest of a surface's state. The id is the load-bearing one: it is minted inside `runTurn`, and a
3962
- * Ctrl+C handler that wants to write the explicit `aborted` event D-19 asks for has no other way
3963
- * to name the turn it is interrupting.
3964
- */
3965
1777
  interface ChatTurnStateSink {
3966
1778
  beginTurn(turnId: string): void;
3967
1779
  /**
@@ -3993,7 +1805,7 @@ interface ChatToolsetFingerprint {
3993
1805
  /** Present only when the caller genuinely observed the set — never reconstructed by guessing. */
3994
1806
  readonly ids?: readonly string[];
3995
1807
  }
3996
- /** The configuration facts a chat session is sensitive to (D-04). */
1808
+ /** The configuration facts a chat session is sensitive to. */
3997
1809
  interface ChatConfigState {
3998
1810
  readonly model: string;
3999
1811
  readonly toolset: ChatToolsetFingerprint;
@@ -4006,80 +1818,12 @@ interface TurnEngineDeps {
4006
1818
  readonly sessionLog: SessionLogPort;
4007
1819
  readonly mint: CallerMint;
4008
1820
  readonly invoke: ChatInvokeBinding;
4009
- /** Absent ⇒ headless: an unresolved clause fails the turn closed BY NAME (D-08). */
4010
1821
  readonly planner?: PlannerStagePort;
4011
1822
  readonly compaction?: CompactionHook;
4012
- /**
4013
- * The configuration in force RIGHT NOW for THIS SESSION, re-read per turn (D-04, CR-02).
4014
- *
4015
- * A THUNK rather than a value, and that is the whole of "config changes are events": a value
4016
- * captured at construction would be the configuration the host started with, and the drift this
4017
- * seam exists to detect is precisely the case where the file changed underneath a running
4018
- * session. Absent ⇒ this host does not re-read, and nothing is claimed about drift — which is
4019
- * different from claiming there was none, and is why the seam is optional rather than defaulted.
4020
- *
4021
- * IT IS HANDED THE SESSION for the reason `state` below is: the answer includes the model in
4022
- * force, a `/model` override is session-scoped by D-04, and a composition-wide re-read would
4023
- * report one session's override as every session's drift.
4024
- */
4025
1823
  readonly config?: (sessionId: string) => ChatConfigState | Promise<ChatConfigState>;
4026
- /**
4027
- * The session directives, where a host binds any, RESOLVED PER SESSION (D-04 / CR-02).
4028
- *
4029
- * Absent, or absent for one directive, means the refusal arm below names it. That is not a
4030
- * degraded mode: a build that recognises `/model` and cannot carry it out should say so, and the
4031
- * `mark_unhandled` row it leaves is what makes the gap countable.
4032
- *
4033
- * A FACTORY, because a directive mutates a session: `/model` moves the model this conversation
4034
- * plans under, `/quit` asks THIS loop to end. Handlers bound once per composition would carry
4035
- * those effects to whoever asked next (W-03).
4036
- */
4037
1824
  readonly directives?: (sessionId: string) => ChatDirectiveHandlers;
4038
- /**
4039
- * Where the live turn state a surface renders from is published, PER SESSION (Open Q2, CR-02).
4040
- *
4041
- * The turn id goes out at the START of the turn rather than with the result, and that ordering is
4042
- * the whole reason the seam exists — an interrupted turn never produces a result, so a Ctrl+C
4043
- * handler that waited for one could never name the turn it was interrupting (D-19's graceful arm).
4044
- *
4045
- * A FACTORY OVER THE SESSION ID, resolved once per turn from the header `open()` returned. What
4046
- * this sink carries — the turn in flight, the conversation, the model — are facts about ONE
4047
- * conversation, and a sink held per composition made them facts about whichever turn wrote last.
4048
- * The engine cannot resolve it any earlier than the open, because a turn that names no session is
4049
- * given one BY the adapter and the id is the adapter's answer rather than the caller's input.
4050
- */
4051
1825
  readonly state?: (sessionId: string) => ChatTurnStateSink;
4052
- /**
4053
- * This project's configured target vocabulary, for the PRE-DISPATCH write_test route (395-08,
4054
- * D-14/D-15 · CFGM-05).
4055
- *
4056
- * A THUNK for `config`'s reason: a scaffold applied earlier in this very turn changes the answer,
4057
- * and a value captured at composition would send the authoring dispatch that follows it against
4058
- * the vocabulary as it stood before the scaffold wrote anything (2026-10-02, 432-40: re-spelled
4059
- * from "before the person approved anything" — the scaffold writes in the call, D-05/D-14).
4060
- *
4061
- * BOUND FROM THE SAME PORT THE AUTHORING LOOP READS (`AuthoringReadPortLike.configuredTargetNames`),
4062
- * so "which targets does this project declare" has one answer on both sides of the dispatch. Two
4063
- * readings of that question would let this seam route on a vocabulary the loop then disagrees with,
4064
- * which is the sibling-input failure this project keeps rediscovering.
4065
- *
4066
- * ABSENT ⇒ NO ROUTING. A host that cannot name its targets dispatches authoring exactly as it did
4067
- * before this seam existed, and the loop's own backstop is unchanged. That is deliberately the
4068
- * permissive direction: this seam adds a scaffold offer and a refusal, and a host that cannot
4069
- * answer the question has not lost a guard — the backstop it always had is still there.
4070
- */
4071
1826
  readonly configuredTargets?: () => Promise<readonly string[]>;
4072
- /**
4073
- * Where a mid-turn artifact a person should read is written (395-13 · CFGM-05).
4074
- *
4075
- * DISPLAY-ONLY. Nothing handed to this port enters a decision, and nothing waits on it.
4076
- *
4077
- * 2026-09-30, 432-11 D-05/D-14: the `write_test` scaffold route writes the configuration in the
4078
- * scaffold call itself, so what this port shows is a receipt of a change already on disk: the
4079
- * summary, the files written and the diff. It is no longer a precondition of an approval, because
4080
- * there is no approval. An absent port, or one that fails, does not stop the turn; the clause
4081
- * receipt still names the files the scaffold wrote.
4082
- */
4083
1827
  readonly present?: (text: string) => void | Promise<void>;
4084
1828
  readonly now: () => string;
4085
1829
  readonly mintTurnId: () => string;
@@ -4091,56 +1835,9 @@ interface TurnEngine {
4091
1835
 
4092
1836
  declare function createTurnEngine(deps: TurnEngineDeps): TurnEngine;
4093
1837
 
4094
- /**
4095
- * The refusal sentence a fail-closed turn carries.
4096
- *
4097
- * It NAMES the clause (D-08). A message that said only "I could not do that" would leave a person
4098
- * unable to tell which half of a compound request was refused, and would make a silent drop and a
4099
- * loud refusal read identically in the log.
4100
- */
4101
1838
  declare function unhandledReason(clause: string): string;
4102
- /**
4103
- * The deterministic override: what the table proved beats what anything else proposed.
4104
- *
4105
- * The mapped set is indexed by CLAUSE INDEX rather than by matching text, and that is the D-12
4106
- * mechanism in miniature: the clause's position is its identity, so a planner entry for a clause
4107
- * the table already resolved cannot displace the mapper's binding no matter how the model spelled
4108
- * it.
4109
- *
4110
- * THE COUNTS MUST AGREE BEFORE ANYTHING BINDS. Every clause gets exactly one disposition —
4111
- * mapped, answered, or needing a proposal — and the number of action calls the model made is
4112
- * compared against the number of clauses in that third group. That comparison is the ONE place a
4113
- * count disagreement is decided, and it happens between the passes, before a single binding exists:
4114
- * a disagreement refuses the turn BY NAME (D-08) rather than shifting the alignment by one, so the
4115
- * materialize walk below has no refusal arm at all. The count and the set it is compared against
4116
- * come from the same pass, because a second predicate computing "which clauses need a proposal"
4117
- * would be free to drift from the one the binder walks.
4118
- *
4119
- * WHEN NO CLAUSE NEEDS A PROPOSAL nothing can be mis-bound — no call is popped by anybody — and
4120
- * the calls are discarded whole. This is a CHOICE, not an absence of alternatives: through the
4121
- * engine there is always a clause a refusal could name here, because stage 2 is skipped entirely
4122
- * when every clause is mapped (`turn-engine.ts`), so a non-empty proposal list beside an empty
4123
- * needy list means at least one clause was ANSWERED. Naming it is available and is not taken. The
4124
- * asymmetry this file has always had is what decides it: the model may lose a proposal, and the
4125
- * person may never lose a request. A clause that was answered did not lose its request — its prose
4126
- * reaches the person — so the discarded call costs the model, not the person. Ratified 2026-08-10
4127
- * against the alternative of refusing by name, which would move this case and cost every such turn
4128
- * a refusal instead of an answer.
4129
- */
4130
1839
  declare const deterministicOverrideStrategy: ReconcileStrategy;
4131
1840
 
4132
- /**
4133
- * The model's answer for a turn it answers in prose (CHAT-01).
4134
- *
4135
- * A first-class action rather than the absence of one: a turn where the person asked a question
4136
- * executes zero ops and returns text, so `the model planned nothing` has to stay distinguishable
4137
- * against `the model answered` — two facts a reader of the log must be able to tell apart.
4138
- *
4139
- * (The backticks are not stylistic. `packages/arch`'s dependency lint reads every source line
4140
- * looking for a specifier, and its pattern is one particular English word ahead of a quoted
4141
- * phrase — so ordinary prose in that shape is reported as an undeclared package import. Two
4142
- * sibling plans have already reddened `validate:arch` this way.)
4143
- */
4144
1841
  declare const ANSWER_ONLY_TOOL_NAME = "answer_only";
4145
1842
  /** The two orchestration tools, and no others. */
4146
1843
  declare const CHAT_META_TOOL_NAMES: readonly ["answer_only", "mark_unhandled"];
@@ -4161,19 +1858,6 @@ declare function isChatMetaToolName(name: string): name is ChatMetaToolName;
4161
1858
  */
4162
1859
  declare function fromToolName(toolName: string): string;
4163
1860
 
4164
- /**
4165
- * Split one utterance into the clauses stage 1 routes.
4166
- *
4167
- * Pure, total and host-free: no clock, no config, no I/O, and no input throws. An unterminated
4168
- * quote is not an error a person should have to hear about — it protects to end-of-utterance,
4169
- * which over-protects (one clause where two were meant) rather than under-protects (a phantom
4170
- * clause and a spurious refusal). Pitfall 5 says which way to be wrong. An unterminated BRACKET
4171
- * gets the identical answer for the identical reason (see {@link BRACKET_PAIRS}).
4172
- *
4173
- * Empty pieces are elided rather than preserved, so `status,,proposals` and `status, proposals`
4174
- * are the same request. A clause whose text is empty could name nothing in a refusal, and D-08
4175
- * requires that a fail-closed turn name what it could not do.
4176
- */
4177
1861
  declare function splitClauses(utterance: string): readonly ChatClause[];
4178
1862
 
4179
1863
  /** What the last turn did, as a surface renders it. Absent before the first turn of a session. */
@@ -4185,22 +1869,7 @@ interface ChatLastTurn {
4185
1869
  }
4186
1870
  /** A frozen view of the session's live state. Handed out per read, never the live object. */
4187
1871
  interface ChatStateSnapshot {
4188
- /**
4189
- * The model in force RIGHT NOW, after any `/model` this session ran.
4190
- *
4191
- * This is the value the config re-read compares against, and that is why the override lives here
4192
- * rather than in a variable beside it: a thunk that kept answering the composed model would emit
4193
- * a `model_change` straight back on the next turn, undoing the verb it just recorded (392-07).
4194
- */
4195
1872
  readonly activeModel: string;
4196
- /**
4197
- * The turn currently in flight, or absent between turns.
4198
- *
4199
- * The ONLY thing that can name an interrupted turn. A SIGINT handler has no other way to write
4200
- * the explicit `aborted` event D-19's graceful arm asks for — the id is minted inside the engine
4201
- * and a surface never sees it until the turn result comes back, which for an interrupted turn is
4202
- * never.
4203
- */
4204
1873
  readonly activeTurnId?: string;
4205
1874
  /** The conversation as compaction left it, oldest first (see the module head for the direction). */
4206
1875
  readonly conversation: readonly ModelMessage[];
@@ -4208,13 +1877,6 @@ interface ChatStateSnapshot {
4208
1877
  /** `/quit` asked the loop to end. The REPL reads it; nothing in the engine acts on it. */
4209
1878
  readonly closing: boolean;
4210
1879
  }
4211
- /**
4212
- * The mutable container. The engine writes; the surface reads.
4213
- *
4214
- * Deliberately NOT an event emitter. A REPL renders after each turn returns, and a subscription
4215
- * would be the seam through which a surface started reacting to a turn mid-flight — which is the
4216
- * streaming design M5 owns, not a thing to half-build here.
4217
- */
4218
1880
  interface ChatStateContainer {
4219
1881
  read(): ChatStateSnapshot;
4220
1882
  beginTurn(turnId: string): void;
@@ -4261,29 +1923,6 @@ declare function parseScenario(document: unknown, expected: {
4261
1923
  readonly id: string;
4262
1924
  }): ChatScenario;
4263
1925
 
4264
- /**
4265
- * The pinned ceiling: at most one refusal per five `should_execute` scenarios.
4266
- *
4267
- * A PINNED CONSTANT WITH A RETUNE COMMENT, NEVER CONFIG (386 D-15). A configurable ceiling is a
4268
- * ceiling that gets raised the first time it is inconvenient, at which point it measures the
4269
- * patience of whoever raised it.
4270
- *
4271
- * OWNER: the chat orchestration subsystem (Phase 392, CHAT-03).
4272
- *
4273
- * RETUNE WHEN, and only when:
4274
- * - the corpus GROWS through a deliberate `SCENARIO_CORPUS` bump, and the new members change what
4275
- * a representative request looks like. At the corpus size this pin ships against, 1/5 leaves no
4276
- * slack at all on the live lane — three `should_execute` live scenarios means the first refusal
4277
- * exceeds it. That is deliberate for a young corpus: with three cells, one miss IS a routing
4278
- * regression, and a tolerance that hid it would be a tolerance for nothing observable.
4279
- * - a MEASURED live rate sits consistently far from the pin in either direction across several
4280
- * gate runs. Consistently far below means the pin has stopped being a gate; consistently just
4281
- * above on requests that genuinely cannot be served means the corpus admitted cells the build
4282
- * was never going to route, and the fix is the corpus, not the number.
4283
- *
4284
- * A retune moves this constant AND `SCENARIO_CORPUS_VERSION`, so a rate recorded in any SUMMARY
4285
- * stays attributable to the pair it was measured against.
4286
- */
4287
1926
  declare const MARK_UNHANDLED_CEILING: {
4288
1927
  readonly num: 1;
4289
1928
  readonly den: 5;
@@ -4298,13 +1937,6 @@ interface MarkUnhandledCounts {
4298
1937
  readonly unhandledCount: number;
4299
1938
  readonly denominator: number;
4300
1939
  }
4301
- /**
4302
- * Count the refusals in a session-event stream against a caller-supplied denominator.
4303
- *
4304
- * The denominator is a PARAMETER rather than something this module derives, and that is the D-15
4305
- * discipline expressed as a signature: a function that could count scenarios for itself would be
4306
- * one refactor away from counting files on a disk.
4307
- */
4308
1940
  declare function computeMarkUnhandledRate(events: readonly ChatSessionEvent[], denominator: number): MarkUnhandledCounts;
4309
1941
  /**
4310
1942
  * Has the corpus exceeded the pinned ceiling?
@@ -4320,118 +1952,6 @@ declare function computeMarkUnhandledRate(events: readonly ChatSessionEvent[], d
4320
1952
  */
4321
1953
  declare function ceilingExceeded(counts: MarkUnhandledCounts): boolean;
4322
1954
 
4323
- /**
4324
- * The corpus this build measures against.
4325
- *
4326
- * BUMPED WHENEVER THE CORPUS STOPS BEING THE SAME CORPUS — a member added or removed, an
4327
- * expectation kind flipped, or an utterance re-authored. A recorded rate can then always be
4328
- * attributed to a known set, and a rate quoted without one is a number whose meaning cannot be
4329
- * reconstructed after the next scenario lands.
4330
- *
4331
- * v1 — 392 plan 02 seeded 13 deterministic + the canon compound pair; 392 plan 08 flipped the four
4332
- * directive scenarios to `should_execute` before this manifest existed.
4333
- * v2 — 392 plan 09 admits the `planner/` seeds (the live lane's natural-language arm).
4334
- * v3 — 392 plan 09, after the FIRST live execution: `run-42-and-digest`'s digest clause names its
4335
- * run (the original was unsatisfiable under D-10's pre-flight atomicity — see that file), and
4336
- * `list-recent-runs` is re-authored as a request to DO something rather than a question about
4337
- * the project.
4338
- * v4 — 392 plan 11, the gap round for 392-VERIFICATION's CR-03 and CR-04: two `compound/` cells
4339
- * admitted, both `fail_closed`, so the ceiling's `should_execute` denominators are unmoved
4340
- * (11 hermetic, 3 live) and the two defects the corpus could not see now have permanent
4341
- * cells. `pin-and-write-test` is the corpus's first HERMETIC compound cell — both halves are
4342
- * deterministic, and its lane is named as an exception in `scenario-manifest.test.ts` rather
4343
- * than derived from its class. The verifier's third probe (two overlapping sessions on one
4344
- * composition, CR-01/CR-02) is deliberately NOT a cell here: the scenario runner is
4345
- * single-turn by design, so it lands as a composition-level concurrency test in plan 392-12.
4346
- * v5 — 392 plan 14, the SECOND gap round for 392-VERIFICATION's round-2 gap: two more `compound/`
4347
- * cells, both `fail_closed` and both HERMETIC, so the denominators stay 11 hermetic / 3 live.
4348
- * `pin-and-pin-nonsense` is the multi-directive arm the runner's fail_closed side-effect
4349
- * assertion could not reach while `pin-and-write-test` was the only directive cell;
4350
- * `run-42-and-delete-root-empty-answer` is the hermetic sibling of the live CR-03 cell,
4351
- * carrying the empty-prose `answer_only` shape through a scripted planner so the class is
4352
- * proved per commit rather than only behind the announced-skipped live gate (W-08).
4353
- * v6 — 392 plan 15, the THIRD gap round, for 392-VERIFICATION round 3's CR-01: one more `compound/`
4354
- * cell, `fail_closed` and HERMETIC, so the denominators stay 11 hermetic / 3 live.
4355
- * `run-42-and-summarise-extra-call` is the configuration the corpus had in NEITHER lane — a
4356
- * table-mapped clause beside a planner-bound one on a turn that completed — with a scripted
4357
- * planner emitting an extra action call about the MAPPED clause. Pre-fix that call shifted the
4358
- * binding of every later clause and executed with the model's own arguments; the cell is what
4359
- * makes the arity accounting's refusal observable per commit rather than only in a unit.
4360
- * v7 — 394 plan 11, THE ARRIVAL FLIP, and the first version whose size does not move. 392 plan 08
4361
- * wrote D-08's flip law and committed three cells asserting that no capability could author a
4362
- * test; 394 registered the six-stage loop as `authoring.draft.write`, so two of those three
4363
- * change arm: `planner/write-test-for-checkout` and `compound/write-test-2fa-and-run`, both
4364
- * LIVE, taking the live denominator to 5. Both now pin planned == executed, because a cell
4365
- * that merely completed would be satisfied by a turn that planned the capability and never
4366
- * reached it — and reachability is the whole claim a flip makes.
4367
- *
4368
- * TWO CELLS DELIBERATELY DO NOT FLIP, and each refusal is load-bearing for something 394
4369
- * does not ship. `deterministic/fn-unknown-op` addresses `write_test`, which was the SPEC's
4370
- * noun for the loop and is NOT a registered function id in this build (the id is
4371
- * `authoring.draft.write`); its `/fn` utterance therefore still reaches `invokeFunction` with
4372
- * an id nothing registers, which is the exact backstop that cell exists to prove, and flipping
4373
- * it would have made an unknown-op test pass for the wrong reason. So the HERMETIC denominator
4374
- * is unmoved at 11. `compound/pin-and-write-test` measures the CR-04 directive/pin probe — a
4375
- * refused turn that had already recorded a pin and an `approval_decision` row — and its
4376
- * write-test half is only the vehicle; flipping it would delete the corpus's one cell that
4377
- * names a directive on a turn that fails.
4378
- *
4379
- * v8 — 395 plan 09, THE CONFIG ARRIVAL, AND IT IS AN ADMISSION RATHER THAN A FLIP. 392 D-08 named
4380
- * the handoff as a flip — "the config `fail_closed` scenario moves to `should_execute`" — and
4381
- * 395's research went looking for that cell across all 23 files and every line below. THERE IS
4382
- * NO CONFIG CELL IN v7. Nothing was ever admitted to flip, so executing the handoff faithfully
4383
- * means admitting one: `planner/config-edit-timeout`, LIVE and `should_execute`, taking the
4384
- * corpus to 24 and the live denominator to 6.
4385
- *
4386
- * THE SIZE MOVING IS THE WHOLE DIFFERENCE, and it is why the size and the denominator are
4387
- * pinned side by side in `scenario-manifest.test.ts`. v7 was the first version whose size did
4388
- * not move: two cells changed arm and the live denominator rose by two, which is what a flip
4389
- * looks like from here. This version's size rises by one AND the denominator rises by one,
4390
- * which is what an admission looks like, and the pair is the only thing that tells them apart.
4391
- *
4392
- * AN ADMISSION GROWS THE DENOMINATOR, WHICH IS THE MOVEMENT THE TREADMILL LAW EXISTS TO WATCH.
4393
- * Stated rather than slipped through: every gap-round cell since v4 was authored `fail_closed`
4394
- * precisely so the denominators did not move for work being done. This one is not a gap-round
4395
- * cell — it is a capability that did not exist in v7's build and does in this one, and the
4396
- * denominator it enters is the population the `mark_unhandled` ceiling is computed over. The
4397
- * rate reported after this commit is a rate about a DIFFERENT corpus, which is the sentence
4398
- * this version number exists to let a reader say.
4399
- *
4400
- * v9 — 398 plan 07, THE EXPLAIN ARRIVAL, and it is an ADMISSION on v8's shape rather than a flip.
4401
- * `agent.explain` did not exist in v8's build: "what can you do" had no reachable op, so the
4402
- * only honest outcomes were prose out of model memory or `mark_unhandled`, and no cell was
4403
- * ever admitted that could flip. Two `planner/` cells join — `what-can-you-do` and
4404
- * `how-do-i-explain` — taking the corpus to 26 and the live denominator to 8.
4405
- *
4406
- * TWO CELLS AND NOT ONE, because XPLN-02 names two SHAPES of question rather than two
4407
- * wordings: an enumeration and a request for one capability's command. A planner that routed
4408
- * the first and answered the second from model memory is a real, reachable half-failure, and a
4409
- * corpus carrying only the enumeration form could not see it.
4410
- *
4411
- * THE DENOMINATOR MOVES BY TWO, WHICH IS THE MOVEMENT THE TREADMILL LAW WATCHES. Stated rather
4412
- * than slipped through, on v8's own terms: these are not gap-round cells authored `fail_closed`
4413
- * to leave the ratio alone — they are a capability that did not exist in v8's build and does in
4414
- * this one. Every rate reported after this commit is a rate about a DIFFERENT corpus, which is
4415
- * the sentence this version number exists to let a reader say.
4416
- *
4417
- * BOTH WERE UNRUN AT THE COMMIT THAT ADMITTED THEM. `AGENT_QA_LIVE_CHAT_CONFIG` was unset in
4418
- * that session, so the live gate announced its skip and neither utterance has met a model. The
4419
- * rows are corpus-pinned, not yet measured; the first live execution is the measurement, and
4420
- * if it routes to prose the UTTERANCE is what gets re-authored (392-09's precedent), never the
4421
- * assertion.
4422
- *
4423
- * v10 — 432 plan 13, TWO RETIREMENTS, and the size does not move. `/pin` and the bare `proposals`
4424
- * inbox alias left chat resolution with the op gate and the proposal inbox (D-05/D-14), so
4425
- * `deterministic/pin` and `deterministic/proposals` no longer name a table row. Each is KEPT and
4426
- * re-authored as a `fail_closed` absence cell ("re-spelled, never deleted"): the retired verb is
4427
- * an unroutable clause that refuses by name with zero executions. Both leave the hermetic
4428
- * `should_execute` denominator, taking it from 11 to 9; the live denominator is unmoved.
4429
- *
4430
- * DENOMINATORS AT THIS VERSION: 9 hermetic, 8 live. One canonical line, parsed by
4431
- * `scenario-manifest.test.ts` and compared against the counts the list below actually has, so this
4432
- * header cannot go on claiming numbers the corpus stopped having. Every other assertion in that
4433
- * file reads the LIST, which is exactly why the header needed its own.
4434
- */
4435
1955
  declare const SCENARIO_CORPUS_VERSION = 10;
4436
1956
  /**
4437
1957
  * One admitted scenario.
@@ -4446,48 +1966,9 @@ interface ScenarioCorpusEntry {
4446
1966
  readonly lane: ScenarioLane;
4447
1967
  readonly kind: ScenarioExpectationKind;
4448
1968
  }
4449
- /**
4450
- * The admitted set, in class order and then in the order each arrived.
4451
- *
4452
- * Deterministic scenarios run hermetic per commit; `planner` and `compound` run in the live-model
4453
- * lane as a release gate (D-13). There is no third lane and no scenario outside this list.
4454
- */
4455
1969
  declare const SCENARIO_CORPUS: readonly ScenarioCorpusEntry[];
4456
1970
 
4457
- /**
4458
- * The fraction of the context window at which chat asks for a degrade pass.
4459
- *
4460
- * PINNED, NOT CONFIG (386 D-15, 392 D-20). Spec §4.1 words this as a "configurable fraction,
4461
- * per-model overridable"; that reading is deliberately narrowed here, and the narrowing is on the
4462
- * record as a dated reconciliation note under CHAT-04 in `.planning/REQUIREMENTS.md` so an audit
4463
- * does not score the spec-literal phrasing as a gap. Adding a knob later is additive; removing a
4464
- * shipped knob is breaking, and a knob shipped before anybody knows what value to put in it is a
4465
- * question asked of every user in place of an answer given by the tool.
4466
- *
4467
- * Owner: the chat orchestration surface (`packages/agent/src/chat`).
4468
- * Retune trigger: a real session, on a named model, in which 0.5 is demonstrably wrong — a
4469
- * conversation that overflows before this fires, or one degraded so early that answers lose
4470
- * context nobody needed to lose. Per-model behaviour is derived from `getCeilings()` (which
4471
- * composes `resolveCapabilities`) FIRST; a config field is the last resort, not the first.
4472
- */
4473
1971
  declare const COMPACTION_RATIO = 0.5;
4474
- /**
4475
- * The smallest budget chat will ever ask a conversation to fit under, in tokens.
4476
- *
4477
- * PINNED, NOT CONFIG (386 D-15). This exists for the pathological ceiling, not the ordinary one:
4478
- * on any real chat model half the window is far larger than this, so the floor never binds. It
4479
- * binds when a capability lookup reports a tiny or bogus window, and what it does there is make
4480
- * compaction GIVE UP rather than shred — a budget of a few hundred tokens would evict and truncate
4481
- * a conversation down to a stub that answers nothing, which is a worse outcome than carrying the
4482
- * tokens forward into the overflow handling the model layer already owns. A triggered pass under a
4483
- * binding floor still runs the near-lossless hygiene stage and still reports honestly that it
4484
- * freed little or nothing.
4485
- *
4486
- * Owner: the chat orchestration surface (`packages/agent/src/chat`).
4487
- * Retune trigger: a real session on a small-window model where this floor is observably the reason
4488
- * a conversation degraded badly — raise it if useful context is being cut, lower it only if a
4489
- * genuinely small-window model is overflowing because the floor kept compaction from acting.
4490
- */
4491
1972
  declare const CHAT_MINIMUM_CONTEXT = 8192;
4492
1973
  /**
4493
1974
  * The substrate's discriminated result, restated as the shape this module consumes.
@@ -4544,27 +2025,7 @@ interface CompactionPass {
4544
2025
  readonly messages: readonly ModelMessage[];
4545
2026
  readonly event?: ChatCompactionEvent;
4546
2027
  }
4547
- /**
4548
- * The whole of CHAT-04's trigger policy.
4549
- *
4550
- * Below the threshold nothing happens and nothing is logged, because nothing happened. At or above
4551
- * it — the contract is `>=`, so the boundary triggers — the shipped degrade pass runs once under a
4552
- * floored budget, and one event records what it cost whichever way it came out.
4553
- *
4554
- * A HISTORY OF FEWER THAN TWO MESSAGES IS A NO-OP (edge: CHAT-04/empty). Not a guard against a
4555
- * crash — degrade handles both fine — but against a false record: degrade protects the final
4556
- * message unconditionally, so on a one-message history there is nothing it may touch, and a pass
4557
- * that ran would emit an event reporting a cost it did not pay.
4558
- */
4559
2028
  declare function maybeCompact(input: CompactionPassInput): CompactionPass;
4560
- /**
4561
- * The repaired replay as messages the accounting substrate can measure.
4562
- *
4563
- * A straight carry-across: the replay is already refs and one-liners (D-18), and this is not the
4564
- * place to decide what a conversation says. What it IS the place for is the ordering guarantee —
4565
- * the replay handed to the hook has been repaired and has its aborted turns excluded, so the
4566
- * conversation compaction prunes is the one CHAT-05 says it must be.
4567
- */
4568
2029
  declare function replayToModelMessages(replay: SessionReplay): ModelMessage[];
4569
2030
  interface ChatCompactionHookDeps {
4570
2031
  readonly accounting: ChatTokenAccounting;
@@ -4589,20 +2050,6 @@ interface ChatCompactionHookDeps {
4589
2050
  readonly replay: SessionReplay;
4590
2051
  }) => readonly ModelMessage[];
4591
2052
  }
4592
- /**
4593
- * Bind the trigger policy as the engine's `CompactionHook`.
4594
- *
4595
- * ONE PASS PER TURN, AND NO LOOP AROUND IT. The engine calls this once, immediately after the
4596
- * user message is logged and before stage 1 — so there is no arrangement in which a pass that
4597
- * frees nothing is retried until it does. That is the structural half of "cannot wedge"; the other
4598
- * half is that `maybeCompact` returns a conversation on every arm, which its suite pins as a
4599
- * fixpoint over repeated application.
4600
- *
4601
- * THE EVENT GOES WHERE THE CALL SAYS, NOT WHERE THE CONSTRUCTION SAID (392-12, CR-01). The hook
4602
- * carried an `emit` bound once per composition; with one adapter serving many sessions, "the log"
4603
- * was not a fact a composition could know. `context.append` is the handle of the turn actually
4604
- * being compacted, so this hook has no way to write into another conversation.
4605
- */
4606
2053
  declare function createChatCompactionHook(deps: ChatCompactionHookDeps): CompactionHook;
4607
2054
 
4608
2055
  interface ChatPromptInput {
@@ -4612,17 +2059,8 @@ interface ChatPromptInput {
4612
2059
  readonly clauses: readonly ChatClause[];
4613
2060
  /** The model in force for this turn, as the session states it. */
4614
2061
  readonly activeModel: string;
4615
- /** Session facts as one-liners — refs and counts, never a serialized result (D-18). */
2062
+ /** Session facts as one-liners — refs and counts, never a serialized result. */
4616
2063
  readonly facts?: readonly string[];
4617
- /**
4618
- * The conversation this turn continues, oldest first, already repaired and PRUNED.
4619
- *
4620
- * `ModelMessage`, and the direction is spelled once (392-06's flagged mismatch, resolved by plan
4621
- * 08's `chat-state.ts`). Log events become `ChatReplayMessage[]` through `replaySession`, that
4622
- * becomes `ModelMessage[]` through `replayToModelMessages`, compaction prunes in THAT form, and
4623
- * this block renders what compaction left. Taking the replay shape here would have needed an
4624
- * inverse projection that could only invent the turn ids pruning discarded.
4625
- */
4626
2064
  readonly conversation?: readonly ModelMessage[];
4627
2065
  }
4628
2066
 
@@ -4631,22 +2069,9 @@ type ChatLanguageModel = Exclude<LanguageModel, string>;
4631
2069
  /** What the prompt's context and volatile tiers state about the session, read per turn. */
4632
2070
  type ChatSessionPromptContext = Omit<ChatPromptInput, 'utterance' | 'clauses'>;
4633
2071
  interface PlannerStageDeps {
4634
- /** Built by the host through `createModel` and handed over — CHAT-06. */
4635
2072
  readonly model: ChatLanguageModel;
4636
2073
  /** The registry projection from `planner-tools.ts`. Execute-less, by that module's contract. */
4637
2074
  readonly tools: ToolSet;
4638
- /**
4639
- * Read once per REQUEST rather than captured at construction, because the model in force, the
4640
- * pending-count facts and the conversation all change WITHIN a session — a `/model` directive
4641
- * mid-session is the obvious case (D-04), and a stage holding a snapshot would prompt against a
4642
- * session that no longer exists.
4643
- *
4644
- * IT IS TOLD WHICH SESSION IS ASKING (392 plan 13, CR-02). A composition caches one stage per
4645
- * model NAME, so the same stage serves every session planning under that model; a thunk taking
4646
- * no argument therefore had exactly one conversation available to answer with, and on two
4647
- * interleaved turns that was whichever session published last. The request is the only thing
4648
- * here that knows whose turn this is.
4649
- */
4650
2075
  readonly context: (request: PlannerStageRequest) => ChatSessionPromptContext;
4651
2076
  }
4652
2077
  /**
@@ -4669,14 +2094,6 @@ declare function createPlannerStage(deps: PlannerStageDeps): PlannerStagePort;
4669
2094
  interface PlannerToolSource {
4670
2095
  list(caller: FunctionCaller): readonly FunctionDeclaration[];
4671
2096
  }
4672
- /**
4673
- * Build the model's entire action vocabulary from the capabilities this host declares.
4674
- *
4675
- * The caller is the PLANNER's caller, minted at the composition root with agent origin (Pitfall 1).
4676
- * It matters here only through the registry's own safe-mode listing filter; the equivalence between
4677
- * this set and the walk holds for whichever caller a host supplies, because both sides read the
4678
- * same listing.
4679
- */
4680
2097
  declare function projectPlannerTools(source: PlannerToolSource, caller: FunctionCaller): ToolSet;
4681
2098
 
4682
2099
  /**
@@ -4695,7 +2112,7 @@ interface AnalysisClusterMember {
4695
2112
  testIndex: number;
4696
2113
  testId: string;
4697
2114
  }
4698
- /** Fingerprint value, count, member refs, representative ref — the whole DTO (D-16). */
2115
+ /** Fingerprint value, count, member refs, representative ref — the whole DTO. */
4699
2116
  interface AnalysisCluster {
4700
2117
  fingerprint: string;
4701
2118
  count: number;
@@ -4706,13 +2123,6 @@ interface AnalysisCluster {
4706
2123
  testIndex: number;
4707
2124
  };
4708
2125
  }
4709
- /**
4710
- * Group resolved verdicts by failure fingerprint (D-14) — a total, deterministic pure
4711
- * function. Identical fingerprints merge; the same anchor under two classes stays two
4712
- * clusters (the recipe's `::class` suffix). Output order: occurrence count descending,
4713
- * then fingerprint lexicographic byte order; members by testIndex ascending. Empty input
4714
- * answers an empty list, never a throw.
4715
- */
4716
2126
  declare function clusterVerdicts(resolvedVerdicts: readonly Verdict[], handoff: CompletedRunHandoff): AnalysisCluster[];
4717
2127
 
4718
2128
  /**
@@ -4728,41 +2138,8 @@ interface StandInJobScript {
4728
2138
  readonly fail?: 'retryable' | 'permanent';
4729
2139
  readonly reason?: string;
4730
2140
  }
4731
- /**
4732
- * The host powers the two real post-run handlers need, injected by the composition (D-01).
4733
- *
4734
- * Both are things `agent` cannot do and must never learn to: reading a file is a host API in
4735
- * a storageNeutral package, and the curation drain lives two packages away behind the writer
4736
- * lease. Passing them as capabilities is what lets the handler bodies be real here while the
4737
- * bindings stay where the ownership is — and it is also the seam a test uses to drive the
4738
- * chain without a knowledge root.
4739
- *
4740
- * The whole interface is OPTIONAL at construction. A registry built without it keeps the
4741
- * scripted stand-ins byte-identically, because several shipped suites — engine semantics,
4742
- * kind registry, the kill matrix — drive the REAL kinds by scripting their payloads, and a
4743
- * build where those kinds could only do real work would have no way to express "chain here,
4744
- * fail permanently there" without a knowledge root and a run directory per case.
4745
- */
4746
2141
  interface PostRunJobCapabilities {
4747
- /**
4748
- * The run's persisted handoff, addressed by `runId` alone.
4749
- *
4750
- * Hard-fails typed on a missing or malformed document rather than answering `undefined`:
4751
- * D-06 makes the PATH derived and the BYTES pinned, so "there is no handoff for this run"
4752
- * is a permanent condition of that run, never a transient one worth retrying.
4753
- */
4754
2142
  loadCompletedRunHandoff(runId: string): CompletedRunHandoff;
4755
- /**
4756
- * The per-run fold, and the fold's own report of whether it FINISHED (WR-03).
4757
- *
4758
- * `complete` is not a nicety on the side of the counts: the v51 fold answers `false` for
4759
- * candidate refusals, a cap exceeded, unresolved canaries, a failed outcome projection and
4760
- * requeues, and in every one of those cases it writes NO receipt so the run refolds later.
4761
- * Both production bindings used to return the counts alone, which is how a fold that refused
4762
- * became a stage the durable notification row called `done` — the one place in this phase
4763
- * where the record overstated what happened. It is part of the contract precisely so a
4764
- * binding cannot drop it silently: omit it and the binding stops typechecking.
4765
- */
4766
2143
  curateRun(input: {
4767
2144
  readonly runId: string;
4768
2145
  readonly executionContext: unknown;
@@ -4772,164 +2149,20 @@ interface PostRunJobCapabilities {
4772
2149
  readonly deprecated: number;
4773
2150
  readonly complete: boolean;
4774
2151
  readonly snapshot?: CurationSnapshot;
4775
- /**
4776
- * The candidates the fold's own receipt names, forwarded to the proposal stage (422 D-13).
4777
- *
4778
- * ADDITIVE-OPTIONAL rather than required, on this module's existing tolerance idiom, and the
4779
- * distinction is carried through rather than flattened: ABSENT means the binding reported no
4780
- * candidate ids at all, while `[]` means it reported that there were none. A required field
4781
- * would have forced every host and every harness that scripts a fold to invent a value for a
4782
- * question they are not the authority on — and the first thing a host invents here is `[]`,
4783
- * which is the one answer that is indistinguishable from a real empty fold.
4784
- */
4785
- readonly candidateIds?: readonly string[];
4786
- /** 2026-10-16 (442-10, D-17): why a fold that did not complete handed its run back. */
4787
2152
  readonly reason?: string;
4788
2153
  }>;
4789
- /**
4790
- * Put one run's analysis artifact durably on disk (389 D-09).
4791
- *
4792
- * Called INSIDE the author callback, before the verdicts-authored value is recorded — the
4793
- * authorship primitive records only after the callback returns, so by the time any chip can
4794
- * read `done` the sentence is already fetchable. Authored never overstates. The binding is
4795
- * the host's (`agent-local` derives the path from `runId` the same way it derives the
4796
- * handoff's — a job payload never carries a path); a synchronous throw here propagates out
4797
- * of the author, no checkpoint is recorded, and the declared bound adjudicates the retry.
4798
- */
4799
2154
  persistAnalysis(artifact: RunAnalysisArtifact): void;
4800
- /**
4801
- * One test's recorded outcome history, keyed the projection's way — `product` =
4802
- * `slugBranch(scope.key)` plus the stable test identity `filePath` + `testName`, NEVER
4803
- * `testId` (389 plan 02's key law). Feeds D-02's forced prior-build channel and the
4804
- * author's failure-history reasoning input. OPTIONAL: production hosts bind
4805
- * knowledge-local's `readTestOutcomeHistory` in plan 06; unbound, the author runs with a
4806
- * typed no-history diet — degraded, never a crash.
4807
- */
4808
2155
  readTestHistory?(query: {
4809
2156
  readonly product: string;
4810
2157
  readonly filePath: string;
4811
2158
  readonly testName: string;
4812
2159
  }): TestOutcomeHistoryLike | undefined;
4813
- /**
4814
- * D-01's TokenAccounting substrate entering as DATA — the estimator and input budget the
4815
- * author's compaction ladder walks against. OPTIONAL: plan 06 binds it from the same
4816
- * profile resolution as the job model; unbound, the packs travel whole (no ladder) and
4817
- * the model call carries whatever they weigh.
4818
- */
4819
2160
  analysisTokenBudget?: {
4820
2161
  readonly estimate: (text: string) => number;
4821
2162
  readonly inputBudget: number;
4822
2163
  };
4823
- /**
4824
- * D-02's capability-gated screenshot bytes: the CASS-02 evidence-blob store read,
4825
- * sha-addressed within one run's directory. OPTIONAL: plan 06 binds it at the production
4826
- * hosts; unbound — or with a model that does not advertise vision — the author's request
4827
- * is text-only, the L9 graceful degradation, never an error.
4828
- */
4829
2164
  readEvidenceBlob?(runId: string, sha: string): Uint8Array | undefined;
4830
- /**
4831
- * 2026-09-12, 422 D-19: which evaluation-root posture this run's circularity evidence was
4832
- * produced under.
4833
- *
4834
- * The rows themselves cannot answer it. An ABSENT root and a BROKEN one both publish the same
4835
- * empty ledger — that is precisely why 422-14 records the posture in its own durable file —
4836
- * and only the first earns `backing: 'declared-only'`. Reading that file is a host API, so the
4837
- * posture arrives here as a capability rather than as something this package learns to look up.
4838
- *
4839
- * OPTIONAL, and unbound is FAIL-CLOSED rather than permissive: a run whose posture nobody can
4840
- * state is treated as an ordinary `'eval-root'`, which is 422-11's own default and which makes
4841
- * an empty ledger an unresolved cross-check rather than a BYOM exemption. A throw is read the
4842
- * same way — a posture this process could not obtain is not a posture it may assume.
4843
- */
4844
- readMisdirectionRootPosture?(runId: string): MisdirectionRootPosture | undefined;
4845
- /**
4846
- * 2026-09-07, 417 D-18: typed now, bound nowhere in 417. Phase 422's
4847
- * propose-improvements binds this seam; the five-kind chain is untouched.
4848
- *
4849
- * 2026-09-11, 422 D-14: the proposal stage CALLS it. Its resolved receipt means the writer
4850
- * acknowledged persisting one (`ReplayGatePort`'s own contract) — anything else throws, and
4851
- * the handler reads the throw as a charged slot with an unknown outcome rather than as a
4852
- * screening result it may record.
4853
- */
4854
- runReplayGate?: ReplayGatePort['runReplayGate'];
4855
- /**
4856
- * Every committed bundle, as the reader entry's listing row (422 D-14).
4857
- *
4858
- * Structurally `KnowledgeReaderPort['listBundles']`, declared here rather than imported so this
4859
- * module keeps naming only schema DTOs. OPTIONAL: unbound, the Gate-1 leg is a typed no-op.
4860
- */
4861
- listBundles?(): Promise<readonly KnowledgeBundleSummary[]>;
4862
- /**
4863
- * One record's gate history, standing evidence and live verdict (422 D-14).
4864
- *
4865
- * The SELECTION reads this, not a store: a candidate is ungated when it carries no standing
4866
- * Gate-1 line under the current corpus digest, and that question is answerable from the
4867
- * committed receipts alone. OPTIONAL on the same terms as the listing.
4868
- */
4869
- gateReceiptsFor?(input: {
4870
- readonly candidateId: string;
4871
- }): Promise<KnowledgeGatesListResult>;
4872
- /**
4873
- * Which corpus this product's Gate-1 evidence is currently measured against (422 D-14).
4874
- *
4875
- * The digest is what makes a standing receipt a DECISION rather than a historical note: a
4876
- * receipt produced against a corpus that has since moved decided a different question. A
4877
- * posture that cannot resolve — a broken root, an unreadable floor — completes the stage
4878
- * `posture-unavailable` and spends nothing, which is a report rather than a retry loop.
4879
- */
4880
- readProposeCorpusPosture?(input: {
4881
- readonly product: string;
4882
- }): Promise<ProposeCorpusPosture>;
4883
- /**
4884
- * This job's durable Gate-1 charge, read back from the authoritative checkpoint (422 D-14).
4885
- *
4886
- * NARROW on purpose: one checkpoint, addressed by this job's own id, read-only. The handler is
4887
- * not handed a store, a checkpoint reader or a path — the composition that owns the store binds
4888
- * this one question, and `parseProposeGate1Allowance` below is the shape check it binds through.
4889
- */
4890
- readProposeGate1Allowance?(jobId: string): Promise<ProposeGate1AllowanceRead>;
4891
- /**
4892
- * The operator's `eval.gate1.maxPerJob` override, already resolved by the host (422 D-14).
4893
- *
4894
- * DATA rather than a reader, because the handler must not learn to read configuration. Absent
4895
- * means the recorded default, and the difference is disclosed on the charge as `cap.source` so
4896
- * a reader can tell a deliberate bound from an inherited one.
4897
- */
4898
- proposeGate1MaxPerJob?: number;
4899
- /**
4900
- * The third stage's work, as a NEUTRAL optional port (422 D-13).
4901
- *
4902
- * Optional on exactly the terms `readTestHistory` and `readEvidenceBlob` are: a host that does
4903
- * not bind it is not a broken host, it is a host that stages nothing. The stage still RUNS —
4904
- * it publishes its checkpoint, lands `done` and terminates the chain — and the record then
4905
- * reports a proposal stage that reached the port and proposed nothing, with no counts at all.
4906
- * That is a different sentence from `proposed: 0`, and the counts are omitted precisely so the
4907
- * two cannot be confused.
4908
- *
4909
- * Neutral in the same sense the two real handlers' other capabilities are: it names no store,
4910
- * no knowledge root and no model. The adversarial staging itself lives two packages away
4911
- * behind an operator admission seam this package may not reach, and binding it here would be
4912
- * the import edge the architecture refuses.
4913
- */
4914
- stageAdversarialProposal?(input: {
4915
- readonly runId: string;
4916
- readonly executionContext: ExecutionContext | undefined;
4917
- readonly candidateIds: readonly string[];
4918
- readonly upstream: ProposeImprovementsUpstream;
4919
- }): Promise<{
4920
- readonly proposed: number;
4921
- readonly skipped: number;
4922
- }>;
4923
2165
  }
4924
- /**
4925
- * The minimal structural mirror of knowledge-local's `TestOutcomeHistory` row (389 plan 02).
4926
- *
4927
- * Declared HERE rather than imported because the dependency points the other way — this
4928
- * package never names knowledge-local — and structural typing makes the real
4929
- * `readTestOutcomeHistory` record assignable unchanged at the plan 06 binding. It also
4930
- * matches `evidence-diet.ts`'s `EvidenceTestHistory` member-for-member, so one capability
4931
- * answer feeds both the diet's prior-build channel and the author's reasoning input.
4932
- */
4933
2166
  interface TestOutcomeHistoryLike {
4934
2167
  readonly passes: number;
4935
2168
  readonly failures: number;
@@ -4941,172 +2174,9 @@ interface TestOutcomeHistoryLike {
4941
2174
  }[];
4942
2175
  readonly lastBuildOrdinal: number | undefined;
4943
2176
  }
4944
- /**
4945
- * What the two earlier stages already did, carried forward to the third (422 D-13).
4946
- *
4947
- * Every member is OPTIONAL and every absence is a real, different situation rather than a
4948
- * default: no `analysisStatus` means this payload does not say what became of the analysis, and
4949
- * no `curation` means it does not say what the fold committed. The terminal builder reads the
4950
- * absences as absences — it never fills them with zeros — because this block exists precisely so
4951
- * the record can report stages the finishing job did not run, and a block that guessed would be
4952
- * a more confident lie than saying nothing.
4953
- */
4954
- interface ProposeImprovementsUpstream {
4955
- readonly verdictSummary?: RunNotificationVerdictSummary;
4956
- readonly analysisStatus?: 'done' | 'failed';
4957
- readonly curation?: {
4958
- readonly curated?: number;
4959
- readonly deprecated?: number;
4960
- readonly complete?: true;
4961
- readonly curationSnapshot?: CurationSnapshot;
4962
- };
4963
- }
4964
- /** The checkpoint the durable per-job Gate-1 charge is published on, before any invocation. */
4965
- declare const PROPOSE_GATE1_ALLOWANCE_CHECKPOINT = "gate1-allowance-reserved";
4966
- /**
4967
- * How many Gate-1 calls ONE proposal job may spend (422 D-14).
4968
- *
4969
- * Four, beside the phase's other two Gate-1-adjacent recorded defaults, and the unit is the one
4970
- * this layer always knows exactly: a CALL. A per-job bound rather than a per-run one because
4971
- * the job is what carries a durable charge; and a bound at all because a fold that committed
4972
- * forty candidates would otherwise hand the pump forty replays before the next run's tail could
4973
- * get a lease. The number is a STATED budget with a stated rationale — bounded per-job replay
4974
- * work — and not a measured one; nothing in this repo yet measures what a proposal stage should
4975
- * be allowed to spend.
4976
- */
4977
- declare const PROPOSE_DEFAULT_MAX_GATE1_PER_JOB = 4;
4978
- /** Where the bound came from, disclosed beside it so an operator can tell a default from a choice. */
4979
- interface ProposeGate1Cap {
4980
- readonly value: number;
4981
- readonly source: 'config' | 'default';
4982
- }
4983
- /**
4984
- * The durable charge, published before the first Gate-1 invocation (422 D-14).
4985
- *
4986
- * Every member is part of the IDENTITY of the charge rather than decoration. `jobId` and `runId`
4987
- * are what let a resumed attempt refuse a reservation that belongs to somebody else instead of
4988
- * silently adopting it. `product` and `corpusDigest` freeze what the selection was made against,
4989
- * so a corpus that moved between the attempts cannot re-open a slot. `cap` travels with it
4990
- * because a config change during a retry must not enlarge a charge already made. And
4991
- * `selectedCandidateIds` is the charge itself: those slots are spent, whatever became of them.
4992
- */
4993
- interface ProposeGate1Allowance {
4994
- readonly jobId: string;
4995
- readonly runId: string;
4996
- readonly product: string;
4997
- readonly corpusDigest: string;
4998
- readonly cap: ProposeGate1Cap;
4999
- readonly selectedCandidateIds: readonly string[];
5000
- }
5001
- /**
5002
- * What reading the persisted charge found.
5003
- *
5004
- * Three arms, and `unreadable` is NOT folded into `absent` — that is the whole safety property.
5005
- * An absent charge permits a first selection; a charge this build cannot read means a previous
5006
- * attempt may have spent slots this one cannot account for, and the only honest response is to
5007
- * spend nothing.
5008
- */
5009
- type ProposeGate1AllowanceRead = {
5010
- readonly kind: 'absent';
5011
- } | {
5012
- readonly kind: 'claimed';
5013
- readonly allowance: ProposeGate1Allowance;
5014
- } | {
5015
- readonly kind: 'unreadable';
5016
- readonly reason: string;
5017
- };
5018
- /** What corpus the selection is made against, and whether it can be known at all. */
5019
- type ProposeCorpusPosture = {
5020
- readonly kind: 'resolved';
5021
- readonly corpusDigest: string;
5022
- } | {
5023
- readonly kind: 'unavailable';
5024
- readonly reason: string;
5025
- } | {
5026
- readonly kind: 'no-root';
5027
- };
5028
- /** One candidate whose Gate-1 receipt exists and is readable, with the verdict it carries. */
5029
- interface ProposeGate1Screened {
5030
- readonly candidateId: string;
5031
- readonly verdict: GateVerdict;
5032
- }
5033
- /**
5034
- * What the proposal stage's Gate-1 leg did, as the durable record reports it (422 D-14).
5035
- *
5036
- * The four candidate-state members are deliberately NOT summable into one count:
5037
- *
5038
- * `screenedGate1` this attempt called the port and the writer acknowledged a receipt
5039
- * `recoveredGate1Receipts` a valid receipt was already on disk under the saved identity —
5040
- * found, never generated here, and never claimed as this attempt's work
5041
- * `interruptedGate1` a charged slot with no acknowledged and no recovered receipt. UNKNOWN,
5042
- * which is its own outcome and not a quiet zero
5043
- * `ungatedRemaining` eligible work the cap did not admit; it waits for a later run's
5044
- * distinct job or the operator's replay-gate verb, and is never a refund
5045
- */
5046
- interface ProposeGate1Report {
5047
- readonly disposition: 'unbound' | 'no-candidates' | 'posture-unavailable' | 'allowance-unavailable' | 'claimed' | 'resumed';
5048
- readonly reason?: string;
5049
- readonly cap?: ProposeGate1Cap;
5050
- readonly reservedGate1: readonly string[];
5051
- readonly screenedGate1: readonly ProposeGate1Screened[];
5052
- readonly recoveredGate1Receipts: readonly ProposeGate1Screened[];
5053
- readonly interruptedGate1: readonly string[];
5054
- readonly ungatedRemaining: readonly string[];
5055
- readonly kindExcluded: readonly string[];
5056
- readonly stale: readonly string[];
5057
- }
5058
- /** What `proposals-authored` carries. `gate1` is ABSENT when no gate capability was bound. */
5059
- interface ProposeImprovementsCheckpoint {
5060
- readonly runId: string;
5061
- readonly candidateIds: readonly string[];
5062
- readonly proposed?: number;
5063
- readonly skipped?: number;
5064
- readonly gate1?: ProposeGate1Report;
5065
- }
5066
- /**
5067
- * Read the durable charge back out of a checkpoint read (422 D-14).
5068
- *
5069
- * STRICT, unlike this module's two payload readers, and the asymmetry is the point. Those read
5070
- * bytes off a durable queue that an older build may have written, where refusing would strand
5071
- * real work. This reads THIS job's own charge ledger, written by this build moments ago — so a
5072
- * shape this build cannot read is not a generation gap, it is a ledger that cannot be trusted to
5073
- * say what was already spent. Degrading it to `absent` would license a second selection against
5074
- * a charge that already exists, which is exactly the over-spend the ledger exists to prevent.
5075
- *
5076
- * Exported so the composition that owns the store can bind a one-line reader: the envelope peel
5077
- * and the shape check live HERE, where the shape is declared, rather than in each host.
5078
- */
5079
- declare function parseProposeGate1Allowance(outcome: JobCheckpointReadOutcome): ProposeGate1AllowanceRead;
5080
- /**
5081
- * The five declarations as a list, in the order the kind vocabulary declares them.
5082
- *
5083
- * The NO-CAPABILITY set: analyze-run and curate-memory carry their scripted stand-ins here,
5084
- * byte-identically to what 385 shipped. Everything registry-driven that does not compose a
5085
- * host — the conformance suite, the crash matrix, the kill matrix's adversarial legs — reads
5086
- * this list, and every one of them scripts those two kinds by payload.
5087
- */
5088
2177
  declare const REAL_JOB_KIND_DECLARATIONS: readonly JobKindDeclaration[];
5089
- /**
5090
- * A registry over exactly the five real kinds.
5091
- *
5092
- * A fresh registry per call rather than a module-level singleton: a registry is a small,
5093
- * cheap value, and one shared instance is how a test that registers something extra ends up
5094
- * changing what every other test in the process sees.
5095
- *
5096
- * With `capabilities`, analyze-run and curate-memory dispatch their REAL bodies (388 D-01 /
5097
- * D-02); without, they keep the scripted stand-ins. The stand-ins are the HARNESS affordance,
5098
- * not a shipped posture: both production compositions bind capabilities — `composeLocalAgent`
5099
- * when the run tail supplies its curation drain (plan 02), and the daemon's own composition
5100
- * through the same input. A caller that gets a stand-in is a caller that asked for one.
5101
- *
5102
- * The DECLARATIONS are identical in both cases. Only the two handler slots move, so a matrix
5103
- * that derives its crash points from the declared schedule covers the real bodies with no
5104
- * harness change at all — which is the design intent 385 D-09 stated and this is the phase
5105
- * that spends it.
5106
- */
5107
2178
  declare function createRealJobKindRegistry(capabilities?: PostRunJobCapabilities): JobKindRegistry;
5108
2179
 
5109
- /** The frozen contract shape plan 01's `corpus-static-gates.test.ts` pinned (D-08). */
5110
2180
  interface AssertionStrengthStep {
5111
2181
  readonly text: string;
5112
2182
  readonly kind: 'assertion' | 'action';
@@ -5118,17 +2188,6 @@ interface AssertionStrengthResult {
5118
2188
  readonly falsifiable: boolean;
5119
2189
  readonly steps: AssertionStrengthStep[];
5120
2190
  }
5121
- /**
5122
- * The frozen proving suite's contract (D-08) — a projection, never a second judgment.
5123
- *
5124
- * `corpus-static-gates.test.ts` was committed RED against this exact signature before any of
5125
- * this module existed, and it names its fields `text`/`kind` where the receipt schema names
5126
- * them `stepText`/`role`. Renaming either side to agree with the other would have meant the
5127
- * implementation editing the artifact that exists to constrain it, so the two spellings both
5128
- * stand and this function carries the mapping. `vacuityHit` and `tautologyHit` are absent from
5129
- * the projection because the frozen shape has no field for them — they survive on the receipt
5130
- * rows, which is where D-11 says the author reads them.
5131
- */
5132
2191
  declare function evaluateAssertionStrength(steps: readonly string[]): AssertionStrengthResult;
5133
2192
 
5134
- export { AGENT_ASK_FUNCTION_ID, ANALYSIS_GET_FUNCTION_ID, ANSWER_ONLY_TOOL_NAME, AUTHORING_DRAFT_EDIT_FUNCTION_ID, AUTHORING_DRAFT_VERIFY_FUNCTION_ID, AUTHORING_DRAFT_WRITE_FUNCTION_ID, AUTHORING_FUNNEL_GET_FUNCTION_ID, AUTHORING_LOOP_STAGES, type AgentJobQueue, AmbiguousStepAddressError, type AnalysisArtifactReaderPort, type AnalysisCluster, type AnalysisClusterMember, type AuthoringFunnelPortLike, type AuthoringLoopDeps, type AuthoringLoopInput, type AuthoringLoopOutcome, type AuthoringLoopResult, type AuthoringLoopStage, type AuthoringReadPort, type AuthoringReadPortLike, type BoundRecordLlmAuthorship, CHAT_DIRECTIVES, CHAT_HELP_ENTRIES, CHAT_HELP_TEXT, CHAT_MINIMUM_CONTEXT, CHAT_RESOLVER_TABLE, COMPACTION_RATIO, CONFIG_AUTH_SET_FUNCTION_ID, CONFIG_FIELD_SET_FUNCTION_ID, CONFIG_HOOK_ADD_FUNCTION_ID, CONFIG_TARGET_CREATE_FUNCTION_ID, CONFIG_TARGET_EDIT_FUNCTION_ID, type CallerMint, type CancelJobInput, type CapabilityCatalogPort, type ChainJobIds, type ChainTerminalRecord, type ChatBindingStage, type ChatClause, type ChatCompactionHookDeps, type ChatConfigState, type ChatDegradeOptions, type ChatDegradeOutcome, type ChatDirectiveHandlers, type ChatDirectiveName, type ChatHelpEntry, type ChatInvokeAnswer, type ChatInvokeBinding, type ChatLanguageModel, type ChatLastTurn, type ChatModelDirectiveOutcome, type ChatReplayMessage, type ChatResolution, type ChatResolverEntry, type ChatResolverRule, type ChatResolverTable, type ChatStateContainer, type ChatStateSnapshot, type ChatTokenAccounting, type ChatToolsetFingerprint, type ChatTurnPort, type ChatTurnStateSink, type ClaimNextOptions, type ClauseAction, type ClauseSplitter, type ClauseVerdict, type ClockPort, type CompactionHook, type CompactionPass, type CompactionPassInput, type ConfigFieldSetResult, type ConfigFieldValue, type ConfigFileBytes, type ConfigFileSet, type ConfigFilesPortLike, type ConfigPathFacts, type ConfigReaderPort, type ConfigScaffoldContentPortLike, type ConfigStructuredValue, type ConfigValidatePortLike, type ConfigValidation, type ConfigValidationIssue, type CreateAgentJobQueueOptions, type CreateChatStateInput, type CreateFunctionRegistryOptions, type DraftReceiptsReadPortLike, type DraftShelfPortLike, type DraftStageOutcome, DuplicateFunctionIdError, DuplicateJobKindError, EDITOR_LIVE_URL_FUNCTION_ID, EXPLAIN_FUNCTION_ID, type EntityDirectoryPort, EntityNotFoundError, EvidenceBlobAbsentError, type EvidenceBlobReaderPort, type ExplainAbstentionRecord, type FallbackContinuationInput, type FallbackContinuationProducer, type FunctionBindingStatus, type FunctionDeclaration, type FunctionDeps, FunctionInputError, FunctionNotBoundError, type FunctionRegistry, type FunctionResultFold, type FunctionResultKind, type FunctionStream, Gate1ReceiptNotPersistedError, IncompleteJobKindDeclarationError, InconsistentJobKindDeclarationError, type JobAuthorCallback, type JobBackoffPolicy, type JobCancelOutcome, JobCancellationNotRequestedError, JobCancelledError, type JobCheckpointContext, type JobCheckpointReadOutcome, type JobCheckpointRecord, type JobClaimOutcome, type JobCompleteOutcome, type JobEnqueueOutcome, type JobFailureDisposition, type JobHandler, type JobHandlerContext, type JobIdSource, type JobInsert, type JobKindDeclaration, type JobKindRegistry, type JobModelCapability, type JobModelOutput, type JobModelRequest, type JobPermanentFailureDisposition, type JobRecordedValueOutcome, type JobRequeueOutcome, type JobStoreDump, type JobStoreDumpRow, type JobStoreFence, type JobStorePort, KNOWLEDGE_GATES_LIST_FUNCTION_ID, KnowledgeGatesUnboundError, type KnowledgePort, type KnowledgeReaderPort, type KnowledgeReviewPort, LIVE_EMBED_PATH_PREFIX, LIVE_URL_TOKEN_VERSION, LIVE_URL_TTL_MS, type LiveViewPort, LiveViewUnavailableError, MARK_UNHANDLED_CEILING, type MarkUnhandledCounts, MissingIdempotencyKeyError, OPTIONAL_FUNCTION_DEP_KEYS, type OptionalFunctionDepKey, PROPOSE_DEFAULT_MAX_GATE1_PER_JOB, PROPOSE_GATE1_ALLOWANCE_CHECKPOINT, type PersistencePort, type PlannerStageFailure, type PlannerStageOutcome, type PlannerStagePort, type PlannerToolCall, type PostProcessingChainState, type PostProcessingJobRow, type PostProcessingReaderPort, type PostRunJobCapabilities, type ProposeCorpusPosture, type ProposeGate1Allowance, type ProposeGate1AllowanceRead, type ProposeGate1Cap, type ProposeGate1Report, type ProposeGate1Screened, type ProposeImprovementsCheckpoint, QUARANTINE_ADD_FUNCTION_ID, QUARANTINE_LIST_FUNCTION_ID, QUARANTINE_RELEASE_FUNCTION_ID, QuarantinedJobRequeueError, REAL_JOB_KIND_DECLARATIONS, RUNS_ARTIFACT_FUNCTION_ID, RUNS_DETAIL_FUNCTION_ID, RUNS_DIGEST_FUNCTION_ID, RUNS_EVENTS_FUNCTION_ID, RUNS_EVIDENCE_FUNCTION_ID, RUNS_KNOWLEDGE_FUNCTION_ID, RUNS_LIST_FUNCTION_ID, RUNS_LIVE_URL_FUNCTION_ID, RUNS_MARKS_HISTORY_FUNCTION_ID, RUNS_MARK_FUNCTION_ID, RUNS_MARK_PREVIEW_FUNCTION_ID, RUNS_OVERVIEW_FUNCTION_ID, RUNS_POST_PROCESSING_FUNCTION_ID, RUNS_START_FUNCTION_ID, type RanJobOutcomeBase, type ReconcileInput, type ReconcileStrategy, type ReconcileVerdict, type RecoverAbandonedClaimsInput, type RecoverAbandonedClaimsOutcome, type RecurrenceAnswer, type RecurrenceLadder, type RecurrenceProvider, type RecurrenceQuery, type RepairSessionOptions, type ReplayGatePort, type ReplayedRunEvent, type ResolvedClause, type RunDetailReaderPort, RunEndedWithoutVerdictError, type RunInvocationPort, type RunNextJobInput, type RunNextJobOutcome, RunNotFinishedError, RunNotFoundError, RunNotRecordedError, type RunsReaderPort, SCENARIO_CORPUS, SCENARIO_CORPUS_VERSION, type ScenarioCorpusEntry, type SessionLogLine, type SessionRepair, type SessionReplay, StaleJobStoreEpochError, type StandInJobScript, type StartedRun, type StepEvidenceAddress, type StepEvidenceLookup, StreamReplayFloorError, type SuiteOutcomeHistoryPortLike, type ToolsPort, type TurnEngine, type TurnEngineDeps, UndeclaredJobCheckpointError, UndeclaredJobSuccessorError, UnknownFunctionError, UnknownJobKindError, UnknownLiveEditorSessionError, UnknownRunError, authoringFanOutBound, ceilingExceeded, chainJobIdsForRun, clusterVerdicts, composeBanner, computeMarkUnhandledRate, createAgentJobQueue, createChatCompactionHook, createChatState, createFunctionRegistry, createJobKindRegistry, createPlannerStage, createRealJobKindRegistry, createTurnEngine, decodeBase64Url, deterministicOverrideStrategy, encodeBase64Url, evaluateAssertionStrength, foldFunctionResult, fromToolName, gatherSuiteStability, invokeFunction, isChatMetaToolName, liveUrlOf, maybeCompact, mintLiveUrlToken, parseProposeGate1Allowance, parseScenario, projectAuthoringFunnel, projectPlannerTools, recordLlmAuthorship, renderAnalysisClusterBlock, renderVerdictSentence, repairSession, replaySession, replayToModelMessages, resolveClause, runAuthoringLoop, scenarioRelativePath, scopeReceiptsToWindow, splitClauses, unhandledReason, validateLiveUrlToken };
2193
+ export { AGENT_ASK_FUNCTION_ID, ANALYSIS_GET_FUNCTION_ID, ANSWER_ONLY_TOOL_NAME, AUTHORING_DRAFT_EDIT_FUNCTION_ID, AUTHORING_DRAFT_VERIFY_FUNCTION_ID, AUTHORING_DRAFT_WRITE_FUNCTION_ID, AUTHORING_FUNNEL_GET_FUNCTION_ID, AUTHORING_LOOP_STAGES, type AgentJobQueue, AmbiguousStepAddressError, type AnalysisArtifactReaderPort, type AnalysisCluster, type AnalysisClusterMember, type AuthoringFunnelPortLike, type AuthoringLoopDeps, type AuthoringLoopInput, type AuthoringLoopOutcome, type AuthoringLoopResult, type AuthoringLoopStage, type AuthoringReadPort, type AuthoringReadPortLike, type BoundRecordLlmAuthorship, CHAT_DIRECTIVES, CHAT_HELP_ENTRIES, CHAT_HELP_TEXT, CHAT_MINIMUM_CONTEXT, CHAT_RESOLVER_TABLE, COMPACTION_RATIO, CONFIG_AUTH_SET_FUNCTION_ID, CONFIG_FIELD_SET_FUNCTION_ID, CONFIG_HOOK_ADD_FUNCTION_ID, CONFIG_TARGET_CREATE_FUNCTION_ID, CONFIG_TARGET_EDIT_FUNCTION_ID, type CallerMint, type CancelJobInput, type CapabilityCatalogPort, type ChainJobIds, type ChainTerminalRecord, type ChatBindingStage, type ChatClause, type ChatCompactionHookDeps, type ChatConfigState, type ChatDegradeOptions, type ChatDegradeOutcome, type ChatDirectiveHandlers, type ChatDirectiveName, type ChatHelpEntry, type ChatInvokeAnswer, type ChatInvokeBinding, type ChatLanguageModel, type ChatLastTurn, type ChatModelDirectiveOutcome, type ChatReplayMessage, type ChatResolution, type ChatResolverEntry, type ChatResolverRule, type ChatResolverTable, type ChatStateContainer, type ChatStateSnapshot, type ChatTokenAccounting, type ChatToolsetFingerprint, type ChatTurnPort, type ChatTurnStateSink, type ClaimNextOptions, type ClauseAction, type ClauseSplitter, type ClauseVerdict, type ClockPort, type CompactionHook, type CompactionPass, type CompactionPassInput, type ConfigFieldSetResult, type ConfigFieldValue, type ConfigFileBytes, type ConfigFileSet, type ConfigFilesPortLike, type ConfigPathFacts, type ConfigReaderPort, type ConfigScaffoldContentPortLike, type ConfigStructuredValue, type ConfigValidatePortLike, type ConfigValidation, type ConfigValidationIssue, type CreateAgentJobQueueOptions, type CreateChatStateInput, type CreateFunctionRegistryOptions, type DraftReceiptsReadPortLike, type DraftShelfPortLike, type DraftStageOutcome, DuplicateFunctionIdError, DuplicateJobKindError, EDITOR_LIVE_URL_FUNCTION_ID, EXPLAIN_FUNCTION_ID, type EntityDirectoryPort, EntityNotFoundError, EvidenceBlobAbsentError, type EvidenceBlobReaderPort, type ExplainAbstentionRecord, type FallbackContinuationInput, type FallbackContinuationProducer, type FunctionBindingStatus, type FunctionDeclaration, type FunctionDeps, FunctionInputError, FunctionNotBoundError, type FunctionRegistry, type FunctionResultFold, type FunctionResultKind, type FunctionStream, IncompleteJobKindDeclarationError, InconsistentJobKindDeclarationError, type JobAuthorCallback, type JobBackoffPolicy, type JobCancelOutcome, JobCancellationNotRequestedError, JobCancelledError, type JobCheckpointContext, type JobCheckpointReadOutcome, type JobCheckpointRecord, type JobClaimOutcome, type JobCompleteOutcome, type JobEnqueueOutcome, type JobFailureDisposition, type JobHandler, type JobHandlerContext, type JobIdSource, type JobInsert, type JobKindDeclaration, type JobKindRegistry, type JobModelCapability, type JobModelOutput, type JobModelRequest, type JobPermanentFailureDisposition, type JobRecordedValueOutcome, type JobRequeueOutcome, type JobStoreDump, type JobStoreDumpRow, type JobStoreFence, type JobStorePort, KNOWLEDGE_GATES_LIST_FUNCTION_ID, KnowledgeGatesUnboundError, type KnowledgePort, type KnowledgeReaderPort, type KnowledgeReviewPort, LIVE_EMBED_PATH_PREFIX, LIVE_URL_TOKEN_VERSION, LIVE_URL_TTL_MS, type LiveViewPort, LiveViewUnavailableError, MARK_UNHANDLED_CEILING, type MarkUnhandledCounts, MissingIdempotencyKeyError, OPTIONAL_FUNCTION_DEP_KEYS, type OptionalFunctionDepKey, type PersistencePort, type PlannerStageFailure, type PlannerStageOutcome, type PlannerStagePort, type PlannerToolCall, type PostProcessingChainState, type PostProcessingJobRow, type PostProcessingReaderPort, type PostRunJobCapabilities, QUARANTINE_ADD_FUNCTION_ID, QUARANTINE_LIST_FUNCTION_ID, QUARANTINE_RELEASE_FUNCTION_ID, QuarantinedJobRequeueError, REAL_JOB_KIND_DECLARATIONS, RUNS_ARTIFACT_FUNCTION_ID, RUNS_DETAIL_FUNCTION_ID, RUNS_DIGEST_FUNCTION_ID, RUNS_EVENTS_FUNCTION_ID, RUNS_EVIDENCE_FUNCTION_ID, RUNS_KNOWLEDGE_FUNCTION_ID, RUNS_LIST_FUNCTION_ID, RUNS_LIVE_URL_FUNCTION_ID, RUNS_MARKS_HISTORY_FUNCTION_ID, RUNS_MARK_FUNCTION_ID, RUNS_MARK_PREVIEW_FUNCTION_ID, RUNS_OVERVIEW_FUNCTION_ID, RUNS_POST_PROCESSING_FUNCTION_ID, RUNS_START_FUNCTION_ID, type RanJobOutcomeBase, type ReconcileInput, type ReconcileStrategy, type ReconcileVerdict, type RecoverAbandonedClaimsInput, type RecoverAbandonedClaimsOutcome, type RecurrenceAnswer, type RecurrenceLadder, type RecurrenceProvider, type RecurrenceQuery, type RepairSessionOptions, type ReplayedRunEvent, type ResolvedClause, type RunDetailReaderPort, RunEndedWithoutVerdictError, type RunInvocationPort, type RunNextJobInput, type RunNextJobOutcome, RunNotFinishedError, RunNotFoundError, RunNotRecordedError, type RunsReaderPort, SCENARIO_CORPUS, SCENARIO_CORPUS_VERSION, type ScenarioCorpusEntry, type SessionLogLine, type SessionRepair, type SessionReplay, StaleJobStoreEpochError, type StandInJobScript, type StartedRun, type StepEvidenceAddress, type StepEvidenceLookup, StreamReplayFloorError, type SuiteOutcomeHistoryPortLike, type TurnEngine, type TurnEngineDeps, UndeclaredJobCheckpointError, UndeclaredJobSuccessorError, UnknownFunctionError, UnknownJobKindError, UnknownLiveEditorSessionError, UnknownRunError, authoringFanOutBound, ceilingExceeded, chainJobIdsForRun, clusterVerdicts, composeBanner, computeMarkUnhandledRate, createAgentJobQueue, createChatCompactionHook, createChatState, createFunctionRegistry, createJobKindRegistry, createPlannerStage, createRealJobKindRegistry, createTurnEngine, decodeBase64Url, deterministicOverrideStrategy, encodeBase64Url, evaluateAssertionStrength, foldFunctionResult, fromToolName, gatherSuiteStability, invokeFunction, isChatMetaToolName, liveUrlOf, maybeCompact, mintLiveUrlToken, parseScenario, projectAuthoringFunnel, projectPlannerTools, recordLlmAuthorship, renderAnalysisClusterBlock, renderVerdictSentence, repairSession, replaySession, replayToModelMessages, resolveClause, runAuthoringLoop, scenarioRelativePath, scopeReceiptsToWindow, splitClauses, unhandledReason, validateLiveUrlToken };