import { L0Call } from '../bin/shim'; import { L0FaultCode } from './l0-fault-codes'; /** * The doc L0's deny messages point at. Lives in @webpieces/rules-config/templates alongside the others, * and is written to /.webpieces/instruct-ai/ lazily, only on an L0 BLOCK. * * That generated doc is the AUTHORITY for the fault table and the allowlist (same arrays, cannot * drift). guards/L0-tooling.md is the hand-written companion: it adds L0's evaluation * ORDER, the use cases and the known gaps, and it documents L1, none of which are rendered from code. * Change L0_FAULTS or L0_ALLOWLIST and the generated doc follows automatically — guards/L0-tooling.md does * not, so update it in the same PR. */ export declare const GUARD_MATRIX_DOC = "webpieces.guard-matrix.md"; /** * One CURE for a fault: the exact call, plus the `mention` that must appear in that fault's deny text. * * Both halves are asserted (l0-matrix.spec.ts): the call must be accepted by isAllowed(), and the deny * message must actually name it. That pairing is the anti-deadlock invariant — a message that * prescribes a command the allowlist rejects is exactly the shape of the three deadlocks CLAUDE.md * records, and it is how the dead `wp-setup-ai-hooks` bin in the config-missing text was caught. */ export declare class L0Cure { readonly mention: string; readonly call: L0Call; /** * The one to reach for first when a fault has several. Exactly one cure per fault carries it, * so the rendered Fix section never asks the reader to choose between equals. */ readonly preferred: boolean; /** * WHEN to pick this cure over its siblings — the sentence that makes a list of commands * actionable instead of a menu ("when the PIN is the stale side", not "an alternative"). */ readonly discriminator: string; constructor(mention: string, call: L0Call, /** * The one to reach for first when a fault has several. Exactly one cure per fault carries it, * so the rendered Fix section never asks the reader to choose between equals. */ preferred: boolean, /** * WHEN to pick this cure over its siblings — the sentence that makes a list of commands * actionable instead of a menu ("when the PIN is the stale side", not "an alternative"). */ discriminator: string); /** A Bash cure renders as a literal command; a tool-shaped one renders as the edit it stands for. */ isCommand(): boolean; } /** One L0 fault. Data-only → a class, per CLAUDE.md. */ export declare class L0Fault { /** * The codebook's letter — typed as the UNION of every declared code, not `string`, so * `L0_FAULT_NAMES[code]` is total and neither this module nor the deny builders need a * `?? 'unknown'` fallback. A fault added without a name fails to compile. */ readonly code: L0FaultCode; readonly name: string; readonly detectedBy: string; readonly enforcedIn: string; readonly cures: readonly L0Cure[]; /** * The artifact carrying this fault's deny text. For S/C/Y that is the deny string itself; for * D/X/U/K the text is built in POSIX sh inside the rendered shim, so it is the rendered shim — * the same bytes the consumer runs, which is what the mention assertion needs to search. */ readonly denyText: string; constructor( /** * The codebook's letter — typed as the UNION of every declared code, not `string`, so * `L0_FAULT_NAMES[code]` is total and neither this module nor the deny builders need a * `?? 'unknown'` fallback. A fault added without a name fails to compile. */ code: L0FaultCode, name: string, detectedBy: string, enforcedIn: string, cures: readonly L0Cure[], /** * The artifact carrying this fault's deny text. For S/C/Y that is the deny string itself; for * D/X/U/K the text is built in POSIX sh inside the rendered shim, so it is the rendered shim — * the same bytes the consumer runs, which is what the mention assertion needs to search. */ denyText: string); } export declare const CONFIG_MISSING_REPORT: string; export declare const CONFIG_OUT_OF_SYNC_HEADER: string; /** * THE L0 faults, in first-match-wins order. D/X/U/K are decided in POSIX sh BEFORE the bin runs (a * stale, missing or broken validator cannot be trusted to validate itself); S/C/Y are decided inside * the bin, in JS. One model, two enforcement points. */ export declare const L0_FAULTS: readonly L0Fault[]; /** * Render webpieces.guard-matrix.md from L0_FAULTS + L0_ALLOWLIST. * * A unit test locks the committed template byte-identical to this output, the same way * templates/ai-hook.sh is locked to renderShim(). That is what makes the doc assertable instead of * aspirational: the table in the doc IS the array the guard consults. */ export declare function renderGuardMatrixDoc(): string; /** * Drop the matrix doc where the AI can read it, and return its absolute path ('' if it could not be * written). Called from the L0 BLOCK path so the deny can say `READ `. * * Best-effort by design: this runs while the tree is already known-broken, and a missing template (an * @webpieces/rules-config older than this package) must degrade the deny message, never replace it * with a crash. */ export declare function writeGuardMatrixDoc(workspaceRoot: string): string; /** * The `READ ` pointer appended to an L0 deny, or '' when the doc could not be written. * * It opens with a NEWLINE, not a space: the JS-side L0 denies render in the house format now (a header, * a `[guard-name]` block, `Fix Option N:` lines), so a pointer glued onto the end of the last line would * be the one place the shape broke. A real newline is safe on both call paths — denyJson() JSON.stringifies * it, exactly as it does for every multi-line L1 report. */ export declare function guardMatrixPointer(docPath: string): string;