Elnor Repo Reader

E1_E2_Design_Review_Claude_Opus_4.8.md

Memory Rebuild Docs/Stage_6_Charters/E1_E2_DOC81_Scope_Policy/Reviews/E1_E2_Design_Review_Claude_Opus_4.8.md

Generated 2026-06-09T01:23:58.539Z from commit dbaa25962edc11ab30e8d4ca1715f9ae5bf77331. Worktree: clean.

Open text page · Open raw txt · Open path URL

# DOC81 (E1/E2) Scope & Policy Plane — Consolidated Design Red-Team Review + Corrected-Spec Package

**Reviewer:** Claude Opus 4.8 — independent design red-teamer (substantive; CODEX did the fidelity audit, in this folder).
**Subject:** `…/Stage_6_Charters/E1_E2_DOC81_Scope_Policy/DOC81_Scope_Policy_Charter_Draft.md` (R1, 1,382 lines, §0–§14).
**Repo:** `wbrody/Elnor-Specs@main`, DOC81 blob `8084899`. Verified against E0, the Skeletal/Owner Map/Import Graph, Round D R0.2, PropA R6.3, EC Core Add A V3.3, and the ADQ ledger.
**Commission:** `…/E1_E2_Design_Review_Prompt.md`. This document follows its structure and answers §3.1–§3.10 + §4.1–§4.6.
**Status of this file:** finished review incorporating the post-review architect discussion on privilege/egress (see §1.5 and the B20/B12 dispositions). Review/tracking artifact — not an operative spec edit.

---

## 0. Bottom line & verdict

> **Verdict: `DESIGN_REVISION_NEEDED`** — *scoped, not alarming.* The **architecture, ownership, and E0 binding are A-grade and stand** (best E0-fidelity in the family; every `E0 §x.y` citation resolves at field level and nothing is redefined). What needs a revision pass is the **policy-meet algebra** and the **scope-topology algebra**. The central contract accumulated **nine Blocking-class defects**; two change the *shape* of the meet (**B6** coherence, **B7** monotonicity) and two are silent cross-matter-firewall / non-termination vectors (**B13**, **B14**). None re-architects the family; all fixes are paste-ready (§2).
>
> **Single biggest strength:** E0 binding integrity. **Single biggest weakness:** the `EffectiveMemoryPolicy` meet (§3.2) — correct *shape*, incomplete *algebra* (no ceiling schema; `tighten_to_floor` for 1 of 5 floors; the domain restrictiveness vector never enters; no cross-axis/obligation coherence; not history-aware; not bitemporal).
> **Most important fix:** **B7** (sticky-restrictive effective policy → make the monotonicity law true), then **B6** (disclosure coherence), then **B13** (firewall-safe equivalence).

One focused revision of §2.1–§2.2, §3.2–§3.4, §4.1/§4.4, §5–§6 + a new `PolicyLattice` module, then a **delta re-review of the changed sections only**.

---

## 1. Findings catalog

### 1.1 BLOCKING (9) — not implementable / not safe as written

| ID | Type | §+line | Defect | Impact |
|---|---|---|---|---|
| **B1** | BUG/GAP | §3.4 L445; §6.3 L1053 | `PolicyCeiling` has **no schema** — `original_ceiling_ref` is a dangling `PolicyCeilingRef` (L38 bare `string`). | `ceiling_compliance_attested` (E0 §3.3) unenforceable. |
| **B2** | GAP | §3.2 L467; §2.4 L299 | `tighten_to_floor` defined only for `fail_closed_candidate`; undefined for 4 floors. | Half the meet unimplementable; 3 floors tag-but-don't-restrict. |
| **B3** | GAP | §3.2; E0 §2.2 | 9-axis `DomainProfileRestrictivenessVector` neither owned nor wired into the meet. | `legal.litigation`'s `disclosure: restricted` never reaches the meet; no always-present default ⇒ usability cliff. |
| **B4** | BUG | §3.2 L448 | Meet never asserts scope + decisions share the active `policy_generation_id`. | Stale scope met with fresh decisions → mixed-generation policy. |
| **B6** | BUG | §4.1 L705; §3.2 | Capability/disclosure not orthogonal in the reveal direction; meet emits incoherent pairs (`reference_only` + `not_disclosable`). | A citation that names a doc you may not acknowledge = leak. |
| **B7** | BUG | §3.2 L482; §6.3 L1053 | Stateless `MIN` meet widens on input removal; L1 ("adding any input can only lower") is false for the full op set. | Revoked/expired inputs silently loosen policy with no restamp; contradicts L4. |
| **B12** | BUG (reframed) | §3.1 L370; §3.2 step 4 | Render actions destination-blind; egress gate covers only `{export,delegate,carryover}` though E0 §22 makes `render_to_model→cloud_api` egress. | See §1.5: kept for local-model support + class distinction, **not** per-query confirms. |
| **B13** | BUG | §2.1 L139 | `ScopeEquivalenceBinding` collapse is transitive with no confidence composition and no firewall guard. | Weak chain (A≡B .85, B≡C .85) merges A≡C at no aggregate confidence and can cross a `firewalled` boundary → silent cross-matter breach. |
| **B14** | BUG | §2.2 L169; §8 L1197 | `ScopeContainerRelation` + §8 traversal have no acyclicity invariant, no depth bound. | `ScopeBoundary` derivation / relation traversal can cycle / not terminate. |

### 1.2 SUBSTANTIVE (18) — fold all

| ID | Type | §+line | Defect | Fix → |
|---|---|---|---|---|
| **B5** | GAP | §5.1 L933 | 5-plane cascade: no ordering / idempotency / resumability. | §2.8 |
| **B8** | GAP | §2.1 L147; §4.4 | `scope_confidence_floor` + `contamination_ceiling` referenced as `DomainProfileId` but no defining field. | §2.7 |
| **B9** | GAP | §3.2 L470; §3.3 | Obligation `union` accumulates contradictory obligations (prior fix was non-transitive — corrected). | §2.6 |
| **B10** | GAP | §4.4 L810 | `eligibility_ceiling` "derived from the meet" but derivation unspecified. | §2.7 |
| **B11** | INCONSISTENCY | §2.4 L299; §3.1 | `minimum_conservatism_floor` has no order; §2.4 list order contradicts the `content_fidelity` chain. | §2.2 |
| **B15** | GAP | §6.3 | Policy-tightening propagation unstated; in-flight handled but not re-gate of prior learning/UI outputs. | §2.9 |
| **B16** | BUG | §3.2 | Post-meet steps have a required order; coherence must run last. | §2.4 |
| **B17** | GAP | §5; §6.1 L1006 | Cascade↔legal-hold not explicit — revocation-triggered erasure must defer to an active hold. | §2.8 |
| **B19** | GAP | §3.2 L448 | Meet's `applicable` filter has no bitemporal validity predicate; generation-selection not valid-time-aware. | §2.4 |
| **B21** | GAP | §3.4 L600 | Restamp trigger-authority unspecified (ceiling-bounded, but ungated decision). | §2.5 |
| **S1** | GAP | §10; E0 §12.1 | Portability/separation invariant (cross-principal bleed) not landed. | §2.9 |
| **S2** | GAP | §14.3; RD §3.8 | Project↔scope seam (hint-only) not landed (§14.3 stops at §3.7). | §2.10 |
| **S3** | GAP | §6.1; E0 §3.3 | `LegalHoldClearance`/`Ref` unowned/undefined (dangling E0 brand on destruction path). | §2.9 |
| **S4** | GAP | §14.3 | ~9 new DOC81 schemas lack Owner Map rows; §6.1 miscites "Owner Map" for `LegalHoldState`. | §2.12 |
| **S5** | GAP | §14; E0 §6 | No memory-object classification rows; `E0DurableRecord` applied to per-request/derived objects. | §2.12 |
| **S6** | BUG | §4.2 L726 | `disclosure_class` ordinal vs `may_disclose_*` booleans unreconciled. | §2.6 |
| **S7** | INCONSISTENCY | §2–§9 | ~16 own-object primary IDs are plain `string` — violates E0 §8.5. | §2.3 |
| **M4↑** | GAP | §13.5; §3.2 | `relation_to_destination` ↔ `E0OutboundDestinationClass` crosswalk needed for the fail-closed gate. | §2.4 |

