/** * The acceptance gate: deny-dominates resolution of a workflow's acceptance * requirement from the QE evidence ledger (Phase 6a made crew the * control-plane owner of the gate; Phase 6c retired wicked-testing, so the * QE pipeline is garden's qe skills writing through wicked-ledger). * * A workflow DECLARES an acceptance requirement through its phases: any phase * carrying `verified_evidence: true` requires its "done" to be re-derived from * evidence rather than asserted (the same field the engine's evidence floor * keys on). When a governed run's workflow declares one, crew resolves it from * the ledger's newest verdict — deny-dominates throughout: * * PASS → satisfied * FAIL → denied (the reviewer's reason is surfaced) * CONDITIONAL → denied — garden's VERDICT_TO_STATUS maps it to * run status `partial` ("approve with listed * fixes", a deliberate ship-with-conditions * outcome, distinct from PASS and FAIL). Under * deny-dominates a conditional approval does not * satisfy the gate on its own: the conditions * are unmet work, and the hold stands until a * clean PASS is recorded or a human approves at * the crew gate with the conditions in view. * PARTIAL → denied (some criteria met — not all) * INCONCLUSIVE / N-A / SKIP → denied (evidence missing or not evaluated — * map to `inconclusive`, the `?? 'inconclusive'` * fallback of garden's convention) * * Verdict vocabulary (garden ≥ 12.37.0, fixall L6): a garden evaluator's * OUTPUT line is `VERDICT: PASS` or `VERDICT: FAIL` only — the engine's * evaluator gate reads the LAST such line and passes on the token PASS * alone, so CONDITIONAL, PARTIAL, INCONCLUSIVE, N-A and SKIP are legacy * RECORD values that older ledgers (and the specialists' DomainStore * records) still carry. This mapping keeps resolving them 1:1 — the enum * and the table are unchanged — so a ledger written by any generation * reads the same here. * missing ledger / verdict → denied (no evidence is never a pass) * unreadable ledger → denied, naming the read failure * * The verdict enum and the status mapping follow garden's qe `accept` action * (VERDICT_TO_STATUS) and wicked-ledger's `RunStatus` — this module maps 1:1 * and never collapses distinct non-PASS outcomes into one another; only the * GATE decision collapses (everything not PASS denies), and each denial keeps * its own reason. */ import type { Verdict } from 'wicked-ledger/manifest'; import type { GovernanceClaim, RecordedEvent, RepoEntry, SessionView, WorkflowDef } from '../core/types.js'; import type { QeAcceptanceState, QeAttemptWindow, QeAttribution, QeManifestSummary, RunLinkage } from './ledger.js'; import type { RunConformance } from './conformance.js'; import { type EnforcementContext } from './conformance.js'; import type { RunAcceptanceSummary, RunAcceptanceWalkthrough, WalkthroughStepState } from 'wicked-crew-api-types'; import type { WalkthroughGate } from './walkthrough-acceptance.js'; /** * Verdict → run-status, 1:1 with garden's qe `accept` action (VERDICT_TO_STATUS) * extended over the full ledger enum: N-A and SKIP take the convention's * `?? 'inconclusive'` fallback ("couldn't evaluate"), exactly as the accept * action computes `runStatus`. CONDITIONAL → `partial` is deliberate — a * ship-with-conditions outcome, not a clean pass and not a failure. */ export declare const VERDICT_TO_STATUS: Record; /** Ids of a workflow's phases that declare the acceptance requirement (`verified_evidence: true`). */ export declare function acceptancePhaseIds(workflow: WorkflowDef | null): string[]; /** * A run's acceptance requirement, read from what the run CONTAINS (seam X2, round 2). * * - A preset or user-plan run: its units whose catalog entry declares `verified_evidence` * (`test`, `walkthrough_review`, `domain_coverage` today — read from the engine's * `Core.catalog()`, never a list here). No registered def is consulted, so a preset whose def * is deleted, and a user plan (which has none), declare exactly what they will run. * - A non-team run of a registered workflow: that def's `verified_evidence` phases. * - A free-text run: nothing. * - Anything the daemon cannot read — an UNKNOWN run, a plan run whose steps carry no catalog * ids yet, an engine whose catalog does not say which entries carry verified evidence, a * workflow run whose def is no longer registered — is DECLARED and fails closed: `failClosed` * names why, and the gate denies with that reason whatever the ledger holds. */ export interface AcceptanceRequirement { declared: boolean; phases: string[]; /** Set when the requirement could not be read: the gate denies with this reason. */ failClosed?: string; } /** * Resolve {@link AcceptanceRequirement} for a run. `verifiedCatalog` is the set of catalog ids whose * entry declares `verified_evidence` (`null`: the engine's catalog does not say — fail closed for a * plan run). Pure. */ export declare function acceptanceRequirementOf(view: SessionView, workflows: WorkflowDef[], verifiedCatalog: ReadonlySet | null, /** * Every catalog id the engine defines (WT-W2, S12). A plan step naming one outside it fails the * requirement closed — an engine downgrade must never shrink what a run has to prove. `null` / * omitted: the engine's full list is not known, and the verified-set rule alone applies. */ knownCatalog?: ReadonlySet | null): AcceptanceRequirement; /** The gate's resolution of one run's acceptance requirement. */ export interface AcceptanceGateResolution { /** Whether the run's workflow declares an acceptance requirement at all. */ required: boolean; /** Deny-dominates decision. `true` only for a clean PASS (or when nothing is required). */ satisfied: boolean; /** The governing ledger verdict, when one was read. */ verdict: Verdict | null; /** That verdict mapped through {@link VERDICT_TO_STATUS} (null when no verdict). */ runStatus: string | null; /** Why the gate decided what it decided — always populated, never empty. */ reason: string; } /** * Resolve the acceptance requirement for one run — pure, deny-dominates. * * `state === null` means the run has no repo context at all (a repo-less run * has nowhere a ledger could live), which is its own denial reason: evidence * that cannot be LOCATED is indistinguishable from evidence that does not * exist, and neither satisfies a gate. */ export declare function resolveAcceptanceGate(required: boolean, state: QeAcceptanceState | null, failClosed?: string): AcceptanceGateResolution; /** The body `GET /runs/:id/acceptance` serves. */ export interface AcceptanceView { runId: string; repo: { id: string; name: string; rootPath: string; } | null; requirement: { declared: boolean; phases: string[]; }; acceptance: { ledgerDir: string; ledgerRoot: string; found: boolean; qeRun: { id: string; status: string; startedAt: string; finishedAt: string | null; projectId: string; scenarioId: string; } | null; verdict: { id: string; verdict: string; reviewer: string; reason: string | null; createdAt: string; qeRunId: string; } | null; manifest: QeManifestSummary | null; /** * How `verdict` was tied to THIS run (`pinned` by `?qeRun`, `stamped` with the run id by the * writer, or a QE run inside the run's lifetime) — or `none`, with the reason naming what the * ledger does hold. The body never serves a repo-wide "newest verdict" as the run's (F-E2E-013). */ attribution: QeAttribution; /** How many live verdicts the whole ledger holds, attributed or not. */ ledgerVerdicts: number; /** * How many QE runs are attributed to this run — the deny-dominates set `verdict` was resolved * over (each contributes its newest verdict; any non-PASS among them denies). 0 when none. */ attributedVerdicts: number; error?: string; } | null; gate: AcceptanceGateResolution; /** * The walkthrough half (WT-W2, DES-walkthrough-proof §4.9): one row per `walkthrough_review` step the * requirement names, each re-verified against its seal at this read; `sealed` = every such step's * seal held; `steps` = the per-creator-step `checkState` from the newest walkthrough. ABSENT when the * run's requirement names no walkthrough step. */ walkthrough?: RunAcceptanceWalkthrough; /** (WT-W3, api-types 0.82.0) The deliver card's acceptance line. Always present. */ summary: RunAcceptanceSummary; /** * The governance half, BESIDE the QE gate (AW-14 / arch-R13a + R16): this run's conformance * claims (wiki rule ids cited), its enforcement status, and the deny-dominates `guardrailed` * headline. Never claims guardrailed for an unenforced, ungoverned, or unverifiable run. */ conformance: RunConformance; } /** * The frames that close a run's lifetime — one per terminal `SessionStatus` the engine defines * (wicked-core `src/domain.rs`: `Completed`, `Failed`, `Cancelled`), spelled as the engine emits * them (`src/event.rs` `event_to_json`: `sessionCompleted`, `sessionFailed`, `runCancelled`). * Pinned from the source, not from memory: the first cut named a `sessionCancelled` frame the * engine never emits, so a CANCELLED run's window never closed and any later QE PASS on the repo * was attributed to it (review of #539, F1). `runOrphaned` is deliberately absent — an orphaned * run is still `executing` and resumable, not finished. */ export declare const TERMINAL_EVENT_TYPES: ReadonlySet; /** * The terminal frames the engine will NOT resume from: `resume_run_inner` returns a `Completed` or * `Cancelled` run's status unchanged (wicked-core `src/actor.rs`, the `Completed | Cancelled` * early return) and re-dispatches only a `Failed` one. So once one of these is recorded the window * is closed for good; `sessionFailed` alone can be followed by a `resumed` frame (r2 review, N1). */ export declare const FINAL_EVENT_TYPES: ReadonlySet; /** * The frames that prove a run is LIVE AGAIN after a non-final terminal frame — the engine emits * each of them only for a run it is actively driving (wicked-core `src/event.rs` `event_to_json`): * - `resumed` — emitted by `confirm_gate` when a human approves a gate (`src/actor.rs`, the only * `CoreEvent::Resumed` emission). NOT by the failed-run resume path: `resume_run_inner` emits * no frame of its own (r3 review, N3 — an earlier comment here claimed otherwise); * - `unitDispatched` / `unitExecuting` / `toolExecutorDispatched` — `dispatch_unit`'s frames, the * first thing a rescued run records after `POST /runs/:id/resume` re-dispatches its cursor unit; * - `unitDistributed` — a (re)distribution for a unit, run-scoped and live-only as well. * `unitDone` / `unitDenied` / `unitPlanned` are deliberately NOT here: a straggling completion or * denial frame after a failure is not evidence the run went on (F4). */ export declare const REOPEN_EVENT_TYPES: ReadonlySet; /** * The crew run's lifetime as its durable event log records it — the window a QE run must have * started inside to count as this run's evidence (F-E2E-013). * * `startedAt` is the capture time of the `sessionStarted` frame (the earliest frame's, for a log * that lacks one). `finishedAt` follows the frames IN LOG ORDER: a terminal frame closes the window * at its capture time; a later {@link REOPEN_EVENT_TYPES} frame reopens it — a FAILED run is * resumable under the same id (`POST /runs/:id/resume` → engine `resume_run_inner`, which * re-dispatches the cursor unit and runs the run on to its own `sessionCompleted`), so QE evidence * recorded after the rescue is that run's, whether the run is still executing (its execution * frames reopen the window — r3 review, N3) or has since completed (r2 review, N1). * `sessionCompleted` / `runCancelled` are FINAL: the engine refuses to resume either, so nothing * after them reopens the window. Any other frame is ignored — a straggling non-terminal frame * after the end does not reopen anything (F4). A log with no terminal frame (or one whose last * terminal frame was followed by execution) belongs to a live run, whose window stays open. A * `null` or empty log places the run nowhere in time, so it can link nothing — and when the log * could not be READ (`unreadable`), the linkage carries that cause so the denial names it instead * of "no sessionStarted" (F5). * * Chosen over gating on `session.status`: the log is what the route already reads for this * purpose, it needs no second engine call, and it yields the same answer — a rescued run's * persisted status is non-terminal from its re-dispatch until its next terminal frame, which is * exactly the span between the reopening frame and the frame that closes the scan again. */ export declare function runWindowFromEvents(events: RecordedEvent[] | null, runId: string, unreadable?: string, currentTree?: string | null, qeUnitOrds?: number[]): RunLinkage; /** * (crew#971) The run's QE attempts: every dispatch of the QE unit (the ords `qeAcceptanceDecided` * names) up to that attempt's `gateEvaluated`, with the tree the gate judged. A dispatch whose * gate never ran stays open (`end: null`, `tree: null`) — it judged no tree yet. */ export declare function qeAttemptsFromEvents(events: RecordedEvent[], qeUnitOrds?: number[]): QeAttemptWindow[]; /** * Assemble the acceptance view for one crew run: read the run's event log, * read the ledger SCOPED TO THIS RUN (a read-only canonical-JSON read — the * read that needs no bus) and resolve the gate. */ export declare function buildAcceptanceView(opts: { runId: string; repo: RepoEntry | null; /** * The run's evidence root (`session.evidence_root`), when it has one: its QE ledger is then * `/.wicked-qe` and nothing else ({@link runQeLedgerRoot}, crew#951). */ evidenceRoot?: string | null; /** * (crew#971) The run's deliverable tree (`session.verified_tree`): only QE verdicts that judged * this tree count. Absent/null ⇒ no tree scoping (every verdict counts). */ deliverableTree?: string | null; /** (crew#971) The ords of the run's QE unit(s) — the units carrying a QE decision. */ qeUnitOrds?: number[]; /** What the run must prove ({@link acceptanceRequirementOf}). */ requirement: AcceptanceRequirement; qeRunId?: string; /** * Loader for the conformance store's claims (the adapter's `listConformanceClaims`). Optional so * older callers keep compiling — but ABSENT reads as "claims unreadable", never as "no claims": * the conformance section reports `claimsAvailable: false` rather than a clean empty list. */ claims?: () => Promise; /** * Loader for the run's durable event log (the adapter's `runEvents`; `null` = no binding). * Optional with the same contract: absent or failing reads as `unverifiable` enforcement — an * unknown enforcement state is never reported guardrailed (arch-R16). */ events?: (runId: string) => Promise; /** * WT-W2: the run's walkthrough steps the requirement names, each resolved against its proof root and * seal ({@link WalkthroughGate}), and the per-step check states. Their step ids are taken OUT of the * repo ledger's share of the requirement: a walkthrough's verdicts live in its proof root. * `ownedByYou`: the accepted plan's floor override removed the pair (§4.9, #791) — the block is * served with no roots so the `owned_by_you` steps reach the wire. */ walkthroughs?: { gates: WalkthroughGate[]; steps: WalkthroughStepState[]; newest?: WalkthroughGate | null; ownedByYou?: boolean; }; /** IG1-crew-2: the run's binding and seat records — `conformance.enforcement.units`. */ enforcementContext?: EnforcementContext; }): Promise; /** * WT-W3: the deliver card's one acceptance line (`RunAcceptanceSummary`), from the resolved gate and * the newest walkthrough — "Checked by a walkthrough: 3 of 3 steps at a1b2c3d." The deliver text cites * the counts and the tree, never a root path (DES-walkthrough-proof §7, N6). */ export declare function acceptanceSummary(gate: AcceptanceGateResolution, walkGates: readonly WalkthroughGate[], steps: readonly WalkthroughStepState[], /** The walkthrough `steps` were computed from (the newest AUTHOR's pair); defaults to the last gate. */ newestGate?: WalkthroughGate | null, /** The accepted plan's override removed the pair (§4.9, #791): no walkthrough ran; the steps are the operator's. */ pairRemoved?: boolean): RunAcceptanceSummary; /** * Deny-dominates across the repo ledger's gate and every walkthrough step's (WT-W2): satisfied only * when each is; the reason is every denial, in that order, or the passes when nothing denies. A * requirement that could not be read (`failClosed`) keeps its own resolution. */ export declare function combineWalkthroughGates(repo: AcceptanceGateResolution, walkthroughs: WalkthroughGate[], declared: boolean, failClosed?: string): AcceptanceGateResolution; //# sourceMappingURL=acceptance.d.ts.map