Elnor Repo Reader

MultiDoc_PropA_R6_3_Compiled_Operative_Spec.md

Current Specs/Miscellaneous Specs/MultiDoc_PropA_R6_3_Compiled_Operative_Spec.md

Short text page 72cab33b636b. Generated 2026-06-09T01:23:58.539Z from commit dbaa25962edc11ab30e8d4ca1715f9ae5bf77331. Worktree: clean.

Open readable HTML page · Open raw txt · Open path URL

ELNOR REPO READER TEXT MIRROR
Original path: Current Specs/Miscellaneous Specs/MultiDoc_PropA_R6_3_Compiled_Operative_Spec.md
Source repo: /Users/OpenClaw1/Elnor/Elnor Specs
Git branch: main
Git commit: dbaa25962edc11ab30e8d4ca1715f9ae5bf77331
Generated: 2026-06-09T01:23:58.539Z

---



**R6.3 changes from R6.2:** Additive corrective pass — fixed remaining purpose-statement placement issues, added missing settings wireframes, aligned the extraction-visibility wireframe to current default policy, and added explicit integration-target precision notes. No schema, code, route, SQL, or normative-rule changes.
# MultiDoc PropA — Knowledge Pipeline, Sensitivity Classification, and Self-Improvement Architecture R6.3

**Date:** 2026-04-16  
**Status:** Compiled operative readability-remediated corrective revision. Carries forward the full R6.2 operative contract set and adds the remaining missing explanatory/wireframe/integration-precision fixes identified by the post-remediation audit, without altering schemas, routes, SQL, functions, or normative rules.  
**Supersedes:** MultiDoc PropA R6.2 in full.  
**Interpretation rule:** This document is the single operative PropA companion spec for the current pair review cycle. It is not a delta overlay. Anything from R5 that is not restated here is superseded.  
**Target docs:** DOC72, DOC24, DOC24 Addendum A, DOC8, DOC20, DOC23, DOC1, DOC11, EC Core Addendum A, DOC21, DOC22  
**Scope:** Source-granular collection control, pre-extraction visibility gating, sensitivity classification, sharing and injection policy semantics, misclassification recovery, self-improvement / self-review / DSPy, routes and commands owned by PropA, storage/retention obligations, and companion obligations into EC Core.

---

## 0. Governing posture and owner split

**Integrates into:** DOC72 owner-posture notes, DOC24 owner-posture notes, EC Core Addendum A owner split, DOC23 task-runtime posture, DOC1 governance boundary notes.

**R6.2 changes from R6.1:** No contract changes. This section now restores the missing explanatory layer so a coding agent can see the owner split, precedence, and supersession intent without reverse-engineering it from code alone.

This section defines the guardrails that keep PropA from becoming a second runtime brain, a second writer, or a shadow delivery system. It explains which pieces of the knowledge/privacy/self-improvement stack PropA owns, which pieces are only consumed from other owner docs, and in what order those boundaries apply when knowledge is collected, classified, and delivered.


### 0.1 Non-negotiables

1. **EC is the sole durable writer.** All durable artifacts produced by this companion spec SHALL be written through EC commands only.
2. **Q is the control/read surface, not a second truth store.** Every meaningful control SHALL map to `user action -> EC command -> durable write or explicit blocked/degraded no-op -> telemetry -> refreshed read model`.
3. **OpenClaw owns native runtime truth.** This document SHALL NOT rewrite OpenClaw native session history or runtime artifacts.
4. **DOC72 owns knowledge shape.** Node payloads, node metadata, canonical node kinds, confidence mechanics, and graph write contracts remain DOC72-owned.
5. **DOC24 owns delivery rendering and packet assembly.** This document defines policy semantics that DOC24 / DOC11 consume; it does not create a parallel prompt assembler.
6. **DOC24 Addendum A remains derived learning infrastructure.** Matrix utility, attribution, patterns, and compiled bundles remain derived and MUST NOT become a second canonical confidence system.
7. **EC Core owns the control plane.** Routing, orchestrator state, task registry, policy evaluation execution, effective runtime truth, route registry, and settings wiring are EC-owned.
8. **No hot-path LLM expansion.** New model work introduced here is limited to extraction, expedited local-only classification, nightly/backfill classification, self-review, and DSPy. No new general live-turn LLM gate is introduced.
9. **Truthful degraded states are mandatory.** When no local model is available, classifiers fail, review quorums degrade, or sync verification cannot complete, the system SHALL emit blocked/deferred/degraded receipts instead of pretending work completed.
10. **Complete-product rule.** A coding agent SHALL build the complete accepted product described here; nothing in this document is “accepted later.”

### 0.2 Owner split

- **PropA owns:** collection categories, source match semantics, visibility classes, sensitivity tags/findings/classification lifecycle, sharing-policy semantics, exposure-context semantics, misclassification recovery semantics, self-review graphs and artifacts, DSPy semantic contracts, storage classes for PropA-owned artifacts, and PropA-owned command payload schemas.
- **EC Core owns:** effective collection/runtime state computation, the `PolicyDecisionEngine`, route-contract registry, command/read-model/telemetry closure, execution-profile selection, background task registry, token/cost governance, sync identity verification, queue truth, and settings manifest generation.
- **DOC23 owns:** the generic task graph runtime. PropA supplies task templates, module bindings, prompt contracts, output schemas, and PropA-specific validation rules.
- **DOC8 owns:** friction, reward, attribution, and learning signals that feed the self-improvement loop, but PropA defines which additional signals MUST exist for knowledge-pipeline diagnosis.
- **DOC1 owns:** write-gate, archive-not-delete, taint-aware promotion, and memory governance constraints.

### 0.3 Cross-doc precedence

The operative control order is:

1. **EC Core global memory hierarchy, kill switch, collection/application split, and incognito**
2. **EC Core per-surface collection controls**
3. **PropA source rules + source-derived tags + collection policy**
4. **PropA visibility decision + deterministic fast-path + expedited local-only classification**
5. **DOC1 write gate and DOC72 graph write**
6. **PropA classification state / nightly-backfill classifiers / review queue**
7. **DOC24 Addendum A source-policy adapter**
8. **EC Core `PolicyDecisionEngine`**
9. **DOC24 / DOC11 dispatch and runtime delivery**

### 0.4 Canonical collapse and supersession rule

This revision resolves the duplicate-contract drift identified in the R5/V2 cycle.

**Normative rule:** the following R5 constructs are superseded and SHALL NOT be implemented:
- `SelfReviewPromptIdSchemaV2` → superseded by `CanonicalPromptIdSchemaV3`
- `CanonicalPromptIdSchemaV2` → superseded by `CanonicalPromptIdSchemaV3`
- `DspyRubricSchemaV2` (all prior variants) → superseded by `DspyRubricSchemaV4`
- any PropA-owned `ApiRouteContractRegistryEntrySchema` → superseded by EC Core ownership
- any PropA-local policy evaluator function that bypasses EC compiled policy execution

```ts
export const NonOperativeContractMarkerSchema = z.object({
  legacy_name: z.string().max(160),
  superseded_by: z.string().max(160),
  implementation_status: z.literal("MUST_NOT_IMPLEMENT"),
  reason: z.string().max(500),
  schema_version: z.literal(1),
});
```

### 0.5 End-to-end pipeline after R6.3

```text
surface event
  -> EC global/surface controls
  -> significance assessment
  -> source rules + deterministic boundary scan
  -> effective collection mode resolution
  -> visibility decision (blocked / local_only / cloud_warn / cloud_allowed)
  -> optional deterministic redaction helper for narrow allowed markers only
  -> extraction or blocked/deferred receipt
  -> DOC1 write gate + DOC72 write
  -> node written with source tags/findings + classification_state
  -> expedited local-only classification when required
  -> nightly/backfill content classification
  -> EC PolicyDecisionEngine at every outbound / injection / sync boundary
  -> DOC24 / DOC11 dispatch with receipts, manifests, and reason traces
  -> DOC8/Addendum A feedback ingestion
  -> misclassification recovery + self-review + optional DSPy branch
```


### 0.6 Integration-target precision notes

The section-level integration annotations in this document are intended to remove guesswork about owner-doc landing zones, but a few of those landing zones are still only known at the topic level rather than by exact numbered parent-doc subsection. That is an owner-doc adoption problem, not a PropA contract gap. Until the later owner-doc compilation pass happens, the following topic-level targets are authoritative and SHALL be used instead of inventing more specific section numbers.

**Exact-number targets still pending later owner-doc adoption:**
- **DOC20** settings/read surfaces for Privacy & Sharing, Knowledge Review, Self-Review, and DSPy operators/inspectors
- **DOC21 / DOC22** exact component ids and page ids for the new privacy/review/optimization surfaces
- **DOC72** exact numbered insertion points for some governance/self-review metadata seams that clearly belong under the knowledge-intake / node-metadata / review-repair topics but are not yet pinned by subsection number
- **DOC23** exact numbered insertion points for self-review, validation/canary, and `step.dspy_optimizer` module registration rows

When an exact numbered landing section is not available, coding agents SHALL use the topic-level landing zone already named in each section and SHALL NOT invent a narrower section number.

---

### Schema consume-path map

| Schema | Consumed by | When | Consume path |
|---|---|---|---|
| `NonOperativeContractMarkerSchema` | Consumed by coding agents, companion-doc integrators, and EC/Core route-policy implementers | drafting, implementation planning, CI validation, and later owner-doc adoption | this section plus EC Core owner split, DOC72 ownership notes, and DOC24 delivery consumption seams |

## 1. Sensitivity, collection, and visibility contracts

**Integrates into:** DOC72 §20A (knowledge intake / extraction pipeline), DOC20 (Settings > Privacy & Sharing > Content Collection / Classification Rules / Extraction Visibility), EC Core compiled policy inputs, DOC1 write-gate crossings.

**R6.2 changes from R6.1:** No contract changes. This section now restores plain-language explanation of collection modes, source rules, visibility classes, and classification states, plus the user-facing settings layouts those contracts imply.

This section defines the front half of the privacy-aware knowledge pipeline: what may be collected, how sources are matched to tags and findings, when extraction is blocked or forced local-only, and how provisional classification state is created before durable graph write. It exists to make sensitive-content handling real before model exposure rather than pretending privacy can be repaired later at delete/share time.


### 1.1 Canonical collection modes

```ts
import { z } from "zod";

export const CollectionModeSchema = z.enum([
  "do_not_collect",
  "collect_and_tag",
  "collect_without_tagging",
]);
```

### 1.2 Canonical sensitivity tags

```ts
export const SensitivityTagSchema = z.enum([
  // Privilege
  "attorney_client_privileged",
  "work_product",
  "privilege_uncertain",
  "firm_gc_privileged",

  // Confidentiality
  "settlement_confidential",
  "court_sealed",

  // Personal
  "personal_private",
  "financial_personal",
  "health_personal",
  "contains_credentials",

  // Third-party / PII
  "contains_pii_third_party",

  // Organizational / business
  "work_related",
  "firm_internal",
  "client_confidential",

  // Content state
  "draft_only",

  // Source-derived
  "browser_history",
  "calendar_personal",

  // Delivery directives
  "shareable_internal",
  "shareable_external",
]);
```

### 1.3 Canonical findings

```ts
export const SensitivityFindingCodeSchema = z.enum([
  "identity_document",
  "personal_contact",
  "personal_location",
  "family_or_dependent",
  "minor_indicator",
  "adversary_communication",
  "joint_defense_or_common_interest",
  "third_party_agent_exception",
  "internal_investigation",
  "whistleblower_protected",
  "mnpi_or_market_sensitive",
  "legal_hold_or_preservation",
  "regulated_compliance_sensitive",
]);

export const VisibilityControlledFindingCodeSchema = z.enum([
  "internal_investigation",
  "whistleblower_protected",
  "mnpi_or_market_sensitive",
  "legal_hold_or_preservation",
  "identity_document",
  "minor_indicator",
]);

export const SensitivityFindingSourceSchema = z.enum([
  "source_rule",
  "boundary_scan",
  "content_classifier",
  "user_override",
  "system_inference",
]);

export const SensitivityFindingSchema = z.object({
  code: SensitivityFindingCodeSchema,
  confidence: z.number().min(0).max(1).default(0.5),
  source: SensitivityFindingSourceSchema,
  note: z.string().max(500).optional(),
  schema_version: z.literal(1),
});
```

### 1.4 Per-category collection policy

**Settings UI wireframe — Settings > Privacy & Sharing > Content Collection**

```text
Sensitive Content Collection
────────────────────────────────────────────────────────────────
Control whether each sensitivity class is allowed into the
knowledge pipeline. "Do not collect" blocks extraction before
graph write. "Collect without tagging" is testing mode only.

PRIVILEGE
  Attorney-client privileged     [▾ Do not collect          ]
  Work product                   [▾ Do not collect          ]
  Firm GC privileged             [▾ Do not collect          ]
  Settlement confidential        [▾ Do not collect          ]
  Court sealed                   [▾ Do not collect          ]

PERSONAL
  General personal / private     [▾ Do not collect          ]
  Financial personal             [▾ Do not collect          ]
  Health personal                [▾ Do not collect          ]
  Credentials / secrets          [▾ Do not collect          ]
  Third-party PII                [▾ Do not collect          ]

ORGANIZATIONAL
  Work related                   [▾ Collect + tag           ]
  Firm internal                  [▾ Collect + tag           ]
  Client confidential            [▾ Collect + tag           ]
  Browser history                [▾ Do not collect          ]
  Personal calendar              [▾ Do not collect          ]

[Test extraction mode info banner]
  Collect without tagging skips content classification and
  outbound eligibility until the normal lifecycle catches up.

[Save policy]  [Restore recommended defaults]
```


```ts
export const SensitiveContentCollectionPolicySchemaV2 = z.object({
  attorney_client_privileged: CollectionModeSchema.default("do_not_collect"),
  work_product: CollectionModeSchema.default("do_not_collect"),
  firm_gc_privileged: CollectionModeSchema.default("do_not_collect"),

  settlement_confidential: CollectionModeSchema.default("do_not_collect"),
  court_sealed: CollectionModeSchema.default("do_not_collect"),

  personal_private: CollectionModeSchema.default("do_not_collect"),
  financial_personal: CollectionModeSchema.default("do_not_collect"),
  health_personal: CollectionModeSchema.default("do_not_collect"),
  contains_credentials: CollectionModeSchema.default("do_not_collect"),
  contains_pii_third_party: CollectionModeSchema.default("do_not_collect"),

  work_related: CollectionModeSchema.default("collect_and_tag"),
  firm_internal: CollectionModeSchema.default("collect_and_tag"),
  client_confidential: CollectionModeSchema.default("collect_and_tag"),

  browser_history: CollectionModeSchema.default("do_not_collect"),
  calendar_personal: CollectionModeSchema.default("do_not_collect"),

  shareable_internal: CollectionModeSchema.default("collect_and_tag"),
  shareable_external: CollectionModeSchema.default("collect_and_tag"),

  schema_version: z.literal(2),
});
```

**Normative default posture**
- High-risk personal, privileged, sealed, settlement, credential, and third-party PII categories default to `do_not_collect`.
- Organizational categories needed for the product to function default to `collect_and_tag`.
- `collect_without_tagging` exists for testing and controlled validation only. UI SHALL mark it as a testing mode.

### 1.5 Source-match and source-rule contracts

**Settings UI wireframe — Settings > Privacy & Sharing > Classification Rules**

