import type { FinalGateOutcomeKind as SharedFinalGateOutcomeKind } from "../../shared/types/remediationOutcome.js"; import { isAuditToolsMonorepo, toolOwnedFinalGateCommands, type FinalGateCommandSpec } from "./gateCommands.js"; export { isAuditToolsMonorepo, toolOwnedFinalGateCommands }; export type { FinalGateCommandSpec }; /** A command's recorded outcome within a gate run. */ export interface FinalGateCommandResult { argv: string[]; layer: FinalGateCommandSpec["layer"]; package_dir?: string; exit_code: number | null; passed: boolean; /** * Trailing slice of what the command printed, present only on a FAILING * command. The gate used to capture output and drop it on the floor — a red * arrived as an exit code and nothing to read, so the one artifact that could * explain it never existed. Bounded here at the source * ({@link GATE_OUTPUT_TAIL_LIMIT}): a whole suite log must not ride into a * state artifact, and the tail is where a failing suite puts its verdict. */ stdout_tail?: string; stderr_tail?: string; } /** * How a gate EVALUATION ended — one vocabulary, used by every gate family that * runs this floor, so the three are never told apart by prose or by inference * from a bare boolean. * * DECLARED IN THE SHARED BASE LAYER ({@link SharedFinalGateOutcomeKind} in * `src/shared/types/remediationOutcome.ts`) and re-exported here for the local * name. A second copy declared in this module would be a second vocabulary for * the same three outcomes, which is the defect this record exists to close. * * `executed` — the command list ran; `passed` is a real verdict. * `scoped_out` — the audit-tools-specific suite does not apply to this target, * so zero commands ran. * `disabled` — a gate was DUE and did not run. TWO distinct causes, both * recorded with this kind and told apart by the record's * `reason`: (1) SUPPRESSED — the `skipFinalGate` hermeticity * option or the `REMEDIATE_SKIP_FINAL_GATE` environment skip, * which never reach {@link runToolOwnedFinalGate} at all, and * (2) NO SUBJECT — the all-terminal funnel found nothing * verified-complete to validate, so it skipped the floor and * went straight to `closing`. The second is the easiest to * misread as a green close, because a run with zero resolved * items still writes a completion report. * * The two not-run kinds are not verdicts. A record for either carries * `passed: null` (see {@link FinalGateOutcomeRecord}), so a gate that ran * nothing can never be PERSISTED as a pass — the distinction the boolean alone * could not carry. */ export type FinalGateOutcomeKind = SharedFinalGateOutcomeKind; export interface ToolOwnedFinalGateResult { passed: boolean; results: FinalGateCommandResult[]; /** * Which of the two REACHABLE outcome kinds this run was. `disabled` never * appears here — a disabled gate returns before the runner is consulted — so * its record is written by the consumer that suppressed it. */ outcome: Exclude; /** * True when the audit-tools-specific suite did not apply (target is not the * audit-tools monorepo). The gate then does not block; it is a declared scope, * not a vacuous pass. Kept alongside {@link outcome} as the boolean draw of * the same fact for the branches that only need "did anything run". */ scoped_out: boolean; /** * The runtime/packaging surface the hard floor does NOT gate, declared as a * residual for a separate pass (CE-002). Always present (the floor is scoped * to build+check+unit by design). */ runtime_residual: { surface: string; commands: string[]; }; } /** * Injectable runner so the gate is unit-testable without spawning a real build. * * `stdout` / `stderr` are OPTIONAL so an injected runner that only reports a * status stays valid — the gate degrades to "no output captured" rather than * refusing a runner that predates output capture. */ export type GateRunner = (argv: string[], cwd: string, packageDir?: string) => { status: number | null; stdout?: string; stderr?: string; }; /** * Run the tool-owned final gate (INV-RS-10). Each command runs through the * shared `runTracked`, which scrubs CLAUDECODE / CLAUDE_CODE_*. * The first failing command short-circuits the floor (a broken build makes the * later layers meaningless). A `runner` may be injected for tests. When the * audit-tools suite does not apply (non-monorepo target), the gate is * `scoped_out` (does not block) rather than vacuously passing. */ export declare function runToolOwnedFinalGate(root: string, opts?: { runner?: GateRunner; }): Promise; /** * Where the failing gate run is recorded, relative to the artifacts dir. The * step prompt carries this PATH and the failing command line — never the tail * itself, which is what keeps a multi-KB suite log out of the prompt while * still leaving it one open away. */ export declare function finalGateRecordPath(artifactsDir: string): string; /** * Where the LAST gate evaluation's outcome is recorded, relative to the * artifacts dir. Distinct from {@link finalGateRecordPath}, which only exists * when a gate ran and went RED: this one is written on EVERY evaluation, * including the two that run nothing. */ export declare function finalGateOutcomePath(artifactsDir: string): string; /** * The ONE gate-outcome record. Every gate family that runs this floor writes * this shape, with these field names, so an executed, a scoped-out and a * disabled gate are three DISTINGUISHABLE records rather than three identical * "passed" notes. * * `passed` is `boolean | null` on purpose. The two not-run kinds carry `null`: * a gate that ran zero commands has no verdict, and a record that cannot hold * `true` for it is the mechanical reason a not-run gate can never be read back * as a green floor. (`ToolOwnedFinalGateResult.passed` stays a plain boolean * because it also drives the NON-BLOCKING decision — a scoped-out gate must not * block — and those are different questions: "may the run proceed" versus "what * actually happened".) */ export interface FinalGateOutcomeRecord { schema_version: "remediate-code-final-gate-outcome/v1alpha1"; /** Which gate observed it — a phase boundary, or the all-terminal funnel. */ scope: string; outcome: FinalGateOutcomeKind; /** The verdict, or null when the gate did not run. Never true for a not-run. */ passed: boolean | null; /** How many commands actually ran. Zero for both not-run kinds. */ commands_run: number; /** Why a not-run gate did not run. Absent on an executed gate. */ reason?: string; recorded_at: string; } /** * Record what a gate evaluation was. Overwrites: the file describes the CURRENT * gate state of the run, the same way {@link writeFinalGateRedRecord} does for a * red, and a repeat next-step re-evaluates rather than accumulating a history * nothing reads. * * The `passed: null` normalization lives HERE rather than at each call site: * a caller cannot record a not-run gate as a pass even by passing `true`. */ export declare function writeFinalGateOutcomeRecord(artifactsDir: string, outcome: { scope: string; outcome: FinalGateOutcomeKind; passed: boolean; commands_run: number; reason?: string; }): Promise; /** * What a RED gate leaves behind. * * This file used to hold a re-block counter and a `terminated` flag — the state * of a backstop that re-opened every item on an unattributable red and, at its * bound, abandoned the run. Both are gone: an unattributable red now records * what failed and pauses, so there is no count to carry and nothing to * terminate. (`terminated` had to go rather than merely stop being written: it * short-circuited the gate entirely, so a run that once reached the bound would * have skipped the suite check forever after.) */ export interface FinalGateRedRecord { schema_version: "remediate-code-final-gate/v1alpha1"; /** Which gate observed the red — a phase boundary, or the all-terminal funnel. */ scope: string; recorded_at: string; failing_command: string; exit_code: number | null; layer: FinalGateCommandSpec["layer"] | null; stdout_tail?: string; stderr_tail?: string; } /** * Record the failing command of a red gate run. Overwrites: the file describes * the CURRENT reason the run is paused, and a repeat next-step that still finds * the suite red rewrites it rather than accumulating a history nothing reads. */ export declare function writeFinalGateRedRecord(artifactsDir: string, scope: string, failed: FinalGateCommandResult | undefined): Promise; //# sourceMappingURL=finalGate.d.ts.map