Elnor Repo Reader

E1_E2_R2_Design_Review_Claude_Opus_4.8.md

Memory Rebuild Docs/Stage_6_Charters/E1_E2_DOC81_Scope_Policy/Reviews/E1_E2_R2_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) R2 — Design Red-Team + Delta Review

**Reviewer model:** Claude Opus 4.8 (independent design red-teamer) · **Date:** 2026-06-04
**Target:** `Memory Rebuild Docs/Stage_6_Charters/E1_E2_DOC81_Scope_Policy/DOC81_Scope_Policy_Charter_Draft.md` — R2 (2,310 lines)
**Repo access:** ✅ Confirmed. Read `wbrody/Elnor-Specs@main` (HEAD `e66dcc3`) via the hosted GitHub connector — the full R2 draft, `E1_E2_Adjudication_Card.md` (§1-bis R-1…R-5, F1–F10, U1–U33, D1–D12), `E1_E2_R2_Application_Report.md`, the R2 fidelity-audit record, and the governing plan. This review judges *design*, not fidelity (the CODEX audit owns fidelity).

---

## VERDICT: `MINOR_FIXES_THEN_RATIFY` — but do **not** ratify the current text

**Bottom line (one line):** The architecture, ownership split, and the entire 33-cluster adjudication stand untouched and are ratification-grade — but the just-added §3.2 `meet.v2` pipeline is **not yet correct**: it silently drops the scope-floor / domain-profile / sticky-restrictive **disclosure** ceilings (a leak), `deriveDisclosureClass` launders `generic_reason_only → full` (a second leak), §4.6's fail-closed floor **contradicts** the §4.0 R-1 internal-use ruling, and nothing in the meet implements R-2's autonomous **restore**-up-to-ceiling or consumes the §4.6.4 crosswalk. All fixes are paste-ready and localized to §3.2/§4.2/§4.6 — land the Blocking tier, delta-re-check the meet, then ratify.

**Why this label and not `DESIGN_REVISION_NEEDED`:** every defect below is a wiring/derivation bug in the executable layer, not an architectural one. No contract is deleted, re-owned, or re-shaped; the lattice, the action/dimension split, the stamp/ceiling/cascade/legal-hold machinery, and the E0 bindings are all sound. But "minor" describes the *size* of the edits, not their *severity* — two are disclosure leaks in the heart of the meet, and two defeat binding architect rulings (R-1, R-2). The honest process is the card's own R1 path applied to the *changed* sections: focused fix + a delta re-review of §3.2 (plus the NI-1 disclosure property fixtures) before ratification.

**Convergence note:** I did **not** re-find the adjudicated record. Every Blocking item below lives in **R2's own new machinery** (the lattice module, meet.v2, the floor-effect table, the disclosure vector, the crosswalk, the restamp authority) — exactly where Calibration §0 said to dig, and none of which the three R1 reviews ever saw.

---

# PART A — the R2 material, design-judged (walked cases + attempted breaks)

## §2.2 walked scenarios (the mandatory three) + two more

### Scenario (a) — privileged source + two conflicting decisions + a blocking obligation
**Setup.** `object_ref=memo`, `action=retrieve`, no destination. Decision d1 (`content_fidelity=full, locality=local_only, disclosure_vector` permits reason summary). Decision d2 (`content_fidelity=reference_only, locality=local_only`, more restrictive). Obligation `require_safe_label` (blocking). Source PropA-classified `provisional_source_only` (privilege provisional). Scope floor `reference_only_candidate`.

**Walk (meet.v2, §3.2 L755–804).** Step 0 generation/freshness ok. Step 0b n/a (not egress). Step 1: both applicable. Step 2: `content_fidelity = meet(full, reference_only) = reference_only`; `eff.disclosure_vector = meetDisclosureVectors([d1.vec, d2.vec])`. Step 3: floor `reference_only_candidate` met via `meetPoints` (scalar axes only). Step 4: no prior. Step 5: `require_safe_label` is blocking → must be discharged before `retrieve`; it is **not** in `OBLIGATION_DISCLOSURE_VECTOR_CEILING` (L929–936), so it contributes **no** disclosure-vector tightening. Step 6: scalar derived from the vector, coherence last.

