/**
 * `planShellReads` — ONE parse of a shell command into the read candidates every shell face judges from.
 *
 * The read boundary today re-derives "which words may be read" at several sites (the compound walk, the
 * containment-only gate, the refused-command whitelist, the execute-time recheck), each with its own idea
 * of launchers, option slots, redirections and re-entry. This module is the single extraction those sites
 * are to consume: a pure, zero-I/O function over the one lexer's word FACTS (`src/shell/lex.ts`) and the
 * program-position reader (`bash-program-position.ts`), returning every path word a command MAY read, each tagged with its
 * PROVENANCE (why it is a candidate) and the working-directory candidates it is relative to. Establishment
 * (`shell-read-establish.ts`) and judgment are downstream; nothing here decides allow/ask/deny.
 *
 * Contract, in one sentence per rule:
 *  · a LISTED reader hands its positional operands and the declared option-value slots (the verb tables of
 *    `bash-readonly-classifier.ts`, read here through their exported readers, never copied);
 *  · an UNLISTED program hands every word of its argv (a program declared to open no path operand hands none);
 *  · an ASSIGNMENT's value is a candidate only in a text that runs a program able to open a file;
 *  · a LAUNCHER chain is peeled to its program and the chain's own words are never candidates; a launcher
 *    option the row cannot read is recorded as a decline and every word after it is handed over;
 *  · a SHELL re-entry (`sh -c S`, `eval S`, a launcher handing text to a shell, `env -S S`, a builtin's code-body
 *    operand the shell runs later — `trap S SIG`, `alias N=S`, `mapfile -C S`) carries its text as a NESTED plan;
 *    an argv re-entry (`xargs`) is a decline unless the target opens nothing it is handed;
 *  · an INTERPRETER's inline program is a shape fact (`reentry:"interpreter"`), never parsed, never a finding;
 *  · `<` targets are read candidates, `>`-family targets are write targets (tagged, not read), here-documents
 *    and here-strings are data (an UNQUOTED here-document body's substitutions are nested plans);
 *  · `$(…)`, backticks and `<(…)` bodies are nested plans; a word carrying one cannot be resolved from the text;
 *  · `cd` moves the base the LATER segments resolve against, as a candidate SET (`&&` proves a move, nothing
 *    else does); `pushd`/`popd` lose the base; a loop body with a base move is replayed per LITERAL beat and
 *    otherwise loses the base for what follows.
 * Every ambiguity resolves toward MORE candidates, never fewer.
 */