### 1.3 MINOR (11) — fold (cheap)

M1 (§11: "all 11 §17.4 lints" — plan has 16; 5 are E10/DOC86's, unrouted → reword + name). M2 (§3.1: `model_class?`/`client_kind?` dropped vs RD §1.4 → restore optional). M3 (§5.1: cascade refs unbranded → E0 brands). M5 (§12; E0 §18/§20: declare golden-scenario phases 2/8/11.5 + complete §20 row). M6 (§3.5 L648: `fallback_if_unanswered:'defer'` no terminal resolution; add request `expires_at`). M7 (§3.6 L682: `superseding_epoch_ref` can fork → EC CAS linear chain). M8 (§9: UI→policy is a command/event, not an import). M9 (§2.1 L148: bind reason-codes to E0 `ReasonCodeRegistry`, L20). M10 (§4.2: count/aggregate disclosure inference → forward-flag, Phase-2). M11 (§3.1: `content_fidelity` merges content-quantity with identity-disclosure → document as content-quantity; identity → disclosure_class).

### 1.4 CONSIDERED AND DECLINED / CONFIRMED

- **§13.2 disclosure-order swap** (`existence_only ⊏ generic_safe_label_only`): sound — keep.
- **§13.1 `ScopeAffinity` union** (`+shared,+unrelated,+uncertain`): correct no-loss reconciliation — keep.
- **B18 disambiguation timeout**: already handled (`fallback_if_unanswered` + `timed_out` + `unanswered_applies_fallback_not_allow`, §3.5). Declined; only M6 refinement remains.
- **ADQ-221 DOC8→DOC85**, **ADQ-316 EC keep/downgrade/block table** (correctly EC's; DOC81 supplies the ceiling), **per-dimension loop is not a collapse** — accepted B6 instead.

### 1.5 Privilege & egress controls — SCOPE BOUNDARY (post-review architect discussion)

The original B20 ("a synthesized output inherits the meet of its sources' privilege") is **WITHDRAWN**. Privilege cannot be composed from source privilege: facts are not privileged even when learned from a privileged communication (the *attorney* cannot be compelled to disclose the communication, but freely uses the facts); the synthesis act can itself create work-product protection regardless of source status; and once a fact/document is disclosed elsewhere it is no longer privileged — a status the system cannot know from provenance. The 100%-fidelity requirement for privilege means **the system must never make a privilege determination with legal effect.** This also matched ELNOR's standing principle: *privilege is an egress concern, not an internal access barrier.*

**What stays in DOC81 (small, mostly already in the findings):**
- The memory-policy axes (`locality`, `disclosure_class`, `mutation_authority`), the meet, scope objects, and stamps — DOC81's owned plane.
- The **egress decision shape** (B12 + M4↑): distinguish destination classes — *controlled-infrastructure* (the user's LLM, firm cloud) vs *external recipient* vs *public* vs *court* — at decision time, and emit a **"confirmation-required" disposition** (same shape as the existing `PolicyDisambiguationRequest`, §3.5). DOC81 decides *that* confirmation is required; it does not run the confirmation. **Reframe B12:** the local-vs-cloud render distinction exists to (i) support a local-model deployment and (ii) feed the egress controls — **not** to confirm every cloud render.
- The **sensitivity/privilege characterization as a consumed input-seam** — referenced exactly like `VisibilityClass` (§2.1, "PropA/DOC82-owned; referenced, not redefined"). DOC81 declares the seam (a memory/source may carry a sensitivity characterization that contributes to the conservatism floor and egress gating) and the policy behavior; it does **not** own the taxonomy.
- **No-save / Incognito rides on the existing `mutation_authority` axis** (=`none` at scope/episode). No new DOC81 surface.

**What is OUT of DOC81 → the security/authorization plane (DOC5, being rebuilt) and memory governance** (per the Flattening Plan §1.2 non-absorption rule — DOC81 must not absorb DOC23/DOC24/DOC20/DOC25 domains):
- The **egress guard** (warn / check / block), the default-on light **third-party warning**, the **Local Only** mode, the **autonomous-send checkpoint**, and the **"remember until tomorrow" standing grant**.
- The **destination classifier** (firm domain + matter participant roster + channel type → classes).
- The **privilege/sensitivity characterization taxonomy + provenance derivation + user override** (incl. the "disclosed-elsewhere → not privileged" override) — Phase-II weight per the architect's roadmap.
- The **confirmation UI** (DOC20/Q).

**Firewall principle (B13/B14):** firewall machinery is **dormant until a screen is explicitly declared** — never computed, never defaulted, never surfaced in the work path. For a solo user it never fires (no boundary is ever `firewalled`); it costs nothing. B13/B14 are pure correctness backstops that only bite when a screen actually exists (the multi-firm/lateral-screen case). No user-facing steps, prompts, or blocks are added for firewalls.


---

## 2. Corrected specification — paste-ready (full coding & implementation)

Drop-in. One new module (`PolicyLattice`, §2.1 = DOC81 **§3.0**) that the meet, floors, coherence, and DAMS ceiling all call; then corrected/added schemas section-by-section. Targets the OpenClaw/Node + TS convention already in DOC81.

### 2.1 NEW DOC81 §3.0 — `PolicyLattice` (the single typed algebra) — closes B2/B6/B10/B11

```typescript
// DOC81 §3.0  PolicyLattice — schema_owner = DOC81. Total-order chains, ⊥ first; "most restrictive wins" = MIN.
class PolicyLatticeError extends Error {}

export const CONTENT_FIDELITY  = ['none','safe_label','reference_only','redacted','full'] as const;
export const LOCALITY          = ['blocked','local_only','approved_external'] as const;
export const LEARNING_SCOPE     = ['none','audit_only','same_scope_only','partitioned','global_allowed'] as const;
export const MUTATION_AUTHORITY = ['none','candidate_only','durable_requires_review','durable_allowed'] as const;
export const DISCLOSURE_CLASS   = ['not_disclosable','existence_only','generic_safe_label_only','redacted_summary','full'] as const;

export type ContentFidelity    = typeof CONTENT_FIDELITY[number];
export type Locality           = typeof LOCALITY[number];
export type LearningScope      = typeof LEARNING_SCOPE[number];
export type MutationAuthority  = typeof MUTATION_AUTHORITY[number];
export type DisclosureClass    = typeof DISCLOSURE_CLASS[number];

export class Chain<T extends string> {
  constructor(private readonly order: readonly T[]) {}
  readonly bottom: T = this.order[0];
  readonly top: T = this.order[this.order.length - 1];
  rank(v: T): number {
    const i = this.order.indexOf(v);
    if (i < 0) throw new PolicyLatticeError(`value '${v}' not on chain [${this.order.join(',')}]`); // unknown ⇒ fail-closed
    return i;
  }
  leq(a: T, b: T): boolean { return this.rank(a) <= this.rank(b); }
  meet(a: T, b: T): T { return this.rank(a) <= this.rank(b) ? a : b; }          // MIN = most restrictive
  meetAll(vs: readonly T[]): T { return vs.length ? vs.reduce((x,y)=>this.meet(x,y)) : this.bottom; } // empty ⇒ ⊥
  clampDown(v: T, ceiling: T): T { return this.meet(v, ceiling); }
}
export const LAT = {
  content: new Chain(CONTENT_FIDELITY), locality: new Chain(LOCALITY), learning: new Chain(LEARNING_SCOPE),
  mutation: new Chain(MUTATION_AUTHORITY), disclosure: new Chain(DISCLOSURE_CLASS),
} as const;

export interface PolicyPoint {
  content_fidelity: ContentFidelity; locality: Locality; learning_scope: LearningScope;
  mutation_authority: MutationAuthority; disclosure_class: DisclosureClass;
}
export const BOTTOM: PolicyPoint = { content_fidelity:'none', locality:'blocked', learning_scope:'none', mutation_authority:'none', disclosure_class:'not_disclosable' };
export const TOP: PolicyPoint    = { content_fidelity:'full', locality:'approved_external', learning_scope:'global_allowed', mutation_authority:'durable_allowed', disclosure_class:'full' };

export function meetPoints(a: PolicyPoint, b: PolicyPoint): PolicyPoint {
  return {
    content_fidelity: LAT.content.meet(a.content_fidelity,b.content_fidelity),
    locality: LAT.locality.meet(a.locality,b.locality),
    learning_scope: LAT.learning.meet(a.learning_scope,b.learning_scope),
    mutation_authority: LAT.mutation.meet(a.mutation_authority,b.mutation_authority),
    disclosure_class: LAT.disclosure.meet(a.disclosure_class,b.disclosure_class),
  };
}
export function meetAllPoints(ps: readonly PolicyPoint[]): PolicyPoint { return ps.length ? ps.reduce(meetPoints) : {...BOTTOM}; }
export function leqPoint(a: PolicyPoint, b: PolicyPoint): boolean {
  return LAT.content.leq(a.content_fidelity,b.content_fidelity) && LAT.locality.leq(a.locality,b.locality)
      && LAT.learning.leq(a.learning_scope,b.learning_scope) && LAT.mutation.leq(a.mutation_authority,b.mutation_authority)
      && LAT.disclosure.leq(a.disclosure_class,b.disclosure_class);
}

// B6: capability that reveals content/identity forces a disclosure floor. Resolve by capping CAPABILITY down to the
// disclosure ceiling — NEVER by raising disclosure (preserves L1). Single-pass, monotone-down, terminating.
export const MAX_CONTENT_FOR_DISCLOSURE: Record<DisclosureClass, ContentFidelity> = {
  not_disclosable:'none', existence_only:'safe_label', generic_safe_label_only:'reference_only',
  redacted_summary:'redacted', full:'full',
};
export function isCoherent(p: PolicyPoint): boolean { return LAT.content.leq(p.content_fidelity, MAX_CONTENT_FOR_DISCLOSURE[p.disclosure_class]); }
export function makeCoherent(p: PolicyPoint): PolicyPoint {
  return { ...p, content_fidelity: LAT.content.clampDown(p.content_fidelity, MAX_CONTENT_FOR_DISCLOSURE[p.disclosure_class]) };
}
```
Single-pass/terminating: `makeCoherent` only lowers `content_fidelity`; lowering capability never raises the disclosure floor. No reverse coupling (capability `none` + disclosure `full` is coherent). Hence B16 only requires coherence to run **after** the disclosure-tightening steps.

> **Note (B20 withdrawn):** an earlier draft of this module included a `composeOutputPolicy(sources)` that met the policies of multiple contributing source objects into one output policy. That is removed — per §1.5, a synthesized output's *privilege* cannot be composed from its sources, and the *confidentiality/egress* posture of an output is an action-gate concern owned by the security plane (DOC5), not a DOC81 memory-policy operation.

### 2.2 §2.4 `ScopeResolutionResult` — floor crosswalk + floor order (B2, B11)

```typescript
// floor total order (B11), ALIGNED to the content_fidelity chain. ⊏ = more restrictive.
// safe_label_candidate is MORE restrictive than reference_only_candidate (matches CONTENT_FIDELITY). Reorder the §2.4 list to match.
export const FLOOR_ORDER = ['fail_closed_candidate','user_disambiguation_candidate','safe_label_candidate','reference_only_candidate','normal_policy_check'] as const;
export type MinimumConservatismFloor = typeof FLOOR_ORDER[number];
const FLOOR_CHAIN = new Chain(FLOOR_ORDER);
export function meetFloors(fs: readonly MinimumConservatismFloor[]): MinimumConservatismFloor {
  return fs.length ? fs.reduce((a,b)=>FLOOR_CHAIN.meet(a,b)) : 'fail_closed_candidate';   // multiple ⇒ MIN
}
// floor → per-axis CEILING (B2). tighten_to_floor(eff,floor) := meetPoints(eff, FLOOR_CEILING[floor]).
export const FLOOR_CEILING: Record<MinimumConservatismFloor, PolicyPoint> = {
  normal_policy_check:           { ...TOP },
  reference_only_candidate:      { content_fidelity:'reference_only', locality:'local_only', learning_scope:'audit_only', mutation_authority:'candidate_only', disclosure_class:'redacted_summary' },
  safe_label_candidate:          { content_fidelity:'safe_label',     locality:'local_only', learning_scope:'none',       mutation_authority:'candidate_only', disclosure_class:'generic_safe_label_only' },
  user_disambiguation_candidate: { content_fidelity:'reference_only', locality:'blocked',    learning_scope:'none',       mutation_authority:'none',           disclosure_class:'existence_only' }, // + MUST raise PolicyDisambiguationRequest (§3.5)
  fail_closed_candidate:         { ...BOTTOM },
};
```
> Intermediate floor *values* are a proposal needing architect sign-off (only `fail_closed→⊥` and `user_disambiguation→raise a request` are strongly implied by the draft). Add to `ScopeResolutionResult`: `evaluated_under_policy_generation_id: PolicyGenerationId` (B4); `effective_time: string` RFC3339 (B19); `relation_to_destination_class?: E0OutboundDestinationClass` (M4↑); `thresholds_ref: DomainProfileId` (B8). Lints: `policy.floor_not_applied_to_all_axes`; `scope.multiple_floors_not_min`.

### 2.3 §3.1 `MemoryPolicyDecision` — branded IDs, restored egress inputs, render destination, valid-time (S7, M2, B12, B19)

```typescript
// brand ALL DOC81-owned primary IDs (E0 §8.5). S7. (16 ids; representative set.)
export type PolicyCeilingRef = string & { readonly __b:'PolicyCeilingRef' };          // was bare string (L38)
export type ScopeEquivalenceClusterRef = string & { readonly __b:'ScopeEquivalenceClusterRef' };
export type LegalHoldClearanceRef = string & { readonly __b:'LegalHoldClearanceRef' };
export type EpisodePolicyEpochId = string & { readonly __b:'EpisodePolicyEpochId' };
export type PolicyStampRestampId = string & { readonly __b:'PolicyStampRestampId' };
// … LegalHoldStateId, ContaminationRiskThresholdRuleId, PolicyCappedDAMSInputId, PolicyUIExportId, etc.  lint: schema.primary_id_not_branded

export type MemoryPolicyAction =
  | 'retrieve' | 'render_inline' | 'render_reference_only' | 'render_safe_label'
  | 'learn' | 'mutate' | 'export' | 'delegate' | 'carryover';

export interface MemoryPolicyDecision extends PolicyPoint {   // carries the 5 axes
  decision_id: string;
  object_ref: MemoryObjectRef;
  action: MemoryPolicyAction;
  policy_generation_id: PolicyGenerationId;
  valid_from: string;  valid_to?: string;          // B19 — bitemporal in-force window (RFC3339-UTC, E0 §8.5)
  destination?: E0OutboundDestinationClass;        // REQUIRED for {export,delegate,carryover}; and {render_*} when locality may leave the machine (B12)
  model_class?: ModelClass;                         // M2 — restored (RD §1.4)
  client_kind?: ClientKind;                         // M2 — restored
  obligations: PolicyObligation[];
  reason_codes: ReasonCodeId[];
}
function toPolicyPoint(d: MemoryPolicyDecision): PolicyPoint {
  const { content_fidelity, locality, learning_scope, mutation_authority, disclosure_class } = d;
  return { content_fidelity, locality, learning_scope, mutation_authority, disclosure_class };
}
function isInForce(d: MemoryPolicyDecision, t: string): boolean {   // B19
  return d.valid_from <= t && (d.valid_to === undefined || t < d.valid_to);
}
```

### 2.4 §3.2 `EffectiveMemoryPolicy` + the corrected meet (B3,B4,B6,B7,B9,B12,B16,B19,M4↑)

Full ordered pipeline; each numbered step cites the finding it closes. **Order is normative** (B16): coherence is last among tightening steps.

```typescript
export interface EffectiveMemoryPolicy extends PolicyPoint {
  effective_policy_ref: EffectiveMemoryPolicyRef; schema_owner: 'DOC81';
  object_ref: MemoryObjectRef; action: MemoryPolicyAction; destination?: E0OutboundDestinationClass;
  policy_generation_id: PolicyGenerationId; effective_time: string;     // B19
  obligations: PolicyObligation[];
  original_ceiling_ref?: PolicyCeilingRef;                              // §3.4 / B1
  contributing_decision_refs: string[]; domain_contribution_ref: string;
  reason_codes: ReasonCodeId[];                                         // populated per tightening step (auditability)
}
type EffectiveResult =
  | { status:'OK'; effective: EffectiveMemoryPolicy }
  | { status:'RE_PLAN_REQUIRED'; reason:'generation_mismatch' };        // B4

export function computeEffectivePolicy(args: {
  object_ref: MemoryObjectRef; action: MemoryPolicyAction; destination?: E0OutboundDestinationClass;
  effectiveTime: string;                              // B19 — the request's as-of time
  activeEpoch: EpisodePolicyEpoch;                    // §3.6 — active policy_generation_id (selected for effectiveTime, B19)
  scopeResolution: ScopeResolutionResult;             // §2.4
  decisions: MemoryPolicyDecision[];                  // §3.1
  domainContribution: PolicyPoint;                    // B3 — DomainProfilePolicyContribution (ALWAYS present)
  priorEffective?: PolicyPoint;                       // B7 — last effective for (object,action) IF no restamp since; else undefined
}): EffectiveResult {
  const g = args.activeEpoch.policy_generation_id;

  // 0. Generation-consistency precondition (B4). Generation itself is selected for effectiveTime (B19).
  if (args.scopeResolution.evaluated_under_policy_generation_id !== g) return { status:'RE_PLAN_REQUIRED', reason:'generation_mismatch' };
  if (!args.decisions.every(d => d.policy_generation_id === g))         return { status:'RE_PLAN_REQUIRED', reason:'generation_mismatch' };

  // 1. Applicable: object/action match, destination wildcard (unset ⇒ all), AND in force at effectiveTime (B19).
  const applicable = args.decisions.filter(d =>
    d.object_ref === args.object_ref && d.action === args.action &&
    (d.destination === undefined || d.destination === args.destination) &&
    isInForce(d, args.effectiveTime));

  // 2. Per-axis meet INCLUDING the always-present domain contribution (B3) ⇒ set never empty ⇒ ⊥ reserved for "no policy".
  let eff: PolicyPoint = meetAllPoints([args.domainContribution, ...applicable.map(toPolicyPoint)]);

  // 3. Scope conservatism floor (B2). Monotone-down.
  eff = meetPoints(eff, FLOOR_CEILING[args.scopeResolution.minimum_conservatism_floor]);

  // 4. Sticky-restrictive (B7): cannot rise above the prior effective absent a restamp ⇒ removal can't widen.
  if (args.priorEffective) eff = meetPoints(eff, args.priorEffective);

  // 5. Coherence (B6) — MUST BE LAST among tightening steps (B16): reads the FINAL disclosure to cap capability.
  eff = makeCoherent(eff);

  // 6. Obligations: union + transitive-closure conflict resolution (B9); reconcile obligation↔axis (B6 facet).
  const obligations = resolveObligations(applicable.flatMap(d => d.obligations));
  eff = reconcileObligationAxis(eff, obligations);   // e.g. hide_existence ⇒ disclosure_class=not_disclosable ⇒ re-cohere

  // 7. Egress destination-typing gate (B12 + M4↑) — covers external render too; fail-closed on untyped destination.
  const EGRESS = new Set<MemoryPolicyAction>(['export','delegate','carryover','render_inline','render_reference_only','render_safe_label']);
  const couldLeave = EGRESS.has(args.action) && (args.action.startsWith('render') ? eff.locality === 'approved_external' : true);
  if (couldLeave && !isTypedDestination(args.destination)) eff = { ...eff, locality:'blocked' };

  return { status:'OK', effective: assemble(eff, obligations, g, args) };
}
```
- Determinism: `MIN` + set-union are commutative/associative/idempotent → order-independent.
- **B12/M4↑:** the egress gate distinguishes destination *classes* and emits a **"confirmation-required" disposition** where policy permits external egress but the security plane (DOC5) must confirm. DOC81 produces the disposition; the security plane runs the confirmation (see §1.5). M4↑: state the `relation_to_destination` ↔ `E0OutboundDestinationClass` crosswalk in §13.5.
- **B19:** the active epoch/generation must be the one in force at `effectiveTime`; superseded/future decisions are excluded by `isInForce`.
- Lints: `policy.meet_inputs_cross_generation`, `policy.capability_exceeds_disclosure_ceiling`, `monotonicity.effective_widened_on_input_removal_without_restamp`, `policy.external_render_without_destination`, `policy.conflicting_obligations_unresolved`, `policy.meet_included_out_of_force_decision`.

### 2.5 §3.4 `PolicyCeiling` + restamp authority (B1, B21)

```typescript
// high-water capability captured at the ORIGINATING generation; the cap a restamp may never exceed (E0 §3.3). B1.
export interface PolicyCeiling extends E0DurableRecord {
  ceiling_id: PolicyCeilingRef; schema_owner:'DOC81';
  object_ref: MemoryObjectRef; action: MemoryPolicyAction;       // per-action (L3)
  origin_policy_generation_id: PolicyGenerationId;
  ceiling: PolicyPoint;            // the POLICY GRANT — meet over decisions + DomainProfile BEFORE the scope floor
  captured_from_effective_policy_ref: EffectiveMemoryPolicyRef;
  reason_codes: ReasonCodeId[];
}
// Restamp legality (the only widening path — L4'): ≤ MIN(ceiling, CURRENT scope floor) on every axis.
export function restampIsLegal(newEff: PolicyPoint, ceiling: PolicyPoint, currentScopeFloor: PolicyPoint): boolean {
  return leqPoint(newEff, meetPoints(ceiling, currentScopeFloor));
}
// B21 — restamp TRIGGER-AUTHORITY. Gate the DECISION, not just the mechanism.
export interface RestampAuthority { authorized_by: PrincipalRef; authority_tier: 'agent_autonomous'|'architect_batched'|'human_required'; }
// RULE: a restamp that RAISES disclosure_class OR crosses a `firewalled` boundary REQUIRES authority_tier='human_required'.
// A same-scope restamp that only restores capability up to the ceiling under the CURRENT generation may be agent_autonomous.
// lints: policy.disclosure_raising_restamp_without_human_authority ; policy.cross_firewall_restamp_without_human_authority
```
Capture rule: EC captures a `PolicyCeiling` at the first `PolicyStamp` for `(object,action)`, snapshotting the policy grant (step-2 meet, before the step-3 floor). Immutable; restamp-of-restamp inherits the originating `ceiling_id` (no ratcheting). B7 dependency: EC must durably persist the effective policy per `(object,action)` (derived from the stamp/invalidation history — single source of truth) so step 4 can read `priorEffective`.

### 2.6 §4.1/§4.2 coherence + obligation precedence (corrected) + ordinal/boolean (B6, B9, S6)

```typescript
// obligation conflict resolution (B9). CORRECTED: domination is a TRANSITIVE strict partial order; keep maximal (most-restrictive).
const OBLIGATION_DOMINANCE: ReadonlyArray<readonly [dominated:string, dominator:string]> = [
  ['require_ui_warning','hide_existence'], ['show_generic_existence_only','hide_existence'], ['render_inline','require_redaction'],
];
const DOMINATES_CLOSURE: Record<string, Set<string>> = (() => {        // transitive closure: dominator → all kinds it dominates
  const direct: Record<string, Set<string>> = {};
  for (const [dom, dr] of OBLIGATION_DOMINANCE) (direct[dr] ??= new Set()).add(dom);
  const close = (k: string, seen = new Set<string>()): Set<string> => {
    for (const d of direct[k] ?? []) if (!seen.has(d)) { seen.add(d); close(d, seen); }
    return seen;
  };
  return Object.fromEntries(Object.keys(direct).map(k => [k, close(k)]));
})();
export function resolveObligations(obs: PolicyObligation[]): PolicyObligation[] {
  const kept = dedupeMergeSameKind(obs);                 // same-kind ⇒ strictest parameter (e.g. union redaction spans)
  const present = new Set(kept.map(o => o.kind));
  const removed = new Set<string>();
  for (const o of kept) for (const d of DOMINATES_CLOSURE[o.kind] ?? []) if (present.has(d)) removed.add(d);
  return kept.filter(o => !removed.has(o.kind));          // order-independent, transitive
}
// B6 facet — reconcile obligations WITH the disclosure axis, then re-cohere.
const OBLIGATION_DISCLOSURE_CEILING: Record<string, DisclosureClass> = { hide_existence:'not_disclosable', show_generic_existence_only:'generic_safe_label_only' };
export function reconcileObligationAxis(p: PolicyPoint, obs: PolicyObligation[]): PolicyPoint {
  let disc = p.disclosure_class;
  for (const o of obs) { const c = OBLIGATION_DISCLOSURE_CEILING[o.kind]; if (c) disc = LAT.disclosure.meet(disc, c); }
  return makeCoherent({ ...p, disclosure_class: disc });
}
// S6 — may_disclose_* booleans bounded by the ordinal disclosure_class (ordinal is the CEILING).
const DISCLOSURE_ALLOWS: Record<DisclosureClass, ReadonlyArray<keyof SafeLabelDisclosurePolicy>> = {
  not_disclosable: [], existence_only: ['may_disclose_existence'],
  generic_safe_label_only: ['may_disclose_existence','may_disclose_container_type','may_disclose_topic_label'],
  redacted_summary: ['may_disclose_existence','may_disclose_container_type','may_disclose_topic_label','may_disclose_count','may_disclose_reason_summary'],
  full: ['may_disclose_existence','may_disclose_container_type','may_disclose_topic_label','may_disclose_source_title','may_disclose_count','may_disclose_reason_summary'],
};  // lint: disclosure.granular_flag_exceeds_disclosure_class
```
§4.1 prose: "orthogonal in the block direction; capability is bounded by the disclosure ceiling in the reveal direction, including obligation-induced ceilings."

### 2.7 §4.4 thresholds + eligibility derivation + domain contribution (B8, B10, B3)

```typescript
export interface DomainPolicyThresholds extends E0DurableRecord {            // B8
  thresholds_id: DomainProfileId; schema_owner:'DOC81';
  scope_confidence_floor: number;   // 0..1 — below ⇒ no collapse / floor rises
  contamination_ceiling: number;    // 0..1 — above ⇒ contamination veto fires
  reason_codes: ReasonCodeId[];
}
export const THRESHOLD_DEFAULTS = { scope_confidence_floor: 1.0, contamination_ceiling: 0.0 } as const; // fail-closed
// lint: policy.threshold_referenced_but_undefined

export interface DomainProfilePolicyContribution extends E0DurableRecord {   // B3 — DOC81 owns RestrictivenessLevel semantics + the 9→5 crosswalk
  contribution_id: string; schema_owner:'DOC81';
  domain_profile_id: DomainProfileId; action: MemoryPolicyAction;
  contribution: PolicyPoint;          // per-axis ceiling from the 9-axis restrictiveness vector (E0 §2.2)
  reason_codes: ReasonCodeId[];
}  // an object may carry MULTIPLE profiles ⇒ the meet includes ALL contributions. lints: policy.domain_profile_restrictiveness_not_in_meet ; domain_profile.restrictiveness_level_semantics_undefined

export type EligibilityCeiling = 'inline_allowed'|'reference_only_max'|'search_only_max'|'notice_only_max'|'blocked';
export function eligibilityCeiling(eff: PolicyPoint): EligibilityCeiling {   // B10 — deterministic
  if (eff.locality === 'blocked' || eff.content_fidelity === 'none') return 'blocked';
  if (eff.content_fidelity === 'safe_label') return 'notice_only_max';
  if (eff.content_fidelity === 'reference_only' && LAT.disclosure.leq(eff.disclosure_class,'existence_only')) return 'search_only_max';
  if (eff.content_fidelity === 'reference_only') return 'reference_only_max';
  return 'inline_allowed';
}  // lint: dams.eligibility_ceiling_not_derived_from_effective_policy
```

### 2.8 §5 cascade — ordering, idempotency, monotone-down, hold dominance (B5, B17)

```typescript
export interface CascadeAdditions {                       // add to CascadingSourceInvalidation
  cascade_sequence: ['doc82_support_edges','doc87_memberships','doc84_delivery_artifacts','doc85_learning_signals','doc86_surfaces'];
  idempotency_key: string;                                // EC re-fire is a no-op if applied (E0 §10.5)
  per_plane_completion: Partial<Record<'doc82_support_edges'|'doc87_memberships'|'doc84_delivery_artifacts'|'doc85_learning_signals'|'doc86_surfaces','pending'|'applied'>>;
  // each plane outcome is an idempotent monotone-DOWN STATE transition (set-to-floor), NOT a relative delta ⇒ concurrent/re-fired cascades COMMUTE.
}
```
**B17 — legal-hold dominance:** the cascade's erasure/destruction outcomes defer to `LegalHoldState` (§6.1) — a held object is invalidated/suppressed but never destroyed; the destruction plane is a no-op-with-defer while `held`. Lints: `revocation.cascade_plane_out_of_order`; `revocation.cascade_not_idempotent`; `revocation.cascade_partial_not_resumable`; `revocation.cascade_destroyed_held_object`.

### 2.9 §6 `LegalHoldClearance`, tightening re-gate, portability (S3, B15, S1)

```typescript
export interface LegalHoldClearance extends E0DurableRecord {   // S3 — affirmative authorization to destruct a HELD object (E0 §3.3)
  clearance_id: LegalHoldClearanceRef; schema_owner:'DOC81';
  hold_ref: LegalHoldStateId; object_refs: MemoryObjectRef[];
  authorized_by: PrincipalRef;                                  // human (litigation-grade)
  clearance_basis: 'hold_released' | 'court_order' | 'explicit_authorized_destruction';
  reason_codes: ReasonCodeId[];
}  // make the destructive-job gate STRUCTURAL at EC job-registration. lint: legal_hold.destructive_job_skipped_check (E0 §12.1)
```
**B15 — policy-tightening propagation (bitemporal; §6.3):** a tightening is NOT retroactive claw-back (prior outputs were legitimate under their generation). Every CONSUMPTION re-gates against the CURRENT effective policy — delivery (DOC84), learning replay (DOC85), and UI display (DOC86), not only plan→render. Durable outputs carry their originating `policy_generation_id`; the sticky/ceiling logic applies at consumption time. Lint `policy.stale_generation_output_consumed_without_regate`.
**S1 — portability/separation:** §6.5 — an export/portable bundle MUST preserve scope/principal separation; it MUST NOT carry memory eligible only under a different principal/scope than the export's `destination_scope_ref`/principal. §10 gate = `EC §8 export + DOC81 locality/disclosure meet`; lint `portability.cross_principal_bleed`.

### 2.10 §2.1–§2.2 equivalence transitivity + topology acyclicity + Project seam (B13, B14, S2)

```typescript
export interface ScopeEquivalenceCluster extends E0DurableRecord {   // B13 — litigation-critical
  cluster_id: ScopeEquivalenceClusterRef; schema_owner:'DOC81';
  member_scope_refs: ScopeRef[];                // transitive closure of CONFIRMED bindings
  spanning_binding_refs: ScopeEquivalenceBindingRef[];
  cluster_confidence: number;                   // = MIN over spanning bindings (weakest link — no laundering)
  reason_codes: ReasonCodeId[];
}
export const MAX_SCOPE_TRAVERSAL_DEPTH = 16;     // B14 — ScopeBoundary derivation + §8 relation traversal bound
```
**B13 rules (§2.1):** (1) transitive collapse only within a cluster whose `cluster_confidence ≥ strictest member domain threshold`; `cluster_confidence = MIN(spanning)`. (2) a transitive merge MUST NOT join scopes separated by a `firewalled` boundary — a firewall is crossed only by an `explicit_user_binding` naming both, never by inference. Lints: `scope.transitive_equivalence_below_cluster_threshold`; `scope.transitive_merge_crossed_firewall`; `scope.equivalence_cluster_confidence_not_min`.
**B14 rules (§2.2/§8):** (1) `contains` edges form a DAG; EC rejects a cycle-creating edge. (2) only `{contains,source_membership,topic_membership}` traverse for containment; `{linked_library,analogical_relation}` are affinity, not traversed. (3) `ScopeBoundary` derivation + §8 traversal bounded by `MAX_SCOPE_TRAVERSAL_DEPTH`; exceeding fails closed (`boundary_kind='unknown'` ⇒ floor rises); visited-set cycle detection. Lints: `scope.container_cycle_created`; `scope.traversal_depth_exceeded_not_fail_closed`; `relation.traversal_cycle_detected`.
**S2 — Project↔scope seam (RD §3.8):** a Project may seed scope identity as a hint but may not create truth identity, override boundaries, broaden policy, or count as proof. Lints `scope.project_membership_created_truth_identity`, `scope.project_mode_broadened_policy`, `scope.project_membership_used_as_proof`, `scope.project_overrode_scope_boundary`.

### 2.11 §3.5 / §3.6 refinements (M6, M7)
- `fallback_if_unanswered:'defer'` resolves to a concrete terminal fallback on a hard timeout; add `expires_at` to `PolicyDisambiguationRequest`. Lint `policy.disambiguation_defer_without_hard_timeout`.
- `EpisodePolicyEpoch.superseding_epoch_ref` = a linear chain under EC CAS (no fork). Lint `policy.epoch_supersession_forked`.

### 2.12 Discharge notes (tracking)
- **S4** — add ~9 Owner Map rows (`PolicyCeiling`, `DomainProfilePolicyContribution`, `DomainPolicyThresholds`, `LegalHoldClearance`, `ScopeEquivalenceCluster`, `CollectionModeSuppressionGovernance`, `SourceExclusionFilterRule`, `RelationTraversalScopeCheckPolicy`, `PolicyUIExport`, `PolicyStampRestamp`); correct §6.1's `LegalHoldState` citation to "Skeletal §10.3" only.
- **S5** — classification rows (E0 §6.1) for all ~25 objects; drop `extends E0DurableRecord` from per-request/derived ones (`ScopeResolutionResult`, `ScopeBoundary`, the meet result, `PolicyMembraneDecision`, `PolicyCappedDAMSInput`) or mark `derived-projection=true`. Lint `classification.doc81_object_without_classification_row`.
- **M5** — §12-bis: DOC81 owns golden-scenario step 2 (preflight) + step 8 (re-check), contributes step 11.5 (egress); §20 row = `MemoryCoordinationTrace` obligations (policy/scope refs + `policy_generation_id`), degraded states `policy_disambiguation_pending`/`scope_unresolved→conservative`.
- **M1** — reword "all 11 §17.4 lints" → "the 11 policy/scope §17.4 lints that are DOC81's; the other 5 route to E10/DOC86" + name them.

---

## 3. Better patterns / new ideas

- **NI-1 — Property-based fixtures for the algebraic laws (auto-catch B6/B7/B11/B9-transitivity).** `fixture.property.*` over generated decision sets: meet idempotence/commutativity/associativity; monotone-down under add AND remove (B7); coherence always holds (B6); obligation-resolution idempotence + transitivity (B9); floor-clamp idempotence. Turns L1–L4 from prose into enforced invariants — the antidote to the all-pass-fixture risk (commission §2.H). A property test can't pass while the law is broken — it caught my own B9 bug.
- **NI-2 — Sticky-restrictive effective policy as a bounded meet-semilattice with a privileged reset (CRDT-style) → Phase-2 networking readiness for free.** Monotone-meet state is conflict-free under replication; multi-node sync merges by `meet` with no coordination, and `restamp` is the single consensus point. Serves the "Phase-1 readiness for complex networking" doctrine at zero Phase-1 behavior cost — it's how you type §3.2 (the `PolicyLattice` already is that semilattice).
- **NI-3 — DIFC / declassification framing (Myers/Liskov).** capability+disclosure = an information-flow label lattice; "restamp = the only widening path, ceiling-bounded, re-floored by scope, human-authorized for disclosure-raising" *is* bounded declassification. Studied soundness argument + multi-principal readiness (the S1 separation seam).
- **NI-4 — Cedar/XACML obligations + deny-overrides; OPA deny-by-default — as the named reference model.** The dimensional decision + accumulating obligations + deny-overrides meet *is* Cedar/XACML; naming it supplies battle-tested obligation-combining (corrected B9) + the ordinal/boolean reconciliation (S6).
- **NI-5 — The `PolicyLattice` module (§2.1) is the highest-leverage move** — converts B2/B6/B10/B11 from ad-hoc enum logic into typed lattice calls, removing every "what should X be here?" guess-point. Takes §3.2 from B-with-holes to A+-with-no-guess-points.

## 4. Schema grades (A–D, load-bearing)

| Schema | § | Grade | Note |
|---|---|---|---|
| Scope identity/topology | §2.1–2.2 | **B** | Topology discipline clean; equivalence transitivity (B13) + acyclicity/depth (B14) unspecified — both safety-relevant. |
| `ScopeAffinity` | §2.3 | **A** | Union reconciliation correct (§13.1). |
| `ScopeResolutionResult`/`…Trace`/`Health` | §2.4–2.5 | **A−** | Faithful; durable/derived ambiguity (S5); missing generation stamp (B4) + effective_time (B19). |
| **`EffectiveMemoryPolicy` + meet** | §3.2 | **B** | Strongest target, weakest algebra: B1/B2/B3/B4/B6/B7/B16/B19. Lifts to **A+** with §2.1+§2.4. |
| `PolicyMembraneDecision`/`Obligation` | §3.3 | **A−** | ADQ-221 handled; obligation precedence missing + non-trivial (B9). |
| `PolicyStamp*`/`Restamp` | §3.4 | **B+** | Restamp contract good; ceiling has no schema (B1); trigger-authority ungated (B21). |
| `PolicyDisambiguationRequest` | §3.5 | **A** | Fail-closed defaults exemplary; `defer`/`expires_at` refinement (M6). |
| `EpisodePolicyEpoch` | §3.6 | **A−** | Mid-episode re-gate correct; supersession fork (M7). |
| Capability/disclosure + `SafeLabelDisclosurePolicy` | §4.1–4.2 | **B+** | Orthogonality false in reveal direction (B6); ordinal/boolean unreconciled (S6). |
| Extraction/DAMS/Contamination/TopicRisk | §4.3–4.5 | **A−** | DAMS clamp faithful; eligibility derivation (B10) + threshold home (B8) missing. |
| `CascadingSourceInvalidation` + 5-plane | §5 | **B+** | Outcomes mirror E0 §12.1; ordering/idempotency/hold-dominance (B5/B17). |
| Cross-cutting (`LegalHoldState`, monotonicity, re-gate) | §6 | **B+** | `LegalHoldClearance` unowned (S3); portability missing (S1); tightening re-gate (B15). |
| Topic privacy / PropA fold | §7 | **A** | §7.3 reconciliation is model work. |
| DOC81→DOC86 export / enforcement / sources | §8–§14 | **A−** | §10 invariant-gate table excellent; M4↑ crosswalk; M1 count. |

## 5. The commission's questions, answered

**§3.1 Meet walk** — per-axis `MIN` correct + deterministic; obligations union (now transitive, B9); destination gate blocks untyped egress. Goes wrong at: floor tighten if floor ≠ fail_closed (B2), cross-generation/out-of-force inputs (B4/B19), domain profile never consulted (B3), incoherent pair (B6). Correct after §2.
**§3.2 Restamp + monotonicity** — only widening path but unenforceable (B1), the law is false under removal (B7), trigger ungated (B21). Restamp-of-restamp correctly inherits the originating ceiling.
**§3.3 5-plane cascade** — complete + composes with E0's cascade (no double-coverage); no ordering/idempotency (B5), hold-dominance unstated (B17).
**§3.4 `LegalHoldState`** — binds the *named* destructive jobs; make the gate structural at EC job-registration + define the dangling `LegalHoldClearance` (S3).
**§3.5 `collection_mode` suppression** — watertight; EC seam real. Sound.
**§3.6 PropA fold (§7.3)** — clean for all 8 rows + DSPy + exclusion + suppression; no over-reach.
**§3.7 `AssertionRelationEdge` (§8)** — dense enough for per-edge gating; add cycle guard + depth bound + "unknown relation_kind ⇒ fail-closed" (B14).
**§3.8 DOC81→DOC86 export (§9)** — downward-only, no B1-cycle; clean separation. Sound (UI→policy is a command, M8).
**§3.9 Scope model (§2)** — sound + right-sized; B13/B14/B4/B19 close the seams.
**§3.10 Ratification readiness** — **not yet**: B1, B2, B3, B4, B6, B7, B12, B13, B14 are each an undefined/unsafe seam. With §2 applied: **yes**.
**§4.1** strong fidelity-audited first attempt; A-architecture, B-algebra; not production-grade as written. **§4.2** weakness = incomplete, non-history-aware, non-bitemporal meet; improvement = the typed `PolicyLattice` + sticky semilattice. **§4.3** first runtime bug: B4/B19 (stale/out-of-force inputs) or B2 (a floor that tags but doesn't restrict) or B13 (a transitive merge across a firewall). **§4.4** skeptical authors: "where's `PolicyCeiling` and the floor bounds?" / "can't fixture the cascade or meet without ordering + the generation/time guard" / "five+ invariants had no lint and the §17.4 count is wrong." **§4.5** I'd write §3.2 as a typed lattice + domain contribution + generation/time precondition + coherence from the start, §2 with cluster-confidence + acyclic topology, §5 ordered/idempotent. **§4.6 — Revise**, one focused pass + the `PolicyLattice` module, then a delta re-review.

## 6. The A → A+ gap (ordered)
- **To A:** B1, B2, B3, B4, B6, B7, B12, B13, B14 (the Blocking nine).
- **To A+:** + B5, B8, B9, B10, B11, B15, B16, B17, B19, B21, S1–S7, M1–M11, and adopt NI-1 (property fixtures) + NI-5 (`PolicyLattice`) + the NI-2/NI-3 framing. Highest leverage: NI-5/§2.1 — discharges B2/B6/B10/B11 structurally.

## 7. Ranked top changes (1 = first)
1. **B7** sticky-restrictive (make L1 true). 2. **B6** disclosure coherence (axis + obligation). 3. **B1** `PolicyCeiling` + capture. 4. **B2** floor crosswalk. 5. **B3** own `RestrictivenessLevel` + wire domain into the meet (+ non-empty default). 6. **B13** firewall-safe equivalence. 7. **B14** acyclic topology + depth bound. 8. **B4** generation precondition. 9. **B19** bitemporal in-force filter. 10. **B12** render destination + egress gate (decision-only; controls → DOC5). 11. **B21** restamp trigger-authority. 12. **B5/B17** cascade ordering/idempotency + hold-dominance. 13. **S1/S2/S3** portability, Project↔scope seam, `LegalHoldClearance`. 14. the rest (B8–B11/B15/B16/S4–S7/M1–M11) + NI-1/NI-5.

## 8. Disposition ledger (for architect-batched adjudication)

| ID | Type | Tier | Proposed disposition |
|---|---|---|---|
| B1 | BUG/GAP | Blocking | ACCEPT — `PolicyCeiling` (§2.5). |
| B2 | GAP | Blocking | ACCEPT — floor crosswalk (§2.2); **confirm intermediate values**. |
| B3 | GAP | Blocking | ACCEPT — own semantics + meet step-2 (§2.7). |
| B4 | BUG | Blocking | ACCEPT — generation precondition (§2.4). |
| B6 | BUG | Blocking | ACCEPT — coherence cap + obligation↔axis (§2.1/§2.6). |
| B7 | BUG | Blocking | ACCEPT — sticky-restrictive + L1' (§2.4). |
| B12 | BUG | Blocking | ACCEPT (reframed) — egress decision + class distinction; **controls → DOC5** (§1.5/§2.4). |
| B13 | BUG | Blocking | ACCEPT — equivalence cluster + firewall guard (§2.10). |
| B14 | BUG | Blocking | ACCEPT — acyclicity + depth bound (§2.10). |
| B20 | GAP | — | **WITHDRAWN** — privilege not composable from sources (§1.5); kernel → DOC5 + sensitivity-input seam (referenced, not owned). |
| B5 | GAP | Substantive | ACCEPT — cascade ordering/idempotency (§2.8). |
| B8 | GAP | Substantive | ACCEPT — `DomainPolicyThresholds` (§2.7). |
| B9 | GAP | Substantive | ACCEPT — transitive obligation precedence (§2.6). |
| B10 | GAP | Substantive | ACCEPT — eligibility derivation (§2.7). |
| B11 | INCONSISTENCY | Substantive | ACCEPT — floor order + reorder enum (§2.2). |
| B15 | GAP | Substantive | ACCEPT — consumption-time re-gate (§2.9). |
| B16 | BUG | Substantive | ACCEPT — coherence-last ordering (§2.4). |
| B17 | GAP | Substantive | ACCEPT — hold dominates cascade (§2.8). |
| B19 | GAP | Substantive | ACCEPT — bitemporal in-force filter (§2.4). |
| B21 | GAP | Substantive | ACCEPT — restamp trigger-authority (§2.5); **confirm tier mapping**. |
| S1 | GAP | Substantive | ACCEPT — portability invariant (§2.9). |
| S2 | GAP | Substantive | ACCEPT — Project↔scope seam (§2.10). |
| S3 | GAP | Substantive | ACCEPT — `LegalHoldClearance` (§2.9). |
| S4 | GAP | Substantive | ACCEPT — Owner Map rows + citation fix (§2.12). |
| S5 | GAP | Substantive | ACCEPT — classification rows + drop `E0DurableRecord` on derived (§2.12). |
| S6 | BUG | Substantive | ACCEPT — booleans bounded by ordinal (§2.6). |
| S7 | INCONSISTENCY | Substantive | ACCEPT — brand IDs (§2.3). |
| M4↑ | GAP | Substantive | ACCEPT — destination crosswalk in §13.5 (§2.4). |
| M1 | BUG(acc.) | Minor | ACCEPT — reword + route 5 E10 lints. |
| M2 | GAP | Minor | ACCEPT — restore `model_class`/`client_kind`. |
| M3 | SUGGESTION | Minor | ACCEPT — brand cascade refs. |
| M5 | GAP | Minor | ACCEPT — golden-scenario phases + §20 row. |
| M6 | SUGGESTION | Minor | ACCEPT — `defer` terminal + `expires_at`. |
| M7 | SUGGESTION | Minor | ACCEPT — serialize epoch chain. |
| M8 | SUGGESTION | Minor | ACCEPT — UI→policy command note. |
| M9 | SUGGESTION | Minor | ACCEPT — bind to E0 `ReasonCodeRegistry`. |
| M10 | SUGGESTION | Minor | FORWARD-FLAG — count/aggregate inference (Phase-2). |
| M11 | SUGGESTION | Minor | ACCEPT — document content_fidelity as content-quantity. |
| §13.2 / §13.1 / B18 / ADQ-221 / ADQ-316 | CONFIRMED | — | CONFIRM (B18 declined as finding; only M6). |

---

*Finished review. Findings are typed, value-tiered, and traced to §+line; all code is paste-ready against the cited sections. The privilege/egress controls and the privilege-characterization taxonomy are out of DOC81 scope (§1.5) and route to the DOC5 security/authorization rebuild + memory governance.*