import { RemediationState } from "../state/store.js"; import { OrchestratorOptions } from "../types/options.js"; import type { RemediationOutcomesReport, RunLogger, VerificationReport } from "audit-tools/shared"; import type { ClosingAction } from "../state/closingActions.js"; import type { OutcomeCoverageLedger } from "../state/types.js"; import { type FinalGateReport } from "../../shared/types/remediationOutcome.js"; /** * Read the run's tool-owned gate outcome for the report. * * The gate WRITES `final-gate-outcome.json` on every evaluation; until this * reader existed, nothing consumed it — so a scoped-out or suppressed run * produced a completion report byte-identical to one written after a green * floor, which is the whole defect the record was added to close. A record that * is missing, unreadable, or fails its schema degrades to * {@link ABSENT_FINAL_GATE_REPORT}: stated as absent, never as green. */ export declare function readFinalGateReport(artifactsDir: string): Promise; export declare function buildRemediationOutcomesReport(state: RemediationState, closingResult: ClosingResult, finalGate?: FinalGateReport): RemediationOutcomesReport; /** * Build the outcomes file's coverage-ledger section: the plan's coverage ledger * with every never-planned entry (cross-lens-deduped, checkpoint-dropped, * no-evidence, phantom-paths) enriched with a `drop_reason` discriminator and * its full `Finding` payload. Payloads resolve from, in order: the ledger entry * itself (when the plan recorded one), the live plan findings, and the * structured-audit intake source. Must run BEFORE close deletes state.json / * the artifacts dir — they are the only payload sources. */ export declare function buildOutcomeCoverageLedger(state: RemediationState, options: OrchestratorOptions): Promise; export interface ClosingCommandResult { command: string[]; exit_code: number | null; stdout?: string; stderr?: string; } export interface ClosingResult { contract_version: "remediate-code-closing-result/v1alpha1"; action: ClosingAction; status: "success" | "failed" | "skipped"; commands: ClosingCommandResult[]; /** See `ClosingActionPreviewSchema.leftover_files` — same untouched-dirt set, at execute time. */ leftover_files?: string[]; } /** * Whether a closing action genuinely COMPLETED (COR-fb656e3f): it succeeded, OR * it was a skipped no-op (`action === "none"` — nothing was configured to do). * A *skipped nonnone* close did NOT complete; treating that as green would * pass the verification report and delete the (gitignored, * unrecoverable) artifacts dir for a run that never landed. Single-sourced here * so the verification trace, the report-level verdict, and the fully-green * cleanup gate can never drift on the classification. */ export declare function closingActionCompleted(closingResult: ClosingResult): boolean; /** `collectStagingFiles`'s manifest-scoped result. */ export interface StagingSelection { /** Manifest (or deliverable) files that are currently dirty — safe to stage. */ files: string[]; /** * Currently-dirty files that are NEITHER in `manifest` nor a deliverable — * pre-existing/unrelated dirt the run never touched. Never staged; surfaced * so the host/user can see what was deliberately left alone. */ leftover: string[]; } /** * INVARIANT (V2 fix): remediation close must never commit files the run * didn't touch. This is the single chokepoint both the preview * (`checkClosingPreview`) and the execute path (`executeClosingAction`) stage * through — never a repo-wide `git diff`/`ls-files` sweep. * * Formula: `files = manifest ∩ currently-dirty` (plus `deliverables`, which * are unioned into the effective manifest so they stage like any other * manifest entry — see `toolDeliverablePaths`). Any currently-dirty path * outside that set is `leftover`: reported, never staged, never committed — * committing LESS than a dirty tree is always safe, so a leftover never blocks * or aborts the close (a full-abort-on-unrelated-dirt policy would make the * tool unusable against a routinely-dirty working tree). * * TOCTOU note: this recomputes "currently-dirty" fresh on every call (by * design — a file the user touched between preview and execute must be * re-observed). What it can NEVER do is widen `files` beyond `manifest ∪ * deliverables`: a newly-dirtied path outside the manifest can only ever land * in `leftover`, never `files`. `pre_authorized: true` (see * `checkClosingPreview`) only skips the interactive preview step; it does not * — and structurally cannot — enlarge what this function is willing to stage. * * `.env*` is excluded even from an explicit manifest entry (defense-in-depth * against ever committing a secret); `.audit-tools/` scratch is excluded * unless the path is exactly a caller-supplied deliverable. * * Path comparison is TWO-TIER: an exact case-preserving key first * (`repoPathExactKey` — forward-slash, `./`-stripped), then the canonical * lowercased `normalizeRepoPath` key as a fallback ONLY when unambiguous * (exactly one dirty path folds to it). The fold tier is what makes a * declared `./Src/Foo.ts` match git's `src/Foo.ts` on win32; the exact tier + * ambiguity guard is what stops a case-SIBLING pair on a case-sensitive * checkout (`Foo.ts` real, `foo.ts` also real and user-dirty) from sweeping * the user's file in through the fold. The STAGED output is always git's * original-cased path string (never a normalized key — `git add` needs the * real on-disk case), and the exclude regexes are tested against the folded * key so `.ENV` / `.Audit-Tools/` casing games cannot bypass them. * * ACCEPTED RESIDUAL (inherent to declared surfaces): in the conversation-first * flow a declared-but-never-edited file that the USER dirties DURING the run * window (post-plan, pre-close) is indistinguishable from the run's own * hand-applied edit — `run_start_dirty` only fences dirt that predates the * run. Closing it fully requires per-edit git ground truth, which that flow * does not have. */ export declare function collectStagingFiles(root: string, manifest: string[], deliverables?: string[]): StagingSelection; /** * Execute the run's closing action. * * ASYNC because every command it spawns is awaited — see * {@link runTrackedCommand} for why a synchronous child under the held phase * lock is a liveness hazard rather than a style preference. */ export declare function executeClosingAction(state: RemediationState, options: OrchestratorOptions): Promise; export interface CombinedTestResult { /** * Whether a suite actually ran. `false` means `plan.test_command` was never * configured — a NEVER-RAN outcome, structurally distinct from `passed`, so * a caller can no longer mistake "nothing ran" for "a real pass" (the * vacuous-pass defect: previously `passed:true` alone claimed a real result * even for an unrun, unconfigured suite). */ ran: boolean; passed: boolean; duration_ms: number; suite_name?: string; /** Tail of combined stdout/stderr captured on failure (empty on pass). */ output: string; } /** * Run the plan's combined test suite over the fully merged post-remediation * state. Returns pass/fail plus the failure-output tail. No test_command => * `ran:false` (never-ran, distinct from a real pass) — `passed` still reports * `true` so existing gates that fold `combinedTest.passed` into `fullyGreen` * keep their vacuously-green behavior for a run with no configured suite; * `ran` is what lets a caller (buildVerificationReport's trace) tell the two * apart rather than rendering "combined test suite passed" for a suite that * never executed. * * The declared command passes the single-invocation shape gate BEFORE any * spawn — the same rule that guards a block's `targeted_commands`. A command * that chains, redirects or substitutes is REFUSED as a non-run * (`ran:false, passed:false`), never executed and never silently treated as a * pass, so a malformed suite declaration fails the close rather than handing a * shell an extra process. * * ⚠ BEHAVIOUR CHANGE, and a NARROWING — declarations this used to run now fail * the close instead. The gate refuses `' \ ^ % $` and backtick in EVERY * position, so two shapes that `shell: true` accepted are now refused outright: * * - an absolute Windows interpreter/script path (`C:\Program * Files\nodejs\node.exe …`) — backslashes; * - a single-quoted argument (`pytest -k 'not slow'`, `node -e "x('y')"`). * * Both are re-expressible: use the bare shim name (`node`, `npm`) and let * `resolveExecArgv` find it, and pass an argument as a DOUBLE-quoted token * rather than a nested single-quoted literal. The refusal is deliberate — a * declaration whose meaning depends on which shell reads it is exactly what the * shape rule exists to reject — but it IS a migration for anyone whose * `test_command` / `e2e_command` carries either shape. * * ASYNC, and that is the point: the close phase holds the state lock across * this call, and a synchronous child blocks the event loop for the whole spawn * — starving the lock's own mtime heartbeat until a live lock reads as stale * and is stolen mid-close. Awaiting {@link runTrackedAsync} keeps the loop * turning for the entire suite. */ export declare function runCombinedTestSuite(state: RemediationState, options: OrchestratorOptions): Promise; /** * On a combined-test failure, selectively re-block items whose touched_files * overlap with the failing tests' implicated paths. When attribution is * ambiguous (no overlap found), falls back to re-blocking all resolved items. * Returns whether any item was blocked — the caller transitions back to triage. */ export declare function blockResolvedItemsOnCombinedFailure(state: RemediationState, testOutput: string): boolean; export interface E2eTestResult { ran: boolean; passed: boolean; output: string; } /** * {@link cleanupTempBranchesAndArtifacts}'s result — lets a caller observe a * cleanup residue programmatically rather than only through console/log * output. Absent (`{}`) on a clean removal, a not-fully-green close (nothing * was attempted), or a final-state-persist failure alone. */ export interface CleanupResult { /** * Set to the artifacts directory path when its recursive removal failed * after an otherwise fully-green close — the caller must remove it * manually. The same fact is also written to the durable structured run log * (`runLogger`) and to the console; this field is what surfaces it in the * function's OWN returned result too, so a caller does not have to parse * log/console output to detect the residue. */ artifacts_residue?: string; } /** * Persist the completed state and clean up the artifact directory. * * The artifacts directory is only deleted on a fully-green close (no blocked * items, combined + e2e tests passed, and the closing action genuinely * completed — succeeded, or was the `action === "none"` no-op). When the run is * not fully green — e2e failed, combined test failed, an item is blocked, or the * closing action failed OR was skipped without completing — the artifacts * directory is preserved for diagnosis. */ export declare function cleanupTempBranchesAndArtifacts(options: OrchestratorOptions, completeState: RemediationState, combinedTest: CombinedTestResult, e2eResult: E2eTestResult, closingResult: ClosingResult, runLogger?: RunLogger): Promise; /** * Build a VerificationReport from the post-remediation state. One * FindingVerificationTrace per terminal finding, with trace entries for: * - combined test suite result (task kind) * - each item's verification evidence from result files (file kind) * - closing action outcome (command kind) * * Overall status is "passed" when combined tests passed and all resolved * items have at least one passing trace. "failed" otherwise. */ export declare function buildVerificationReport(state: RemediationState, options: OrchestratorOptions, closingResult: ClosingResult, combinedTest: CombinedTestResult): VerificationReport; export declare function runClosePhase(state: RemediationState, options: OrchestratorOptions, runLogger?: RunLogger): Promise; //# sourceMappingURL=close.d.ts.map