/** Which tools a row covers. `B` Bash · `R` Read · `E` Write/Edit. */ export type L2Tool = 'B' | 'R' | 'E'; /** What L2 does with a row — the same action codebook every layer reports in (GUARD_MATRIX.md). */ export type L2ActionKind = 'allow' | 'exempt' | 'block' | 'fail-open'; /** * The TERMINAL fail-open row, and the one number in this table that is not from the 1-10 design. * * Everything in rows 6-10 needs the main-sync cache, and the cache is written by a fire-and-forget * refresher that populates it for the NEXT call — so the first tool call of every session has none. * "Stop here and ALLOW" was written as a DIVIDER in the design table, i.e. as prose between two blocks * of rows. Prose cannot be stamped into a log line, and this is the single most frequently taken exit * in the whole layer (every session's first call, every unreadable branch, every unreachable forge), so * it is a row with a number like any other. * * It is 11 rather than 6-with-a-renumber because row numbers are IDENTITY here: they are printed in the * doc and logged as `row=`, so shifting 6-10 down would silently re-point every reference. The doc * prints it in its true position, between rows 5 and 6, with its number shown — same treatment L1 gives * row 8, which is printed third and numbered 8. */ export declare const L2_FAIL_OPEN_ROW = 11; /** The `act` cell: the doc's literal label, plus the machine-readable kind behind it. */ export declare class L2Action { readonly label: string; readonly kind: L2ActionKind; constructor(label: string, kind: L2ActionKind); } export declare const L2_ALLOW: L2Action; export declare const L2_EXEMPT: L2Action; export declare const L2_BLOCK: L2Action; export declare const L2_FAIL_OPEN: L2Action; /** * One row of the "L2 use cases" table: what you SEE, the state it puts you in, the verdict, the fix. * * THE POINT OF THIS CLASS is that a use case is added HERE, in code, beside the row it exercises — not * into a hand-written doc section that drifts. When a new situation comes up in a session, it becomes * one more `new L2UseCase(...)` on the row that judged it, `pnpm guards:generate` re-renders the doc, * and the byte-lock spec fails if anyone edits the rendered table instead. * * The four text fields are rendered VERBATIM. `reason` is the ENFORCEMENT half and is never rendered: * it is the exact `reason` string the guard logs for this case, so a spec can push it back through * `l2RowForReason` and assert it lands on the row this use case is filed under. That closes the loop * the L2 decision log opens — `row=` in the trail, this table on the page, one join between them. * * `reason` is REQUIRED, and a case that exercises something which is not an L2 row exit says so with * `NO_ROW_EXIT` rather than by omitting the argument. An optional field would make opting OUT of the * only real enforcement here the shortest thing to type and impossible to grep — the widening-by-absence * shape CLAUDE.md rejects. `grep NO_ROW_EXIT` now lists every unenforced case. */ export declare class L2UseCase { readonly num: number; readonly symptom: string; readonly state: string; readonly verdict: string; readonly fix: string; readonly reason: string; constructor(num: number, symptom: string, state: string, verdict: string, fix: string, reason: string); } /** * The `reason` for a use case that is NOT an L2 row exit, and so has nothing to join back to. * * The only legitimate case today is row 3: merge-in-progress is L4's state, and L2 exempts it without * logging a reason of its own. Named rather than absent, so "this case is not enforced" is a value in * the table you can grep for instead of a missing argument nobody notices. */ export declare const NO_ROW_EXIT = "NO_ROW_EXIT (not an L2 row exit \u2014 another layer owns this state)"; /** * One row of L2's decision table. * * `cure` is rendered verbatim into the doc and is LITERAL by policy: L0's cure-reachability discipline * says a message pointing at documentation for its own remedy cannot be tested, and it caught a fault * prescribing a bin that had been renamed away. `—` is the only legal non-command cure, and only on a * row that allows. */ export declare class L2Row { readonly num: number; readonly tools: readonly L2Tool[]; /** The `state` cell, verbatim. */ readonly state: string; readonly action: L2Action; /** The `cure` cell, verbatim. `—` when the row allows. */ readonly cure: string; /** * The observed situations this row judges. Rendered as the "L2 use cases" table. * * A NON-EMPTY tuple, and required: a row nobody has ever seen fire is either dead or * undocumented, and both are worth knowing. Expressing that in the TYPE rather than as a * runtime assertion is the JwtRoles pattern — the invariant is enforced at the moment the row * is written, which is the only moment that changes what somebody types. */ readonly useCases: readonly [L2UseCase, ...L2UseCase[]]; constructor(num: number, tools: readonly L2Tool[], /** The `state` cell, verbatim. */ state: string, action: L2Action, /** The `cure` cell, verbatim. `—` when the row allows. */ cure: string, /** * The observed situations this row judges. Rendered as the "L2 use cases" table. * * A NON-EMPTY tuple, and required: a row nobody has ever seen fire is either dead or * undocumented, and both are worth knowing. Expressing that in the TYPE rather than as a * runtime assertion is the JwtRoles pattern — the invariant is enforced at the moment the row * is written, which is the only moment that changes what somebody types. */ useCases: readonly [L2UseCase, ...L2UseCase[]]); /** `B R E`, the doc's own spelling of the tool cell. */ toolCell(): string; } /** * THE ELEVEN L2 ROWS, in first-match-wins order. * * Rows 1-5 need NO cache and fire on call #1: rows 1, 2 and 4 are text matches, row 3 is a marker-file * scan, row 5 is one `git rev-parse`. Row 11 is the cache divider. Rows 6-10 all read the cache. * * THE ORDER OF ROW 5 IS THE MOST LOAD-BEARING THING IN THIS TABLE. Put "on main" BELOW the divider and * WRITES on `main` are permitted for the whole first call of every session — and permanently in a * multi-worktree repo, where another tree may hold the cache lock indefinitely. * * `B` AND `E` PART COMPANY ON `main`, and rows 5/6/7 are where. A WRITE on `main` is wrong at any * freshness — the work lands somewhere unreviewable and unrevertable — so row 5 is `E` only, judged on * the branch alone, above the divider. A READ or a BUILD on a CURRENT `main` is harmless, and blocking * it strands the agent right after `pnpm wp-checkout-clean-main` put it there; so `B` joins `R` on the * FRESHNESS-gated pair below the divider (row 6 behind → block, row 7 current → allow), where "cannot * tell" fails open at row 11 by construction. `B` and `R` still differ in SHAPE inside row 6: a Read * names one file and is judged precisely, a Bash command is opaque and gets default-deny plus row 4. */ export declare const L2_ROWS: readonly L2Row[]; /** * Every use case on every row, in row order, for the doc and for the exhaustiveness specs. * * Numbering is GLOBAL and is identity, exactly as the row numbers are: a use case is cited by number in * review and in the doc, so add new ones at the END of the highest number rather than renumbering to * keep a row's block contiguous. */ export declare function allL2UseCases(): readonly L2UseCase[]; /** * The row a logged reason belongs to, or null when nothing claims it. * * Null rather than a default row: a reason with no row is a HOLE in the table, and defaulting it to * "fail-open" would hide exactly the drift the exhaustiveness spec exists to catch. The guards render * null as `row=-`, so an unmapped reason is visible in the log too, not only in CI. */ export declare function l2RowForReason(reason: string): number | null; /** Every reason string this table claims, for the exhaustiveness spec. */ export declare function l2MappedReasons(): readonly string[]; /** One documented gap between a row and what the guards actually do today. Data-only. */ export declare class L2NotDone { readonly row: number; readonly gap: string; readonly why: string; constructor(row: number, gap: string, why: string); } /** * WHERE THE TABLE AND THE CODE DISAGREE, stated rather than papered over. * * The L1 precedent is `## Not done — \`o\` is not exempt yet`: a row the runner cannot reach, named in * the generated doc with the reason it has not shipped. The same treatment applies here, and it is what * makes it safe to publish a table the guards do not yet dispatch from — a reader is told exactly which * rows describe intent rather than behaviour, and the log's `row=` stamps land on row 11 for every one * of these, so the trail never claims the strict row fired. */ export declare const NOT_DONE: readonly L2NotDone[];