```text
Source Classification Rules
────────────────────────────────────────────────────────────────
Rules let the user say "content from these sources should
receive these tags/findings and optional collection overrides."

┌──────────────────────────────────────────────────────────────┐
│ Rule: "Personal Gmail"                               [Edit] │
│ When: surface=email AND account matches *@gmail.com         │
│ Tags: personal_private                                    │
│ Findings: (none)                                           │
│ Collection override: do_not_collect                        │
│ Priority: 100        Enabled: [✓]       Exclusive: [ ]     │
├──────────────────────────────────────────────────────────────┤
│ Rule: "Firm Outlook"                                [Edit] │
│ When: surface=email AND account matches *@firm.com         │
│ Tags: work_related, firm_internal                           │
│ Findings: (none)                                           │
│ Collection override: inherit                               │
│ Priority: 80         Enabled: [✓]       Exclusive: [ ]     │
├──────────────────────────────────────────────────────────────┤
│ Rule: "Personal Obsidian vault"                     [Edit] │
│ When: surface=notes AND note_vault_id matches Personal     │
│ Tags: personal_private                                     │
│ Collection override: do_not_collect                        │
│ Priority: 90         Enabled: [✓]       Exclusive: [ ]     │
└──────────────────────────────────────────────────────────────┘

[+ Add rule]   [Import seed rules]   [Test a source match]
```

**Design note:** EC surface toggles remain the coarse upstream gate. These rules are the sub-filter that applies only within an enabled surface.


```ts
export const MatchOperatorSchema = z.enum([
  "exact",
  "glob",
  "regex",
  "prefix",
  "suffix",
  "contains",
]);

export const MatchPatternSchema = z.object({
  operator: MatchOperatorSchema.default("glob"),
  pattern: z.string().max(500),
  case_sensitive: z.boolean().default(false),
  normalize_domain: z.boolean().default(false),
  normalize_email: z.boolean().default(false),
  schema_version: z.literal(1),
});

export const SourceMatchSchemaV2 = z.object({
  surface: z.string().max(80).optional(),

  account_pattern: MatchPatternSchema.optional(),
  sender_pattern: MatchPatternSchema.optional(),
  folder_pattern: MatchPatternSchema.optional(),
  calendar_id: MatchPatternSchema.optional(),

  domain_pattern: MatchPatternSchema.optional(),
  app_bundle_id: MatchPatternSchema.optional(),
  browser_profile_id: MatchPatternSchema.optional(),
  document_root_id: MatchPatternSchema.optional(),
  document_path_pattern: MatchPatternSchema.optional(),

  chat_session_pattern: MatchPatternSchema.optional(),
  room_id: MatchPatternSchema.optional(),
  panel_id: MatchPatternSchema.optional(),
  forum_id: MatchPatternSchema.optional(),
  participant_pattern: MatchPatternSchema.optional(),

  note_store_id: MatchPatternSchema.optional(),
  note_source_id: MatchPatternSchema.optional(),
  note_vault_id: MatchPatternSchema.optional(),
  notebook_id: MatchPatternSchema.optional(),
  note_folder_pattern: MatchPatternSchema.optional(),
  note_tag_pattern: MatchPatternSchema.optional(),

  schema_version: z.literal(2),
});

export const SourceRuleMatchResolutionSchema = z.object({
  exclusive: z.boolean().default(false),
  collection_mode_override: CollectionModeSchema.optional(),
  schema_version: z.literal(1),
});

export const SourceClassificationRuleSchemaV3 = z.object({
  rule_id: z.string().max(120),
  rule_name: z.string().max(200),
  source_match: SourceMatchSchemaV2,
  assigned_tags: z.array(SensitivityTagSchema).default([]),
  assigned_findings: z.array(SensitivityFindingCodeSchema).default([]),
  priority: z.number().int().default(0),
  enabled: z.boolean().default(true),
  match_resolution: SourceRuleMatchResolutionSchema.default({}),
  schema_version: z.literal(3),
});

export const SourceClassificationRulesConfigSchemaV3 = z.object({
  rules: z.array(SourceClassificationRuleSchemaV3).default([]),
  default_tags: z.array(SensitivityTagSchema).default([]),
  default_findings: z.array(SensitivityFindingCodeSchema).default([]),
  schema_version: z.literal(3),
});
```

**Normative matcher rules**
1. Exact, glob, regex, prefix, suffix, and contains semantics SHALL be tested and documented in code.
2. Domain/email normalization SHALL occur before matching when `normalize_domain` / `normalize_email` are true.
3. Lower-priority rules still contribute tags/findings/mode votes unless a matched higher-priority rule sets `exclusive = true`.
4. `default_tags` and `default_findings` SHALL be applied when no source rule matches. This is not optional.

### 1.6 Source-resolution result and algorithm

```ts
export const ResolvedSourceClassificationSchema = z.object({
  matched_rule_ids: z.array(z.string().max(120)).default([]),
  source_tags: z.array(SensitivityTagSchema).default([]),
  source_findings: z.array(SensitivityFindingCodeSchema).default([]),
  effective_collection_mode: z.enum([
    "inherit",
    "do_not_collect",
    "collect_and_tag",
    "collect_without_tagging",
  ]).default("inherit"),
  requires_user_review: z.boolean().default(false),
  schema_version: z.literal(1),
});
```

```ts
export function resolveSourceClassification(
  matches: SourceClassificationRule[],
  config: z.infer<typeof SourceClassificationRulesConfigSchemaV3>,
): z.infer<typeof ResolvedSourceClassificationSchema> {
  const active = matches
    .filter((r) => r.enabled)
    .sort((a, b) => (b.priority - a.priority) || a.rule_id.localeCompare(b.rule_id));

  if (active.length === 0) {
    return {
      matched_rule_ids: [],
      source_tags: [...config.default_tags],
      source_findings: [...config.default_findings],
      effective_collection_mode: "inherit",
      requires_user_review:
        config.default_tags.includes("work_related") &&
        config.default_tags.includes("personal_private"),
      schema_version: 1,
    };
  }

  const exclusiveWinner = active.find((r) => r.match_resolution.exclusive === true);
  const effectiveRules = exclusiveWinner ? [exclusiveWinner] : active;

  const source_tags = [...new Set([
    ...config.default_tags,
    ...effectiveRules.flatMap((r) => r.assigned_tags),
  ])];

  const source_findings = [...new Set([
    ...config.default_findings,
    ...effectiveRules.flatMap((r) => r.assigned_findings),
  ])];

  const modes = effectiveRules
    .map((r) => r.match_resolution.collection_mode_override)
    .filter(Boolean) as z.infer<typeof CollectionModeSchema>[];

  const effective_collection_mode =
    modes.includes("do_not_collect") ? "do_not_collect" :
    modes.includes("collect_and_tag") ? "collect_and_tag" :
    modes.length > 0 && modes.every((m) => m === "collect_without_tagging") ? "collect_without_tagging" :
    "inherit";

  return {
    matched_rule_ids: effectiveRules.map((r) => r.rule_id),
    source_tags,
    source_findings,
    effective_collection_mode,
    requires_user_review:
      source_tags.includes("work_related") &&
      source_tags.includes("personal_private"),
    schema_version: 1,
  };
}
```

### 1.7 Deterministic boundary scan

```ts
export const SensitiveBoundaryMarkerSchema = z.enum([
  "credential_pattern",
  "government_id_pattern",
  "matter_number_pattern",
  "privilege_banner_pattern",
  "sealed_case_pattern",
  "settlement_marker_pattern",
  "legal_hold_marker_pattern",
  "mnpi_marker_pattern",
]);

export const SensitiveBoundaryScanResultSchemaV2 = z.object({
  matched_markers: z.array(SensitiveBoundaryMarkerSchema).default([]),
  inferred_tags: z.array(SensitivityTagSchema).default([]),
  inferred_findings: z.array(SensitivityFindingCodeSchema).default([]),
  visibility_floor: z.enum(["blocked", "local_only", "cloud_warn", "cloud_allowed"]),
  schema_version: z.literal(2),
});
```

**Normative mapping rules**
- `credential_pattern` → `contains_credentials`
- `government_id_pattern` → finding `identity_document`; may also imply `personal_private`
- `privilege_banner_pattern` → at least `privilege_uncertain`, often `attorney_client_privileged`
- `sealed_case_pattern` → `court_sealed`
- `settlement_marker_pattern` → `settlement_confidential`
- `legal_hold_marker_pattern` → finding `legal_hold_or_preservation`
- `mnpi_marker_pattern` → finding `mnpi_or_market_sensitive`

### 1.8 Deterministic fast-path classification and co-occurrence validation

```ts
export const ClassificationFastPathPolicySchemaV2 = z.object({
  allow_org_safe_promotion: z.boolean().default(true),
  org_safe_tag_set: z.array(z.enum([
    "work_related",
    "firm_internal",
    "client_confidential",
  ])).default(["work_related", "firm_internal", "client_confidential"]),
  high_risk_blocking_tags: z.array(z.string().max(80)).default([
    "attorney_client_privileged",
    "work_product",
    "firm_gc_privileged",
    "personal_private",
    "financial_personal",
    "health_personal",
    "contains_credentials",
    "contains_pii_third_party",
    "court_sealed",
    "settlement_confidential",
  ]),
  require_existing_labeled_example_count_ref: z.boolean().default(true),
  schema_version: z.literal(2),
});

export const LabeledExampleCountSnapshotSchema = z.object({
  corpus_id: z.string().max(120),
  eligible_labeled_example_count: z.number().int().nonnegative(),
  sampled_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const EmbeddingFastPathPolicySchema = z.object({
  enabled: z.boolean().default(false),
  local_only_required: z.boolean().default(true),
  minimum_labeled_examples: z.number().int().positive().default(500),
  candidate_similarity_threshold: z.number().min(0).max(1).default(0.92),
  max_candidate_votes: z.number().int().positive().default(5),
  max_distance_review_threshold: z.number().min(0).max(1).default(0.08),
  schema_version: z.literal(1),
});

export const TagCoOccurrenceValidationRuleSchemaV2 = z.object({
  invalid_pairs: z.array(z.tuple([SensitivityTagSchema, SensitivityTagSchema])).default([]),
  force_review_when_pairs_present: z.array(z.tuple([SensitivityTagSchema, SensitivityTagSchema])).default([
    ["work_related", "personal_private"],
    ["shareable_external", "attorney_client_privileged"],
    ["shareable_external", "work_product"],
    ["shareable_external", "court_sealed"],
  ]),
  schema_version: z.literal(2),
});
```

**Normative rules**
1. Fast-path promotion may only auto-classify observations when the final tag set is entirely within `org_safe_tag_set` and no invalid/force-review pairs are present.
2. The count threshold for optional embedding prescreen SHALL be derived from `LabeledExampleCountSnapshotSchema.eligible_labeled_example_count`; it SHALL NOT be caller-supplied ad hoc.
3. High-risk tags never auto-promote to cloud-eligible classification without local-only classification or an existing settled classified state.
4. The optional embedding fast-path is a local-only accelerator. It MUST NOT replace deterministic fast-path rules, MUST NOT override a blocking/high-risk tag, and MUST NOT run unless `minimum_labeled_examples` is satisfied.
5. If the embedding fast-path proposes a class but the max-vote band is ambiguous or the nearest-neighbor distance exceeds `max_distance_review_threshold`, the observation SHALL remain unresolved and SHALL continue through the ordinary local-only classification path.

### 1.9 Visibility policies

**Settings UI wireframe — Settings > Privacy & Sharing > Extraction Visibility**

```text
Extraction Visibility
────────────────────────────────────────────────────────────────
Decide which execution class may see content during extraction.
These rules apply AFTER source-rule / collection policy resolution.

Category / signal                 Visibility        Redact before cloud   Local unavailable
────────────────────────────────────────────────────────────────────────────────────────────
attorney_client_privileged        [▾ local_only    ] [ ]                 [▾ block ]
work_product                      [▾ local_only    ] [ ]                 [▾ block ]
personal_private                  [▾ cloud_warn    ] [ ]                 [▾ defer ]
contains_credentials              [▾ local_only    ] [ ]                 [▾ block ]
client_confidential               [▾ cloud_allowed ] [ ]                 [▾ defer ]
identity_document (finding)       [▾ local_only    ] [ ]                 [▾ block ]
mnpi_or_market_sensitive          [▾ local_only    ] [ ]                 [▾ block ]

Legend:
- local_only = verified same-machine execution only
- cloud_warn = may use cloud only if the later sharing path also permits it
- blocked = no extraction dispatch

[Save visibility policy]  [Preview effective decisions]
```


```ts
export const ModelVisibilityClassSchema = z.enum([
  "blocked",
  "local_only",
  "cloud_warn",
  "cloud_allowed",
]);

export const VisibilitySignalSchema = z.discriminatedUnion("signal_kind", [
  z.object({
    signal_kind: z.literal("tag"),
    tag: SensitivityTagSchema,
  }),
  z.object({
    signal_kind: z.literal("finding"),
    finding_code: VisibilityControlledFindingCodeSchema,
  }),
]);

export const ExtractionVisibilityRuleSchemaV2 = z.object({
  signal: VisibilitySignalSchema,
  visibility_class: ModelVisibilityClassSchema,
  redact_before_cloud: z.boolean().default(false),
  on_local_unavailable: z.enum(["block", "defer"]).default("block"),
  schema_version: z.literal(2),
});

export const ExtractionVisibilityPolicySchemaV2 = z.object({
  rules: z.array(ExtractionVisibilityRuleSchemaV2).default([]),
  schema_version: z.literal(2),
});
```

**Required default policy**
- `attorney_client_privileged`, `work_product`, `firm_gc_privileged`, `court_sealed`, `settlement_confidential`, `financial_personal`, `health_personal`, `contains_credentials`, `contains_pii_third_party` → `local_only`
- `personal_private` → `cloud_warn`
- `work_related`, `firm_internal`, `client_confidential`, `shareable_internal` → `cloud_allowed`
- `internal_investigation`, `whistleblower_protected`, `mnpi_or_market_sensitive`, `legal_hold_or_preservation`, `identity_document`, `minor_indicator` findings SHALL raise visibility to at least `local_only`

### 1.10 Narrow deterministic redaction helper (optional secondary path only)

Redaction is **not** the primary privacy model. It is a narrow, deterministic helper for selected marker classes under `cloud_warn`.

**Settings UI wireframe — Settings > Privacy & Sharing > Extraction Visibility > Deterministic Redaction Helper**

```text
Deterministic Redaction Helper
────────────────────────────────────────────────────────────────
Use a temporary local masked working copy only for narrow,
deterministic marker classes. This is a secondary helper,
not the primary privacy boundary.

Enabled markers
  [✓] Credential patterns
  [ ] Government ID patterns
  [ ] Matter number patterns

Only apply when visibility class is   [ cloud_warn ]
Preserve original locally             [✓]
Ephemeral copy TTL (minutes)          [ 30 ]

[Save redaction helper]  [Preview masked working copy rules]
```

```ts
export const DeterministicRedactionPolicySchema = z.object({
  enabled_markers: z.array(z.enum([
    "credential_pattern",
    "government_id_pattern",
    "matter_number_pattern",
  ])).default([]),
  apply_only_when_visibility_class: z.literal("cloud_warn").default("cloud_warn"),
  preserve_original_locally: z.boolean().default(true),
  ephemeral_copy_ttl_minutes: z.number().int().positive().default(30),
  schema_version: z.literal(1),
});

export const LocalRedactionReceiptSchemaV2 = z.object({
  receipt_id: z.string().max(120),
  observation_id: z.string().max(120),
  marker_codes: z.array(SensitiveBoundaryMarkerSchema).default([]),
  redacted_copy_ref: z.string().max(240),
  expires_at: z.string().datetime(),
  deleted_at: z.string().datetime().optional(),
  schema_version: z.literal(2),
});
```

