Elnor Repo Reader

DOC81_Scope_Policy_Charter_Draft.md

Memory Rebuild Docs/Stage_6_Charters/E1_E2_DOC81_Scope_Policy/DOC81_Scope_Policy_Charter_Draft.md

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

Open text page · Open raw txt · Open path URL

# DOC81 — Scope & Policy Plane (Stage 6 E1/E2 Charter Draft R3)

**Repository:** github.com/wbrody/Elnor-Specs — branch `main`
**Document status:** `charter-draft` — **R3.1 — delta-review fixes applied; RATIFICATION-READY (pending architect ratification + discharge).** (R3 applied the `E1_E2_R2_Adjudication_Card.md` package; R3 then passed an independent fidelity audit + two scoped delta design re-reviews [Claude + GPT, both `MINOR_FIXES_THEN_RATIFY`, 4 walked cases PASS, meta-test PASS]; **R3.1** applied their adjudicated fix set per `E1_E2_R3_Delta_Adjudication_Card.md` — D1 render-may-leave egress floor + action partition, D2 `local_file_export` seed, D3 `principal_scope` on both cache keys, D4 pipeline-helper definitions, D5 algebra hygiene, D6 self-contradictory-decision exclusion [F1-aligned], D7 step-order prose. The R1/R2-card record [§1-bis R-1…R-5, F1–F10, V1–V24, D1–D12, D-R1…D-R6] remains binding and untouched. Becomes the operative DOC81 member spec after architect ratification [`Ratification.md`] + discharge sweep.)
**R3 magnitude:** §3.0/§3.2 rebuilt around `PolicyEffectiveState { point, disclosure_vector }` (scalar `disclosure_class` left `PolicyPoint`; derived once at finalize); 24 V-clusters + 17 GPT QF helpers landed as executable functions; value-table seeds (floor cells, predicate minima, crosswalk rows, protection ranks, threshold split, traversal depth) are marked architect-confirm (§13.6).
**Versioning discipline:** git-native — stable filename `DOC81_Scope_Policy_Charter_Draft.md`, no version suffix. Git history is the version record (Skeletal Baseline B5).
**Family member:** DOC81 (member **2 of 8** — Scope & Policy plane). Family = Memory Control Plane (ADQ-210).
**Drafted as:** the **E1 + E2 lockstep pair** (plan §12.2). E1 = declare (scope objects, policy objects, dimensional vector, imports); E2 = validate (policy meet, disclosure meet, fail-closed, stamps/restamp/invalidation, fixtures).
**Governing inputs:** `Charter_Opening_Brief.md` (20 draft targets), `Charter_Input_Deck.md` (OPA rows + ADQs + Skeletal fold-ins), the **ratified E0 `DOC80_Core_Charter_Draft.md`** (the contracts DOC81 binds to), `DOC80_Skeletal_Target_Baseline.md` (§DOC81 + §10/§11), `DOC80_Owner_Map.md` (rows 62, 71–94, 194), `DOC80_Import_Graph.md`, `DOC80_Retired_Names.md`, `Architect_Decision_Queue.md`, `Supersession_Matrix.md`, `OPA_V4.md` §6, `13_Round_D_Policy_Scope_UI_Micro_Patch_R0_2.md`, `MultiDoc_PropA_R6_3_Compiled_Operative_Spec.md`.

---

## §0. How to read this draft

DOC81 is the **scope-and-policy foundation** every other plane consumes: DOC82/DOC83 gate extraction and writes on it, DOC84 caps delivery on it, DOC86 surfaces it, DOC87 must not overlap it, and **EC executes all of it**. DOC81 **defines contracts; EC executes them** (Owner Map §9; Import Graph §3 `DOC81 → EC`). Nothing in this plane computes scope resolution, enforces policy, or writes durably — those are EC. DOC81 owns the *shapes, the meet algebra, the fail-closed posture, and the invariants*.

**Citation convention (Commission §1.4).** Every contract / field / lint / fixture traces to one of: an ADQ (`ADQ-NNN`), a Skeletal section (`Skeletal §10.x` / `§11.x` / `§DOC81`), an Owner Map row (`Owner Map line N`), a Supersession Matrix row (`SM-NNN`), a Round-D section (`Round D §1.x`), an OPA obligation (`OBL-…`), or an **E0 section** (`E0 §3.3`). Untraceable claims are flagged `OPEN_FOR_ARCHITECT_REVIEW` in §13 — never invented.

**Schema convention.** Schemas are paste-ready TypeScript-style interfaces. All cross-object refs are **branded strings** per E0 §8.5 / N12 (there is no `ContentReference` object — Round D's `ContentReference` normalizes to the branded `MemoryObjectRef`). Durable records extend **`E0DurableRecord`** (E0 §1.6.2: `schema_version` + `created_at`). Every owned schema names exactly one `schema_owner: 'DOC81'` (Owner Map one-owner rule, Skeletal §1.2).

**Bind-by-reference (E0-owned — DOC81 consumes, NEVER redefines).** `ReasonCodeRegistry` / `ReasonCodeId` (E0 §2.1), `DomainProfileRegistry` / `DomainProfileId` + `conservative` fallback + per-axis `DomainProfileRestrictivenessVector` (E0 §2.2 — DOC81 owns only the 9-axis→5-axis crosswalk semantics, §4.6.2), `WarrantDegradationTrigger` (`policy_change`) (E0 §2.4), the `EffectiveMemoryPolicyRef` consumption-protocol pointer (E0 §3.2), `MemoryFlowCertificate` + `RestampMFC` / `ErasureMFC` / `RestoreMFC` (E0 §3.3), `MemoryCoordinationTrace` (E0 §3.4), `MemoryMutationEnvelope` (E0 §5.1, NAMED), the policy-generation carrier `policy_generation_id` (E0 §8.3), the §12.1 invariant set, and the §22 egress vocabulary `E0OutboundDestinationClass` / `E0EgressAttestation`. **R2 additions (referenced, never re-declared):** the freshness-key components `EffectiveStateGenerationId` + the EC §1 effective-state, the compiled-policy-evaluator hash + EC §3, the registry versions (E0 §2.1/§2.2), and the `E0InteractionMode` value set (E0/EC-owned) — all composed by DOC81 into `PolicyRuntimeFreshnessKey` (§3.0) but owned by EC/E0. **DOC81 references all of these by E0 section number and never re-declares a registry or carrier DOC80 owns** (Hard Constraint §2/§3; ADQ-310 "do not invent local reason-code systems").

**Branded-ref types introduced by DOC81 (declared here, used throughout).** R2 makes these **real brands** (U31 / CL S7 + GPT M8): `type X = string` is not a brand, so every DOC81-owned primary ID + ref uses the `Brand<T,B>` pattern; lint `schema.primary_id_not_branded`. Foreign vocabulary values use the owner/version-qualified `ExternalVocabularyValueRef` (U30 / GPT M7), not loose strings.

```typescript
// Real brand pattern (U31 — GPT §4.1 / CL §2.3). A bare `string` is NOT a brand.
type Brand<T, B extends string> = T & { readonly __brand: B };

// ---- shared value brands ----
type ContentHash = Brand<string, 'ContentHash'>;            // E0 §8.5 sha256 digest
type SchemaVersionRef = Brand<string, 'SchemaVersionRef'>;  // E0 §8.5
type OwnerDocId = Brand<string, 'OwnerDocId'>;
// ---- scope-plane brands ----
type MemoryObjectRef = Brand<string, 'MemoryObjectRef'>;    // the object a policy/scope decision is about (was Round D ContentReference; N12)
type ScopeRef = Brand<string, 'ScopeRef'>;                  // a ScopeIdentityRoot id
type ScopeBoundaryRef = Brand<string, 'ScopeBoundaryRef'>;
type ScopeResolutionResultRef = Brand<string, 'ScopeResolutionResultRef'>;   // matches E0 §3.2
type ScopeResolutionTraceRef = Brand<string, 'ScopeResolutionTraceRef'>;
type ScopeEquivalenceBindingRef = Brand<string, 'ScopeEquivalenceBindingRef'>;
type ScopeEquivalenceClusterRef = Brand<string, 'ScopeEquivalenceClusterRef'>;   // U10
type ScopeContainerRelationRef = Brand<string, 'ScopeContainerRelationRef'>;
// ---- policy-plane brands ----
type MemoryPolicyDecisionRef = Brand<string, 'MemoryPolicyDecisionRef'>;
type EffectiveMemoryPolicyRef = Brand<string, 'EffectiveMemoryPolicyRef'>;   // SAME brand as E0 §3.2 — the consumption-protocol pointer
type PolicyEvaluationContextRef = Brand<string, 'PolicyEvaluationContextRef'>;   // U6
type PolicyObligationRef = Brand<string, 'PolicyObligationRef'>;
type PolicyObligationConflictRef = Brand<string, 'PolicyObligationConflictRef'>; // U9
type PolicyObligationDischargeRef = Brand<string, 'PolicyObligationDischargeRef'>; // U9
type PolicyStampRef = Brand<string, 'PolicyStampRef'>;
// type PolicyStampScopeRef — RETIRED at R3 (V20/B-S6): PolicyStampScope collapsed into PolicyStamp.scope_items; brand no longer declared. Lint `schema.policy_stamp_scope_ref_used`.
type PolicyCeilingRef = Brand<string, 'PolicyCeilingRef'>;                   // matches E0 §3.3 RestampMFC.original_ceiling_ref (was bare string — B1)
type ConservatismFloorEffectRef = Brand<string, 'ConservatismFloorEffectRef'>;   // U2
type ThresholdEvaluationRef = Brand<string, 'ThresholdEvaluationRef'>;       // U17
type PolicyDisambiguationRequestRef = Brand<string, 'PolicyDisambiguationRequestRef'>;
type PolicyDisambiguationAnswerRef = Brand<string, 'PolicyDisambiguationAnswerRef'>; // U23
type SafeReferenceLabelRef = Brand<string, 'SafeReferenceLabelRef'>;         // APPROVED safe-label vocabulary key (PropA registry; never derived from content)
type DisclosureScopeAttestationRef = Brand<string, 'DisclosureScopeAttestationRef'>; // E0 §3.3 / §22
type LegalHoldClearanceRef = Brand<string, 'LegalHoldClearanceRef'>;         // U12 / S3
type TopicRiskClassRef = Brand<string, 'TopicRiskClassRef'>;                 // U33 / GPT §4.24
type TopicRiskConfirmationRef = Brand<string, 'TopicRiskConfirmationRef'>;   // U33 / GPT M4
// ---- R2 fidelity sweep (Adj U31; CODEX R2-S1) — primary-ID brands for every DOC81-owned record ----
type ScopePopulationHealthRef = Brand<string, 'ScopePopulationHealthRef'>;
type ScopeSearchCoverageProofRef = Brand<string, 'ScopeSearchCoverageProofRef'>;
type PolicyMembraneDecisionRef = Brand<string, 'PolicyMembraneDecisionRef'>;
type PolicyMembraneDispositionDerivationRef = Brand<string, 'PolicyMembraneDispositionDerivationRef'>;
type RestampChainIntegrityRef = Brand<string, 'RestampChainIntegrityRef'>;
type PolicyStampRestampRef = Brand<string, 'PolicyStampRestampRef'>;
type PolicyStampInvalidationRef = Brand<string, 'PolicyStampInvalidationRef'>;
type EpisodePolicyEpochRef = Brand<string, 'EpisodePolicyEpochRef'>;
type SafeLabelDisclosurePolicyRef = Brand<string, 'SafeLabelDisclosurePolicyRef'>;
type ExtractionRoutePolicyEnvelopeRef = Brand<string, 'ExtractionRoutePolicyEnvelopeRef'>;
type PolicyCappedDAMSInputRef = Brand<string, 'PolicyCappedDAMSInputRef'>;
type EligibilityCeilingDerivationRef = Brand<string, 'EligibilityCeilingDerivationRef'>;
type ContaminationRiskThresholdRuleRef = Brand<string, 'ContaminationRiskThresholdRuleRef'>;
type DomainPolicyThresholdsRef = Brand<string, 'DomainPolicyThresholdsRef'>;   // V20 — own primary ID for DomainPolicyThresholds (was foreign DomainProfileId)
type DomainProfilePolicyContributionRef = Brand<string, 'DomainProfilePolicyContributionRef'>;
type DestinationPolicyCrosswalkRef = Brand<string, 'DestinationPolicyCrosswalkRef'>;
type MemoryPolicyActionClosureRuleRef = Brand<string, 'MemoryPolicyActionClosureRuleRef'>;
type ActionPermissionPredicateRef = Brand<string, 'ActionPermissionPredicateRef'>;
type CascadingSourceInvalidationRef = Brand<string, 'CascadingSourceInvalidationRef'>;
type CascadingSourceInvalidationRunRef = Brand<string, 'CascadingSourceInvalidationRunRef'>;
type LastActiveSupportEdgeEvaluationRef = Brand<string, 'LastActiveSupportEdgeEvaluationRef'>;
type LegalHoldStateRef = Brand<string, 'LegalHoldStateRef'>;
type DestructiveJobLegalHoldGateRef = Brand<string, 'DestructiveJobLegalHoldGateRef'>;
type BatchOperationQuotaEnvelopeRef = Brand<string, 'BatchOperationQuotaEnvelopeRef'>;
type CollectionSuppressionEvaluationRef = Brand<string, 'CollectionSuppressionEvaluationRef'>;
type RelationTraversalScopeCheckPolicyRef = Brand<string, 'RelationTraversalScopeCheckPolicyRef'>;
type RelationTraversalBudgetRef = Brand<string, 'RelationTraversalBudgetRef'>;
type RelationTraversalExecutionTraceRef = Brand<string, 'RelationTraversalExecutionTraceRef'>;
type PolicyUIExportRef = Brand<string, 'PolicyUIExportRef'>;
// (ExtractionRouteContextRef, CollectionGateResultRef, SourceExclusionFilterRuleRef,
//  CollectionModeSuppressionGovernanceRef are declared at §4.3 and used from there.)
// ---- referenced (consumed, not owned) brands ----
type SourceRef = Brand<string, 'SourceRef'>;                // a DOC82/DOC25 source id (consumed, not owned)
type ContextProductRequestRef = Brand<string, 'ContextProductRequestRef'>;   // DOC84-owned context-product request id; referenced (U21)
type ContaminationRiskMeasurementRef = Brand<string, 'ContaminationRiskMeasurementRef'>; // DOC84-owned measurement (F9); referenced
type PrincipalRef = Brand<string, 'PrincipalRef'>;
type SurfaceRef = Brand<string, 'SurfaceRef'>;
type TopicRef = Brand<string, 'TopicRef'>;                  // DOC87-owned Topic identity (ADQ-220); referenced
type WorkEpisodeRef = Brand<string, 'WorkEpisodeRef'>;      // DOC83-owned; referenced
type TopicCollectionDirectiveRef = Brand<string, 'TopicCollectionDirectiveRef'>; // DOC83-owned (Owner Map line 118); referenced, never redefined

/** Owner/version-qualified external vocabulary value (U30 / GPT M7) — replaces loose foreign-vocab strings.
 *  lint: schema.external_vocabulary_unversioned_string. */
interface ExternalVocabularyValueRef {
  owner_doc: OwnerDocId; registry_id: string; value_id: string; registry_version: SchemaVersionRef;
}
type VisibilityClassRef = ExternalVocabularyValueRef;       // PropA/DOC82-owned visibility vocabulary; referenced, not redefined

// E0/EC-owned brands + value sets referenced verbatim, NEVER re-declared (Hard Constraint §3):
//   ReasonCodeId, PolicyGenerationId, DomainProfileId, EffectiveStateGenerationId (E0 §1.6),
//   MemoryFlowCertificateId, MemoryMutationEnvelopeRef, MemoryCoordinationTraceRef,
//   E0OutboundDestinationClass + E0EgressAttestation / E0EgressAttestationRef (E0 §22 — the send-time attestation brand; referenced
//   by §3.2 issued_egress_attestation, never owned here), E0InteractionMode = 'interactive' | 'background_non_interactive'
//   | 'scheduled' | 'agent_initiated' (E0/EC-owned interaction-mode value set — referenced per U6).
```

**Lint:** `schema.branded_ref_declared_as_plain_string`; `schema.primary_id_not_branded` (U31).

---

## §1. Identity, scope, non-goals, and the E1/E2 lockstep

### §1.1 Member identity (family member 2 of 8)

DOC81 is the **Scope & Policy plane** of the Memory Control Plane family (Skeletal §DOC81; ADQ-220 fixes the family at 8). It sits at topological position 2 — directly below DOC80 core and above every other member — on the `schema_import` edge kind (Import Graph §2.5: `DOC80 < DOC81 < DOC82 < DOC87 < DOC83 < DOC84 < {DOC85, DOC86}`). Its only upstream prerequisite, E0's ReasonCode + DomainProfile registries, is **ratified** (Opening Brief pre-condition; ADQ-310/313), so E1/E2 is unblocked.

The Skeletal §DOC81 section map (§1 Scope plane · §2 Policy plane · §3 Coordination/meet · §4 Fail-closed · §5 CascadingSourceInvalidation envelope · §6 DOC81→DOC86 export · §7 Lints) is the architecture this charter formalizes. This draft expands that map into paste-ready contracts, preserving the section semantics while following the Commission §3.1 outline.

### §1.2 What DOC81 owns / does NOT own (Owner Map)

DOC81 **owns** exactly the Owner Map rows 62, 71–94, 194 (Input Deck "Owner Map — DOC81-owned rows"):

| Owner Map line | schema | EC's role (executor) |
|---|---|---|
| 62 | `ExtractionRoutePolicyEnvelope` | DOC83 + EC consume; EC enforces the envelope at extraction routing |
| 71 | `ScopeIdentityRoot` | EC executes |
| 72 | `ScopeEquivalenceBinding` | EC executes dedupe collapse |
| 73 | `ScopeContainerRelation` | EC + DOC72 + DOC73 reference |
| 74 | `ScopeBoundary` (topology only) | EC executes boundary check |
| 75 | `ScopeAffinity` | EC + DOC82 + DOC84 consume |
| 76 | `ScopeResolutionResult` (+ `minimum_conservatism_floor`) | **EC computes** (Import Graph §3 `DOC81 → EC`) |
| 77 | `ScopeResolutionTrace` | DOC86 surfaces in Inspector |
| 78 | `ScopePopulationHealth` | EC + DOC83 (NullResultMemory) consume |
| 79 | `MemoryPolicyDecision` (5-axis) | EC executes; PropA contributes inputs |
| 80 | `EffectiveMemoryPolicy` (wholly DOC81) | **EC executes the meet** |
| 81 | `PolicyMembraneDecision` (membrane crossing only) | EC executes the membrane-crossing decision |
| 82 | `PolicyObligation` | EC executes; PropA enforces |
| 83 | `PolicyStamp` (`scope_items`) + `PolicyStampInvalidation` | EC executes; DOC86 surfaces restamp UI — *R3/V20: `PolicyStampScope` collapsed into `PolicyStamp.scope_items`* |
| 84 | `PolicyDisambiguationRequest` | EC executes (replaces `ask_user`); DOC84 implements park/resume |
| 85 | `EpisodePolicyEpoch` | EC executes; DOC83 consumes for episode bounds |
| 87 | `SafeLabelDisclosurePolicy` | EC enforces; DOC86 references |
| 90 | `PolicyCappedDAMSInput` (eligibility ceiling) | DOC84/DAMS + EC consume; computation at DOC84 |
| 91 | `contamination_risk` **threshold rule** | DOC84/DAMS execute veto; computation at DOC84 |
| 93 | `disclosure_class` | EC executes; DOC86 surfaces |
| 94 | `CascadingSourceInvalidation` **envelope** | **EC executes**; source-side payload at DOC82 |
| 194 | `TopicRiskClass` (policy) | EC executes; DOC20 surfaces; DOC87 consumes |

Plus the **restamp contract** `PolicyStampRestamp` (the widening-path object behind E0 `RestampMFC`, ADQ-316) — DOC81-owned, named at draft target 6.

DOC81 **does NOT own** (Opening Brief "what DOC81 does NOT own"; Hard Constraint §8): truth / canonical objects (DOC82); source/corpus artifacts (DOC25/DOC73/DOC82); extraction routing + `TopicCollectionDirective` (DOC83 — Owner Map line 118; DOC81 owns only the `collection_mode` suppression *governance* layer, §7.1); membership / Topic identity / Library-as-container (DOC87); delivery + prompt products + **DAMS computation** + `contamination_risk` *computation* (DOC84 — Owner Map line 92); learning (DOC85); UI rendering (DOC86/DOC20); the cross-cutting registries — ReasonCode / DomainProfile / proof spine (DOC80 core); and durable writes + policy enforcement *execution* + `ScopeResolutionResult` *computation* (EC). The `AssertionRelationEdge` *schema* stays DOC82 (Owner Map line 33); DOC81 owns only the **traversal scope-check policy** pinned to E1 (§8; ADQ-315).

### §1.3 The lockstep: scope identifies, policy decides (plan §12.2)

Scope and policy are interdependent and are drafted together (plan §12.2; Opening Brief "The lockstep"). **Scope identifies** — identity, equivalence, containment, boundary topology, affinity, and uncertainty — and **may emit conservatism floors and policy-posture hints, but never final allow/block/render/export/learn decisions** (Round D §3.2). **Policy consumes scope and decides** — capability (what the system may do) and disclosure (what it may say) — as a per-dimension conservative meet (Round D §1.2). This charter honors the two-pass discipline: §2 (scope) + §3 (policy) are the **E1 declare** pass; §4 (meet/disclosure/fail-closed) + §5 (cascading) + the stamp/restamp/invalidation lifecycle are the **E2 validate** pass. Per plan §12.2: *do not draft policy as if scope were unavailable; do not draft scope as if it emitted final allow/block decisions.*

### §1.4 Binding to E0 (DOC80 core) — consume by reference, never redefine

DOC81 imports DOC80 core on the `schema_import` edge (Import Graph §2.1 `DOC80 → DOC81`) and imports PropA + EC on external edges (Import Graph §3: `DOC81 → PropA`, `DOC81 → EC`, each pinned by an `ExternalDependencyRecord` at E0 §7). The bind-by-reference list is in §0. Three bindings are load-bearing and recur:

First, **`EffectiveMemoryPolicyRef` is E0's consumption-protocol pointer; the `EffectiveMemoryPolicy` *schema* is wholly DOC81's** (E0 §3.2; Owner Map line 80 / B4 correction; SM-013 / Round D §1.5). E0 holds only the branded ref so `MemoryContextPlan` (E0 §3.2), `ContextPacketProof` (E0 §4.1), and every `MemoryFlowCertificate` (E0 §3.3) can point at a policy without DOC80 owning the policy schema. DOC81 declares the schema in §3.2 and never re-declares the pointer.

Second, **the policy-generation carrier `policy_generation_id` is E0's** (E0 §8.3; SM-101). DOC81 owns the *boundary object* (`EpisodePolicyEpoch`, §3.6) that opens/closes a generation, but the carried field and the header-stability + extraction-side re-gate rules are E0's; DOC81 binds to them (§6.4).

Third, **the restamp ceiling lives on E0's `RestampMFC`** (`original_ceiling_ref: PolicyCeilingRef` + `ceiling_compliance_attested`, E0 §3.3). DOC81's `PolicyStampRestamp` (§3.4) is the decision contract that *produces* a `RestampMFC`; EC issues the certificate. DOC81 never re-declares the MFC union.

### §1.5 Fail-closed default posture (conservative; ADQ-313)

DOC81's default posture is **fail-closed**: missing, unknown, or incomparable scope or policy resolves to the **most-restrictive** behavior, never the least (ADQ-313; E0 §2.2 conservative fallback; Round D §3.6; Skeletal §DOC81 §4). Concretely (formalized in §4.6): a missing policy input on any of the five axes resolves to that axis's bottom (most-restrictive) element; an unresolved or low-confidence scope resolves to a `conservative` domain profile and a `fail_closed_candidate` / `reference_only_candidate` floor; a destination that cannot be typed to a recognized `E0OutboundDestinationClass` is blocked before any attestation issues (E0 §22 CR2 default-deny). `orientation_only` is **not** a confidentiality control (Round D §3.6). This posture is the structural defense against the failure modes in Commission §1.5 — *policy widened without a restamp, scope confused with membership, capability/disclosure collapsed, silent degradation*.

---

## §2. Scope plane (E1 — declare)

Scope objects answer *who/what/where* a memory object belongs to and how it relates to a request — never *whether it may be used*. Source: Round D §3 (restored scope model); Owner Map lines 71–78; SM-010/011; Skeletal §DOC81 §1. **Owner:** DOC81 (all rows). **Executor:** EC computes `ScopeResolutionResult` (Owner Map line 76; Import Graph §3). **Consumers:** DOC82 (write gating), DOC84 (delivery), DOC87 (non-overlap, §6.2), DOC86 (Inspector trace, §2.5).

### §2.1 `ScopeIdentityRoot` / `ScopeEquivalenceBinding`

**Source:** Round D §3.3; Owner Map lines 71–72; SM-010 (split from retired `ScopeMembrane`). **Retired-name guard:** `ScopeMembrane` (monolithic policy+boundary) is retired (SM-010 / Retired Names) — this plane carries topology only; the policy half lives in §3.

```typescript
/** The identity anchor of a scope. schema_owner = DOC81 (Owner Map line 71). EC executes. */
type ScopeKind =
  | 'project' | 'matter' | 'engagement' | 'research_topic'
  | 'initiative' | 'library' | 'personal_domain' | 'global'
  | 'unknown_synthetic';                       // U16/GPT §4.16.1 — the §2.4 empty-path synthetic scope; NEVER reuse 'global'

interface ScopeIdentityRoot extends E0DurableRecord {
  scope_ref: ScopeRef;
  schema_owner: 'DOC81';
  display_name?: string;                       // NOT a safe label; not for disclosure
  scope_kind: ScopeKind;                       // Round D §3.3 + unknown_synthetic (U16)
  owner_principal_ref?: PrincipalRef;          // REQUIRED-bearing for R-5 multi-principal readiness (never dropped)
  visibility_class_ref?: VisibilityClassRef;   // §0 ExternalVocabularyValueRef (U30/M7) — PropA/DOC82-owned, referenced, never redefined
  lifecycle_state: 'active' | 'deprecated' | 'synthetic_unknown';   // U16/U28
  // corpus/library identity TERM is DOC87's (ADQ-PASS2-02); DOC81 consumes it as vocabulary only — never owns it.
}

/** Pairwise evidence for an equivalence (U10/GPT §4.16.2 — kept where it exists). */
interface ScopeEquivalencePairEvidence {
  left_scope_ref: ScopeRef; right_scope_ref: ScopeRef; confidence: number;   // 0..1
  evidence_refs: string[]; evaluated_under_model_generation_id?: string; reason_codes: ReasonCodeId[];
}

/** Asserts two or more scopes are the same identity. schema_owner = DOC81 (Owner Map line 72). EC executes the dedupe collapse. */
interface ScopeEquivalenceBinding extends E0DurableRecord {
  binding_id: ScopeEquivalenceBindingRef;
  schema_owner: 'DOC81';
  scope_refs: ScopeRef[];                      // ≥2
  equivalence_basis:
    | 'explicit_user_binding' | 'entity_resolution' | 'folder_binding'
    | 'project_binding' | 'corpus_binding' | 'manual_merge' | 'migration_inferred';  // Round D §3.3
  pairwise_evidence: ScopeEquivalencePairEvidence[];
  confidence: number;                          // 0..1 (single-pair)
  domain_threshold_ref?: DomainProfileId;      // E0 §2.2
  reason_codes: ReasonCodeId[];                // E0 §2.1
}

/** Transitive equivalence cluster with weakest-link confidence + firewall guard (Adj U10/F2 — CL §2.10 as default).
 *  schema_owner = DOC81 (Owner Map row added §14.3). EC executes the collapse. Litigation-critical: no laundering, no silent
 *  cross-matter merge. (GPT §4.16.2's full n(n−1)/2 pairwise-matrix-or-zero is the STRICTER alternative — DECLINED as default
 *  per D7/F2: it defeats legitimate transitive dedupe; the firewall guard is the real safety net. GPT's collapse_disposition +
 *  pairwise records are kept above where they exist.) */
interface ScopeEquivalenceCluster extends E0DurableRecord {
  cluster_id: ScopeEquivalenceClusterRef; schema_owner: 'DOC81';
  member_scope_refs: ScopeRef[];               // transitive closure of CONFIRMED bindings
  spanning_binding_refs: ScopeEquivalenceBindingRef[];
  spanning_selection_algorithm: 'maximum_bottleneck_spanning_tree' | 'explicit_user_binding_only' | 'strict_full_pairwise_profile';   // V15/QF5 — deterministic proof tree (F2 default = maximum_bottleneck)
  cluster_confidence: number;                  // = MIN edge confidence over the SELECTED spanning tree (weakest link — no laundering)
  cluster_confidence_formula: 'min_edge_confidence_over_selected_spanning_tree';   // V15/QF5
  collapse_disposition: 'collapse_confirmed' | 'do_not_collapse_below_threshold' | 'requires_user_confirmation' | 'crossed_firewall_blocked';
  contradictory_evidence_refs: string[];       // V15 — contrary bindings above the domain threshold
  contradiction_disposition: 'none' | 'do_not_collapse' | 'requires_user_confirmation';   // V15/QF5
  lifecycle_state: 'active' | 'invalidated' | 'recomputing';   // V15/A2-S8 — invalidated on any spanning-binding change
  equivalence_generation_id: string;           // V15/A2-S8 — bumped on any spanning-binding change ⇒ stale-cluster invalidation
  domain_threshold_ref: DomainProfileId;       // collapse only when cluster_confidence ≥ the STRICTEST member's domain threshold
  threshold_evaluation_ref: ThresholdEvaluationRef;   // §4.6 (U17)
  reason_codes: ReasonCodeId[];
}
```

**V15 (Adj V15/QF5/A2-S8 — merge seam re-derived: CL cluster-lifecycle + GPT spanning determinism).** The cluster confidence is now **deterministic**: choose the **maximum-bottleneck spanning tree** over confirmed positive bindings and take `cluster_confidence = min(edge.confidence)` over that selected tree (different spanning trees could otherwise produce different bottlenecks). The cluster is **invalidated on any spanning-binding change** (`equivalence_generation_id` bump ⇒ `lifecycle_state='invalidated'`), so a revoked/un-merged binding can never leave two scopes silently collapsed (A2-S8). Contradictory evidence above the domain threshold ⇒ `do_not_collapse`/`requires_user_confirmation`. The F2 settlement (MIN-over-spanning + firewall guard; full-pairwise declined as default, D7) is preserved. **Lints:** `scope.equivalence_cluster_stale_after_binding_change` (A2-S8); `scope.equivalence_cluster_without_pairwise_matrix` (QF5); `scope.equivalence_cluster_confidence_not_min_pairwise` (QF5). **Fixtures:** `fixture.scope.cluster_invalidates_on_spanning_binding_revocation` (A2-S8); `fixture.scope.three_scope_cluster_low_pair_prevents_collapse` (QF5).

**Equivalence collapse rules (Adj U10/F2; CL §2.10).** (1) Transitive collapse only within a cluster whose `cluster_confidence ≥ the strictest member's domain threshold`; `cluster_confidence = MIN(spanning bindings)` (weakest link). (2) **A `firewalled` boundary is crossed only by an `explicit_user_binding` naming both scopes — NEVER by inference** (dormant for a solo user, §1.6). An inferred merge that would join firewall-separated scopes is blocked (`collapse_disposition = 'crossed_firewall_blocked'`). Equivalence **never** confers access — it collapses identity only (DOC81/DOC87 non-overlap, §6.2).

**Lifecycle.** A `ScopeIdentityRoot` is `created → (referenced) → (deprecated on merge)`. A `ScopeEquivalenceBinding` is `proposed → confirmed (EC dedupe collapse) → invalidated` (on `migration_inferred` re-check, embedding-model migration per E0 §8.1, or user un-merge). Equivalence **never** confers access — it only collapses identity for resolution (DOC81/DOC87 non-overlap, §6.2).

**Unhappy paths.** *Sub-threshold confidence* (`confidence < domain_threshold` profile floor) → the binding does **not** collapse; resolution treats the scopes as distinct and `minimum_conservatism_floor` rises (§2.4, fail-closed). *Missing `scope_kind`* → resolves to `global`-conservative under the fallback profile, never to the most-permissive scope. *Equivalence cited as authority* → blocked (a binding is identity, not permission). *Empty `scope_refs` (<2)* → integrity reject.

**Lints (Stage 9):** `scope.equivalence_binding_below_domain_threshold_collapsed`; `scope.identity_root_missing_kind_not_conservative`; `scope.equivalence_used_as_access_authority`; `scope.equivalence_binding_under_two_refs`; `scope.transitive_equivalence_below_cluster_threshold` (U10); `scope.transitive_merge_crossed_firewall` (U10 — litigation-critical); `scope.equivalence_cluster_confidence_not_min` (U10); `scope.unknown_resolution_reused_global_scope` (U16).

**Fixtures (Stage 8):** `fixture.scope.subthreshold_equivalence_does_not_collapse`; `fixture.scope.equivalence_confers_no_access`; `fixture.scope.three_scope_cluster_low_pair_prevents_collapse` (U10); `fixture.scope.transitive_merge_cannot_cross_firewall` (U10); `fixture.scope.unresolvable_object_gets_synthetic_unknown_scope_not_global` (U16).

**Cross-charter consumption.** DOC82 (request-scope identity for write gating), DOC87 (the corpus/library identity *term* is DOC87's per ADQ-PASS2-02; DOC81 consumes), DOC84 (scope identity on delivery), EC (executes dedupe).

### §2.2 `ScopeContainerRelation` / `ScopeBoundary` (topology only)

**Source:** Round D §3.3–§3.4; Owner Map lines 73–74; SM-011. **Hard constraint (§4 / Skeletal §10.4):** containment is **topology**, not membership and not authority — `ScopeContainerRelation` (DOC81) and `MemoryMembershipEdge` (DOC87) are distinct; neither implies the other or any access/source/truth authority (§6.2).

```typescript
/** A topological containment/relation edge between scopes. schema_owner = DOC81 (Owner Map line 73).
 *  EC + DOC72 + DOC73 reference. NOT a membership edge (DOC87) and NOT an access grant (§6.2). */
type ScopeContainerRelationKind =
  | 'contains' | 'linked_library'
  | 'source_topology_link'                     // U30/M1 — RENAMED from `source_membership` (§6.2 non-overlap hygiene)
  | 'project_binding'
  | 'topic_topology_link'                      // U30/M1 — RENAMED from `topic_membership`
  | 'episode_touched_scope' | 'analogical_relation';   // Round D §3.3 (renamed values recorded in Retired-Names style, §14.3)

interface ScopeContainerRelation extends E0DurableRecord {
  relation_id: ScopeContainerRelationRef;
  schema_owner: 'DOC81';
  container_scope_ref: ScopeRef;
  member_scope_ref: ScopeRef;
  relation_kind: ScopeContainerRelationKind;
  policy_load_bearing: boolean;                // true iff this relation may TIGHTEN (never widen) a meet input
  load_bearing_effects?: Array<{               // U30/M2 — replaces a bare boolean: WHICH actions/axes it tightens, never widens
    applies_to_actions: MemoryPolicyAction[];
    tightens_axes: PolicyAxis[];               // §3.4 PolicyAxis (minus disclosure_vector)
    max_floor?: ScopeConservatismFloor;        // §4.6
    may_widen: false;                          // structural — a container relation can NEVER widen
    reason_codes: ReasonCodeId[];
  }>;
  lifecycle_state: 'active' | 'invalidated';   // U28
  reason_codes: ReasonCodeId[];
}

/** Structural boundary between a request and an object's scope. schema_owner = DOC81 (Owner Map line 74).
 *  EC executes the boundary check. TOPOLOGY ONLY — emits no allow/block (Round D §3.2). */
type ScopeBoundaryKind =
  | 'same_scope' | 'contained' | 'adjacent' | 'cross_scope' | 'firewalled' | 'unknown';  // Round D §3.4

interface ScopeBoundary extends E0DurableRecord {
  boundary_ref: ScopeBoundaryRef;
  schema_owner: 'DOC81';
  from_scope_ref: ScopeRef;
  to_scope_ref: ScopeRef;
  boundary_kind: ScopeBoundaryKind;
  // boundary_kind answers "what structural boundary exists?" — NOT "how relevant?" (that is ScopeAffinity, §2.3)
  persistence_kind: 'derived_projection'; canonical_source_refs: string[]; rebuild_key: string;   // V13 — derived per resolution (D-R2 local marker)
}
```

**Lifecycle.** A `ScopeBoundary` is **derived per resolution** (computed by EC inside `ScopeResolutionResult`, §2.4) — it is not an independently mutated durable object except where cached; a cached boundary invalidates on any `ScopeEquivalenceBinding` change or scope-population change (`ScopePopulationHealth`, §2.5). A `ScopeContainerRelation` is `created → (policy_load_bearing evaluated) → invalidated`.

**Unhappy paths.** *`boundary_kind = unknown`* → never treated as `same_scope`; drives `minimum_conservatism_floor` upward (§2.4). *`policy_load_bearing` relation used to widen a meet* → blocked (containment may tighten, never widen — monotonicity, §6.3). *`relation_kind: analogical_relation` rendered as containment* → blocked (analogy is affinity, not containment). *Container relation read as membership* → `membership_edge_used_as_scope_container_relation` family (§6.2).

**Topology acyclicity + traversal bound (Adj U11/B14; CL §2.10).** `contains` edges form a **DAG** — EC rejects any cycle-creating edge (`scope.container_cycle_created`). Only `{contains, source_topology_link, topic_topology_link}` traverse for containment; `{linked_library, analogical_relation}` are **affinity-only** (not traversed). `ScopeBoundary` derivation (and §8 relation traversal) is bounded by `MAX_SCOPE_TRAVERSAL_DEPTH = 16` (seed — architect confirm, §13.6) with visited-set cycle handling; exceeding the bound **fails closed** (`boundary_kind='unknown'` ⇒ floor rises). The closed traversal registry + budget object live in §8 (U11).

```typescript
export const MAX_SCOPE_TRAVERSAL_DEPTH = 16;   // B14/U11 — seed; architect confirm (§13.6)
```

**Project ↔ scope seam (Adj U27c; Round D §3.8 — closes the §14.3 source-coverage gap that stopped at §3.7).** A Project may **seed** scope identity as an *operational hint only*: active Project mode may supply `request_scope_ref` and a preferred `ScopeIdentityRoot`, and Project bindings may contribute to a `ScopeEquivalenceBinding` / `ScopeContainerRelation`. A Project may **NOT** create truth identity, override a scope boundary, broaden policy, or count as proof. This is hint-only; no boundary is ever derived *from* Project membership.

**Lints (Stage 9):** `scope.unknown_boundary_treated_as_same_scope`; `scope.container_relation_widened_meet`; `scope.analogical_relation_used_as_containment`; `scope.boundary_emitted_allow_block_decision` (Round D §3.2); `scope.container_cycle_created` (U11); `scope.traversal_depth_exceeded_not_fail_closed` (U11); `scope.membership_term_used_for_topology_relation` (U30/M1); `scope.policy_load_bearing_boolean_without_effects` (U30/M2); `scope.project_membership_created_truth_identity` (U27c); `scope.project_mode_broadened_policy` (U27c); `scope.project_membership_used_as_proof` (U27c); `scope.project_overrode_scope_boundary` (U27c).

**Fixtures (Stage 8):** `fixture.scope.unknown_boundary_fails_closed`; `fixture.scope.container_relation_cannot_widen`; `fixture.scope.container_cycle_rejected` (U11); `fixture.scope.traversal_depth_exceeded_fails_closed` (U11); `fixture.scope.project_seed_is_hint_only` (U27c).

**Cross-charter consumption.** EC (boundary check), DOC72/DOC73 (reference container relations), DOC87 (non-overlap check, §6.2), DOC84 (cross-scope render gating).

### §2.3 `ScopeAffinity` (direct/secondary/shared/analogical/background …)

**Source:** Round D §3.4; Owner Map line 75; Opening Brief draft target 1. **`GAP` (reconciled — see §13.1):** the value set differs across authorities. Opening Brief / Owner Map line 75 / Commission §3.1 name `{direct, secondary, shared, analogical, background}`; Round D §3.4 (the schema source) names `{direct, secondary, background, analogical, unrelated, uncertain}`. To lose no source value (no phantom-create — every value traces), DOC81 defines the **traceable union** and flags the reconciliation in §13.1.

```typescript
/** How relevant an object is to the request — the affinity axis, ORTHOGONAL to ScopeBoundaryKind (Round D §3.4).
 *  schema_owner = DOC81 (Owner Map line 75). EC + DOC82 + DOC84 consume.
 *  Union of Opening-Brief/Owner-Map {direct,secondary,shared,analogical,background}
 *  and Round D §3.4 {direct,secondary,background,analogical,unrelated,uncertain}. See §13.1. */
type ScopeAffinity =
  | 'direct'        // brief + Round D
  | 'secondary'     // brief + Round D
  | 'shared'        // brief / Owner Map line 75 only
  | 'background'    // brief + Round D
  | 'analogical'    // brief + Round D
  | 'unrelated'     // Round D §3.4 only
  | 'uncertain';    // Round D §3.4 only — drives fail-closed (§4.6)
```

`ScopeAffinity` answers *how relevant?*; `ScopeBoundaryKind` (§2.2) answers *what boundary?*. They are independent axes: an object can be `same_scope` + `background`, or `cross_scope` + `direct`. Affinity **informs** policy posture and DAMS salience eligibility (§4.4) but **never** decides allow/block (Round D §3.2).

**Lifecycle.** Affinity is computed per resolution (EC), carried on `ScopeResolutionResult.relation_to_request` (§2.4), and recomputed when the request scope, equivalence bindings, or population change.

**Unhappy paths.** *`uncertain`* → fail-closed: floor rises to at least `reference_only_candidate` (§4.6). *`analogical` treated as `direct`* → blocked (analogy must not promote relevance into evidence eligibility). *Affinity used as an allow/block gate* → blocked (Round D §3.2).

**Lints (Stage 9):** `scope.affinity_uncertain_not_fail_closed`; `scope.analogical_affinity_promoted_to_direct`; `scope.affinity_used_as_capability_gate`.

**Fixtures (Stage 8):** `fixture.scope.uncertain_affinity_floors_reference_only`; `fixture.scope.affinity_orthogonal_to_boundary`.

**Cross-charter consumption.** DOC84 (salience eligibility input to `PolicyCappedDAMSInput`, §4.4), DOC82 (relevance for candidate ranking — never warrant), EC (computes), DOC86 (Inspector shows affinity in the trace, §2.5).

### §2.4 `ScopeResolutionResult` (+ `minimum_conservatism_floor`) + `ScopeResolutionTrace`

**Source:** Round D §3.5; Owner Map lines 76–77; Opening Brief draft target 2; Skeletal §DOC81 §1. **Owner:** DOC81. **Executor:** **EC computes** the result (Owner Map line 76; Import Graph §3 `DOC81 → EC: ScopeResolutionResult computation`). **Consumers:** DOC82 + DOC84 + DOC87 consume the result; DOC86 surfaces the trace.

```typescript
/** The per-request scope resolution. schema_owner = DOC81 (Owner Map line 76); EC COMPUTES it.
 *  Emits conservatism floors + posture hints; emits NO final allow/block (Round D §3.2 / §3.5). */
/** Principal sentinel (Adj V20/GPT §4 — R-5 bleed guard): an ABSENT principal must never silently mean "any principal".
 *  Replaces the optional principal_ref on ScopeResolutionResult + cache keys. `unknown_principal` fails closed. */
type PrincipalScope =
  | { kind: 'principal'; principal_ref: PrincipalRef }
  | { kind: 'single_principal_local' }            // V5 solo-user default (the local single principal)
  | { kind: 'unknown_principal' };                // fail-closed — never treated as a permissive wildcard

interface ScopeResolutionResult extends E0DurableRecord {
  result_id: ScopeResolutionResultRef;
  schema_owner: 'DOC81';

  request_scope_ref?: ScopeRef;
  object_scope_ref: ScopeRef;
  source_scope_ref?: ScopeRef;
  destination_scope_ref?: ScopeRef;
  principal_scope: PrincipalScope;                  // V20 — the requesting principal (sentinel, never bare-optional)
  object_owner_principal: PrincipalScope;           // V4a/R-5 — the OWNER of the object (meet §3.2 step 1c compares these for internal-baseline eligibility)

  boundary_kind: ScopeBoundaryKind;                 // §2.2
  relation_to_request: ScopeAffinity;               // §2.3
  relation_to_destination:
    | 'same_runtime' | 'same_machine_local' | 'same_principal' | 'same_project'
    | 'same_firewall' | 'external_destination' | 'unknown_destination' | 'blocked_destination';  // Round D §3.5

  sensitive_tag_summary:
    | 'none' | 'sensitive_present' | 'protected_present' | 'classification_unknown';
  /** DERIVED (Adj V12/A-S5): = protection_derivation.resolved_protection_state. NOT an independent scalar (one 8-value enum).
   *  `privileged`/`client_confidential`/`matter_or_project_sensitive` drive EGRESS floors only (R-1); never an internal-use block. */
  active_scope_protection_state: ScopeProtectionStateDerivation['resolved_protection_state'];

  confidence: number;                               // 0..1
  domain_threshold_ref: DomainProfileId;            // E0 §2.2 — the profile this resolution is judged against
  ambiguity_reason_codes: ReasonCodeId[];           // E0 §2.1

  equivalence_binding_refs: ScopeEquivalenceBindingRef[];
  container_relation_refs: ScopeContainerRelationRef[];
  scope_trace_ref: ScopeResolutionTraceRef;

  /** The conservatism floor scope HINTS at — policy (§3) decides the final action. */
  minimum_conservatism_floor:
    | 'normal_policy_check'
    | 'reference_only_candidate'
    | 'safe_label_candidate'
    | 'user_disambiguation_candidate'
    | 'fail_closed_candidate';                       // Round D §3.5

  /** Adj V12/A2-S12 — the floor's BASIS, so the meet (§3.2 step 3) can apply it ACTION-AWARE (A-B3/R-1): an internal action
   *  floors only when r1_qualifying; egress actions floor on the full rule-3 set. `r1_qualifying` is true IFF a flag in the
   *  R-1 four (firewalled / revoked / sealed-PO-outside-its-matter / malformed-F1) is present; `flags` are preserved (V12,
   *  never collapsed to the max-rank summary). `disambiguation_cause` parameterizes the user_disambiguation floor (V21). */
  conservatism_floor_basis: {
    r1_qualifying: boolean;
    flags: ScopeProtectionFlag[];                    // §2.4 — preserved, not max-ranked
    disambiguation_cause?: 'firewall' | 'sealed' | 'scope_ambiguity' | 'destination_ambiguity' | 'identity_ambiguity';   // V21 (merges GPT DisambiguationCause)
  };

  policy_actions_required: MemoryPolicyAction[];     // §3.1 — which actions this resolution must be policy-checked for

  // ---- R2 additions (Adj U16/U5/U2) ----
  confidence_breakdown: ScopeResolutionConfidenceBreakdown;   // U16/GPT §4.16.3 — min-of-required-components (high identity must not mask low destination/classification)
  protection_derivation: ScopeProtectionStateDerivation;      // U16/GPT §4.16.5 — multi-flag, max-restrictiveness summary, flags preserved
  population_generation_inputs: Array<{                       // U16 — so ScopePopulationHealth (§2.5) can actually invalidate this result
    scope_ref: ScopeRef; scope_population_generation_id: string;
    population_state_at_resolution: ScopePopulationHealth['population_state'];
  }>;
  sensitivity_inputs: Array<{                                 // U16 — PropA classification states (the unclassified-privileged-source case, Round D §3.6)
    source_ref?: SourceRef; propa_classification_ref: string; classification_generation_id: string;
    classification_state: 'final' | 'provisional_source_only' | 'unclassified' | 'deferred_unavailable' | 'quarantined_review';
    fail_closed_required: boolean;
  }>;
  threshold_evaluation_ref: ThresholdEvaluationRef;           // §4.6 (U17) — the comparator/equality-rule used for the floor
  evaluated_under_policy_generation_id: PolicyGenerationId;   // B4/U5 — the generation this resolution was computed under
  effective_time: string;                                     // B19/U5 — the request's as-of time (RFC3339-UTC)
  freshness_key: PolicyRuntimeFreshnessKey;                   // §3.0 (U5)
  // ---- R3 (Adj V13) — derived/durable split (local marker, NOT an E0DerivedProjection base — D-R2) ----
  persistence_kind: 'derived_projection'; canonical_source_refs: string[]; rebuild_key: string;   // per-request; rebuildable from sources
}

/** Adj V13/GPT №6/№10 — the persistence marker, a LOCAL DOC81 type (D-R2: naming an E0-owned base would force an E0 edit; declined).
 *  The six derived per-request objects carry `persistence_kind: 'derived_projection'` + canonical_source_refs + rebuild_key:
 *  ScopeResolutionResult, ScopeBoundary, EffectiveMemoryPolicy (the meet result), PolicyMembraneDecision, PolicyCappedDAMSInput,
 *  PolicyEvaluationContext. They are rebuildable from their sources; they are NOT audit-durable (the durable objects are the
 *  stamp/restamp/cluster/cascade/legal-hold/governance/exclusion/topic-risk records). §14.3 lists the classification rows. */
type DOC81PersistenceKind = 'derived_projection' | 'audit_durable';

/** Confidence breakdown (U16/GPT §4.16.3) — aggregate = MIN of required components; a missing required component = 0. */
interface ScopeResolutionConfidenceBreakdown {
  identity_confidence?: UnitInterval; boundary_confidence?: UnitInterval; affinity_confidence?: UnitInterval;        // V17/QF6 — UnitInterval (finite, 0..1)
  destination_relation_confidence?: UnitInterval; sensitivity_classification_confidence?: UnitInterval; population_freshness_confidence?: UnitInterval;
  aggregation_method: 'minimum_of_required_components';
  required_components: Array<'identity_confidence' | 'boundary_confidence' | 'destination_relation_confidence' | 'sensitivity_classification_confidence' | 'population_freshness_confidence'>;
  aggregate_confidence: UnitInterval;          // min(required); missing required ⇒ 0 ⇒ floor ≥ reference_only_candidate
}
// Adj V17/QF1 — `required_components` is action-derived, non-empty, duplicate-free, auditable (a high identity_confidence must not
// mask an absent destination/classification dimension). schema_owner = DOC81.
function requiredScopeConfidenceComponents(input: { action: MemoryPolicyAction; destination?: E0OutboundDestinationClass; boundary_kind: ScopeBoundaryKind; sensitive_tag_summary: ScopeResolutionResult['sensitive_tag_summary'] }): ScopeResolutionConfidenceBreakdown['required_components'] {
  const req = new Set(['identity_confidence','boundary_confidence','population_freshness_confidence'] as const);
  if (['export','delegate','carryover'].includes(input.action) || input.destination) req.add('destination_relation_confidence');
  if (input.boundary_kind !== 'same_scope' || input.sensitive_tag_summary !== 'none') req.add('sensitivity_classification_confidence');
  return [...req].sort();
}

/** Protection-state derivation (U16/GPT §4.16.5) — max-restrictiveness over preserved flags. Rank order is a §13.6 seed. */
type ScopeProtectionFlag = 'matter_or_project_sensitive' | 'firewalled' | 'sealed' | 'personal_private' | 'client_confidential' | 'privileged' | 'classification_unknown';
interface ScopeProtectionStateDerivation {
  active_flags: ScopeProtectionFlag[];
  resolved_protection_state: 'ordinary' | 'matter_or_project_sensitive' | 'personal_private' | 'client_confidential' | 'firewalled' | 'privileged' | 'sealed' | 'unknown_sensitive';
  derivation_rule: 'max_restrictiveness_over_active_flags';
  // rank (seed — architect confirm, §13.6): ordinary < matter_or_project_sensitive < personal_private < client_confidential < firewalled < privileged < sealed < unknown_sensitive
  classification_input_refs: string[]; classification_generation_id: string; reason_codes: ReasonCodeId[];
}

/** Auditable resolution trace — Inspector-surfaced. schema_owner = DOC81 (Owner Map line 77).
 *  DOC86 surfaces it (subject to InspectorVisibilityPlan disclosure discipline, §4.2 / Round D §5.6). */
interface ScopeResolutionTrace extends E0DurableRecord {
  trace_id: ScopeResolutionTraceRef;
  schema_owner: 'DOC81';
  result_ref: ScopeResolutionResultRef;
  coordination_trace_ref: MemoryCoordinationTraceRef;   // E0 §3.4 — ties to the per-request spine
  considered_scope_refs: ScopeRef[];
  considered_boundary_refs: ScopeBoundaryRef[];
  decisive_factor_reason_codes: ReasonCodeId[];          // why this floor / boundary was chosen
  evaluated_under_policy_generation_id: PolicyGenerationId;  // E0 §8.3
}
```

**`minimum_conservatism_floor` semantics.** The floor is a **lower bound on restrictiveness that policy may not undercut** — it is scope's posture hint, not a decision. Policy (§3.2 meet) computes the effective decision and takes the **more restrictive** of (its own meet, the floor). A `fail_closed_candidate` floor forces the meet to the bottom of every axis unless an explicit, ceiling-bounded stamp/restamp says otherwise (§3.4). `orientation_only` is never a floor and never a confidentiality control (Round D §3.6).

**Lifecycle.** `requested → resolved (EC) → consumed (policy meet §3.2) → invalidated` on policy-generation change (re-resolve; §6.4), scope-population change (`ScopePopulationHealth`, §2.5), or equivalence change. A result computed under an obsolete `policy_generation_id` must be recomputed, never reused (ties E0 §8.3 / §3.2 re-plan rule).

**Unhappy paths.** *Empty* — no object scope resolvable → `object_scope_ref` resolves to a synthetic `unknown` scope, `boundary_kind = unknown`, floor = `fail_closed_candidate`. *Degraded* — `confidence < domain_threshold` → floor rises ≥ `reference_only_candidate`; `MemoryContextPlanDegradedMode = 'scope_unresolved_conservative'` is emitted (E0 §3.2). *Error* — resolver fault → conservative profile + `fail_closed_candidate` (never fail-open). *Blocked* — `relation_to_destination = blocked_destination` → no egress action proceeds (§4). *Unknown classification* — `sensitive_tag_summary = classification_unknown` with a cross/firewalled boundary → `fail_closed_candidate` (Round D §3.6 — the unclassified-privileged-source case).

**Lints (Stage 9):** `scope.resolution_missing_minimum_floor`; `scope.resolution_low_confidence_not_floored`; `scope.resolution_reused_under_stale_policy_generation`; `scope.resolution_emitted_final_allow_block` (Round D §3.2); `scope.fail_closed_candidate_tag_only` (plan §17.4); `scope.resolution_confidence_weighted_average_used` (U16 — must be min-of-required, not weighted avg); `scope.resolution_missing_confidence_component_not_zeroed` (U16); `scope.population_generation_not_pinned_to_resolution` (U16); `scope.multiple_protection_flags_collapsed_without_derivation` (U16).

**Fixtures (Stage 8):** `fixture.scope.unresolved_object_fails_closed`; `fixture.scope.low_confidence_floors_reference_only`; `fixture.scope.stale_generation_forces_reresolve`; `fixture.scope.low_destination_confidence_forces_conservative_floor` (U16); `fixture.scope.population_generation_bump_invalidates_scope_resolution_result` (U16); `fixture.scope.sealed_plus_personal_resolves_to_sealed_but_preserves_flags` (U16).

**Cross-charter consumption.** DOC82 (write gating reads the floor), DOC84 (`MemoryContextPlan.scope_resolution_ref` per E0 §3.2; delivery caps on it), DOC87 (non-overlap, §6.2), DOC86 (Inspector renders `ScopeResolutionTrace`), EC (computes the result + trace). The `ScopeResolutionResultRef` brand is shared with E0 §3.2 so `MemoryContextPlan` binds without redefinition.

### §2.5 `ScopePopulationHealth`

**Source:** Owner Map line 78 (DAMS V5 outline); Skeletal §DOC81 §1; Opening Brief draft target 2. **Owner:** DOC81. **Consumers:** EC + DOC83 (`NullResultMemory` freshness, Owner Map line 117 carries `scope_population_generation_id`). This is the freshness/health signal that makes scope-derived null results and cached boundaries invalidatable.

```typescript
/** Population/health of a scope's resolvable membership — the freshness anchor for null results and
 *  cached boundaries. schema_owner = DOC81 (Owner Map line 78). EC + DOC83 consume. */
interface ScopePopulationHealth extends E0DurableRecord {
  health_id: ScopePopulationHealthRef;
  schema_owner: 'DOC81';
  scope_ref: ScopeRef;
  scope_population_generation_id: string;        // bumped on any membership/source change that changes resolution
  resolvable_object_count: number;
  population_state: 'healthy' | 'sparse' | 'stale' | 'rebuilding' | 'unavailable';
  last_evaluated_at: string;                      // RFC3339-UTC (E0 §8.5)
  reason_codes: ReasonCodeId[];
}
```

**Lifecycle.** `healthy → (population change bumps generation) → stale → rebuilding → healthy`, or `→ unavailable` on resolver outage. A bump of `scope_population_generation_id` invalidates any `NullResultMemory` (DOC83) and any cached `ScopeBoundary` keyed to the prior generation.

**Unhappy paths.** *`unavailable`* → scope resolution degrades to `fail_closed_candidate` (§2.4 error path). *`stale` consumed as `healthy`* → a null result is served against an out-of-date population → `scope.population_stale_served_as_fresh`. *`sparse` over-injected* → guarded downstream by DAMS eligibility (§4.4), not here.

**Lints (Stage 9):** `scope.population_stale_served_as_fresh`; `scope.population_unavailable_not_fail_closed`; `scope.null_result_without_population_generation` (ties DOC83 `NullResultMemory` freshness, SM-103/SM-205).

**Fixtures (Stage 8):** `fixture.scope.population_bump_invalidates_null_result`; `fixture.scope.population_unavailable_fails_closed`.

**Cross-charter consumption.** DOC83 (`NullResultMemory` freshness — `scope_population_generation_id`), EC (evaluates health; invalidates caches), DOC84 (null-result delivery). This is a DOC81 → DOC83 consumption (carried on the DOC83 `NullResultMemory` row), not a new edge.

### §2.6 `ScopeSearchCoverageProof` (Adj U24; GPT §4.27)

**Source:** Adj U24 (ACCEPT — DOC81-owned scope-plane object; EC produces); GPT §4.27; Round D §6.3 (`not_searched ≠ not_found`). Closes the Round D rule with a mechanism: `not_found` may be emitted **iff** all required scopes were fully searched.

```typescript
/** Numerator/denominator proof gating not_found vs not_searched. schema_owner = DOC81 (U24). EC produces; DOC86 consumes for notices. */
interface ScopeSearchCoverageProof extends E0DurableRecord {
  proof_id: ScopeSearchCoverageProofRef; schema_owner: 'DOC81';
  request_ref: string; scope_resolution_ref: ScopeResolutionResultRef;
  required_scope_refs: ScopeRef[];     // V21 — EXCLUDES scopes firewalled-relative-to-this-principal + other-principal scopes (else the can't-say-not-found leak)
  searched_scope_refs: ScopeRef[]; not_searched_scope_refs: ScopeRef[];
  coverage_numerator: UnitInterval | number; coverage_denominator: number; coverage_ratio: UnitInterval;   // V17/QF2 + QF6
  result_disposition: 'fully_searched' | 'partially_searched' | 'not_searched' | 'search_unavailable_fail_closed';
  may_emit_not_found: boolean;   // true IFF denominator>0 AND numerator==denominator AND not_searched is empty
  reason_codes: ReasonCodeId[];
}
// Adj V17/QF2 — required uniqueness/subset/disjointness; zero-denominator guard. schema_owner = DOC81.
function computeCoverage(required: ScopeRef[], searched: ScopeRef[]) {
  const req = [...new Set(required)]; const got = [...new Set(searched)].filter(s => req.includes(s));   // dedup; searched ⊆ required
  const not = req.filter(s => !got.includes(s));
  const denominator = req.length, numerator = got.length;
  return { coverage_numerator: numerator, coverage_denominator: denominator, coverage_ratio: denominator === 0 ? 0 : numerator / denominator,
    not_searched_scope_refs: not, may_emit_not_found: denominator > 0 && numerator === denominator && not.length === 0 };
}
```

**Unhappy paths.** *`not_found` on a partial search* → `disclosure.not_found_without_scope_search_coverage_proof` (ties §4.2 `disclosure.not_searched_reported_as_not_found`). *Search unavailable* → `search_unavailable_fail_closed`, never `not_found`. *Zero-bucket count rendered as "none" without this proof* → `disclosure.zero_bucket_count_without_coverage_proof` (V17/QF7 — a bucketed "none" leaks exact absence unless `may_emit_not_found`; else render no count / "not available", §4.2). *Walled scope in `required_scope_refs`* → `disclosure.coverage_proof_includes_undisclosable_scope` (V21 — the permanent-inability-to-say-not-found leak).
**Lints (Stage 9):** `disclosure.not_found_without_scope_search_coverage_proof` (U24); `scope_search.required_scope_refs_not_unique` (QF2); `scope_search.searched_scope_not_subset_of_required` (QF2); `scope_search.searched_and_not_searched_overlap` (QF2); `scope_search.zero_denominator_reported_as_full_coverage` (QF2); `disclosure.zero_bucket_count_without_coverage_proof` (QF7); `disclosure.coverage_proof_includes_undisclosable_scope` (V21). **Fixtures (Stage 8):** `fixture.disclosure.partial_search_cannot_emit_not_found` (U24); `fixture.disclosure.zero_bucket_requires_coverage_proof` (QF7); `fixture.disclosure.walled_scope_excluded_from_coverage_denominator` (V21).
**Cross-charter consumption.** EC (produces), DOC86 (null-result/blocked notices consume it, §9), DOC84 (`null_result_notice` ContextProduct).

---

## §3. Policy plane (E1 — declare)

Policy objects consume scope (§2) and decide capability + disclosure. Source: Round D §1–§2; Owner Map lines 79–85; SM-012/013/014/101/102; Skeletal §DOC81 §2. **Owner:** DOC81. **Executor:** EC's compiled policy evaluator computes every decision and meet (Owner Map lines 79–80; E0 §7.1 `EC §3` compiled policy engine; Round D §1.2). **Retired-name guards:** scalar `MemoryPolicyDecision` (SM-012) and scalar/allow-block `PolicyMembraneDecision` (SM-014) and `ask_user` (SM-102) are retired (Retired Names) — this plane is dimensional, and disambiguation replaces `ask_user`.

### §3.0 `PolicyLattice` — the single typed algebra (Adj U1; CL §2.1 + GPT §4.4) + runtime freshness (U5) + evaluation context (U6)

**Source:** Adj U1 (ACCEPT — adopt CL §2.1 `PolicyLattice` as DOC81 §3.0, GPT §4.4 explicit rank maps as the rank source); CL B2/B3/B6/B7/B11/B16 + NI-5; GPT §4.2/§4.3/§4.4; F1 (malformed axis → floor THAT axis). **Owner:** DOC81 (the algebra module + freshness-key shape + context shape). **Executor:** EC's compiled policy evaluator imports this module; the meet (§3.2), floors (§4.6), coherence, ceilings (§3.4), and DAMS ceiling (§4.4) all call it. This module exists so the meet is typed-lattice calls, not ad-hoc enum logic (NI-5) — it removes every "what should X be here?" guess-point.

```typescript
// DOC81 §3.0  PolicyLattice — schema_owner = DOC81. Total-order chains, ⊥ first; "most restrictive wins" = MIN.
// Rank SOURCE is the explicit AXIS_RANKS map (GPT §4.4) — NEVER enum declaration order (Adj U1).
class PolicyLatticeError extends Error {}

// Adj V17/QF6 — finite [0,1] guard for every confidence / risk-score / coverage-ratio / threshold value (used family-wide).
type UnitInterval = number & { readonly __brand: 'UnitInterval_0_1_finite' };
function asUnitInterval(n: number): UnitInterval { if (!Number.isFinite(n) || n < 0 || n > 1) throw new PolicyLatticeError('value outside [0,1]'); return n as UnitInterval; }

const AXIS_RANKS = {
  content_fidelity:   { none:0, safe_label:1, reference_only:2, redacted:3, full:4 },
  locality:           { blocked:0, local_only:1, approved_external:2 },
  learning_scope:     { none:0, audit_only:1, same_scope_only:2, same_firewall_only:3, partitioned:4, global_allowed:5 }, // +same_firewall_only (U20/F4)
  mutation_authority: { none:0, candidate_only:1, durable_requires_review:2, durable_allowed:3 },
  disclosure_class:   { not_disclosable:0, existence_only:1, generic_safe_label_only:2, redacted_summary:3, full:4 },     // §13.2 order
} as const;

export type ContentFidelityLevel   = keyof typeof AXIS_RANKS.content_fidelity;
export type LocalityLevel          = keyof typeof AXIS_RANKS.locality;
export type LearningScopeLevel     = keyof typeof AXIS_RANKS.learning_scope;   // includes 'same_firewall_only' (U20)
export type MutationAuthorityLevel = keyof typeof AXIS_RANKS.mutation_authority;
export type DisclosureClass        = keyof typeof AXIS_RANKS.disclosure_class;

/** A chain over an explicit rank map. unknown value ⇒ rank() returns -1 ⇒ caller floors to ⊥ (F1, fail-closed). */
export class Chain<T extends string> {
  constructor(private readonly ranks: Record<string, number>, readonly bottom: T, readonly top: T) {}
  isMember(v: string): v is T { return v in this.ranks; }
  rank(v: T): number { const r = this.ranks[v]; if (r === undefined) throw new PolicyLatticeError(`'${v}' not on chain`); return r; }
  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<ContentFidelityLevel>(AXIS_RANKS.content_fidelity, 'none', 'full'),
  locality:   new Chain<LocalityLevel>(AXIS_RANKS.locality, 'blocked', 'approved_external'),
  learning:   new Chain<LearningScopeLevel>(AXIS_RANKS.learning_scope, 'none', 'global_allowed'),
  mutation:   new Chain<MutationAuthorityLevel>(AXIS_RANKS.mutation_authority, 'none', 'durable_allowed'),
  disclosure: new Chain<DisclosureClass>(AXIS_RANKS.disclosure_class, 'not_disclosable', 'full'),
} as const;

// R3 (Adj V1 / CL B.3): PolicyPoint is the FOUR CAPABILITY AXES ONLY. `disclosure_class` LEFT the point — disclosure
// travels solely in the DisclosurePermissionVector (§4.2) and the scalar is derived EXACTLY ONCE at finalize (§3.2 step 6).
// This makes the floor/domain/sticky disclosure-loss bug class (A-B1/Q4) structurally unrepresentable. The disclosure
// CHAIN (LAT.disclosure) stays — used by deriveDisclosureClass, meetDisclosureVectors, the vector partial order (V19), and coherence.
export interface PolicyPoint {
  content_fidelity: ContentFidelityLevel; locality: LocalityLevel; learning_scope: LearningScopeLevel; mutation_authority: MutationAuthorityLevel;
}
export const BOTTOM: PolicyPoint = { content_fidelity:'none', locality:'blocked', learning_scope:'none', mutation_authority:'none' };
export const TOP: PolicyPoint    = { content_fidelity:'full', locality:'approved_external', learning_scope:'global_allowed', mutation_authority:'durable_allowed' };

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) };
}
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);
}

// B6 (coherence): content_fidelity (the only axis that REVEALS the object's content) is capped DOWN to the FINAL disclosure
// ceiling — NEVER by raising disclosure (preserves L1). Coherence is content-only (A2-IDEA-3: locality/mutation/learning are
// orthogonal to disclosure). Takes the derived scalar as an argument now that disclosure_class is not on the point.
export const MAX_CONTENT_FOR_DISCLOSURE: Record<DisclosureClass, ContentFidelityLevel> = {
  not_disclosable:'none', existence_only:'safe_label', generic_safe_label_only:'reference_only', redacted_summary:'redacted', full:'full',
};
export function isCoherent(p: PolicyPoint, dc: DisclosureClass): boolean { return LAT.content.leq(p.content_fidelity, MAX_CONTENT_FOR_DISCLOSURE[dc]); }
export function makeCoherent(p: PolicyPoint, dc: DisclosureClass): PolicyPoint { return { ...p, content_fidelity: LAT.content.clampDown(p.content_fidelity, MAX_CONTENT_FOR_DISCLOSURE[dc]) }; }

// ── Unified policy state (Adj V1 — GPT §15 shape + CL B.3 semantics). Point + vector are met TOGETHER at every pipeline
//    step (§3.2), so a path that tightens one but not the other is impossible. The scalar is derived once at finalize. ──
export interface PolicyEffectiveState {
  point: PolicyPoint;                                  // 4 capability axes
  disclosure_vector: DisclosurePermissionVector;       // §4.2 — the SOLE disclosure carrier through the pipeline
  obligations: PolicyObligationRef[];                  // accumulated; §3.3
  required_attestations: Array<'egress' | 'mfc' | 'obligation_discharge'>;   // fail-safe OR compose (A2-M5)
  applied_floor_refs: ConservatismFloorEffectRef[];    // §4.6.1 audit
  destination_crosswalk_ref?: DestinationPolicyCrosswalkRef;   // §4.6.4 (V6)
  sticky_prior_effective_policy_ref?: EffectiveMemoryPolicyRef; // §3.2 step 4 (V7)
}
export function meetPolicyState(a: PolicyEffectiveState, b: PolicyEffectiveState): PolicyEffectiveState {
  return {
    point: meetPoints(a.point, b.point),
    disclosure_vector: meetDisclosureVectors([a.disclosure_vector, b.disclosure_vector]),   // §4.2 AND-meet (preserves rendering refs; conflict ⇒ drop-to-none, V11)
    obligations: [...new Set([...a.obligations, ...b.obligations])],                                  // D5/M3 — Set-union (dedupe by ref) so NI-1 idempotent/commutative holds on the RAW state, not only the finalized result
    required_attestations: [...new Set([...a.required_attestations, ...b.required_attestations])],   // OR
    applied_floor_refs: [...new Set([...a.applied_floor_refs, ...b.applied_floor_refs])],             // D5/M3 — Set-union (mirrors required_attestations)
    destination_crosswalk_ref: b.destination_crosswalk_ref ?? a.destination_crosswalk_ref,            // order-independent invariant: ≤1 input carries each (egress 0c sets it once)
    sticky_prior_effective_policy_ref: b.sticky_prior_effective_policy_ref ?? a.sticky_prior_effective_policy_ref,
  };
}
export function meetAllStates(ss: readonly PolicyEffectiveState[]): PolicyEffectiveState {           // D5/M4 — empty ⇒ ⊥-state (fail-closed), never a raw TypeError. Caller still guarantees ≥1 (domain always present).
  if (ss.length === 0) return { point: {...BOTTOM}, disclosure_vector: bottomDisclosureVector(), obligations: [], required_attestations: [], applied_floor_refs: [] };
  return ss.reduce(meetPolicyState);
}

// D4 — pipeline helpers made explicit (Adj D4; CL M2 / GPT F-3) so a Stage-7 author cannot guess.
export function toPolicyPoint(d: MemoryPolicyDecision): PolicyPoint {                                 // drops disclosure_class (vector-carried, V1)
  return { content_fidelity: d.content_fidelity, locality: d.locality, learning_scope: d.learning_scope, mutation_authority: d.mutation_authority };
}
export function priorState(e: EffectiveMemoryPolicy): PolicyEffectiveState {                          // carries obligations/attestations/floor-refs (GPT C3 — not just point+vector)
  return {
    point: { content_fidelity: e.effective_content_fidelity, locality: e.effective_locality, learning_scope: e.effective_learning_scope, mutation_authority: e.effective_mutation_authority },
    disclosure_vector: e.effective_disclosure_vector, obligations: e.obligations,
    required_attestations: e.egress_attestation_required ? ['egress'] : [], applied_floor_refs: e.floor_effect_ref ? [e.floor_effect_ref] : [],
    destination_crosswalk_ref: e.destination_crosswalk_ref, sticky_prior_effective_policy_ref: e.effective_policy_id,
  };
}
export function clampPointDown(p: PolicyPoint, c: PolicyPoint): PolicyPoint {                          // per-axis lift of Chain.clampDown (restamp_reeval ceiling clamp)
  return { content_fidelity: LAT.content_fidelity.clampDown(p.content_fidelity, c.content_fidelity), locality: LAT.locality.clampDown(p.locality, c.locality),
           learning_scope: LAT.learning_scope.clampDown(p.learning_scope, c.learning_scope), mutation_authority: LAT.mutation_authority.clampDown(p.mutation_authority, c.mutation_authority) };
}

// CL A-B1: project a scalar disclosure ceiling (from any scalar-only source) into a vector bound, keyed to DISCLOSURE_ALLOWS (§4.2).
// Under V1 the floor/domain/prior carry vectors directly; this remains for any residual scalar-only disclosure source.
export function disclosureVectorCeilingFor(c: DisclosureClass): DisclosurePermissionVector {
  const a = new Set<string>(DISCLOSURE_ALLOWS[c]);                                            // §4.2
  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'),
  };
}

// THE ONE place the scalar is derived (Adj V1; CODEX R2-B1). Derive from the FINAL vector, THEN cohere content LAST.
// Nothing after this may change any disclosure value. Returns the full 5-field effective point for emission.
export function finalizeDisclosureAndCoherence(s: PolicyEffectiveState): { point: PolicyPoint; disclosure_class: DisclosureClass; disclosure_vector: DisclosurePermissionVector } {
  const dc = deriveDisclosureClass(s.disclosure_vector);                                      // §4.2 — never falls through to full (V3)
  return { point: makeCoherent(s.point, dc), disclosure_class: dc, disclosure_vector: s.disclosure_vector };
}
```

**R3 unified-state rebuild (Adj V1 — headline; CL B.3 + GPT §15, merge seam re-derived).** R3 makes the disclosure **vector** the single source of truth: `PolicyPoint` is now the **four capability axes only**; `disclosure_class` left `PolicyPoint`/`BOTTOM`/`TOP`/`meetPoints`/`leqPoint`. Every pipeline step (§3.2) operates on a `PolicyEffectiveState { point, disclosure_vector, … }` met by `meetPolicyState` (point via `meetPoints`, disclosure via `meetDisclosureVectors`), so the floor/domain/sticky **disclosure** ceilings can no longer be dropped (closes A-B1/Q4: the bug class is unrepresentable). The scalar `disclosure_class` is derived **exactly once** at `finalizeDisclosureAndCoherence`, after which `makeCoherent` caps content against it (B6 last). *Seam reconciliation:* GPT's §15 state object + pipeline-function names are adopted; CL's `disclosureVectorCeilingFor` projection (keyed to `DISCLOSURE_ALLOWS`) handles any residual scalar-only source; the floor (`ConservatismFloorEffect.max_disclosure_vector`, §4.6.1), the domain contribution (`contribution_disclosure_vector`, §4.6.2 — V2/Q1), and the sticky prior (`effective_disclosure_vector`) all carry vectors directly, so under V1 they meet into the one vector without projection. The NI-1 laws hold (AND/MIN meets only); the disclosure-axis monotone-down property fixture is extended accordingly (§11). `MemoryPolicyDecision` keeps its declared 5-axis shape (Round D-sourced); its scalar is consistency-guarded against its vector (V10), never independently consumed by the meet.

**F1 — malformed/unknown axis on an *applicable* decision floors THAT axis to ⊥** (Adj F1; CL/GK position, NOT GPT's block-the-whole-action — one corrupt record must not DoS an ordinary request; the floored axis is equally safe). When a chain's `isMember` is false for an applicable decision's axis value, the meet floors that axis to its chain bottom and records `malformed_axis` in `EffectiveMemoryPolicy.excluded_decision_refs` (§3.2) + lint `policy.malformed_applicable_decision_axis_filtered_out` / `policy.axis_missing_not_floored_conservative`. The `BOTTOM`-on-empty rule (NI-5) means the per-axis meet never fails open. **B20 withdrawn (§1):** this module carries NO `composeOutputPolicy(sources)` — privilege is not composable from source privilege; output egress posture is DOC5's, not a DOC81 meet operation.

**`PolicyRuntimeFreshnessKey` (Adj U5; GPT §4.2) — declared by DOC81; components owned by EC/E0 and referenced (no E0 re-declaration, Hard Constraint §3).** `policy_generation_id` alone cannot invalidate a cached artifact after an EC toggle / incognito / compiled-evaluator / registry / vocab change. Every runtime-consumed DOC81 artifact carries this key and is reusable **only on an exact match** of the active key.

```typescript
interface PolicyRuntimeFreshnessKey {                       // U5 — DOC81 composes E0/EC-owned components; declares the composite
  policy_generation_id: PolicyGenerationId;                 // E0 §8.3
  effective_state_generation_id: EffectiveStateGenerationId; // E0 §1.6 / EC §1 effective-state (off-switch/incognito/collection)
  compiled_policy_evaluator_hash: ContentHash;              // EC §3 compiled evaluator version
  domain_profile_registry_version: SchemaVersionRef;        // E0 §2.2
  reason_code_registry_version: SchemaVersionRef;           // E0 §2.1
  safe_label_vocabulary_version?: SchemaVersionRef;         // PropA ApprovedSafeLabelVocabularyRegistry (Owner Map line 88)
}
// Carried on (U5 artifact list): MemoryPolicyDecision, EffectiveMemoryPolicy, PolicyStamp/Scope/Restamp/Invalidation,
// ExtractionRoutePolicyEnvelope, PolicyCappedDAMSInput, SafeLabelDisclosurePolicy, PolicyDisambiguationRequest/Answer,
// PolicyUIExport, SourceExclusionFilterRule, CollectionModeSuppressionGovernance, CollectionSuppressionEvaluation,
// CascadingSourceInvalidation(+Run), ScopeResolutionResult.
```

**`PolicyEvaluationContext` (Adj U6; GPT §4.3) — keys a decision so it cannot bleed across principal/surface/exposure/model/client/interaction/instruction.** Carries the PropA `ExposureContextSchema` ref (PropA-owned, referenced — this makes the §7.3 NEW-01 landing executable), `model_class`/`client_kind`/`interaction_mode` (E0/EC value set, restoring Round D L172–173), and a policy-bounded user-instruction ref.

```typescript
interface PolicyEvaluationContext extends E0DurableRecord {
  evaluation_context_id: PolicyEvaluationContextRef;
  schema_owner: 'DOC81';
  object_ref: MemoryObjectRef; action: MemoryPolicyAction; destination?: E0OutboundDestinationClass;
  principal_ref: PrincipalRef;                              // REQUIRED — never dropped (R-5 multi-principal readiness)
  surface_ref?: SurfaceRef;
  exposure_context_ref: string;                            // PropA ExposureContextSchema instance; DOC81 references, never redefines (NEW-01)
  model_class: 'same_machine_local_model' | 'cloud_model' | 'hybrid_model' | 'non_model_tool' | 'unknown_model_class';
  client_kind: 'interactive_user' | 'background_job' | 'scheduled_job' | 'agent_initiated' | 'connector_adapter';
  interaction_mode: E0InteractionMode;                     // E0/EC-owned value set (§0)
  user_instruction_ref?: string; user_instruction_policy_bound: true;   // V1.3-M8 invariant: a user instruction may NARROW context, never WIDEN any effective axis
  scope_resolution_ref?: ScopeResolutionResultRef;
  freshness_key: PolicyRuntimeFreshnessKey;
  persistence_kind: 'derived_projection'; canonical_source_refs: string[]; rebuild_key: string;   // V13 — per-request context (D-R2 local marker)
  reason_codes: ReasonCodeId[];
}
// Adj V20/U6 — the contract gating decision reuse (the meet's applicable filter, §3.2 step 1, calls this). A decision computed
// under context A may serve request B ONLY when the policy-relevant context dimensions match EXACTLY (a local-interactive decision
// must not be reused for a background agent, etc.). schema_owner = DOC81. Missing/unknown dimension ⇒ NOT compatible (fail-closed).
function contextCompatible(d: { context: PolicyEvaluationContext }, request: { context: PolicyEvaluationContext }): boolean {
  const a = d.context, b = request.context;
  return a.principal_ref === b.principal_ref && a.action === b.action && a.destination === b.destination
      && a.model_class === b.model_class && a.client_kind === b.client_kind && a.interaction_mode === b.interaction_mode
      && a.exposure_context_ref === b.exposure_context_ref;   // any undefined/mismatch ⇒ false ⇒ decision excluded (fail-closed, U6)
}
```

**Applicability rule (U6).** A `MemoryPolicyDecision` is applicable only if `object_ref` + `action` match **and** its `policy_evaluation_context_ref` resolves to a context compatible with the request's principal / surface / exposure_context / model_class / client_kind / interaction_mode / destination / active `PolicyRuntimeFreshnessKey`. Destinationless decisions may tighten non-egress results but may never authorize egress (§3.2).

**Design-rationale note (Adj U33 / NI-2,3,4 — anchors Stage-7 implementers in studied models).** The sticky-restrictive meet (§3.2 step 4) is a **bounded meet-semilattice with a privileged reset** (CRDT-style, NI-2): monotone-meet state is conflict-free under replication, so Phase-2 multi-node sync merges by `meet` with no coordination and `restamp` is the single consensus point — Phase-2 networking readiness at zero Phase-1 cost. Capability+disclosure is an **information-flow label lattice** and "restamp = the only widening path, ceiling-bounded, re-floored by scope" *is* bounded declassification (DIFC / Myers-Liskov, NI-3). The dimensional decision + accumulating obligations + deny-overrides meet *is* **Cedar/XACML obligations + OPA deny-by-default** (NI-4). These are framing notes, not new contracts.

**Lints (Stage 9):** `policy.malformed_applicable_decision_axis_filtered_out` (F1); `policy.axis_missing_not_floored_conservative` (F1); `policy.artifact_missing_effective_state_generation` (U5); `policy.decision_context_not_part_of_key` (U6); `policy.local_interactive_decision_reused_for_background_agent` (U6); `policy.decision_missing_model_or_interaction_context` (U6); `schema.primary_id_not_branded` (U31); `policy.disclosure_class_present_on_policy_point` (V1 — `PolicyPoint` must be 4-axis); `policy.policy_state_point_and_vector_met_separately` (V1 — every step uses `meetPolicyState`, never point-only).

**Fixtures (Stage 8):** `fixture.policy.missing_axis_in_one_applicable_decision_floors_that_axis` (F1); `fixture.policy.application_toggle_invalidates_policy_exports` (U5); `fixture.policy.incognito_toggle_blocks_cached_collection_policy` (U5); `fixture.policy.cloud_model_render_policy_differs_from_local_model_render` (U6); `fixture.propa.exposure_context_changes_policy_decision_key` (U6); `fixture.policy.policy_point_has_no_disclosure_class` (V1); the **NI-1 property-fixture family** (§11; extended to the disclosure axis per V1) exercises the algebra laws over this module.

**Cross-charter consumption.** EC (imports the module; computes the meet), every DOC81 contract (uses the brands + `PolicyPoint` + freshness key + context), DOC84/DOC86 (consume `EffectiveMemoryPolicy` produced by the module — never re-import the algebra to recompute, §9).

### §3.1 `MemoryPolicyDecision` — the 5-axis dimensional vector (SM-012)

**Source:** Round D §1.3–§1.4; SM-012; Owner Map line 79; Opening Brief draft target 3. A policy decision is **action-scoped and dimension-scoped** — never a scalar rank (Round D §1.2; SM-012 retires the scalar). Bare `render` is retired; rendering is action-specific (Round D §1.3).

```typescript
/** The action a policy decision is about. schema_owner = DOC81 (Round D §1.3). Bare `render` retired. */
type MemoryPolicyAction =
  | 'collect' | 'extract' | 'classify' | 'write_candidate' | 'write_durable'
  | 'retrieve' | 'render_inline' | 'render_reference_only' | 'render_safe_label'
  | 'export' | 'delegate' | 'carryover'
  | 'learn_same_scope' | 'learn_partitioned' | 'learn_global'
  | 'ui_disclose' | 'inspect';

/** Dimensional policy decision over ONE (object, action, destination?) triple.
 *  schema_owner = DOC81 (Owner Map line 79; SM-012). EC executes; PropA contributes inputs.
 *  THE FIVE AXES: four capability axes (content_fidelity, locality, learning_scope, mutation_authority)
 *  + one disclosure axis (disclosure_class). Capability meet and disclosure meet are ORTHOGONAL (§4.1; Round D §1.6). */
interface MemoryPolicyDecision extends E0DurableRecord {
  decision_id: MemoryPolicyDecisionRef;
  schema_owner: 'DOC81';
  policy_evaluation_context_ref: PolicyEvaluationContextRef;   // §3.0 (U6) — carries principal/surface/exposure/model_class/client_kind/interaction_mode/scope_resolution
  object_ref: MemoryObjectRef;
  action: MemoryPolicyAction;
  destination?: E0OutboundDestinationClass;     // E0 §22 — REQUIRED for {export,delegate,carryover}; carried on {render_*} when content could leave (B12 / §4.6)

  valid_from: string;                            // B19 (U5c) — bitemporal in-force window (RFC3339-UTC, E0 §8.5)
  valid_to?: string;

  // ---- capability axes (axes 1–4; the §3.0 chains) ----
  content_fidelity: ContentFidelityLevel;
  locality: LocalityLevel;
  learning_scope: LearningScopeLevel;            // includes 'same_firewall_only' (U20/F4 — between same_scope_only and partitioned)
  mutation_authority: MutationAuthorityLevel;
  // ---- disclosure axis (axis 5; the disclosure-meet axis, §4.1) ----
  disclosure_class: DisclosureClass;             // ALWAYS derived from disclosure_vector (deriveDisclosureClass, §4.2) — never independently set (U8)
  disclosure_vector: DisclosurePermissionVector; // §4.2 (U8) — the executable disclosure expansion; the scalar is its summary

  obligations: PolicyObligationRef[];            // §3.3 (typed union, U9)
  freshness_key: PolicyRuntimeFreshnessKey;      // §3.0 (U5) — carries policy_generation_id (E0 §8.3) + effective-state/evaluator/registry/vocab versions
  reason_codes: ReasonCodeId[];                  // E0 §2.1
}
```

**R2 note (U5/U6/U8/U20).** The context fields (`principal_ref`, `surface_ref`, `model_class`, `client_kind`, `interaction_mode`, `exposure_context_ref`, `scope_resolution_ref`) move onto `PolicyEvaluationContext` (§3.0) so a local/interactive decision can never be reused for a background/cloud evaluation; `model_class`/`client_kind` are thereby restored (Round D L172–173 / CL M2). `policy_generation_id` is carried *inside* `freshness_key` (§3.0) — the bare field is gone, killing the "generation alone can't invalidate" gap (B2/U5). `disclosure_vector` (§4.2) is the executable disclosure model; the scalar `disclosure_class` is **always derived from it** (U8). `learning_scope` gains `same_firewall_only` (U20/F4) for firewalled-source learning signals.

**The five axes, named (Opening Brief draft target 3; Round D §1.4).** *Axis 1 — `content_fidelity`:* how much of the object's content may be used. *Axis 2 — `locality`:* how far it may travel. *Axis 3 — `learning_scope`:* whether/where it may train learning. *Axis 4 — `mutation_authority`:* whether it may be durably written. *Axis 5 — `disclosure_class`:* what may be *said about* it. Axes 1–4 are the **capability** axes; axis 5 is the **disclosure** axis. Their separation is enforced in §4.1.

**Per-axis restrictiveness order (the lattice; DOC81-defined, §4.2 uses it).** Each axis is a total order (a chain) from most-restrictive (⊥) to least-restrictive (⊤):

```text
content_fidelity:    none ⊏ safe_label ⊏ reference_only ⊏ redacted ⊏ full
locality:            blocked ⊏ local_only ⊏ approved_external
learning_scope:      none ⊏ audit_only ⊏ same_scope_only ⊏ same_firewall_only ⊏ partitioned ⊏ global_allowed   // +same_firewall_only (U20/F4)
mutation_authority:  none ⊏ candidate_only ⊏ durable_requires_review ⊏ durable_allowed
disclosure_class:    not_disclosable ⊏ existence_only ⊏ generic_safe_label_only ⊏ redacted_summary ⊏ full
```

The `disclosure_class` order is a DOC81 refinement of Round D §1.4's *declaration* order (which was unordered): `existence_only` ⊏ `generic_safe_label_only` because a generic safe label discloses existence **plus** an approved category hint, strictly more than bare existence. (Flagged decisively in §13.2.) Every chain's ⊥ is the conservative fallback target (§4.6).

**Lifecycle.** A decision is `computed (EC, under a policy_generation_id) → contributes to a meet (§3.2) → invalidated` on policy-generation change (§6.4). A decision is never mutated in place; a new generation produces a new decision.

**Unhappy paths.** *Scalar usage* — any consumer collapsing the vector to one rank → `policy.scalar_decision_used` (SM-012 retired). *Bare `render`* → `policy.bare_render_action` (plan §17.4; Round D §1.3). *`export`/`delegate`/`carryover` without `destination`* → reject (the action requires a destination; §3.3). *Missing axis* → that axis resolves to ⊥ (fail-closed, §4.6), never to ⊤. *Cross-action meet* — a retrieval decision constraining export, or vice versa → blocked (§3.2 keys by action; Round D §1.5).

**Lints (Stage 9):** `policy.scalar_decision_used` (SM-012); `policy.bare_render_action` (plan §17.4); `policy.action_without_required_destination`; `policy.axis_missing_not_floored_conservative`; `policy.undefined_policy_obligation` (plan §17.4 — an `obligations` ref that resolves to nothing).

**Fixtures (Stage 8):** `fixture.policy.five_axes_present_and_independent`; `fixture.policy.missing_axis_resolves_bottom`; `fixture.policy.export_requires_destination`.

**Cross-charter consumption.** EC (computes), PropA (contributes inputs — sensitivity tags/findings/source rules → axis values, §7.3), DOC82/DOC83 (write/extraction gating), DOC84 (delivery caps), DOC86 (disclosure surfacing of axis 5). Every member that gates a memory movement consumes this contract; none redefines it.

### §3.2 `EffectiveMemoryPolicy` — the meet schema + meet algorithm

**Source:** Round D §1.5; SM-013; Owner Map line 80 (**wholly DOC81**; E0 §3.2 holds only the consumption-protocol pointer); Opening Brief draft target 4; Skeletal §DOC81 §2. **Owner:** DOC81. **Executor:** **EC executes the meet** (Owner Map line 80). **Bind:** the `EffectiveMemoryPolicyRef` brand is E0's pointer (E0 §3.2) — DOC81 declares the schema it points at; it does not re-declare the pointer.

```typescript
/** The per-(object, action, destination?) effective policy — the MEET. schema_owner = DOC81
 *  (Owner Map line 80 / SM-013; wholly DOC81). EC executes the meet. NOT a meet across actions. */
interface EffectiveMemoryPolicy extends E0DurableRecord {
  effective_policy_id: EffectiveMemoryPolicyRef;   // E0 §3.2 brand — consumed by MemoryContextPlan / proofs / MFCs
  schema_owner: 'DOC81';
  object_ref: MemoryObjectRef;
  action: MemoryPolicyAction;
  destination?: E0OutboundDestinationClass;
  policy_evaluation_context_ref: PolicyEvaluationContextRef;   // §3.0 (U6) — the context the meet was keyed under

  contributing_decision_refs: MemoryPolicyDecisionRef[];       // §3.1 — the applicable inputs that were met
  excluded_decision_refs: Array<{                              // U1/F1/U6 — every excluded input + WHY (auditability)
    decision_ref: MemoryPolicyDecisionRef;
    exclusion_reason:
      | 'wrong_object' | 'wrong_action' | 'wrong_destination' | 'wrong_principal' | 'wrong_surface'
      | 'wrong_exposure_context' | 'wrong_model_class' | 'wrong_client_kind' | 'wrong_interaction_mode'
      | 'stale_generation' | 'stale_effective_state' | 'malformed_axis';
  }>;

  // ---- effective capability axes (PolicyEffectiveState.point, per-dimension most-restrictive meet, §3.0) ----
  effective_content_fidelity: ContentFidelityLevel;
  effective_locality: LocalityLevel;
  effective_learning_scope: LearningScopeLevel;
  effective_mutation_authority: MutationAuthorityLevel;
  // ---- effective disclosure (the vector is the source of truth; scalar derived ONCE at finalize, §3.0/§3.2 step 6) ----
  effective_disclosure_class: DisclosureClass;                  // DERIVED from effective_disclosure_vector (V1/U8) — never set independently
  effective_disclosure_vector: DisclosurePermissionVector;      // §4.2 (U8) — vector AND-meet through every pipeline step (V1)

  conservatism_floor_applied?: ScopeConservatismFloor;          // §4.6
  floor_effect_ref?: ConservatismFloorEffectRef;                // §4.6 (U2) — the floor's per-axis effect that was met in
  destination_crosswalk_ref?: DestinationPolicyCrosswalkRef;    // §4.6.4 (V6) — the crosswalk row consumed at step 0c
  egress_attestation_required: boolean;                         // V6 — fail-safe OR over crosswalk/closure/predicate (A2-M5)
  egress_attestation_ref?: E0EgressAttestationRef;              // E0 §22 — the attestation produced downstream (EC/DOC24)
  sticky_prior_effective_policy_ref?: EffectiveMemoryPolicyRef; // V7 — the sticky prior met in at step 4 (ordinary mode)
  sticky_prior_effective_policy_hash?: ContentHash;             // V7 — part of the cache key (§6.6)
  meet_mode: 'ordinary' | 'restamp_reeval';                     // V5 — restamp re-eval skips sticky + clamps to root ceiling
  meet_kind: 'per_dimension_most_restrictive';                  // the ONLY legal meet kind (Round D §1.5; ranked-union forbidden — L2)
  meet_algorithm_version: 'doc81.meet.v2';                      // pins the §3.2 ordered pipeline

  obligations: PolicyObligationRef[];                           // §3.3 — accumulated; never dropped under meet
  obligation_conflict_refs: PolicyObligationConflictRef[];      // §3.3 (U9) — genuinely-contradictory residue → fail-closed
  /** V11/A2-IDEA-1 — per-axis binding-constraint attribution: which input bound each axis (the Inspector "why?" feed, §9). One assignment per axis during the meet.
   *  PRESENCE CONDITION: present iff EC is recording attribution (Inspector/audit build); when present it MUST cover every axis the meet tightened (no partial attribution). */
  binding_constraint?: Partial<Record<PolicyAxis, { source_kind: 'decision' | 'domain' | 'floor' | 'crosswalk' | 'obligation' | 'prior' | 'coherence'; ref?: string; reason_code: ReasonCodeId }>>;
  action_permission_result?: ActionPermissionResult;           // §4.7 (V23) — step-7 outcome. PRESENCE CONDITION: present on every assembled (non-blocked) result; absent only on a blockedBottomState short-circuit before step 7
  original_ceiling_ref?: PolicyCeilingRef;                      // §3.4 restamp cap (ADQ-316)
  freshness_key: PolicyRuntimeFreshnessKey;                     // §3.0 (U5) — carries policy_generation_id (E0 §8.3)
  persistence_kind: 'derived_projection'; canonical_source_refs: string[]; rebuild_key: string;   // V13 — the meet result is rebuildable from decisions+context+scope (D-R2 local marker)
  reason_codes: ReasonCodeId[];                                 // populated per tightening step (auditability)
}
```

**The meet algorithm (Opening Brief draft target 4; Acceptance criterion "the meet algorithm is specified").** The effective policy for a triple `(object_ref, action, destination?)` is computed by EC as follows. It is **monotone-restrictive**, **conservative on missing/incomparable**, and **keyed by action** (never a meet across actions, Round D §1.5):

The pipeline is `doc81.meet.v2` (Adj U1/U5/U6/U7/U9; CL §2.4 + GPT §4.6). **The step order 0→7 is normative** (B16 — coherence is LAST among tightening steps, before the step-7 action-predicate gate; D7); within each step `MIN` + vector AND-meet + set-union are commutative/associative/idempotent, so the meet is order-independent (NI-1 property fixtures, §11).

```text
function meet_v2(request, decisions[], scopeResolution, activeEpoch, priorEffective?, mode='ordinary'):  # all import §3.0; R3 rebuild (Adj V1)
  g = activeEpoch.policy_generation_id
  try:   # V10/A-S2: ANY PolicyLatticeError ⇒ fail closed.

    # 0. Generation/effective-state precondition (B4/U5). Active epoch in force at request.effectiveTime (B19).
    if scopeResolution.freshness_key != request.freshness_key:   return RE_PLAN_REQUIRED('stale_effective_state')
    if request.freshness_key.policy_generation_id != g:          return RE_PLAN_REQUIRED('generation_mismatch')

    # 0b. Egress STRUCTURAL precondition (V8/A-S1) — destination present+typed, BEFORE applicable is built.
    if request.action in {export, delegate, carryover}:
       if not (request.destination present AND request.destination ∈ E0OutboundDestinationClass):
          return blockedBottomState('policy.destination_required_action_without_destination')

    # 0c. Destination-crosswalk CONSUMPTION (V6/A-S3/Q2) — the ONLY consumption point of §4.6.4; render-may-leave included.
    destination_floor = 'normal_policy_check'; egress_attestation_required = false; crosswalk_ref = none
    if request.action in {export, delegate, carryover} OR (request.action startsWith 'render' AND request.destination present):
       cw = resolveDestinationPolicyCrosswalk(scopeResolution.relation_to_destination, request.destination)   # §4.6.4
       crosswalk_ref = cw.crosswalk_id
       if cw.disposition in {'block','block_until_terminal_destination_resolved'}: return blockedBottomState('egress.destination_crosswalk_block', cw.reason_codes)
       if cw.allowed_outbound_destination_classes and request.destination ∉ cw.allowed_outbound_destination_classes: return blockedBottomState('egress.destination_class_not_allowed_for_relation')
       destination_floor = cw.policy_floor; egress_attestation_required = cw.egress_attestation_required

    # 1. Applicable filter: context-compatible (U6) AND in force (B19); F1 malformed axis + V10 scalar/vector-mismatch guard.
    applicable = []; forceBottom = {}
    for d in decisions:
       if !contextCompatible(d, request) or d.freshness_key != request.freshness_key: excluded.add(d, reason); continue
       if !isInForce(d, request.effectiveTime):                                        excluded.add(d, 'stale_generation'); continue
       for axis in FOUR_CAPABILITY_AXES: if !LAT[axis].isMember(d[axis]): forceBottom[axis]=true; excluded.note(d,'malformed_axis')   # F1
       if d.disclosure_class != deriveDisclosureClass(d.disclosure_vector): excluded.add(d,'malformed_disclosure_vector'); continue   # D6/M5 — EXCLUDE the self-contradictory decision (F1-aligned: one corrupt record must not DoS the object's disclosure); meet proceeds on valid inputs (domain always present)
       applicable.add(d)

    # 1c. Empty-applicable INTERNAL baseline (V4a/GPT B1): empty + same-principal INTERNAL action + no R-1 basis ⇒ domain-profile-only
    #     baseline (reason_code policy.domain_profile_only_internal_baseline) — NOT a fifth hard-block basis (R-1, §4.0).
    #     Hard block RETAINED for egress / write_durable / learn_* / delegate / carryover / cross-principal.
    if applicable is empty:
       if isInternalAction(request.action) and request.principal == scopeResolution.object_owner_principal and not scopeResolution.conservatism_floor_basis.r1_qualifying:
          use_domain_profile_only_internal_baseline = true           # D4 — applicable stays []; step 2's always-present domainContribution IS the baseline (reason policy.domain_profile_only_internal_baseline at assemble). Removes the fake-decision type mismatch (GPT C3).
       else: return blockedBottomState('policy.no_applicable_decision')   # fail-closed

    # 1b. Egress APPLICABLE precondition (V8/A-S1) — AFTER the filter: ≥1 applicable destination-matching decision.
    if request.action in {export, delegate, carryover}:
       if not applicable.any(d => d.destination == request.destination): return blockedBottomState('policy.egress_authorized_by_destinationless_decision')

    # 2. Meet ALL inputs as PolicyEffectiveState — point + DISCLOSURE VECTOR met TOGETHER (V1). Domain contribution is always
    #    present (B3) AND carries its own vector (V2/Q1) ⇒ its disclosure ceiling can no longer be overwritten at finalize.
    domain = domainContribution(request)                               # §4.6.2 — { point: PolicyPoint(4), disclosure_vector } (V2/A2-B5)
    inputs = [ domain, ...applicable.map(d => ({ point: toPolicyPoint(d), disclosure_vector: d.disclosure_vector, obligations:[], required_attestations:[], applied_floor_refs:[] })) ]
    eff = meetAllStates(inputs)                                        # §3.0 — never empty (domain always present)
    for axis where forceBottom[axis]: eff.point[axis] = LAT[axis].bottom            # F1 applied (capability axes; self-contradictory disclosure decisions were already EXCLUDED at step 1 per D6)

    # 3. Conservatism floor — ACTION-AWARE (V4b/A-B3 + A2-S12): EGRESS actions get the rule-3 floor; INTERNAL actions floor ONLY
    #    on one of the R-1 four bases (from scopeResolution.conservatism_floor_basis.r1_qualifying). combined_floor = meetFloors([scope, crosswalk]).
    floorEff = FLOOR_EFFECT(meetFloors([scopeResolution.minimum_conservatism_floor, destination_floor]))   # §4.6.1 (V6)
    if isEgressAction(request.action) OR (request.action startsWith 'render' AND request.destination present) OR scopeResolution.conservatism_floor_basis.r1_qualifying:   # D1 — render-may-leave applies the 0c crosswalk floor too (was egress|r1 only ⇒ external render was attested-but-unfloored)
       eff = meetPolicyState(eff, { point: floorEff.point, disclosure_vector: floorEff.max_disclosure_vector, obligations:[], required_attestations:[], applied_floor_refs:[floorEff.effect_id] })   # A-B1: floor vector meets in
    # else INTERNAL non-R-1 action: NO floor raise (R-1, §4.0) — cross-matter relevance handled by DAMS contamination ranking (§4.4), never a floor.

    # 4. Sticky-restrictive — ORDINARY mode ONLY (V5/A-B4): clamp point AND disclosure vector to priorEffective ⇒ input removal cannot widen (L1).
    #    restamp_reeval mode skips sticky and clamps to MIN(root ceiling, current scope floor) so autonomous restore-up-to-ceiling is reachable (R-2).
    if mode == 'ordinary' and priorEffective:
       eff = meetPolicyState(eff, priorState(priorEffective)); eff.sticky_prior_effective_policy_ref = priorEffective.effective_policy_id
    if mode == 'restamp_reeval':
       ceil = rootCeilingItems(request)                               # §3.4 PolicyCeilingSnapshot for (object,action,destination)
       eff.point = clampPointDown(eff.point, ceil.point); eff.disclosure_vector = meetDisclosureVectors([eff.disclosure_vector, ceil.max_disclosure_vector])

    # 5. Obligations: resolve + conflicts; obligation disclosure ceilings tighten the VECTOR only (CODEX R2-B1; never an independent scalar).
    obligations = resolveObligations(applicable.flatMap(d => d.obligations))        # §3.3
    conflicts   = detectObligationConflicts(obligations)
    if conflicts.any(blocking): return blockedBottomState('policy.obligation_conflict', conflicts)
    ov = obligationDisclosureVectorCeilings(obligations)                            # §3.3 OBLIGATION_DISCLOSURE_VECTOR_CEILING
    if ov not empty: eff.disclosure_vector = meetDisclosureVectors([eff.disclosure_vector, ...ov])
    # CAPABILITY-axis obligations are NOT runtime-reconciled here (D-R4): EC rejects an axis-vs-blocking-obligation contradiction at
    #   EMISSION (V14 lint policy.capability_obligation_contradicts_axis); the blocking obligation governs over the axis at the
    #   consumption point (V14 precedence line). Runtime step-5b deferred to Phase-2 DOC5 non-EC producers.

    # 6. Finalize: derive the scalar from the FINAL vector (ONCE), THEN cohere content LAST. Nothing after may change any disclosure value.
    final = finalizeDisclosureAndCoherence(eff)                                     # §3.0 — { point, disclosure_class, disclosure_vector }

    # 7. Action closure + predicate gate (V23/QF17). Carried on EffectiveMemoryPolicy.action_permission_result.
    closure = ACTION_CLOSURE[request.action]; require all closure.required_policy_actions have effective_policy_refs    # §4.7
    permission = evaluateActionPredicate(final, eff, ACTION_PERMISSION_PREDICATE[request.action])
    if permission.disposition == 'blocked': return blockedBottomState(permission.reason_code)

    return OK(assemble(final, eff, obligations, conflicts, crosswalk_ref, (egress_attestation_required OR permission.requires_egress_attestation), permission, mode, request))
  catch (PolicyLatticeError e): return blockedBottomState('policy.lattice_error_failed_closed')   # V10/A-S2 — fail closed on any lattice error
```

**Pipeline helper definitions (Adj D1/D4 — referenced above; defined here so Stage-7 cannot guess).**
```text
# Action partition (D1; exhaustive + mutually exclusive over MemoryPolicyAction). A render WITH a destination is egress-for-floor (the step-3 clause handles it).
isEgressAction(a)   := a in { export, delegate, carryover }
isInternalAction(a) := a in { retrieve, classify, inspect, ui_disclose, collect, extract, render_inline, render_reference_only, render_safe_label }   # render WITHOUT a destination
# FLOOR_EFFECT projection (D4/M1) — project the §4.6.1 ConservatismFloorEffect row for `floor` to the shape step 3 reads:
FLOOR_EFFECT(floor) := { point: { content_fidelity: e.max_content_fidelity, locality: e.max_locality, learning_scope: e.max_learning_scope, mutation_authority: e.max_mutation_authority },
                         max_disclosure_vector: e.max_disclosure_vector, effect_id: e.effect_id }     # e = the §4.6.1 row for `floor`
# rootCeilingItems (D4) — the root PolicyCeilingSnapshot item for THIS (object, action, destination?), exact match:
rootCeilingItems(request) := the §3.4 PolicyCeilingSnapshot.ceiling_items entry matching (request.object_ref, request.action, request.destination?);
                             missing ⇒ blockedBottomState('restamp.missing_root_ceiling_item')   # exposes .point + .max_disclosure_vector
# domainBaselineDecision is REMOVED (D4): step 1c sets use_domain_profile_only_internal_baseline=true; the always-present domainContribution(request) (step 2) IS the baseline.
# toPolicyPoint / priorState / clampPointDown / meetAllStates(empty) are defined in §3.0.
```

Four laws fall out and are enforced as invariants (§6.3): **(L1) monotonic restriction** — adding *or removing* any input can only lower (never raise) an effective axis (made true over the full op set by the step-4 sticky-restrictive meet, B7); **(L2) no ranked union** — a scalar union over decisions is forbidden (`meet_kind` is the only legal kind); **(L3) action independence** — a permissive retrieval decision must not constrain export, and a restrictive export decision must not over-block same-scope retrieval (Round D §1.5; the meet is keyed by action); **(L4) widening only via restamp** — the sole way an effective axis rises is `PolicyStampRestamp` (§3.4), and it cannot exceed `original_ceiling_ref` (ADQ-316). Obligations **accumulate** under meet — never dropped; genuinely-contradictory residue fails the action closed (U9), it is never silently merged.

**Action ↔ exposure-context ↔ relation consistency (Adj V22/A3-S14 — protects the headline cross-task injection feature from the egress tax; coheres with V4/V6).** Three orthogonal classifiers touch the same movement — the **action** (§3.1), `relation_to_destination` (§2.4), and PropA's `ExposureContextSchema` (§7.3). R3 pins their mapping normatively: **internal same-principal memory movement** — cross-task / cross-episode context hand-off, **including 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 (§4.6.4) affirm "internal, no egress gate," they are not outbound classes. This kills the latent `carryover`-with-internal-relation contradiction (an internal relation has no outbound class → would be untypeable → blocked) and ensures my-Matter-A context injected into my-Matter-B episode is permitted internally (contamination-ranked), not blocked by the firewall. **Lints:** `policy.internal_injection_routed_through_egress_carryover`; `policy.carryover_with_internal_relation_to_destination`. **Fixtures:** `fixture.policy.explicit_memory_attach_is_internal_retrieve_not_carryover`; `fixture.policy.same_principal_context_handoff_not_egress_gated`.

**Lifecycle.** `computed (EC) → carried on plan/proof/cert as EffectiveMemoryPolicyRef → invalidated` on policy-generation change → `restamped | downgraded | blocked` (§3.4). It is consumed by E0 `MemoryContextPlan.effective_policy_ref` (§3.2), `ContextPacketProof` (E0 §4.1), and every `MemoryFlowCertificate.effective_policy_ref` (E0 §3.3) — DOC81 supplies the schema those E0 contracts point at.

**Unhappy paths.** *Empty applicable set* → every axis ⊥ (fully fail-closed). *Ranked-union attempt* → `policy.ranked_union_over_scalar_results`. *Cross-action meet* → `policy.meet_across_actions`. *Floor undercut* → `policy.effective_undercut_scope_floor`. *Unresolved policy ref* → `policy.unresolved_policy_ref` (plan §17.4). *Widening without restamp* → `monotonicity.policy_widened_without_restamp` (E0 §12.1; §6.3).

**Lints (Stage 9):** `policy.ranked_union_over_scalar_results` (Round D §1.5; L2); `policy.meet_across_actions` (L3); `policy.effective_undercut_scope_floor`; `policy.unresolved_policy_ref` (plan §17.4); `policy.obligation_dropped_under_meet`; `monotonicity.policy_widened_without_restamp` (E0 §12.1); `monotonicity.effective_widened_on_input_removal_without_restamp` (B7/U1 — sticky-restrictive); `policy.meet_inputs_cross_generation` (B4/U5); `policy.meet_included_out_of_force_decision` (B19/U5); `policy.no_applicable_decision_not_fail_closed` (U1); `policy.malformed_axis_not_failed_closed` (F1); `policy.egress_authorized_by_destinationless_decision` (U7); `policy.destination_required_action_without_destination` (U7); `policy.capability_exceeds_disclosure_ceiling` (B6/coherence); `policy.obligation_conflict_not_failed_closed` (U9); `policy.scalar_disclosure_set_independently_of_vector` (U8; CODEX R2-B1 — any disclosure value changed after step 6 is a violation); `policy.floor_or_domain_or_prior_disclosure_ceiling_not_applied_to_vector` (V1/A-B1); `egress.destination_crosswalk_not_consulted` (V6/A-S3); `cache.effective_policy_reused_across_different_sticky_prior` (V7); `policy.egress_precondition_checked_before_applicable_filter` (V8/A-S1); `policy.internal_use_floor_raised_without_qualifying_basis` (V4b/A-B3 — internal action floored on a basis outside the R-1 four); `policy.empty_applicable_internal_hard_blocked_without_r1_basis` (V4a); `policy.restamp_reeval_applied_sticky_restrictive` (V5/A-B4 — restamp must not re-clamp to prior); `policy.lattice_error_not_failed_closed` (V10/A-S2); `policy.decision_scalar_vector_mismatch_not_failed_closed` (V10); `policy.action_predicate_not_evaluated` (V23/QF17); `egress.render_with_destination_floor_not_applied` (D1 — a render that consulted the 0c crosswalk floor but did not apply it). **D1 fixture:** `fixture.egress.render_may_leave_applies_crosswalk_floor` (a `render_inline` to an allowed external class is floored, not just attested). **D6 fixture:** `fixture.policy.self_contradictory_decision_excluded_not_object_bottomed` (a scalar/vector-mismatched decision is dropped; the object's disclosure is decided by the remaining valid inputs).

**Fixtures (Stage 8):** `fixture.policy.meet_is_per_dimension_most_restrictive`; `fixture.policy.empty_inputs_fail_closed`; `fixture.policy.retrieval_decision_does_not_constrain_export` (L3); `fixture.policy.obligations_accumulate_under_meet`; `fixture.policy.malformed_axis_in_applicable_decision_floors_axis` (F1); `fixture.egress.destinationless_policy_decision_cannot_authorize_export` (U7); `fixture.golden.policy_tighten_between_plan_and_render_blocks_or_restamps` (E0 §12.1 negative fixture, §10); `fixture.policy.scope_floor_disclosure_ceiling_survives_scalar_derivation` (V1/A-B1); `fixture.property.disclosure_monotone_down_under_floor_domain_and_prior` (V1 — the disclosure-axis extension of the NI-1 monotone-down property); `fixture.egress.crosswalk_floor_and_attestation_enforced_in_meet` (V6); `fixture.cache.sticky_prior_effective_policy_changes_cache_key` (V7); `fixture.policy.cross_matter_internal_retrieve_not_floored` (V4b/A-B3); `fixture.policy.empty_applicable_internal_uses_domain_baseline_not_block` (V4a); `fixture.policy.autonomous_restamp_restores_up_to_ceiling_after_floor_lift` (V5/A-B4); `fixture.policy.lattice_error_fails_closed` (V10); `fixture.policy.decision_scalar_vector_mismatch_bottoms_disclosure` (V10); + the **NI-1 property fixtures** for L1–L4 (§11; extended to the disclosure axis per V1).

**Cross-charter consumption.** EC (executes the meet), DOC84 (`MemoryContextPlan.effective_policy_ref` + `PolicyCappedDAMSInput`, E0 §3.2 / §4.4), DOC82 (write gating), DOC85 (`learning_scope` gates learning eligibility), DOC86 (`disclosure_class` export, §9). The `EffectiveMemoryPolicyRef` brand is the single most-consumed DOC81 contract across the family.

### §3.3 `PolicyMembraneDecision` + `PolicyObligation`

**Source:** Round D §1.7, §19.1; SM-014; Owner Map lines 81–82; Opening Brief draft target 5. **`PolicyMembraneDecision`** is the **membrane-crossing decision only** (SM-014 retires the allow/block scalar) — distinct from the per-dimension `MemoryPolicyDecision`. It records whether a movement may cross a scope membrane (boundary), and is the policy companion to `ScopeBoundary` (§2.2).

```typescript
/** The decision to cross a scope membrane (boundary). schema_owner = DOC81 (Owner Map line 81; SM-014).
 *  Membrane crossing ONLY — NOT a per-dimension capability (that is MemoryPolicyDecision, §3.1).
 *  EC executes the membrane-crossing decision. */
interface PolicyMembraneDecision extends E0DurableRecord {
  membrane_decision_id: PolicyMembraneDecisionRef;
  schema_owner: 'DOC81';
  object_ref: MemoryObjectRef;
  boundary_ref: ScopeBoundaryRef;                 // §2.2 — the membrane being crossed
  action: MemoryPolicyAction;                     // the movement attempting the crossing
  crossing_disposition:
    | 'crossing_permitted'
    | 'crossing_permitted_with_obligations'
    | 'crossing_reference_only'
    | 'crossing_blocked'
    | 'crossing_requires_disambiguation';         // → PolicyDisambiguationRequest (§3.5)
  effective_policy_ref: EffectiveMemoryPolicyRef;  // §3.2 — the membrane decision is consistent with the meet
  obligations: PolicyObligationRef[];
  policy_generation_id: PolicyGenerationId;
  persistence_kind: 'derived_projection'; canonical_source_refs: string[]; rebuild_key: string;   // V13 — derived from the effective policy (U22; D-R2 local marker)
  reason_codes: ReasonCodeId[];
}

/** Expanded obligation kinds (Adj U9; Round D §1.7 + the PropA / DOC73 V1.5.1 fold-in kinds, §7.3).
 *  DOC8 remains EXCLUDED as an owner (ADQ-221; E0 §7.3) — learning routes to DOC85. */
type PolicyObligationKind =
  | 'require_redaction' | 'require_safe_label' | 'require_user_confirmation'
  | 'require_restamp_before_action' | 'require_audit_certificate' | 'require_final_prompt_proof'
  | 'forbid_global_learning' | 'forbid_cross_scope_relation_traversal'
  | 'require_source_sandbox_isolation' | 'require_prompt_injection_isolation'
  | 'require_review_queue' | 'require_ui_warning'
  | 'hide_existence' | 'show_generic_existence_only'
  | 'partition_learning' | 'receipt_required'                          // Round D §1.7
  // ---- PropA / DOC73 V1.5.1 fold-in kinds (make §7.3 executable; U9/U20) ----
  | 'require_migration_plan' | 'route_manual_review_only'              // V15-02
  | 'require_verifier_calibration_current' | 'exclude_uncalibrated_verifier_signals'  // V15-03
  | 'forbid_sealed_source_fixture_generation' | 'restrict_firewalled_fixture_generation_to_same_firewall'  // V15-04
  | 'discard_sealed_learning_signals' | 'restrict_firewalled_learning_to_same_firewall'  // V15-01
  | 'require_dspy_target_eligibility'                                  // DSPY-TARGETS
  | 'require_post_retrieval_critique_policy_gate'                      // V15-05
  | 'require_counterfactual_prompt_policy_gate' | 'require_additive_synthesis_prompt_policy_gate';  // V15-06

/** DOC84/DOC86 are valid owners (U9/GPT §4.11.2) — delivery/render + UI/Inspector surfaces discharge obligations.
 *  DOC8 is NOT a member (ADQ-221). */
type PolicyObligationEnforcementOwner =
  | 'EC' | 'PropA' | 'DOC24' | 'KDA' | 'DOC84' | 'DOC86' | 'DOC11' | 'DOC20' | 'DOC1' | 'DOC25' | 'DOC73' | 'DOC85';

/** Typed obligation union (U9). schema_owner = DOC81 (Owner Map line 82). EC executes; named owner discharges. */
interface PolicyObligationBase extends E0DurableRecord {
  obligation_id: PolicyObligationRef;
  schema_owner: 'DOC81';
  obligation_kind: PolicyObligationKind;
  applies_to_actions: MemoryPolicyAction[];
  enforcement_owner: PolicyObligationEnforcementOwner;
  blocking: boolean;
  reason_code: ReasonCodeId;                       // E0 §2.1
}
// Adj V9 (GPT Q5/B3/№6 — merge seam re-derived): a PARAMETER MAP so EVERY PolicyObligationKind has a typed contract and a future
// kind cannot drift in untyped. `hide_existence`/`show_generic_existence_only` are now concrete members (they key the
// OBLIGATION_DISCLOSURE_VECTOR_CEILING table below). Lint `policy.obligation_kind_without_typed_parameter_contract`.
type PolicyObligationParametersByKind = {
  // disclosure obligations (drive the §3.2-step-5 vector ceilings):
  hide_existence: { disclosure_ceiling: 'bottomDisclosureVector' };
  show_generic_existence_only: { count_disclosure_mode: 'none' | 'bucketed'; source_title_allowed: false };
  require_safe_label: { safe_label_policy_ref: SafeReferenceLabelRef; disclosure_ceiling: 'generic_safe_label_only'; allow_existence_disclosure_only_if_policy_allows: true };   // CL A-M4: ≤ generic_safe_label_only vector ceiling
  // content / mutation / movement obligations:
  require_redaction: { redaction_map_required: true; redaction_map_ref?: string };
  require_user_confirmation: { confirmation_context: 'egress' | 'wall_crossing' | 'policy_disambiguation'; prompt_must_be_safe_label: true };
  require_restamp_before_action: { required_before_actions: MemoryPolicyAction[] };
  require_audit_certificate: { certificate_kind: 'memory_flow_certificate' };
  require_final_prompt_proof: {};
  require_source_sandbox_isolation: {}; require_prompt_injection_isolation: {};
  require_ui_warning: { warning_kind: string };
  receipt_required: { receipt_kind: 'memory_flow_certificate' | 'egress_attestation' | 'obligation_discharge' | 'ui_notice_receipt' };
  require_review_queue: { queue_kind: 'privacy_review' | 'schema_migration_review' | 'policy_conflict_review' | 'source_revocation_review' };
  route_manual_review_only: { queue_kind: 'privacy_review' | 'schema_migration_review' | 'policy_conflict_review' | 'source_revocation_review' };
  // learning obligations (capability-axis; reconciled at emission per V14/D-R4, not runtime):
  forbid_global_learning: {}; partition_learning: {}; discard_sealed_learning_signals: {};
  restrict_firewalled_learning_to_same_firewall: { firewall_scope_ref: ScopeRef };
  forbid_cross_scope_relation_traversal: {};
  // PropA / DOC73 V1.5.1 fold-in obligations (§7.3):
  require_migration_plan: { schema_migration_plan_ref?: string; if_missing: 'route_manual_review_only' };       // V15-02
  require_verifier_calibration_current: { verifier_calibration_ledger_ref?: string; if_uncalibrated_or_drifting: 'exclude_verifier_signal' };   // V15-03
  exclude_uncalibrated_verifier_signals: {};                                                                    // V15-03
  forbid_sealed_source_fixture_generation: {}; restrict_firewalled_fixture_generation_to_same_firewall: { firewall_scope_ref: ScopeRef };   // V15-04
  require_dspy_target_eligibility: { target_id: string };                                                       // DSPY-TARGETS
  require_post_retrieval_critique_policy_gate: {};                                                              // V15-05
  require_counterfactual_prompt_policy_gate: {}; require_additive_synthesis_prompt_policy_gate: {};             // V15-06
  // EVERY PolicyObligationKind MUST appear above (CI guard: keyof PolicyObligationParametersByKind ≡ PolicyObligationKind).
};
type TypedPolicyObligation<K extends PolicyObligationKind> = PolicyObligationBase & { obligation_kind: K; parameters: PolicyObligationParametersByKind[K] };
type PolicyObligation = { [K in PolicyObligationKind]: TypedPolicyObligation<K> }[PolicyObligationKind];   // exhaustive mapped union — future kinds cannot drift

/** Conflict model (U9/GPT §4.11.4): genuinely-contradictory residue (e.g. a confirmation prompt that would itself leak) → fail-closed. */
interface PolicyObligationConflict extends E0DurableRecord {
  conflict_id: PolicyObligationConflictRef;
  schema_owner: 'DOC81';
  effective_policy_ref: EffectiveMemoryPolicyRef; action: MemoryPolicyAction; destination?: E0OutboundDestinationClass;
  conflicting_obligation_refs: PolicyObligationRef[];
  conflict_kind: 'disclosure_vs_ui_notice' | 'redaction_vs_hide_existence' | 'receipt_vs_suppressed_manifest'
    | 'confirmation_prompt_would_leak' | 'enforcement_owner_conflict' | 'parameter_conflict' | 'unknown_conflict';
  disposition: 'fail_closed_for_action';
  blocked_reason_codes: ReasonCodeId[];
}

/** Discharge proof (U9/GPT §4.11.5): a `blocking` obligation must be discharged before the movement proceeds. */
interface PolicyObligationDischarge extends E0DurableRecord {
  discharge_id: PolicyObligationDischargeRef;
  schema_owner: 'DOC81';
  obligation_ref: PolicyObligationRef; enforcement_owner: PolicyObligationEnforcementOwner; discharged_by_ref: string;
  discharge_status: 'satisfied' | 'blocked' | 'waived_by_policy' | 'failed'; blocking: boolean;
  required_before_actions: MemoryPolicyAction[];
  certificate_ref?: MemoryFlowCertificateId; receipt_ref?: string;
  freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[];
}
```

**Obligation resolution (Adj U9; CL §2.6 — transitive dominance closure; order-independent).** Same-kind obligations merge to the strictest parameter (e.g. union redaction spans); a **transitive strict-partial-order dominance closure** keeps the maximal (most-restrictive) obligation and drops dominated ones (e.g. `hide_existence` dominates `show_generic_existence_only` and `require_ui_warning`); genuinely-contradictory residue produces a `PolicyObligationConflict` (fail-closed for the action). Obligation-induced disclosure ceilings then tighten the **disclosure VECTOR** (§3.2 step 5; U8/U9 — never an independently-set scalar; the scalar is derived from the final vector at step 6, then coherence runs last; CODEX R2-B1). This is the antidote both to the prior non-transitive union that could accumulate contradictions (B9) and to the scalar-overwrite leak (an obligation ceiling silently undone by a later, more-permissive derivation).

```typescript
/** Obligation → disclosure-vector ceilings (Adj U8/U9; CODEX R2-B1). Met into eff.disclosure_vector at §3.2 step 5.
 *  Values are monotone-down ceilings; count held at 'none' for show_generic_existence_only (stricter than the
 *  reviewer seed — seed-confirmable, §13.6). */
const OBLIGATION_DISCLOSURE_VECTOR_CEILING: Partial<Record<PolicyObligationKind, DisclosurePermissionVector>> = {
  hide_existence: bottomDisclosureVector(),                       // §4.2 — nothing disclosable
  show_generic_existence_only: {
    may_disclose_existence: true,  may_disclose_container_type: true,  may_disclose_topic_label: true,
    may_disclose_source_title: false, count_disclosure_mode: 'none',
    may_disclose_reason_summary: false, max_summary_fidelity: 'none',
  },
  require_safe_label: disclosureVectorCeilingFor('generic_safe_label_only'),   // CL A-M4 (V11) — cap the meet's disclosure vector to ≤ generic_safe_label_only (belt-and-suspenders to the downstream render discharge)
};
function obligationDisclosureVectorCeilings(obs: PolicyObligation[]): DisclosurePermissionVector[] {
  return obs.map(o => OBLIGATION_DISCLOSURE_VECTOR_CEILING[o.obligation_kind]).filter(v => v !== undefined);
}
```

**Capability-axis obligations: reconciled at EMISSION, not at runtime (Adj V14; D-R4 — no runtime step-5b).** Some obligations name a **capability** axis (e.g. `restrict_firewalled_learning_to_same_firewall` ⇒ `learning_scope`, `require_redaction` ⇒ `content_fidelity`). R3 does **not** add a runtime reconciliation step that re-meets these against the already-met point (that was GPT's step-5b; **declined** D-R4 — it would let a late obligation silently re-lower a finalized axis, re-opening the very scalar-overwrite class V1 closed). Instead: **(1) emission lint** — EC rejects, at policy-compile time, any `EffectiveMemoryPolicy` whose `point` axis is *more permissive* than a co-emitted **blocking** capability-obligation requires (`policy.capability_obligation_contradicts_axis`); a compiled policy may not ship the contradiction. **(2) consumer precedence** — if a blocking capability-obligation and the axis ever disagree at a consumption point, **the blocking obligation governs over the axis** (the obligation is the stricter, explicitly-blocking statement). The meet's step 5 therefore only ever tightens the **disclosure vector** at runtime (§3.2); capability axes are settled by step 2's point-meet + this emission-time check. **Lint:** `policy.capability_obligation_contradicts_axis` (V14). **Fixture:** `fixture.policy.capability_obligation_axis_contradiction_rejected_at_emission` (V14).

**Note on `enforcement_owner` (E0 alignment).** Round D §1.7 listed `DOC8`; DOC8 is **capability-mining only** (ADQ-221; E0 §7.3) and is excluded — learning-side obligations (`forbid_global_learning`, `partition_learning`, `discard_sealed_learning_signals`, `restrict_firewalled_learning_to_same_firewall`) route to **DOC85**; delivery/render + UI/Inspector obligations route to **DOC84/DOC86** (U9/GPT §4.11.2). No-phantom-owner correction, not a new capability.

**Membrane disposition is DERIVED, not synthesized (Adj U22/F6; GPT §4.23 — RESOLVES §13.4).** The §3.3 `PolicyMembraneDecision.crossing_disposition` enum is **confirmed** as the disposition vocabulary; what was open (its authority) is closed by making the disposition a **derivation** of `f(effective policy, boundary kind, obligations)`:

```typescript
/** Derives the membrane crossing disposition from the effective policy — never independently set. schema_owner = DOC81 (U22/F6). */
interface PolicyMembraneDispositionDerivation extends E0DurableRecord {
  derivation_id: PolicyMembraneDispositionDerivationRef;
  schema_owner: 'DOC81';
  effective_policy_ref: EffectiveMemoryPolicyRef; boundary_ref: ScopeBoundaryRef; action: MemoryPolicyAction;
  derived_crossing_disposition:
    | 'crossing_permitted' | 'crossing_permitted_with_obligations' | 'crossing_reference_only'
    | 'crossing_blocked' | 'crossing_requires_disambiguation';
  derivation_inputs: {
    effective_content_fidelity: ContentFidelityLevel; effective_locality: LocalityLevel;
    effective_disclosure_class: DisclosureClass; boundary_kind: ScopeBoundaryKind; obligations: PolicyObligationRef[];
  };
  invariant: 'membrane_disposition_derived_from_effective_policy_not_independent';
  reason_codes: ReasonCodeId[];
}
// Derivation rule: blocked locality OR fail_closed floor → crossing_blocked; pending disambiguation → crossing_requires_disambiguation;
// reference_only fidelity → crossing_reference_only; blocking obligations present → crossing_permitted_with_obligations; else crossing_permitted.
```

`PolicyMembraneDecision.crossing_disposition` (above) MUST equal the derived value; EC never sets it independently.

**Lifecycle.** A `PolicyMembraneDecision` is `computed → enforced (EC) → invalidated` on generation change. A `PolicyObligation` is `attached → discharged (enforcement_owner) → audited (receipt if receipt_required)`. A `blocking` obligation that is not discharged **hard-blocks** the action.

**Unhappy paths.** *Membrane decision used as a per-dimension capability* → `policy.membrane_decision_used_as_capability` (SM-014). *`crossing_blocked` leaks the object* → routed through `disclosure_class` + safe-label (§4.2), never a raw block message. *Blocking obligation undischarged but action proceeds* → `policy.blocking_obligation_bypassed`. *Obligation references a retired/unknown reason code* → `policy.undefined_policy_obligation` (plan §17.4). *`crossing_requires_disambiguation`* → `PolicyDisambiguationRequest` (§3.5), never a silent block or silent allow.

**Lints (Stage 9):** `policy.membrane_decision_used_as_capability` (SM-014); `policy.blocking_obligation_bypassed`; `policy.undefined_policy_obligation` (plan §17.4); `policy.obligation_enforcement_owner_doc8` (ADQ-221 — DOC8 may not be an enforcement owner); `policy.obligation_conflict_not_failed_closed` (U9); `policy.obligation_owner_missing_doc84_doc86` (U9); `policy.obligation_parameters_untyped` (U9); `policy.obligation_not_discharged_before_movement` (U9); `policy.membrane_disposition_not_derived_from_effective_policy` (U22).

**Fixtures (Stage 8):** `fixture.policy.membrane_distinct_from_capability`; `fixture.policy.blocking_obligation_hard_blocks`; `fixture.policy.obligation_owner_not_doc8`; `fixture.policy.conflicting_obligations_fail_closed_for_action` (U9); `fixture.policy.require_redaction_obligation_needs_redaction_map` (U9); `fixture.policy.membrane_crossing_blocked_when_effective_locality_blocked` (U22).

**Cross-charter consumption.** EC (executes membrane decision + obligations), PropA (enforces sensitivity-derived obligations, §7.3), DOC84 (delivery honors `require_redaction` / `require_safe_label` / `require_final_prompt_proof`), DOC85 (`forbid_global_learning` / `partition_learning`), DOC86 (`require_ui_warning` / `show_generic_existence_only`).

### §3.4 `PolicyStamp` (`scope_items`) + `PolicyStampInvalidation` + `PolicyStampRestamp` (ADQ-316)

**Source:** Round D §2.1–§2.3, §5.4; SM-014/SM-101 context; Owner Map line 83; ADQ-316; Opening Brief draft target 6; E0 §3.3 `RestampMFC`. **Owner:** DOC81. **Executor:** EC's compiled policy evaluator stamps; DOC86 surfaces the restamp UI (Owner Map line 83). The stamp is the durable record that an object was policy-evaluated under a specific generation; restamp is the **only** widening path and **cannot exceed original ceilings** (ADQ-316; Hard Constraint §5).

```typescript
/** Per-(action, destination) scope item — FIXES the triple-binding bug (Adj U4/GPT §4.9.1): each item binds its OWN
 *  effective_policy_ref + ceiling, so a retrieval-priced stamp can never appear valid for render/export. */
interface PolicyStampScopeItem {
  action: MemoryPolicyAction;
  destination?: E0OutboundDestinationClass;          // required iff action ∈ {export, delegate, carryover}
  effective_policy_ref: EffectiveMemoryPolicyRef;    // MUST point to the SAME object/action/destination/context triple (§3.2)
  original_ceiling_ref: PolicyCeilingRef;            // per-item ceiling (§3.4 snapshot)
}

/** Durable proof an object was policy-evaluated. schema_owner = DOC81 (Owner Map line 83; Round D §2.1). issued_by EC. */
interface PolicyStamp extends E0DurableRecord {
  stamp_id: PolicyStampRef;
  schema_owner: 'DOC81';
  object_ref: MemoryObjectRef;
  issued_by: 'EC_compiled_policy_evaluator';
  scope_items: PolicyStampScopeItem[];               // non-empty (U4) — replaces the single EffectiveMemoryPolicyRef triple-binding
  lifecycle_state: 'active' | 'superseded' | 'invalidated' | 'expired' | 'blocked';   // U4/U28
  freshness_key: PolicyRuntimeFreshnessKey;          // §3.0 (U5) — carries policy_generation_id (E0 §8.3)
  expires_at?: string;                               // RFC3339-UTC (E0 §8.5)
  reason_codes: ReasonCodeId[];
}

// Adj V20/B-S6 — `PolicyStampScope` COLLAPSED into `PolicyStamp.scope_items` (above). Post-U4 it was a duplicate carrier of the
// same `PolicyStampScopeItem[]`; two must-stay-in-sync objects invite divergence. The standalone interface is removed; Owner Map
// line 83 repoints at `PolicyStamp.scope_items` at discharge (§14.3). The `PolicyStampScopeRef` brand is retired (§0 sweep).

/** High-water capability captured at the ROOT (first) stamp, per (action, destination), from the step-2 policy GRANT
 *  BEFORE the scope floor (CL §2.5 capture rule). The cap a restamp may never exceed (ADQ-316; B1). schema_owner = DOC81 (U3). */
interface PolicyCeilingSnapshot extends E0DurableRecord {
  ceiling_id: PolicyCeilingRef; schema_owner: 'DOC81'; root_stamp_ref: PolicyStampRef; object_ref: MemoryObjectRef;
  ceiling_items: Array<{
    action: MemoryPolicyAction; destination?: E0OutboundDestinationClass;
    max_content_fidelity: ContentFidelityLevel; max_locality: LocalityLevel; max_learning_scope: LearningScopeLevel;
    max_mutation_authority: MutationAuthorityLevel; max_disclosure_class: DisclosureClass; max_disclosure_vector: DisclosurePermissionVector;
  }>;
  issued_under_freshness_key: PolicyRuntimeFreshnessKey; issued_at: string; issued_by: 'EC'; reason_codes: ReasonCodeId[];
}
/** Restamp-of-restamp compares to the ROOT snapshot, NEVER a prior restamp (no ratcheting; CL §2.5). schema_owner = DOC81 (U3). */
interface RestampChainIntegrity extends E0DurableRecord {
  chain_id: RestampChainIntegrityRef; schema_owner: 'DOC81'; root_stamp_ref: PolicyStampRef; ceiling_snapshot_ref: PolicyCeilingRef;
  restamp_refs: string[]; invariant: 'every_restamp_compared_to_root_ceiling_snapshot';
}

/** Restamp authority (Adj R-2 — SUPERSEDES F8). `human_required` ONLY for `firewalled`-boundary crossings (dormant for a solo
 *  user, §1.6). ALL other restamps — BOTH directions, ≤ ceiling, INCLUDING disclosure-raising — are `agent_autonomous`, because
 *  the send-time egress gate (DOC5) is the human checkpoint at the actual boundary: one human moment at the right place, not two.
 *  No batch-approval queues for routine restoration. schema_owner = DOC81. */
interface RestampAuthority {
  authorized_by?: PrincipalRef;                      // present iff authority_tier === 'human_required'
  authority_tier: 'agent_autonomous' | 'human_required';   // F8's 'architect_batched' / 'always-human' removed by R-2
}
// RULE (R-2): authority_tier = 'human_required' IFF the restamp crosses a `firewalled` boundary; otherwise 'agent_autonomous'.

/** The restamp decision contract — the ONLY widening path (ADQ-316). Discriminated union (U3/GPT §4.9.3); `block` carries NO
 *  authorizing effective policy. Produces an E0 RestampMFC (E0 §3.3). schema_owner = DOC81 (draft target 6). */
type PolicyStampRestamp = PolicyStampRestampKeep | PolicyStampRestampDowngrade | PolicyStampRestampBlock;
type PolicyAxis = 'content_fidelity' | 'locality' | 'learning_scope' | 'mutation_authority' | 'disclosure_class' | 'disclosure_vector';
interface PolicyAxisCeilingComparison { axis: Exclude<PolicyAxis,'disclosure_vector'>; comparison: 'within_ceiling' | 'downgraded' | 'exceeds_ceiling'; prior_value: string; new_value?: string; ceiling_value: string; }
/** Adj V19/QF13 — the disclosure VECTOR needs a partial order, not a scalar comparison. A ≤ B iff: every boolean field A⇒B;
 *  rank(A.count_disclosure_mode) ≤ rank(B); rank(A.max_summary_fidelity) ≤ rank(B); any A template/ref allowed by B's ref policy. */
interface DisclosureVectorCeilingComparison {
  axis: 'disclosure_vector'; prior_vector: DisclosurePermissionVector; new_vector: DisclosurePermissionVector; ceiling_vector: DisclosurePermissionVector;
  comparison_by_field: Array<{ field: keyof DisclosurePermissionVector; comparison: 'within_ceiling' | 'downgraded' | 'exceeds_ceiling' }>;
  aggregate_comparison: 'within_ceiling' | 'downgraded' | 'exceeds_ceiling';
}
interface PolicyStampRestampBase extends E0DurableRecord {
  restamp_id: PolicyStampRestampRef; schema_owner: 'DOC81'; object_ref: MemoryObjectRef;
  prior_stamp_ref: PolicyStampRef; root_stamp_ref: PolicyStampRef;     // legality vs ROOT snapshot, never a prior restamp (no ratcheting)
  prior_freshness_key: PolicyRuntimeFreshnessKey; new_freshness_key: PolicyRuntimeFreshnessKey;
  original_ceiling_ref: PolicyCeilingRef; authority: RestampAuthority; // R-2
  issued_memory_flow_certificate_ref?: MemoryFlowCertificateId;        // V19/QF14 — POST-ISSUANCE only (the decision PRECEDES the MFC it produces); NOT a second RestampIssuance object (D-R1)
  ceiling_comparisons: Array<PolicyAxisCeilingComparison | DisclosureVectorCeilingComparison>;   // V19/QF13 — scalar axes + the disclosure-vector partial order
  reason_codes: ReasonCodeId[];
}
// Invariant (V19): issued_memory_flow_certificate_ref is REQUIRED once restamp_disposition commits (keep/downgrade/block issued); absent only in the pre-issuance decision phase.
interface PolicyStampRestampKeep extends PolicyStampRestampBase { restamp_disposition: 'keep'; new_effective_policy_refs: EffectiveMemoryPolicyRef[]; ceiling_compliance_attested: true; }
interface PolicyStampRestampDowngrade extends PolicyStampRestampBase { restamp_disposition: 'downgrade'; new_effective_policy_refs: EffectiveMemoryPolicyRef[]; downgraded_axes: PolicyAxis[]; ceiling_compliance_attested: true; }
interface PolicyStampRestampBlock extends PolicyStampRestampBase { restamp_disposition: 'block'; new_effective_policy_refs?: never; blocked_reason_codes: ReasonCodeId[]; ceiling_compliance_attested: true; }

/** Records that a freshness/generation change tightened a stamp — per-(action,destination) axis deltas (Adj U4/GPT §4.9.4).
 *  schema_owner = DOC81 (Owner Map line 83; Round D §2.3). */
interface PolicyAxisDelta { axis: PolicyAxis; prior_value: string; new_value: string; delta_kind: 'tightened' | 'unchanged' | 'widened_illegal'; }
interface PolicyStampInvalidationScopeItem { action: MemoryPolicyAction; destination?: E0OutboundDestinationClass;
  prior_effective_policy_ref: EffectiveMemoryPolicyRef; new_effective_policy_ref?: EffectiveMemoryPolicyRef;
  axis_deltas: PolicyAxisDelta[]; required_remediation: 'restamp' | 'downgrade' | 'block'; }
interface PolicyStampInvalidation extends E0DurableRecord {
  invalidation_id: PolicyStampInvalidationRef; schema_owner: 'DOC81'; invalidated_stamp_ref: PolicyStampRef; object_ref: MemoryObjectRef;
  prior_freshness_key: PolicyRuntimeFreshnessKey; new_freshness_key: PolicyRuntimeFreshnessKey;
  affected_scope_items: PolicyStampInvalidationScopeItem[];
  notice_disposition: 'no_notice' | 'generic_notice' | 'specific_in_app_notice';   // V11 — walls produce nothing; non-wall in-app may be specific (R-3)
  user_visible_summary_ref?: SafeReferenceLabelRef;          // safe label only; OPTIONAL and FORBIDDEN when notice_disposition='no_notice' (V11)
  reason_codes: ReasonCodeId[];
}
// Invariant (V11): user_visible_summary_ref MUST be absent when notice_disposition='no_notice' (a wall invalidation surfaces nothing); REQUIRED otherwise.
```

**Restamp legality (Adj U3; CL L4′ + GPT §4.9.2).** A restamp is valid only if, for **every** `(action, destination)` item, every axis comparison against the **root `PolicyCeilingSnapshot`** is `within_ceiling` or `downgraded` — i.e. `new ≤ MIN(root ceiling, CURRENT scope floor)` per axis. Any `exceeds_ceiling` blocks issuance and emits `restamp.exceeds_original_ceiling`. Restamp-of-restamp inherits the **root** ceiling (`RestampChainIntegrity`), never a prior restamp's — no ratcheting.

**The ceiling rule (ADQ-316; Hard Constraint §5).** A restamp re-runs EC's evaluator under the current freshness key and may **keep, downgrade, or block** — it **cannot grant access beyond original ceilings**, and on every axis the new effective value is `≤ MIN(root ceiling, current scope floor)` (Round D §5.4; U3). A restamp that raises any axis above the ceiling is a hard error. This is the schema-level companion to E0 `RestampMFC` (`original_ceiling_ref` + `ceiling_compliance_attested`, E0 §3.3) and the E0 §12.1 "policy monotonicity" invariant (§6.3); the restamp flows through a `MemoryMutationEnvelope` (E0 §5.1).

**Restamp authority (Adj R-2 — supersedes F8).** Re-checks after a policy change are **fully silent and autonomous in both directions** — tighten *and* restore-up-to-ceiling, including disclosure-raising restamps — because the send-time egress gate (DOC5) already provides the human checkpoint at the actual boundary; one human moment at the right place, not two. `RestampAuthority.human_required` is reserved for `firewalled`-boundary crossings only (dormant for a solo user, §1.6). There are no batch-approval queues for routine restoration. This replaces the earlier F8 authority-tier rule (disclosure-raising ⇒ human) and the CL §2.5 `architect_batched` tier.

**Restamp re-evaluation mode (Adj V5/A-B4 — makes R-2's autonomous restore reachable).** A `PolicyStampRestamp` computes its candidate effective policy via `meet_v2(…, mode='restamp_reeval')` — **never** `mode='ordinary'`. In `restamp_reeval` the step-4 sticky clamp does **not** apply (sticky would re-pin the candidate to the prior, lower value, so a restamp could only keep/downgrade — "over-tightens irrecoverably"); instead the candidate is bounded only by `MIN(root ceiling, current scope floor)` per axis + the ceiling disclosure vector. So an axis lowered by a now-lifted transient floor can rise back to the ceiling, and the §3.4 restamp-legality check then validates it. **QF10 resources-only guard (R-2):** for a `policy_generation_restamp_batch` quota envelope (§6.7), `on_quota_exceeded: 'manual_resource_approval_required'` approves **resource usage only** — it MUST NOT become human approval of individual routine restamps (those stay autonomous per R-2). **Lint:** `policy.restamp_reeval_applied_sticky_restrictive`. **Fixture:** `fixture.policy.autonomous_restamp_restores_up_to_ceiling_after_floor_lift`.

**Lifecycle.** `stamp issued (EC) → (generation change) → invalidation recorded → remediation { restamp | downgrade | block }`. Restamp `keep` preserves; `downgrade` lowers; `block` revokes. A retrieval-approved object is **not** render-approved; a local-inline-approved object is **not** export/delegate/carryover/learn-approved (Round D §2.2 stamp-scope rules).

**Unhappy paths.** *Restamp exceeds ceiling* → `restamp.exceeds_original_ceiling` (E0 §3.3 token; ADQ-316). *Export-scoped stamp without destinations* → `policy.export_stamp_without_destination` (plan §17.4). *Stale stamp reused after generation change* → `policy.stale_stamp_used_after_generation_change` (Round D §2.4). *Invalidation summary leaks identity* → `policy.invalidation_summary_leaks_object` (must be a safe label). *Widening without a restamp record* → `monotonicity.policy_widened_without_restamp` (§6.3).

**Lints (Stage 9):** `restamp.exceeds_original_ceiling` (ADQ-316); `policy.export_stamp_without_destination` (plan §17.4); `policy.stale_stamp_used_after_generation_change` (Round D §2.4); `policy.invalidation_summary_leaks_object`; `monotonicity.policy_widened_without_restamp` (E0 §12.1); `policy.stamp_scope_action_without_matching_effective_policy` (U4); `policy.retrieval_stamp_used_for_render_or_export` (U4); `restamp.chain_ceiling_rebased_to_prior_restamp` (U3); `restamp.block_disposition_with_new_effective_policy_ref` (U3); `policy.invalidation_axis_delta_unmapped_to_action` (U4); `policy.cross_firewall_restamp_without_human_authority` (R-2); `policy.routine_restamp_required_human_approval` (R-2 — no re-introducing batch queues).

**Fixtures (Stage 8):** `fixture.policy.restamp_cannot_exceed_original_ceiling` (ADQ-316); `fixture.policy.retrieval_stamp_is_not_render_stamp`; `fixture.policy.export_stamp_requires_destination`; `fixture.golden.restamp_keep_downgrade_block_only`; `fixture.policy.stamp_scope_item_matches_effective_policy_triple` (U4); `fixture.policy.restamp_of_restamp_compares_against_root_ceiling` (U3); `fixture.policy.blocked_restamp_has_no_authorizing_effective_policy` (U3); `fixture.policy.disclosure_raising_restamp_is_autonomous` (R-2); `fixture.policy.cross_firewall_restamp_requires_human` (R-2).

**Cross-charter consumption.** EC (issues stamps + restamps; issues the `RestampMFC`), DOC86 (renders restamp eligibility + invalidation summary — the DOC81→DOC86 export, §9), DOC84 (honors stamp scope on delivery/export), DOC82 (write gating reads `mutation_authority` ceiling). The `original_ceiling_ref` + `ceiling_compliance_attested` link directly to E0 `RestampMFC`.

### §3.5 `PolicyDisambiguationRequest` (DOC84 park/resume seam)

**Source:** Round D §1.8; SM-102; Owner Map line 84; Opening Brief draft target 7. **Replaces the retired `ask_user` scalar** (SM-102 / Retired Names) — because *asking the user can itself leak protected information* (Round D §1.8). **Owner:** DOC81. **Executor:** EC raises it; **DOC84 implements pipeline park/resume** (F-async; Owner Map line 84; E0 §1.5 / §3.2 `policy_disambiguation_pending`).

```typescript
/** A safe clarification request that replaces ask_user. schema_owner = DOC81 (Owner Map line 84; SM-102).
 *  EC raises; DOC84 parks/resumes the pipeline (F-async). The prompt is a SAFE label, never raw identity. */
/** Blocking/non-blocking union (Adj U23/GPT §4.15.1; CL M6). `defer` is legal ONLY when non-blocking.
 *  schema_owner = DOC81 (Owner Map line 84; SM-102). EC raises; DOC84 parks/resumes. */
type PolicyDisambiguationRequest = BlockingPolicyDisambiguationRequest | NonBlockingPolicyDisambiguationRequest;
interface PolicyDisambiguationRequestBase extends E0DurableRecord {
  request_id: PolicyDisambiguationRequestRef;
  schema_owner: 'DOC81';
  question_kind:
    | 'scope_confirmation' | 'destination_confirmation' | 'identity_confirmation'
    | 'privilege_review' | 'export_confirmation' | 'carryover_confirmation';   // Round D §1.8
  safe_prompt_label_ref: SafeReferenceLabelRef;     // approved safe-label vocabulary; NEVER derived from content
  may_name_object: boolean;                          // default false (fail-closed)
  may_name_scope: boolean;                           // default false
  policy_decision_ref: MemoryPolicyDecisionRef;      // §3.1 — the decision that raised the question
  deadline_at?: string;                              // RFC3339-UTC (CL M6) — hard timeout
  freshness_key: PolicyRuntimeFreshnessKey;          // §3.0 (U5)
  reason_codes: ReasonCodeId[];
}
interface BlockingPolicyDisambiguationRequest extends PolicyDisambiguationRequestBase {
  blocked_until_answered: true;
  fallback_if_unanswered: 'block' | 'reference_only' | 'search_only';   // NOT 'defer' (a blocking request cannot defer forever)
}
interface NonBlockingPolicyDisambiguationRequest extends PolicyDisambiguationRequestBase {
  blocked_until_answered: false;
  fallback_if_unanswered: 'block' | 'reference_only' | 'search_only' | 'defer';   // Round D §1.8 (D2: `defer_to_safe_label_only` DECLINED)
}

/** The answer — its effect is NEVER a direct allow (Adj U23/GPT §4.15.2; the keystone invariant). An answer only creates a new
 *  scope/policy input under the current generation and FORCES A NEW MEET. schema_owner = DOC81. */
interface PolicyDisambiguationAnswer extends E0DurableRecord {
  answer_id: PolicyDisambiguationAnswerRef;
  schema_owner: 'DOC81';
  request_ref: PolicyDisambiguationRequestRef; answered_at: string; answered_by: PrincipalRef;
  answer_value: 'confirm_scope' | 'deny_scope' | 'confirm_destination' | 'deny_destination' | 'narrow_to_safe_material_only' | 'unknown_or_no_answer';
  effect: 'new_scope_resolution_required' | 'new_policy_decision_required' | 'restamp_required' | 'fallback_applied' | 'block';   // never a direct allow
  resulting_scope_resolution_ref?: ScopeResolutionResultRef;
  resulting_policy_decision_ref?: MemoryPolicyDecisionRef;
  resulting_effective_policy_ref?: EffectiveMemoryPolicyRef;
  freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[];
}
```

**The safe-prompt rule.** The disambiguation prompt MUST come from the approved safe-label vocabulary (§4.2; PropA registry) and must not name the protected object or scope unless `may_name_object` / `may_name_scope` is explicitly true under disclosure policy. Round D's worked example stands: *unsafe* = "Do you want me to use the privileged Marex strategy memo?"; *safe* = "Some potentially relevant protected material may exist outside this request's eligible scope. Continue using only eligible material?"

**Lifecycle.** `raised (EC) → pipeline parked (DOC84; MemoryContextPlanDegradedMode = 'policy_disambiguation_pending', E0 §3.2) → answered | timed_out → resumed | fallback applied`. On timeout, `fallback_if_unanswered` applies — never a silent allow.

**Unhappy paths.** *Prompt leaks identity* → `policy.disambiguation_prompt_leaks_protected_identity`. *No fallback / silent block* → `policy.disambiguation_without_fallback`. *`ask_user` reintroduced* → `supersession.retired_name_used` (`ask_user`, Retired Names SM-102). *Unanswered defaults to allow* → blocked (fallback must be block/reference_only/search_only/defer). *Parked pipeline never resumes* → `policy.parked_pipeline_orphaned` (DOC84 seam).

**Lints (Stage 9):** `policy.disambiguation_prompt_leaks_protected_identity`; `policy.disambiguation_without_fallback`; `supersession.retired_name_used` (`ask_user`); `policy.disambiguation_default_allow`; `policy.blocking_disambiguation_with_defer_fallback` (U23); `policy.disambiguation_answer_used_as_direct_allow` (U23 — the keystone); `state.disambiguation_without_terminal_state` (U23).

**Fixtures (Stage 8):** `fixture.policy.disambiguation_prompt_is_safe_label`; `fixture.policy.unanswered_applies_fallback_not_allow`; `fixture.policy.blocking_disambiguation_cannot_defer_forever` (U23); `fixture.policy.user_confirmation_forces_new_meet_not_direct_allow` (U23 keystone).

**Cross-charter consumption.** EC (raises), DOC84 (park/resume — owns `Pipeline park/resume state`, Owner Map line 164), DOC86 (renders the safe prompt), DOC82/DOC83 (extraction/write parked on `policy_disambiguation_pending`).

### §3.6 `EpisodePolicyEpoch` + `policy_generation_id` boundary (bind to E0 §8.3)

**Source:** Round D §2.5; SM-101; Owner Map line 85; Opening Brief draft target 8; E0 §8.3 (policy-generation carrier). **Owner:** DOC81 (the boundary object). **Executor:** EC; DOC83 consumes for episode bounds (Owner Map line 85). **Bind:** the carried `policy_generation_id` is E0's (E0 §8.3) — DOC81 owns the *epoch that opens/closes a generation within a WorkEpisode*, not the carrier field.

```typescript
/** The atomic policy-generation boundary within a WorkEpisode. schema_owner = DOC81 (Owner Map line 85; SM-101).
 *  EC executes; DOC83 consumes for episode bounds. Binds the carried policy_generation_id (E0 §8.3). */
interface EpisodePolicyEpoch extends E0DurableRecord {
  epoch_id: EpisodePolicyEpochRef;
  schema_owner: 'DOC81';
  work_episode_ref: WorkEpisodeRef;                 // DOC83-owned (Owner Map line 109); referenced
  surface_ref?: SurfaceRef;
  policy_generation_id: PolicyGenerationId;          // E0 §8.3 — the generation active for this epoch
  epoch_version: number;                             // U5/GK — monotonic CAS token; EC: incoming.epoch_version < current ⇒ re-gate at Step 0
  opened_at: string;                                 // RFC3339-UTC — the epoch's valid-time window START (B19; selected for effectiveTime, U5c)
  closed_at?: string;                                // valid-time window END
  last_re_gate_at?: string;                          // RFC3339-UTC (GK)
  superseding_epoch_ref?: string;                    // LINEAR chain under EC CAS — single successor, no fork (M7)
  reason_codes: ReasonCodeId[];
}
```

**Epoch concurrency (Adj U5/M7; GK).** The epoch is a **linear chain under EC compare-and-swap** with a monotonic `epoch_version`: a restamp issued under generation N that races with a concurrent extraction admission under N+1 is caught by `if (incoming.epoch_version < current.epoch_version) re-gate at Step 0`. `superseding_epoch_ref` admits exactly one successor (no fork). The active epoch's `opened_at`/`closed_at` are its valid-time window, selected for the request's `effectiveTime` (B19). GK's `PolicyEpochVersion`-on-`MemoryMutationEnvelope` (a global policy clock) is **declined for Phase-1 (D4)** — E0 §7.1 CAS + this epoch chain cover single-EC Phase-1; it is forward-flagged for Phase-2 networking (§12).

**Boundary rules (Round D §2.5; E0 §8.3).** Durable-write, carryover, export, and delegation decisions made within a `WorkEpisode` record the active `epoch_id`. A **mid-episode policy-generation change closes the prior epoch, opens a new epoch, and forces restamp of in-flight context products and movement actions** (§3.4). This is the DOC81-owned half of the policy-generation boundary; the **extraction-side re-gate** (re-gate at ABC §7.7 Step 0 if the generation changes between admission and resolution) is co-owned with DOC83 and is specified in §6.4 (Skeletal §11.3; E0 §8.3); the **resolution-side re-gate** is EC's `ECSeamContract` (E0 §7.1).

**Lifecycle.** `opened (EC) → (in-flight decisions stamped with epoch_id) → closed-on-supersede | closed-on-episode-end`. A superseded epoch's in-flight products are forced through restamp/downgrade/block (§3.4) before delivery.

**Unhappy paths.** *Mid-episode change without restamp* → `policy.episode_generation_change_without_restamp` (the core silent-stale-policy bug Round D §2.4/§2.5 fixes). *In-flight product delivered under closed epoch* → blocked + re-plan (E0 §3.2 stale-generation rule). *Epoch never closed* → audit lint. *Carryover crosses an epoch boundary unstamped* → `policy.carryover_across_epoch_unstamped`.

**Lints (Stage 9):** `policy.episode_generation_change_without_restamp`; `policy.in_flight_product_under_closed_epoch`; `policy.carryover_across_epoch_unstamped`; `policy.epoch_never_closed`; `policy.epoch_supersession_forked` (M7); `policy.epoch_version_not_monotonic` (U5/GK); `policy.stale_epoch_version_not_regated` (U5/GK).

**Fixtures (Stage 8):** `fixture.policy.midepisode_change_forces_restamp`; `fixture.policy.closed_epoch_blocks_in_flight_delivery`; `fixture.policy.concurrent_restamp_extraction_regates_on_epoch_version` (U5/GK — the cascade×restamp race).

**Cross-charter consumption.** EC (executes), DOC83 (episode bounds; extraction re-gate, §6.4), DOC84 (in-flight products re-stamped at epoch close), DOC85 (learning eligibility tied to epoch generation). Binds E0 §8.3 (carrier) + E0 §3.2 (re-plan) + E0 §7.1 (EC resolution-side re-gate).

---

## §4. Capability / disclosure coordination (E2 — validate)

This is the E2 validate pass: the two meets, the disclosure machinery, the extraction/DAMS policy envelopes, the topic risk class, and the fail-closed default. Source: Round D §1.6, §6, §8; Owner Map lines 87, 90–93, 194; Skeletal §DOC81 §3–§4; ADQ-308/213/313.

### §4.0 Internal-use-permissive / egress-gated default posture (Adj R-1 — architect ruling, binding)

**Source:** §1-bis R-1 (architect, 2026-06-04 — controlling); CL §1.5. The architect's controlling principle: *privilege, privacy, and personal restrictions are meant to block **sharing** of information, mostly.* **Internal use of the user's own memories — `retrieve`, local `render_*`, summaries, drafts delivered to the user — defaults to PERMITTED.** The five-axis / action-scoped design (§3.1) already supports this; R2 states it as a normative default posture for internal actions.

**The exhaustive internal-use block list (R-1) — these four, and ONLY these four, may hard-block an internal-use action:**
1. a declared **ethical wall** (`firewalled`, §2.2 — dormant for a solo user, §1.6);
2. a **revoked / clawed-back source** (incl. clawback orders, §5);
3. **sealed / protective-order material used outside its own matter** (POs restrict *use*, not just sharing);
4. the **malformed-record conservative containment** (F1, §3.0 / §3.2 step 1 — contain-and-continue, R-4).

**Explicit non-blockers (R-1) — none of these may hard-block an internal-use action:** **privilege never blocks internal use** (egress-only, §1; the determination is DOC5's, never DOC81's); **cross-matter confidentiality** is handled as relevance/contamination ranking (§4.4) + the **send-time egress gate** on outputs (§4.6.4 / DOC5), not an internal block; **legal holds block destruction ONLY**, never use (§6.1); **privacy topics block collection**, so there is nothing saved to block (§7.1). The restrictive machinery concentrates at **save-time** (privacy topics, incognito, toggles, source exclusion, inert risky topics), **share-time** (the egress gate — typed destinations, send-time confirms, per-destination stamps), **delete-time** (holds, registry default-block, §6.1), and **learning-time** (sealed/firewalled/calibration gates, §6.3/§7.3).

**Enforcement.** R2 adds lint **`policy.internal_use_blocked_without_qualifying_basis`** (§11) + a negative fixture: an internal-use block whose basis is **not** one of the four enumerated above is a spec violation. This is the §1.5/§1-bis boundary audit's positive form (regression-checklist item 10): nothing in DOC81 classifies destinations, runs confirmations, owns a privilege taxonomy, or blocks internal use outside the R-1 four.

**Lints (Stage 9):** `policy.internal_use_blocked_without_qualifying_basis` (R-1, hard block); **`policy.internal_use_floor_raised_without_qualifying_basis`** (R-1/V4b/A-B3 — an internal action *degraded* to reference_only/safe_label/blocked/disambiguation on a basis outside the R-1 four; degradation counts, not just hard blocks — this is the §4.6 rule-3 fix's enforcement). **Fixtures (Stage 8):** `fixture.policy.internal_use_block_requires_one_of_four_bases` (R-1 negative); `fixture.policy.cross_matter_internal_retrieve_not_floored` (V4b — a same-principal cross-matter retrieve returns full content; only egress is gated).

### §4.1 Capability meet vs disclosure meet — SEPARATION (Round D §1.6; orthogonal)

**Source:** Round D §1.6; SM-104; Skeletal §DOC81 §3; Hard Constraint §3; Commission §1.5 ("capability/disclosure meet collapsed into one"). **The two meets are orthogonal and MUST NOT be collapsed.**

```text
Capability meet = what the system may DO with the object.
                  = meet over { content_fidelity, locality, learning_scope, mutation_authority }  (axes 1–4)
Disclosure meet = what the system may SAY about the object (incl. that it may not use it).
                  = meet over { disclosure_class }                                                 (axis 5)
```

The separation is load-bearing because **a blocked object may still have any of several disclosure postures** (Round D §1.6): its existence may be undisclosable; or generically disclosable; or its source-kind disclosable; or a reason-summary disclosable; or fully explainable. Capability `blocked` does **not** imply disclosure `not_disclosable`, and disclosure `full` does **not** imply capability `full`. Both meets run independently over the same `MemoryPolicyDecision` inputs (§3.1) and both are carried on `EffectiveMemoryPolicy` (§3.2: the four `effective_*` capability fields vs `effective_disclosure_class`).

The disclosure meet (`effective_disclosure_class`) MUST be consumed by every surface that might reveal or conceal the object (Round D §1.6): `BlockedScopeNotice`, `ReferenceOnlyNotice`, `SearchAffordance`, `InspectorVisibilityPlan`, `UserContextSurfacePlan`, `ConsideredItemLedger`, `CarryoverCapsule`, and export/delegation receipts. These surfaces are **owned downstream** (DOC86/DOC84) — DOC81 owns only the disclosure *meaning* they consume.

**Lifecycle / unhappy paths / enforcement** are specified at §4.6 (fail-closed) and §10 (the runtime-gate + lint + fixture triple for the separation invariant). The separation invariant's negative fixture is `fixture.golden.capability_block_with_disclosable_existence_does_not_leak` (§10).

**Lints (Stage 9):** `policy.capability_and_disclosure_meet_collapsed` (Hard Constraint §3); `policy.disclosure_inferred_from_capability` (the specific collapse — inferring `not_disclosable` from `blocked`); `policy.capability_inferred_from_disclosure`.

**Fixtures (Stage 8):** `fixture.policy.capability_and_disclosure_meets_independent`; `fixture.policy.blocked_object_can_be_existence_disclosable`.

### §4.2 `disclosure_class` + `SafeLabelDisclosurePolicy` (ADQ-308)

**Source:** Round D §1.6, §6.1–§6.2; SM-104/SM-105; Owner Map lines 87, 93; ADQ-308; ADQ-317; Opening Brief draft target 9–10. **Owner:** DOC81 owns `disclosure_class` (Owner Map line 93) and `SafeLabelDisclosurePolicy` (the policy/disclosure *meaning*, Owner Map line 87). **One-owner split (Stage 5R2):** DOC86 owns `SafeLabelRenderContract` (the UI render, Owner Map line 89); **PropA** owns the `ApprovedSafeLabelVocabularyRegistry` (the vocabulary, Owner Map line 88); EC enforces. DOC81 references the PropA vocabulary, never derives labels from content.

`DisclosureClass` is the lattice axis (defined in §3.0); R2 makes the **disclosure vector** the executable model and **derives** the scalar from it (U8 — one source of truth).

```typescript
// NOTE: `AvailabilityDisposition` is **DOC86-owned** (Owner Map line 191; Skeletal DOC86 §6; Import Graph DOC84→DOC86). DOC81 does
// NOT define or carry it — embedding it here would create an upward DOC86→DOC81 dependency against the B1 acyclic fix. DOC81 exports
// `disclosure_class` (§3.0) + the vector + safe-label constraints; **DOC86 maps those to its own `AvailabilityDisposition` + notices** (CODEX fidelity audit, 2026-06-01).

/** Executable disclosure expansion (Adj U8/GPT §4.5). The scalar disclosure_class (§3.0) is ALWAYS derived from this vector
 *  (deriveDisclosureClass) — never independently set. Booleans AND-meet; count/summary take the most-restrictive. */
type CountDisclosureMode = 'none' | 'bucketed' | 'exact';
type SummaryFidelity = 'none' | 'generic_reason_only' | 'redacted_reason' | 'full_reason';
interface DisclosurePermissionVector {
  may_disclose_existence: boolean; may_disclose_container_type: boolean; may_disclose_topic_label: boolean; may_disclose_source_title: boolean;
  count_disclosure_mode: CountDisclosureMode; count_bucket_policy_ref?: string;
  may_disclose_reason_summary: boolean; reason_summary_template_ref?: SafeReferenceLabelRef;
  max_summary_fidelity: SummaryFidelity;
}
function bottomDisclosureVector(): DisclosurePermissionVector {
  return { may_disclose_existence:false, may_disclose_container_type:false, may_disclose_topic_label:false, may_disclose_source_title:false,
    count_disclosure_mode:'none', may_disclose_reason_summary:false, max_summary_fidelity:'none' };
}
const COUNT_MODE_RANK: Record<CountDisclosureMode, number> = { none:0, bucketed:1, exact:2 };
const SUMMARY_FIDELITY_RANK: Record<SummaryFidelity, number> = { none:0, generic_reason_only:1, redacted_reason:2, full_reason:3 };
// Adj V11/GPT №8 + CL A-S4: the AND/MIN meet now PRESERVES the rendering refs (count_bucket_policy_ref, reason_summary_template_ref).
// A ref is carried iff its host capability survives the meet AND all contributing vectors agree on it; on CONFLICT it drops to
// undefined and the host mode/flag drops to its floor (fail-closed) — a divergent bucket/template can never be silently picked.
function meetDisclosureVectors(vs: DisclosurePermissionVector[]): DisclosurePermissionVector {
  if (vs.length === 0) return bottomDisclosureVector();
  const count_mode = vs.map(v=>v.count_disclosure_mode).reduce((a,b)=>COUNT_MODE_RANK[b]<COUNT_MODE_RANK[a]?b:a,'exact');
  const summary_fid = vs.map(v=>v.max_summary_fidelity).reduce((a,b)=>SUMMARY_FIDELITY_RANK[b]<SUMMARY_FIDELITY_RANK[a]?b:a,'full_reason');
  const reason_ok = vs.every(v=>v.may_disclose_reason_summary);
  const agree = <T>(xs: (T|undefined)[]) => { const s = new Set(xs.filter(x=>x!==undefined)); return s.size === 1 ? [...s][0] : undefined; };  // conflict ⇒ undefined
  const bucket_ref = count_mode === 'none' ? undefined : agree(vs.map(v=>v.count_bucket_policy_ref));        // dropped on conflict
  const tmpl_ref   = (!reason_ok || summary_fid === 'none') ? undefined : agree(vs.map(v=>v.reason_summary_template_ref));
  return { may_disclose_existence: vs.every(v=>v.may_disclose_existence), may_disclose_container_type: vs.every(v=>v.may_disclose_container_type),
    may_disclose_topic_label: vs.every(v=>v.may_disclose_topic_label), may_disclose_source_title: vs.every(v=>v.may_disclose_source_title),
    count_disclosure_mode: bucket_ref === undefined && count_mode !== 'none' && vs.some(v=>v.count_bucket_policy_ref!==undefined) ? 'none' : count_mode,   // bucket conflict ⇒ no count (fail-closed)
    count_bucket_policy_ref: bucket_ref,
    may_disclose_reason_summary: reason_ok, reason_summary_template_ref: tmpl_ref,
    max_summary_fidelity: summary_fid };
}
// Adj V3 (merge seam: CL A-B2 over-class + GPT №7 under-class) — NEVER falls through to `full`. schema_owner = DOC81.
function deriveDisclosureClass(v: DisclosurePermissionVector): DisclosureClass {   // U8 — the scalar is the vector's summary
  if (!v.may_disclose_existence) return 'not_disclosable';
  const onlyExistence = !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;
  if (onlyExistence) return 'existence_only';
  if (v.may_disclose_source_title || v.count_disclosure_mode === 'exact' || v.max_summary_fidelity === 'full_reason') return 'full';   // GPT №7: source-title/exact-count/full-reason are `full`
  if (v.may_disclose_reason_summary || v.max_summary_fidelity === 'generic_reason_only' || v.max_summary_fidelity === 'redacted_reason') return 'redacted_summary';   // CL A-B2: generic_reason_only no longer launders to full
  return 'generic_safe_label_only';                                                 // else; NEVER a fall-through to full (fail-closed, §1.5)
}
interface CountDisclosurePolicy { mode: CountDisclosureMode; bucket_scheme_ref?: string; exact_count_allowed_only_if_disclosure_class: 'full'; }
// default bucket scheme: 0→"none"; 1→"one"; 2–5→"a few"; 6–10→"several"; >10→"multiple". Exact counts ONLY at disclosure_class='full'.

// S6 consistency (CL §2.6): the granular may_disclose_* booleans are bounded by the ordinal disclosure_class (the ordinal is the CEILING).
const DISCLOSURE_ALLOWS: Record<DisclosureClass, ReadonlyArray<keyof DisclosurePermissionVector>> = {
  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_reason_summary'],
  full: ['may_disclose_existence','may_disclose_container_type','may_disclose_topic_label','may_disclose_source_title','may_disclose_reason_summary'],
};  // lint: disclosure.granular_flag_exceeds_disclosure_class

/** The policy MEANING of a safe label (Adj U33/GPT §4.13/M5). schema_owner = DOC81 (Owner Map line 87; ADQ-308; Round D §6.2).
 *  EC enforces; DOC86 references via SafeLabelRenderContract; vocabulary is PropA's ApprovedSafeLabelVocabularyRegistry.
 *  Labels MUST come from approved vocabulary and MUST NOT be derived from protected content (ADQ-308). */
interface SafeLabelDisclosurePolicy extends E0DurableRecord {
  policy_id: SafeLabelDisclosurePolicyRef;
  schema_owner: 'DOC81';
  disclosure_class: DisclosureClass;                 // §3.0 — derived from disclosure_vector (U8); DOC86 maps to its own AvailabilityDisposition
  disclosure_vector: DisclosurePermissionVector;     // U8 — the executable disclosure model
  protected_reason_class:
    | 'privileged' | 'sealed' | 'firewalled' | 'personal'
    | 'client_confidential' | 'matter_specific' | 'policy_unknown';   // Round D §6.2
  count_disclosure_policy: CountDisclosurePolicy;
  default_label_ref?: SafeReferenceLabelRef;         // GPT M5: REQUIRED iff may_disclose_existence; FORBIDDEN when disclosure_class='not_disclosable'
  internal_suppressed_manifest_label_ref?: SafeReferenceLabelRef;   // GPT §4.13: internal-only audit/Inspector label; never model-visible unless disclosure permits
  inspector_label_ref?: SafeReferenceLabelRef;
  effective_policy_ref: EffectiveMemoryPolicyRef;    // §3.2 — consistent with the disclosure meet
  freshness_key: PolicyRuntimeFreshnessKey;          // §3.0 (U5)
  reason_codes: ReasonCodeId[];
}
```

**Disclosure-to-availability rules (Round D §6.3; ADQ-317 — EC policy evaluator decides).** `blocked_known_exists_not_disclosable` → `suppressed_manifest_only`; **no prompt or UI notice may confirm existence**. `blocked_known_exists_disclosable` → a safe generic notice only (a `blocked_scope_notice` ContextProduct, E0 §3.1). `available_reference_only` → a diagnostic label only, never a compact substantive summary. `not_found` is legal **only when the relevant scope was actually searched** (proven by `ScopeSearchCoverageProof`, §2.6); `not_searched` must not be reported as `not_found` (Round D §6.3). Allowed labels are generic ("Protected cross-scope material", "Policy-restricted material"); forbidden labels name the object ("Marex privileged memo", "14 protected scienter strategy notes") unless `disclosure_class = full`.

**Surface-keyed notice posture (Adj R-3 — architect ruling; default decision postures, NOT new schemas).** Notices are surface-keyed and **walls are invisible**: material behind a declared wall (`firewalled`) produces **NO notice** — existence hidden, no count, nothing (confirms the Round D `not_disclosable` rule). For **non-wall** restrictions (other-matter material, PO material, personal-private, revoked sources): a notice shown to the owning user **in-app** MAY be specific (matter name, exact counts — it is the user's own material on the user's machine), expressed as a *more-open* `ui_disclose` decision; but any notice text that can be **embedded in an output** (`render_*`, `export`, `delegate`, `carryover` — anything that can leave) stays **generic with bucketed counts** (the U8 default). The action-scoped disclosure meet (§4.1) already supports this split: the `ui_disclose` action carries a more-open disclosure decision than the `render_*`/`export` actions for the same object. R2 states this as the default posture pair; no new schema. **Lint:** `disclosure.output_embeddable_notice_specific_for_non_wall` (R-3); `disclosure.wall_produced_a_notice` (R-3 — a wall must be silent). **Fixture:** `fixture.disclosure.ui_notice_specific_but_output_notice_bucketed` (R-3); `fixture.disclosure.wall_produces_no_notice` (R-3).

**Wall-integrity one-liners (Adj V21 — Phase-2 contracts; NON-GATING; dormant for a solo user per §1.6; D-R5: encoded as invariants + lints, NOT built/gating).** Three invariants protect ethical-wall integrity now that the R-5 principal keys are carried; they bite only when a screen actually exists: **(1)** `ScopeSearchCoverageProof.required_scope_refs` (§2.6) MUST exclude scopes `firewalled` relative to the requesting principal (and other-principal scopes) — else the *permanent inability to emit `not_found`* leaks that something undisclosable exists (lint `disclosure.coverage_proof_includes_undisclosable_scope`). **(2)** `SafeLabelDisclosurePolicy.internal_suppressed_manifest_label_ref` and `inspector_label_ref` MUST be absent for `protected_reason_class='firewalled'` material relative to a walled-off principal — else the walled-off principal's own Inspector leaks the wall's existence (lint `disclosure.firewalled_material_has_internal_inspector_label`). **(3)** the `user_disambiguation_candidate` floor is **cause-parameterized** via `conservatism_floor_basis.disambiguation_cause` (§2.4): `firewall`/`sealed` causes ⇒ bottom disclosure vector + **no prompt** unless the prompt is independently safe (a disambiguation prompt must never itself reveal a wall). None of the three gates ratification; each is a one-line invariant + lint for R-5 readiness.

**Lifecycle.** `computed (EC) → carried on notice/inspector/affordance surfaces → invalidated` on generation change. The choice of `suppressed_manifest_only` vs generic notice is EC's policy-evaluator decision keyed on `disclosure_class` (ADQ-317).

**Unhappy paths.** *Label derived from protected content* → `safe_label.derived_from_protected_content` (plan §17.4; ADQ-308). *Blocked notice leaks title/count/source* → `blocked_notice.leaks_title_count_or_source` (plan §17.4). *`not_searched` reported as `not_found`* → `disclosure.not_searched_reported_as_not_found`. *Reference-only carries substantive summary* → `reference_only.contains_substantive_summary` (plan §17.4). *Hidden ref without a disclosure class* → `ui.hidden_ref_without_disclosure_class` (plan §17.4).

**Lints (Stage 9):** `safe_label.derived_from_protected_content` (plan §17.4); `blocked_notice.leaks_title_count_or_source` (plan §17.4); `reference_only.contains_substantive_summary` (plan §17.4); `ui.hidden_ref_without_disclosure_class` (plan §17.4); `disclosure.not_searched_reported_as_not_found`; `disclosure.scalar_class_used_without_permission_vector` (U8); `disclosure.count_disclosed_exact_when_only_bucket_allowed` (U8); `disclosure.granular_flag_exceeds_disclosure_class` (U8/S6); `safe_label.default_label_present_when_not_disclosable` (M5); `safe_label.existence_disclosed_when_not_disclosable` (M5); `safe_label.exact_count_disclosed_without_full_disclosure` (M5).

**Fixtures (Stage 8):** `fixture.disclosure.not_disclosable_suppresses_manifest_only`; `fixture.disclosure.safe_label_from_approved_vocabulary_only`; `fixture.disclosure.blocked_notice_no_title_count_source`; `fixture.disclosure.not_disclosable_has_no_user_visible_label` (M5); `fixture.disclosure.count_bucketed_unless_full` (U8).

**Cross-charter consumption.** EC (enforces; ADQ-317 decision), DOC86 (`SafeLabelRenderContract` + `InspectorVisibilityPlan` consume the meaning — the DOC81→DOC86 export, §9), PropA (owns the vocabulary registry), DOC84 (notice/affordance ContextProducts carry the disclosure class).

### §4.3 `ExtractionRoutePolicyEnvelope`

**Source:** Owner Map line 62 (Round D §1.x extraction policy); Skeletal §DOC81 §3; Opening Brief draft target 10. **Owner:** DOC81. **Consumers:** DOC83 + EC consume (Owner Map line 62). This is the policy envelope wrapped around an extraction route — it caps what the `collect` / `extract` / `classify` / `write_candidate` actions may do **before** content reaches the model, which is the structural defense against over-aggressive ingestion (Commission §1.5).

```typescript
/** Policy envelope on an extraction route — caps pre-model actions. schema_owner = DOC81 (Owner Map line 62).
 *  DOC83 (extraction triage) + EC consume. Wraps the route; does NOT own the route (that is DOC83's
 *  ExtractionRouteContext, Owner Map line 60 / DOC82). */
type ExtractionRouteContextRef = Brand<string, 'ExtractionRouteContextRef'>;          // DOC82-owned route/provenance; referenced
type CollectionGateResultRef = Brand<string, 'CollectionGateResultRef'>;               // EC §1 gate result; referenced
type SourceExclusionFilterRuleRef = Brand<string, 'SourceExclusionFilterRuleRef'>;     // §7.2
type CollectionModeSuppressionGovernanceRef = Brand<string, 'CollectionModeSuppressionGovernanceRef'>;  // §7.1

interface ExtractionRoutePolicyEnvelope extends E0DurableRecord {                       // Adj U19/GPT §4.19.2 — real refs, not booleans
  envelope_id: ExtractionRoutePolicyEnvelopeRef;
  schema_owner: 'DOC81';
  extraction_route_context_ref: ExtractionRouteContextRef;   // DOC82-owned route/provenance context; referenced (was bare `route_ref: string`)
  source_ref?: SourceRef;
  permitted_actions: Extract<MemoryPolicyAction, 'collect' | 'extract' | 'classify' | 'write_candidate'>[];
  effective_policy_ref: EffectiveMemoryPolicyRef;            // §3.2 — the meet the route runs under
  scope_resolution_ref: ScopeResolutionResultRef;           // §2.4 — the route's scope posture
  collection_gate_result_ref?: CollectionGateResultRef;     // EC §1 collection gate (§7.1)
  source_exclusion_rule_refs: SourceExclusionFilterRuleRef[];  // §7.2 — real rule refs (was a bare boolean)
  topic_collection_governance_ref?: CollectionModeSuppressionGovernanceRef;  // §7.1 (ADQ-406)
  unknown_source_disposition: 'fail_closed';                // unclassified source ⇒ conservative (Round D §3.6)
  freshness_key: PolicyRuntimeFreshnessKey;                 // §3.0 (U5)
  reason_codes: ReasonCodeId[];
}
```

**Lifecycle.** `computed at admission (EC, ABC §7.7 Step 0) → re-gated on generation change (§6.4) → discharged at candidate emission`. The envelope is the policy half of the extraction-side re-gate (§6.4): if `policy_generation_id` changes between admission and resolution, the candidate is re-gated under a fresh envelope.

**Unhappy paths.** *Unknown source extracted at full fidelity* → blocked (`fail_closed_on_unknown_source`; Round D §3.6). *Route exceeds `permitted_actions`* → `extraction.route_exceeded_policy_envelope`. *Suppressed topic collected* → `collection.suppressed_topic_admitted` (§7.1; E0 §12.1). *Excluded source extracted* → `source.excluded_source_extracted` (§7.2). *Envelope absent on a privileged-source route* → fail-closed reject.

**Lints (Stage 9):** `extraction.route_exceeded_policy_envelope`; `extraction.unknown_source_not_fail_closed`; `extraction.route_without_policy_envelope`; `policy.extraction_started_under_obsolete_policy_generation` (Skeletal §11.3; §6.4).

**Fixtures (Stage 8):** `fixture.extraction.envelope_caps_permitted_actions`; `fixture.extraction.unknown_source_fails_closed`.

**Cross-charter consumption.** DOC83 (extraction triage consumes the envelope at Step 0), EC (computes + enforces), DOC82 (candidate eligibility bounded by the envelope). DOC81 → DOC83 schema_import (Import Graph §2.1).

### §4.4 `PolicyCappedDAMSInput` (eligibility ceiling) + `contamination_risk` threshold rule

**Source:** Round D §8.2–§8.3; SM-107/SM-106; Owner Map lines 90–92; Skeletal §DOC81 §3; Opening Brief draft target 10; E0 §12.1 (DAMS eligibility ceiling invariant). **Owner:** DOC81 owns the `PolicyCappedDAMSInput` eligibility-ceiling contract (Owner Map line 90) and the `contamination_risk` **threshold rule** (Owner Map line 91). **Computation is DOC84's** (the DAMS substrate; Owner Map line 92 — `contamination_risk` *computation* is DOC84; DAMS is a substrate inside DOC84 per SM-020). DOC81 caps; DOC84 computes; EC constructs the capped input (E0 §12.1).

```typescript
/** The policy-capped DAMS input — DAMS may rank only what policy already made eligible. schema_owner = DOC81
 *  (Owner Map line 90; SM-107). EC constructs (E0 §12.1); DOC84/DAMS consume. */
type DAMSEligibilityCeiling = 'inline_allowed' | 'reference_only_max' | 'search_only_max' | 'notice_only_max' | 'blocked';  // Round D §8.2

interface PolicyCappedDAMSInput extends E0DurableRecord {                               // Adj U21/GPT §4.25 — keyed to the context-product request
  capped_input_id: PolicyCappedDAMSInputRef;
  schema_owner: 'DOC81';
  object_ref: MemoryObjectRef; action: MemoryPolicyAction;
  context_product_request_id: ContextProductRequestRef; context_product_kind: string;                    // keyed to request/kind (U21)
  eligibility_ceiling: DAMSEligibilityCeiling;
  effective_policy_ref: EffectiveMemoryPolicyRef;    // §3.2 — the ceiling DERIVES from the meet (below)
  scope_affinity?: ScopeAffinity;                    // §2.3 — relevance input (NOT warrant)
  freshness_key: PolicyRuntimeFreshnessKey;          // §3.0 (U5)
  persistence_kind: 'derived_projection'; canonical_source_refs: string[]; rebuild_key: string;   // V13 — per context-product request (D-R2 local marker)
  reason_codes: ReasonCodeId[];
}

/** Deterministic ceiling derivation from the effective axes (Adj U21/B10; CL §2.7 ∪ GPT §4.25 incl. disclosure + redacted override). */
interface EligibilityCeilingDerivation extends E0DurableRecord {
  derivation_id: EligibilityCeilingDerivationRef; schema_owner: 'DOC81';
  context_product_request_id: ContextProductRequestRef; context_product_kind: string;
  effective_policy_ref: EffectiveMemoryPolicyRef;
  effective_content_fidelity: ContentFidelityLevel; effective_locality: LocalityLevel;
  effective_disclosure_class: DisclosureClass; effective_disclosure_vector: DisclosurePermissionVector;
  derived_ceiling: DAMSEligibilityCeiling; derivation_rule: 'doc81.dams_ceiling.v1'; reason_codes: ReasonCodeId[];
}
// Formula: locality=blocked OR content=none OR disclosure=not_disclosable → blocked; disclosure=existence_only OR content=safe_label
// → notice_only_max; content=reference_only → reference_only_max (search_only_max if disclosure≤existence_only); content=redacted
// → reference_only_max unless the product kind allows redacted inline (ProductKindCeilingOverride); content=full → inline_allowed unless capped.

/** The contamination-risk MEASUREMENT is DOC84's product (Adj F9; Owner Map L92 computation @ DOC84). DOC81 NAMES the consumed
 *  interface with schema_owner:'DOC84' pending E7/E8 confirmation — a cross-charter handoff, NOT a DOC81-owned schema (one-owner). */
interface ContaminationRiskMeasurement {            // schema_owner: 'DOC84' (named-not-owned here; F9 — confirmed at E7/E8)
  measurement_id: ContaminationRiskMeasurementRef; schema_owner: 'DOC84';
  object_ref: MemoryObjectRef; context_product_request_id: ContextProductRequestRef;
  risk_model_ref: string; risk_model_generation_id: string;   // DOC84-owned model
  risk_score: UnitInterval; confidence: UnitInterval; contributing_feature_refs: string[];   // V17/QF6 — finite [0,1]
  measured_under_policy_ref: EffectiveMemoryPolicyRef; reason_codes: ReasonCodeId[];
}

/** The contamination-risk THRESHOLD rule (a veto, not a linear penalty). schema_owner = DOC81 (Owner Map line 91; SM-106; GPT §4.26).
 *  DOC84/DAMS COMPUTE the risk value (the interface above, Owner Map line 92); DOC81 owns the threshold + reroute rule.
 *  V17/QF4: confidence is consulted — a high-risk/low-confidence and a low-risk/low-confidence score do not pass as high-confidence. */
interface ContaminationRiskThresholdRule extends E0DurableRecord {
  rule_id: ContaminationRiskThresholdRuleRef; schema_owner: 'DOC81';
  domain_profile_ref: DomainProfileId;               // E0 §2.2 — per-domain threshold
  risk_model_ref: string; risk_model_generation_id: string;
  threshold_value: UnitInterval; minimum_measurement_confidence: UnitInterval;   // V17/QF4 — below ⇒ low_confidence_disposition (fail-closed family)
  comparator: 'gte'; equality_rule: 'block_on_equal';   // executable (merges with §4.6 ThresholdEvaluation; polarity = lower_is_safer, §4.6.3)
  low_confidence_disposition: 'fail_closed' | 'route_manual_review_only' | 'reroute_reference_only_notice';   // V17/QF4 — score/confidence NaN|missing OR confidence < minimum
  over_threshold_disposition:                        // when computed risk ≥ threshold (high enough confidence): bypass DAMS salience, reroute:
    | 'reroute_warning_constraint' | 'reroute_blocked_scope_notice' | 'reroute_reference_only_notice' | 'suppress';   // Round D §8.3
  reason_codes: ReasonCodeId[];
}
```

**The two rules (Round D §8.2–§8.3).** *Eligibility ceiling:* DAMS may rank **only** candidates already eligible for the product kind under policy, scope, and warrant; DAMS may **not** convert `blocked` / `reference_only` / `search_only` / `notice_only` into inline render, and may **not** suppress mandatory warnings, safe-label notices, or policy-required products (Round D §8.2; E0 §12.1 invariant). *Contamination veto:* `contamination_risk` is **not** a linear penalty — when the computed value exceeds the domain/policy threshold, the item **bypasses ordinary DAMS salience** and is rerouted to a `WarningConstraint` / `BlockedScopeNotice` / `ReferenceOnlyNotice` / suppression per policy (Round D §8.3; SM-106 retires the linear penalty).

**Lifecycle.** `ceiling constructed (EC) → DAMS ranks within it (DOC84) → invalidated on generation change`. The threshold rule is consulted at every DAMS ranking pass; an over-threshold value short-circuits salience.

**Unhappy paths.** *DAMS converts blocked→inline* → `dams.eligibility_ceiling_violation` (plan §17.4; E0 §12.1). *Linear contamination penalty* → `dams.contamination_treated_as_linear_penalty` (SM-106). *Mandatory warning suppressed by salience* → `dams.suppressed_mandatory_product`. *DAMS bypasses policy entirely* → `supersession.retired_name_used` (`DAMS standalone ranking`, Retired Names SM-021).

**Lints (Stage 9):** `dams.eligibility_ceiling_violation` (plan §17.4; E0 §12.1); `dams.contamination_treated_as_linear_penalty` (SM-106); `dams.suppressed_mandatory_product`; `dams.standalone_ranking_bypassed_policy` (SM-021); `dams.ceiling_without_derivation` (U21); `dams.redacted_treated_as_full_without_product_override` (U21); `contamination.threshold_without_value_or_model` (F9/GPT §4.26).

**Fixtures (Stage 8):** `fixture.dams.cannot_convert_blocked_to_inline`; `fixture.dams.over_threshold_reroutes_not_penalizes`; `fixture.golden.dams_eligibility_ceiling_exceeded_fails` (E0 §12.1 negative fixture); `fixture.dams.ceiling_derives_from_effective_policy_axes` (U21); `fixture.contamination.over_threshold_reroutes_not_linear_penalty` (F9).

**Cross-charter consumption.** DOC84 (computes contamination_risk + DAMS salience within the ceiling — Owner Map line 92), EC (constructs the capped input, E0 §12.1), DOC85 (false-suppression sampling reads policy partitions, Owner Map line 176). The ceiling is the E0 §12.1 "DAMS eligibility ceiling never exceeded" invariant's DOC81-side construction point.

### §4.5 `TopicRiskClass` (ADQ-213)

**Source:** ADQ-213; Owner Map line 194; Opening Brief draft target (PropA fold-in / topic privacy). **Owner:** DOC81 (policy). **Executor:** EC; DOC20 surfaces; **DOC87 consumes for `TopicActivationState` transitions** (Owner Map line 194). Per ADQ-213, a Topic may be auto-created only as an **inert, reviewable lens** — no prospective collection, no substantive injection, no hidden expansion — until `TopicRiskClass` evaluation **and** explicit user confirmation permit each.

```typescript
/** Policy risk classification of a Topic. schema_owner = DOC81 (Owner Map line 194; ADQ-213).
 *  EC executes; DOC20 surfaces; DOC87 consumes for TopicActivationState. Gates collection + injection. */
/** The ADQ-213 confirmation proof (Adj U33/M4/GPT §4.24) — who/when/scope of the confirmation. schema_owner = DOC81. */
interface TopicRiskConfirmation extends E0DurableRecord {
  confirmation_id: TopicRiskConfirmationRef; schema_owner: 'DOC81';
  topic_ref: TopicRef; risk_class_ref: TopicRiskClassRef;
  confirmed_by: PrincipalRef; confirmed_at: string;   // RFC3339-UTC
  confirmation_scope: 'permit_collection' | 'permit_substantive_injection' | 'permit_both';
  freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[];
}

interface TopicRiskClass extends E0DurableRecord {
  risk_class_id: TopicRiskClassRef;
  schema_owner: 'DOC81';
  topic_ref: TopicRef;                                // DOC87-owned Topic identity (ADQ-220); referenced
  risk_level: 'low' | 'medium' | 'high' | 'unbounded_requires_review';   // D12: the sourced value set STANDS (GPT's `unknown` swap DECLINED)
  auto_created_lens_only: boolean;                    // U33/M4 — inert reviewable lens until confirmed (ADQ-213)
  collection_permitted: boolean;                      // false until evaluation + user confirmation (ADQ-213)
  substantive_injection_permitted: boolean;           // false for an inert auto_created_lens (ADQ-213)
  requires_user_confirmation: boolean;
  confirmation_ref?: TopicRiskConfirmationRef;        // U33/M4 — REQUIRED if collection/substantive injection permitted by confirmation
  effective_policy_ref?: EffectiveMemoryPolicyRef;
  reason_codes: ReasonCodeId[];
}
```

**Lifecycle.** `auto_created_lens (inert) → risk-evaluated (EC) → user-confirmed → collection/injection permitted`. The lens is cheap and reversible; collection and injection stay gated until both the risk evaluation and explicit confirmation clear (ADQ-213). DOC87's `TopicActivationState` (`latent_cluster / suggested_review / auto_created_lens / user_confirmed_topic / collection_enabled / paused / archived`, Owner Map line 135) transitions consume this class.

**Unhappy paths.** *Collection before confirmation* → `topic.collection_before_risk_confirmation` (ADQ-213). *Substantive injection from an inert lens* → `topic.inert_lens_injected_substantively`. *Hidden expansion* → `topic.hidden_expansion_without_review`. *`unbounded_requires_review` auto-collected* → blocked.

**Lints (Stage 9):** `topic.collection_before_risk_confirmation` (ADQ-213); `topic.inert_lens_injected_substantively`; `topic.hidden_expansion_without_review`; `topic_risk.collection_permitted_without_confirmation` (U33/M4).

**Fixtures (Stage 8):** `fixture.topic.auto_lens_is_inert_until_confirmed`; `fixture.topic.high_risk_blocks_collection`; `fixture.topic_risk.auto_created_topic_lens_only_until_confirmed` (U33/M4).

**Cross-charter consumption.** EC (executes), DOC87 (`TopicActivationState` transitions — Owner Map line 135/194), DOC20 (surfaces risk), DOC83 (`TopicCollectionDirective` honors the class before collecting). DOC81 → DOC87 is a `schema_import` (Import Graph §2.1).

### §4.6 Fail-closed meet behavior + capability/disclosure lints

**Source:** Round D §3.6; Skeletal §DOC81 §4; ADQ-313; E0 §2.2 conservative fallback; Hard Constraint §7; Opening Brief draft target 9. This section makes the fail-closed default (§1.5) concrete as a meet rule and consolidates the capability/disclosure lints.

**Fail-closed meet rule (normative).** Missing, unknown, incomparable, or low-confidence inputs resolve **conservative** on every axis (the chain ⊥ of §3.1), and the scope floor (§2.4) is met in as a lower bound on restrictiveness:

```text
1. Missing policy input on an axis           → axis = BOTTOM (content_fidelity=none, locality=blocked,
                                                learning_scope=none, mutation_authority=none,
                                                disclosure_class=not_disclosable).
2. Missing/absent domain profile             → DomainProfileRegistry.conservative_fallback (E0 §2.2; ADQ-313).
3. boundary_kind ∈ {cross_scope, firewalled, unknown} AND either side protected/sensitive/
   matter-specific/personal/sealed/firewalled/UNCLASSIFIED
                                              → minimum_conservatism_floor = fail_closed_candidate
                                                or reference_only_candidate (Round D §3.6),
                                                BUT ACTION-AWARE (V4b/A-B3 — see below).
4. scope confidence < domain threshold        → floor ≥ reference_only_candidate (egress-aware per rule 3).
5. destination not typable to E0OutboundDestinationClass
                                              → BLOCK before any attestation (E0 §22 CR2 default-deny).
6. orientation_only is NOT a confidentiality control (Round D §3.6) — never substitutes for a floor.
```

**Rule 3 is ACTION-AWARE (Adj V4b/A-B3/A2-S12 — honors R-1 for internal actions; the architect-confirmed direction of card §4).** Define **INTERNAL** actions = `{retrieve, classify, render_inline, render_reference_only, render_safe_label, ui_disclose, inspect}` (delivered to the owning/authorized principal, locally) and **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 an **INTERNAL** action 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) — recorded in `ScopeResolutionResult.conservatism_floor_basis.r1_qualifying` (§2.4), which the meet's step 3 reads. Cross-matter relevance for internal actions is handled by DAMS contamination ranking (§4.4), **never a floor**. This makes §4.6 stop contradicting §4.0 R-1 (the prior unconditional rule 3 blocked the user from their own cross-matter content — the fifth-basis internal restriction R-1 forbids). **Architect confirm (card §4 confirm #1 — APPROVED):** internal use of the user's own *unclassified* material is permitted; classification gates *sharing*. R-1 governs over Round D §3.6's internal reading.

The dangerous case Round D §3.6 still fixes — for **egress** — is the **unclassified privileged source**: an object whose `sensitive_tag_summary = classification_unknown` crossing an egress boundary fails closed, exactly as a tagged-protected object would.

#### §4.6.1 `ConservatismFloorEffect` — the floor → per-axis effect table (Adj U2/F3; GPT §4.7 shape + the per-cell CL∪GPT seed)

The five floors map to per-axis maxima (the §3.2 step-3 meet uses `FLOOR_EFFECT(floor)`). **Intermediate values are architect-confirm seeds (§13.6); the table's existence is not.**

```typescript
type ScopeConservatismFloor =
  | 'normal_policy_check' | 'reference_only_candidate' | 'safe_label_candidate' | 'user_disambiguation_candidate' | 'fail_closed_candidate';
// Floor total order (B11), ALIGNED to the content chain (most→least restrictive):
//   fail_closed_candidate ⊏ user_disambiguation_candidate ⊏ safe_label_candidate ⊏ reference_only_candidate ⊏ normal_policy_check
// Multiple floors ⇒ MIN (meetFloors).

interface ConservatismFloorEffect extends E0DurableRecord {
  effect_id: ConservatismFloorEffectRef; schema_owner: 'DOC81';   // V20 — the primary ID `floor_effect_ref` (§3.2) points at (was dangling)
  floor: ScopeConservatismFloor;
  max_content_fidelity: ContentFidelityLevel; max_locality: LocalityLevel; max_learning_scope: LearningScopeLevel;
  max_mutation_authority: MutationAuthorityLevel; max_disclosure_class: DisclosureClass; max_disclosure_vector: DisclosurePermissionVector;
  movement_allowed: boolean; disambiguation_required: boolean; allowed_actions_before_disambiguation: MemoryPolicyAction[];
  reason_codes: ReasonCodeId[];
}
// F3 merged seed (per-cell MEET of CL §2.2 + GPT §4.7 — intermediate values are §13.6 seeds):
//   normal_policy_check           → {full, approved_external, global_allowed, durable_allowed, full}; movement ✓
//   reference_only_candidate      → {reference_only, local_only, audit_only, candidate_only, generic_safe_label_only}; movement ✓
//   safe_label_candidate          → {safe_label, local_only, none, none, generic_safe_label_only}; movement ✓
//   user_disambiguation_candidate → {none, blocked, none, none, existence_only}; movement ✗; disambiguation_required ✓ (MUST raise §3.5)
//   fail_closed_candidate         → BOTTOM all axes (bottomDisclosureVector); movement ✗
```

#### §4.6.2 `DomainProfilePolicyContribution` (B3/U1) — the always-present domain contribution

DOC81 owns the **9-axis→5-axis crosswalk semantics** over E0's `DomainProfileRestrictivenessVector` (E0 §2.2); the registry stays E0's (never re-declared). It enters the meet at step 2 as an **always-present** contribution, so the input set is never empty and ⊥ is reserved for "no policy" (kills the usability cliff, B3).

```typescript
interface DomainProfilePolicyContribution extends E0DurableRecord {     // schema_owner = DOC81 (B3/U1/V2); E0 §2.2 owns the registry
  contribution_id: DomainProfilePolicyContributionRef; schema_owner: 'DOC81';
  domain_profile_id: DomainProfileId; action: MemoryPolicyAction;
  contribution: PolicyPoint;                          // 4 capability axes (V1); derived from the E0 9-axis vector (below)
  contribution_disclosure_vector: DisclosurePermissionVector;   // V2/Q1 — the domain's disclosure ceiling AS A VECTOR (so finalize can't overwrite it)
  reason_codes: ReasonCodeId[];
}  // an object may carry MULTIPLE profiles ⇒ the meet includes ALL contributions as states (most-restrictive wins).

// V2/A2-B5 — the 9→5 domain crosswalk (E0 §2.2 → DOC81 §3.0 chains). Per-ACTION axis selection + per-level mapping.
// E0 axes (E0 §2.2): extraction, rendering, disclosure, export, carryover, delegation, learning, retention, source_authority.
// source_authority is a DOC82 warrant concern — NOT a DOC81 policy axis. All maps monotone ('open'=⊤ … 'blocked'=⊥). SEEDS — architect confirm (§13.6).
type E0RestrictivenessLevel = 'open' | 'normal' | 'restricted' | 'highly_restricted' | 'blocked';
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' };

function domainContribution(req): PolicyEffectiveState {   // §3.0 state — point + disclosure_vector (the always-present meet input, V2)
  const v = profileFor(req).restrictiveness_vector;       // E0 §2.2 (referenced, never redefined)
  const localitySrc = req.action === 'export' ? v.export : req.action === 'delegate' ? v.delegation : req.action === 'carryover' ? v.carryover : 'open';  // internal: not egress-constrained
  const contentSrc  = (req.action === 'collect' || req.action === 'extract') ? v.extraction : v.rendering;
  return {
    point: { content_fidelity: LVL_CONTENT[contentSrc], locality: LVL_LOCALITY[localitySrc], learning_scope: LVL_LEARNING[v.learning], mutation_authority: LVL_MUTATION[v.retention] },
    disclosure_vector: disclosureVectorCeilingFor(LVL_DISCLOSE[v.disclosure]),   // §3.0 — the domain disclosure ceiling AS A VECTOR (V2/Q1)
    obligations: [], required_attestations: [], applied_floor_refs: [],
  };
}
```

**V2 (Adj V2/A2-B5; merge seam re-derived).** The meet's always-present domain input (B3) is now **buildable**: the 9→5 crosswalk maps E0's `DomainProfileRestrictivenessVector` (E0 §2.2 — `extraction/rendering/disclosure/export/carryover/delegation/learning/retention/source_authority`) onto DOC81's 4 capability axes + disclosure **vector**, per-action (export→`v.export`, etc.; internal actions unconstrained by the egress axes; `source_authority` stays a DOC82 warrant concern). It emits a `PolicyEffectiveState` so its disclosure ceiling meets in via the vector (V2/Q1 — closes the "domain disclosure overwritten at finalize" leak). Constants are §13.6 seeds; the *existence* of the maps is not. **Lints:** `policy.domain_contribution_derivation_unspecified` (CI guard the maps exist); `domain_profile.restrictiveness_level_semantics_undefined`. **Fixture:** `fixture.policy.domain_contribution_maps_nine_axes_to_five_per_action`; `fixture.policy.domain_disclosure_vector_survives_finalize` (V2/Q1).

#### §4.6.3 `DomainPolicyThresholds` + `ThresholdEvaluation` (B8/U17; GPT §4.17)

```typescript
interface DomainPolicyThresholds extends E0DurableRecord {              // B8 — homes scope_confidence_floor + contamination_ceiling
  thresholds_id: DomainPolicyThresholdsRef; schema_owner: 'DOC81';      // V20 — own primary ID (was foreign DomainProfileId doubling as a DOC81 ID)
  domain_profile_id: DomainProfileId;                                   // the E0 profile these thresholds belong to (FK, not the primary ID)
  scope_confidence_floor: UnitInterval;   // below ⇒ no collapse / floor rises
  contamination_ceiling: UnitInterval;    // at/above ⇒ contamination veto fires
  reason_codes: ReasonCodeId[];
}
// Adj card §2 + V17/QF3 — SPLIT (both reviews converge): 1.0/0.0 are the NO-PROFILE fail-closed FALLBACK only; shipped
// default-profile OPERATIONAL values are usable (≈0.85 confidence / ≈0.7 contamination) — never 0.0 operational. ALL seeds — architect confirm (§13.6).
const THRESHOLD_FALLBACK_NO_PROFILE = { scope_confidence_floor: 1.0, contamination_ceiling: 0.0 } as const;   // maximally fail-closed; ONLY when no profile resolves
const THRESHOLD_SHIPPED_DEFAULT_PROFILE = { scope_confidence_floor: 0.85, contamination_ceiling: 0.70 } as const;   // (seed — architect confirm §13.6) usable out of the box

// Adj V17/QF3 — polarity-specific evaluation, not a generic comparator (some metrics are higher-is-safer, others lower-is-riskier).
type ThresholdPolarity = 'higher_is_safer' | 'lower_is_safer' | 'range_is_safer';
interface ThresholdEvaluation extends E0DurableRecord {                 // U17/GPT §4.17 + QF3
  evaluation_id: ThresholdEvaluationRef; schema_owner: 'DOC81';
  metric_kind: 'scope_equivalence_confidence' | 'scope_resolution_confidence' | 'contamination_risk' | 'topic_risk_score' | 'population_freshness' | 'topic_match_confidence';
  domain_profile_ref: DomainProfileId; domain_profile_registry_version: SchemaVersionRef;
  polarity: ThresholdPolarity; observed_value?: UnitInterval; threshold_value: UnitInterval;
  equality_disposition: 'pass_on_equal' | 'fail_on_equal';   // confidence passes at ≥; risk fails (blocks) at ≥
  missing_or_nan_disposition: 'fail_closed' | 'requires_review';   // unknown/NaN/missing ⇒ fail-closed family (never a permissive default)
  prior_state?: string; enter_threshold_value?: UnitInterval; exit_threshold_value?: UnitInterval; hysteresis_applied: boolean;   // hysteresis may only TIGHTEN safety gates
  disposition: 'pass' | 'fail_closed' | 'requires_review' | 'defer_until_more_evidence';
  reason_codes: ReasonCodeId[];
}
function evaluateThreshold(observed: number | undefined, r: { polarity: ThresholdPolarity; threshold_value: number; equality_disposition: 'pass_on_equal'|'fail_on_equal'; missing_or_nan_disposition: 'fail_closed'|'requires_review' }): 'pass' | 'fail_closed' | 'requires_review' {
  if (observed === undefined || Number.isNaN(observed)) return r.missing_or_nan_disposition;
  const onEqual = () => r.equality_disposition === 'pass_on_equal' ? 'pass' : 'fail_closed';
  if (r.polarity === 'higher_is_safer') return observed > r.threshold_value ? 'pass' : observed === r.threshold_value ? onEqual() : 'fail_closed';
  if (r.polarity === 'lower_is_safer')  return observed < r.threshold_value ? 'pass' : observed === r.threshold_value ? onEqual() : 'fail_closed';
  throw new PolicyLatticeError('range_is_safer requires bounds');   // never silently passes
}
```

#### §4.6.4 `DestinationPolicyCrosswalk` (Adj F5/U7; GPT §4.8 audited shape — RESOLVES §13.5)

A **constraint table** (relation → *allowed* destination classes + floor + attestation flags + disposition), NOT a total function. `relation_to_destination` (scope relation) **never** substitutes for `E0OutboundDestinationClass` (egress class); an unknown terminal destination fails closed **before** classing. (GK's total-function table is **REJECTED** per D3/F5 — it mapped `unknown`/`blocked` to a nonexistent E0 class and invented `same_principal→local_file_export`.)

```typescript
interface DestinationPolicyCrosswalk extends E0DurableRecord {
  crosswalk_id: DestinationPolicyCrosswalkRef; schema_owner: 'DOC81';
  relation_to_destination: 'same_runtime' | 'same_machine_local' | 'same_principal' | 'same_project' | 'same_firewall' | 'external_destination' | 'unknown_destination' | 'blocked_destination';
  outbound_destination_class?: E0OutboundDestinationClass; allowed_outbound_destination_classes?: E0OutboundDestinationClass[];
  policy_floor: ScopeConservatismFloor; egress_attestation_required: boolean; policy_lookup_required: boolean;
  disposition: 'no_egress_same_runtime_only' | 'same_machine_runtime_only' | 'policy_bound_egress' | 'block_until_terminal_destination_resolved' | 'block';
  reason_codes: ReasonCodeId[];
}
// Seed rules (GPT §4.8; §13.6 seed): same_runtime → same_machine_local_runtime, no attestation; same_machine_local → local_file_export,
//   floor normal_policy_check, E0EgressAttestation REQUIRED (§7.4 / §13.6 — treat as egress + attest; do NOT content-floor routine local exports of the user's own work product; D2); external_destination → {local_network_peer|firm_server|remote_peer|cloud_api|
//   email_outbound|agent_messaging}, floor fail_closed_candidate, attestation REQUIRED; unknown_destination → block_until_terminal_destination_resolved;
//   blocked_destination → block. RULE: any terminal bytes leaving same-machine runtime resolve an E0 class and (unless same_machine_local_runtime) carry an E0EgressAttestation.
```

**Capability/disclosure lints (the separation, §4.1).** `policy.capability_and_disclosure_meet_collapsed`; `policy.disclosure_inferred_from_capability`; `policy.capability_inferred_from_disclosure`; `scope.fail_closed_candidate_tag_only` (plan §17.4 — a floor that tags but does not restrict). **Fail-closed lints:** `policy.missing_input_not_floored_conservative`; `policy.unclassified_source_not_fail_closed` (Round D §3.6); `policy.profile_absent_not_conservative_fallback` (E0 §2.2).

**Fixtures (Stage 8):** `fixture.policy.unclassified_privileged_source_fails_closed` (Round D §3.6); `fixture.policy.missing_profile_uses_conservative_fallback` (E0 §2.2); `fixture.policy.fail_closed_floor_actually_restricts` (not tag-only).

**Lints (§4.6.1–§4.6.4).** `policy.floor_without_axis_effect_table` (U2); `scope.multiple_floors_not_min` (B11); `policy.domain_profile_restrictiveness_not_in_meet` (B3); `domain_profile.restrictiveness_level_semantics_undefined` (B3); `policy.threshold_referenced_but_undefined` (B8); `threshold.equality_rule_missing` (U17); `threshold.nan_or_missing_value_not_failed_closed` (U17); `threshold.hysteresis_widened_safety_gate` (U17); `egress.relation_to_destination_used_as_egress_class` (F5/U7); `egress.local_file_export_treated_as_same_runtime` (F5). **Fixtures.** `fixture.policy.floor_mapping_tightens_each_axis` (U2); `fixture.threshold.risk_equal_threshold_blocks` + `fixture.threshold.confidence_equal_threshold_passes` (U17); `fixture.egress.same_machine_local_file_export_requires_attestation` (F5).

**Cross-charter consumption.** EC (applies the fail-closed meet + thresholds + crosswalk), every downstream consumer of `EffectiveMemoryPolicy` inherits the conservative default. This is the E0 §12.1 capability/disclosure-separation invariant's DOC81-side rule (runtime gate + lint + fixture in §10).

### §4.7 Action closure + permission predicates (Adj U29; GPT §4.10)

**Source:** Adj U29 (ACCEPT-MODIFIED — the closure table lands normatively; the predicate minima land as architect-confirm seeds, §13.6). **Owner:** DOC81. Terminal actions imply prerequisite policy checks (a `render_inline` implies `retrieve` + `render_inline` + `ui_disclose` were all policy-checked) — action-specific checking alone leaves real holes (a render with no retrieve-check).

```typescript
interface MemoryPolicyActionClosureRule extends E0DurableRecord {       // the closure table — lands normatively
  rule_id: MemoryPolicyActionClosureRuleRef; schema_owner: 'DOC81';
  requested_action: MemoryPolicyAction; required_policy_actions: MemoryPolicyAction[];
  egress_attestation_required: boolean; memory_flow_certificate_required: boolean; scope_resolution_required: boolean;
  require_effective_policy_per_action: true; reason_codes: ReasonCodeId[];
}
// Seed: render_inline → [retrieve, render_inline, ui_disclose] (+MFC, +scope); export → [retrieve, export, ui_disclose] (+egress attestation,
//   +MFC, +scope); learn_global → [retrieve, learn_global] (+MFC, +scope). (GPT §4.10 closure rules.)

interface ActionPermissionPredicate extends E0DurableRecord {           // per-action required minima — VALUES are §13.6 seeds
  predicate_id: ActionPermissionPredicateRef; schema_owner: 'DOC81'; action: MemoryPolicyAction;
  required_minima: Partial<{ content_fidelity: ContentFidelityLevel; locality: LocalityLevel; learning_scope: LearningScopeLevel; mutation_authority: MutationAuthorityLevel; disclosure_class: DisclosureClass }>;
  forbidden_values?: Partial<Record<'content_fidelity' | 'locality' | 'learning_scope' | 'mutation_authority' | 'disclosure_class', string[]>>;
  requires_destination: boolean; requires_egress_attestation: boolean; requires_mfc: boolean; requires_obligation_discharge: boolean;
  reason_codes: ReasonCodeId[];
}
// The exact minima are architect-confirmable (§13.6); the EXISTENCE of a predicate table is not (U29).

// Adj V23/QF17 — the action predicate the meet's step 7 calls. Evaluates the FINAL state against the per-action minima/forbidden/flag
// requirements and returns a typed result carried on EffectiveMemoryPolicy.action_permission_result. schema_owner = DOC81.
interface ActionPermissionResult {
  disposition: 'permitted' | 'permitted_with_obligations' | 'blocked';
  requires_egress_attestation: boolean; unmet_minima: PolicyAxis[]; hit_forbidden: PolicyAxis[]; reason_code: ReasonCodeId;
}
function evaluateActionPredicate(final: { point: PolicyPoint; disclosure_class: DisclosureClass }, eff: EffectiveMemoryPolicy, p: ActionPermissionPredicate): ActionPermissionResult {
  const CH: Record<PolicyAxis, Chain<any>> = {   // axis-name → chain (LAT is keyed by short names; this maps the long axis names)
    content_fidelity: LAT.content, locality: LAT.locality, learning_scope: LAT.learning,
    mutation_authority: LAT.mutation, disclosure_class: LAT.disclosure, disclosure_vector: LAT.disclosure };
  const val = (ax: PolicyAxis) => (ax === 'disclosure_vector' || ax === 'disclosure_class') ? final.disclosure_class : (final.point as any)[ax];
  const geMin = (ax: string, min: any) => CH[ax as PolicyAxis].leq(min, val(ax as PolicyAxis));   // min ⊑ final ⇒ final at least as permissive as the required minimum
  const unmet = Object.entries(p.required_minima ?? {}).filter(([ax, min]) => !geMin(ax, min)).map(([ax]) => ax as PolicyAxis);
  const hitForbidden = Object.entries(p.forbidden_values ?? {}).filter(([ax, vals]) => (vals as string[]).includes(String(val(ax as PolicyAxis)))).map(([ax]) => ax as PolicyAxis);
  if (unmet.length || hitForbidden.length) return { disposition: 'blocked', requires_egress_attestation: p.requires_egress_attestation, unmet_minima: unmet, hit_forbidden: hitForbidden, reason_code: 'policy.action_predicate_minima_unmet' as ReasonCodeId };
  const withObs = eff.obligations && eff.obligations.length > 0;
  return { disposition: withObs ? 'permitted_with_obligations' : 'permitted', requires_egress_attestation: p.requires_egress_attestation, unmet_minima: [], hit_forbidden: [], reason_code: 'policy.action_permitted' as ReasonCodeId };
}
```

**Lints (Stage 9):** `policy.terminal_action_checked_without_prerequisite_actions` (U29); `policy.action_predicate_missing` (U29). **Fixtures (Stage 8):** `fixture.policy.render_inline_requires_retrieve_and_ui_disclose_policy` (U29); `fixture.policy.export_requires_retrieve_export_ui_disclose_and_egress` (U29).

**Cross-charter consumption.** EC (enforces closure before any terminal action), DOC84 (delivery honors the prerequisite-action checks), DOC85 (`learn_*` closure).

---

## §5. Source revocation + cascading invalidation (E2)

**Source:** Skeletal §10.11 (5-plane fan-out), §11.9 (monotonicity); SM-208 (B3 split); Owner Map lines 94–95; ADQ-304; E0 §12.1 (`SourceRevocationCascade` invariant shape + `revocation_event_ref`); Opening Brief draft target 11. **Owner:** DOC81 owns the **envelope** (Owner Map line 94 — policy-driven); **DOC82** owns the source-side payload (Owner Map line 95); **EC executes** the durable writes/receipts. The 5-plane wiring check is an **explicit Stage 6 charter obligation** (Skeletal §10.11; Acceptance criterion).

### §5.1 `CascadingSourceInvalidation` envelope (B3 split)

```typescript
/** The policy-driven envelope for a source revocation. schema_owner = DOC81 (Owner Map line 94; SM-208 B3 split).
 *  EC executes; the source-side payload is DOC82's (Owner Map line 95). Binds the E0 §12.1 SourceRevocationCascade
 *  invariant shape + revocation_event_ref (E0 §5.1 MemoryMutationEnvelope). */
interface CascadingSourceInvalidation extends E0DurableRecord {
  invalidation_id: CascadingSourceInvalidationRef;
  schema_owner: 'DOC81';
  source_ref: SourceRef;                              // the revoked source (DOC82/DOC25-owned); referenced
  revocation_event_ref: MemoryMutationEnvelopeRef;    // E0 §5.1 — the mutation that recorded the revocation
  source_side_payload_ref: string;                    // DOC82-owned payload (Owner Map line 95); referenced
  affected_set_manifest_ref: string;                  // E0 §12.1 R3 §(b)#5 — names the affected set; per-plane scans downstream
  trigger_reason: 'source_deleted' | 'source_revoked' | 'source_policy_changed' | 'source_integrity_failed';  // U15/GPT §4.20.1
  /** The FIVE settled planes of effect (Skeletal §10.11; E0 §12.1 / N3 / F10). per-plane execution lives in DOC82/84/85/86/87.
   *  `doc84_published_views` is an outcome ROW WITHIN the DOC84 plane (F10/GPT M6), NOT a 6th plane. */
  required_plane_outcomes: {
    doc82_support_edges: 'invalidated' | 'verify_required';
    doc87_memberships: 'restamped' | 'removed' | 'hidden';
    doc84_delivery_artifacts: 'invalidated';            // PriorDeliveryLedger + CarryoverCapsule
    doc84_published_views: 'invalidated_or_restamped';  // row WITHIN the DOC84 plane (F10) — published-view lint owner assigned here (GPT M6)
    doc85_learning_signals: 'ineligible_for_future_utility';
    doc86_surfaces: 'safe_labeled' | 'suppressed';      // per disclosure policy (§4.2)
  };
  freshness_key: PolicyRuntimeFreshnessKey;           // §3.0 (U5)
  reason_codes: ReasonCodeId[];
}

/** Per-plane execution status (Adj U15/GPT §4.20.1 + CL §2.8) — receipts/retry/degraded so no plane silently fails. */
interface PlaneCascadeStatus {
  status: 'pending' | 'completed' | 'blocked' | 'degraded' | 'not_applicable';
  started_at?: string; completed_at?: string; receipt_refs: string[]; retry_count: number; reason_codes: ReasonCodeId[];
}
/** The cascade RUN: idempotent, ordered, resumable. schema_owner = DOC81 (U15). EC executes; per-plane bodies downstream. */
interface CascadingSourceInvalidationRun extends E0DurableRecord {
  run_id: CascadingSourceInvalidationRunRef; schema_owner: 'DOC81';
  invalidation_ref: string; idempotency_key: string;   // EC re-fire is a no-op if applied (E0 §10.5) — outcomes are idempotent monotone-DOWN set-to-floor transitions ⇒ COMMUTE under concurrency
  execution_order: 'freeze_admission_then_support_membership_delivery_learning_ui' | 'architect_confirmed_other';
  pre_fanout_freeze_complete: boolean;                  // V16/A2-S9 — the 5 settled planes MUST NOT begin until DOC83 in-flight freeze is 'completed' (closes the TOCTOU where extraction keeps consuming revoked material mid-cascade)
  published_view_closure: 'transitive_until_fixpoint'; // V16 — a published view built ON a published view is invalidated transitively until no dependent view references the revoked set (no nested-view leak)
  plane_statuses: {
    doc83_inflight_extraction: PlaneCascadeStatus;      // PRE-FANOUT FREEZE (F10) — REQUIRED gate (was optional); NOT a 6th settled plane; stops in-flight extraction using revoked material
    doc82_support_edges: PlaneCascadeStatus; doc87_memberships: PlaneCascadeStatus;
    doc84_delivery_artifacts: PlaneCascadeStatus; doc84_published_views: PlaneCascadeStatus;
    doc85_learning_signals: PlaneCascadeStatus; doc86_surfaces: PlaneCascadeStatus;
  };
  all_required_planes_complete: boolean; degraded_reason_codes: ReasonCodeId[]; freshness_key: PolicyRuntimeFreshnessKey;
}
// Adj V16/QF11 — `all_required_planes_complete` is a DERIVED formula, not a free boolean: the freeze must be complete AND every
// settled plane is 'completed' or 'not_applicable' (a 'pending'/'blocked'/'degraded' plane ⇒ NOT complete). schema_owner = DOC81.
function cascadeComplete(run: CascadingSourceInvalidationRun): boolean {
  if (!run.pre_fanout_freeze_complete) return false;                                  // gate first (TOCTOU)
  const done = (s: PlaneCascadeStatus) => s.status === 'completed' || s.status === 'not_applicable';
  const p = run.plane_statuses;
  return done(p.doc83_inflight_extraction) && done(p.doc82_support_edges) && done(p.doc87_memberships)
      && done(p.doc84_delivery_artifacts) && done(p.doc84_published_views) && done(p.doc85_learning_signals) && done(p.doc86_surfaces);
}
/** Denominator proof for the last-active-support-edge conclusion (Adj U15/GPT §4.20.2; ADQ-304) — closes the conclusion-boolean gap. */
type SupportEdgeStatus = 'active_lawful' | 'inactive_or_revoked' | 'indeterminate';   // V16/QF12 — an edge whose own source is unresolved is NOT silently counted as inactive
interface LastActiveSupportEdgeEvaluation extends E0DurableRecord {
  evaluation_id: LastActiveSupportEdgeEvaluationRef; schema_owner: 'DOC81';
  source_ref: SourceRef; affected_set_manifest_ref: string; evaluated_variant_refs: MemoryObjectRef[];
  edge_statuses: Array<{ edge_ref: string; status: SupportEdgeStatus }>;   // V16/QF12 — per-edge tri-state inputs to the count
  lawful_support_edges_remaining_count: number; remaining_support_edge_refs: string[];
  last_active_support_edge_lost: 'lost' | 'not_lost' | 'indeterminate_fail_closed';   // V16/QF12 — 3-state (was boolean); any 'indeterminate' edge ⇒ indeterminate_fail_closed (treat as retained-but-degraded, never silent total loss)
  polarity_recompute_trace_ref?: string;   // required iff net warrant rises after contrary-source removal (§5.3; E0 §12.1)
  evaluated_under_freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[];
}
// Adj V16/QF12 — the conclusion is derived from the tri-state edge set: only when EVERY remaining edge is provably inactive/revoked
// is the last edge 'lost'; ≥1 active ⇒ 'not_lost'; any 'indeterminate' (and none active) ⇒ 'indeterminate_fail_closed'. schema_owner = DOC81.
function lastActiveSupportEdgeLost(edges: Array<{ status: SupportEdgeStatus }>): LastActiveSupportEdgeEvaluation['last_active_support_edge_lost'] {
  if (edges.some(e => e.status === 'active_lawful')) return 'not_lost';
  if (edges.some(e => e.status === 'indeterminate')) return 'indeterminate_fail_closed';
  return 'lost';
}
```

**Cascade execution (Adj U15; CL §2.8 + GPT §4.20; V16 seam re-derived).** Execution order = **freeze in-flight extraction (DOC83 pre-fanout) → the 5 settled planes** (support → membership → delivery → learning → UI). **The freeze is a hard gate (V16/A2-S9):** no settled plane begins until `pre_fanout_freeze_complete` is true (`doc83_inflight_extraction.status='completed'`) — otherwise extraction keeps minting artifacts from revoked material *during* the cascade (a TOCTOU race the prior optional-freeze shape allowed). Each plane outcome is an **idempotent monotone-DOWN set-to-floor transition**, so a concurrent or re-fired cascade **commutes** (same `idempotency_key` ⇒ no-op if applied). **Nested published views (V16):** the `doc84_published_views` outcome closes **transitively until fixpoint** (`published_view_closure`) — a view built on a now-invalidated view is itself invalidated, so no second-order view survives. **Completion is derived** (`cascadeComplete()`, QF11): a `pending`/`blocked`/`degraded` plane means the run is NOT complete (the boolean can't be set optimistically). **Legal-hold dominance (B17/CL):** the cascade's erasure/destruction outcomes defer to `LegalHoldState` (§6.1) — a held object is invalidated/suppressed but **never destroyed**; the destruction effect is a no-op-with-defer while `held`. The cascade×restamp race is covered by the `epoch_version` CAS (§3.6) + a Stage-8 race fixture (GK).

**ADQ-304 (last-active-support-edge rule) + denominator proof (U15).** A variant retires or becomes `audit_only` **iff** no other lawful support edge from another source remains; **partial** loss → warrant degradation only (ADQ-304). The conclusion is no longer a bare boolean: `LastActiveSupportEdgeEvaluation` (above) carries the **remaining-edge count + refs**, so EC and DOC82 can prove total invalidation vs partial degradation. The recompute is **polarity-aware** (E0 §12.1 / UR-09): removing a *contrary* source MAY raise net warrant — recorded via `polarity_recompute_trace_ref` — but that is a DOC82 warrant recompute, not a policy widening (§5.3 boundary note).

**Lifecycle.** `revocation recorded (EC MemoryMutationEnvelope) → envelope constructed (DOC81) → 5-plane fan-out (per-plane execution) → EC receipts → audited`. The envelope is the policy-side anchor; the source-side payload (DOC82) and per-plane scans run downstream.

**Unhappy paths.** *Cascade missing the affected-set manifest* → `revocation.cascade_missing_affected_set_manifest` (E0 §12.1 R3 §(b)#5). *Any plane not fanned out* → the six §5.2 lints. *Partial loss treated as total* → over-retirement (guarded by `last_active_support_edge_lost` + ADQ-304). *Revocation raises eligibility* → `monotonicity.revocation_raised_eligibility` (§5.3).

**Lints (Stage 9):** `revocation.cascade_missing_affected_set_manifest` (E0 §12.1); `revocation.envelope_without_revocation_event_ref`; `source.deleted_or_clawed_back_support_retained_as_fact` (plan §17.4); `revocation.cascade_plane_out_of_order` (U15); `revocation.cascade_not_idempotent` (U15); `revocation.cascade_partial_not_resumable` (U15); `revocation.cascade_plane_without_receipt` (U15); `revocation.cascade_destroyed_held_object` (B17 — hold dominance); `revocation.last_active_support_edge_boolean_without_denominator` (U15); `revocation.published_view_not_invalidated_after_revocation` (E0 §12.1 — owner assigned to the DOC84 plane row, F10/GPT M6); `revocation.settled_plane_began_before_freeze_complete` (V16/A2-S9); `revocation.nested_published_view_survived_cascade` (V16); `revocation.cascade_complete_set_with_pending_plane` (V16/QF11); `revocation.indeterminate_support_edge_counted_as_lost` (V16/QF12). plus the §5.2 plane lints.

**Fixtures (Stage 8):** `fixture.revocation.last_edge_lost_retires_partial_degrades`; `fixture.golden.revoked_source_invalidates_support_membership_delivery_learning_ui` (E0 §12.1 negative fixture); `fixture.revocation.cascade_run_is_idempotent_per_plane` (U15); `fixture.golden.cascade_defers_to_legal_hold` (B17); `fixture.revocation.cascade_restamp_race` (GK); `fixture.quota.revocation_cascade_chunks_without_skipping_planes` (U15/U25); `fixture.revocation.settled_planes_blocked_until_freeze_complete` (V16); `fixture.revocation.nested_published_view_invalidated_transitively` (V16); `fixture.revocation.indeterminate_edge_fails_to_degraded_not_total_loss` (V16/QF12).

**Cross-charter consumption.** DOC82 (source-side payload + re-proof), DOC84/DOC85/DOC86/DOC87 (per-plane execution), EC (executes; issues receipts). DOC81 owns the envelope; it does not execute the cascade.

### §5.2 The 5-plane fan-out WIRING CHECK (Skeletal §10.11)

**The wiring check is the explicit Stage 6 charter obligation** (Skeletal §10.11; Acceptance criterion). Source revocation MUST fan out to all five planes; each plane has a required outcome and a canonical revocation lint (Skeletal §10.11; E0 §12.1 revocation lint set):

| plane | required outcome on revocation | runtime gate (owner) | Stage 9 lint (canonical) |
|---|---|---|---|
| **DOC82** support edges | `invalidated` / `verify_required` | DOC82 re-proof path; EC `CascadingSourceInvalidation` execution | `revocation.support_edge_survives_revoked_source` |
| **DOC87** source-derived memberships | `restamped` / `removed` / `hidden` | DOC87 `MembershipInvalidationPolicy`; EC | `revocation.membership_survives_revoked_source_without_restamp` |
| **DOC84** `PriorDeliveryLedger` + `CarryoverCapsule` | `invalidated` | DOC84 delivery invalidation; EC | `revocation.carryover_capsule_survives_revoked_source` |
| **DOC85** learning signals | `ineligible_for_future_utility` | DOC85 learning eligibility; EC | `revocation.learning_credit_after_revocation`; `revocation.learning_signal_survives_revoked_source` |
| **DOC86** Inspector surfaces | `safe_labeled` / `suppressed` (per disclosure policy §4.2) | DOC86 `InspectorVisibilityPlan`; EC | `revocation.inspector_leaks_revoked_source` |

The sixth canonical lint covers the published-view case (E0 §12.1): `revocation.published_view_not_invalidated_after_revocation`. These are the **six revocation lints from Skeletal §10.11** (Acceptance criterion). Per E0 §12.1 / UR-08/09 / N3: the cascade is **exactly these 5 planes — no 6th plane and no promotion-time invariant is added** (clawbacks are rare and handled manually; recorded so no later charter silently re-inherits a 6th plane). The polarity lints (`revocation.supporting_source_removed_without_recompute`, `revocation.contrary_source_removed_without_recompute`, `revocation.net_warrant_changed_without_recompute_trace`) attach at DOC82's recompute, referenced here.

**Wiring-check fixture (Stage 8):** `fixture.golden.revoked_source_invalidates_support_membership_delivery_learning_ui` exercises all five planes in one scenario (E0 §12.1). A revocation that misses any plane fails the fixture.

### §5.3 Source-revocation monotonicity (Skeletal §11.9)

**Source:** Skeletal §11.9; E0 §12.1 (source-revocation monotonicity, polarity-aware); ADQ-304; Opening Brief draft target 14 context. **Rule:** source revocation **monotonically lowers** eligibility until re-proof; `CascadingSourceInvalidation` **cannot raise** warrant — only re-proof through a fresh `EvidenceSupportEdge` to a non-revoked source can (Skeletal §11.9; DOC82-owned re-proof). This is one of the four monotonicity laws DOC81 gates (§6.3).

**Polarity boundary note (E0 §12.1 / UR-09 — the supported-only scoping).** The monotonicity law applies to **supported** assertions only. A *contradicted* assertion's net warrant MAY rise on recompute when a contrary source is removed — but this is a **DOC82 warrant recompute keyed to a polarity-aware recompute trace**, not a DOC81 policy widening. DOC81's `EffectiveMemoryPolicy` meet (§3.2) is never widened by a revocation; the only policy-widening path remains `PolicyStampRestamp` (§3.4, ceiling-bounded). The `monotonicity.revocation_raised_eligibility` lint is therefore tied to a polarity-aware recompute trace, not a flat prohibition (E0 §12.1).

**Lints (Stage 9):** `monotonicity.revocation_raised_eligibility` (E0 §12.1 — polarity-aware); `revocation.net_warrant_changed_without_recompute_trace` (E0 §12.1); `source.deleted_or_clawed_back_support_retained_as_fact` (plan §17.4).

**Fixtures (Stage 8):** `fixture.revocation.lowers_until_reproof`; `fixture.revocation.contrary_removal_raises_only_with_recompute_trace`.

**Cross-charter consumption.** DOC82 (re-proof + warrant recompute — owns the polarity logic), EC (executes the monotone-down cascade), DOC85 (learning monotonicity is a sibling law, E0 §12.1). DOC81 owns the envelope + the invariant shape; DOC82 owns the recompute.

---

## §6. Cross-cutting invariants DOC81 owns

DOC81 owns four cross-cutting invariants the rest of the family gates on. Each names a runtime gate, a Stage-9 lint, and (in §10) a Stage-8 negative fixture (Skeletal §11.4; E0 §12.1). Source: Skeletal §10.3, §10.4, §11.3, §11.9; E0 §12.1; ADQ-316; OPA-033.

### §6.1 `LegalHoldState` + destructive-job invariant (Skeletal §10.3; OPA-033)

**Source:** Skeletal §10.3; OPA-033; E0 §1.4 (manual-deletion + unreachable=uninjectable) + §3.3 (`ErasureMFC.legal_hold_clearance_ref`) + §12.1 (LegalHoldState invariant); Opening Brief draft target 12. **Owner:** DOC81 owns the flag + the invariant. **Executor:** EC; every automated destructive job queries it. Hard destruction itself remains **manual** (E0 §1.4 — automated destruction-cascade machinery is NOT in scope for V5; degradation, not destruction, is the privileged-clawback default).

```typescript
/** The legal-hold flag. schema_owner = DOC81 (Owner Map / Skeletal §10.3). EC queries at every destructive job. */
/** Selector semantics (Adj U12/GPT §4.21.1) — disambiguates scope-wide vs explicit-object holds. */
type LegalHoldSelector =
  | { selector_kind: 'scope_wide'; hold_scope_ref: ScopeRef }
  | { selector_kind: 'explicit_objects'; object_refs: MemoryObjectRef[] }
  | { selector_kind: 'scope_plus_explicit_objects'; hold_scope_ref: ScopeRef; object_refs: MemoryObjectRef[] };

interface LegalHoldState extends E0DurableRecord {
  hold_id: LegalHoldStateRef;
  schema_owner: 'DOC81';
  state: 'held' | 'released';                        // Skeletal §10.3 {held, not_held}: 'released' is the post-hold not_held state
  selector: LegalHoldSelector;                        // U12 — replaces the ambiguous hold_scope_ref + optional object_refs
  placed_at: string; placed_by: PrincipalRef;         // litigation-grade provenance
  release_clearance_ref?: string; released_at?: string;
  reason_codes: ReasonCodeId[];
  // No expiry by default — a hold persists until explicitly released (litigation-grade).
}

/** Structural destructive-job gate (Adj U12/CL S3). The gate becomes structural at EC job-registration — a FUTURE destructive
 *  job that is unregistered defaults to BLOCK, so the invariant cannot be skipped by prose omission. schema_owner = DOC81. */
interface DestructiveJobLegalHoldGate extends E0DurableRecord {
  gate_id: DestructiveJobLegalHoldGateRef; schema_owner: 'DOC81';
  job_kind: string; owning_doc: OwnerDocId; executor: 'EC' | 'DOC23' | 'DOC25' | 'DOC72' | 'DOC84' | 'DOC85' | 'other';
  destructive_effect: 'hard_destruction' | 'redaction' | 'retention_expiry' | 'materialization_clearing' | 'semantic_folding' | 'tombstone' | 'archive_or_suppress';
  target_selector_ref: string; legal_hold_query_required: true;
  effect_class: 'destructive' | 'non_destructive';   // V20 — split: hard_destruction/retention_expiry/materialization_clearing/semantic_folding/tombstone = destructive; archive_or_suppress/redaction(visibility) = non_destructive
  held_object_disposition: 'skip' | 'require_manual_clearance' | 'redact_only_with_clearance' | 'block_job';
  held_non_destructive_disposition: 'proceed_suppress_or_hide';   // V20 — a hold suppresses/hides without clearance (visibility ≠ destruction); only DESTRUCTIVE effects defer to clearance
  clearance_required_for: Array<'hard_destruction' | 'redaction' | 'materialization_clearing'>;
  clearance_ref_required: boolean; reason_codes: ReasonCodeId[];
}
// Adj V20 — the effect-class split is canonical, not per-job opinion: schema_owner = DOC81.
const LEGAL_HOLD_EFFECT_CLASS: Record<DestructiveJobLegalHoldGate['destructive_effect'], 'destructive' | 'non_destructive'> = {
  hard_destruction: 'destructive', retention_expiry: 'destructive', materialization_clearing: 'destructive', semantic_folding: 'destructive', tombstone: 'destructive',
  redaction: 'destructive',                 // redaction DESTROYS content ⇒ destructive (needs clearance); visibility-suppression is the archive_or_suppress row
  archive_or_suppress: 'non_destructive',   // a held object may be hidden/suppressed from injection without clearance
};
interface DestructiveJobLegalHoldRegistry extends E0DurableRecord {
  registry_id: 'doc81.destructive_job_legal_hold_registry'; schema_owner: 'DOC81';
  jobs: DestructiveJobLegalHoldGate[];
  default_for_unregistered_destructive_job: 'block';   // U12/CL — STRUCTURAL: unregistered destructive job ⇒ blocked
  reason_code_for_unregistered_job: ReasonCodeId;
}

/** Affirmative authorization to destruct a HELD object (Adj U12/S3; E0 §3.3 LegalHoldClearanceRef now has a schema). schema_owner = DOC81. */
interface LegalHoldClearance extends E0DurableRecord {
  clearance_id: LegalHoldClearanceRef; schema_owner: 'DOC81';
  hold_ref: LegalHoldStateRef; object_refs: MemoryObjectRef[];   // V20 — branded (was bare string)
  authorized_by: PrincipalRef;                        // human (litigation-grade)
  clearance_basis: 'hold_released' | 'court_order' | 'explicit_authorized_destruction';
  reason_codes: ReasonCodeId[];
}
```

**The family-wide invariant (Skeletal §10.3; OPA-033; E0 §12.1).** **Every automated destructive job MUST check `LegalHoldState` before acting and MUST skip held objects.** The known destructive jobs outside DOC80 honor the flag via OPA-033: DOC72 §42 nightly graph cleanup / entropy / semantic-folding; DOC23 task-output retention/expiry; DOC25 source-deletion / materialization-clearing. A hard-destruction or redaction `ErasureMFC` under a hold requires `legal_hold_clearance_ref` (E0 §3.3); a `soft_tombstone` is ungated. This composes with the global recycle-bin (ADQ-407, EC-owned) — `LegalHoldState` blocks hard-purge even at retention expiry.

**Lifecycle.** `not_held → held (placed) → not_held (released)`. While `held`, destructive jobs skip; restore-from-recycle-bin re-evaluates the hold at restore time (E0 §12.1 restore re-evaluation).

**Unhappy paths.** *Destructive job skips the check* → `legal_hold.destructive_job_skipped_check` (E0 §12.1; Skeletal §11.4). *Hard destruction of a held object without clearance* → `erasure.hard_destruction_without_legal_hold_clearance_ref` (E0 §3.3). *Redaction under hold without clearance* → `erasure.redaction_under_legal_hold_without_clearance` (E0 §3.3). *Restore reintroduces held material* → `restore.reintroduces_revoked_or_held_material` (E0 §12.1).

**Destructive vs non-destructive split (Adj V20).** A hold defers **destructive** effects (`LEGAL_HOLD_EFFECT_CLASS='destructive'`: hard-destruction, retention-expiry, materialization-clearing, semantic-folding, tombstone, and content-redaction) until a `LegalHoldClearance`; it does **not** block **non-destructive** visibility effects (`archive_or_suppress` — a held object may be hidden/suppressed from injection without clearance). This keeps a held object out of products while preserving it intact for litigation — suppression is not spoliation.

**Lints (Stage 9):** `legal_hold.destructive_job_skipped_check` (E0 §12.1); `legal_hold.held_object_hard_destroyed_without_clearance` (ties E0 `erasure.*`); `legal_hold.released_without_audit`; `legal_hold.destructive_job_unregistered` (U12 — structural default-block); `legal_hold.selector_semantics_ambiguous` (U12); `legal_hold.destructive_effect_proceeded_as_non_destructive` (V20 — effect-class mislabel bypasses clearance). **Fixture:** `fixture.legal_hold.suppress_proceeds_destroy_defers_under_hold` (V20).

**Fixtures (Stage 8):** `fixture.golden.hard_destruction_without_legal_hold_clearance_fails` (E0 §12.1 negative fixture); `fixture.legal_hold.destructive_jobs_skip_held_objects`; `fixture.legal_hold.unregistered_destructive_job_fails_closed` (U12).

**Cross-charter consumption.** EC (queries at every destructive job; issues `ErasureMFC` with clearance), DOC72/DOC23/DOC25 (the destructive jobs that honor the flag, OPA-033), DOC84/DOC85/DOC11 (consume the `MemoryDestructionLedger`, EC-written per E0 §3.7). Runtime gate + lint + fixture triple in §10.

### §6.2 DOC81 / DOC87 non-overlap invariant + lints (Skeletal §10.4)

**Source:** Skeletal §10.4; E0 §6.3 (classification invariant 3) + §12.1; Hard Constraint §4; Opening Brief draft target 13; Commission §1.5 ("scope containment confused with membership or authority"). **The invariant:** `ScopeContainerRelation` (DOC81, §2.2) is **policy/scope topology**; `MemoryMembershipEdge` (DOC87) is **organization**. Neither implies the other, and **membership confers no access permission, no source authority, and no truth authority** (Skeletal §10.4).

> **Non-overlap (Skeletal §10.4, verbatim semantics):** Membership does NOT imply scope containment; scope containment does NOT imply UI membership; membership does NOT imply access permission, source authority, or truth authority.

The five lints (Skeletal §10.4):

| confusion guarded | Stage 9 lint |
|---|---|
| membership edge read as a scope container relation | `membership_edge_used_as_scope_container_relation` |
| scope container relation read as UI membership | `scope_container_relation_used_as_ui_membership` |
| membership edge treated as policy authority | `membership_edge_used_as_policy_authority` |
| membership edge treated as source authority | `membership_edge_used_as_source_authority` |
| membership edge treated as truth authority | `membership_edge_used_as_truth_authority` |

**Why DOC81 owns this.** The invariant is a scope/policy invariant (it governs what `ScopeContainerRelation` may and may not imply), so it lands here even though `MemoryMembershipEdge` is DOC87's. DOC81 names the invariant + the lints; DOC87's `MembershipLifecycleState` gate and DOC84's packet-assembly check are the runtime gates (E0 §12.1 "removed/blocked membership edge never reaches injection" is the sibling DOC87 invariant). Scope equivalence (§2.1) and containment (§2.2) collapse and relate **identity**, never **authority**.

**Unhappy paths.** *Membership used to widen a policy meet* → `membership_edge_used_as_policy_authority` (the policy-widening-via-membership case; also tripped by monotonicity §6.3). *Containment rendered as UI membership* → `scope_container_relation_used_as_ui_membership`. *Membership cited as truth/source* → the corresponding lint. Each is a hard block, not a warning.

**Lints (Stage 9):** the five Skeletal §10.4 lints above.

**Fixtures (Stage 8):** `fixture.golden.scope_containment_is_not_membership` (negative — containment must not confer membership); `fixture.golden.membership_confers_no_policy_authority` (§10 negative fixture).

**Cross-charter consumption.** DOC87 (the membership owner — must not read DOC81 containment as membership), DOC84 (packet assembly honors both without conflating), DOC86 (UI renders membership and scope distinctly), EC (enforces). Runtime gate + lint + fixture in §10.

### §6.3 Policy monotonicity invariants + lints (Skeletal §11.9; ADQ-316)

**Source:** Skeletal §11.9; E0 §12.1 (policy monotonicity); ADQ-316; Hard Constraint §5; Opening Brief draft target 14. **The law (Skeletal §11.9):** *policy may only narrow without an explicit `PolicyStampRestamp`. `EffectiveMemoryPolicy` meets monotonically restrict; restamp is the ONLY widening path; restamp cannot grant access beyond original ceilings (ADQ-316).*

This is the algebraic statement of §3.2's laws L1 + L4 and §3.4's ceiling rule:

```text
For any effective policy E and any added input or generation change Δ:
   meet(E, Δ) ≤ E    on every axis                          (L1 — monotone restriction)
   the ONLY operation that yields E' with E' > E on any axis is PolicyStampRestamp (§3.4)
   and even then  E'[axis] ≤ original_ceiling_ref[axis]      for every axis (ADQ-316)
```

**Runtime gate.** DOC81 `EffectiveMemoryPolicy` meet (§3.2) + restamp gate (§3.4), executed by EC's compiled policy evaluator (E0 §12.1). Any path that raises an effective axis without a `PolicyStampRestamp` record, or a restamp that exceeds the ceiling, is a violation.

**Unhappy paths.** *Widening without restamp* → `monotonicity.policy_widened_without_restamp` (E0 §12.1). *Restamp exceeds ceiling* → `restamp.exceeds_original_ceiling` (E0 §3.3; ADQ-316). *Membership used to widen* → `membership_edge_used_as_policy_authority` (§6.2). *Stale stamp reused as a widening* → `policy.stale_stamp_used_after_generation_change` (§3.4).

**Lints (Stage 9):** `monotonicity.policy_widened_without_restamp` (E0 §12.1); `restamp.exceeds_original_ceiling` (ADQ-316); `policy.effective_raised_without_restamp_record`.

**Fixtures (Stage 8):** `fixture.golden.policy_tighten_between_plan_and_render_blocks_or_restamps` (E0 §12.1 negative fixture); `fixture.policy.restamp_cannot_exceed_original_ceiling` (ADQ-316). Runtime gate + lint + fixture in §10.

**Consumption-time re-gate (Adj U27a/CL B15 — bitemporal framing).** Tightening is **not retroactive claw-back**: a prior output was legitimate under its generation. Instead, **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` (inside the freshness key); the sticky/ceiling logic (§3.2 step 4) applies at consumption time. Lint `policy.stale_generation_output_consumed_without_regate`.

**Cross-charter consumption.** EC (executes the meet + restamp gate), DOC84/DOC82/DOC85 (every consumer of `EffectiveMemoryPolicy` relies on monotone restriction + re-gates at consumption), DOC86 (restamp eligibility surfacing, §9).

### §6.4 Policy-generation re-gate boundary (Skeletal §11.3)

**Source:** Skeletal §11.3; E0 §8.3 (policy-generation carrier; extraction-side re-gate) + §7.1 (EC resolution-side re-gate); Input Deck §11.3 fold-in. **The boundary:** DOC81 owns the **policy-generation boundary** (`EpisodePolicyEpoch`, §3.6); **DOC83 re-gates** extraction; **EC** re-gates resolution. Three rules attach across the pipeline:

First, **header stability (E0 §8.3 / ADQ-305):** a prompt header/shell is KV-cache-reusable only if it is **both** hash-pinned **and** policy-invariant; `policy_generation_id`, scope resolution, and policy stamps are always volatile and must not be cached across a policy change. DOC81 supplies the volatile policy material; DOC84/KDA own the header ledger (Owner Map line 153).

Second, **the extraction-side re-gate (Skeletal §11.3; E0 §8.3):** if `policy_generation_id` changes between extraction admission (ABC §7.7 Step 0) and resolution (Step 11 / EC durable write), the candidate **MUST be re-gated at Step 0 under the new generation**. **Owner: DOC83 (extraction triage) + DOC81 (policy-generation boundary)** (Skeletal §11.3). DOC81's `ExtractionRoutePolicyEnvelope` (§4.3) is the policy half re-evaluated on re-gate.

Third, **the resolution-side re-gate** is EC's `ECSeamContract` (E0 §7.1) — the compare-and-swap / `dedupe_basis_generation_id` token plus policy-generation re-gating at the durable write. DOC81 binds to it; it does not re-declare it.

**Unhappy paths.** *Extraction started under an obsolete generation* → `policy.extraction_started_under_obsolete_policy_generation` (Skeletal §11.3). *Generation changed without Step-0 re-gate* → `extraction.policy_generation_changed_without_step_0_regate` (Skeletal §11.3). *Stale policy header KV-cached across a change* → `policy.volatile_policy_material_kv_cached` (E0 §8.3 / ADQ-305).

**Lints (Stage 9):** `extraction.policy_generation_changed_without_step_0_regate` (Skeletal §11.3); `policy.extraction_started_under_obsolete_policy_generation` (Skeletal §11.3); `policy.volatile_policy_material_kv_cached` (ADQ-305).

**Fixtures (Stage 8):** `fixture.policy.generation_change_forces_step0_regate`; `fixture.policy.scope_and_stamps_never_kv_cached_across_generation`.

**Cross-charter consumption.** DOC83 (extraction re-gate co-owner), EC (resolution-side re-gate, E0 §7.1), DOC84/KDA (header stability, Owner Map line 153), DOC85 (learning eligibility tied to the generation). DOC81 owns the boundary object; DOC83/EC own the re-gate execution points.

### §6.5 Portability / separation invariant (Adj U27b/S1; CL §2.9 — multi-principal readiness)

**Source:** Adj U27b (ACCEPT); CL S1; E0 §12.1 (portability/separation). An export/portable bundle **MUST preserve scope/principal separation**: it must **not** carry memory eligible only under a *different* principal or scope than the export's `destination_scope_ref` / principal. The §10 gate = `EC §8 export` + the DOC81 `locality`/`disclosure` meet + the `DestinationPolicyCrosswalk` (§4.6.4). This is the cross-principal-bleed invariant that makes R-5 Phase-2 readiness real (the keys are already carried: `principal_ref` on `PolicyEvaluationContext`, `owner_principal_ref` on `ScopeIdentityRoot`).

**Lints (Stage 9):** `portability.cross_principal_bleed` [proposed] (E0 §12.1). **Fixtures (Stage 8):** `fixture.golden.portability_separation_preserved` (E0 §12.1).

### §6.6 Cache keys (Adj U26; GPT §4.28) — DOC81 specializes E0 reproducibility

**Source:** Adj U26 (ACCEPT — components referenced, not re-owned); GPT §4.28; CL B4/B19 substrate. A cached `ScopeResolutionResult` / `EffectiveMemoryPolicy` / `PolicyUIExport` is reusable **only on an exact key match** — no partial reuse across `policy_generation_id`, `effective_state_generation_id`, compiled-evaluator hash, safe-label vocab, source/classification generation, or population generation.

```typescript
interface ScopeResolutionCacheKey {                  // composition over the §3.0 freshness key + scope generations
  request_input_hash: ContentHash; request_scope_ref?: ScopeRef; object_ref: MemoryObjectRef;
  object_scope_ref?: ScopeRef; source_scope_ref?: ScopeRef; destination_scope_ref?: ScopeRef;
  principal_scope: PrincipalScope;   // D3/C2 — REQUIRED sentinel (replaces the optional principal_ref?; the optional shape is exactly the cross-principal bleed V20/R-5 closes)
  scope_topology_generation_id: string; equivalence_binding_generation_id: string; container_relation_generation_id: string;
  scope_population_generation_inputs: Array<{ scope_ref: ScopeRef; scope_population_generation_id: string }>;
  classification_generation_id?: string; domain_profile_registry_version: SchemaVersionRef;
  policy_generation_id: PolicyGenerationId; effective_state_generation_id: EffectiveStateGenerationId;
}
interface EffectivePolicyCacheKey {
  object_ref: MemoryObjectRef; action: MemoryPolicyAction; destination?: E0OutboundDestinationClass;
  principal_scope: PrincipalScope;   // D3/C2 — REQUIRED + must equal the PolicyEvaluationContext principal; the key is self-contained (does not rely on dereferencing policy_evaluation_context_ref to separate principals)
  policy_evaluation_context_ref: PolicyEvaluationContextRef; scope_resolution_ref?: ScopeResolutionResultRef;
  policy_generation_id: PolicyGenerationId; effective_state_generation_id: EffectiveStateGenerationId; compiled_policy_evaluator_hash: ContentHash;
  contributing_decision_hash: ContentHash; obligation_set_hash: ContentHash;
  meet_mode: 'ordinary' | 'restamp_reeval';                        // V7 — restamp_reeval ≠ ordinary result for the same inputs (different sticky/clamp)
  sticky_prior_effective_policy_hash?: ContentHash;                // V7/GPT Q3 — two requests differing ONLY in sticky prior must NOT share a cache entry (the B7/U1 sticky-restrictive leak)
  reason_code_registry_version: SchemaVersionRef; domain_profile_registry_version: SchemaVersionRef;
  safe_label_vocabulary_version?: SchemaVersionRef;   // V20 (conditional): PRESENT iff the keyed policy/export actually carries a safe label (disclosure_class ≤ generic_safe_label_only OR any require_safe_label obligation); absent otherwise — its omission must not silently share a cache entry across a vocab bump for label-bearing exports
}

// Adj V17/QF15 — every ContentHash above is computed under ONE canonicalization (else two equal states hash differently / two
// different states collide). schema_owner = DOC81; defers to the E0 canonical-hash primitive where one exists (E0 §8.5).
interface CanonicalHashSpec {
  spec_id: 'doc81.canonical_hash.v1'; schema_owner: 'DOC81';
  key_ordering: 'lexicographic_by_field_name'; absent_optional: 'omit_key_entirely';   // never serialize undefined
  set_ordering: 'sort_by_branded_id_then_value'; number_form: 'shortest_roundtrip_decimal'; string_form: 'nfc_utf8';
  enum_form: 'literal_token'; boolean_form: 'true_false_literal'; timestamp_form: 'rfc3339_utc_z'; hash_algorithm: 'sha256_hex';
}
```

**Lints (Stage 9):** `cache.policy_reused_across_effective_state_generation` (U26); `cache.safe_label_vocab_change_not_in_ui_export_key` (U26); `cache.scope_resolution_reused_after_population_generation_change` (U26); `cache.effective_policy_reused_across_different_sticky_prior` (V7); `cache.content_hash_computed_without_canonical_spec` (QF15); `cache.policy_key_missing_principal_scope` (D3/C2 — a cache key lacking the required `principal_scope` sentinel). **Fixtures (Stage 8):** `fixture.cache.scope_resolution_invalidates_on_population_generation_change`; `fixture.cache.policy_export_invalidates_on_safe_label_vocab_change`; `fixture.cache.sticky_prior_change_busts_effective_policy_cache` (V7); `fixture.cache.canonical_hash_stable_across_key_reorder` (QF15); `fixture.cache.distinct_principals_do_not_share_cache_entry` (D3/C2).

### §6.7 Fan-out quota envelopes (Adj U25; GPT §4.29) — prevents restamp/cascade storms on a single M4 Pro

**Source:** Adj U25 (ACCEPT — ties to the E0 quota-lint family). DOC81's fan-out operations (restamp batch / cascade / exclude-backfill / traversal / legal-hold scan) carry an estimate + maxima + `on_quota_exceeded` (incl. fail-closed-for-affected) + idempotency + progress.

```typescript
interface DOC81BatchOperationQuotaEnvelope extends E0DurableRecord {
  quota_envelope_id: BatchOperationQuotaEnvelopeRef; schema_owner: 'DOC81';
  operation_kind: 'policy_generation_restamp_batch' | 'source_revocation_cascade' | 'collection_exclude_backfill' | 'relation_traversal_scope_check' | 'legal_hold_destructive_job_scan';
  estimated_object_count: number; estimated_policy_decision_count: number; estimated_scope_resolution_count: number; estimated_plane_updates: number;
  max_object_count: number; max_policy_decision_count: number; max_wall_clock_ms?: number; max_cost_usd?: number;
  on_quota_exceeded: 'chunk_and_continue' | 'pause_and_surface_review' | 'fail_closed_for_affected_items' | 'manual_approval_required' | 'manual_resource_approval_required';
  manual_approval_semantics?: 'resource_usage_only';   // V5/QF10/R-2 — REQUIRED ='resource_usage_only' when operation_kind='policy_generation_restamp_batch': approves spend, NEVER individual restamps (those stay autonomous, §3.4)
  idempotency_key: string; progress_ref?: string; reason_codes: ReasonCodeId[];
}
// Adj V17/QF10 — quota arithmetic is validated, not decorative: every estimate/max is a non-negative finite integer and each
// estimate ≤ its max at admission; a restamp batch MUST NOT use a plain 'manual_approval_required' (that would gate routine
// restamps on a human, violating R-2) — it uses 'manual_resource_approval_required' + manual_approval_semantics='resource_usage_only'.
function validateQuotaEnvelope(q: DOC81BatchOperationQuotaEnvelope): boolean {
  const nn = (n: number | undefined) => n === undefined || (Number.isInteger(n) && n >= 0);
  const within = q.estimated_object_count <= q.max_object_count && q.estimated_policy_decision_count <= q.max_policy_decision_count;
  const r2Guard = q.operation_kind !== 'policy_generation_restamp_batch'
    || (q.on_quota_exceeded !== 'manual_approval_required' && (q.on_quota_exceeded !== 'manual_resource_approval_required' || q.manual_approval_semantics === 'resource_usage_only'));
  return [q.estimated_object_count,q.estimated_policy_decision_count,q.estimated_scope_resolution_count,q.estimated_plane_updates,q.max_object_count,q.max_policy_decision_count,q.max_wall_clock_ms,q.max_cost_usd].every(nn) && within && r2Guard;
}
```

**Lints (Stage 9):** `quota.policy_restamp_batch_unbounded` (U25); `quota.collection_exclude_backfill_unbounded` (U25); `quota.relation_traversal_unbounded` (U25); `quota.revocation_cascade_unbounded` (U25); `quota.estimate_exceeds_max_at_admission` (QF10); `quota.restamp_batch_gates_individual_restamps_on_human` (V5/QF10/R-2). **Fixtures (Stage 8):** `fixture.quota.revocation_cascade_chunks_without_skipping_planes` (U25); `fixture.quota.restamp_batch_manual_approval_is_resources_only` (QF10/R-2). **Cross-charter consumption.** EC (enforces the quota at fan-out); ties to the E0 `MemoryOperationQuota` family (E0 §10).

---

## §7. Topic-level privacy + source exclusion + PropA fold-in

DOC81 is the post-flatten home of the **PropA Knowledge-Pipeline Sensitivity & Self-Improvement** policy content (PropA whole-section retarget to DOC81, OPA V4 §6; CLAUDE.md PropA term). This section lands the two E0-handed obligations (§7.1, §7.4), the new source-exclusion filter (§7.2), and provides landing sites for the 10 PropA OPA rows (§7.3).

### §7.1 `collection_mode` suppression governance (ADQ-406; OBL-D81-TOPIC-COLLECTION-SUPPRESSION-01)

**Source:** ADQ-406 (extends ADQ-205); `OBL-D81-TOPIC-COLLECTION-SUPPRESSION-01` (OPA §6.Z); EC Core Addendum A V3.3 §1/§1.3; E0 §12.1 (collection-suppression gating). **Owner:** DOC81 owns the **suppression governance rules**; **EC §1 collection gate enforces at admission**. `TopicCollectionDirective` itself stays at **DOC83** (Owner Map line 118; Hard Constraint §8) — DOC81 owns only the `collection_mode` governance layer over it. A user-defined "privacy / dummy topic" is a topic whose directive is `suppress`.

```typescript
/** The suppression governance layer over DOC83's TopicCollectionDirective. schema_owner = DOC81 (ADQ-406).
 *  EC §1 collection gate enforces at admission. TopicCollectionDirective stays DOC83 (Owner Map line 118). */
type CollectionMode = 'collect' | 'suppress' | 'exclude';        // ADQ-406 — the polarity DOC81 governs
const COLLECTION_MODE_RANK: Record<CollectionMode, number> = { collect: 0, suppress: 1, exclude: 2 };   // most-restrictive wins (U18)
function meetCollectionMode(modes: CollectionMode[]): CollectionMode {
  if (modes.length === 0) return 'suppress';                     // fail-closed until topic/governance resolved
  return modes.reduce((a, b) => COLLECTION_MODE_RANK[b] > COLLECTION_MODE_RANK[a] ? b : a);
}

interface CollectionModeSuppressionGovernance extends E0DurableRecord {     // Adj U18/GPT §4.18.2
  governance_id: CollectionModeSuppressionGovernanceRef;
  schema_owner: 'DOC81';
  topic_ref: TopicRef;                                            // DOC87-owned Topic identity; the directive stays DOC83 (Owner Map line 118)
  collection_mode: CollectionMode;
  backfill_on_mode_change: 'none' | 'hide_existing' | 'reference_only_existing' | 'invalidate_existing_until_review' | 'legal_hold_preserve_but_suppress_injection';
  existing_material_scan_required: boolean;                       // required when collection_mode = 'exclude' (the promised backfill)
  existing_material_scan_ref?: string;
  freshness_key: PolicyRuntimeFreshnessKey;                       // §3.0 (U5) — keys EC toggle/incognito composition
  reason_codes: ReasonCodeId[];
}

/** The admission-time evaluation (Adj U18 + R-4). schema_owner = DOC81. EC §1 collection gate enforces.
 *  R-4: ambiguous match involving any suppress/exclude candidate ⇒ `defer_review_fail_closed` — NOT collected, listed in a
 *  reviewable queue, NO interrupting prompt. no-topic-match ⇒ ordinary admission (NOT suppress-everything). */
type GovernanceResolutionState = 'resolved' | 'topic_unresolved' | 'governance_unavailable';   // V18/QF9 — governance/topic lookup may itself be unavailable
interface CollectionSuppressionEvaluation extends E0DurableRecord {
  evaluation_id: CollectionSuppressionEvaluationRef; schema_owner: 'DOC81';
  candidate_object_ref?: MemoryObjectRef; topic_ref_candidates: TopicRef[]; matched_governance_refs: string[];
  topic_match_confidence_max: UnitInterval; topic_match_confidence_min: UnitInterval;   // V17/QF6
  topic_match_ambiguity_band: UnitInterval;   // V17/QF8 — if (max−min) > band OR max < admit-threshold with a suppress|exclude candidate ⇒ ambiguous
  ambiguity_state: 'unambiguous' | 'ambiguous' | 'no_topic_match';
  governance_resolution_state: GovernanceResolutionState;   // V18/QF9 — unresolved/unavailable ⇒ fail-closed disposition
  ec_surface_collection_enabled: boolean; ec_incognito_active: boolean; effective_state_generation_id: EffectiveStateGenerationId;
  effective_collection_mode: CollectionMode;
  disposition: 'admit_collect' | 'refuse_suppress' | 'refuse_exclude_and_backfill' | 'defer_review_fail_closed';   // R-4
  existing_material_backfill_required: boolean; existing_material_backfill_ref?: string;
  freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[];
}
// Adj V17/QF8 — ambiguity is a FORMULA, not a free label (a high-max/low-min spread, or a sub-threshold top match alongside any
// suppress|exclude candidate, is ambiguous). schema_owner = DOC81.
function topicMatchAmbiguity(maxc: number, minc: number, band: number, admitThreshold: number, hasRestrictiveCandidate: boolean): CollectionSuppressionEvaluation['ambiguity_state'] {
  if (maxc <= 0) return 'no_topic_match';
  if ((maxc - minc) > band) return 'ambiguous';
  if (maxc < admitThreshold && hasRestrictiveCandidate) return 'ambiguous';
  return 'unambiguous';
}
// Adj V18/QF9 — the disposition selector, executable (the prior prose comment made executable + governance-availability + R-4).
// schema_owner = DOC81. EC §1 enforces. Order: availability gate → EC toggles → ambiguity → mode.
function evaluateCollectionDisposition(e: CollectionSuppressionEvaluation): CollectionSuppressionEvaluation['disposition'] {
  if (e.governance_resolution_state !== 'resolved') return 'defer_review_fail_closed';            // V18 — unresolved governance never silently admits
  if (!e.ec_surface_collection_enabled || e.ec_incognito_active) return 'refuse_suppress';        // EC defense-in-depth (most-restrictive)
  if (e.ambiguity_state === 'no_topic_match') return 'admit_collect';                             // R-4 — ordinary admission, NOT suppress-everything
  const restrictive = e.effective_collection_mode !== 'collect';
  if (e.ambiguity_state === 'ambiguous') return restrictive ? 'defer_review_fail_closed' : 'admit_collect';   // R-4 — reviewable queue, no prompt
  if (e.effective_collection_mode === 'exclude') return 'refuse_exclude_and_backfill';
  if (e.effective_collection_mode === 'suppress') return 'refuse_suppress';
  return 'admit_collect';
}
```

**Governance rules (ADQ-406; OBL-D81-TOPIC-COLLECTION-SUPPRESSION-01).** Content mapped to a `suppress` topic is **not collected or persisted**; admission emits a why-blocked reason code; the directive is user-editable; the layer **composes with EC §1 per-surface toggles + §1.3 incognito as defense-in-depth (most-restrictive wins)**. `exclude` is the stronger form (never collect + actively filter existing). DOC81 owns the rules; EC §1 enforces; the directive schema is DOC83's.

**Lifecycle.** `directive tagged (DOC83) → governance evaluated (DOC81) → EC §1 admission gate { collect | refuse } → why-blocked receipt`. A change to `collection_mode` re-evaluates at the next admission.

**Unhappy paths.** *Suppressed topic admitted* → `collection.suppressed_topic_admitted` (plan §17.4; E0 §12.1). *Collection while subsystem disabled* → `ec.flow_issued_while_subsystem_disabled` (E0 §12.1 effective-state gating). *Governance redefines the DOC83 directive* → one-owner violation (DOC81 governs polarity only). *Privacy topic silently collected* → hard block.

**Lints (Stage 9):** `collection.suppressed_topic_admitted` (E0 §12.1); `ec.flow_issued_while_subsystem_disabled` (E0 §12.1); `collection.governance_redefined_doc83_directive` (one-owner); `collection.ambiguous_topic_match_collected` (U18/R-4); `collection.exclude_without_existing_material_backfill` (U18); `collection.incognito_not_part_of_collection_policy_freshness` (U18); `collection.no_topic_match_suppressed_everything` (R-4 — ordinary admission on no match); `collection.unresolved_governance_admitted` (V18/QF9 — `topic_unresolved`/`governance_unavailable` must defer-fail-closed); `collection.ambiguity_state_set_without_formula` (V17/QF8).

**Fixtures (Stage 8):** `fixture.collection_suppression.suppressed_topic_admitted_fails` (E0 §12.1); `fixture.collection_suppression.disabled_subsystem_flow_fails` (E0 §12.1); `fixture.collection_suppression.privacy_topic_composes_most_restrictive`; `fixture.collection.ambiguous_privacy_topic_fails_closed` (R-4); `fixture.collection.exclude_filters_existing_material` (U18); `fixture.collection.unresolved_governance_defers_fail_closed` (V18/QF9); `fixture.collection.ambiguity_band_spread_marks_ambiguous` (V17/QF8).

**Cross-charter consumption.** EC (§1 collection gate enforces), DOC83 (owns `TopicCollectionDirective`; DOC81 governs `collection_mode`), DOC87 (Topic identity, ADQ-220), DOC86 (surfaces the privacy-topic setting). DOC81 → DOC83 schema_import.

### §7.2 Source-exclusion filter (OBL-PROPA-NEW-SOURCE-EXCLUSION-FILTER-01)

**Source:** `OBL-PROPA-NEW-SOURCE-EXCLUSION-FILTER-01` (OPA §6.A row 1; D3a ADD); DOC24 R3.1.1 §27; Opening Brief draft target 16. **Owner:** DOC81 (the filter rule at the scope/policy layer). **Executor:** EC (under DOC81 scope/policy execution) enforces at retrieval-eligibility evaluation. PropA emits the source-exclusion rule as part of `MemoryPolicyDecision` input (§3.1); exclusion reasons are logged with policy-decision provenance.

```typescript
/** A source-exclusion filter rule at the scope/policy layer. schema_owner = DOC81 (OBL-PROPA-NEW-SOURCE-EXCLUSION-FILTER-01).
 *  PropA emits as MemoryPolicyDecision input; EC enforces at retrieval-eligibility. */
interface SourceExclusionFilterRule extends E0DurableRecord {     // Adj U19/GPT §4.19.1 — INPUT to the meet, not a dependent output
  rule_id: SourceExclusionFilterRuleRef;
  schema_owner: 'DOC81';
  excluded_source_ref: SourceRef;                     // the excluded source (DOC82/DOC25-owned); referenced
  exclusion_scope_ref?: ScopeRef;                      // optional scope the exclusion applies within
  applies_to_actions: MemoryPolicyAction[];            // typically {retrieve, render_inline, extract}
  exclusion_reason_code: ReasonCodeId;                 // logged with policy-decision provenance
  policy_input_decision_ref: MemoryPolicyDecisionRef;  // U19 — the rule is carried as a policy-decision INPUT (fixes the effective_policy_ref input/output cycle)
  contributes_to_effective_policy: true;
  exclusion_closure: 'direct_source_only' | 'direct_source_plus_segments' | 'source_and_derived_artifacts' | 'source_derived_learning_and_delivery_artifacts';
  derived_artifact_classes_blocked: Array<'artifact_segment' | 'evidence_support_edge' | 'source_bound_synthesis' | 'context_product' | 'learning_signal' | 'cached_delivery_artifact'>;
  freshness_key: PolicyRuntimeFreshnessKey;            // §3.0 (U5)
  reason_codes: ReasonCodeId[];
}
```

**Starter acceptance (OBL-PROPA-NEW-SOURCE-EXCLUSION-FILTER-01).** PropA emits the source-exclusion filter rule as part of `MemoryPolicyDecision` input; EC (under DOC81 scope/policy execution) enforces the filter at retrieval-eligibility evaluation; exclusion reasons are logged with policy-decision provenance. Full acceptance criteria / `depends_on` / fixture refs are completed at this charter (OPA §6.A note: "completed at Stage 6 charter authoring").

**Lifecycle.** `rule defined (PropA → DOC81) → enforced at retrieval-eligibility (EC) → logged with provenance → invalidated on generation change`.

**Unhappy paths.** *Excluded source retrieved* → `source.excluded_source_retrieved`. *Exclusion not logged with provenance* → `source.exclusion_without_policy_provenance`. *Exclusion silently dropped on meet* → `policy.obligation_dropped_under_meet` (§3.2). *Excluded source extracted* → guarded at §4.3 (`source.excluded_source_extracted`).

**Lints (Stage 9):** `source.excluded_source_retrieved`; `source.exclusion_without_policy_provenance`; `source.excluded_source_extracted` (§4.3); `source_exclusion.effective_policy_ref_input_output_cycle` (U19); `source_exclusion.derived_artifact_not_excluded` (U19); `extraction.source_exclusion_boolean_without_rule_ref` (U19/§4.3).

**Fixtures (Stage 8):** `fixture.source_exclusion.excluded_source_not_retrievable`; `fixture.source_exclusion.exclusion_logged_with_provenance`; `fixture.source_exclusion.derived_artifact_exclusion_closure` (U19); `fixture.extraction.source_exclusion_rule_ref_blocks_matching_source` (U19).

**Cross-charter consumption.** PropA (emits the rule), EC (enforces at retrieval-eligibility), DOC82 (source/evidence — the excluded source's plane), DOC83 (extraction honors the exclusion via §4.3 envelope).

### §7.3 PropA sensitivity & self-improvement policy fold-in (the 10 OPA rows → landing sites)

**Source:** OPA V4 §6 (10 PropA → DOC81 rows); PropA R6.3 (§0.2 owner split; §1 sensitivity/collection/visibility; §2 sharing/injection/governance; §6 self-review; §7 DSPy); Opening Brief draft target 19; Import Graph §3 (`DOC81 → PropA` ExternalDependencyRecord). **Boundary (PropA §0.2 owner split; verify-don't-redefine, Opening Brief target 19):** **PropA owns** the sensitivity tags/findings/classification lifecycle, source-match semantics, sharing-policy semantics, self-review graphs, and DSPy contracts; **DOC81 owns** the policy/scope contracts those feed into (the meet, obligations, disclosure, eligibility ceilings). DOC81 **compiles PropA policy rules into `EffectiveMemoryPolicy`** (Import Graph §3); it does **not** redefine PropA's content. The 8 carried rows' canonical bodies are sourced from archived `OPA_V3_18.md §6.25` (CODEX fidelity audit 2026-06-01) and each is landed below against its **actual** body. Net correction over the R1 mapping: the V15 family is **DOC73 V1.5.1 learning-pipeline privacy/governance** (PropA owns the prompt-iteration consumer + the DSPy / fixture machinery; **DOC81 owns the policy gates that bound what self-improvement may consume**), and NEW-01 / NEW-02 are policy-decision-input + meet-precedence — not the sensitivity/collection topics the R1 draft mapped them to.

**Landing-site table (every one of the 10 rows has an explicit landing site):**

| OPA row | canonical carried body (`OPA_V3_18.md §6.25`) | DOC81 landing (policy gate / handoff — DOC81 names the gate; it does not redefine PropA internals) |
|---|---|---|
| `OBL-PROPA-NEW-01` | PropA-owned **`ExposureContextSchema`** (exposure-context vocabulary: `automatic_packet_injection` / `explicit_memory_attach` / `outbound_dispatch` / `cache_warming` / `digest_generation` / …); DOC24 passes the context to the EC PolicyDecisionEngine, which returns a decision (`OPA_V3_18.md:3218`) | §3.1 `MemoryPolicyDecision` + §3.2 meet take the PropA `ExposureContextSchema` as a **named policy-evaluation input** — the exposure context keys the decision; DOC81 **references** the PropA schema, never redefines the enum. |
| `OBL-PROPA-NEW-02` | policy **hard-block precedence over BDSM `force_level`** — a policy block is final; `force_level` operates only within policy-eligible candidates (`OPA_V3_18.md:3228`) | §3.2 meet + §4.4: a policy block is **final**; BDSM `force_level` / DAMS salience operate **only inside `PolicyCappedDAMSInput` eligibility** and can never override `EffectiveMemoryPolicy`. |
| `OBL-PROPA-NEW-V15-01` | **`LearningVisibilityScope`** filtering of prompt-iteration signals (DOC73 V1.5.1 §6D.10 / §23.9): sealed-source signals silently discarded; firewalled-source signals same-firewall iteration only (`OPA_V3_18.md:3240`) | §6.3 learning-policy **partition gate**: DOC81 owns the policy rule (sealed → discarded; firewalled → same-firewall); DOC85 / PropA consume it. |
| `OBL-PROPA-NEW-V15-02` | **`SchemaMigrationPlan`** emission for schema-changing prompt iterations (V1.5.1 §6D.13 / §27.9); plan-less iterations route to `manual_review_only` (`OPA_V3_18.md:3250`) | §3.3 `PolicyObligation` (`require_migration_plan`; else route to `manual_review_only`) on schema-changing iterations. |
| `OBL-PROPA-NEW-V15-03` | **`VerifierCalibrationLedger`** gating (V1.5.1 §6.7C / INV-19.5): exclude signals from verifiers with `calibration_status ∈ {uncalibrated, drifting, failed}` (`OPA_V3_18.md:3260`) | §6.3 / §3.3 learning-eligibility gate excluding uncalibrated / drifting / failed-verifier signals. |
| `OBL-PROPA-NEW-V15-04` | **AVAPO regression-fixture exclusion** (V1.5.1 §6D.11): sealed-source fixtures NOT generated; firewalled-source fixtures stay same-firewall (`OPA_V3_18.md:3270`) | §6.3 fixture-generation **privacy gate** (policy rule): sealed → not generated; firewalled → same-firewall. (Fixture machinery is PropA/DOC85; DOC81 owns the rule.) |
| `OBL-PROPA-NEW-V15-05` | **post-retrieval critique prompt surface** (V1.5.1 §5A.4) (`OPA_V3_18.md:3280`) | §3.3 / §6.3 policy + learning-eligibility landing for the post-retrieval critique signal (PropA registers the prompt; DOC81 sets eligibility). |
| `OBL-PROPA-NEW-V15-06` | **counterfactual-ambiguity classification + additive-synthesis** prompt surfaces (V1.5.1 §6.7A / §6.9.3) (`OPA_V3_18.md:3290`) | §3.3 / §6.3 policy + learning-eligibility landing for these prompt-surface signals (PropA registers; DOC81 sets eligibility). |
| `OBL-XDOC-PROPA-DSPY-TARGETS-01` | four DSPy target IDs — `claim_extractor_main`, `outcome_evaluator_main`, `revision_compiler_main`, `outcome_compiler_main` — added to `DspyTargetIdSchemaV4`, each under `DspyTargetEligibilitySchemaV4`; PropA owns the target registry (`OPA_V3_18.md:5449`) | §4.4 `PolicyCappedDAMSInput` + §6.3 learning monotonicity: PropA owns the target registry; **DOC81 supplies the policy eligibility ceilings** — the four targets run under policy ceilings and cannot widen policy or warrant (E0 §12.1). |
| `OBL-PROPA-NEW-SOURCE-EXCLUSION-FILTER-01` | V4 source-exclusion starter body (`OPA_V4.md §6.A`) | §7.2 `SourceExclusionFilterRule` — already landed; keep. |

**The self-improvement boundary (`OBL-XDOC-PROPA-DSPY-TARGETS-01`; PropA §7).** PropA's DSPy self-improvement (§7) optimizes prompts/policies but **may not widen policy or warrant**: DSPy targets run under `PolicyCappedDAMSInput` eligibility (§4.4), are subject to learning monotonicity (§6.3 — learning cannot raise warrant beyond the non-learning ceiling, E0 §12.1), and honor `forbid_global_learning` / `partition_learning` obligations (§3.3). This is the structural defense against self-improvement silently widening policy (Commission §1.5). PropA §7.7 "required safety floors" map to DOC81 obligations; the DSPy *contracts* stay PropA's (PropA §0.2).

**Lints (Stage 9):** `propa.policy_rule_not_compiled_to_effective_policy` (a PropA rule that bypasses the meet); `propa.self_improvement_widened_policy` (DSPy widening — ties §6.3); `propa.sensitivity_tag_without_policy_landing`; `propa.policy_decision_without_exposure_context` (NEW-01); `propa.force_level_overrode_policy_block` (NEW-02); `propa.learning_signal_crossed_visibility_scope` (V15-01); `propa.schema_changing_iteration_without_migration_plan` (V15-02); `propa.uncalibrated_verifier_signal_iterated` (V15-03); `propa.sealed_source_fixture_generated` (V15-04); `propa.dspy_target_unregistered_or_uncapped` (DSPy).

**Fixtures (Stage 8, per-row — a fixture that checks only IDs would pass even with wrong bodies):** `fixture.propa.exposure_context_reaches_policy_engine`; `fixture.propa.policy_block_overrides_force_level`; `fixture.propa.learning_visibility_scope_filters_prompt_iteration` (sealed discarded; firewalled same-firewall); `fixture.propa.schema_changing_iteration_requires_migration_plan`; `fixture.propa.uncalibrated_verifier_signal_excluded`; `fixture.propa.sealed_source_avapo_fixture_not_generated`; `fixture.propa.dspy_target_ids_registered_with_eligibility`; `fixture.propa.dspy_target_cannot_widen_policy`.

**Cross-charter consumption.** PropA (owns the content; emits inputs), EC (compiles + enforces; E0 §7.1 `EC §3`), DOC85 (learning/self-improvement monotonicity), DOC86 (visibility/disclosure surfacing). DOC81 ← PropA is an `ExternalDependencyRecord`-pinned import (Import Graph §3; E0 §7).

### §7.4 PropA `local_file_export` outbound patch (OBL-PROPA-LOCALFILEEXPORT-OUTBOUND-PATCH-01; cross-doc note)

**Source:** `OBL-PROPA-LOCALFILEEXPORT-OUTBOUND-PATCH-01` (OPA §6.Z3); E0 card R3 §22 / C2; PropA R6.3 §2.6 principles #2 (L1116) + #3 (L1117); E0 §22 (`E0OutboundDestinationClass`). **This is a cross-doc note, not a DOC81 schema.** **Owner:** DOC81 / PropA (the patch lands in PropA's §2 outbound matrix; DOC81 records the obligation and binds the egress meaning).

**The inconsistency (verified against PropA §2.6).** PropA §2.6 principle #2 (L1116) enumerates `local_network_peer, firm_server, remote_peer, cloud_api, email_outbound, agent_messaging` as outbound and **omits `local_file_export`**, while principle #3 (L1117) says `same_machine_local_runtime` is the **only** non-egress class. The two are self-inconsistent; `local_file_export` is the likeliest privilege-exfil path (a privileged file written to disk → synced/emailed).

**The obligation (OBL-PROPA-LOCALFILEEXPORT-OUTBOUND-PATCH-01).** Treat `local_file_export` as egress; patch PropA §2.6 principle #2 to include it, or reword as "all destinations except `same_machine_local_runtime` are outbound." **Acceptance:** a privileged artifact → `local_file_export` requires a destination policy decision (fixture `egress.local_file_export_privileged_requires_policy_decision`). **DOC81's role:** the `MemoryPolicyDecision.destination` axis (§3.1) and the meet's destination-typing gate (§3.2 step 4) treat `local_file_export` as a non-`same_machine_local_runtime` destination requiring a policy decision, consistent with E0 §22 INV-E0-EGRESS-1. DOC81 does **not** edit PropA (Hard Constraint §1) — it records the obligation and aligns its destination handling; the PropA §2.6 edit is PropA's discharge item.

**Cross-doc lint (Stage 9):** `egress.local_file_export_privileged_requires_policy_decision` (E0 §22; OPA §6.Z3). **Fixture (Stage 8):** `fixture.egress.local_file_export_privileged_requires_policy_decision`.

**Cross-charter consumption.** PropA (applies the §2.6 patch — its discharge), EC/DOC24 (the egress gate issues the `E0EgressAttestation`, E0 §22), DOC81 (destination-axis alignment). Recorded in §14 OPA-landings as a cross-doc obligation with a landing site, not a DOC81-owned schema.

---

## §8. `AssertionRelationEdge` traversal scope checks (ADQ-315 — pinned to E1)

**Source:** ADQ-315 (resolved; **decision pinned to the E1 charter**); Round D §3.7; Owner Map line 33 (`AssertionRelationEdge` schema is DOC82; **EC executes traversal scope checks**); Opening Brief draft target 17. **Ownership split (no boundary redraw, Hard Constraint §8):** the `AssertionRelationEdge` *schema* is **DOC82's** (Owner Map line 33); **owner-doc EC specifies** `relation_kind → required scope checks + render gating`; the **policy/decision is pinned to DOC81/E1**. DOC81 therefore owns the **traversal-scope-check policy table** (the mapping); DOC82 owns the edge; EC executes.

**The rule (Round D §3.7).** `AssertionRelationEdge` traversal for **conflict projection, analogy, supersession, or comparison injection** MUST run a scope-resolution (§2.4) and effective-policy (§3.2) check **for every related variant**. If a related variant is cross-scope / firewalled / not-disclosable, it **may not render, may not be summarized, may not be named**, and may at most trigger a safe blocked notice **if `ui_disclose` permits existence disclosure** (Round D §3.7; §4.2 disclosure meet).

```typescript
/** The DOC81-owned policy mapping for AssertionRelationEdge traversal (ADQ-315; pinned to E1).
 *  The edge schema is DOC82's (Owner Map line 33); EC executes the checks; DOC81 owns this table. */
/** Closed relation-kind set — the `| string` escape is REMOVED (Adj U11/GPT §4.22). Unknown kinds default-deny via the registry. */
type RelationTraversalPolicyKind = 'conflict_projection' | 'analogy' | 'supersession' | 'comparison_injection';

interface RelationTraversalScopeCheckPolicy extends E0DurableRecord {
  policy_id: RelationTraversalScopeCheckPolicyRef;
  schema_owner: 'DOC81';
  relation_kind: RelationTraversalPolicyKind;                               // closed — no `| string` (U11)
  related_variant_check_required: true;
  required_scope_check: 'resolve_and_policy_check_every_related_variant';   // Round D §3.7
  policy_actions_required: MemoryPolicyAction[]; required_effective_policy_ref_per_variant: true;
  cross_scope_disposition: 'may_not_render_summarize_or_name';              // hard gate
  firewalled_disposition: 'may_not_render_summarize_or_name';
  not_disclosable_disposition: 'safe_blocked_notice_only_if_existence_disclosable';   // §4.2
  allowed_notice_product_kinds: Array<'blocked_scope_notice' | 'reference_only_notice'>;
  forbidden_product_kinds: Array<'assertion_packet' | 'topic_slice' | 'library_source_slice' | 'cu_source_bound_synthesis'>;
  obligations: PolicyObligationRef[];                                        // e.g. forbid_cross_scope_relation_traversal (§3.3)
  reason_codes: ReasonCodeId[];
}

/** Closed registry with default-deny for unknown relation kinds (U11). schema_owner = DOC81. */
interface RelationTraversalPolicyRegistry extends E0DurableRecord {
  registry_id: 'doc81.relation_traversal_policy_registry'; schema_owner: 'DOC81';
  policies: RelationTraversalScopeCheckPolicy[];
  unknown_relation_kind_disposition: 'block' | 'safe_blocked_notice_only_if_existence_disclosable';   // default-deny
  unknown_relation_kind_reason_code: ReasonCodeId;
}

/** Traversal budget — depth/variants/cycle handling; budget-exceeded fails closed (U11/B14; CL MAX_SCOPE_TRAVERSAL_DEPTH=16 seed). */
interface RelationTraversalBudget extends E0DurableRecord {
  budget_id: RelationTraversalBudgetRef; schema_owner: 'DOC81'; relation_kind: RelationTraversalPolicyKind;
  max_depth: number; max_related_variants: number; max_scope_resolutions: number;   // seeds — architect confirm (§13.6)
  cycle_handling: 'visited_set_skip_and_record' | 'fail_closed_on_cycle';
  on_budget_exceeded: 'fail_closed' | 'render_safe_truncated_notice_if_disclosable';
  reason_codes: ReasonCodeId[];
}

/** Per-variant policy-ref trace (U11) — every related variant carries its own scope-resolution + effective-policy ref. */
interface RelationTraversalExecutionTrace extends E0DurableRecord {
  trace_id: RelationTraversalExecutionTraceRef; schema_owner: 'DOC81'; root_object_ref: MemoryObjectRef; relation_kind: string;
  visited_object_refs: MemoryObjectRef[]; skipped_cycle_refs: MemoryObjectRef[]; truncated: boolean; budget_ref: string;
  per_variant_policy_refs: Array<{ object_ref: MemoryObjectRef; scope_resolution_ref: ScopeResolutionResultRef; effective_policy_ref: EffectiveMemoryPolicyRef }>;
  final_disposition: 'all_variants_checked' | 'blocked_budget_exceeded' | 'safe_truncated_notice_only' | 'blocked_unknown_relation_kind';
  reason_codes: ReasonCodeId[];
}
```

**Why pinned to E1.** ADQ-315 defers the *decision* to the E1 charter (the scope/policy lockstep) even though EC executes and DOC82 owns the edge — because the question "what scope checks apply per relation kind" is a scope/policy question. This charter answers it: every traversal of a relation edge for projection/analogy/supersession/comparison runs the full resolve-and-policy-check, and cross-boundary related variants are gated to at most an existence-disclosable safe notice. The `forbid_cross_scope_relation_traversal` obligation (§3.3) is the obligation form.

**Unhappy paths.** *Traversal without per-variant policy check* → `relation.traversal_without_scope_policy_check` (ADQ-315). *Cross-scope variant rendered/named* → `relation.cross_scope_variant_leaked`. *Analogy edge bypasses the check* → `relation.analogy_traversal_bypassed_policy`. *Not-disclosable variant named in a notice* → `relation.not_disclosable_variant_named` (§4.2).

**Lints (Stage 9):** `relation.traversal_without_scope_policy_check` (ADQ-315); `relation.cross_scope_variant_leaked`; `relation.analogy_traversal_bypassed_policy`; `policy.forbid_cross_scope_relation_traversal_not_enforced`; `relation.unknown_kind_without_default_deny` (U11); `relation.traversal_without_budget` (U11); `relation.traversal_cycle_not_handled` (U11); `relation.traversal_kind_string_escape` (U11 — no `| string`).

**Fixtures (Stage 8):** `fixture.relation.cross_scope_traversal_gated` (negative — a cross-scope related variant cannot render); `fixture.relation.every_variant_policy_checked`; `fixture.relation.unknown_relation_kind_fails_closed` (U11); `fixture.relation.cyclic_relation_graph_does_not_loop_or_leak` (U11); `fixture.relation.budget_exceeded_fails_closed` (U11).

**Cross-charter consumption.** DOC82 (owns `AssertionRelationEdge`; consumes the traversal policy), EC (executes traversal scope checks — Owner Map line 33), DOC84 (comparison/conflict injection honors the gate), DOC86 (safe blocked notice on cross-boundary variants). DOC81 owns the policy table only; the edge and execution are DOC82/EC.

---

## §9. DOC81 → DOC86 export (Patch B1)

**Source:** Import Graph §1 (B1 fix) + §2.1 (`DOC81 → DOC86`); Skeletal §DOC81 §6; Owner Map line 89 (DOC86 `SafeLabelRenderContract`); Opening Brief draft target 18. **The export:** DOC81 exports to the UI member (DOC86) **PolicyStamp + EffectiveMemoryPolicy reference + disclosure_class + restamp eligibility + safe-label constraints** (Skeletal §DOC81 §6; Import Graph §1). **Acyclicity (Hard Constraint / Import Graph G-3):** the round-1 cycle `DOC81 → DOC84 → DOC86 → DOC81` was fixed at Patch B1 by **deleting `DOC86 → DOC81` and adding `DOC81 → DOC86`**. This export respects that direction; DOC81 **MUST NOT** import from DOC86 (no reintroduction of the B1 cycle). Any DOC86→DOC81 need is an external control flow through EC (`DOC86 → EC → DOC81`, Import Graph §2.3), not a schema import.

```typescript
/** The DOC81 → DOC86 export bundle (Patch B1). schema_owner = DOC81 (Import Graph §1; Skeletal §DOC81 §6).
 *  Carries policy/disclosure material DOWNWARD to the UI member. DOC86 consumes; DOC81 never imports DOC86. */
interface PolicyUIExport extends E0DurableRecord {     // Adj U13/F7/GPT §4.14 — per-(action,destination) facts; DOC86 renders, never recomputes
  export_id: PolicyUIExportRef;
  schema_owner: 'DOC81';
  object_ref: MemoryObjectRef;
  coordination_trace_ref: MemoryCoordinationTraceRef; // E0 §3.4 — ties the export to the per-request spine
  policy_stamp_ref: PolicyStampRef;                   // §3.4 — `scope_items` live on PolicyStamp now (V20 collapsed PolicyStampScope)
  scope_items: Array<{                                // per (action, destination): the policy FACTS DOC86 renders
    action: MemoryPolicyAction;
    destination?: E0OutboundDestinationClass;
    effective_policy_ref: EffectiveMemoryPolicyRef;   // §3.2 reference only — DOC86 does NOT recompute the meet
    restamp_eligibility: 'eligible' | 'downgrade_only' | 'blocked' | 'requires_disambiguation';   // §3.4
    disclosure_class: DisclosureClass;                // V11/A-S4/R-3 — PER ACTION (ui_disclose may be more open than export)
    disclosure_vector: DisclosurePermissionVector;    // V11/A-S4/R-3 — per action; DOC86 renders specific-in-app vs generic-output from this
    // F7: `visible_action_disposition` is EXCISED — UI availability is DOC86's AvailabilityDisposition (Owner Map L191).
    //     DOC86 DERIVES visibility from these policy facts; DOC81 never names enabled/disabled/hidden.
  }>;
  disclosure_class: DisclosureClass;                  // §4.2 — the most-restrictive SUMMARY across scope_items (per-action is authoritative; V11)
  disclosure_vector: DisclosurePermissionVector;      // §4.2 (U8) — most-restrictive summary
  safe_label_policy_ref: string;
  safe_label_constraints: {                           // §4.2 — what DOC86 SafeLabelRenderContract may render
    default_label_ref?: SafeReferenceLabelRef; inspector_label_ref?: SafeReferenceLabelRef;
    may_disclose_existence: boolean; may_disclose_container_type: boolean; may_disclose_topic_label: boolean;
    may_disclose_source_title: boolean; count_disclosure_mode: CountDisclosureMode; may_disclose_reason_summary: boolean;
  };
  pending_disambiguation_ref?: PolicyDisambiguationRequestRef;   // §3.5
  memory_flow_certificate_ref?: MemoryFlowCertificateId;        // GK — optional Inspector provenance (E0 §3.3)
  expires_at?: string;
  freshness_key: PolicyRuntimeFreshnessKey;           // §3.0 (U5)
  reason_codes: ReasonCodeId[];
}
```

**M8 (Adj U33/CL M8) — the reverse direction is a command, not an import.** A DOC86 → DOC81 need (e.g. a restamp request from the Inspector) is an **EC-routed command/event** (`DOC86 → EC → DOC81`, Import Graph §2.3), never a schema import — this is what keeps §9 downward-only (the B1 acyclic fix). DOC86 consumes `PolicyUIExport` and maps it to its own `AvailabilityDisposition` / `VisibleContextActionSpec` / `InspectorVisibilityPlan`; it MUST NOT recompute `EffectiveMemoryPolicy` (F7).

**What DOC86 does with it (Owner Map lines 87–89).** DOC86 owns `SafeLabelRenderContract` (the UI render) and `InspectorVisibilityPlan` / `UserContextSurfacePlan` (Round D §5); it consumes this export to render restamp affordances, blocked/reference-only notices, and Inspector disclosure — all subject to the same disclosure meet and safe-label discipline as prompt rendering (Round D §5.6 — Inspector is a policy-gated inspect action, not a debug bypass). DOC86 **renders**; it never recomputes policy.

**Unhappy paths.** *DOC81 imports DOC86* → `import_graph.upward_schema_import_to_policy` (Import Graph §2.5; the B1-cycle guard). *Export carries a recomputable meet instead of a reference* → DOC86 might diverge from EC's meet → `policy.ui_recomputed_effective_policy`. *Restamp UI offers a widening beyond eligibility* → `policy.restamp_ui_offered_beyond_eligibility` (ties §3.4 ceiling). *Safe-label constraint omits disclosure class* → `ui.hidden_ref_without_disclosure_class` (§4.2).

**Lints (Stage 9):** `import_graph.upward_schema_import_to_policy` (Import Graph §2.5 — DOC81 must not import DOC86); `policy.ui_recomputed_effective_policy`; `policy.restamp_ui_offered_beyond_eligibility`; `ui.policy_export_missing_action_destination_eligibility` (U13); `ui.doc86_recomputed_effective_policy` (U13/F7); `ui.policy_export_carried_visible_action_disposition` (F7 — DOC86-owned enum must not appear on the export).

**Fixtures (Stage 8):** `fixture.export.doc81_to_doc86_is_downward_only` (acyclicity — no DOC86→DOC81 schema import); `fixture.export.ui_renders_reference_not_recomputed_meet`; `fixture.ui.policy_export_renders_without_recomputing_meet` (U13); `fixture.cache.safe_label_vocab_change_invalidates_policy_ui_export` (U13/U26).

**Cross-charter consumption.** DOC86 (consumes the export — `SafeLabelRenderContract`, `InspectorVisibilityPlan`, restamp UI), DOC20/Q (render the panels), EC (the `DOC86 → EC → DOC81` control flow for restamp commands, Import Graph §2.3). This is the single DOC81 → DOC86 `schema_import` edge (Import Graph §2.1).

---

## §10. Invariant enforcement-point naming (runtime gate + Stage 9 lint + Stage 8 negative fixture per invariant)

**Source:** Skeletal §11.4 (each named invariant has BOTH a runtime gate AND a Stage 9 lint); E0 §12.1 (the cross-family invariants table + UR-48 fixture column); Acceptance criterion ("`LegalHoldState`, policy monotonicity, DOC81/DOC87 non-overlap, and capability/disclosure separation each have a runtime gate + a Stage-9 lint + a Stage-8 negative fixture"). This section is the consolidated enforcement-point table for the **four DOC81-gated invariants the acceptance criteria name**, plus the cascading-revocation invariant (§5). Every row is the anti-"invariant that sounds good but is never enforced" mechanism (E0 §12).

| invariant | runtime gate (owner / location) | Stage 9 lint | Stage 8 negative fixture |
|---|---|---|---|
| **`LegalHoldState` honored by destructive jobs** (§6.1; Skeletal §10.3; OPA-033) | DOC81 `LegalHoldState` query at every destructive-job invocation (DOC72 §42 / DOC23 / DOC25); EC issues `ErasureMFC` with `legal_hold_clearance_ref` | `legal_hold.destructive_job_skipped_check` (E0 §12.1) | `fixture.golden.hard_destruction_without_legal_hold_clearance_fails` (E0 §12.1) |
| **policy monotonicity** (§6.3; Skeletal §11.9; ADQ-316) | DOC81 `EffectiveMemoryPolicy` meet (§3.2) + restamp gate (§3.4), EC compiled evaluator | `monotonicity.policy_widened_without_restamp` (E0 §12.1); `restamp.exceeds_original_ceiling` (ADQ-316) | `fixture.golden.policy_tighten_between_plan_and_render_blocks_or_restamps` (E0 §12.1) |
| **DOC81 / DOC87 non-overlap** (§6.2; Skeletal §10.4) | DOC81 names the invariant; DOC87 `MembershipLifecycleState` gate + DOC84 packet-assembly check execute | `membership_edge_used_as_policy_authority` (+ the 4 sibling §6.2 lints) | `fixture.golden.membership_confers_no_policy_authority` |
| **capability / disclosure separation** (§4.1; Round D §1.6) | DOC81 `EffectiveMemoryPolicy` dual meet (§3.2 — capability axes vs `effective_disclosure_class`), EC | `policy.capability_and_disclosure_meet_collapsed` (Hard Constraint §3) | `fixture.golden.capability_block_with_disclosable_existence_does_not_leak` |
| **DAMS eligibility ceiling never exceeded** (§4.4; E0 §12.1) | EC `PolicyCappedDAMSInput` construction at DOC81; DOC84 DAMS-substrate enforcement | `dams.eligibility_ceiling_violation` (plan §17.4; E0 §12.1) | `fixture.golden.dams_eligibility_ceiling_exceeded_fails` (E0 §12.1) |
| **5-plane source-revocation fan-out** (§5.2; Skeletal §10.11) | EC `CascadingSourceInvalidation` execution; per-plane gates in DOC82/84/85/86/87 | the six §5.2 revocation lints | `fixture.golden.revoked_source_invalidates_support_membership_delivery_learning_ui` (E0 §12.1) |
| **source-revocation monotonicity** (§5.3; Skeletal §11.9) | EC monotone-down cascade; DOC82 re-proof path | `monotonicity.revocation_raised_eligibility` (E0 §12.1, polarity-aware) | `fixture.golden.revoked_source_does_not_raise_eligibility_without_reproof` |
| **collection-suppression gating** (§7.1; ADQ-406) | DOC81 `collection_mode` suppression; EC §1 enforces at admission | `collection.suppressed_topic_admitted` (E0 §12.1) | `fixture.collection_suppression.suppressed_topic_admitted_fails` (E0 §12.1) |
| **runtime freshness** (§3.0; U5) | EC reuses a DOC81 artifact only on exact `PolicyRuntimeFreshnessKey` match | `policy.artifact_missing_effective_state_generation` | `fixture.policy.application_toggle_invalidates_policy_exports` |
| **stamp triple-binding** (§3.4; U4) | EC `PolicyStampScopeItem` per (action, destination), each binding its own `effective_policy_ref` | `policy.stamp_scope_action_without_matching_effective_policy` | `fixture.policy.retrieval_stamp_cannot_authorize_render_or_export` |
| **disclosure coherence** (§3.0/§3.2; B6/U8; CODEX R2-B1) | EC: obligation vector-ceilings met in (step 5) → scalar derived from the FINAL vector → `makeCoherent` LAST (step 6); no disclosure value changes after step 6 | `policy.capability_exceeds_disclosure_ceiling`; `policy.scalar_disclosure_set_independently_of_vector` | `fixture.property.coherence_invariant_always_holds`; `fixture.policy.obligation_ceiling_survives_scalar_derivation` |
| **sticky-restrictive** (§3.2 step 4; B7) | EC meets in `priorEffective` ⇒ input removal cannot widen | `monotonicity.effective_widened_on_input_removal_without_restamp` | `fixture.property.monotone_down_under_add_and_remove` |
| **action closure** (§4.7; U29) | EC `MemoryPolicyActionClosureRule` — terminal action requires prerequisite-action policy | `policy.terminal_action_checked_without_prerequisite_actions` | `fixture.policy.render_inline_requires_retrieve_and_ui_disclose_policy` |
| **dormant-firewall** (§2.1/§6.2; F2) | EC — a firewall is crossed only by an explicit user binding (never inference); dormant for a solo user | `scope.transitive_merge_crossed_firewall` | `fixture.scope.transitive_merge_cannot_cross_firewall` |
| **R-1 internal-use** (§4.0) | EC — an internal-use block requires one of the four R-1 bases | `policy.internal_use_blocked_without_qualifying_basis` | `fixture.policy.internal_use_block_requires_one_of_four_bases` |
| **R-5 principal separation** (§6.5/§12-ter) | EC export gate — no cross-principal bleed; principal keys mandatory | `policy.internal_use_default_assumed_single_principal`; `portability.cross_principal_bleed` | `fixture.golden.portability_separation_preserved` |

The four acceptance-named invariants (`LegalHoldState`, policy monotonicity, DOC81/DOC87 non-overlap, capability/disclosure separation) each have all three enforcement points (gate + lint + fixture) — rows 1–4. Rows 5–8 add the DAMS ceiling, the 5-plane cascade, revocation monotonicity, and collection suppression, completing the DOC81 column of E0 §12.1. DOC81 **names** the invariants and their enforcement points; the **runtime gates live where named** (EC + the member docs); **Stage 9 implements the lints; Stage 8 implements the fixtures** (E0 §12.2 — DOC81 does not implement them here).

---

## §11. Lints + fixtures roll-up (Stage 9 / Stage 8)

This is the consolidated DOC81 lint + fixture inventory (the Skeletal §DOC81 §7 lint suite: policy + scope + capability/disclosure). Lints follow the E0 status convention: `[canonical]` = verbatim from a source; unmarked = `[proposed]` (named at this charter; enforced at Stage 9).

**M1 lint-count fix (Adj U33/CL M1).** Plan §17.4 has **16** policy/scope/UI lints; **11 are DOC81's** (`policy.bare_render_action`, `policy.unresolved_policy_ref`, `policy.undefined_policy_obligation`, `policy.export_stamp_without_destination`, `scope.fail_closed_candidate_tag_only`, `ui.hidden_ref_without_disclosure_class`, `safe_label.derived_from_protected_content`, `blocked_notice.leaks_title_count_or_source`, `dams.eligibility_ceiling_violation`, `source.deleted_or_clawed_back_support_retained_as_fact`, `reference_only.contains_substantive_summary` — all landed in §2–§7), and **5 route to E10/DOC86** (`ui.inspector_leak`, `ui.visible_action_without_command_route_or_noop`, `search_affordance.auto_pull_without_preflight`, `project.auto_link_sensitive_capture_without_quarantine`, `review_queue.gridlock_warning`) — named here, owned by DOC86's charter.

**R2 additions (Adj — every per-section lint/fixture above is the operative list; this roll-up indexes the load-bearing R2 ones).** The R2 clusters add the §3.0 algebra lints (F1/U1/U5/U6), the §3.2 meet-v2 lints (U7/U9/B7), the §3.4 stamp/ceiling/restamp lints (U3/U4) + the R-2 restamp-authority lints, the §4.2 disclosure-vector lints (U8/M5), the §4.6 floor/threshold/crosswalk lints (U2/U17/F5), the §5 cascade-run lints (U15/B17), the §6 legal-hold/portability/cache/quota lints (U12/U27/U26/U25), the §7 suppression/exclusion lints (U18/U19), the §8 traversal lints (U11), and the §9 export lints (U13/F7). **The two R-lints:** `policy.internal_use_blocked_without_qualifying_basis` (R-1, §4.0); `policy.internal_use_default_assumed_single_principal` (R-5, §12-ter). Plus the R-3 notice lints (`disclosure.wall_produced_a_notice`, `disclosure.output_embeddable_notice_specific_for_non_wall`), the §14.2 staleness lint (`propa.opa_landing_table_stale_after_fix`, U14), and the brand lint (`schema.primary_id_not_branded`, U31).

**NI-1 property-fixture family (Adj U33/NI-1; CL NI-1 + GPT §8; R3/V1 disclosure-axis extension) — turns L1–L4 from prose into enforced invariants** (a property test cannot pass while the law is broken): `fixture.property.meet_idempotent`; `fixture.property.meet_commutative`; `fixture.property.meet_associative`; `fixture.property.monotone_down_under_add_and_remove` (B7 — the sticky-restrictive law; **R3/V1: this fixture now exercises the `disclosure_vector` axis too**, not just the four capability axes — the meet is over `PolicyEffectiveState`, so the law must hold for disclosure); `fixture.property.disclosure_monotone_down_under_floor_domain_and_prior` (V1 — the explicit disclosure-axis monotone-down property: floor, domain contribution, and sticky prior each only ever lower the vector); `fixture.property.coherence_invariant_always_holds` (B6); `fixture.property.obligation_resolution_transitive_and_idempotent` (B9); `fixture.property.floor_clamp_idempotent`; `fixture.property.disclosure_derivation_edge_cases` (U8 — incl. V3's four-value enumeration: every derived class is the floor of its vector, never a fall-through to `full`); `fixture.policy.obligation_ceiling_survives_scalar_derivation` (CODEX R2-B1 — `hide_existence` + a more-open vector together MUST yield final scalar `not_disclosable` and `content_fidelity='none'`). These run over generated decision sets in the Stage-8 taxonomy.

**R3 additions (the operative per-section lists are in §2–§9; this indexes the load-bearing R3 ones).** Pipeline (Cluster 1): the §3.0 V1 lints (`policy.disclosure_class_present_on_policy_point`, `policy.policy_state_point_and_vector_met_separately`) + the §3.2 meet lints (V4/V5/V6/V8/V10/V23). Disclosure (Cluster 2): `disclosure.granular_flag_exceeds_disclosure_class` (V3), `policy.obligation_kind_without_typed_parameter_contract` (V9), the V11 per-action/notice lints, the three V21 wall lints. Quant+lifecycle (Cluster 3): `cache.effective_policy_reused_across_different_sticky_prior` (V7), `policy.lattice_error_not_failed_closed` + `policy.decision_scalar_vector_mismatch_not_failed_closed` (V10), `policy.capability_obligation_contradicts_axis` (V14), the V15 cluster-lifecycle lints, the V16 cascade-freeze/nested-view/3-state lints, the V17 QF lints (coverage/threshold/contamination/zero-bucket/quota/canonical-hash), the V18 collection-governance lints, `legal_hold.destructive_effect_proceeded_as_non_destructive` (V20). Bookkeeping (Cluster 4): the six §13.7 minor lints (per-object CAS, attestation-OR, user-instruction-narrowing, unknown-exposure-context, etc.).

The plan §17.4 Round-D lints are all landed in §2–§7 above.

**Scope lints (§2):** `scope.equivalence_binding_below_domain_threshold_collapsed`; `scope.identity_root_missing_kind_not_conservative`; `scope.equivalence_used_as_access_authority`; `scope.equivalence_binding_under_two_refs`; `scope.unknown_boundary_treated_as_same_scope`; `scope.container_relation_widened_meet`; `scope.analogical_relation_used_as_containment`; `scope.boundary_emitted_allow_block_decision`; `scope.affinity_uncertain_not_fail_closed`; `scope.analogical_affinity_promoted_to_direct`; `scope.affinity_used_as_capability_gate`; `scope.resolution_missing_minimum_floor`; `scope.resolution_low_confidence_not_floored`; `scope.resolution_reused_under_stale_policy_generation`; `scope.resolution_emitted_final_allow_block`; `scope.fail_closed_candidate_tag_only` [canonical, plan §17.4]; `scope.population_stale_served_as_fresh`; `scope.population_unavailable_not_fail_closed`; `scope.null_result_without_population_generation`.

**Policy lints (§3):** `policy.scalar_decision_used`; `policy.bare_render_action` [canonical, plan §17.4]; `policy.action_without_required_destination`; `policy.axis_missing_not_floored_conservative`; `policy.undefined_policy_obligation` [canonical, plan §17.4]; `policy.ranked_union_over_scalar_results`; `policy.meet_across_actions`; `policy.effective_undercut_scope_floor`; `policy.unresolved_policy_ref` [canonical, plan §17.4]; `policy.obligation_dropped_under_meet`; `policy.membrane_decision_used_as_capability`; `policy.blocking_obligation_bypassed`; `policy.obligation_enforcement_owner_doc8`; `restamp.exceeds_original_ceiling` [canonical, E0 §3.3]; `policy.export_stamp_without_destination` [canonical, plan §17.4]; `policy.stale_stamp_used_after_generation_change`; `policy.invalidation_summary_leaks_object`; `policy.disambiguation_prompt_leaks_protected_identity`; `policy.disambiguation_without_fallback`; `supersession.retired_name_used` (`ask_user` / `ScopeMembrane` / scalar decisions) [canonical]; `policy.disambiguation_default_allow`; `policy.episode_generation_change_without_restamp`; `policy.in_flight_product_under_closed_epoch`; `policy.carryover_across_epoch_unstamped`; `policy.epoch_never_closed`.

**Capability/disclosure + coordination lints (§4):** `policy.capability_and_disclosure_meet_collapsed`; `policy.disclosure_inferred_from_capability`; `policy.capability_inferred_from_disclosure`; `safe_label.derived_from_protected_content` [canonical, plan §17.4]; `blocked_notice.leaks_title_count_or_source` [canonical, plan §17.4]; `reference_only.contains_substantive_summary` [canonical, plan §17.4]; `ui.hidden_ref_without_disclosure_class` [canonical, plan §17.4]; `disclosure.not_searched_reported_as_not_found`; `extraction.route_exceeded_policy_envelope`; `extraction.unknown_source_not_fail_closed`; `extraction.route_without_policy_envelope`; `policy.extraction_started_under_obsolete_policy_generation` [canonical, Skeletal §11.3]; `dams.eligibility_ceiling_violation` [canonical, plan §17.4]; `dams.contamination_treated_as_linear_penalty`; `dams.suppressed_mandatory_product`; `dams.standalone_ranking_bypassed_policy`; `topic.collection_before_risk_confirmation`; `topic.inert_lens_injected_substantively`; `topic.hidden_expansion_without_review`; `policy.missing_input_not_floored_conservative`; `policy.unclassified_source_not_fail_closed`; `policy.profile_absent_not_conservative_fallback`.

**Invariant + cascade lints (§5–§10):** `revocation.cascade_missing_affected_set_manifest` [canonical, E0 §12.1]; `revocation.envelope_without_revocation_event_ref`; the six §5.2 plane lints (`revocation.support_edge_survives_revoked_source` / `…membership_survives_revoked_source_without_restamp` / `…carryover_capsule_survives_revoked_source` / `…learning_credit_after_revocation` / `…inspector_leaks_revoked_source` / `…published_view_not_invalidated_after_revocation`) [canonical, E0 §12.1]; `source.deleted_or_clawed_back_support_retained_as_fact` [canonical, plan §17.4]; `monotonicity.revocation_raised_eligibility` [canonical, E0 §12.1]; `monotonicity.policy_widened_without_restamp` [canonical, E0 §12.1]; `legal_hold.destructive_job_skipped_check` [canonical, E0 §12.1]; the five §6.2 non-overlap lints [canonical, Skeletal §10.4]; `import_graph.upward_schema_import_to_policy` [canonical, Import Graph §2.5].

**§7 fold-in lints:** `collection.suppressed_topic_admitted` [canonical, E0 §12.1]; `ec.flow_issued_while_subsystem_disabled` [canonical, E0 §12.1]; `source.excluded_source_retrieved`; `source.exclusion_without_policy_provenance`; `propa.policy_rule_not_compiled_to_effective_policy`; `propa.self_improvement_widened_policy`; `egress.local_file_export_privileged_requires_policy_decision` [canonical, E0 §22]; the §8 relation-traversal lints.

**Key fixtures (Stage 8) — the negative/invariant set.** The eight §10 golden fixtures (one per invariant) plus: `fixture.policy.meet_is_per_dimension_most_restrictive`; `fixture.policy.restamp_cannot_exceed_original_ceiling`; `fixture.policy.empty_inputs_fail_closed`; `fixture.policy.unclassified_privileged_source_fails_closed`; `fixture.scope.unresolved_object_fails_closed`; `fixture.disclosure.not_disclosable_suppresses_manifest_only`; `fixture.propa.learning_visibility_scope_filters_prompt_iteration`; `fixture.propa.dspy_target_cannot_widen_policy`; `fixture.relation.cross_scope_traversal_gated`; `fixture.collection_suppression.suppressed_topic_admitted_fails`. Per-contract fixtures are listed in §2–§9.

---

## §12. Cross-charter dependencies (what E1/E2 unblocks; what it binds to in E0)

**What DOC81 binds to in E0 (consume-by-reference; §0; §1.4).** ReasonCode registry (E0 §2.1); DomainProfile registry + conservative fallback + per-axis meet (E0 §2.2); warrant-degradation-trigger `policy_change` → restamp eligibility (E0 §2.4); the `EffectiveMemoryPolicyRef` consumption-protocol pointer (E0 §3.2); `MemoryFlowCertificate` + `RestampMFC`/`ErasureMFC`/`RestoreMFC` (E0 §3.3); `MemoryCoordinationTrace` (E0 §3.4); `MemoryMutationEnvelope` (E0 §5.1, NAMED); the policy-generation carrier + re-gate (E0 §8.3) + EC resolution-side re-gate (E0 §7.1); the §12.1 invariant set; the §22 egress vocabulary. **No E0 registry or carrier is re-declared** (Hard Constraint §2).

**What DOC81 imports externally (`ExternalDependencyRecord`-pinned, E0 §7; Import Graph §3).** PropA (policy rules → compiled into `EffectiveMemoryPolicy`; §7.3); EC (policy enforcement + `ScopeResolutionResult` computation; `dependency_status = partial/moving`).

**What E1/E2 unblocks (Opening Brief; Input Deck pre-conditions).** DOC81 is a **pre-condition** for: **E3/E4 (DOC82)** — knowledge writes gate on `mutation_authority` + `ScopeResolutionResult`; **E5/E6 (DOC83)** — extraction re-gates on `policy_generation` (§6.4) + consumes `ExtractionRoutePolicyEnvelope` (§4.3); **E7/E8 (DOC84)** — delivery caps on `PolicyCappedDAMSInput` + `EffectiveMemoryPolicy` (§3.2/§4.4) and implements `PolicyDisambiguationRequest` park/resume (§3.5); **E_org (DOC87)** — consumes `TopicRiskClass` (§4.5) and honors non-overlap (§6.2); **E9 (DOC85)** — learning monotonicity + `forbid_global_learning`/`partition_learning` (§6.3/§3.3); **E10 (DOC86)** — surfaces the DOC81→DOC86 export (§9). EC executes all of it.

**Schema_import edges DOC81 emits (Import Graph §2.1).** `DOC81 → {DOC82, DOC83, DOC84, DOC85, DOC86, DOC87}` carrying `EffectiveMemoryPolicy` contract, `ScopeResolutionResult`, `PolicyStamp`, `disclosure_class`, restamp-eligibility contract, `SafeLabelDisclosurePolicy`. **Acyclic** (topological position 2; no upward edge; B1 cycle not reintroduced, §9).

**§12-bis Golden scenario + degraded states (Adj U33/CL M5).** DOC81 owns golden-scenario (plan §18) **step 2 (policy preflight)** and **step 8 (mid-episode policy re-check)**, and contributes **step 11.5 (egress gate)** for the `same_firewall_only` chain change (U20) + the egress phase. The §20 `MemoryCoordinationTrace` row carries the DOC81 obligations (scope/policy refs + the freshness key, §3.0). Degraded states surfaced to the plan: `policy_disambiguation_pending` (§3.5) and `scope_unresolved_conservative` (§2.4). These land in the E0 §18 golden scenario + §20 obligations table at ratification.

**§12-ter Phase-2 multi-principal readiness (Adj R-5 — architect, 2026-06-04).** "Internal use" (R-1, §4.0) means **same principal, or principals authorized for the scope** — NOT "any user of a shared database." In Phase 2, delivering one principal's restricted memory to another user **is a sharing event**: the egress concept generalizes from "leaves the machine" to "**crosses a principal boundary**." The contracts already carry the keys — required `principal_ref` on `PolicyEvaluationContext` (§3.0); `owner_principal_ref` on `ScopeIdentityRoot` (§2.1); `same_principal` in `relation_to_destination` (§2.4); the §6.5 cross-principal-bleed invariant; the NI-2 replication-safe meet (§3.0). Per-user restriction rules arrive in Phase 2 as a new **producer** of principal-scoped `MemoryPolicyDecision`s from the DOC5 grants model — **no DOC81 schema change**. R2 **MUST NOT** bake a solo-principal assumption into any contract (no dropping principal keys as "unused"); lint `policy.internal_use_default_assumed_single_principal` (§11) guards the R-1 posture wording. GK's `PolicyEpochVersion`-on-E0 global clock is the Phase-2 pickup for cross-node policy ordering (D4).

---

## §13. Open items + architect-review flags

Per the adjudication card §7 §13-dispositions. **R1's two `OPEN_FOR_ARCHITECT_REVIEW` items go to ZERO at R2:** §13.4 RESOLVED (U22/F6), §13.5 RESOLVED (U7/F5). §13.1/§13.2 are CONFIRMED. §13.3 was FIX-APPLIED (CODEX, 2026-06-01). R2 introduces architect-confirm **seeds** (§13.6) — value-constants awaiting a cheap confirm pass, **NOT open design questions**. Total open `OPEN_FOR_ARCHITECT_REVIEW` items: **0**.

**§13.1 `ScopeAffinity` enum reconciliation — CONFIRMED.** The traceable union `{direct, secondary, shared, background, analogical, unrelated, uncertain}` (§2.3) stands (CL: correct no-loss reconciliation; GK: the right traceable choice). R2 adds a one-line definition: **`shared` = relevant to the request because the object lives in a scope shared across the request's scopes** (distinct from `direct`, which is the request's own scope), per the Opening-Brief/Owner-Map value (GPT B+ note). `uncertain` is load-bearing for fail-closed (§4.6).

**§13.2 `disclosure_class` lattice order — CONFIRMED.** `not_disclosable ⊏ existence_only ⊏ generic_safe_label_only ⊏ redacted_summary ⊏ full` (§3.0) — `existence_only ⊏ generic_safe_label_only` because a generic safe label discloses existence **plus** a category hint (CL: sound; GK: "mathematically correct… total order holds"). The total order is now load-bearing for `meetDisclosureVectors` / `deriveDisclosureClass` (§4.2) and the `AXIS_RANKS` map (§3.0).

**§13.3 — FIX APPLIED (2026-06-01, CODEX fidelity audit).** The 8 carried PropA rows (`OBL-PROPA-NEW-01`, `-02`, `-V15-01..06`) are now landed against their **canonical bodies from `OPA_V3_18.md §6.25`** in the §7.3 reconciliation table — no longer topic-level guesses. The R1 draft had mapped 8 of 10 to the wrong PropA topics (the V15 family is DOC73 V1.5.1 learning-pipeline privacy/governance, not sensitivity/collection); corrected, with per-row fixtures added (§7.3). No longer an open item. *(Root cause: the drafting commission did not include archived `OPA_V3_18.md` for the carried bodies; future carried-PropA work must carry it.)*

**§13.4 — RESOLVED (U22/F6).** The `PolicyMembraneDecision.crossing_disposition` enum is **confirmed** as the disposition vocabulary; what was open — its *authority* — is closed by making the disposition **derived** from the effective policy (`PolicyMembraneDispositionDerivation`, §3.3): `derived_crossing_disposition = f(effective policy, boundary kind, obligations)`. EC never sets it independently. No longer open.

**§13.5 — RESOLVED (U7/F5).** The two destination vocabularies are reconciled by the **`DestinationPolicyCrosswalk` constraint table** (§4.6.4, GPT §4.8 audited shape): `relation_to_destination` (scope relation) → *allowed* `E0OutboundDestinationClass` classes + floor + attestation flags + disposition. `relation_to_destination` **never substitutes** for the egress class; unknown terminal destinations fail closed *before* classing. (GK's total-function table was **rejected**, D3 — it mapped unknown/blocked to a nonexistent E0 class and invented `same_principal→local_file_export`.) No longer open.

### §13.6 Architect-confirm seeds (value-constants, NOT open design questions)

R2 lands the *shapes*; R3 re-seeds them per card §2. These *constants* get one cheap confirm pass at the next review. Each is sourced and conservative-by-default; none is an open design question.

| seed | location | default (seed) | basis |
|---|---|---|---|
| `ConservatismFloorEffect` intermediate cells (F3) | §4.6.1 | per-cell MEET of CL §2.2 + GPT §4.7 | F3; only `fail_closed→⊥` and `user_disambiguation→raise §3.5` are strongly implied |
| `ActionPermissionPredicate` required minima (U29) | §4.7 | GPT §4.10 seed minima | U29 — the table's existence is not optional; the values are confirmable |
| protection-state rank (U16) | §2.4 | `ordinary < … < privileged < sealed < unknown_sensitive` | GPT §4.16.5 |
| `SourceExclusionFilterRule.exclusion_closure` default (U19) | §7.2 | recommend `source_and_derived_artifacts` | U19 |
| `MAX_SCOPE_TRAVERSAL_DEPTH` + traversal budget maxima (U11) | §2.2 / §8 | `16` | CL §2.10 — **R3: stays 16 (D-R3; GPT's proposed 8 declined — 16 covers the local single-principal graph without DoS)** |
| threshold **no-profile fallback** (R3 split — card §2) | §4.6.3 | `THRESHOLD_FALLBACK_NO_PROFILE = { scope_confidence_floor: 1.0, contamination_ceiling: 0.0 }` — used ONLY when no profile resolves | both reviews: 0.0 must not be the operational default |
| threshold **shipped default-profile operational** (R3 split — card §2) | §4.6.3 | `THRESHOLD_SHIPPED_DEFAULT_PROFILE = { scope_confidence_floor: 0.85, contamination_ceiling: 0.70 }` | usable out-of-box; conservative but not self-blocking |
| `contamination minimum_measurement_confidence` (V17/QF4) | §4.4 | recommend `0.60` | QF4 — below ⇒ `low_confidence_disposition` |
| `topic_match_ambiguity_band` + admit-threshold (V17/QF8) | §7.1 | band `0.20`, admit-threshold `0.70` | QF8 |
| 9→5 domain crosswalk `LVL_*` maps (V2/A2-B5) | §4.6.2 | per CL §2.1 level maps | V2 — the *maps' existence* is not a seed; the cell values are |
| `local_file_export` egress floor (R3 — card §2) | §4.6.4 / §7.4 | `normal_policy_check` **with `E0EgressAttestation` retained** (NOT a hard floor) | card §2 — treat as egress + attest; do not over-block routine local exports |
| `DestinationPolicyCrosswalk` seed rows (F5) | §4.6.4 | per GPT §4.8 | F5 |
| F2 stricter-alternative note | §2.1 | full-pairwise-matrix equivalence (GPT §4.16.2) **declined as default**; firewall guard + maximum-bottleneck spanning tree (V15) are the real protection | F2/D7 — recorded, available if the architect prefers the stricter model |

### §13.7 R3 minors, forward notes, and declines (card §1.3 / §3)

**R3 minors landed (card §1.3 — six).** (1) **Restamp/cascade race attribution** → resolved by **EC per-object compare-and-swap** on `epoch_version` (§3.6), not a lock — the loser retries against the new generation; lint `policy.restamp_cascade_race_without_per_object_cas`. (2) **Fixture-name alignment** — R3 fixtures use the `fixture.<area>.<behavior>` convention uniformly (swept §10/§11). (3) **Bracket consistency** — `Array<T>` form used for all multi-field element arrays; bare `T[]` kept only for scalar/branded-id lists. (4) **Attestation composition = fail-safe OR** — when multiple sources require an attestation, the requirement composes by **OR** (any source requiring it ⇒ required); `egress_attestation_required` is `true` if ANY contributing decision/crosswalk row demands it (§3.2 step 0c/1b); lint `egress.attestation_requirement_composed_as_and`. (5) **`user_instruction_policy_bound` invariant** — a user instruction may **narrow** context, never **widen** any effective axis (stated inline at `PolicyEvaluationContext`, §3.0); lint `policy.user_instruction_widened_effective_axis`. (6) **Unknown `exposure_context_ref` fails closed** — an unresolved/unknown PropA `ExposureContextSchema` instance ⇒ conservative baseline (never permissive default); lint `policy.unknown_exposure_context_not_fail_closed`; fixture `fixture.policy.unknown_exposure_context_fails_closed`.

**V24 forward notes (carried, not built — Phase-2 / sibling-charter handoffs).** (a) **`default.ordinary` profile obligation** — the no-profile fallback (§4.6.3) carries a forward note that the shipped `default.ordinary` DomainProfile must be defined by E0/EC before first run (else every request takes the maximally-conservative `THRESHOLD_FALLBACK_NO_PROFILE`); tracked, not a DOC81 schema. (b) **Delegation envelope → DOC11** — the `delegate` action's transport/envelope contract is **DOC11's** (sub-agent architecture); DOC81 owns only the policy decision + `required_attestations`; forward note, no local schema. (c) **As-of read-contract stub + honest valid-time scoping (CL A2-M6)** — §3.2 makes only the honest claim that the meet is evaluated **as-of `effective_time` under one `policy_generation_id`**; full bitemporal (valid-time vs transaction-time) as-of reads are a **stub forwarded to E0's temporal contract** — R3 does NOT claim bitemporal correctness it cannot yet enforce (the R2 over-claim is scoped down here).

**R3 declines — binding non-changes (card §3; verified NOT applied).** **D-R1** — no second `PolicyStampRestampIssuance` object; V19 uses the optional `issued_memory_flow_certificate_ref?` + invariant instead (two lockstep objects invite divergence). **D-R2** — no E0-named `E0DerivedProjection` base; V13 uses the **local** `persistence_kind` marker (an E0-named base would imply an E0 edit). **D-R3** — traversal depth stays **16** (GPT's 8 declined). **D-R4** — no runtime step-5b capability-obligation reconciliation; V14 emission lint + consumer-precedence suffices while EC is sole producer (revisit at Phase-2 DOC5 grants). **D-R5** — wall items (V21) are **non-gating** one-line Phase-2 contracts, not built/gating (anti-over-protection). **D-R6** — CL B.3 is **adopted NOW as V1** (the "later A+ pass" framing is the superseded alternative). All R1-card declines **D1–D12** remain binding and untouched.

---

## §14. Sources + lineage

### §14.1 ADQ landings (the pinned ADQs — Acceptance criterion: explicit landing sites)

| ADQ | resolution | DOC81 landing site |
|---|---|---|
| **ADQ-310** | ReasonCode registry @ DOC80 core; refer, don't invent | §0 + §1.4 bind-by-reference; all schemas use `ReasonCodeId` (E0 §2.1), no local reason-code system |
| **ADQ-315** | `AssertionRelationEdge` traversal: `relation_kind → scope checks + render gating`; **pinned to E1** | **§8** `RelationTraversalScopeCheckPolicy` (DOC81 owns the table; DOC82 owns the edge; EC executes) |
| **ADQ-316** | restamp: keep/downgrade/block; **cannot exceed original ceilings** | **§3.4** `PolicyStampRestamp` + `original_ceiling_ref`; §6.3 monotonicity; ties E0 `RestampMFC` |
| **ADQ-313** | DomainProfile registry + `conservative` fallback (highest restrictiveness) | §1.5 + **§4.6** fail-closed meet; binds E0 §2.2 (never redefines) |
| **ADQ-308** | SafeLabel disclosure policy split (DOC81 policy/disclosure; DOC86 references) | **§4.2** `SafeLabelDisclosurePolicy` (meaning @ DOC81; render @ DOC86; vocab @ PropA) |
| **ADQ-213** | `TopicRiskClass` (DOC81 policy; EC executes; DOC87 consumes) | **§4.5** `TopicRiskClass` |
| **ADQ-406** | `collection_mode` (collect/suppress/exclude); DOC81 governance, EC §1 enforces | **§7.1** `CollectionModeSuppressionGovernance` |
| **ADQ-PASS2-02** | corpus↔library identity authority is **DOC87**; DOC81 consumes the term as vocabulary | §2.1 (`ScopeIdentityRoot` consumes the term; never owns it) |
| ADQ-304 (supporting) | last-active-support-edge → retire/audit_only; partial → degrade | §5.1 `last_active_support_edge_lost` |
| ADQ-317 (supporting) | suppressed_manifest_only vs generic notice → EC policy evaluator | §4.2 disclosure-to-availability rules |

### §14.2 OPA-row landings (the 10 PropA rows + the 2 E0-handed obligations — Acceptance criterion)

**R2 replacement (Adj U14/GPT §4.12) — corrected after the CODEX §7.3 fix; row-accurate against the operative §7.3 table.** The R1 lineage table contradicted the fixed §7.3 (e.g. V15-02 listed "→ §4.2 disclosure" vs the corrected `SchemaMigrationPlan` → §3.3; V15-05 listed "→ §3.1 destination + §7.4 egress" vs the corrected post-retrieval critique → §3.3/§6.3). Lint `propa.opa_landing_table_stale_after_fix`.

| OPA row | canonical body | DOC81 landing site |
|---|---|---|
| `OBL-PROPA-NEW-01` | PropA-owned `ExposureContextSchema`; DOC24 passes exposure context to EC PolicyDecisionEngine | §7.3 + §3.0/§3.1/§3.2: `exposure_context_ref` is a named policy-evaluation input (`PolicyEvaluationContext`, U6); DOC81 references PropA, never redefines the enum |
| `OBL-PROPA-NEW-02` | policy hard-block precedence over BDSM `force_level` | §7.3 + §3.2 + §4.4: policy block is final; `force_level` / DAMS salience operate only inside `PolicyCappedDAMSInput` eligibility |
| `OBL-PROPA-NEW-V15-01` | `LearningVisibilityScope`; sealed discarded; firewalled same-firewall only | §7.3 + §3.3 (`discard_sealed_learning_signals` / `restrict_firewalled_learning_to_same_firewall`) + §6.3 + `learning_scope='same_firewall_only'` (U20) |
| `OBL-PROPA-NEW-V15-02` | `SchemaMigrationPlan` required for schema-changing iterations; plan-less → `manual_review_only` | §7.3 + §3.3: `require_migration_plan` / `route_manual_review_only` obligations |
| `OBL-PROPA-NEW-V15-03` | `VerifierCalibrationLedger` gating; uncalibrated/drifting/failed verifier signals excluded | §7.3 + §3.3: `require_verifier_calibration_current` / `exclude_uncalibrated_verifier_signals` (DOC85/PropA consume) |
| `OBL-PROPA-NEW-V15-04` | AVAPO regression-fixture exclusion; sealed not generated; firewalled same-firewall | §7.3 + §3.3: `forbid_sealed_source_fixture_generation` / `restrict_firewalled_fixture_generation_to_same_firewall` |
| `OBL-PROPA-NEW-V15-05` | post-retrieval critique prompt surface | §7.3 + §3.3 (`require_post_retrieval_critique_policy_gate`) + §6.3 — critique-utility eligibility bounded by policy |
| `OBL-PROPA-NEW-V15-06` | counterfactual-ambiguity classification + additive-synthesis prompt surfaces | §7.3 + §3.3 (`require_counterfactual_prompt_policy_gate` / `require_additive_synthesis_prompt_policy_gate`) + §6.3 |
| `OBL-XDOC-PROPA-DSPY-TARGETS-01` | four DSPy target IDs + eligibility discipline | §7.3 + §4.4 + §6.3: PropA owns the registry; DOC81 supplies eligibility ceilings (`require_dspy_target_eligibility`) |
| `OBL-PROPA-NEW-SOURCE-EXCLUSION-FILTER-01` | source-exclusion rule | **§7.2** `SourceExclusionFilterRule` |
| `OBL-D81-TOPIC-COLLECTION-SUPPRESSION-01` (E0-handed, §6.Z) | topic-level collection suppression | **§7.1** `CollectionModeSuppressionGovernance` + `CollectionSuppressionEvaluation` |
| `OBL-PROPA-LOCALFILEEXPORT-OUTBOUND-PATCH-01` (E0-handed, §6.Z3) | `local_file_export` treated as egress | **§7.4** cross-doc note + `DestinationPolicyCrosswalk` (§4.6.4) |

### §14.3 Owner Map / Skeletal / SM / Round D references

**Owner Map rows landed:** 62 (`ExtractionRoutePolicyEnvelope`, §4.3); 71–78 (scope family, §2); 79–85 (policy family, §3); 87 (`SafeLabelDisclosurePolicy`, §4.2); 90–91 (`PolicyCappedDAMSInput` + `contamination_risk` threshold, §4.4); 93 (`disclosure_class`, §4.2); 94 (`CascadingSourceInvalidation` envelope, §5.1); 194 (`TopicRiskClass`, §4.5). **Not owned (referenced):** 80 consumption pointer (E0 §3.2); 88 (`ApprovedSafeLabelVocabularyRegistry` @ PropA); 89 (`SafeLabelRenderContract` @ DOC86); 92 (`contamination_risk` computation @ DOC84); 95 (`CascadingSourceInvalidation` source-side payload @ DOC82); 33 (`AssertionRelationEdge` @ DOC82 — §8 owns only the traversal policy); 118 (`TopicCollectionDirective` @ DOC83 — §7.1 owns only `collection_mode` governance).

**Skeletal fold-ins landed:** §DOC81 §1–§7 (the member map); §10.3 (`LegalHoldState`, §6.1); §10.4 (non-overlap, §6.2); §10.5 (`MemoryMutationEnvelope` ref, §5.1); §10.11 (5-plane cascade, §5.2); §11.3 (policy-generation re-gate, §6.4); §11.9 (monotonicity, §5.3/§6.3).

**Supersession Matrix rows landed:** SM-010/011 (ScopeBoundary, §2.2 — `ScopeMembrane` retired); SM-012 (dimensional `MemoryPolicyDecision`, §3.1); SM-013 (`EffectiveMemoryPolicy`, §3.2); SM-014 (`PolicyMembraneDecision` membrane-only, §3.3); SM-101 (`EpisodePolicyEpoch`, §3.6); SM-102 (`PolicyDisambiguationRequest`, §3.5 — `ask_user` retired); SM-104 (`disclosure_class`, §4.2); SM-105 (`SafeLabel*`, §4.2); SM-106 (`contamination_risk` threshold, §4.4 — linear retired); SM-107 (`PolicyCappedDAMSInput`, §4.4); SM-208 (`CascadingSourceInvalidation`, §5.1).

**Round D R0.2 sections formalized:** §1.3–§1.8 (policy plane, §3); §1.6 (capability/disclosure separation, §4.1); §2.1–§2.5 (stamps/invalidation/epoch, §3.4/§3.6); §3.2–§3.8 (scope model, §2; **§3.8 Project↔scope seam, §2.2 — U27c closes the gap that R1 §14.3 stopped at §3.7**); ADQ-315 (§8); §5.4 (restamp, §3.4); §6.1–§6.5 (availability/safe-label/notices, §4.2); §8.2–§8.3 (DAMS cap + contamination veto, §4.4).

**Retired names guarded (Retired Names; Hard Constraint §10):** `ScopeMembrane` (§2.2), scalar `MemoryPolicyDecision` (§3.1), scalar/allow-block `PolicyMembraneDecision` (§3.3), `ask_user` (§3.5), linear `contamination_risk` (§4.4), `DAMS standalone ranking` (§4.4), ad-hoc `safe labels` (§4.2). None reintroduced.

**Plan §17.4 Round-D lint suite:** the **11 DOC81-owned** lints (of 16; the other 5 route to E10/DOC86 — §11 M1 fix) landed in §2–§7.

#### §14.3.R2 — R2 discharge additions (Adj U28/U30/U32/S4)

**New DOC81-owned Owner Map rows to add at ratification (card §6 inventory; ~28 contracts + S4's R1-object rows).** `PolicyLattice` module (§3.0), `PolicyRuntimeFreshnessKey`, `PolicyEvaluationContext`, `ConservatismFloorEffect`, `DomainProfilePolicyContribution`, `DomainPolicyThresholds`, `ThresholdEvaluation`, `DestinationPolicyCrosswalk`, `DisclosurePermissionVector`/`CountDisclosurePolicy`, `PolicyObligationConflict`, `PolicyObligationDischarge` (+ typed obligation union), `PolicyMembraneDispositionDerivation`, `PolicyStampScopeItem`, `PolicyCeilingSnapshot`, `RestampChainIntegrity`, `RestampAuthority`, restamp union, `PolicyDisambiguationAnswer`, `ScopeEquivalenceCluster` (+ `ScopeEquivalencePairEvidence`), `ScopeResolutionConfidenceBreakdown`, `ScopeProtectionStateDerivation`, `ScopeSearchCoverageProof`, `CascadingSourceInvalidationRun` (+ `PlaneCascadeStatus`), `LastActiveSupportEdgeEvaluation`, `LegalHoldSelector`, `LegalHoldClearance`, `DestructiveJobLegalHoldGate`/`Registry`, `CollectionSuppressionEvaluation`, `RelationTraversalPolicyRegistry`/`Budget`/`ExecutionTrace`, `TopicRiskConfirmation`, `EligibilityCeilingDerivation`, `MemoryPolicyActionClosureRule`, `ActionPermissionPredicate`, `DOC81BatchOperationQuotaEnvelope`, the §6.6 cache keys, the §6.7 quota envelope; **plus CL S4's R1 objects that never got rows:** `PolicyStampRestamp`, `CollectionModeSuppressionGovernance`, `SourceExclusionFilterRule`, `RelationTraversalScopeCheckPolicy`, `PolicyUIExport`; **and fix §6.1's `LegalHoldState` citation to "Skeletal §10.3" only.** **Cross-charter named-not-owned:** `ContaminationRiskMeasurement` (DOC84, F9); `ExternalVocabularyValueRef` pattern (vocab owners).

**SM / Retired-Names entries for the U30 renames (recorded in Retired-Names style; lineage-only — `allowed_historical_context = "Round D §3.3 lineage only"`):** `source_membership` → `source_topology_link`; `topic_membership` → `topic_topology_link` (§2.2; M1 §6.2 non-overlap hygiene — these are pure renames, not Supersession Matrix rows). Lint `scope.membership_term_used_for_topology_relation` catches stale use.

**Memory-object classification rows (E0 §6, U28/S5 — to add at the E0 taxonomy at ratification).** Durable (`E0DurableRecord` + lifecycle): equivalence binding/cluster, container relation, stamp, restamp, disambiguation, cascade, suppression governance, exclusion rule, topic risk, legal hold, ceiling snapshot. **Per-request / derived** (R3/V13 chose the **`persistence_kind: 'derived_projection'` marker** — NOT dropping `E0DurableRecord` and NOT an E0-named base, D-R2): `ScopeResolutionResult`, `ScopeBoundary`, the meet result (`EffectiveMemoryPolicy`), `PolicyMembraneDecision`, `PolicyCappedDAMSInput`, `PolicyEvaluationContext` — each now carries `persistence_kind` + `canonical_source_refs` + `rebuild_key`. Lint `classification.doc81_object_without_classification_row`.

#### §14.3.R3 — R3 discharge additions (to apply at ratification)

**Owner Map deltas.** (1) **Line 83 repoints** from the now-collapsed `PolicyStampScope` to `PolicyStamp.scope_items` (V20/B-S6); the `PolicyStampScopeRef` brand is **retired** (add to Retired-Names: `allowed_historical_context = "pre-R3 PolicyStampScope wrapper"`; lint `schema.policy_stamp_scope_ref_used`). (2) **New DOC81-owned primary-ID row:** `DomainPolicyThresholds` now keys on `DomainPolicyThresholdsRef` (was foreign `DomainProfileId`); add the row + `domain_profile_id` FK note (V20). (3) `ConservatismFloorEffect` gains its own `effect_id` primary ID (V20) — confirm the §3.2 `floor_effect_ref` target row. **New executable helpers to register (functions, not schemas):** `meetPolicyState`/`finalizeDisclosureAndCoherence`/`disclosureVectorCeilingFor` (§3.0, V1), `domainContribution` (§4.6.2, V2), `requiredScopeConfidenceComponents`/`computeCoverage`/`evaluateThreshold`/`topicMatchAmbiguity`/`evaluateCollectionDisposition`/`cascadeComplete`/`lastActiveSupportEdgeLost`/`validateQuotaEnvelope`/`contextCompatible`/`asUnitInterval` (§2–§7, V17/QF pack). **Value types added:** `UnitInterval`, `DOC81PersistenceKind`, `ThresholdPolarity`, `SupportEdgeStatus`, `GovernanceResolutionState`, `CanonicalHashSpec`, `PrincipalScope` — all DOC81-local. **E0 referenced (not owned):** `E0EgressAttestationRef` (E0 §22) added to the §0 referenced-brand note. **Counts:** R3 adds the 24 V-clusters + 17 QF helpers; lint suite grows by the R3 lints indexed in §11; no E0 contract re-declared; one-owner held (`ContaminationRiskMeasurement` stays DOC84).

---

*End of DOC81 — Scope & Policy Plane (Stage 6 E1/E2 Charter Draft R3). R2 → the `E1_E2_R2_Adjudication_Card.md` package applied per `E1_E2_R3_Application_Commission_Claude_Code.md` (the V1 unified-state pipeline rebuild + clusters V2–V24, the §1.3 minors, the §2 seed re-adjudications, D-R1…D-R6 as verified non-changes; the R1-card record §1-bis R-1…R-5 / F1–F10 / D1–D12 remains binding). Awaiting post-R3 application-fidelity audit (automated CODEX) + NI-1 property gate + delta design re-review of changed sections. Git history is the version record.*