**Verdict: PASS on capability, but two latent problems surface (see B1, S-?).** (i) The floor `reference_only_candidate` carries `max_disclosure_class=generic_safe_label_only` and a `max_disclosure_vector` (L1501/§4.6.1) — **but neither reaches the final disclosure**, because step 3 only meets the floor's *scalar* via `meetPoints` and step 6 overwrites that scalar from a vector the floor never touched (→ **A-B1**). (ii) `require_safe_label` caps *rendering*, not the meet's disclosure vector — defensible (it's discharged downstream), but it means a `full`-disclosure-vector survives the meet on a provisional-privilege object; only the blocking-discharge gate stops the leak. Acceptable, but the floor-loss in (i) is not.

### Scenario (b) — `hide_existence` + a more-open vector (the just-fixed leak case)
**Setup.** Obligation `hide_existence`. Applicable decision with `disclosure_vector` = full-ish (`may_disclose_existence=true, …, max_summary_fidelity=full_reason`), `content_fidelity=full`.

**Walk.** Step 2: `eff.disclosure_vector` = the full-ish vector. Step 5: `obligation_vectors = [bottomDisclosureVector()]` (L930) → `eff.disclosure_vector = meet([full-ish, ⊥]) = ⊥`. Step 6: `deriveDisclosureClass(⊥)` → `not_disclosable` (L1262, `!may_disclose_existence`); `makeCoherent` caps `content_fidelity` to `MAX_CONTENT_FOR_DISCLOSURE['not_disclosable']='none'` (L580/L584).

**Verdict: PASS — the CODEX R2-B1 fix holds for this path.** The obligation tightens the *vector*, the scalar is derived from the *final* vector, coherence runs last; `content_fidelity` is correctly floored to `none`. `fixture.policy.obligation_ceiling_survives_scalar_derivation` (L2185) genuinely covers it. **This is the one disclosure path that is wired correctly** — precisely because the obligation route (step 5) feeds the *vector*. The floor and domain and sticky routes do **not** (A-B1), because they feed only the scalar.

### Scenario (c) — egress action with only destinationless decisions
**Setup.** `action=export`, `destination=cloud_api` (present, typed). Decisions = [d1 destinationless `retrieve`, d2 destinationless `export`].

**Walk.** Step 0b (L763–766): `require ≥1 applicable decision d with d.destination == cloud_api`. d2 is `export` but **destinationless**, so `d2.destination != cloud_api` → requirement fails → blocked. **Correct outcome.**

**But the step order is broken (→ A-S1).** Step 0b at L766 references "≥1 **applicable** decision," yet `applicable` is not built until step 1 (L768–775). As written, 0b must be checking raw `decisions`. Now make d2 a *destination-matching* decision that step 1 later **excludes** (e.g., `d2.destination==cloud_api` but `d2.freshness_key` stale, or context-incompatible): 0b passes on the raw match, step 1 drops d2 into `excluded_decision_refs('stale_generation')`, step 2 meets only the destinationless retrieve decision + domain contribution → a **non-blocked** export policy is assembled on a destinationless basis. **That is an egress leak.** The "destinationless tightens, never authorizes" guarantee (L766, L807-L3) only holds if the dest-match is checked against the **post-filter** `applicable` set. Fix in A-S1.

### Scenario (d, added) — cross-matter INTERNAL retrieve of the user's own material (the R-1 hunt, §2.4)
**Setup.** Securities litigator working in Matter A issues `action=retrieve` (internal, local, to self) for a memo whose `object_scope_ref` is Matter B (both the user's own matters). `boundary_kind=cross_scope`; `active_scope_protection_state=matter_or_project_sensitive`.

**Walk.** §4.6 fail-closed rule **3** (L1475–1478): `boundary_kind ∈ {cross_scope,…} AND either side …matter-specific…` → `minimum_conservatism_floor = reference_only_candidate`. meet.v2 step 3 meets `FLOOR_EFFECT(reference_only_candidate)` → `content_fidelity` capped to `reference_only`. **The litigator cannot see the content of their own Matter-B memo while working in Matter A.**

**Verdict: BLOCKING contradiction with R-1 (→ A-B3).** §4.0 R-1 (L1191–1199) states the **exhaustive** internal-use block list is exactly four bases (firewalled / revoked / sealed-PO-outside-matter / malformed), and that **cross-matter confidentiality is handled as relevance/contamination ranking + the send-time egress gate, NOT an internal block.** A `reference_only` content floor on an internal retrieve **is** an internal block (degradation to a non-content view). §4.6 rule 3 produces exactly the fifth-basis internal restriction R-1 forbids. The §4.0 lint `policy.internal_use_blocked_without_qualifying_basis` does not catch it, because the floor *degrades* rather than hard-*blocks*. This is the "find any path that blocks internal use on a fifth basis — that's a Blocking finding" item from Commission §2.4. The charter is internally contradictory as written (§4.0 vs §4.6 #3).

### Scenario (e, added) — autonomous restore after a transient floor lifts (the R-2 hunt, §2.2)
**Setup.** Generation N: a transient scope condition floored `locality` to `blocked`; `priorEffective.effective_locality=blocked` is recorded on the durable stamp. Generation N+1: the condition clears (scope floor back to `normal_policy_check`). R-2 (L1029–1037, L1075) says restamps **autonomously restore up to ceiling**, including disclosure-raising.

**Walk.** An ordinary re-evaluation runs meet.v2 with `priorEffective` (the gen-N blocked value). Step 4 (L787): `eff = meetPoints(eff, priorEffective)` → `locality` re-clamped to `blocked`. The only escape is `PolicyStampRestamp` (§3.4). But §3.4 (L1073) says a restamp **"re-runs EC's evaluator"** — and the **only** specified evaluator is `meet.v2`, which *includes* the step-4 sticky clamp against the same blocked `priorEffective`. So the restamp's candidate value is *also* clamped to `blocked` → it can only `keep`/`downgrade`, **never restore**.

**Verdict: BLOCKING gap — R-2's autonomous restore is structurally unreachable (→ A-B4).** There is no `mode` distinguishing an ordinary meet (sticky applies) from a restamp re-evaluation (ceiling-bounded, sticky does **not** apply). Without it, "over-tightens irrecoverably" is exactly what happens, and the architect ruling R-2 cannot be implemented. The restamp legality rule (L1071, `new ≤ MIN(root ceiling, current scope floor)`) presumes the candidate can rise to the ceiling — but the candidate is produced by sticky-clamped meet.v2, so it never can. Fix in A-B4.

---

## §2.1 — `PolicyLattice` + rank maps (L518–587)

**CONFIRMED (with shown work):** The algebra is airtight where it is reached. Chains over explicit `AXIS_RANKS` (never enum order); `meet=MIN`; `meetAll([])=⊥` (L548, fail-closed-on-empty); `clampDown=meet(v,ceiling)`; `makeCoherent` is single-pass monotone-down and terminating. `leqPoint`/`meetPoints` are correct per-axis.

**Chain-ordering disputes — none.** I specifically checked the two the commission flagged:
- `learning_scope: none ⊏ audit_only ⊏ same_scope_only ⊏ same_firewall_only ⊏ partitioned ⊏ global_allowed` (L530/L688). **Correct.** `same_scope_only` (one scope) is strictly narrower than `same_firewall_only` (a firewall may group several scopes), and `same_firewall_only` is strictly narrower than `partitioned` (cross-scope but partitioned). The V15-01 body ("firewalled-source signals → same-firewall iteration only," L2001) is exactly `same_firewall_only`. The insertion point is right.
- `disclosure_class: not_disclosable ⊏ existence_only ⊏ generic_safe_label_only ⊏ redacted_summary ⊏ full` (L532). **Correct** (a generic safe label = existence + a category hint > bare existence).

**Unknown-value behavior — one real gap (→ A-S2).** `Chain.rank` **throws** `PolicyLatticeError` on an unknown value (L545); `meet`/`leq`/`meetPoints` call `rank` with no `isMember` guard (L546–569). meet.v2 step 1's F1 guard (L773–774) floors malformed axes **on applicable decisions** — but step 2's `domainContribution(request)` (L779) and step 3's `FLOOR_EFFECT(...)` and step 4's `priorEffective` are fed straight into `meetPoints` with **no** `isMember` guard. A malformed value from the 9→5 domain crosswalk (the one input that is *always present*, B3/U1) therefore **throws uncaught** instead of flooring. There is no stated "any `PolicyLatticeError` fails closed" rule. F1 protects decision axes but not the always-present contribution it just made mandatory. Fix in A-S2.

## §3.2 — meet.v2 (the heart) — BLOCKING: floor/domain/sticky disclosure ceilings are silently dropped

This is the headline finding (**A-B1**), confirmed by grep against the whole file.

**The mechanism.** `PolicyPoint` (L559–562) carries the scalar `disclosure_class` but **no** vector. Therefore every `meetPoints`-based step meets only the *scalar* disclosure:
- **Step 2 (L779):** `eff = meetAllPoints([domainContribution, …applicable])` meets the domain profile's scalar `disclosure_class`. But `DomainProfilePolicyContribution.contribution` is a `PolicyPoint` (L1521) with **no vector**, and step 2's vector line (L781) meets only the *applicable decisions'* vectors. → the **domain profile's disclosure ceiling never reaches the vector.**
- **Step 3 (L784):** `eff = meetPoints(eff, FLOOR_EFFECT(floor))` meets the floor's scalar `disclosure_class`. `ConservatismFloorEffect` *has* a `max_disclosure_vector` (L1501), but `meetPoints` ignores it and **nothing else meets it in.** → the **scope floor's disclosure ceiling never reaches the vector.**
- **Step 4 (L787):** `eff = meetPoints(eff, priorEffective)` meets the scalar. `priorEffective.effective_disclosure_vector` is **not** met in. → **sticky-restrictive does not apply to the disclosure vector** (so L1 monotonicity is *false* on the disclosure axis — a generation change can widen disclosure).
- **Step 6 (L801):** `eff.disclosure_class = deriveDisclosureClass(eff.disclosure_vector)` — **overwrites** the scalar that steps 2–4 carefully tightened, deriving it from a vector that only ever saw (a) applicable decisions' vectors and (b) obligation vectors.

**Net effect:** the scope conservatism floor (the *primary* fail-closed disclosure mechanism, §1.5/§4.6), the domain-profile disclosure restrictiveness, and the sticky-restrictive disclosure history are **all discarded** from the final disclosure decision. Only applicable-decision vectors and obligations survive.

**Break (concrete).** `user_disambiguation_candidate` floor (`max_disclosure_class=existence_only`, L1509). Applicable decisions permit a full disclosure vector. Step 2: `eff.disclosure_vector`=full, `eff.disclosure_class`=full. Step 3: `meetPoints` → scalar `existence_only` ✓ (momentarily). Step 6: `deriveDisclosureClass(full vector)` → `full` ✗ — the floor's `existence_only` is **overwritten back to `full`**, and `makeCoherent` leaves `content_fidelity=full`. **A user-disambiguation floor produces a full-disclosure, full-content effective policy.** This is a leak, and it defeats §2.4's own contract ("Policy … takes the more restrictive of (its own meet, the floor)," L450). Fixed in **A-B1**.

## §3.2 — `deriveDisclosureClass` laundering — BLOCKING (A-B2)

`deriveDisclosureClass` (L1261–1267) has **four** `SummaryFidelity` inputs (`none`/`generic_reason_only`/`redacted_reason`/`full_reason`, L1240) but only handles three; **`generic_reason_only` falls through to `return 'full'`** (L1266). Trace: a vector `{existence:true, reason_summary:true, max_summary_fidelity:'generic_reason_only'}` (a decision that permits a *generic* reason) → not `not_disclosable`, not `existence_only` (reason_summary true), `max_summary_fidelity !== 'none'` and `!== 'redacted_reason'` → **`return 'full'`**. A "generic reason only" posture is laundered into the *most permissive* disclosure class, and coherence then permits `content_fidelity=full`. This violates the charter's own fail-closed rule (§1.5: unknown/unmapped → *most* restrictive). It is reachable whenever the meet's most-restrictive `max_summary_fidelity` lands on `generic_reason_only`. Fixed in **A-B2**.

## §3.3 — obligations / membrane / derivation (L819–976)

- **Dominance closure — CONFIRMED.** Transitive strict-partial-order dominance (L923) correctly keeps the maximal obligation (`hide_existence` dominates `show_generic_existence_only`/`require_ui_warning`); same-kind merge to strictest. Order-independent. Sound.
- **Conflict kinds — exhaustive-by-fallback.** `conflict_kind` (L905–906) ends in `unknown_conflict`; `PolicyObligationConflict.disposition='fail_closed_for_action'`. Good.
- **`OBLIGATION_DISCLOSURE_VECTOR_CEILING` values — CONFIRMED**, with one Minor: it covers only `hide_existence` + `show_generic_existence_only` (L929–936). `require_safe_label` arguably should also cap the vector to ≤ `generic_safe_label_only` rather than relying solely on downstream render discharge (→ A-M4). The `count='none'` for `show_generic_existence_only` (stricter than the reviewer seed) is the right call.
- **Membrane derivation — CONFIRMED (closes §13.4).** `PolicyMembraneDispositionDerivation` (L948–963) + the rule (L962–963) makes the disposition a function of effective policy; `PolicyMembraneDecision.crossing_disposition MUST equal` it (L966). Good. (One Part-B simplification note — the decision and the derivation are two objects that must stay in sync; see B-§3.)

## §3.4 — ceilings / stamps / restamps (L978–1085)

- **Triple-binding — CONFIRMED.** `PolicyStampScopeItem[]` each binding its own `effective_policy_ref` + per-item ceiling (L985–990, L998) genuinely closes GPT B1.
- **Restamp-of-restamp vs root — CONFIRMED (no ratchet).** `RestampChainIntegrity` (L1024–1027) + legality vs the **root** `PolicyCeilingSnapshot` (L1046, L1071) is correct; I tried to build a ratchet (restamp A widens within ceiling → restamp B uses A as its base) and the `root_stamp_ref` pin defeats it. No orphaned chain (every restamp carries `root_stamp_ref`).
- **`block` discriminant — CONFIRMED.** `PolicyStampRestampBlock` has `new_effective_policy_refs?: never` (L1054). Good.
- **R-2 authority — CONFIRMED implementable.** `authority_tier='human_required' IFF firewalled crossing, else agent_autonomous` (L1035–1037); dormant for a solo user. The rule is decidable from the boundary kind. **But its *restore* half is unreachable as written — see A-B4** (the gap is in meet.v2, not in the authority rule).

## §4.0 — R-1 internal-use posture (L1189–1203)

**The 4-item list is NOT exhaustive against the rest of the charter (→ A-B3).** As walked in Scenario (d), **§4.6 fail-closed rule 3** (L1475–1478) raises a use-blocking floor (`reference_only_candidate`/`fail_closed_candidate`) for **internal** actions on `cross_scope`/`matter-specific`/`personal`/`classification_unknown` bases — none of which is one of the four R-1 bases, and `cross-matter`/`privilege` are explicit R-1 *non-blockers*. The §4.0 enforcement lint catches hard *blocks* but not *degradation*, so this fifth-basis restriction passes silently. This is the Blocking finding Commission §2.4 asked for. (Resolution + the one architect judgment call: A-B3.)

## §4.2/§4.5–§4.7 — disclosure vector, DAMS, thresholds, crosswalk, closure

- **`DestinationPolicyCrosswalk` is defined but NEVER consumed (→ A-S3).** Confirmed by grep: the only references are the §0 brand (L76), the §4.6.4 definition (L1549–1566), prose claims that "EC applies … the crosswalk" (L1574) and that it is part of the §6.5 portability gate (L1848), §13.5, and the seed table. **No step of meet.v2 looks up the crosswalk row for `relation_to_destination` and enforces its `policy_floor` / `egress_attestation_required` / `allowed_outbound_destination_classes`.** Step 0b uses `E0OutboundDestinationClass` directly and never consults the crosswalk. So the table that "RESOLVES §13.5" is a schema with no consumer (the plan's `schema_with_no_consumer` smell). Fix in A-S3.
- **`EligibilityCeilingDerivation` (L1375–1386) — CONFIRMED.** Deterministic, disclosure-aware, redacted-product override hook. Good.
- **Action closure (§4.7) — CONFIRMED.** `render_inline → [retrieve, render_inline, ui_disclose]` etc. (L1587–1588) closes the "render with no retrieve-check" hole. Predicate minima as seeds — fine.
- **`THRESHOLD_DEFAULTS = {1.0, 0.0}` (L1535)** — fail-closed, but as the *shipped* default-domain values this makes the system unusable out of the box (no equivalence ever collapses; any contamination vetoes). See seed table (recommend: keep 1.0/0.0 as the *no-profile fallback*; ship usable default-profile values).

## §5–§9

- **Cascade idempotency + ordering + hold dominance — CONFIRMED.** `idempotency_key` + monotone-DOWN-set-to-floor transitions commute (L1646, L1666); freeze-then-5-planes; legal-hold dominance (a held object is invalidated/suppressed, never destroyed, L1666). `LastActiveSupportEdgeEvaluation` denominator proof (L1656–1663) closes the conclusion-boolean gap. Polarity-aware monotonicity (§5.3) is right.
- **Revocation×restamp race — attribution is imprecise (→ A-M1).** L1666 claims the race is "covered by the `epoch_version` CAS (§3.6)." But the `epoch_version` CAS serializes **epoch (generation) transitions**, and a source revocation is a `MemoryMutationEnvelope` that does **not** necessarily open a new `EpisodePolicyEpoch` / bump `policy_generation_id`. The actual serializer for cascade-vs-restamp on the same object is **EC's per-object CAS / write-queue** (E0 §7.1 + the §10.5 idempotency), not the epoch chain. The fixture is fine; the cited mechanism is wrong and will mislead the Stage-7 implementer. Reword (A-M1). (No leak: a restamp restoring `locality` post-revocation is not a policy leak, because the revoked object has no warrant and won't be delivered regardless — policy ⟂ warrant.)
- **Legal-hold default-block registry — CONFIRMED (excellent).** `default_for_unregistered_destructive_job:'block'` (L1751) makes the gate structural at EC registration — a future destructive job cannot skip it by prose omission. Litigation-grade. `LegalHoldClearance` gives the dangling E0 §3.3 ref a schema. This is A-grade.
- **Traversal depth 16 (L310) — CONFIRMED safe** (it's a ceiling; real scope/relation traversals terminate in 2–4 hops; budget-exceeded fails closed + the §6.7 quota envelope caps cost). 16 errs toward not-falsely-truncating, which is correct; it rarely binds. No change.
- **UI export — one real gap (→ A-S4).** `PolicyUIExport` (L2106–2133) carries a **single top-level** `disclosure_class`/`disclosure_vector` (L2120–2121). But R-3 (L1302) requires a **per-action** disclosure split (`ui_disclose` may be specific in-app; `render_*`/`export` stay generic-bucketed in any output). A single top-level disclosure vector **cannot represent that split**, so DOC86 cannot render "specific in-app notice but generic output notice" from the export without recomputing. The per-action `scope_items` (L2112–2119) lack `disclosure_class`/`disclosure_vector`. Fix in A-S4.
- **Internal consistency — two divergences.** (i) `ScopeResolutionResult.active_scope_protection_state` (L377–379: 6 values, **no** `privileged`/`client_confidential`) vs `ScopeProtectionStateDerivation.resolved_protection_state` (L430: 8 values, **with** them) — two protection-state vocabularies on the same object, no derivation linking them (→ A-S5). (ii) `PolicyStampScope` (L1006–1010) now duplicates `PolicyStamp.scope_items` post-U4 (→ B-§3 simplification).

## §10/§11 roll-up vs per-section reality
Mostly consistent. Two Minor mismatches: the §10 fixture name `fixture.policy.retrieval_stamp_cannot_authorize_render_or_export` (L2165) ≠ §3.4's `fixture.policy.retrieval_stamp_is_not_render_stamp` (L1083) (→ A-M2); and `portability.cross_principal_bleed` is `[proposed]` at §6.5 (L1850) but unbracketed in §10/§11 (→ A-M3).

---

# PART B — fresh substantive pass (new bugs, missing contracts, better ideas, A/A+)

## B.1 New bugs (golden-scenario walk, plan §18)
Walking collection → extraction → write → retrieval → delivery → render → export → revocation → legal hold against the full R2 machinery, the jams/leaks are exactly the Part-A Blocking items surfacing at specific phases:
- **Render phase:** the floor/domain/sticky disclosure loss (A-B1) and the `generic_reason_only→full` launder (A-B2) leak here — a rendered item can disclose more than its floor permits.
- **Export phase:** the crosswalk is never consulted (A-S3); the egress precondition can be satisfied by a soon-to-be-excluded decision (A-S1).
- **Mid-episode re-check (step 8):** R-2 restore is unreachable (A-B4) — once a transient condition over-tightens, the item is stuck until a manual change.
- **Otherwise the golden path is clean** — collection suppression (R-4), extraction re-gate (§6.4), cascade (§5), and legal hold (§6.1) all hold up under the walk.

## B.2 Missing contracts (forward — none block Phase-1 ratification)
- **`GAP` (forward): delegated-agent policy envelope.** The DOC11 external-agent adapter has its *inbound* hooks (`delegate` action, `client_kind ∈ {agent_initiated, connector_adapter}` L617, `destination='agent_messaging'` L1563, `principal_ref`, the egress gate + attestation). What has **no home** is the *outbound* analog of `ExtractionRoutePolicyEnvelope` — a `DelegatedAgentPolicyEnvelope` declaring the ceiling under which a delegated agent operates and what it may do with returned material. R-5 correctly defers per-principal *grants* to DOC5/Phase-2, but the *envelope shape* an agent runs under is a DOC81 scope/policy contract that will be needed when DOC11 lands. Forward-flag for the DOC11 charter; the keys are present, the envelope is not.
- **`GAP` (forward): policy-history / as-of audit query.** The substrate exists (`valid_from`/`valid_to` L661–662, `isInForce(d, effectiveTime)`, epoch valid-time windows, freshness keys, `ScopeResolutionTrace`), but there is no "what was the effective policy for object X at time T under generation G" query contract. For a litigator this is a real audit need ("show the policy in force when this was disclosed"). Likely an EC/DOC86 read-model, but DOC81 should name the as-of read contract so it isn't reinvented. Forward-flag.
- **`SUGGESTION` (forward): bulk legacy-matter import posture.** `migration_inferred` equivalence (L220), `require_migration_plan` (V15-02), and the §6.7 backfill quota cover the mechanics, but there is no stated default posture for ingesting a legacy corpus at scale (recommend: everything defaults to matter-scoped + `conservative` profile until reviewed). The fail-closed default covers it implicitly; a one-line posture statement would remove a Stage-7 guess.

## B.3 Better patterns (simplifications — the "mudpit" guard)
- **`BETTER_IDEA` (the A+ move): make the disclosure vector the *single* source of truth — drop `disclosure_class` from `PolicyPoint`.** The entire A-B1 leak class exists because `PolicyPoint` carries a scalar disclosure that travels through `meetPoints` and is then re-derived from a vector that never saw the same inputs. If `PolicyPoint` carried only the **four capability axes** and the disclosure **vector** were the sole disclosure carrier through every step (floor vector, domain vector, prior vector, obligation vector all meet into one vector; scalar derived exactly once at step 6 for coherence), the floor/domain/sticky losses become *structurally impossible*. This is ~1 schema change + removing `disclosure_class` from `BOTTOM`/`TOP`/`leqPoint`/`meetPoints` and is the highest-leverage simplification in the charter. (If too invasive for R2, the minimal A-B1 fix below is the interim.)
- **`SUGGESTION`: collapse `PolicyStampScope` into `PolicyStamp`.** Post-U4, `PolicyStampScope` (L1006–1010) carries the same `scope_items` + freshness + expiry already on `PolicyStamp` (L998). Two objects that must stay in sync invite divergence. Remove `PolicyStampScope`; point Owner Map line 83 at `PolicyStamp.scope_items` (→ A-S? / B simplification).
- **`SUGGESTION`: derive `active_scope_protection_state` from `protection_derivation`.** Two protection enums on one object (A-S5) is redundant *and* divergent; make the scalar `= protection_derivation.resolved_protection_state` and unify the value set.
- **Considered, declined as a simplification:** merging `PolicyMembraneDecision` into `PolicyMembraneDispositionDerivation`. Keep them separate — the derivation's `derivation_inputs` are worth the audit trail; the sync risk is covered by L966 + the `policy.membrane_disposition_not_derived_from_effective_policy` lint.

## B.4 The A/A+ verdict, refreshed
**Grade: A− as written; A once the Blocking tier lands.** The charter is genuinely cutting-edge: a typed meet-semilattice with explicit rank maps, bitemporal in-force windows, per-(action,destination) stamps with root-anchored ceilings, a structural destructive-job/legal-hold registry, a derived membrane disposition, and a CRDT/DIFC/Cedar design-rationale anchor (L628) are all ahead of what a 2027–2028 competitor would ship. What holds it at A− is that **the central algorithm has two disclosure leaks and two architect-ruling gaps** — and a charter whose *job* is to be the fail-closed policy foundation cannot ship with a leaky meet.

**Ordered gap to A+:**
1. Land A-B1 (floor/domain/sticky disclosure→vector) and A-B2 (`deriveDisclosureClass`) — close the two leaks.
2. Resolve A-B3 (§4.0 vs §4.6 internal-use contradiction) and A-B4 (R-2 restore mode) — make the two architect rulings actually implementable.
3. Wire A-S3 (crosswalk) and A-S1 (egress order) — close the egress gaps.
4. **A+:** adopt the B.3 single-source-of-truth disclosure refactor (drop scalar from `PolicyPoint`) — turns "we fixed the leaks" into "leaks of this class are unrepresentable."

**Single highest-value change:** the B.3 disclosure-vector-as-sole-source refactor — it subsumes A-B1 *and* removes the substrate that produced it.

## B.5 Ratification readiness
**Yes, after the Blocking tier.** The §12 cross-charter table is thorough and every downstream member (E3/E4 on `mutation_authority`+`ScopeResolutionResult`; E5/E6 on `ExtractionRoutePolicyEnvelope`+§6.4 re-gate; E7/E8 on `PolicyCappedDAMSInput`+`EffectiveMemoryPolicy`+disambiguation park/resume; E_org on `TopicRiskClass`+non-overlap; E9 on learning monotonicity; E10 on the §9 export) has a defined seam. The **two seams that would otherwise bite a downstream builder** are the ones A-B4 (meet `mode` — E7/E8 delivery + restamp) and A-S3 (crosswalk — E7/E8 egress) leave undefined. Fix those and the text is a foundation E3–E10 can build on without hitting an undefined seam.

---

# VALUE-TIERED FINDINGS

## BLOCKING (gate ratification)

**A-B1 — BUG — meet.v2 silently drops the floor/domain/sticky DISCLOSURE ceilings (§3.2 L779–801; §4.6.1 L1501; §4.6.2 L1521).** `meetPoints` carries only the scalar `disclosure_class`; step 6 re-derives the scalar from a vector that never received the floor's `max_disclosure_vector`, the domain contribution's disclosure, or `priorEffective`'s vector. Result: the scope conservatism floor, domain profile, and sticky-restrictive history are all discarded from the final disclosure → a `user_disambiguation_candidate`/`reference_only_candidate` floor yields `full` disclosure + full content. Also breaks L1 monotonicity on the disclosure axis (generation change can widen disclosure). *Leak in the core meet.*

**A-B2 — BUG — `deriveDisclosureClass` launders `generic_reason_only → full` (§4.2 L1261–1267).** The 4th `SummaryFidelity` value is unhandled and falls through to `return 'full'`, the *least*-restrictive class, violating the charter's own fail-closed rule. *Disclosure + content leak.*

**A-B3 — BUG — §4.6 fail-closed rule 3 contradicts §4.0 R-1 (internal-use is blocked/degraded on a fifth basis) (§4.6 L1475–1478 vs §4.0 L1191–1203).** A `cross_scope`/`matter-specific`/`personal`/`classification_unknown` boundary floors **internal** retrieve to `reference_only`, blocking the user from their own cross-matter content — exactly the fifth-basis internal restriction R-1 forbids (cross-matter is an R-1 explicit non-blocker; the four bases don't include it). The §4.0 lint catches hard-blocks but not floor *degradation*. The charter is internally contradictory. *Violates a binding architect ruling.*

**A-B4 — GAP — R-2's autonomous restore-up-to-ceiling is structurally unreachable (§3.4 L1073/L1075 + §3.2 step 4 L786–787).** A restamp "re-runs EC's evaluator" = meet.v2, whose step-4 sticky clamp re-pins the candidate to the prior (lower) `priorEffective` — so a restamp can only keep/downgrade, never restore. No `mode` distinguishes ordinary meet (sticky) from restamp re-eval (ceiling-bounded, no sticky). *Defeats a binding architect ruling (R-2); "over-tightens irrecoverably."*

## SUBSTANTIVE (fold all)

**A-S1 — BUG — egress precondition is a forward reference and checks the wrong set (§3.2 L763–766 vs L768–775).** Step 0b cites "applicable" before step 1 builds it; checking raw `decisions` lets a stale/context-incompatible destination-matching decision satisfy 0b, get excluded at step 1, and the export proceed on destinationless decisions. *Egress hole.* Fix: split into a cheap structural 0b (destination present+typed) and a post-step-1 "≥1 *applicable* dest-matching decision" check.

**A-S2 — GAP — F1 floor-the-axis does not cover the always-present domain contribution; `meetPoints` throws uncaught on a malformed value (§3.2 L773–787; §3.0 L545).** Extend the `isMember` guard to `domainContribution` (and `priorEffective`), and add a "any `PolicyLatticeError` → fail closed" wrap.

**A-S3 — GAP — `DestinationPolicyCrosswalk` is never consumed by meet.v2 (§4.6.4 L1549–1566; not referenced in §3.2).** The table that "RESOLVES §13.5" has no algorithm step enforcing its `policy_floor`/`egress_attestation_required`/`allowed_outbound_destination_classes`. Add a meet.v2 step that resolves the row for `relation_to_destination` and enforces it.

**A-S4 — GAP — `PolicyUIExport` cannot represent R-3's per-action disclosure split (§9 L2106–2133 vs R-3 L1302).** A single top-level disclosure vector cannot carry "specific in-app `ui_disclose` vs generic output." Move `disclosure_class`/`disclosure_vector` into the per-action `scope_items`.

**A-S5 — BUG — divergent protection-state enums on `ScopeResolutionResult` (L377–379 vs L430), no derivation linking them.** `active_scope_protection_state` lacks `privileged`/`client_confidential`; `resolved_protection_state` has them. Derive the scalar from `protection_derivation`; unify the value set; add the R-1 caveat that `privileged`/`matter_or_project_sensitive` drive egress floors only.

**B-S6 — SUGGESTION — collapse `PolicyStampScope` into `PolicyStamp.scope_items` (§3.4 L1006–1010).** Redundant post-U4; remove to prevent divergence.

## MINOR (fold; cheap)

**A-M1 — BUG (precision) — cascade×restamp race attribution is wrong (§5.1 L1666).** A revocation doesn't open an epoch; the serializer is EC's per-object CAS/write-queue (E0 §7.1 + §10.5), not the `epoch_version` chain. Reword.

**A-M2 — Naming — §10 fixture `…retrieval_stamp_cannot_authorize_render_or_export` (L2165) ≠ §3.4 `…retrieval_stamp_is_not_render_stamp` (L1083).** Align.

**A-M3 — Consistency — `portability.cross_principal_bleed` is `[proposed]` at §6.5 (L1850) but unbracketed at §10/§11.** Make consistent.

**A-M4 — SUGGESTION — review `OBLIGATION_DISCLOSURE_VECTOR_CEILING` completeness (§3.3 L929–936).** Consider adding `require_safe_label → ≤ generic_safe_label_only` so the disclosure cap doesn't rely solely on downstream render discharge.

## CONSIDERED AND DECLINED (with reasoning)

- **`same_firewall_only` chain position** — CONFIRMED correct (shown reasoning §2.1); not a finding.
- **§13.2 `disclosure_class` order** — CONFIRMED.
- **F2 equivalence default (MIN + firewall guard)** — CONFIRMED; full-pairwise rightly declined (it defeats legitimate transitive dedupe; the firewall guard is the real protection). I would not revisit.
- **Restamp-of-restamp / ratchet** — CONFIRMED no-ratchet (root-anchored).
- **R-2 firewalled-only human authority** — CONFIRMED *decidable/implementable* (the restore *reachability* gap is A-B4, separate).
- **5-plane framing (DOC83 freeze pre-fanout; published-views = DOC84-plane row)** — CONFIRMED.
- **`meetCollectionMode([])='suppress'`, legal-hold default-block** — CONFIRMED (correct fail-closed; the latter is A-grade).
- **MAX_SCOPE_TRAVERSAL_DEPTH=16** — CONFIRMED safe (a ceiling that rarely binds; quota envelope caps cost).
- **Re-arguing any D1–D12 decline, F1–F10 fork, or R-1…R-5 ruling** — not done; the only ruling-adjacent items (A-B3, A-B4) are *implementation gaps that defeat* R-1/R-2, not disagreements with them.


---

# PASTE-READY FIXES (anchored `§ + line`)

### FIX A-B1 — make the disclosure vector receive the floor / domain / prior ceilings
**Where:** §3.0 (add helper, after L584) + §3.2 meet.v2 (steps 2–4, L779–787).

Add to §3.0 (`PolicyLattice` module), after `makeCoherent` (L584):
```typescript
// A-B1 — project a scalar disclosure_class ceiling into a vector bound (uses DISCLOSURE_ALLOWS, §4.2).
// Lets a PolicyPoint-level disclosure ceiling (floor / domain / prior) reach the disclosure VECTOR,
// which is the source of truth deriveDisclosureClass reads at step 6.
export function disclosureVectorCeilingFor(c: DisclosureClass): DisclosurePermissionVector {
  const a = new Set<string>(DISCLOSURE_ALLOWS[c]);           // §4.2 L1272
  return {
    may_disclose_existence:      a.has('may_disclose_existence'),
    may_disclose_container_type: a.has('may_disclose_container_type'),
    may_disclose_topic_label:    a.has('may_disclose_topic_label'),
    may_disclose_source_title:   a.has('may_disclose_source_title'),
    may_disclose_reason_summary: a.has('may_disclose_reason_summary'),
    count_disclosure_mode: c === 'full' ? 'exact' : (c === 'redacted_summary' || c === 'generic_safe_label_only' ? 'bucketed' : 'none'),
    max_summary_fidelity:  c === 'full' ? 'full_reason' : (c === 'redacted_summary' ? 'redacted_reason' : 'none'),
  };
}
```

Edit meet.v2 (§3.2 L778–787) — meet the disclosure vector at steps 2, 3, 4 (additions in **bold-comment**):
```text
  # 2. Per-axis MIN incl. the ALWAYS-PRESENT DomainProfilePolicyContribution …
  eff = meetAllPoints([domainContribution(request), ...applicable.map(toPolicyPoint)])
  for axis where forceBottom[axis]: eff[axis] = LAT[axis].bottom
  eff.disclosure_vector = meetDisclosureVectors([
      disclosureVectorCeilingFor(domainContribution(request).disclosure_class),   # A-B1: domain disclosure → vector
      ...applicable.map(d => d.disclosure_vector)])

  # 3. Scope conservatism floor (B2/U2): meet the floor's per-axis effect AND its disclosure vector.
  floorEff = FLOOR_EFFECT(scopeResolution.minimum_conservatism_floor)              # §4.6 ConservatismFloorEffect
  eff = meetPoints(eff, floorEff)
  eff.disclosure_vector = meetDisclosureVectors([eff.disclosure_vector, floorEff.max_disclosure_vector])  # A-B1

  # 4. Sticky-restrictive (B7/U1): clamp capability AND disclosure vector to priorEffective (ordinary mode; see A-B4).
  if mode == 'ordinary' and priorEffective:
     eff = meetPoints(eff, priorEffective)
     eff.disclosure_vector = meetDisclosureVectors([eff.disclosure_vector, priorEffective.effective_disclosure_vector])  # A-B1
```
**Add lint:** `policy.floor_or_domain_or_prior_disclosure_ceiling_not_applied_to_vector`.
**Add fixtures (NI-1 family, §11):** `fixture.policy.scope_floor_disclosure_ceiling_survives_scalar_derivation`; `fixture.property.disclosure_monotone_down_under_floor_domain_and_prior` (the existing `fixture.property.monotone_down_under_add_and_remove`, L2167, MUST be extended to assert the **disclosure** axis, which today it does not exercise).
> *(Best long-term fix: B.3 — drop `disclosure_class` from `PolicyPoint` so the scalar can never travel separately from the vector. The above is the minimal interim.)*

### FIX A-B2 — `deriveDisclosureClass` must never fall through to `full`
**Where:** §4.2 L1261–1267 (replace the function body).
```typescript
function deriveDisclosureClass(v: DisclosurePermissionVector): DisclosureClass {   // U8 — scalar is the vector's summary
  if (!v.may_disclose_existence) return 'not_disclosable';
  if (!v.may_disclose_container_type && !v.may_disclose_topic_label && !v.may_disclose_source_title
      && v.count_disclosure_mode === 'none' && !v.may_disclose_reason_summary) return 'existence_only';
  if (!v.may_disclose_reason_summary || v.max_summary_fidelity === 'none') return 'generic_safe_label_only';
  if (v.max_summary_fidelity === 'full_reason') return 'full';
  return 'redacted_summary';   // 'generic_reason_only' | 'redacted_reason' → redacted_summary; NEVER fall through to 'full' (fail-closed)
}
```
**Add fixture:** `fixture.disclosure.generic_reason_only_does_not_derive_full`; and require `fixture.property.disclosure_derivation_edge_cases` (L2185) to enumerate **all four** `SummaryFidelity` values.

### FIX A-B3 — make the conservatism floor action-aware (honor R-1 for internal actions)
**Where:** §4.6 fail-closed rule 3 (L1475–1478) + §4.0 lint (L1203).
Add to §4.6 before rule 3:
```text
# A-B3 (R-1, §4.0) — action-class gating of the fail-closed floor.
#   INTERNAL actions := {retrieve, classify, render_inline, render_reference_only, render_safe_label,
#                        ui_disclose, inspect}  (delivered to the owning/authorized principal, locally).
#   EGRESS actions   := {export, delegate, carryover} ∪ any render whose `destination` could leave the machine.
# Rule 3's protected/sensitive/matter-specific/personal/cross_scope/classification_unknown floor-raise
#   applies to EGRESS actions ONLY. For INTERNAL actions the floor may rise above normal_policy_check ONLY on
#   one of the four R-1 bases (firewalled; revoked/clawed-back; sealed/PO-outside-its-matter; malformed/F1).
#   Cross-matter relevance for internal actions is handled by DAMS contamination ranking (§4.4), NOT a floor.
```
And extend the §4.0 enforcement lint so degradation (not just hard-block) is caught:
```text
Lints (§4.0): policy.internal_use_blocked_without_qualifying_basis (R-1, hard block)
            + policy.internal_use_floor_raised_without_qualifying_basis (R-1 — an internal action floored to
              reference_only/safe_label/blocked/disambiguation on a basis outside the R-1 four).
Fixture: fixture.policy.cross_matter_internal_retrieve_not_floored (a same-principal cross-matter retrieve
         returns full content; only egress is gated).
```
> **Architect judgment (the one call this fix forces):** this narrows Round D §3.6's unclassified-source fail-closed to **egress** for INTERNAL actions. Per card §1-bis ("where these conflict … THIS section [R-1] governs"), R-1 wins and internal use of the user's own unclassified material is permitted (the unknown classification gates *sharing*). Flagging explicitly because it relaxes a Round D safety rule — confirm the direction. (As written the charter is self-contradictory; *something* must give, and R-1 is the newer, governing ruling.)

### FIX A-B4 — give meet.v2 a restamp-reeval mode so R-2 restore is reachable
**Where:** §3.2 meet.v2 signature (L756) + step 4 (L786–787) + §3.4 restamp prose (L1073).
```text
function meet_v2(request, decisions[], scopeResolution, activeEpoch, priorEffective?, mode='ordinary'):
  ...
  # 4. ORDINARY: sticky-restrictive (clamp to priorEffective).  RESTAMP_REEVAL: NO sticky clamp; the candidate is
  #    bounded only by MIN(root ceiling, current scope floor) per axis, so autonomous restore-up-to-ceiling is reachable (R-2).
  if mode == 'ordinary' and priorEffective:
     eff = meetPoints(eff, priorEffective); eff.disclosure_vector = meetDisclosureVectors([eff.disclosure_vector, priorEffective.effective_disclosure_vector])
  if mode == 'restamp_reeval':
     ceil = rootCeilingItems(request)                       # §3.4 PolicyCeilingSnapshot for (object,action,destination)
     eff = clampPointDown(eff, ceil)                        # per-axis MIN(eff, ceil)  [capability + scalar disclosure]
     eff.disclosure_vector = meetDisclosureVectors([eff.disclosure_vector, ceil.max_disclosure_vector])
  ...
```
And §3.4 (after L1073): "A `PolicyStampRestamp` computes its candidate effective policy via `meet_v2(..., mode='restamp_reeval')` — **never** ordinary mode — so an axis lowered by a now-lifted transient floor can rise back to `MIN(root ceiling, current scope floor)`. The restamp legality check (`new ≤ MIN(root ceiling, current scope floor)`) then validates the candidate."
**Add lint:** `policy.restamp_reeval_applied_sticky_restrictive` (a restamp that re-clamped to the prior lower value ⇒ R-2 restore unreachable).
**Add fixture:** `fixture.policy.autonomous_restamp_restores_up_to_ceiling_after_floor_lift`.

### FIX A-S1 — split the egress precondition (structural at 0b; applicable-set at 1b)
**Where:** §3.2 L763–766.
```text
  # 0b. Egress STRUCTURAL precondition (cheap, before the meet).
  if request.action in {export, delegate, carryover}:
     require request.destination present AND ∈ E0OutboundDestinationClass
        else return blockedBottomPolicy('policy.destination_required_action_without_destination')
  ... step 1 builds `applicable` ...
  # 1b. Egress APPLICABLE precondition (AFTER the applicable filter).
  if request.action in {export, delegate, carryover}:
     if not applicable.any(d => d.destination == request.destination):
        return blockedBottomPolicy('policy.egress_authorized_by_destinationless_decision')
```

### FIX A-S2 — F1 covers the domain contribution; lattice errors fail closed
**Where:** §3.2 step 2 (L778–780) + a top-level wrap.
```text
  for axis in FIVE_AXES:
     if !LAT[axis].isMember(domainContribution(request)[axis]): forceBottom[axis]=true; note('malformed_domain_contribution_axis')
  # wrap meet_v2 body: catch PolicyLatticeError e => return blockedBottomPolicy('policy.lattice_error_failed_closed')
```
**Add lint:** `policy.lattice_error_not_failed_closed`; `policy.malformed_domain_contribution_axis_not_floored`.

### FIX A-S3 — consume the DestinationPolicyCrosswalk in the meet
**Where:** §3.2 (new step 0c, after the structural 0b).
```text
  # 0c. Destination crosswalk (U7/F5; §4.6.4) — the ONLY consumption point for the crosswalk table.
  if request.action in {export, delegate, carryover} OR (request.action startsWith 'render' AND request.destination present):
     cw = CROSSWALK[scopeResolution.relation_to_destination]
     if cw.disposition in {'block'}:                                   return blockedBottomPolicy('egress.relation_blocked')
     if cw.disposition == 'block_until_terminal_destination_resolved': return blockedBottomPolicy('egress.unknown_destination_unresolved')
     if cw.allowed_outbound_destination_classes and request.destination not in cw.allowed_outbound_destination_classes:
        return blockedBottomPolicy('egress.destination_class_not_allowed_for_relation')
     scopeResolution.minimum_conservatism_floor = meetFloors([scopeResolution.minimum_conservatism_floor, cw.policy_floor])
     request.egress_attestation_required = cw.egress_attestation_required
```
**Add lint:** `egress.destination_crosswalk_not_consulted`. **Fixture:** `fixture.egress.crosswalk_floor_and_attestation_enforced_in_meet`.

### FIX A-S4 — per-action disclosure on the UI export (R-3 split)
**Where:** §9 L2112–2121.
```typescript
  scope_items: Array<{
    action: MemoryPolicyAction; destination?: E0OutboundDestinationClass;
    effective_policy_ref: EffectiveMemoryPolicyRef;
    restamp_eligibility: 'eligible' | 'downgrade_only' | 'blocked' | 'requires_disambiguation';
    disclosure_class: DisclosureClass;                 // R-3 (L1302) — PER ACTION (ui_disclose may be more open than export)
    disclosure_vector: DisclosurePermissionVector;     // R-3 — per action; DOC86 renders specific-in-app vs generic-output from this
  }>;
  // top-level disclosure_class/disclosure_vector REMAIN as the most-restrictive summary (or drop).
```
**Add lint:** `ui.policy_export_single_disclosure_cannot_represent_ui_vs_output_split` (R-3).

### FIX A-S5 / B-S6 — reconcile protection-state enums; collapse PolicyStampScope
- §2.4: make `active_scope_protection_state` a derived field `= protection_derivation.resolved_protection_state` (drop the independent scalar) and unify on the 8-value set (L430). Add the comment: "`privileged`/`client_confidential`/`matter_or_project_sensitive` drive **egress** floors only (R-1); never an internal-use block."
- §3.4: delete `interface PolicyStampScope` (L1006–1010); Owner Map line 83 points at `PolicyStamp.scope_items`.

### Minor
- **A-M1:** §5.1 L1666 — replace "covered by the `epoch_version` CAS (§3.6)" with "serialized by EC's per-object compare-and-swap / write-queue (E0 §7.1; §10.5 idempotency); the `epoch_version` CAS orders generation transitions, which a revocation does not necessarily create."
- **A-M2:** rename §10 L2165 fixture to match §3.4 L1083 (or vice versa).
- **A-M3:** bracket `portability.cross_principal_bleed` as `[proposed]` consistently (§10 L2171, §11 L2183) or promote it everywhere.
- **A-M4:** add `require_safe_label → {≤ generic_safe_label_only}` to `OBLIGATION_DISCLOSURE_VECTOR_CEILING` (L929) if the architect wants the disclosure cap independent of downstream render discharge.

---

# §13.6 SEED-VALUE TABLE — per-cell confirm / change (securities-litigation-first)

| seed | loc | seed value | recommendation |
|---|---|---|---|
| `ConservatismFloorEffect` cells — `fail_closed` = ⊥ | §4.6.1 L1510 | BOTTOM all axes | **CONFIRM** (strongly implied). |
| — `user_disambiguation_candidate` {none, blocked, none, none, existence_only} + raise §3.5 | L1509 | as written | **CONFIRM** (coherent; raises disambiguation). |
| — `safe_label_candidate` {safe_label, local_only, none, none, generic_safe_label_only} | L1508 | as written | **CONFIRM** (coherent: content safe_label ≤ MAX_CONTENT[generic_safe_label_only]=reference_only). |
| — `reference_only_candidate` {reference_only, local_only, audit_only, candidate_only, generic_safe_label_only} | L1507 | as written | **CONFIRM** (coherent at the boundary). |
| — `normal_policy_check` = ⊤ | L1506 | TOP | **CONFIRM.** |
| — *(cross-cutting)* | §4.6.1 | — | **CHANGE (A-B3):** these floors must apply to **egress** actions; internal actions get only the R-1-four floor. The cells are right; their *action scope* is the fix. |
| `ActionPermissionPredicate` minima | §4.7 L1597 | GPT §4.10 seed | **CONFIRM** (render_inline/export/learn_global minima look right). |
| protection-state rank `ordinary < … < privileged < sealed < unknown_sensitive` | §2.4 L432 | as written | **CONFIRM the order for egress-floor purposes**; but reconcile with the divergent `active_scope_protection_state` enum (A-S5) and bind the `privileged` rank to **egress** floors only (R-1). |
| `SourceExclusionFilterRule.exclusion_closure` default | §7.2 L2242 | `source_and_derived_artifacts` | **CONFIRM** (right for revocation/exclusion — derived artifacts must follow). |
| `MAX_SCOPE_TRAVERSAL_DEPTH` + budget maxima | §2.2/§8 L310 | `16` | **CONFIRM** (a ceiling; real traversals are 2–4 hops, so it rarely binds; budget-exceeded fails closed + §6.7 quota caps cost). 16 errs toward not-falsely-truncating — correct. |
| `THRESHOLD_DEFAULTS` | §4.6.3 L1535 | `scope_confidence_floor=1.0`, `contamination_ceiling=0.0` | **SPLIT:** **CONFIRM** 1.0/0.0 as the **no-profile fallback** (maximally fail-closed). **CHANGE** the *shipped default-domain-profile* values to usable ones (suggest `scope_confidence_floor≈0.85`, `contamination_ceiling≈0.7`) — at 1.0/0.0 the first-run system never collapses any equivalence and vetoes on any contamination (unusable out of the box). |
| `DestinationPolicyCrosswalk` seed rows | §4.6.4 L1562 | per GPT §4.8 | **CHANGE one cell:** `same_machine_local → local_file_export` floor `reference_only_candidate` → `normal_policy_check`. Keep `egress_attestation_required=true` and `local_only` locality, but a `reference_only` **content** floor stops a litigator saving their own full-content work product to their own disk (over-applied protection that damages workflow). The egress gate + attestation is the right control, not a content floor. (Also wire the table — A-S3.) |
| F2 stricter-alternative (full-pairwise equivalence) | §2.1 L2246 | declined-as-default | **CONFIRM declined** — MIN-over-spanning + the firewall guard is right; full-pairwise defeats legitimate transitive dedupe. |

---

# RANKED TOP CHANGES (highest value first)

1. **A-B1** — stop dropping the floor/domain/sticky **disclosure** ceilings in meet.v2. *(The leak in the heart. One helper + 3 vector-meets.)*
2. **A-B3** — make the conservatism floor action-aware so §4.6 stops contradicting R-1 and blocking internal use. *(Resolves an internal contradiction + a binding-ruling violation; forces one architect confirm.)*
3. **A-B4** — give meet.v2 a `restamp_reeval` mode so R-2's autonomous restore is reachable. *(Makes a binding ruling implementable; removes "over-tightens irrecoverably.")*
4. **A-B2** — fix `deriveDisclosureClass` `generic_reason_only → full`. *(One line; closes the second leak.)*
5. **A-S3 + A-S1** — wire the crosswalk into the meet and fix the egress-precondition order. *(Two egress holes; both paste-ready.)*
6. **A-S2 / A-S4 / A-S5 / B-S6** — fail-closed on lattice errors + domain F1; per-action UI disclosure; reconcile the protection-state enums; collapse `PolicyStampScope`.
7. **B.3 (A+)** — drop `disclosure_class` from `PolicyPoint`; make the disclosure vector the single source of truth. *(Turns "we fixed A-B1" into "A-B1's bug class is unrepresentable." The single highest-leverage simplification — schedule it as the A+ pass if not in this round.)*
8. **Minors** A-M1…A-M4.

**Recommended process:** land 1–6 as a focused meet-pipeline revision (all confined to §3.0/§3.2/§4.0/§4.2/§4.6/§9), run a **delta re-review of §3.2** + the NI-1 disclosure property fixtures (extended per A-B1), confirm the three changed §13.6 seeds (THRESHOLD_DEFAULTS shipped values; the `local_file_export` floor cell; the floor-action-scope), then ratify. The architecture and the 33-cluster adjudication need no further review.

*— End of review. Produced by Claude Opus 4.8 as an independent design red-teamer; Will adjudicates. Every Blocking/Substantive finding is anchored to a `§ + line` in the R2 draft and was verified against the file text (HEAD `e66dcc3`).*

---
---

# DEEP-DIVE ADDENDUM (Round 2) — Claude Opus 4.8

*Second, harder pass after the initial review. New findings below are independent of Round 1 and do not re-tread it. Verdict is unchanged (`MINOR_FIXES_THEN_RATIFY`), but Round 2 adds one **Blocking** item (the domain-contribution crosswalk has no derivation — the meet literally cannot run on a real profile), one strong Substantive (obligations tighten only disclosure, not the capability axes they name), several Substantive correctness/lifecycle gaps, and two value-add ideas. Several are now ground-truthed against ratified E0 (`E0_DOC80_Core/DOC80_Core_Charter_Draft.md`, HEAD `e66dcc3`).*

## NEW BLOCKING

### A2-B5 — BUG/GAP — the always-present `DomainProfilePolicyContribution` has **no specified 9→5 derivation**; `meet.v2` step 2 calls `domainContribution(request)` with nothing to compute it (§4.6.2 L1513–1524; §3.2 L779; E0 §2.2 L203–212)
B3/U1 made the domain contribution **always present** so the input set is never empty and ⊥ is reserved for "no policy." But the contribution is `contribution: PolicyPoint` "per-axis ceiling **derived from** the E0 9-axis restrictiveness vector" (L1521) — and **the derivation is never given.** Ground truth: E0's `DomainProfileRestrictivenessVector` (E0 L203–212) is **9 named axes** — `extraction, rendering, disclosure, export, carryover, delegation, learning, retention, source_authority` — each a `RestrictivenessLevel ∈ {open, normal, restricted, highly_restricted, blocked}` (E0 L201). DOC81's 5 axes have **different cardinalities** (content 5 / locality 3 / learning 6 / mutation 4 / disclosure 5). So the contribution requires (a) a **9→5 axis map** (and it must be **per-action**, since E0 splits `export`/`carryover`/`delegation` while DOC81 has one `locality`), and (b) a **5-level → per-chain-value map**. The charter delivers neither, even though E0 L238 explicitly says *"DOC81 owns the axis vocabulary and per-level semantics."* A Stage-7 implementer cannot build `domainContribution()` — the most-frequently-called input in the meet. This is Blocking: it's a missing load-bearing contract the meet depends on every call, not a value-seed.

**Fix (paste-ready seed — §4.6.2; architect-confirm the constants, not the existence).**
```typescript
// A2-B5 — the 9→5 domain crosswalk (E0 §2.2 → DOC81 §3.0 chains). Per-ACTION axis selection + per-level mapping.
// E0 axes: extraction, rendering, disclosure, export, carryover, delegation, learning, retention, source_authority.
type E0RestrictivenessLevel = 'open' | 'normal' | 'restricted' | 'highly_restricted' | 'blocked';

// Per-DOC81-axis level→chain-value maps (monotone; 'open'=⊤ ... 'blocked'=⊥). SEEDS — architect-confirm (§13.6).
const LVL_CONTENT:  Record<E0RestrictivenessLevel, ContentFidelityLevel>   = { open:'full', normal:'redacted', restricted:'reference_only', highly_restricted:'safe_label', blocked:'none' };
const LVL_LOCALITY: Record<E0RestrictivenessLevel, LocalityLevel>         = { open:'approved_external', normal:'local_only', restricted:'local_only', highly_restricted:'local_only', blocked:'blocked' };
const LVL_LEARNING: Record<E0RestrictivenessLevel, LearningScopeLevel>    = { open:'global_allowed', normal:'partitioned', restricted:'same_firewall_only', highly_restricted:'same_scope_only', blocked:'none' };
const LVL_MUTATION: Record<E0RestrictivenessLevel, MutationAuthorityLevel>= { open:'durable_allowed', normal:'durable_allowed', restricted:'durable_requires_review', highly_restricted:'candidate_only', blocked:'none' };
const LVL_DISCLOSE: Record<E0RestrictivenessLevel, DisclosureClass>       = { open:'full', normal:'redacted_summary', restricted:'generic_safe_label_only', highly_restricted:'existence_only', blocked:'not_disclosable' };

// Per-action: which E0 axis ceilings each DOC81 axis. (source_authority is a DOC82 warrant concern — NOT a DOC81 policy axis.)
function domainContribution(req): PolicyPoint {              // §4.6.2 — derives the always-present contribution
  const v = profileFor(req).restrictiveness_vector;          // E0 §2.2 (referenced)
  const localitySrc = req.action === 'export' ? v.export
                    : req.action === 'delegate' ? v.delegation
                    : req.action === 'carryover' ? v.carryover
                    : 'open';                                 // internal actions: locality not constrained by the egress axes
  const contentSrc  = (req.action === 'collect' || req.action === 'extract') ? v.extraction : v.rendering;
  return {
    content_fidelity:   LVL_CONTENT[contentSrc],
    locality:           LVL_LOCALITY[localitySrc],
    learning_scope:     LVL_LEARNING[v.learning],
    mutation_authority: LVL_MUTATION[v.retention],
    disclosure_class:   LVL_DISCLOSE[v.disclosure],
  };
}
```
**Add lint:** `policy.domain_contribution_derivation_unspecified` (CI guard that the maps exist); `domain_profile.restrictiveness_level_semantics_undefined` (L1568) is the existing placeholder — give it these maps. **Fixture:** `fixture.policy.domain_contribution_maps_nine_axes_to_five_per_action`.
> Note: this also makes the §4.6.2 disclosure contribution *real* — feeding A-B1's `disclosureVectorCeilingFor(domainContribution(req).disclosure_class)`. Without A2-B5, A-B1's domain-disclosure fix has nothing to read.

## NEW SUBSTANTIVE

### A2-S7 — BUG — the meet reconciles obligations into the **disclosure** vector only; **capability-axis** obligations (learning/content/mutation) never tighten their axes (§3.2 step 5 L789–796; §3.3 L879–897; consumers L817)
Step 5 maps obligations → `OBLIGATION_DISCLOSURE_VECTOR_CEILING` → `eff.disclosure_vector` (disclosure only). But `forbid_global_learning`, `partition_learning`, `discard_sealed_learning_signals`, `restrict_firewalled_learning_to_same_firewall` (should cap `learning_scope`), and `require_redaction` / `route_manual_review_only` (should cap `content_fidelity` / `mutation_authority`) are **not** applied to their axes. So a decision can carry `learning_scope='global_allowed'` **and** a blocking `forbid_global_learning` obligation, and the effective policy's `learning_scope` axis says `global_allowed`. DOC85 "gates learning eligibility" on `learning_scope` (L817) — it reads the *axis*. The blocking obligation is supposed to dominate (L968), but **the meet emits a self-contradictory effective policy** (axis says one thing, obligation another) with no stated precedence at the consumption point. The disclosure axis is reconciled precisely because it's derived-and-consumed immediately; the asymmetry for learning (which is litigation/privacy-critical — sealed/firewalled signals) is a real fragility. This parallels CL's `reconcileObligationAxis` (which the card scoped to disclosure only) — extend it to the capability axes.

**Fix (§3.2 — add a step 5b, before step 6).**
```text
  # 5b. Reconcile CAPABILITY-axis obligations into their axes (parallels the disclosure reconciliation).
  for o in obligations:
     if o.kind in {forbid_global_learning}:                     eff.learning_scope = LAT.learning.meet(eff.learning_scope, 'partitioned')
     if o.kind in {partition_learning}:                         eff.learning_scope = LAT.learning.meet(eff.learning_scope, 'partitioned')
     if o.kind in {restrict_firewalled_learning_to_same_firewall}: eff.learning_scope = LAT.learning.meet(eff.learning_scope, 'same_firewall_only')
     if o.kind in {discard_sealed_learning_signals}:            eff.learning_scope = LAT.learning.meet(eff.learning_scope, 'none')
     if o.kind in {route_manual_review_only}:                   eff.mutation_authority = LAT.mutation.meet(eff.mutation_authority, 'durable_requires_review')
     # require_redaction stays a downstream render-discharge (content stays full, redaction map applied) — by design; state it.
  # then step 6 (scalar derive + coherence) as before.
```
**Add lint:** `policy.capability_obligation_not_reconciled_into_axis` (a blocking learning/mutation obligation whose axis exceeds the obligation's ceiling). **Fixture:** `fixture.policy.forbid_global_learning_obligation_caps_learning_scope_axis`. (Alternative, if you prefer obligations stay pure side-constraints: state normatively that consumers MUST honor a blocking capability obligation over the axis, and lint the inconsistency — but the meet-reconciliation is cleaner and keeps the effective policy self-consistent.)

### A2-S8 — GAP — `ScopeEquivalenceCluster` has no `lifecycle_state` and no invalidation trigger; it goes stale when a spanning binding changes (§2.1 L232–241; U28 L2306)
`ScopeContainerRelation` has `lifecycle_state` (L284) and `ScopeEquivalenceBinding` has a prose lifecycle (proposed→confirmed→invalidated, L246), but `ScopeEquivalenceCluster` (L232–241) has **neither a `lifecycle_state` nor a generation/freshness field** — yet U28's durable list explicitly names "equivalence binding/**cluster**" as needing lifecycle (L2306). When a spanning binding is invalidated (un-merge, embedding-model migration), the cluster's `member_scope_refs` (transitive closure) and `cluster_confidence` (MIN over spanning bindings) are **stale**, and nothing invalidates it. A stale cluster could keep two scopes collapsed after the binding that joined them was revoked.
**Fix (§2.1).** Add to `ScopeEquivalenceCluster`: `lifecycle_state: 'active' | 'invalidated' | 'recomputing'` and `equivalence_generation_id: string` (bumped on any spanning-binding change). Invalidate on spanning-binding invalidation. Lint `scope.equivalence_cluster_stale_after_binding_change`; fixture `fixture.scope.cluster_invalidates_on_spanning_binding_revocation`.

### A2-S9 — BUG — cascade TOCTOU: a degraded DOC83 in-flight freeze lets revoked material be **re-derived** into new artifacts mid-cascade, which the 5-plane scan then misses (§5.1 L1647–1653, L1666)
Execution order is "freeze in-flight extraction → the 5 settled planes" (L1647), and the freeze is `plane_statuses.doc83_inflight_extraction?` (optional, L1649). But the spec never says the cascade **halts if the freeze does not complete.** If the freeze is `degraded`/`blocked`, in-flight extraction can finish *using the revoked source* and write new derived artifacts (segments, support edges, syntheses) **after** the affected-set scan already passed — and the idempotent monotone-down cascade won't re-fire on artifacts that didn't exist when it scanned. Result: revoked material survives as freshly-derived artifacts. Classic time-of-check/time-of-use.
**Fix (§5.1).** Normative: the DOC83 freeze MUST reach `completed` (or extraction using the revoked source MUST be blocked) **before** the 5-plane scan begins; a non-completed freeze blocks the cascade or forces a re-scan after the freeze settles. Lint `revocation.cascade_proceeded_without_completed_freeze`; fixture `fixture.revocation.degraded_freeze_blocks_cascade_or_rescans`.

### A2-S10 — BUG — `ScopeSearchCoverageProof` must exclude walled / other-principal scopes from `required_scope_refs`, or a wall leaks via the permanent inability to emit `not_found` (§2.6 L497–505; R-3 L1302)
`may_emit_not_found = (numerator==denominator AND not_searched empty)` (L503). If a `firewalled`/`not_disclosable` scope (whose existence must be hidden, R-3) is in `required_scope_refs`, its results can never be disclosed, so `not_found` can **never** be emitted for the visible scopes — and "I can never tell you it's not there" is itself a signal that *something undisclosable exists*. The coverage proof leaks the wall.
**Fix (§2.6).** `required_scope_refs` MUST contain only scopes the requesting principal/context is authorized to know exist (exclude `firewalled`-relative-to-this-principal and other-principal scopes); walled scopes are not part of the numerator/denominator. Lint `disclosure.coverage_proof_includes_undisclosable_scope`; fixture `fixture.disclosure.walled_scope_excluded_from_coverage_denominator`. (Dormant for a solo user, but encode now — the R-5 keys are already carried.)

### A2-S11 — BUG — `SafeLabelDisclosurePolicy.internal_suppressed_manifest_label_ref` must be **null for `firewalled` material relative to a walled-off principal**, or the Inspector leaks the wall's existence to the very principal it screens (§4.2 L1292; R-3 L1302; Round D §5.6)
GPT M5's `internal_suppressed_manifest_label_ref` is an "internal-only audit/Inspector label." For **non-wall** restricted material owned by the requesting principal, that's correct. But for an **ethical wall** (a conflict screen), R-3 says existence is hidden *entirely* — and the walled-off lawyer must not learn the matter exists even via their own Inspector. As written, the field isn't conditioned on wall-vs-non-wall, so a walled-off principal's Inspector could surface "[suppressed manifest]" — leaking the wall. Litigation-critical for wall integrity.
**Fix (§4.2).** Forbid `internal_suppressed_manifest_label_ref` (and `inspector_label_ref`) when `protected_reason_class='firewalled'` **and** the viewing principal is on the excluded side of the wall. Lint `disclosure.firewalled_material_has_internal_inspector_label`; fixture `fixture.disclosure.walled_off_principal_inspector_shows_nothing`. (Phase-2/multi-principal; encode the contract now.)

### A2-S12 — Refinement of A-B3 — the conservatism floor must carry its **basis** so `meet.v2` can apply it action-aware (§2.4 L389–395; §4.6 L1475)
A-B3 (make the floor honor R-1 for internal actions) needs `meet.v2` step 3 to know *why* the floor was raised. Today `minimum_conservatism_floor` is a bare enum. Add `conservatism_floor_basis: { r1_qualifying: boolean; flags: ScopeProtectionFlag[] }` to `ScopeResolutionResult` (or split into `use_floor` + `egress_floor`). Step 3 then applies the floor's content/locality/disclosure cap to an **internal** action only if `r1_qualifying` (basis ∈ {firewalled, revoked, sealed-PO-outside-matter, malformed}); otherwise internal actions get `normal_policy_check` and the conservative floor applies to **egress** actions. This is the mechanism that makes A-B3 implementable without guessing.

### A2-S13 — Cross-charter GAP — a profile-less scope falls to `conservative_fallback` (maximally restrictive); confirm a usable **`default.ordinary`** profile exists and that scopes get it at creation (E0 §2.2 L194, L232, L238–242)
E0's fallback is correctly fail-closed: missing profile → `conservative_fallback` (most-restrictive on *every* axis, E0 L238). E0 names `legal.litigation` and `default.conservative` (L197) — but there is **no `default.ordinary`.** So a brand-new scope, or any non-legal work scope, with no assigned profile resolves to `conservative_fallback` → effectively blocked out of the box. For a tool Will uses for *both* legal and non-legal work, the default experience is "everything maximally restricted until a profile is assigned." This is the generalized form of the `THRESHOLD_DEFAULTS=1.0/0.0` first-run concern.
**Fix (cross-charter note in §14 / forward to E0/PropA).** Confirm a usable `default.ordinary` profile exists and that scope creation assigns it (legal scopes → `legal.litigation`); reserve `conservative_fallback` for genuine *unknowns*, not *unconfigured ordinaries*. DOC81 records the obligation; E0/PropA own the profile values and assignment.

## NEW MINOR

- **A2-M5 — `egress_attestation_required` is specified in three places** — `DestinationPolicyCrosswalk.egress_attestation_required` (L1558), `MemoryPolicyActionClosureRule.egress_attestation_required` (L1584), `ActionPermissionPredicate.requires_egress_attestation` (L1594). State the authoritative rule (recommend: **required if ANY is true** — fail-safe OR), or consolidate. Lint `policy.egress_attestation_requirement_specified_in_multiple_sources_without_compose_rule`.
- **A2-M6 — bitemporal "in-force" is current-validity only; a true as-of-*past* query is blocked by the step-0 freshness-key generation match** (§3.2 L760–761; §3.6 valid-time). `isInForce(d, effectiveTime)` + epoch `opened_at/closed_at` look like time-travel, but step 0 requires `request.freshness_key.policy_generation_id == activeEpoch.generation` and the freshness key carries *today's* `compiled_policy_evaluator_hash` + registry versions — so a past-epoch evaluation always `RE_PLAN_REQUIRED`s. Either scope the claim ("valid-window filtering of *current* decisions, not historical replay") or add an explicit `as_of` evaluation mode that pins the freshness key to the historical epoch (ties the Round-1 Part-B as-of-audit gap).
- **A2-M7 — `safe_label_vocabulary_version` is optional in `PolicyRuntimeFreshnessKey`** (L598) — make it **required** for any artifact that carries a `SafeReferenceLabelRef`, else a safe-label-bearing artifact won't invalidate on a vocab change (the §6.6 UI-export lint guards only the export, not the general artifact).
- **A2-M8 — `user_instruction_policy_bound: true` is a bare flag with no stated invariant** (§3.0 L619). Specify what it guarantees (e.g., "a user instruction may *narrow* the evaluation context but may never *widen* any effective axis; instructions are inputs to context-keying, never to decision minting"). Otherwise it reads as decorative.

## NEW IDEAS

- **A2-IDEA-1 (BETTER_IDEA) — per-axis binding-constraint attribution on `EffectiveMemoryPolicy`, for a precise Inspector "why."** `reason_codes` are "populated per tightening step" (L747) but there is no per-axis record of *which* input was the binding constraint. For the litigation Inspector ("why is this reference-only?" / "why-not-remembered?"), add optional `binding_constraint: Partial<Record<PolicyAxis, { source_kind: 'decision'|'domain'|'floor'|'obligation'|'prior'|'coherence'; ref?: string; reason_code: ReasonCodeId }>>`. It's the natural feed for DOC86's why-included/why-excluded surface and costs one assignment per axis during the meet. (Value-add, not a bug; ties §9 export + Round D §5.6.)
- **A2-IDEA-2 (strengthens B.3) — the A-B1 fix leaves the step-2/3/4 scalar `disclosure_class` meets as *dead computation* (meet'd, then overwritten by `deriveDisclosureClass` at step 6).** That's a second, independent reason to do the B.3 refactor (drop `disclosure_class` from `PolicyPoint`; capability `PolicyPoint` = 4 axes; the vector is the sole disclosure carrier; derive the scalar exactly once at step 6 for coherence). Post-B.3, `meetPoints`/`leqPoint`/`BOTTOM`/`TOP` shrink, A-B1 becomes structurally impossible, and there is no redundant scalar disclosure path to keep in sync. This remains the single highest-leverage simplification — Round 2 only adds confidence.
- **A2-IDEA-3 (CONFIRMED, considered) — coherence is content-only, and that is correct.** I re-checked whether `locality`/`mutation`/`learning` need a coherence relation to `disclosure` (as content does). They do not: only `content_fidelity` *reveals the object's content*, so only it must be capped to the disclosure ceiling (Round D §1.6). `locality`/`mutation`/`learning` are orthogonal to disclosure. No change — recorded so it isn't re-raised.

## Round-2 grading delta (folds into the Round-1 tiers)
- **Blocking += A2-B5** (domain crosswalk derivation). Now **5 Blocking** (A-B1, A-B2, A-B3, A-B4, A2-B5).
- **Substantive += A2-S7…A2-S13** (obligation-axis reconciliation; cluster lifecycle; cascade TOCTOU; coverage-proof wall exclusion; Inspector wall-leak; floor-basis refinement; default-profile).
- **Minor += A2-M5…A2-M8.** **Ideas += A2-IDEA-1/2/3.**

**Revised ranked top changes (whole review):** (1) A-B1 disclosure-vector loss; (2) **A2-B5 domain 9→5 crosswalk** *(co-equal #1 — the meet can't run without it; do them together)*; (3) A-B3 + A2-S12 floor action-awareness vs R-1; (4) A-B4 restamp restore mode; (5) A2-S7 capability-obligation reconciliation; (6) A-B2 `deriveDisclosureClass`; (7) A-S3 + A-S1 crosswalk-wiring + egress order; (8) the remaining Substantives; (9) **B.3 / A2-IDEA-2** disclosure single-source refactor as the A+ pass.

*— End of Round-2 addendum. Verdict unchanged: `MINOR_FIXES_THEN_RATIFY`, with the meet pipeline (now §3.0/§3.2/§4.0/§4.2/§4.6.2/§4.6 + §2.1/§2.6/§5.1) the focus of the fix-and-delta-recheck. Every item anchored `§+line` and verified against the R2 draft + ratified E0 at HEAD `e66dcc3`.*

---
---

# DEEP-DIVE ADDENDUM (Round 3) — Claude Opus 4.8

*Third pass. Two prior passes already moved the meet pipeline from "looks done" to "5 Blocking, paste-ready." This pass deliberately does two things the additive review process resists: it **retracts** two candidate findings that don't survive the text, and it turns the lens on **my own ~26 findings** as an advocate for the minimal build (the stroke Will flags as the weak one). Net new defects: **0 Blocking, 1 Substantive, 1 Minor** — and that near-null result is itself the signal that the Blocking set has converged. Verdict unchanged.*

## RETRACTIONS (checked hard; they do not hold — recorded so no one re-raises them)

- **RETRACT "the property suite doesn't prove meet order-independence."** It does. The NI-1 family (L2185) ships `fixture.property.meet_idempotent`, `fixture.property.meet_commutative`, `fixture.property.meet_associative`, `fixture.property.obligation_resolution_transitive_and_idempotent`, and `fixture.property.floor_clamp_idempotent`, run over generated decision sets; L753 states the laws explicitly. Order-independence is enforced, not assumed. (See the fix-interaction note below — my own A-B1/A2-S7 fixes preserve these laws.)
- **RETRACT "global cache invalidation → re-plan storm under active memory mutation."** The frequent-change axis is **per-scope**: `ScopePopulationHealth.scope_population_generation_id` is "bumped on any membership/source change that changes resolution" (L473) and invalidates only artifacts keyed to that scope's prior generation (L481; `ScopeResolutionResult.population_generation_inputs[]` L402–403). The **global** freshness-key components are coarse and rare: `EffectiveStateGenerationId` tracks *effective-state toggles* — off-switch / incognito / collection (L594, E0 §1.6), not per-write — and `policy_generation_id` / `compiled_policy_evaluator_hash` / registry versions change on policy edits / deploys / registry edits. A durable write bumps a *scope* population generation (scoped invalidation), not a global one. The cache design is sound for a running system; no storm. (Good news worth stating for the build.)

## NEW SUBSTANTIVE

### A3-S14 — GAP/BUG — the **action enum**, the **exposure-context enum**, and **`relation_to_destination`** are three orthogonal classifiers, and the charter never pins their mapping — so the core **cross-task / attachment-triggered injection** feature can fall on the egress-gated side, and a same-principal `carryover` is a latent contradiction (§3.0 L645/L659/L987; §4.6 L1480/L1551; §2.4 L1556; OBL-PROPA-NEW-01 L1999)
Three independent classifiers touch the same movement:
- **Action** (L645): `… | export | delegate | carryover | …`. `carryover` **requires** an `E0OutboundDestinationClass` destination (L987/L659/L697); a destination not typable to an outbound class is **blocked** (§4.6 fail-closed item 5, L1480).
- **`relation_to_destination`** (L1556) includes the **internal** relations `same_runtime | same_machine_local | same_principal | same_project` — and mapping any of those to an outbound class was **explicitly rejected** (the GK `same_principal→local_file_export` rejection, L1481/L1551).
- **Exposure context** (PropA `ExposureContextSchema`, L1999): `automatic_packet_injection | explicit_memory_attach | outbound_dispatch | cache_warming | digest_generation | …` — and "the exposure context **keys the decision**" (L1999).

Two problems fall out:

**(a) Latent contradiction.** If any path constructs a `carryover` whose `relation_to_destination` is `same_principal`/`same_project`/`same_machine_local` (the user carrying their own context to their own next episode), it **requires** an outbound destination class (L987) that, for an internal relation, **does not exist** (L1551) → untypeable → blocked (L1480). So either same-principal movement *never* uses an egress action, or it is structurally blocked. The charter never says which.

**(b) The feature seam.** ELNOR's core cross-task / cross-episode injection — `automatic_packet_injection`, `explicit_memory_attach` (the attachment-triggered-retrieval feature) — is, per R-1 and Will's egress-only principle, **internal** (same principal, same machine; gated by *relevance/contamination ranking* (DAMS §4.4), never by the matter firewall). The exposure context distinguishes it from `outbound_dispatch` for *decision keying*. **But the exposure context does not drive the egress preconditions — the ACTION does** (destination-required, crosswalk, conservatism floor as an egress action). The charter never maps `automatic_packet_injection`/`explicit_memory_attach` to an **action**. If an implementer reads "inject memory = carry it in = `carryover`" (the natural reading for a memory system), the core feature inherits the egress tax: a required outbound destination it can't have (→ blocked per (a)), plus the egress conservatism floor — **exactly the over-applied-protection-damages-workflow failure mode.** If instead injection is `retrieve` (internal-permissive), it works. The intent is almost certainly the latter, but it is unstated at the one seam where getting it wrong breaks the headline feature.

**Fix (§3.0 + §4.6 — one normative paragraph + a lint; no schema change).**
```text
# A3-S14 — action ↔ exposure-context ↔ relation consistency (R-1 + the egress-only principle).
# Internal same-principal memory movement — cross-task / cross-episode context handoff, incl. the
# `automatic_packet_injection` and `explicit_memory_attach` exposure contexts — is the INTERNAL
# `retrieve` / context-product action (R-1-permissive): NO outbound destination, NO egress conservatism
# floor, gated only by relevance/contamination ranking (DAMS §4.4). It is NEVER the `carryover` action.
# `carryover` | `export` | `delegate` are OUTBOUND-only: they require an E0OutboundDestinationClass and
# pair with the `outbound_dispatch` exposure context. The internal `relation_to_destination` values
# (`same_runtime`/`same_machine_local`/`same_principal`/`same_project`) therefore NEVER appear on a
# `carryover`/`export`/`delegate` decision (their crosswalk rows are `internal_no_egress`, not an outbound
# class — L1551); they appear only to let the crosswalk affirm "internal, no egress gate."
```
**Lints:** `policy.internal_injection_routed_through_egress_carryover` (an `automatic_packet_injection`/`explicit_memory_attach` decision carrying `carryover`/`export`/`delegate` or an outbound destination); `policy.carryover_with_internal_relation_to_destination` (the (a) contradiction — a `carryover` whose relation is same-principal/same-machine). **Fixtures:** `fixture.policy.explicit_memory_attach_is_internal_retrieve_not_carryover`; `fixture.policy.same_principal_context_handoff_not_egress_gated` (the cross-matter-injection case: my Matter-A context injected into my Matter-B episode is permitted internally, ranked by contamination, not blocked by the firewall). This is the §2.4-hunt failure mode (over-gating internal use) surfacing on the *injection* path, complementary to A-B3 (which caught it on the *retrieve/render* path). It also reframes A-S3: the crosswalk's internal-relation rows are the **only** place "internal, no egress gate" is affirmed, so wiring the crosswalk (A-S3) is what makes the internal path explicitly permissive rather than untyped.

## NEW MINOR

- **A3-M9 — an unknown/unmapped `ExposureContextSchema` value must fail closed (§3.1/§3.2; L1999).** The PropA enum is open-ended (`… | digest_generation | …`). DOC81 references it. State that an exposure context DOC81 cannot map fails closed — treated as the most-restrictive exposure (`outbound_dispatch`-equivalent) — never as a permissive default. Lint `policy.unknown_exposure_context_not_failed_closed`. (Parallels the F1 unknown-axis posture; one line.)

## SELF-CRITIQUE — advocate for the minimal build (which of my own findings are over-reach, Phase-2-only, or over-fixable)

Will's flag: the additive process strawmans the minimal architecture, and reviewers over-value privilege/confidentiality to the point of workflow damage. Auditing my own ~26 findings against that:

- **My two wall-protection findings (A2-S10 coverage-proof exclusion, A2-S11 Inspector wall-leak) are the place I over-weighted.** Both depend on **ethical walls**, which §1.6 declares **dormant for the solo user** (Phase-2). They are genuine integrity contracts, but: **encode the one-line invariant now for R-5 readiness; do NOT build the enforcement; do NOT gate ratification on them.** I am down-ranking both from "Substantive (fold now)" to "Phase-2 contract — one-line each, non-gating." (This is me applying Will's own anti-over-protection rule to my own output.)
- **A2-S7 (capability-obligation reconciliation) has a more minimal form.** Rather than runtime step-5b reconciliation in the meet, the minimal fix is an **emission lint**: a `MemoryPolicyDecision` whose capability axis contradicts its own *blocking* capability obligation (e.g., `learning_scope='global_allowed'` + `forbid_global_learning`) is **rejected at emission** (EC computes decisions, so the emitter can be held to consistency). That is lighter than runtime logic and keeps the meet smaller. Runtime reconciliation is more robust against a buggy emitter; the emission lint is sufficient if emitters are trusted. **Recommend the emission lint as the default; add runtime reconciliation only if a non-EC producer (Phase-2 DOC5 grants) can emit decisions.** Either way the *defect* (axis-vs-obligation inconsistency with no precedence) is real and must be closed — but the minimal close is a lint, not pipeline code.
- **A-S5 (protection-state enum reconcile) and B-S6 (collapse `PolicyStampScope`) are pure cleanups, not leaks.** They improve clarity and remove divergence risk, but neither gates ratification on its own. Fold them opportunistically with the Blocking pass; don't let them expand scope.
- **A2-S13 (default-profile) is not a DOC81 fix.** E0/PropA own profile values and assignment; DOC81's only action is to *record the obligation* (a one-line forward note). Don't build profile logic into DOC81.
- **The 5 Blocking all stand; none is over-reach.** A-B1/A-B2 are leaks; A-B3/A-B4 defeat binding rulings (R-1/R-2); A2-B5 is a load-bearing input the meet cannot run without. A2-B5 is the only one that is *design* (a mapping decision) rather than *wiring* — and the paste-ready seed reduces it to "confirm these constants," which is light. I would not soften any of the five.
- **Things I checked for over-engineering and decided are correctly minimal as-is:** the legal-hold default-block registry (structural, not bureaucratic — it's one boolean that prevents a class of bug); the 5-plane cascade (each plane is load-bearing); `MAX_SCOPE_TRAVERSAL_DEPTH=16` (a ceiling, not a cost). No subtraction warranted there.

**Net:** the honest gating set is the **5 Blocking + A3-S14 + the non-wall Substantives (A-S1/A-S2/A-S3/A-S4, A2-S8/A2-S9)**. The wall items (A2-S10/A2-S11) are one-line Phase-2 contracts. A2-S7 minimizes to a lint. The cleanups (A-S5/B-S6) and the cross-charter note (A2-S13) ride along, non-gating.

## FIX-INTERACTION / COMPOSITION (do the fixes compose? what's the delta-recheck gate?)

- **A-B1 depends on A2-B5.** A-B1's domain-disclosure meet reads `domainContribution(req).disclosure_class`; without A2-B5's 9→5 derivation there is nothing to read. **Do them together** (they are co-#1 for this reason).
- **A-B3 depends on A2-S12.** Action-aware flooring needs the floor's *basis* (the `conservatism_floor_basis` field); without it, step 3 can't decide internal-vs-egress application. Land A2-S12 as part of A-B3.
- **A3-S14 interacts with A-B3 and A-S3.** A-B3's "internal actions get only the R-1-four floor" must include the corrected action sets from A3-S14 (internal injection = `retrieve`, not `carryover`); and A-S3's crosswalk wiring is what affirms the internal-relation rows as `internal_no_egress`. These three are one coherent edit to "what counts as egress."
- **All fixes preserve the NI-1 laws.** A-B1's added vector-meets use `meetDisclosureVectors` (AND — commutative/associative/idempotent); A2-S7's reconciliation (if taken at runtime) uses `LAT.meet` (MIN — same); A-B4's `restamp_reeval` clamp is `clampPointDown` (MIN against a fixed ceiling — idempotent). **None threatens `meet_commutative`/`associative`/`idempotent`** — so the NI-1 property family is the correct, sufficient **delta-recheck gate**: re-run NI-1 + the extended disclosure property (the monotone-down fixture extended to the disclosure axis per A-B1) + the new fixtures named above, and the meet is re-validated without a full re-review.
- **No fix conflicts with another.** The only ordering constraint is the three dependencies above; everything else is independent.

## VERDICT — reaffirmed, with a convergence note
`MINOR_FIXES_THEN_RATIFY` stands, unchanged across three passes. **The signal in Round 3 is the near-null new-defect result**: a deliberately hard third pass added **0 Blocking**, retracted 2 candidates, and produced 1 Substantive (a *clarity/consistency* fix at the injection seam, not a leak) + 1 Minor. Across R1→R2→R3 the Blocking set converged at **5** and has stopped growing. That convergence — not the absence of findings, but the *stabilization* of the serious ones — is the evidence that the recommendation is right: **land the 5 Blocking (+ A3-S14 + the non-wall Substantives), re-run the NI-1 property gate on §3.2, confirm the changed §13.6 seeds, then ratify.** The architecture, the ownership split, and the 33-cluster adjudication need no further review; three independent passes have not dislodged them.

*— End of Round-3 addendum. New: A3-S14 (Substantive), A3-M9 (Minor); 2 retractions; a minimal-build self-critique that down-ranks the two wall items to Phase-2 contracts and minimizes A2-S7 to an emission lint; a fix-composition map with the NI-1 delta gate. All anchored `§+line` against the R2 draft + ratified E0 at HEAD `e66dcc3`.*