**Normative rules**
- No unredact path exists. The original source remains canonical.
- Redacted working copies SHALL be ephemeral and SHALL NOT become graph truth.
- Redaction SHALL NOT be used for broad privilege or general personal categories.

### 1.11 Classification states and node metadata

**State-lifecycle explanation**

The classification state is the sensitivity lifecycle marker, not a cosmetic field. A node starts `provisional_source_only` when source rules or boundary scan have assigned early signals but content-level classification has not finished. It moves to `classified` only when the required classification work is complete and the node is eligible to be treated as settled at outbound boundaries. `quarantined_review` means the node is intentionally held in review, and `tombstoned` means the node remains part of durable history but is no longer eligible for normal delivery/use. A coding agent should read these states as policy gates, not just labels for the UI.


```ts
export const SensitivityClassificationStateSchema = z.enum([
  "unclassified",
  "provisional_source_only",
  "classified",
  "deferred_unavailable",
  "quarantined_review",
  "tombstoned",
]);

export const NodeSensitivityDataClassSchema = z.enum([
  "public",
  "internal",
  "privileged_or_local_only",
]);

export const NodeSensitivityMetadataSchemaV3 = z.object({
  sensitivity_tags: z.array(SensitivityTagSchema).default([]),
  source_level_tags: z.array(SensitivityTagSchema).default([]),
  content_level_tags: z.array(SensitivityTagSchema).default([]),
  user_override_tags: z.array(SensitivityTagSchema).default([]),

  findings: z.array(SensitivityFindingSchema).default([]),
  source_rule_ids: z.array(z.string().max(120)).default([]),

  classification_state: SensitivityClassificationStateSchema.default("unclassified"),
  data_class: NodeSensitivityDataClassSchema.default("internal"),
  classifier_confidence: z.record(z.string(), z.number().min(0).max(1)).default({}),
  last_classified_at: z.string().datetime().optional(),

  requires_user_review: z.boolean().default(false),
  requires_privilege_review: z.boolean().default(false),

  tombstoned_at: z.string().datetime().optional(),
  tombstone_reason: z.enum([
    "policy_retroactive_delete",
    "user_delete",
    "merged",
  ]).optional(),

  schema_version: z.literal(3),
});
```

### 1.12 Expedited sensitivity resolution

```ts
export const ExpeditedSensitivityResolutionReceiptSchema = z.object({
  receipt_id: z.string().max(120),
  node_id: z.string().max(120),
  attempted: z.boolean(),
  used_local_classifier: z.boolean().default(false),
  blocked_reason_codes: z.array(z.string().max(80)).default([]),
  result_state: SensitivityClassificationStateSchema,
  created_at: z.string().datetime(),
  schema_version: z.literal(1),
});
```

**Normative algorithm**
1. If a node already has only organizational safe tags and no blocking findings, deterministic fast-path may promote it to `classified`.
2. If any high-risk tag or visibility-controlled finding is present, expedited resolution SHALL use **local-only** classification if invoked at all.
3. On-demand expedited classification SHALL NEVER route to cloud.
4. If no local model is available, the node SHALL become `deferred_unavailable` and fail closed for non-local outbound use.
5. `provisional_source_only`, `unclassified`, `deferred_unavailable`, and `quarantined_review` SHALL fail closed for all outbound destinations except same-machine local runtime.

### 1.13 Effective collection mode composition with EC controls

PropA defines semantic inputs; EC computes effective truth.

```ts
export const EffectiveCollectionComputationInputSchema = z.object({
  global_collection_enabled: z.boolean(),
  surface_collection_enabled: z.boolean(),
  incognito_active: z.boolean(),
  source_rule_result: ResolvedSourceClassificationSchema,
  collection_policy: SensitiveContentCollectionPolicySchemaV2,
  boundary_scan_tags: z.array(SensitivityTagSchema).default([]),
  schema_version: z.literal(1),
});

export const EffectiveCollectionModeResultSchema = z.object({
  effective_mode: z.enum([
    "blocked",
    "collect_and_tag",
    "collect_without_tagging",
  ]),
  reason_codes: z.array(z.string().max(80)).default([]),
  schema_version: z.literal(1),
});
```

**Normative composition**
- If global collection or surface collection is off, effective mode = `blocked`.
- If incognito is active, effective mode = `blocked`.
- Else compose `source_rule_result.source_tags + boundary_scan_tags`.
- If any final category resolves to `do_not_collect`, effective mode = `blocked`.
- Else if source rule forces `collect_without_tagging`, effective mode = `collect_without_tagging`.
- Else effective mode = `collect_and_tag`.

---

### Schema consume-path map

| Schema | Consumed by | When | Consume path |
|---|---|---|---|
| `CollectionModeSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SensitivityTagSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SensitivityFindingCodeSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `VisibilityControlledFindingCodeSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SensitivityFindingSourceSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SensitivityFindingSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SensitiveContentCollectionPolicySchemaV2` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `MatchOperatorSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `MatchPatternSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SourceMatchSchemaV2` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SourceRuleMatchResolutionSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SourceClassificationRuleSchemaV3` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SourceClassificationRulesConfigSchemaV3` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `ResolvedSourceClassificationSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SensitiveBoundaryMarkerSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SensitiveBoundaryScanResultSchemaV2` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `ClassificationFastPathPolicySchemaV2` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `LabeledExampleCountSnapshotSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `EmbeddingFastPathPolicySchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `TagCoOccurrenceValidationRuleSchemaV2` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `ModelVisibilityClassSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `VisibilitySignalSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `ExtractionVisibilityRuleSchemaV2` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `ExtractionVisibilityPolicySchemaV2` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `DeterministicRedactionPolicySchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `LocalRedactionReceiptSchemaV2` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `SensitivityClassificationStateSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `NodeSensitivityDataClassSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `NodeSensitivityMetadataSchemaV3` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `ExpeditedSensitivityResolutionReceiptSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `EffectiveCollectionComputationInputSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |
| `EffectiveCollectionModeResultSchema` | Consumed by EC extraction dispatch, EC PolicyDecisionEngine inputs, DOC72 node metadata writes, DOC20 privacy settings/read surfaces, and local-only classification flows | surface intake, extraction dispatch, expedited sensitivity resolution, nightly/backfill classification, and settings saves | PropA §1, EC compiled policy evaluation, DOC72 intake / metadata adoption, DOC20 Privacy & Sharing surfaces |

## 2. Sharing, injection, and governance semantics

**Integrates into:** DOC24 §§26-37 (packet assembly, rendering, injection and delivery), DOC11 runtime dispatch truth, DOC72 governance metadata adoption, EC Core PolicyDecisionEngine inputs, DOC20 (Settings > Privacy & Sharing > Sharing & Injection).

**R6.2 changes from R6.1:** No contract changes. This section now restores the conceptual explanation of exposure contexts, sharing matrices, override receipts, and trust-domain governance so a builder can understand how injection and outbound use differ from intake.

This section defines the outbound half of the privacy/governance model: what automatic injection may do, what explicit attachment may do, when the user can override once, how restrictive and permissive signals compose, and how future sync domains inherit those rules. It exists so cloud use, local use, explicit user intent, and future networking all run through one coherent delivery policy rather than a collection of ad hoc exceptions.


### 2.1 Exposure contexts

```ts
export const ExposureContextSchema = z.enum([
  "automatic_packet_injection",
  "explicit_memory_attach",
  "user_authored_prompt",
]);
```

**Normative rule**
- `user_authored_prompt` applies only to literal text the user typed.
- If the system resolves a prompt to graph nodes and injects them, those nodes SHALL be evaluated as `explicit_memory_attach`.
- Any system-assisted insertion into the composer SHALL carry `origin = "system_memory_insert"` and SHALL NOT be treated as pure user-authored prompt text.

### 2.2 Automatic injection guard settings

**Settings UI wireframe — Settings > Privacy & Sharing > Sharing & Injection > Automatic Injection Guard**

```text
Automatic Sensitive-Memory Injection Guard
────────────────────────────────────────────────────────────────
Control what happens when ELNOR wants to auto-inject memory
or when the user explicitly attaches a memory to a turn.

Cloud runtime default mode        [▾ block ]
Automatic block scopes            [ private ] [ personal ]
Automatic block tags              [ personal_private ] [ financial_personal ]
                                  [ health_personal ] [ contains_credentials ]
Explicit memory attach behavior   [▾ warn ]
User-authored prompt behavior     [▾ allow ]
One-turn override expiry          [ after this turn ]

Behavior summary:
- Automatic packet injection: blocked for configured sensitive scopes/tags.
- Explicit attach: allowed only after per-turn warning/override if configured.
- User-authored prompt text: never hard-blocked by graph-memory policy.

[Save guard settings]  [Open why-blocked inspector]
```


```ts
export const SensitiveAutoInjectionGuardSettingsSchemaV3 = z.object({
  auto_block_scopes: z.array(z.enum(["private", "personal"])).default(["private"]),
  auto_block_tags: z.array(z.string().max(80)).default([
    "personal_private",
    "financial_personal",
    "health_personal",
    "contains_credentials",
    "contains_pii_third_party",
  ]),
  cloud_runtime_default_mode: z.enum(["allow", "warn", "block"]).default("block"),
  local_runtime_default_mode: z.enum(["allow", "warn", "block"]).default("allow"),
  explicit_attach_behavior: z.enum([
    "allow",
    "warn",
    "block",
    "blocked_requires_consent",
  ]).default("warn"),
  user_authored_prompt_behavior: z.enum(["allow", "warn"]).default("allow"),
  schema_version: z.literal(3),
});
```

### 2.3 One-turn override receipts

```ts
export const MemoryExposureOverrideReceiptSchemaV2 = z.object({
  receipt_id: z.string().max(120),
  session_id: z.string().max(120),
  turn_id: z.string().max(120),
  node_ids: z.array(z.string().max(120)).default([]),
  destination: z.enum(["cloud_api", "same_machine_local_runtime"]),
  request_fingerprint: z.string().length(64),
  nonce: z.string().max(120),
  approved_once: z.boolean().default(true),
  expires_after_turn: z.boolean().default(true),
  consumed: z.boolean().default(false),
  created_at: z.string().datetime(),
  schema_version: z.literal(2),
});
```

**Normative rules**
1. Override receipts SHALL be atomic consume-once.
2. Override receipts SHALL be bound to session, turn, destination, request fingerprint, and nonce.
3. Reuse on retry or later dispatch without an unconsumed exact-match receipt is forbidden.

### 2.4 Node governance metadata, edge inheritance, and future sync semantics

```ts
export const NodeScopeSchema = z.enum([
  "firm_shared",
  "matter_scoped",
  "personal",
  "private",
]);

export const TrustDomainSchema = z.enum([
  "personal_node",
  "firm_server",
  "matter_space",
  "peer_node",
]);

export const NodeGovernanceMetadataSchemaV3 = z.object({
  node_scope: NodeScopeSchema,
  owner_principal_id: z.string().max(120).optional(),
  source_trust_domain: TrustDomainSchema.default("personal_node"),
  allowed_destination_domains: z.array(TrustDomainSchema).default([]),
  denied_destination_domains: z.array(TrustDomainSchema).default([]),
  allowed_principal_ids: z.array(z.string().max(120)).default([]),
  allowed_matter_entity_ids: z.array(z.string().max(120)).default([]),
  migrated_requires_manual_review: z.boolean().default(false),
  schema_version: z.literal(3),
});

export const EdgeTrustInheritancePolicySchema = z.object({
  inherit_strictest_endpoint_domain: z.boolean().default(true),
  strip_edges_on_blocked_sync: z.boolean().default(true),
  schema_version: z.literal(1),
});

export const SyncEdgeStripDecisionSchema = z.object({
  edge_id: z.string().max(120),
  source_node_id: z.string().max(120),
  target_node_id: z.string().max(120),
  stripped: z.boolean().default(false),
  reason_codes: z.array(z.string().max(80)).default([]),
  schema_version: z.literal(1),
});

export const NodeTombstoneSyncPayloadSchema = z.object({
  node_id: z.string().max(120),
  tombstone_reason: z.enum([
    "policy_retroactive_delete",
    "user_delete",
    "merged",
  ]),
  tombstoned_at: z.string().datetime(),
  source_trust_domain: TrustDomainSchema,
  schema_version: z.literal(1),
});
```

**Normative rules**
- `private` scope SHALL NEVER sync to non-personal domains.
- `personal` scope SHALL remain local unless an explicit governance rule allows otherwise.
- Scope alone is not enough. Sync requires both governance approval and EC identity-proof verification.
- Graph edges SHALL inherit the strictest trust-domain restrictions of their endpoints.
- If a node is eligible for transfer but an attached edge is not, the edge SHALL be stripped with an explicit receipt; EC SHALL NOT synthesize ordinary graph nodes to represent the stripped edge.
- Tombstoned nodes SHALL sync only as tombstones through `NodeTombstoneSyncPayloadSchema`; they SHALL NOT be silently resurrected as active content on the destination node.

### 2.5 Source-policy adapter semantics

PropA does not own the evaluator, but it does define the semantic adapter that EC must consume.

```ts
export const SourcePolicyToSharingInputAdapterSchema = z.object({
  source_policy_result: z.enum([
    "allow",
    "warn_on_secret",
    "redact_secret",
    "block",
  ]),
  mapped_effect: z.enum([
    "no_change",
    "force_warn",
    "force_redaction",
    "force_block",
  ]),
  schema_version: z.literal(1),
});
```

**Required order**
`source-policy -> visibility gate -> classification fail-closed -> exposure-context guard -> sharing matrix + finding tightenings -> interaction-mode coercion`

### 2.6 Sharing matrix semantics

**Settings UI wireframe — Settings > Privacy & Sharing > Sharing Matrix**

```text
Sharing Matrix
────────────────────────────────────────────────────────────────
Choose the default action when tagged knowledge is sent toward
a destination class. Restrictive tags override permissive/shareable tags.

Tag / Destination                 Local runtime   Cloud API   Firm server   Export file
─────────────────────────────────────────────────────────────────────────────────────────
attorney_client_privileged        allow           block       block         warn
work_product                      allow           warn        warn          warn
personal_private                  allow           warn        block         warn
financial_personal                allow           block       block         block
client_confidential               allow           warn        warn          warn
shareable_internal                allow           allow       allow         allow
shareable_external                allow           allow       warn          allow

[Edit finding overrides]   [Preview decision]   [Reset to recommended defaults]
```


```ts
export const SharingActionSchema = z.enum([
  "allow",
  "warn",
  "block",
  "strip",
  "redact",
]);

export const SharingRuleSchema = z.object({
  sensitivity_tag: SensitivityTagSchema,
  destination: z.enum([
    "same_machine_local_runtime",
    "local_file_export",
    "local_network_peer",
    "firm_server",
    "remote_peer",
    "cloud_api",
    "email_outbound",
    "agent_messaging",
  ]),
  action: SharingActionSchema,
  schema_version: z.literal(1),
});

