/** Coordinate that groups results: {unit_id, lens, pass_id}. */ export interface IdentityKeyInput { unit_id: string; lens: string; pass_id: string; } /** * Inputs to `buildTaskContentSignature`. Any task-defining content may be passed; * `task_id` and provenance/timestamp fields are accepted but explicitly STRIPPED * (FC-002) so renumbering a task id never changes the signature. */ export interface TaskContentSignatureInput { [key: string]: unknown; task_id?: unknown; } /** * The closed set of emit paths a result can originate from. The discriminator * recipe is keyed off this enum (C-002) — callers pick the source, never the * discriminator string. */ export type ResultEmitSource = 'base' | 'deepening' | 'steward' | 'redispatch'; /** * Inputs to `buildResultContentDiscriminator`. Discriminated union: `task_id` * is required (at the type level) for `deepening`/`steward`, `attempt` for * `redispatch`, and neither for `base` — compile-time enforcement of the * constraints the runtime already checks (prevents the class of bug that * caused the deepening-loop: omitting task_id silently collides keys). */ export type ResultContentDiscriminatorInput = ResultDiscriminatorBase | ResultDiscriminatorRedispatch | ResultDiscriminatorDeepening | ResultDiscriminatorSteward; interface ResultDiscriminatorCommon { /** * Per-split discriminator (N-IDEMPOTENCY). File-split sibling tasks of one * unit+lens+pass share the grouping coordinate but carry DISTINCT task_ids. * Without this component every sibling base result would derive the SAME * idempotencyKey. An EMPTY component yields a discriminator BYTE-IDENTICAL to * the legacy lone-base value; a non-empty one appends so siblings diverge. * Tool-owned, never caller-chosen. */ split_discriminator?: string; } interface ResultDiscriminatorBase extends ResultDiscriminatorCommon { source: 'base'; } interface ResultDiscriminatorRedispatch extends ResultDiscriminatorCommon { source: 'redispatch'; attempt: number; } interface ResultDiscriminatorDeepening extends ResultDiscriminatorCommon { source: 'deepening'; task_id: string; } interface ResultDiscriminatorSteward extends ResultDiscriminatorCommon { source: 'steward'; task_id: string; } /** * Build a `ResultContentDiscriminatorInput` from an emit result that carries * all fields in a flat shape. Bridges the superset-of-fields pattern (callers * that have `source`, `attempt`, `task_id`, `split_discriminator` from a * result record) to the discriminated union (callers constructing from * scratch). */ export declare function resultDiscriminatorForEmit(source: ResultEmitSource, opts: { attempt?: number; task_id?: string; split_discriminator?: string; }): ResultContentDiscriminatorInput; /** Inputs to `idempotencyKey`: the identity coordinate + the result discriminator. */ export interface IdempotencyKeyInput extends IdentityKeyInput { result_content_discriminator: string; } /** Inputs to `contentKey`: everything `idempotencyKey` needs + the task signature. */ export interface ContentKeyInput extends IdempotencyKeyInput { task_content_signature: string; } /** * TOOL-OWNED task-content signature (FC-002 / fail-1). Derives a stable signature * from the task-defining content ONLY — `task_id` and timestamp/provenance fields * are stripped, then the remaining fields are run through the shared * `normalizeForMetadataHash` + canonical `stableStringify` (the single serializer, * INV-CK-2), so reordered keys produce an identical signature. */ export declare function buildTaskContentSignature(input: TaskContentSignatureInput): string; /** * Canonicalize a split-discriminator component OS-agnostically (N-IDEMPOTENCY). * A large-file split task_id embeds the file path (`…:`), so the raw * value carries the host's path separator — `unit/a.ts` on POSIX vs `unit\a.ts` * on win32 for the SAME logical split. Backslashes are normalized to forward * slashes so the idempotencyKey is byte-identical cross-platform (a win32 run and * a POSIX run of the same split must NOT mint two records). An empty/whitespace * component canonicalizes to the empty string (the lone-base sentinel). This is * the SOLE canonicalization point — callers pass the raw component. */ export declare function canonicalSplitDiscriminator(component: string | undefined): string; /** * TOOL-OWNED result-content discriminator (C-002 / fail-3). The discriminator * string is derived from the emit-source enum (plus the attempt counter for a * re-dispatch), never chosen by a caller — because it feeds the signature-STABLE * idempotencyKey, an operator-chosen value would be a correctness hazard. * * The per-split component (N-IDEMPOTENCY) folds in identically for every emit * source: an EMPTY canonical component reproduces the legacy lone-base / * lone-source string BYTE-FOR-BYTE (no key churn for non-split tasks), while a * non-empty component appends a `#split:` suffix so file-split sibling * tasks sharing a {unit_id, lens, pass_id} coordinate diverge into distinct * idempotencyKeys instead of colliding through the INV-2 gate. */ export declare function buildResultContentDiscriminator(input: ResultContentDiscriminatorInput): string; /** * Derive the per-split discriminator component from a task_id (N-IDEMPOTENCY). * Split sibling task_ids are `${scope}:${lens}:part-N` or `${scope}:${lens}:`; * a lone (non-split) task is exactly `${scope}:${lens}`. The component is the * suffix AFTER the trailing `:${lens}` segment — empty for a lone task (⇒ * byte-identical lone-base key), the split-distinguishing tail otherwise. Returns * empty when the task_id/lens are missing or the expected shape is absent (fail * safe to the legacy lone key rather than mint a spurious split). The result is * the RAW tail; `buildResultContentDiscriminator` canonicalizes it. */ export declare function splitDiscriminatorFromTaskId(task_id: string | undefined, lens: string | undefined): string; /** * sha256 over the canonical {unit_id, lens, pass_id} tuple (INV-CK-3). EXCLUDES * task_id and all volatile fields. ONE-TO-MANY grouping key (INV-CK-4) — never a * primary key. Throws if any component is missing (fail-3). */ export declare function identityKey(input: IdentityKeyInput): string; /** * sha256 over {identity_key, result_content_discriminator} (INV-CK-7). SIGNATURE- * STABLE: the task_content_signature is deliberately NOT an input, so a benign * content edit does not move it (fail-4). This is the key O2 ingests on. equal * idempotencyKey ⟹ equal identityKey, since identity_key is a signed input. */ export declare function idempotencyKey(input: IdempotencyKeyInput): string; /** * sha256 over {idempotency_key, task_content_signature} (INV-CK-5). SIGNATURE- * SENSITIVE: bumps when the idempotencyKey OR the task_content_signature changes, * so it drives staleness (CE-001 / inv-5). equal contentKey ⟹ equal idempotencyKey * ⟹ equal identityKey, since each is a signed input of the next. */ export declare function contentKey(input: ContentKeyInput): string; /** * Mint a fresh per-record instance id (CE-001 / fail-2). The ledger keys on this, * so every appended record is distinct and identityKey/idempotencyKey are never * primary keys. This is the ONLY non-deterministic helper in the seam — the three * key derivations above are pure. */ export declare function newInstanceId(): string; export {}; //# sourceMappingURL=contentKey.d.ts.map