/** * Pack conformance — runtime invariants for `PackV0`. * * `Pack.basisCodes` declares the refusal-code taxonomy a Pack's policy may * emit. The compile-time `satisfies PackV0<...>` clause catches structural * drift; this module catches drift that the type system cannot see. * * # Three conformance surfaces, three responsibilities * * The framework offers three Pack-conformance surfaces. They are * deliberately separate; understanding the split matters when wiring * a Pack into a new application. * * | Surface | When it runs | What it does | Blocking? | * |---|---|---|---| * | `assertPackConformance(pack, opts)` | **boot-time, sync** | Structural: required fields present, intents/basisCodes non-empty + unique, default polarity opt-in. | **Yes** — throws `PackConformanceError`. | * | `withBasisAudit(pack)` wrapper | **runtime, every decision** | Telemetry: REFUSE/basis/REWRITE-taint/DEFER-signal drift recorded as `recordSinkFailure(…)`. A taint-elevating REWRITE additionally fails closed to a SECURITY REFUSE (011/T5). | **Only** the taint-elevating REWRITE — all other drift passes through. | * | `runConformance(pack, opts)` from `@adjudicate/conformance` | **CI / boot-time, sync** | Property: probes many synthetic envelopes against the policy and asserts replay-determinism, taint protection, basis-vocabulary purity, guard ordering. | **Returns a report; caller decides.** | * * The first two live in this module. The third is in `@adjudicate/conformance` * because it depends on a deterministic PRNG and a check-set abstraction * that does not belong on the kernel hot path. * * # Pick the right surface * * - **Adopting a Pack in production?** Call `assertPackConformance(pack)` * at startup, wrap with `withBasisAudit(pack)` before passing to the * kernel, and run `runConformance(pack)` in CI. * - **Authoring a Pack?** All three surfaces target your Pack; the unit * tests in your Pack's repo should cover `runConformance` against * representative state fixtures. * - **Adding a kernel-emitted refusal code?** Add it to * `KERNEL_REFUSAL_CODES` below; `withBasisAudit` and `runConformance` * pick it up automatically. * * Kernel-vocabulary refusals (`schema_*`, `taint_*`, `default_deny`, * `kill_switch_active`, `kernel_deadline_exceeded`, * `ledger_replay_suppressed`, `guard_panic`) are exempt from drift — * those codes belong to the framework and live outside the Pack's * taxonomy. */ import type { RecordedAuthoritySnapshot } from "./envelope.js"; import type { PackV0 } from "./pack.js"; export declare class PackConformanceError extends Error { readonly packId: string; readonly violations: ReadonlyArray; constructor(packId: string, violations: ReadonlyArray); } /** * Refusal codes the kernel itself may emit (not domain-specific). These * codes pass through `withBasisAudit` without contributing to drift — * they're the framework's vocabulary, not the Pack's. * * Stays in sync with the `BASIS_CODES.{kill,deadline,kernel,ledger,…}` * categories: every refusal code the kernel produces at adjudication * time (or that `adjudicateAndAudit` overlays on a kernel decision) * appears here. Adding a kernel-emitted code without adding it to this * set is a forensic regression — Packs would incorrectly see drift on * their own audit dashboards. */ export declare const KERNEL_REFUSAL_CODES: ReadonlySet; export interface AssertPackConformanceOptions { /** * When false (default), `policy.default = "EXECUTE"` throws * `PackConformanceError`. Adopters with read-only Packs explicitly opt * in. T4 (#20): the framework should refuse a fail-open default by * default — silent EXECUTE on no-guard-matched is the most direct * authority leak. */ readonly allowDefaultExecute?: boolean; } /** * Boot-time conformance check. Throws `PackConformanceError` if the Pack * violates any contract invariant. Adopters typically call this once at * startup (or `installPack` does it on their behalf). */ export declare function assertPackConformance(pack: PackV0, options?: AssertPackConformanceOptions): void; /** * Wrap every guard in the Pack's PolicyBundle so the wrapper observes * drift events without altering Decisions: * * - REFUSE with `refusal.code` outside `pack.basisCodes ∪ KERNEL` * records `basis_code_drift`. * - Any decision with a basis category:code outside `BASIS_CODES` * records `basis_vocabulary_drift`. * - REWRITE with `rewritten.taint` of higher rank than `envelope.taint` * records `rewrite_taint_regression` AND fails closed to a SECURITY REFUSE * (011/T5) — a friction-decreasing rewrite is blocked, not merely observed. * - DEFER with a `signal` outside `pack.signals` (when declared) * records `defer_signal_drift`. * * All drift surfaces EXCEPT the taint-elevating REWRITE are observe-only: those * Decisions pass through unchanged. The taint-elevating REWRITE is the one * monotonicity-violating case the wrapper now blocks (§C / invariant #7). */ export declare function withBasisAudit(pack: PackV0): PackV0; /** * 033 — RECORD the injected authority snapshot onto a pack, reusing the * `withBasisAudit`/`wrapBundle` discipline: produce a NEW pack object (don't * mutate the input), STAMP the recorded snapshot under a symbol, and stay * NON-BLOCKING (no guard, policy, or Decision is altered — recording is * observe-only telemetry, like the audit wrap). The impure audit shell reads it * back with `readRecordedAuthoritySnapshot` and records it onto each * `AuditRecord` so the decision replays bit-identically (§D-5, invariant #5). * * Idempotent: re-stamping with an EQUAL snapshot (same `snapshotHash`) is a * no-op carry; re-stamping with a DIFFERENT snapshot replaces the tag (the * latest injected snapshot is the one recorded). The tag is non-enumerable, so * it never serializes into a record or perturbs a hash. NO authority guard is * wired here (that is 034) — `pack.policy.authGuards` is untouched. */ export declare function recordAuthoritySnapshotOnPack(pack: PackV0, snapshot: RecordedAuthoritySnapshot): PackV0; /** * 033 — read the RECORDED authority snapshot stamped on a pack by * `recordAuthoritySnapshotOnPack`, or `undefined` when none was injected. * `undefined` is a permanent valid state — packs installed without an * `authoritySnapshot` carry no tag, so their audit records omit the field and * hash byte-identically to their pre-033 value. */ export declare function readRecordedAuthoritySnapshot(pack: object): RecordedAuthoritySnapshot | undefined; //# sourceMappingURL=pack-conformance.d.ts.map