export const FindingTighteningRuleSchema = z.object({
  finding_code: SensitivityFindingCodeSchema,
  destination: z.enum([
    "same_machine_local_runtime",
    "local_file_export",
    "local_network_peer",
    "firm_server",
    "remote_peer",
    "cloud_api",
    "email_outbound",
    "agent_messaging",
  ]),
  action: z.enum(["warn", "block", "redact"]),
  schema_version: z.literal(1),
});
```

**Normative matrix principles**
1. Restrictive tags always override permissive/shareable tags.
2. `local_network_peer`, `firm_server`, `remote_peer`, `cloud_api`, `email_outbound`, and `agent_messaging` are all outbound boundaries.
3. `same_machine_local_runtime` is the only non-egress local execution destination class.
4. `warn && interaction_mode = background_non_interactive` SHALL coerce to `block`.
5. `redact` and `strip` SHALL generate explicit receipts; they are not silent side effects.

### 2.7 Baseline sharing defaults

The coding agent SHALL seed the matrix with at least these defaults:

| Tag / Destination | same_machine_local_runtime | cloud_api | firm_server | local_network_peer | email_outbound |
|---|---|---|---|---|---|
| attorney_client_privileged | allow | block | block | block | block |
| work_product | allow | block | warn | warn | block |
| firm_gc_privileged | allow | block | block | block | block |
| court_sealed | allow | block | block | block | block |
| settlement_confidential | allow | block | warn | warn | block |
| personal_private | allow | warn | block | block | warn |
| financial_personal | allow | block | block | block | block |
| health_personal | allow | block | block | block | block |
| contains_credentials | allow | block | block | block | block |
| contains_pii_third_party | allow | block | warn | warn | block |
| work_related | allow | allow | allow | warn | warn |
| firm_internal | allow | warn | allow | warn | warn |
| client_confidential | allow | warn | warn | warn | warn |
| shareable_internal | allow | allow | allow | allow | warn |
| shareable_external | allow | allow | allow | allow | allow |

This table is the **semantic seed**; EC compiles it into the effective policy artifact.

### 2.8 Required injection behavior

- Automatic injection of nodes in `private` scope or carrying blocked tags SHALL be blocked by default for cloud-bound packet assembly.
- Explicit attachment of such nodes SHALL warn or require one-turn consent, depending on settings.
- User-authored prompt text SHALL not be hard-blocked by graph policy, but the system MAY warn if a cloud model is selected and sensitive-looking text is detected.
- Local runtime default mode SHOULD be `allow` unless the user changes it.

---

### Schema consume-path map

| Schema | Consumed by | When | Consume path |
|---|---|---|---|
| `ExposureContextSchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `SensitiveAutoInjectionGuardSettingsSchemaV3` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `MemoryExposureOverrideReceiptSchemaV2` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `NodeScopeSchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `TrustDomainSchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `NodeGovernanceMetadataSchemaV3` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `EdgeTrustInheritancePolicySchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `SyncEdgeStripDecisionSchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `NodeTombstoneSyncPayloadSchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `SourcePolicyToSharingInputAdapterSchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `SharingActionSchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `SharingRuleSchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |
| `FindingTighteningRuleSchema` | Consumed by EC PolicyDecisionEngine, DOC24 packet assembly/injection, DOC11 dispatch truth, sync-preflight logic, and privacy/sharing settings/read surfaces | automatic injection, explicit memory attach, one-turn override creation/consumption, outbound send, export, and future sync evaluation | PropA §2, EC compiled policy engine, DOC24 delivery chain, DOC20 Sharing & Injection settings |

## 3. Misclassification recovery and review queue

**End-to-end walkthrough**

A misclassification typically starts when feedback, user correction, routing failure evidence, or review heuristics suggest that the captured content is substantively right but structurally wrong. The system writes a review item, suppresses decay while that item is pending, and surfaces the item through the Knowledge Review Queue. Once a reviewer resolves the issue, EC executes the repair command, writes the corrected node or scope mutation through the normal durable path, and then invalidates dependent edges, caches, manifests, and policy receipts according to the repair rules below.


**Integrates into:** DOC72 review / repair seams, DOC1 write-gate and archive-not-delete governance, DOC20 Knowledge Manager and review surfaces, EC Core command routing for repair actions.

**R6.2 changes from R6.1:** No contract changes. This section now adds the plain-language explanation of how a misclassification is surfaced, reviewed, repaired, and prevented from silently decaying.

This section defines what happens when the system captured the right underlying content but assigned the wrong labels, scope, or node shape. It exists to route those failures into an explicit repair queue, preserve evidence while the issue is under review, and ensure reclassify/rescope work triggers the invalidation and governance consequences a coding agent must not guess.


### 3.1 Queue item and action schemas

**Read-surface wireframe — Knowledge Review Queue**

```text
Knowledge Review Queue
────────────────────────────────────────────────────────────────
[Filter: all | sensitivity | misclassification | sync | validation ]

┌──────────────────────────────────────────────────────────────┐
│ Misclassified preference → domain_concept             HIGH   │
│ Observation: email:firm:12345                                 │
│ Current node kind: domain_concept                            │
│ Suggested node kind: memory_directive                        │
│ Current scope: personal     Suggested scope: private         │
│ Why surfaced: repeated correction + routing-first evidence   │
│ [Open details] [Reclassify] [Rescope] [Quarantine]           │
└──────────────────────────────────────────────────────────────┘

Details panel:
- provenance excerpt
- route trace references
- policy decision receipts
- user corrections
- decay status (suppressed / active)
```

**Walkthrough:** when a classification is wrong, the item enters this queue, review suppresses decay, the user or authorized agent issues reclassify/rescope, EC writes the change, and dependent edges/receipts/caches are invalidated according to the repair rules below.


```ts
export const KnowledgeReviewItemKindSchema = z.enum([
  "sensitivity_label_review",
  "misclassification_candidate",
  "rescope_review",
  "governance_migration_review",
]);

export const KnowledgeReviewQueueItemSchemaV2 = z.object({
  review_item_id: z.string().max(120),
  kind: KnowledgeReviewItemKindSchema,
  node_id: z.string().max(120),
  status: z.enum(["open", "in_progress", "resolved", "dismissed"]),
  reason_codes: z.array(z.string().max(80)).default([]),
  created_at: z.string().datetime(),
  schema_version: z.literal(2),
});

export const ResolveSensitivityLabelReviewItemCommandSchemaV3 = z.object({
  review_item_id: z.string().max(120),
  add_tags: z.array(SensitivityTagSchema).default([]),
  remove_tags: z.array(SensitivityTagSchema).default([]),
  add_findings: z.array(SensitivityFindingCodeSchema).default([]),
  remove_findings: z.array(SensitivityFindingCodeSchema).default([]),
  require_privilege_review: z.boolean().optional(),
  reviewer_note: z.string().max(500).optional(),
  schema_version: z.literal(3),
}).superRefine((value, ctx) => {
  const overlappingTags = value.add_tags.filter((tag) => value.remove_tags.includes(tag));
  const overlappingFindings = value.add_findings.filter((finding) => value.remove_findings.includes(finding));

  if (overlappingTags.length > 0) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: `The same tag cannot appear in add_tags and remove_tags: ${overlappingTags.join(", ")}`,
      path: ["add_tags"],
    });
  }

  if (overlappingFindings.length > 0) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: `The same finding cannot appear in add_findings and remove_findings: ${overlappingFindings.join(", ")}`,
      path: ["add_findings"],
    });
  }
});

export const QuarantineReviewItemCommandSchema = z.object({
  review_item_id: z.string().max(120),
  reason_codes: z.array(z.string().max(80)).default([]),
  reviewer_note: z.string().max(500).optional(),
  schema_version: z.literal(1),
});

export const MergeTombstoneNodesCommandSchema = z.object({
  surviving_node_id: z.string().max(120),
  tombstoned_node_id: z.string().max(120),
  reviewer_note: z.string().max(500).optional(),
  schema_version: z.literal(1),
});
```

### 3.2 Reclassify and rescope

```ts
export const CanonicalNodeKindSchema = z.enum([
  "world_entity",
  "procedure",
  "standing_procedure",
  "goal",
  "obligation",
  "domain_concept",
  "memory_directive",
  "work_product",
  "execution_trace",
  "application",
]);

export const ReclassifyPayloadSchemaV2 = z.object({
  target_node_kind: CanonicalNodeKindSchema,
  payload_fragment: z.record(z.string(), z.unknown()).default({}),
  preserve_node_id_if_compatible: z.boolean().default(true),
  reviewer_note: z.string().max(500).optional(),
  schema_version: z.literal(2),
});

export const RescopePayloadSchemaV2 = z.object({
  new_scope: NodeScopeSchema,
  scope_inference_basis: z.enum([
    "matter_linked",
    "domain_profile",
    "personal_surface",
    "user_override",
    "default",
  ]),
  principal_id: z.string().max(120).optional(),
  matter_entity_id: z.string().max(120).optional(),
  add_edges: z.array(z.object({
    target_id: z.string().max(120),
    relation_type: z.string().max(80),
  })).default([]),
  remove_edges: z.array(z.object({
    target_id: z.string().max(120),
    relation_type: z.string().max(80),
  })).default([]),
  reviewer_note: z.string().max(500).optional(),
  schema_version: z.literal(2),
});
```

**Normative invalidation rules**
- If node kind can change in place, preserve node id.
- If not, create a new node id, tombstone the old node, migrate compatible edges, and emit a durable `entity.reclassified` receipt.
- In all reclassify/rescope cases, invalidate embeddings, FTS, delivery caches, Matrix references, route traces, and any pending packet manifests involving the node.

### 3.3 Decay suppression during review

While a node is in `misclassification_candidate` or `rescope_review`, confidence decay SHALL pause until review resolves.

---

### Schema consume-path map

| Schema | Consumed by | When | Consume path |
|---|---|---|---|
| `KnowledgeReviewItemKindSchema` | Consumed by Knowledge Review Queue surfaces, EC repair commands, DOC72/DOC1 write-gate repair flows, and invalidation/repair jobs | review queue population, user review, reclassify/rescope, quarantine, and merge/tombstone actions | PropA §3, DOC20 Knowledge Manager, EC command routes, DOC72/DOC1 governance seams |
| `KnowledgeReviewQueueItemSchemaV2` | Consumed by Knowledge Review Queue surfaces, EC repair commands, DOC72/DOC1 write-gate repair flows, and invalidation/repair jobs | review queue population, user review, reclassify/rescope, quarantine, and merge/tombstone actions | PropA §3, DOC20 Knowledge Manager, EC command routes, DOC72/DOC1 governance seams |
| `ResolveSensitivityLabelReviewItemCommandSchemaV3` | Consumed by Knowledge Review Queue surfaces, EC repair commands, DOC72/DOC1 write-gate repair flows, and invalidation/repair jobs | review queue population, user review, reclassify/rescope, quarantine, and merge/tombstone actions | PropA §3, DOC20 Knowledge Manager, EC command routes, DOC72/DOC1 governance seams |
| `QuarantineReviewItemCommandSchema` | Consumed by Knowledge Review Queue surfaces, EC repair commands, DOC72/DOC1 write-gate repair flows, and invalidation/repair jobs | review queue population, user review, reclassify/rescope, quarantine, and merge/tombstone actions | PropA §3, DOC20 Knowledge Manager, EC command routes, DOC72/DOC1 governance seams |
| `MergeTombstoneNodesCommandSchema` | Consumed by Knowledge Review Queue surfaces, EC repair commands, DOC72/DOC1 write-gate repair flows, and invalidation/repair jobs | review queue population, user review, reclassify/rescope, quarantine, and merge/tombstone actions | PropA §3, DOC20 Knowledge Manager, EC command routes, DOC72/DOC1 governance seams |
| `CanonicalNodeKindSchema` | Consumed by Knowledge Review Queue surfaces, EC repair commands, DOC72/DOC1 write-gate repair flows, and invalidation/repair jobs | review queue population, user review, reclassify/rescope, quarantine, and merge/tombstone actions | PropA §3, DOC20 Knowledge Manager, EC command routes, DOC72/DOC1 governance seams |
| `ReclassifyPayloadSchemaV2` | Consumed by Knowledge Review Queue surfaces, EC repair commands, DOC72/DOC1 write-gate repair flows, and invalidation/repair jobs | review queue population, user review, reclassify/rescope, quarantine, and merge/tombstone actions | PropA §3, DOC20 Knowledge Manager, EC command routes, DOC72/DOC1 governance seams |
| `RescopePayloadSchemaV2` | Consumed by Knowledge Review Queue surfaces, EC repair commands, DOC72/DOC1 write-gate repair flows, and invalidation/repair jobs | review queue population, user review, reclassify/rescope, quarantine, and merge/tombstone actions | PropA §3, DOC20 Knowledge Manager, EC command routes, DOC72/DOC1 governance seams |

## 4. Canonical node payload contracts for extraction

**Why these payload contracts exist**

Extraction is only useful if what comes out of the model lines up with what the graph can actually validate and what delivery/rendering can later consume. These payload fragments are the bridge between the extraction prompt world and the canonical DOC72 node world: they constrain what the extractor is allowed to propose before the validator decides whether that proposal becomes durable graph truth.


**Integrates into:** DOC72 node payload contracts and validator registry, DOC23 extraction-task outputs, DOC24/KDA rendering consumers, DOC1 memory-directive adoption.

**R6.2 changes from R6.1:** No contract changes. This section now explains why extraction candidates are typed by canonical node payload contract and how those fragments align to later graph validation and delivery rendering.

This section defines the typed payload fragments that extraction writes are allowed to produce before they enter DOC72’s canonical graph contracts. It exists so extraction, validation, and delivery all share the same node-shape expectations instead of depending on loosely structured JSON that every downstream subsystem interprets differently.


