@vostride/agent-qa-agent 0.2.3 → 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.cjs +218 -1525
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -2961
- package/dist/index.d.ts +20 -2961
- package/dist/index.js +219 -1527
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import { ScopeKey, AuthoringBrief, Delta, CanaryOutcome, RecordId, GateReceipt, PromotionOutcome, GateReceiptRecordOutcome,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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 };
|