import { type LenientConnector, type LexedRedirect, type LexedWord } from "../../shell/lex.js";
import { type ShellScanDecline } from "./bash-program-position.js";
/** WHY a word is a read candidate — a closed set, registered in `docs/CLOSED-SETS.md`. */
export declare const READ_CANDIDATE_PROVENANCES: readonly ["operand", "option_value", "stdin_redirect", "write_redirect_target", "env_assignment", "unlisted_word", "traversal_root", "cd_target", "program_path", "script_slot_file"];
export type ReadCandidateProvenance = (typeof READ_CANDIDATE_PROVENANCES)[number];
/** What the caller declares about the deployment: the listed readers and the boundary the bases start from. */
export interface ShellReadDeclarations {
    /** The listed read verbs. They decide whether a segment is read by verb slots or word by word, never whether it hands candidates. */
    readonly readers: ReadonlySet<string>;
    /** The declared home: a word that spells it (`$HOME` / `${HOME}` — {@link homeExpansionOf}) is named by it,
     *  unless the command may rebind `HOME` (then `~` is unresolvable too); `~` itself is left to establishment. */
    readonly homeDir?: string;
    /** The working directory the first segment's relative words resolve against. */
    readonly cwd: string;
    readonly roots: readonly string[];
    readonly face?: "open" | "roots";
}
export interface ReadCandidate {
    /** The folded word (quotes removed, a literal leading tilde spelled `./~`). */
    readonly spelled: string;
    /** The raw spelling, quotes retained — where "is this metacharacter quoted" is still answerable. */
    readonly raw: string;
    readonly provenance: ReadCandidateProvenance;
    /** `"glob"` = an unquoted pathname-expansion character; `"unresolvable"` = the shell rewrites the word
     *  (brace/variable/command substitution) so its text is not the path. */
    readonly expands: false | "glob" | "unresolvable";
    /** The working directories this word may be relative to (a candidate SET where a `cd` could not be proven);
     *  EMPTY for an absolute or home-rooted spelling; `"unmodelled"` for a relative word in a segment whose base the
     *  text does not settle (an unfollowable move before it, a loop this parser cannot count) — it cannot be placed. */
    readonly bases: readonly string[] | "unmodelled";
    /** Only beside `bases: "unmodelled"`: the working directories the text was certainly standing in when it LOST
     *  track (the reachable candidate set at the unprovable move; the declared cwd when nothing had moved yet).
     *  A deny row is a floor, so the judge resolves the spelling against these for deny rows ONLY — never to
     *  establish a read. `cd sub && cd $'\x2e' && cat <deny row under sub>` (bash: `cd .`) is refused through it. */
    readonly floorBases?: readonly string[];
    /** A home word the plan does not place (the text may rebind `HOME`, it sits in a value, or it is a literal tilde the
     *  program may expand itself): spelled with the DECLARED home. The judge reads it for deny rows ONLY. */
    readonly floorSpelled?: string;
    /** A deny-floor shadow of a `floorSpelled` spelling, established as a read and judged for deny rows only. */
    readonly denyOnly?: true;
    /** A WRITE target of the command (a `>`-family redirection, a `sed` `w` file, the operands of a file-writing command):
     *  established like a read and judged against the deny table's WRITE face only — never for containment, never a read. */
    readonly face?: "write";
}
/** How the shell reached one segment — what the base-candidate walk needs from the connectors. */
export interface SegmentReach {
    /** Reached from the previous emitted segment through `&&` only: everything before it exited zero. */
    readonly andJoined: boolean;
    /** The crossing that broke the `&&` run carried a `||`. */
    readonly orBroken: boolean;
    /** A directory change here, once proven to have run, moves the base the later segments see. */
    readonly moveProvable: boolean;
}
export interface ShellReadRedirect {
    readonly op: LexedRedirect["op"];
    readonly direction: "in" | "out" | "dup";
    readonly target?: ReadCandidate;
}
export interface ShellReadSegment {
    readonly text: string;
    readonly connector: LenientConnector;
    /** Every argv word (structure removed); the shape consumers' input. The read judgment reads `reads` only. */
    readonly argv: readonly LexedWord[];
    /** The launcher chain in front of the program (`env`, `nice`, …) — never candidates. */
    readonly launcherChain: readonly string[];
    readonly verb?: {
        readonly name: string;
        readonly basename: string;
        readonly listed: boolean;
    };
    /** `shell` = the program runs its argument as shell text; `argv` = its operands arrive on a stream;
     *  `interpreter` = it runs an inline program in a language this parser does not read (a shape fact only). */
    readonly reentry?: "shell" | "argv" | "interpreter";
    /** Program text the station READS as a rule (a `sed` script names the files it opens) but could not read here —
     *  supplied from a file, or not parseable to its end. What such a segment reads is then not established. */
    readonly unreadProgramText?: string;
    readonly declines: readonly ShellScanDecline[];
    readonly reads: readonly ReadCandidate[];
    readonly redirects: readonly ShellReadRedirect[];
    readonly baseMove?: {
        readonly target: ReadCandidate | "home" | "previous" | "unmodelled";
    };
    /** The working directories this segment may be standing in (what every relative candidate's `bases` is), or
     *  `"unmodelled"` when the text does not settle it. */
    readonly bases: readonly string[] | "unmodelled";
    /** Substitution bodies, unquoted here-document substitutions, and re-entered shell text, each its own plan. */
    readonly nested: readonly ShellReadPlan[];
    readonly reach: SegmentReach;
}
export interface ShellReadPlan {
    readonly segments: readonly ShellReadSegment[];
    /** The lexer refused the whole text (an unterminated construct, nesting past the bound). UNCONDITIONAL
     *  fail-closed for the consumer: a quote-blind re-read yields more words, never zero, so this is not "no candidates". */
    readonly unreadable?: string;
    /** Shape facts the same lex produced, for the shape consumers; the read judgment does not read them. */
    readonly shape: {
        readonly grouped: boolean;
        readonly backgrounded: boolean;
        readonly strayRedirection: boolean;
    };
}
/** Plan every read candidate of one command (the contract is this module's header). `arithmetic` = the text is an
 *  `$((…))` body, which bash evaluates and never globs (no extended-glob floor). */
export declare function planShellReads(command: string, decl: ShellReadDeclarations, depth?: number, homeRebound?: boolean, arithmetic?: boolean): ShellReadPlan;