```ts
export const WorldEntityPayloadFragmentSchema = z.object({
  entity_type: z.string().max(80).optional(),
  aliases: z.array(z.string().max(120)).default([]),
});

export const ProcedurePayloadFragmentSchema = z.object({
  semantic_intent: z.string().max(500),
  constraints: z.array(z.string().max(240)).default([]),
});

export const StandingProcedurePayloadFragmentSchema = z.object({
  trigger_summary: z.string().max(240),
  procedure_ref: z.string().max(120).optional(),
});

export const GoalPayloadFragmentSchema = z.object({
  goal_summary: z.string().max(240),
  target_state: z.string().max(240).optional(),
});

export const ObligationPayloadFragmentSchema = z.object({
  deadline: z.string().max(80).optional(),
  obligee: z.string().max(200).optional(),
});

export const DomainConceptPayloadFragmentSchema = z.object({
  concept_summary: z.string().max(500),
  domain_area: z.string().max(120).optional(),
});

export const MemoryDirectivePayloadFragmentSchema = z.object({
  directive_type: z.string().max(80),
  directive_value: z.string().max(500),
});

export const WorkProductPayloadFragmentSchema = z.object({
  work_product_type: z.string().max(80),
  document_ref: z.string().max(240).optional(),
});

export const ExecutionTracePayloadFragmentSchema = z.object({
  trace_summary: z.string().max(500),
  trace_ref: z.string().max(240).optional(),
});

export const ApplicationPayloadFragmentSchema = z.object({
  app_name: z.string().max(120),
  bundle_id: z.string().max(120).optional(),
  capability_notes: z.array(z.string().max(240)).default([]),
});

export const NodePayloadCandidateUnionSchemaV3 = z.discriminatedUnion("node_kind", [
  z.object({ node_kind: z.literal("world_entity"), payload_fragment: WorldEntityPayloadFragmentSchema }),
  z.object({ node_kind: z.literal("procedure"), payload_fragment: ProcedurePayloadFragmentSchema }),
  z.object({ node_kind: z.literal("standing_procedure"), payload_fragment: StandingProcedurePayloadFragmentSchema }),
  z.object({ node_kind: z.literal("goal"), payload_fragment: GoalPayloadFragmentSchema }),
  z.object({ node_kind: z.literal("obligation"), payload_fragment: ObligationPayloadFragmentSchema }),
  z.object({ node_kind: z.literal("domain_concept"), payload_fragment: DomainConceptPayloadFragmentSchema }),
  z.object({ node_kind: z.literal("memory_directive"), payload_fragment: MemoryDirectivePayloadFragmentSchema }),
  z.object({ node_kind: z.literal("work_product"), payload_fragment: WorkProductPayloadFragmentSchema }),
  z.object({ node_kind: z.literal("execution_trace"), payload_fragment: ExecutionTracePayloadFragmentSchema }),
  z.object({ node_kind: z.literal("application"), payload_fragment: ApplicationPayloadFragmentSchema }),
]);

export const ExtractionCandidateSchemaV3 = z.object({
  node_kind: CanonicalNodeKindSchema,
  canonical_name: z.string().max(240),
  context_note: z.string().max(1000).optional(),
  typed_payload: NodePayloadCandidateUnionSchemaV3,
  proposed_edges: z.array(z.object({
    target_node_name: z.string().max(240),
    relation_type: z.string().max(80),
  })).default([]),
  provenance_refs: z.array(z.string().max(240)).default([]),
  schema_version: z.literal(3),
}).superRefine((value, ctx) => {
  if (value.node_kind !== value.typed_payload.node_kind) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: `ExtractionCandidateSchemaV3 node_kind (${value.node_kind}) must equal typed_payload.node_kind (${value.typed_payload.node_kind})`,
      path: ["typed_payload"],
    });
  }
});

export const ExtractionCandidateBatchSchema = z.object({
  candidates: z.array(ExtractionCandidateSchemaV3).default([]),
  schema_version: z.literal(1),
});
```

**Normative rules**
- Every extracted candidate SHALL use `ExtractionCandidateSchemaV3`.
- `context_note` is required whenever the source text contains context needed to preserve downstream classifier meaning.
- Coding agents SHALL NOT emit untyped `record<string, unknown>` payloads for canonical node kinds.

### Schema consume-path map

| Schema | Consumed by | When | Consume path |
|---|---|---|---|
| `WorldEntityPayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `ProcedurePayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `StandingProcedurePayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `GoalPayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `ObligationPayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `DomainConceptPayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `MemoryDirectivePayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `WorkProductPayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `ExecutionTracePayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `ApplicationPayloadFragmentSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `NodePayloadCandidateUnionSchemaV3` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `ExtractionCandidateSchemaV3` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |
| `ExtractionCandidateBatchSchema` | Consumed by extraction-task outputs, DOC72 payload validation, rendering consumers, and later graph-write commands | deep extraction, payload validation, graph write, and downstream delivery/rendering | PropA §4, DOC72 validator registry, DOC24/KDA rendering consumers |

## 5. Prompt registry, prompt contracts, and prompt bodies

**What is frozen versus optimizable**

In this architecture, the contract is the durable promise: what the prompt is for, what it takes in, and what it must return. The body is the operational wording sent to the model. That split matters because self-review and DSPy are allowed to improve bodies, but they are not allowed to quietly change schemas, enums, or semantic obligations under the coding agent's feet.


**Integrates into:** DOC23 prompt-governed task runtime, EC Core canonical truth manifest, Prompt Lab / DOC17 reference surfaces, self-review and DSPy task bindings.

**R6.2 changes from R6.1:** No contract changes. This section now restores the explanation of why prompt contracts and prompt bodies are split, what is frozen versus optimizable, and how the canonical registry prevents drift.

This section defines the canonical prompt registry for extraction, classification, review, synthesis, validation, and human gate tasks. It exists to keep semantic contracts stable while still allowing carefully governed prompt-body iteration, and to make prompt use auditable enough that self-improvement can compare like with like.


### 5.1 Canonical prompt IDs

```ts
export const CanonicalPromptIdSchemaV3 = z.enum([
  "P0_master_extraction",
  "P1_privilege_classifier",
  "P2_employment_vs_personal",
  "P3_personal_info_classifier",
  "P4_data_export",
  "P5_independent_reevaluation",
  "P6_root_cause_diagnosis",
  "P7_synthesis_proposal",
  "P8_findings_report",
  "P9_adversarial_review",
  "P10_implementation_branch",
  "P11_validation_canary",
  "P12_human_review_gate",
]);
```

### 5.2 Prompt contract/body split

```ts
export const PromptContractRegistryEntrySchemaV3 = z.object({
  prompt_id: CanonicalPromptIdSchemaV3,
  lifecycle_role: z.enum([
    "extraction",
    "classifier",
    "self_review",
    "validation",
    "human_gate",
  ]),
  input_schema_ref: z.string().max(200),
  output_schema_ref: z.string().max(200),
  contract_description: z.string().max(1000),
  contract_hash: z.string().length(64),
  dspy_eligible: z.boolean().default(false),
  required_for_task_types: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(3),
});

export const PromptBodyRegistryEntrySchemaV2 = z.object({
  prompt_id: CanonicalPromptIdSchemaV3,
  canonical_body_markdown: z.string().min(1),
  current_body_hash: z.string().length(64),
  optimizer_source: z.enum([
    "human_authored",
    "dspy_candidate",
    "merged_synthesis",
  ]),
  active: z.boolean().default(true),
  schema_version: z.literal(2),
});

export const PromptChangeLogEntrySchema = z.object({
  change_id: z.string().max(120),
  prompt_id: CanonicalPromptIdSchemaV3,
  prior_body_hash: z.string().length(64),
  new_body_hash: z.string().length(64),
  cause: z.enum([
    "manual_edit",
    "dspy_candidate_promoted",
    "rollback",
    "registry_normalization",
  ]),
  linked_run_id: z.string().max(120).optional(),
  changed_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const SelfReviewPromptModuleBindingSchemaV2 = z.object({
  task_template_id: z.string().max(120),
  module_id: z.string().max(120),
  prompt_id: CanonicalPromptIdSchemaV3,
  required_for_task_types: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(2),
});

export const PromptRegistryCompletenessManifestSchema = z.object({
  required_prompt_ids: z.array(CanonicalPromptIdSchemaV3),
  registry_prompt_ids: z.array(CanonicalPromptIdSchemaV3),
  missing_prompt_ids: z.array(CanonicalPromptIdSchemaV3).default([]),
  extra_prompt_ids: z.array(CanonicalPromptIdSchemaV3).default([]),
  generated_at: z.string().datetime(),
  schema_version: z.literal(1),
});
```

**Normative rules**
1. Contract and body are separate artifacts.
2. DSPy may optimize prompt bodies only; it SHALL NOT mutate contract schemas or enum semantics.
3. CI SHALL fail if any required prompt id lacks both a contract entry and an active body entry.
4. CI SHALL validate that prompt body enum tokens match referenced output schemas.
5. Provider-native hidden reasoning MAY be used by a model/runtime internally, but ELNOR SHALL NOT persist or parse free-form scratchpad artifacts into durable truth.

### 5.3 Baseline prompt bodies

The following are the baseline canonical bodies for build initialization. They are operationally sufficient but remain optimizable where eligible.

#### P0 — master extraction

```text
You are the master extraction model for ELNOR's DOC72 knowledge graph.

Your task:
1. Read the observation content plus source metadata.
2. Extract only durable, useful knowledge that belongs in the graph.
3. Prefer precise, typed output over exhaustive output.
4. Do not invent facts, relationships, or node kinds.
5. Preserve enough context in `context_note` for later sensitivity classification and review.
6. Explicitly avoid extracting transient fluff, superficial mentions, or purely conversational filler.

Hard rules:
- Output strict JSON only.
- Use only canonical node kinds.
- If content is ambiguous, lower confidence and explain why in the allowed rationale field.
- Do not collapse personal and work contexts if both are present.
- Preserve internal-firm or matter-linked context when it materially affects meaning.
- Prefer fewer correct nodes over many speculative nodes.
- Do not emit raw secrets or credentials into summaries; note their presence structurally.

Expected output:
- candidate nodes
- candidate edges
- extraction rationale (bounded)
- context_note
- red_flags / uncertainty markers
```

#### P1 — privilege classifier

```text
You are classifying legal privilege and adjacent sensitivity for ELNOR.

Choose exactly one primary privilege result:
- attorney_client_privileged
- work_product
- firm_gc_privileged
- privilege_uncertain
- not_privileged

You may also emit structured findings:
- joint_defense_or_common_interest
- third_party_agent_exception
- adversary_communication
- internal_investigation
- legal_hold_or_preservation

Rules:
- Favor caution over under-labeling.
- Distinguish attorney-client privilege from work product.
- Do not treat every lawyer-involved communication as privileged.
- Consider whether a third party is acting as an agent preserving privilege.
- If the evidence is mixed, use `privilege_uncertain` and set review required.
- Output strict JSON only.
```

#### P2 — employment vs personal classifier

```text
Classify the observation as:
- work_related
- personal
- ambiguous

Rules:
- Use source context, participants, folders, and content together.
- A personal account discussing firm or matter work may still be work_related.
- If both lanes are materially present, return ambiguous and explain why.
- Output strict JSON only.
```

#### P3 — personal information classifier

```text
Identify whether the observation contains personal-sensitive content.

Possible tag recommendations:
- personal_private
- financial_personal
- health_personal
- contains_credentials
- contains_pii_third_party

Possible findings:
- identity_document
- personal_contact
- personal_location
- family_or_dependent
- minor_indicator

Rules:
- Do not over-tag routine organizational details.
- Mark credentials separately from general personal info.
- Use third-party PII when the sensitive subject is not the user.
- Output strict JSON only.
```

#### P4 — data export

```text
Prepare a typed self-review export packet from the provided trajectory bundles, route traces, node writes, corrections, and signals.

Rules:
- Do not diagnose.
- Do not propose solutions.
- Normalize all references.
- Preserve evidence completeness status and contamination state.
- Output strict JSON only.
```

#### P5 — independent reevaluation

```text
Reevaluate the raw or redacted observation package independently.

Rules:
- You are blinded to the prior written node payloads.
- Re-extract what should have been written.
- Identify missed, unnecessary, and misclassified outputs.
- Do not read or assume the previous diagnosis.
- Output strict JSON only.
```

#### P6 — root cause diagnosis

```text
Given exported evidence, determine the most likely root causes.

Available root causes:
- prompt_gap
- threshold_miscalibration
- schema_gap
- source_rule_gap
- delivery_or_routing_gap
- noisy_feedback_signal
- human_label_issue
- domain_profile_gap
- novelty_gate_failure
- classifier_prompt_gap
- ambiguity
- redaction_upstream

Rules:
- Respect evidence completeness ceiling.
- Prefer routing/delivery explanations before prompt blame when route traces support that.
- Multi-cause diagnosis is allowed.
- Output strict JSON only.
```

#### P7 — synthesis proposal

```text
Synthesize the diagnosis and propose concrete changes.

Rules:
- Proposals must target one of: prompt body, threshold profile, source rule, classifier config, schema, routing config.
- Do not mutate prompt contracts or canonical enums.
- Specify expected effect and safety risks.
- Output strict JSON only.
```

#### P8 — findings report

```text
Write a concise human-readable findings summary from typed review artifacts.

Rules:
- This is a side artifact only.
- Do not add new claims beyond the typed evidence.
- Highlight confidence ceilings, contamination, and rollback recommendations.
```

#### P9 — adversarial review

```text
Critique the proposed diagnosis and proposals.

Rules:
- Attack weak evidence, confounding variables, overfit risk, contamination, and portability assumptions.
- You may disagree completely.
- Output strict JSON only.
```

#### P10 — implementation branch

```text
Convert approved typed proposals into a concrete implementation patch plan.

Rules:
- No code mutation happens here.
- Produce files to edit, schema names, migration steps, tests, and rollback notes.
- Output strict JSON only.
```

#### P11 — validation canary

```text
Evaluate the candidate change against the assigned validation or canary cohort.

Rules:
- Compare against baseline.
- Report weighted metrics, safety floors, confidence intervals, and whether rollback is required.
- Output strict JSON only.
```

#### P12 — human review gate

```text
Summarize the candidate, evidence, risks, and decision options for the human approver.

Rules:
- Present approve / reject / approve-with-conditions options.
- Include why the system believes this is safe or unsafe.
- Output strict JSON only.
```

---

### Schema consume-path map

| Schema | Consumed by | When | Consume path |
|---|---|---|---|
| `CanonicalPromptIdSchemaV3` | Consumed by DOC23 tasks, self-review, DSPy, validation/canary, the canonical truth manifest, and prompt-registry inspectors | task registration, prompt execution, optimization, rollback, and audit | PropA §5, DOC23 runtime, EC manifest/CI checks, self-review surfaces |
| `PromptContractRegistryEntrySchemaV3` | Consumed by DOC23 tasks, self-review, DSPy, validation/canary, the canonical truth manifest, and prompt-registry inspectors | task registration, prompt execution, optimization, rollback, and audit | PropA §5, DOC23 runtime, EC manifest/CI checks, self-review surfaces |
| `PromptBodyRegistryEntrySchemaV2` | Consumed by DOC23 tasks, self-review, DSPy, validation/canary, the canonical truth manifest, and prompt-registry inspectors | task registration, prompt execution, optimization, rollback, and audit | PropA §5, DOC23 runtime, EC manifest/CI checks, self-review surfaces |
| `PromptChangeLogEntrySchema` | Consumed by DOC23 tasks, self-review, DSPy, validation/canary, the canonical truth manifest, and prompt-registry inspectors | task registration, prompt execution, optimization, rollback, and audit | PropA §5, DOC23 runtime, EC manifest/CI checks, self-review surfaces |
| `SelfReviewPromptModuleBindingSchemaV2` | Consumed by DOC23 tasks, self-review, DSPy, validation/canary, the canonical truth manifest, and prompt-registry inspectors | task registration, prompt execution, optimization, rollback, and audit | PropA §5, DOC23 runtime, EC manifest/CI checks, self-review surfaces |
| `PromptRegistryCompletenessManifestSchema` | Consumed by DOC23 tasks, self-review, DSPy, validation/canary, the canonical truth manifest, and prompt-registry inspectors | task registration, prompt execution, optimization, rollback, and audit | PropA §5, DOC23 runtime, EC manifest/CI checks, self-review surfaces |

## 6. Self-review architecture

This section defines the governed evidence-and-diagnosis loop used to improve the knowledge pipeline over time. It exists so PropA can ask what actually went wrong, separate extraction failures from routing/delivery or policy failures, and only promote changes after blinding, adversarial challenge, validation, and human review. The wireframe below shows the user-facing run inspector for that loop; the schemas that follow define the artifacts, states, and validation controls that make it real.

**Read-surface wireframe — Self-Review Run Inspector**

