import { type GateEvidence } from './handoff-types.js'; /** * Why a `gateEvidence` read produced what it produced. * * `ok` is the only success. `field-absent` is NOT a failure — a capsule with * no declaration is a valid capsule (every pre-B1 handoff) — while * `field-not-map` and `value-not-string` are declarations that exist and are * broken, and `file-missing` / `read-error` / `frontmatter-malformed` mean * nothing could be read at all. Collapsing any two of these is the defect * this reader was rewritten to remove. */ export type HandoffGateEvidenceStatus = 'ok' | 'file-missing' | 'read-error' | 'frontmatter-malformed' | 'field-absent' | 'field-not-map' | 'value-not-string'; /** * Discriminated result. `status` alone decides the branch, so a caller that * only wants "is there a usable declaration?" checks `result.status === 'ok'` * and reads `result.evidence` — and a caller that must refuse corrupt * declarations gets `keys` / `actualType` / `reason` to name the problem in * its failure message. */ export type HandoffGateEvidenceResult = { readonly status: 'ok'; /** Only the five known keys. Absent key = not declared. An EMPTY map * on disk yields an empty `evidence`, not an error: the field was * written and declares nothing, which is a different fact from * `field-absent` and is reported as such. */ readonly evidence: GateEvidence; /** Keys present in the YAML that are not one of the five. Sorted. * Reported rather than dropped: a typo'd key is why a required gate * reads as "not declared", and this is the only place that can say so. * The serializer cannot emit these, so they are hand-authored only. */ readonly unknownKeys: readonly string[]; } | { readonly status: 'file-missing'; } | { readonly status: 'read-error'; readonly reason: string; } | { readonly status: 'frontmatter-malformed'; readonly reason: string; } | { readonly status: 'field-absent'; } /** `actualType` is the YAML type found (`array`, `null`, `string`, …) — * `array` is the pre-B1 shape, present in old test fixtures only. */ | { readonly status: 'field-not-map'; readonly actualType: string; } /** At least one value was not a string; `keys` names every offender. The * whole declaration is refused rather than filtered down to its good * entries — dropping a malformed value is the collapse this replaced. */ | { readonly status: 'value-not-string'; readonly keys: readonly string[]; }; /** * THE shape rule for a `gateEvidence` value — one home, two callers. * * `isGateEvidenceMap`, added by B1, and this module's own inline checks) with * the SAME accept/reject boundary but DIFFERENT results: the same bytes * produced a map WITH unknown keys through `readHandoff` and a map WITHOUT * them through the reader, and `gateEvidence:` (null) threw on one path and * reported a status on the other. That falsified the invariant * `handoff-service.ts` stated about itself. Both paths now call this, and * only the REFUSAL MECHANISM differs (a status vs a throw) — which is the * pre-existing, documented policy split between the throwing `readHandoff` * and the permissive reader, not a second opinion about the shape. * * `absent` is a first-class kind rather than an error: a capsule that * declares nothing is valid, and (invariant 1 of B1) every pre-B1 handoff * is one. */ export type GateEvidenceShape = { readonly kind: 'absent'; } | { readonly kind: 'not-map'; readonly actualType: string; } | { readonly kind: 'value-not-string'; readonly keys: readonly string[]; } | { readonly kind: 'ok'; readonly evidence: GateEvidence; readonly unknownKeys: readonly string[]; }; /** Classify a `gateEvidence` VALUE (already located — see the reader for the * fence/YAML/presence layers, which are file-level concerns, not shape). */ export declare function classifyGateEvidence(value: unknown): GateEvidenceShape; /** * Read + classify the `gateEvidence` map out of `filePath`. Never throws. * See the header for the permissive contract and * `HandoffGateEvidenceResult` for the outcomes. */ export declare function readHandoffGateEvidence(filePath: string): Promise;