/** * 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. Exactly one such follow-up per * run: a gate that keeps prompting after the model has decided it is done buys * nothing but extra full-length final answers, so the demand carries its own * fallback ("say which changes went unverified") and the gate then goes silent. * Prompt-only "verify before finishing" instructions can be ignored; this gate * is harness-owned bookkeeping on what actually executed. * * Simplification: verification is recognised by a conservative runner-shape * classifier (package-manager/runner + test/lint/check keyword) applied to each * segment of a compound command. Both error directions are safe — a missed * recognition leaves the gate silent (today's behavior), a false positive merely * skips one continuation. * * 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"; /** Follow-ups per run. After this the gate is silent for the rest of the run. */ 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[]): Message; /** * Bookkeeping for "code was edited, nothing proved it since". Callers record * successful edit/write mutations on code files and completed foreground * verification commands, in occurrence order; the gate is owed whenever the * newest recorded event is a mutation. */ export declare class VerificationGate { private seq; private lastMutationSeq; private lastVerificationSeq; private injections; private tamperInjections; /** 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; recordVerification(): 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; /** * The blocking message for the pre-stop hook, or null when nothing is owed * or the single injection is spent. Still-owed on a later stop is deliberately * silent: the demand already told the model to disclose what went unverified, * and one more injection only costs the user another restated final answer. */ followUp(): Message[] | null; reset(): void; } //# sourceMappingURL=verification-gate.d.ts.map