/** * Decision — the 6-valued output of adjudicate(envelope, state, policy). * * EXECUTE → mutation is authorized; proceed to side effect * REFUSE → not allowed; surface a typed Refusal to the user * ESCALATE → defer to a human or supervisor; block until resolved * REQUEST_CONFIRMATION → ask the user to re-confirm, then re-adjudicate * DEFER → valid but awaits an external signal (e.g. payment webhook) * REWRITE → kernel substitutes a sanitized/normalized/capped envelope * * REWRITE is scope-restricted: sanitization, normalization, and safe mechanical * capping only. Never business transformation. See @adjudicate/core/README.md. * * **REWRITE re-adjudication & executed-hash recording (011).** A REWRITE is a * *proposal substitution*, not authorization. The `rewritten` envelope carries * its own content-addressed `intentHash`; the kernel never mutates that recipe. * Two things happen to a REWRITE before any side effect: * 1. In the pure kernel (`adjudicate`), the rewritten `intentHash` is re-derived * fail-closed and a taint-elevating rewrite is blocked (a non-deterministic * rewrite may only INCREASE friction — §C monotonicity). * 2. In the audited shell (`adjudicateAndAudit`), the rewritten envelope * re-enters the kernel ONCE; only a second-pass EXECUTE lets it reach the * executor. The EXECUTED (rewritten) hash — not the original — is the audit * row's indexed `intentHash` and the ledger-claim key, linked back to the * original via a `rewrite_executed` supersession. * REWRITE→REWRITE is bounded to that single pass: a rewrite that itself rewrites * falls through to REFUSE, never recursing. */ import type { AggregateSnapshot, AuthorityEdge, AuthorityGraph, AuthorityRelationship, IntentEnvelope, RecordedAggregateSnapshot, RecordedAuthoritySnapshot } from "./envelope.js"; import type { DecisionBasis } from "./basis-codes.js"; import type { Refusal } from "./refusal.js"; export type DecisionKind = "EXECUTE" | "REFUSE" | "ESCALATE" | "REQUEST_CONFIRMATION" | "DEFER" | "REWRITE"; export type Decision = { kind: "EXECUTE"; basis: readonly DecisionBasis[]; } | { kind: "REFUSE"; refusal: Refusal; basis: readonly DecisionBasis[]; } | { kind: "ESCALATE"; to: "human" | "supervisor"; reason: string; basis: readonly DecisionBasis[]; } | { kind: "REQUEST_CONFIRMATION"; prompt: string; basis: readonly DecisionBasis[]; } | { kind: "DEFER"; signal: string; timeoutMs: number; basis: readonly DecisionBasis[]; } | { kind: "REWRITE"; rewritten: IntentEnvelope; reason: string; basis: readonly DecisionBasis[]; }; /** Construct an EXECUTE decision with the given basis list. */ export declare function decisionExecute(basis: readonly DecisionBasis[]): Decision; export declare function decisionRefuse(refusal: Refusal, basis: readonly DecisionBasis[]): Decision; export declare function decisionEscalate(to: "human" | "supervisor", reason: string, basis: readonly DecisionBasis[]): Decision; export declare function decisionRequestConfirmation(prompt: string, basis: readonly DecisionBasis[]): Decision; export declare function decisionDefer(signal: string, timeoutMs: number, basis: readonly DecisionBasis[]): Decision; export declare function decisionRewrite(rewritten: IntentEnvelope, reason: string, basis: readonly DecisionBasis[]): Decision; /** * The restrictiveness rank of a `DecisionKind` on the §C lattice: 0 = least * restrictive (EXECUTE) … 5 = most restrictive (REFUSE). Higher == more friction. * * Pure, total, and derived from `kind` alone — no `Decision` field is read or * added. Used by `clampToCeiling`; exported so downstream ceiling consumers * (05x/10x/11x) and tests can reason about the same total order. */ export declare function restrictivenessRank(kind: DecisionKind): number; /** * Total restrictiveness order: `true` iff `a` is at least as restrictive as `b` * (a's friction >= b's friction) on the §C lattice. Reflexive. */ export declare function isAtLeastAsRestrictive(a: DecisionKind, b: DecisionKind): boolean; /** * `clampToCeiling` — index §C's `final = min(deterministic, ceiling)` over the * restrictiveness lattice. Returns whichever of the two Decisions carries the * GREATER friction (the more-restrictive `kind`), so a ceiling may only RAISE * friction, never lower it. * * Semantics (the monotonicity primitive 05x/10x/11x consume): * • If `ceiling` is strictly MORE restrictive than `deterministic`, the * ceiling Decision is returned verbatim (friction is raised). * • Otherwise (`ceiling` equal or LESS restrictive) the `deterministic` * Decision is returned UNCHANGED. A ceiling can never weaken below the * deterministic decision: `REFUSE`-ceiling over an `EXECUTE`-deterministic * raises to REFUSE; an `EXECUTE`-ceiling over a `REFUSE`-deterministic is a * no-op (the REFUSE stands). Only deterministic rules authorize EXECUTE — * the result is EXECUTE iff `deterministic.kind === "EXECUTE"` (invariant * #1/§C), because EXECUTE is the unique minimum of the lattice, so any * non-EXECUTE ceiling clamps an EXECUTE deterministic upward and an EXECUTE * ceiling never lowers a more-restrictive deterministic decision. * * This adds NO field and NO 7th outcome: it only SELECTS between two existing * Decisions by their `kind`'s lattice rank, returning one of them as-is (so its * `basis`/payload is preserved exactly — no synthesis, no mutation). * * Fail-closed (§C / invariant #6): the function is total over the closed algebra * and is biased to friction on a tie (it keeps `deterministic` only when the * ceiling is NOT strictly more restrictive, i.e. it never trades a more- * restrictive ceiling for a less-restrictive deterministic decision). * * Pure & synchronous (kernel-purity §D): no clock, RNG, or IO. */ export declare function clampToCeiling(deterministic: Decision, ceiling: Decision): Decision; /** * The keys in an envelope's `resourceRefs` the resolver consults (032). Both are * OPTIONAL on any given kind — a kind that declares neither yields a fact with * no matched edges (the resolver never throws on a missing ref). `principal` is * the actor's RESOLVED identity (the owner/account holder the action is taken * as), `resource` is the target the action operates on. * * Defaulted to the conventional ref names so the canonical call site is just * `resolveOwnership(store, envelope)`; a kind with bespoke ref names passes an * override. */ export interface OwnershipRefKeys { /** `resourceRefs` key carrying the acting principal's identity. Default `"owner"`. */ readonly principalKey?: string; /** `resourceRefs` key carrying the target resource's identity. Default `"resource"`. */ readonly resourceKey?: string; } /** * The ownership FACT the resolver returns — predicate results over the injected * snapshot, NOT a `Decision`. A guard (034) reads these booleans to decide; the * resolver itself never authorizes. * * `bound` is the load-bearing predicate (does an edge bind this principal to this * resource?). `relationships` enumerates the matched edges' relationship labels * in snapshot order (so `advisor`-only vs `owns` is distinguishable); `permits` * unions the matched edges' permitted actions; `edges` is the matched subset * verbatim for a downstream limit check. All deterministic functions of the * snapshot + the resolved (principal, resource). */ export interface OwnershipFact { /** The resolved acting principal identity (or `null` when the ref is absent). */ readonly principal: string | null; /** The resolved target resource identity (or `null` when the ref is absent). */ readonly resource: string | null; /** True iff ≥1 snapshot edge binds `principal` to `resource`. */ readonly bound: boolean; /** Matched edges' relationship labels, snapshot order, de-duplicated. */ readonly relationships: readonly AuthorityRelationship[]; /** Union of the matched edges' permitted action names, snapshot order, de-duplicated. */ readonly permits: readonly string[]; /** The matched edges verbatim (for a downstream limit/permits check). */ readonly edges: readonly AuthorityEdge[]; } /** * Read-only lookup surface over a frozen authority-graph snapshot (032). The * store is a constructor: it captures the snapshot and exposes pure edge lookups. * No `Date.now`/`Math.random`/IO on any path (kernel-purity §D, invariant #5). * * The store NEVER mutates the snapshot. `graph` is the captured snapshot (its * `edges` array is frozen on construction so a holder cannot mutate it in place). */ export interface AuthorityGraphStore { /** The captured (frozen) snapshot — exposed read-only for serialization/audit. */ readonly graph: AuthorityGraph; /** * Edges binding `principal` to `resource`, in snapshot order. Pure: a stable * function of the snapshot. Returns `[]` when either id is `null`/absent or no * edge matches. */ readonly edgesFor: (principal: string | null, resource: string | null) => readonly AuthorityEdge[]; } /** * Construct a read-only authority-graph store over an immutable snapshot (032). * Freezes the snapshot's `edges` array (shallow) so the store cannot be mutated * through the captured reference. Pure — no clock/RNG/IO. */ /** * Content-address an authority-graph snapshot (032 T3) for the audit record so * the injected snapshot replays bit-identically (invariant #5). Rides * `@adjudicate/canonical` (`sha256SnapshotCanonical`, RFC 8785 / JCS) — NOT a * forked canonicalizer — so a recorded graph never drifts from `intentHash` * semantics (NFC, fail-on-non-finite). Pure: no clock/RNG/IO. */ export declare function hashAuthorityGraph(graph: AuthorityGraph): string; /** * Build the RECORDED authority snapshot (033 T3/T4) from the graph the kernel * decision was INJECTED with: pairs the immutable `graph` with its * content-address (`hashAuthorityGraph`). The audit shell records this onto the * `AuditRecord` so the decision replays bit-identically (§D-5, invariant #5). * * Pure & synchronous (kernel-purity §D): no clock/RNG/IO. Re-running it over the * same graph yields a byte-identical `snapshotHash`. */ export declare function recordAuthoritySnapshot(graph: AuthorityGraph): RecordedAuthoritySnapshot; /** * Replay primitive (033 T5): re-derive the read-only `AuthorityGraphStore` from * a RECORDED snapshot so the pure resolver re-runs over the SAME injected edges * and reproduces the byte-identical `OwnershipFact` — the §D-5 replayability * proof that the recorded snapshot is sufficient to re-derive the decision. * * Fail-closed integrity (§D-5 / invariant #6): re-content-addresses the recorded * `graph` and compares to the stored `snapshotHash`. A mismatch means the * recorded snapshot was tampered with or drifted from its hash — replaying it * would silently re-decide over a DIFFERENT graph than the hash claims, so we * throw rather than produce a non-faithful replay. A faithful recorded snapshot * always passes and yields a store identical to the one used at decision time. * * Pure & synchronous: no clock/RNG/IO. */ export declare function authorityGraphStoreFromRecorded(recorded: RecordedAuthoritySnapshot): AuthorityGraphStore; export declare function createAuthorityGraphStore(graph: AuthorityGraph): AuthorityGraphStore; /** * Content-address an aggregate/limit snapshot (052 T2) for the audit record so * the injected snapshot replays bit-identically (invariant #5). Rides * `@adjudicate/canonical` (`sha256SnapshotCanonical`, RFC 8785 / JCS) — NOT a * forked canonicalizer — so a recorded snapshot never drifts from `intentHash` * semantics (NFC, fail-on-non-finite). Pure: no clock/RNG/IO. */ export declare function hashAggregateSnapshot(snapshot: AggregateSnapshot): string; /** * Build the RECORDED aggregate snapshot (052 T2) from the snapshot the kernel * decision was INJECTED with: pairs the immutable `snapshot` with its * content-address (`hashAggregateSnapshot`). The audit shell records this onto * the `AuditRecord` so the decision replays bit-identically (§D-5, invariant #5). * * Pure & synchronous (kernel-purity §D): no clock/RNG/IO. Re-running it over the * same snapshot yields a byte-identical `snapshotHash`. */ export declare function recordAggregateSnapshot(snapshot: AggregateSnapshot): RecordedAggregateSnapshot; /** * Replay primitive (052 T2/T6): return the SAME immutable `AggregateSnapshot` * from a RECORDED snapshot so the pure kernel re-reads byte-identical windows * and reproduces the byte-identical decision — the §D-5 replayability proof that * the recorded snapshot is sufficient to re-derive the decision. * * Fail-closed integrity (§D-5 / invariant #6): re-content-addresses the recorded * `snapshot` and compares to the stored `snapshotHash`. A mismatch means the * recorded snapshot was tampered with or drifted from its hash — replaying it * would silently re-decide over DIFFERENT windows than the hash claims, so we * throw rather than produce a non-faithful replay. A faithful recorded snapshot * always passes and yields a snapshot identical to the one used at decision time. * * Pure & synchronous: no clock/RNG/IO. */ export declare function aggregateSnapshotFromRecorded(recorded: RecordedAggregateSnapshot): AggregateSnapshot; /** * The PURE ownership resolver (032): `(store, envelope) => OwnershipFact`. Binds * the envelope's declared principal/resource (`resourceRefs`, 031) to the * injected snapshot and returns the predicate FACT — NEVER a `Decision`. * * Resolves the `(principal, resource)` pair from the envelope's `resourceRefs` * (by `OwnershipRefKeys`, defaulting to `owner`/`resource`), then asks the store * for the matched edges. `bound` is true iff an edge binds the pair; the matched * relationships (`owns`/`joint`/`advisor`/`custodian`) and permitted actions ride * along for a downstream authority guard (034) to interpret. * * Pure & synchronous (kernel-purity §D, invariant #5): a deterministic function * of the snapshot + envelope only — no clock, RNG, or IO — so it replays * bit-identically. It NEVER authorizes EXECUTE and NEVER lowers friction * (index §B/§C): a missing ref or no matching edge simply yields `bound: false`. */ export declare function resolveOwnership(store: AuthorityGraphStore, envelope: IntentEnvelope, refKeys?: OwnershipRefKeys): OwnershipFact; //# sourceMappingURL=decision.d.ts.map