import { EffectiveTree } from './effective-tree'; import { BlockedResult } from './types'; /** * L1 row 5: git/gh commands must run from the repo root of the tree they act on, where the guards can * reason about git state coherently. guards/L1-location.md carries the table and the use cases; change * this predicate and that file is stale until you update it. * * ONE variable decides it: `tree.effectiveCwd` — the directory the command actually runs in, which is * the shell's cwd unless the command leads with `cd &&`. Root or not-root, nothing else. * * It used to be `shellAtRoot || cdsToRoot`, two variables OR'd, and that produced opposite verdicts for * the same destination: `git status` with the shell in packages/http/ was BLOCKED, while * `cd packages/http && git status` from the root was ALLOWED, because shellAtRoot short-circuited before * the destination was ever considered. An agent that cd's INTO a subdir has the same broken mental model * as one stranded there, so it gets the same answer now. * * The remedy is ONE runnable line, `cd && `, not "cd first, then re-run" — that advice * made this guard print the very command it had just rejected, and a `cd` is unreliable in both * directions (INTO this repo it sticks; OUT of it the harness resets it). * * It goes through remedyAtRoot(), NOT bare atRoot(), and that is load-bearing: A REMEDY MUST SATISFY THE * PREDICATE THAT PRINTED IT. Prefixing `cd '' &&` onto a command already leading with * `cd &&` left effectiveCwd in ``, so the identical block fired on the remedy with * the prefix doubled, then tripled. remedyAtRoot REPLACES the leading `cd` run instead, and * effective-tree.spec.ts asserts that property for every L1 remedy, not just this one. */ export declare class ForceToRootGuard { private readonly resolver; /** The deny report, or null to allow. */ block(command: string, tree: EffectiveTree, isGitOrGh: boolean): string | null; } export declare function gitFromSubdirBlock(command: string, tree: EffectiveTree, isGitOrGh: boolean): BlockedResult | null;