```text
Self-Review
────────────────────────────────────────────────────────────────
Run state: [ nightly_review_2026_04_15 ]   Status: blinded_reevaluation
Exposure class: local_only
Target surfaces: email, notes, browser
Signals complete: 92%      Contamination risk: low

Pipeline
  1. Evidence export                   ✓
  2. Blinded reevaluation              ✓
  3. Root-cause diagnosis              • in progress
  4. Adversarial review                ○ queued
  5. Synthesis proposal                ○ pending
  6. Validation / canary               ○ pending
  7. Human review gate                 ○ pending

Sample family
  Extracted samples        42
  Missed observations       8
  Routing-first failures    5
  Redacted samples         11

[Open evidence bundle] [Open disagreement records] [Cancel run]
```

**How it works end to end:** self-review exports evidence from written nodes and pre-write misses, reruns blinded evaluation, diagnoses the likely cause, challenges that diagnosis with adversarial review, and only then proposes changes for validation/canary. It is meant to improve the knowledge pipeline with evidence, not just with plausible explanations.


**Self-review in plain language**

Self-review is the governed mechanism that asks: “What went wrong in the knowledge pipeline, what evidence proves that, and what change would actually improve it?” It does not simply look at bad outcomes and blame the extraction prompt. It gathers extracted and missed samples, route/delivery traces, policy receipts, user corrections, and contamination state, then forces blinded reevaluation and adversarial challenge before any proposal reaches validation or human review.


**Integrates into:** DOC23 task-graph templates and module bindings, DOC8 / Matrix diagnostic signal obligations, DOC20 review surfaces, EC Core execution-profile and budget consumers.

**R6.2 changes from R6.1:** No contract changes. This section now restores the end-to-end explanation of what self-review is, why it exists, and how evidence, blinding, diagnosis, adversarial review, and validation fit together.

This section defines the governed evidence loop that lets PropA improve the knowledge pipeline without turning learning into an opaque autopilot. It exists to gather the right evidence, separate extraction problems from routing or delivery problems, run blinded and adversarial review, and produce proposals that are validated before any promotion.


### 6.1 Core run state

```ts
export const SelfReviewRunStateSchema = z.enum([
  "queued",
  "exported",
  "reevaluated",
  "diagnosed",
  "adversarial_reviewed",
  "synthesized",
  "validated",
  "canary_active",
  "promoted",
  "reverted",
  "failed_data_export",
  "implementation_pending_manual",
  "canceled",
]);
```

### 6.2 Evidence, contamination, and sampling

```ts
export const EvidenceCompletenessSchema = z.object({
  completeness: z.enum(["full", "partial_redacted", "partial_missing", "minimal"]),
  missing_reasons: z.array(z.enum([
    "local_only_restriction",
    "artifact_expired",
    "source_revoked",
    "retention_gc",
  ])).default([]),
  confidence_ceiling: z.number().min(0).max(1).default(1),
  schema_version: z.literal(1),
});

export const ModelFingerprintSchema = z.object({
  provider: z.string().max(80),
  model_id: z.string().max(120),
  model_revision: z.string().max(120).optional(),
  tokenizer_revision: z.string().max(120).optional(),
  sampled_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const DataContaminationStateSchema = z.enum([
  "clean",
  "contaminated",
  "quarantined",
]);

export const ContaminationPropagationRecordSchema = z.object({
  record_id: z.string().max(120),
  source_artifact_ref: z.string().max(240),
  contamination_state: DataContaminationStateSchema,
  reason_codes: z.array(z.string().max(80)).default([]),
  propagated_to_refs: z.array(z.string().max(240)).default([]),
  schema_version: z.literal(1),
});

export const SelfReviewSignalCompletenessSchema = z.object({
  include_pre_write_misses: z.boolean().default(true),
  include_false_negative_at_delivery_signals: z.boolean().default(true),
  include_delivery_route_failures: z.boolean().default(true),
  include_policy_block_events: z.boolean().default(true),
  schema_version: z.literal(1),
});
```

### 6.3 Extraction samples and pre-write misses

```ts
export const RoutingFirstDiagnosisInputSchema = z.object({
  has_routing_failure_artifacts: z.boolean().default(false),
  has_delivery_failure_artifacts: z.boolean().default(false),
  route_trace_refs: z.array(z.string().max(240)).default([]),
  delivery_trace_refs: z.array(z.string().max(240)).default([]),
  routing_failure_event_ids: z.array(z.string().max(120)).default([]),
  policy_decision_receipt_refs: z.array(z.string().max(240)).default([]),
  override_receipt_refs: z.array(z.string().max(240)).default([]),
  schema_version: z.literal(1),
});

export const SelfReviewExtractionSampleSchemaV6 = z.object({
  observation_id: z.string().max(120),
  source_surface: z.string().max(80),
  extraction_model_fingerprint: ModelFingerprintSchema.optional(),
  extraction_prompt_id: CanonicalPromptIdSchemaV3.optional(),
  extraction_prompt_hash: z.string().length(64).optional(),
  classifier_prompt_hashes: z.array(z.string().length(64)).default([]),
  source_rule_ids: z.array(z.string().max(120)).default([]),
  classification_state: SensitivityClassificationStateSchema,
  classifier_results_ref: z.string().max(240).optional(),
  raw_content_ref: z.string().max(240).optional(),
  raw_content_excerpt: z.string().max(8000).optional(),
  redacted_content_ref: z.string().max(240).optional(),
  is_redacted: z.boolean().default(false),
  written_node_ids: z.array(z.string().max(120)).default([]),
  node_payload_at_write_ref: z.string().max(240).optional(),
  node_payload_at_write_excerpt: z.string().max(4000).optional(),
  final_node_payload_refs: z.array(z.string().max(240)).default([]),
  trajectory_bundle_refs: z.array(z.string().max(240)).default([]),
  route_trace_refs: z.array(z.string().max(240)).default([]),
  delivery_trace_refs: z.array(z.string().max(240)).default([]),
  routing_failure_event_ids: z.array(z.string().max(120)).default([]),
  policy_decision_receipt_refs: z.array(z.string().max(240)).default([]),
  override_receipt_refs: z.array(z.string().max(240)).default([]),
  explicit_corrections_ref: z.string().max(240).optional(),
  user_edit_diff_ref: z.string().max(240).optional(),
  user_correction_refs: z.array(z.string().max(240)).default([]),
  friction_event_refs: z.array(z.string().max(240)).default([]),
  false_negative_at_delivery_signal_ids: z.array(z.string().max(120)).default([]),
  review_exposure_class: z.enum(["local_only", "cloud_permitted", "blocked"]),
  evidence_completeness: EvidenceCompletenessSchema,
  contamination_state: DataContaminationStateSchema.default("clean"),
  routing_first_inputs: RoutingFirstDiagnosisInputSchema,
  schema_version: z.literal(6),
});

export const PreWriteMissStageSchema = z.enum([
  "significance_gate_skip",
  "source_rule_skip",
  "collection_policy_block",
  "visibility_block",
  "visibility_deferred_no_local_model",
  "write_gate_reject",
]);

export const SelfReviewMissedObservationSampleSchemaV2 = z.object({
  observation_id: z.string().max(120),
  source_surface: z.string().max(80),
  missed_stage: PreWriteMissStageSchema,
  reason_codes: z.array(z.string().max(80)).default([]),
  source_rule_ids: z.array(z.string().max(120)).default([]),
  provisional_tags: z.array(SensitivityTagSchema).default([]),
  visibility_class: ModelVisibilityClassSchema.optional(),
  significance_score: z.number().min(0).max(1).optional(),
  manual_recovery_ref: z.string().max(240).optional(),
  later_extracted_node_ids: z.array(z.string().max(120)).default([]),
  sampled_at: z.string().datetime(),
  schema_version: z.literal(2),
});

export const DeliveryFalseNegativeSignalSchemaV2 = z.object({
  signal_id: z.string().max(120),
  node_id: z.string().max(120),
  route_trace_ref: z.string().max(240).optional(),
  delivery_trace_ref: z.string().max(240).optional(),
  attribution_kind: z.enum(["knowledge_miss", "delivery_miss", "mixed_uncertain"]),
  outcome_summary: z.string().max(500),
  schema_version: z.literal(2),
});

export const SelfReviewDataExportSchemaV6 = z.object({
  export_id: z.string().max(120),
  extraction_samples: z.array(SelfReviewExtractionSampleSchemaV6).default([]),
  missed_observation_samples: z.array(SelfReviewMissedObservationSampleSchemaV2).default([]),
  false_negative_at_delivery_signals: z.array(DeliveryFalseNegativeSignalSchemaV2).default([]),
  signal_completeness: SelfReviewSignalCompletenessSchema,
  review_exposure_class: z.enum(["local_only", "cloud_permitted", "blocked"]),
  schema_version: z.literal(6),
});
```

**Normative rules**
- Export SHALL include both written samples and pre-write misses.
- `review_exposure_class` SHALL be computed from the most restrictive sample and SHALL NOT be user-settable.
- Matrix or routing failures SHALL only enter extraction review exports if attribution remains knowledge-bound or the routing-first diagnosis contract requires them.
- If `is_redacted = true`, the diagnosis layer SHALL cap confidence and SHALL NOT assert extraction failure solely from redacted text.
- `routing_failure_event_ids`, `policy_decision_receipt_refs`, and `routing_first_inputs` SHALL be present whenever route/delivery failure evidence exists.

### 6.4 Review independence and blinding

```ts
export const ReviewIndependencePolicySchema = z.object({
  reevaluation_must_be_blinded: z.boolean().default(true),
  hide_original_written_payload_from_reevaluator: z.boolean().default(true),
  require_distinct_model_family_from_original_extractor: z.boolean().default(true),
  adversarial_review_min_distinct_model_families: z.number().int().min(2).default(2),
  require_human_escalation_on_split_verdict: z.boolean().default(true),
  schema_version: z.literal(1),
});

export const BlindedReevaluationPackageSchema = z.object({
  package_id: z.string().max(120),
  observation_id: z.string().max(120),
  source_content_variant_ref: z.string().max(240),
  hidden_fields: z.array(z.enum([
    "original_node_ids",
    "original_payloads",
    "original_tags",
    "prior_root_cause",
    "prior_proposals",
  ])).default([]),
  exposure_class: z.enum(["local_only", "cloud_permitted"]),
  schema_version: z.literal(1),
});

export const BlindedReviewDisagreementRecordSchema = z.object({
  disagreement_id: z.string().max(120),
  run_id: z.string().max(120),
  reviewer_ids: z.array(z.string().max(120)).default([]),
  disagreement_summary: z.string().max(800),
  requires_human_escalation: z.boolean().default(false),
  schema_version: z.literal(1),
});
```

### 6.5 Diagnosis taxonomy and outputs

```ts
export const RootCauseTaxonomySchema = z.enum([
  "prompt_gap",
  "threshold_miscalibration",
  "schema_gap",
  "source_rule_gap",
  "delivery_or_routing_gap",
  "noisy_feedback_signal",
  "human_label_issue",
  "domain_profile_gap",
  "novelty_gate_failure",
  "classifier_prompt_gap",
  "ambiguity",
  "redaction_upstream",
]);

export const SelfReviewDivergenceTypeSchema = z.enum([
  "false_classification",
  "missed_extraction",
  "unnecessary_extraction",
  "sensitivity_misclassification",
  "routing_or_delivery_miss",
]);

export const SelfReviewFixKindSchema = z.enum([
  "prompt_change",
  "threshold_change",
  "schema_change",
  "rule_addition",
  "config_change",
  "routing_fix",
  "delivery_fix",
  "manual_review_only",
]);

export const WeightedRootCauseSchema = z.object({
  cause: RootCauseTaxonomySchema,
  weight: z.number().min(0).max(1),
});

export const P6RootCauseOutputSchemaV4 = z.object({
  findings: z.array(z.object({
    observation_id: z.string().max(120),
    divergence_type: SelfReviewDivergenceTypeSchema,
    root_causes: z.array(WeightedRootCauseSchema).min(1).max(4),
    confidence: z.number().min(0).max(1),
    confidence_ceiling_applied: z.number().min(0).max(1),
    bounded_rationale: z.string().max(1200),
    evidence_refs: z.array(z.string().max(240)).default([]),
    proposed_fix_kind: SelfReviewFixKindSchema,
    proposed_fix_text: z.string().max(2000),
    requires_followup_review: z.boolean().default(false),
  })).default([]),
  schema_version: z.literal(4),
});

export const SelfReviewDiagnosisRecordSchemaV2 = z.object({
  diagnosis_id: z.string().max(120),
  observation_id: z.string().max(120),
  divergence_type: SelfReviewDivergenceTypeSchema,
  root_causes: z.array(WeightedRootCauseSchema).min(1).max(4),
  confidence: z.number().min(0).max(1),
  confidence_ceiling_applied: z.number().min(0).max(1),
  bounded_rationale: z.string().max(1200),
  evidence_refs: z.array(z.string().max(240)).default([]),
  proposed_fix_kind: SelfReviewFixKindSchema,
  proposed_fix_text: z.string().max(2000),
  requires_followup_review: z.boolean().default(false),
  schema_version: z.literal(2),
});
```

**Normative diagnosis rules**
1. Diagnosis confidence SHALL be capped by `EvidenceCompletenessSchema.confidence_ceiling`.
2. If `routing_failure_event_ids`, route-trace failures, or delivery-trace failures are present, the diagnosis layer SHALL classify the sample as `routing_or_delivery_miss` unless contrary evidence disproves that explanation.
3. Multi-causal diagnosis is allowed, but weights SHALL sum to approximately 1.0 and no more than 4 causes may be emitted.
4. `proposed_fix_kind` and `proposed_fix_text` are required; the self-improvement loop SHALL NOT emit diagnosis-only records with no actionable next step.

### 6.6 Task graph

The canonical self-review task graph SHALL be:

1. `m1 select_sample_bundle`
2. `m2 export_review_data`
3. `m3 independent_reevaluation`
4. `m4 diagnose_root_cause`
5. `m5 adversarial_review_fanout`
6. `m6 synthesize_proposals`
7. `m7 implementation_patch_plan`
8. `m8 validation_bundle_replay`
9. `m9 canary_assignment_or_offline_validation`
10. `m10 human_review_gate`
11. `m11 promote_or_revert`
12. `m12 archive_artifacts_and_receipts`

### 6.7 Validation and canary

```ts
export const PromptValidationModeSchema = z.enum([
  "offline_holdout_only",
  "shadow_eval_only",
  "offline_plus_shadow",
]);

export const CanaryIsolationLockSchema = z.object({
  lock_id: z.string().max(120),
  scope: z.enum(["prompt", "threshold", "source_rule", "sharing_policy", "classifier_config"]),
  target_id: z.string().max(120),
  active: z.boolean(),
  started_at: z.string().datetime(),
  expires_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const PromptCanaryAssignmentSchemaV2 = z.object({
  assignment_id: z.string().max(120),
  candidate_id: z.string().max(120),
  target_prompt_id: CanonicalPromptIdSchemaV3,
  validation_mode: PromptValidationModeSchema,
  rollout_scope: z.enum([
    "offline_only",
    "background_only",
    "selected_sources_only",
    "manual_review_only",
  ]),
  eligible_surfaces: z.array(z.string().max(80)).default([]),
  excluded_sensitivity_tags: z.array(z.string().max(80)).default([]),
  excluded_visibility_classes: z.array(ModelVisibilityClassSchema).default(["blocked", "local_only"]),
  traffic_percentage: z.number().min(0).max(1).default(0.1),
  start_at: z.string().datetime(),
  planned_end_at: z.string().datetime(),
  rollback_threshold_profile_id: z.string().max(120),
  live_kill_switch_required: z.boolean().default(false),
  schema_version: z.literal(2),
});

export const PromptCanaryMetricSchema = z.object({
  bootstrap_ci_95: z.tuple([z.number(), z.number()]),
  p_value: z.number().min(0).max(1),
  effect_size: z.number(),
  schema_version: z.literal(1),
});

export const PromptValidationRunSchema = z.object({
  validation_run_id: z.string().max(120),
  target_prompt_id: CanonicalPromptIdSchemaV3,
  validation_mode: PromptValidationModeSchema,
  baseline_body_hash: z.string().length(64),
  candidate_body_hash: z.string().length(64),
  assignment_id: z.string().max(120).optional(),
  metrics: PromptCanaryMetricSchema,
  schema_version: z.literal(1),
});
```

