ELNOR REPO READER TEXT MIRROR Original path: Memory Rebuild Docs/Stage_6_Charters/E1_E2_DOC81_Scope_Policy/DOC81_Scope_Policy_Charter_Draft.md Source repo: /Users/OpenClaw1/Elnor/Elnor Specs Git branch: main Git commit: dbaa25962edc11ab30e8d4ca1715f9ae5bf77331 Generated: 2026-06-09T01:23:58.539Z --- # 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` 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 & { readonly __brand: B }; // ---- shared value brands ---- type ContentHash = Brand; // E0 §8.5 sha256 digest type SchemaVersionRef = Brand; // E0 §8.5 type OwnerDocId = Brand; // ---- scope-plane brands ---- type MemoryObjectRef = Brand; // the object a policy/scope decision is about (was Round D ContentReference; N12) type ScopeRef = Brand; // a ScopeIdentityRoot id type ScopeBoundaryRef = Brand; type ScopeResolutionResultRef = Brand; // matches E0 §3.2 type ScopeResolutionTraceRef = Brand; type ScopeEquivalenceBindingRef = Brand; type ScopeEquivalenceClusterRef = Brand; // U10 type ScopeContainerRelationRef = Brand; // ---- policy-plane brands ---- type MemoryPolicyDecisionRef = Brand; type EffectiveMemoryPolicyRef = Brand; // SAME brand as E0 §3.2 — the consumption-protocol pointer type PolicyEvaluationContextRef = Brand; // U6 type PolicyObligationRef = Brand; type PolicyObligationConflictRef = Brand; // U9 type PolicyObligationDischargeRef = Brand; // U9 type PolicyStampRef = Brand; // 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; // matches E0 §3.3 RestampMFC.original_ceiling_ref (was bare string — B1) type ConservatismFloorEffectRef = Brand; // U2 type ThresholdEvaluationRef = Brand; // U17 type PolicyDisambiguationRequestRef = Brand; type PolicyDisambiguationAnswerRef = Brand; // U23 type SafeReferenceLabelRef = Brand; // APPROVED safe-label vocabulary key (PropA registry; never derived from content) type DisclosureScopeAttestationRef = Brand; // E0 §3.3 / §22 type LegalHoldClearanceRef = Brand; // U12 / S3 type TopicRiskClassRef = Brand; // U33 / GPT §4.24 type TopicRiskConfirmationRef = Brand; // U33 / GPT M4 // ---- R2 fidelity sweep (Adj U31; CODEX R2-S1) — primary-ID brands for every DOC81-owned record ---- type ScopePopulationHealthRef = Brand; type ScopeSearchCoverageProofRef = Brand; type PolicyMembraneDecisionRef = Brand; type PolicyMembraneDispositionDerivationRef = Brand; type RestampChainIntegrityRef = Brand; type PolicyStampRestampRef = Brand; type PolicyStampInvalidationRef = Brand; type EpisodePolicyEpochRef = Brand; type SafeLabelDisclosurePolicyRef = Brand; type ExtractionRoutePolicyEnvelopeRef = Brand; type PolicyCappedDAMSInputRef = Brand; type EligibilityCeilingDerivationRef = Brand; type ContaminationRiskThresholdRuleRef = Brand; type DomainPolicyThresholdsRef = Brand; // V20 — own primary ID for DomainPolicyThresholds (was foreign DomainProfileId) type DomainProfilePolicyContributionRef = Brand; type DestinationPolicyCrosswalkRef = Brand; type MemoryPolicyActionClosureRuleRef = Brand; type ActionPermissionPredicateRef = Brand; type CascadingSourceInvalidationRef = Brand; type CascadingSourceInvalidationRunRef = Brand; type LastActiveSupportEdgeEvaluationRef = Brand; type LegalHoldStateRef = Brand; type DestructiveJobLegalHoldGateRef = Brand; type BatchOperationQuotaEnvelopeRef = Brand; type CollectionSuppressionEvaluationRef = Brand; type RelationTraversalScopeCheckPolicyRef = Brand; type RelationTraversalBudgetRef = Brand; type RelationTraversalExecutionTraceRef = Brand; type PolicyUIExportRef = Brand; // (ExtractionRouteContextRef, CollectionGateResultRef, SourceExclusionFilterRuleRef, // CollectionModeSuppressionGovernanceRef are declared at §4.3 and used from there.) // ---- referenced (consumed, not owned) brands ---- type SourceRef = Brand; // a DOC82/DOC25 source id (consumed, not owned) type ContextProductRequestRef = Brand; // DOC84-owned context-product request id; referenced (U21) type ContaminationRiskMeasurementRef = Brand; // DOC84-owned measurement (F9); referenced type PrincipalRef = Brand; type SurfaceRef = Brand; type TopicRef = Brand; // DOC87-owned Topic identity (ADQ-220); referenced type WorkEpisodeRef = Brand; // DOC83-owned; referenced type TopicCollectionDirectiveRef = Brand; // 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 { constructor(private readonly ranks: Record, 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(AXIS_RANKS.content_fidelity, 'none', 'full'), locality: new Chain(AXIS_RANKS.locality, 'blocked', 'approved_external'), learning: new Chain(AXIS_RANKS.learning_scope, 'none', 'global_allowed'), mutation: new Chain(AXIS_RANKS.mutation_authority, 'none', 'durable_allowed'), disclosure: new Chain(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 = { 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(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>; 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 = PolicyObligationBase & { obligation_kind: K; parameters: PolicyObligationParametersByKind[K] }; type PolicyObligation = { [K in PolicyObligationKind]: TypedPolicyObligation }[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> = { 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; 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; // 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 = { none:0, bucketed:1, exact:2 }; const SUMMARY_FIDELITY_RANK: Record = { 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]v.max_summary_fidelity).reduce((a,b)=>SUMMARY_FIDELITY_RANK[b]v.may_disclose_reason_summary); const agree = (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> = { 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; // DOC82-owned route/provenance; referenced type CollectionGateResultRef = Brand; // EC §1 gate result; referenced type SourceExclusionFilterRuleRef = Brand; // §7.2 type CollectionModeSuppressionGovernanceRef = Brand; // §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[]; 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 = { open:'full', normal:'redacted', restricted:'reference_only', highly_restricted:'safe_label', blocked:'none' }; const LVL_LOCALITY: Record = { open:'approved_external', normal:'local_only', restricted:'local_only', highly_restricted:'local_only', blocked:'blocked' }; const LVL_LEARNING: Record = { open:'global_allowed', normal:'partitioned', restricted:'same_firewall_only', highly_restricted:'same_scope_only', blocked:'none' }; const LVL_MUTATION: Record = { open:'durable_allowed', normal:'durable_allowed', restricted:'durable_requires_review', highly_restricted:'candidate_only', blocked:'none' }; const LVL_DISCLOSE: Record = { 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>; 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> = { // 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 = { 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 = { 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..` convention uniformly (swept §10/§11). (3) **Bracket consistency** — `Array` 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.*