import { type ValidationIssue, type Finding } from "audit-tools/shared"; import { type PairingVerdict } from "../contractPipeline/changeClassification.js"; import type { ContractPipelineArtifactName } from "../contractPipeline/artifactStore.js"; /** * Deterministic structural gates run before the adversarial critic phase. * Returns ValidationIssue[] — errors block the pipeline (re-emit design phase), * warnings are advisory (appended to the critic prompt). Circular obligation * dependency detection yields a warning (not an error) routing to N-R21. * * Call this with the design_spec payload and, optionally, the obligation_ledger * payload for the invariant-coverage cross-check. */ export declare function validateDesignSpecGates(designSpec: unknown, obligationLedger?: unknown): ValidationIssue[]; /** * ARC-86b18f1b: validate that every contract-pipeline artifact that carries a * `goal_id` field contains the SAME value. A mismatch indicates that two * artifacts were produced for different goals and must not be used together. * * Pass in a map of artifact-name → payload. Payloads that are not records, or * that have no `goal_id` field, are silently skipped (the per-artifact * validators already flag missing goal_ids). Issues are errors. */ export declare function validateGoalIdConsistency(artifacts: Record): ValidationIssue[]; /** * ARC-86b18f1b-2: validate the implementation_dag against the obligation_ledger * and counterexample/judge artifacts for: * * 1. Referential integrity — every id referenced in `satisfies_obligations`, * `verification_obligation_ids`, and `addresses_counterexamples` must exist * in the obligation_ledger or as an accepted counterexample in the judge * report (respectively). * * 2. Bidirectional coverage — every obligation in the ledger, and every * accepted counterexample (per the judge report), must be addressed by at * least one DAG node. * * All issues are errors. Accepts `undefined` payloads for missing artifacts — * referential checks are skipped when the target artifact is absent (the caller * is responsible for ensuring the artifacts exist before calling this gate). */ export declare function validateImplementationDAGIntegrity(dagPayload: unknown, obligationLedgerPayload: unknown, counterexamplePayload: unknown, judgeReportPayload: unknown): ValidationIssue[]; /** * Testable obligation kinds — THE single source of truth for whether an * obligation needs a test-plan spec (derive.ts's buildTestValidatorPlanScaffold) * and whether the coverage gate below requires that spec to be filled * (validatePairedObligations). MNT-e10b9d9b: previously duplicated by hand as * TESTABLE_KINDS in derive.ts and TESTABLE_OBLIGATION_KINDS here, and the two * copies had already diverged — derive.ts failed OPEN on an unrecognized kind * (conservatively testable) while this file failed CLOSED (silently skipped, * i.e. treated as non-testable, so a scaffold-offered spec was never actually * required). Single-sourced here; derive.ts imports isTestablePhaseObligation * from this module instead of re-declaring it. */ export declare const TESTABLE_OBLIGATION_KINDS: Set; /** * testable (invariant/behavioral) → true; the structural contract-conformance * kind → false; an unrecognized/unexpected kind → true (fail-OPEN into the * paired-test gate rather than silently skipping coverage — matches * derive.ts's original scaffold semantics, which this predicate now also * governs on the coverage-gate side, closing the divergence). */ export declare function isTestablePhaseObligation(kind: string): boolean; /** * OBL-CO-01 / DC-5 — paired-obligation gate (fail-closed, change-scoped). * * Every TESTABLE (invariant / behavioral) obligation must be covered by at least * one test_validator_plan spec. Whether that coverage must be a positive+negative * PAIR depends on the obligation's change-vs-addition classification (CE-013): * * - A behavior CHANGE (it touches an existing symbol) requires BOTH a positive * (satisfied-path) assertion AND a negative (failure-path) assertion, and the * negative must be SCOPED to the changed symbol/file (CE-006): an unscoped, * repo-wide negative does not count. A narrow positive-only test, or a negative * that greps the whole tree, is the exact latent failure mode this gate stops. * - A pure ADDITION has no prior behavior to regress, so it is NEVER forced to * pair — coverage by any spec is sufficient. * - An UNCLASSIFIED testable obligation is treated as a CHANGE (fail-closed): a * dropped classification can never silently relax the requirement. * * The classification is recorded on the ledger by `deriveObligationLedger` * (deterministic first pass, LLM-confirmable). Pairing/scoping/polarity are all * evaluated through the single-source `changeClassification` helpers so this gate * and the `mergeImplementResults` verify gate agree exactly. * * An obligation may opt out only via an explicit, falsifiable `inapplicable_claim` * on a spec that cites that obligation id — bare omission is an error. * * Accepts `undefined` for a missing test_validator_plan: with testable * obligations present and no plan at all, every testable obligation is reported * uncovered (fail-closed). */ export declare function validatePairedObligations(obligationLedgerPayload: unknown, testValidatorPlanPayload: unknown): ValidationIssue[]; /** Re-exported PairingVerdict so importers of this gate module can type the result. */ export type { PairingVerdict }; /** * OBL-CO-03 — evidence-threading gate (fail-closed). * * Evidence produced upstream must survive every downstream seam: * * 1. A contract_assessment_report finding with status "violated" must carry * non-empty evidence — a violation asserted without evidence is unfalsifiable. * 2. Every judge-accepted counterexample must be threaded forward into the * implementation_dag (a node must list it in addresses_counterexamples). * This is the seam where adversarial evidence is most often dropped. * 3. Every DAG node that satisfies an obligation must carry obligation-derived * evidence in its description (the node must not be an empty placeholder). * * Accepts `undefined` payloads — a check whose source artifact is absent is * skipped, except the counterexample-threading check, which is fail-closed when * accepted counterexamples exist but the DAG is missing. */ export declare function validateEvidenceThreaded(assessmentReportPayload: unknown, judgeReportPayload: unknown, dagPayload: unknown): ValidationIssue[]; /** * OBL-CO-04 — source_type-scoped digest-coverage gate (fail-closed for * enumerable sources). * * For a `structured_audit` (enumerable) intake every enumerated finding must map * to at least one obligation in the ledger, so no auditor finding silently * vanishes between intake and the contract. For `conversation` / `document` * sources — or any finding-enumeration explicitly marked `is_enumerable:false` * — the gate passes vacuously, because there is no closed finding set to cover. * * The mapping is established by finding-id appearing in any obligation's * `source_finding_ids` (preferred) OR being referenced by id within an * obligation's `description` (word-boundary match, fallback). * * `sourceType` comes from goal_spec.source_type. `findingEnumerationPayload` is * the intake finding-enumeration.json ({ is_enumerable, findings:[{id}] }). */ export declare function validateDigestCoverage(sourceType: string | undefined, findingEnumerationPayload: unknown, obligationLedgerPayload: unknown): ValidationIssue[]; /** * Path-A work-topology gate. Every dangerous audit overlap must have exactly one * distinct seam-preparation module, and both participating implementation blocks * must remain represented by implementation modules. This turns the auditor's * overlap metadata into a mechanically enforced decomposition boundary instead * of prompt-only advice. */ export declare function validateWorkBlockSeamPreparation(pathASeedPayload: unknown, moduleDecompositionPayload: unknown): ValidationIssue[]; /** * OBL-CO-12 / INV-CO-12 — reconciliation-derivation gate (fail-closed). * * Every mismatch reconciled in the seam_reconciliation_report must be DERIVED * into the finalized_module_contracts: the report's `agreed_interface` for each * mismatch must be reflected in the finalized contracts (matched against the * union of every finalized module's inputs/outputs/invariants/side_effects text). * A reconciliation decision that never reaches the finalized contract is a * dropped derivation — the exact failure INV-CO-12 forbids. * * Fail-closed: if the report declares mismatches but the finalized contracts * artifact is absent, every mismatch is reported as underived. */ export declare function validateReconciliationDerivation(seamReconciliationReportPayload: unknown, finalizedModuleContractsPayload: unknown): ValidationIssue[]; /** A finding-shaped citation the gate grounds. Reuses the shared Finding shape. */ export interface ContractCitationGroundingResult { /** True when the repo tree could be enumerated (≥1 path). */ treeReadable: boolean; /** ValidationIssue[] — errors block promotion / re-emit the producing phase. */ issues: ValidationIssue[]; } /** * Enumerate the working-tree paths at `repoRoot`, normalized through the shared * `normalizeRepoPath` for membership matching. This is the lowercased *draw* over * the one shared corpus — `enumerateTrackedFilePaths` owns the git invocation and * its NUL-delimited parsing, so the two corpora cannot drift on how a path is * read off the index (they did: both split newline-terminated `ls-files` output, * so a C-quoted non-ASCII path corrupted both). Returns an empty set when git is * unavailable or the tree is empty (caller treats empty as the fail-closed * unreadable-tree signal). */ export declare function enumerateRepoTreePaths(repoRoot: string): Set; /** * Whether `repoRoot` is inside a VALID git working tree, via * `git rev-parse --is-inside-work-tree`. This distinguishes the two reasons * `enumerateRepoTreePaths` can return empty: (a) git missing / not a repo → * `false` (genuinely unreadable — fail-closed); (b) a valid git work tree that * simply has zero tracked files yet (a fresh/never-committed repo) → `true` * (the citations may be sound; degrade to pass-with-warning, never hard-block). * OS-agnostic: `shell: false`. NEVER throws — any failure is treated as not-a-tree. */ export declare function isInsideGitWorkTree(repoRoot: string): boolean; /** * M-B3 — source-grounded citation gate (fail-closed only on an unreadable tree). * * For each finding: * 1. If it cites at least one real `affected_files` path → grounded (delegates * to the shared `groundDesignFinding` against the repo-tree path set — no * re-implementation). * 2. Otherwise, partition the citation tokens (affected_files paths that did * not resolve + symbol tokens from the description) into path-shaped vs * symbol-shaped. A path-shaped token grounds against the known-path set; a * symbol-shaped token grounds against the symbol corpus. If ANY token * grounds, the finding passes. If NONE grounds — including a finding that * cites only non-existent symbols — it is rejected (error). * * `findings` is the array of finding-shaped citations to ground (each carries * `affected_files` and an optional `description`). `repoRoot` is the working-tree * root enumerated by `git ls-files`. */ export declare function validateContractCitationGrounding(findings: readonly Finding[], repoRoot: string): ContractCitationGroundingResult; /** * INV-CVG-4 (B5) — reject a module whose `file_scope` points ONLY at thin * re-export shims rather than the file where its named responsibility logic * lives. `moduleDecompositionPayload` is the (envelope-unwrapped) module_decomposition * payload; `repoRoot` is the working-tree root enumerated via `git ls-files`. * * Behaviour: * - A scoped path that does not resolve to any tracked file is SKIPPED here (the * M-B3 citation gate owns path-existence; this gate only judges resolved files). * - A module is flagged only when it resolves ≥1 scoped path and EVERY resolved * path is a re-export shim (no real-logic file among them). * - Fail-closed only on an unreadable git tree (git missing / not a repo → error); * a valid-but-empty tree degrades to a warning (never hard-block a fresh repo). */ export declare function validateDecompositionFileScope(moduleDecompositionPayload: unknown, repoRoot: string): ValidationIssue[]; /** * INV-CO-13 — `finalized_module_contracts` must carry EXACTLY the module names * its drafted `module_contracts` input carries. Finalization is a mechanical * merge (`deriveFinalizedModuleContracts` maps the drafts 1:1), and the * contract_finalization role text says "for every module contract in * module_contracts", so this is a POST-CONDITION on a guarantee the pipeline * already states — not a new authoring requirement on any host. * * It exists because the derive is not the only writer: a judge repair or a * critique repair re-emits `contract_finalization` as an LLM step, and that * rewrite re-enters through ingestion, whose validator for this artifact * (`validateFinalizedModuleContracts`) is shape-only and structurally cannot see * the drafts. An LLM rewrite that merges two modules under an invented name and * drops a third is therefore shape-valid, and every downstream consumer — the * phase cut, the derived obligation ids, and the DAG write-scope prefix join — * is then built on a module set that has already silently lost a module. That is * not hypothetical: it collapsed 7 modules to 4 in the * dispatch-effectiveness-observability run and stranded two DAG nodes with an * empty write scope (docs/reviews/observability-dag-scope-join-2026-08-09.md). * * Ground truth is unambiguous and no host discretion is involved, so this is an * exact comparison rather than any form of tolerant matching: a renamed module is * reported as BOTH a drop and an invention, which is exactly what it is. It checks * multiplicity as well as membership — a repeated name is set-equal to the drafts * but still loses content, since consumers keep the first entry per name. * * Absent-tolerant on both sides (a run that has not reached finalization yet, or * a partial single-artifact self-check, must never fabricate an issue). */ export declare function validateFinalizedModuleSetPreserved(draftedModuleContracts: unknown, finalizedModuleContracts: unknown): ValidationIssue[]; /** * Whether one of the eight cross-artifact gates genuinely ran (`evaluated`), * or was skipped because its primary input was absent, malformed, or (digest * coverage only) its source was not enumerable — OBS-cca3801c / OBS-cca3801c-2. * An empty `issues` array alone cannot distinguish "ran clean" from "never * ran"; `evaluated` can. `reason` is present only when `evaluated` is false. * * INV-CPGV-OUTCOME-RECORD-OWNER: this module owns this record. The finalized * module contract's fuller design places the shared type in audit-tools/shared * so both adopters (contract-pipeline-orchestration, * remediate-nextstep-and-final-gate) import one shape — that relocation needs * an edit outside this work item's write scope (src/remediate/validation/ + * src/remediate/contractPipeline/ only), so it is deliberately deferred to * whichever work item can touch src/shared. The type is defined here, * additively, alongside the gates it classifies, so the OBS-cca3801c / * OBS-cca3801c-2 signalling obligation is discharged now rather than blocked * on that follow-up; see CP-BLOCK-CP-NODE-14's result deviations. */ export interface GateOutcome { /** Stable identity, in evaluateContractPipelineCrossGates's fixed canonical order. */ gate: "paired_obligations" | "evidence_threaded" | "digest_coverage" | "reconciliation_derivation" | "design_spec" | "implementation_dag_integrity" | "decomposition_file_scope" | "finalized_module_set_preserved"; evaluated: boolean; issues: ValidationIssue[]; /** Present only when `evaluated` is false. */ reason?: string; } /** Input to {@link evaluateContractPipelineCrossGates}. */ export interface ContractPipelineCrossGateInputs { /** Every contract-pipeline artifact payload currently known, by name. An * absent entry means "this artifact is not available" — every gate below * tolerates that (see the absent-tolerance note on the function itself). */ payloads: ReadonlyMap; /** The intake finding-enumeration payload (already read from disk), or * undefined when absent/not applicable. Passed through to * validateDigestCoverage verbatim. */ findingEnumeration?: unknown; /** Working-tree root, used by validateDecompositionFileScope's git-tree * enumeration. */ root: string; } /** * Evaluate the SAME 8 cross-artifact gates the plural `validate-artifacts` * sweep runs, returning one `ValidationIssue[]` PER gate in a FIXED canonical * order (never a flattened single array — callers that need per-gate issue * counts, e.g. INV-CVG-1's "one issue-string entry per failing gate", rely on * this shape; a caller that just wants everything can `.flat()` the result). * * Absent-input tolerance is guaranteed PER GATE, not by this runner — this * function adds NO extra tolerance logic of its own, it only wires named * payloads to each gate's positional arguments. Every gate below already * early-returns `[]` when its primary payload is missing/malformed: * * 1. validatePairedObligations — returns [] when obligationLedgerPayload * is not a record with an `obligations` array (see its own guard, top of * the function body, above in this file). * 2. validateEvidenceThreaded — has no single top-level guard; each of * its 3 checks is individually gated by its own `isRecord(...)` / * `Array.isArray(...)` condition, so with every payload absent all 3 * checks are skipped and it returns []. * 3. validateDigestCoverage — returns [] unless sourceType is * "structured_audit"/"mixed" AND findingEnumerationPayload is a record * AND is_enumerable !== false AND it has a non-empty findings array. * 4. validateReconciliationDerivation — returns [] when * seamReconciliationReportPayload is not a record with a `mismatches` * array (or that array is empty). * 5. validateDesignSpecGates — returns [] when its first argument is * not a record (called here with finalizedContracts as that argument). * 6. validateImplementationDAGIntegrity — returns [] when dagPayload is not a * record with a `nodes` array. * 7. validateDecompositionFileScope — returns [] when moduleDecompositionPayload * is not a record with a `modules` array. * 8. validateFinalizedModuleSetPreserved — returns [] unless BOTH the drafted * and the finalized module contracts are records with a `module_contracts` * array (and the drafted one names at least one module). * * So a partial pipeline (most artifacts absent — e.g. a single-artifact * self-check in an otherwise-empty run) can never false-fail: every gate * lacking its input contributes an empty array, not a fabricated issue. */ export declare function evaluateContractPipelineCrossGates(inputs: ContractPipelineCrossGateInputs): ValidationIssue[][]; /** * Evaluate the SAME 8 cross-artifact gates {@link evaluateContractPipelineCrossGates} * calls, in the SAME canonical order, additionally classifying each as * evaluated or skipped-with-reason (OBS-cca3801c / OBS-cca3801c-2, see * {@link GateOutcome}). Additive: evaluateContractPipelineCrossGates keeps its * existing `ValidationIssue[][]` shape unchanged for its existing callers * (validation/artifacts.ts, index.ts) — this is a parallel, richer view for a * caller that must refuse an empty `issues` array as proof-of-clean without * checking `evaluated` first. */ export declare function evaluateContractPipelineCrossGateOutcomes(inputs: ContractPipelineCrossGateInputs): GateOutcome[]; //# sourceMappingURL=contractPipelineGates.d.ts.map