**Normative canary rules**
1. No live canary may run without a typed cohort assignment.
2. Only one active canary isolation lock per scope is allowed.
3. Local-only and blocked content are excluded from cloud canaries.
4. Promotion requires positive lower bound of the confidence interval **and** all safety floors satisfied.
5. Live-shadow validation is optional and target-gated; offline holdout remains the minimum required validation mode.

### Schema consume-path map

| Schema | Consumed by | When | Consume path |
|---|---|---|---|
| `SelfReviewRunStateSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `EvidenceCompletenessSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `ModelFingerprintSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `DataContaminationStateSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `ContaminationPropagationRecordSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `SelfReviewSignalCompletenessSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `RoutingFirstDiagnosisInputSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `SelfReviewExtractionSampleSchemaV6` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `PreWriteMissStageSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `SelfReviewMissedObservationSampleSchemaV2` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `DeliveryFalseNegativeSignalSchemaV2` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `SelfReviewDataExportSchemaV6` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `ReviewIndependencePolicySchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `BlindedReevaluationPackageSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `BlindedReviewDisagreementRecordSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `RootCauseTaxonomySchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `SelfReviewDivergenceTypeSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `SelfReviewFixKindSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `WeightedRootCauseSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `P6RootCauseOutputSchemaV4` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `SelfReviewDiagnosisRecordSchemaV2` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `PromptValidationModeSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `CanaryIsolationLockSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `PromptCanaryAssignmentSchemaV2` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `PromptCanaryMetricSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |
| `PromptValidationRunSchema` | Consumed by DOC23 self-review tasks, evidence export builders, adjudication/read surfaces, validation/canary workflows, and DSPy feeder logic | nightly or on-demand self-review runs, evidence export, blinded reevaluation, diagnosis, adversarial review, and canary validation | PropA §6, DOC23 task graph, DOC8/Matrix signal feeds, DOC20 review surfaces |

## 7. DSPy branch

This section defines the optional DSPy optimizer lane that can improve selected prompt bodies when frozen datasets, stable rubrics, and safe execution conditions exist. It exists to make bounded prompt optimization reproducible and measurable without replacing the broader self-review loop or weakening privacy guarantees. The wireframe below shows the operator-facing configuration surface for that lane; the schemas that follow define target eligibility, datasets, rubrics, ledgers, and promotion gates.

**Settings / operator wireframe — Self-Review > DSPy**

```text
DSPy Optimization
────────────────────────────────────────────────────────────────
DSPy is an optional optimizer lane for selected prompt bodies.
It does NOT replace the main self-review diagnosis pipeline.

Target prompt                    [▾ P0_master_extraction ]
Eligibility                      [ local-only curated dataset required ]
Dataset                          [▾ extraction_email_v1_holdout_locked ]
Rubric                           [▾ extraction_precision_weighted_v3 ]
Student model                    [▾ local_qwen_runner ]
Judge model                      [▾ local_qwen_judge ]
Run mode                         [ shadow validation only ]
Safety floor                     [ precision >= baseline ]
Human enable required            [✓]
Auto-promote                     [ ]

[Run optimizer]  [Preview candidate vs baseline]  [Open experiment ledger]
```

**What this is doing:** the DSPy branch tries to improve a bounded set of prompt bodies using frozen datasets and measurable rubrics. Any candidate still flows back through validation, canary, and human review before it can affect production behavior.


**DSPy in this system**

DSPy is not the main self-improvement engine. It is a narrow helper lane for optimizing selected prompt bodies when there is a frozen dataset, a measurable rubric, and a safe execution environment. The self-review pipeline determines whether a prompt is even a good candidate for optimization; the DSPy lane then tries to improve that prompt body under stricter reproducibility and safety rules than a normal free-form prompt tweak would have.


**Integrates into:** DOC23 `step.dspy_optimizer` runtime, EC Core execution-profile selection and cost governance, DOC20 self-review/prompt-optimization surfaces, prompt registry and validation gates from §5-§6.

**R6.2 changes from R6.1:** No contract changes. This section now restores the explanation of what DSPy is allowed to optimize in ELNOR, what it must never replace, and why the branch stays additive to the main self-review loop.

This section defines the optional DSPy optimizer lane used to improve selected prompt bodies when stable truth sets and measurable rubrics exist. It exists to provide disciplined prompt optimization under frozen datasets, explicit safety floors, and human-controlled promotion—without replacing the broader diagnosis-and-validation loop in self-review.


### 7.1 Governing posture

DSPy is additive to the self-review loop, not a replacement for it.

### 7.2 Target registry and eligibility

```ts
export const DspyTargetIdSchemaV4 = z.enum([
  "P0_master_extraction",
  "P1_privilege_classifier",
  "P2_employment_vs_personal",
  "P3_personal_info_classifier",
  "P5_independent_reevaluation",
]);

export const DspyTargetEligibilitySchemaV4 = z.object({
  target_id: DspyTargetIdSchemaV4,
  dspy_enabled_by_default: z.boolean().default(false),
  requires_explicit_user_enable: z.boolean().default(false),
  requires_local_only_dataset: z.boolean().default(false),
  requires_manual_dataset_curation: z.boolean().default(false),
  permitted_validation_modes: z.array(PromptValidationModeSchema).default(["offline_holdout_only"]),
  data_class: z.enum(["public", "internal", "privileged", "local_only"]).default("internal"),
  schema_version: z.literal(4),
}).superRefine((value, ctx) => {
  if (value.requires_explicit_user_enable && value.dspy_enabled_by_default) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: "A target requiring explicit user enable cannot also be enabled by default.",
    });
  }
});
```

**Normative target defaults**
- `P0_master_extraction`: eligible, default off
- `P1_privilege_classifier`: eligible, default off, explicit enable required, local-only dataset required, manual dataset curation required
- `P2_employment_vs_personal`: eligible, default off
- `P3_personal_info_classifier`: eligible, default off
- `P5_independent_reevaluation`: eligible, default off

### 7.3 Datasets, slices, and fingerprints

```ts
export const SensitivityDataClassSchema = z.enum([
  "public",
  "internal",
  "privileged",
  "local_only",
]);

export const DspyDatasetLifecycleSchema = z.enum([
  "created",
  "approved",
  "frozen",
  "running",
  "archived",
]);

export const DspyDatasetFingerprintSchemaV2 = z.object({
  dataset_id: z.string().max(120),
  dataset_ref: z.string().max(240),
  dataset_sha256: z.string().length(64),
  label_schema_version: z.string().max(80),
  row_count: z.number().int().nonnegative(),
  train_split_ref: z.string().max(240),
  validation_split_ref: z.string().max(240),
  locked_holdout_split_ref: z.string().max(240),
  frozen_at: z.string().datetime(),
  mutable: z.boolean().default(false),
  sensitivity_data_class: SensitivityDataClassSchema,
  lifecycle_state: DspyDatasetLifecycleSchema,
  schema_version: z.literal(2),
});

export const DspyDatasetSliceSchemaV2 = z.object({
  slice_id: z.string().max(120),
  dataset_id: z.string().max(120),
  surface: z.enum(["email", "browser", "notes", "chat", "calendar", "documents"]).optional(),
  node_kind: CanonicalNodeKindSchema.optional(),
  sensitivity_class: SensitivityDataClassSchema.optional(),
  model_family: z.string().max(80).optional(),
  row_count: z.number().int().nonnegative(),
  slice_sha256: z.string().length(64).optional(),
  schema_version: z.literal(2),
});

export const SelfReviewDatasetCatalogEntrySchema = z.object({
  dataset_id: z.string().max(120),
  title: z.string().max(240),
  sensitivity_data_class: SensitivityDataClassSchema,
  lifecycle_state: DspyDatasetLifecycleSchema,
  compatible_target_ids: z.array(DspyTargetIdSchemaV4).default([]),
  schema_version: z.literal(1),
});

export const SelfReviewDatasetCatalogReadModelSchema = z.object({
  entries: z.array(SelfReviewDatasetCatalogEntrySchema).default([]),
  generated_at: z.string().datetime(),
  schema_version: z.literal(1),
});
```

### 7.4 Rubrics, metrics, and invalidation

```ts
export const DspyRubricSchemaV4 = z.object({
  rubric_id: z.string().max(120),
  rubric_body_markdown: z.string().min(1),
  rubric_hash: z.string().length(64),
  scorer_impl_hash: z.string().length(64),
  judge_prompt_hash: z.string().length(64).optional(),
  false_positive_cost_weight: z.number().nonnegative(),
  false_negative_cost_weight: z.number().nonnegative(),
  offline_benchmark_weight: z.number().nonnegative().default(1),
  real_world_utility_weight: z.number().nonnegative().default(0),
  holdout_safety_floor_weight: z.number().nonnegative().default(1),
  safety_floor_rules: z.array(z.string().max(240)).default([]),
  schema_version: z.literal(4),
});

export const DspyScoreRecordSchemaV2 = z.object({
  score_record_id: z.string().max(120),
  run_id: z.string().max(120),
  target_id: DspyTargetIdSchemaV4,
  rubric_id: z.string().max(120),
  valid: z.boolean().default(true),
  invalidated_reason: z.string().max(240).optional(),
  metrics: PromptCanaryMetricSchema,
  schema_version: z.literal(2),
});
```

**Normative metric immutability rule**
- If a judge prompt or scorer implementation changes, all historical score records produced under the prior judge/scorer SHALL be marked invalidated and SHALL NOT be used for direct numerical comparison.
- Privilege-sensitive and personal-sensitive targets SHALL use weighted rubric terms; raw aggregate benchmark score alone is insufficient for promotion.

### 7.5 Experiment ledger

```ts
export const DspyExperimentLedgerEntrySchemaV2 = z.object({
  run_id: z.string().max(120),
  target_prompt_id: DspyTargetIdSchemaV4,
  dataset_fingerprint: DspyDatasetFingerprintSchemaV2,
  slice_ids: z.array(z.string().max(120)).default([]),
  rubric_id: z.string().max(120),
  scorer_hash: z.string().length(64),
  baseline_body_hash: z.string().length(64),
  selected_candidate_body_hash: z.string().length(64).optional(),
  validation_mode: PromptValidationModeSchema,
  validation_run_id: z.string().max(120).optional(),
  canary_assignment_id: z.string().max(120).optional(),
  student_model_fingerprint: ModelFingerprintSchema,
  judge_model_fingerprint: ModelFingerprintSchema.optional(),
  created_at: z.string().datetime(),
  schema_version: z.literal(2),
});
```

### 7.6 Runtime guardrails

```ts
export const SchemaMutationLockSchema = z.object({
  lock_id: z.string().max(120),
  active_schema_change_window: z.boolean().default(false),
  block_prompt_promotions: z.boolean().default(true),
  block_dspy_promotions: z.boolean().default(true),
  created_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const DspyModelOptionSchema = z.object({
  model_id: z.string().max(120),
  verified_local_only: z.boolean().default(false),
  supports_judge_role: z.boolean().default(false),
  supports_schema_locked_json: z.boolean().default(false),
  max_context_tokens: z.number().int().positive(),
  max_output_tokens: z.number().int().positive(),
  allowed_data_classes: z.array(SensitivityDataClassSchema).default([]),
  schema_version: z.literal(1),
});

export const DspyModelOptionsReadModelSchema = z.object({
  options: z.array(DspyModelOptionSchema).default([]),
  generated_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const DspyRunConfigSchemaV3 = z.object({
  run_id: z.string().max(120),
  target_id: DspyTargetIdSchemaV4,
  max_iterations: z.number().int().positive().default(3),
  max_run_cost_usd: z.number().positive().default(5),
  validation_mode: PromptValidationModeSchema.default("offline_holdout_only"),
  local_proxy_required: z.boolean().default(true),
  require_canary_assignment_for_live_shadow: z.boolean().default(true),
  require_schema_mutation_lock_clear: z.boolean().default(true),
  schema_version: z.literal(3),
});
```

**Normative DSPy rules**
1. All optimizer child processes SHALL go through the local API proxy defined in EC Core.
2. Raw provider credentials SHALL NOT be passed directly to child processes.
3. P1 privilege-classifier optimization is disabled by default and requires:
   - explicit human enable,
   - local-only curated dataset,
   - manual dataset curation,
   - frozen holdout split,
   - local-only student and judge models,
   - safety floor on privilege recall.
4. A DSPy run SHALL not execute while a conflicting schema or prompt registry mutation lock is active.
5. Live-shadow validation mode SHALL require a typed canary assignment and live kill switch support.

### 7.7 Required safety floors

- `P1_privilege_classifier`: privilege recall MUST NOT fall below baseline by more than 0.005 on any privileged slice.
- `P3_personal_info_classifier`: credential and identity precision floors SHALL be enforced.
- `P0_master_extraction`: hallucination rate ceiling SHALL be enforced.
- Raw score deltas alone are insufficient; statistical confidence metrics and safety floors are required.

---

### Schema consume-path map

| Schema | Consumed by | When | Consume path |
|---|---|---|---|
| `DspyTargetIdSchemaV4` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyTargetEligibilitySchemaV4` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `SensitivityDataClassSchema` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyDatasetLifecycleSchema` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyDatasetFingerprintSchemaV2` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyDatasetSliceSchemaV2` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `SelfReviewDatasetCatalogEntrySchema` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `SelfReviewDatasetCatalogReadModelSchema` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyRubricSchemaV4` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyScoreRecordSchemaV2` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyExperimentLedgerEntrySchemaV2` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `SchemaMutationLockSchema` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyModelOptionSchema` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyModelOptionsReadModelSchema` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |
| `DspyRunConfigSchemaV3` | Consumed by DOC23 DSPy runs, EC execution-profile selection, dataset/rubric management surfaces, and validation/promotion gates | optimizer setup, guarded execution, scoring, ledger writes, and candidate promotion/rejection | PropA §7, EC profile selection/cost governance, self-review and DSPy UI |

## 8. PropA-owned commands, routes, and artifact classes

**Command/route ownership note**

PropA defines the semantic payloads for privacy, review, self-review, and DSPy operations, but EC remains the route-contract owner and the sole durable writer. A coding agent should read this section as: these are the shapes the rest of the system must honor, while the canonical route inventory and read-model refresh truth live on the EC side.


**Integrates into:** EC Core route-contract registry, DOC20 settings/read surfaces, DOC21 component inventory, DOC22 page inventory, DOC20 §6.18.2 content registry.

**R6.2 changes from R6.1:** No contract changes. This section now explains which commands/routes are PropA semantic surfaces versus EC control-plane surfaces, and what durable artifact classes the UI and runtime are expected to read.

This section defines the PropA-owned command payloads, route-facing semantics, and artifact types that have to be visible in the system rather than living only in internal code. It exists to close the path from user control to durable write to read-model so privacy, review, and self-improvement features do not regress into ghost behavior.


PropA defines the command payloads. EC Core owns the route-contract registry and runtime registration.

### 8.1 Command schemas

```ts
export const UpdateSensitiveCollectionPolicyCommandSchema = z.object({
  policy: SensitiveContentCollectionPolicySchemaV2,
  expected_version: z.string().max(80).optional(),
  schema_version: z.literal(1),
});

