@vostride/agent-qa-agent-local 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.d.cts CHANGED
@@ -1,16 +1,17 @@
1
1
  import { EvidenceBlobReaderPort, JobStorePort, ConfigReaderPort, JobModelCapability, StartedRun, ConfigFilesPortLike, ConfigValidatePortLike, ConfigScaffoldContentPortLike, FunctionDeps, CapabilityCatalogPort, LiveViewPort, RunDetailReaderPort, ClockPort, JobIdSource, JobKindRegistry, PostRunJobCapabilities, ReconcileStrategy, ChatLanguageModel, ChatTokenAccounting, FunctionRegistry, ChatStateSnapshot, AgentJobQueue, KnowledgeReaderPort, KnowledgeReviewPort, RunInvocationPort, RunsReaderPort, JobStoreFence, RecoverAbandonedClaimsOutcome, RunNextJobOutcome, PersistencePort } from '@vostride/agent-qa-agent';
2
2
  import { TokenAccounting } from '@vostride/agent-qa-model';
3
- import { ModelCapabilities, TrustPriorTable, ScopeKey, RecordedRetrievalRung, RecordId, ReadTrace, RunEventEnvelope, RunsStartInput, RunSession, RunId, EvidenceBlobLookup, EvidenceBlobPresence, QuarantineAppliedEntry, RunNotification, ExecutionContext, ResolvePromotionPosture, FunctionCaller, MaskFn, FeedbackPort, FeedbackReadsPort, TestEditPatchSet, TestFileWrite, ChatSessionEvent, ArtifactRef, GateReceipt, GateReceiptRecordOutcome, PromotionOutcome, JobKind, SessionLogPort, OpenedChatSession } from '@vostride/agent-qa-schema';
4
- export { ALLOW_PRODUCTION_OPT_IN, buildProductionRefusal } from '@vostride/agent-qa-schema';
3
+ import * as _vostride_agent_qa_schema from '@vostride/agent-qa-schema';
4
+ import { ModelCapabilities, RecordedRetrievalRung, ScopeKey, RecordId, ReadTrace, RunEventEnvelope, RunsStartInput, RunSession, RunId, EvidenceBlobLookup, EvidenceBlobPresence, QuarantineAppliedEntry, RunNotification, ExecutionContext, FunctionCaller, MaskFn, FeedbackPort, FeedbackReadsPort, TestEditPatchSet, TestFileWrite, ChatSessionEvent, ArtifactRef, GateReceipt, GateReceiptRecordOutcome, PromotionOutcome, JobKind, MarkReceipt, MarkAddress, SessionLogPort, OpenedChatSession } from '@vostride/agent-qa-schema';
5
5
  import { ScreenshotCompressorLogger } from '@vostride/agent-qa-runner-local';
6
- export { BuildOrdinalReservationError, latestBuildOrdinal, reserveBuildOrdinalForProducts, resolveBuildTag } from '@vostride/agent-qa-runner-local';
7
- import { KnowledgeHintReader, PlatformAdapter, Planner, Verifier, ActionPlanCache, WorkingMemoryPort, StorageStatePort, ScreenshotCompressor, RecordLedger, ConfigDigests, MemberRunConfig, HookDefinition, SandboxRunnerOptions, SecretStore, SecretRedactorPort, MemberEventSink, EvidenceArtifactStorePort, PlannerConfig, LoopLogger, EventLogPort, HandoffStorePort, RunArtifactStorePort, PostVerdictPort, NonActionToolMap, MemberTestRunContext, TestResult, MemberTestExecutor, MemberTest, RunConfig } from '@vostride/agent-qa-runner';
6
+ export { BuildOrdinalReservationError, HANDOFF_FILE, RUNS_SUBDIR, latestBuildOrdinal, loadCompletedRunHandoff, purgeActionCache, reserveBuildOrdinalForProducts, resolveBuildTag } from '@vostride/agent-qa-runner-local';
7
+ import { PlatformAdapter, Planner, Verifier, ActionPlanCache, WorkingMemoryPort, StorageStatePort, ScreenshotCompressor, KnowledgeHintReader, RecordLedger, ConfigDigests, MemberRunConfig, HookDefinition, SandboxRunnerOptions, SecretStore, SecretRedactorPort, MemberEventSink, EvidenceArtifactStorePort, PlannerConfig, LoopLogger, EventLogPort, HandoffStorePort, RunArtifactStorePort, PostVerdictPort, NonActionToolMap, MemberTestRunContext, TestResult, MemberTestExecutor, MemberTest, RunConfig } from '@vostride/agent-qa-runner';
8
8
  export { BaselineProbeResult, ConfigDigests, MemberTestExecutor, PostVerdictOutcome, PostVerdictPort, PostVerdictStepInput, RunConfig, RunMemberDescriptor, mapVerdictToExitCode } from '@vostride/agent-qa-runner';
9
9
  export { runSafetyScope } from '@vostride/agent-qa-safety-local';
10
10
  import { planMarkEvidence, projectMarkEvidenceOutcome, canaryOutcomeObservationId } from '@vostride/agent-qa-knowledge-local/reader';
11
11
  export { QUARANTINE_CLASS_DISPOSITIONS, QUARANTINE_FLOOR_ELIGIBILITY, QuarantineDisposition, QuarantineFloorEligibility } from '@vostride/agent-qa-knowledge-local/reader';
12
- import { ExplicitReviewRequest, CreateLocalKnowledgeStoreConfig, ExplicitReviewDelegate, ServedRecordFacts, appendMarkEvidence, triageMark, servedIdsOn, procedureReadOn, applyFeedback, openKnowledgeReader, drainQueue, foldRunOutcomes, reconcileRunOutcomesProjection, enqueueCurationCandidateProjection, readCanonicalBundleAnchors, readCanonicalBundleStateDigests, canaryPostureFactsFor, standingReceiptDigestsFor, lowerTrustSharesFor, RUN_OUTCOMES_PROJECTION_VERSION, acquireWriterLease, refreshWriterLeaseAuthority, releaseLease, rebuildIndex, purgeRecord, resolvePurgeAffectedSet, purgeAffectedSetFingerprint, rollbackCampaign, rollbackCurationJob, resolveRollbackAffectedSet, rollbackAffectedSetFingerprint, heartbeatLease, readWriterLease, isLeaseStale, deriveWriterLeaseFreshness, countPendingCurationJobs, countRemainingCurationWork, inspectDrainableCurationWork, listQueuedCandidates, acquireEmbedder, runMaintenanceTail, readEmbedderWorkerRegistration, embedderWorkerProcessIsAlive, embedderWorkerSocketPath, probeEmbedderWorkerSocket, verifyEmbedderWorkerIncarnation, resolveProcessIncarnation, EmbedderWorkerRegistration, removeEmbedderWorkerRegistration, classifyIndexFace, foldRetrievalRung, indexDbPath, resolveEmbedderCapability, EntityIndexSweepReport, ExplicitReviewResult } from '@vostride/agent-qa-knowledge-local';
12
+ import { ExplicitReviewRequest, ExplicitReviewDelegate, ServedRecordFacts, appendMarkEvidence, triageMark, servedIdsOn, procedureReadOn, applyFeedback, CreateLocalKnowledgeStoreConfig, openKnowledgeReader, drainQueue, foldRunOutcomes, reconcileRunOutcomesProjection, enqueueCurationCandidateProjection, readCanonicalBundleAnchors, RUN_OUTCOMES_PROJECTION_VERSION, acquireWriterLease, refreshWriterLeaseAuthority, releaseLease, rebuildIndex, purgeRecord, resolvePurgeAffectedSet, purgeAffectedSetFingerprint, rollbackCampaign, rollbackCurationJob, resolveRollbackAffectedSet, rollbackAffectedSetFingerprint, heartbeatLease, readWriterLease, isLeaseStale, deriveWriterLeaseFreshness, countPendingCurationJobs, countRemainingCurationWork, inspectDrainableCurationWork, listQueuedCandidates, acquireEmbedder, runMaintenanceTail, readEmbedderWorkerRegistration, embedderWorkerProcessIsAlive, embedderWorkerSocketPath, probeEmbedderWorkerSocket, verifyEmbedderWorkerIncarnation, resolveProcessIncarnation, EmbedderWorkerRegistration, removeEmbedderWorkerRegistration, classifyIndexFace, foldRetrievalRung, indexDbPath, resolveEmbedderCapability, EntityIndexSweepReport, ExplicitReviewResult } from '@vostride/agent-qa-knowledge-local';
13
13
  export { AcquireEmbedderOutcome, AcquireWriterLeaseOutcome, ApplyFeedbackInput, AuthorOutcome, AuthoringTestRef, CurationCandidateProjection, CurationJobView, CurationProjectionArtifactSource, CuratorAuthor, DRAIN_BUDGET_MS, EMBEDDER_WORKER_REGISTRATION_FILE, EMBEDDER_WORKER_START_TIMEOUT_MS, EMBEDDER_WORKER_TERM_GRACE_MS, ENTITY_INDEX_SWEEP_INTERVAL_MS, EmbedderWorkerTimeoutError, EnqueueCurationCandidateProjectionOutcome, ExplicitReviewDelegate, ExplicitReviewDelegation, ExplicitReviewRequest, ExplicitReviewResult, HeartbeatLeaseOutcome, IndexFace, LeaseOperationOptions, MarkTriage, NeighborRecord, OpenServingKnowledgeReaderConfig, PurgeAffectedSet, PurgeReceipt, ReadEmbedderWorkerRegistrationOutcome, ReadWriterLeaseOutcome, RebuildReport, ReleaseLeaseOutcome, RollbackAffectedSet, RollbackCampaignInput, RollbackCurationJobInput, RollbackReport, ServedRecordFacts, WriterLease, WriterLeaseAuthority, WriterLeaseFreshness } from '@vostride/agent-qa-knowledge-local';
14
+ import { z } from 'zod';
14
15
 
15
16
  /** Provisional share of usable model context reserved for the frozen Channel-1 brief. */
16
17
  declare const BRIEF_FRACTION = 0.05;
@@ -44,78 +45,14 @@ interface ComputeKnowledgeBudgetsInput {
44
45
  qaRulesTokens: number;
45
46
  workingContextTokens: number;
46
47
  }
47
- /**
48
- * Derive every learned-knowledge budget beside model token accounting. The raw fraction is
49
- * floored before any floor/ceiling clamp, preserving D-02's exact boundary behavior. The reader
50
- * and provider-neutral loop receive only the resulting plain numbers.
51
- */
52
48
  declare function computeKnowledgeBudgets(input: ComputeKnowledgeBudgetsInput): KnowledgeBudgets;
53
- /**
54
- * Provisional D-09 predicate pinned by Phase 380 research. Channel 2 is absent when the model
55
- * cannot call tools, is text-only, or has too little context; Channels 1 and 3 remain available.
56
- */
57
49
  declare function memoryToolGate(capabilities: ModelCapabilities): boolean;
58
- /** Apply D-17's weak-model cap reduction through the exact D-09 gate predicate. */
59
50
  declare function scaledRecordCap(capabilities: ModelCapabilities): number;
60
51
 
61
- /** Inputs the composition root resolves when opening the run-scoped knowledge reader. */
62
- interface ConstructKnowledgeReaderInput {
63
- resolveTrustPriors?: () => TrustPriorTable | undefined;
64
- knowledgeRoot: string;
65
- product?: string;
66
- /** Known containing scopes for a member (the suite scope on prepared-suite runs). */
67
- parentScopes?: readonly ScopeKey[];
68
- asOfBuild: number;
69
- recordCap: number;
70
- }
71
- /**
72
- * Freeze Channel 1 at the composition root. The budget is deliberately absent from the key:
73
- * budgets are run-fixed, while canonical `[tier,key]` material gives one immutable memo entry per
74
- * scope. All other reader methods remain live pass-throughs for Channels 2, 3, and canary output.
75
- *
76
- * KREV-02 · D-13: the memo holds the WHOLE composite, so a repeat serve returns the trace captured
77
- * at the moment the brief was actually assembled — not a fresh trace describing the cache hit.
78
- * That is the deliberate choice and it is what makes the why-verb answer about *then*: a step that
79
- * replans four times against one frozen brief was served that brief once, and the attribution that
80
- * explains its lines is the one from that single serve. A "hit"-flavoured trace would describe the
81
- * memo rather than the retrieval, and there would be no honest rank or score to put in it. The
82
- * value type is written out rather than inferred so this stays a decision instead of a side effect
83
- * of `ReturnType`; `knowledge-budgets.test.ts` pins that a repeat carries the original trace.
84
- */
85
- declare function createFrozenKnowledgeReader(reader: KnowledgeHintReader): KnowledgeHintReader;
86
- /**
87
- * Construct the run-scoped reader through the physically read-only package subpath, then freeze
88
- * its Channel-1 brief for the lifetime of the run.
89
- */
90
- declare function constructKnowledgeReader(input: ConstructKnowledgeReaderInput): KnowledgeHintReader;
91
-
92
52
  /** Mirror of schema's ExecutionContext union (safety-local's detectExecutionContext returns it). */
93
53
  type RunExecutionContext = 'local-interactive' | 'local-headless' | 'ci' | 'cloud';
94
- /**
95
- * The provider-substrate model handle, named through the runner's own config rather than through
96
- * the substrate package. It is the identical type — the runner types both model slots with it —
97
- * and taking it this way keeps a provider-substrate edge off a package whose declared external
98
- * dependency set is empty and whose whole point is that it installs nothing (JOBQ-02).
99
- */
100
54
  type RunLanguageModel = MemberRunConfig['plannerModel'];
101
55
  type KnowledgeAccounting = Pick<TokenAccounting, 'estimate' | 'getCeilings'>;
