/** * Verification gate — the turn cannot claim "done" while a promised check is * still owed. * * When a run mutated code files and no test / typecheck / lint / build command * completed after the last mutation, the pre-stop hook injects a follow-up * demanding the project's verification be run. One initial demand plus at most * one post-verification recheck, only when new edits invalidate a completed * check. Unchanged work never repeats the demand. If the bounded budget runs * out, the demand requires disclosure of what remains unverified. * Prompt-only "verify before finishing" instructions can be ignored; this gate * is harness-owned bookkeeping on what actually executed. * * The broad runner-shape classifier below identifies verification attempts. * Authoritative success uses the stricter verification-evidence classifier * plus host-observed exit status and the revision captured at command start. * Missing or rejected evidence cannot clear outstanding verification. * * Second gate — TAMPER DISCLOSURE. A passing check only proves something if the * check itself was not the thing that changed. Editing a test, a test runner's * config, or adding a suppression pragma makes a red suite go green without * fixing anything, and the resulting transcript is byte-for-byte the shape of a * real fix: mutation, then `pnpm test`, then exit 0. So mutations that alter * what the check ASSERTS are recorded separately from ordinary code mutations, * and survive the verification they enabled — a check cannot clear the * suspicion that it was rigged. * * This gate DISCLOSES, it does not block: writing or repairing a test is normal, * legitimate work (TDD, adding coverage), so refusing to finish would punish the * common case. One demand per run: name what changed in the checks and why, and * confirm the fix stands without it. */ import type { Message } from "@kenkaiiii/gg-ai"; export declare const VERIFICATION_STATE_KIND = "verification_state"; /** Initial demands per run; at most one separate post-verification recheck is allowed. */ export declare const MAX_VERIFICATION_INJECTIONS = 1; /** Tamper-disclosure demands per run. Same reasoning as above: one is enough. */ export declare const MAX_TAMPER_INJECTIONS = 1; /** * True when the command looks like a test/typecheck/lint/build invocation: * compound segments considered independently, wrappers stripped, a known runner * leading, and a verification keyword among its operands. Deliberately strict — * `grep test x`, `git commit -m test` and `./run_tests.sh` all read as NOT * verification. */ export declare function isVerificationCommand(command: string): boolean; /** True for paths that look like source code (not docs, config or assets). */ export declare function isCodeFilePath(filePath: string): boolean; /** * Files that define what a check ASSERTS rather than what it tests: test files * and test directories, test-runner configs, and the type/lint configs whose * strictness the checks inherit. Editing one of these can turn a failing check * green without touching the behaviour under test. * * Deliberately matched on path shape only. Both error directions are cheap: a * miss leaves today's behavior, a false positive costs one disclosure sentence. */ export declare function isCheckOwnFile(filePath: string): boolean; /** The `+` lines of a unified diff, with the marker stripped. */ export declare function extractAddedLines(diff: string): string; /** Suppression/skip markers present in newly added text, de-duplicated. */ export declare function detectCheckWeakening(addedText: string): string[]; /** A mutation that changed what a check asserts, and why it looked that way. */ export interface SuspectMutation { filePath: string; reason: string; } export declare function buildTamperDisclosureMessage(suspects: readonly SuspectMutation[]): Message; export declare function buildVerificationFollowUpMessage(files: readonly string[], recheck?: boolean): Message; /** * Bookkeeping for "code was edited, nothing proved it since". Callers record * successful edit/write mutations on code files and host-observed check results. * Approval requires current successful evidence and no unresolved failures; * exhausting the reminder budget never clears the underlying problem. */ export declare class VerificationGate { private seq; private lastMutationSeq; private lastVerificationSeq; private injections; private recheckInjections; private lastDemandedMutationSeq; private tamperInjections; private failedChecks; private passedChecks; private unknownVerification; /** Code files mutated since the last verification — the gate's file list. */ private mutatedFiles; /** * Mutations that changed what a check asserts. Keyed by path so repeated edits * to one file disclose once. Deliberately NOT cleared by recordVerification(): * the whole point is that the passing check cannot clear the suspicion that it * was the thing edited. */ private suspects; /** * @param addedText Text the model ADDED in this mutation (the `+` lines of a * diff, or a written file's full content). Scanned for suppression and skip * markers; omit it and only the path check applies. */ recordMutation(filePath: string, addedText?: string): void; get revision(): number; recordVerification(revision?: number, command?: string): void; recordFailedVerification(command: string, revision?: number): void; requireFreshVerification(invalidateRevision?: boolean): void; verificationProblem(): string | null; snapshot(): { version: 1; seq: number; mutation: number; verified: number; files: string[]; failedChecks: string[]; unknown: boolean; }; restore(value: unknown): void; beginRun(): void; isOwed(): boolean; /** * True when a check passed after the run altered what the checks assert — * the false-green shape. Requires a verification to have completed: with none, * the standard gate already demands one, and demanding disclosure of an * unproven fix on top of it is noise. */ isTamperOwed(): boolean; /** Suspect mutations recorded this run, sorted for stable output. */ tamperSuspects(): SuspectMutation[]; /** * Would a stop right now inject? Lets the session arm clients BEFORE the * candidate final answer streams, so the draft the injection replaces is held * rather than painted and then superseded. */ willInject(): boolean; pendingReason(): "initial" | "recheck" | "tamper" | null; /** * The blocking message for the pre-stop hook, or null when nothing is owed * or its bounded budget is spent. A later edit after the demanded check gets * one additional pass; unchanged/unverified work never repeats a reminder. */ followUp(): Message[] | null; reset(): void; } //# sourceMappingURL=verification-gate.d.ts.map