export const UpdateExtractionVisibilityPolicyCommandSchema = z.object({
  policy_ref: z.string().max(240),
  expected_version: z.string().max(80).optional(),
  schema_version: z.literal(1),
});

export const UpdateSharingPolicyCommandSchema = z.object({
  policy_ref: z.string().max(240),
  expected_version: z.string().max(80).optional(),
  schema_version: z.literal(1),
});

export const UpsertSourceClassificationRuleCommandSchema = z.object({
  rule: SourceClassificationRuleSchemaV3,
  expected_registry_version: z.string().max(80).optional(),
  schema_version: z.literal(1),
});

export const DeleteSourceClassificationRuleCommandSchema = z.object({
  rule_id: z.string().max(120),
  expected_registry_version: z.string().max(80).optional(),
  schema_version: z.literal(1),
});

export const UpdateAutoInjectionGuardSettingsCommandSchema = z.object({
  settings: SensitiveAutoInjectionGuardSettingsSchemaV3,
  expected_version: z.string().max(80).optional(),
  schema_version: z.literal(1),
});

export const CreateOneTurnExposureOverrideCommandSchema = z.object({
  session_id: z.string().max(120),
  turn_id: z.string().max(120),
  node_ids: z.array(z.string().max(120)).default([]),
  destination: z.enum(["cloud_api", "same_machine_local_runtime"]),
  request_fingerprint: z.string().length(64),
  schema_version: z.literal(1),
});

export const CreateSelfReviewRunCommandSchema = z.object({
  sample_profile_id: z.string().max(120),
  local_only_required: z.boolean().default(false),
  schema_version: z.literal(1),
});

export const UpsertSelfReviewDatasetCommandSchema = z.object({
  dataset_id: z.string().max(120),
  dataset_ref: z.string().max(240),
  sensitivity_data_class: SensitivityDataClassSchema,
  schema_version: z.literal(1),
});

export const CreateDspyRunCommandSchema = z.object({
  target_id: DspyTargetIdSchemaV4,
  dataset_id: z.string().max(120),
  rubric_id: z.string().max(120),
  run_config: DspyRunConfigSchemaV3,
  schema_version: z.literal(1),
});
```

### 8.2 PropA artifact classes

```ts
export const PropAArtifactClassSchema = z.enum([
  "redacted_working_copy",
  "policy_decision_receipt",
  "self_review_export",
  "trajectory_bundle",
  "dspy_dataset",
  "dspy_candidate",
  "canary_artifact",
  "review_report",
]);
```

```ts
export const NodeMutationReceiptSchema = z.object({
  receipt_id: z.string().max(120),
  node_id: z.string().max(120),
  mutation_kind: z.enum(["reclassify", "rescope", "quarantine", "merge_tombstone"]),
  created_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const SelfReviewRunReceiptSchema = z.object({
  run_id: z.string().max(120),
  state: SelfReviewRunStateSchema,
  created_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const SelfReviewRunReadModelSchema = z.object({
  run_id: z.string().max(120),
  state: SelfReviewRunStateSchema,
  sample_profile_id: z.string().max(120).optional(),
  latest_artifact_refs: z.array(z.string().max(240)).default([]),
  contamination_state: DataContaminationStateSchema.default("clean"),
  created_at: z.string().datetime(),
  updated_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const DspyRunReceiptSchema = z.object({
  run_id: z.string().max(120),
  target_id: DspyTargetIdSchemaV4,
  status: z.enum(["queued", "running", "completed", "failed", "blocked"]),
  created_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const DspyCatalogEntrySchema = z.object({
  target_id: DspyTargetIdSchemaV4,
  enabled_by_default: z.boolean().default(false),
  requires_explicit_user_enable: z.boolean().default(false),
  requires_local_only_dataset: z.boolean().default(false),
  requires_manual_dataset_curation: z.boolean().default(false),
  active_rubric_ids: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});

export const DspyCatalogReadModelSchema = z.object({
  entries: z.array(DspyCatalogEntrySchema).default([]),
  generated_at: z.string().datetime(),
  schema_version: z.literal(1),
});
```

```ts
export const SensitiveCollectionPolicyReadModelSchema = z.object({
  policy: SensitiveContentCollectionPolicySchemaV2,
  version: z.string().max(80),
  updated_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const SourceRuleRegistryReadModelSchema = z.object({
  rules: z.array(SourceClassificationRuleSchemaV3).default([]),
  registry_version: z.string().max(80),
  updated_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const ExtractionVisibilityPolicyReadModelSchema = z.object({
  policy_ref: z.string().max(240),
  version: z.string().max(80),
  updated_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const SharingPolicyReadModelSchema = z.object({
  policy_ref: z.string().max(240),
  version: z.string().max(80),
  updated_at: z.string().datetime(),
  schema_version: z.literal(1),
});
```



---

### Schema consume-path map

| Schema | Consumed by | When | Consume path |
|---|---|---|---|
| `UpdateSensitiveCollectionPolicyCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `UpdateExtractionVisibilityPolicyCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `UpdateSharingPolicyCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `UpsertSourceClassificationRuleCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `DeleteSourceClassificationRuleCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `UpdateAutoInjectionGuardSettingsCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `CreateOneTurnExposureOverrideCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `CreateSelfReviewRunCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `UpsertSelfReviewDatasetCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `CreateDspyRunCommandSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `PropAArtifactClassSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `NodeMutationReceiptSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `SelfReviewRunReceiptSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `SelfReviewRunReadModelSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `DspyRunReceiptSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `DspyCatalogEntrySchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `DspyCatalogReadModelSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `SensitiveCollectionPolicyReadModelSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `SourceRuleRegistryReadModelSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `ExtractionVisibilityPolicyReadModelSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |
| `SharingPolicyReadModelSchema` | Consumed by EC route registry, DOC20 settings/read surfaces, inspectors, and artifact viewers | user setting changes, review actions, self-review/DSPy control actions, and artifact persistence | PropA §8, EC route contracts, DOC20/DOC21/DOC22 UI surfaces |

## 9. Canonical storage paths and retention classes

**Integrates into:** EC Core retention/backup policy, DOC20 §6.18.2 content registry, DOC1 archive-not-delete governance, audit/replay tooling.

**R6.2 changes from R6.1:** No contract changes. This section now adds plain-language explanation of what each artifact family stores, why retention differs by class, and how those classes support auditability without inventing extra truth stores.

This section defines where PropA-owned artifacts live on disk and how long they remain active, reviewable, or archived. It exists so review evidence, prompt optimization artifacts, redaction receipts, and policy receipts have predictable lifecycle behavior instead of being scattered across ad hoc files that no one can govern later.


### 9.1 Paths

```ts
export const PropAPaths = {
  collectionPolicy: "ELNOR_MEMORY/config/knowledge_pipeline/sensitive_collection_policy.json",
  sourceRules: "ELNOR_MEMORY/config/knowledge_pipeline/source_classification_rules.json",
  visibilityPolicy: "ELNOR_MEMORY/config/knowledge_pipeline/extraction_visibility_policy.json",
  autoInjectionGuard: "ELNOR_MEMORY/config/knowledge_pipeline/auto_injection_guard.json",

  redactionReceipts: "ELNOR_MEMORY/system/privacy/local_redaction_receipts.jsonl",
  overrideReceipts: "ELNOR_MEMORY/system/privacy/memory_exposure_overrides.jsonl",

  selfReviewExports: "ELNOR_MEMORY/system/self_review/exports/",
  selfReviewRuns: "ELNOR_MEMORY/system/self_review/runs/",
  trajectoryBundles: "ELNOR_MEMORY/system/self_review/trajectory_bundles/",
  disagreementRecords: "ELNOR_MEMORY/system/self_review/disagreements.jsonl",

  dspyDatasets: "ELNOR_MEMORY/system/dspy/datasets/",
  dspyExperiments: "ELNOR_MEMORY/system/dspy/experiments/",
  dspyRuns: "ELNOR_MEMORY/system/dspy/runs/",
  promptRegistry: "ELNOR_MEMORY/system/prompt_registry/",
};
```

### 9.2 Retention table

| Artifact class | Default TTL | Local-only required | Notes |
|---|---:|---|---|
| redacted_working_copy | 30m | yes | ephemeral helper only |
| policy_decision_receipt | 90d | no | immutable for replay and audit |
| self_review_export | 30d | yes when exposure class != cloud_permitted | archive if attached to promoted change |
| trajectory_bundle | 30d | yes when raw/redacted content present | may outlive export if bound to active run |
| dspy_dataset | until archived | depends on data class | frozen once approved |
| dspy_candidate | 30d | follows run trust class | archive promoted candidates separately |
| canary_artifact | 90d | depends on cohort | keep until rollback window closes |
| review_report | 90d | no | human-readable side artifact |

---

## 10. UI and read-surface obligations

This section defines the user-visible settings panels, inspectors, queues, and degraded/blocked states that make the PropA control layer real instead of decorative. It exists so a coding agent can map the contracts in earlier sections to concrete read surfaces and so later owner-doc integration has a clear inventory of what must appear in DOC20/DOC21/DOC22. The summary below is the top-level surface inventory for that work.

**Surface summary**

```text
Required PropA UI/read surfaces
────────────────────────────────────────────────────────────────
Settings
  - Privacy & Sharing > Content Collection
  - Privacy & Sharing > Classification Rules
  - Privacy & Sharing > Extraction Visibility
  - Privacy & Sharing > Sharing & Injection
  - Self-Review > Runs / Evidence / DSPy

Inspectors / Managers
  - Knowledge Review Queue
  - Why Blocked / Policy Decision Inspector
  - Sync Preflight / Sync Receipt Inspector
  - Prompt Registry / Experiment Ledger views

States required everywhere
  loading / empty / blocked / degraded / error / stale-data
```


**Integrates into:** DOC20 settings/read surfaces, DOC21 component inventory, DOC22 page inventory, DOC20 §6.18.2 content registry, EC Core wiring manifest generation.

**R6.2 changes from R6.1:** No contract changes. This section now restores the user-facing explanation of what surfaces must exist and what they show, rather than leaving those obligations implicit in command and artifact schemas.

This section defines what the user is actually supposed to see for privacy controls, review, DSPy, and policy inspection. It exists so a coding agent can map the contract layer to concrete settings panels, inspectors, queues, and degraded/blocked states instead of stopping at backend types.


The coding agent SHALL implement truthful UI for:
- collection policy settings,
- source-rule editor,
- visibility and privacy inspector,
- automatic-injection guard settings,
- one-turn override receipts,
- self-review runs,
- DSPy target selector / dataset selector / rubric selector,
- canary state,
- contamination and evidence-completeness indicators.

**Truth rules**
1. Display `desired` vs `effective` whenever EC can inhibit a PropA policy.
2. Any blocked, warned, redacted, or stripped decision SHALL expose a `why blocked` / `why changed` inspector path.
3. UI SHALL show when a node was not injected because it was `automatic_packet_injection` and not because the user was prohibited from sending typed text.
4. UI SHALL show when a dataset/score was invalidated because rubric/judge changes broke comparability.

---

## 11. Non-goals and explicit rejections

**Owned by:** PropA companion spec (no parent-doc integration — this section defines architectural boundaries and explicit non-goals for this companion set).

**R6.2 changes from R6.1:** No contract changes. This section now restores plain-language rationale for what PropA deliberately does not do, so implementers do not reintroduce rejected ideas through later drift.

This section records the ideas PropA explicitly refuses to turn into product obligations for this cycle. It exists to prevent future drafting or implementation from quietly resurrecting rejected second-truth stores, over-engineered privacy theater, or hidden reasoning artifacts under new names.


The next version SHALL NOT implement:
- free-form durable chain-of-thought or XML scratchpad artifacts,
- MPC/TEE-style zero-knowledge extraction proofs,
- differential-privacy noise on embeddings as a PropA requirement,
- ephemeral decryption proxy schemes for cloud work,
- OpenClaw native history scrubbing,
- a second route registry or second policy evaluator.

Provider-native hidden reasoning MAY exist internally, but ELNOR SHALL ignore/drop it and SHALL NOT persist it.

---

## 12. Coding-agent implementation order

**Owned by:** PropA companion spec (implementation guidance for the current drafting/build-prep cycle; consumed jointly with EC Core implementation order).

**R6.2 changes from R6.1:** No contract changes. This section now clarifies why the implementation sequence is ordered the way it is and what each phase is protecting against.

This section defines the order a coding agent should follow so foundational contracts are stabilized before dependent behaviors are built on top of them. It exists to prevent underwired implementations where later features are coded before the policy, artifact, and prompt foundations they rely on are truly in place.


1. Canonical contract collapse and supersession cleanup
2. Source-rule resolver + matcher tests
3. Visibility policy + fast-path + expedited local-only classification
4. Exposure-context guard + override receipts
5. Node governance + misclassification/reclassify flows
6. Prompt contract/body registry + CI
7. Self-review export / diagnosis / blinding / canary
8. DSPy dataset/rubric/ledger hardening
9. Storage paths + retention hooks
10. UI surfaces after EC Core V3 routes/read models are live



## 13. CI gates and acceptance tests

**Owned by:** PropA companion spec with cross-doc obligations into EC Core CI, DOC23 task-registration validation, and DOC21/22 wiring checks.

**R6.2 changes from R6.1:** No contract changes. This section now clarifies what each gate is proving and why those proofs are required before implementation can be called complete.

This section defines the automated checks that keep the spec honest once it leaves the drafting phase. It exists to catch prompt-registry drift, route gaps, policy-composition drift, missing read surfaces, unsafe DSPy enablement, and other failure classes that previously slipped through because the architecture sounded good in prose but was not mechanically enforced.


At minimum, CI SHALL enforce:

1. **Prompt-registry completeness**
   - every required prompt id has one contract entry and one active body entry
   - prompt body enum tokens match referenced output schemas

2. **Prompt/module binding completeness**
   - every self-review module bound in the task graph has a registered prompt binding
   - `required_for_task_types` coverage is complete for `knowledge_deep_extraction`, `sensitivity_content_classification`, `knowledge_self_review`, and `knowledge_dspy_optimization`

3. **Source-rule matcher tests**
   - exact / glob / regex / prefix / suffix / contains semantics are deterministic and normalized where configured
   - `default_tags` and `default_findings` are applied when no rule matches

4. **Exposure-context safety**
   - `user_authored_prompt` may not be used for system-resolved graph-node injection
   - override receipts are consume-once and bound to request fingerprint + nonce

5. **Fast-path and expedited classification**
   - org-safe promotion may not occur when high-risk tags/findings are present
   - expedited classification may not route to cloud

6. **Review-independence**
   - blinded reevaluation packages exclude prior payloads
   - adversarial review quorum satisfies model-family diversity or escalates

7. **DSPy reproducibility**
   - frozen dataset fingerprints are immutable
   - score records are invalidated when judge/scorer hashes change
   - canary isolation locks block concurrent conflicting promotions

8. **Retention and purge**
   - redacted working copies expire
   - retained artifacts honor local-only restrictions and TTL classes

---