/**
 * The synthetic-Read DENY judge — "would a `Read` of this file be refused outright?", asked without a call,
 * without an approver and without a hook. One judge, one set of layers, for every consumer that needs the
 * answer for a file the model did not itself ask to read (the changed-file notice today; a user's
 * attachment next). It answers in THREE states and never picks a direction:
 *
 *   deny          — some layer refuses it;
 *   no_objection  — every layer it consulted answered and none refused (an `ask` is not a refusal);
 *   unanswerable  — no layer refused, and at least one could not answer (see the table below).
 *
 * The CONSUMER decides where `unanswerable` goes: a notice that merely tells the model a file changed
 * reads it as no objection (the worst case is a notice about a file it then cannot re-read); a face that
 * would put a file's contents in front of the model reads it as a refusal.
 *
 * THE LAYERS (every one that can refuse a model's own `Read`): the run's skill deny rows; the
 * deny-narrowing fold (session rules, inherited ancestor rules, frozen constraint projections); the
 * deployment's caller policy; each live inherited parent policy; the person's persisted-rule lane; the org
 * lane. Not consulted, by design: PreToolUse hooks (the deployment's screening of a model's CALL), the
 * permission mode's write gate and the integrity guards (neither refuses a read), and the read-deny table
 * and containment roots (the path's own resolution judges those before this judge is asked). A deny
 * anywhere wins over an unanswerable layer elsewhere.
 *
 * FAULT → STATE (the live gate's own readings, through the same functions — never a second copy):
 *   | layer                       | fault                                                | state                         |
 *   | skill rows                  | throws                                               | unanswerable `threw`          |
 *   | narrowing fold              | throws / exceeds the judgment's budget               | unanswerable `threw`/`timed_out` |
 *   | caller / inherited policy   | throws / times out                                   | unanswerable `threw`/`timed_out` |
 *   | caller / inherited policy   | its deny carries an engine settlement (an approval   | unanswerable `approval_seat`  |
 *   |                             | seat that ended unanswered; in a combinePolicies     |                               |
 *   |                             | composition a rule deny anywhere is returned instead)|                               |
 *   | persisted lane              | store unreadable, throws, times out (readPersistedRuleLane ⇒ `{unreadable}`) | its rows take no part |
 *   | persisted lane              | the call cannot be read against the rows (`reason`) — applyPersistedTightening ⇒ `unreadable` | unanswerable `call_unreadable` |
 *   | org lane                    | unavailable: throws, garbage (readOrgRuleLane)       | unanswerable `org_unavailable` |
 *   | any layer                   | the judgment's budget (one dependency timeout, or a  | unanswerable `timed_out`      |
 *   |                             | batch's shared deadline) is spent                    | (persisted: rows take no part)|
 *
 * NEVER AN APPROVER. A deployment policy (the caller's, an inherited parent's) is consulted with an
 * ALREADY-ABORTED signal and no approver seat: the `ToolPolicy.check` contract is that an aborted signal
 * releases a pending approval, and every engine policy factory checks it before reaching a seat (the
 * approval factory answers a deny attested `task_aborted`, read here as `approval_seat`; the inherited
 * PreToolUse constraint does the same before invoking its hook). A deny that carries no engine settlement
 * is a rule's deny and stands. RESIDUALS (stated, not closed): a deployment closure that reaches an
 * approver before it reads its signal can still be asked; a deployment wrapper that answers an aborted
 * signal with a deny of its own (no settlement) is read as a deny — the attachment face then withholds,
 * the notice face stays silent. The engine's own layers (narrowing, skill rows, the two lanes) get the
 * run's real signal: they are pure matchers and store reads.
 *
 * THE PROBE'S IDENTITY. Every layer sees `toolName: "Read"`, the probed arguments, and the stable synthetic
 * call id `<face>-visibility-probe` ({@link syntheticReadProbeId}), so a deployment's audit can tell a
 * visibility probe from a model's call.
 */
import type { ToolCallRequest, ToolCallFace, ToolPolicy } from "./tool-policy.js";
import { type SkillGrantLane } from "./skill-grant.js";
import type { OrgGateVerdict, PersistedRuleAnswer } from "./hooks.js";
/** The layers the judge consults, as named in its verdicts. */
export declare const SYNTHETIC_READ_LAYERS: readonly ["skill_rows", "narrowing", "caller_policy", "inherited_constraint", "persisted_rules", "org_rules"];
export type SyntheticReadLayer = (typeof SYNTHETIC_READ_LAYERS)[number];
/** Why a layer could not answer — the consumer's disclosure word. */
export declare const SYNTHETIC_READ_UNANSWERABLE_CAUSES: readonly ["threw", "timed_out", "approval_seat", "call_unreadable", "org_unavailable"];
export type SyntheticReadUnanswerableCause = (typeof SYNTHETIC_READ_UNANSWERABLE_CAUSES)[number];
export type SyntheticReadVerdict = {
    verdict: "deny";
    layer: SyntheticReadLayer;
    message?: string;
} | {
    verdict: "no_objection";
} | {
    verdict: "unanswerable";
    layer: SyntheticReadLayer;
    cause: SyntheticReadUnanswerableCause;
    reason: string;
};
export interface SyntheticReadLayers {
    skillGrants?: Pick<SkillGrantLane, "denying">;
    denyNarrowingPolicy?: ToolPolicy;
    callerPolicy?: ToolPolicy;
    inheritedPolicies?: readonly ToolPolicy[];
    persistedRuleLane?: {
        adjudicate: (req: ToolCallRequest) => PersistedRuleAnswer | Promise<PersistedRuleAnswer>;
    };
    orgRuleLane?: {
        adjudicate: (req: ToolCallRequest) => Promise<OrgGateVerdict>;
    };
    /** The run's signal, for the engine's own layers. */
    signal?: AbortSignal;
}
/** The arguments a synthetic `Read` carries, and the facts a request stamps. */
export interface SyntheticReadCall {
    file_path: string;
    offset?: number;
    limit?: number;
    /** The tracked working directory a relative `file_path` resolves against. */
    cwd?: string;
    /** The live roster's face for `Read`, when the consumer has one. */
    face?: ToolCallFace;
}
/** The stable synthetic call id a probe carries: `<face>-visibility-probe`, `face` a lowercase word
 *  (letters and hyphens) naming the consumer. */
export declare function syntheticReadProbeId(face: string): string;
/** One judgment's wait budget. `deadlineAt` (epoch ms) bounds the WHOLE judgment — every layer waits at most
 *  what is left of it, and a layer reached after it has passed is not consulted (`timed_out`). Absent ⇒ the
 *  dependency timeout from the start of this judgment. A consumer judging a batch passes one shared deadline,
 *  so a hung layer costs the batch one bound, not one per file per layer. */
export interface SyntheticReadBudget {
    deadlineAt?: number;
}
export type SyntheticReadJudge = (call: SyntheticReadCall, face: string, budget?: SyntheticReadBudget) => Promise<SyntheticReadVerdict>;
export declare function createSyntheticReadJudge(layers: SyntheticReadLayers): SyntheticReadJudge;
