@vostride/agent-qa-knowledge-local 0.0.1 → 0.2.2

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.
@@ -0,0 +1,3983 @@
1
+ import { KnowledgePort } from '@vostride/agent-qa-agent';
2
+ import { KnowledgeHintReader } from '@vostride/agent-qa-runner';
3
+ import * as _vostride_agent_qa_schema from '@vostride/agent-qa-schema';
4
+ import { CurationSnapshotChange, CurationSnapshot, EntityKind, EmbeddingProfile, EntityDescriptor, EntityResolution, VerdictClass, QuarantineOperationsPort, ArtifactRef, MaskFn, CurationAuthorship, BundleDraft, BundleDiscriminant, OriginClass, ScreenState, BundleOutcomeEvent, BundleRelation, DistilledObservation, RecordId, BundleIdentityCore, GateReceipt, MeasurementObservation, PromotionReceipt, MeasurementBaseline, RetrievalTraceLeg, RecordedRetrievalRung, Anchor, IndexEntry, TrustPriorTable, CompletedRunHandoff, PerTestOutcome, StepEvidence, QuarantineLedgerEvent, RunAnalysisArtifact, Verdict, ScopeKey, ExecutionContext, BundleKind, FactSubtype, Delta, CurationFallbackReason, CompareAndSet, BundleStatus, ResolvePromotionPosture, PromotionGateHoldReason, PromotionPath, KnowledgeAcceptInput, KnowledgeRejectInput, KnowledgeForgetInput, KnowledgeMarkInput, KnowledgeProjection, KnowledgeReviewVerb, CommitReceipt, ForgetReceiptV1, KnowledgeTargetRef, DraftReceipt, MarkReceipt, OriginStage, NoDraftableFixChecked, MarkQuarantineClass, ImprovementPlan, Mark } from '@vostride/agent-qa-schema';
5
+ export { testFingerprintElements } from '@vostride/agent-qa-schema';
6
+ import { z } from 'zod';
7
+ import { I as IndexReaderConnection, E as EmbedderPort, a as IndexFace, K as KnowledgeReader, V as VecCapability, Q as QuarantineAppendOutcome, b as QuarantineDetectionSighting, R as RefusedQuarantineLine, r as readRebuildRequest, c as readIndexMaintenanceState, d as RebuildRequest, o as openKnowledgeReader, L as LoadedBundle, A as AppendMarkEvidenceOutcome } from './mark-evidence-plan-BCkYN07i.cjs';
8
+ export { e as AUTHORING_BRIEF_DEFAULT_BUDGET, f as AUTHORING_DENSE_K, g as AUTHORING_EXCERPT_CHAR_BUDGET, h as AUTHORING_EXEMPLAR_CAP, i as AUTHORING_PROCEDURE_CAP, j as AUTO_QUARANTINE_CLASSES, k as AssembleAuthoringBriefInput, l as AssembledAuthoringBrief, m as AuthoringEntryCandidate, n as AuthoringExemplarRow, p as AuthoringReaderDependencies, q as AuthoringTestRef, C as CHECKPOINT_EVERY_N_BUNDLES, s as CanaryPostureFacts, t as CanonicalBundleAnchorLookup, D as DenseHit, u as DenseSearchOptions, v as DiffSinceOptions, w as EnumeratedBundleDir, x as EvaluateQuarantineDetectionInput, G as GATES_REL_PATH, y as INDEX_DB_FILE, z as INDEX_RENDERING_SCHEMA_VERSION, B as INDEX_SCHEMA_VERSION, F as IndexHealth, H as IndexWriterHandle, J as InspectRecordsOptions, M as IntegrityReport, N as KnowledgeHit, O as KnowledgeMaintenanceScanError, P as KnowledgeMaintenanceScanIssue, S as KnowledgeRecordNotFoundError, T as KnowledgeSearchOptions, U as LoadBundleOutcome, W as LowerTrustShare, X as MARK_EVIDENCE_TOKEN_PREFIX, Y as MarkEvidenceEffect, Z as MarkEvidencePlan, _ as MarkEvidenceTarget, $ as Measurements, a0 as OpenKnowledgeReaderOptions, a1 as OpenServingKnowledgeReaderConfig, a2 as PlanMarkEvidenceInput, a3 as QUARANTINE_AUTO_EXIT_PASSES, a4 as QUARANTINE_CLASS_DISPOSITIONS, a5 as QUARANTINE_DEFAULT_EXPIRY_DAYS, a6 as QUARANTINE_ESCALATE_THRESHOLD, a7 as QUARANTINE_FLAG_FLOOR, a8 as QUARANTINE_LEDGER_FILENAME, a9 as QuarantineDetectionFailure, aa as QuarantineDetectionResult, ab as QuarantineDetectionTestWindow, ac as QuarantineDisposition, ad as QuarantineLedgerRead, ae as QuarantineOrdinalOutcome, af as READER_BUSY_TIMEOUT_MS, ag as REBUILD_MARKER_FILE, ah as REQUIRED_INDEX_TABLES, ai as ReadRebuildRequestOutcome, aj as RebuildReason, ak as RequestRebuildOptions, al as ScanBundleByIdOptions, am as ScanBundlesOptions, an as ShowRecordOptions, ao as StandingReceiptDigest, ap as WRITER_BUSY_TIMEOUT_MS, aq as assembleAuthoringBrief, ar as canaryPostureFactsFor, as as classifyIndexFace, at as clearRebuildRequest, au as createAuthoringReader, av as deriveBundleTrust, aw as diffSince, ax as embeddingProfileKey, ay as enumerateBundleDirs, az as evaluateQuarantineDetection, aA as foldQuarantineLedgerState, aB as indexDbPath, aC as inspectRecords, aD as isServable, aE as loadBundle, aF as lowerTrustSharesFor, aG as markEvidenceEvent, aH as openIndexReaderConnection, aI as openIndexWriter, aJ as openKnowledgeReader, aK as planMarkEvidence, aL as probeIndexHealth, aM as projectMarkEvidenceOutcome, aN as quarantineLedgerPath, aO as readCanonicalBundleAnchors, aP as readGateReceipts, aQ as readMeasurements, aR as readQuarantineLedger, aS as requestRebuild, aT as resolveActiveProfile, aU as resolveVecCapability, aV as scanBundleById, aW as scanBundles, aX as showRecord, aY as standingReceiptDigestsFor, aZ as toInspectRecordView, a_ as vecTableName, a$ as verifyBundleIntegrity } from './mark-evidence-plan-BCkYN07i.cjs';
9
+ import { DatabaseSync } from 'node:sqlite';
10
+ import { Tokenizer } from '@huggingface/tokenizers';
11
+ import { E as EmbedderWorkerRegistration, a as EmbedderWorkerIncarnationVerdict } from './worker-registration-C-9spEsf.cjs';
12
+ export { b as EMBEDDER_WORKER_REGISTRATION_FILE, c as EMBEDDER_WORKER_REGISTRATION_SCHEMA_VERSION, d as EMBEDDER_WORKER_REQUEST_TIMEOUT_MS, e as EMBEDDER_WORKER_START_TIMEOUT_MS, f as EMBEDDER_WORKER_TERM_GRACE_MS, g as EMBEDDER_WORKER_UNVERIFIABLE_INCARNATION, h as EmbedderWorkerTimeoutError, i as EmbedderWorkerTimeoutPhase, R as ReadEmbedderWorkerRegistrationOutcome, j as embedderWorkerProcessIsAlive, k as embedderWorkerSocketPath, p as probeEmbedderWorkerSocket, r as readEmbedderWorkerRegistration, l as removeEmbedderWorkerRegistration, w as writeEmbedderWorkerRegistration } from './worker-registration-C-9spEsf.cjs';
13
+
14
+ interface ReadCurationEffectsOptions {
15
+ knowledgeRoot: string;
16
+ sourceRunId: string;
17
+ observationIds?: readonly string[];
18
+ }
19
+ /** Read only successful, surviving effects; current status is never classification authority. */
20
+ declare function readCurationEffects(options: ReadCurationEffectsOptions): CurationSnapshotChange[];
21
+ /** One affected record, strongest classification, with all historical identities retained. */
22
+ declare function foldCurationSnapshot(effects: readonly CurationSnapshotChange[]): CurationSnapshot;
23
+
24
+ declare const PAYLOAD_BENEFITS: readonly ["exercised-benefited", "rode-along", "exercised-harmed"];
25
+ declare const PAYLOAD_STATE_VALIDITIES: readonly ["valid", "stale", "unknown"];
26
+ declare const canaryOutcomePayloadSchema: z.ZodObject<{
27
+ schemaVersion: z.ZodLiteral<2>;
28
+ benefit: z.ZodEnum<{
29
+ "exercised-benefited": "exercised-benefited";
30
+ "rode-along": "rode-along";
31
+ "exercised-harmed": "exercised-harmed";
32
+ }>;
33
+ stateValidity: z.ZodEnum<{
34
+ unknown: "unknown";
35
+ valid: "valid";
36
+ stale: "stale";
37
+ }>;
38
+ corpusDigest: z.ZodOptional<z.ZodString>;
39
+ observedStateDigest: z.ZodOptional<z.ZodString>;
40
+ ablation: z.ZodOptional<z.ZodObject<{
41
+ crossCheck: z.ZodEnum<{
42
+ inconclusive: "inconclusive";
43
+ flipped: "flipped";
44
+ held: "held";
45
+ "not-run": "not-run";
46
+ }>;
47
+ k: z.ZodNumber;
48
+ baseSeed: z.ZodNumber;
49
+ seeds: z.ZodArray<z.ZodNumber>;
50
+ seedHonored: z.ZodBoolean;
51
+ spend: z.ZodObject<{
52
+ modelCalls: z.ZodNumber;
53
+ screens: z.ZodNumber;
54
+ promptTokens: z.ZodNumber;
55
+ completionTokens: z.ZodNumber;
56
+ }, z.core.$strict>;
57
+ }, z.core.$strict>>;
58
+ jobKind: z.ZodLiteral<"canary-outcome">;
59
+ candidateId: z.ZodString;
60
+ runId: z.ZodString;
61
+ testId: z.ZodString;
62
+ testIndex: z.ZodNumber;
63
+ buildOrdinal: z.ZodNumber;
64
+ stepIndex: z.ZodNumber;
65
+ outcome: z.ZodEnum<{
66
+ positive: "positive";
67
+ negative: "negative";
68
+ }>;
69
+ exercisedAnchor: z.ZodType<_vostride_agent_qa_schema.Anchor, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.Anchor, unknown>>;
70
+ attribution: z.ZodString;
71
+ briefDigest: z.ZodString;
72
+ }, z.core.$strict>;
73
+ type CanaryOutcomePayload = z.infer<typeof canaryOutcomePayloadSchema>;
74
+ type QueuedCanaryPayloadRead = {
75
+ kind: 'current';
76
+ payload: CanaryOutcomePayload;
77
+ } | {
78
+ kind: 'refused';
79
+ issues: string[];
80
+ };
81
+ declare function readQueuedCanaryPayload(value: unknown): QueuedCanaryPayloadRead;
82
+
83
+ /**
84
+ * Stamped into `PRAGMA user_version` at creation and checked on every open.
85
+ *
86
+ * Stamp and hard-fail, never migrate — the 385 D-04 convention. The milestone ships nothing
87
+ * between its releases, so a ledger written by another build is a development artifact, and
88
+ * migration machinery for it would be code with no user that everything afterwards has to
89
+ * keep working. Note what this does NOT license: the ROWS are permanent regardless. A version
90
+ * refusal tells an operator to move a stale development file aside; it never rewrites one.
91
+ */
92
+ declare const ADDRESSING_LEDGER_USER_VERSION = 2;
93
+ /**
94
+ * Busy timeout for every connection.
95
+ *
96
+ * D-02's story is "any process mints, sqlite arbitrates", which only works if a writer
97
+ * waiting on the write lock retries rather than failing immediately. Matched to the job
98
+ * store's window because the transactions are the same shape and size: retune from observed
99
+ * contention, not from a guess about how many hosts there are.
100
+ */
101
+ declare const ADDRESSING_LEDGER_BUSY_TIMEOUT_MS = 2000;
102
+ /** The ledger's own file name under the agent-qa root, beside `agent-jobs.db` (D-02). */
103
+ declare const ADDRESSING_LEDGER_DB_FILE = "entity-addresses.db";
104
+ /** A ledger file this build cannot read. Refused before a single schema statement runs. */
105
+ declare class AddressingLedgerSchemaVersionError extends Error {
106
+ readonly path: string;
107
+ readonly expected: number;
108
+ readonly found: number;
109
+ constructor(path: string, expected: number, found: number);
110
+ }
111
+ /**
112
+ * An EXPLICITLY recorded target rename (D-03).
113
+ *
114
+ * There is no heuristic rename detection in this file and there must not be. Inferring that
115
+ * two target slugs are "the same target" from their spelling, their order of appearance or
116
+ * their contents is a guess, and ENTY-01's never-guess law is not weaker inside the ledger
117
+ * than it is inside the resolver — a wrong link here would silently re-point a published
118
+ * address, which is the exact failure D-02 exists to make impossible.
119
+ */
120
+ interface TargetRename {
121
+ readonly fromSlug: string;
122
+ readonly toSlug: string;
123
+ readonly recordedAt: string;
124
+ }
125
+ /** A minted address: the durable mapping plus the short-ID it renders as. */
126
+ interface EntityAddress {
127
+ readonly kind: EntityKind;
128
+ readonly machineId: string;
129
+ /** Present for ordinal-addressed kinds; absent for slug-addressed ones (D-03). */
130
+ readonly ordinal?: number;
131
+ readonly shortId: string;
132
+ readonly mintedAt: string;
133
+ }
134
+ interface AddressingLedger {
135
+ /**
136
+ * Assign this machine id its permanent address, or return the one it already has.
137
+ *
138
+ * Idempotent by construction rather than by a caller-side check: the unique index decides,
139
+ * so two processes calling this at the same instant agree, and a caller that forgot to look
140
+ * first cannot create a second address.
141
+ */
142
+ mint(kind: EntityKind, machineId: string): EntityAddress;
143
+ /** The address for a machine id, or `undefined` when it has never been sighted. */
144
+ lookup(kind: EntityKind, machineId: string): EntityAddress | undefined;
145
+ /** The address a published short-ID names, or `undefined`. Tier (a)'s whole job. */
146
+ lookupByShortId(shortId: string): EntityAddress | undefined;
147
+ /**
148
+ * Every address ever minted, in a fixed order (kind, then short-ID).
149
+ *
150
+ * READ-only, and the append-only law is what makes it worth having: this list only grows, so
151
+ * a caller enumerating it is reading a complete history rather than a snapshot that could
152
+ * have lost rows. The entity index is rebuilt FROM this (D-06) — a projection can only claim
153
+ * to heal if the thing it is rebuilt from can be enumerated in full.
154
+ */
155
+ listAddresses(): EntityAddress[];
156
+ /**
157
+ * Record that a config target was renamed (D-03). See {@link TargetRename} for why this is
158
+ * the only way a link between two slugs ever comes to exist.
159
+ *
160
+ * Both slugs are minted, so both keep their own permanent address: the predecessor keeps
161
+ * the one already sitting in somebody's CI log, and the successor gets its own rather than
162
+ * inheriting one. Recording is idempotent — a repeat is the same fact, not a second one.
163
+ */
164
+ recordTargetRename(fromSlug: string, toSlug: string): TargetRename;
165
+ /** The rename recorded FOR this slug, if any. Never the reverse direction. */
166
+ lookupTargetRename(fromSlug: string): TargetRename | undefined;
167
+ close(): void;
168
+ }
169
+ interface OpenAddressingLedgerOptions {
170
+ /** Absolute path to the ledger file, typically `<agentQaRoot>/entity-addresses.db`. */
171
+ readonly path: string;
172
+ /** ISO-8601 stamp source. Injected so mint times are deterministic under test. */
173
+ readonly now?: () => string;
174
+ readonly timeoutMs?: number;
175
+ }
176
+ declare function openAddressingLedger(options: OpenAddressingLedgerOptions): AddressingLedger;
177
+
178
+ /**
179
+ * The projection's own file name, beside the knowledge index rather than inside it.
180
+ *
181
+ * Beside, because an entity is not a learned fact (D-06). Its own file, because the knowledge
182
+ * index's schema version, rebuild and per-profile vec bookkeeping belong to records, and
183
+ * sharing them would have coupled two projections whose only similarity is their shape.
184
+ */
185
+ declare const ENTITY_INDEX_DB_FILE = "entity-index.db";
186
+ /**
187
+ * Stamped into `PRAGMA user_version` at creation and checked on every open.
188
+ *
189
+ * Stamp and hard-fail, never migrate — the 385 D-04 house style. Migration would be effort
190
+ * spent on a file whose contents can be regenerated in full from the ledger; a refusal that
191
+ * says which version it found is more useful than a migration nobody would maintain.
192
+ */
193
+ declare const ENTITY_INDEX_USER_VERSION = 2;
194
+ /**
195
+ * How often a standing daemon re-renders the projection from its sources (D-06).
196
+ *
197
+ * PROVISIONAL 600_000 (ten minutes); retune from operational evidence without adding config,
198
+ * the `LEASE_HEARTBEAT_TTL_MS` house style. What it is bounding is STALENESS of a title or a
199
+ * status in a fuzzy-match target — never correctness of an address, which the ledger owns and
200
+ * no cadence can affect. Sightings already refresh an entity the moment anything touches it,
201
+ * so this sweep exists for the entities nothing has touched since their source data moved.
202
+ *
203
+ * Deliberately its own interval rather than the daemon's `MAINTENANCE_CADENCE_MS`. That
204
+ * cadence runs the lease-holding maintenance tail; this sweep must never require the writer
205
+ * lease (D-02/D-06) because it writes only this file, and sharing a timer with lease-gated
206
+ * work would have made a lease-free projection stop being maintained the moment a lease was
207
+ * lost — a coupling nothing in the projection's own contract asks for.
208
+ */
209
+ declare const ENTITY_INDEX_SWEEP_INTERVAL_MS = 600000;
210
+ declare class EntityIndexSchemaVersionError extends Error {
211
+ readonly path: string;
212
+ readonly supportedVersion: number;
213
+ readonly foundVersion: number;
214
+ constructor(path: string, supportedVersion: number, foundVersion: number);
215
+ }
216
+ interface EntityIndexHandle {
217
+ /** The connection shape the retrieval legs take. `vec` is pinned `brute` — see the header. */
218
+ readonly connection: IndexReaderConnection;
219
+ readonly path: string;
220
+ close(): void;
221
+ }
222
+ interface OpenEntityIndexOptions {
223
+ /** Absolute path to the projection file, typically `entityIndexDbPath(knowledgeRoot)`. */
224
+ readonly path: string;
225
+ /**
226
+ * The knowledge root the path lives under. When given, the path's directory chain must stay
227
+ * inside it (261003-mqc): a linked `memory/` or `memory/index/` is refused before the open.
228
+ */
229
+ readonly knowledgeRoot?: string;
230
+ readonly timeoutMs?: number;
231
+ }
232
+ /** The projection's path: beside the knowledge index, never inside it. */
233
+ declare function entityIndexDbPath(knowledgeRoot: string): string;
234
+ declare function openEntityIndex(options: OpenEntityIndexOptions): EntityIndexHandle;
235
+ /**
236
+ * Project one entity into the index (D-06's incremental half).
237
+ *
238
+ * Called from the resolver's sighting/mint path, so every process posture — daemon, one-shot
239
+ * CLI, a test harness — maintains the projection by using it. Idempotent: re-projecting an
240
+ * unchanged entity leaves the same row.
241
+ */
242
+ declare function refreshEntity(handle: EntityIndexHandle, descriptor: EntityDescriptor): void;
243
+ /**
244
+ * Drop everything and re-project from the supplied descriptor set (D-06's healing half).
245
+ *
246
+ * The set comes from the ledger plus the owning stores, which is what makes this a rebuild
247
+ * rather than a repair: nothing in this file is consulted to produce the new contents, so a
248
+ * file damaged in any way at all converges to the same result as a file that never existed.
249
+ * Embeddings are re-derived rather than carried, because a rebuild that preserved derived
250
+ * bytes would preserve exactly the state a rebuild is run to be rid of.
251
+ */
252
+ declare function rebuildEntityIndex(handle: EntityIndexHandle, descriptors: Iterable<EntityDescriptor>): number;
253
+ interface EntityIndexSweepReport {
254
+ readonly examined: number;
255
+ readonly refreshed: number;
256
+ }
257
+ /**
258
+ * Re-project the entities whose source data moved (D-06's maintenance-cadence half).
259
+ *
260
+ * A sweep is maintenance of a disposable projection, so it writes only where a row disagrees
261
+ * with its source. It never touches the addressing ledger — the ledger is not a projection
262
+ * (D-02) — and it takes no writer lease, because everything it writes lives in this file.
263
+ */
264
+ declare function sweepEntityIndex(handle: EntityIndexHandle, descriptors: Iterable<EntityDescriptor>): EntityIndexSweepReport;
265
+ /**
266
+ * Embed every row whose rendering has no vector yet, returning how many were embedded.
267
+ *
268
+ * Separate from `refreshEntity` on purpose. Projecting an entity is synchronous and always
269
+ * possible; embedding one needs a model that a floor host may not have. Splitting them is what
270
+ * lets the degradation ladder work by omission — an index with no vectors simply serves its
271
+ * lexical leg, and the resolver rides that without a posture flag anywhere.
272
+ *
273
+ * A profile change invalidates every stored vector rather than mixing two geometries in one
274
+ * table, which is the same disposability law the records index applies to its own.
275
+ */
276
+ declare function embedPendingEntities(handle: EntityIndexHandle, embedder: EmbedderPort, profile?: EmbeddingProfile): Promise<number>;
277
+
278
+ /**
279
+ * One store's answer to "do you own an entity with exactly this machine id?".
280
+ *
281
+ * A port rather than a switch over kinds, because each kind's machine ids live behind a
282
+ * different store and the tracer wires only one of them. 387-03 adds sources for the
283
+ * remaining kinds and neither this function nor its callers change.
284
+ */
285
+ interface EntitySightingSource {
286
+ readonly kind: EntityKind;
287
+ /** The entity with this exact machine id, or `undefined`. Never a fuzzy match. */
288
+ sight(machineId: string): EntitySighting | undefined;
289
+ /** The entity a minted address maps back to, for rendering a ledger hit. */
290
+ hydrate(machineId: string): EntitySighting | undefined;
291
+ }
292
+ /** What a store reports about one of its entities. Facts only — no card, no address. */
293
+ interface EntitySighting {
294
+ readonly machineId: string;
295
+ readonly title: string;
296
+ /**
297
+ * The status as the OWNING store reports it. Validated against the kind's closed union at
298
+ * the card boundary (D-12), not here: this layer reports, the producer renders, and the
299
+ * schema refuses. Three responsibilities, three places.
300
+ */
301
+ readonly statusSource: string;
302
+ }
303
+ interface ResolveEntityRefDeps {
304
+ readonly ledger: AddressingLedger;
305
+ readonly sources: readonly EntitySightingSource[];
306
+ /**
307
+ * The D-06 projection, maintained by USE.
308
+ *
309
+ * Optional, and that is the degradation ladder rather than a convenience: a composition that
310
+ * supplies no index gets tier (a) and the honest `none` arm, which is exactly what 387-01
311
+ * shipped. Nothing here silently becomes a guess when the projection is absent.
312
+ */
313
+ readonly entityIndex?: EntityIndexHandle;
314
+ /** Test/integration seam for tier (b)'s dense leg; omission runs the acquisition ladder. */
315
+ readonly embedder?: EmbedderPort;
316
+ }
317
+ /**
318
+ * Every entity the ledger has ever addressed and a source can still describe.
319
+ *
320
+ * This is the set `rebuildEntityIndex` is rebuilt FROM and the set the daemon sweep re-renders
321
+ * (D-06). It reads the LEDGER, never the index: a projection rebuilt from itself would
322
+ * preserve exactly the state a rebuild is run to be rid of.
323
+ *
324
+ * An address whose source can no longer describe it is skipped rather than projected as a
325
+ * husk. The address keeps resolving through tier (a) either way — D-04's permanence lives in
326
+ * the ledger — but a row with no title is not something fuzzy text should ever match.
327
+ */
328
+ declare function describeAllEntities(deps: ResolveEntityRefDeps): EntityDescriptor[];
329
+ /**
330
+ * THE sighting leg (D-01): exact machine-id equality, mint, project, describe.
331
+ *
332
+ * Extracted so the resolver's own leg and the LIST VERBS' sighting site are one mechanism
333
+ * rather than two that mint the same way today. D-01 names four sighting sites — a resolver
334
+ * hit, a card render, verdict authoring, a list verb — and a second minting path written for
335
+ * the fourth would be a second place where "first sighting mints" could stop being true, with
336
+ * nothing comparing them.
337
+ *
338
+ * `undefined` means no registered source recognises the id at all.
339
+ *
340
+ * EXACT equality, always. A prefix or case-insensitive comparison here would be tier (b)
341
+ * smuggled in below the never-guess contract. Sources are consulted in registered order so
342
+ * the answer does not depend on iteration accidents.
343
+ */
344
+ declare function sightEntity(machineId: string, deps: ResolveEntityRefDeps): EntityResolution | undefined;
345
+ /**
346
+ * Resolve a reference through the three tiers, minting on first sighting (D-01).
347
+ *
348
+ * Order matters and is fixed: URI, then short-ID, then exact machine id, then — for free text
349
+ * only — the hybrid tiers. Addresses are tried before machine ids because an address is
350
+ * unambiguous by construction, and a machine id that happened to LOOK like a short-ID would
351
+ * otherwise be able to shadow a published address.
352
+ *
353
+ * ASYNC because tier (b)'s dense leg is: an embedding query cannot be made synchronous, and
354
+ * the alternative — a second, synchronous entry that quietly answers with tier (a) only —
355
+ * would be a resolver that under-answers depending on which function a caller happened to
356
+ * import. The PORT this reaches surfaces through was already asynchronous, so no composition
357
+ * edge changes shape.
358
+ */
359
+ declare function resolveEntityRef(ref: string, deps: ResolveEntityRefDeps): Promise<EntityResolution>;
360
+ /**
361
+ * `entities.get`'s read path: the descriptor for an EXACT address, or `undefined`.
362
+ *
363
+ * Deliberately narrower than `resolveEntityRef` — it neither mints nor consults the sighting
364
+ * leg. `get` means "I already have the address", and a `get` that could mint would make every
365
+ * card render a potential write.
366
+ */
367
+ declare function getEntityDescriptor(ref: string, deps: ResolveEntityRefDeps): EntityDescriptor | undefined;
368
+
369
+ /**
370
+ * The two leg weights, and they are EQUAL for a reason that is not aesthetic.
371
+ *
372
+ * The degradation invariant (D-08) says a floor host may only produce MORE disambiguation,
373
+ * never a different answer. Floor mode is full mode minus the dense leg, so each candidate's
374
+ * floor score is its full score minus its own dense contribution. With equal weights the most
375
+ * any candidate can gain from the dense leg is one rank-1 contribution, which is what bounds
376
+ * how far the dense leg can reorder the lexical ranking — and that bound is what
377
+ * `RESOLVER_STRONG_HIT_MARGIN` is set against. Weighting the dense leg above the lexical one
378
+ * (as the `semantic` query-class vector does for records) would let the dense leg invert an
379
+ * ordering by more than the margin, and floor mode could then resolve to a different entity.
380
+ *
381
+ * The resolver therefore owns its own weights rather than importing a query-class vector: this
382
+ * pair is load-bearing for a safety property, and a vector tuned for record retrieval has no
383
+ * obligation to stay equal.
384
+ */
385
+ declare const RESOLVER_LEG_WEIGHT_LEXICAL = 1;
386
+ declare const RESOLVER_LEG_WEIGHT_DENSE = 1;
387
+ /**
388
+ * The fused score of a candidate ranked first in EVERY leg — the scale's maximum.
389
+ *
390
+ * Derived rather than typed out, so a `C_RRF` change or a weight change moves the scale and
391
+ * the thresholds below keep meaning what they say. Dividing by it turns the fused score into
392
+ * agreement on [0, 1]: 1 means every leg put this candidate first, and exactly 0.5 means one
393
+ * leg put it first and the other did not rank it at all.
394
+ *
395
+ * This is a rescale by a constant and nothing else — it preserves the ordering `fuseWeightedRrf`
396
+ * produced, and no ranking decision is taken here. It exists so a card can print a number next
397
+ * to tier (a)'s 1.0 without inventing a second vocabulary for confidence.
398
+ */
399
+ declare const RESOLVER_FUSED_SCORE_NORMALIZER: number;
400
+ /**
401
+ * The absolute half of the D-07 rule: a strong hit is at the head of at least one leg.
402
+ *
403
+ * 0.5 is exactly one leg's rank-1 contribution on the normalised scale, and picking it says
404
+ * something narrow and true: a candidate that no leg ranked first is never a strong hit, no
405
+ * matter how far ahead of the runner-up it ended up. That case is reachable in practice — the
406
+ * index is refreshed on sighting, so it can hold a row for an entity whose owning store has
407
+ * since forgotten it; when that row heads a leg it is dropped after fusion, and the survivor
408
+ * behind it must NOT be promoted into a confident answer just because the leader vanished.
409
+ *
410
+ * Honest about its limits: on an RRF scale an absolute threshold cannot do much more than
411
+ * this. The fused top is always at least one leg's rank-1 contribution whenever any leg head
412
+ * survives, so a threshold below 0.5 would never fire, and a threshold above 0.5 would forbid
413
+ * a floor host from ever resolving anything — including a reference that matched exactly one
414
+ * entity. The discrimination work is therefore done by the margin below, which is where the
415
+ * two-part rule's second half earns its place rather than restating the first.
416
+ *
417
+ * The corpus report confirms both halves of that claim rather than only the comfortable one:
418
+ * 31 of its 110 cases resolve at the floor, so the threshold is not quietly forbidding the
419
+ * floor rung; and the case where a leg head is dropped after fusion still refuses, so it is not
420
+ * quietly permitting a survivor to inherit a leader's confidence.
421
+ */
422
+ declare const RESOLVER_STRONG_HIT_MIN_SCORE = 0.5;
423
+ /**
424
+ * The margin half of the D-07 rule, and the number that makes a safety property structural.
425
+ *
426
+ * A candidate can gain at most 0.5 on the normalised scale by being ranked first in the dense
427
+ * leg. For floor mode (which has no dense leg) to resolve entity E while full mode resolves a
428
+ * DIFFERENT entity Z, the dense leg would have to move Z past E by more than twice the margin.
429
+ * Any margin above 0.25 therefore makes that impossible rather than unlikely: floor mode can
430
+ * only ever produce the same resolved entity or refuse to resolve at all. 0.26 is the pinned
431
+ * value with room above the bound so an adjacent-rank rounding difference cannot reach it.
432
+ *
433
+ * Its everyday meaning is simpler: a strong hit is one the legs AGREE on more than they agree
434
+ * on anything else. Two near-twins ranked 1 and 2 differ by 0.016 and always disambiguate —
435
+ * including the equal-strength typo case, which differs by exactly zero and therefore cannot
436
+ * be resolved by any positive margin at all (D-07).
437
+ *
438
+ * WHERE THE BOUNDARY ACTUALLY IS, measured by the 387-05 corpus rather than argued.
439
+ *
440
+ * The 0.25 above comes from a CONTINUOUS inequality, and this scale is not continuous. A floor
441
+ * host's score comes from the lexical leg alone, so its top is one leg's rank-1 contribution
442
+ * (0.5) and its runner-up, when it has one, is rank 2 (0.4919). The largest margin a floor host
443
+ * can therefore ever clear against a rival is the gap between two adjacent ranks — 1/124, about
444
+ * 0.00806 — and above that the floor can only resolve a reference that matched exactly ONE
445
+ * entity, which no dense leg can reorder. So the real bound is thirty times smaller than the
446
+ * one the paragraph above derives, and 0.26 clears both with room to spare.
447
+ *
448
+ * That is not a licence to lower it. The corpus runs its whole population through this same
449
+ * rule at several margins and reports what it finds: no divergence at 0.26, none at 1.5×1/124,
450
+ * and a real one BELOW 1/124, where a case whose two lexical candidates sit 0.008 apart resolves
451
+ * the first at the floor while the dense leg promotes the second in full. That measurement is
452
+ * what makes the invariant's test a gate rather than an arithmetic tautology — and it is only
453
+ * reachable because the decision is applied through one exported function (`applyStrongHitRule`)
454
+ * that the corpus can hand a different margin. Nothing in production passes it one.
455
+ *
456
+ * Retune only alongside `RESOLVER_STRONG_HIT_MIN_SCORE`, and only against the corpus report
457
+ * named at the top of this file: dropping below 1/124 trades the degradation guarantee for
458
+ * recall, and dropping to 0.25 or below gives up the argument that it cannot be traded at all.
459
+ */
460
+ declare const RESOLVER_STRONG_HIT_MARGIN = 0.26;
461
+ /**
462
+ * How many candidates each leg is asked for.
463
+ *
464
+ * Larger than the top-3 a disambiguation card shows, because fusion needs to SEE the runner-up
465
+ * to refuse: a leg truncated at 3 would hide the competitor that should have turned a resolve
466
+ * into a disambiguation. Bounded because the entity corpus is bounded by what a human can name.
467
+ */
468
+ declare const RESOLVER_CANDIDATE_K = 32;
469
+ /**
470
+ * The lexical admission floor for the ENTITY corpus, which is not the records corpus floor.
471
+ *
472
+ * `TAU_LEX` is 6.86, calibrated from a recorded serving replay over knowledge records. Measured
473
+ * against entity renderings on 2026-08-02, the entire observed positive BM25 range was 0.0 to
474
+ * 3.24 — an entity rendering is six words where a record rendering is a paragraph, so the two
475
+ * corpora are not on the same scale and `TAU_LEX` rejects EVERY entity lexical candidate. The
476
+ * dense leg keeps its own floor, `tau_dense`, which is profile-owned and therefore correct
477
+ * wherever it is applied; only the lexical floor is corpus-specific, and applying a record
478
+ * corpus's number here would have silently deleted the lexical leg.
479
+ *
480
+ * Zero means "the FTS query matched at all", and that is deliberately permissive. Under the
481
+ * never-guess rule a weak extra candidate can only ADD a runner-up, and a runner-up can only
482
+ * turn a resolve into a disambiguation — never into a different answer, because `lexicalLeg`
483
+ * orders by BM25, so a genuine match always outranks a noise match. Raising this floor would
484
+ * make small corpora answer `none` where several entities plainly do mention the word, which
485
+ * is a worse lie than an honest disambiguation card.
486
+ */
487
+ declare const RESOLVER_LEXICAL_MIN_BM25 = 0;
488
+
489
+ /** Build the MEM sighting source over a knowledge root. */
490
+ declare function createMemorySightingSource(resolveKnowledgeRoot: () => string): EntitySightingSource;
491
+
492
+ /**
493
+ * Read side of the fingerprint-keyed recurrence window (Phase 390, D-07).
494
+ *
495
+ * `run_fingerprint_windows` is written by `applyRunOutcomes`; this module is the one place that
496
+ * queries it. Strictly read-only: prepared SELECTs, no statement that mutates a row, ever — the
497
+ * writer half stays whole in `run-outcomes.ts`, exactly as `run-outcome-read.ts` does for the
498
+ * per-test counters it is the sibling of.
499
+ *
500
+ * KEY LAW — a window row is addressed by `(product, fingerprint)`. Stable test identity
501
+ * `(product, filePath, testName)` travels ON the row because a crossing has to name the test it
502
+ * quarantines, but it is never part of the recurrence key: one fingerprint may be exercised by
503
+ * several tests, and D-07's rule is about the FAILURE recurring, not the test.
504
+ *
505
+ * THE WINDOW SLIDES OVER ORDINALS, NOT RUNS (D-07). Recurrence is counted over the ordinal range
506
+ * `[current - QUARANTINE_WINDOW_ORDINALS + 1, current]`:
507
+ *
508
+ * - two failing runs inside ONE build ordinal count once (the numerator counts DISTINCT ordinals),
509
+ * - a failure at ordinals 5/7/9 still crosses at 9 even though no two runs were consecutive,
510
+ * - and an occurrence that has slid out of the range is FORGIVEN. That is the whole point of a
511
+ * window: the lifetime `flake_count` never forgives, which is exactly why D-07 demoted it to a
512
+ * ranking input and made this the trigger substrate instead.
513
+ *
514
+ * `windowOrdinals` is the honest denominator: how many distinct ordinals in the range this product
515
+ * recorded a failure in at all. A caller renders "3rd occurrence in 5 builds" from the pair, and an
516
+ * empty window answers typed zeros rather than an absence a renderer has to guess about.
517
+ *
518
+ * PRECONDITION — the caller supplies a connection where `ensureRunOutcomeTables` has run (every
519
+ * `openIndexWriter` does). A read-only connection against a store predating these tables raises
520
+ * sqlite's `no such table`, which the composition layer folds to typed absence the same way it
521
+ * already does for `readTestOutcomeHistory`.
522
+ */
523
+
524
+ /**
525
+ * §7.3's locked detection window, verbatim: the same fingerprint recurring within the last five
526
+ * build ordinals. A pinned constant with no config key (386 D-15) — retune it from operational
527
+ * evidence, in the source, where the change is reviewable. Plan 05's rule reads the threshold
528
+ * against this window; D-10's auto-exit is symmetric with it by design.
529
+ */
530
+ declare const QUARANTINE_WINDOW_ORDINALS = 5;
531
+ /** The recurrence key: the failure, inside one product partition. */
532
+ interface FingerprintWindowIdentity {
533
+ product: string;
534
+ fingerprint: string;
535
+ }
536
+ /** One window row, as the writer stored it. */
537
+ interface FingerprintWindowRow {
538
+ product: string;
539
+ filePath: string;
540
+ testName: string;
541
+ fingerprint: string;
542
+ verdictClass: VerdictClass;
543
+ buildOrdinal: number;
544
+ runId: string;
545
+ }
546
+ interface FingerprintRecurrence {
547
+ /** DISTINCT build ordinals inside the window carrying this fingerprint. */
548
+ occurrenceOrdinals: number;
549
+ /** DISTINCT build ordinals inside the window carrying ANY failure for this product. */
550
+ windowOrdinals: number;
551
+ }
552
+ /**
553
+ * Every stored row for one fingerprint inside the bounded read scope, ordinal-ascending.
554
+ *
555
+ * The read scope is the last {@link LAST_OUTCOMES_LIMIT} ordinals rather than the detection window,
556
+ * so a caller that wants to SHOW the recent history (D-10's M-consecutive-passes exit, the list
557
+ * verb) sees the same depth the per-test outcome window carries. An identity the projection never
558
+ * recorded answers `[]` — typed absence, never a stand-in row.
559
+ */
560
+ declare function readFingerprintWindow(db: DatabaseSync, identity: FingerprintWindowIdentity, currentBuildOrdinal: number): FingerprintWindowRow[];
561
+ /**
562
+ * One fingerprint's recurrence over the sliding ordinal window (D-07) — computed LIVE on every
563
+ * call, never stored. Both legs count DISTINCT ordinals, which is what makes two failing runs in
564
+ * one build one occurrence.
565
+ */
566
+ declare function fingerprintRecurrence(db: DatabaseSync, identity: FingerprintWindowIdentity, currentBuildOrdinal: number): FingerprintRecurrence;
567
+
568
+ declare const LEASE_FILE = ".lease";
569
+ declare const EPOCH_FILE = ".epoch";
570
+ /** Provisional writer-heartbeat TTL; retune from operational evidence without adding config. */
571
+ declare const LEASE_HEARTBEAT_TTL_MS = 30000;
572
+ /** Bounded mutation-lock takeover; retune from operational evidence without adding config. */
573
+ declare const MUTATION_LOCK_TAKEOVER_MS = 45000;
574
+ /** Authority-store filename is pinned; retune only through a data-migration plan. */
575
+ declare const WRITER_AUTHORITY_DB_FILE = ".writer-authority.sqlite";
576
+ /**
577
+ * Provisional SQLite contention window; retune from operational evidence without adding config.
578
+ *
579
+ * The takeover deadline remains separate: this short timeout makes each failed write attempt
580
+ * observable so the contender can apply the lease and verified-termination policy.
581
+ */
582
+ declare const MUTATION_LOCK_BUSY_TIMEOUT_MS = 2000;
583
+ /**
584
+ * Transient guard serializing the beat's read-check-write with release's read-check-delete.
585
+ *
586
+ * A directory holding exactly one `owner-<token>` file, published whole by renaming a staged
587
+ * directory into place. It lives under `memory/queue/`, which every scaffolded knowledge
588
+ * `.gitignore` already ignores, so neither a guard in use nor one a crash left behind is ever a
589
+ * commit-visible file in a user's knowledge repository (261002-lse follow-up; the user's
590
+ * `.gitignore` is never rewritten, D-22).
591
+ */
592
+ declare const LEASE_GUARD_DIR = "memory/queue/lease-guard";
593
+ declare const writerLeaseSchema: z.ZodObject<{
594
+ leaseId: z.ZodString;
595
+ pid: z.ZodNumber;
596
+ epoch: z.ZodNumber;
597
+ heartbeatAtMs: z.ZodNumber;
598
+ }, z.core.$strict>;
599
+ type WriterLease = z.infer<typeof writerLeaseSchema>;
600
+ /** What the contender could observe about the owner it gave up on; never an authority claim. */
601
+ interface UnreclaimableMutationLockOwner {
602
+ pid: number;
603
+ acquiredAtMs: number;
604
+ generationToken: string;
605
+ /** False when the row carries no incarnation, which is why escalation could never fire. */
606
+ incarnationAttributed: boolean;
607
+ }
608
+ type AcquireWriterLeaseOutcome = {
609
+ kind: 'held';
610
+ holder: WriterLease;
611
+ } | {
612
+ kind: 'acquired';
613
+ leaseId: string;
614
+ epoch: number;
615
+ reclaimed?: WriterLease;
616
+ } | {
617
+ kind: 'refused';
618
+ reason: 'corrupt-epoch-state';
619
+ error: CorruptEpochStateError;
620
+ } | {
621
+ /**
622
+ * CR-01 (round 5): the lease file's own sibling of `corrupt-epoch-state`.
623
+ *
624
+ * `code` is the raw errno so an operator sees EACCES rather than a contender that silently
625
+ * spun to its deadline over an authority state nobody can evaluate; `error` keeps this arm
626
+ * inside the union's stated contract that every refusal is rethrowable.
627
+ */
628
+ kind: 'refused';
629
+ reason: 'unreadable-lease-state';
630
+ code: string;
631
+ error: UnreadableWriterLeaseStateError;
632
+ } | {
633
+ kind: 'refused';
634
+ reason: 'mutation-lock-unreclaimable';
635
+ /**
636
+ * Both refusal arms carry a thrown-shaped `error` ON PURPOSE.
637
+ *
638
+ * Every caller of this union already renders `refused` as `[reason]: error.message` or
639
+ * rethrows it, so a new arm that also carries one is surfaced by name at every entry point —
640
+ * including any added later — without a list of call sites to keep in step. Naming the
641
+ * refusal is the requirement; enumerating the callers who must name it is the failure mode
642
+ * this phase keeps rediscovering.
643
+ */
644
+ error: WriterLeaseUnreclaimableError;
645
+ attempts: number;
646
+ waitedMs: number;
647
+ observedOwner?: UnreclaimableMutationLockOwner;
648
+ observedLease?: WriterLease;
649
+ };
650
+ interface LeaseOperationOptions {
651
+ knowledgeRoot: string;
652
+ leaseId: string;
653
+ }
654
+ /** Test/chaos seam on the lease-file guard taken by `heartbeatLease` and `releaseLease`. */
655
+ interface LeaseGuardHooks {
656
+ /** Fires once, when the guard is first found held by a live section other than this caller's. */
657
+ onLeaseGuardContended?: () => void;
658
+ /** Fires each time a guard has been judged residue, before this caller reclaims it. */
659
+ beforeLeaseGuardReclaim?: () => void;
660
+ /** Fires inside a reclaim, after its first step and before its last. */
661
+ duringLeaseGuardReclaim?: () => void;
662
+ }
663
+ type HeartbeatLeaseOutcome = {
664
+ kind: 'heartbeated';
665
+ holder: WriterLease;
666
+ } | {
667
+ kind: 'refused';
668
+ reason: 'lease-missing' | 'lease-malformed'
669
+ /** CR-01: the lease exists and could not be opened — never write over what you cannot read. */
670
+ | 'lease-unreadable' | 'lease-id-mismatch' | 'stale-epoch' | 'corrupt-epoch-state';
671
+ holder?: WriterLease;
672
+ observedEpoch?: number;
673
+ error?: CorruptEpochStateError;
674
+ };
675
+ type ReleaseLeaseOutcome = {
676
+ kind: 'released';
677
+ } | {
678
+ kind: 'refused';
679
+ reason: 'lease-missing' | 'lease-malformed' | 'lease-unreadable' | 'lease-id-mismatch';
680
+ holder?: WriterLease;
681
+ };
682
+ /** Exact writer identity carried by governed maintenance mutators. */
683
+ interface WriterLeaseAuthority extends LeaseOperationOptions {
684
+ epoch: number;
685
+ }
686
+ type WriterAuthorityLostReason = Extract<HeartbeatLeaseOutcome, {
687
+ kind: 'refused';
688
+ }>['reason'] | 'epoch-mismatch-before-heartbeat' | 'epoch-mismatch-after-heartbeat' | 'mutation-lock-generation-lost';
689
+ /** Fail-closed maintenance refusal with the holder and fresh disk epoch attached. */
690
+ declare class WriterAuthorityLostError extends Error {
691
+ readonly name = "WriterAuthorityLostError";
692
+ readonly authority: WriterLeaseAuthority;
693
+ readonly reason: WriterAuthorityLostReason;
694
+ readonly observedEpoch: number;
695
+ constructor(authority: WriterLeaseAuthority, reason: WriterAuthorityLostReason, observedEpoch: number);
696
+ }
697
+ type CorruptEpochStateReason = 'unreadable' | 'malformed' | 'overflow';
698
+ /** Fail-closed authority-state error; callers must never substitute epoch zero for this state. */
699
+ declare class CorruptEpochStateError extends Error {
700
+ readonly name = "CorruptEpochStateError";
701
+ readonly path: string;
702
+ readonly reason: CorruptEpochStateReason;
703
+ readonly rawPreview: string;
704
+ constructor(path: string, reason: CorruptEpochStateReason, rawPreview: string);
705
+ }
706
+ /**
707
+ * Fail-closed acquisition refusal after the deadline; carries why the wait could not end well.
708
+ *
709
+ * A contender that gives up is reporting a fact about the SYSTEM, not about itself, so the
710
+ * diagnostics travel with the error: the owner it kept losing to, whether that owner was even
711
+ * attributable (an unattributed row can never be escalated against, which is usually the whole
712
+ * story), and the lease it kept reading. `agent-qa daemon` prints this and exits non-zero where it
713
+ * previously hung with no output at all.
714
+ */
715
+ declare class WriterLeaseUnreclaimableError extends Error {
716
+ readonly name = "WriterLeaseUnreclaimableError";
717
+ readonly reason: "mutation-lock-unreclaimable";
718
+ readonly attempts: number;
719
+ readonly waitedMs: number;
720
+ readonly observedOwner?: UnreclaimableMutationLockOwner;
721
+ readonly observedLease?: WriterLease;
722
+ constructor(attempts: number, waitedMs: number, observedOwner?: UnreclaimableMutationLockOwner, observedLease?: WriterLease);
723
+ }
724
+ /**
725
+ * The lease file's sibling of `CorruptEpochStateError`, for a read that produced no throwable.
726
+ *
727
+ * `readFileSync` threw, but the value that reaches a consumer is a kinded outcome rather than an
728
+ * exception, and `AcquireWriterLeaseOutcome`'s own contract is that EVERY refusal arm carries a
729
+ * thrown-shaped `error` — three call sites rethrow or render it without knowing which arm they
730
+ * hold (`drain.ts:842` among them). A refusal that carried only an errno string would have made
731
+ * that contract a list of call sites to keep in step, which is the failure mode this module keeps
732
+ * rediscovering. Deliberately not re-exported from the package index (IN-04): the refusal is fully
733
+ * legible from `reason`, `code` and `error.message`.
734
+ */
735
+ declare class UnreadableWriterLeaseStateError extends Error {
736
+ readonly name = "UnreadableWriterLeaseStateError";
737
+ readonly path: string;
738
+ readonly code: string;
739
+ constructor(path: string, code: string);
740
+ }
741
+ /**
742
+ * The authority-file read verdict, and it has four values rather than three.
743
+ *
744
+ * Round-5 CR-01: `unreadable` is the sibling of the epoch read's
745
+ * `CorruptEpochStateError(…, 'unreadable', …)`. A file that EXISTS and could not be opened is a
746
+ * different fact from a file that is not there and from a file whose bytes were read and failed to
747
+ * parse, and the previous three arms had nowhere to put it — so EACCES, EIO, EMFILE and ENFILE all
748
+ * landed on `malformed`, which every consumer reads as "nobody holds authority". `code` carries
749
+ * the errno so the refusal that follows can name the cause to an operator.
750
+ */
751
+ type ReadWriterLeaseOutcome = {
752
+ kind: 'absent';
753
+ } | {
754
+ kind: 'malformed';
755
+ } | {
756
+ kind: 'unreadable';
757
+ code: string;
758
+ } | {
759
+ kind: 'parsed';
760
+ lease: WriterLease;
761
+ };
762
+ /**
763
+ * Opaque process-incarnation token for one pid, anchored OUTSIDE any value this module writes.
764
+ *
765
+ * CR-04: a durable row's pid is not an identity. Once the recorded process dies, the kernel is
766
+ * free to hand that number to a stranger, and every later decision keyed on the number alone
767
+ * (above all `SIGKILL`) targets whoever holds it now. The one fact that distinguishes them is
768
+ * assigned by the OS and cannot be forged from inside the store: when the process at that pid
769
+ * started.
770
+ *
771
+ * Linux reads field 22 (`starttime`) of `/proc/<pid>/stat`, counted from the LAST `)` because the
772
+ * `comm` field is parenthesised and may itself contain spaces and parentheses. Other POSIX hosts
773
+ * read `/bin/ps -p <pid> -o lstart=` in the C locale and UTC, so every reader renders one start
774
+ * time as one token. Windows has no equivalent builtin, so it resolves to `undefined` and every
775
+ * incarnation check there fails closed.
776
+ *
777
+ * Returns `undefined` — never throws — whenever the pid cannot be attributed. `undefined` means
778
+ * "do not signal", never "safe to signal".
779
+ *
780
+ * WR-04: `timeoutMs` bounds the POSIX arm's `ps` fork, which is the last wait beneath the
781
+ * acquisition deadline that used to be able to outlive it — a wedged `ps` never returns, and this
782
+ * resolution sits directly on the pre-signal path. A cap that expires resolves
783
+ * through the SAME funnel every other failure uses (`undefined`), so a timed-out resolution is
784
+ * unverifiable and therefore signal-ineligible. Callers with no budget of their own omit it.
785
+ *
786
+ * 386-32 consumes this same helper for the embedder-worker signal path, so both kill paths speak
787
+ * one incarnation vocabulary.
788
+ */
789
+ declare function resolveProcessIncarnation(pid: number, timeoutMs?: number, psExecutable?: string): string | undefined;
790
+ interface MutationLockOptions {
791
+ /** Test/chaos seam after the claim is committed and before the held transaction begins. */
792
+ afterMutationLockClaimed?: (generationToken: string) => void;
793
+ afterMutationLockAcquired?: () => void;
794
+ /** Test/chaos seam inside acquisition, after the epoch rewrite and before lease publication. */
795
+ afterEpochRewrite?: () => void;
796
+ mutationLockTakeoverMs?: number;
797
+ /**
798
+ * Test-only observation of every process-incarnation resolution the escalation path performs.
799
+ *
800
+ * Purely an observer, never a replacement: the production resolver still runs, so what a test
801
+ * counts is what a daemon pays. It exists because the cost being bounded (a `ps` fork per
802
+ * resolution, on the caller's main thread) is invisible to any assertion about outcomes.
803
+ */
804
+ onProcessIncarnationResolved?: (pid: number) => void;
805
+ psExecutable?: string;
806
+ }
807
+ interface AcquireWriterLeaseOptions extends MutationLockOptions {
808
+ /**
809
+ * Test/chaos seam for the pinned acquisition deadline; production never sets it.
810
+ *
811
+ * The same shape as `mutationLockTakeoverMs` and the heartbeat supervisor's `stopTimeoutMs`: a
812
+ * real unreclaimable wait is 60 s, which no test can afford to sit through, and shortening the
813
+ * window is the only way to reach the refusal branch (D-15 — a seam, never a config key).
814
+ */
815
+ acquireDeadlineMs?: number;
816
+ }
817
+ interface CurrentWriterAuthority {
818
+ leaseId: string;
819
+ epoch: number;
820
+ }
821
+ interface CurrentWriterAuthorityHooks extends MutationLockOptions {
822
+ /** Test/chaos seam after the lock-held epoch comparison and before mutation. */
823
+ afterFinalAuthorityCompare?: () => void;
824
+ }
825
+ /**
826
+ * Read and strictly validate current writer authority without acquiring or refreshing it.
827
+ *
828
+ * CR-01 (round 5): ENOENT is the only read failure that is evidence of absence, and NO read
829
+ * failure is evidence of corruption. This is `readEpoch`'s rule applied to the file it was not
830
+ * applied to — the derivation's own W-01 note says the guard belongs at the READ, so that
831
+ * consumers inherit it rather than each re-deriving it, and this read has three consumers that
832
+ * select SIGKILL, epoch minting and registration deletion from its answer.
833
+ *
834
+ * The `existsSync` fallback that used to sit here is deleted rather than kept beside the errno
835
+ * test: under fd exhaustion it fails for the same reason the open did, so it degraded an EMFILE
836
+ * read from `malformed` to `absent` — the strongest possible statement that nobody holds the
837
+ * lease, produced by the weakest possible evidence.
838
+ */
839
+ declare function readWriterLease(knowledgeRoot: string): ReadWriterLeaseOutcome;
840
+ /** Purely classify a guarded or untrusted lease value against one explicit wall-clock instant. */
841
+ declare function isLeaseStale(lease: unknown, nowMs: number): boolean;
842
+ /**
843
+ * The one freshness answer, and it has three values rather than two.
844
+ *
845
+ * CR-01: the previous derivation returned a boolean, so every way of failing to READ the epoch
846
+ * collapsed into the same value a genuinely expired lease produces. `readEpoch` raises
847
+ * `CorruptEpochStateError(…, 'unreadable', …)` for EACCES, EIO, EMFILE and ENFILE, not only for
848
+ * corrupt bytes, and fd exhaustion produces both that failure and the daemon identity-handshake
849
+ * failure at once. A live, healthy holder therefore became de-registrable and SIGKILL-eligible on
850
+ * a transient read error.
851
+ *
852
+ * `unknown` is deliberately NOT resolved to either boolean here. Resolving it toward "fresh" makes
853
+ * a permanently unreadable epoch an immortal phantom holder no contender can ever reclaim;
854
+ * resolving it toward "reclaimable" is the kill this closes. It is returned to the consumers so
855
+ * each of them names what it does with it, and so a fourth consumer added later cannot inherit a
856
+ * default it never chose.
857
+ *
858
+ * W-01: the epoch conjunct lives HERE, in the derivation, not at the consumers. Three surfaces
859
+ * classify recoverability from this verdict — `leasePermitsEscalation`, the acquisition
860
+ * pre-check, and `classifyDaemonRegistration` in the CLI — and a fix applied consumer by consumer
861
+ * is a list that the next surface joins without joining the guard. A lease naming a superseded
862
+ * epoch is recovery input no matter how fresh its bytes are: nobody can still be inside a
863
+ * transaction for an epoch that has already been replaced.
864
+ *
865
+ * WR-04: a crash between the epoch rewrite and the lease rewrite leaves a lease naming the
866
+ * previous epoch. Reporting that lease as held manufactures a phantom holder no TTL sweep ever
867
+ * reclaims, so an epoch-invalid lease is recovery input rather than an answer.
868
+ *
869
+ * The inputs are the disk epoch and the lease value, and nothing else. There is no second
870
+ * freshness channel and no further conjunct: three conjuncts produced three defects.
871
+ */
872
+ type WriterLeaseFreshness = {
873
+ kind: 'fresh';
874
+ } | {
875
+ kind: 'reclaimable';
876
+ } | {
877
+ kind: 'unknown';
878
+ error: CorruptEpochStateError;
879
+ };
880
+ declare function deriveWriterLeaseFreshness(knowledgeRoot: string, lease: WriterLease, nowMs: number): WriterLeaseFreshness;
881
+ /** Return a live holder or atomically mint a new, monotonically fenced writer identity. */
882
+ declare function acquireWriterLease(knowledgeRoot: string, hooks?: AcquireWriterLeaseOptions): AcquireWriterLeaseOutcome;
883
+ /**
884
+ * Compare one exact writer authority and run its mutation under the same generation lock.
885
+ * The generation check after the failpoint prevents an expired owner from resuming after a
886
+ * bounded takeover. Mutation implementations must keep irreversible work inside `mutate`.
887
+ */
888
+ declare function withCurrentWriterAuthority<T>(knowledgeRoot: string, authority: CurrentWriterAuthority, mutate: () => T, hooks?: CurrentWriterAuthorityHooks): T;
889
+ /**
890
+ * Refresh the matching current-epoch holder without entering the mutation lock.
891
+ *
892
+ * CR-03 dissolves here rather than being worked around. The beat used to share
893
+ * `withMutationLock` with every governed write, and it runs in a worker THREAD, so the daemon's
894
+ * long synchronous mutation on the main thread held the lock while the worker blocked on it: the
895
+ * holder starved its own heartbeat, crossed the TTL and became a takeover target. Writing `.lease`
896
+ * directly means a governed mutation of any length cannot starve the beat at all, so heartbeat
897
+ * independence is structural instead of a fallback that has to be maintained beside the real path.
898
+ *
899
+ * What the lock used to buy was check-then-write atomicity of the identity/epoch ladder below
900
+ * against a concurrent reclaim, and dropping it opens exactly one race: a holder resuming from a
901
+ * >=TTL wedge can land its read-check-write astride a contender's reclaim and clobber the
902
+ * successor's lease. That is contained rather than prevented, and the containment is the epoch
903
+ * fence: a reclaim rewrites `.epoch` strictly BEFORE it publishes its lease, both inside the lock,
904
+ * so any lease clobbered afterwards names a superseded epoch. Every classifier reads it as
905
+ * recovery input, no store write is admitted under it (the epoch fence at the store verbs), and
906
+ * the successor's next authority check surfaces WriterAuthorityLostError, after which a fresh
907
+ * acquisition reclaims at the next epoch. The worst case is one successor bounce in a microsecond
908
+ * window after a 30 s wedge — a liveness hiccup, never minted authority. During the exact schedule
909
+ * this lock-free write exists for (the holder's own mutation in flight) no contender can be
910
+ * mid-reclaim at all, because a reclaim requires the very lock that mutation is holding.
911
+ *
912
+ * 2026-08-01 W-02 re-scope: `MUTATION_LOCK_TAKEOVER_MS` now governs reclaim from DEAD and
913
+ * whole-process-STOPPED owners, which write no beats and cross the TTL. Auto-takeover of a LIVE
914
+ * holder whose beat thread is still ticking is deliberately impossible; a wedged-forever live
915
+ * daemon surfaces at every contender as the typed `mutation-lock-unreclaimable` refusal naming its
916
+ * pid. That is an operator kill rather than an automated one, which is this module's own law: a
917
+ * wrong kill is unrecoverable, a wedged contender is not.
918
+ *
919
+ * Identity is still proven first, and every refusal below leaves `.lease` byte-identical.
920
+ *
921
+ * 2026-10-02 (quick 261002-lse, 432-19 #7): the ladder and the write run under the lease-file
922
+ * guard `releaseLease` also takes. Without it a beat that read before a release wrote after it,
923
+ * and since release deliberately keeps the epoch, the resurrected lease named the CURRENT epoch:
924
+ * the fence above contains a reclaim but not a release, and the next acquire answered `held`.
925
+ * The guard is not the mutation lock, so everything said above about CR-03 still holds.
926
+ */
927
+ declare function heartbeatLease(opts: LeaseOperationOptions, hooks?: LeaseGuardHooks): HeartbeatLeaseOutcome;
928
+ /**
929
+ * Refresh one exact maintenance writer at a non-mutating boundary.
930
+ *
931
+ * The epoch reads deliberately bracket the heartbeat: the first rejects an already-reclaimed
932
+ * holder, while the second detects a reclaim race after the lease rewrite. The authority-store
933
+ * transaction is released before this function returns, so this is NOT a mutation fence. Any
934
+ * irreversible mutation must instead run inside `withCurrentWriterAuthority`.
935
+ */
936
+ declare function refreshWriterLeaseAuthority(authority: WriterLeaseAuthority): WriterLeaseAuthority;
937
+ /**
938
+ * Remove only the matching lease file; the monotonic epoch is deliberately retained forever.
939
+ *
940
+ * The read and the deletion run under the lease-file guard, taken inside the mutation lock, so a
941
+ * heartbeat in flight finishes before the lease is read and any later beat finds it gone
942
+ * (261002-lse). The guard is always taken second, and the beat never takes the mutation lock, so
943
+ * the two cannot wait on each other.
944
+ */
945
+ declare function releaseLease(opts: LeaseOperationOptions, hooks?: LeaseGuardHooks): ReleaseLeaseOutcome;
946
+
947
+ /**
948
+ * What a manually added window records where an auto-entry records the fingerprint that crossed.
949
+ *
950
+ * A human quarantining a known-broken test is not reporting a recurrence, so there is no window key
951
+ * to carry — and the `entered` event REQUIRES one (an entry with no fingerprint would fail the
952
+ * ledger's own shape guard). A typed literal that cannot be mistaken for a real key is the honest
953
+ * answer: every computed fingerprint contains `::`, this one contains none.
954
+ */
955
+ declare const QUARANTINE_MANUAL_ENTRY_FINGERPRINT = "manual-entry";
956
+ /**
957
+ * The class a manual entry records: `unknown`, on the merits rather than for convenience.
958
+ *
959
+ * `unknown` is precisely "a failure carrying no verdict" — nothing classified this test, because a
960
+ * human decided to suppress it rather than a detector deciding it recurred. Recording a made-up
961
+ * `flake` would put an assertion nobody made into committed truth, and the detection ladder reads
962
+ * the class of AUTO entries only, so no rule changes behaviour on the strength of this value.
963
+ */
964
+ declare const QUARANTINE_MANUAL_ENTRY_CLASS: VerdictClass;
965
+ /**
966
+ * The `filePath` a caller sent cannot mint the durable identity the ledger is keyed on (390-12).
967
+ *
968
+ * Same shape and same reason as the lease refusal above: the port's result unions carry no refusal
969
+ * arm by design (390-04), so a refusal that is not one of the named outcomes surfaces as a typed
970
+ * throw. Raised BEFORE the knowledge root is resolved, before the lease is acquired and before a
971
+ * byte is read, so a refused call costs the ledger nothing and the next writer nothing.
972
+ *
973
+ * WHAT THIS OWNS, AND WHAT THE LEDGER'S OWN WRITE GUARD OWNS (390-11). They are not two spellings
974
+ * of one rule and neither subsumes the other:
975
+ *
976
+ * - THIS decides what the identity IS. It is the only gate that knows the checkout root, so it is
977
+ * the only one that can turn `/Users/me/repo/tests/a.yaml` — a real file inside the workspace,
978
+ * the exact bytes an operator pastes out of a failure report — into `tests/a.yaml` and ACCEPT
979
+ * it. The structural guard would refuse that input; refusing it is the wrong answer, because
980
+ * the operator named a file the workspace genuinely contains.
981
+ * - The STRUCTURAL guard in `appendQuarantineEvents` decides what the FILE may carry, for every
982
+ * writer including one that never met a mint. It is a backstop with no host context, and it
983
+ * throws rather than answering data because a line the reader refuses is corruption.
984
+ *
985
+ * What cannot be canonicalized at all — a path escaping the config directory — is refused here,
986
+ * with the mint's own message carried so the operator learns why rather than that.
987
+ */
988
+ declare class QuarantineIdentityRefusedError extends Error {
989
+ readonly filePath: string;
990
+ readonly reason: unknown;
991
+ constructor(filePath: string, reason: unknown);
992
+ }
993
+ interface QuarantineOperationsOptions {
994
+ /** The committed knowledge root. A FACTORY, because the CLI host resolves it asynchronously. */
995
+ resolveKnowledgeRoot: () => Promise<string> | string;
996
+ /** The ONLY way wall clock enters these verbs. Required — see the module head. */
997
+ now: () => string;
998
+ /**
999
+ * Mints the durable workspace-relative identity a `filePath` is stored under (390-12 / D-01).
1000
+ *
1001
+ * Production supplies core's ONE recipe, `workspaceRelativeIdentityPath`, pre-bound to the host's
1002
+ * own config directory. INJECTED rather than imported for two independent reasons, and either
1003
+ * alone would be sufficient: this package may not depend on `@vostride/agent-qa-core` (only the
1004
+ * surface packages may), and — the load-bearing one — the checkout root is a HOST fact these
1005
+ * verbs cannot discover. A verb that guessed it would mint identities against the wrong root.
1006
+ *
1007
+ * REQUIRED, with no default. A default passthrough is exactly the unvalidated pass-through that
1008
+ * produced the round-2 regression: the verbs answered `entered` over lines the reader is
1009
+ * guaranteed to refuse. An absent mint must be a compile error at the composition that forgot it,
1010
+ * not a runtime surprise inside a committed artifact.
1011
+ *
1012
+ * @throws whatever the mint throws for a path it cannot canonicalize; re-raised as
1013
+ * {@link QuarantineIdentityRefusedError}.
1014
+ */
1015
+ mintIdentityPath: (filePath: string) => string;
1016
+ /**
1017
+ * The writer lease THIS process already holds, when it holds one (387-08 D-18 / D-5).
1018
+ *
1019
+ * Supplied by the daemon host, which acquires at startup and holds for its whole lifetime: a
1020
+ * holder that calls `acquireWriterLease` is answered `held` by itself and, unlike the apply path,
1021
+ * has nobody to delegate to. Absent on the CLI in-process host, which holds nothing and therefore
1022
+ * takes the lease per call.
1023
+ *
1024
+ * THE VALUE IS CONSULTED FOR FENCING (390-15 / G-390-2R), and this sentence used to say the
1025
+ * opposite. It is re-validated against disk on every write verb, so the question it answers is
1026
+ * "is this process STILL the holder?" rather than "did this process already serialize itself?".
1027
+ * The two are the same question only while nothing reclaims: a daemon whose heartbeat starved
1028
+ * and whose lease was then taken answers yes to the second one forever, and the two-OS-process
1029
+ * harness measured what that costs — two `entered` lines for one identity, and the verb
1030
+ * returning an owner committed truth does not record. See {@link underWriterLease}.
1031
+ *
1032
+ * AND WHAT THE RE-VALIDATION DOES NOT BUY (390-22 / WR-07): it is a read, not a lock. It answers
1033
+ * "still the holder?" as of the read, and the decide it licenses runs after it. A reclaim landing
1034
+ * between the two is still unfenced. `lease/under-writer-lease.ts` states the residual in full.
1035
+ */
1036
+ resolveWriterAuthority?: () => {
1037
+ leaseId: string;
1038
+ epoch: number;
1039
+ } | undefined;
1040
+ }
1041
+ /**
1042
+ * The three verbs over one committed ledger.
1043
+ *
1044
+ * Each call is a full read-fold-decide-append cycle. Nothing is cached between calls, which is what
1045
+ * makes `quarantine.list` and the sentence's ladder clause two readings of ONE live computation
1046
+ * (SC4): an event appended between two calls is visible to both.
1047
+ */
1048
+ declare function createQuarantineOperations(options: QuarantineOperationsOptions): QuarantineOperationsPort;
1049
+
1050
+ declare const ARTIFACT_STEP_CAP_BYTES: number;
1051
+ declare const ARTIFACT_RUN_CAP_BYTES: number;
1052
+ interface ArtifactCopyInput {
1053
+ ref: ArtifactRef;
1054
+ sourcePath: string;
1055
+ stepIndex: number;
1056
+ role?: string;
1057
+ originalName?: string;
1058
+ }
1059
+ interface CopiedArtifact {
1060
+ ref: ArtifactRef;
1061
+ relPath: string;
1062
+ bytes: number;
1063
+ stepIndex: number;
1064
+ role?: string;
1065
+ originalName?: string;
1066
+ }
1067
+ type ArtifactCopySkipReason = 'step-cap' | 'run-cap' | 'pruned-source' | 'sha-mismatch';
1068
+ interface SkippedArtifact<Reason extends string = ArtifactCopySkipReason> {
1069
+ ref: ArtifactRef;
1070
+ stepIndex: number;
1071
+ reason: Reason;
1072
+ }
1073
+ interface ArtifactCopyResult {
1074
+ copied: CopiedArtifact[];
1075
+ skipped: SkippedArtifact[];
1076
+ evidenceIncomplete: boolean;
1077
+ }
1078
+ interface ArtifactCopyOptions {
1079
+ /** Pre-copy refusals retained in the same inventory as copy-time refusals. */
1080
+ preSkipped?: readonly SkippedArtifact[];
1081
+ mask?: MaskFn;
1082
+ }
1083
+ declare function copyArtifacts(inputs: readonly ArtifactCopyInput[], destArtifactsDir: string, options?: ArtifactCopyOptions): ArtifactCopyResult;
1084
+
1085
+ interface RenderedBundlePart {
1086
+ relPath: string;
1087
+ label: string;
1088
+ text: string;
1089
+ }
1090
+ type BundleProvenanceInput = {
1091
+ mode: 'run-linked';
1092
+ runId: string;
1093
+ testId: string;
1094
+ stepIndex: number;
1095
+ curationJobId?: string;
1096
+ sourceBuildOrdinal?: number;
1097
+ authorship?: CurationAuthorship;
1098
+ needsEnrichment?: boolean;
1099
+ withheld?: boolean;
1100
+ } | {
1101
+ mode: 'standalone';
1102
+ sourceDescription: string;
1103
+ };
1104
+ interface RenderBundlePartsInput {
1105
+ draft: BundleDraft;
1106
+ discriminant: BundleDiscriminant;
1107
+ originClass: OriginClass;
1108
+ createdAtOrdinal: number;
1109
+ screenStates: readonly ScreenState[];
1110
+ initialEvents: readonly BundleOutcomeEvent[];
1111
+ relations: readonly BundleRelation[];
1112
+ provenance: BundleProvenanceInput;
1113
+ copiedArtifacts: ArtifactCopyResult;
1114
+ evidenceIncomplete: boolean;
1115
+ evidenceMarkdown?: string;
1116
+ distilledObservations?: readonly DistilledObservation[];
1117
+ }
1118
+ declare function renderBundleParts(input: RenderBundlePartsInput): {
1119
+ parts: RenderedBundlePart[];
1120
+ id: RecordId;
1121
+ identityHash: string;
1122
+ identityCore: BundleIdentityCore;
1123
+ };
1124
+
1125
+ interface BundleStoreConfig {
1126
+ /** Absolute committed-root path. Deliberately no working-directory default. */
1127
+ knowledgeRoot: string;
1128
+ productKey: string;
1129
+ commitArtifacts: 'none' | 'thumbnails';
1130
+ /** Active writer identity used to attribute crash-left staging trees. */
1131
+ writerLeaseId?: string;
1132
+ /** Fencing epoch embedded in reader-invisible staging names. */
1133
+ writerEpoch?: number;
1134
+ hooks?: {
1135
+ /** Test/chaos seam after all staged bytes exist but before the visibility rename. */
1136
+ beforePublishRename?: () => void;
1137
+ /** Test/chaos seam after each durable mutable-file replacement. */
1138
+ afterMutableReplacement?: (targetRelPath: string) => void;
1139
+ };
1140
+ }
1141
+ interface PublishBundleInput extends Omit<RenderBundlePartsInput, 'copiedArtifacts' | 'evidenceIncomplete'> {
1142
+ artifacts: readonly ArtifactCopyInput[];
1143
+ /** Refusals already decided by the Curator's pre-copy pass. */
1144
+ preSkippedArtifacts?: readonly SkippedArtifact<ArtifactCopySkipReason>[];
1145
+ }
1146
+ type PublishBundleOutcome = {
1147
+ kind: 'published';
1148
+ id: RecordId;
1149
+ dir: string;
1150
+ evidenceIncomplete: boolean;
1151
+ } | {
1152
+ kind: 'already-exists';
1153
+ id: RecordId;
1154
+ } | {
1155
+ kind: 'identity-collision';
1156
+ id: RecordId;
1157
+ dir: string;
1158
+ reason: 'identity-hash-mismatch' | 'identity-core-mismatch' | 'existing-manifest-corrupt';
1159
+ expectedIdentityHash: string;
1160
+ existingIdentityHash: string | null;
1161
+ };
1162
+
1163
+ /** Unfenced compatibility composition for test data setup only. */
1164
+ declare function publishBundle(config: BundleStoreConfig, input: PublishBundleInput): PublishBundleOutcome;
1165
+
1166
+ type AppendOutcome = {
1167
+ kind: 'appended';
1168
+ } | {
1169
+ kind: 'duplicate';
1170
+ } | {
1171
+ kind: 'not-found';
1172
+ };
1173
+ /** 416 D-04: receipt identity is its verified content digest, before any committed write. */
1174
+ declare function appendGateReceipt(config: BundleStoreConfig, id: RecordId, receipt: GateReceipt): AppendOutcome;
1175
+ /** 416 D-05/D-07: one promotion line per candidate/path; lifecycle bytes remain unchanged. */
1176
+ declare function appendPromotionReceipt(config: BundleStoreConfig, id: RecordId, lineWithoutDigest: Omit<PromotionReceipt, 'receiptDigest' | 'citedReceiptDigests'> & {
1177
+ citedReceiptDigests: readonly string[];
1178
+ }): AppendOutcome;
1179
+ declare const MEASUREMENTS_REL_PATH = "evidence/measurements.json";
1180
+ declare function appendOutcomeEvent(config: BundleStoreConfig, id: RecordId, event: BundleOutcomeEvent): AppendOutcome;
1181
+ /**
1182
+ * Persist D-03's immutable validity-window snapshot. An existing measurements
1183
+ * file is an idempotent refusal: baseline bytes are never rewritten.
1184
+ */
1185
+ declare function writeMeasurementBaseline(config: BundleStoreConfig, id: RecordId, baseline: MeasurementBaseline): AppendOutcome;
1186
+ /**
1187
+ * Append one observation through the full journaled replacement path. Existing
1188
+ * lines remain byte-for-byte unchanged; only the new canonical line is added.
1189
+ */
1190
+ declare function appendMeasurementObservation(config: BundleStoreConfig, id: RecordId, observation: MeasurementObservation): AppendOutcome;
1191
+ declare function appendRelation(config: BundleStoreConfig, id: RecordId, relation: BundleRelation): AppendOutcome;
1192
+
1193
+ type CanonicalBundleStateDigests = {
1194
+ kind: 'found';
1195
+ digests: string[];
1196
+ } | {
1197
+ kind: 'not-found';
1198
+ };
1199
+ declare function readCanonicalBundleStateDigests(input: {
1200
+ knowledgeRoot: string;
1201
+ candidateId: string;
1202
+ }): CanonicalBundleStateDigests;
1203
+
1204
+ /**
1205
+ * The leg Channel-1 run-brief assembly attributes to. See the module header: the assembly's whole
1206
+ * ordering signal is the scope/anchor token overlap, so this is a statement about the code that
1207
+ * ranks, not a default.
1208
+ */
1209
+ declare const CHANNEL_1_SERVING_LEG: RetrievalTraceLeg;
1210
+ /**
1211
+ * The leg Channel-3 caution selection attributes to. Unambiguous here: a caution with no matching
1212
+ * anchor is dropped before it is ever ranked.
1213
+ */
1214
+ declare const CHANNEL_3_CAUTION_LEG: RetrievalTraceLeg;
1215
+ /**
1216
+ * The legs a serving channel in this package can actually emit. This is the `knowledge-local` half
1217
+ * of the cross-package vocabulary check the trace DTO asks for: the schema declares
1218
+ * `RETRIEVAL_TRACE_LEGS`, the retrieval legs module declares a module-private `CandidateLeg`, and
1219
+ * this package is the only one that can see both. Exported so a test can assert the three sets
1220
+ * agree instead of a comment claiming they do.
1221
+ */
1222
+ declare const SERVING_TRACE_LEGS: readonly RetrievalTraceLeg[];
1223
+
1224
+ type EmbedCacheLeg = 'doc' | 'query';
1225
+ type EmbedCacheMode = 'check-only' | 'check-and-store';
1226
+ interface EmbedCacheLookup {
1227
+ db: DatabaseSync;
1228
+ profile: EmbeddingProfile;
1229
+ leg: EmbedCacheLeg;
1230
+ rendering: string;
1231
+ }
1232
+ interface EmbedCachePut extends EmbedCacheLookup {
1233
+ vector: Float32Array;
1234
+ }
1235
+ interface EmbedCacheWrapperOptions {
1236
+ embedder: EmbedderPort;
1237
+ profile: EmbeddingProfile;
1238
+ db: DatabaseSync;
1239
+ mode: EmbedCacheMode;
1240
+ }
1241
+ /** Create the derived cache table from a writer-role connection. */
1242
+ declare function ensureEmbedCacheTable(db: DatabaseSync): void;
1243
+ /** Return a copied vector so callers cannot mutate SQLite-owned row bytes. */
1244
+ declare function getCachedEmbedding({ db, profile, leg, rendering, }: EmbedCacheLookup): Float32Array | undefined;
1245
+ /** Store an exact Float32 byte copy under the raw-rendering content hash. */
1246
+ declare function putCachedEmbedding({ db, profile, leg, rendering, vector, }: EmbedCachePut): void;
1247
+ /** Evict every cached leg/revision/dtype row owned by one model id. */
1248
+ declare function dropEmbedCacheForModel(db: DatabaseSync, modelId: string): void;
1249
+ /**
1250
+ * Decorate an embedder with the M2 connection-role posture.
1251
+ *
1252
+ * Reader connections use check-only: they benefit from materialized rows but never write on a
1253
+ * miss. Writer-role backfill, migration, and bench paths use check-and-store, which is where the
1254
+ * steady-state cache materializes. Callers must ensure the table once through a writer first.
1255
+ */
1256
+ declare function wrapEmbedderWithCache({ embedder, profile, db, mode, }: EmbedCacheWrapperOptions): EmbedderPort;
1257
+
1258
+ type WorkerDiscoveryResult = {
1259
+ kind: 'no-worker';
1260
+ } | {
1261
+ kind: 'worker';
1262
+ };
1263
+ type EmbedderRung = 'native' | 'floor';
1264
+ type EmbedderWorkerPolicy = 'preferred' | 'required';
1265
+ type DeferReason = 'cold-artifact' | 'load-failure' | 'worker-unavailable';
1266
+ type AcquireEmbedderOutcome = {
1267
+ kind: 'ready';
1268
+ rung: EmbedderRung;
1269
+ profile: EmbeddingProfile;
1270
+ embedder: EmbedderPort;
1271
+ } | {
1272
+ kind: 'deferred';
1273
+ reason: DeferReason;
1274
+ } | {
1275
+ kind: 'lexical-only';
1276
+ };
1277
+ /**
1278
+ * D-12/D-17 test-only capability injection seam.
1279
+ *
1280
+ * Production callers must omit this field so rung selection reflects host truth. No probe is a
1281
+ * user-facing selector, configuration key, environment variable, or CLI switch.
1282
+ */
1283
+ interface EmbedderProbes {
1284
+ discoverWorker?: (agentQaRoot: string) => Promise<WorkerDiscoveryResult>;
1285
+ createNativeEmbedder?: (options: {
1286
+ profile: EmbeddingProfile;
1287
+ allowDownload: boolean;
1288
+ }) => Promise<EmbedderPort>;
1289
+ createFloorEmbedder?: () => EmbedderPort | Promise<EmbedderPort>;
1290
+ }
1291
+ interface AcquireEmbedderOptions {
1292
+ preferredProfile?: EmbeddingProfile;
1293
+ allowDownload: boolean;
1294
+ /** Daemon posture requires the child; ordinary one-shot callers retain the preferred ladder. */
1295
+ workerPolicy?: EmbedderWorkerPolicy;
1296
+ /** Explicit hosted runtime root. Required policy must never infer it from ambient cwd. */
1297
+ agentQaRoot?: string;
1298
+ cache?: {
1299
+ db: DatabaseSync;
1300
+ mode: EmbedCacheMode;
1301
+ };
1302
+ /** D-12/D-17 test seam. Production callers resolve actual host capabilities. */
1303
+ probes?: EmbedderProbes;
1304
+ }
1305
+ interface ResolveEmbedderCapabilityOptions {
1306
+ preferredProfile?: EmbeddingProfile;
1307
+ allowDownload: boolean;
1308
+ /** Daemon posture requires the child; ordinary one-shot callers retain the preferred ladder. */
1309
+ workerPolicy?: EmbedderWorkerPolicy;
1310
+ /** Explicit hosted runtime root. Required policy must never infer it from ambient cwd. */
1311
+ agentQaRoot?: string;
1312
+ /** D-12/D-17 test seam. Production callers resolve actual host capabilities. */
1313
+ probes?: EmbedderProbes;
1314
+ }
1315
+ type CapabilityOutcome = AcquireEmbedderOutcome;
1316
+ /**
1317
+ * Invalidate process-lifetime capability resolution without touching process-local pipelines.
1318
+ *
1319
+ * A named profile clears only its memo entry. Omitting the profile clears every production and
1320
+ * probe-isolated entry so a long-lived host can re-probe after its worker or artifact state moves.
1321
+ */
1322
+ declare function resetEmbedderCapability(profile?: EmbeddingProfile): void;
1323
+ interface DiscoverEmbedderWorkerOptions {
1324
+ /** Test-only shortening seam; production omits this and uses the pinned request deadline. */
1325
+ readonly requestTimeoutMs?: number;
1326
+ }
1327
+ /** D-09's worker arm, rooted in the hosted project rather than ambient process cwd. */
1328
+ declare function discoverEmbedderWorker(agentQaRoot: string, options?: DiscoverEmbedderWorkerOptions): Promise<WorkerDiscoveryResult>;
1329
+ /**
1330
+ * Resolve the automatic ladder once per profile for this process (D-17).
1331
+ *
1332
+ * Every host/runtime failure collapses to a typed capability outcome; callers cannot select a
1333
+ * rung. Injected test seams receive an isolated memo while production keys solely by the stable
1334
+ * embedding profile identity.
1335
+ */
1336
+ declare function resolveEmbedderCapability(options: ResolveEmbedderCapabilityOptions): Promise<CapabilityOutcome>;
1337
+ declare function acquireEmbedder(options: AcquireEmbedderOptions): Promise<AcquireEmbedderOutcome>;
1338
+
1339
+ /**
1340
+ * The ONE fold from the host's two retrieval axes to the recorded rung (407 TRST-01 · D-13/D-17).
1341
+ *
1342
+ * ONE resolver, three callers: the runner's evidence recorder (407-07) stamps the value into
1343
+ * `StepEvidence.retrievalRung`, `doctor` (407-08) grades it live, and the run header banner
1344
+ * (407-10) renders the recorded copy. Three callers deriving a rung independently from the same
1345
+ * two axes is exactly how a banner and a doctor row come to disagree about one machine, so the
1346
+ * fold table lives here once and its cells are pinned by `__tests__/retrieval-rung.test.ts`.
1347
+ *
1348
+ * WHAT THE VALUE MEANS. It is the HOST's ladder at serve time — never a claim about how any
1349
+ * individual brief line was ranked. The run-brief serve path reads index rows directly and
1350
+ * attributes channels 1 and 3 to the `anchor` leg by construction; the dense leg and the
1351
+ * bundle-scan fallback belong to the general `retrieve()` path the curator, the entity resolver
1352
+ * and the authoring reader use. A surface renders "retrieval was on rung X for this run".
1353
+ *
1354
+ * This module lives in knowledge-local because it is the only package that can see either axis:
1355
+ * the index face is `index-reader`'s degrade ladder and the embedder outcome is
1356
+ * `embedder-acquire`'s capability probe. What it hands back is the schema-owned value.
1357
+ */
1358
+
1359
+ /** The index half of the fold — `index-reader`'s degrade ladder, read without side effects. */
1360
+ type IndexAxis = IndexFace;
1361
+ /** The embedder half of the fold — `embedder-acquire`'s capability outcome, unchanged. */
1362
+ type EmbedderAxis = AcquireEmbedderOutcome;
1363
+ /**
1364
+ * Fold the two axes into the value that gets recorded (D-17).
1365
+ *
1366
+ * ORDERING IS PART OF THE CONTRACT: the index axis is consulted FIRST. No dense leg can run
1367
+ * against an index that will not open, whatever the embedder resolved to, so a `scan` face is
1368
+ * `bundle-scan` even on a host with a live native embedder. Folding the embedder first would
1369
+ * label a host with an unopenable index `memidex` and make the banner lie.
1370
+ *
1371
+ * `reason` is ABSENT for `memidex` — the top rung is not a degradation and has no cause to name.
1372
+ * Every lower rung carries one, which is what lets the banner say rung AND cause in one line.
1373
+ */
1374
+ declare function foldRetrievalRung(index: IndexAxis, embedder: EmbedderAxis, preferredProfile?: EmbeddingProfile): RecordedRetrievalRung;
1375
+ interface ResolveRetrievalRungOptions {
1376
+ knowledgeRoot: string;
1377
+ /** Explicit hosted runtime root for worker discovery; defaults to the acquirer's own rule. */
1378
+ agentQaRoot?: string;
1379
+ preferredProfile?: EmbeddingProfile;
1380
+ /** D-12/D-17 test seam, passed through unchanged. Production callers omit it. */
1381
+ probes?: EmbedderProbes;
1382
+ }
1383
+ /**
1384
+ * Probe both axes and fold them (D-17). NEVER throws, NEVER downloads, NEVER writes.
1385
+ *
1386
+ * - `allowDownload: false` is hard-coded, not an option: this call answers "what is this host
1387
+ * doing today", and a probe that fetched a model would change the answer by asking it (T-407-09).
1388
+ * - A `scan` face short-circuits before the embedder is touched at all. The fold does not need the
1389
+ * embedder axis in that arm, and the probe can cost a model load.
1390
+ * - A rejected capability probe folds to `deferred/load-failure` rather than propagating. This
1391
+ * resolver's callers are a banner, a doctor row and an evidence recorder; none of them has a
1392
+ * sensible behaviour for "asking about the ladder threw".
1393
+ * - `classifyIndexFace` writes no rebuild marker, so asking is not a mutation of the knowledge root.
1394
+ *
1395
+ * The memo inside `resolveEmbedderCapability` is what makes a second call in the same process free,
1396
+ * which is why `doctor` and the runner can both ask without paying twice.
1397
+ */
1398
+ declare function resolveRetrievalRung(options: ResolveRetrievalRungOptions): Promise<RecordedRetrievalRung>;
1399
+
1400
+ /**
1401
+ * Fixed weighted-RRF vector for each deterministic query class (D-06).
1402
+ *
1403
+ * PROVISIONAL vectors pending M3's learned per-product replacements. Plain RRF with all three
1404
+ * weights equal to 1 is the declared cold-start floor; learning may replace vectors, never the
1405
+ * classifier itself.
1406
+ */
1407
+ declare const QUERY_CLASS_WEIGHTS: {
1408
+ readonly literal: {
1409
+ readonly wLex: 1.5;
1410
+ readonly wDense: 0.5;
1411
+ readonly wAnchor: 2;
1412
+ };
1413
+ readonly mixed: {
1414
+ readonly wLex: 1;
1415
+ readonly wDense: 1;
1416
+ readonly wAnchor: 1.5;
1417
+ };
1418
+ readonly semantic: {
1419
+ readonly wLex: 0.5;
1420
+ readonly wDense: 1.5;
1421
+ readonly wAnchor: 1;
1422
+ };
1423
+ };
1424
+
1425
+ /**
1426
+ * D-06's closed query-class contract. The classifier is deterministic and total; future learned
1427
+ * weights may replace class vectors, but they must not replace or widen these classes implicitly.
1428
+ */
1429
+ type QueryClass = 'literal' | 'mixed' | 'semantic';
1430
+
1431
+ /**
1432
+ * The deterministic memory.md §5.5.8 query door (377 D-13). Every identity anchor is normalized
1433
+ * through the schema authority before keys or matching helpers are derived. Raw selector atoms
1434
+ * remain lexical-only; dense text and fallback regions are assembled from structured fields.
1435
+ */
1436
+
1437
+ interface RetrievalQueryInput {
1438
+ screenState?: ScreenState;
1439
+ intent: string;
1440
+ anchors: readonly Anchor[];
1441
+ }
1442
+ interface FallbackRendering {
1443
+ anchorIndex: number;
1444
+ lookupKey: string;
1445
+ text: string;
1446
+ reconfirmToken: string;
1447
+ fallback: true;
1448
+ anchorExact: false;
1449
+ }
1450
+ interface RetrievalQuery {
1451
+ lexicalQuery: string;
1452
+ semanticText: string;
1453
+ normalizedAnchors: Anchor[];
1454
+ anchorLookupKeys: string[];
1455
+ queryClass: QueryClass;
1456
+ weights: (typeof QUERY_CLASS_WEIGHTS)[QueryClass];
1457
+ fallbackRenderings: FallbackRendering[];
1458
+ }
1459
+ /**
1460
+ * Build both recall inputs from structured runner state. Fallback renderings are candidates only:
1461
+ * callers use them after exact/normalized anchor lookup misses and may set the admission
1462
+ * anchorExact carve-out only after a lexical hit re-confirms reconfirmToken (T-377-23).
1463
+ */
1464
+ declare function buildRetrievalQuery(input: RetrievalQueryInput): RetrievalQuery;
1465
+
1466
+ interface AsOfRange {
1467
+ lo: number;
1468
+ hi: number;
1469
+ }
1470
+
1471
+ /**
1472
+ * Public memory.md §5.5 retrieval composition.
1473
+ *
1474
+ * Usage telemetry (§5.5.9) is deferred by architecture: reader connections are query_only, so
1475
+ * counters land with Phases 378/380's writer-owned paths and are never an input to trust.
1476
+ */
1477
+
1478
+ interface RetrieveOptions {
1479
+ n: number;
1480
+ product: string;
1481
+ testKey?: string;
1482
+ suiteKey?: string;
1483
+ asOfBuild?: number;
1484
+ resolveLatestOrdinal?: (product: string) => number | undefined;
1485
+ runtimeRoot?: string;
1486
+ includeSuperseded?: boolean;
1487
+ asOfRange?: AsOfRange;
1488
+ /** Host-resolved posture; a deferred daemon capability forbids in-process fallback. */
1489
+ embeddingCapability?: AcquireEmbedderOutcome;
1490
+ }
1491
+ type RetrieveResult = IndexEntry[];
1492
+ /** Retrieve a deterministic ranked headline list through the reader's current capability. */
1493
+ declare function retrieve(reader: KnowledgeReader, query: RetrievalQueryInput, options: RetrieveOptions): Promise<RetrieveResult>;
1494
+
1495
+ interface RebuildReport {
1496
+ projected: number;
1497
+ skippedIncomplete: number;
1498
+ scanIssues: string[];
1499
+ removed: number;
1500
+ backfilled: number;
1501
+ vec: VecCapability;
1502
+ pendingEmbeddings: number;
1503
+ maintenanceRemaining: boolean;
1504
+ }
1505
+ type RebuildMutationFamily = 'project-bundle' | 'checkpoint-passive' | 'remove-missing-bundles' | 'complete-rendering-epoch' | 'complete-trust-epoch' | 'clear-rebuild-request';
1506
+ interface RebuildIndexHooks {
1507
+ /** Test/chaos seam after synchronous DML and immediately before the final commit fence. */
1508
+ beforeTransactionCommit?: () => void;
1509
+ /** Test/chaos seam immediately before one named authority unit acquires its hold. */
1510
+ beforeMutationAuthority?: (family: RebuildMutationFamily) => void;
1511
+ /** Test/chaos seam after authority is held and before the named mutation begins. */
1512
+ insideMutationAuthority?: (family: RebuildMutationFamily) => void;
1513
+ }
1514
+ interface RebuildIndexOptions {
1515
+ knowledgeRoot: string;
1516
+ priors?: TrustPriorTable;
1517
+ embedder?: EmbedderPort;
1518
+ vecLoadablePath?: string;
1519
+ writerAuthority?: WriterLeaseAuthority;
1520
+ hooks?: RebuildIndexHooks;
1521
+ }
1522
+ /**
1523
+ * WR-01 record-scoped healing: delete the total projection set for exactly the supplied bundle
1524
+ * ids inside one transaction and return the number of records removed. An absent index db,
1525
+ * empty ids, and ids without records rows are no-ops; embed_cache is untouched (purge-only A4).
1526
+ */
1527
+ declare function removeBundleProjectionRows(knowledgeRoot: string, bundleIds: readonly string[], writerAuthority: WriterLeaseAuthority): number;
1528
+ /** Hash-selected projection plus the independent D-02 backfill and D-10 reconciliation passes. */
1529
+ declare function rebuildIndex(opts: RebuildIndexOptions): Promise<RebuildReport>;
1530
+
1531
+ declare function readLatestBuildOrdinal(runtimeRoot: string, product: string): number | undefined;
1532
+
1533
+ /** The one table name, so the read port and every writer cannot spell it apart. */
1534
+ declare const SUITE_FINGERPRINTS_TABLE = "suite_fingerprints";
1535
+
1536
+ /** One test's ordered fingerprint elements, keyed by the stable identity. */
1537
+ interface SuiteFingerprintTestDelta {
1538
+ filePath: string;
1539
+ testName: string;
1540
+ /** `actionAnchorElement` bytes, in exercised order. Never empty — see `testFingerprint`. */
1541
+ elements: string[];
1542
+ }
1543
+ /** The row set one run contributes, plus the provenance the replacement rule compares on. */
1544
+ interface SuiteFingerprintsProjection {
1545
+ product: string;
1546
+ runId: string;
1547
+ buildOrdinal: number;
1548
+ tests: SuiteFingerprintTestDelta[];
1549
+ }
1550
+ interface FoldSuiteFingerprintsOptions {
1551
+ /**
1552
+ * The projection partition — `projectionPartition(handoff.scope).product`. Supplied by the
1553
+ * caller so this module never re-spells the partition law that `run-outcomes.ts` owns (and so
1554
+ * the two modules do not have to import each other).
1555
+ */
1556
+ product: string;
1557
+ /**
1558
+ * The `excludedFromOutcomes` decision, CARRIED rather than re-read. See the exclusion note in
1559
+ * this module's header: a gate-run contributes no rows.
1560
+ */
1561
+ excluded: boolean;
1562
+ }
1563
+ /**
1564
+ * The per-identity deltas for a member list (pure).
1565
+ *
1566
+ * Takes MEMBERS rather than a handoff because the production caller has already applied the one
1567
+ * exclusion gate to them — see the exclusion note in the header. A test contributing zero
1568
+ * elements yields no delta at all, which is what stops an evidence-less run from ERASING an
1569
+ * identity's accumulated coverage: the union only ever moves through evidence.
1570
+ */
1571
+ declare function suiteFingerprintDeltas(members: readonly PerTestOutcome[]): SuiteFingerprintTestDelta[];
1572
+ /**
1573
+ * Writer against the one index connection.
1574
+ *
1575
+ * Deliberately opens NO transaction of its own. On the production path it runs inside
1576
+ * `applyRunOutcomes`'s `BEGIN IMMEDIATE`, so the fingerprint rows and the outcome counters commit
1577
+ * or roll back together and the two tables can never describe different runs; SQLite has no
1578
+ * nested transactions, so a second `BEGIN IMMEDIATE` here would fail exactly on that path. Called
1579
+ * standalone it rides autocommit, which is sound because the replacement rule is convergent.
1580
+ */
1581
+ declare function applySuiteFingerprints(db: DatabaseSync, projection: SuiteFingerprintsProjection): number;
1582
+ /**
1583
+ * The standalone fold door: project one handoff's fingerprints and write them.
1584
+ *
1585
+ * The production path does NOT come through here — it folds both projections in one pass through
1586
+ * `projectRunOutcomes`/`applyRunOutcomes` so a single transaction and a single applied-runs marker
1587
+ * cover both tables. This entry exists for callers that hold a handoff and want only the
1588
+ * fingerprint half (the gate evaluator's own harness, and this module's corpus).
1589
+ */
1590
+ declare function foldSuiteFingerprints(db: DatabaseSync, handoff: CompletedRunHandoff, opts: FoldSuiteFingerprintsOptions): SuiteFingerprintsProjection;
1591
+ /**
1592
+ * The read port gate 3 consumes through composition (D-15).
1593
+ *
1594
+ * MINIMAL BY DESIGN. Conservatism lives in the normalizer (D-14), not here: this port answers
1595
+ * what the table holds and nothing more, so a later retune of "near-same elements normalize
1596
+ * equal" moves in exactly one place. There is no similarity verb here because there is no
1597
+ * similarity threshold to defend — gate 3's value test is set membership, spec §6.3 verbatim.
1598
+ */
1599
+ interface SuiteFingerprintIndex {
1600
+ /** The union D-14's novel-element test compares against. Empty table, empty union. */
1601
+ elementUnion(): Set<string>;
1602
+ /**
1603
+ * The identities that HAVE fingerprint rows, sorted. The consumer subtracts these from the
1604
+ * authored-test universe to derive the evidence-less set that rides D-16's text lane — which is
1605
+ * why this port reports coverage rather than trying to enumerate the suite, a fact it does not
1606
+ * own and could only guess at.
1607
+ */
1608
+ coveredTests(): Array<{
1609
+ filePath: string;
1610
+ testName: string;
1611
+ }>;
1612
+ /** One identity's elements in exercised order; `[]` for an identity with no rows. */
1613
+ elementsForTest(filePath: string, testName: string): string[];
1614
+ }
1615
+ interface SuiteFingerprintIndexOptions {
1616
+ /**
1617
+ * The partition to read. REQUIRED rather than optional: an unscoped union would compare a draft
1618
+ * against another product's suite, and "everything in the table" is never the question gate 3 is
1619
+ * asking.
1620
+ */
1621
+ product: string;
1622
+ }
1623
+ declare function createSuiteFingerprintIndex(db: DatabaseSync, opts: SuiteFingerprintIndexOptions): SuiteFingerprintIndex;
1624
+
1625
+ /**
1626
+ * PROVISIONAL last-N window for per-test outcome history (~10 pending ABRF-02 ranking
1627
+ * measurements). Entries are {buildOrdinal, passed} — ordinals only, wall-clock never.
1628
+ * Shared by readers and writers without pulling writer authority into the read closure.
1629
+ */
1630
+ declare const LAST_OUTCOMES_LIMIT = 10;
1631
+
1632
+ /**
1633
+ * Stamped into the curation-tail receipt's projection component. Bump when the fold
1634
+ * semantics or table shapes change so already-receipted runs refold on the next tail.
1635
+ *
1636
+ * HOW THAT REFOLD ACTUALLY HAPPENS (393 CR-02), because the receipt alone cannot do it. The tail
1637
+ * stores this number in each run's receipt, so a bump invalidates every receipt and the sweep
1638
+ * re-runs each historical run — but the per-run fold then answers 'already-applied' off the
1639
+ * durable marker and writes nothing. The stamp is therefore ALSO stored in the index itself, in
1640
+ * `run_outcome_projection_version`, and `reconcileRunOutcomesProjection` compares the two ONCE per
1641
+ * curation-tail sweep, before the per-run loop: a differing or absent stamp rebuilds the whole
1642
+ * projection family from the runs root through `rebuildRunOutcomes`, which clears the family
1643
+ * (marker included) and re-reads every `handoff.json` + `analysis.json` from disk. THAT is how an
1644
+ * already-receipted run contributes its elements. The per-run receipts then rewrite at the new
1645
+ * version through the same sweep. The family is derived, so the rebuild is the whole upgrade, and
1646
+ * it is a mechanism that runs, not an argument that it is unnecessary.
1647
+ *
1648
+ * NOT the case argued at `cli/src/commands/run.ts:1949` (390-08 D-01), where a bump was
1649
+ * deliberately declined: there the rebuild would have replayed stored handoffs carrying the OLD
1650
+ * identities, landing in the same mixed state the bump was meant to clear. Here the new table has
1651
+ * no historical rows at all, and every row it will hold is derivable from evidence the stored
1652
+ * handoffs already carry — so the refold produces the complete answer rather than a mixed one.
1653
+ */
1654
+ declare const RUN_OUTCOMES_PROJECTION_VERSION = 2;
1655
+
1656
+ /** Anchor extraction seam: the CLI tail passes its exercisedAnchors(step) walk. */
1657
+ type ExtractStepAnchors = (step: StepEvidence) => Anchor[];
1658
+ /**
1659
+ * Projection inputs contain no exclusion override. The durable handoff marker is the sole
1660
+ * authority for current-run folds, sibling backfill, and full rebuild alike (D-14 §6).
1661
+ */
1662
+ interface ProjectRunOutcomesOptions {
1663
+ extractAnchors: ExtractStepAnchors;
1664
+ /**
1665
+ * The run's persisted analysis artifact when one exists (390 D-09, the `ProjectCanaryOutcomes
1666
+ * Input.artifact` precedent). DATA, not a port: `foldRunOutcomes` and `rebuildRunOutcomes` each
1667
+ * load it from the run's own dir through the one shared loader and hand it here, so ONE
1668
+ * projection function with ONE signature serves both paths — a second signature only one path
1669
+ * calls is exactly how L12 rebuild parity breaks in silence.
1670
+ */
1671
+ artifact?: RunAnalysisArtifact;
1672
+ }
1673
+ /**
1674
+ * What a FAILED member contributes to the fingerprint-keyed recurrence window (390 D-07).
1675
+ *
1676
+ * The fingerprint is composed through the ONE exported vocabulary — `failingStepOf`/`errorTextOf`
1677
+ * over `exercisedAnchors`, keyed by `failureFingerprint` — so this window and the render-time
1678
+ * cluster presentation cannot disagree about what "the same failure" is. The verdict class is part
1679
+ * of the key by recipe, which is why it travels beside it rather than being re-derived downstream.
1680
+ */
1681
+ interface RunOutcomeFailureKey {
1682
+ fingerprint: string;
1683
+ verdictClass: VerdictClass;
1684
+ /**
1685
+ * WHICH pipeline authored the class (390-16). Not part of the fingerprint and not stored on
1686
+ * `run_fingerprint_windows` — it travels on the in-memory delta only, because the quarantine
1687
+ * ladder's floor-eligibility gate needs to know whether the class is a statement about THIS
1688
+ * TEST (an authored `analyze-run` verdict) or about the RUN (the deterministic floor, whose
1689
+ * `environment` arms are one health-probe result applied to every failing member).
1690
+ */
1691
+ producer: Verdict['producer'];
1692
+ }
1693
+ interface RunOutcomeTestDelta {
1694
+ filePath: string;
1695
+ testName: string;
1696
+ passed: boolean;
1697
+ heals: number;
1698
+ flake: boolean;
1699
+ /** canonicalJson(normalizeAnchor(...)) keys — byte-deterministic (ABRF-02 encoding edge). */
1700
+ anchorKeys: string[];
1701
+ /** Present for exactly the failed members — a pass contributes no window row (D-07). */
1702
+ failure?: RunOutcomeFailureKey;
1703
+ }
1704
+ interface RunOutcomesProjection {
1705
+ product: string;
1706
+ scope: ScopeKey;
1707
+ runId: string;
1708
+ buildOrdinal: number;
1709
+ excluded: boolean;
1710
+ tests: RunOutcomeTestDelta[];
1711
+ /**
1712
+ * The suite-fingerprint deltas for the SAME members (393 D-15). They travel on this projection
1713
+ * rather than being folded by a second pass so that one traversal of the handoff produces both
1714
+ * tables, one transaction commits them, and one applied-runs marker covers them — which is what
1715
+ * makes rebuild parity structural instead of asserted.
1716
+ */
1717
+ fingerprints: SuiteFingerprintTestDelta[];
1718
+ }
1719
+ type ApplyRunOutcomesResult = {
1720
+ kind: 'applied';
1721
+ tests: number;
1722
+ } | {
1723
+ kind: 'already-applied';
1724
+ tests: 0;
1725
+ };
1726
+ interface RebuildRunOutcomesOptions {
1727
+ extractAnchors: ExtractStepAnchors;
1728
+ }
1729
+ /**
1730
+ * What one rebuild did, with its two skip causes kept APART (393 round 7, CR-01-WIDENED).
1731
+ *
1732
+ * A single conflating counter answered "how many runs did not fold" and nothing an operator could
1733
+ * act on: a malformed `handoff.json` and an unloadable `analysis.json` are different files, written
1734
+ * by different producers, and fixed differently. There is deliberately no third counter for a
1735
+ * projection or write failure — that class is no longer a skip at all. It propagates out of
1736
+ * `rebuildRunOutcomes` and the rebuild produces no report.
1737
+ */
1738
+ interface RebuildRunOutcomesReport {
1739
+ folded: number;
1740
+ /** Runs whose `handoff.json` would not load or carries a pre-381 / structurally unsafe shape. */
1741
+ skippedHandoffs: number;
1742
+ /** Runs whose `handoff.json` loaded but whose `analysis.json` threw (the D-09 second document). */
1743
+ skippedArtifacts: number;
1744
+ }
1745
+ /**
1746
+ * What the fold needs before it may evaluate the quarantine detection ladder (390 D-08).
1747
+ *
1748
+ * Grouped and OPTIONAL on purpose: a caller that cannot supply a clock does
1749
+ * not get a silently empty evaluation, it gets `{kind: 'not-evaluated', reason: 'not-requested'}`
1750
+ * on the report. "Detection ran and found nothing" and "detection never ran" are different facts.
1751
+ */
1752
+ interface QuarantineDetectionOptions {
1753
+ /**
1754
+ * The ISO-8601 instant the evaluation is anchored to — the drain's own wall clock, read ONCE by
1755
+ * the caller. The fold and the rule below it stay clock-free (390-01's purity-by-argument), which
1756
+ * is what lets both expiry and the ladder be pinned one step either side.
1757
+ */
1758
+ now: string;
1759
+ }
1760
+ interface FoldRunOutcomesOptions {
1761
+ /** Absolute agent-qa-knowledge root hosting the derived index SQLite. */
1762
+ knowledgeRoot: string;
1763
+ handoff: CompletedRunHandoff;
1764
+ extractAnchors: ExtractStepAnchors;
1765
+ /**
1766
+ * The run's own directory (`.agent-qa/runs/<slug(runId)>/`). When supplied, the fold loads
1767
+ * `analysis.json` from it so the counters read RESOLVED verdicts (D-09). Absent — a caller that
1768
+ * has no run dir — folds the deterministic floor, byte-identically to pre-390.
1769
+ */
1770
+ runDir?: string;
1771
+ /** Present when the caller wants the D-08 detection step; absent skips it explicitly. */
1772
+ quarantine?: QuarantineDetectionOptions;
1773
+ }
1774
+ /**
1775
+ * What the fold's detection step did, as DATA (390 Pitfall 4).
1776
+ *
1777
+ * Every arm is a fact the caller can print. A ledger that could not be read is a value, not an
1778
+ * exception: a failure that vanished inside a catch is exactly how an `entered` event goes missing
1779
+ * in silence, and a missing `entered` event is a test that keeps failing builds with nobody told
1780
+ * why it was supposed to stop.
1781
+ */
1782
+ type QuarantineFoldOutcome = {
1783
+ kind: 'not-evaluated';
1784
+ reason: 'already-applied' | 'not-requested';
1785
+ }
1786
+ /** The committed ledger could not be read at all — detection refuses rather than guessing. */
1787
+ | {
1788
+ kind: 'ledger-unreadable';
1789
+ reason: string;
1790
+ } | {
1791
+ kind: 'evaluated';
1792
+ append: QuarantineAppendOutcome;
1793
+ events: readonly QuarantineLedgerEvent[];
1794
+ flagged: readonly QuarantineDetectionSighting[];
1795
+ escalatedNotQuarantined: readonly QuarantineDetectionSighting[];
1796
+ refusedLines: readonly RefusedQuarantineLine[];
1797
+ };
1798
+ type FoldRunOutcomesReport = ApplyRunOutcomesResult & {
1799
+ quarantine: QuarantineFoldOutcome;
1800
+ };
1801
+ /** Pure fold: identical (handoff, artifact) bytes yield identical row deltas. */
1802
+ declare function projectRunOutcomes(handoff: CompletedRunHandoff, opts: ProjectRunOutcomesOptions): RunOutcomesProjection;
1803
+ /**
1804
+ * Writer against the one index connection. Idempotent per product/runId: the applied-runs
1805
+ * marker commits in the same transaction as the counter rows, so a crash-rerun tail that
1806
+ * refolds an already-applied partition (receipt withheld mid-tail) changes nothing.
1807
+ */
1808
+ declare function applyRunOutcomes(db: DatabaseSync, projection: RunOutcomesProjection): ApplyRunOutcomesResult;
1809
+ /**
1810
+ * Full rebuild: scan and read every run dir under `runsRoot`, then clear the projection tables
1811
+ * and refold each loadable handoff. Reproduces tables byte-identical to the incremental fold
1812
+ * (L12); a rebuild over zero handoff dirs yields empty tables without error (ABRF-02
1813
+ * empty edge). BAD-BYTES load failures and pre-381 shapes are counted skips — including a run whose
1814
+ * `analysis.json` will not decode, which costs that run's contribution and nothing else (393 round
1815
+ * 6, CR-01). One undecodable artifact must never truncate a family this function has already
1816
+ * read; an errno-coded HOST fault on either document, on the runs root, or on any run dir
1817
+ * propagates BEFORE the family is deleted, so no sweep can stamp — or empty — a family the
1818
+ * filesystem prevented it from reading (418-12, CR-01-EXISTS).
1819
+ *
1820
+ * Phase A: no document is destroyed before every document was read — a host fault during reads
1821
+ * leaves the previous family and its stamp untouched (418-12, CR-02-DESTRUCTIVE, load-before-delete).
1822
+ * The list holds parsed handoffs for one sweep; the runs root is bounded by host run retention.
1823
+ *
1824
+ * Phase B is DELETE-FIRST, STAMP-LAST, and that order is the crash story rather than a style choice (393
1825
+ * CR-02). The version row is cleared with the family it describes and re-written only after the
1826
+ * refold loop has run to completion, so a crash anywhere in between leaves NO stamp — which
1827
+ * `reconcileRunOutcomesProjection` reads as a mismatch and rebuilds again. The projection
1828
+ * therefore self-heals from a partial rebuild instead of stamping a half-built family as current.
1829
+ */
1830
+ declare function rebuildRunOutcomes(db: DatabaseSync, runsRoot: string, opts: RebuildRunOutcomesOptions): RebuildRunOutcomesReport;
1831
+ /** What one reconcile did — a fact the caller can print, never a silent side effect. */
1832
+ type ReconcileRunOutcomesProjectionResult = {
1833
+ kind: 'current';
1834
+ } | {
1835
+ kind: 'rebuilt';
1836
+ report: RebuildRunOutcomesReport;
1837
+ } | {
1838
+ kind: 'skipped';
1839
+ reason: 'no-writer-authority';
1840
+ };
1841
+ interface ReconcileRunOutcomesProjectionOptions {
1842
+ /** The exact live writer lease owned by the caller; never inferred from an epoch alone. */
1843
+ authority?: {
1844
+ leaseId: string;
1845
+ epoch: number;
1846
+ };
1847
+ /** Absolute agent-qa-knowledge root hosting the derived index SQLite. */
1848
+ knowledgeRoot: string;
1849
+ /** `<agentQaRoot>/runs` — the scan root the rebuild refolds from. */
1850
+ runsRoot: string;
1851
+ extractAnchors: ExtractStepAnchors;
1852
+ }
1853
+ /**
1854
+ * THE version trigger: reconcile the stored fold semantics with the shipped ones (393 CR-02).
1855
+ *
1856
+ * `RUN_OUTCOMES_PROJECTION_VERSION`'s doc block has always promised that a bump makes
1857
+ * already-receipted runs refold. Nothing performed that refold: `applyRunOutcomes` short-circuits
1858
+ * on the `run_outcome_applied_runs` marker before it can reach the fingerprint write, and nothing
1859
+ * cleared the marker on a version change — so on any project that folded runs before a bump, the
1860
+ * new table stayed empty forever and gate 3's fingerprint lane never had data to decide on. This
1861
+ * function is what makes that promise true, and it composes machinery that already existed rather
1862
+ * than adding a parallel path: `rebuildRunOutcomes` already clears the family, already re-reads
1863
+ * every `handoff.json` + `analysis.json` through the one shared loader, and already reaches
1864
+ * `applySuiteFingerprints` for every fold.
1865
+ *
1866
+ * ABSENT MEANS MISMATCH, deliberately. Every pre-stamp index reads as absent, which is exactly the
1867
+ * population that needs to converge; so does an index whose file was deleted, which is exactly the
1868
+ * population that needs to re-heal. Treating absence as "probably fine" would have made the
1869
+ * upgrade path the one path this function does not serve.
1870
+ *
1871
+ * WHY A REBUILD RATHER THAN A PER-RUN VERSION COLUMN. Refolding runs in place through the
1872
+ * incremental path would add each run's counters a SECOND time — the fold accumulates, it does not
1873
+ * replace. The rebuild is the mechanism precisely because it clears before it refolds, which is
1874
+ * also why the derived family is safe to move this way at all (L12: every row here is reproducible
1875
+ * from run artifacts on disk).
1876
+ *
1877
+ * 418-12 WR-03: a destructive rebuild of a shared derived family is a leased act. A caller
1878
+ * without authority gets a typed skip before opening a writer; it stays stale until a leased
1879
+ * sweep. The version check and entire rebuild share the current-authority generation lock.
1880
+ */
1881
+ declare function reconcileRunOutcomesProjection(opts: ReconcileRunOutcomesProjectionOptions): ReconcileRunOutcomesProjectionResult;
1882
+ /**
1883
+ * Incremental fold wrapper for the post-verdict tail: opens the one index writer, applies
1884
+ * the projection, and closes. The tail invokes it inside the SAME crash boundary and the
1885
+ * SAME (extended) receipt as candidate/canary emit — never a second marker file.
1886
+ *
1887
+ * Detection hangs off the `'applied'` arm ONLY (D-08). `applyRunOutcomes` answers
1888
+ * `'already-applied'` from an early return that fires before its transaction even opens, so
1889
+ * "a refold never double-quarantines" is true by POSITION — there is no second idempotency
1890
+ * condition here to keep in sync with the marker.
1891
+ */
1892
+ declare function foldRunOutcomes(opts: FoldRunOutcomesOptions): FoldRunOutcomesReport;
1893
+
1894
+ /**
1895
+ * Read side of the run-outcome projection (389 ANLZ-03, RESEARCH Pitfall 2).
1896
+ *
1897
+ * `run_test_outcomes` is written by `foldRunOutcomes`/`applyRunOutcomes`; this module is
1898
+ * the one place that queries it. Strictly read-only: one prepared SELECT, no statement
1899
+ * that mutates a row, ever — the writer half stays whole in `run-outcomes.ts`.
1900
+ *
1901
+ * KEY LAW — stable test identity is `(product, filePath, testName)`, exactly the
1902
+ * projection's primary key and per-test-outcome.ts's identity rule. Callers joining from
1903
+ * a handoff use `PerTestOutcome.filePath` + `testName`, NEVER `testId` (a per-run
1904
+ * explicit id, not a durable identity).
1905
+ */
1906
+
1907
+ /** One window entry: ordinals only, wall-clock never (mirrors the writer's OutcomeEntry). */
1908
+ interface TestOutcomeEntry {
1909
+ buildOrdinal: number;
1910
+ passed: boolean;
1911
+ }
1912
+ interface TestOutcomeHistory {
1913
+ passes: number;
1914
+ failures: number;
1915
+ heals: number;
1916
+ flakeCount: number;
1917
+ /** Ordinal-ascending, at most LAST_OUTCOMES_LIMIT entries; [] when the stored JSON is malformed. */
1918
+ lastOutcomes: TestOutcomeEntry[];
1919
+ lastBuildOrdinal: number | undefined;
1920
+ }
1921
+ /** The projection's key, spelled the way the table is keyed. */
1922
+ interface TestOutcomeIdentity {
1923
+ product: string;
1924
+ filePath: string;
1925
+ testName: string;
1926
+ }
1927
+ /**
1928
+ * Answer one test's failure history from `run_test_outcomes`, or `undefined` when the
1929
+ * projection has never counted the test — typed absence, never an empty stand-in row.
1930
+ */
1931
+ declare function readTestOutcomeHistory(db: DatabaseSync, identity: TestOutcomeIdentity): TestOutcomeHistory | undefined;
1932
+ /**
1933
+ * Every identity the projection has counted inside ONE file, in one product (394-17 · WTST-05).
1934
+ *
1935
+ * The neighbours side of D-14's stability read: "which tests does this suite file already have run
1936
+ * evidence for". Keyed on the projection's own primary key, so `product` is REQUIRED for the same
1937
+ * reason `readTestOutcomeHistory` requires it — a query that dropped it would serve every
1938
+ * product's rows under one file path and report another application's tests as this target's.
1939
+ *
1940
+ * `test_name` ORDER, so a caller's message names its neighbours in a stable order across runs. An
1941
+ * empty array is the honest answer for a file the projection has never counted; the caller turns
1942
+ * that into a `no-history` posture rather than into a clean bill of health.
1943
+ */
1944
+ declare function listTestsInFile(db: DatabaseSync, scope: {
1945
+ product: string;
1946
+ filePath: string;
1947
+ }): string[];
1948
+ /**
1949
+ * D-02's forced discriminator channel: did the same step pass on the previous build?
1950
+ * Answers the entry with the greatest buildOrdinal STRICTLY below `currentBuildOrdinal`,
1951
+ * or `undefined` when no prior build is in the window (empty history, a single
1952
+ * current-build entry, or every entry at/above current) — prior-build honestly absent,
1953
+ * never faked from the current entry.
1954
+ */
1955
+ declare function priorBuildOutcome(history: TestOutcomeHistory, currentBuildOrdinal: number): TestOutcomeEntry | undefined;
1956
+ interface FailureRecurrence {
1957
+ /** Failed entries with buildOrdinal AT OR BELOW current — includes the current failure when recorded. */
1958
+ kth: number;
1959
+ /** Distinct buildOrdinals observed in lastOutcomes — honest about the ≤LAST_OUTCOMES_LIMIT window. */
1960
+ windowBuilds: number;
1961
+ }
1962
+ /**
1963
+ * Feeds "Kth failure in the last N builds", computed LIVE at render time (D-08) — counts
1964
+ * derived from the window on every call, never stored prose. 390 rebinds the same seam to
1965
+ * its fingerprint-window computation; this derivation is 389's binding, not the seam's
1966
+ * final owner. An empty history answers {kth: 0, windowBuilds: 0}: the renderer (plan 05)
1967
+ * turns zero-history into an explicit no-history clause, so absence stays typed.
1968
+ */
1969
+ declare function failureRecurrence(history: TestOutcomeHistory, currentBuildOrdinal: number): FailureRecurrence;
1970
+
1971
+ /**
1972
+ * PROVISIONAL D-13 superseded-generation retention depth. M3 may calibrate the
1973
+ * value against gate receipts, but it remains an internal constitution value
1974
+ * and never a runtime config key.
1975
+ */
1976
+ declare const GC_SUPERSEDED_RETAIN_K = 3;
1977
+ /**
1978
+ * D-13 gate seam. M2 always passes `{ kind: 'none' }`; M3 plugs real gate
1979
+ * receipt ordinals into the second variant with zero shape change.
1980
+ */
1981
+ type GateHorizon = {
1982
+ kind: 'none';
1983
+ } | {
1984
+ kind: 'ordinal';
1985
+ deepestGateOrdinal: number;
1986
+ };
1987
+ interface GcSummary {
1988
+ trimmedRecordIds: RecordId[];
1989
+ skipped: {
1990
+ unknownRef: number;
1991
+ withinK: number;
1992
+ userOrigin: number;
1993
+ horizon: number;
1994
+ };
1995
+ }
1996
+ interface RunKnowledgeGcInput {
1997
+ knowledgeRoot: string;
1998
+ horizon: GateHorizon;
1999
+ retainK?: number;
2000
+ }
2001
+ interface RunKnowledgeGcHooks {
2002
+ /** Test/chaos seam immediately before the authority check for one planned removal. */
2003
+ beforeGcRemoval?(recordId: RecordId): void;
2004
+ }
2005
+ /**
2006
+ * Trim only whole superseded bundle directories after constructing every D-13
2007
+ * exclusion set. No projection SQL exists here by design: the drain tail feeds
2008
+ * trimmedRecordIds straight into removeBundleProjectionRows, so the disposable
2009
+ * index heals in the same drain (WR-01).
2010
+ *
2011
+ * RATIFICATION: this typed in-memory summary, surfaced through drain diagnostics,
2012
+ * is the low-cost §9.2-style operational note. GC writes no persisted note artifact.
2013
+ */
2014
+ declare function runKnowledgeGc(input: RunKnowledgeGcInput, writerAuthority: WriterLeaseAuthority, hooks?: RunKnowledgeGcHooks): GcSummary;
2015
+
2016
+ /**
2017
+ * D-19 active-profile migration over the derived index.
2018
+ *
2019
+ * M2 keeps this library-only: Phase 378's drainer binds it to runtime
2020
+ * coordination, while Phase 382 owns any eventual CLI cutover surface. Rows
2021
+ * projected after the frozen snapshot take the benign lag direction: the flip
2022
+ * marks them pending so 375's unchanged backfill selector heals them.
2023
+ */
2024
+
2025
+ type MigrationOutcome = {
2026
+ flipped: true;
2027
+ profileKey: string;
2028
+ } | {
2029
+ flipped: false;
2030
+ missing: string[];
2031
+ };
2032
+ interface SetActiveEmbeddingProfileOptions {
2033
+ db: DatabaseSync;
2034
+ vec: VecCapability;
2035
+ target: {
2036
+ profile: EmbeddingProfile;
2037
+ embedder: EmbedderPort;
2038
+ };
2039
+ }
2040
+ /**
2041
+ * Stage every frozen record for a schema-owned target profile, then atomically
2042
+ * flip only when the exact frozen member set is complete.
2043
+ */
2044
+ declare function setActiveEmbeddingProfile(options: SetActiveEmbeddingProfileOptions): Promise<MigrationOutcome>;
2045
+
2046
+ type FloorAssetLoadErrorCode = 'package-root-not-found' | 'manifest-invalid' | 'asset-read-failed' | 'asset-size-mismatch' | 'digest-mismatch' | 'binary-invalid' | 'tokenizer-invalid' | 'tokenizer-runtime-unavailable';
2047
+ declare class FloorAssetLoadError extends Error {
2048
+ readonly code: FloorAssetLoadErrorCode;
2049
+ constructor(code: FloorAssetLoadErrorCode, message: string, options?: ErrorOptions);
2050
+ }
2051
+ type FloorEmbeddingErrorCode = 'empty-token-set' | 'token-id-out-of-range' | 'zero-vector';
2052
+ declare class FloorEmbeddingError extends Error {
2053
+ readonly code: FloorEmbeddingErrorCode;
2054
+ constructor(code: FloorEmbeddingErrorCode, message: string, options?: ErrorOptions);
2055
+ }
2056
+ interface FloorAssets {
2057
+ tokenizer: Tokenizer;
2058
+ vocabularySize: number;
2059
+ dimension: number;
2060
+ scales: Float32Array;
2061
+ weights: Int8Array;
2062
+ }
2063
+ interface FloorAssetLoadOptions {
2064
+ /** Test-only injection seam. Production callers resolve the committed package asset. */
2065
+ assetsDir?: string;
2066
+ /** Optional caller-owned token cap for bounded benchmark/runtime work. */
2067
+ maxTokens?: number;
2068
+ }
2069
+ declare function loadFloorAssets(options?: FloorAssetLoadOptions): Promise<FloorAssets>;
2070
+ /**
2071
+ * Construct the pure-TypeScript degradation-floor embedder without loading assets eagerly.
2072
+ *
2073
+ * The static floor profile has empty document and query prefixes, so both legs intentionally
2074
+ * share exactly the same token lookup, mean pooling, and L2-normalization implementation.
2075
+ */
2076
+ declare function createFloorEmbedder(options?: FloorAssetLoadOptions): EmbedderPort;
2077
+
2078
+ declare class NativeEmbeddingModuleLoadError extends Error {
2079
+ constructor(cause: unknown);
2080
+ }
2081
+ declare class NativeEmbeddingDimensionError extends RangeError {
2082
+ readonly actualDim: number;
2083
+ readonly expectedDim: number;
2084
+ constructor(actualDim: number, expectedDim: number);
2085
+ }
2086
+ declare function createNativeEmbedder(options: {
2087
+ profile: EmbeddingProfile;
2088
+ allowDownload: boolean;
2089
+ /** Optional process-wide token budget fixed by the first acquisition for this model identity. */
2090
+ maxTokens?: number;
2091
+ }): Promise<EmbedderPort>;
2092
+
2093
+ declare const MODEL_CACHE_DIR_ENV_VAR = "AGENT_QA_MODEL_CACHE_DIR";
2094
+ /**
2095
+ * Resolve the shared model cache used by every native embedder acquisition.
2096
+ *
2097
+ * D-11: one download per machine across all projects, with artifacts identified
2098
+ * by (model_id, revision). An explicit agent-qa override wins, then the XDG
2099
+ * cache root, with the conventional home cache as the final fallback.
2100
+ */
2101
+ declare function resolveModelCacheDir(): string;
2102
+
2103
+ /**
2104
+ * The embedder worker's half of the one process-identity vocabulary in this package.
2105
+ *
2106
+ * This is a separate module from `worker-registration.ts` on purpose. Registration reading and
2107
+ * socket probing are reachable from the read-only serving entry, and `reader-import-closure`
2108
+ * forbids that closure from crossing into `lease/`. The *signal decision* is a writer-side concern
2109
+ * that the reader never makes, so it lives here — which lets both kill paths in this package share
2110
+ * CR-04's single `resolveProcessIncarnation` definition instead of growing a second, subtly
2111
+ * different identity scheme.
2112
+ */
2113
+
2114
+ /**
2115
+ * Compare the registration's recorded incarnation against the pid's CURRENT occupant.
2116
+ *
2117
+ * CR-05: a schema-valid registration naming a live pid whose socket has stopped answering is not
2118
+ * proof that the worker is still there. Once the worker died, the kernel is free to hand its pid to
2119
+ * a stranger, and every decision keyed on that number alone targets whoever holds it now. The one
2120
+ * fact that separates them is assigned by the OS and cannot be forged from inside the file: when
2121
+ * the process at that pid started.
2122
+ *
2123
+ * `mismatch` means the registered worker is provably gone; `unverifiable` means we cannot say, and
2124
+ * that is a refusal rather than a default-allow. Neither may be signalled — and neither needs to
2125
+ * be, because generation-specific sockets (Plan 24) let a fresh worker take a clean address without
2126
+ * the old pid ever being touched.
2127
+ */
2128
+ declare function verifyEmbedderWorkerIncarnation(registration: EmbedderWorkerRegistration): EmbedderWorkerIncarnationVerdict;
2129
+
2130
+ declare const CURATION_QUEUE_SCHEMA_VERSION = 2;
2131
+ declare const curationArtifactRoleSchema: z.ZodEnum<{
2132
+ "dom-snapshot": "dom-snapshot";
2133
+ "ax-snapshot": "ax-snapshot";
2134
+ screenshot: "screenshot";
2135
+ "opaque-evidence": "opaque-evidence";
2136
+ }>;
2137
+ declare const curationArtifactDescriptorSchema: z.ZodObject<{
2138
+ ref: z.ZodType<ArtifactRef, unknown, z.core.$ZodTypeInternals<ArtifactRef, unknown>>;
2139
+ stepIndex: z.ZodNumber;
2140
+ role: z.ZodEnum<{
2141
+ "dom-snapshot": "dom-snapshot";
2142
+ "ax-snapshot": "ax-snapshot";
2143
+ screenshot: "screenshot";
2144
+ "opaque-evidence": "opaque-evidence";
2145
+ }>;
2146
+ originalName: z.ZodString;
2147
+ }, z.core.$strict>;
2148
+ declare const artifactInventoryEntrySchema: z.ZodObject<{
2149
+ role: z.ZodEnum<{
2150
+ "dom-snapshot": "dom-snapshot";
2151
+ "ax-snapshot": "ax-snapshot";
2152
+ screenshot: "screenshot";
2153
+ "opaque-evidence": "opaque-evidence";
2154
+ }>;
2155
+ originalName: z.ZodString;
2156
+ relativePath: z.ZodString;
2157
+ sha256: z.ZodString;
2158
+ bytes: z.ZodNumber;
2159
+ stepIndex: z.ZodNumber;
2160
+ }, z.core.$strict>;
2161
+ declare const curationCandidateProjectionSchema: z.ZodObject<{
2162
+ artifactDescriptors: z.ZodArray<z.ZodObject<{
2163
+ ref: z.ZodType<ArtifactRef, unknown, z.core.$ZodTypeInternals<ArtifactRef, unknown>>;
2164
+ stepIndex: z.ZodNumber;
2165
+ role: z.ZodEnum<{
2166
+ "dom-snapshot": "dom-snapshot";
2167
+ "ax-snapshot": "ax-snapshot";
2168
+ screenshot: "screenshot";
2169
+ "opaque-evidence": "opaque-evidence";
2170
+ }>;
2171
+ originalName: z.ZodString;
2172
+ }, z.core.$strict>>;
2173
+ emittedAt: z.ZodString;
2174
+ sourceRunId: z.ZodString;
2175
+ scope: z.ZodType<ScopeKey, unknown, z.core.$ZodTypeInternals<ScopeKey, unknown>>;
2176
+ buildOrdinal: z.ZodNumber;
2177
+ executionContext: z.ZodType<ExecutionContext, unknown, z.core.$ZodTypeInternals<ExecutionContext, unknown>>;
2178
+ kind: z.ZodType<BundleKind, unknown, z.core.$ZodTypeInternals<BundleKind, unknown>>;
2179
+ subtype: z.ZodOptional<z.ZodType<FactSubtype, unknown, z.core.$ZodTypeInternals<FactSubtype, unknown>>>;
2180
+ rootCauseClass: z.ZodOptional<z.ZodEnum<{
2181
+ environment: "environment";
2182
+ timeout: "timeout";
2183
+ flake: "flake";
2184
+ }>>;
2185
+ anchors: z.ZodArray<z.ZodType<_vostride_agent_qa_schema.Anchor, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.Anchor, unknown>>>;
2186
+ summarySeed: z.ZodString;
2187
+ bodySeed: z.ZodString;
2188
+ distilledObservations: z.ZodArray<z.ZodObject<{
2189
+ schemaVersion: z.ZodNumber;
2190
+ step: z.ZodObject<{
2191
+ naturalKey: z.ZodString;
2192
+ ordinal: z.ZodNumber;
2193
+ }, z.core.$strict>;
2194
+ platform: z.ZodOptional<z.ZodEnum<{
2195
+ web: "web";
2196
+ mobile: "mobile";
2197
+ }>>;
2198
+ test: z.ZodOptional<z.ZodObject<{
2199
+ id: z.ZodString;
2200
+ }, z.core.$strict>>;
2201
+ procedure: z.ZodOptional<z.ZodObject<{
2202
+ id: z.ZodString;
2203
+ }, z.core.$strict>>;
2204
+ flow: z.ZodOptional<z.ZodObject<{
2205
+ name: z.ZodString;
2206
+ precedes: z.ZodOptional<z.ZodString>;
2207
+ }, z.core.$strict>>;
2208
+ surface: z.ZodOptional<z.ZodObject<{
2209
+ kind: z.ZodEnum<{
2210
+ page: "page";
2211
+ screen: "screen";
2212
+ }>;
2213
+ naturalKey: z.ZodString;
2214
+ }, z.core.$strict>>;
2215
+ element: z.ZodOptional<z.ZodObject<{
2216
+ naturalKey: z.ZodString;
2217
+ }, z.core.$strict>>;
2218
+ locator: z.ZodOptional<z.ZodObject<{
2219
+ selector: z.ZodString;
2220
+ }, z.core.$strict>>;
2221
+ error: z.ZodOptional<z.ZodObject<{
2222
+ naturalKey: z.ZodString;
2223
+ rootCauseClass: z.ZodOptional<z.ZodEnum<{
2224
+ environment: "environment";
2225
+ timeout: "timeout";
2226
+ flake: "flake";
2227
+ }>>;
2228
+ }, z.core.$strict>>;
2229
+ observedAtOrdinal: z.ZodNumber;
2230
+ }, z.core.$strict>>;
2231
+ evidenceToken: z.ZodString;
2232
+ attributingObservationId: z.ZodString;
2233
+ }, z.core.$strict>;
2234
+ declare const curationCandidateAddressFieldsSchema: z.ZodObject<{
2235
+ artifactInventory: z.ZodArray<z.ZodObject<{
2236
+ role: z.ZodEnum<{
2237
+ "dom-snapshot": "dom-snapshot";
2238
+ "ax-snapshot": "ax-snapshot";
2239
+ screenshot: "screenshot";
2240
+ "opaque-evidence": "opaque-evidence";
2241
+ }>;
2242
+ originalName: z.ZodString;
2243
+ relativePath: z.ZodString;
2244
+ sha256: z.ZodString;
2245
+ bytes: z.ZodNumber;
2246
+ stepIndex: z.ZodNumber;
2247
+ }, z.core.$strict>>;
2248
+ emittedAt: z.ZodString;
2249
+ sourceRunId: z.ZodString;
2250
+ scope: z.ZodType<ScopeKey, unknown, z.core.$ZodTypeInternals<ScopeKey, unknown>>;
2251
+ buildOrdinal: z.ZodNumber;
2252
+ executionContext: z.ZodType<ExecutionContext, unknown, z.core.$ZodTypeInternals<ExecutionContext, unknown>>;
2253
+ kind: z.ZodType<BundleKind, unknown, z.core.$ZodTypeInternals<BundleKind, unknown>>;
2254
+ subtype: z.ZodOptional<z.ZodType<FactSubtype, unknown, z.core.$ZodTypeInternals<FactSubtype, unknown>>>;
2255
+ rootCauseClass: z.ZodOptional<z.ZodEnum<{
2256
+ environment: "environment";
2257
+ timeout: "timeout";
2258
+ flake: "flake";
2259
+ }>>;
2260
+ anchors: z.ZodArray<z.ZodType<_vostride_agent_qa_schema.Anchor, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.Anchor, unknown>>>;
2261
+ summarySeed: z.ZodString;
2262
+ bodySeed: z.ZodString;
2263
+ distilledObservations: z.ZodArray<z.ZodObject<{
2264
+ schemaVersion: z.ZodNumber;
2265
+ step: z.ZodObject<{
2266
+ naturalKey: z.ZodString;
2267
+ ordinal: z.ZodNumber;
2268
+ }, z.core.$strict>;
2269
+ platform: z.ZodOptional<z.ZodEnum<{
2270
+ web: "web";
2271
+ mobile: "mobile";
2272
+ }>>;
2273
+ test: z.ZodOptional<z.ZodObject<{
2274
+ id: z.ZodString;
2275
+ }, z.core.$strict>>;
2276
+ procedure: z.ZodOptional<z.ZodObject<{
2277
+ id: z.ZodString;
2278
+ }, z.core.$strict>>;
2279
+ flow: z.ZodOptional<z.ZodObject<{
2280
+ name: z.ZodString;
2281
+ precedes: z.ZodOptional<z.ZodString>;
2282
+ }, z.core.$strict>>;
2283
+ surface: z.ZodOptional<z.ZodObject<{
2284
+ kind: z.ZodEnum<{
2285
+ page: "page";
2286
+ screen: "screen";
2287
+ }>;
2288
+ naturalKey: z.ZodString;
2289
+ }, z.core.$strict>>;
2290
+ element: z.ZodOptional<z.ZodObject<{
2291
+ naturalKey: z.ZodString;
2292
+ }, z.core.$strict>>;
2293
+ locator: z.ZodOptional<z.ZodObject<{
2294
+ selector: z.ZodString;
2295
+ }, z.core.$strict>>;
2296
+ error: z.ZodOptional<z.ZodObject<{
2297
+ naturalKey: z.ZodString;
2298
+ rootCauseClass: z.ZodOptional<z.ZodEnum<{
2299
+ environment: "environment";
2300
+ timeout: "timeout";
2301
+ flake: "flake";
2302
+ }>>;
2303
+ }, z.core.$strict>>;
2304
+ observedAtOrdinal: z.ZodNumber;
2305
+ }, z.core.$strict>>;
2306
+ evidenceToken: z.ZodString;
2307
+ attributingObservationId: z.ZodString;
2308
+ schemaVersion: z.ZodLiteral<2>;
2309
+ }, z.core.$strict>;
2310
+ declare const curationCandidatePayloadSchema: z.ZodObject<{
2311
+ artifactInventory: z.ZodArray<z.ZodObject<{
2312
+ role: z.ZodEnum<{
2313
+ "dom-snapshot": "dom-snapshot";
2314
+ "ax-snapshot": "ax-snapshot";
2315
+ screenshot: "screenshot";
2316
+ "opaque-evidence": "opaque-evidence";
2317
+ }>;
2318
+ originalName: z.ZodString;
2319
+ relativePath: z.ZodString;
2320
+ sha256: z.ZodString;
2321
+ bytes: z.ZodNumber;
2322
+ stepIndex: z.ZodNumber;
2323
+ }, z.core.$strict>>;
2324
+ emittedAt: z.ZodString;
2325
+ sourceRunId: z.ZodString;
2326
+ scope: z.ZodType<ScopeKey, unknown, z.core.$ZodTypeInternals<ScopeKey, unknown>>;
2327
+ buildOrdinal: z.ZodNumber;
2328
+ executionContext: z.ZodType<ExecutionContext, unknown, z.core.$ZodTypeInternals<ExecutionContext, unknown>>;
2329
+ kind: z.ZodType<BundleKind, unknown, z.core.$ZodTypeInternals<BundleKind, unknown>>;
2330
+ subtype: z.ZodOptional<z.ZodType<FactSubtype, unknown, z.core.$ZodTypeInternals<FactSubtype, unknown>>>;
2331
+ rootCauseClass: z.ZodOptional<z.ZodEnum<{
2332
+ environment: "environment";
2333
+ timeout: "timeout";
2334
+ flake: "flake";
2335
+ }>>;
2336
+ anchors: z.ZodArray<z.ZodType<_vostride_agent_qa_schema.Anchor, unknown, z.core.$ZodTypeInternals<_vostride_agent_qa_schema.Anchor, unknown>>>;
2337
+ summarySeed: z.ZodString;
2338
+ bodySeed: z.ZodString;
2339
+ distilledObservations: z.ZodArray<z.ZodObject<{
2340
+ schemaVersion: z.ZodNumber;
2341
+ step: z.ZodObject<{
2342
+ naturalKey: z.ZodString;
2343
+ ordinal: z.ZodNumber;
2344
+ }, z.core.$strict>;
2345
+ platform: z.ZodOptional<z.ZodEnum<{
2346
+ web: "web";
2347
+ mobile: "mobile";
2348
+ }>>;
2349
+ test: z.ZodOptional<z.ZodObject<{
2350
+ id: z.ZodString;
2351
+ }, z.core.$strict>>;
2352
+ procedure: z.ZodOptional<z.ZodObject<{
2353
+ id: z.ZodString;
2354
+ }, z.core.$strict>>;
2355
+ flow: z.ZodOptional<z.ZodObject<{
2356
+ name: z.ZodString;
2357
+ precedes: z.ZodOptional<z.ZodString>;
2358
+ }, z.core.$strict>>;
2359
+ surface: z.ZodOptional<z.ZodObject<{
2360
+ kind: z.ZodEnum<{
2361
+ page: "page";
2362
+ screen: "screen";
2363
+ }>;
2364
+ naturalKey: z.ZodString;
2365
+ }, z.core.$strict>>;
2366
+ element: z.ZodOptional<z.ZodObject<{
2367
+ naturalKey: z.ZodString;
2368
+ }, z.core.$strict>>;
2369
+ locator: z.ZodOptional<z.ZodObject<{
2370
+ selector: z.ZodString;
2371
+ }, z.core.$strict>>;
2372
+ error: z.ZodOptional<z.ZodObject<{
2373
+ naturalKey: z.ZodString;
2374
+ rootCauseClass: z.ZodOptional<z.ZodEnum<{
2375
+ environment: "environment";
2376
+ timeout: "timeout";
2377
+ flake: "flake";
2378
+ }>>;
2379
+ }, z.core.$strict>>;
2380
+ observedAtOrdinal: z.ZodNumber;
2381
+ }, z.core.$strict>>;
2382
+ evidenceToken: z.ZodString;
2383
+ attributingObservationId: z.ZodString;
2384
+ schemaVersion: z.ZodLiteral<2>;
2385
+ candidateId: z.ZodString;
2386
+ }, z.core.$strict>;
2387
+ type CurationCandidateAddressFields = z.infer<typeof curationCandidateAddressFieldsSchema>;
2388
+ type CurationCandidatePayload = z.infer<typeof curationCandidatePayloadSchema>;
2389
+ type CurationCandidateArtifact = z.infer<typeof artifactInventoryEntrySchema>;
2390
+ type CurationArtifactRole = z.infer<typeof curationArtifactRoleSchema>;
2391
+ type CurationArtifactDescriptor = z.infer<typeof curationArtifactDescriptorSchema>;
2392
+ type CurationCandidateProjection = z.infer<typeof curationCandidateProjectionSchema>;
2393
+ type BuildCurationCandidateAddressFieldsInput = CurationCandidateAddressFields;
2394
+ /** Constructs strict version-2 fields with descriptor metadata for every copied artifact. */
2395
+ declare function buildCurationCandidateAddressFields(input: BuildCurationCandidateAddressFieldsInput): CurationCandidateAddressFields;
2396
+ /**
2397
+ * Hashes every payload field except derived candidateId and diagnostic emittedAt. Identity covers
2398
+ * schema/run/scope/ordinal/execution context, candidate kind/subtype, anchors and seeds, distilled
2399
+ * observations, evidence attribution, and the complete artifact inventory.
2400
+ */
2401
+ declare function candidateContentAddress(fields: CurationCandidateAddressFields): string;
2402
+
2403
+ /** Gitignored durable curation transport root; provisional until M2 field evidence retunes it. */
2404
+ declare const QUEUE_DIR = "memory/queue";
2405
+ /** Pending candidates visible to a drainer. */
2406
+ declare const QUEUED_SUBDIR = "queued";
2407
+ /** Lease-partitioned candidates atomically removed from the pending set. */
2408
+ declare const CLAIMED_SUBDIR = "claimed";
2409
+ /** Durable lifecycle checkpoints keyed by candidate content address. */
2410
+ declare const JOBS_SUBDIR = "jobs";
2411
+ /** Provisional fail-closed disk-growth limit for one knowledge scope. */
2412
+ declare const QUEUE_MAX_JOBS_PER_SCOPE = 200;
2413
+ /** Provisional fail-closed candidate emission limit for one source run. */
2414
+ declare const QUEUE_MAX_EMITS_PER_RUN = 50;
2415
+ declare function queueRoot(knowledgeRoot: string): string;
2416
+ declare function queuedRoot(knowledgeRoot: string): string;
2417
+ declare function claimedRoot(knowledgeRoot: string): string;
2418
+ declare function jobsRoot(knowledgeRoot: string): string;
2419
+ declare function queueStagingRoot(knowledgeRoot: string): string;
2420
+ declare function queuedCandidateDir(knowledgeRoot: string, candidateId: string): string;
2421
+ declare function claimedLeaseDir(knowledgeRoot: string, leaseId: string): string;
2422
+ declare function claimedCandidateDir(knowledgeRoot: string, leaseId: string, candidateId: string): string;
2423
+ declare function jobStatePath(knowledgeRoot: string, candidateId: string): string;
2424
+
2425
+ interface CurationProjectionArtifactSource {
2426
+ /** Binds one raw projection descriptor by its ArtifactRef id. */
2427
+ artifactId: string;
2428
+ /** Absolute source outside the queue's durable projection. */
2429
+ sourcePath: string;
2430
+ }
2431
+ interface EnqueueCurationCandidateProjectionOptions {
2432
+ knowledgeRoot: string;
2433
+ projection: CurationCandidateProjection;
2434
+ artifactSources: readonly CurationProjectionArtifactSource[];
2435
+ mask?: MaskFn;
2436
+ }
2437
+ type CurationEnqueueRefusalReason = 'invalid-payload' | 'artifact-inventory-mismatch' | 'artifact-missing' | 'artifact-sha-mismatch' | 'artifact-byte-length-mismatch' | 'artifact-step-cap' | 'artifact-run-cap';
2438
+ type EnqueueCurationCandidateProjectionOutcome = {
2439
+ kind: 'enqueued';
2440
+ candidateId: string;
2441
+ } | {
2442
+ kind: 'coalesced';
2443
+ candidateId: string;
2444
+ } | {
2445
+ kind: 'cap-exceeded';
2446
+ candidateId: string;
2447
+ } | {
2448
+ kind: 'refused';
2449
+ reason: CurationEnqueueRefusalReason;
2450
+ };
2451
+ type ReadCurationCandidatePayloadOutcome = {
2452
+ kind: 'absent';
2453
+ present: false;
2454
+ } | {
2455
+ kind: 'parsed';
2456
+ present: true;
2457
+ payload: CurationCandidatePayload;
2458
+ } | {
2459
+ kind: 'malformed';
2460
+ present: true;
2461
+ };
2462
+ interface ClaimNextJobOptions {
2463
+ knowledgeRoot: string;
2464
+ leaseId: string;
2465
+ authority: CurrentWriterAuthority;
2466
+ }
2467
+ interface ClaimedCurationJob {
2468
+ kind: 'claimed';
2469
+ candidateId: string;
2470
+ leaseId: string;
2471
+ payload: CurationCandidatePayload;
2472
+ candidateDir: string;
2473
+ payloadPath: string;
2474
+ artifactsDir: string;
2475
+ }
2476
+ interface MalformedClaimedCurationJob {
2477
+ kind: 'malformed';
2478
+ candidateId: string;
2479
+ leaseId: string;
2480
+ candidateDir: string;
2481
+ payloadPath: string;
2482
+ artifactsDir: string;
2483
+ payloadPresent: true;
2484
+ }
2485
+ type ClaimNextJobOutcome = ClaimedCurationJob | MalformedClaimedCurationJob;
2486
+ interface RequeueClaimedOptions {
2487
+ knowledgeRoot: string;
2488
+ leaseId: string;
2489
+ authority: CurrentWriterAuthority;
2490
+ }
2491
+ interface InspectDrainableCurationWorkOptions {
2492
+ readonly knowledgeRoot: string;
2493
+ readonly currentLeaseId: string;
2494
+ }
2495
+ interface DrainableCurationWork {
2496
+ readonly queuedCandidateIds: readonly string[];
2497
+ readonly recoverableClaimedCandidateIds: readonly string[];
2498
+ }
2499
+ interface CountRemainingCurationWorkOptions {
2500
+ /** Diagnostic identity only. The current claim remains included in the count. */
2501
+ readonly currentLeaseId?: string;
2502
+ }
2503
+ interface CurationRemainingWork {
2504
+ readonly queued: number;
2505
+ readonly claimedNonterminal: number;
2506
+ readonly quarantined: number;
2507
+ }
2508
+ type CurationQuarantineReason = 'malformed-payload' | 'malformed-job-state' | 'malformed-authored-checkpoint';
2509
+ interface QuarantineClaimedOptions {
2510
+ readonly knowledgeRoot: string;
2511
+ readonly leaseId: string;
2512
+ readonly candidateId: string;
2513
+ readonly authority: CurrentWriterAuthority;
2514
+ readonly reason: CurationQuarantineReason;
2515
+ }
2516
+ /** Return deterministic candidate-id order and ignore non-contract directory names. */
2517
+ declare function listQueuedCandidates(knowledgeRoot: string): string[];
2518
+ /** Visibility-only count over the same durable pending layout consumed by claimNextJob. */
2519
+ declare function countPendingCurationJobs(knowledgeRoot: string): number;
2520
+ /**
2521
+ * Count every durable unit that prevents the queue from being complete.
2522
+ *
2523
+ * Unlike recovery visibility this includes the current writer's claim: a process can crash
2524
+ * after its last pending rename, so excluding that claim would produce a false zero.
2525
+ */
2526
+ declare function countRemainingCurationWork(knowledgeRoot: string, _options?: CountRemainingCurationWorkOptions): CurationRemainingWork;
2527
+ /** Return deterministic active/recoverable lease directory order. */
2528
+ declare function listClaimedLeases(knowledgeRoot: string): string[];
2529
+ /**
2530
+ * Observe work the current writer can reach through the canonical reconciliation path.
2531
+ *
2532
+ * This deliberately performs no recovery itself. The current lease is excluded by the same
2533
+ * identity rule as reconcileOnDrainStart; drainQueue remains the only owner of requeue mutation.
2534
+ */
2535
+ declare function inspectDrainableCurationWork(options: InspectDrainableCurationWorkOptions): DrainableCurationWork;
2536
+ declare function readCurationCandidatePayload(candidateDir: string): ReadCurationCandidatePayloadOutcome;
2537
+ /** Atomically claim the lexicographically first queued content address. */
2538
+ declare function claimNextJob(opts: ClaimNextJobOptions): ClaimNextJobOutcome | undefined;
2539
+ /** Reclaim one dead lease without discarding an already-authored deterministic checkpoint. */
2540
+ declare function requeueClaimed(opts: RequeueClaimedOptions): string[];
2541
+ /**
2542
+ * Fenced terminal disposition for a claimed job whose durable inputs cannot be interpreted.
2543
+ * The claimed payload, optional lifecycle bytes, and reason document move as one directory.
2544
+ */
2545
+ declare function quarantineClaimed(opts: QuarantineClaimedOptions): string;
2546
+ /**
2547
+ * Copies the projection's artifacts through the declared mask before the candidate content
2548
+ * address or any queue-visible directory exists.
2549
+ */
2550
+ declare function enqueueCurationCandidateProjection(opts: EnqueueCurationCandidateProjectionOptions): EnqueueCurationCandidateProjectionOutcome;
2551
+ declare const enqueueCurationCandidate: typeof enqueueCurationCandidateProjection;
2552
+
2553
+ declare const CURATION_JOB_STATE_SCHEMA_VERSION = 2;
2554
+ type CurationJobLifecycle = 'queued' | 'authored' | 'bundling' | 'committing' | 'done';
2555
+ declare const curationJobStateSchema: z.ZodObject<{
2556
+ schemaVersion: z.ZodLiteral<2>;
2557
+ candidateId: z.ZodString;
2558
+ lifecycle: z.ZodType<CurationJobLifecycle, unknown, z.core.$ZodTypeInternals<CurationJobLifecycle, unknown>>;
2559
+ embedCrashCount: z.ZodDefault<z.ZodNumber>;
2560
+ authored: z.ZodOptional<z.ZodUnknown>;
2561
+ updatedAt: z.ZodString;
2562
+ }, z.core.$strict>;
2563
+ type CurationJobState = z.infer<typeof curationJobStateSchema>;
2564
+ interface WriteJobStateOptions {
2565
+ knowledgeRoot: string;
2566
+ candidateId: string;
2567
+ lifecycle: CurationJobLifecycle;
2568
+ authority: CurrentWriterAuthority;
2569
+ embedCrashCount?: number;
2570
+ authored?: unknown;
2571
+ }
2572
+ type ReadJobStateOutcome = {
2573
+ kind: 'absent';
2574
+ present: false;
2575
+ } | {
2576
+ kind: 'parsed';
2577
+ present: true;
2578
+ state: CurationJobState;
2579
+ } | {
2580
+ kind: 'malformed';
2581
+ present: true;
2582
+ };
2583
+ interface PersistAuthoredCheckpointOptions {
2584
+ knowledgeRoot: string;
2585
+ candidateId: string;
2586
+ authority: CurrentWriterAuthority;
2587
+ authored: unknown;
2588
+ }
2589
+ type PersistAuthoredCheckpointOutcome = {
2590
+ kind: 'persisted';
2591
+ state: CurationJobState;
2592
+ } | {
2593
+ kind: 'malformed-state';
2594
+ present: true;
2595
+ };
2596
+ /** Read a strict lifecycle checkpoint; corrupt bytes remain a typed present signal. */
2597
+ declare function readJobState(knowledgeRoot: string, candidateId: string): ReadJobStateOutcome;
2598
+ declare function writeJobState(opts: WriteJobStateOptions): CurationJobState;
2599
+ /** Persist the validated-later authored value before any deterministic bundle mutation begins. */
2600
+ declare function persistAuthoredCheckpoint(opts: PersistAuthoredCheckpointOptions): PersistAuthoredCheckpointOutcome;
2601
+
2602
+ declare const COMMIT_INTENT_SCHEMA_VERSION = 2;
2603
+ declare const commitIntentSchema: z.ZodObject<{
2604
+ schemaVersion: z.ZodLiteral<2>;
2605
+ candidateId: z.ZodString;
2606
+ bundleId: z.ZodString;
2607
+ deltaKind: z.ZodEnum<{
2608
+ Add: "Add";
2609
+ Confirm: "Confirm";
2610
+ Supersede: "Supersede";
2611
+ Deprecate: "Deprecate";
2612
+ MintUnknown: "MintUnknown";
2613
+ Noop: "Noop";
2614
+ }>;
2615
+ targetRecordId: z.ZodOptional<z.ZodString>;
2616
+ epoch: z.ZodNumber;
2617
+ observation: z.ZodOptional<z.ZodObject<{
2618
+ effects: z.ZodArray<z.ZodObject<{
2619
+ recordId: z.ZodString;
2620
+ product: z.ZodString;
2621
+ change: z.ZodEnum<{
2622
+ confirmed: "confirmed";
2623
+ superseded: "superseded";
2624
+ deprecated: "deprecated";
2625
+ added: "added";
2626
+ }>;
2627
+ atOrdinal: z.ZodNumber;
2628
+ evidenceToken: z.ZodString;
2629
+ byRecordId: z.ZodOptional<z.ZodString>;
2630
+ }, z.core.$strict>>;
2631
+ sourceRunId: z.ZodString;
2632
+ observationId: z.ZodString;
2633
+ originalDeltaKind: z.ZodEnum<{
2634
+ Add: "Add";
2635
+ Confirm: "Confirm";
2636
+ Supersede: "Supersede";
2637
+ Deprecate: "Deprecate";
2638
+ MintUnknown: "MintUnknown";
2639
+ Noop: "Noop";
2640
+ }>;
2641
+ appliedDeltaKind: z.ZodEnum<{
2642
+ Add: "Add";
2643
+ Confirm: "Confirm";
2644
+ Supersede: "Supersede";
2645
+ Deprecate: "Deprecate";
2646
+ MintUnknown: "MintUnknown";
2647
+ Noop: "Noop";
2648
+ }>;
2649
+ disposition: z.ZodEnum<{
2650
+ apply: "apply";
2651
+ downgraded: "downgraded";
2652
+ }>;
2653
+ downgradeReason: z.ZodOptional<z.ZodString>;
2654
+ schemaVersion: z.ZodLiteral<2>;
2655
+ }, z.core.$strict>>;
2656
+ }, z.core.$strict>;
2657
+ type CommitIntent = z.infer<typeof commitIntentSchema>;
2658
+
2659
+ declare const AUTHORED_CHECKPOINT_SCHEMA_VERSION = 2;
2660
+ /** The deterministic author-facing projection of one durable queue payload. */
2661
+ type CurationJobView = Pick<CurationCandidatePayload, 'candidateId' | 'kind' | 'subtype' | 'rootCauseClass' | 'anchors' | 'summarySeed' | 'bodySeed' | 'distilledObservations' | 'scope' | 'buildOrdinal' | 'attributingObservationId' | 'executionContext'>;
2662
+ /** One retrieval hit as shown to the author through a small integer alias. */
2663
+ interface NeighborRecordBase {
2664
+ alias: number;
2665
+ recordId: RecordId;
2666
+ summary: string;
2667
+ anchors: Anchor[];
2668
+ observedStatus: string;
2669
+ observedOrdinal: number;
2670
+ }
2671
+ type NonFactNeighborRecord = {
2672
+ [Kind in Exclude<BundleKind, 'fact'>]: {
2673
+ kind: Kind;
2674
+ subtype?: never;
2675
+ };
2676
+ }[Exclude<BundleKind, 'fact'>];
2677
+ type NeighborRecord = NeighborRecordBase & ({
2678
+ kind: 'fact';
2679
+ subtype: FactSubtype;
2680
+ } | NonFactNeighborRecord);
2681
+ /**
2682
+ * One authored outcome may carry multiple deltas (D-02): a procedure-extraction job can yield
2683
+ * whole-flow AND sub-flow candidates from one trajectory. Fact/caution authoring produces a
2684
+ * one-element array. The array is never empty.
2685
+ */
2686
+ type AuthorOutcome = {
2687
+ kind: 'authored';
2688
+ deltas: Delta[];
2689
+ authorship: CurationAuthorship;
2690
+ } | {
2691
+ kind: 'fallback-required';
2692
+ reason: CurationFallbackReason;
2693
+ };
2694
+ /** Provider-neutral author seam injected by the CLI composition root. */
2695
+ type CuratorAuthor = (job: CurationJobView, neighbors: NeighborRecord[]) => Promise<AuthorOutcome>;
2696
+ /**
2697
+ * Caller-owned disposition authority. Phase 379 may extend this profile with its deterministic
2698
+ * born-active drift-supersede carve-out without changing the author or checkpoint contracts.
2699
+ */
2700
+ interface DispositionProfile {
2701
+ mode: 'drain-to-active' | 'propose-only';
2702
+ }
2703
+ /**
2704
+ * Resolve automatic curation authority from the run's already-captured execution context.
2705
+ * Local work applies directly (432 D-08); CI and cloud stay proposed for the rolling-PR flow.
2706
+ */
2707
+ declare function resolveDispositionProfile(executionContext: ExecutionContext): DispositionProfile;
2708
+ type CheckpointBundleDraft = BundleDraft & {
2709
+ needsEnrichment?: true;
2710
+ };
2711
+ /**
2712
+ * The SHARED structural boundary for a checkpoint. Neighbor membership and CAS freshness are
2713
+ * deliberately disposition checks, so this schema validates structure without applying them.
2714
+ * D-02: the checkpoint carries the authored outcome's FULL delta array — one durable authoring
2715
+ * checkpoint per job, disposed delta-by-delta through the commit boundary.
2716
+ *
2717
+ * Deliberately origin-WIDE, and that is not an oversight — see
2718
+ * `AUTHORED_CHECKPOINT_ORIGIN_ADMISSION` below. `AuthoredCheckpoint`'s inferred delta type is
2719
+ * the type every in-process checkpoint carries, including the user-origin deltas the review
2720
+ * verbs commit and the system-origin deltas rollback and the canary commit. Origin admission is
2721
+ * a property of the DRAIN's parse boundary, not of the checkpoint shape.
2722
+ */
2723
+ declare const authoredCheckpointSchema: z.ZodObject<{
2724
+ schemaVersion: z.ZodLiteral<2>;
2725
+ deltas: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
2726
+ bundle: z.ZodType<CheckpointBundleDraft, unknown, z.core.$ZodTypeInternals<CheckpointBundleDraft, unknown>>;
2727
+ origin: z.ZodEnum<{
2728
+ user: "user";
2729
+ import: "import";
2730
+ agent: "agent";
2731
+ system: "system";
2732
+ }>;
2733
+ scope: z.ZodType<ScopeKey, unknown, z.core.$ZodTypeInternals<ScopeKey, unknown>>;
2734
+ citedIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>>;
2735
+ precondition: z.ZodOptional<z.ZodArray<z.ZodType<CompareAndSet, unknown, z.core.$ZodTypeInternals<CompareAndSet, unknown>>>>;
2736
+ authorship: z.ZodOptional<z.ZodType<CurationAuthorship, unknown, z.core.$ZodTypeInternals<CurationAuthorship, unknown>>>;
2737
+ kind: z.ZodLiteral<"Add">;
2738
+ }, z.core.$strict>, z.ZodObject<{
2739
+ targetId: z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>;
2740
+ evidenceToken: z.ZodPipe<z.ZodString, z.ZodTransform<_vostride_agent_qa_schema.EvidenceToken, string>>;
2741
+ origin: z.ZodEnum<{
2742
+ user: "user";
2743
+ import: "import";
2744
+ agent: "agent";
2745
+ system: "system";
2746
+ }>;
2747
+ scope: z.ZodType<ScopeKey, unknown, z.core.$ZodTypeInternals<ScopeKey, unknown>>;
2748
+ citedIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>>;
2749
+ precondition: z.ZodOptional<z.ZodArray<z.ZodType<CompareAndSet, unknown, z.core.$ZodTypeInternals<CompareAndSet, unknown>>>>;
2750
+ authorship: z.ZodOptional<z.ZodType<CurationAuthorship, unknown, z.core.$ZodTypeInternals<CurationAuthorship, unknown>>>;
2751
+ kind: z.ZodLiteral<"Confirm">;
2752
+ }, z.core.$strict>, z.ZodObject<{
2753
+ victimId: z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>;
2754
+ newBundle: z.ZodType<CheckpointBundleDraft, unknown, z.core.$ZodTypeInternals<CheckpointBundleDraft, unknown>>;
2755
+ evidenceToken: z.ZodPipe<z.ZodString, z.ZodTransform<_vostride_agent_qa_schema.EvidenceToken, string>>;
2756
+ origin: z.ZodEnum<{
2757
+ user: "user";
2758
+ import: "import";
2759
+ agent: "agent";
2760
+ system: "system";
2761
+ }>;
2762
+ scope: z.ZodType<ScopeKey, unknown, z.core.$ZodTypeInternals<ScopeKey, unknown>>;
2763
+ citedIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>>;
2764
+ precondition: z.ZodOptional<z.ZodArray<z.ZodType<CompareAndSet, unknown, z.core.$ZodTypeInternals<CompareAndSet, unknown>>>>;
2765
+ authorship: z.ZodOptional<z.ZodType<CurationAuthorship, unknown, z.core.$ZodTypeInternals<CurationAuthorship, unknown>>>;
2766
+ kind: z.ZodLiteral<"Supersede">;
2767
+ }, z.core.$strict>, z.ZodObject<{
2768
+ targetId: z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>;
2769
+ reason: z.ZodString;
2770
+ origin: z.ZodEnum<{
2771
+ user: "user";
2772
+ import: "import";
2773
+ agent: "agent";
2774
+ system: "system";
2775
+ }>;
2776
+ scope: z.ZodType<ScopeKey, unknown, z.core.$ZodTypeInternals<ScopeKey, unknown>>;
2777
+ citedIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>>;
2778
+ precondition: z.ZodOptional<z.ZodArray<z.ZodType<CompareAndSet, unknown, z.core.$ZodTypeInternals<CompareAndSet, unknown>>>>;
2779
+ authorship: z.ZodOptional<z.ZodType<CurationAuthorship, unknown, z.core.$ZodTypeInternals<CurationAuthorship, unknown>>>;
2780
+ kind: z.ZodLiteral<"Deprecate">;
2781
+ }, z.core.$strict>, z.ZodObject<{
2782
+ conflict: z.ZodObject<{
2783
+ recordIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>>;
2784
+ kind: z.ZodEnum<{
2785
+ contradiction: "contradiction";
2786
+ drift: "drift";
2787
+ clarification: "clarification";
2788
+ }>;
2789
+ }, z.core.$strict>;
2790
+ origin: z.ZodEnum<{
2791
+ user: "user";
2792
+ import: "import";
2793
+ agent: "agent";
2794
+ system: "system";
2795
+ }>;
2796
+ scope: z.ZodType<ScopeKey, unknown, z.core.$ZodTypeInternals<ScopeKey, unknown>>;
2797
+ citedIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>>;
2798
+ precondition: z.ZodOptional<z.ZodArray<z.ZodType<CompareAndSet, unknown, z.core.$ZodTypeInternals<CompareAndSet, unknown>>>>;
2799
+ authorship: z.ZodOptional<z.ZodType<CurationAuthorship, unknown, z.core.$ZodTypeInternals<CurationAuthorship, unknown>>>;
2800
+ kind: z.ZodLiteral<"MintUnknown">;
2801
+ }, z.core.$strict>, z.ZodObject<{
2802
+ origin: z.ZodEnum<{
2803
+ user: "user";
2804
+ import: "import";
2805
+ agent: "agent";
2806
+ system: "system";
2807
+ }>;
2808
+ scope: z.ZodType<ScopeKey, unknown, z.core.$ZodTypeInternals<ScopeKey, unknown>>;
2809
+ citedIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>>;
2810
+ precondition: z.ZodOptional<z.ZodArray<z.ZodType<CompareAndSet, unknown, z.core.$ZodTypeInternals<CompareAndSet, unknown>>>>;
2811
+ authorship: z.ZodOptional<z.ZodType<CurationAuthorship, unknown, z.core.$ZodTypeInternals<CurationAuthorship, unknown>>>;
2812
+ kind: z.ZodLiteral<"Noop">;
2813
+ }, z.core.$strict>], "kind">>;
2814
+ neighborIds: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<RecordId, string>>>;
2815
+ preconditions: z.ZodArray<z.ZodType<CompareAndSet, unknown, z.core.$ZodTypeInternals<CompareAndSet, unknown>>>;
2816
+ authorship: z.ZodType<CurationAuthorship, unknown, z.core.$ZodTypeInternals<CurationAuthorship, unknown>>;
2817
+ }, z.core.$strict>;
2818
+ type AuthoredCheckpoint = z.infer<typeof authoredCheckpointSchema>;
2819
+ /**
2820
+ * Parse authored bytes at the drain's boundary — both when wrapping fresh author output
2821
+ * (drain.ts:537) and when replaying crash-recovered state (drain.ts:992).
2822
+ *
2823
+ * BOTH sites, deliberately. Guarding only the recovery read would leave the injected author seam
2824
+ * able to put a non-agent origin into the durable slot in the first place, which is the same
2825
+ * defect one step upstream: a delta the store attributes to a person, authored by a model, that
2826
+ * the autonomy floor would then let tombstone standing knowledge.
2827
+ */
2828
+ declare function parseAuthoredCheckpoint(value: unknown): AuthoredCheckpoint;
2829
+ /**
2830
+ * The per-delta commit unit sliced from a widened checkpoint. `commitCurationDelta` remains
2831
+ * the ONE serialized mutation boundary and disposes exactly one delta per call; the drain
2832
+ * slices the checkpoint and disposes each delta independently (D-02).
2833
+ */
2834
+ interface AuthoredDeltaCheckpoint {
2835
+ schemaVersion: typeof AUTHORED_CHECKPOINT_SCHEMA_VERSION;
2836
+ delta: AuthoredCheckpoint['deltas'][number];
2837
+ neighborIds: AuthoredCheckpoint['neighborIds'];
2838
+ preconditions: AuthoredCheckpoint['preconditions'];
2839
+ authorship: AuthoredCheckpoint['authorship'];
2840
+ }
2841
+ /** Slice one widened checkpoint into its ordered per-delta commit units. */
2842
+ declare function sliceAuthoredCheckpoint(checkpoint: AuthoredCheckpoint): AuthoredDeltaCheckpoint[];
2843
+ /** Re-widen one commit unit into a parseable singleton checkpoint (job-state persistence). */
2844
+ declare function widenAuthoredDeltaCheckpoint(slice: AuthoredDeltaCheckpoint): AuthoredCheckpoint;
2845
+
2846
+ interface CanonicalBundleSnapshot {
2847
+ recordId: RecordId;
2848
+ kind: 'fact' | 'procedure' | 'caution' | 'unknown' | 'policy';
2849
+ anchors: readonly Anchor[];
2850
+ scope: ScopeKey;
2851
+ createdAtOrdinal: number;
2852
+ events: readonly BundleOutcomeEvent[];
2853
+ status: BundleStatus;
2854
+ curationJobId?: string;
2855
+ }
2856
+
2857
+ /** Provisional §7.3.1 cap. Phase 379 may retune it only through an explicit constant edit. */
2858
+ declare const BLAST_RADIUS_MAX_SUPERSEDES_PER_ANCHOR_PER_DRAIN = 1;
2859
+
2860
+ /**
2861
+ * 381-03 (PROC-01 D-02/D-03): the verified queue evidence the procedure-extraction wall
2862
+ * validates against. It is code-derived queue truth — distilled observations — never LLM text.
2863
+ */
2864
+ interface ProcedureDispositionEvidence {
2865
+ distilledObservations: readonly DistilledObservation[];
2866
+ }
2867
+ interface DispositionCandidateContext {
2868
+ anchors: readonly Anchor[];
2869
+ scope: ScopeKey;
2870
+ buildOrdinal: number;
2871
+ /** Durable queue address used as the observation's idempotency identity. */
2872
+ curationJobId?: string;
2873
+ /** Original measurement seed value; prose authored by a model is never parsed for authority. */
2874
+ measurementValue?: string;
2875
+ /** Present only for procedure-extraction jobs; procedure deltas fail closed without it. */
2876
+ procedureEvidence?: ProcedureDispositionEvidence;
2877
+ }
2878
+ interface RelationIntent {
2879
+ type: 'proposed-supersession-of' | 'renamed-from';
2880
+ targetId: RecordId;
2881
+ }
2882
+ interface DispositionDrainState {
2883
+ supersedesSeenByAnchorKey: Map<string, number>;
2884
+ /** 379 Open Question 3: one canonical O(store) scan, lazily cached per drain. */
2885
+ canonicalBundles?: CanonicalBundleSnapshot[];
2886
+ /** Test-only observability seam for proving the canonical scan runs once. */
2887
+ onCanonicalBundleScan?: () => void;
2888
+ /** Prevent two jobs in one drain from arming the same not-yet-published unknown. */
2889
+ unknownMarkersSeen?: Set<string>;
2890
+ /** New anchor groups observed by earlier jobs after the once-per-drain snapshot. */
2891
+ firstSeenAnchorsByScope?: Map<string, Map<string, {
2892
+ buildOrdinal: number;
2893
+ recordId?: RecordId;
2894
+ }>>;
2895
+ }
2896
+ interface FirstSeenRegistration {
2897
+ scopeKey: string;
2898
+ supersessionKey: string;
2899
+ buildOrdinal: number;
2900
+ }
2901
+ interface UnknownMintIntent {
2902
+ kind: 'thrash' | 'scope-burst';
2903
+ marker: string;
2904
+ triggerKey: string;
2905
+ scope: ScopeKey;
2906
+ windowOpenOrdinal: number;
2907
+ windowCloseOrdinal: number;
2908
+ evidenceRecordIds: RecordId[];
2909
+ includeCommittedBundle: boolean;
2910
+ firstSeenCount?: number;
2911
+ }
2912
+ interface UnknownResolutionIntent {
2913
+ targetId: RecordId;
2914
+ reason: 'thrash-stable-window';
2915
+ }
2916
+ interface DisposeDeltaInput {
2917
+ delta: Delta;
2918
+ checkpoint: AuthoredDeltaCheckpoint;
2919
+ profile: DispositionProfile;
2920
+ knowledgeRoot: string;
2921
+ priors?: TrustPriorTable;
2922
+ /**
2923
+ * Verified queue evidence needed for anchor and new-ordinal checks. Optional only for the
2924
+ * non-mutating delta shapes; Add and Supersede fail closed when it is absent.
2925
+ */
2926
+ candidate?: DispositionCandidateContext;
2927
+ drainState: DispositionDrainState;
2928
+ }
2929
+ type DispositionOutcome = {
2930
+ kind: 'apply';
2931
+ delta: Delta;
2932
+ relationIntents?: RelationIntent[];
2933
+ bornActive?: true;
2934
+ measurementAppend?: {
2935
+ victimId: RecordId;
2936
+ observation: MeasurementObservation;
2937
+ };
2938
+ unknownMintIntents?: UnknownMintIntent[];
2939
+ unknownResolutionIntents?: UnknownResolutionIntent[];
2940
+ firstSeenRegistration?: FirstSeenRegistration;
2941
+ } | {
2942
+ kind: 'downgraded';
2943
+ delta: Delta;
2944
+ reason: string;
2945
+ original: Delta;
2946
+ relationIntents?: RelationIntent[];
2947
+ measurementAppend?: {
2948
+ victimId: RecordId;
2949
+ observation: MeasurementObservation;
2950
+ };
2951
+ unknownMintIntents?: UnknownMintIntent[];
2952
+ unknownResolutionIntents?: UnknownResolutionIntent[];
2953
+ firstSeenRegistration?: FirstSeenRegistration;
2954
+ } | {
2955
+ kind: 'requeue-resynthesis';
2956
+ reason: string;
2957
+ } | {
2958
+ kind: 'noop';
2959
+ reason: string;
2960
+ };
2961
+ /**
2962
+ * Every way `checkCitedIdIntegrity` can decide this store cannot stand behind a cited id.
2963
+ *
2964
+ * The registry exists so the count stops being something a comment or a corpus has to keep in
2965
+ * step. Round 2 measured what happens when it is not one: the check's second return is a
2966
+ * three-way disjunction, the corpus and the doc comment both said "three conditions", and
2967
+ * carving `scope-mismatch` out of the refusal for every origin left 3 files / 70 tests green.
2968
+ * A fifth member added here is a compile error at `citedIdIntegrityFailure`'s derivation and at
2969
+ * the corpus's `satisfies Record<CitedIdIntegrityCondition, …>` fixture table.
2970
+ */
2971
+ declare const CITED_ID_INTEGRITY_CONDITIONS: readonly ["not-a-persisted-neighbor", "missing-from-disk", "deprecated", "scope-mismatch"];
2972
+ type CitedIdIntegrityCondition = (typeof CITED_ID_INTEGRITY_CONDITIONS)[number];
2973
+ /**
2974
+ * The typed refusal a USER-authored delta receives when its cited-id integrity check fails.
2975
+ *
2976
+ * Thrown rather than returned, and out of the check funnel rather than out of the commit, so it
2977
+ * fires before any canonical byte moves for this delta: `disposeDelta` runs `checkCitedIdIntegrity`
2978
+ * first of the six §7.3.1 checks, ahead of every append, projection and promotion.
2979
+ * `applyReview`'s `commitAndPublish` catches it, restores the proposal to pending and rethrows,
2980
+ * which is how the caller gets a typed answer instead of a receipt for work nobody did.
2981
+ *
2982
+ * WHAT MAKES "NOTHING MOVED" TRUE, and it is not this sentence. The previous version of this
2983
+ * comment asserted whole-store neutrality as free-standing prose, naming no mechanism, and the
2984
+ * round-2 verifier measured that claim FALSE: the delta had already reached disposition through
2985
+ * `knowledge-store.ts`'s bridge, which materialized a claimed curation candidate on disk before
2986
+ * `commitWithProfile` ever called into the commit, and the throw left it there for the next
2987
+ * drain's reconcile to requeue (CR-02″). Two mechanisms now establish the claim, and each of them
2988
+ * reds if it is removed:
2989
+ *
2990
+ * 1. `bridgeDelta` is WRITE-FREE (`curator/knowledge-store.ts`). It computes the candidate's
2991
+ * address and materializes nothing, so a check that throws at any position has nothing to
2992
+ * leave behind. Reintroducing its two write statements reds the case in (2).
2993
+ * 2. `review/__tests__/apply-review.test.ts` asserts `hashTree(knowledgeRoot)` and the tree's
2994
+ * full entry listing — QUEUE TREE INCLUDED — are identical across a refused `accept`, with
2995
+ * a potency control proving the digest is falsifiable over that exact root.
2996
+ *
2997
+ * The scope of the guarantee is the knowledge root. The writer lease's own fencing state
2998
+ * (`.epoch`, `.writer-authority.sqlite`) advances on acquisition regardless of what the caller
2999
+ * then does, which is why the case above holds the lease across its measurement rather than
3000
+ * excusing those two names.
3001
+ */
3002
+ declare class UserDeltaCitationIntegrityError extends Error {
3003
+ readonly recordId: RecordId;
3004
+ readonly deltaKind: Delta['kind'];
3005
+ readonly condition: CitedIdIntegrityCondition;
3006
+ constructor(recordId: RecordId, deltaKind: Delta['kind'], condition: CitedIdIntegrityCondition);
3007
+ }
3008
+ /**
3009
+ * Run memory.md §7.3.1's six checks in order. The function is deliberately synchronous: every
3010
+ * decision is made from the persisted checkpoint and canonical bundle tree, never an index row.
3011
+ */
3012
+ declare function disposeDelta(input: DisposeDeltaInput): DispositionOutcome;
3013
+
3014
+ interface CommitCurationJob extends ClaimedCurationJob {
3015
+ screenStates?: readonly ScreenState[];
3016
+ /** Product authority resolved with the checkpoint; absent ids must be globally unique. */
3017
+ targetProducts?: Readonly<Record<string, string>>;
3018
+ }
3019
+ interface CommitCurationDependencies {
3020
+ knowledgeRoot: string;
3021
+ epoch: number;
3022
+ profile: DispositionProfile;
3023
+ drainState: DispositionDrainState;
3024
+ /** Host-resolved posture as data; absent means the v51 floor. */
3025
+ resolvePromotionPosture?: ResolvePromotionPosture;
3026
+ priors?: TrustPriorTable;
3027
+ /**
3028
+ * Multi-delta disposal (D-02): when set, terminal paths skip queue completion so the
3029
+ * claimed candidate directory stays available for the checkpoint's remaining deltas.
3030
+ * The drain leaves it unset on the final delta, which completes the job as before.
3031
+ */
3032
+ deferCompletion?: true;
3033
+ hooks?: {
3034
+ /**
3035
+ * Test/chaos seam after every pre-staging write and immediately before the commit tail
3036
+ * enters its current-writer-authority section. Nothing durable and reader-relevant to this
3037
+ * candidate has been published yet when it fires.
3038
+ */
3039
+ beforeCommitAuthorityEntry?: () => void;
3040
+ /** Test/chaos seam immediately after the durable intent rename. */
3041
+ afterIntentWritten?: () => void;
3042
+ /** Test/chaos seam while the complete bundle is staged but still reader-invisible. */
3043
+ beforePublishRename?: () => void;
3044
+ /** Test/chaos seam after lock-held authority comparison and before finalize. */
3045
+ afterFinalAuthorityCompare?: () => void;
3046
+ /** Test-only bounded-takeover override; production uses the pinned lock deadline. */
3047
+ mutationLockTakeoverMs?: number;
3048
+ /** Test/chaos seam after each durable mutable-file replacement. */
3049
+ afterMutableReplacement?: (targetRelPath: string) => void;
3050
+ /** Test/chaos seam after a fresh bundle publish and before its index projection. */
3051
+ afterBundlePublished?: (bundleId: RecordId) => void;
3052
+ /** Test/chaos seam after the index transaction and before intent removal. */
3053
+ afterIndexApplied?: (bundleId: RecordId) => void;
3054
+ /** Test/chaos seams around the durable observation/cleanup boundary. */
3055
+ afterEffectReceiptsWritten?: () => void;
3056
+ afterIntentCleared?: () => void;
3057
+ };
3058
+ operations?: {
3059
+ /** Test/chaos seam; production always uses the canonical publisher. */
3060
+ publishBundle?: (config: BundleStoreConfig, input: PublishBundleInput) => PublishBundleOutcome;
3061
+ };
3062
+ }
3063
+ interface CommittedBundleResult {
3064
+ bundleId: RecordId;
3065
+ bundleDir: string;
3066
+ deltaKind: Delta['kind'];
3067
+ /** D-16 exception-bucket observability; absent when the gate promotes or never runs. */
3068
+ promotionHoldReason?: PromotionGateHoldReason;
3069
+ promotionPath?: PromotionPath;
3070
+ }
3071
+ /**
3072
+ * 381-47 (F-381-45-1): WHY a delta-noop disposition had nothing to apply. `authored-noop`
3073
+ * = a capable-model author deliberately emitted a Noop delta; the `fallback-*` members
3074
+ * name WHICH author fallback defeated extraction (the §7.9 one-element procedure Noop).
3075
+ * Benign noop reasons (evidence-token dedup, disposition rejects) never carry a cause.
3076
+ */
3077
+ type EmptyDeltaCause = 'authored-noop' | 'fallback-malformed' | 'fallback-no-model' | 'fallback-token-budget' | 'fallback-model-error';
3078
+ type CommitCurationOutcome = ({
3079
+ kind: 'applied';
3080
+ } & CommittedBundleResult) | ({
3081
+ kind: 'downgraded';
3082
+ reason: string;
3083
+ original: Delta;
3084
+ } & CommittedBundleResult) | ({
3085
+ kind: 'converted-confirm';
3086
+ } & CommittedBundleResult) | {
3087
+ kind: 'abandoned-stale-epoch';
3088
+ observedEpoch: number;
3089
+ } | {
3090
+ kind: 'failed';
3091
+ reason: 'identity-collision' | 'append-failed';
3092
+ collisionReason?: Extract<PublishBundleOutcome, {
3093
+ kind: 'identity-collision';
3094
+ }>['reason'];
3095
+ } | {
3096
+ kind: 'noop';
3097
+ reason: string;
3098
+ emptyDeltaCause?: EmptyDeltaCause;
3099
+ } | {
3100
+ kind: 'requeue-resynthesis';
3101
+ reason: string;
3102
+ };
3103
+ interface ApplyIndexDeltaForBundleInput {
3104
+ knowledgeRoot: string;
3105
+ priors?: TrustPriorTable;
3106
+ bundleId: string;
3107
+ product?: string;
3108
+ }
3109
+ /** Shared Plan 378-10 projection authority used by commit and journal reconciliation. */
3110
+ declare function applyIndexDeltaForBundle(input: ApplyIndexDeltaForBundleInput): void;
3111
+ /** Recovery alias deliberately delegates to the same projection unit as the live tail. */
3112
+ declare function deriveIndexRowFromBundle(input: ApplyIndexDeltaForBundleInput): void;
3113
+ /**
3114
+ * The one serialized mutation boundary: fresh epoch, disposition, intent, fresh epoch, publish,
3115
+ * append-only lifecycle, canonical index projection, intent clear, and queue completion.
3116
+ */
3117
+ declare function commitCurationDelta(deps: CommitCurationDependencies, job: CommitCurationJob, checkpoint: AuthoredDeltaCheckpoint): CommitCurationOutcome;
3118
+
3119
+ interface ReconcileOnDrainStartHooks {
3120
+ /**
3121
+ * Fires immediately before a stale intent is renamed aside. The only seam that can reproduce
3122
+ * the race a second reconciler on another host creates: it removes the intent file between our
3123
+ * listing and our rename.
3124
+ */
3125
+ beforeStaleQuarantine?(candidateId: string, epoch: number): void;
3126
+ }
3127
+ interface ReconcileOnDrainStartDependencies {
3128
+ knowledgeRoot: string;
3129
+ writerAuthority: CurrentWriterAuthority;
3130
+ hasIndexRow(bundleId: string): boolean;
3131
+ applyIndexDelta(intent: CommitIntent): void;
3132
+ deriveIndexRowFromBundle(bundleId: string): void;
3133
+ hooks?: ReconcileOnDrainStartHooks;
3134
+ }
3135
+ interface ReconcileReport {
3136
+ intentsCleared: number;
3137
+ intentsReplayed: number;
3138
+ intentsDiscarded: number;
3139
+ /** Intents authored before the reconciled-epoch watermark: renamed aside, never replayed. */
3140
+ intentsQuarantinedStale: number;
3141
+ jobsRequeued: number;
3142
+ orphansRemoved: number;
3143
+ /** Present on production reconciliation; optional keeps injected legacy test reports compatible. */
3144
+ mutationsRecovered?: number;
3145
+ malformed: number;
3146
+ }
3147
+ declare function reconcileOnDrainStart(deps: ReconcileOnDrainStartDependencies): ReconcileReport;
3148
+
3149
+ /**
3150
+ * CR-02 takeover barriers. Test-only seams naming the three points at which a canary flow is
3151
+ * about to make canonical state durable; production callers omit them.
3152
+ *
3153
+ * The two entry barriers fire OUTSIDE the held section, which is what lets a schedule complete a
3154
+ * real takeover there and then observe the resumed writer's refusal. `beforeIndexProjection` fires
3155
+ * INSIDE it, so its schedule observes a contender blocked behind the tail instead.
3156
+ */
3157
+ interface CanaryAuthorityHooks {
3158
+ /** Immediately before the negative arm enters its held section. */
3159
+ beforeNegativeAppend?: () => void;
3160
+ /** Immediately before the positive arm enters its held promotion section. */
3161
+ beforePromotionAppend?: () => void;
3162
+ /** Inside the negative arm's held section, between its append and its projection. */
3163
+ beforeIndexProjection?: () => void;
3164
+ }
3165
+ interface CreateLocalKnowledgeStoreConfig {
3166
+ knowledgeRoot: string;
3167
+ executionContext: ExecutionContext;
3168
+ /** The holder's fresh lease epoch. Defaults to a fresh disk read for composition roots. */
3169
+ epoch?: number;
3170
+ /**
3171
+ * The caller's real writer-lease id, paired with `epoch`.
3172
+ *
3173
+ * CR-02: every canonical append, promotion, and index projection this store performs carries
3174
+ * this identity into `withCurrentWriterAuthority`, so a store constructed by someone who is not
3175
+ * the published holder is refused at its first canonical mutation instead of laundering a
3176
+ * fabricated id. It is required precisely so no code path can omit it and fall back to one.
3177
+ */
3178
+ writerLeaseId: string;
3179
+ /** Port calls lack a run ordinal; callers with run context should supply it. */
3180
+ sourceBuildOrdinal?: number;
3181
+ sourceRunId?: string;
3182
+ /** 2026-09-07, 418 D-07: queue entry identity stamped as producedBy.jobId. */
3183
+ sourceJobId?: string;
3184
+ screenStates?: readonly ScreenState[];
3185
+ resolvePromotionPosture?: ResolvePromotionPosture;
3186
+ /** Resolved once at construction; only neutral immutable data reaches folds and projections. */
3187
+ resolveTrustPriors?: () => TrustPriorTable | undefined;
3188
+ onPromotionOutcome?: (report: PromotionOutcomeReport) => void;
3189
+ /** Fault-injection seam; production callers must omit it. */
3190
+ canaryAuthorityHooks?: CanaryAuthorityHooks;
3191
+ /**
3192
+ * Joins the caller's own terminal work to the canary tail's held section. The drain's `done`
3193
+ * job-state write uses this so the append, the projection, and the lifecycle transition are one
3194
+ * indivisible section; implementations must use within-held write forms only.
3195
+ */
3196
+ onCanaryTailWithinCurrentAuthority?: () => void;
3197
+ operations?: {
3198
+ /** Test/chaos seam; production always uses commitCurationDelta. */
3199
+ commitCurationDelta?: typeof commitCurationDelta;
3200
+ };
3201
+ }
3202
+ interface PromotionOutcomeReport {
3203
+ candidateId: RecordId;
3204
+ promotionPath?: PromotionPath;
3205
+ promotionHoldReason?: PromotionGateHoldReason;
3206
+ }
3207
+ /**
3208
+ * @deprecated `recordCanaryOutcome` is implemented. The exported class remains only so code
3209
+ * compiled against the pre-380 package surface does not lose its named import.
3210
+ */
3211
+ declare class KnowledgeStoreNotImplementedError extends Error {
3212
+ readonly operation: 'recordCanaryOutcome';
3213
+ constructor(operation: 'recordCanaryOutcome', _retiredOwnerPhase?: 380);
3214
+ }
3215
+ type KnowledgePromotionErrorReason = PromotionGateHoldReason | 'propose-only' | 'not-found';
3216
+ /** Typed refusal for the evidence-gated promote port; callers never receive a force path. */
3217
+ declare class KnowledgePromotionError extends Error {
3218
+ readonly candidateId: RecordId;
3219
+ readonly reason: KnowledgePromotionErrorReason;
3220
+ constructor(candidateId: RecordId, reason: KnowledgePromotionErrorReason);
3221
+ }
3222
+ declare class KnowledgeCommitError extends Error {
3223
+ readonly outcome: CommitCurationOutcome;
3224
+ constructor(outcome: CommitCurationOutcome);
3225
+ }
3226
+ /** Typed WR-02 refusal for procedure drafts that require evidence this port cannot carry. */
3227
+ declare class KnowledgeUnsupportedDeltaError extends Error {
3228
+ readonly deltaKind: Delta['kind'];
3229
+ readonly reason: 'procedure-evidence-unsupported';
3230
+ constructor(deltaKind: Delta['kind']);
3231
+ }
3232
+ /**
3233
+ * Realizes the agent write port over the canonical mutation path. D-08 propose-only is a
3234
+ * disposition profile inside commitCurationDelta, never a second mutation path. Because the port
3235
+ * carries bare deltas, this bridge materializes the smallest queue/checkpoint envelope and resolves
3236
+ * cited neighbor truth from canonical bundles before entering disposition.
3237
+ */
3238
+ declare function createLocalKnowledgeStore(config: CreateLocalKnowledgeStoreConfig): KnowledgePort;
3239
+
3240
+ type MaintenanceSkipReason = 'lease-held' | 'budget-exhausted' | 'aborted' | 'daemon-cadence';
3241
+ type WriterPassReport = {
3242
+ kind: 'not-requested';
3243
+ } | {
3244
+ kind: 'skipped';
3245
+ reason: MaintenanceSkipReason;
3246
+ } | {
3247
+ kind: 'rebuilt';
3248
+ request?: RebuildRequest;
3249
+ rebuild: RebuildReport;
3250
+ } | {
3251
+ kind: 'failed';
3252
+ message: string;
3253
+ };
3254
+ type GcPassReport = {
3255
+ kind: 'not-run';
3256
+ } | {
3257
+ kind: 'skipped';
3258
+ reason: MaintenanceSkipReason;
3259
+ } | {
3260
+ kind: 'completed';
3261
+ summary: GcSummary;
3262
+ /** WR-01: projection rows removed by the same-tail scoped cleanup; absent on zero-trim. */
3263
+ projectionRowsRemoved?: number;
3264
+ } | {
3265
+ kind: 'failed';
3266
+ message: string;
3267
+ };
3268
+ interface MaintenanceOperations {
3269
+ readRebuildRequest: typeof readRebuildRequest;
3270
+ readIndexMaintenanceState: typeof readIndexMaintenanceState;
3271
+ acquireEmbedder: typeof acquireEmbedder;
3272
+ rebuildIndex: typeof rebuildIndex;
3273
+ runKnowledgeGc: typeof runKnowledgeGc;
3274
+ removeBundleProjectionRows: typeof removeBundleProjectionRows;
3275
+ beforeGcRemoval?: RunKnowledgeGcHooks['beforeGcRemoval'];
3276
+ }
3277
+ interface MaintenanceBudget {
3278
+ now: () => number;
3279
+ startedAtMs: number;
3280
+ limitMs: number;
3281
+ }
3282
+ interface RunMaintenanceTailOptions {
3283
+ knowledgeRoot: string;
3284
+ /** Same host-resolved data supplied to drains and review stores. */
3285
+ priors?: TrustPriorTable;
3286
+ lease: {
3287
+ leaseId: string;
3288
+ epoch: number;
3289
+ };
3290
+ /** Host-resolved posture; daemon callers pass worker-required, one-shot callers omit it. */
3291
+ embeddingCapability?: AcquireEmbedderOutcome;
3292
+ /** Omit for daemon cadence; one-shot drains provide their 60 s boundary explicitly. */
3293
+ budget?: MaintenanceBudget;
3294
+ /** Fault-injection seam; production callers must omit it. */
3295
+ operations?: Partial<MaintenanceOperations>;
3296
+ /** One-shot drains fold authority loss into their report; daemon callers surface it by default. */
3297
+ surfaceAuthorityLoss?: boolean;
3298
+ }
3299
+ interface MaintenanceTailFailure {
3300
+ stage: 'writer-pass' | 'gc';
3301
+ error: unknown;
3302
+ }
3303
+ interface MaintenanceTailResult {
3304
+ writerPass: WriterPassReport;
3305
+ gc: GcPassReport;
3306
+ failures: MaintenanceTailFailure[];
3307
+ }
3308
+ /**
3309
+ * Run the writer-repair and canonical-GC tail as one host-independent callable.
3310
+ *
3311
+ * Daemon callers omit budget and receive stale authority errors unchanged. The one-shot drain
3312
+ * supplies its existing wall-clock boundary and folds typed failures into DrainReport.
3313
+ */
3314
+ declare function runMaintenanceTail(options: RunMaintenanceTailOptions): Promise<MaintenanceTailResult>;
3315
+
3316
+ /** Ratified M2 one-shot budget. A run's tail aborts an author request in flight at its end. */
3317
+ declare const DRAIN_BUDGET_MS = 60000;
3318
+ /** Provisional §7.4 cap over distinct search-before-create query targets. */
3319
+ declare const SEARCH_BEFORE_CREATE_MAX_QUERIES = 5;
3320
+ /** Provisional 376 D-14 per-job breaker before a vector-deferred commit is forced. */
3321
+ declare const EMBED_CRASH_VECTORLESS_THRESHOLD = 2;
3322
+ type DrainFailureStage = 'lease' | 'reconcile' | 'claim' | 'heartbeat' | 'job-state' | 'search' | 'author' | 'checkpoint' | 'commit' | 'quarantine' | 'requeue' | 'release' | 'writer-pass' | 'gc';
3323
+ interface DrainFailure {
3324
+ stage: DrainFailureStage;
3325
+ message: string;
3326
+ candidateId?: string;
3327
+ }
3328
+ /**
3329
+ * A commit that decided there was nothing to apply. Legitimate reasons exist (evidence-token
3330
+ * dedup), so a noop is never a failure — but dropping it on the floor made live procedure
3331
+ * extraction die silently for 13 UAT runs (G-381-1). Visibility, never a gate.
3332
+ */
3333
+ interface DrainNoop {
3334
+ candidateId: string;
3335
+ reason: string;
3336
+ /**
3337
+ * 381-47: present only on delta-noop entries — distinguishes a defeated extraction
3338
+ * (`fallback-*`) from a deliberate authored Noop; benign dedup carries no cause.
3339
+ */
3340
+ emptyDeltaCause?: EmptyDeltaCause;
3341
+ }
3342
+ interface DrainPromotionOutcome {
3343
+ candidateId: string;
3344
+ promotionPath?: PromotionPath;
3345
+ promotionHoldReason?: PromotionGateHoldReason;
3346
+ }
3347
+ interface DrainReportBase {
3348
+ /** Actual surviving effects for the requested run, also present on incomplete drains. */
3349
+ effects?: readonly CurationSnapshotChange[];
3350
+ /** Certified only after a complete drain; absent for historical missing receipts. */
3351
+ snapshot?: CurationSnapshot;
3352
+ /**
3353
+ * The effects committed; only the projection over them failed, so `snapshot` is absent. Never a
3354
+ * failure: the drain stays complete and nothing is retried or refolded.
3355
+ */
3356
+ snapshotFoldFailed?: true;
3357
+ jobsProcessed: number;
3358
+ committed: number;
3359
+ evidenceCommitted?: number;
3360
+ /**
3361
+ * Committed deltas whose kind was `Deprecate` (388-02).
3362
+ *
3363
+ * OPTIONAL because the drain report is constructed by fault-injection fakes across four
3364
+ * packages and a required field would make every one of them a compile error over a number
3365
+ * they have no opinion about. The real drain always sets it, so a production report saying
3366
+ * `deprecated: 0` is a measurement rather than an absence — which is what the post-run
3367
+ * chain's notification (D-11) needs in order to tell a drain that deprecated nothing apart
3368
+ * from a drain that never ran at all.
3369
+ */
3370
+ deprecated?: number;
3371
+ /** Optional for fault-injection fakes; the real drain always sets this measurement. */
3372
+ promotions?: readonly DrainPromotionOutcome[];
3373
+ downgraded: number;
3374
+ withheld: number;
3375
+ quarantined: number;
3376
+ requeued: number;
3377
+ failures: number;
3378
+ failureDetails: DrainFailure[];
3379
+ noops: number;
3380
+ noopDetails: DrainNoop[];
3381
+ reconcile: ReconcileReport | undefined;
3382
+ writerPass: WriterPassReport;
3383
+ gc: GcPassReport;
3384
+ /** Visibility only: every durable disposition that still prevents queue completion. */
3385
+ remaining: number;
3386
+ /** Durable remainder partition. The current writer's nonterminal claim is included. */
3387
+ remainingWork: CurationRemainingWork;
3388
+ }
3389
+ type DrainReport = DrainReportBase & ({
3390
+ kind: 'drained' | 'aborted';
3391
+ } | {
3392
+ kind: 'handed-off';
3393
+ holder: WriterLease;
3394
+ });
3395
+ type CurationDrainLifecycleEvent = {
3396
+ readonly kind: 'job-claimed';
3397
+ readonly candidateId: string;
3398
+ } | {
3399
+ readonly kind: 'job-completed';
3400
+ readonly candidateId: string;
3401
+ };
3402
+ /**
3403
+ * Internal capability table used by focused fault-injection tests. Production callers omit it;
3404
+ * every default delegates directly to the shipped queue, lease, retrieval, commit, and rebuild
3405
+ * authorities.
3406
+ */
3407
+ interface DrainOperations extends MaintenanceOperations {
3408
+ acquireWriterLease: typeof acquireWriterLease;
3409
+ heartbeatLease: typeof heartbeatLease;
3410
+ releaseLease: typeof releaseLease;
3411
+ reconcileOnDrainStart: typeof reconcileOnDrainStart;
3412
+ claimNextJob: typeof claimNextJob;
3413
+ quarantineClaimed: typeof quarantineClaimed;
3414
+ requeueClaimed: typeof requeueClaimed;
3415
+ readJobState: typeof readJobState;
3416
+ writeJobState: typeof writeJobState;
3417
+ persistAuthoredCheckpoint: typeof persistAuthoredCheckpoint;
3418
+ openKnowledgeReader: typeof openKnowledgeReader;
3419
+ buildRetrievalQuery: typeof buildRetrievalQuery;
3420
+ retrieve: typeof retrieve;
3421
+ commitCurationDelta: typeof commitCurationDelta;
3422
+ /** The canonical commit already projects dense work as pending; the breaker selects this seam. */
3423
+ commitCurationDeltaVectorless: typeof commitCurationDelta;
3424
+ hasIndexRow(knowledgeRoot: string, bundleId: string): boolean;
3425
+ applyIndexDeltaForBundle: typeof applyIndexDeltaForBundle;
3426
+ deriveIndexRowFromBundle: typeof deriveIndexRowFromBundle;
3427
+ /** CR-02 takeover barriers for the canary branch; production omits them. */
3428
+ canaryAuthorityHooks?: CanaryAuthorityHooks;
3429
+ }
3430
+ interface DrainOptions {
3431
+ /** A per-run reader request, independent of which queued jobs this drain claims. */
3432
+ sourceRunId?: string;
3433
+ observationIds?: readonly string[];
3434
+ knowledgeRoot: string;
3435
+ /** `unbounded` is reserved for a standing host whose process lifetime is the boundary. */
3436
+ budgetMs?: number | 'unbounded';
3437
+ /** Standing writer authority owned by the caller; drainQueue never mutates its lifecycle. */
3438
+ lease?: {
3439
+ leaseId: string;
3440
+ epoch: number;
3441
+ };
3442
+ author: CuratorAuthor;
3443
+ executionContext: ExecutionContext;
3444
+ /** Host-resolved posture; daemon sessions pass worker-required capability here. */
3445
+ embeddingCapability?: AcquireEmbedderOutcome;
3446
+ /** Built once per drain on the host; synchronous per candidate inside the gate. */
3447
+ resolvePromotionPosture?: ResolvePromotionPosture;
3448
+ /** Already-resolved host snapshot; the host retains this data across queue retries. */
3449
+ priors?: TrustPriorTable;
3450
+ /** Default inline preserves one-shot behavior; daemon sessions skip for their own cadence. */
3451
+ maintenance?: 'inline' | 'skip';
3452
+ signal?: AbortSignal;
3453
+ /** Synchronous durable-boundary observer; daemon hosts append these facts before proceeding. */
3454
+ onJobLifecycle?: (event: CurationDrainLifecycleEvent) => void;
3455
+ /** D-06 test seam. Production uses the one wall clock only at drain boundaries. */
3456
+ now?: () => number;
3457
+ /** Fault-injection seam; production callers must omit it. */
3458
+ operations?: Partial<DrainOperations>;
3459
+ }
3460
+ /**
3461
+ * Lease-gated one-shot Curator drain. It owns no provider: authoring is injected, serialized, and
3462
+ * durably checkpointed before the deterministic commit tail. Abort and budget are checked at job
3463
+ * boundaries; an in-flight model request is cut off only by the host's own author signal.
3464
+ */
3465
+ declare function drainQueue(options: DrainOptions): Promise<DrainReport>;
3466
+
3467
+ declare const PURGE_RECEIPT_SCHEMA_VERSION = 2;
3468
+ /**
3469
+ * Content-free committed audit receipt shared by the purge writer and read-only maintenance
3470
+ * history. Keeping the contract outside purge.ts prevents the reader graph from importing the
3471
+ * destructive implementation merely to validate committed receipt bytes.
3472
+ */
3473
+ declare const PurgeReceiptSchema: z.ZodObject<{
3474
+ schemaVersion: z.ZodLiteral<2>;
3475
+ purgedRecordId: z.ZodString;
3476
+ purgedAtOrdinal: z.ZodNumber;
3477
+ initiator: z.ZodString;
3478
+ affectedPaths: z.ZodArray<z.ZodString>;
3479
+ firstCommittedOrdinal: z.ZodNumber;
3480
+ }, z.core.$strict>;
3481
+ declare const purgeReceiptSchema: z.ZodObject<{
3482
+ schemaVersion: z.ZodLiteral<2>;
3483
+ purgedRecordId: z.ZodString;
3484
+ purgedAtOrdinal: z.ZodNumber;
3485
+ initiator: z.ZodString;
3486
+ affectedPaths: z.ZodArray<z.ZodString>;
3487
+ firstCommittedOrdinal: z.ZodNumber;
3488
+ }, z.core.$strict>;
3489
+ type PurgeReceipt = z.infer<typeof PurgeReceiptSchema>;
3490
+ declare function guardPurgeReceipt(value: unknown): PurgeReceipt;
3491
+
3492
+ interface PurgeAffectedBundleTarget {
3493
+ product: string;
3494
+ bundleDir: string;
3495
+ kind: LoadedBundle['frontmatter']['kind'];
3496
+ affectedPaths: string[];
3497
+ }
3498
+ interface PurgeAffectedQueueTarget {
3499
+ candidateId: string;
3500
+ candidateDir: string;
3501
+ jobStatePath: string;
3502
+ }
3503
+ /**
3504
+ * Content-free, deterministic statement of every local surface one purge operation will affect.
3505
+ * This is the single planning shape shared by preview, the under-lease recheck, and destructive
3506
+ * execution.
3507
+ */
3508
+ interface PurgeAffectedSet {
3509
+ recordId: string;
3510
+ state: 'live' | 'resume' | 'already-purged' | 'not-found';
3511
+ bundles: PurgeAffectedBundleTarget[];
3512
+ receiptAffectedPaths: string[];
3513
+ projectionTargets: string[];
3514
+ embedCacheAction: 'clear-all';
3515
+ queueTargets: PurgeAffectedQueueTarget[];
3516
+ intentPath: string;
3517
+ receiptPath: string;
3518
+ }
3519
+ interface PurgeRecordInput {
3520
+ knowledgeRoot: string;
3521
+ recordId: RecordId;
3522
+ /** Caller-owned sovereign identity; Phase 382 supplies the confirmed user identity. */
3523
+ initiator: string;
3524
+ /** Runtime-counter reservation used for a fresh destruction receipt; resumes reuse intent. */
3525
+ purgedAtOrdinal?: number;
3526
+ /** Governed maintenance callers fence this exact lease at every mutation boundary. */
3527
+ writerAuthority?: WriterLeaseAuthority;
3528
+ /** Fingerprint of the caller's preview; drift must fail before any mutation. */
3529
+ expectedAffectedSetFingerprint?: string;
3530
+ /** Test/chaos seam between purge phases; production callers omit it (commit.ts precedent). */
3531
+ hooks?: {
3532
+ /** Immediately after the durable intent publication. */
3533
+ afterIntentWritten?: () => void;
3534
+ /** After the transactional projection purge. */
3535
+ afterProjectionPurge?: () => void;
3536
+ /** After every committed bundle tree removal. */
3537
+ afterBundleRemoval?: () => void;
3538
+ /** After the queue payload sweep. */
3539
+ afterQueueSweep?: () => void;
3540
+ /** Immediately before the completion receipt publication. */
3541
+ beforeReceiptPublish?: () => void;
3542
+ };
3543
+ }
3544
+ type PurgeRecordOutcome = {
3545
+ kind: 'purged';
3546
+ receipt: PurgeReceipt;
3547
+ } | {
3548
+ kind: 'already-purged';
3549
+ receipt: PurgeReceipt;
3550
+ } | {
3551
+ kind: 'not-found';
3552
+ };
3553
+ declare class PurgeTargetChangedError extends Error {
3554
+ readonly expectedFingerprint: string;
3555
+ readonly actualFingerprint: string;
3556
+ readonly code = "purge-target-changed";
3557
+ constructor(expectedFingerprint: string, actualFingerprint: string);
3558
+ }
3559
+ declare class PurgeTargetIntegrityError extends Error {
3560
+ readonly targetPath: string;
3561
+ readonly reason: string;
3562
+ readonly code = "purge-target-integrity";
3563
+ constructor(targetPath: string, reason: string);
3564
+ }
3565
+ declare class PurgeQueueIdentityError extends Error {
3566
+ readonly targetPath: string;
3567
+ readonly reason: string;
3568
+ readonly code = "purge-queue-identity";
3569
+ constructor(targetPath: string, reason: string);
3570
+ }
3571
+ declare class PurgeCompletionVerificationError extends Error {
3572
+ readonly survivingSurface: string;
3573
+ readonly code = "purge-completion-verification";
3574
+ constructor(survivingSurface: string);
3575
+ }
3576
+ declare class PurgeChronologyRequiredError extends Error {
3577
+ readonly code = "purge-chronology-required";
3578
+ constructor();
3579
+ }
3580
+ declare function resolvePurgeAffectedSet(input: {
3581
+ knowledgeRoot: string;
3582
+ recordId: RecordId;
3583
+ }): PurgeAffectedSet;
3584
+ declare function purgeAffectedSetFingerprint(affectedSet: PurgeAffectedSet): string;
3585
+ /**
3586
+ * Hard-remove one confirmed-sensitive record from every recoverable local-store surface.
3587
+ *
3588
+ * Recovery invariant (CR-04): nothing irreversible ever precedes a durable intent, so a retry
3589
+ * either re-resolves intact state (crash before the intent) or replays the intent's persisted
3590
+ * identities to the identical completion receipt (crash at any later boundary).
3591
+ *
3592
+ * Revival ratification (SC-4/DCAY-05, option a): bundle ids are deterministic content addresses,
3593
+ * so purged-then-re-learned content re-mints the SAME record id. Receipt-exists-but-live-surfaces
3594
+ * therefore runs a FRESH destruction cycle publishing the next append-only versioned receipt in
3595
+ * the series; original audit receipts are never rewritten, and already-purged is attested only
3596
+ * over verified-dead surfaces (live resolution always precedes any receipt-based attestation).
3597
+ */
3598
+ declare function purgeRecord(input: PurgeRecordInput): PurgeRecordOutcome;
3599
+
3600
+ /**
3601
+ * The §7.7 projections a forget in THIS store actually scrubs, enumerated honestly.
3602
+ *
3603
+ * `KNOWLEDGE_PROJECTIONS` declares four — `fts`, `vec`, `graph`, `exemplar` — and a receipt
3604
+ * claiming all four would be a receipt nobody can trust. `scrubDeadRecallRows` (index-rebuild.ts)
3605
+ * deletes the FTS row, deletes the record from every materialised vec table, and deletes the
3606
+ * record's graph edges plus the nodes those edges orphaned. It does NOT touch the exemplar
3607
+ * ranking, and it should not: an exemplar in this system is a TEST reference drawn from the
3608
+ * run-outcome projection (`serving/get-authoring-brief.ts`), not a knowledge record, so a
3609
+ * forgotten record has no exemplar row to scrub. Listing `exemplar` anyway would be the receipt
3610
+ * asserting work nobody did — which is worse than a receipt that says less, because a shorter
3611
+ * true list can be lengthened and a false one cannot be believed.
3612
+ *
3613
+ * When the exemplar ranking gains knowledge-record rows, this constant is the one place that
3614
+ * fact is recorded, and the receipt widens with it.
3615
+ */
3616
+ declare const FORGET_SCRUBBED_PROJECTIONS: readonly KnowledgeProjection[];
3617
+ /** Resolves a `MEM-` short-ID to the machine id it addresses, or `undefined` (D-01/D-04). */
3618
+ type ResolveBundleShortId = (shortId: string) => string | undefined;
3619
+ /** A validated mark, accept, reject or forget, in the shape the review verbs take. */
3620
+ interface ReviewChangeRequest {
3621
+ readonly verb: 'accept' | 'reject' | 'forget' | 'mark';
3622
+ readonly input: KnowledgeAcceptInput | KnowledgeRejectInput | KnowledgeForgetInput | KnowledgeMarkInput;
3623
+ }
3624
+ declare class MarkPlanIntegrityError extends Error {
3625
+ readonly recordId: string;
3626
+ constructor(recordId: string);
3627
+ }
3628
+
3629
+ /**
3630
+ * How long a delegating process waits for the leaseholder to finish an apply (D-18).
3631
+ *
3632
+ * PINNED, not configured, on the `LEASE_HEARTBEAT_TTL_MS` idiom. 30s is chosen as "longer than any
3633
+ * commit this store performs, shorter than a person's patience": a commit is a handful of file
3634
+ * appends plus one bundle re-projection, all local. RETUNE from an observed apply that legitimately
3635
+ * exceeded it — never from a flaky test, which is a symptom of a wedged holder rather than a slow
3636
+ * one, and the wedged holder is exactly what this bound exists to refuse rather than wait out.
3637
+ */
3638
+ declare const APPLY_DELEGATE_TIMEOUT_MS = 30000;
3639
+ /** The typed refusal a bounded wait produces. Never a generic timeout, never a takeover. */
3640
+ declare class ApplyDelegateTimeoutError extends Error {
3641
+ readonly changeId: string;
3642
+ readonly timeoutMs: number;
3643
+ constructor(changeId: string, timeoutMs: number);
3644
+ }
3645
+ /** The typed refusal when a live holder exists and this host was given no way to reach it. */
3646
+ declare class ApplyDelegateUnavailableError extends Error {
3647
+ readonly changeId: string;
3648
+ readonly holder: WriterLease;
3649
+ constructor(changeId: string, holder: WriterLease);
3650
+ }
3651
+ /** The record a change addresses left the store between resolution and commit. */
3652
+ declare class ReviewTargetVanishedError extends Error {
3653
+ readonly changeId: string;
3654
+ constructor(changeId: string);
3655
+ }
3656
+ interface WriterAuthority {
3657
+ readonly leaseId: string;
3658
+ readonly epoch: number;
3659
+ }
3660
+ /** A validated mark, accept, reject or forget, in the shape the review verbs already take. */
3661
+ type ExplicitReviewRequest = ReviewChangeRequest;
3662
+ /** What the writer-lease holder is handed: the same request and the same stable identity. */
3663
+ interface ExplicitReviewDelegation {
3664
+ readonly request: ExplicitReviewRequest;
3665
+ readonly operationId: string;
3666
+ }
3667
+ /**
3668
+ * Hand the direct request to the leaseholder and wait bounded for its answer (D-18). Injected as
3669
+ * an optional operation rather than built here: `knowledge-local` neither knows nor may know that a
3670
+ * unix socket exists. A host supplies its transport; the leaseholder's own composition supplies
3671
+ * nothing, which makes delegation recursion impossible by construction.
3672
+ */
3673
+ type ExplicitReviewDelegate = (delegation: ExplicitReviewDelegation) => Promise<ExplicitReviewResult>;
3674
+ interface ApplyExplicitReviewInput {
3675
+ readonly agentQaRoot: string;
3676
+ readonly knowledgeRoot: string;
3677
+ readonly request: ExplicitReviewRequest;
3678
+ /**
3679
+ * The caller's stable identity for this change, such as a mark intent's UUID. A retry with the
3680
+ * same identity is the same change: its evidence tokens, receipts and record ids repeat.
3681
+ */
3682
+ readonly operationId: string;
3683
+ /** The op making the change, stamped on Gate-3 receipts as `producedBy.opId`. */
3684
+ readonly opId: string;
3685
+ /** The caller's actual context. Recorded as-is; explicit authority never rewrites it. */
3686
+ readonly executionContext: ExecutionContext;
3687
+ readonly resolveBundleShortId?: ResolveBundleShortId;
3688
+ readonly delegate?: ExplicitReviewDelegate;
3689
+ readonly writerAuthority?: WriterAuthority;
3690
+ readonly resolvePromotionPosture?: ResolvePromotionPosture;
3691
+ readonly resolveTrustPriors?: () => TrustPriorTable | undefined;
3692
+ readonly now?: () => number;
3693
+ readonly delegateTimeoutMs?: number;
3694
+ }
3695
+ type ExplicitReviewResult = {
3696
+ readonly outcome: 'applied';
3697
+ readonly verb: KnowledgeReviewVerb;
3698
+ readonly changeId: string;
3699
+ /** The records the change added, confirmed or tombstoned, read back after the write. */
3700
+ readonly recordIds: readonly string[];
3701
+ readonly commitReceipt?: CommitReceipt;
3702
+ readonly forgetReceipt?: ForgetReceiptV1;
3703
+ } | {
3704
+ readonly outcome: 'target-not-found';
3705
+ readonly verb: KnowledgeReviewVerb;
3706
+ readonly changeId: string;
3707
+ readonly target: KnowledgeTargetRef;
3708
+ } | {
3709
+ readonly outcome: 'policy-materialize-refused';
3710
+ readonly verb: KnowledgeReviewVerb;
3711
+ readonly changeId: string;
3712
+ };
3713
+ /**
3714
+ * Apply an explicit knowledge change in the call (432-06, D-04/D-05/D-09/D-11).
3715
+ *
3716
+ * No envelope is written, claimed or reloaded. A holder commits with its own authority, a free lease is acquired and released, and a live holder is
3717
+ * handed the same validated request and identity, bounded, and is never stolen from. Retrying
3718
+ * the same `operationId` lands nothing twice and answers the same record ids.
3719
+ */
3720
+ declare function applyExplicitReview(input: ApplyExplicitReviewInput): Promise<ExplicitReviewResult>;
3721
+
3722
+ /** The one directory name receipts live under, beneath the agent-qa runtime root. */
3723
+ declare const DRAFT_RECEIPTS_DIR_NAME = "draft-receipts";
3724
+ /** Thrown when a content digest could not be safely turned into a filename. */
3725
+ declare class DraftReceiptDigestError extends Error {
3726
+ readonly contentDigest: string;
3727
+ constructor(contentDigest: string);
3728
+ }
3729
+ /** Thrown when a persisted receipt was written under a different schema version. */
3730
+ declare class DraftReceiptSchemaVersionError extends Error {
3731
+ readonly path: string;
3732
+ readonly foundVersion: unknown;
3733
+ constructor(path: string, foundVersion: unknown);
3734
+ }
3735
+ interface DraftReceiptsStoreOptions {
3736
+ /**
3737
+ * Where receipts are durable — `<configDir>/.agent-qa`. A FACTORY on the `createProposalsInbox`
3738
+ * precedent: the CLI resolves its root asynchronously and per call, so closing over one resolved
3739
+ * value would cache a fact this module does not own.
3740
+ */
3741
+ readonly resolveAgentQaRoot: () => Promise<string> | string;
3742
+ }
3743
+ interface DraftReceiptsStore {
3744
+ /** Persist a receipt under its own `draftRef.contentDigest`. Create-on-first-write. */
3745
+ put(receipt: DraftReceipt): Promise<void>;
3746
+ /** The receipt for these draft bytes, or `undefined` if none was ever written. */
3747
+ get(contentDigest: string): Promise<DraftReceipt | undefined>;
3748
+ /** Every stored receipt, ordered by digest so two calls render the same table. */
3749
+ list(): Promise<DraftReceipt[]>;
3750
+ }
3751
+ declare function draftReceiptsDir(agentQaRoot: string): string;
3752
+ /**
3753
+ * Bind the three receipt verbs over one agent-qa runtime root.
3754
+ *
3755
+ * Nothing opens here and nothing is created until the first write, which is what lets a
3756
+ * composition root bind this unconditionally without deciding whether a project has ever
3757
+ * evaluated a draft — an absent directory and an empty store are the same answer.
3758
+ */
3759
+ declare function createDraftReceiptsStore(options: DraftReceiptsStoreOptions): DraftReceiptsStore;
3760
+
3761
+ type RollbackKind = 'job' | 'campaign';
3762
+ interface RollbackCounterEvent {
3763
+ recordId: RecordId;
3764
+ evidenceToken: string;
3765
+ curationJobId: string;
3766
+ type: 'confirmed' | 'deprecated' | 'superseded' | 'promoted';
3767
+ }
3768
+ type RollbackPlannedEffect = {
3769
+ kind: 'deprecate-bundle';
3770
+ product: string;
3771
+ recordId: RecordId;
3772
+ } | {
3773
+ kind: 'retract-counter' | 'retract-window-close' | 'retract-promotion';
3774
+ product: string;
3775
+ recordId: RecordId;
3776
+ eventKey: string;
3777
+ evidenceToken: string;
3778
+ curationJobId: string;
3779
+ };
3780
+ interface RollbackBundleTarget {
3781
+ product: string;
3782
+ recordId: RecordId;
3783
+ }
3784
+ interface RollbackAffectedSet {
3785
+ operationId: string;
3786
+ /** Compatibility summary; plannedEffects is the product-qualified mutation authority. */
3787
+ mintedBundleIds: RecordId[];
3788
+ counterEvents: RollbackCounterEvent[];
3789
+ plannedEffects: RollbackPlannedEffect[];
3790
+ /** Projection healing targets; these never require a new rollback ordinal on their own. */
3791
+ projectionTargets: RollbackBundleTarget[];
3792
+ products: string[];
3793
+ }
3794
+ interface RollbackReport {
3795
+ receiptId: string;
3796
+ inverseCount: number;
3797
+ restoredVictims: RecordId[];
3798
+ deprecatedBundles: RecordId[];
3799
+ /** Bundles whose surviving promoted events were cascade-retracted after support loss. */
3800
+ retractedPromotions: RecordId[];
3801
+ /** Auditable exact resolution that drove inverse construction. */
3802
+ affected: RollbackAffectedSet;
3803
+ }
3804
+ type RollbackMutationKind = 'append' | 'projection';
3805
+ interface RollbackHooks {
3806
+ /** Test/chaos seam after canonical resolution and causal preflight, before the first write. */
3807
+ afterResolution?: () => void;
3808
+ /** Test/chaos seam after one actual append or projection mutation completes. */
3809
+ afterMutation?: (kind: RollbackMutationKind, recordId: RecordId) => void;
3810
+ }
3811
+ interface RollbackBaseInput {
3812
+ knowledgeRoot: string;
3813
+ resolvedAtOrdinal: number;
3814
+ /** Governed maintenance callers fence this exact lease at every mutation boundary. */
3815
+ writerAuthority?: WriterLeaseAuthority;
3816
+ /** Preview fingerprint revalidated by the mutator immediately before its first write. */
3817
+ expectedAffectedSetFingerprint?: string;
3818
+ hooks?: RollbackHooks;
3819
+ }
3820
+ interface RollbackCurationJobInput extends RollbackBaseInput {
3821
+ curationJobId: string;
3822
+ }
3823
+ interface RollbackCampaignInput extends RollbackBaseInput {
3824
+ fromOrdinal?: number;
3825
+ toOrdinal?: number;
3826
+ anchorClusterKey?: string;
3827
+ originClass?: OriginClass;
3828
+ }
3829
+ type ResolveRollbackAffectedSetInput = RollbackCurationJobInput | RollbackCampaignInput;
3830
+ /**
3831
+ * CR-02: a rollback must never attest success at a resolution ordinal that is powerless to
3832
+ * change lifecycle status. At equal buildOrdinal the trust fold's fixed STATUS_PRECEDENCE keeps
3833
+ * active above deprecated, and an older event can never replace a newer status, so a
3834
+ * non-strictly-later resolvedAtOrdinal cannot causally invert a minted bundle.
3835
+ */
3836
+ declare class RollbackCausalityError extends Error {
3837
+ readonly offendingRecordIds: RecordId[];
3838
+ readonly minimumRequiredOrdinal: number;
3839
+ constructor(offendingRecordIds: RecordId[], minimumRequiredOrdinal: number);
3840
+ }
3841
+ declare class RollbackPlanChangedError extends Error {
3842
+ readonly name = "RollbackPlanChangedError";
3843
+ readonly expectedFingerprint: string;
3844
+ readonly actualFingerprint: string;
3845
+ constructor(expectedFingerprint: string, actualFingerprint: string);
3846
+ }
3847
+ /** Stable identity for one selector and its product-qualified source-effect universe. */
3848
+ declare function rollbackReceiptId(kind: RollbackKind, keys: Readonly<Record<string, unknown>>, effectKeys?: readonly string[]): string;
3849
+ declare function rollbackAffectedSetFingerprint(affected: RollbackAffectedSet): string;
3850
+ /** Resolve the canonical rollback preview without appending inverse events or touching indexes. */
3851
+ declare function resolveRollbackAffectedSet(input: ResolveRollbackAffectedSetInput): RollbackAffectedSet;
3852
+ /** Resolve and append the ratified inverse map for one curation job. */
3853
+ declare function rollbackCurationJob(input: RollbackCurationJobInput): RollbackReport;
3854
+ /** Resolve and append the inverse map for an AND-composed provenance campaign. */
3855
+ declare function rollbackCampaign(input: RollbackCampaignInput): RollbackReport;
3856
+
3857
+ /**
3858
+ * 378-17 canonical mutable-target order, additively extended by 379 D-03:
3859
+ * record, provenance, outcomes, measurements, relations, then manifest.
3860
+ */
3861
+ declare const MUTABLE_BUNDLE_TARGETS: readonly ["record.md", "provenance.json", "evidence/outcomes.jsonl", "evidence/measurements.json", "evidence/gates.jsonl", "relations.json", "manifest.json"];
3862
+
3863
+ type MarkTriage = {
3864
+ kind: 'quarantined';
3865
+ class: MarkQuarantineClass;
3866
+ producer: string;
3867
+ } | {
3868
+ kind: 'learnable';
3869
+ rootCause?: {
3870
+ class: VerdictClass;
3871
+ producer: string;
3872
+ };
3873
+ };
3874
+ declare function triageMark(member: PerTestOutcome): MarkTriage;
3875
+ declare function servedIdsOn(step: StepEvidence): string[];
3876
+ declare function procedureReadOn(member: PerTestOutcome, stepIndex: number): string | undefined;
3877
+ declare function assertionDrift(member: PerTestOutcome, step: StepEvidence): boolean;
3878
+ interface ServedRecordFacts {
3879
+ recordId: string;
3880
+ kind: BundleKind;
3881
+ subtype?: FactSubtype;
3882
+ anchors: Anchor[];
3883
+ scope: ScopeKey;
3884
+ summary: string;
3885
+ distilledEvidenceRef: ArtifactRef;
3886
+ originStage: OriginStage;
3887
+ }
3888
+ type FixSelection = {
3889
+ fix: 'fact' | 'caution';
3890
+ target: ServedRecordFacts;
3891
+ } | {
3892
+ fix: 'procedure-hint';
3893
+ target: ServedRecordFacts;
3894
+ } | {
3895
+ fix: 'test-edit';
3896
+ } | {
3897
+ fix: 'none';
3898
+ checked: NoDraftableFixChecked;
3899
+ };
3900
+ declare function selectFixKind(input: {
3901
+ kind: Mark;
3902
+ member: PerTestOutcome;
3903
+ step: StepEvidence;
3904
+ stepIndex: number;
3905
+ served: ServedRecordFacts[];
3906
+ procedure?: ServedRecordFacts;
3907
+ }): FixSelection;
3908
+ interface ApplyFeedbackInput {
3909
+ mark: Pick<MarkReceipt, 'markId' | 'runId' | 'runSlug' | 'testIndex' | 'testId' | 'stepIndex' | 'kind' | 'note'>;
3910
+ handoff: CompletedRunHandoff;
3911
+ resolvedStepIndex: number;
3912
+ served: ServedRecordFacts[];
3913
+ procedure?: ServedRecordFacts;
3914
+ testRef?: string;
3915
+ }
3916
+ declare function applyFeedback(input: ApplyFeedbackInput): ImprovementPlan;
3917
+
3918
+ interface FloorProseInput {
3919
+ kind: Mark;
3920
+ fix: 'fact' | 'caution' | 'procedure-hint';
3921
+ stepText: string;
3922
+ verifierReasoning: string;
3923
+ servedSummary?: string;
3924
+ note?: string;
3925
+ }
3926
+ declare function floorProse(input: FloorProseInput): {
3927
+ summary: string;
3928
+ body: string;
3929
+ };
3930
+
3931
+ interface AppendMarkEvidenceInput {
3932
+ knowledgeRoot: string;
3933
+ executionContext: ExecutionContext;
3934
+ lease: {
3935
+ leaseId: string;
3936
+ epoch: number;
3937
+ };
3938
+ mark: Pick<MarkReceipt, 'markId' | 'runId' | 'kind'>;
3939
+ servedRecordIds: readonly string[];
3940
+ /** Historical observation hashes require the original member/step coordinates. */
3941
+ handoff?: CompletedRunHandoff;
3942
+ /** Host-derived chainJobIdsForRun(runId, idSource).curateMemory; never guessed here. */
3943
+ curationJobIds?: readonly string[];
3944
+ /** Resuming one mark reports its complete logical effect, including already durable events. */
3945
+ includeAlreadyAppended?: boolean;
3946
+ }
3947
+ declare function appendMarkEvidence(input: AppendMarkEvidenceInput): Promise<AppendMarkEvidenceOutcome>;
3948
+
3949
+ declare class PolicyTextRefusedError extends Error {
3950
+ constructor();
3951
+ }
3952
+ declare function policySectionMarkers(recordId: string): {
3953
+ open: string;
3954
+ close: string;
3955
+ };
3956
+ declare function policyShelfPath(knowledgeRoot: string, scope: string): string;
3957
+ interface MaterializePolicyInput {
3958
+ writerAuthority: CurrentWriterAuthority;
3959
+ knowledgeRoot: string;
3960
+ recordId: string;
3961
+ scope: string;
3962
+ ruleText: string;
3963
+ title?: string;
3964
+ /** Recovery validates a previously authored section and must never recreate a missing one. */
3965
+ existingOnly?: boolean;
3966
+ }
3967
+ type MaterializeOutcome = {
3968
+ kind: 'written' | 'unchanged';
3969
+ relPath: string;
3970
+ sectionBytes: number;
3971
+ };
3972
+ /**
3973
+ * The authored shelf is append-only under the caller's writer lease. An existing section ID
3974
+ * always wins, including when its human-edited body differs. Never rewrite that body.
3975
+ * commitArtifacts governs binary bundle artifacts, not policy text or git commits.
3976
+ * The authored text is written as given; no detector scans it (Phase 441, D-15).
3977
+ */
3978
+ declare function materializePolicy(input: MaterializePolicyInput): MaterializeOutcome;
3979
+
3980
+ type LocalKnowledgeStore = KnowledgePort;
3981
+ type LocalKnowledgeReader = KnowledgeHintReader;
3982
+
3983
+ export { ADDRESSING_LEDGER_BUSY_TIMEOUT_MS, ADDRESSING_LEDGER_DB_FILE, ADDRESSING_LEDGER_USER_VERSION, APPLY_DELEGATE_TIMEOUT_MS, ARTIFACT_RUN_CAP_BYTES, ARTIFACT_STEP_CAP_BYTES, AUTHORED_CHECKPOINT_SCHEMA_VERSION, type AcquireEmbedderOptions, type AcquireEmbedderOutcome, type AcquireWriterLeaseOptions, type AcquireWriterLeaseOutcome, type AddressingLedger, AddressingLedgerSchemaVersionError, type AppendMarkEvidenceInput, AppendMarkEvidenceOutcome, type AppendOutcome, ApplyDelegateTimeoutError, ApplyDelegateUnavailableError, type ApplyExplicitReviewInput, type ApplyFeedbackInput, type ApplyIndexDeltaForBundleInput, type ApplyRunOutcomesResult, type ArtifactCopyInput, type ArtifactCopyResult, type AuthorOutcome, type AuthoredCheckpoint, type AuthoredDeltaCheckpoint, BLAST_RADIUS_MAX_SUPERSEDES_PER_ANCHOR_PER_DRAIN, type BuildCurationCandidateAddressFieldsInput, type BundleProvenanceInput, type BundleStoreConfig, CHANNEL_1_SERVING_LEG, CHANNEL_3_CAUTION_LEG, CITED_ID_INTEGRITY_CONDITIONS, CLAIMED_SUBDIR, COMMIT_INTENT_SCHEMA_VERSION, CURATION_JOB_STATE_SCHEMA_VERSION, CURATION_QUEUE_SCHEMA_VERSION, type CanonicalBundleStateDigests, type CitedIdIntegrityCondition, type ClaimNextJobOptions, type ClaimNextJobOutcome, type ClaimedCurationJob, type CommitCurationDependencies, type CommitCurationJob, type CommitCurationOutcome, type CopiedArtifact, CorruptEpochStateError, type CorruptEpochStateReason, type CountRemainingCurationWorkOptions, type CreateLocalKnowledgeStoreConfig, type CurationArtifactDescriptor, type CurationArtifactRole, type CurationCandidateAddressFields, type CurationCandidateArtifact, type CurationCandidatePayload, type CurationCandidateProjection, type CurationDrainLifecycleEvent, type CurationEnqueueRefusalReason, type CurationJobLifecycle, type CurationJobState, type CurationJobView, type CurationProjectionArtifactSource, type CurationQuarantineReason, type CurationRemainingWork, type CuratorAuthor, type CurrentWriterAuthority, type CurrentWriterAuthorityHooks, DRAFT_RECEIPTS_DIR_NAME, DRAIN_BUDGET_MS, type DeferReason, type DiscoverEmbedderWorkerOptions, type DisposeDeltaInput, type DispositionCandidateContext, type DispositionDrainState, type DispositionOutcome, type DispositionProfile, DraftReceiptDigestError, DraftReceiptSchemaVersionError, type DraftReceiptsStore, type DraftReceiptsStoreOptions, type DrainFailure, type DrainFailureStage, type DrainNoop, type DrainOptions, type DrainPromotionOutcome, type DrainReport, type DrainableCurationWork, EMBED_CRASH_VECTORLESS_THRESHOLD, ENTITY_INDEX_DB_FILE, ENTITY_INDEX_SWEEP_INTERVAL_MS, ENTITY_INDEX_USER_VERSION, EPOCH_FILE, type EmbedCacheLeg, type EmbedCacheLookup, type EmbedCacheMode, type EmbedCachePut, type EmbedCacheWrapperOptions, type EmbedderAxis, EmbedderPort, type EmbedderProbes, type EmbedderRung, EmbedderWorkerIncarnationVerdict, type EmbedderWorkerPolicy, EmbedderWorkerRegistration, type EmptyDeltaCause, type EnqueueCurationCandidateProjectionOptions, type EnqueueCurationCandidateProjectionOutcome, type EntityAddress, type EntityIndexHandle, EntityIndexSchemaVersionError, type EntityIndexSweepReport, type EntitySighting, type EntitySightingSource, type ExplicitReviewDelegate, type ExplicitReviewDelegation, type ExplicitReviewRequest, type ExplicitReviewResult, type ExtractStepAnchors, FORGET_SCRUBBED_PROJECTIONS, type FailureRecurrence, type FingerprintRecurrence, type FingerprintWindowIdentity, type FingerprintWindowRow, type FixSelection, FloorAssetLoadError, type FloorAssetLoadErrorCode, type FloorAssetLoadOptions, type FloorAssets, FloorEmbeddingError, type FloorEmbeddingErrorCode, type FloorProseInput, type FoldRunOutcomesOptions, type FoldRunOutcomesReport, type FoldSuiteFingerprintsOptions, GC_SUPERSEDED_RETAIN_K, type GateHorizon, type GcPassReport, type GcSummary, type HeartbeatLeaseOutcome, type IndexAxis, IndexFace, IndexReaderConnection, type InspectDrainableCurationWorkOptions, JOBS_SUBDIR, KnowledgeCommitError, KnowledgePromotionError, type KnowledgePromotionErrorReason, KnowledgeReader, KnowledgeStoreNotImplementedError, KnowledgeUnsupportedDeltaError, LAST_OUTCOMES_LIMIT, LEASE_FILE, LEASE_GUARD_DIR, LEASE_HEARTBEAT_TTL_MS, type LeaseOperationOptions, LoadedBundle, type LocalKnowledgeReader, type LocalKnowledgeStore, MEASUREMENTS_REL_PATH, MODEL_CACHE_DIR_ENV_VAR, MUTABLE_BUNDLE_TARGETS, MUTATION_LOCK_BUSY_TIMEOUT_MS, MUTATION_LOCK_TAKEOVER_MS, type MaintenanceBudget, type MaintenanceOperations, type MaintenanceSkipReason, type MaintenanceTailFailure, type MaintenanceTailResult, type MalformedClaimedCurationJob, MarkPlanIntegrityError, type MarkTriage, type MaterializeOutcome, type MaterializePolicyInput, type MigrationOutcome, NativeEmbeddingDimensionError, NativeEmbeddingModuleLoadError, type NeighborRecord, type OpenAddressingLedgerOptions, type OpenEntityIndexOptions, PAYLOAD_BENEFITS, PAYLOAD_STATE_VALIDITIES, PURGE_RECEIPT_SCHEMA_VERSION, type PersistAuthoredCheckpointOptions, type PersistAuthoredCheckpointOutcome, PolicyTextRefusedError, type ProcedureDispositionEvidence, type ProjectRunOutcomesOptions, type PromotionOutcomeReport, type PublishBundleInput, type PublishBundleOutcome, type PurgeAffectedSet, PurgeChronologyRequiredError, PurgeCompletionVerificationError, PurgeQueueIdentityError, type PurgeReceipt, PurgeReceiptSchema, type PurgeRecordInput, type PurgeRecordOutcome, PurgeTargetChangedError, PurgeTargetIntegrityError, QUARANTINE_MANUAL_ENTRY_CLASS, QUARANTINE_MANUAL_ENTRY_FINGERPRINT, QUARANTINE_WINDOW_ORDINALS, QUEUED_SUBDIR, QUEUE_DIR, QUEUE_MAX_EMITS_PER_RUN, QUEUE_MAX_JOBS_PER_SCOPE, QuarantineAppendOutcome, type QuarantineClaimedOptions, type QuarantineDetectionOptions, QuarantineDetectionSighting, type QuarantineFoldOutcome, QuarantineIdentityRefusedError, type QuarantineOperationsOptions, type QueuedCanaryPayloadRead, RESOLVER_CANDIDATE_K, RESOLVER_FUSED_SCORE_NORMALIZER, RESOLVER_LEG_WEIGHT_DENSE, RESOLVER_LEG_WEIGHT_LEXICAL, RESOLVER_LEXICAL_MIN_BM25, RESOLVER_STRONG_HIT_MARGIN, RESOLVER_STRONG_HIT_MIN_SCORE, RUN_OUTCOMES_PROJECTION_VERSION, type ReadCurationCandidatePayloadOutcome, type ReadCurationEffectsOptions, type ReadJobStateOutcome, type ReadWriterLeaseOutcome, type RebuildIndexHooks, type RebuildIndexOptions, type RebuildMutationFamily, type RebuildReport, RebuildRequest, type RebuildRunOutcomesOptions, type RebuildRunOutcomesReport, type ReconcileRunOutcomesProjectionOptions, type ReconcileRunOutcomesProjectionResult, RefusedQuarantineLine, type RelationIntent, type ReleaseLeaseOutcome, type RenderBundlePartsInput, type RenderedBundlePart, type RequeueClaimedOptions, type ResolveBundleShortId, type ResolveEmbedderCapabilityOptions, type ResolveEntityRefDeps, type ResolveRetrievalRungOptions, type ResolveRollbackAffectedSetInput, type RetrievalQuery, type RetrievalQueryInput, type RetrieveOptions, type RetrieveResult, type ReviewChangeRequest, ReviewTargetVanishedError, type RollbackAffectedSet, type RollbackCampaignInput, RollbackCausalityError, type RollbackCounterEvent, type RollbackCurationJobInput, type RollbackKind, RollbackPlanChangedError, type RollbackPlannedEffect, type RollbackReport, type RunKnowledgeGcHooks, type RunKnowledgeGcInput, type RunMaintenanceTailOptions, type RunOutcomeFailureKey, type RunOutcomeTestDelta, type RunOutcomesProjection, SEARCH_BEFORE_CREATE_MAX_QUERIES, SERVING_TRACE_LEGS, SUITE_FINGERPRINTS_TABLE, type ServedRecordFacts, type SetActiveEmbeddingProfileOptions, type SkippedArtifact, type SuiteFingerprintIndex, type SuiteFingerprintIndexOptions, type SuiteFingerprintTestDelta, type SuiteFingerprintsProjection, type TargetRename, type TestOutcomeEntry, type TestOutcomeHistory, type TestOutcomeIdentity, type UnreclaimableMutationLockOwner, UserDeltaCitationIntegrityError, VecCapability, WRITER_AUTHORITY_DB_FILE, type WorkerDiscoveryResult, type WriteJobStateOptions, WriterAuthorityLostError, type WriterAuthorityLostReason, type WriterLease, type WriterLeaseAuthority, type WriterLeaseFreshness, type WriterPassReport, acquireEmbedder, acquireWriterLease, appendGateReceipt, appendMarkEvidence, appendMeasurementObservation, appendOutcomeEvent, appendPromotionReceipt, appendRelation, applyExplicitReview, applyFeedback, applyIndexDeltaForBundle, applyRunOutcomes, applySuiteFingerprints, assertionDrift, authoredCheckpointSchema, buildCurationCandidateAddressFields, buildRetrievalQuery, candidateContentAddress, claimNextJob, claimedCandidateDir, claimedLeaseDir, claimedRoot, commitCurationDelta, copyArtifacts, countPendingCurationJobs, countRemainingCurationWork, createDraftReceiptsStore, createFloorEmbedder, createLocalKnowledgeStore, createMemorySightingSource, createNativeEmbedder, createQuarantineOperations, createSuiteFingerprintIndex, curationArtifactDescriptorSchema, curationArtifactRoleSchema, curationCandidatePayloadSchema, curationCandidateProjectionSchema, curationJobStateSchema, deriveIndexRowFromBundle, deriveWriterLeaseFreshness, describeAllEntities, discoverEmbedderWorker, disposeDelta, draftReceiptsDir, drainQueue, dropEmbedCacheForModel, embedPendingEntities, enqueueCurationCandidate, enqueueCurationCandidateProjection, ensureEmbedCacheTable, entityIndexDbPath, failureRecurrence, fingerprintRecurrence, floorProse, foldCurationSnapshot, foldRetrievalRung, foldRunOutcomes, foldSuiteFingerprints, getCachedEmbedding, getEntityDescriptor, guardPurgeReceipt, heartbeatLease, inspectDrainableCurationWork, isLeaseStale, jobStatePath, jobsRoot, listClaimedLeases, listQueuedCandidates, listTestsInFile, loadFloorAssets, materializePolicy, openAddressingLedger, openEntityIndex, parseAuthoredCheckpoint, persistAuthoredCheckpoint, policySectionMarkers, policyShelfPath, priorBuildOutcome, procedureReadOn, projectRunOutcomes, publishBundle as publishBundleForTest, purgeAffectedSetFingerprint, purgeReceiptSchema, purgeRecord, putCachedEmbedding, quarantineClaimed, queueRoot, queueStagingRoot, queuedCandidateDir, queuedRoot, readCanonicalBundleStateDigests, readCurationCandidatePayload, readCurationEffects, readFingerprintWindow, readJobState, readLatestBuildOrdinal, readQueuedCanaryPayload, readRebuildRequest, readTestOutcomeHistory, readWriterLease, rebuildEntityIndex, rebuildIndex, rebuildRunOutcomes, reconcileRunOutcomesProjection, refreshEntity, refreshWriterLeaseAuthority, releaseLease, renderBundleParts, requeueClaimed, resetEmbedderCapability, resolveDispositionProfile, resolveEmbedderCapability, resolveEntityRef, resolveModelCacheDir, resolveProcessIncarnation, resolvePurgeAffectedSet, resolveRetrievalRung, resolveRollbackAffectedSet, retrieve, rollbackAffectedSetFingerprint, rollbackCampaign, rollbackCurationJob, rollbackReceiptId, runKnowledgeGc, runMaintenanceTail, selectFixKind, servedIdsOn, setActiveEmbeddingProfile, sightEntity, sliceAuthoredCheckpoint, suiteFingerprintDeltas, sweepEntityIndex, triageMark, verifyEmbedderWorkerIncarnation, widenAuthoredDeltaCheckpoint, withCurrentWriterAuthority, wrapEmbedderWithCache, writeJobState, writeMeasurementBaseline };