ELNOR REPO READER TEXT MIRROR Original path: Memory Rebuild Docs/Stage_6_Charters/E1_E2_DOC81_Scope_Policy/Reviews/DOC81_ChatGPT FULL_red_team_final_review.md Source repo: /Users/OpenClaw1/Elnor/Elnor Specs Git branch: main Git commit: dbaa25962edc11ab30e8d4ca1715f9ae5bf77331 Generated: 2026-06-09T01:23:58.539Z --- # ELNOR DOC81 Scope & Policy Charter — Consolidated Red-Team Review and Patch Pack **Target:** `Memory Rebuild Docs/Stage_6_Charters/E1_E2_DOC81_Scope_Policy/DOC81_Scope_Policy_Charter_Draft.md` **Repo:** `wbrody/Elnor-Specs`, branch `main` **Reviewer posture:** independent design red-team; substantive review, not fidelity audit **Deliverable status:** consolidated final analysis from the full review sequence in this conversation **Final verdict:** `DESIGN_REVISION_NEEDED_BEFORE_RATIFICATION` ## One-line bottom line DOC81 has the right architecture and should not be rewritten from scratch, but it is not yet ratifiable because too many runtime-critical policy/scope invariants remain prose-backed instead of schema-backed: stamp validity, restamp ceilings, policy freshness, egress destination specificity, disclosure aggregation, confidence/threshold semantics, obligation discharge, relation traversal, source revocation, collection suppression, cache keys, and state transitions all need executable contracts. --- # 0. Executive summary DOC81 correctly splits **scope** from **policy**. Scope resolves identity, boundary, relation, population, affinity, and conservative floors. Policy decides capability and disclosure through a dimensional meet. That architecture is right and should be preserved. The issue is not that DOC81 picked the wrong system boundary. The issue is that DOC81 is currently a good architectural charter with several implementation traps. It often says the correct invariant, adds a lint name, and states that EC enforces it, but it does not always give EC the data structure needed to prove enforcement. Since DOC81 is the Scope & Policy foundation consumed by DOC82, DOC83, DOC84, DOC85, DOC86, DOC87, and EC, this is not acceptable at ratification. The strongest conclusion after three passes is: > DOC81 should become a small executable policy/scope calculus: **evaluation context → policy decisions → per-axis meet → disclosure-vector meet → conservatism floor → action predicate → stamp/export/discharge proof**, all keyed by current runtime freshness. The present draft has many of these concepts in prose, but not enough executable schema. A targeted schema patch round should be enough; this is not a return-to-zero. ## Current grade | Dimension | Grade | Rationale | |---|---:|---| | Architecture direction | A− | Correct DOC80/DOC81 split, scope/policy separation, DOC81→DOC86 export direction, dimensional policy model. | | Executable policy calculus | B− / C+ | Meet is directionally right but under-keyed, malformed-axis handling is unsafe, floor mapping absent, egress specificity under-specified. | | Schema completeness | B− | Many strong objects exist; several are missing proof/discharge/freshness/context fields. | | Quantitative/function correctness | C+ | Thresholds, confidence, aggregation, cache keys, quotas, and state transitions are mostly named, not fully specified. | | Runtime freshness / cache safety | C | `policy_generation_id` alone is not enough; EC effective state and compiled evaluator hash must be part of cache/stamp validity. | | UI/export seam | C+ | Correct direction, insufficient payload for DOC86 to render without recomputing or guessing. | | Overall | B / B+ | Strong design skeleton; not yet ratifiable without schema-level patches. | ## Ratification posture **Do not ratify as-is.** Require one targeted revision round. The revision should be schema-level, not just explanatory prose. After the patch bundle below, DOC81 should reach A−/A. With property-style fixtures and complete lints, it can reach A/A+. --- # 1. Source basis and review frame The review sequence followed the committed DOC81 design-review commission. The review pack included, in substance: - `E1_E2_Design_Review_Prompt.md` - `DOC81_Scope_Policy_Charter_Draft.md` - `Charter_Opening_Brief.md` - `Charter_Input_Deck.md` - `Reviews/E1_E2_Fidelity_Audit_CODEX.md` - `E1_E2_CODEX_Audit_Fix_Record.md` - `E0_DOC80_Core/DOC80_Core_Charter_Draft.md` - `DOC80_Skeletal_Target_Baseline.md` - `DOC80_Owner_Map.md` - `DOC80_Import_Graph.md` - `STAGE_6_CHARTER_INPUT_INDEX.md` - `Architect_Decision_Queue.md` - Round D Policy / Scope / UI micro-patch material - OPA / PropA carried-row bodies - EC Core Addendum A V3.3 compiled operative material No repo files were modified in this conversation. This document consolidates the final settled findings and patch recommendations from the review passes. --- # 2. Final ranked patch queue These are the changes I would make before ratification, in priority order. 1. **Triple-bind `PolicyStamp` / `PolicyStampScope` to action/destination-specific `EffectiveMemoryPolicyRef`s.** A stamp currently has one policy ref but can claim multiple actions/destinations. 2. **Add `PolicyRuntimeFreshnessKey` with `effective_state_generation_id` and compiled evaluator hash.** `policy_generation_id` alone cannot invalidate cached policy after runtime toggle/incognito/application changes. 3. **Replace the meet pseudo-code with `meet_v2`: context-keyed, malformed-axis conservative, destination-specific egress, disclosure-vector meet.** 4. **Add required `PolicyEvaluationContext` to every `MemoryPolicyDecision`.** Include exposure context, model class, client kind, interaction mode, principal, surface, destination, and bounded user instruction. 5. **Add `PolicyCeilingSnapshot` and root restamp-chain integrity.** Restamp ceilings must be axis-comparable and root-bound. 6. **Make `PolicyStampRestamp` a discriminated union.** Blocked restamps must not carry an authorizing new effective policy ref. 7. **Add `ConservatismFloorEffect` table.** Scope floors need deterministic axis effects. 8. **Add `DestinationPolicyCrosswalk`.** `relation_to_destination` must never substitute for `E0OutboundDestinationClass`. 9. **Add `DisclosurePermissionVector`; derive scalar `DisclosureClass`.** Scalar disclosure alone cannot safely aggregate existence/count/title/source/reason permissions. 10. **Add `ThresholdEvaluation`, confidence breakdowns, and pairwise equivalence confidence.** Thresholds need comparator/equality/hysteresis/version semantics. 11. **Add `PolicyObligationConflict` + `PolicyObligationDischarge`; expand PropA obligation kinds.** Obligations must be conflict-checked and proof-discharged before movement. 12. **Close relation traversal with default-deny registry, budget, and cycle handling.** Remove `| string` escape hatches. 13. **Add `CascadingSourceInvalidationRun` with per-plane idempotent state and receipts.** Five-plane cascade is correct but needs execution proof. 14. **Add destructive-job legal-hold registry and selector semantics.** Future destructive jobs must register the legal-hold gate. 15. **Patch collection suppression with ambiguity, rank aggregation, and existing-material backfill for `exclude`.** 16. **Patch source exclusion with input/output-cycle fix and derived-artifact closure.** 17. **Expand DOC81→DOC86 `PolicyUIExport` to per-action/per-destination eligibility, safe-label constraints, restamp/disambiguation metadata, and freshness.** 18. **Add action-closure and action-permission predicate tables.** Terminal actions must imply prerequisite policy checks. 19. **Add cache keys for scope/policy/UI exports.** Include source/classification/population/safe-label/effective-state generations. 20. **Add fan-out quota envelopes for restamp, cascade, relation traversal, legal-hold scans, and collection backfill.** --- # 3. Consolidated finding register ## Blocking findings | ID | Type | Severity | Finding | Fix package | |---|---|---:|---|---| | B1 | BUG | Blocker | `PolicyStamp` points to one `EffectiveMemoryPolicyRef` while `PolicyStampScope` can cover many actions/destinations. | `PolicyStampScopeItem`, per-triple effective-policy refs. | | B2 | BUG | Blocker | `policy_generation_id` alone is insufficient freshness. | `PolicyRuntimeFreshnessKey` on every runtime policy artifact. | | B3 | BUG | Blocker | Meet applicability is under-keyed and can bleed across principal/surface/exposure/model/client context. | Required `PolicyEvaluationContext`; `meet_v2`. | | B4 | BUG | Blocker | Destinationless decisions can accidentally authorize egress. | Destination-specific egress precondition. | | B5 | BUG | Blocker | Meet pseudo-code filters missing axis values, allowing malformed decisions to fail open on that axis. | Explicit rank maps; malformed applicable decisions bottom the axis or block. | | B6 | BUG | Blocker | Restamp ceiling is opaque and chain root is not explicit. | `PolicyCeilingSnapshot`, `RestampChainIntegrity`, axis comparison. | | B7 | BUG | Blocker | `PolicyStampRestamp` requires `new_effective_policy_ref` even when `block`. | Discriminated union. | | B8 | GAP | Blocker | `minimum_conservatism_floor` has no executable axis-effect table. | `ConservatismFloorEffect`. | | B9 | GAP | Blocker | Destination relation and egress destination class have no crosswalk. | `DestinationPolicyCrosswalk`. | | B10 | BUG | Blocker | Disclosure scalar cannot safely aggregate all disclosure permissions. | `DisclosurePermissionVector` meet. | | B11 | GAP | Blocker | PropA `ExposureContextSchema` is textually named but not structurally carried. | `PolicyEvaluationContext.exposure_context_ref`; PropA obligations. | | B12 | BUG | Blocker | `PolicyObligation` lacks conflict model and discharge proof. | `PolicyObligationConflict`, `PolicyObligationDischarge`. | | B13 | BUG | Blocker | Relation traversal allows arbitrary `string` relation kinds. | Closed registry + default-deny unknowns. | | B14 | GAP | Blocker | Legal hold covers known destructive jobs by prose only. | `DestructiveJobLegalHoldRegistry`. | | B15 | GAP | Blocker | DOC81→DOC86 export payload is too thin for safe rendering. | Expanded `PolicyUIExport`. | ## High-value substantive findings | ID | Type | Severity | Finding | Fix package | |---|---|---:|---|---| | S1 | BUG | High | `ScopeIdentityRoot` lacks synthetic unknown scope representation. | `unknown_synthetic` scope kind and lifecycle. | | S2 | GAP | High | `ScopeResolutionResult.confidence` is a naked scalar. | Component breakdown with min aggregation. | | S3 | GAP | High | `ScopeEquivalenceBinding` collapses clusters with one scalar confidence. | Pairwise evidence matrix. | | S4 | GAP | High | Thresholds lack comparator/equality/version/hysteresis. | `ThresholdEvaluation`. | | S5 | GAP | High | `ScopePopulationHealth` cannot invalidate cached resolution results because result lacks population generation inputs. | Add population-generation inputs to `ScopeResolutionResult`. | | S6 | GAP | High | Sensitivity/protection states are lossy singletons. | Multi-flag `ScopeProtectionStateDerivation`. | | S7 | BUG | High | `learning_scope` lacks `same_firewall_only`, required by PropA firewalled learning. | Add lattice value. | | S8 | BUG | High | `SourceExclusionFilterRule.effective_policy_ref` creates an input/output cycle. | Replace with `policy_input_decision_ref`. | | S9 | GAP | High | Source exclusion does not close over derived artifacts. | `exclusion_closure` and artifact classes. | | S10 | GAP | High | Collection `exclude` promises existing-material filtering but lifecycle covers only future admission. | Backfill/scan contract. | | S11 | GAP | High | Collection suppression topic ambiguity can collect protected content. | `CollectionSuppressionEvaluation`. | | S12 | GAP | High | Five-plane revocation lacks idempotent execution state and receipts. | `CascadingSourceInvalidationRun`. | | S13 | GAP | High | `last_active_support_edge_lost` is a conclusion boolean without denominator proof. | `LastActiveSupportEdgeEvaluation`. | | S14 | GAP | High | `PolicyCappedDAMSInput` not keyed to context product request. | Add product request ID/kind and derivation. | | S15 | GAP | High | Contamination threshold rule lacks actual measurement/threshold/comparator. | `ContaminationRiskMeasurement` + executable threshold. | | S16 | GAP | High | Not-found vs not-searched needs search coverage proof. | `ScopeSearchCoverageProof`. | | S17 | GAP | High | Cache keys are missing for scope/policy/UI exports. | `ScopeResolutionCacheKey`, `EffectivePolicyCacheKey`. | | S18 | GAP | High | Relation traversal can cycle or blow up. | `RelationTraversalBudget`, execution trace. | | S19 | GAP | High | Fan-out operations lack cost/quota envelopes. | `DOC81BatchOperationQuotaEnvelope`. | | S20 | BUG | High | `PolicyDisambiguationRequest` lacks answer/effect object; answer can be misused as direct allow. | `PolicyDisambiguationAnswer`. | ## Medium cleanups | ID | Type | Severity | Finding | Fix package | |---|---|---:|---|---| | M1 | SUGGESTION | Medium | `ScopeContainerRelation` uses `source_membership` / `topic_membership`, risking DOC87 membership confusion. | Rename to topology terms. | | M2 | GAP | Medium | `ScopeContainerRelation.policy_load_bearing` boolean is too coarse. | Add load-bearing effect list by action/axis. | | M3 | GAP | Medium | `PolicyMembraneDecision.crossing_disposition` needs derivation table. | `PolicyMembraneDispositionDerivation`. | | M4 | GAP | Medium | `TopicRiskClass` needs confirmation proof. | `TopicRiskConfirmation`. | | M5 | GAP | Medium | `SafeLabelDisclosurePolicy.default_label_ref` should be conditional. | Forbid default label when not disclosable. | | M6 | GAP | Medium | Published-view revocation lint lacks plane owner. | Assign to DOC84 or DOC86 explicitly. | | M7 | BUG | Medium | `VisibilityClass = string` is too loose. | External vocabulary value ref. | | M8 | BUG | Medium | Type aliases claim branded refs but are plain strings. | TS `Brand` pattern. | | M9 | GAP | Medium | `LegalHoldState` selector semantics are ambiguous. | Discriminated selector union. | | M10 | CLEANUP | Medium | E0 header/status drift should be cleaned in ratification pack. | Update header or add status pointer. | --- # 4. Core patch pack This section consolidates the concrete schemas and formulas I would add to DOC81. Some values are architect-confirmable, but the contract shapes are not optional if DOC81 is to be implementable without guessing. ## 4.1 Shared branded refs and external vocabulary refs DOC81 currently says refs are branded, but TypeScript `type X = string` is not an actual brand. Use a lightweight brand pattern. ```typescript type Brand = T & { readonly __brand: B }; type ContentHash = Brand; type SchemaVersionRef = Brand; type OwnerDocId = Brand; type MemoryObjectRef = Brand; type ScopeRef = Brand; type ScopeBoundaryRef = Brand; type ScopeResolutionResultRef = Brand; type ScopeContainerRelationRef = Brand; type ScopeEquivalenceBindingRef = Brand; type MemoryPolicyDecisionRef = Brand; type EffectiveMemoryPolicyRef = Brand; type PolicyEvaluationContextRef = Brand; type PolicyStampRef = Brand; type PolicyStampScopeRef = Brand; type PolicyCeilingRef = Brand; type PolicyObligationRef = Brand; type PolicyObligationConflictRef = Brand; type PolicyObligationDischargeRef = Brand; type SafeReferenceLabelRef = Brand; type PolicyDisambiguationRequestRef = Brand; type PolicyDisambiguationAnswerRef = Brand; type ThresholdEvaluationRef = Brand; type EffectiveStateGenerationId = Brand; type DomainProfileId = Brand; type PolicyGenerationId = Brand; type ReasonCodeId = Brand; type PrincipalRef = Brand; type SurfaceRef = Brand; type SourceRef = Brand; type TopicRef = Brand; type MemoryFlowCertificateId = Brand; type E0InteractionMode = 'interactive' | 'background' | 'scheduled' | 'agent_initiated'; ``` Replace loose foreign-vocabulary strings with owner/version-qualified refs. ```typescript interface ExternalVocabularyValueRef { owner_doc: OwnerDocId; registry_id: string; value_id: string; registry_version: SchemaVersionRef; } type VisibilityClassRef = ExternalVocabularyValueRef; ``` **Lints:** ```text schema.branded_ref_declared_as_plain_string schema.external_vocabulary_unversioned_string ``` --- ## 4.2 Runtime freshness key `policy_generation_id` is necessary but not sufficient. Runtime toggles, incognito/application state, compiled evaluator version, registry versions, and safe-label vocabulary changes must invalidate cached policy artifacts. ```typescript interface PolicyRuntimeFreshnessKey { policy_generation_id: PolicyGenerationId; effective_state_generation_id: EffectiveStateGenerationId; compiled_policy_evaluator_hash: ContentHash; domain_profile_registry_version: SchemaVersionRef; reason_code_registry_version: SchemaVersionRef; safe_label_vocabulary_version?: SchemaVersionRef; } interface RequiresPolicyRuntimeFreshness { freshness_key: PolicyRuntimeFreshnessKey; } ``` Apply `freshness_key` to all runtime-consumed DOC81 artifacts: ```text MemoryPolicyDecision EffectiveMemoryPolicy PolicyStamp PolicyStampRestamp PolicyStampInvalidation ExtractionRoutePolicyEnvelope PolicyCappedDAMSInput SafeLabelDisclosurePolicy PolicyDisambiguationRequest PolicyDisambiguationAnswer PolicyUIExport SourceExclusionFilterRule CollectionModeSuppressionGovernance CollectionSuppressionEvaluation CascadingSourceInvalidation CascadingSourceInvalidationRun ``` **Rule:** ```text A DOC81 runtime artifact is reusable only if its PolicyRuntimeFreshnessKey exactly matches the active policy runtime freshness key. No artifact may be reused across policy_generation_id, effective_state_generation_id, compiled_policy_evaluator_hash, domain profile registry, reason-code registry, or safe-label vocabulary changes. ``` **Lints / fixtures:** ```text policy.artifact_missing_effective_state_generation policy.stamp_reused_after_effective_state_change cache.policy_reused_across_effective_state_generation cache.safe_label_vocab_change_not_in_ui_export_key fixture.policy.application_toggle_invalidates_policy_exports fixture.policy.incognito_toggle_blocks_cached_collection_policy fixture.cache.policy_export_invalidates_on_safe_label_vocab_change ``` --- ## 4.3 Policy evaluation context The draft under-keys `MemoryPolicyDecision`. It needs an explicit evaluation context so decisions cannot bleed across principal, surface, exposure, model, client, interaction, or user-instruction conditions. ```typescript type E0OutboundDestinationClass = | 'same_machine_local_runtime' | 'local_file_export' | 'cloud_model_provider' | 'external_api' | 'connector_destination' | 'email_or_message' | 'browser_or_web' | 'unknown_destination'; 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'; interface PolicyEvaluationContext extends E0DurableRecord { evaluation_context_id: PolicyEvaluationContextRef; schema_owner: 'DOC81'; object_ref: MemoryObjectRef; action: MemoryPolicyAction; destination?: E0OutboundDestinationClass; principal_ref: PrincipalRef; surface_ref?: SurfaceRef; /** PropA-owned ExposureContextSchema instance/ref; DOC81 references, does not redefine. */ exposure_context_ref: string; 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; user_instruction_ref?: string; user_instruction_policy_bound: true; scope_resolution_ref?: ScopeResolutionResultRef; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` Then require it on `MemoryPolicyDecision`: ```typescript type ContentFidelityLevel = 'none' | 'safe_label' | 'reference_only' | 'redacted' | 'full'; type LocalityLevel = 'blocked' | 'local_only' | 'approved_external'; type LearningScopeLevel = | 'none' | 'audit_only' | 'same_scope_only' | 'same_firewall_only' | 'partitioned' | 'global_allowed'; type MutationAuthorityLevel = 'none' | 'candidate_only' | 'durable_requires_review' | 'durable_allowed'; type DisclosureClass = 'not_disclosable' | 'existence_only' | 'generic_safe_label_only' | 'redacted_summary' | 'full'; interface MemoryPolicyDecision extends E0DurableRecord { decision_id: MemoryPolicyDecisionRef; schema_owner: 'DOC81'; policy_evaluation_context_ref: PolicyEvaluationContextRef; object_ref: MemoryObjectRef; action: MemoryPolicyAction; destination?: E0OutboundDestinationClass; content_fidelity: ContentFidelityLevel; locality: LocalityLevel; learning_scope: LearningScopeLevel; mutation_authority: MutationAuthorityLevel; disclosure_class: DisclosureClass; disclosure_vector: DisclosurePermissionVector; obligations: PolicyObligationRef[]; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` **Applicability rule:** ```text A MemoryPolicyDecision is applicable only if: decision.object_ref == requested.object_ref decision.action == requested.action decision.policy_evaluation_context_ref resolves to a context compatible with: principal, surface, exposure_context, model_class, client_kind, interaction_mode, destination, and active PolicyRuntimeFreshnessKey. Destinationless decisions may tighten non-egress results but may not authorize egress. ``` **Lints / fixtures:** ```text policy.decision_context_not_part_of_key policy.local_interactive_decision_reused_for_background_agent propa.policy_decision_without_exposure_context policy.decision_missing_model_or_interaction_context fixture.policy.cloud_model_render_policy_differs_from_local_model_render fixture.propa.exposure_context_changes_policy_decision_key ``` --- ## 4.4 Explicit axis rank maps Never rely on enum declaration order. Use explicit rank maps. ```typescript 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, }, 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, }, } as const; ``` **Rule:** ```text Missing, NaN, unknown, out-of-vocabulary, or incomparable axis values resolve conservatively. For an applicable decision, a missing axis is not filtered out; it floors the affected axis or blocks the effective policy for the action. ``` **Lints / fixtures:** ```text policy.malformed_applicable_decision_axis_filtered_out policy.axis_missing_not_floored_conservative fixture.policy.missing_axis_in_one_applicable_decision_floors_that_axis fixture.policy.five_axes_present_and_independent ``` --- ## 4.5 Disclosure permission vector A scalar `DisclosureClass` is useful for display and coarse meet summaries, but it cannot be the only executable disclosure model. Disclosure should be a vector and the scalar class should be derived after vector meet. ```typescript 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, }; function minCountMode(values: CountDisclosureMode[]): CountDisclosureMode { return values.reduce((a, b) => COUNT_MODE_RANK[b] < COUNT_MODE_RANK[a] ? b : a, 'exact'); } function minSummaryFidelity(values: SummaryFidelity[]): SummaryFidelity { return values.reduce((a, b) => SUMMARY_FIDELITY_RANK[b] < SUMMARY_FIDELITY_RANK[a] ? b : a, 'full_reason'); } function meetDisclosureVectors(vectors: DisclosurePermissionVector[]): DisclosurePermissionVector { if (vectors.length === 0) return bottomDisclosureVector(); return { may_disclose_existence: vectors.every(v => v.may_disclose_existence), may_disclose_container_type: vectors.every(v => v.may_disclose_container_type), may_disclose_topic_label: vectors.every(v => v.may_disclose_topic_label), may_disclose_source_title: vectors.every(v => v.may_disclose_source_title), count_disclosure_mode: minCountMode(vectors.map(v => v.count_disclosure_mode)), may_disclose_reason_summary: vectors.every(v => v.may_disclose_reason_summary), max_summary_fidelity: minSummaryFidelity(vectors.map(v => v.max_summary_fidelity)), }; } function deriveDisclosureClass(v: DisclosurePermissionVector): DisclosureClass { if (!v.may_disclose_existence) return 'not_disclosable'; if ( v.may_disclose_existence && !v.may_disclose_container_type && !v.may_disclose_topic_label && !v.may_disclose_source_title && v.count_disclosure_mode === 'none' && !v.may_disclose_reason_summary ) return 'existence_only'; if (v.max_summary_fidelity === 'none') return 'generic_safe_label_only'; if (v.max_summary_fidelity === 'redacted_reason') return 'redacted_summary'; return 'full'; } ``` **Count-disclosure policy:** ```typescript interface CountDisclosurePolicy { mode: CountDisclosureMode; bucket_scheme_ref?: string; exact_count_allowed_only_if_disclosure_class: 'full'; } ``` Default bucket scheme: ```text 0 → "none" 1 → "one" 2-5 → "a few" 6-10 → "several" >10 → "multiple" ``` **Lints / fixtures:** ```text disclosure.scalar_class_used_without_permission_vector disclosure.count_disclosed_exact_when_only_bucket_allowed disclosure.enum_order_matches_rank_table safe_label.exact_count_disclosed_without_full_disclosure fixture.disclosure.vector_meet_ands_boolean_permissions fixture.disclosure.count_bucketed_unless_full ``` --- ## 4.6 Effective policy and meet algorithm v2 ```typescript interface EffectiveMemoryPolicy extends E0DurableRecord { effective_policy_id: EffectiveMemoryPolicyRef; schema_owner: 'DOC81'; object_ref: MemoryObjectRef; action: MemoryPolicyAction; destination?: E0OutboundDestinationClass; policy_evaluation_context_ref: PolicyEvaluationContextRef; contributing_decision_refs: MemoryPolicyDecisionRef[]; excluded_decision_refs: Array<{ 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_content_fidelity: ContentFidelityLevel; effective_locality: LocalityLevel; effective_learning_scope: LearningScopeLevel; effective_mutation_authority: MutationAuthorityLevel; effective_disclosure_class: DisclosureClass; effective_disclosure_vector: DisclosurePermissionVector; conservatism_floor_applied?: ScopeConservatismFloor; floor_effect_ref?: ConservatismFloorEffectRef; meet_kind: 'per_dimension_most_restrictive'; meet_algorithm_version: 'doc81.meet.v2'; obligations: PolicyObligationRef[]; obligation_conflict_refs: PolicyObligationConflictRef[]; original_ceiling_ref?: PolicyCeilingRef; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` Algorithm: ```text function meet_v2(request, decisions, scopeResolution): validate request.action is a known MemoryPolicyAction validate request.freshness_key matches active policy/effective-state generations if request.action in {export, delegate, carryover}: require destination require destination ∈ E0OutboundDestinationClass require at least one destination-specific decision where d.destination == destination destinationless decisions may tighten but never authorize egress applicable = [] excluded = [] for d in decisions: if !contextCompatible(d, request): excluded.add(...) else if any required axis is missing or unrecognized: return blockedBottomPolicy(reason = malformed_axis) else applicable.add(d) if applicable is empty: return blockedBottomPolicy(reason = policy.no_applicable_decision) for each capability axis: eff[axis] = min_by_rank(applicable.map(d => d[axis])) eff.disclosure_vector = meetDisclosureVectors(applicable.map(d => d.disclosure_vector)) eff.disclosure_class = deriveDisclosureClass(eff.disclosure_vector) eff = applyConservatismFloor(eff, scopeResolution.minimum_conservatism_floor) obligation_conflicts = detectObligationConflicts(unionObligations(applicable)) if obligation_conflicts.any(blocking): return blockedBottomPolicy(reason = policy.obligation_conflict) return eff ``` Destination-specific egress precondition: ```text If action ∈ {export, delegate, carryover}: a. destination MUST be present. b. destination MUST resolve to a recognized E0OutboundDestinationClass. c. destination MUST NOT be inferred from relation_to_destination. d. at least one applicable MemoryPolicyDecision MUST have d.destination === destination. Destinationless decisions may tighten the result but may not satisfy the destination-specific policy requirement. e. if destination !== 'same_machine_local_runtime', the downstream egress gate MUST produce an E0EgressAttestation whose policy_decision_destination === outbound_destination_class. ``` **Lints / fixtures:** ```text policy.egress_authorized_by_destinationless_decision policy.destination_required_action_without_destination policy.meet_across_actions policy.no_applicable_decision_not_fail_closed policy.malformed_axis_not_failed_closed fixture.egress.destinationless_policy_decision_cannot_authorize_export fixture.policy.meet_is_per_dimension_most_restrictive fixture.policy.empty_inputs_fail_closed fixture.policy.malformed_axis_in_applicable_decision_floors_axis ``` --- ## 4.7 Conservatism floor effect table The draft names floors but does not map them to axis effects. Add a table. ```typescript type ScopeConservatismFloor = | 'normal_policy_check' | 'reference_only_candidate' | 'safe_label_candidate' | 'user_disambiguation_candidate' | 'fail_closed_candidate'; type ConservatismFloorEffectRef = Brand; interface ConservatismFloorEffect extends E0DurableRecord { floor: ScopeConservatismFloor; schema_owner: 'DOC81'; 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[]; } ``` Seed table: ```typescript const DOC81_CONSERVATISM_FLOOR_TABLE: ConservatismFloorEffect[] = [ { floor: 'normal_policy_check', schema_owner: 'DOC81', max_content_fidelity: 'full', max_locality: 'approved_external', max_learning_scope: 'global_allowed', max_mutation_authority: 'durable_allowed', max_disclosure_class: 'full', max_disclosure_vector: { may_disclose_existence: true, may_disclose_container_type: true, may_disclose_topic_label: true, may_disclose_source_title: true, count_disclosure_mode: 'exact', may_disclose_reason_summary: true, max_summary_fidelity: 'full_reason', }, movement_allowed: true, disambiguation_required: false, allowed_actions_before_disambiguation: [ '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' ], reason_codes: [] }, { floor: 'reference_only_candidate', schema_owner: 'DOC81', max_content_fidelity: 'reference_only', max_locality: 'local_only', max_learning_scope: 'audit_only', max_mutation_authority: 'candidate_only', max_disclosure_class: 'generic_safe_label_only', max_disclosure_vector: { may_disclose_existence: true, may_disclose_container_type: true, may_disclose_topic_label: false, may_disclose_source_title: false, count_disclosure_mode: 'bucketed', may_disclose_reason_summary: true, max_summary_fidelity: 'generic_reason_only', }, movement_allowed: true, disambiguation_required: false, allowed_actions_before_disambiguation: [ 'retrieve', 'render_reference_only', 'render_safe_label', 'ui_disclose', 'inspect' ], reason_codes: ['DOC81.policy.reference_only_floor_applied'] }, { floor: 'safe_label_candidate', schema_owner: 'DOC81', max_content_fidelity: 'safe_label', max_locality: 'local_only', max_learning_scope: 'none', max_mutation_authority: 'none', max_disclosure_class: 'generic_safe_label_only', max_disclosure_vector: { may_disclose_existence: true, 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', }, movement_allowed: true, disambiguation_required: false, allowed_actions_before_disambiguation: ['render_safe_label', 'ui_disclose', 'inspect'], reason_codes: ['DOC81.policy.safe_label_floor_applied'] }, { floor: 'user_disambiguation_candidate', schema_owner: 'DOC81', max_content_fidelity: 'none', max_locality: 'blocked', max_learning_scope: 'none', max_mutation_authority: 'none', max_disclosure_class: 'generic_safe_label_only', max_disclosure_vector: { may_disclose_existence: true, 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', }, movement_allowed: false, disambiguation_required: true, allowed_actions_before_disambiguation: ['render_safe_label', 'ui_disclose'], reason_codes: ['DOC81.policy.disambiguation_required_before_use'] }, { floor: 'fail_closed_candidate', schema_owner: 'DOC81', max_content_fidelity: 'none', max_locality: 'blocked', max_learning_scope: 'none', max_mutation_authority: 'none', max_disclosure_class: 'not_disclosable', max_disclosure_vector: bottomDisclosureVector(), movement_allowed: false, disambiguation_required: false, allowed_actions_before_disambiguation: [], reason_codes: ['DOC81.policy.fail_closed_floor_applied'] } ]; ``` **Meet step:** ```text After computing the per-axis most-restrictive meet, EC applies ConservatismFloorEffect by taking the stricter value of each effective axis and the floor’s max axis. If movement_allowed = false, the effective policy blocks all movement actions unless a valid ceiling-bounded restamp or disambiguation answer creates a new policy decision under the current freshness key. ``` **Lints / fixtures:** ```text policy.effective_undercut_scope_floor policy.floor_without_axis_effect_table fixture.policy.floor_mapping_tightens_each_axis fixture.scope.uncertain_affinity_floors_reference_only ``` --- ## 4.8 Destination policy crosswalk `relation_to_destination` is a scope relation. `E0OutboundDestinationClass` is an egress class. They must not collapse into each other. ```typescript interface DestinationPolicyCrosswalk extends E0DurableRecord { crosswalk_id: string; 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; 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: ```typescript const DOC81_DESTINATION_CROSSWALK_RULES: DestinationPolicyCrosswalk[] = [ { crosswalk_id: 'doc81.dest.same_runtime', schema_owner: 'DOC81', relation_to_destination: 'same_runtime', outbound_destination_class: 'same_machine_local_runtime', policy_floor: 'normal_policy_check', egress_attestation_required: false, policy_lookup_required: false, disposition: 'no_egress_same_runtime_only', reason_codes: [] }, { crosswalk_id: 'doc81.dest.same_machine_local_file_export', schema_owner: 'DOC81', relation_to_destination: 'same_machine_local', outbound_destination_class: 'local_file_export', policy_floor: 'reference_only_candidate', egress_attestation_required: true, policy_lookup_required: true, disposition: 'policy_bound_egress', reason_codes: ['DOC81.egress.local_file_export_requires_policy_decision'] }, { crosswalk_id: 'doc81.dest.external', schema_owner: 'DOC81', relation_to_destination: 'external_destination', outbound_destination_class: 'external_api', policy_floor: 'fail_closed_candidate', egress_attestation_required: true, policy_lookup_required: true, disposition: 'policy_bound_egress', reason_codes: ['DOC81.egress.external_destination_requires_attestation'] }, { crosswalk_id: 'doc81.dest.unknown', schema_owner: 'DOC81', relation_to_destination: 'unknown_destination', policy_floor: 'fail_closed_candidate', egress_attestation_required: false, policy_lookup_required: false, disposition: 'block_until_terminal_destination_resolved', reason_codes: ['DOC81.egress.unknown_terminal_destination_blocked'] }, { crosswalk_id: 'doc81.dest.blocked', schema_owner: 'DOC81', relation_to_destination: 'blocked_destination', policy_floor: 'fail_closed_candidate', egress_attestation_required: false, policy_lookup_required: false, disposition: 'block', reason_codes: ['DOC81.egress.destination_blocked_by_policy'] } ]; ``` **Rule:** ```text relation_to_destination NEVER substitutes for E0OutboundDestinationClass. Any action with terminal bytes leaving same-machine runtime must resolve an E0 destination class and, unless the class is exactly same_machine_local_runtime, must carry an E0EgressAttestation. ``` **Lints / fixtures:** ```text egress.relation_to_destination_used_as_egress_class egress.local_file_export_treated_as_same_runtime fixture.egress.same_machine_local_file_export_requires_attestation ``` --- ## 4.9 Policy stamp, scope items, ceiling snapshot, and restamp ### 4.9.1 Fix stamp triple binding ```typescript interface PolicyStampScopeItem { action: MemoryPolicyAction; destination?: E0OutboundDestinationClass; // required iff action ∈ {export, delegate, carryover} effective_policy_ref: EffectiveMemoryPolicyRef; // MUST point to same object/action/destination/context triple original_ceiling_ref: PolicyCeilingRef; } interface PolicyStamp extends E0DurableRecord { stamp_id: PolicyStampRef; schema_owner: 'DOC81'; object_ref: MemoryObjectRef; issued_by: 'EC_compiled_policy_evaluator'; freshness_key: PolicyRuntimeFreshnessKey; scope_items: PolicyStampScopeItem[]; // non-empty expires_at?: string; // RFC3339-UTC lifecycle_state: 'active' | 'superseded' | 'invalidated' | 'expired' | 'blocked'; reason_codes: ReasonCodeId[]; } interface PolicyStampScope extends E0DurableRecord { stamp_scope_id: PolicyStampScopeRef; schema_owner: 'DOC81'; stamp_ref: PolicyStampRef; object_ref: MemoryObjectRef; scope_items: PolicyStampScopeItem[]; freshness_key: PolicyRuntimeFreshnessKey; expires_at?: string; } ``` **Lints / fixtures:** ```text policy.stamp_scope_action_without_matching_effective_policy policy.retrieval_stamp_used_for_render_or_export fixture.policy.stamp_scope_item_matches_effective_policy_triple fixture.policy.retrieval_stamp_cannot_authorize_render_or_export ``` ### 4.9.2 Ceiling snapshot ```typescript 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; // RFC3339-UTC issued_by: 'EC'; reason_codes: ReasonCodeId[]; } interface RestampChainIntegrity extends E0DurableRecord { chain_id: string; schema_owner: 'DOC81'; root_stamp_ref: PolicyStampRef; ceiling_snapshot_ref: PolicyCeilingRef; restamp_refs: string[]; invariant: 'every_restamp_compared_to_root_ceiling_snapshot'; } ``` **Rule:** ```text A restamp is valid only if every action/destination item is compared against the root PolicyCeilingSnapshot and every axis comparison is within_ceiling or downgraded. Any exceeds_ceiling result blocks issuance and emits restamp.exceeds_original_ceiling. ``` **Lints / fixtures:** ```text restamp.chain_ceiling_rebased_to_prior_restamp restamp.axis_comparison_missing fixture.policy.restamp_of_restamp_compares_against_root_ceiling fixture.policy.restamp_ceiling_vector_blocks_axis_widening ``` ### 4.9.3 Restamp discriminated union ```typescript type PolicyStampRestamp = | PolicyStampRestampKeep | PolicyStampRestampDowngrade | PolicyStampRestampBlock; interface PolicyAxisCeilingComparison { axis: | 'content_fidelity' | 'locality' | 'learning_scope' | 'mutation_authority' | 'disclosure_class' | 'disclosure_vector'; comparison: 'within_ceiling' | 'downgraded' | 'exceeds_ceiling'; prior_value: string; new_value?: string; ceiling_value: string; } interface PolicyStampRestampBase extends E0DurableRecord { restamp_id: string; schema_owner: 'DOC81'; object_ref: MemoryObjectRef; prior_stamp_ref: PolicyStampRef; root_stamp_ref: PolicyStampRef; prior_freshness_key: PolicyRuntimeFreshnessKey; new_freshness_key: PolicyRuntimeFreshnessKey; original_ceiling_ref: PolicyCeilingRef; memory_flow_certificate_ref: MemoryFlowCertificateId; ceiling_comparisons: PolicyAxisCeilingComparison[]; reason_codes: ReasonCodeId[]; } 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: Array< 'content_fidelity' | 'locality' | 'learning_scope' | 'mutation_authority' | 'disclosure_class' | 'disclosure_vector' >; ceiling_compliance_attested: true; } interface PolicyStampRestampBlock extends PolicyStampRestampBase { restamp_disposition: 'block'; new_effective_policy_refs?: never; blocked_reason_codes: ReasonCodeId[]; ceiling_compliance_attested: true; } ``` **Lints / fixtures:** ```text restamp.block_disposition_with_new_effective_policy_ref restamp.exceeds_original_ceiling fixture.policy.blocked_restamp_has_no_authorizing_effective_policy ``` ### 4.9.4 Invalidation deltas ```typescript interface PolicyAxisDelta { axis: | 'content_fidelity' | 'locality' | 'learning_scope' | 'mutation_authority' | 'disclosure_class' | 'disclosure_vector'; 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: string; schema_owner: 'DOC81'; invalidated_stamp_ref: PolicyStampRef; object_ref: MemoryObjectRef; prior_freshness_key: PolicyRuntimeFreshnessKey; new_freshness_key: PolicyRuntimeFreshnessKey; affected_scope_items: PolicyStampInvalidationScopeItem[]; user_visible_summary_ref: SafeReferenceLabelRef; reason_codes: ReasonCodeId[]; } ``` **Lints / fixtures:** ```text policy.invalidation_axis_delta_unmapped_to_action fixture.policy.invalidation_reports_axis_delta_per_action_destination ``` --- ## 4.10 Action closure and permission predicates Action-specific policy checking is not enough; terminal actions imply prerequisite policy checks. ```typescript interface MemoryPolicyActionClosureRule extends E0DurableRecord { rule_id: string; 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[]; } const DOC81_ACTION_CLOSURE_RULES: MemoryPolicyActionClosureRule[] = [ { rule_id: 'doc81.action.render_inline', schema_owner: 'DOC81', requested_action: 'render_inline', required_policy_actions: ['retrieve', 'render_inline', 'ui_disclose'], egress_attestation_required: false, memory_flow_certificate_required: true, scope_resolution_required: true, require_effective_policy_per_action: true, reason_codes: [] }, { rule_id: 'doc81.action.export', schema_owner: 'DOC81', requested_action: 'export', required_policy_actions: ['retrieve', 'export', 'ui_disclose'], egress_attestation_required: true, memory_flow_certificate_required: true, scope_resolution_required: true, require_effective_policy_per_action: true, reason_codes: ['DOC81.egress.export_requires_policy_and_attestation'] }, { rule_id: 'doc81.action.learn_global', schema_owner: 'DOC81', requested_action: 'learn_global', required_policy_actions: ['retrieve', 'learn_global'], egress_attestation_required: false, memory_flow_certificate_required: true, scope_resolution_required: true, require_effective_policy_per_action: true, reason_codes: ['DOC81.learning.requires_policy_gate'] } ]; ``` Action predicate table: ```typescript interface ActionPermissionPredicate extends E0DurableRecord { predicate_id: string; 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[]; } const DOC81_ACTION_PERMISSION_PREDICATES: ActionPermissionPredicate[] = [ { predicate_id: 'doc81.predicate.retrieve', schema_owner: 'DOC81', action: 'retrieve', required_minima: { content_fidelity: 'reference_only', locality: 'local_only', }, requires_destination: false, requires_egress_attestation: false, requires_mfc: false, requires_obligation_discharge: true, reason_codes: [] }, { predicate_id: 'doc81.predicate.render_inline', schema_owner: 'DOC81', action: 'render_inline', required_minima: { content_fidelity: 'redacted', locality: 'local_only', disclosure_class: 'redacted_summary', }, requires_destination: false, requires_egress_attestation: false, requires_mfc: true, requires_obligation_discharge: true, reason_codes: [] }, { predicate_id: 'doc81.predicate.export', schema_owner: 'DOC81', action: 'export', required_minima: { content_fidelity: 'redacted', locality: 'approved_external', disclosure_class: 'redacted_summary', }, requires_destination: true, requires_egress_attestation: true, requires_mfc: true, requires_obligation_discharge: true, reason_codes: ['DOC81.egress.export_requires_destination_policy'] }, { predicate_id: 'doc81.predicate.learn_global', schema_owner: 'DOC81', action: 'learn_global', required_minima: { learning_scope: 'global_allowed', }, requires_destination: false, requires_egress_attestation: false, requires_mfc: true, requires_obligation_discharge: true, reason_codes: ['DOC81.learning.global_learning_requires_policy_clearance'] } ]; ``` The exact minima are architect-confirmable; the existence of a predicate table is not optional. **Lints / fixtures:** ```text policy.terminal_action_checked_without_prerequisite_actions policy.action_predicate_missing fixture.policy.render_inline_requires_retrieve_and_ui_disclose_policy fixture.policy.export_requires_retrieve_export_ui_disclose_and_egress ``` --- ## 4.11 Policy obligations, conflicts, discharge, and PropA kinds ### 4.11.1 Expanded obligation kinds ```typescript 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' // PropA / DOC73 V1.5.1 fold-in obligations: | 'require_migration_plan' | 'route_manual_review_only' | 'require_verifier_calibration_current' | 'exclude_uncalibrated_verifier_signals' | 'forbid_sealed_source_fixture_generation' | 'restrict_firewalled_fixture_generation_to_same_firewall' | 'discard_sealed_learning_signals' | 'restrict_firewalled_learning_to_same_firewall' | 'require_dspy_target_eligibility' | 'require_post_retrieval_critique_policy_gate' | 'require_counterfactual_prompt_policy_gate' | 'require_additive_synthesis_prompt_policy_gate'; ``` ### 4.11.2 Enforcement owners DOC84 and DOC86 must be valid owners because delivery/render and UI/Inspector surfaces discharge several policy obligations. ```typescript type PolicyObligationEnforcementOwner = | 'EC' | 'PropA' | 'DOC24' | 'KDA' | 'DOC84' | 'DOC86' | 'DOC11' | 'DOC20' | 'DOC1' | 'DOC25' | 'DOC73' | 'DOC85'; ``` ### 4.11.3 Typed obligation union ```typescript 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; } type PolicyObligation = | RequireRedactionObligation | RequireSafeLabelObligation | RequireMigrationPlanObligation | RequireVerifierCalibrationCurrentObligation | RestrictFirewalledLearningObligation | ReceiptRequiredObligation | GenericReviewQueueObligation; interface RequireRedactionObligation extends PolicyObligationBase { obligation_kind: 'require_redaction'; parameters: { redaction_map_required: true; redaction_map_ref?: string; }; } interface RequireSafeLabelObligation extends PolicyObligationBase { obligation_kind: 'require_safe_label'; parameters: { safe_label_policy_ref: SafeReferenceLabelRef; allow_existence_disclosure_only_if_policy_allows: true; }; } interface RequireMigrationPlanObligation extends PolicyObligationBase { obligation_kind: 'require_migration_plan'; parameters: { schema_migration_plan_ref?: string; if_missing: 'route_manual_review_only'; }; } interface RequireVerifierCalibrationCurrentObligation extends PolicyObligationBase { obligation_kind: 'require_verifier_calibration_current'; parameters: { verifier_calibration_ledger_ref?: string; if_uncalibrated_or_drifting: 'exclude_verifier_signal'; }; } interface RestrictFirewalledLearningObligation extends PolicyObligationBase { obligation_kind: 'restrict_firewalled_learning_to_same_firewall'; parameters: { firewall_scope_ref: ScopeRef; }; } interface ReceiptRequiredObligation extends PolicyObligationBase { obligation_kind: 'receipt_required'; parameters: { receipt_kind: 'memory_flow_certificate' | 'egress_attestation' | 'obligation_discharge' | 'ui_notice_receipt'; }; } interface GenericReviewQueueObligation extends PolicyObligationBase { obligation_kind: 'require_review_queue' | 'route_manual_review_only'; parameters: { queue_kind: 'privacy_review' | 'schema_migration_review' | 'policy_conflict_review' | 'source_revocation_review'; }; } ``` ### 4.11.4 Conflict model ```typescript 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[]; } ``` ### 4.11.5 Discharge proof ```typescript 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[]; } ``` **Lints / fixtures:** ```text policy.undefined_policy_obligation policy.obligation_dropped_under_meet policy.obligation_conflict_not_failed_closed policy.obligation_owner_missing_doc84_doc86 policy.obligation_parameters_untyped policy.obligation_not_discharged_before_movement fixture.policy.conflicting_obligations_fail_closed_for_action fixture.policy.require_redaction_obligation_needs_redaction_map ``` --- ## 4.12 PropA row landings correction The CODEX fix aligned §7.3 with actual PropA carried rows, but the lineage table and executable obligation schema must match it. Paste-ready §14.2 replacement: ```markdown ### §14.2 OPA-row landings — corrected after CODEX 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.1/§3.2: `ExposureContextSchemaRef` is a named policy-evaluation input; 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 may operate only inside `PolicyCappedDAMSInput` eligibility. | | `OBL-PROPA-NEW-V15-01` | `LearningVisibilityScope` filtering; sealed discarded; firewalled same-firewall only | §7.3 + §3.3 + §6.3: learning-policy partition gate consumed by DOC85 / PropA. | | `OBL-PROPA-NEW-V15-02` | `SchemaMigrationPlan` required for schema-changing prompt iterations; plan-less iterations route `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: verifier-calibration policy obligation consumed by DOC85 / PropA. | | `OBL-PROPA-NEW-V15-04` | AVAPO regression fixture exclusion; sealed fixtures not generated; firewalled fixtures same-firewall | §7.3 + §3.3: fixture-generation privacy gate. | | `OBL-PROPA-NEW-V15-05` | Post-retrieval critique prompt surface | §7.3: critique prompt registration / critique-utility eligibility, bounded by policy ceilings. | | `OBL-PROPA-NEW-V15-06` | Counterfactual ambiguity classification + additive synthesis prompt surfaces | §7.3: prompt-surface eligibility and learning-policy ceilings. | | `OBL-XDOC-PROPA-DSPY-TARGETS-01` | Four DSPy target IDs + eligibility discipline | §7.3 + §4.4 + §6.3: PropA owns registry; DOC81 supplies eligibility ceilings. | | `OBL-PROPA-NEW-SOURCE-EXCLUSION-FILTER-01` | Source-exclusion rule | §7.2. | | `OBL-D81-TOPIC-COLLECTION-SUPPRESSION-01` | Topic-level collection suppression | §7.1. | | `OBL-PROPA-LOCALFILEEXPORT-OUTBOUND-PATCH-01` | `local_file_export` treated as egress | §7.4 + DestinationPolicyCrosswalk. | ``` **Lints / fixtures:** ```text propa.opa_landing_table_stale_after_fix propa.schema_changing_iteration_without_migration_plan_not_manual_review fixture.propa.firewalled_learning_signal_same_firewall_only fixture.propa.schema_changing_iteration_without_migration_plan_routes_manual_review ``` --- ## 4.13 Safe-label disclosure policy ```typescript interface SafeLabelDisclosurePolicy extends E0DurableRecord { policy_id: string; schema_owner: 'DOC81'; disclosure_class: DisclosureClass; disclosure_vector: DisclosurePermissionVector; protected_reason_class: | 'privileged' | 'sealed' | 'firewalled' | 'personal' | 'client_confidential' | 'matter_specific' | 'policy_unknown'; count_disclosure_policy: CountDisclosurePolicy; /** Required iff may_disclose_existence = true. Forbidden when disclosure_class = 'not_disclosable'. */ default_label_ref?: SafeReferenceLabelRef; /** Internal-only label for audit/Inspector redaction paths; never model-visible unless disclosure permits. */ internal_suppressed_manifest_label_ref?: SafeReferenceLabelRef; inspector_label_ref?: SafeReferenceLabelRef; effective_policy_ref: EffectiveMemoryPolicyRef; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` **Lints / fixtures:** ```text safe_label.default_label_present_when_not_disclosable safe_label.existence_disclosed_when_not_disclosable safe_label.exact_count_disclosed_without_full_disclosure fixture.disclosure.not_disclosable_has_no_user_visible_label fixture.disclosure.count_bucketed_unless_full ``` --- ## 4.14 Policy UI export for DOC86 DOC86 must render from DOC81 output without recomputing policy. ```typescript interface PolicyUIExport extends E0DurableRecord { export_id: string; schema_owner: 'DOC81'; object_ref: MemoryObjectRef; coordination_trace_ref: string; policy_stamp_ref: PolicyStampRef; policy_stamp_scope_ref: PolicyStampScopeRef; scope_items: Array<{ action: MemoryPolicyAction; destination?: E0OutboundDestinationClass; effective_policy_ref: EffectiveMemoryPolicyRef; restamp_eligibility: 'eligible' | 'downgrade_only' | 'blocked' | 'requires_disambiguation'; visible_action_disposition: | 'enabled' | 'disabled_policy_blocked' | 'disabled_requires_restamp' | 'disabled_requires_disambiguation' | 'hidden_not_disclosable'; }>; disclosure_class: DisclosureClass; disclosure_vector: DisclosurePermissionVector; safe_label_policy_ref: string; safe_label_constraints: { 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; expires_at?: string; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` **Rule:** ```text DOC86 consumes PolicyUIExport as a render input only. DOC86 may map it to DOC86-owned AvailabilityDisposition, VisibleContextActionSpec, InspectorVisibilityPlan, and notices, but it MUST NOT recompute EffectiveMemoryPolicy. ``` **Lints / fixtures:** ```text ui.policy_export_missing_action_destination_eligibility ui.doc86_recomputed_effective_policy fixture.ui.policy_export_renders_without_recomputing_meet fixture.cache.safe_label_vocab_change_invalidates_policy_ui_export ``` --- ## 4.15 Disambiguation request and answer ### 4.15.1 Request union `fallback_if_unanswered = defer` should only be legal when `blocked_until_answered = false`. ```typescript 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'; safe_prompt_label_ref: SafeReferenceLabelRef; may_name_object: boolean; may_name_scope: boolean; policy_decision_ref: MemoryPolicyDecisionRef; deadline_at?: string; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } interface BlockingPolicyDisambiguationRequest extends PolicyDisambiguationRequestBase { blocked_until_answered: true; fallback_if_unanswered: 'block' | 'reference_only' | 'search_only'; } interface NonBlockingPolicyDisambiguationRequest extends PolicyDisambiguationRequestBase { blocked_until_answered: false; fallback_if_unanswered: 'block' | 'reference_only' | 'search_only' | 'defer'; } ``` ### 4.15.2 Answer/effect object ```typescript interface PolicyDisambiguationAnswer extends E0DurableRecord { answer_id: PolicyDisambiguationAnswerRef; schema_owner: 'DOC81'; request_ref: PolicyDisambiguationRequestRef; answered_at: string; // RFC3339-UTC answered_by: PrincipalRef; answer_value: | 'confirm_scope' | 'deny_scope' | 'confirm_destination' | 'deny_destination' | 'narrow_to_safe_material_only' | 'unknown_or_no_answer'; /** An answer never grants capability directly. It can only create a new scope/policy input under the current generation, then force a new meet. */ effect: | 'new_scope_resolution_required' | 'new_policy_decision_required' | 'restamp_required' | 'fallback_applied' | 'block'; resulting_scope_resolution_ref?: ScopeResolutionResultRef; resulting_policy_decision_ref?: MemoryPolicyDecisionRef; resulting_effective_policy_ref?: EffectiveMemoryPolicyRef; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` **Lints / fixtures:** ```text policy.blocking_disambiguation_with_defer_fallback policy.disambiguation_answer_used_as_direct_allow state.disambiguation_without_terminal_state fixture.policy.blocking_disambiguation_cannot_defer_forever fixture.policy.user_confirmation_forces_new_meet_not_direct_allow ``` --- ## 4.16 Scope model patches ### 4.16.1 Synthetic unknown scope ```typescript type ScopeKind = | 'project' | 'matter' | 'engagement' | 'research_topic' | 'initiative' | 'library' | 'personal_domain' | 'global' | 'unknown_synthetic'; interface ScopeIdentityRoot extends E0DurableRecord { scope_ref: ScopeRef; schema_owner: 'DOC81'; display_name?: string; // NOT a safe label; not for disclosure scope_kind: ScopeKind; owner_principal_ref?: PrincipalRef; visibility_class_ref?: VisibilityClassRef; lifecycle_state: 'active' | 'deprecated' | 'synthetic_unknown'; } ``` **Lints / fixtures:** ```text scope.unknown_resolution_reused_global_scope fixture.scope.unresolvable_object_gets_synthetic_unknown_scope_not_global ``` ### 4.16.2 Pairwise equivalence confidence ```typescript 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[]; } 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'; pairwise_evidence: ScopeEquivalencePairEvidence[]; /** Computed as min(pairwise_evidence.confidence) across every required pair. Any missing pair for a cluster of size >2 is treated as 0. */ cluster_confidence: number; threshold_evaluation_ref: ThresholdEvaluationRef; collapse_disposition: | 'collapse_confirmed' | 'do_not_collapse_below_threshold' | 'requires_user_confirmation' | 'invalid_missing_pairwise_evidence'; domain_threshold_ref: DomainProfileId; reason_codes: ReasonCodeId[]; } ``` Formula: ```text required_pairs = n * (n - 1) / 2 if pairwise_evidence.count < required_pairs: cluster_confidence = 0 else: cluster_confidence = min(pair.confidence for all required pairs) collapse_allowed iff cluster_confidence >= resolved_threshold_value ``` **Lints / fixtures:** ```text scope.equivalence_cluster_without_pairwise_matrix scope.equivalence_cluster_confidence_not_min_pairwise fixture.scope.three_scope_cluster_low_pair_prevents_collapse ``` ### 4.16.3 Scope resolution confidence breakdown ```typescript interface ScopeResolutionConfidenceBreakdown { identity_confidence?: number; boundary_confidence?: number; affinity_confidence?: number; destination_relation_confidence?: number; sensitivity_classification_confidence?: number; population_freshness_confidence?: number; aggregation_method: 'minimum_of_required_components'; required_components: Array< | 'identity_confidence' | 'boundary_confidence' | 'destination_relation_confidence' | 'sensitivity_classification_confidence' | 'population_freshness_confidence' >; aggregate_confidence: number; // min(required component values); missing required component = 0 } ``` Formula: ```text aggregate_confidence = min(component_value for component in required_components) where missing required component = 0 if aggregate_confidence < resolved_threshold_value: minimum_conservatism_floor >= reference_only_candidate ``` ### 4.16.4 Scope resolution result additions ```typescript interface ScopeResolutionResult extends E0DurableRecord { resolution_ref: ScopeResolutionResultRef; schema_owner: 'DOC81'; request_ref: string; object_ref: MemoryObjectRef; object_scope_ref: ScopeRef; request_scope_ref?: ScopeRef; destination_scope_ref?: ScopeRef; relation_to_request: ScopeAffinity; relation_to_destination?: string; boundary_ref: ScopeBoundaryRef; minimum_conservatism_floor: ScopeConservatismFloor; confidence_breakdown: ScopeResolutionConfidenceBreakdown; threshold_evaluation_ref: ThresholdEvaluationRef; population_generation_inputs: Array<{ scope_ref: ScopeRef; scope_population_generation_id: string; population_state_at_resolution: 'healthy' | 'sparse' | 'stale' | 'rebuilding' | 'unavailable'; }>; sensitivity_inputs: Array<{ 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; }>; protection_derivation: ScopeProtectionStateDerivation; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` ### 4.16.5 Protection flags ```typescript type ScopeProtectionFlag = | 'matter_or_project_sensitive' | 'firewalled' | 'sealed' | 'personal_private' | 'client_confidential' | 'privileged' | 'classification_unknown'; interface ScopeProtectionStateDerivation { active_flags: ScopeProtectionFlag[]; /** Most restrictive resolved state for backward-compatible summary displays. */ resolved_protection_state: | 'ordinary' | 'matter_or_project_sensitive' | 'personal_private' | 'client_confidential' | 'firewalled' | 'privileged' | 'sealed' | 'unknown_sensitive'; derivation_rule: 'max_restrictiveness_over_active_flags'; classification_input_refs: string[]; classification_generation_id: string; reason_codes: ReasonCodeId[]; } ``` Rank: ```text ordinary < matter_or_project_sensitive < personal_private < client_confidential < firewalled < privileged < sealed < unknown_sensitive ``` **Lints / fixtures:** ```text scope.resolution_confidence_weighted_average_used scope.resolution_missing_confidence_component_not_zeroed scope.population_generation_not_pinned_to_resolution scope.multiple_protection_flags_collapsed_without_derivation fixture.scope.low_destination_confidence_forces_conservative_floor fixture.scope.population_generation_bump_invalidates_scope_resolution_result fixture.scope.sealed_plus_personal_resolves_to_sealed_but_preserves_flags ``` ### 4.16.6 Scope container relation cleanup Rename misleading membership-like relation kinds: ```typescript type ScopeContainerRelationKind = | 'contains' | 'linked_library' | 'source_topology_link' // replaces source_membership | 'project_binding' | 'topic_topology_link' // replaces topic_membership | 'episode_touched_scope' | 'analogical_relation'; ``` Add load-bearing effects: ```typescript 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; load_bearing_effects?: Array<{ applies_to_actions: MemoryPolicyAction[]; tightens_axes: Array< 'content_fidelity' | 'locality' | 'learning_scope' | 'mutation_authority' | 'disclosure_class' >; max_floor?: ScopeConservatismFloor; may_widen: false; reason_codes: ReasonCodeId[]; }>; reason_codes: ReasonCodeId[]; } ``` **Lints / fixtures:** ```text scope.container_relation_widened_meet scope.membership_term_used_for_topology_relation scope.policy_load_bearing_boolean_without_effects fixture.scope.container_relation_cannot_widen ``` --- ## 4.17 Threshold evaluation Threshold semantics must include comparator, equality rule, versions, and conservative handling of missing/NaN. ```typescript interface ThresholdEvaluation extends E0DurableRecord { evaluation_id: ThresholdEvaluationRef; schema_owner: 'DOC81'; threshold_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; observed_value: number; // finite number threshold_value: number; // finite number comparator: 'gte_pass' | 'gt_pass' | 'lte_pass' | 'lt_pass'; /** Conservative equality rule: * confidence passes at >= threshold; * risk blocks at >= threshold. */ equality_rule: 'pass_on_equal' | 'block_on_equal'; prior_state?: string; enter_threshold_value?: number; exit_threshold_value?: number; hysteresis_applied: boolean; disposition: | 'pass' | 'fail_closed' | 'requires_review' | 'defer_until_more_evidence'; reason_codes: ReasonCodeId[]; } ``` Recommended defaults: ```text confidence gates: pass iff observed_value >= threshold_value risk gates: block/reroute iff observed_value >= threshold_value unknown / NaN / missing observed_value: fail_closed hysteresis: allowed for UI/review state flapping; forbidden for hard safety gates unless it tightens ``` **Lints / fixtures:** ```text threshold.equality_rule_missing threshold.nan_or_missing_value_not_failed_closed threshold.hysteresis_widened_safety_gate fixture.threshold.risk_equal_threshold_blocks fixture.threshold.confidence_equal_threshold_passes ``` --- ## 4.18 Collection suppression ### 4.18.1 Collection mode rank ```typescript type CollectionMode = 'collect' | 'suppress' | 'exclude'; const COLLECTION_MODE_RANK: Record = { collect: 0, suppress: 1, exclude: 2, }; 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 ); } ``` ### 4.18.2 Governance and evaluation ```typescript interface CollectionModeSuppressionGovernance extends E0DurableRecord { governance_id: string; schema_owner: 'DOC81'; topic_ref: TopicRef; 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' existing_material_scan_ref?: string; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } interface CollectionSuppressionEvaluation extends E0DurableRecord { evaluation_id: string; schema_owner: 'DOC81'; candidate_object_ref?: MemoryObjectRef; topic_ref_candidates: TopicRef[]; matched_governance_refs: string[]; topic_match_confidence_max: number; topic_match_confidence_min: number; ambiguity_state: 'unambiguous' | 'ambiguous' | 'no_topic_match'; 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'; existing_material_backfill_required: boolean; existing_material_backfill_ref?: string; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` Rules: ```text If any matched governance is exclude → effective_collection_mode = exclude. If any matched governance is suppress and none exclude → suppress. If topic match is ambiguous and any candidate is suppress/exclude → defer_review_fail_closed. If EC collection disabled or incognito active → suppress regardless of DOC81 mode. If collection_mode changes to exclude → existing_material_backfill_required = true. ``` **Lints / fixtures:** ```text collection.ambiguous_topic_match_collected collection.exclude_without_existing_material_backfill collection.incognito_not_part_of_collection_policy_freshness fixture.collection.ambiguous_privacy_topic_fails_closed fixture.collection.exclude_filters_existing_material ``` --- ## 4.19 Source exclusion and extraction route envelope ### 4.19.1 Fix input/output cycle and add closure ```typescript interface SourceExclusionFilterRule extends E0DurableRecord { rule_id: string; schema_owner: 'DOC81'; excluded_source_ref: SourceRef; exclusion_scope_ref?: ScopeRef; applies_to_actions: MemoryPolicyAction[]; exclusion_reason_code: ReasonCodeId; policy_input_decision_ref: MemoryPolicyDecisionRef; 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; reason_codes: ReasonCodeId[]; } ``` **Rule:** ```text SourceExclusionFilterRule is an input to EffectiveMemoryPolicy, not a dependent output. The resulting EffectiveMemoryPolicy.contributing_decision_refs records the policy decision that carried the source-exclusion rule. ``` ### 4.19.2 Extraction route envelope ```typescript type ExtractionRouteContextRef = Brand; type CollectionGateResultRef = Brand; type SourceExclusionFilterRuleRef = Brand; type CollectionModeSuppressionGovernanceRef = Brand; interface ExtractionRoutePolicyEnvelope extends E0DurableRecord { envelope_id: string; schema_owner: 'DOC81'; extraction_route_context_ref: ExtractionRouteContextRef; // DOC82-owned route/provenance context source_ref?: SourceRef; permitted_actions: Extract< MemoryPolicyAction, 'collect' | 'extract' | 'classify' | 'write_candidate' >[]; effective_policy_ref: EffectiveMemoryPolicyRef; scope_resolution_ref: ScopeResolutionResultRef; collection_gate_result_ref?: CollectionGateResultRef; source_exclusion_rule_refs: SourceExclusionFilterRuleRef[]; topic_collection_governance_ref?: CollectionModeSuppressionGovernanceRef; unknown_source_disposition: 'fail_closed'; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` **Lints / fixtures:** ```text source_exclusion.effective_policy_ref_input_output_cycle source_exclusion.derived_artifact_not_excluded extraction.source_exclusion_boolean_without_rule_ref fixture.source_exclusion.derived_artifact_exclusion_closure fixture.extraction.source_exclusion_rule_ref_blocks_matching_source ``` --- ## 4.20 Source revocation cascade ### 4.20.1 Cascade run state ```typescript interface CascadingSourceInvalidation extends E0DurableRecord { invalidation_id: string; schema_owner: 'DOC81'; source_ref: SourceRef; affected_set_manifest_ref: string; trigger_reason: 'source_deleted' | 'source_revoked' | 'source_policy_changed' | 'source_integrity_failed'; required_plane_outcomes: { doc82_support_edges: 'invalidated' | 'verify_required'; doc87_memberships: 'restamped' | 'removed' | 'hidden'; doc84_delivery_artifacts: 'invalidated'; doc84_published_views: 'invalidated_or_restamped'; doc85_learning_signals: 'ineligible_for_future_utility'; doc86_surfaces: 'safe_labeled' | 'suppressed'; }; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } interface PlaneCascadeStatus { status: 'pending' | 'completed' | 'blocked' | 'degraded' | 'not_applicable'; started_at?: string; completed_at?: string; receipt_refs: string[]; retry_count: number; reason_codes: ReasonCodeId[]; } interface CascadingSourceInvalidationRun extends E0DurableRecord { run_id: string; schema_owner: 'DOC81'; invalidation_ref: string; idempotency_key: string; execution_order: | 'freeze_admission_then_support_membership_delivery_learning_ui' | 'architect_confirmed_other'; plane_statuses: { doc83_inflight_extraction?: PlaneCascadeStatus; 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; } ``` `doc83_inflight_extraction` is a pre-fanout freeze, not a sixth settled cascade plane. It prevents in-flight extraction candidates from using revoked material while the cascade is underway. ### 4.20.2 Last active support edge proof ```typescript interface LastActiveSupportEdgeEvaluation extends E0DurableRecord { evaluation_id: string; schema_owner: 'DOC81'; source_ref: SourceRef; affected_set_manifest_ref: string; evaluated_variant_refs: MemoryObjectRef[]; lawful_support_edges_remaining_count: number; remaining_support_edge_refs: string[]; last_active_support_edge_lost: boolean; polarity_recompute_trace_ref?: string; // required if net warrant rises after contrary-source removal evaluated_under_freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } ``` **Lints / fixtures:** ```text revocation.published_view_not_invalidated_after_revocation revocation.cascade_plane_without_receipt revocation.last_active_support_edge_boolean_without_denominator fixture.revocation.cascade_run_is_idempotent_per_plane fixture.quota.revocation_cascade_chunks_without_skipping_planes ``` --- ## 4.21 Legal hold ### 4.21.1 Selector semantics ```typescript 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: string; schema_owner: 'DOC81'; state: 'held' | 'released'; selector: LegalHoldSelector; placed_at: string; placed_by: PrincipalRef; release_clearance_ref?: string; released_at?: string; reason_codes: ReasonCodeId[]; } ``` ### 4.21.2 Destructive job registry ```typescript interface DestructiveJobLegalHoldGate extends E0DurableRecord { gate_id: string; 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; held_object_disposition: | 'skip' | 'require_manual_clearance' | 'redact_only_with_clearance' | 'block_job'; clearance_required_for: Array<'hard_destruction' | 'redaction' | 'materialization_clearing'>; clearance_ref_required: boolean; reason_codes: ReasonCodeId[]; } interface DestructiveJobLegalHoldRegistry extends E0DurableRecord { registry_id: 'doc81.destructive_job_legal_hold_registry'; schema_owner: 'DOC81'; jobs: DestructiveJobLegalHoldGate[]; default_for_unregistered_destructive_job: 'block'; reason_code_for_unregistered_job: ReasonCodeId; } ``` **Lints / fixtures:** ```text legal_hold.destructive_job_unregistered legal_hold.selector_semantics_ambiguous fixture.legal_hold.unregistered_destructive_job_fails_closed ``` --- ## 4.22 Relation traversal Remove `| string`. Unknown relation kinds must default-deny. ```typescript type RelationTraversalPolicyKind = | 'conflict_projection' | 'analogy' | 'supersession' | 'comparison_injection'; type RelationTraversalUnknownDisposition = | 'block' | 'safe_blocked_notice_only_if_existence_disclosable'; interface RelationTraversalScopeCheckPolicy extends E0DurableRecord { policy_id: string; schema_owner: 'DOC81'; relation_kind: RelationTraversalPolicyKind; related_variant_check_required: true; required_scope_check: 'resolve_and_policy_check_every_related_variant'; policy_actions_required: MemoryPolicyAction[]; required_effective_policy_ref_per_variant: true; cross_scope_disposition: 'may_not_render_summarize_or_name'; firewalled_disposition: 'may_not_render_summarize_or_name'; not_disclosable_disposition: 'safe_blocked_notice_only_if_existence_disclosable'; 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[]; reason_codes: ReasonCodeId[]; } interface RelationTraversalPolicyRegistry extends E0DurableRecord { registry_id: 'doc81.relation_traversal_policy_registry'; schema_owner: 'DOC81'; policies: RelationTraversalScopeCheckPolicy[]; unknown_relation_kind_disposition: RelationTraversalUnknownDisposition; unknown_relation_kind_reason_code: ReasonCodeId; } ``` Budget and trace: ```typescript interface RelationTraversalBudget extends E0DurableRecord { budget_id: string; schema_owner: 'DOC81'; relation_kind: RelationTraversalPolicyKind; max_depth: number; max_related_variants: number; max_scope_resolutions: number; 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[]; } interface RelationTraversalExecutionTrace extends E0DurableRecord { trace_id: string; 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[]; } ``` **Lints / fixtures:** ```text relation.unknown_kind_without_default_deny relation.traversal_without_budget relation.traversal_cycle_not_handled fixture.relation.unknown_relation_kind_fails_closed fixture.relation.cyclic_relation_graph_does_not_loop_or_leak fixture.relation.budget_exceeded_fails_closed ``` --- ## 4.23 Policy membrane disposition derivation The membrane decision should be derived from effective policy, not independently synthesized. ```typescript interface PolicyMembraneDispositionDerivation extends E0DurableRecord { derivation_id: string; 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[]; } ``` **Lints / fixtures:** ```text policy.membrane_disposition_not_derived_from_effective_policy fixture.policy.membrane_crossing_blocked_when_effective_locality_blocked ``` --- ## 4.24 Topic risk confirmation ```typescript type TopicRiskClassRef = Brand; type TopicRiskConfirmationRef = Brand; type ActorRef = PrincipalRef; interface TopicRiskConfirmation extends E0DurableRecord { confirmation_id: TopicRiskConfirmationRef; schema_owner: 'DOC81'; topic_ref: TopicRef; risk_class_ref: TopicRiskClassRef; confirmed_by: ActorRef; 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; risk_level: 'low' | 'medium' | 'high' | 'unknown'; auto_created_lens_only: boolean; collection_permitted: boolean; substantive_injection_permitted: boolean; requires_user_confirmation: boolean; confirmation_ref?: TopicRiskConfirmationRef; // required if collection/substantive injection permitted by user confirmation effective_policy_ref: EffectiveMemoryPolicyRef; reason_codes: ReasonCodeId[]; } ``` **Lints / fixtures:** ```text topic_risk.collection_permitted_without_confirmation fixture.topic_risk.auto_created_topic_lens_only_until_confirmed ``` --- ## 4.25 DAMS policy cap derivation `PolicyCappedDAMSInput` should be keyed to a context product request and its ceiling should be derived. ```typescript type DAMSEligibilityCeiling = | 'inline_allowed' | 'reference_only_max' | 'search_only_max' | 'notice_only_max' | 'blocked'; interface PolicyCappedDAMSInput extends E0DurableRecord { capped_input_id: string; schema_owner: 'DOC81'; object_ref: MemoryObjectRef; action: MemoryPolicyAction; context_product_request_id: string; context_product_kind: string; eligibility_ceiling: DAMSEligibilityCeiling; effective_policy_ref: EffectiveMemoryPolicyRef; scope_affinity?: ScopeAffinity; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } interface EligibilityCeilingDerivation extends E0DurableRecord { derivation_id: string; schema_owner: 'DOC81'; context_product_request_id: string; 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: ```text if effective_locality == blocked or effective_content_fidelity == none or effective_disclosure_class == not_disclosable: blocked else if effective_disclosure_class == existence_only: notice_only_max else if effective_content_fidelity == safe_label: notice_only_max else if effective_content_fidelity == reference_only: reference_only_max else if effective_content_fidelity == redacted: reference_only_max unless context product kind allows redacted inline else if effective_content_fidelity == full: inline_allowed unless another axis/obligation caps it ``` Override hook: ```typescript interface ProductKindCeilingOverride { context_product_kind: string; max_allowed_ceiling: DAMSEligibilityCeiling; reason_codes: ReasonCodeId[]; } ``` **Lints / fixtures:** ```text dams.ceiling_without_derivation dams.redacted_treated_as_full_without_product_override fixture.dams.ceiling_derives_from_effective_policy_axes ``` --- ## 4.26 Contamination risk measurement and threshold ```typescript interface ContaminationRiskMeasurement extends E0DurableRecord { measurement_id: string; schema_owner: 'DOC81'; object_ref: MemoryObjectRef; context_product_request_id: string; risk_model_ref: string; // DOC84-owned model; DOC81 references risk_model_generation_id: string; risk_score: number; // 0..1 confidence: number; // 0..1 contributing_feature_refs: string[]; measured_under_policy_ref: EffectiveMemoryPolicyRef; freshness_key: PolicyRuntimeFreshnessKey; reason_codes: ReasonCodeId[]; } interface ContaminationRiskThresholdRule extends E0DurableRecord { rule_id: string; schema_owner: 'DOC81'; domain_profile_ref: DomainProfileId; risk_model_ref: string; risk_model_generation_id: string; threshold_value: number; // 0..1 comparator: 'gte'; equality_rule: 'block_on_equal'; over_threshold_disposition: | 'reroute_warning_constraint' | 'reroute_blocked_scope_notice' | 'reroute_reference_only_notice' | 'suppress'; reason_codes: ReasonCodeId[]; } ``` Formula: ```text if risk_score is missing/NaN/outside [0,1] → suppress or fail_closed if risk_score >= threshold_value → over_threshold_disposition else → ordinary DAMS ranking within PolicyCappedDAMSInput ceiling ``` **Lints / fixtures:** ```text contamination.threshold_without_value_or_model threshold.risk_equal_threshold_blocks fixture.contamination.over_threshold_reroutes_not_linear_penalty ``` --- ## 4.27 Search coverage proof for not-found vs not-searched ```typescript interface ScopeSearchCoverageProof extends E0DurableRecord { proof_id: string; schema_owner: 'DOC81'; request_ref: string; scope_resolution_ref: ScopeResolutionResultRef; required_scope_refs: ScopeRef[]; searched_scope_refs: ScopeRef[]; not_searched_scope_refs: ScopeRef[]; coverage_ratio: number; // searched / required coverage_denominator: number; coverage_numerator: number; result_disposition: | 'fully_searched' | 'partially_searched' | 'not_searched' | 'search_unavailable_fail_closed'; may_emit_not_found: boolean; // true iff fully_searched reason_codes: ReasonCodeId[]; } ``` Formula: ```text coverage_denominator = count(required_scope_refs) coverage_numerator = count(intersection(required_scope_refs, searched_scope_refs)) coverage_ratio = coverage_numerator / max(coverage_denominator, 1) may_emit_not_found = coverage_denominator > 0 and coverage_numerator == coverage_denominator and not_searched_scope_refs is empty ``` **Lints / fixtures:** ```text disclosure.not_found_without_scope_search_coverage_proof fixture.disclosure.partial_search_cannot_emit_not_found ``` --- ## 4.28 Cache keys DOC81 should specialize E0 reproducibility into scope/policy/UI cache keys. ```typescript interface ScopeResolutionCacheKey { request_input_hash: ContentHash; request_scope_ref?: ScopeRef; object_ref: MemoryObjectRef; object_scope_ref?: ScopeRef; source_scope_ref?: ScopeRef; destination_scope_ref?: ScopeRef; principal_ref?: PrincipalRef; 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; 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; reason_code_registry_version: SchemaVersionRef; domain_profile_registry_version: SchemaVersionRef; safe_label_vocabulary_version?: SchemaVersionRef; } ``` Rule: ```text A cached ScopeResolutionResult / EffectiveMemoryPolicy / PolicyUIExport is reusable only if its cache key exactly matches the current key. No partial reuse across policy_generation_id, effective_state_generation_id, compiled_policy_evaluator_hash, safe-label vocabulary, source/classification generation, or population generation. ``` **Lints / fixtures:** ```text cache.policy_reused_across_effective_state_generation cache.safe_label_vocab_change_not_in_ui_export_key cache.scope_resolution_reused_after_population_generation_change fixture.cache.scope_resolution_invalidates_on_population_generation_change fixture.cache.policy_export_invalidates_on_safe_label_vocab_change ``` --- ## 4.29 Batch/fan-out quota envelopes DOC81 fan-out operations include policy generation restamp, source revocation cascade, collection exclude backfill, relation traversal, and legal-hold scans. ```typescript interface DOC81BatchOperationQuotaEnvelope extends E0DurableRecord { quota_envelope_id: string; 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'; idempotency_key: string; progress_ref?: string; reason_codes: ReasonCodeId[]; } ``` **Lints / fixtures:** ```text quota.policy_restamp_batch_unbounded quota.collection_exclude_backfill_unbounded quota.relation_traversal_unbounded quota.revocation_cascade_unbounded fixture.quota.revocation_cascade_chunks_without_skipping_planes ``` --- ## 4.30 Lifecycle state pattern Several DOC81 objects describe lifecycle in prose but do not carry state. Add either this common field pattern or per-object discriminated unions. ```typescript type DOC81LifecycleState = | 'candidate' | 'active' | 'confirmed' | 'superseded' | 'invalidated' | 'expired' | 'blocked' | 'archived'; interface DOC81LifecycleFields { lifecycle_state: DOC81LifecycleState; activated_at?: string; superseded_at?: string; invalidated_at?: string; invalidated_by_ref?: string; replacement_ref?: string; } ``` Objects needing lifecycle fields or discriminated unions: ```text ScopeEquivalenceBinding ScopeContainerRelation PolicyStamp PolicyStampRestamp PolicyDisambiguationRequest CascadingSourceInvalidation CollectionModeSuppressionGovernance SourceExclusionFilterRule TopicRiskClass LegalHoldState ``` Example disambiguation state machine: ```typescript type PolicyDisambiguationLifecycleRecord = | PolicyDisambiguationRaised | PolicyDisambiguationAnswered | PolicyDisambiguationTimedOut | PolicyDisambiguationFallbackApplied; interface PolicyDisambiguationRaised extends E0DurableRecord { state: 'raised'; request_id: PolicyDisambiguationRequestRef; deadline_at: string; blocked_until_answered: boolean; fallback_if_unanswered: 'block' | 'reference_only' | 'search_only' | 'defer'; } interface PolicyDisambiguationAnswered extends E0DurableRecord { state: 'answered'; request_id: PolicyDisambiguationRequestRef; answer_ref: PolicyDisambiguationAnswerRef; resumed_pipeline_ref: string; } interface PolicyDisambiguationTimedOut extends E0DurableRecord { state: 'timed_out'; request_id: PolicyDisambiguationRequestRef; timed_out_at: string; fallback_if_unanswered: 'block' | 'reference_only' | 'search_only'; // no defer here } interface PolicyDisambiguationFallbackApplied extends E0DurableRecord { state: 'fallback_applied'; request_id: PolicyDisambiguationRequestRef; fallback_applied: 'block' | 'reference_only' | 'search_only'; resulting_effective_policy_ref?: EffectiveMemoryPolicyRef; } ``` **Lint family:** ```text state.proposed_scope_equivalence_consumed state.invalidated_policy_stamp_used state.disambiguation_without_terminal_state state.source_exclusion_rule_used_after_invalidated_generation state.topic_risk_class_permitted_without_confirmed_state ``` --- # 5. Quantitative / spec-function / technical-function audit ## 5.1 Overall function-audit grade **Grade:** `C+` The architecture is strong qualitatively, but many quantitative and functional contracts are not yet executable. The draft needs formulas, denominators, rank maps, cache keys, state transition invariants, and edge-case behavior for thresholds and aggregation. ## 5.2 Function audit table | Function / mechanism | Current state | Edge case | Required patch | |---|---|---|---| | Per-axis policy meet | Has pseudo-code and conceptual rank order. | Missing axis in one applicable decision is filtered out. | Explicit rank maps; malformed applicable decision floors/blocks. | | Destination-specific egress gate | Destinationless decisions can join meet. | Destinationless export policy authorizes cloud/email export. | Egress precondition; require destination-specific decision. | | Policy freshness | Mostly `policy_generation_id`. | EC application/incognito toggle changes but cached policy reused. | `PolicyRuntimeFreshnessKey`. | | Decision applicability | Object/action/destination only. | Local interactive decision reused for background/cloud render. | `PolicyEvaluationContext`. | | Stamp validity | One policy ref, multi-action scope. | Retrieval stamp authorizes render/export. | `PolicyStampScopeItem`. | | Restamp ceiling | Opaque `original_ceiling_ref`. | Restamp chain rebases to prior lowered/widened restamp. | `PolicyCeilingSnapshot`, root chain integrity. | | Scope equivalence confidence | One scalar per cluster. | A≈B high, B≈C high, A≈C low collapses all. | Pairwise matrix; cluster confidence = min pairwise. | | Scope resolution confidence | One scalar. | High identity masks low destination/classification confidence. | Minimum-of-required-components breakdown. | | Thresholds | Pointers/enums. | Risk exactly equals threshold; NaN/missing differs by implementation. | `ThresholdEvaluation`. | | Conservatism floor | Named enum only. | DOC84/DOC86 interpret `safe_label_candidate` differently. | `ConservatismFloorEffect`. | | Disclosure meet | Scalar class. | Count/source/title disclosure permissions mis-aggregated. | `DisclosurePermissionVector`. | | DAMS cap | Enum only. | Redacted object treated as full inline. | `EligibilityCeilingDerivation`. | | Contamination threshold | Threshold ref only. | No risk model/value/comparator. | `ContaminationRiskMeasurement` + threshold rule. | | Collection suppression aggregation | “Most restrictive wins” prose. | Ambiguous privacy topic gets collected. | Rank table + `CollectionSuppressionEvaluation`. | | Source revocation fan-out | Five-plane outcome object. | One plane silently fails. | `CascadingSourceInvalidationRun`. | | Last active support edge | Boolean. | Boolean set true despite remaining support edge. | Denominator proof object. | | Relation traversal | Required check but no budget/cycle semantics. | Cyclic graph or 10k related variants. | Budget + trace. | | Cache hashes | E0 has general reproducibility key; DOC81 lacks specialized keys. | Safe-label vocab changes but UI export reused. | Scope/policy/UI cache keys. | | State transitions | Lifecycle prose. | Proposed equivalence consumed as confirmed. | State fields / discriminated unions. | | Cost/quota | E0 has quota lints; DOC81 operations unbounded. | Restamp/cascade storm. | Batch quota envelopes. | | Not-found vs not-searched | Correct prose. | Partial scope search emits “not found.” | `ScopeSearchCoverageProof`. | --- # 6. Updated schema grades | Contract / schema | Final grade | Key reason | |---|---:|---| | `ScopeIdentityRoot` | B | Needs `unknown_synthetic`, branded refs, visibility vocab ref. | | `ScopeEquivalenceBinding` | B− | Scalar confidence inadequate for clusters; needs pairwise matrix. | | `ScopeContainerRelation` | B− | Topology correct; names and `policy_load_bearing` too coarse. | | `ScopeBoundary` | A− | Clean topology-only object. | | `ScopeAffinity` | B+ | Union acceptable; `shared` needs definition/confirmation. | | `ScopeResolutionResult` | B− | Good role; needs confidence breakdown, population gen, sensitivity/protection provenance. | | `ScopeResolutionTrace` | A− | Strong inspectability concept. | | `ScopePopulationHealth` | B+ | Good, but must link into resolution cache invalidation. | | `MemoryPolicyDecision` | B− | Good 5-axis model; missing policy evaluation context and exposure context. | | `EffectiveMemoryPolicy` | B− | Meet backbone right; context, egress, malformed-axis, floor, disclosure-vector gaps. | | `PolicyMembraneDecision` | C+ | Separate from dimensional policy, but needs derivation object. | | `PolicyObligation` | C | Missing PropA kinds, typed parameters, conflict/discharge model, DOC84/DOC86 owners. | | `PolicyStamp` / `PolicyStampScope` | C− | Single policy ref conflicts with multi-action/multi-destination scope. | | `PolicyStampInvalidation` | B− | Needs per-action/destination axis deltas. | | `PolicyStampRestamp` | C | Needs discriminated union, root ceiling, blocked-no-policy semantics. | | `PolicyDisambiguationRequest` | B− | Safe prompt design good; `defer` constraint and answer/effect missing. | | `EpisodePolicyEpoch` | A− | Strong policy-generation boundary, but should include runtime freshness. | | `SafeLabelDisclosurePolicy` | B− | Good direction; needs disclosure vector/count policy/default label conditionality. | | `ExtractionRoutePolicyEnvelope` | B− | Right object; booleans/provenance too weak. | | `PolicyCappedDAMSInput` | B− | Correct cap idea; needs product request and derivation formula. | | `ContaminationRiskThresholdRule` | C | Veto concept right; lacks measurement/threshold/comparator. | | `TopicRiskClass` | B− | Needs confirmation proof state. | | `CascadingSourceInvalidation` | B− | Five-plane target right; lacks run state/idempotency/proofs. | | `LegalHoldState` | B− | Correct invariant; selector and destructive-job registry missing. | | `CollectionModeSuppressionGovernance` | B− | Placement right; ambiguity and backfill missing. | | `SourceExclusionFilterRule` | C+ | Good row landing; input/output cycle and closure missing. | | `RelationTraversalScopeCheckPolicy` | C+ | Rule right; `| string`, budget, cycle handling missing. | | `PolicyUIExport` | C+ | Direction right; payload thin and not per-triple. | --- # 7. Responses to the commission’s specific questions ## 7.1 Does the `EffectiveMemoryPolicy` meet work? **Conceptually yes; mechanically not yet.** The per-axis most-restrictive meet is the right backbone. It must not collapse to a scalar. It must not meet across actions. It must accumulate obligations. It must treat missing/unknown/incomparable values conservatively. But the current meet is under-keyed and has a pseudo-code bug: it filters out missing axis values instead of flooring them. It also does not require destination-specific policy decisions for egress and does not incorporate disclosure-vector aggregation. Patch with `PolicyEvaluationContext`, rank maps, `DisclosurePermissionVector`, destination-specific preconditions, and `meet_v2`. ## 7.2 Are restamp and monotonicity sufficient? **No.** The monotonicity invariant is correct, but enforcement is not executable. `original_ceiling_ref` is opaque; restamp chains lack `root_stamp_ref`; `PolicyStampRestamp` is not a discriminated union; and block restamps still require a new effective policy ref. Patch with `PolicyCeilingSnapshot`, `RestampChainIntegrity`, axis comparisons, and a restamp union. ## 7.3 Does the five-plane source revocation cascade cover all necessary fan-out? **Mostly yes, but not proof-complete.** The five settled planes are correct: support edges, memberships, delivery artifacts, learning signals, and UI/surfaces. The draft should add per-plane run state, idempotency key, receipts, retry/degraded status, published-view ownership, and a DOC83 in-flight extraction freeze before fan-out. ## 7.4 Is `LegalHoldState` sufficient? **No.** The flag and invariant are correct, but future destructive jobs can bypass it unless there is a `DestructiveJobLegalHoldRegistry` with default block for unregistered destructive jobs. Selector semantics also need a discriminated union. ## 7.5 Does collection-mode suppression block dummy/privacy topics? **Directionally yes, but not fully.** The design composes with EC toggles/incognito, which is right. The missing pieces are an executable rank table, ambiguity behavior, and `exclude` backfill for existing material. Ambiguous topic matches involving any suppress/exclude candidate must fail closed or route to review. ## 7.6 Are PropA obligations folded cleanly? **Partially.** §7.3 appears directionally corrected after the CODEX fix. But the lineage/landing table must be corrected, `ExposureContextSchema` must be structurally carried on policy decisions, `LearningScopeLevel` must add `same_firewall_only`, and `PolicyObligationKind` must include PropA V15 obligation kinds. ## 7.7 Is relation traversal dense enough? **No.** The rule is right: every related variant must be scope-resolved and policy-checked. The schema is not dense enough because `relation_kind | string` defeats exhaustiveness. Add a registry, default-deny unknowns, traversal budget, visited-set/cycle handling, and per-variant policy refs. ## 7.8 Is DOC81→DOC86 export sufficient? **No.** The direction is correct, but the payload is too thin. DOC86 needs per-action/per-destination eligibility, effective policies, restamp disposition, disambiguation refs, safe-label constraints, disclosure vector, count policy, and freshness key. DOC86 must render from this export and not recompute policy. ## 7.9 Is the scope model sufficient? **Broadly yes, but not executable enough.** Boundary vs affinity is good. Topology vs membership separation is good. Needed fixes: synthetic unknown scope, pairwise equivalence confidence, component confidence aggregation, population generation pinning, sensitivity/protection provenance, and better load-bearing effect definitions. ## 7.10 Should DOC81 be ratified? **Not yet.** The architecture should be approved directionally, but the draft needs one schema-level patch round before ratification. --- # 8. A/A+ gap ## To reach A DOC81 needs these executable foundations: ```text PolicyRuntimeFreshnessKey PolicyEvaluationContext meet_v2 ConservatismFloorEffect PolicyCeilingSnapshot RestampChainIntegrity DisclosurePermissionVector ThresholdEvaluation ActionPermissionPredicate PolicyStampScopeItem PolicyObligationConflict PolicyObligationDischarge CascadingSourceInvalidationRun ScopeResolutionConfidenceBreakdown ScopeEquivalencePairEvidence ScopeSearchCoverageProof RelationTraversalBudget DOC81BatchOperationQuotaEnvelope ``` ## To reach A+ It needs property-style fixtures and edge-case test vectors: ```text fixture.policy.stamp_scope_item_matches_effective_policy_triple fixture.policy.retrieval_stamp_cannot_authorize_render_or_export fixture.policy.effective_state_generation_change_invalidates_cached_stamp fixture.policy.exposure_context_changes_policy_decision_key fixture.policy.destinationless_decision_cannot_authorize_egress fixture.policy.malformed_axis_in_applicable_decision_floors_axis fixture.policy.floor_mapping_tightens_each_axis fixture.policy.restamp_of_restamp_compares_against_root_ceiling fixture.policy.blocked_restamp_has_no_authorizing_effective_policy fixture.policy.conflicting_obligations_fail_closed_for_action fixture.disclosure.vector_meet_ands_boolean_permissions fixture.disclosure.count_bucketed_unless_full fixture.scope.three_scope_cluster_low_pair_prevents_collapse fixture.scope.low_destination_confidence_forces_conservative_floor fixture.scope.population_generation_bump_invalidates_scope_resolution_result fixture.threshold.risk_equal_threshold_blocks fixture.threshold.confidence_equal_threshold_passes fixture.collection.ambiguous_privacy_topic_fails_closed fixture.collection.exclude_filters_existing_material fixture.source_exclusion.derived_artifact_exclusion_closure fixture.revocation.cascade_run_is_idempotent_per_plane fixture.cache.safe_label_vocab_change_invalidates_policy_ui_export fixture.relation.cyclic_relation_graph_does_not_loop_or_leak fixture.relation.budget_exceeded_fails_closed fixture.quota.revocation_cascade_chunks_without_skipping_planes fixture.disambiguation.answer_forces_new_meet_not_direct_allow fixture.ui.policy_export_renders_without_recomputing_meet ``` --- # 9. Suggested amendment structure for DOC81 I would not scatter these fixes across prose paragraphs. I would add a consolidated patch section and then thread references back through the existing sections. ## 9.1 Add a new §3.0 “Policy evaluation context and runtime freshness” Include: - `PolicyRuntimeFreshnessKey` - `PolicyEvaluationContext` - cache/freshness invalidation rule - active EC effective-state key requirement ## 9.2 Replace §3.2 meet pseudo-code with `meet_v2` Include: - explicit axis rank maps - malformed-axis behavior - context compatibility - destination-specific egress precondition - disclosure-vector meet - floor application - obligation conflict fail-closed ## 9.3 Replace §3.4 stamp/restamp model Include: - `PolicyStampScopeItem` - revised `PolicyStamp` - `PolicyCeilingSnapshot` - restamp discriminated union - root-chain integrity - per-action/destination invalidation deltas ## 9.4 Expand §3.3 obligations Include: - expanded PropA obligation kinds - typed obligation union - conflict model - discharge proof - DOC84/DOC86 as owners ## 9.5 Add §4.1A disclosure vector Include: - `DisclosurePermissionVector` - `CountDisclosurePolicy` - scalar derivation - safe-label default-label conditionality ## 9.6 Add §4.6A thresholds and confidence Include: - `ThresholdEvaluation` - scope equivalence pairwise formula - scope resolution confidence breakdown - risk equal-threshold block rule ## 9.7 Add §4.7 action predicates Include: - action closure table - permission predicate table - prerequisite-policy checks ## 9.8 Expand §5 source revocation Include: - `CascadingSourceInvalidationRun` - plane receipts - last-active-support-edge proof - DOC83 in-flight extraction freeze ## 9.9 Expand §6 legal hold and state transitions Include: - destructive job registry - legal hold selector - lifecycle state conventions ## 9.10 Replace §7.1/§7.2 source/suppression patches Include: - collection suppression rank and evaluation - existing-material backfill - source-exclusion input-cycle fix - derived-artifact closure ## 9.11 Replace §8 relation traversal Include: - closed registry - unknown default-deny - budget/cycle handling - trace ## 9.12 Replace §9 policy UI export Include: - per-triple export payload - safe-label constraints - disambiguation refs - freshness key - no recomputation rule ## 9.13 Correct §14.2 OPA landings Use the replacement table in §4.12 of this document. --- # 10. Final recommendation The draft should receive a targeted schema-level patch round before ratification. The architectural split is sound and should be preserved. The revision should focus on making every runtime-critical invariant mechanically checkable. The highest-risk runtime bug remains the `PolicyStamp` triple-binding issue: a stamp can appear valid for an action/destination not actually covered by its single `EffectiveMemoryPolicyRef`. The highest-risk systemic gap is freshness: without `effective_state_generation_id` and compiled evaluator hash, cached policy artifacts can survive runtime-state changes that should invalidate them. A good acceptance criterion for the next draft is: ```text A Stage 7 implementer can implement DOC81 policy/scope without inventing: - a policy decision key, - a meet algorithm edge case, - a disclosure aggregation formula, - a restamp ceiling comparison, - an egress destination rule, - an obligation discharge model, - a source revocation run state, - a collection suppression ambiguity rule, - a relation traversal budget, - or a cache freshness key. ``` Until that is true, DOC81 should remain `DESIGN_REVISION_NEEDED_BEFORE_RATIFICATION`.