UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

507 lines (506 loc) • 34.5 kB
/** * The settlement record and the gate outcome — the L0 vocabulary of what ONE tool-gate pass decided, * what ask it settled and who asked. One structure, minted once (the gate's exit; the decide lane for a * durable park) and projected three times without re-derivation: the `permissionDenied` observer's * payload, the call's `tool_end` frame, and the durable row's resolved outcome. Lives beside * `tool-policy.ts` rather than in it because it is a different concern from the permission verdict a * policy composes: a policy says allow / ask / deny; this file says how the pass that consumed that * verdict ended. */ import { type AskClass, type AskOrigin, type AssertAllKeysHandled, type ModeReleaseReason, type PermissionMode, type PersistedRuleMandate } from "./ask-origin.js"; import { type AutoModeClassifierRound, type AutoModeFallbackCause } from "./auto-mode.js"; /** * How the wait an ask was in ENDED — the closed set. One word per way a wait can end; the `who` on the * record says which party ended it and is fixed BY the word (see {@link Settlement}). * - `human_allowed` / `human_refused` — a person (or the approver acting for one) decided; the refusal's * own note, when the decider attached one, rides `note`. * - `policy_allowed` / `policy_refused` — the DEPLOYMENT'S OWN POLICY decided: the approval channel reported * that a machine, not a person, answered this ask (an unattended lane answering by class, a headless lane * with no interactive approval surface, a standing deployment posture). A person and a policy are the two * DECIDERS: each answers either way, with the same capabilities, and differs only in whose decision the * record names. A policy's refusal is distinct from `human_refused` because the model is told a different * thing: a person's "no" is the user's direction, a policy's is a capability of this run. Distinct from * `no_approver` because an approval channel WAS wired and answered — core did not fall back to its own * headless posture. The refusal's own note, when the channel attached one, reaches the model as the * policy's words and NOT as a person's; it is not on this record (the note seat belongs to the person's * two words, whose ABSENCE is a control-flow fact the policy's do not have). * - `approval_window_expired` — an approval window elapsed with no answer: the engine's own approval * factory window (`createApprovalPolicy.approvalTimeoutMs`), or a synchronous host approver that * reported its own window elapsed. * - `denial_limit_window_expired` — the classifier denial-limit fallback's auto-deny window (core's own * timer over the synchronous leg) elapsed with no answer. * - `park_sla_expired` — a DURABLE park's SLA deadline passed and the host's sweep resolved it as a deny * (FACET A). A store `expire`/`reap` produces no `tool_end` and therefore no settlement. * - `no_approver` — headless: no approver (or no question face) is wired, or the deny posture string. * - `approver_unavailable` — the approver (or an ancestor's frozen approver) answered the ROUTING question * "nobody reachable", and no park took the ask. * - `approver_error` — the approver threw. * - `approver_contract` — the approver answered outside its contract (a non-boolean verdict, an * unreadable member, a timeout-settled allow, a refused attribution…). * - `presentation_failed` — the args or the edit could not be safely presented/adopted (unclonable). * - `blanket_allow_refused` — an allow NO PERSON JUDGED met a `requiresRealApproval` ask: a blanket * `onAsk:"allow"`, or an approver's allow it reported as the deployment's policy (`settledBy: "policy"`). * - `task_aborted` — the wait's abort signal ended it (the run's own end, or a turn-level interrupt). */ export declare const SETTLEMENT_KINDS: readonly ["human_allowed", "human_refused", "approval_window_expired", "denial_limit_window_expired", "park_sla_expired", "no_approver", "approver_unavailable", "approver_error", "approver_contract", "presentation_failed", "blanket_allow_refused", "policy_allowed", "policy_refused", "task_aborted"]; export type SettlementKind = (typeof SETTLEMENT_KINDS)[number]; /** Membership test for {@link SettlementKind} — the word crosses process boundaries on `tool_end`, so a * consumer validating one it received must not hand-roll the set. */ export declare function isSettlementKind(v: unknown): v is SettlementKind; /** * The settlement record: WHAT ended the wait (`kind`), WHO ended it (`who` — its shape is decided by the * word: the person's two words carry a person, the policy's two words the policy, the window words say whose * window, the park word the host, the fail-closed family nobody), WHEN (epoch ms, written at the settlement site), and for a person's * refusal the decider's own NOTE when one was attached (a refusal WITHOUT a note is a bare "no", the arm * the runner treats as a control-flow boundary for the issuing batch). * * It is a discriminated union and not a free product of `kind` × `who` on purpose: a free product can * express "human_allowed, ended by the engine's window" — a structurally complete record that contradicts * itself — and a consumer would then need the pairing rules this shape makes unwritable. * * @contract settlement.single_mint — every `Settlement` kind is composed by CORE at the site whose wait * ended (the ask resolver's arms, the approval factory's arms, the decide lane over a host's decision * FACTS), and the four DECIDER words by one composer ({@link settledByDecider}). A host never mints a kind: a synchronous approver reports `"human"`/`"timeout"`/`"policy"` on its * outcome and a durable decide reports `hostDecision.decidedBy` (`"person"`/`"sla_timeout"`/`"policy"`) — facts * core turns into the word. A settlement reaches the gate's exit only inside an engine-minted record on the decision * (`PermissionResult.attestation`, {@link import("./tool-policy.js").EngineAttestation}), which a policy cannot compose: * the receipt refuses a record the engine did not mint. */ export type Settlement = { kind: "human_allowed" | "human_refused"; who: { party: "person"; approver?: string; }; when: number; note?: string; } | { kind: "policy_allowed" | "policy_refused"; who: { party: "policy"; approver?: string; }; when: number; } | { kind: "approval_window_expired"; who: { party: "engine"; window: "approval_factory"; } | { party: "host"; }; when: number; } | { kind: "denial_limit_window_expired"; who: { party: "engine"; window: "denial_limit"; }; when: number; } | { kind: "park_sla_expired"; who: { party: "host"; approver?: string; }; when: number; } | { kind: "no_approver" | "approver_unavailable" | "approver_error" | "approver_contract" | "presentation_failed" | "blanket_allow_refused" | "task_aborted"; who: { party: "none"; }; when: number; }; /** * WHO ended a wait — the closed set of `Settlement.who.party` words, one per party a record can name: * `person` (a person decided), `policy` (the deployment's own policy decided), `engine` (one of the engine's * own windows elapsed), `host` (the host's window or its park sweep), `none` (nobody: the engine ended the * wait itself — no approver, a throw, an abort, an out-of-contract answer). Which party a word names is * fixed by the word ({@link Settlement}); the word crosses process boundaries on `tool_end`, so a consumer * validating a record it received reads this set rather than hand-rolling one. */ export declare const SETTLEMENT_PARTIES: readonly ["person", "policy", "engine", "host", "none"]; export type SettlementParty = (typeof SETTLEMENT_PARTIES)[number]; /** Compile-time fence: `never` while the closed set and the record's `who.party` union are the same words. */ export type SettlementPartiesMatchTheRecord = AssertAllKeysHandled<Exclude<Settlement["who"]["party"], SettlementParty> | Exclude<SettlementParty, Settlement["who"]["party"]>>; /** Membership test for {@link SettlementParty}. */ export declare function isSettlementParty(v: unknown): v is SettlementParty; /** Who REPORTED a durable decide (`hostDecision.decidedBy`): a person, the host's own SLA sweep at the row's deadline, or the * deployment's policy — the closed set; a host's decide door mirrors the words it accepts from this one. */ export declare const HOST_DECIDERS: readonly ["person", "sla_timeout", "policy"]; export type HostDecider = (typeof HOST_DECIDERS)[number]; export declare const isHostDecider: (word: unknown) => word is HostDecider; /** The host's DECISION FACTS on a durable decide — who ended the wait, and the attribution the approval channel * reported for that party. ONE shape for every decide that records its decider (a tool approval's and a plan * pause's); core mints the settlement word from it ({@link settlementOfHostDecision}), the host never supplies one. */ export interface HostDecisionFacts { decidedBy: HostDecider; approver?: string; } /** The DECIDER arms of {@link Settlement} — a person's and the deployment policy's, the two parties that * answer an ask either way. Named so a site that both composes one and quotes its note can read the seat * off the record instead of re-deriving it. */ export type DeciderSettlement = Extract<Settlement, { who: { party: "person" | "policy"; }; }>; /** * A decider's NOTE, normalised — the one rule for "did the decider say anything": empty text is a * decider who attached nothing and reads as absent wherever the note travels. Whitespace-only text is * not empty by this rule and stays a note: that half is PINNED CURRENT BEHAVIOUR, not a promise — * nothing here requires a note to carry content, and narrowing it is a separate decision with its own * consumers to warn. */ export declare function decidersNote(text: string | undefined): string | undefined; /** * THE mint for a decider's settlement — the party (a person, or the deployment's own policy) and its verdict * pick the word, and the channel's attribution is transcribed onto `who`. The synchronous resolver's arms, * the approval factory's arms and the durable decide lane all compose their decider record here, so one * answer cannot end as two different records depending on which path carried it, and the two parties are * one rule with a parameter rather than two composers. * * @contract settlement.note — the `note` seat is the PERSON's: filled exactly when the person attached a * reason, and its absence is how the record says the refusal was bare (the runner reads that absence, and * nothing else, as the issuing batch's control-flow boundary). Empty text folds to absence HERE, once, for * every path that fills the seat ({@link decidersNote}); a site that hand-builds the record instead is a * second answer to the same question, which is how one path came to halt a batch the other let run. A * policy's record has no note seat — its words reach the model inside the policy sentence, and a policy's * refusal is never a control-flow boundary — so a note handed with the policy party is not filed here. */ export declare function settledByDecider(party: DeciderSettlement["who"]["party"], verdict: "allowed" | "refused", approver: string | undefined, note?: string): DeciderSettlement; /** * THE mint for a durable decide's settlement, from the host's decision FACTS: the SLA sweep's word is the host's own * record (`park_sla_expired`, whatever the verdict — the capture refuses a sweep that allows), a person's and the * deployment policy's go through {@link settledByDecider}. Every durable decide that records its decider — a tool * approval's and a plan pause's — composes its record here, so one answer cannot end as two records depending on * which kind of row it resolved. `note` is the decider's own text on a refusal (filed on the person's refusal only). */ export declare function settlementOfHostDecision(facts: HostDecisionFacts, verdict: "allowed" | "refused", note?: string): Settlement; /** Which settlement kinds are REFUSALS (the ask ended as a deny). Total over the closed set — the fence * below refuses a member without a row — so invariant I2 is a table lookup, not a list in a condition. */ export declare const SETTLEMENT_IS_REFUSAL: { readonly human_allowed: false; readonly human_refused: true; readonly approval_window_expired: true; readonly denial_limit_window_expired: true; readonly park_sla_expired: true; readonly no_approver: true; readonly approver_unavailable: true; readonly approver_error: true; readonly approver_contract: true; readonly presentation_failed: true; readonly blanket_allow_refused: true; readonly policy_allowed: false; readonly policy_refused: true; readonly task_aborted: true; }; /** Compile-time fence: `never` while every settlement kind has a refusal row. */ export type SettlementRefusalTableCoversEveryKind = AssertAllKeysHandled<Exclude<SettlementKind, keyof typeof SETTLEMENT_IS_REFUSAL>>; /** * Which settlement kinds are a refusal NOBODY JUDGED — the wait ended because this run has no one who * could answer the question, not because someone considered it and said no. The classifier's * denial-limit fallback reads it: that fallback exists to bound a classifier that would otherwise deny * without end, and its last resort is to STOP the run rather than let the model keep re-asking a * question nothing in this deployment can answer. * * It is a TABLE and not a condition for the reason the sibling above is: this question used to be a * hand-written two-word disjunction at one site and a three-word one at another, so a settlement word * added later answered it by falling out of both — which is exactly what `policy_refused` did on the * commit that introduced it (a headless lane that refused by policy stopped reaching the stop seat that * the same lane reached when it refused by returning a bare `false`). * * The rows that say `false` each say something: * - the person's two words, `policy_allowed` and the window words — someone WAS asked; a decider answered, * or a window ran out. The run is not out of judges. (`policy_refused` is `true`: the deployment's own * policy declined a question it exists to answer, so nothing in this run will say yes to it.) * - `approver_unavailable` — a ROUTING answer, not a posture: the main gate owns that arm (it may park * the ask on a durable queue instead), and the two sites that treat it as headless anyway say so at * their own call, because they are the ones with no park route left. * - `approver_error` / `approver_contract` / `presentation_failed` — the approver was there and the * engine could not use it. A defect to report, not a deployment with nobody home. * - `task_aborted` — the run ended; nothing is escalating anywhere. */ export declare const SETTLEMENT_NOBODY_JUDGED: { readonly human_allowed: false; readonly human_refused: false; readonly approval_window_expired: false; readonly denial_limit_window_expired: false; readonly park_sla_expired: false; readonly no_approver: true; readonly approver_unavailable: false; readonly approver_error: false; readonly approver_contract: false; readonly presentation_failed: false; readonly blanket_allow_refused: true; readonly policy_allowed: false; readonly policy_refused: true; readonly task_aborted: false; }; /** Compile-time fence: `never` while every settlement kind has a nobody-judged row. */ export type NobodyJudgedTableCoversEveryKind = AssertAllKeysHandled<Exclude<SettlementKind, keyof typeof SETTLEMENT_NOBODY_JUDGED>>; /** * WHO REFUSED a call — the layer whose verdict is the deny. The other question a deny raises, "who ASKED", * is answered by {@link GateOutcome.origin} (an {@link import("./ask-origin.js").AskOrigin}); the two used * to share one seven-word list (`PermissionDeniedSource`) in which `classifier` meant both "the classifier * asked" and "the classifier denied". * - `policy` — the deployment `ToolPolicy` denied (directly, or re-checking an approved edit), or the * approval-edit chain hit its round cap. * - `hook` — a PreToolUse hook denied, threw, or never answered (the engine's fail-closed block on a * hook that made no decision counts as the hook layer's refusal for attribution). * - `org` — an organization policy rule denied; or, on a durable resume, the org's current verdict demands a person and * the recorded decider is the deployment's policy, not a person. * - `classifier` — the auto-mode classifier denied directly (its ASK-side role is `origin: * "denial_limit_fallback"`). * - `persisted_rule` — the person's persisted-rule lane refused: a deny row (live), or, on a durable resume, a deny * row, a call unreadable against the rows, or a wait for them the task's cancel ended. * - `compliance` — the compliance call-time lock. * - `write_protection` — an approved edit was rewritten by the restriction chain onto a write-protected * path that no approval covers. * - `ask_resolution` — the ask's own settlement is the refusal (a person said no, a window elapsed, * nobody could be asked…); the detail is on {@link GateOutcome.settlement}. * - `read_boundary` — the shell read station refused: a deny-listed read (refused on every seat, with no * ask), a read the read-only shell face may not make, or a rewritten / re-established command whose reads * no longer are what a person approved. * - `mode` — the permission MODE refused a surviving ask at the gate's arbitration point: `dontAsk` turns every ask * but a hook's own into a refusal, and `bypassPermissions` refuses a write whose protection * could not be judged. The disposition carries `mode`, `askClass` and `classes` beside the word. */ export declare const DENIED_BY_VALUES: readonly ["policy", "hook", "org", "persisted_rule", "classifier", "compliance", "write_protection", "ask_resolution", "read_boundary", "mode"]; export type DeniedBy = (typeof DENIED_BY_VALUES)[number]; /** Membership test for {@link DeniedBy}. */ export declare function isDeniedBy(v: unknown): v is DeniedBy; /** Which layers may VETO an approval a decider already gave (invariant I3: an approval settlement — a * non-refusal kind, a person's or a policy's — beside a `denied` disposition names one of these). The * approval-edit re-screen (hook), the policy, org and persisted-rule re-checks, and the write-protection * re-judge run AFTER the decider's yes; the * classifier and compliance run before any ask exists, and `ask_resolution` IS the * settlement, not a veto of it. `persisted_rule` = a person's own standing DENY row (their settings' * deny list, imported) — the personal-store sibling of `org`, and like it re-judged on an edited * command. `read_boundary` vetoes too: the rewrite re-judge and the pre-spawn re-establishment run after * the decider's yes, on bytes or targets the decider never saw. */ export declare const DENIED_BY_MAY_VETO: { readonly policy: true; readonly hook: true; readonly org: true; readonly persisted_rule: true; readonly classifier: false; readonly compliance: false; readonly write_protection: true; readonly ask_resolution: false; readonly read_boundary: true; readonly mode: false; }; /** Compile-time fence: `never` while every deny layer has a veto row. */ export type VetoTableCoversEveryDeniedBy = AssertAllKeysHandled<Exclude<DeniedBy, keyof typeof DENIED_BY_MAY_VETO>>; /** * The FORM of an auto-mode classifier deny — the one structural position a consumer branches on when a * deny came through the classifier station. The classifier ends a round three ways, and until this * member the wire carried the three as one layer word plus prose: * - `unavailable` — the classifier could not run (the model leg threw / rejected / ran past the cap): * a fail-closed refusal that SAYS SO — the call may simply be retried later (CC 2.1.250 `x1t`); * - `parse_error` — the classifier answered outside its verdict contract: blocked for safety, counted by * the denial limit like a ruling (CC 2.1.250 `Ure`); * - ABSENT beside `deniedBy: "classifier"` — the classifier's own RULING (a `<block>yes` verdict). * The WHY behind an `unavailable` (error vs timeout) is not a branching fact: the deny sentence spells it * and the `auto_mode.classified` trace frame carries it (`cause: "error" | "timeout"`). */ export declare const CLASSIFIER_DENY_CAUSES: readonly ["unavailable", "parse_error"]; export type ClassifierDenyCause = (typeof CLASSIFIER_DENY_CAUSES)[number]; /** Membership test for {@link ClassifierDenyCause}. */ export declare function isClassifierDenyCause(v: unknown): v is ClassifierDenyCause; /** Which deny layers a classifier cause may stand beside: the gate's own classifier station (`classifier`), * and an ANCESTOR's frozen classifier reached through the inherited policy chain, whose deny the child's * gate attributes to the chain (`policy`, station row 14) — the cause is attested by the station that * minted the deny, so the word beside it is whichever layer word the child's ledger assigns. */ export declare const DENIED_BY_MAY_CARRY_CAUSE: { readonly policy: true; readonly hook: false; readonly org: false; readonly persisted_rule: false; readonly classifier: true; readonly compliance: false; readonly write_protection: false; readonly ask_resolution: false; readonly read_boundary: false; readonly mode: false; }; /** Compile-time fence: `never` while every deny layer has a cause row. */ export type CauseTableCoversEveryDeniedBy = AssertAllKeysHandled<Exclude<DeniedBy, keyof typeof DENIED_BY_MAY_CARRY_CAUSE>>; /** The final disposition of a gate pass. `cause` is present exactly when the deny came out of an auto-mode * classifier round that did not RULE ({@link ClassifierDenyCause}); a ruling block and every non-classifier * deny carry none. Invariant I5 ({@link screenGateOutcome}): a cause stands only beside a layer word in * {@link DENIED_BY_MAY_CARRY_CAUSE}. * * `classifier` — the classification's model attribution, on BOTH arms (a classification ends either way, * allow or deny) and from BOTH stations (this gate's own classifier step, and an ANCESTOR's frozen * classifier reached through the inherited policy chain, whose allow and deny carry it out on the same seat * that carries the FORM): which model it was for, which one answered, and — only when the ladder moved — * the rung it left and why. It is the classification's account, not the settlement's: a classification that * RULED keeps it whoever then decided the call — a call whose question a person then answered (an ask a * station after the classifier raised, or a block at the DENIAL-LIMIT bound, which becomes an ask) carries * it beside that person's settlement on {@link GateOutcome}; who decided is read from the settlement * (`human_allowed`, `policy_allowed`, …), never from this member's presence. ABSENT reads UNKNOWN, never * "the seat answered" (that is `fallback` absent WITH the member present), and a consumer cannot tell its * three causes apart from the record alone: no classification happened; one ran on a decider given no * candidate ladder and can name no model (a hand-built decider; a parked ancestor's cross-process rebuild, * whose single fresh leg has none); or one did not rule (it could not run, or replied with no verdict) and * handed the question to the approval seat, whose settlement then names the resolution that ask reached. * Additive. Invariant I6 ({@link screenGateOutcome}) screens its shape. */ export type GateDisposition = { kind: "allowed"; classifier?: AutoModeClassifierRound; released?: ModeRelease; } | { kind: "denied"; deniedBy: DeniedBy; cause?: ClassifierDenyCause; classifier?: AutoModeClassifierRound; mode?: ModeRefusal["mode"]; askClass?: AskClass; classes?: readonly AskClass[]; }; /** * A permission MODE released a surviving ask — the allowed disposition's account of the question it did not put * to anyone: the mode, the disclosed class word (`askClass`, the first member of the folded set in `ASK_CLASSES` * order — one of {@link releasableAskClassesFor} on the call's face), the whole set, and the origin / mandate words the card would * have shown. `reason` is a member of {@link import("./ask-origin.js").MODE_RELEASE_REASONS} (`isModeReleaseReason`): * `"target_unresolved"` marks a release (the shell face) over a write target whose reading could not say where it leads; * absent, the release names none. The record carries no face, so the * screen asks that SOME face releases the class. */ export interface ModeRelease { mode: PermissionMode; askClass: AskClass; classes: readonly AskClass[]; origin?: AskOrigin; mandate?: PersistedRuleMandate; reason?: ModeReleaseReason; } /** The two modes that refuse on their own account (`deniedBy: "mode"`): `dontAsk` every ask but a hook's own, * `bypassPermissions` a write whose protection could not be judged. `plan` refuses nothing here — its questions ask. */ export interface ModeRefusal { mode: "bypassPermissions" | "dontAsk"; askClass: AskClass; classes: readonly AskClass[]; } /** * The WHOLE record of one tool-gate pass: the final disposition, plus — when this pass settled an ask — * the settlement it consumed and the ask's origin. Minted ONCE (the gate's exit; the decide lane for a * durable park) and projected three times without re-derivation: the `permissionDenied` observer's * payload, the call's `tool_end` frame, and the durable row's resolved outcome. The three faces are the * same object graph, so they cannot tell different stories. * * The three members are orthogonal but bound by four invariants ({@link screenGateOutcome}): * I1 `settlement` present ⇔ `origin` present (they describe the same ask); * I2 `disposition.deniedBy === "ask_resolution"` ⇒ `settlement` present and a refusal kind; * I3 `settlement` an approval (a kind {@link SETTLEMENT_IS_REFUSAL} says is not a refusal) ∧ * `disposition.kind === "denied"` ⇒ `deniedBy` is a veto layer ({@link DENIED_BY_MAY_VETO}) — a decider * approved, a later re-check refused; the approver stays on the settlement (it is not the approver's * refusal); * I4 `disposition.kind === "allowed"` ⇒ `settlement` absent or an approval (not a refusal kind); * I5 `disposition.cause` present ⇒ a member of {@link CLASSIFIER_DENY_CAUSES} beside a layer word * {@link DENIED_BY_MAY_CARRY_CAUSE} admits (the classifier station, or the inherited chain's `policy`); * I6 `disposition.classifier` present ⇒ both model ids are non-empty strings, and a `fallback` on it * names a `to` that IS `modelUsed` (the ladder landed where the record says) with a cause in * {@link import("./auto-mode.js").AUTO_MODE_FALLBACK_CAUSES} — a record whose `to` and `modelUsed` * disagree would let a reader answer "which model decided this call?" two ways; * I7 `disposition.released` present ⇒ `kind === "allowed"`, no settlement, `mode` is `bypassPermissions`, * `askClass` is an ask class (a member of the closed set — SHAPE; whether the class was releasable is the mint's * law at mint time, read by the gate's mode-release arm, never re-judged here against today's table: a persisted * record outlives the policy of its day), every member of `classes` is an ask class, `origin` / `mandate` / `reason`, * when present, are members of their closed sets, and `reason` names a member of `classes` (the mint takes it from * that set) — a mode releases nothing a person answered; * I8 `deniedBy === "mode"` ⇔ `mode` present (`bypassPermissions` | `dontAsk`) with `askClass` and `classes`. * Nothing here is read out of an ABSENCE: an ordinary allow with no ask is `{ disposition: { kind: "allowed" } }`, * a direct policy deny is `{ disposition: { kind: "denied", deniedBy: "policy" } }`, and neither carries a * settlement because neither settled one. */ export interface GateOutcome { disposition: GateDisposition; settlement?: Settlement; origin?: AskOrigin; } /** A release as `major.minor.patch`: the version whose engine stopped minting a retired word. */ type ReleaseVersion = `${number}.${number}.${number}`; /** * The words that LEFT a closed set a gate record carries, one list per set, each word with the version whose engine stopped * minting it. A persisted record outlives the vocabulary of its day as it outlives the policy of its day, so the screen's * archive reading ({@link screenGateOutcome}) reads each set as its live members plus these, and its producer reading reads the * live members alone. Every set the screen reads has a list (empty when nothing left it); no word is both live and retired * ({@link RetiredMembersAreNotLive}). A word-indexed table ({@link DENIED_BY_MAY_VETO}, {@link DENIED_BY_MAY_CARRY_CAUSE}, * {@link SETTLEMENT_IS_REFUSAL}) keeps rows for live words only: a retired word is read as granted nothing by it. */ export declare const RETIRED_MEMBERS: Readonly<{ askClass: readonly (Readonly<{ word: "write_protection_unresolved"; retiredIn: ReleaseVersion; }> | Readonly<{ word: "governance_unavailable"; retiredIn: ReleaseVersion; }>)[]; modeReleaseReason: readonly (Readonly<{ word: "write_protection_unresolved"; retiredIn: ReleaseVersion; }> | Readonly<{ word: "rule_unreadable"; retiredIn: ReleaseVersion; }>)[]; askOrigin: readonly Readonly<{ word: "unresolvable"; retiredIn: ReleaseVersion; }>[]; persistedRuleMandate: readonly Readonly<{ word: "write_protection_unresolved"; retiredIn: ReleaseVersion; }>[]; deniedBy: readonly Readonly<{ word: "plan_mode"; retiredIn: ReleaseVersion; }>[]; settlementKind: readonly []; settlementParty: readonly []; classifierDenyCause: readonly []; permissionMode: readonly []; autoModeFallbackCause: readonly []; }>; type MemberSet = keyof typeof RETIRED_MEMBERS; type RetiredWord<S extends MemberSet> = (typeof RETIRED_MEMBERS)[S][number]["word"]; /** Compile-time fence: `never` while no retired word is also a live member of its set. */ export type RetiredMembersAreNotLive = AssertAllKeysHandled<Extract<RetiredWord<"askClass">, AskClass> | Extract<RetiredWord<"modeReleaseReason">, ModeReleaseReason> | Extract<RetiredWord<"askOrigin">, AskOrigin> | Extract<RetiredWord<"persistedRuleMandate">, PersistedRuleMandate> | Extract<RetiredWord<"deniedBy">, DeniedBy> | Extract<RetiredWord<"settlementKind">, SettlementKind> | Extract<RetiredWord<"settlementParty">, SettlementParty> | Extract<RetiredWord<"classifierDenyCause">, ClassifierDenyCause> | Extract<RetiredWord<"permissionMode">, PermissionMode> | Extract<RetiredWord<"autoModeFallbackCause">, AutoModeFallbackCause>>; /** Compile-time fence: `never` while every reason word, live or retired, is an ask class of its day (I7 reads it in `classes`). */ export type RetiredReasonsAreClassWords = AssertAllKeysHandled<Exclude<RetiredWord<"modeReleaseReason">, AskClass | RetiredWord<"askClass">>>; /** Whether `word` left the closed set `set` ({@link RETIRED_MEMBERS}). A set name outside the registry throws. */ export declare function isRetiredMember<S extends MemberSet>(set: S, word: unknown): word is RetiredWord<S>; /** * The release law of THIS engine (I7's policy half): a mode release names a set of classes that one face of the engine's table * releases whole (`releasableAskClassesFor`) — the arbitration releases a set only when no member is immune on the call's face. * Policy, not shape — it moves between versions — so only the producer reading of {@link screenGateOutcome} reads it. * Module-level only; not part of the package root. */ export declare function releasePolicyDefects(g: unknown): string[]; /** The members only the denied arm of a {@link GateDisposition} carries. */ declare const DENY_ONLY_MEMBERS: readonly ["deniedBy", "cause", "mode", "askClass", "classes"]; type DenyOnlyMember = Exclude<keyof Extract<GateDisposition, { kind: "denied"; }>, keyof Extract<GateDisposition, { kind: "allowed"; }>>; /** Compile-time fence: `never` while the list is exactly the denied arm's own members. */ export type DenyOnlyMembersAreListed = AssertAllKeysHandled<Exclude<DenyOnlyMember, (typeof DENY_ONLY_MEMBERS)[number]> | Exclude<(typeof DENY_ONLY_MEMBERS)[number], DenyOnlyMember>>; /** * The one invariant screen for a {@link GateOutcome} (I1–I8 above, plus the closed-set word each member carries). Two readings: * - `"archive"` (the default) — a record read back from where it was kept (a replay, a ledger, a durable row): its shape, with * every closed set read as its live members plus {@link RETIRED_MEMBERS}. A record lawful when minted is not a defect later. * - `"producer"` — a record THIS engine is composing or publishing now: the live sets alone, and the engine's release law * ({@link releasePolicyDefects}). The gate's exit and its station screen read this. * The record is read as whatever it holds (`unknown`): a member that is not the shape the type names — `null`, an array, a * primitive where a record belongs, an object where a word belongs — is a defect naming its position, never an exception, and * every class, reason, origin and mandate word is read wherever the record carries it. * Returns the defects as sentences (an empty list is a clean record); the caller decides the fail-loud form (report + withhold, * or refuse), never silently repairs the record. An options value that is not an object, or a reading outside the two, throws. */ export declare function screenGateOutcome(g: unknown, opts?: { readonly reading?: "archive" | "producer"; }): string[]; export {};