import { type RemediationState } from "../state/store.js"; import type { Finding } from "../state/types.js"; import { RunLogger, type FrictionTriageDecision, type InterpretedIntent } from "audit-tools/shared"; import { hostDependencyLevels, type RemediationHostIngestSummary } from "./dispatch/hostHandoff.js"; import type { RemediationStep } from "./types.js"; import { isTerminalStatus, isVerifiedCompleteStatus } from "../state/itemStatus.js"; import { type GateRunner } from "./finalGate.js"; import type { IntentCheckpoint } from "audit-tools/shared"; export interface NextStepOptions { root?: string; artifactsDir?: string; input?: string | string[]; finalizeClosing?: boolean; forceReplan?: boolean; /** * True when this invocation supplied `--guidance-file` (folded into * intake/conversation-start.md before the step decision). Like a fresh * `--input`, a guidance file introduces NEW intake, so against a run already * past intake it must trip the resume-vs-restart conflict gate rather than * silently resuming (and executing) the old, unrelated run. Set once at the * bootstrap call; bare `next-step` follow-ups leave it undefined. */ guidanceFileSupplied?: boolean; /** * Skip the tool-owned final completion gate (INV-RS-10) at the all-terminal * transition. Production never sets this; it is a test-hermeticity affordance * so suites that drive an unrelated flow to completion do not spawn a real * build. Also honored via `REMEDIATE_SKIP_FINAL_GATE`. The gate's correctness * is verified directly by the final-gate suites regardless of this flag. */ skipFinalGate?: boolean; /** * Injectable runner for the tool-owned final gate (INV-RS-10). When set, the * gate uses it instead of spawning real commands, so the all-terminal * transition (coarse re-block / bounded terminate) can be exercised * deterministically in tests. Unset in production → real env-scrubbed builds. */ finalGateRunner?: GateRunner; } /** * Where an autonomous run's LEFTOVER deliverable pair lands. * * REMEDIATION-OWNED, deliberately. This module used to write the canonical * `.audit-tools/audit-findings.json` + `audit-report.md` pair directly and * unarchived, which destroys the audit source `defaultInputCandidates` resolves * FIRST — the original contract becomes unrecoverable for any external consumer * (INV-RNF-NO-CANONICAL-PAIR-WRITE). The canonical pair belongs to * audit-artifact-promotion-lifecycle, whose exported write-with-archive is the * only sanctioned way to replace it. */ export declare function autonomousLeftoverFindingsPath(root: string): string; export declare function autonomousLeftoverReportPath(root: string): string; /** * The intake sources a bare `next-step` discovers, IN PRIORITY ORDER — index 0 * wins. Exported so the ordering can be asserted by CALLING it: the property * that matters ("a real audit always beats this run's own leftovers") is a fact * about the returned array, and a test that reads it out of the source text is * asserting the prose, not the order. */ export declare function defaultInputCandidates(root: string): string[]; export type { FindingRiskTier, FindingClassification, } from "./stepUtils.js"; export { dependencyVerifiedComplete, classifyFindingRisk, } from "./stepUtils.js"; export { isTerminalStatus, isVerifiedCompleteStatus }; export { hostDependencyLevels }; /** * The phase ordinal whose UNTOUCHED entry a whole-repo test-suite gate must run * before, or null when no per-phase gate is due this pass (auto-phasing, T3 — * the integration checkpoint layered on top of the INV-PHASE-01 ordering * barrier). A gate is due iff: * - the eligible handoff frontier this pass (`hostDependencyLevels`, which * already applies the phase barrier, so the frontier is a SINGLE phase) is at * a phase P > 0 — i.e. a lower foundations phase precedes it (and, by the * barrier, is fully VERIFIED-complete now); AND * - phase P is at its untouched entry — every block at phase P still has all * its items `pending` (nothing dispatched yet). * The second clause makes the predicate pure and reblock-safe: it fires exactly * once as foundations→consumers crosses into P, never again on P's later * intra-phase levels, and re-fires only if a coarse re-block reopens the lower * phases and the frontier later re-climbs to P. Phase 0 (and an ordinal-free * single-phase plan) is never gated here — there is no preceding phase to * validate; the all-terminal tool-owned final gate (INV-RS-10) is the whole-repo * checkpoint for the last/only phase. */ export declare function phaseBoundaryToGate(state: RemediationState): number | null; export { isAuditToolsMonorepo, toolOwnedFinalGateCommands, runToolOwnedFinalGate, finalGateOutcomePath, writeFinalGateOutcomeRecord, } from "./finalGate.js"; export type { FinalGateCommandSpec, FinalGateCommandResult, ToolOwnedFinalGateResult, FinalGateOutcomeKind, FinalGateOutcomeRecord, GateRunner, } from "./finalGate.js"; /** * The re-plan carry-forward identity of a finding: canonical JSON with the * plan-time bookkeeping keys stripped, so a re-plan whose only delta is a * recomputed file hash or a re-evaluated grounding flag carries the prior item * (and its `item_spec`) forward, while a real change to the finding does not. * * EXPORTED so the invariant suite can call THIS function. It was module-internal, * and the suite claiming to cover the invariant declared its own copy of the key * set, the strip and the key builder — so dropping `evidence_grounded` from the * production set, or widening it with a real field like `severity`, left the * block green while carry-forward regressed. A test asserting against its own * re-implementation pins nothing about shipped behaviour. */ export declare function findingCarryForwardKey(finding: Finding): string; /** * The `recover-ingest` verb's whole body: ingest the host's landed results in * RECOVERY mode and persist through the same file-locked, atomically-writing * store, with the same `contract_version` strip. * * It is a separate verb rather than a flag on `next-step` because the * relaxation it enables must be an operator's explicit act — see * `ingestRemediationHostResults`, which states what is waived and the residual * risk. Nothing else here differs from the normal ingestion: the same workload, * the same contract gates, the same eligibility frontier. * * ## Why this runs in two phases * * A required-test rerun is `spawnSync`, which blocks the event loop for its * whole duration. Run inside the state lock, it would starve the lock's own * heartbeat timer (`setInterval` in the shared fileLock) — the held lock's mtime * would stop being refreshed, a second acquirer would classify it as stale at * ~30s and steal it, and mutual exclusion would be gone precisely during the * longest critical section in the codebase. Holding a lock across a blocking * spawn is therefore not merely slow; it is unsound. * * So: * * - **Phase 1, UNLOCKED.** Snapshot the state, capture HEAD, and run every * distinct required-test command exactly once * (`precomputeRecoveryTestVerdicts`). HEAD is captured BEFORE the spawns, not * after, because a host-authored command that MOVES HEAD would otherwise * produce verdicts of mixed provenance and go undetected. (The guard compares * commit shas: it sees HEAD movement, not worktree dirt — a command that only * dirties files is invisible to it, which is acceptable because phase 2's * corroboration is commit-based.) * - **Phase 2, LOCKED.** Re-read HEAD and abort the whole recovery if it moved * (`tree_moved_between_phases`) — the phase-1 verdicts would describe a tree * that no longer exists, and nothing is accepted or appended. Otherwise ingest * with the pre-computed verdicts, which the ingest only READS: in recovery * mode it never spawns, and a command missing from the table fails closed. * * What remains inside the lock is git plumbing (ancestry, ref scan, diff-tree), * the ledger append, and the state write — sub-second work, comfortably inside * heartbeat coverage. The HEAD-unchanged guard closes the gap the phase split * opens; the operational protocol is still one writer at a time, now enforced by * a lock that cannot be stolen mid-hold instead of by convention. * * One accepted cost: `StateStore.mutate` always writes, so a recovery run that * changes nothing rewrites `state.json` with identical content. Expressing a * true no-op means plumbing the locked store's `SKIP_WRITE` sentinel through * `StateStore.mutate`, which is a change to the store's API rather than to this * verb. The `state_changed` flag on the returned summary stays authoritative * for callers either way. */ export declare function recoverIngestHostResults(options: { readonly root: string; readonly artifactsDir: string; readonly runId: string; }): Promise; /** * The terminal friction-TRIAGE close-out for the remediate half. Thin delegation to * the single-sourced `decideFrictionTriage` (`audit-tools/shared`) — the exact analog * of audit-code's `decideAuditFrictionCloseout`, so the triage shape, disposition * vocabulary, blocking semantics, and close-out logic cannot drift between the two * halves. Drops the former false-green (an empty up-front record no longer satisfies): * the blocking triage stays unsatisfied ("dispose") until every captured mechanical * event AND every surfaced agent-feedback reflection carries a disposition; an empty * set (zero events AND zero reflections) is trivially "disposed". Keyed only off * `(artifactsDir, runId)`; never coupled to any repo's backlog doc. */ export declare function decideRemediateFrictionCloseout(artifactsDir: string, state: RemediationState | null): Promise; export declare function decideNextStep(options?: NextStepOptions | string): Promise; /** Sidecar artifact recording the deterministic interpretation of free_form_intent. */ export declare const INTENT_INTERPRETATION_FILENAME = "intent-interpretation.json"; export declare const INTENT_INTERPRETATION_SCHEMA_VERSION = "remediate-code-intent-interpretation/v1alpha1"; export interface PersistedIntentInterpretation { schema_version: typeof INTENT_INTERPRETATION_SCHEMA_VERSION; /** The interpreter's structured output (lens weights / priority / scope). */ interpreted: InterpretedIntent; /** * Clauses the interpreter could not encode as a lens weight, priority signal, * or scope emphasis. Surfaced so the host can promote them to constraints — * never silently dropped. */ unencodable_clauses: string[]; created_at: string; } /** * Interpret a confirmed checkpoint's `free_form_intent` via the shared * deterministic interpreter and persist the structured signals to a sidecar * artifact. Idempotent and best-effort: returns the persisted interpretation (or * null when there is nothing to interpret / no confirmed checkpoint) and never * throws into the decide loop. The raw `free_form_intent` string is NOT returned * or threaded anywhere — only the structured `InterpretedIntent` is (INV-S04). */ export declare function interpretConfirmedCheckpointIntent(artifactsDir: string, checkpoint: IntentCheckpoint | undefined, runLogger?: RunLogger): Promise; //# sourceMappingURL=nextStep.d.ts.map