/** * The audit draw of the shared submission core: how a GATE lane's bound path is * computed, and where the expected-set / ledger for those lanes are persisted. * * Two facts about the audit gates shape everything here: * * 1. **They carry no run id.** `NextStepParams` has none, and threading one * into every gate would change ten signatures to carry a value the gates * never use. Their submissions are therefore ARTIFACTS-DIR-scoped: the * constant `AUDIT_GATE_SUBMISSION_SCOPE` stands in for run identity, so the * same gate lane in the same artifacts dir denotes the same submission * across `next-step` calls. That is not a shortcut — it is exactly the * K-of-N resume property, which requires a re-emitted step to re-declare * the identical bound path rather than mint a new one and re-ask for work * the host already delivered. * * 2. **The lane id is the only join key.** The emitter and the gate reader * never meet; they agree because both derive the id from the lane id * through this module. A lane id is declared once in this module's * `GATE_LANES` table and used verbatim on both sides. * * The bound path is DERIVED, not looked up. The persisted expected set states * what is owed (and feeds the diff and the ledger), but a gate that could only * read a submission when a bookkeeping file happened to exist would lose a * valid submission the first time that file was absent — so derivation is the * mechanism and the record is the record. */ import { z } from "zod"; import { type CharterKind, type SubmissionIssueCode, type SubmissionRoots } from "audit-tools/shared"; import type { AuditHostIngestIssue } from "../validation/ingestIssueCodes.js"; /** * Gate lane ids — the single join key between an emitter and its gate reader. * * The two never meet: an emitter writes a lane's prompt and declares where the * answer goes, and (possibly several `next-step` calls later) the gate reader * looks for it. They agree because both derive the bound path from the lane id * through this module. Declared once, here, so no site can spell a lane one way * on the emit side and another on the read side. */ export declare const GATE_LANES: { readonly analyzer_consent: "analyzer_consent"; readonly analyzer_decisions: "analyzer_decisions"; readonly edge_reasoning: "edge_reasoning"; readonly design_review_legacy: "design_review_legacy"; readonly design_review_contract: "design_review_contract"; readonly design_review_conceptual: "design_review_conceptual"; readonly critical_flow_fallback: "critical_flow_fallback"; readonly intent_equivalence: "intent_equivalence"; readonly synthesis_narrative: "synthesis_narrative"; readonly charter_delta: "charter_delta"; readonly charter_clarification: "charter_clarification"; readonly systemic_challenge: "systemic_challenge"; }; /** The blind per-kind charter lanes (one per estimator channel). */ export declare function charterExtractionLane(kind: CharterKind): string; /** Recover the charter kind a `charter_extraction_*` lane id denotes. */ export declare function charterKindForLane(lane: string): CharterKind | undefined; export declare const CONCEPTUAL_PERSPECTIVE_LANE_PREFIX = "design_review_conceptual_p"; /** * The deep conceptual pass's independent perspective lanes. Their COUNT is * resolved at emit from confirmed intent, so the ids are generated rather than * enumerated. * * The id carries the ROUND — a digest of the upstream content the round asks * about — because a perspective is the one lane class whose reuse is WRONG. * Every other lane's identity is deliberately stable so a re-emitted step * re-declares the same bound path (K-of-N resume); a perspective, though, is a * fresh independent reading of the artifacts as they now stand. Keyed on the * index alone, a re-review after staleness would find the PREVIOUS round's * submission at the bound path, skip the lane, leave its prompt unrewritten, * and hand the judge the old round's findings as if they were this round's. * Same round, same digest, same id — so resume WITHIN a round still works. */ export declare function conceptualPerspectiveLane(index: number, roundToken: string): string; /** * The round token every perspective lane in one deep conceptual emission * shares: a digest of exactly what this round asks. Deterministic in its inputs * — never a timestamp or a counter, which would mint new ids (and re-ask for * already-delivered work) on every re-emission of an unchanged round. */ export declare function conceptualRoundToken(inputs: readonly string[]): string; /** * The per-kind evidence packet a charter lane reads. TOOL-written, so it lives * under the lane-asset dir with the lane prompts — a host only ever reads it. */ export declare function charterExtractionPacketFilename(kind: CharterKind): string; /** * The tool-side merge of the per-kind charter lanes, handed to the extraction * executor by path and deleted once ingested. Tool-written, so it is a lane * asset and not a submission. */ export declare const CHARTER_EXTRACTION_MERGED_FILENAME = "charter-extraction-merged.json"; /** * The submission FAMILY every audit gate lane belongs to. Distinguishes these * from the host-handoff work-item submissions, which mint through the same rule * under their own run id. */ export declare const AUDIT_LANE_SUBMISSION_KIND = "audit_host_lane"; /** See the module comment: the audit gates' stand-in for run identity. */ export declare const AUDIT_GATE_SUBMISSION_SCOPE = "audit-host-gates"; /** * Bound paths for gate lanes are expressed relative to the artifact tree root * (`.audit-tools/`), not the repository root: the gates are handed an artifacts * dir and nothing else, and a recorded path must be derivable from exactly what * the reader has. */ export declare function laneSubmissionRoots(artifactsDir: string): SubmissionRoots; /** The tool-minted id for one lane. Deterministic in the lane id alone. */ export declare function laneSubmissionId(lane: string, runId?: string): string; /** * Absolute on-disk path a lane's submission must land at. * * `runId` is threaded rather than defaulted-and-forgotten: the bound path and * the recorded expectation must come from ONE derivation. They were two — the * path taking the module constant while the record took the caller's scope — * which coincided only because every caller happened to pass the constant, i.e. * the invariant held by the caller remembering. */ export declare function laneSubmissionPath(artifactsDir: string, lane: string, runId?: string): string; /** * The current statement of what is owed, or `undefined` when there isn't one. * * REGENERABLE state: the set is rewritten at every emit from the lanes the * emission declares, so a set left by another release is DISCARDED rather than * reinterpreted under this release's field semantics. The fail-shape is the one * this reader already has for an absent file — the merge below starts from * nothing, every declared lane counts as newly added, and the carried-lane diff * therefore sees no prior entries and reports no shortfall. That is exactly * right after an upgrade: nothing was carried into this emission that this * release ever asked for, so the emission is first-emission-silent instead of * accusing the host of dropping lanes it was never coherently asked for. */ /** One lane an emission owes, as the emitter knows it. */ export interface DeclaredLane { readonly lane: string; readonly promptText: string; } export declare const LaneSubmissionShortfallSchema: z.ZodObject<{ /** Lanes CARRIED into this emission (a prior emission already asked). */ expected: z.ZodNumber; /** How many of those have a readable submission at their bound path. */ accepted: z.ZodNumber; outstanding: z.ZodArray, "many">; }, "strict", z.ZodTypeAny, { expected: number; accepted: number; outstanding: { message: string; submission_path: string; lane: string; submission_id: string; issue_code: string; }[]; }, { expected: number; accepted: number; outstanding: { message: string; submission_path: string; lane: string; submission_id: string; issue_code: string; }[]; }>; export type LaneSubmissionShortfall = z.infer; /** * Record what this emission owes, and report what a PREVIOUS one is still owed. * * Merges into the current statement (a step that materializes several lane * groups accumulates them) and appends one `expected` event per NEWLY declared * lane, so a re-emission does not spam the arrival record. * * The returned shortfall diffs only the lanes CARRIED into this emission — the * ones an earlier emission already asked for and that are still not on disk. * Restricting it to carried lanes is what makes the report meaningful: on a * first emission every lane is legitimately absent (it is being asked for right * now), so counting those would make every fan-out look like a failure. */ export declare function recordExpectedLanes(artifactsDir: string, runId: string, lanes: readonly DeclaredLane[]): Promise; /** Merge the shortfalls of a step that materializes several lane groups. */ export declare function mergeLaneShortfalls(shortfalls: readonly LaneSubmissionShortfall[]): LaneSubmissionShortfall; /** * The host-facing statement of an unmet expectation, by lane and issue code. * Empty when nothing was carried or everything carried has arrived — a clean * emission says nothing, so the notice's presence is itself the signal. */ export declare function renderLaneShortfallLines(shortfall: LaneSubmissionShortfall): string[]; /** * Record the outcome of one lane. An accepted lane stops being owed; a rejected * one stays owed (the host is asked again) but the refusal is on the record * with its reason, which is what makes a repaired run distinguishable from a * clean one. */ export declare function recordLaneOutcome(artifactsDir: string, lane: string, outcome: { readonly kind: "accepted"; readonly message?: string; } | { readonly kind: "rejected"; readonly issueCode: SubmissionIssueCode; readonly message: string; }): Promise; /** * Record what the host-handoff ingest just decided about each work item, on the * same ledger the gate lanes use — so a submission that never arrived (or * arrived unreadable) is a durable fact rather than a value that died inside * the call that computed it. * * Two rules keep the record drift-focused and arithmetically honest: * * • Deduped against the LAST recorded event for each submission. Ingest runs * on every `next-step`, so a host still working through its workload would * otherwise append the same "missing" line every poll, burying the state * CHANGES the ledger exists to preserve. A changed classification still * appends, in arrival order. * • An acceptance is recorded ONLY where a refusal precedes it. A work item * the host got right first try says nothing (the ledger is not an inventory * of work), but one that was refused and later accepted must close its own * story — otherwise a run where every failure was repaired reports its * refusals with no matching repairs, and the report reads as a run that * never recovered. */ export declare function recordHostResultOutcomes(artifactsDir: string, runId: string, outcomes: { readonly issues: readonly AuditHostIngestIssue[]; /** Work items whose results this run has accepted (ingest's completed set). */ readonly acceptedIds: readonly string[]; }): Promise; //# sourceMappingURL=laneSubmissions.d.ts.map