102
- /**
103
- * 407 D-13/D-17 — the shape of THE retrieval-rung resolver, declared structurally here and
104
- * INJECTED rather than imported.
105
- *
106
- * knowledge-local's main entry has exactly ONE importer in this workspace by ratified rule
107
- * (385 D-14/SC5, enforced at file granularity by `findKnowledgeWriterSingletonOffenders`), and
108
- * that importer is `compose-local-agent.ts` — the local composition root. `resolveRetrievalRung`
109
- * lives on the main entry deliberately (407-03: it opens an index probe and may load a model, so
110
- * it is not `./reader` material), so naming it from this file would have made a second importer
111
- * and quietly retired the invariant. The seam keeps the singleton intact while leaving exactly
112
- * one implementation in the tree: `compose-local-agent.ts` re-exports the real resolver as
113
- * `resolveHostRetrievalRung`, and both CLI doors pass THAT.
114
- *
115
- * It is REQUIRED, not optional-with-a-default. A default would be a second place the production
116
- * resolver could be chosen from, and an omission at a new door would show up as a run with no
117
- * rung — a banner that silently says nothing — rather than as a compile error.
118
- */
119
56
  type RetrievalRungResolver = (options: {
120
57
  knowledgeRoot: string;
121
58
  agentQaRoot?: string;
@@ -123,13 +60,6 @@ type RetrievalRungResolver = (options: {
123
60
  interface RunKnowledgeComposition {
124
61
  reader: KnowledgeHintReader;
125
62
  budgets: KnowledgeBudgets;
126
- /**
127
- * 407 D-13 — the HOST's retrieval ladder at THIS run's serve time, started here and awaited
128
- * exactly once by the first step's evidence recorder. It is a promise rather than a value
129
- * because the resolver's embedder probe can cost a model load, and overlapping that with run
130
- * start keeps it off the critical path (T-407-16). Never re-probed per step: a run's rung is a
131
- * fact about when it was served, not a live indicator of the machine.
132
- */
133
63
  retrievalRung: Promise<RecordedRetrievalRung>;
134
64
  recordLedger: RecordLedger;
135
65
  nonActionTools?: NonActionToolMap;
@@ -137,7 +67,6 @@ interface RunKnowledgeComposition {
137
67
  drainMemoryReadTraces?: () => ReadTrace[];
138
68
  }
139
69
  interface OpenRunKnowledgeCompositionInput {
140
- resolveTrustPriors?: ConstructKnowledgeReaderInput['resolveTrustPriors'];
141
70
  knowledgeRoot: string;
142
71
  product?: string;
143
72
  scope: ScopeKey;
@@ -148,17 +77,7 @@ interface OpenRunKnowledgeCompositionInput {
148
77
  accounting: KnowledgeAccounting;
149
78
  qaRulesText: string;
150
79
  workingContextText: string;
151
- /**
152
- * 407 D-13 — the hosted runtime root (`<configDir>/.agent-qa`) the embedder probe discovers its
153
- * worker under. Absent falls back to the acquirer's own rule, so a caller that does not know the
154
- * root still gets an honest answer rather than an error.
155
- */
156
80
  agentQaRoot?: string;
157
- /**
158
- * 407 D-17 — the injected rung resolver. Production callers pass `resolveHostRetrievalRung`
159
- * (the composition root's binding of THE one knowledge-local resolver); tests pass a fake and
160
- * never touch the host. See {@link RetrievalRungResolver} for why it is injected and required.
161
- */
162
81
  resolveRetrievalRung: RetrievalRungResolver;
163
82
  }
164
83
  /**
@@ -174,86 +93,29 @@ interface RunDigestMemberInput {
174
93
  testFileContent: string;
175
94
  suiteFileContent?: string;
176
95
  }
177
- /**
178
- * The per-member cache pair AND the run-level handoff pair, both produced by the SINGLE call
179
- * below (D-20). The cache-key threading consumes `perMember[i]`; the handoff header carries
180
- * `runLevel`. Because both draw from one mint, run identity and handoff identity cannot fork.
181
- */
182
96
  interface RunConfigDigests {
183
97
  runLevel: ConfigDigests;
184
98
  perMember: ConfigDigests[];
185
99
  }
186
- /**
187
- * Mint the run's config digests ONCE (D-20; RESEARCH A1 pinning).
188
- *
189
- * - Per-member cache pair:
190
- * suiteConfigDigest = sha256hex16(configContent + '\n' + (suiteFileContent ?? ''))
191
- * testConfigDigest = sha256hex16(testFileContent)
192
- * - Run-level handoff pair:
193
- * suiteConfigDigest = sha256hex16(ordered DISTINCT per-member suite digests joined by '\n')
194
- * testConfigDigest = sha256hex16(ordered per-member test digests joined by '\n')
195
- * A single-member run collapses the run-level pair to the member's values.
196
- *
197
- * Both consumers (per-member cache, run-level handoff) draw from this one call — identities
198
- * cannot fork. This is the ONLY digest computation in the composition layer.
199
- */
200
100
  declare function computeRunConfigDigests(input: {
201
101
  configContent: string;
202
102
  members: readonly RunDigestMemberInput[];
203
103
  }): RunConfigDigests;
204
- /**
205
- * Resolve the run's execution context via safety-local's canonical `detectExecutionContext`
206
- * (RESEARCH Q1.4 recommendation (a) — already tested; an explicit override wins). Carried into
207
- * `RunConfig.executionContext` and the handoff header.
208
- */
209
104
  declare function resolveRunExecutionContext(override?: RunExecutionContext): RunExecutionContext;
210
105
  /** The durable run ports the shell wires behind `RunConfig.ports` (event log + handoff + artifact). */
211
106
  interface RunPersistencePorts {
212
107
  eventLog: EventLogPort;
213
108
  handoffStore: HandoffStorePort;
214
109
  runArtifactStore: RunArtifactStorePort;
215
- /**
216
- * CASS-02 run-scoped decision-screenshot store (Plan 13). Constructed once here from the SAME
217
- * persistence root + runId as the other adapters; the shell hands this exact instance to every
218
- * member so Plan 12's live-decision producer persists into one durable, run-addressable namespace.
219
- */
220
110
  evidenceArtifacts: EvidenceArtifactStorePort;
221
- /**
222
- * 403-13 (WR-06, LIVE-03): where a live-frame VIEWER's fault is reported. Declared here as well
223
- * as on the runner's `RunPorts`, because `assembleCliRun` spreads THIS interface into
224
- * `RunConfig.ports` — a member declared on only one side is invisible in the type across that
225
- * hop. Non-durable and off the bus by construction; the run's transcript never records a viewer.
226
- */
227
111
  onSubscriberFault?: (code: string, message: string) => void;
228
- /**
229
- * 404-01 (D-05/D-10): the host-composed post-verdict step the shell awaits between `verdict` and
230
- * `runFinished`. Declared here for the SAME reason `onSubscriberFault` is — `assembleCliRun`
231
- * spreads THIS interface into `RunConfig.ports`, so a member declared on only one side of that
232
- * hop is invisible in the type and would silently never reach `startRun`.
233
- *
234
- * It is NOT built by `buildRunPorts`: this package's runner seam names no queue and no writer
235
- * lease, and inventing a second spelling of either here is what D-05 exists to prevent. The core
236
- * threads whatever the HOST handed it (`RunInvocationCoreOptions.postVerdict`) and nothing else.
237
- */
238
112
  postVerdict?: PostVerdictPort;
239
113
  }
240
- /**
241
- * Construct the runner-local persistence ports rooted at the run's `.agent-qa` runtime dir (the
242
- * FsActionCache rooting idiom). `FsEventLog`/`FsHandoffStore`/`FsRunArtifactStore`/
243
- * `FsEvidenceArtifactStore` all write under the SAME `<configDir>/.agent-qa/runs/<slug(runId)>/…`
244
- * family (D-08 — one self-contained run dir).
245
- */
246
114
  declare function buildRunPorts(input: {
247
115
  configDir: string;
248
116
  runId: string;
249
117
  }): RunPersistencePorts;
250
- /**
251
- * Construct the D-08/Q10 screenshot compressor at the composition root (sharp's host-adapter home).
252
- * The runner-local `SharpScreenshotCompressor` is injected into every member's runner loop via
253
- * `RunSuiteConfig.compressor` / `RunTestConfig.compressor` — core no longer constructs one.
254
- */
255
118
  declare function buildScreenshotCompressor(logger?: ScreenshotCompressorLogger): ScreenshotCompressor;
256
- /** Resolve D-16 build-tag precedence without reading ambient process state. */
257
119
  declare function resolveBuildTagSetting(input: {
258
120
  flag?: string;
259
121
  env?: string;
@@ -261,42 +123,11 @@ declare function resolveBuildTagSetting(input: {
261
123
  buildTag?: string;
262
124
  };
263
125
  }): string | undefined;
264
- /**
265
- * Advance the per-product buildOrdinal counter under the same `.agent-qa` root (D-18), then
266
- * record D-14/D-16's first-seen tag mapping beside it when supplied. Both files are lock-free,
267
- * best-effort runtime truth; the resolved ordinal rides `RunConfig.buildOrdinal` into the handoff.
268
- */
269
126
  declare function resolveRunBuildOrdinal(input: {
270
127
  configDir: string;
271
128
  product: string;
272
129
  buildTag?: string;
273
130
  }): Promise<number>;
274
- /**
275
- * The ONE exclusive lane every suite member declares (402-02, D-09). The runner's bounded member
276
- * pool never runs two members of the same lane concurrently, and the suite path needs that for two
277
- * independent reasons, either of which alone would be sufficient:
278
- *
279
- * 1. Every member of a suite drives the ONE `PlatformAdapter` its suite set up — one browser, one
280
- * page. Two members on it at once would interleave navigations and clicks on a shared surface.
281
- * D-09 requires each concurrent member its own browser/CDP handle; the suite path does not
282
- * supply that today, so it does not get concurrency today.
283
- * 2. `createSuiteLifecycle` admits ONE ACTIVE SUITE at a time. Two suites live at once is a
284
- * lifecycle violation, not a scheduling preference.
285
- *
286
- * A single lane string (rather than one lane per suite) is what encodes BOTH: it serializes within
287
- * a suite and across suites. When a producer starts handing each member its own session AND the
288
- * lifecycle admits concurrent suites, this becomes a per-suite key — or disappears — and the shell
289
- * needs no argument re-litigated for that to be safe.
290
- *
291
- * STATUS 2026-08-24 (402-02): reason (2) is CLOSED — `createSuiteLifecycle` below is now a
292
- * once-latch with member reference counting, so two members of one suite merge into one setup and
293
- * a boundary crossed while members are live is a named error rather than a mid-flight teardown.
294
- * Reason (1) is OPEN and is what still holds the lane: `suiteConfig.adapter` is per-SUITE, so
295
- * within-suite concurrency would still put two tests on one browser page. Giving each suite member
296
- * its own session is a change to what a suite MEANS (suite setup navigates once and members
297
- * inherit that page state, per-test hooks run against it, auth state is captured from it) and is
298
- * not this plan's to make. Do not drop this lane until that is decided.
299
- */
300
131
  declare const SUITE_LIFECYCLE_LANE = "suite-lifecycle";
301
132
  /** The setup/teardown pair a suite runs when it first becomes active / when it is torn down. */
302
133
  interface SuiteLifecycleHooks {
@@ -305,22 +136,10 @@ interface SuiteLifecycleHooks {
305
136
  /** Suite-level teardown: teardown hooks + auth-state capture (when all passed) + adapter.cleanup. */
306
137
  teardown(): Promise<void>;
307
138
  }
308
- /**
309
- * One member's hold on the active suite (402-02). Held from the moment `ensureReady` admits the
310
- * member until the member settles; the suite cannot be torn down while any hold is outstanding.
311
- */
312
139
  interface SuiteMemberLease {
313
140
  /** Drop this member's hold. Idempotent — a double release never double-decrements. */
314
141
  release(): void;
315
142
  }
316
- /**
317
- * Thrown when `ensureReady` is asked for a suite while a DIFFERENT suite still has live members
318
- * (402-02). This is the one-active-suite invariant made loud. The alternative — tearing the live
319
- * suite down to make room — closes the browser out from under a test that is still running in it,
320
- * and does so silently. The shell's exclusive lane (`SUITE_LIFECYCLE_LANE`) means no shipped
321
- * composition can reach this, so it reads as an assertion rather than a runtime condition; that is
322
- * exactly the point of naming it instead of assuming it.
323
- */
324
143
  declare class SuiteBoundaryWhileLiveError extends Error {
325
144
  readonly activeSuiteKey: string;
326
145
  readonly requestedSuiteKey: string;
@@ -342,30 +161,6 @@ interface SuiteLifecycle {
342
161
  /** Tear down the last active suite (if any). Called once at run end after `session.done`. */
343
162
  finalize(): Promise<void>;
344
163
  }
345
- /**
346
- * A lazy, CONCURRENCY-SAFE suite-lifecycle manager (the Q1.2 "pre/post-run composition" rows,
347
- * redesigned for the 402-02 member pool). A wholly SKIPPED suite never sets up — the shell only
348
- * invokes the executor (hence `ensureReady`) for non-skipped members, so a suite whose members are
349
- * all fail-fast/cancel skips is never touched.
350
- *
351
- * WHAT CHANGED AND WHY. The pre-402 version read the active key, awaited `hooks.setup()`, and THEN
352
- * assigned the key — three steps with awaits between them. Under a `for` loop that is fine because
353
- * only one member is ever inside it. Under a pool, two members of one suite both observe "no active
354
- * suite" and both run setup: two browsers for one suite, and the second teardown handle silently
355
- * overwriting the first, so the first suite's browser is never closed. The fix is not a lock; it is
356
- * to make the state transition SYNCHRONOUS and let the promise be the shared thing:
357
- *
358
- * - the once-latch: `activeKey`, `activeTeardown` and `activeSetup` are all assigned in the same
359
- * synchronous region, BEFORE any await, so there is no window in which a second member can
360
- * observe the pre-setup state. Every later member of the suite awaits `activeSetup` itself.
361
- * - member reference counting: the count is incremented before awaiting setup, so a concurrent
362
- * boundary crossing sees the hold. Teardown fires at a BOUNDARY (or at `finalize`) and only
363
- * when the count is zero — never merely because it hit zero, which would tear the suite down
364
- * and set it back up between every pair of members.
365
- * - a failed setup poisons the suite: the rejected latch is kept and re-thrown to every later
366
- * member, so setup is attempted exactly once and no member half-starts against a suite that
367
- * never came up. Such a suite is never torn down — nothing came up to take down.
368
- */
369
164
  declare function createSuiteLifecycle(): SuiteLifecycle;
370
165
  /**
371
166
  * One prepared suite member: its suite key + lifecycle hooks (setup runs on the first member of
@@ -376,32 +171,10 @@ declare function createSuiteLifecycle(): SuiteLifecycle;
376
171
  interface SuiteMemberBinding {
377
172
  suiteKey: string;
378
173
  hooks: SuiteLifecycleHooks;
379
- /**
380
- * 403 D-05: the suite's ONE adapter, read only for its live-stream declaration. Present so the
381
- * executor can register the capability AFTER `ensureReady` has resolved the suite's `setup()`
382
- * (which is where `adapter.setup()` runs, so `this.page` exists) and BEFORE the first step.
383
- */
384
174
  adapter?: PlatformAdapter;
385
175
  run(ctx: MemberTestRunContext): Promise<TestResult>;
386
176
  }
387
- /**
388
- * Declare (or withdraw) a member's live stream on the shell-provided context (403 D-05), by the one
389
- * selection rule `liveStreamFor` (428 D-11/D-12). A continuous stream is registered with the
390
- * labelled 1s snapshot producer over `screenshot()` as its fallback, which the hub starts when the
391
- * stream fails to start. An adapter with no continuous stream but a `screenshot()` registers the
392
- * snapshot producer itself; an adapter whose own stream is already a snapshot registers that alone;
393
- * an adapter with neither registers `undefined` and its handle reads `{ kind: 'none' }`.
394
- * The `ctx.registerLiveStream?.()` call is itself optional so a composition that predates the port
395
- * is a no-op rather than a crash.
396
- */
397
177
  declare function registerAdapterLiveStream(ctx: MemberTestRunContext, adapter: PlatformAdapter | undefined): void;
398
- /**
399
- * Build the injected `MemberTestExecutor` (D-01). Per member the closure looks up the prepared
400
- * binding by the shell-derived `testIndex` (array position — authoritative), calls
401
- * `lifecycle.ensureReady(...)` to lazily set up / cross-boundary tear down the member's suite,
402
- * then runs the member behind `executeSuiteMember`. Wrapping ONE audited function instead of
403
- * replicating the loop body.
404
- */
405
178
  declare function buildSuiteMemberExecutor(input: {
406
179
  lifecycle: SuiteLifecycle;
407
180
  bindings: readonly SuiteMemberBinding[];
@@ -410,9 +183,6 @@ declare function buildSuiteMemberExecutor(input: {
410
183
  * `filePath` is the member's own file. A suite-patched test carries no `url` (the suite owns nav), so
411
184
  * `runMemberTest` skips the per-test navigate for suite members. */
412
185
  declare function toMemberTest(test: any, filePath: string): MemberTest;
413
- /** Everything both executor closures resolve at the composition root and hand to
414
- * {@link buildMemberRunConfig}. The adapter-backed injected handlers are constructed here from
415
- * `adapter` + `secretStore` so the two sites never drift (Pitfall 8). */
416
186
  interface BuildMemberRunConfigInput {
417
187
  adapter: PlatformAdapter;
418
188
  planner: Planner;
@@ -422,7 +192,6 @@ interface BuildMemberRunConfigInput {
422
192
  storageState?: StorageStatePort;
423
193
  compressor?: ScreenshotCompressor;
424
194
  knowledgeReader?: KnowledgeHintReader;
425
- /** 407 D-13 — the composed rung, carried through untouched to the loop's evidence recorder. */
426
195
  knowledgeRetrievalRung?: Promise<RecordedRetrievalRung>;
427
196
  knowledgeBudgets?: KnowledgeBudgets;
428
197
  knowledgeAsOfBuild?: number;
@@ -445,7 +214,6 @@ interface BuildMemberRunConfigInput {
445
214
  sink: MemberEventSink;
446
215
  testIndex: number;
447
216
  abortSignal: AbortSignal;
448
- /** CASS-02 run-scoped evidence store the shell threads onto the member ctx (Plan 13). */
449
217
  evidenceArtifacts?: EvidenceArtifactStorePort;
450
218
  captureScreenshots?: boolean;
451
219
  accessibility?: MemberRunConfig['accessibility'];
@@ -470,11 +238,6 @@ interface BuildMemberRunConfigInput {
470
238
  /** The member's own file, for the core-parity fileUpload path resolver. */
471
239
  filePath: string;
472
240
  }
473
- /**
474
- * Assemble the {@link MemberRunConfig} `runMemberTest` slots behind, wiring the adapter-backed
475
- * injected handlers (runJS/appium/a11y/failure-shot), the store-neutral secret resolver, and the
476
- * fileUpload path resolver — reproducing core's `runTest` per-test behavior on runner soil (D-05).
477
- */
478
241
  declare function buildMemberRunConfig(input: BuildMemberRunConfigInput): MemberRunConfig;
479
242
 
480
243
  /** A bus subscriber a host attaches: an envelope `handler` plus an optional post-run `flush`. */
@@ -487,176 +250,44 @@ interface AttachableRunSession {
487
250
  subscribe(fromSeq: number | undefined, handler: (envelope: RunEventEnvelope) => void): () => void;
488
251
  }
489
252
 
490
- /**
491
- * A target's declared environment, as the config carries it (`TargetSchema.environment`).
492
- *
493
- * `undefined` is a first-class member and means UNDECLARED — not "unknown, assume harmless".
494
- *
495
- * `production` (432-03, D-18) outranks every other class in a fold. A production run is refused
496
- * before any effect unless the config opts in (`refuseProductionWithoutOptIn`); one that is opted
497
- * in starts like any other run (432-13).
498
- */
499
- type RunTargetClass = 'local' | 'shared' | 'production' | undefined;
500
-
501
- /**
502
- * The request fields this seam refuses, and the reason is not the one the CLI host had.
503
- *
504
- * `createCliRunExecution` refused these because the `run` COMMAND had no argv to express them
505
- * (`local-agent-host.ts`). This core can express both — and honouring either would be the defect
506
- * RESEARCH Pitfall 7 names:
507
- *
508
- * `executionContext` — the context a run records comes from the composition that owns the door
509
- * (D-07) and from nowhere else. The FIELD survives on the contract as the label a run RECORDS,
510
- * and it is refused here until some door has a use for recording a label that differs from the
511
- * context it was started in.
512
- *
513
- * `scope` — resolves from the config's own target/product resolution. Accepting an override
514
- * would let a caller address one product's run ledger while running another's.
515
- *
516
- * Read OFF the request rather than enumerated positively, so a field added to `RunsStartInput`
517
- * with no plan for carrying it shows up here as a refusal instead of as a silent drop.
518
- */
519
- declare const REFUSED_RUN_REQUEST_FIELDS: readonly ["scope", "executionContext"];
520
- /**
521
- * The request named a field this seam refuses, so the request is refused rather than reshaped.
522
- *
523
- * The successor to `UnsupportedRunRequestError` (cli `local-agent-host.ts`), which dies with the
524
- * spawn port in 401-05. Same class of refusal, different rationale: that one could not express the
525
- * fields, this one declines to.
526
- */
527
- declare class RunRequestFieldRefusedError extends Error {
528
- readonly refusedFields: readonly string[];
529
- constructor(refusedFields: readonly string[]);
253
+ /** Inputs the composition root resolves when opening the run-scoped knowledge reader. */
254
+ interface ConstructKnowledgeReaderInput {
255
+ knowledgeRoot: string;
256
+ product?: string;
257
+ /** Known containing scopes for a member (the suite scope on prepared-suite runs). */
258
+ parentScopes?: readonly ScopeKey[];
259
+ asOfBuild: number;
260
+ recordCap: number;
530
261
  }
262
+ declare function createFrozenKnowledgeReader(reader: KnowledgeHintReader): KnowledgeHintReader;
531
263
  /**
532
- * The request named a run id this system would not have minted (2026-08-23, 401-20 · CR-04).
533
- *
534
- * A DECISION, NOT A FAULT, and it joins `RunRequestFieldRefusedError` under the same rule: the
535
- * caller asked for something this seam declines, and says so by name before anything happens.
536
- *
537
- * WHY IT IS ASKED HERE AND NOT IN THE SCHEMA. `packages/schema` owns PATH-SAFETY on the id — the
538
- * id becomes a directory name and that is judgeable with no dependencies — but it may not name
539
- * `@vostride/agent-qa-ids` and must not re-spell the canonical id shape. So "is this an id this
540
- * system would have minted" is asked here, through a predicate supplied beside `mintRunId` and for
541
- * `mintRunId`'s stated reason: a locally invented answer would be a second spelling of a run's
542
- * identity. Measured pre-fix: `runId: 'not-a-minted-id'` was acknowledged, and `runId: ''` ran a
543
- * whole run into a shared `runs/default/` directory before the op reported `RunNotRecordedError`.
264
+ * Construct the run-scoped reader through the physically read-only package subpath, then freeze
265
+ * its Channel-1 brief for the lifetime of the run.
544
266
  */
267
+ declare function constructKnowledgeReader(input: ConstructKnowledgeReaderInput): KnowledgeHintReader;
268
+
545
269
  declare class RunIdNotMintedError extends Error {
546
270
  readonly runId: string;
547
271
  constructor(runId: string);
548
272
  }
549
- /**
550
- * The request named a run id that already belongs to a run (2026-08-23, 401-20 · CR-04).
551
- *
552
- * WHAT A COLLISION COSTS, measured rather than reasoned about. Pre-fix, a second `runs.start`
553
- * naming a completed run's id APPENDED seven more envelopes to that run's `events.jsonl`
554
- * (2111 → 4221 bytes), OVERWROTE its `handoff.json`, and added a second row to its
555
- * `execution_audit.jsonl` — and then acknowledged the caller with the victim's own id. The
556
- * acknowledgment law makes that worse rather than better: `runs.start` reads every ack field back
557
- * off the run's durable record specifically so a host cannot fabricate one, and a colliding id
558
- * satisfies that read with a PRIOR run's record. So the refusal has to land before the record is
559
- * touched at all — which is why it sits in the identity step, above assembly.
560
- *
561
- * THE TEST IS THE RUN'S OWN DIRECTORY, derived through `runDirFor` — the same composition the
562
- * run's ports use — because every artifact a collision could tamper with lives inside it.
563
- * Asking after `handoff.json` alone would let a run that crashed before writing one be silently
564
- * appended to.
565
- */
566
273
  declare class RunIdAlreadyInUseError extends Error {
567
274
  readonly runId: string;
568
275
  constructor(runId: string);
569
276
  }
570
- /**
571
- * This run was refused before it started.
572
- *
573
- * Since 432-13 the one producer is the production refusal (`refuseProductionWithoutOptIn`): its
574
- * reason is the plain sentence naming the target and the config line. 2026-10-01 (432-19, D-15):
575
- * it carries no remedy list, because the remedy vocabulary left with the execution gate.
576
- * `neutralReason` is the same sentence; the dashboard and run-service doors read that member to
577
- * tell a run denial from any other rejection, and a door renders it and never composes its own.
578
- */
579
- declare class RunDeniedError extends Error {
580
- readonly runId: string;
581
- readonly reason: string;
582
- readonly neutralReason: string;
583
- constructor(runId: string, reason: string);
584
- }
585
- /**
586
- * What one resolution of this request's targets answers (401-14).
587
- *
588
- * THE CLASS AND THE NAMES COME BACK TOGETHER because they are two renderings of ONE pass over the
589
- * run's members: `targetClass` is the most-restrictive fold over them, `targetNames` the declared
590
- * names of the members it folded over. A door that answered them from two passes could refuse on
591
- * one reading of the config while the run it describes came from another.
592
- */
593
- interface RunTargetDeclaration {
594
- /** The DECLARED class of the run's members — a config read, never a probe (D-06). */
595
- readonly targetClass: RunTargetClass;
596
- /**
597
- * The distinct declared target names, in first-appearance member order.
598
- *
599
- * DISPLAY-ONLY (T-401-G3b). Members that declare no target contribute nothing, which is how an
600
- * all-undeclared run arrives here as an empty list rather than as a list of invented names.
601
- */
602
- readonly targetNames: readonly string[];
603
- /**
604
- * The members' targets DECLARED `environment: production`, in first-appearance member order
605
- * (432-03, D-18). Repeats are allowed; the refusal names each target once.
606
- *
607
- * REQUIRED, so a door that forgets it does not compile. It comes from the SAME resolution as
608
- * `targetClass` and `targetNames`, which is what keeps a mixed run from hiding a production member
609
- * behind its siblings.
610
- */
611
- readonly productionTargetNames: readonly string[];
612
- /**
613
- * The config's top-level `allowProduction`, read from the same config the targets resolved
614
- * against. Only the literal `true` opts in; absence and `false` do not. No request field and no
615
- * CLI flag reaches this.
616
- */
617
- readonly allowProduction?: boolean;
618
- }
619
- /**
620
- * Refuse a run that drives a production target the config has not opted into (432-03, D-18).
621
- *
622
- * A production CLASS with no names still refuses, so a producer that folded the class but lost the
623
- * names fails closed. The refusal is a plain `RunDeniedError` carrying the sentence from
624
- * `buildProductionRefusal`.
625
- *
626
- * EXPORTED because a producer that must acquire a device or an adapter before the core runs (the
627
- * suite half) calls it first, so production is refused before that acquisition too.
628
- */
629
- declare function refuseProductionWithoutOptIn(runId: string, declaration: Pick<RunTargetDeclaration, 'productionTargetNames' | 'allowProduction'> & {
630
- readonly targetClass?: RunTargetClass;
631
- }): void;
632
- /**
633
- * The identity refusals, asked against the run's own directory (401-20, CR-04).
634
- *
635
- * EXPORTED for the same producers `refuseProductionWithoutOptIn` is: a door that refuses production
636
- * ahead of the core asks these first, so a duplicate or unminted id keeps its own refusal on every
637
- * path.
638
- */
639
- declare function refuseUnavailableRunId(input: {
640
- readonly configDir: string;
641
- readonly runId: string;
642
- readonly isMintedRunId: (value: string) => boolean;
643
- }): void;
644
277
  /** What the assembler is handed: everything the core resolved, and nothing it did not. */
645
278
  interface RunAssemblyContext {
646
279
  /** The run's identity, already resolved. The core starts the run under THIS id and no other. */
647
280
  readonly runId: string;
648
- /** The caller's request, refused fields already rejected. */
281
+ /** The caller's request, its run id already checked. */
649
282
  readonly request: RunsStartInput;
650
283
  /** The context the door carried — the same value, so a run cannot record a different one. */
651
284
  readonly executionContext: RunExecutionContext;
652
- /** The durable ports, rooted at `<configDir>/.agent-qa` (Pitfall 5 — the evidence check's root). */
285
+ /** The durable ports, rooted at `<configDir>/.agent-qa`, the evidence check's root. */
653
286
  readonly ports: RunPersistencePorts;
654
287
  /** `dirname(agentQaRoot)` — the project directory the config sits in. */
655
288
  readonly configDir: string;
656
289
  /** The config file this run resolves against (request override honoured). */
657
290
  readonly configPath: string;
658
- /** The declared class the door resolved. Handed over so an assembler need not re-resolve it. */
659
- readonly targetClass: RunTargetClass;
660
291
  }
661
292
  /**
662
293
  * Build the `RunConfig` the runner shell starts.
@@ -677,29 +308,7 @@ interface RunInvocationCoreOptions {
677
308
  readonly configPath: string;
678
309
  /** The project's runtime root, i.e. `<configDir>/.agent-qa`. */
679
310
  readonly agentQaRoot: string;
680
- /**
681
- * The context this door's runs are started in and record (D-07).
682
- *
683
- * CALLER-RESOLVED, NEVER SNIFFED. The CLI resolves its TTY posture, the daemon carries
684
- * `local-headless`, CI passes its own. A core that detected this itself would be answering a
685
- * question about the machine when the question is about the door.
686
- */
687
311
  readonly executionContext: RunExecutionContext;
688
- /**
689
- * Resolve what this request's targets DECLARE (D-06; names added 401-14).
690
- *
691
- * INJECTED BECAUSE THE CONFIG LIVES BEHIND THE DOOR, not because any of it is negotiable. Target
692
- * resolution needs the CLI's `resolveTarget`/`AgentQaConfig` vocabulary, which this package may
693
- * not name. `undefined` is undeclared, which is not production.
694
- *
695
- * ONE RESOLUTION, ALL FIELDS, AND THAT IS THE POINT OF THE SHAPE. The class, the names, the
696
- * production members and the opt-in come back from one pass, so the production refusal is asked
697
- * about the members the run will actually drive.
698
- *
699
- * Must not touch the target, and must not probe. A resolver that read a URL would put a network
700
- * call before the production refusal, which is the one ordering this file exists to hold.
701
- */
702
- readonly resolveTargetDeclaration: (request: RunsStartInput) => RunTargetDeclaration | Promise<RunTargetDeclaration>;
703
312
  /** Build the `RunConfig`. Called ONLY once every refusal has passed. */
704
313
  readonly assembleRun: RunAssembler;
705
314
  /**
@@ -710,62 +319,18 @@ interface RunInvocationCoreOptions {
710
319
  * spelling of a run's identity. Every shipped door already holds `generateRunId`.
711
320
  */
712
321
  readonly mintRunId: () => string;
713
- /**
714
- * Is this a run id this system would have minted? (2026-08-23, 401-20 · CR-04)
715
- *
716
- * REQUIRED, WITH NO DEFAULT AND NO FALLBACK, for exactly `mintRunId`'s reason and one more. The
717
- * canonical predicate lives in `@vostride/agent-qa-ids`, which this package does not depend on,
718
- * and a locally invented shape check would be a second spelling of a run's identity — the same
719
- * argument that made minting an option. The extra reason is that an OPTIONAL predicate with a
720
- * permissive fallback is this defect wearing a type: every composition that forgot it would be
721
- * back to accepting any string, and the build would say nothing. A composition that omits this
722
- * does not compile.
723
- *
724
- * It answers about the SHAPE only. Whether the id is free is this file's own question, asked
725
- * against the run's own directory.
726
- */
727
322
  readonly isMintedRunId: (value: string) => boolean;
728
- /**
729
- * Notified with the started session, synchronously, before the core awaits it (D-09's bridge
730
- * hook). Subscribers attach here; the bus replays from seq 1, so nothing is missed.
731
- */
732
323
  readonly onSession?: (session: RunSession) => void;
733
- /**
734
- * Build this run's post-verdict step (404 D-05/D-10) — the durable `analyze-run` enqueue plus
735
- * ONE writer-lease consult, awaited by the shell between `verdict` and `runFinished`, whose
736
- * answer becomes `runFinished.disposition`.
737
- *
738
- * HOST-COMPOSED, CORE-THREADED, and both halves are load-bearing. It is host-composed because
739
- * the step speaks queue and writer-lease vocabulary this package's runner seam may not name —
740
- * the same argument that made `mintRunId` an option rather than a local. It is threaded HERE
741
- * rather than left to each door because the enqueue is precisely what the `runs.start` door and
742
- * the daemon door forgot: a door that supplies no factory gets a run whose wire honestly reads
743
- * `detached`, and a door that supplies one cannot then forget to wire it in.
744
- *
745
- * Called ONCE per run, after every refusal has passed and the persistence ports exist, so a
746
- * refused run never composes one. Absent ⇒ no `postVerdict` member reaches `RunConfig.ports` at all
747
- * (conditional spread, never an `undefined` field), and the shell reports `detached` with no
748
- * warning — which is the truth about a host from which nothing will be drained.
749
- */
750
324
  readonly postVerdict?: (input: {
751
325
  readonly runId: string;
752
326
  readonly configDir: string;
753
327
  }) => PostVerdictPort;
754
- /** The door's first third-party code, awaited once the request is allowed and before any port. */
328
+ /**
329
+ * The door's own code, awaited once the request and its run id are allowed and before any port.
330
+ * A door opens its run record and loads its plugins here.
331
+ */
755
332
  readonly afterRefusal?: () => Promise<void>;
756
333
  }
757
- /**
758
- * The one in-process run execution core.
759
- *
760
- * Returns a `RunExecutionPort` (the shape `createLocalRunInvocation` binds `runs.start` over), so
761
- * the op's evidence check — read the run's own event log back, refuse an empty one — applies to
762
- * this seam unchanged.
763
- *
764
- * NO DEFENSIVE CATCH ANYWHERE (D-03). A run that crashes hard takes its host with it, on purpose:
765
- * crash-through surfaces bugs instead of absorbing them, and the runner's budget/abort machinery
766
- * is the orderly containment. The only `throw`s this file introduces are the two refusals above,
767
- * which are decisions rather than faults.
768
- */
769
334
  declare function createRunInvocationCore(options: RunInvocationCoreOptions): {
770
335
  execute(request: RunsStartInput): Promise<{
771
336
  runId: RunId;
@@ -777,27 +342,7 @@ interface CreateEvidenceBlobReaderInput {
777
342
  readonly agentQaRoot: string;
778
343
  }
779
344
  declare function lookupEvidenceBlob(input: CreateEvidenceBlobReaderInput, runId: string, sha: string): EvidenceBlobLookup;
780
- /**
781
- * THE SAME RULE, MINUS THE BYTES (Phase 406, D-17) — presence for a whole run's artifact refs.
782
- *
783
- * WHY NOT `lookupEvidenceBlob(...).status`: a run page annotates every ref it is about to render,
784
- * and the read arm loads a screenshot into memory to reach the status it then discards. This
785
- * shaping walks the identical three-way split — malformed sha, unknown run, then the store's own
786
- * answer — and asks the store to STAT instead of read. The two arms above `runDirectoryExists`
787
- * are shared code, not a copy, so `found` here and `found` there are the same claim.
788
- */
789
345
  declare function statEvidenceBlob(input: CreateEvidenceBlobReaderInput, runId: string, sha: string): EvidenceBlobPresence;
790
- /**
791
- * The op's PORT over {@link lookupEvidenceBlob}.
792
- *
793
- * BOTH MEMBERS ARE BOUND HERE (406, D-17): this host holds run directories, so it can honestly
794
- * answer the optional presence member, and an op reading it gets `present` for every ref rather
795
- * than the omitted key a host without local storage would leave.
796
- *
797
- * ASYNC EVEN THOUGH THE STORE IS NOT, because the port is the shape a host that does not hold its
798
- * blobs on a local disk would also have to satisfy — and a synchronous contract is one such a host
799
- * could never implement without lying about it. The local answer is simply already resolved.
800
- */
801
346
  declare function createEvidenceBlobReader(input: CreateEvidenceBlobReaderInput): EvidenceBlobReaderPort;
802
347
  /**
803
348
  * The analysis job's shaping: the bytes, or nothing, with the two absences collapsed.
@@ -817,18 +362,6 @@ interface RunQuarantineSnapshotInput {
817
362
  now: string;
818
363
  warn?: (message: string) => void;
819
364
  }
820
- /**
821
- * Resolve the entries this run will apply: active (`'quarantined'`) and belonging to this run's
822
- * product. Expired entries are deliberately EXCLUDED — a lapsed entry's test fails builds again
823
- * (D-11) — and released entries are gone.
824
- *
825
- * Measured consequence worth knowing at THIS seam, because this is where the exclusion is decided:
826
- * for a test still crossing the threshold that is exactly ONE red build. The snapshot below is
827
- * taken at run start (entry lapsed ⇒ not applied ⇒ red), and that same run's post-run fold
828
- * re-enters it with a fresh full-lifetime deadline before the next run takes its own snapshot. No
829
- * human is scheduled into that loop at any point, and nothing bounds the repetition
830
- * (measured 2026-08-06).
831
- */
832
365
  declare function resolveRunQuarantineSnapshot(input: RunQuarantineSnapshotInput): readonly QuarantineAppliedEntry[];
833
366
 
834
367
  /**
@@ -857,7 +390,7 @@ interface LogicalDump {
857
390
  readonly checkpoints: readonly LogicalDumpCheckpoint[];
858
391
  }
859
392
 
860
- /** One chain-terminal notification row, with its payload guarded on the way out (388 D-09). */
393
+ /** One chain-terminal notification row, with its payload guarded on the way out. */
861
394
  interface JobStoreNotification {
862
395
  readonly runId: string;
863
396
  readonly payload: RunNotification;
@@ -865,68 +398,13 @@ interface JobStoreNotification {
865
398
  }
866
399
  interface SqliteJobStore extends JobStorePort {
867
400
  close(): void;
868
- /**
869
- * The chain-terminal notifications this database holds, ordered by run.
870
- *
871
- * On the implementation rather than the neutral port, beside `logicalDump`, and for the
872
- * same reason: the port is the ENGINE's vocabulary and the engine never reads a
873
- * notification — it writes one, once, as part of ending a job. Every reader of these rows
874
- * is a surface, and 388 D-14 routes surfaces through the function layer, not through here.
875
- *
876
- * The payload is parsed through the schema guard rather than handed back as text: the
877
- * column is bytes another build could have written, and a row that lost a stage cannot be
878
- * allowed to travel as a `RunNotification`.
879
- */
880
401
  listNotifications(): Promise<readonly JobStoreNotification[]>;
881
- /**
882
- * The D-03 comparison basis, in full: every table this database holds plus a digest per
883
- * checkpoint file.
884
- *
885
- * Kept beside `dumpState()` rather than replacing it, because the two answer different
886
- * questions and collapsing them would weaken both. `dumpState()` is the typed, engine-facing
887
- * projection of the job rows and it is part of the neutral port. This one is the storage
888
- * implementation's own full-state instrument: it knows about the checkpoint directory and
889
- * about tables the port has no vocabulary for, and it is the thing a crash comparison holds
890
- * up against another run.
891
- */
892
402
  logicalDump(): Promise<LogicalDump>;
893
403
  /** The same dump as one comparable string. Byte equality here is state equality. */
894
404
  dumpLogicalState(): Promise<string>;
895
405
  }
896
406
 
897
- /**
898
- * 407 D-13/D-17 — THE retrieval-rung resolver, bound at the one edge that may name the writer
899
- * entry (385 D-14/SC5) and handed to `openRunKnowledgeComposition` by every run door.
900
- *
901
- * The binding exists so there is exactly ONE resolver in the tree with exactly ONE importer of
902
- * the package that owns it. `run-composition.ts` declares the seam's shape structurally and takes
903
- * the function as a required input rather than importing it — naming knowledge-local's main entry
904
- * from a second file would retire the singleton the arch gate enforces, and the fix would look
905
- * like relaxing the gate.
906
- *
907
- * Typed as {@link RetrievalRungResolver} rather than by its own signature, so the pass-through
908
- * that reaches the CLI is the narrow two-root contract the doors actually use — the embedder
909
- * `probes` seam and the preferred profile stay knowledge-local's business.
910
- */
911
407
  declare const resolveHostRetrievalRung: RetrievalRungResolver;
912
- /**
913
- * 407 D-18/D-20 — the READ-ONLY knowledge probes a diagnostic grades a host with.
914
- *
915
- * `doctor` needs the two axes SEPARATELY, not just the folded word: its `Knowledge store` row is
916
- * about the index face, its `Embedder` row is about the capability outcome, and only its third row
917
- * is the fold. {@link resolveHostRetrievalRung} above hands back the fold alone, so a surface that
918
- * had only that would have to re-derive the other two — which is a second fold in everything but
919
- * name, and exactly what D-17's one-resolver rule exists to prevent.
920
- *
921
- * Published as its own frozen table rather than folded into {@link LOCAL_DAEMON_CAPABILITIES}
922
- * because every member here is a READ. Nothing in it acquires a lease, spawns a worker, downloads
923
- * an artifact or writes to the knowledge root, and a caller that holds this object cannot mutate
924
- * the thing it is reporting on. That is the whole mitigation for T-407-19: the boundary is the
925
- * shape of the capability, not a promise in a doc comment at the call site.
926
- *
927
- * `foldRetrievalRung` rides along for the same reason the resolver does — one fold table in one
928
- * package, reached through the one edge that may name the writer entry (385 D-14/SC5).
929
- */
930
408
  interface LocalKnowledgeProbeCapabilities {
931
409
  readonly classifyIndexFace: typeof classifyIndexFace;
932
410
  readonly foldRetrievalRung: typeof foldRetrievalRung;
@@ -950,13 +428,6 @@ interface LocalDaemonCapabilities {
950
428
  readonly releaseLease: typeof releaseLease;
951
429
  readonly readWriterLease: typeof readWriterLease;
952
430
  readonly isLeaseStale: typeof isLeaseStale;
953
- /**
954
- * CR-01/CR-08: the one three-valued freshness verdict — fresh, reclaimable, or unknown.
955
- *
956
- * Any surface that classifies a holder as recoverable must read this and not the lease file
957
- * alone, and must name what it does with `unknown` rather than inheriting a boolean it never
958
- * chose. An authority state that cannot be read is not evidence that nobody holds it.
959
- */
960
431
  readonly deriveWriterLeaseFreshness: typeof deriveWriterLeaseFreshness;
961
432
  readonly countPendingCurationJobs: typeof countPendingCurationJobs;
962
433
  readonly countRemainingCurationWork: typeof countRemainingCurationWork;
@@ -970,7 +441,6 @@ interface LocalDaemonCapabilities {
970
441
  readonly embedderWorkerProcessIsAlive: typeof embedderWorkerProcessIsAlive;
971
442
  readonly embedderWorkerSocketPath: typeof embedderWorkerSocketPath;
972
443
  readonly probeEmbedderWorkerSocket: typeof probeEmbedderWorkerSocket;
973
- /** CR-05: no surface may signal a registered worker pid without this verdict returning 'match'. */
974
444
  readonly verifyEmbedderWorkerIncarnation: typeof verifyEmbedderWorkerIncarnation;
975
445
  readonly resolveProcessIncarnation: typeof resolveProcessIncarnation;
976
446
  readonly removeEmbedderWorkerRegistration: (agentQaRoot: string, expected: EmbedderWorkerRegistration) => ReturnType<typeof removeEmbedderWorkerRegistration>;
@@ -996,7 +466,7 @@ interface LocalKnowledgeMaintenanceCapabilities {
996
466
  readonly resolveRollbackAffectedSet: typeof resolveRollbackAffectedSet;
997
467
  readonly rollbackAffectedSetFingerprint: typeof rollbackAffectedSetFingerprint;
998
468
  }
999
- interface ReceiptWriterInput extends Pick<CreateLocalKnowledgeStoreConfig, 'knowledgeRoot' | 'executionContext' | 'resolvePromotionPosture' | 'resolveTrustPriors' | 'sourceBuildOrdinal' | 'sourceRunId'> {
469
+ interface ReceiptWriterInput extends Pick<CreateLocalKnowledgeStoreConfig, 'knowledgeRoot' | 'executionContext' | 'sourceBuildOrdinal' | 'sourceRunId'> {
1000
470
  /** The caller already holds this authority; these capabilities never acquire a lease. */
1001
471
  lease: {
1002
472
  leaseId: string;
@@ -1021,10 +491,6 @@ declare function readFeedbackRecord(input: {
1021
491
  interface LocalCurationCapabilities {
1022
492
  readonly readFeedbackRecord: typeof readFeedbackRecord;
1023
493
  readonly appendMarkEvidence: typeof appendMarkEvidence;
1024
- /**
1025
- * 2026-09-15, 423 D-16/D-17: the lease-free planning half appendMarkEvidence executes, so a
1026
- * consequence preview names every confirm/overturn effect without the writer. Reads only.
1027
- */
1028
494
  readonly planMarkEvidence: typeof planMarkEvidence;
1029
495
  readonly projectMarkEvidenceOutcome: typeof projectMarkEvidenceOutcome;
1030
496
  readonly triageMark: typeof triageMark;
@@ -1037,25 +503,9 @@ interface LocalCurationCapabilities {
1037
503
  readonly openKnowledgeReader: typeof openKnowledgeReader;
1038
504
  readonly drainQueue: typeof drainQueue;
1039
505
  readonly foldRunOutcomes: typeof foldRunOutcomes;
1040
- /**
1041
- * THE mechanism a version bump rides (393 CR-02). The per-run receipt below carries the
1042
- * version, but invalidating receipts only re-runs folds that answer 'already-applied' on the
1043
- * durable marker — so the refold a bump promises happens through THIS capability, as a rebuild
1044
- * of the derived family, and nowhere else.
1045
- *
1046
- * It is invoked at exactly two sites, one per supported host (393 round 6, CR-02-daemon): once
1047
- * per CLI curation-tail sweep, and once per daemon lease acquisition. Both, because the CLI
1048
- * tail is unreachable whenever a standing daemon takes the hand-off — a promise true of one
1049
- * host is, on the other host, a projection that never converges.
1050
- */
1051
506
  readonly reconcileRunOutcomesProjection: typeof reconcileRunOutcomesProjection;
1052
507
  readonly enqueueCurationCandidateProjection: typeof enqueueCurationCandidateProjection;
1053
508
  readonly readCanonicalBundleAnchors: typeof readCanonicalBundleAnchors;
1054
- readonly readCanonicalBundleStateDigests: typeof readCanonicalBundleStateDigests;
1055
- readonly readCanaryPostureFacts: typeof canaryPostureFactsFor;
1056
- readonly readStandingGateReceipts: typeof standingReceiptDigestsFor;
1057
- readonly readLowerTrustShares: typeof lowerTrustSharesFor;
1058
- /** 418 D-07: reuse the writer's observation identity through the existing CLI bridge. */
1059
509
  readonly canaryOutcomeObservationId: typeof canaryOutcomeObservationId;
1060
510
  /**
1061
511
  * Component of the tail's receipt: a bump refolds runs already receipted at the old version.
@@ -1072,51 +522,15 @@ declare const LOCAL_CURATION_CAPABILITIES: LocalCurationCapabilities;
1072
522
  * skipped so one bad run never hides every other run.
1073
523
  */
1074
524
  declare function createRunsReader(agentQaRoot: string): RunsReaderPort;
1075
- /**
1076
- * The knowledge READ port, served through the PHYSICALLY read-only `/reader` package entry.
1077
- *
1078
- * The five read verbs live in `knowledge-review-reader.ts`, which is registered in the arch
1079
- * reader carve-out's pinned file list and therefore CANNOT name the writer-gated entry. This
1080
- * file can (it also binds the curator), so keeping the verbs here would have put them in the
1081
- * one place in the workspace where a write import is legal — separation by discipline instead
1082
- * of by rule. What stays here is the half that genuinely belongs to a composition root: the
1083
- * D-01 sighting site, which mints into the durable addressing ledger and so must live where
1084
- * the writer entry may be named.
1085
- *
1086
- * The knowledge ROOT arrives as a host-supplied resolver rather than being derived here: deciding
1087
- * which configuration file is in force, layering an environment over it and choosing whether to
1088
- * load and execute auth plugins are three decisions a daemon makes differently from a terminal,
1089
- * and by the plan-08 dissection rule a thing a headless host would substitute is a port.
1090
- */
1091
- declare function createKnowledgeReader(resolveKnowledgeRoot: () => Promise<string> | string, sightMemoryEntities?: SightMemoryEntities, resolvePromotionPosture?: () => Promise<ResolvePromotionPosture | undefined>, resolveTrustPriors?: CreateLocalKnowledgeStoreConfig['resolveTrustPriors']): KnowledgeReaderPort;
525
+ declare function createKnowledgeReader(resolveKnowledgeRoot: () => Promise<string> | string, sightMemoryEntities?: SightMemoryEntities): KnowledgeReaderPort;
1092
526
  interface CreateKnowledgeReviewInput {
1093
- readonly resolveTrustPriors?: CreateLocalKnowledgeStoreConfig['resolveTrustPriors'];
1094
- readonly resolvePromotionPosture?: () => Promise<ResolvePromotionPosture | undefined>;
1095
527
  readonly agentQaRoot: string;
1096
528
  readonly resolveKnowledgeRoot: () => Promise<string> | string;
1097
- /**
1098
- * The writer lease this host ALREADY holds, when it is the holder (D-18).
1099
- *
1100
- * The daemon's half of the routing rule. A holder cannot acquire its own lease a second time, so
1101
- * it hands the authority it owns to the change; supplying this is mutually exclusive with
1102
- * supplying a delegate, and a host that supplies both is a host claiming to be two places.
1103
- */
1104
529
  readonly resolveWriterAuthority?: () => {
1105
530
  readonly leaseId: string;
1106
531
  readonly epoch: number;
1107
532
  } | undefined;
1108
- /**
1109
- * 432-12 (D-05): how a direct change reaches a live writer-lease holder. The holder receives the
1110
- * validated request and the change's stable identity, bounded; the lease is never taken. Absent,
1111
- * a held lease refuses the change by name. Exclusive with {@link resolveWriterAuthority}: a holder
1112
- * never delegates. The daemon supplies none, which is why its own changes cannot recurse.
1113
- */
1114
533
  readonly knowledgeChangeDelegate?: ExplicitReviewDelegate;
1115
- /**
1116
- * 432-12: the context a direct change is recorded under. Absent means the process resolver, the
1117
- * default every registry in this composition already reads. The context is recorded as-is; an
1118
- * explicit change applies in every context (D-04/D-08).
1119
- */
1120
534
  readonly resolveExecutionContext?: () => ExecutionContext;
1121
535
  }
1122
536
  /**
@@ -1139,8 +553,6 @@ interface ApplyKnowledgeChangeInput {
1139
553
  readonly opId: string;
1140
554
  /** The caller's actual context, recorded as-is. An explicit change applies in every context. */
1141
555
  readonly executionContext: ExecutionContext;
1142
- readonly resolveTrustPriors?: CreateLocalKnowledgeStoreConfig['resolveTrustPriors'];
1143
- readonly resolvePromotionPosture?: () => Promise<ResolvePromotionPosture | undefined>;
1144
556
  /**
1145
557
  * How this host reaches a live writer-lease holder. The holder receives exactly the validated
1146
558
  * request and identity, bounded; the lease is never taken. Absent, a held lease refuses by name.
@@ -1152,10 +564,6 @@ interface ApplyKnowledgeChangeInput {
1152
564
  readonly epoch: number;
1153
565
  };
1154
566
  }
1155
- /**
1156
- * Apply one explicit knowledge change through the direct writer (432-06), with this host's short-id
1157
- * resolver. Nothing is filed: the change is committed, or it is refused by type.
1158
- */
1159
567
  declare function applyKnowledgeChange(input: ApplyKnowledgeChangeInput): Promise<ExplicitReviewResult>;
1160
568
  interface CreateEntityDirectoryInput {
1161
569
  readonly agentQaRoot: string;
@@ -1172,53 +580,10 @@ interface SweepEntityIndexOutcome extends EntityIndexSweepReport {
1172
580
  /** How many renderings gained a vector this sweep. Zero on a floor host, by design. */
1173
581
  readonly embedded: number;
1174
582
  }
1175
- /**
1176
- * The daemon's maintenance-cadence sweep over the D-06 projection.
1177
- *
1178
- * Three properties, and each is a deliberate refusal rather than an omission:
1179
- *
1180
- * - it takes NO writer lease. Everything it writes lives in the entity index's own file, and
1181
- * a projection that needed the lease could not be maintained by a host that does not hold
1182
- * one — which is every one-shot CLI process.
1183
- * - it never writes the addressing ledger. It READS the ledger to know what exists (D-02: the
1184
- * ledger is the durable truth, not a projection) and writes only the rendering of it.
1185
- * - it embeds only what a host can already embed. `acquireEmbedder` with no download is the
1186
- * same ladder the retrieval legs ride: ready means the dense rung joins the resolver on this
1187
- * host, and anything else leaves the projection lexical-only rather than blocking the sweep.
1188
- */
1189
583
  declare function sweepEntityIndexProjection(input: SweepEntityIndexInput): Promise<SweepEntityIndexOutcome>;
1190
- /**
1191
- * The host's run-execution capability: run this request to completion, and say which run it was.
1192
- *
1193
- * A PORT rather than shared code, by the plan-08 dissection rule. Would a headless drainer
1194
- * execute a suite the way a terminal host does? It would substitute — the process model
1195
- * (in-process, a child, a worker pool), the signal handling, the reporters and the exit
1196
- * discipline are all decisions the host owns and answers differently. What crosses into the
1197
- * composition is the type; what stays with the host is the orchestration.
1198
- *
1199
- * The verb resolves the IDENTITY of the run and nothing else. Anything richer would be the
1200
- * host telling the composition what happened, and the whole point of the binding below is
1201
- * that the composition does not take the host's word for it.
1202
- */
1203
584
  interface RunExecutionPort {
1204
- /**
1205
- * 2026-10-01 (432-18, D-15): the optional `gate` argument (401-08, D-08) carried the standing
1206
- * approvals in force — the durable ledger's answer unioned with any dispatch-scoped grant — to a
1207
- * run gate behind the port. The run gate went in 432-13 and the ledger and the dispatch overlay
1208
- * go in 432-18, so a host is handed the request and nothing else.
1209
- */
1210
585
  execute(request: RunsStartInput): Promise<StartedRun>;
1211
586
  }
1212
- /**
1213
- * The run this host reported did not leave a durable event log behind.
1214
- *
1215
- * The composition's evidence check, and the reason `runs.start` cannot be satisfied by a host
1216
- * that returns a plausible run id without running anything. The v49 bus writes `runStarted`
1217
- * before anything else can happen, so a run that began at all has a non-empty log; an empty
1218
- * one means no run took place under that id. Read through {@link buildRunPorts} — the same
1219
- * `FsEventLog` family the runner appends with — so what is inspected is the run's own record
1220
- * rather than a path this file re-derives.
1221
- */
1222
587
  declare class RunProducedNoEventsError extends Error {
1223
588
  readonly runId: string;
1224
589
  constructor(runId: string);
@@ -1253,15 +618,6 @@ interface ParsedDraftDescriptor {
1253
618
  readonly testId: string;
1254
619
  readonly filePath: string;
1255
620
  readonly testName: string;
1256
- /**
1257
- * The parsed test's `target`. REQUIRED, exactly as `ParsedDraftLike.target` and
1258
- * `TestDefinitionSchema` require it (394-37, WR-04).
1259
- *
1260
- * A mirror that dropped it would hand `packages/agent` a draft whose target the compile stage
1261
- * cannot check against the vocabulary it just served — and the cost of not checking is paid as
1262
- * real child gate runs on a name `resolveTarget` throws on, against a key no repair patch may
1263
- * edit.
1264
- */
1265
621
  readonly target: string;
1266
622
  readonly steps: readonly string[];
1267
623
  /**
@@ -1272,14 +628,6 @@ interface ParsedDraftDescriptor {
1272
628
  readonly context?: string;
1273
629
  readonly contentDigest: string;
1274
630
  }
1275
- /**
1276
- * What a host can say about a caller-supplied draft path.
1277
- *
1278
- * Four refusal arms and one success, because `packages/agent` is `storageNeutral` and genuinely
1279
- * cannot answer any of them: it can neither stat a path nor know where the workspace ends. The op
1280
- * turns each arm into its own NAMED error, so the refusal a caller catches is declared beside the
1281
- * capability while the syscall stays with the host that owns a filesystem (T-393-01).
1282
- */
1283
631
  type DraftParseOutcome = {
1284
632
  readonly kind: 'parsed';
1285
633
  readonly draft: ParsedDraftDescriptor;
@@ -1293,89 +641,18 @@ type DraftParseOutcome = {
1293
641
  readonly kind: 'unparsable';
1294
642
  readonly reason: string;
1295
643
  };
1296
- /** One authored suite test as TEXT, for gate 3's degraded lane (D-16). */
1297
644
  interface SuiteTextEntry {
1298
645
  readonly filePath: string;
1299
646
  readonly testName: string;
1300
647
  readonly stepsText: readonly string[];
1301
648
  }
1302
- /**
1303
- * The host's authoring capability: parse ONE draft, and enumerate the authored-test universe.
1304
- *
1305
- * BOTH VERBS OR NEITHER, and the bundling is a report rather than a convenience. A host that
1306
- * supplied only the parse would bind a capability whose gate-3 text lane is permanently empty —
1307
- * evidence-less suite tests would silently stop joining the comparison, and a draft would look
1308
- * novel purely because whatever it duplicates has never been run. That is D-16's unsafe direction
1309
- * arrived at by omission, and the only way to make it un-omittable is to make the two arrive
1310
- * together.
1311
- *
1312
- * Both verbs live at the HOST because both need `parseTestFile` from `@vostride/agent-qa-core`,
1313
- * which neither this package nor `agent` may import (the FUNC-02 firewall) — the parse-at-host
1314
- * precedent `cli/src/curation/authoring-test-discovery.ts` established.
1315
- */
1316
649
  interface DraftAuthoringPort {
1317
650
  parseDraft(filePath: string): Promise<DraftParseOutcome>;
1318
651
  suiteTextCorpus(): Promise<readonly SuiteTextEntry[]>;
1319
- /**
1320
- * 394-33 · WR-07: the names a `target` may legally take in this project.
1321
- *
1322
- * ON THIS PORT for the reason `deriveDefaultScope` is: both hosts build their authoring
1323
- * capability from ONE factory, so a verb that lives here is reached by the CLI `fn` surface and
1324
- * the chat/daemon host without a second binding either could forget. A vocabulary served by only
1325
- * one of the two hosts would make "does stage 3 guess at `target`" depend on which surface a
1326
- * person happened to author from.
1327
- *
1328
- * IT ENUMERATES AND NEVER REFUSES — the opposite posture to `deriveDefaultScope` right below it,
1329
- * which throws when the answer is not unique. There is no such thing as an ambiguous list, and a
1330
- * project declaring no targets has an honest empty answer rather than a failure.
1331
- */
1332
652
  configuredTargetNames(): Promise<readonly string[]>;
1333
- /**
1334
- * 394-26 · WINDOWS 110: which scope this project authors for, when the caller named none.
1335
- *
1336
- * ON THIS PORT rather than beside it, and that placement is the guarantee. Both hosts build
1337
- * their authoring capability from one factory, so a verb that lives here is reached by the CLI
1338
- * `fn` surface and the chat/daemon host without a second binding either could forget — which is
1339
- * what makes "there is ONE derivation" a fact about the wiring rather than a convention.
1340
- *
1341
- * IT THROWS RATHER THAN ANSWERING `undefined` when the project has no unique product: the host
1342
- * is the only side that knows which candidates it saw, and a refusal that cannot name them
1343
- * leaves an author with no way to write the `scope` that would have worked.
1344
- */
1345
653
  deriveDefaultScope(): Promise<ScopeKey>;
1346
- /**
1347
- * 394 D-01 · WTST-01: PARSE the compiled draft, then WRITE it to the shelf — in that order, as
1348
- * one verb, joining for exactly the reason the two above bundle.
1349
- *
1350
- * ONE VERB AND NOT TWO, because the order is the mitigation (T-394-06). Model-authored YAML is
1351
- * the one input on the authoring path that no schema at the op boundary can vet, and separate
1352
- * verbs would let a caller write first and discover the parse failure afterwards — with bytes
1353
- * already on this workspace's disk. Here an unparsable draft answers `unparsable` having
1354
- * created nothing at all.
1355
- *
1356
- * THE DIGEST IS THE HOST'S, over the bytes it is writing, and it is the caller's only
1357
- * addressing: the shelf path is minted from it, so nothing the model emitted reaches the
1358
- * filesystem as a path component. `DraftRef.contentDigest`'s whole promise — a later edit
1359
- * cannot inherit an earlier receipt — is only true while the digest is over what was stored.
1360
- */
1361
654
  stageDraft(contents: string): Promise<DraftStageOutcome>;
1362
- /**
1363
- * 394-06 D-09/D-20: apply an RFC-6902 patch set to a staged draft and stage the result.
1364
- *
1365
- * AT THE HOST for the same reason its two neighbours are: `applyPatchSetToTestDoc` needs the
1366
- * yaml CST from `@vostride/agent-qa-core`, which neither this package nor `agent` may import.
1367
- * It answers the same `DraftStageOutcome`, so a patch that renders an unrunnable document is
1368
- * refused with the shelf untouched rather than written and discovered later.
1369
- */
1370
655
  applyPatchesToDraft(draft: ParsedDraftDescriptor, patchSet: TestEditPatchSet): Promise<DraftStageOutcome>;
1371
- /**
1372
- * 432 D-06/D-07: write a staged draft's bytes to its test path — the direct final-test writer.
1373
- *
1374
- * AT THE HOST for its neighbours' reason: the workspace identity mint lives in
1375
- * `@vostride/agent-qa-core`. The host checks the boundary and that the target still holds the
1376
- * bytes the caller read (absent for a new test), writes through a sibling temp and a rename, runs
1377
- * no git, and answers `not-written` with its reason whenever the bytes did not land.
1378
- */
1379
656
  publishDraft(request: DraftPublishRequest): Promise<TestFileWrite>;
1380
657
  }
1381
658
  /** One draft to write: its shelf path, its target, and what the target held when it was read. */
@@ -1401,13 +678,6 @@ type DraftStageOutcome = {
1401
678
  /** Wall-clock in the one format every store column and dump row is written in. */
1402
679
  declare const SYSTEM_CLOCK: ClockPort;
1403
680
  declare const DIGEST_JOB_ID_SOURCE: JobIdSource;
1404
- /**
1405
- * The durable half of the host, opened on demand.
1406
- *
1407
- * `store` is the concrete sqlite store rather than the neutral port: the host that opened the
1408
- * database is the one entitled to close it and to take the D-03 logical dump, and both verbs live
1409
- * on the implementation. Nothing else in the package publishes it.
1410
- */
1411
681
  interface LocalAgentJobs {
1412
682
  readonly queue: AgentJobQueue;
1413
683
  readonly store: SqliteJobStore;
@@ -1440,15 +710,8 @@ declare class LocalAgentClosedError extends Error {
1440
710
  constructor(agentQaRoot: string);
1441
711
  }
1442
712
  interface LocalAgentHost {
1443
- /** The 384 function registry with every seed capability bound over local storage. */
713
+ /** The function registry with every seed capability bound over local storage. */
1444
714
  readonly registry: FunctionRegistry;
1445
- /**
1446
- * The live chat session composed beside the registry (392 plan 08).
1447
- *
1448
- * Present on EVERY host, because the turn engine is (D-24) — a host whose chat session were
1449
- * optional would make `agent.ask` bound and its state absent, which is two answers to whether
1450
- * this host can hold a conversation.
1451
- */
1452
715
  readonly chat: LocalAgentChatSession;
1453
716
  /** The knowledge-writer powers this host grants — the same frozen object for every host. */
1454
717
  readonly curation: LocalCurationCapabilities;
@@ -1468,419 +731,324 @@ interface LocalAgentHost {
1468
731
  /** Close whatever was opened and join an open already in flight. Idempotent. */
1469
732
  close(): Promise<void>;
1470
733
  }
1471
- /**
1472
- * What a host's curation binding does with candidate prose (388-10, UF-388-01 / D-11).
1473
- *
1474
- * A closed union, REQUIRED wherever `postRun` is supplied: a host that cannot say what its
1475
- * fold does with candidate prose has not been reviewed for UF-388-01, and the typechecker is
1476
- * what makes that review mandatory rather than customary.
1477
- *
1478
- * - `'masker-active'` — the fold has the declared-secret mask bound (441-01 decision 10), or the
1479
- * binding never reaches candidate emission at all.
1480
- * - `'no-masker'` — the binding folds with NO declared mask. A statement about DISCLOSURE, not
1481
- * about capability: the fold would run, but its prose masking would degrade to a pass-through,
1482
- * so a host declaring this may not claim the kind that runs it.
1483
- */
1484
734
  type CandidateProsePosture = 'masker-active' | 'no-masker';
1485
- /**
1486
- * The chain kinds a host of each posture MAY NOT CLAIM — one table, no second spelling.
1487
- *
1488
- * A `Record` over the union rather than a condition, deliberately: this phase's named defect
1489
- * class is a guard whose input domain is written in prose while the code ranges over a
1490
- * subset, and a total `Record` stops compiling the day a third posture is added. The queue's
1491
- * `unclaimableKinds` is derived from this table and from nothing else.
1492
- */
1493
735
  declare const UNCLAIMABLE_BY_PROSE_POSTURE: Record<CandidateProsePosture, readonly JobKind[]>;
1494
736
  interface ComposeLocalAgentInput {
1495
737
  readonly mask: MaskFn;
1496
- /** Immutable neutral data resolved by the host, reused until recomposition. */
1497
- readonly resolveTrustPriors?: CreateLocalKnowledgeStoreConfig['resolveTrustPriors'];
1498
738
  readonly feedback?: FeedbackPort;
1499
- /**
1500
- * 423 D-19/D-16: read-only mark history and consequence preview; absent leaves runs.marks.history
1501
- * and runs.mark.preview unbound by name, and a port without `preview` leaves only the preview unbound.
1502
- */
1503
739
  readonly feedbackReads?: FeedbackReadsPort;
1504
- /** DATA across the wall: host preparation may await; candidate evaluation is synchronous. */
1505
- readonly resolvePromotionPosture?: () => Promise<ResolvePromotionPosture | undefined>;
1506
740
  /** The project's runtime root, i.e. `<configDir>/.agent-qa`. Absolute. */
1507
741
  readonly agentQaRoot: string;
1508
- /**
1509
- * Masked configuration reads. Injected because flattening and masking a resolved configuration
1510
- * needs the host's own loader — its file layering, its environment, its plugin policy — and the
1511
- * masking decision is a disclosure boundary the host owns (T-384-07 / T-384-17).
1512
- */
1513
742
  readonly config: ConfigReaderPort;
1514
743
  /** Resolves the knowledge root this invocation serves from. See `createKnowledgeReader`. */
1515
744
  readonly knowledgeRoot: () => Promise<string> | string;
1516
- /**
1517
- * The model a job may author with (D-10, plan 05).
1518
- *
1519
- * A required factory. REQUIRED because the queue's own `model` option is required and not
1520
- * defaultable — an optional capability means a queue can be built with none, and the only place
1521
- * to discover that is inside an author callback far from the composition that forgot it. A
1522
- * FACTORY because `jobs()` is deferred, and resolving a provider handle costs a configuration
1523
- * read and an auth resolution that a host listing the registry must not pay.
1524
- *
1525
- * It is called at most once, when the queue is opened. `agent-local` never constructs a model:
1526
- * its ownership contract forbids provider invocation, so the handle arrives already built and no
1527
- * provider name is named here.
1528
- */
1529
745
  readonly model: () => JobModelCapability | Promise<JobModelCapability>;
1530
- /**
1531
- * How this host executes a run (D-16). OPTIONAL, and the optionality is the reporting
1532
- * mechanism rather than a convenience: a host that supplies it lists `runs.start` as
1533
- * `bound` and can run it; a host that does not lists the same capability as `unbound` and
1534
- * refuses it by name. Not every composition can start a run — a listing, a read-only
1535
- * surface, 386's daemon before it wires an executor — and D-09 requires that to be a
1536
- * visible state rather than a call that fails somewhere far from the composition.
1537
- */
1538
746
  readonly runExecution?: RunExecutionPort;
1539
- /**
1540
- * How this host parses a draft and enumerates its authored suite (393 D-01), on the exact
1541
- * optional-operation idiom `runExecution` above uses.
1542
- *
1543
- * OPTIONAL, and — like its neighbour — the optionality is the reporting mechanism rather than a
1544
- * convenience. `authoring.draft.verify` is bound only where this AND `runExecution` are both
1545
- * present, because a host that cannot parse a test file and a host that cannot start a run are
1546
- * both hosts that cannot verify a draft. Either one missing lists the capability `unbound` BY
1547
- * NAME, which is the honest answer; the alternative is a receipt whose four booleans were
1548
- * computed from no runs at all, and a refusal wearing a verdict's clothes is the one answer that
1549
- * could get a draft kept on the strength of nothing.
1550
- *
1551
- * Host-supplied for the reason `mintQuarantineIdentityPath` is: it needs `parseTestFile` and the
1552
- * CHECKOUT ROOT, and neither this package nor `agent` may name `@vostride/agent-qa-core`.
1553
- */
1554
747
  readonly draftAuthoring?: DraftAuthoringPort;
1555
- /**
1556
- * 395-01 D-03/D-17 · CFGM-01/04: the committed config file SET as RAW BYTES, plus the
1557
- * comment-preserving field edit over them.
1558
- *
1559
- * Host-supplied because the edit is `yaml`'s Document API, and neither this package nor `agent`
1560
- * may name that dependency under their ownership contracts. The CLI host already depends on it and already owns the file format.
1561
- */
1562
748
  readonly configFiles?: ConfigFilesPortLike;
1563
- /**
1564
- * 395-01 CFGM-01: whole-config validation, run BEFORE a proposal surfaces.
1565
- *
1566
- * Host-supplied because `AgentQaConfigSchema` lives in `@vostride/agent-qa-core`, which neither
1567
- * this package nor `agent` may name — the `mintQuarantineIdentityPath` arrangement exactly.
1568
- */
1569
749
  readonly configValidate?: ConfigValidatePortLike;
1570
- /**
1571
- * 395-08 D-04 · CFGM-05: the shared scaffold CONTENT, rendered by the host that owns it.
1572
- *
1573
- * A SEPARATE OPTIONAL BINDING. The substance lives beside
1574
- * `init`'s own write path in `packages/cli`, which neither this package nor `packages/agent` may
1575
- * import; its absence leaves `config.project.scaffold` — and only that op — unbound, which is the
1576
- * honest catalogue answer for a host that can edit a configuration but cannot create one.
1577
- */
1578
750
  readonly scaffoldContent?: ConfigScaffoldContentPortLike;
1579
- /**
1580
- * 2026-09-30, 432-07 D-05/D-07: the direct config writer. Host-supplied because its transaction
1581
- * is the dashboard's config file transaction, shared per path with the dashboard's own writes.
1582
- */
1583
751
  readonly configWrite?: FunctionDeps['configWrite'];
1584
- /**
1585
- * 2026-08-17 (398 D-09 / D-11 · XPLN-02): the four host halves `agent.explain` needs — the
1586
- * committed capability catalog as data, the model's citation over it, and the two coverage hooks.
1587
- *
1588
- * Host-supplied for the reason `scaffoldContent` is, and the boundary is harder here rather than
1589
- * softer: the artifact is read by `loadCapabilityCatalog` in `packages/cli`, which walks the
1590
- * package root and touches `fs`, and this package is host-neutral. The citation call needs the
1591
- * model the chat host already composed through `createModel`, which is a resolution neither this
1592
- * package nor `packages/agent` may perform. The coverage append is a file write.
1593
- *
1594
- * OPTIONAL, and the absence is the report. A composition that omits it lists `agent.explain`
1595
- * `unbound` BY NAME rather than degrading to a model-only answer — which is the one behaviour
1596
- * XPLN-02 exists to forbid, and the one that would be hardest to notice, because an explain
1597
- * answering from model memory still sounds exactly like an explain.
1598
- */
1599
752
  readonly capabilityCatalog?: CapabilityCatalogPort;
1600
- /**
1601
- * 403 D-03: the live-view mint seam `runs.liveurl` binds through, handed straight through from
1602
- * the host — this composition decides nothing about it.
1603
- *
1604
- * OPTIONAL because the absence is a genuine host fact and not a degradation: a host serving no
1605
- * HTTP origin has no URL it could honestly mint, so the op lists `unbound` BY NAME rather than
1606
- * returning a link that resolves to nothing.
1607
- */
1608
753
  readonly liveView?: LiveViewPort;
1609
- /**
1610
- * 405 D-03: the durable run-detail projection read `runs.detail` binds through, handed straight
1611
- * through from the host — this composition decides nothing about it and constructs no database.
1612
- *
1613
- * OPTIONAL because a host may have no projection to read, and then `runs.detail` lists `unbound`
1614
- * BY NAME. The CLI host binds a reader over the configured or default runs database path whenever
1615
- * its config parses (a read before that file exists answers not recorded in the runs database);
1616
- * the dashboard passes its own handle.
1617
- */
1618
754
  readonly runDetail?: RunDetailReaderPort;
1619
- /**
1620
- * 432-12 (D-05): how a direct knowledge change reaches the writer-lease holder when one is alive,
1621
- * as an OPTIONAL operation on the input bag rather than a new production seam (the 386-41 D-2
1622
- * idiom): `agent-local` cannot name a socket and does not. The CLI host injects a closure
1623
- * over `invokeViaDaemon` that invokes the change op there with the same identity; the daemon's
1624
- * own composition injects nothing, because it holds the lease.
1625
- */
1626
755
  readonly knowledgeChangeDelegate?: ExplicitReviewDelegate;
1627
- /** The writer lease a leaseholding host already owns (387-08 D-18). See `createKnowledgeReview`. */
756
+ /** The writer lease a leaseholding host already owns. See `createKnowledgeReview`. */
1628
757
  readonly resolveWriterAuthority?: () => {
1629
758
  readonly leaseId: string;
1630
759
  readonly epoch: number;
1631
760
  } | undefined;
1632
- /**
1633
- * Mints the durable workspace-relative identity the quarantine ledger keys `filePath` on
1634
- * (390-12 / D-01). Core's `workspaceRelativeIdentityPath`, pre-bound by the host to its own
1635
- * config directory.
1636
- *
1637
- * Host-supplied for the same reason `resolveWriterAuthority` one field up is: the CHECKOUT ROOT is
1638
- * a fact only a host knows. Neither this composition nor the verbs beneath it can discover it, and
1639
- * a layer that guessed would mint identities against the wrong root.
1640
- *
1641
- * REQUIRED, and — unlike its neighbour — not conditionally spread. The optionality of the writer
1642
- * authority is a real report about a real difference between hosts: one holds the lease, one does
1643
- * not. There is no host that can legitimately lack a checkout root, so an absent mint is not a
1644
- * posture, it is exactly the unvalidated pass-through that let the write verbs author permanently
1645
- * unreadable lines into a committed artifact. It belongs at compile time, at the composition that
1646
- * forgot it.
1647
- */
1648
761
  readonly mintQuarantineIdentityPath: (filePath: string) => string;
1649
762
  readonly clock?: ClockPort;
1650
763
  readonly idSource?: JobIdSource;
1651
- /** The kind registry the queue dispatches through. Defaults to the six real declarations. */
764
+ /** The kind registry the queue dispatches through. Defaults to the real declarations. */
1652
765
  readonly jobKinds?: JobKindRegistry;
1653
- /**
1654
- * What the post-run chain's real handlers may do (388 D-01), on the optional-operation
1655
- * idiom `runExecution` and `applyDelegate` already use.
1656
- *
1657
- * OPTIONAL, and the optionality is the report rather than a convenience: a host that
1658
- * supplies it dispatches the REAL analyze-run and curate-memory bodies, and a host that
1659
- * does not keeps the scripted stand-ins. Only `curateRun` is asked for — the handoff read
1660
- * is built here, from `agentQaRoot`, because it is a path derivation this package already
1661
- * owns and a caller-supplied one would be a second layout to keep in step with the writer's.
1662
- *
1663
- * `curateRun` is the caller's because it is not a local concern at all: it needs the writer
1664
- * lease, a knowledge root and the v51 drain, and the CLI's run tail is where all three
1665
- * already meet (plan 02 binds it).
1666
- */
1667
766
  readonly postRun?: {
1668
767
  readonly curateRun: PostRunJobCapabilities['curateRun'];
1669
- /**
1670
- * REQUIRED beside the binding it describes: a `postRun` that omits it must stop
1671
- * typechecking, because the posture is the UF-388-01 review of this binding and an
1672
- * unreviewed binding is exactly what let the daemon fold unmasked. See
1673
- * `CandidateProsePosture` for what each member does and does not certify.
1674
- */
1675
768
  readonly candidateProse: CandidateProsePosture;
1676
- /**
1677
- * The 422 D-13 proposal stage's staging port, passed straight through.
1678
- *
1679
- * OPTIONAL beside a REQUIRED `curateRun`, and the asymmetry is the contract: without
1680
- * `curateRun` there is no real fold and therefore no real chain, whereas a host that binds
1681
- * no staging port still runs the third stage — it stages nothing, records no counts, and
1682
- * the terminal says exactly that. Passed through rather than built here for the same reason
1683
- * `curateRun` is: the adversarial staging is not a local concern, and this package may not
1684
- * name the seam it lives behind.
1685
- */
1686
- readonly stageAdversarialProposal?: PostRunJobCapabilities['stageAdversarialProposal'];
1687
- /**
1688
- * The 422 D-14 Gate-1 leg's four ports, passed straight through (422-09).
1689
- *
1690
- * NEUTRAL FORWARDING, and the neutrality is the contract rather than a style note: every one
1691
- * of these needs a committed corpus, a screening run and a writer this package may not name,
1692
- * and a version of this file that could construct them could not be this file. What crosses
1693
- * here is four closures and a number; what stays on the other side is every fact about where
1694
- * they read from.
1695
- *
1696
- * Each is OPTIONAL and each is spread on presence, because a host that binds none is the
1697
- * shipped posture — the stage runs, screens nothing, and says so by omitting the member.
1698
- */
1699
- readonly runReplayGate?: PostRunJobCapabilities['runReplayGate'];
1700
- readonly listBundles?: PostRunJobCapabilities['listBundles'];
1701
- readonly gateReceiptsFor?: PostRunJobCapabilities['gateReceiptsFor'];
1702
- readonly readProposeCorpusPosture?: PostRunJobCapabilities['readProposeCorpusPosture'];
1703
- /**
1704
- * 2026-09-13, 422 D-19, gap plan 422-27: the host's durable per-run evaluation-root
1705
- * posture, forwarded as a neutral closure. Absence leaves the handler's fail-closed
1706
- * default in force; this composition neither resolves nor interprets the posture.
1707
- */
1708
- readonly readMisdirectionRootPosture?: PostRunJobCapabilities['readMisdirectionRootPosture'];
1709
- /**
1710
- * The operator's per-job Gate-1 bound, as a FACTORY resolved once at queue open.
1711
- *
1712
- * A factory on the exact `model` / `analysisTokenBudget` idiom, for the same reason those are:
1713
- * reading it needs a configuration read, and paying for one at composition would charge every
1714
- * host that only lists the registry. The capability itself is plain data — a number the
1715
- * handler reads and never a reader it calls.
1716
- */
1717
- readonly proposeGate1MaxPerJob?: () => Promise<number | undefined> | number | undefined;
1718
769
  };
1719
- /**
1720
- * D-01's TokenAccounting substrate for the analyze-run author (389 plan 06). OPTIONAL
1721
- * and host-resolved: both production hosts derive it from the SAME model-profile
1722
- * resolution as the job model itself (one source of truth, no second config surface —
1723
- * 386 D-15), and `composeLocalAgent` only passes the resolved value through. A FACTORY
1724
- * on the exact `model` idiom — resolution may need a configuration read, and it is paid
1725
- * once, at queue open, never by a host that only lists the registry. A factory that
1726
- * answers `undefined` composes the plan-04 no-ladder posture: packs travel whole.
1727
- */
1728
770
  readonly analysisTokenBudget?: () => Promise<PostRunJobCapabilities['analysisTokenBudget']> | PostRunJobCapabilities['analysisTokenBudget'];
1729
- /**
1730
- * The host's execution context for this composition (400-01, D-05).
1731
- *
1732
- * 2026-10-02 (432-39, D-12): it reaches the knowledge review, which records a direct change under
1733
- * it, and nothing else here; the registry option it was also forwarded to is deleted, because no
1734
- * op decides in a context. The history below explains why it is injected per composition.
1735
- *
1736
- * INJECTED RATHER THAN PINNED ON THE PROCESS, and that is the whole reason this field exists.
1737
- * The registry accepted a `resolveExecutionContext` from 384 until 432-39; what it did NOT have
1738
- * was a way for a host to supply one, so the only lever a host had was
1739
- * `setActiveExecutionContextResolver` — a PROCESS GLOBAL. Reaching for it changes the answer
1740
- * for everything composed in that process, including compositions the host does not own, and a
1741
- * host that forgot to restore it leaks its posture into every later caller. The dashboard door
1742
- * is the first host that has to state a posture per composition rather than per process
1743
- * (D-01/D-05: the browser confirm channel is what makes the context interactive, and it is a
1744
- * property of a server, not of the machine), so the injected form is what it binds.
1745
- *
1746
- * ABSENT MEANS THE SHIPPED AMBIENT RESOLVER, unchanged: the knowledge review's own default reads
1747
- * safety's process resolver, so every existing host composes exactly as it did before. This is
1748
- * a passthrough and nothing here decides what a context MEANS. (432-19: `requiresFailClosed`, the
1749
- * gate's reading of a context, is deleted; the four-member vocabulary stays.)
1750
- */
1751
771
  readonly resolveExecutionContext?: () => ExecutionContext;
1752
- /**
1753
- * Notified AFTER one session event has been appended to its log (400-01, D-03).
1754
- *
1755
- * AN OBSERVER, AND EVERY WORD OF THAT IS THE CONTRACT. It is called with an event that is
1756
- * ALREADY ON THE FILE — after `append` resolves, never before and never instead — so a host
1757
- * pushing these somewhere cannot show a person an event the durable log does not carry. It
1758
- * cannot append, cannot rewrite and cannot refuse: the return value is discarded and a throw is
1759
- * swallowed at the notify site, because a surface's push channel failing must not fail the turn
1760
- * that produced the event.
1761
- *
1762
- * WHY THIS AND NOT A HOST-SUPPLIED `SessionLogPort`. The obvious alternative — let a host wrap
1763
- * or replace the log — is exactly what D-04 forbids: sessions belong to the agent, not to a
1764
- * surface, and a replaceable log is a second store one conversation at a time. This seam widens
1765
- * nothing. The log stays the single producer and the single writer; what a host gets is a
1766
- * carbon copy of what was written.
1767
- *
1768
- * The SESSION ID travels with the event because the event itself carries only a `turnId` — the
1769
- * grammar's exact-key guard means it never will carry more (Pitfall 4), so the fact a subscriber
1770
- * needs is supplied beside the record rather than smuggled into it.
1771
- */
1772
772
  readonly observeSessionEvent?: (sessionId: string, event: ChatSessionEvent) => void;
1773
- /**
1774
- * The turn engine's reconciliation strategy (392 D-16).
1775
- *
1776
- * AN INTERNAL COMPOSITION OPTION, and every word of that is load-bearing. It is here so the
1777
- * mutation proof can assemble the REAL host twice — override on, override off — and read the
1778
- * difference off a named compound scenario, which is the only differential that measures what
1779
- * production actually wires (Pitfall 3, the produced-not-literal law).
1780
- *
1781
- * IT IS NOT, AND MUST NEVER BECOME, CONFIG-REACHABLE. No field of `AgentQaConfig` names it and
1782
- * none may: the deterministic override is the mechanism that stops a model's plan from beating
1783
- * a proven mapping, so a production knob that switched it off would be a supported way to
1784
- * disable a safety property. Absent, the shipped strategy is used — which is what makes the
1785
- * absence the safe default rather than an unconfigured state.
1786
- */
1787
773
  readonly chatReconcile?: ReconcileStrategy;
1788
- /**
1789
- * The model stage 2 plans with, ALREADY BUILT by the host through `createModel` (CHAT-06).
1790
- *
1791
- * A model OBJECT and never a model id: the SDK's own type also admits an id it resolves by
1792
- * constructing a client, which would put a provider client behind chat without the masking
1793
- * middleware and the profile resolution `createModel` owns. The type closes that door here.
1794
- *
1795
- * ABSENT IS A REAL POSTURE, NOT AN UNCONFIGURED ONE. A host with no model bound is HEADLESS: any
1796
- * clause the deterministic table cannot resolve fails the turn closed by name (D-08), which is
1797
- * the correct behaviour rather than a degraded one. That is also why no planner is stubbed in —
1798
- * a stub answering "I could not plan that" would say the same words about an entirely different
1799
- * fact, that a model was asked and declined.
1800
- */
1801
774
  readonly chatModel?: ChatLanguageModel;
1802
- /**
1803
- * The accounting instance chat's degrade pass measures against (CHAT-04, plan 06).
1804
- *
1805
- * HOST-RESOLVED and passed through untouched, on `analysisTokenBudget`'s precedent and for its
1806
- * reason: the ceilings must come from the SAME capability resolution as the chat model itself,
1807
- * or a session would compact against a window the model does not have. This composition adds no
1808
- * knob of its own and cannot construct one — `agent-local` reaches no provider.
1809
- *
1810
- * Absent ⇒ no degrade pass runs and the `compaction` seat stays empty, which is the posture
1811
- * every host had before a model was bound.
1812
- */
1813
775
  readonly chatAccounting?: ChatTokenAccounting;
1814
- /**
1815
- * How this host resolves a model NAMED by `/model <name>` (D-04, plan 08).
1816
- *
1817
- * Supplied by the host for the same reason `chatModel` is, and enforced by the same rule: the
1818
- * chat subtree may not construct a model, so a session-scoped switch has to arrive as a
1819
- * capability the composition was handed. Absent ⇒ `/model` still records the session's declared
1820
- * model and emits its `model_change` (the header, the prompt and the drift check all follow it),
1821
- * and no new client is resolved — the honest behaviour for a host that composed one model.
1822
- */
1823
776
  readonly chatModelFor?: (name: string) => ChatLanguageModel | Promise<ChatLanguageModel>;
1824
- /**
1825
- * Where a chat session PRINTS a mid-turn artifact a person reads (395-13, D-14).
1826
- *
1827
- * A PLAIN FUNCTION: printing needs nothing from this composition. The surface is handed text and
1828
- * writes it. (432-16 deleted the `chatConfirm` factory that used to sit beside it.)
1829
- *
1830
- * PASSED THROUGH UNTOUCHED to the turn engine's `present`. Absent ⇒ the engine renders nothing
1831
- * mid-turn, which is every host that is not a REPL — and no gate, receipt or session row moves.
1832
- */
1833
777
  readonly chatPresent?: (text: string) => void | Promise<void>;
1834
778
  }
1835
- /**
1836
- * The live chat session a host holds beside the registry (plan 08, RESEARCH Open Q2).
1837
- *
1838
- * Exposed because two of its facts have no other route to a surface: the state a REPL renders
1839
- * from, and the ability to close an interrupted turn honestly. Everything else a conversation does
1840
- * goes through `agent.ask` like every other capability (D-24).
1841
- */
1842
779
  interface LocalAgentChatSession {
1843
- /**
1844
- * The live state of ONE conversation, or `undefined` for a session this host has not opened
1845
- * (392 plan 13, CR-02).
1846
- *
1847
- * A LOOKUP RATHER THAN A CONTAINER, because the facts inside it — the turn in flight, the
1848
- * conversation, the model in force, what `/pin` denies, whether `/quit` was typed — are facts
1849
- * about a session and never about a host. A composition serving two conversations that handed
1850
- * out one container was answering both of them with whichever turn wrote last, which is what
1851
- * let a planner read the wrong history (CR-02) and a pin outlive its session (W-03).
1852
- *
1853
- * `undefined` is the honest answer for an id nobody here is holding, and reading NEVER creates
1854
- * a scope: a surface asking about a session cannot bring one into being by asking.
1855
- */
1856
780
  stateFor(sessionId: string): ChatStateSnapshot | undefined;
1857
- /**
1858
- * Write the explicit `aborted` event for the turn in flight, and fsync (D-19's graceful arm).
1859
- *
1860
- * A no-op between turns, deliberately: an `aborted` row for a turn that already carries its
1861
- * `turn_complete` would make the repair on the next open trim a turn that finished.
1862
- */
1863
781
  abortActiveTurn(): Promise<void>;
1864
782
  }
1865
- /** Compose the local agent host. */
1866
- /**
1867
- * The two callers chat mints, one per stage (392, RESEARCH Pitfall 1 / 391 D-06).
1868
- *
1869
- * IDENTICAL BUT FOR `origin`. `FunctionCaller.origin` answers "did a person decide this", and inside
1870
- * ONE turn both answers occur: a person typing a canonical alias decided that call, and a model
1871
- * planning one did not. The receipts a turn logs record that origin per clause.
1872
- *
1873
- * `surface` is `'chat'` on both and is ATTRIBUTION ONLY. Nothing in the function layer may branch
1874
- * on it (`invoke.ts:28-29`).
1875
- *
1876
- * 2026-10-01 (432-20/432-21, D-12): no scope set and no safe-mode flag. `CHAT_STAGE_SCOPES`, the
1877
- * whole closed scope union both stages held, is deleted: every caller can call every operation, and
1878
- * the human-origin restriction the origin split once protected is deleted with `requiresHumanOrigin`.
1879
- */
1880
783
  declare const CHAT_RESOLVER_CALLER: FunctionCaller;
1881
784
  declare const CHAT_PLANNER_CALLER: FunctionCaller;
1882
785
  declare function composeLocalAgent(input: ComposeLocalAgentInput): LocalAgentHost;
1883
786
 
787
+ declare const MARKS_LEDGER_FILE = "marks.jsonl";
788
+ declare const MARKS_LEDGER_LOCK_FILE = "marks.jsonl.lock";
789
+ /** A run's one unfinished mark, beside its ledger. */
790
+ declare const MARK_INTENT_FILE = "mark-intent.json";
791
+ declare const intentSchema: z.ZodObject<{
792
+ schemaVersion: z.ZodLiteral<2>;
793
+ mark: z.ZodObject<{
794
+ runSlug: z.ZodString;
795
+ kind: z.ZodEnum<{
796
+ "false-fail": "false-fail";
797
+ "false-pass": "false-pass";
798
+ "confirmed-bug": "confirmed-bug";
799
+ }>;
800
+ origin: z.ZodLiteral<"user">;
801
+ ordinal: z.ZodNumber;
802
+ markedAt: z.ZodNumber;
803
+ note: z.ZodOptional<z.ZodString>;
804
+ rootCause: z.ZodOptional<z.ZodObject<{
805
+ class: z.ZodEnum<{
806
+ unknown: "unknown";
807
+ environment: "environment";
808
+ timeout: "timeout";
809
+ test_bug: "test_bug";
810
+ app_bug: "app_bug";
811
+ flake: "flake";
812
+ config_error: "config_error";
813
+ llm_error: "llm_error";
814
+ }>;
815
+ producer: z.ZodString;
816
+ }, z.core.$strict>>;
817
+ isRerun: z.ZodOptional<z.ZodLiteral<true>>;
818
+ disposition: z.ZodDiscriminatedUnion<[z.ZodObject<{
819
+ kind: z.ZodLiteral<"quarantined">;
820
+ class: z.ZodEnum<{
821
+ environment: "environment";
822
+ timeout: "timeout";
823
+ flake: "flake";
824
+ }>;
825
+ }, z.core.$strict>, z.ZodObject<{
826
+ kind: z.ZodLiteral<"applied">;
827
+ fix: z.ZodEnum<{
828
+ fact: "fact";
829
+ caution: "caution";
830
+ "procedure-hint": "procedure-hint";
831
+ "test-edit": "test-edit";
832
+ }>;
833
+ change: z.ZodDiscriminatedUnion<[z.ZodObject<{
834
+ kind: z.ZodLiteral<"knowledge">;
835
+ changeId: z.ZodString;
836
+ recordIds: z.ZodArray<z.ZodString>;
837
+ }, z.core.$strict>, z.ZodObject<{
838
+ kind: z.ZodLiteral<"test-file">;
839
+ filePath: z.ZodString;
840
+ contentDigest: z.ZodString;
841
+ }, z.core.$strict>], "kind">;
842
+ }, z.core.$strict>, z.ZodObject<{
843
+ kind: z.ZodLiteral<"no-draftable-fix">;
844
+ checked: z.ZodObject<{
845
+ servedRecords: z.ZodNumber;
846
+ procedureReads: z.ZodNumber;
847
+ assertionDrift: z.ZodBoolean;
848
+ rootCause: z.ZodOptional<z.ZodEnum<{
849
+ unknown: "unknown";
850
+ environment: "environment";
851
+ timeout: "timeout";
852
+ test_bug: "test_bug";
853
+ app_bug: "app_bug";
854
+ flake: "flake";
855
+ config_error: "config_error";
856
+ llm_error: "llm_error";
857
+ }>>;
858
+ }, z.core.$strict>;
859
+ }, z.core.$strict>, z.ZodObject<{
860
+ kind: z.ZodLiteral<"confirmed-bug">;
861
+ confirmedRecords: z.ZodNumber;
862
+ }, z.core.$strict>, z.ZodObject<{
863
+ kind: z.ZodLiteral<"not-applied">;
864
+ fix: z.ZodEnum<{
865
+ fact: "fact";
866
+ caution: "caution";
867
+ "procedure-hint": "procedure-hint";
868
+ "test-edit": "test-edit";
869
+ }>;
870
+ reason: z.ZodEnum<{
871
+ "policy-materialize-refused": "policy-materialize-refused";
872
+ "target-not-found": "target-not-found";
873
+ "outside-workspace": "outside-workspace";
874
+ }>;
875
+ }, z.core.$strict>, z.ZodObject<{
876
+ kind: z.ZodLiteral<"drafted">;
877
+ fix: z.ZodEnum<{
878
+ fact: "fact";
879
+ caution: "caution";
880
+ "procedure-hint": "procedure-hint";
881
+ "test-edit": "test-edit";
882
+ }>;
883
+ proposalId: z.ZodString;
884
+ proposalKind: z.ZodEnum<{
885
+ knowledge: "knowledge";
886
+ "test-draft": "test-draft";
887
+ }>;
888
+ }, z.core.$strict>], "kind">;
889
+ evidence: z.ZodObject<{
890
+ confirmed: z.ZodNumber;
891
+ overturned: z.ZodNumber;
892
+ }, z.core.$strict>;
893
+ supersedes: z.ZodOptional<z.ZodString>;
894
+ runId: z.ZodType<_vostride_agent_qa_schema.RunId, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.RunId, unknown>>;
895
+ testIndex: z.ZodNumber;
896
+ testId: z.ZodOptional<z.ZodString>;
897
+ stepIndex: z.ZodOptional<z.ZodNumber>;
898
+ schemaVersion: z.ZodLiteral<2>;
899
+ markId: z.ZodString;
900
+ }, z.core.$strict>;
901
+ stage: z.ZodEnum<{
902
+ applied: "applied";
903
+ evidence: "evidence";
904
+ prepared: "prepared";
905
+ }>;
906
+ operationId: z.ZodUUID;
907
+ plan: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
908
+ fix: z.ZodEnum<{
909
+ fact: "fact";
910
+ caution: "caution";
911
+ "procedure-hint": "procedure-hint";
912
+ }>;
913
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
914
+ grain: z.ZodLiteral<"record">;
915
+ recordId: z.ZodString;
916
+ }, z.core.$strict>, z.ZodObject<{
917
+ grain: z.ZodLiteral<"bundle">;
918
+ shortId: z.ZodString;
919
+ }, z.core.$strict>], "grain">;
920
+ deltas: z.ZodArray<z.ZodType<_vostride_agent_qa_schema.Delta, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.Delta, unknown>>>;
921
+ wording: z.ZodEnum<{
922
+ model: "model";
923
+ floor: "floor";
924
+ }>;
925
+ schemaVersion: z.ZodLiteral<2>;
926
+ markId: z.ZodString;
927
+ feedbackSource: z.ZodObject<{
928
+ runId: z.ZodType<_vostride_agent_qa_schema.RunId, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.RunId, unknown>>;
929
+ testIndex: z.ZodNumber;
930
+ testId: z.ZodOptional<z.ZodString>;
931
+ stepIndex: z.ZodOptional<z.ZodNumber>;
932
+ kind: z.ZodEnum<{
933
+ "false-fail": "false-fail";
934
+ "false-pass": "false-pass";
935
+ "confirmed-bug": "confirmed-bug";
936
+ }>;
937
+ }, z.core.$strict>;
938
+ runTraceRef: z.ZodObject<{
939
+ runId: z.ZodType<_vostride_agent_qa_schema.RunId, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.RunId, unknown>>;
940
+ runSlug: z.ZodString;
941
+ }, z.core.$strict>;
942
+ kind: z.ZodLiteral<"knowledge">;
943
+ }, z.core.$strict>, z.ZodObject<{
944
+ fix: z.ZodLiteral<"test-edit">;
945
+ testRef: z.ZodString;
946
+ intent: z.ZodString;
947
+ wording: z.ZodEnum<{
948
+ model: "model";
949
+ floor: "floor";
950
+ }>;
951
+ schemaVersion: z.ZodLiteral<2>;
952
+ markId: z.ZodString;
953
+ feedbackSource: z.ZodObject<{
954
+ runId: z.ZodType<_vostride_agent_qa_schema.RunId, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.RunId, unknown>>;
955
+ testIndex: z.ZodNumber;
956
+ testId: z.ZodOptional<z.ZodString>;
957
+ stepIndex: z.ZodOptional<z.ZodNumber>;
958
+ kind: z.ZodEnum<{
959
+ "false-fail": "false-fail";
960
+ "false-pass": "false-pass";
961
+ "confirmed-bug": "confirmed-bug";
962
+ }>;
963
+ }, z.core.$strict>;
964
+ runTraceRef: z.ZodObject<{
965
+ runId: z.ZodType<_vostride_agent_qa_schema.RunId, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.RunId, unknown>>;
966
+ runSlug: z.ZodString;
967
+ }, z.core.$strict>;
968
+ kind: z.ZodLiteral<"test-edit">;
969
+ }, z.core.$strict>, z.ZodObject<{
970
+ checked: z.ZodObject<{
971
+ servedRecords: z.ZodNumber;
972
+ procedureReads: z.ZodNumber;
973
+ assertionDrift: z.ZodBoolean;
974
+ rootCause: z.ZodOptional<z.ZodEnum<{
975
+ unknown: "unknown";
976
+ environment: "environment";
977
+ timeout: "timeout";
978
+ test_bug: "test_bug";
979
+ app_bug: "app_bug";
980
+ flake: "flake";
981
+ config_error: "config_error";
982
+ llm_error: "llm_error";
983
+ }>>;
984
+ }, z.core.$strict>;
985
+ schemaVersion: z.ZodLiteral<2>;
986
+ markId: z.ZodString;
987
+ feedbackSource: z.ZodObject<{
988
+ runId: z.ZodType<_vostride_agent_qa_schema.RunId, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.RunId, unknown>>;
989
+ testIndex: z.ZodNumber;
990
+ testId: z.ZodOptional<z.ZodString>;
991
+ stepIndex: z.ZodOptional<z.ZodNumber>;
992
+ kind: z.ZodEnum<{
993
+ "false-fail": "false-fail";
994
+ "false-pass": "false-pass";
995
+ "confirmed-bug": "confirmed-bug";
996
+ }>;
997
+ }, z.core.$strict>;
998
+ runTraceRef: z.ZodObject<{
999
+ runId: z.ZodType<_vostride_agent_qa_schema.RunId, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.RunId, unknown>>;
1000
+ runSlug: z.ZodString;
1001
+ }, z.core.$strict>;
1002
+ kind: z.ZodLiteral<"no-draftable-fix">;
1003
+ }, z.core.$strict>], "kind">>;
1004
+ }, z.core.$strict>;
1005
+ type MarkIntent = z.infer<typeof intentSchema>;
1006
+ declare class MarkLedgerBusyError extends Error {
1007
+ constructor();
1008
+ }
1009
+ declare class MarkLedgerCorruptError extends Error {
1010
+ readonly line: number;
1011
+ constructor(line: number);
1012
+ }
1013
+ /**
1014
+ * The run's unfinished-mark record cannot be resumed, refused by the record's own name. Nothing is
1015
+ * migrated or read further; the message names the file to remove, after which the run can be
1016
+ * marked again.
1017
+ */
1018
+ declare class MarkIntentUnusableError extends Error {
1019
+ readonly intentPath: string;
1020
+ readonly reason: 'earlier-version' | 'unreadable' | 'mismatched';
1021
+ constructor(intentPath: string, reason: 'earlier-version' | 'unreadable' | 'mismatched');
1022
+ }
1023
+ /** The one latest-per-address rule, over receipts a caller has already read once. */
1024
+ declare function latestMarkReceiptsPerAddress(receipts: readonly MarkReceipt[]): MarkReceipt[];
1025
+ declare function latestMarkReceiptFor(receipts: readonly MarkReceipt[], runId: string, address: MarkAddress): MarkReceipt | undefined;
1026
+ declare class MarkLedgerNoRunError extends Error {
1027
+ constructor();
1028
+ }
1029
+ /** The per-run marks ledger under the run directory. */
1030
+ declare class FsMarkLedger {
1031
+ private readonly dir;
1032
+ private readonly file;
1033
+ private readonly lock;
1034
+ private readonly runId;
1035
+ private readonly runSlug;
1036
+ private inTransaction;
1037
+ private intentValue;
1038
+ constructor(config: {
1039
+ root: string;
1040
+ runId: string;
1041
+ });
1042
+ read(): MarkReceipt[];
1043
+ latestPerAddress(): MarkReceipt[];
1044
+ latestFor(address: MarkAddress): MarkReceipt | undefined;
1045
+ /** Private resumable state is admitted only while this run's existing lock is held. */
1046
+ prepareIntent(mark: MarkReceipt, operationId: string): MarkIntent;
1047
+ checkpointIntent(value: MarkIntent): MarkIntent;
1048
+ transact(fn: (nextOrdinal: number) => Promise<Omit<MarkReceipt, 'ordinal'> | undefined>): Promise<MarkReceipt | undefined>;
1049
+ append(record: Omit<MarkReceipt, 'ordinal'>): Promise<MarkReceipt | undefined>;
1050
+ }
1051
+
1884
1052
  /**
1885
1053
  * How many jobs one `resumePending` call will run before it refuses to continue.
1886
1054
  *
@@ -1893,7 +1061,7 @@ declare const JOB_RECOVERY_MAX_RESUMED_JOBS = 64;
1893
1061
  interface ComposeJobRecoveryInput {
1894
1062
  /** The project's runtime root, i.e. `<configDir>/.agent-qa`. Absolute. */
1895
1063
  readonly agentQaRoot: string;
1896
- /** Every column and dump row this host writes is stamped from here (D-03). */
1064
+ /** Every column and dump row this host writes is stamped from here. */
1897
1065
  readonly clock: ClockPort;
1898
1066
  /** Ids derived from `(kind, idempotencyKey)`, so a resumed chain addresses the same rows. */
1899
1067
  readonly idSource: JobIdSource;
@@ -1926,35 +1094,18 @@ interface JobRecoveryHost {
1926
1094
  }
1927
1095
  declare function composeJobRecovery(input: ComposeJobRecoveryInput): JobRecoveryHost;
1928
1096
 
1929
- /** Directory segment under the agent-qa runtime root. Gitignored, per D-17. */
1930
1097
  declare const CHAT_SESSIONS_SUBDIR = "sessions";
1931
- /** A caller-supplied session id that could not be a path segment (T-392-01). */
1098
+ /** A caller-supplied session id that could not be a path segment. */
1932
1099
  declare class InvalidChatSessionIdError extends Error {
1933
1100
  readonly sessionId: string;
1934
1101
  readonly name = "InvalidChatSessionIdError";
1935
1102
  constructor(sessionId: string);
1936
1103
  }
1937
- /**
1938
- * A session id that names no session.
1939
- *
1940
- * A REFUSAL and never a create (D-17, RESEARCH Open Q4). Creating a fresh session for a mistyped
1941
- * `--session` would tell a person their history was lost when in fact it was never looked at, and
1942
- * would do so in the one direction no later read can undo.
1943
- */
1944
1104
  declare class UnknownChatSessionError extends Error {
1945
1105
  readonly sessionId: string;
1946
1106
  readonly name = "UnknownChatSessionError";
1947
1107
  constructor(sessionId: string);
1948
1108
  }
1949
- /**
1950
- * A session somebody is already holding open (Phase 392 plan 12, T-392-BUSY).
1951
- *
1952
- * A REFUSAL BY NAME rather than a second descriptor. Two handles on one file would interleave two
1953
- * turns inside a single conversation — the same damage the cross-session aliasing did, one level
1954
- * down — and it is exactly what a double-submit of one `sessionId` over the daemon socket asks for.
1955
- * The remedy is to wait for the holder, which is why the id is on the error rather than only in
1956
- * the message.
1957
- */
1958
1109
  declare class ChatSessionBusyError extends Error {
1959
1110
  readonly sessionId: string;
1960
1111
  readonly name = "ChatSessionBusyError";
@@ -1987,20 +1138,6 @@ interface CreateJsonlSessionLogOptions {
1987
1138
  readonly model: () => string;
1988
1139
  /** The toolset fingerprint at open, on the same thunk reasoning as `model` above. */
1989
1140
  readonly toolsetHash: () => string;
1990
- /**
1991
- * WHICH DOOR THIS LOG BELONGS TO (Phase 400 plan 03, D-04). Absent ⇒ no tag is written.
1992
- *
1993
- * A VALUE AND NOT A THUNK, which is the opposite of `model` and `toolsetHash` above and for the
1994
- * mirror of their reason. Those are resolved lazily because a composition's model can arrive
1995
- * after the log does and can drift afterwards; a surface cannot. Which door composed this host is
1996
- * decided at the composition root and is a constant for the life of the process, so a thunk would
1997
- * be a seam for a value to change through — and a header is written once and never rewritten,
1998
- * so a changing surface has nowhere to go.
1999
- *
2000
- * ABSENT IS THE PRE-400 CLI-CHAT POSTURE, stated in `ChatSessionHeader.surface` and preserved
2001
- * here: a host that names no surface writes a header byte-identical to the ones every build
2002
- * before this one wrote.
2003
- */
2004
1141
  readonly surface?: string;
2005
1142
  /** Random source for the id suffix, injectable so a test can pin an id. */
2006
1143
  readonly random?: () => number;
@@ -2011,15 +1148,6 @@ interface ChatSessionListEntry {
2011
1148
  readonly createdAt: string;
2012
1149
  /** The model in force when the session OPENED. Drift after that is `model_change` events. */
2013
1150
  readonly model: string;
2014
- /**
2015
- * Event lines on the file, counted without parsing one.
2016
- *
2017
- * An EVENT count and not a turn count, and the difference is not a shortcut. A turn count is not
2018
- * a header fact and cannot become one — the header is written once and never rewritten, which is
2019
- * the property CHAT-05 asks for by name — so the only way to produce one is to parse every line
2020
- * of every log, which is the work a listing exists to avoid. The line count answers "is there
2021
- * anything in here" for a fraction of the cost, and `open()` answers everything else.
2022
- */
2023
1151
  readonly eventCount: number;
2024
1152
  }
2025
1153
  /** One session file the listing could not read, surfaced as data rather than thrown over. */
@@ -2041,18 +1169,6 @@ interface ChatSessionListing {
2041
1169
  * guard that protects a supplied one.
2042
1170
  */
2043
1171
  declare function mintChatSessionId(nowIso: string, random: () => number): string;
2044
- /**
2045
- * Every session under one runtime root, read from headers alone (D-17).
2046
- *
2047
- * A DAMAGED FILE IS DATA, NOT AN EXCEPTION — the posture the grant ledger had (391; deleted in 432-18).
2048
- * A corrupt neighbour costs the caller that one entry and nothing else; throwing would let a single
2049
- * unreadable file hide every healthy session from the person trying to find their conversation.
2050
- *
2051
- * It reads through the real filesystem with no injection seam, and that absence is on purpose: the
2052
- * seam beside `createJsonlSessionLogOn` exists because syscall ORDER is a durability property no
2053
- * crash test can observe. A listing has no such property. What it returns is its whole contract,
2054
- * and a temp directory proves that directly.
2055
- */
2056
1172
  declare function listSessions(root: string): ChatSessionListing;
2057
1173
  declare function createJsonlSessionLog(options: CreateJsonlSessionLogOptions): SessionLogPort;
2058
1174
 
@@ -2068,20 +1184,6 @@ interface MemorySessionLog extends SessionLogPort {
2068
1184
  }
2069
1185
  declare function createMemorySessionLog(options: CreateMemorySessionLogOptions): MemorySessionLog;
2070
1186
 
2071
- /**
2072
- * The local implementation of the neutral persistence port.
2073
- *
2074
- * 2026-07-29 (Phase 385 plan 02, D-06): this alias now covers approvals and patches ONLY.
2075
- * Job rows and checkpoints left the port for the agent job queue's own store, whose verbs are
2076
- * compound and transactional rather than generic CRUD, and the conversation surface left it
2077
- * entirely. The alias is deliberately narrower than the package's name suggests, and saying
2078
- * so here is cheaper than letting a later reader infer that anything job-shaped belongs
2079
- * behind it.
2080
- *
2081
- * The job store itself is not on this barrel. It is reachable by relative path within the
2082
- * package and is published, if at all, by the composition root — a store that any importer
2083
- * can construct is a second way to open the database beside the one the host owns.
2084
- */
2085
1187
  type LocalPersistenceStore = PersistencePort;
2086
1188
 
2087
- export { type ApplyKnowledgeChangeInput, type AttachableRunSession, BRIEF_CEILING, BRIEF_FRACTION, type BuildMemberRunConfigInput, CAUTION_FRACTION, CHAT_PLANNER_CALLER, CHAT_RESOLVER_CALLER, CHAT_SESSIONS_SUBDIR, type CandidateProsePosture, ChatSessionBusyError, ChatSessionHeaderError, type ChatSessionListEntry, type ChatSessionListing, type ComposeJobRecoveryInput, type ComposeLocalAgentInput, type ComputeKnowledgeBudgetsInput, type ConstructKnowledgeReaderInput, type CreateEvidenceBlobReaderInput, type CreateJsonlSessionLogOptions, type CreateKnowledgeReviewInput, type CreateMemorySessionLogOptions, DIGEST_JOB_ID_SOURCE, type DraftAuthoringPort, type DraftParseOutcome, type DraftStageOutcome, GLOBAL_RECORD_CAP, InvalidChatSessionIdError, JOB_RECOVERY_MAX_RESUMED_JOBS, type JobRecoveryHost, type KnowledgeBudgets, LOCAL_CURATION_CAPABILITIES, LOCAL_DAEMON_CAPABILITIES, LOCAL_KNOWLEDGE_PROBES, LocalAgentClosedError, type LocalAgentHost, type LocalAgentJobs, type LocalCurationCapabilities, type LocalDaemonCapabilities, type LocalKnowledgeMaintenanceCapabilities, type LocalKnowledgeProbeCapabilities, type LocalPersistenceStore, MIN_BRIEF, MIN_CAUTION, MIN_MEMORY_TOOL_CONTEXT_WINDOW, type MemorySessionLog, type OpenRunKnowledgeCompositionInput, type ParsedDraftDescriptor, type PromoteWithReceiptsInput, READ_BODY_FRACTION, REFUSED_RUN_REQUEST_FIELDS, type RecordGateReceiptInput, type RefusedChatSessionFile, type RetrievalRungResolver, type RunAssembler, type RunAssemblyContext, type RunConfigDigests, RunDeniedError, type RunDigestMemberInput, type RunExecutionContext, type RunExecutionPort, RunIdAlreadyInUseError, RunIdNotMintedError, type RunInvocationCoreOptions, type RunKnowledgeComposition, type RunPersistencePorts, RunProducedNoEventsError, type RunQuarantineSnapshotInput, RunRequestFieldRefusedError, type RunSubscriberHandle, type RunTargetClass, type RunTargetDeclaration, SUITE_LIFECYCLE_LANE, SYSTEM_CLOCK, SuiteBoundaryWhileLiveError, type SuiteLifecycle, type SuiteLifecycleHooks, type SuiteMemberBinding, type SuiteMemberLease, type SuiteTextEntry, type SweepEntityIndexInput, type SweepEntityIndexOutcome, UNCLAIMABLE_BY_PROSE_POSTURE, UnknownChatSessionError, WEAK_MODEL_CAP_SCALE, applyKnowledgeChange, buildMemberRunConfig, buildRunPorts, buildScreenshotCompressor, buildSuiteMemberExecutor, composeJobRecovery, composeLocalAgent, computeKnowledgeBudgets, computeRunConfigDigests, constructKnowledgeReader, createEvidenceBlobReader, createFrozenKnowledgeReader, createJsonlSessionLog, createKnowledgeReader, createKnowledgeReview, createLocalRunInvocation, createMemorySessionLog, createRunInvocationCore, createRunsReader, createSuiteLifecycle, listSessions, lookupEvidenceBlob, memoryToolGate, mintChatSessionId, openRunKnowledgeComposition, readEvidenceBlobBytes, refuseProductionWithoutOptIn, refuseUnavailableRunId, registerAdapterLiveStream, resolveBuildTagSetting, resolveHostRetrievalRung, resolveRunBuildOrdinal, resolveRunExecutionContext, resolveRunQuarantineSnapshot, scaledRecordCap, statEvidenceBlob, sweepEntityIndexProjection, toMemberTest };
1189
+ export { type ApplyKnowledgeChangeInput, type AttachableRunSession, BRIEF_CEILING, BRIEF_FRACTION, type BuildMemberRunConfigInput, CAUTION_FRACTION, CHAT_PLANNER_CALLER, CHAT_RESOLVER_CALLER, CHAT_SESSIONS_SUBDIR, type CandidateProsePosture, ChatSessionBusyError, ChatSessionHeaderError, type ChatSessionListEntry, type ChatSessionListing, type ComposeJobRecoveryInput, type ComposeLocalAgentInput, type ComputeKnowledgeBudgetsInput, type ConstructKnowledgeReaderInput, type CreateEvidenceBlobReaderInput, type CreateJsonlSessionLogOptions, type CreateKnowledgeReviewInput, type CreateMemorySessionLogOptions, DIGEST_JOB_ID_SOURCE, type DraftAuthoringPort, type DraftParseOutcome, type DraftStageOutcome, FsMarkLedger, GLOBAL_RECORD_CAP, InvalidChatSessionIdError, JOB_RECOVERY_MAX_RESUMED_JOBS, type JobRecoveryHost, type KnowledgeBudgets, LOCAL_CURATION_CAPABILITIES, LOCAL_DAEMON_CAPABILITIES, LOCAL_KNOWLEDGE_PROBES, LocalAgentClosedError, type LocalAgentHost, type LocalAgentJobs, type LocalCurationCapabilities, type LocalDaemonCapabilities, type LocalKnowledgeMaintenanceCapabilities, type LocalKnowledgeProbeCapabilities, type LocalPersistenceStore, MARKS_LEDGER_FILE, MARKS_LEDGER_LOCK_FILE, MARK_INTENT_FILE, MIN_BRIEF, MIN_CAUTION, MIN_MEMORY_TOOL_CONTEXT_WINDOW, MarkIntentUnusableError, MarkLedgerBusyError, MarkLedgerCorruptError, MarkLedgerNoRunError, type MemorySessionLog, type OpenRunKnowledgeCompositionInput, type ParsedDraftDescriptor, type PromoteWithReceiptsInput, READ_BODY_FRACTION, type RecordGateReceiptInput, type RefusedChatSessionFile, type RetrievalRungResolver, type RunAssembler, type RunAssemblyContext, type RunConfigDigests, type RunDigestMemberInput, type RunExecutionContext, type RunExecutionPort, RunIdAlreadyInUseError, RunIdNotMintedError, type RunInvocationCoreOptions, type RunKnowledgeComposition, type RunPersistencePorts, RunProducedNoEventsError, type RunQuarantineSnapshotInput, type RunSubscriberHandle, SUITE_LIFECYCLE_LANE, SYSTEM_CLOCK, SuiteBoundaryWhileLiveError, type SuiteLifecycle, type SuiteLifecycleHooks, type SuiteMemberBinding, type SuiteMemberLease, type SuiteTextEntry, type SweepEntityIndexInput, type SweepEntityIndexOutcome, UNCLAIMABLE_BY_PROSE_POSTURE, UnknownChatSessionError, WEAK_MODEL_CAP_SCALE, applyKnowledgeChange, buildMemberRunConfig, buildRunPorts, buildScreenshotCompressor, buildSuiteMemberExecutor, composeJobRecovery, composeLocalAgent, computeKnowledgeBudgets, computeRunConfigDigests, constructKnowledgeReader, createEvidenceBlobReader, createFrozenKnowledgeReader, createJsonlSessionLog, createKnowledgeReader, createKnowledgeReview, createLocalRunInvocation, createMemorySessionLog, createRunInvocationCore, createRunsReader, createSuiteLifecycle, latestMarkReceiptFor, latestMarkReceiptsPerAddress, listSessions, lookupEvidenceBlob, memoryToolGate, mintChatSessionId, openRunKnowledgeComposition, readEvidenceBlobBytes, registerAdapterLiveStream, resolveBuildTagSetting, resolveHostRetrievalRung, resolveRunBuildOrdinal, resolveRunExecutionContext, resolveRunQuarantineSnapshot, scaledRecordCap, statEvidenceBlob, sweepEntityIndexProjection, toMemberTest };