DOC81_consolidated_red_team_final_review_AUDITED.md
Memory Rebuild Docs/Stage_6_Charters/E1_E2_DOC81_Scope_Policy/Reviews/DOC81_consolidated_red_team_final_review_AUDITED.md
# 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.
**Self-audit correction applied:** this audited copy corrects the E0 egress vocabulary used in the consolidated patch pack. The E0 outbound destination enum is the PropA/E0 eight-value set (`same_machine_local_runtime`, `local_file_export`, `local_network_peer`, `firm_server`, `remote_peer`, `cloud_api`, `email_outbound`, `agent_messaging`). `unknown_destination` remains a DOC81 relation/fail-closed state, not an E0 outbound destination class. `background_non_interactive` is the E0 interaction-mode value, not `background`. No substantive finding was removed.
---
# 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<T,B>` 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, B extends string> = T & { readonly __brand: B };
type ContentHash = Brand<string, 'ContentHash'>;
type SchemaVersionRef = Brand<string, 'SchemaVersionRef'>;
type OwnerDocId = Brand<string, 'OwnerDocId'>;
type MemoryObjectRef = Brand<string, 'MemoryObjectRef'>;
type ScopeRef = Brand<string, 'ScopeRef'>;
type ScopeBoundaryRef = Brand<string, 'ScopeBoundaryRef'>;
type ScopeResolutionResultRef = Brand<string, 'ScopeResolutionResultRef'>;
type ScopeContainerRelationRef = Brand<string, 'ScopeContainerRelationRef'>;
type ScopeEquivalenceBindingRef = Brand<string, 'ScopeEquivalenceBindingRef'>;
type MemoryPolicyDecisionRef = Brand<string, 'MemoryPolicyDecisionRef'>;
type EffectiveMemoryPolicyRef = Brand<string, 'EffectiveMemoryPolicyRef'>;
type PolicyEvaluationContextRef = Brand<string, 'PolicyEvaluationContextRef'>;
type PolicyStampRef = Brand<string, 'PolicyStampRef'>;
type PolicyStampScopeRef = Brand<string, 'PolicyStampScopeRef'>;
type PolicyCeilingRef = Brand<string, 'PolicyCeilingRef'>;
type PolicyObligationRef = Brand<string, 'PolicyObligationRef'>;
type PolicyObligationConflictRef = Brand<string, 'PolicyObligationConflictRef'>;
type PolicyObligationDischargeRef = Brand<string, 'PolicyObligationDischargeRef'>;
type SafeReferenceLabelRef = Brand<string, 'SafeReferenceLabelRef'>;
type PolicyDisambiguationRequestRef = Brand<string, 'PolicyDisambiguationRequestRef'>;
type PolicyDisambiguationAnswerRef = Brand<string, 'PolicyDisambiguationAnswerRef'>;
type ThresholdEvaluationRef = Brand<string, 'ThresholdEvaluationRef'>;
type EffectiveStateGenerationId = Brand<string, 'EffectiveStateGenerationId'>;
type DomainProfileId = Brand<string, 'DomainProfileId'>;
type PolicyGenerationId = Brand<string, 'PolicyGenerationId'>;
type ReasonCodeId = Brand<string, 'ReasonCodeId'>;
type PrincipalRef = Brand<string, 'PrincipalRef'>;
type SurfaceRef = Brand<string, 'SurfaceRef'>;
type SourceRef = Brand<string, 'SourceRef'>;
type TopicRef = Brand<string, 'TopicRef'>;
type MemoryFlowCertificateId = Brand<string, 'MemoryFlowCertificateId'>;
type E0InteractionMode = 'interactive' | 'background_non_interactive' | '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'
| 'local_network_peer'
| 'firm_server'
| 'remote_peer'
| 'cloud_api'
| 'email_outbound'
| 'agent_messaging';
// Unknown/unresolved destinations are NOT members of E0OutboundDestinationClass.
// They fail closed before attestation and are represented only in DOC81's
// relation_to_destination / DestinationPolicyCrosswalk layer.
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<CountDisclosureMode, number> = { none: 0, bucketed: 1, exact: 2 };
const SUMMARY_FIDELITY_RANK: Record<SummaryFidelity, number> = {
none: 0,
generic_reason_only: 1,
redacted_reason: 2,
full_reason: 3,
};
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<string, 'ConservatismFloorEffectRef'>;
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;
allowed_outbound_destination_classes?: E0OutboundDestinationClass[];
policy_floor: ScopeConservatismFloor;
egress_attestation_required: boolean;
policy_lookup_required: boolean;
disposition:
| 'no_egress_same_runtime_only'
| 'same_machine_runtime_only'
| 'policy_bound_egress'
| 'block_until_terminal_destination_resolved'
| 'block';
reason_codes: ReasonCodeId[];
}
```
Seed rules:
```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',
allowed_outbound_destination_classes: [
'local_network_peer', 'firm_server', 'remote_peer',
'cloud_api', 'email_outbound', 'agent_messaging'
],
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<Record<
'content_fidelity' | 'locality' | 'learning_scope' | 'mutation_authority' | 'disclosure_class',
string[]
>>;
requires_destination: boolean;
requires_egress_attestation: boolean;
requires_mfc: boolean;
requires_obligation_discharge: boolean;
reason_codes: ReasonCodeId[];
}
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<CollectionMode, number> = {
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<string, 'ExtractionRouteContextRef'>;
type CollectionGateResultRef = Brand<string, 'CollectionGateResultRef'>;
type SourceExclusionFilterRuleRef = Brand<string, 'SourceExclusionFilterRuleRef'>;
type CollectionModeSuppressionGovernanceRef = Brand<string, 'CollectionModeSuppressionGovernanceRef'>;
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<string, 'TopicRiskClassRef'>;
type TopicRiskConfirmationRef = Brand<string, 'TopicRiskConfirmationRef'>;
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`.