DOC24_Addendum_A_BDSM_V6_5_DRAFT_v0_3_1.md
Current Specs/DOC24/DOC24_Addendum_A_BDSM_V6_5_DRAFT_v0_3_1.md
ELNOR REPO READER TEXT MIRROR
Original path: Current Specs/DOC24/DOC24_Addendum_A_BDSM_V6_5_DRAFT_v0_3_1.md
Source repo: /Users/OpenClaw1/Elnor/Elnor Specs
Git branch: main
Git commit: dbaa25962edc11ab30e8d4ca1715f9ae5bf77331
Generated: 2026-06-09T01:23:58.539Z
---
# DOC24 Addendum A — Big Dynamic Satisfaction Machine (BDSM) V6.5 Draft v0.3
**Status:** Draft operative revision v0.3 for review — applies the v0.3 corrective patch plan over the v0.2 BDSM V6.5 draft and replaces BDSM v6.4 for the V6.5+ revision lane.
**Target docs:** DOC24 R3.1.1 primary consumption; DOC8 computation; DOC72 schema ownership; EC durable writes/effective state/orchestration; PropA policy gates; KDA R3 rendering metadata; DOC23 Addenda B learning signals; DOC73 V1.6 privacy / RecentActivityRollup / AVAPO surfaces.
**Draft basis:** BDSM/KDA Adjudication Card V1.3.1-OPA15; DOC24 R3.1.1; KDA R3 Draft v0.2; BDSM V6.5 Draft v0.2 Patch Plan; BDSM V6.5 Draft v0.3 Patch Plan; OP-A V3.15; EC Core Addendum A V3.3; PropA R6.3; DOC73 V1.6 Artifacts 1–5; DOC23 Addenda B Core R0.7 + current family documents.
**Interpretation rule:** This document is standalone for BDSM V6.5 drafting. Anything from BDSM v6.4 that conflicts with this document is superseded. Requirements use SHALL, MUST, MUST NOT, SHOULD, and MAY in their ordinary normative sense.
---
## V0.3.1 seam patch note
V0.3.1 is a focused seam-hardening patch over v0.3. It defines the missing post-join `MatrixLearningSignal`, adds the executable delivery-learning boundary orchestrator, imports DOC24-owned inspector/tokenizer drift types, imports KDA-owned neuroplasticity actions, maps KDA suppression kinds into Matrix no-signal reasons, and closes the remaining reason-code namespace drift.
## 0. V6.5 revision summary
BDSM v6.4 was not buildable against the current DOC24 R3.1.1 / OP-A V3.15 substrate because it treated delivery as a singular manifest, emitted local delivery-force vocabulary, used privacy-unpartitioned utility ledgers, kept a local settings/control plane, and contained formula-level hazards in reward, decay, smoothing, Shapley, confidence, and denominator handling.
V6.5 repairs that by making the following changes normative:
1. **DOC24 manifest chain is the attribution spine.** BDSM attribution consumes `PacketInjectionManifest × FinalPromptInjectionManifest × PacketReconciliationEvent[]`. A card absent from the final prompt manifest receives no utility signal.
2. **BDSM emits DOC24 `DirectiveConstraint[]`, not scalar force levels.** Matrix may emit Matrix-origin force floors only. Policy/user/authority ceilings are resolved by DOC24.
3. **BDSM emits normalized relevance.** `MatrixBoundaryScoring.normalized_relevance ∈ [0,1]` is the within-band sort signal consumed by DOC24.
4. **All learning is partitioned.** Utility ledgers, aggregates, counters, canaries, and compiled bundles are partitioned by `LearningVisibilityScope`, model class, principal/matter/firewall scope, and policy generation.
5. **EC owns effective state, durable writes, and background orchestration.** BDSM defines payloads and derived read models; EC owns routes, writes, task registry, pause controls, and active generation swaps.
6. **DOC8 owns computation implementation.** BDSM defines normative math contracts, schema shapes, fixtures, and runtime consumption boundaries; DOC8 implements the learning computation under those contracts.
7. **KDA R3 render metadata is required for variant attribution.** BDSM reads KDA variant IDs, template versions, and rendering specialization IDs from DOC24 manifests; it does not infer them.
8. **DOC23 signal envelopes enter through a single typed ingestion path.** In-run feedback and post-run learning remain distinct.
9. **Reason codes are closed literals.** Runtime-specific values are metadata, never interpolated into code strings.
10. **Quantitative formulas are guarded.** No-signal rows do not dilute rewards; decay cannot amplify evidence; smoothing is parent-chain shrinkage; residual Shapley buckets are not ledger-attributable.
---
### 0.1 v0.2 patch integration summary
V0.2 applies the interim BDSM V6.5 Draft v0.2 Patch Plan. It preserves the v0.1 V6.5 redesign and restores v6.4 operative surfaces that were not card-deleted and not otherwise superseded. The v0.2 patch adds or strengthens:
1. the V6.5 two-system / four-ledger / three-gate architecture map;
2. legacy/runtime signal payload registry coverage;
3. feedback classification and ledger-routing records;
4. procedure verification records;
5. reversible pattern detection and scoped auto-apply;
6. concrete compiled bundle payload schemas for all four bundle kinds;
7. compiler-stage runner receipts and no-op/error receipts;
8. question traces, phrasing shells, interaction protocol cards, and execution preference cards;
9. Knowledge Manager direct feedback and DOC8 proactive feedback request schemas/routes;
10. background job desired policy, EMA threshold registry/receipts, staged activation, and privacy-surface supersession;
11. expanded conformance fixtures and a coding-agent implementation checklist.
The v0.2 patch does not reintroduce v6.4's retired manifest, local force/tag vocabulary, local settings plane, privacy evaluator, or local durable writer surfaces.
### 0.2 v0.3 corrective patch summary
V0.3 applies the interim BDSM V6.5 Draft v0.3 Patch Plan. It does not reopen the V6.5 adjudication. It corrects the remaining material issues found in the v0.2 final audit:
1. consolidates duplicate compiler receipt type names by renaming the lifecycle-state transition receipt to `MatrixCompilerStateTransitionReceipt` while keeping `MatrixCompilationStageReceipt` as the per-stage runner receipt;
2. completes coverage of all 33 v6.4 legacy/runtime signal classes in `MATRIX_SIGNAL_PAYLOAD_REGISTRY` or marks them through typed payload refs and policy gates;
3. closes storage/path/index coverage for restored v6.4 artifacts, including procedure verification, pattern records, compiled bundle payload views, Knowledge Manager feedback, DOC8 proactive feedback requests, compiler checkpoints, and background job state;
4. restores and supersedes the v6.4 `MatrixExecutionTraceExtensionSchema` as `MatrixExecutionTraceExtensionV65`, with manifest refs, reconciliation refs, KDA variant refs, capture-policy refs, and partitioned context signature.
The v0.3 patch preserves the v0.2 restorations and does not reintroduce retired manifest names, local force/tag vocabulary, local settings routes, KTO export triggers, local privacy evaluation, or local durable writer behavior.
---
## 1. Purpose and scope
The Big Dynamic Satisfaction Machine is the derived self-learning layer that determines, from observed outcomes, whether ELNOR's knowledge delivery and interaction choices helped in a specific context.
The Satisfaction Matrix SHALL:
- learn whether injected knowledge helped in a specific context;
- learn whether a tool or procedure was the right choice in a specific context;
- learn whether a question should be asked, deferred, or suppressed;
- learn which deterministic phrasing shell or profile works for a question kind and context;
- compile those learned outputs into deterministic policy bundles;
- expose those bundles to DOC24, DOC23, and KDA only through approved runtime seams;
- remain local-first, hot-path deterministic, inspectable, reversible, and governance-safe.
The Satisfaction Matrix SHALL NOT:
- create a second prompt-control language;
- become a second durable writer;
- call an LLM on the live turn path;
- silently promote durable standing rules;
- replace DOC72 Beta confidence as ELNOR's epistemic confidence system;
- override policy, authority, source eligibility, or final-prompt delivery proof;
- aggregate sealed or firewalled learning signals into global ledgers.
---
## 2. Normative invariants
1. **EC is the sole durable writer.** All Matrix artifacts SHALL be written by EC only.
2. **No hot-path LLM calls.** DOC24 runtime consumption SHALL use compiled resident indexes and point lookups only.
3. **DOC72 owns schemas; Matrix artifacts are derived.** Matrix artifacts are rebuildable learning artifacts under DOC72-governed schema surfaces.
4. **DOC24 owns delivery vocabulary.** BDSM consumes and emits DOC24 canonical `DirectiveConstraint[]`, `ForceLevel`, `DeliveryTag`, `CardPresence`, and manifest types. BDSM MUST NOT define local lookalike enums.
5. **Final-prompt delivery proof is required.** No utility signal flows from a card absent from `FinalPromptInjectionManifest`.
6. **Policy and authority are ceilings.** Matrix utility may raise relevance or emit Matrix-origin force floors; it MUST NOT override policy, PropA sharing action, DOC73 authority exclusion, or DOC24 final directive resolution.
7. **Privacy partitioning is mandatory.** Sealed signals are audit-only; firewalled signals remain same-firewall; private/personal/matter-scoped signals do not enter global counters.
8. **No second canonical confidence or reliability store.** BDSM records utility and derived snapshots, not canonical DOC72 confidence or DOC24 tool reliability evidence.
9. **Compiled-bundle-only runtime.** Runtime consumes only an active validated `MatrixBundleGenerationManifest`; staged, failed, degraded-inactive, or superseded generations remain inactive.
10. **Resident lookup requirement.** Hot-path Matrix lookup SHALL operate on resident indexes or indexed point lookups. On-demand parsing of large bundle files on the live path is forbidden.
11. **Closed reason codes.** `reason_code`, `degraded_reason_code`, `blocked_reason_code`, `validation_code`, `invalidation_reason_code`, and `no_signal_reason_code` SHALL be closed literal strings. Runtime details live in metadata.
12. **DOC8 computation ownership.** BDSM defines contracts and fixtures; DOC8 implements learning algorithms and compilation under those contracts.
13. **KDA variant metadata is attribution input.** Rendering variant IDs and template versions come from DOC24 manifests populated by KDA R3; BDSM does not infer them.
14. **In-run feedback is not durable learning.** DOC23 in-run `EvaluationFeedbackBundle` guides the current task. Post-run `EvaluationLearningSignalEnvelope` feeds Matrix learning.
---
## 3. Owner split and import contract
### 3.1 Owner split
- **DOC24 owns:** packet lifecycle, candidate/packet/final-prompt manifests, delivery directive resolution, Packet Inspector, source eligibility aggregation, final runtime delivery, and runtime consumption of compiled Matrix bundles.
- **BDSM owns:** utility-ledger contract, attribution decision contract, Matrix relevance/constraint output contract, reason-code surfaces, compiled Matrix bundle shapes, and learning-read-model semantics.
- **DOC8 owns:** reward scoring, attribution computation, signal classification, Shapley approximation implementation, pattern detection, compiler implementation, and proactive-feedback selection under BDSM contracts.
- **DOC72 owns:** graph schemas, entity/node references, context signature shape, utility record schema governance, and graph snapshot identity.
- **EC owns:** durable writes, settings/effective state, control routes, background task registry, orchestrator truth, import/export/restore, atomic current-view writes, and active bundle pointer swaps.
- **PropA owns:** policy decision semantics, source eligibility policy, sensitivity/sharing action, visibility class, redaction maps, and reclassification recovery triggers.
- **KDA R3 owns:** deterministic rendering templates, rendering-tier allocation, render variant IDs, template versions, rendering specialization IDs, execution-strategy cache, and KDA manifest patches consumed by DOC24.
- **DOC23 Addenda B owns:** task-system signal producers, in-run feedback delivery, EvaluationLearningSignalEnvelope definitions, and Task Agent task-design surfaces.
- **DOC11/OpenClaw owns:** final-prompt assembly and final-prompt span truth when it is the final-prompt owner.
### 3.2 Import registry
BDSM V6.5 consumes, and MUST NOT redefine, the following foreign types:
```ts
// DOC24 R3.1.1
import type {
ActionReceipt,
CandidateInjectionManifest,
PacketInjectionManifest,
FinalPromptInjectionManifest,
BlockedPacketManifest,
PacketReconciliationEvent,
ToolOutcomeEvidenceEvent,
DirectiveConstraint,
ConstraintOrigin,
DOC24DeliveryDirective,
DeliveryTag,
ForceLevel,
CardPresence,
TokenizerRef,
TokenizerDriftObservation,
TokenizerDriftComponent,
TokenizerDriftDecision,
DeliveryLearningInspectorRow,
PolicyGenerationSnapshot,
} from "@elnor/doc24-runtime-lifecycle";
// KDA R3
import type {
KdaRenderResult,
KdaManifestPatch,
KdaVariantOutcome,
KdaNeuroplasticityAction,
constrainKdaActionByAttribution,
} from "@elnor/kda-r3";
// DOC23 Common Contracts / Addenda B
import type {
EvaluationLearningSignalEnvelope,
EvaluationFeedbackBundle,
Criterion,
TaskInvocationLearningSignal,
} from "@elnor/doc23-evaluation-common-contracts";
// EC / PropA / DOC72 / DOC73 / DOC8
import type { EffectiveRuntimeStateV4, BackgroundTaskTypeRegistryEntry } from "@elnor/ec-core";
import type { PolicyDecision, SharingAction, VisibilityClass } from "@elnor/propa";
import type { LearningVisibilityScope, RecentActivityRollup } from "@elnor/doc73";
import type { NodeRef, GraphSnapshotRef } from "@elnor/doc72";
import type { FeedbackEventV1 } from "@elnor/doc8";
```
### 3.3 Retired names and non-operative constructs
The following names SHALL NOT appear in BDSM V6.5 source, generated artifacts, compiled bundles, migration output, or build artifacts except inside the allowed declaration block in this subsection:
```ts
export const RETIRED_BDSM_TYPE_NAMES = [
"UnifiedInjectionManifestSchema",
"PacketReconciliationOverlay",
"MatrixDirectiveConstraintSchema",
"MatrixRuntimeSettingsSchema",
"TelemetrySourcePolicySchema",
] as const;
export type RetiredBDSMTypeName = typeof RETIRED_BDSM_TYPE_NAMES[number];
export const BDSM_CANONICAL_MANIFEST_TYPES = {
attribution_input: "PacketInjectionManifest × FinalPromptInjectionManifest × PacketReconciliationEvent[]",
runtime_packet_truth: "PacketInjectionManifest",
final_delivery_truth: "FinalPromptInjectionManifest",
} as const;
export function stripAllowedRetiredNameDeclarationBlock(input: {
text: string;
allowed_block_start?: string;
allowed_block_end?: string;
}): string {
const start_marker = input.allowed_block_start ?? "// BEGIN_ALLOWED_RETIRED_BDSM_TYPE_NAMES";
const end_marker = input.allowed_block_end ?? "// END_ALLOWED_RETIRED_BDSM_TYPE_NAMES";
const start = input.text.indexOf(start_marker);
const end = input.text.indexOf(end_marker);
if (start === -1 || end === -1 || end < start) return input.text;
return input.text.slice(0, start) + input.text.slice(end + end_marker.length);
}
export function assertNoRetiredBDSMTypeName(input: {
text: string;
artifact_kind: "source" | "generated_declaration" | "compiled_bundle" | "migration_output";
}): void {
const scan_text = stripAllowedRetiredNameDeclarationBlock({ text: input.text });
for (const retired_name of RETIRED_BDSM_TYPE_NAMES) {
if (scan_text.includes(retired_name)) {
throw new MatrixValidationError({
reason_code: "matrix.retired_delivery_type_name_used",
metadata: { retired_name, artifact_kind: input.artifact_kind },
});
}
}
}
```
CI SHALL run this check against source text, generated TypeScript declarations, compiled bundle manifests, and migration outputs. The scanner SHALL strip only the explicit declaration block above and MUST NOT exempt arbitrary prose or comments.
### 3.3A Superseded v6.4 privacy surface
The v6.4 `TelemetrySourcePolicySchema` and any BDSM-local source-collection/privacy policy surface are superseded. V6.5 does not own collection eligibility, sensitivity classification, sharing policy, or outbound exposure policy.
Canonical ownership is:
| Surface | Owner |
|---|---|
| collection/application global controls | EC Core |
| incognito/effective runtime truth | EC Core |
| source sensitivity, visibility, sharing, redaction policy | PropA |
| packet delivery and manifest write | DOC24 |
| final runtime prompt truth | DOC11/OpenClaw |
| derived utility learning and compiled bundles | BDSM/DOC8 under EC writes |
BDSM may consume policy decisions and persistence/capture decisions, but MUST NOT implement a local `TelemetrySourcePolicySchema`, local sharing evaluator, or local visibility classifier.
---
## 4. Storage classification and canonical paths
### 4.1 Storage classification
| Artifact | Schema owner | Durable writer | Canonical or derived | Runtime role |
|---|---|---|---|---|
| feedback/signal receipts | DOC72/DOC8/BDSM | EC | append-only derived inputs | compiler input |
| attribution decisions | BDSM/DOC8 | EC | derived | learning / inspector |
| utility ledgers | DOC72/BDSM | EC | derived | compiler input |
| utility aggregates | DOC72/BDSM | EC | derived | compiler input only |
| compiled bundles | DOC72/BDSM | EC | derived | runtime read-only |
| active generation manifest | BDSM/EC | EC | derived active pointer | runtime gating |
| effective state projection | EC/BDSM | EC | derived read model | runtime gating |
| reason-code registry | DOC24/BDSM/KDA | build artifact | generated | build/inspector |
| fixture gate results | BDSM/DOC8 | EC/build | derived | release gate |
### 4.2 Canonical paths
```ts
export const SatisfactionMatrixPathsV65 = {
signalReceiptsJsonl: "ELNOR_MEMORY/system/learning/satisfaction_matrix/signal_receipts.jsonl",
attributionDecisionsJsonl: "ELNOR_MEMORY/system/learning/satisfaction_matrix/attribution_decisions.jsonl",
classificationJsonl: "ELNOR_MEMORY/system/learning/satisfaction_matrix/feedback_classifications.jsonl",
procedureVerificationJsonl: "ELNOR_MEMORY/system/learning/satisfaction_matrix/procedure_verification.jsonl",
feedbackEventsJsonl: "ELNOR_MEMORY/system/learning/satisfaction_matrix/feedback_events.jsonl",
utilityLedgersDir: "ELNOR_MEMORY/system/learning/satisfaction_matrix/ledgers/",
currentViewsDir: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/",
knowledgeUtilityView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/knowledge_utility.json",
toolProcedureUtilityView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/tool_procedure_utility.json",
questionUtilityView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/question_utility.json",
phrasingUtilityView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/phrasing_utility.json",
utilityAggregateView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/utility_aggregates.json",
attributionIndexView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/attribution_index.json",
patternRecordView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/pattern_records.json",
compiledInteractionPolicyView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/compiled_interaction_policy.json",
compiledKnowledgeUtilityView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/compiled_knowledge_utility.json",
compiledToolProcedureUtilityView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/compiled_tool_procedure_utility.json",
compiledPhrasingPolicyView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/compiled_phrasing_policy.json",
compiledBundleManifestView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/compiled_bundle_generation_manifest.json",
compilerCheckpointJson: "ELNOR_MEMORY/system/learning/satisfaction_matrix/nightly_checkpoint.json",
compilerHealthView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/compiler_health.json",
inspectorRowsView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/delivery_learning_inspector_rows.json",
knowledgeManagerQueueView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/knowledge_manager_queue.json",
doc8FeedbackRequestsJsonl: "ELNOR_MEMORY/system/learning/satisfaction_matrix/doc8_feedback_requests.jsonl",
doc8FeedbackRequestView: "ELNOR_MEMORY/system/learning/satisfaction_matrix/current_views/doc8_feedback_requests.json",
backgroundJobPolicyJson: "ELNOR_MEMORY/system/settings/satisfaction_matrix_background_job_policy.json",
backgroundJobStatusJson: "ELNOR_MEMORY/system/state/satisfaction_matrix_background_job_status.json",
reasonCodeRegistryGenerated: "ELNOR_MEMORY/system/learning/satisfaction_matrix/generated/reason_code_registry.json",
} as const;
```
### 4.4 Logical storage registry
V6.5 preserves v6.4's inspectable local storage posture while updating ownership boundaries: EC writes, DOC72/BDSM/DOC8 own schema/learning contracts, and current views remain derived/rebuildable. Physical implementation may be SQLite, append-only JSONL plus atomic JSON, derived SQLite, or another EC-approved indexed representation, but the logical registry below MUST close.
```ts
export type MatrixStorageArtifactKind =
| "append_only_signal_log"
| "append_only_feedback_log"
| "append_only_classification_log"
| "append_only_verification_log"
| "atomic_current_view"
| "atomic_compiled_bundle_view"
| "atomic_settings_view"
| "atomic_state_view"
| "generated_build_artifact";
export type MatrixStorageRegistryEntry = {
artifact_id: keyof typeof SatisfactionMatrixPathsV65;
path: string;
artifact_kind: MatrixStorageArtifactKind;
durable_writer: "EC";
canonical_or_derived: "derived" | "generated" | "ec_owned_settings";
rebuild_source?: string;
required_logical_indexes: string[];
schema_version: 1;
};
export const SatisfactionMatrixStorageRegistryV65: MatrixStorageRegistryEntry[] = [
{ artifact_id: "feedbackEventsJsonl", path: SatisfactionMatrixPathsV65.feedbackEventsJsonl, artifact_kind: "append_only_feedback_log", durable_writer: "EC", canonical_or_derived: "derived", required_logical_indexes: ["idx_matrix_feedback_trace_id", "idx_matrix_feedback_signal_class", "idx_matrix_feedback_observed_at"], schema_version: 1 },
{ artifact_id: "signalReceiptsJsonl", path: SatisfactionMatrixPathsV65.signalReceiptsJsonl, artifact_kind: "append_only_signal_log", durable_writer: "EC", canonical_or_derived: "derived", required_logical_indexes: ["idx_matrix_signal_class", "idx_matrix_signal_partition", "idx_matrix_signal_policy_decision"], schema_version: 1 },
{ artifact_id: "classificationJsonl", path: SatisfactionMatrixPathsV65.classificationJsonl, artifact_kind: "append_only_classification_log", durable_writer: "EC", canonical_or_derived: "derived", required_logical_indexes: ["idx_matrix_classification_feedback_event", "idx_matrix_classification_failure_category"], schema_version: 1 },
{ artifact_id: "procedureVerificationJsonl", path: SatisfactionMatrixPathsV65.procedureVerificationJsonl, artifact_kind: "append_only_verification_log", durable_writer: "EC", canonical_or_derived: "derived", required_logical_indexes: ["idx_matrix_verification_procedure_id", "idx_matrix_verification_outcome", "idx_matrix_verification_observed_at"], schema_version: 1 },
{ artifact_id: "patternRecordView", path: SatisfactionMatrixPathsV65.patternRecordView, artifact_kind: "atomic_current_view", durable_writer: "EC", canonical_or_derived: "derived", rebuild_source: "signalReceiptsJsonl + utility ledgers", required_logical_indexes: ["idx_matrix_pattern_context_signature", "idx_matrix_pattern_state"], schema_version: 1 },
{ artifact_id: "knowledgeManagerQueueView", path: SatisfactionMatrixPathsV65.knowledgeManagerQueueView, artifact_kind: "atomic_current_view", durable_writer: "EC", canonical_or_derived: "derived", rebuild_source: "feedbackEventsJsonl + knowledge review actions", required_logical_indexes: ["idx_matrix_km_queue_state", "idx_matrix_km_queue_priority"], schema_version: 1 },
{ artifact_id: "doc8FeedbackRequestsJsonl", path: SatisfactionMatrixPathsV65.doc8FeedbackRequestsJsonl, artifact_kind: "append_only_signal_log", durable_writer: "EC", canonical_or_derived: "derived", required_logical_indexes: ["idx_matrix_doc8_feedback_request_id", "idx_matrix_doc8_feedback_request_state"], schema_version: 1 },
{ artifact_id: "doc8FeedbackRequestView", path: SatisfactionMatrixPathsV65.doc8FeedbackRequestView, artifact_kind: "atomic_current_view", durable_writer: "EC", canonical_or_derived: "derived", rebuild_source: "doc8FeedbackRequestsJsonl", required_logical_indexes: ["idx_matrix_doc8_feedback_request_state", "idx_matrix_doc8_feedback_request_expires_at"], schema_version: 1 },
{ artifact_id: "compiledInteractionPolicyView", path: SatisfactionMatrixPathsV65.compiledInteractionPolicyView, artifact_kind: "atomic_compiled_bundle_view", durable_writer: "EC", canonical_or_derived: "derived", rebuild_source: "utility ledgers + active MatrixBundleGenerationManifest", required_logical_indexes: ["idx_matrix_bundle_generation_id"], schema_version: 1 },
{ artifact_id: "compiledKnowledgeUtilityView", path: SatisfactionMatrixPathsV65.compiledKnowledgeUtilityView, artifact_kind: "atomic_compiled_bundle_view", durable_writer: "EC", canonical_or_derived: "derived", rebuild_source: "knowledge utility ledger + active MatrixBundleGenerationManifest", required_logical_indexes: ["idx_matrix_bundle_generation_id", "idx_matrix_knowledge_node_context"], schema_version: 1 },
{ artifact_id: "compiledToolProcedureUtilityView", path: SatisfactionMatrixPathsV65.compiledToolProcedureUtilityView, artifact_kind: "atomic_compiled_bundle_view", durable_writer: "EC", canonical_or_derived: "derived", rebuild_source: "tool/procedure utility ledger + active MatrixBundleGenerationManifest", required_logical_indexes: ["idx_matrix_bundle_generation_id", "idx_matrix_tool_procedure_context"], schema_version: 1 },
{ artifact_id: "compiledPhrasingPolicyView", path: SatisfactionMatrixPathsV65.compiledPhrasingPolicyView, artifact_kind: "atomic_compiled_bundle_view", durable_writer: "EC", canonical_or_derived: "derived", rebuild_source: "phrasing utility ledger + active MatrixBundleGenerationManifest", required_logical_indexes: ["idx_matrix_bundle_generation_id", "idx_matrix_phrasing_context"], schema_version: 1 },
];
```
### 4.5 Required logical indexes
These are logical lookup requirements, not a mandate that every implementation use SQLite. EC/DOC72 may implement them as SQLite indexes, materialized JSON indexes, resident in-memory indexes, or other indexed point-lookup mechanisms. The implementation MUST satisfy the lookup semantics and hot-path constraints in this spec.
```ts
export const SatisfactionMatrixRequiredLogicalIndexesV65 = [
"idx_matrix_feedback_trace_id",
"idx_matrix_feedback_signal_class",
"idx_matrix_feedback_observed_at",
"idx_matrix_signal_class",
"idx_matrix_signal_partition",
"idx_matrix_signal_policy_decision",
"idx_matrix_classification_feedback_event",
"idx_matrix_classification_failure_category",
"idx_matrix_verification_procedure_id",
"idx_matrix_verification_outcome",
"idx_matrix_verification_observed_at",
"idx_matrix_ledger_target_context_partition",
"idx_matrix_aggregate_target_context_partition",
"idx_matrix_attribution_card_id",
"idx_matrix_attribution_render_variant_id",
"idx_matrix_pattern_context_signature",
"idx_matrix_pattern_state",
"idx_matrix_bundle_generation_id",
"idx_matrix_km_queue_state",
"idx_matrix_km_queue_priority",
"idx_matrix_doc8_feedback_request_id",
"idx_matrix_doc8_feedback_request_state",
"idx_matrix_doc8_feedback_request_expires_at",
] as const;
```
### 4.6 Execution trace extension supersession
V6.5 supersedes the v6.4 `MatrixExecutionTraceExtensionSchema` with a manifest-aware bridge. The extension preserves v6.4 observability and active-tool-set capture, but attribution and ledger eligibility now require the §6 delivery-learning boundary plus EC learning-policy acceptance.
```ts
export type MatrixExecutionTraceExtensionV65 = {
trace_id: string;
packet_id?: string;
packet_manifest_id?: string;
final_prompt_manifest_id?: string;
reconciliation_event_ids: string[];
packet_decision_graph_id?: string;
context_signature: LearningContextSignature;
active_bundle_generation_id?: string;
kda_render_variant_ids: string[];
question_trace_ids: string[];
tool_ids_used: string[];
active_tool_set: string[];
procedure_ids_used: string[];
terminal_outcome?: string;
latency_to_first_token_ms?: number;
capture_decision_ref: string;
learning_policy_decision_ref: string;
matrix_capture_enabled: boolean;
observed_at: string;
schema_version: 2;
};
export const MatrixExecutionTraceExtensionSupersessionV65 = {
legacy_type_name: "MatrixExecutionTraceExtensionSchema",
superseded_by: "MatrixExecutionTraceExtensionV65",
reason:
"V6.5 requires packet/final-prompt manifest refs, reconciliation event refs, KDA variant refs, capture decision refs, learning policy refs, and LearningContextSignature partitioning.",
migration_rule:
"v6.4 trace extensions may be imported for audit and replay, but they cannot update V6.5 utility ledgers until joined to PacketInjectionManifest, FinalPromptInjectionManifest, PacketReconciliationEvent[], and MatrixLearningPolicyDecision.",
schema_version: 1,
} as const;
```
Normative rules:
1. `MatrixExecutionTraceExtensionV65` is an observability and replay bridge, not attribution truth.
2. Attribution truth is the delivery-learning boundary in §6: `PacketInjectionManifest × FinalPromptInjectionManifest × PacketReconciliationEvent[]` plus exposure evidence.
3. A v6.4 `MatrixExecutionTraceExtensionSchema` record imported without packet/final/reconciliation refs SHALL be audit-only and MUST NOT update V6.5 ledgers.
4. `active_tool_set` preserves the v6.4 rule that tool/procedure attribution uses the tools actually exposed to the live turn, not the theoretical registry.
### 4.3 Persistence rules
- Append-only signal/attribution logs SHALL be written by EC.
- Current views SHALL be atomically replaced.
- Compiled bundle activation SHALL be all-or-none across interaction, knowledge, tool/procedure, and phrasing bundle kinds.
- Restored active bundle pointers SHALL NOT be trusted after backup/import. Restored bundle generations start `staged_inactive` and must be validated before activation.
- Node archive/delete or PropA reclassification SHALL invalidate dependent current views, pending manifests, KDA execution caches, KDA specializations, and Matrix compiled generations in the order specified in §18.
---
## 4A. System architecture: two systems, four ledgers, three gates
This section preserves the v6.4 architectural distinction between epistemic reliability and contextual utility while updating the runtime seams to DOC24 R3.1.1, EC Core, DOC23 signal envelopes, DOC73/PropA partitioning, and KDA R3.
### 4A.1 Two systems
**System A — DOC72 Beta confidence.** DOC72 confidence answers whether knowledge is reliable. It is epistemic. It SHALL be updated only through owner-approved reliability-relevant signals and SHALL remain the canonical confidence system.
**System B — Satisfaction Matrix.** The Satisfaction Matrix answers whether injecting, selecting, asking, or phrasing helps in a specific context. It is utility. It SHALL be updated by contextual outcome signals only after the V6.5 delivery-learning attribution boundary, EC learning-policy gate, and privacy partition gate accept the signal.
**Invariant:** Utility penalties MUST NOT erode DOC72 truth confidence. A reliable fact can have low contextual injection utility; an unreliable proposition cannot become reliable because it was useful.
### 4A.2 Four ledgers
The Matrix maintains four derived utility-ledger families. Every ledger key SHALL carry `LearningContextSignature`, `MatrixLedgerPartition`, `model_class`, and policy/source snapshot lineage as defined in §9 and §13.
| Ledger | Purpose | Primary consumers | Runtime influence |
|---|---|---|---|
| Knowledge Utility | Per-card/per-node contextual injection utility | DOC24 matrix_boosted stage | normalized relevance + Matrix-origin directive floors |
| Tool/Procedure Utility | Context-specific tool/procedure selection utility, not canonical tool reliability | DOC24, DOC3, DOC23 Task Agent via compiled read-only bundles | ranking, suggestion, design hints |
| Question Utility | Per-question-kind contextual utility | DOC24 question/interruption calculus | ask / defer / suppress non-blocking questions |
| Phrasing Utility | Per-phrasing-profile and deterministic-shell utility | DOC24/KDA interaction surfaces | shell/profile choice only |
### 4A.3 Three gates in V6.5 terms
**Gate 1 — Confidence.** DOC72 confidence gates factual eligibility and hedge posture. BDSM may feed Gate 1 only through classified proposition-level reliability failure contracts and never through raw dissatisfaction, generic latency, routing frustration, or utility-only outcomes.
**Gate 2 — Relevance.** DOC24 structural relevance establishes eligibility. BDSM may only adjust the Matrix boundary score for structurally eligible, explicitly requested, pinned, or direct-match override candidates as specified in §7. Matrix MUST NOT make a below-floor candidate eligible by utility alone.
**Gate 3 — Delivery directive influence.** BDSM emits Matrix-origin `DirectiveConstraint[]` floors and rationale metadata. DOC24 owns final directive reconciliation. BDSM MUST NOT emit local delivery-directive enums or policy ceilings as if they were Matrix-owned authority.
### 4A.4 Context signature replacement for v6.4 context class key
The v6.4 `context_class_key` is superseded for durable ledger keys by `LearningContextSignature`. Short context strings MAY remain as display labels only.
```ts
export type LegacyContextClassKey = string;
export type ContextSignatureResolutionInput = {
domain: string;
task_type: string;
artifact_class?: string;
model_class: MatrixModelClass;
partition: MatrixLedgerPartition;
destination_class: "same_machine_local_runtime" | "cloud_api" | "external_share" | "audit_only";
schema_version: 1;
};
export function resolveLearningContextSignature(
input: ContextSignatureResolutionInput,
): LearningContextSignature {
const normalized_key = canonicalJson({
domain: input.domain,
task_type: input.task_type,
artifact_class: input.artifact_class ?? null,
model_class: input.model_class,
learning_visibility_scope: input.partition.learning_visibility_scope,
firewall_scope_id: input.partition.firewall_scope_id ?? null,
matter_id: input.partition.matter_id ?? null,
destination_class: input.destination_class,
});
return {
domain: input.domain,
task_type: input.task_type,
artifact_class: input.artifact_class ?? "unspecified",
matter_id: input.partition.matter_id,
model_class: input.model_class,
learning_visibility_scope: input.partition.learning_visibility_scope,
firewall_scope_id: input.partition.firewall_scope_id,
destination_class: input.destination_class,
normalized_key: sha256(normalized_key),
schema_version: 1,
};
}
```
---
## 5. EC effective state, settings, cold start, and learning policy
### 5.1 EC-owned control plane
BDSM V6.5 deletes v6.4's local settings route ownership. BDSM defines payloads and derived projections only. EC owns the command route, durable write, effective-state computation, telemetry, and read-model refresh.
```ts
export type MatrixSettingsDesiredPatch = {
apply_matrix?: boolean;
gather_matrix_data?: boolean;
gate2_experience_boost?: boolean;
gate3_attribution_tags?: boolean;
gate1_classified_feedback?: boolean;
apply_question_policy?: boolean;
apply_phrasing_policy?: boolean;
schema_version: 1;
};
export type MatrixEffectiveRuntimeState = {
desired: {
matrix_settings: MatrixSettingsDesiredPatch;
suppress_doc8_feedback_requests: boolean;
};
effective: {
matrix_influence_enabled: boolean;
matrix_signal_capture_enabled: boolean;
question_policy_enabled: boolean;
phrasing_policy_enabled: boolean;
proactive_feedback_requests_enabled: boolean;
active_bundle_generation_id?: string;
};
divergence_reason_codes: Array<
| "matrix.apply_disabled_by_user"
| "matrix.satisfaction_matrix_influence_disabled_by_ec"
| "matrix.doc8_learning_outputs_disabled_by_ec"
| "matrix.bundle_stale_compilation_paused"
| "matrix.compilation_failed_circuit_open"
| "matrix.index_unavailable_cold_start"
| "matrix.fresh_deployment_gather_only"
>;
ec_effective_state_generation_id: string;
computed_at: string;
schema_version: 1;
};
```
### 5.2 Fresh deployment / cold-start reconciliation
A fresh deployment SHALL begin in gather-only behavior even when the user's desired `apply_matrix` default is true. This prevents empty or unvalidated bundles from influencing delivery.
```ts
export function deriveColdStartMatrixEffectiveState(input: {
desired_apply_matrix: boolean;
active_bundle_generation_id?: string;
resident_index_ready: boolean;
fresh_deployment: boolean;
}): {
matrix_influence_enabled: boolean;
matrix_signal_capture_enabled: boolean;
divergence_reason_codes: string[];
} {
const divergence_reason_codes: string[] = [];
const gather = true;
if (input.fresh_deployment || !input.active_bundle_generation_id || !input.resident_index_ready) {
divergence_reason_codes.push(
input.fresh_deployment ? "matrix.fresh_deployment_gather_only" : "matrix.index_unavailable_cold_start",
);
return {
matrix_influence_enabled: false,
matrix_signal_capture_enabled: gather,
divergence_reason_codes,
};
}
return {
matrix_influence_enabled: input.desired_apply_matrix,
matrix_signal_capture_enabled: gather,
divergence_reason_codes,
};
}
```
### 5.3 Unified Matrix learning policy
All learning writes pass through `decideMatrixLearningPolicy`. There is no separate capture decision function and no separate off-policy decision function.
```ts
export type MatrixLearningPolicyDecision =
| {
kind: "on_policy_learning";
policy_decision_id: string;
ec_effective_state_generation_id: string;
schema_version: 1;
}
| {
kind: "off_policy_shadow_learning";
policy_decision_id: string;
ec_effective_state_generation_id: string;
reason_code: "matrix.influence_disabled_but_capture_enabled";
schema_version: 1;
}
| {
kind: "audit_only_no_learning";
policy_decision_id?: string;
ec_effective_state_generation_id: string;
reason_code:
| "matrix.global_incognito_active"
| "matrix.task_incognito_suppresses_optional_learning"
| "matrix.capture_disabled"
| "matrix.doc8_learning_outputs_disabled"
| "matrix.missing_policy_decision"
| "matrix.policy_blocks_learning_signal";
schema_version: 1;
};
export function decideMatrixLearningPolicy(input: {
matrix_influence_enabled: boolean;
matrix_signal_capture_enabled: boolean;
doc8_learning_outputs_enabled: boolean;
global_incognito: boolean;
task_incognito_suppresses_optional_learning: boolean;
policy_decision_id?: string;
policy_allows_learning: boolean;
ec_effective_state_generation_id: string;
}): MatrixLearningPolicyDecision {
if (input.global_incognito) {
return { kind: "audit_only_no_learning", reason_code: "matrix.global_incognito_active", policy_decision_id: input.policy_decision_id, ec_effective_state_generation_id: input.ec_effective_state_generation_id, schema_version: 1 };
}
if (input.task_incognito_suppresses_optional_learning) {
return { kind: "audit_only_no_learning", reason_code: "matrix.task_incognito_suppresses_optional_learning", policy_decision_id: input.policy_decision_id, ec_effective_state_generation_id: input.ec_effective_state_generation_id, schema_version: 1 };
}
if (!input.matrix_signal_capture_enabled) {
return { kind: "audit_only_no_learning", reason_code: "matrix.capture_disabled", policy_decision_id: input.policy_decision_id, ec_effective_state_generation_id: input.ec_effective_state_generation_id, schema_version: 1 };
}
if (!input.doc8_learning_outputs_enabled) {
return { kind: "audit_only_no_learning", reason_code: "matrix.doc8_learning_outputs_disabled", policy_decision_id: input.policy_decision_id, ec_effective_state_generation_id: input.ec_effective_state_generation_id, schema_version: 1 };
}
if (!input.policy_decision_id) {
return { kind: "audit_only_no_learning", reason_code: "matrix.missing_policy_decision", ec_effective_state_generation_id: input.ec_effective_state_generation_id, schema_version: 1 };
}
if (!input.policy_allows_learning) {
return { kind: "audit_only_no_learning", reason_code: "matrix.policy_blocks_learning_signal", policy_decision_id: input.policy_decision_id, ec_effective_state_generation_id: input.ec_effective_state_generation_id, schema_version: 1 };
}
if (!input.matrix_influence_enabled) {
return { kind: "off_policy_shadow_learning", reason_code: "matrix.influence_disabled_but_capture_enabled", policy_decision_id: input.policy_decision_id, ec_effective_state_generation_id: input.ec_effective_state_generation_id, schema_version: 1 };
}
return { kind: "on_policy_learning", policy_decision_id: input.policy_decision_id, ec_effective_state_generation_id: input.ec_effective_state_generation_id, schema_version: 1 };
}
```
### 5.4 Ledger write permissions
```ts
export type MatrixLedgerWritePermissions = {
may_update_active_utility_ledgers: boolean;
may_update_shadow_ledgers: boolean;
schema_version: 1;
};
export function ledgerWritePermissionsFromPolicy(decision: MatrixLearningPolicyDecision): MatrixLedgerWritePermissions {
if (decision.kind === "on_policy_learning") {
return { may_update_active_utility_ledgers: true, may_update_shadow_ledgers: false, schema_version: 1 };
}
if (decision.kind === "off_policy_shadow_learning") {
return { may_update_active_utility_ledgers: false, may_update_shadow_ledgers: true, schema_version: 1 };
}
return { may_update_active_utility_ledgers: false, may_update_shadow_ledgers: false, schema_version: 1 };
}
```
---
### 5.6 Staged Matrix activation progression
Fresh deployments and rebuilds SHALL enter Matrix runtime through staged activation. EC owns effective state; this section defines the default progression and guard conditions.
```ts
export type MatrixActivationStage =
| "gather_only"
| "gate3_directive_floors"
| "question_policy"
| "gate2_relevance_boost"
| "phrasing_policy"
| "gate1_classified_feedback";
export type MatrixActivationStageStatus = {
stage: MatrixActivationStage;
state: "inactive" | "gathering" | "eligible" | "active" | "paused" | "blocked";
support_count: number;
min_support_required: number;
attribution_confidence_floor: number;
blocking_reason_code?:
| "matrix.activation_insufficient_support"
| "matrix.activation_confidence_below_floor"
| "matrix.activation_blocked_by_ec_effective_state"
| "matrix.activation_bundle_generation_missing";
schema_version: 1;
};
export const DEFAULT_MATRIX_ACTIVATION_ORDER: MatrixActivationStage[] = [
"gather_only",
"gate3_directive_floors",
"question_policy",
"gate2_relevance_boost",
"phrasing_policy",
"gate1_classified_feedback",
];
```
Rules:
- Fresh deployments SHALL begin `gather_only` even when desired `apply_matrix = true`; EC effective state records the divergence until activation criteria pass.
- Rollout order is the default progression, not an absolute dependency chain. EC MAY keep later stages inactive while earlier stages remain active.
- No stage applies below `min_attribution_confidence_for_application`.
- All policy regression, revert, and auto-apply actions scope to exact observed context first.
---
## 6. Delivery directive influence
### 6.1 Canonical DOC24 directive consumption
BDSM V6.5 consumes DOC24 canonical delivery types. It does not define local primary tag, hedge, or force schemas.
```ts
export type MatrixDirectiveInfluence = {
card_id: string;
normalized_relevance: number; // [0,1], see §7
attribution_strength: number; // [0,1]
directive_constraints: DirectiveConstraint[]; // Matrix-origin floor constraints only
matrix_decision_id: string;
schema_version: 1;
};
export function matrixForceFloorConstraint(input: {
floor: Extract<ForceLevel, "standard" | "strong" | "hard">;
matrix_decision_id: string;
}): DirectiveConstraint {
return {
kind: "force_level_floor",
floor: input.floor,
origin: { kind: "matrix", matrix_decision_id: input.matrix_decision_id },
};
}
export function assertMatrixConstraintOwnership(c: DirectiveConstraint): void {
if (c.origin.kind !== "matrix") {
throw new MatrixValidationError({
reason_code: "matrix.must_not_emit_non_matrix_directive_constraint",
metadata: { origin_kind: c.origin.kind },
});
}
if (c.kind !== "force_level_floor") {
throw new MatrixValidationError({
reason_code: "matrix.must_emit_force_floor_only",
metadata: { constraint_kind: c.kind },
});
}
}
```
### 6.2 BDSM may emit floors only
BDSM MAY emit `force_level_floor` constraints for structurally eligible cards. BDSM MUST NOT emit:
- policy ceilings;
- authority ceilings;
- user overrides;
- redaction constraints;
- reference-only requirements;
- block-inline constraints.
Those constraints belong to DOC24, PropA, DOC73 authority, or user/policy origin. BDSM observes the resolved directive later for attribution/learning, but the final `DOC24DeliveryDirective` is DOC24-owned.
---
## 7. Matrix boundary relevance and opportunity sets
### 7.1 Boundary score
`MatrixBoundaryScoring` is the only BDSM relevance object DOC24 consumes for within-band ordering.
```ts
export const RELEVANCE_EPSILON = 1e-6;
export type MatrixEligibilityOrigin =
| "structural_floor_met"
| "direct_match_override"
| "pinned_override"
| "explicit_request_override"
| "not_delivery_eligible";
export type MatrixBoundaryScoring = {
card_id: string;
structural_relevance_signed: number;
effective_relevance_signed: number;
structural_floor_signed: number;
structurally_eligible_before_matrix: boolean;
delivery_eligible: boolean;
eligibility_origin: MatrixEligibilityOrigin;
override_priority: 0 | 1 | 2 | 3;
normalized_relevance: number;
normalization_basis: "structural_baseline" | "matrix_boosted" | "cold_start_default";
matrix_generation_id?: string;
schema_version: 1;
};
export function clampUnit(n: number): number {
return Number.isFinite(n) ? Math.max(0, Math.min(1, n)) : 0;
}
export function clampSigned(n: number): number {
return Number.isFinite(n) ? Math.max(-1, Math.min(1, n)) : 0;
}
export function normalizeRelevanceWithinFloor(input: {
effective_relevance_signed: number;
structural_floor_signed: number;
delivery_eligible: boolean;
override_priority: 0 | 1 | 2 | 3;
}): number {
if (!input.delivery_eligible) return 0;
const floor = clampSigned(input.structural_floor_signed);
const effective = clampSigned(input.effective_relevance_signed);
if (floor >= 1 - RELEVANCE_EPSILON) {
return effective >= floor - RELEVANCE_EPSILON ? 1 : 0;
}
const normalized = clampUnit((effective - floor) / Math.max(RELEVANCE_EPSILON, 1 - floor));
if (normalized === 0 && input.override_priority > 0) {
return Math.min(1, input.override_priority * 0.01);
}
return normalized;
}
```
### 7.2 Boundary computation
```ts
export function computeMatrixBoundaryScoring(input: {
card_id: string;
structural_relevance_signed: number;
structural_floor_signed: number;
smoothed_utility: number;
attribution_confidence: number;
experience_weight: number;
direct_match?: boolean;
pinned?: boolean;
explicitly_requested?: boolean;
matrix_generation_id?: string;
}): MatrixBoundaryScoring {
const structural = clampSigned(input.structural_relevance_signed);
const floor = clampSigned(input.structural_floor_signed);
const utility_boost = clampSigned(
clampSigned(input.smoothed_utility) * clampUnit(input.attribution_confidence) * Math.max(0, input.experience_weight),
);
const effective = clampSigned(structural + utility_boost);
const structurally_eligible_before_matrix = structural >= floor;
const override_priority: 0 | 1 | 2 | 3 = input.explicitly_requested ? 3 : input.pinned ? 2 : input.direct_match ? 1 : 0;
const delivery_eligible = structurally_eligible_before_matrix || override_priority > 0;
const eligibility_origin: MatrixEligibilityOrigin = structurally_eligible_before_matrix
? "structural_floor_met"
: input.explicitly_requested
? "explicit_request_override"
: input.pinned
? "pinned_override"
: input.direct_match
? "direct_match_override"
: "not_delivery_eligible";
return {
card_id: input.card_id,
structural_relevance_signed: structural,
effective_relevance_signed: delivery_eligible ? effective : Math.min(effective, floor - RELEVANCE_EPSILON),
structural_floor_signed: floor,
structurally_eligible_before_matrix,
delivery_eligible,
eligibility_origin,
override_priority,
normalized_relevance: normalizeRelevanceWithinFloor({
effective_relevance_signed: effective,
structural_floor_signed: floor,
delivery_eligible,
override_priority,
}),
normalization_basis: "matrix_boosted",
matrix_generation_id: input.matrix_generation_id,
schema_version: 1,
};
}
```
### 7.3 Matrix lookup cap receipt
If hot-path Matrix lookup is capped, omitted candidates are recorded as opportunity-set omissions and SHALL NOT become negative absence signals.
```ts
export type MatrixCandidateCapReceipt = {
packet_id: string;
max_matrix_lookup_candidates: number;
omitted_candidate_ids: string[];
omitted_reason_code: "matrix.lookup_cap_omitted_candidate";
omission_is_negative_learning_signal: false;
schema_version: 1;
};
```
### 7.4 Ledger-family support thresholds
Tool/procedure utility uses the same Gate-2 support threshold as knowledge utility. Question and phrasing ledgers use their own thresholds.
```ts
export type MatrixLedgerFamily = "knowledge" | "tool_procedure" | "question" | "phrasing";
export function supportThresholdForLedger(
ledger: MatrixLedgerFamily,
policy: {
activate_gate2_after_support: number;
activate_question_policy_after_support: number;
activate_phrasing_policy_after_support: number;
},
): number {
switch (ledger) {
case "knowledge":
case "tool_procedure":
return policy.activate_gate2_after_support;
case "question":
return policy.activate_question_policy_after_support;
case "phrasing":
return policy.activate_phrasing_policy_after_support;
}
}
```
---
## 8. Delivery-learning attribution boundary
### 8.1 Shared boundary input and output
BDSM consumes the shared delivery-learning boundary. This is the integration spine connecting DOC24, KDA, final-prompt owners, receipts, reconciliation events, PropA policy snapshots, EC effective state, and Matrix/KDA learning.
```ts
export type CardExposureEvidence =
| { kind: "explicit_output_reference"; output_span_ref: string; card_id: string; confidence: number }
| { kind: "tool_or_action_uses_card_content"; action_receipt_id: string; card_id: string; confidence: number }
| { kind: "llm_reconciliation_marked_used"; reconciliation_event_id: string; card_id: string; confidence: number }
| { kind: "user_feedback_targets_card"; feedback_event_id: string; card_id: string; confidence: number };
export type MatrixAttributionNoSignalReasonCode =
| "matrix.dropped_by_final_prompt_owner"
| "matrix.policy_excluded"
| "matrix.authority_excluded"
| "matrix.budget_excluded"
| "matrix.reference_only_delivered"
| "matrix.no_exposure_evidence";
export type BdsmAttributionOutcome =
| "positive"
| "ignored"
| "corrected"
| "negative"
| "no_signal"
| "invariant_failure";
export function bdsmInspectorLabel(
outcome: BdsmAttributionOutcome,
): "positive_signal" | "ignored_signal" | "corrected_signal" | "negative_signal" | "no_signal" | "invariant_failure" {
switch (outcome) {
case "positive": return "positive_signal";
case "ignored": return "ignored_signal";
case "corrected": return "corrected_signal";
case "negative": return "negative_signal";
case "no_signal": return "no_signal";
case "invariant_failure": return "invariant_failure";
}
}
export type CardAttributionDecision =
| { kind: "invariant_failure"; card_id: string; reason_code: "matrix.card_not_in_packet_manifest"; metadata: { card_id: string }; schema_version: 1 }
| { kind: "no_signal"; card_id: string; reason_code: MatrixAttributionNoSignalReasonCode; schema_version: 1 }
| { kind: "ledger_signal"; card_id: string; signal: "positive" | "ignored" | "corrected" | "negative"; evidence: CardExposureEvidence[]; render_variant_id?: string; attribution_decision_id: string; policy_decision_id: string; ec_effective_state_generation_id: string; schema_version: 1 };
export type MatrixLearningSignal = {
signal_id: string;
signal_source: "delivery_learning_boundary";
card_id: string;
context_signature: LearningContextSignature;
ledger_target: MatrixSignalLedgerTarget;
signal: "positive" | "ignored" | "corrected" | "negative";
evidence: CardExposureEvidence[];
render_variant_id?: string;
attribution_decision_id: string;
policy_decision_id: string;
ec_effective_state_generation_id: string;
schema_version: 1;
};
export type DeliveryLearningBoundaryInput = {
packet_manifest: PacketInjectionManifest;
final_prompt_manifest: FinalPromptInjectionManifest;
reconciliation_events: PacketReconciliationEvent[];
action_receipts: ActionReceipt[];
tool_outcome_evidence_events: ToolOutcomeEvidenceEvent[];
kda_render_results: KdaRenderResult[];
propA_policy_snapshot_id: string;
policy_decision_id: string;
ec_effective_state_generation_id: string;
context_signature: LearningContextSignature;
schema_version: 1;
};
export type DeliveryLearningBoundaryOutput = {
card_attribution_decisions: CardAttributionDecision[];
kda_variant_outcomes: KdaVariantOutcome[];
matrix_learning_signals: MatrixLearningSignal[];
no_signal_receipts: Extract<CardAttributionDecision, { kind: "no_signal" }>[];
degraded_reason_codes: string[];
schema_version: 1;
};
```
### 8.1A Delivery-learning boundary orchestrator
The orchestrator is the only normative join path from delivery artifacts to BDSM/KDA learning. It reads DOC24-written manifests as truth. KDA render results may supply provenance, but they do not supersede `PacketInjectionManifest` once DOC24 has written it.
```ts
export function computeDeliveryLearningBoundary(
input: DeliveryLearningBoundaryInput,
): DeliveryLearningBoundaryOutput {
const packet_card_ids = new Set(input.packet_manifest.cards.map(c => c.card_id));
const final_prompt_card_ids = new Set(input.final_prompt_manifest.delivered_card_ids);
const renderByCardId = new Map(input.kda_render_results.map(r => [r.manifest_patch.card_id, r]));
const reconciliationByCardId = indexReconciliationByCard(input.reconciliation_events);
const card_attribution_decisions: CardAttributionDecision[] = [];
const kda_variant_outcomes: KdaVariantOutcome[] = [];
const matrix_learning_signals: MatrixLearningSignal[] = [];
const degraded_reason_codes: string[] = [];
for (const card of input.packet_manifest.cards) {
const render = renderByCardId.get(card.card_id);
const decision = decideCardAttribution({
card_id: card.card_id,
packet_card_ids,
final_prompt_card_ids,
packet_card_presence: card.card_presence,
packet_exclusion_reason: card.suppression_kind ? exclusionReasonFromSuppressionKind(card.suppression_kind) : card.packet_exclusion_reason,
reconciliation_outcome: reconciliationByCardId.get(card.card_id) ?? "unknown",
exposure_evidence: gatherExposureEvidence(card.card_id, input),
render_variant_id: card.render_variant_id,
attribution_decision_id: `${input.packet_manifest.packet_id}:${card.card_id}:attribution`,
policy_decision_id: input.policy_decision_id,
ec_effective_state_generation_id: input.ec_effective_state_generation_id,
});
card_attribution_decisions.push(decision);
if (render && card.render_variant_id) {
kda_variant_outcomes.push(buildKdaVariantOutcome({ packet_id: input.packet_manifest.packet_id, card, render, decision }));
}
if (decision.kind === "ledger_signal") {
matrix_learning_signals.push(buildMatrixLearningSignal(decision, input.context_signature));
}
}
return {
card_attribution_decisions,
kda_variant_outcomes,
matrix_learning_signals,
no_signal_receipts: card_attribution_decisions.filter((d): d is Extract<CardAttributionDecision, { kind: "no_signal" }> => d.kind === "no_signal"),
degraded_reason_codes,
schema_version: 1,
};
}
export function indexReconciliationByCard(events: PacketReconciliationEvent[]): Map<string, "used" | "ignored" | "corrected" | "negative" | "unknown"> {
const out = new Map<string, "used" | "ignored" | "corrected" | "negative" | "unknown">();
for (const event of events) {
for (const card_id of event.card_ids ?? []) out.set(card_id, event.outcome ?? "unknown");
}
return out;
}
export function gatherExposureEvidence(card_id: string, input: DeliveryLearningBoundaryInput): CardExposureEvidence[] {
const evidence: CardExposureEvidence[] = [];
for (const event of input.reconciliation_events) {
if ((event.card_ids ?? []).includes(card_id) && event.outcome === "used") {
evidence.push({ kind: "llm_reconciliation_marked_used", reconciliation_event_id: event.event_id, card_id, confidence: 1 });
}
}
for (const receipt of input.action_receipts) {
if ((receipt.card_ids_used ?? []).includes(card_id)) {
evidence.push({ kind: "tool_or_action_uses_card_content", action_receipt_id: receipt.receipt_id, card_id, confidence: receipt.confidence ?? 0.8 });
}
}
return evidence;
}
export function buildKdaVariantOutcome(input: { packet_id: string; card: PacketInjectionManifest["cards"][number]; render: KdaRenderResult; decision: CardAttributionDecision }): KdaVariantOutcome {
const patch = input.render.manifest_patch;
let variant_outcome: KdaVariantOutcome["variant_outcome"] = "no_variant_signal";
if (input.decision.kind === "ledger_signal") {
variant_outcome = input.decision.signal === "positive" ? "variant_used" : input.decision.signal === "ignored" ? "variant_ignored" : input.decision.signal === "corrected" ? "variant_corrected" : "variant_negative";
}
return {
card_id: input.card.card_id,
packet_id: input.packet_id,
render_variant_id: input.card.render_variant_id ?? (patch.card_presence !== "excluded" ? patch.render_variant_id : "none"),
rendering_specialization_id: input.card.rendering_specialization_id,
template_version: input.card.template_version ?? (patch.card_presence !== "excluded" ? patch.template_version : "none"),
rendering_tier: input.card.rendering_tier ?? (patch.card_presence !== "excluded" ? patch.rendering_tier : null),
canary_state: input.card.canary_state,
composition_id: input.card.composition_id,
component_card_ids: input.card.component_card_ids,
variant_outcome,
attribution_decision_id: input.decision.kind === "ledger_signal" ? input.decision.attribution_decision_id : `${input.packet_id}:${input.card.card_id}:no_signal`,
schema_version: 1,
};
}
export function buildMatrixLearningSignal(decision: Extract<CardAttributionDecision, { kind: "ledger_signal" }>, context_signature: LearningContextSignature): MatrixLearningSignal {
return { signal_id: `${decision.attribution_decision_id}:signal`, signal_source: "delivery_learning_boundary", card_id: decision.card_id, context_signature, ledger_target: "knowledge", signal: decision.signal, evidence: decision.evidence, render_variant_id: decision.render_variant_id, attribution_decision_id: decision.attribution_decision_id, policy_decision_id: decision.policy_decision_id, ec_effective_state_generation_id: decision.ec_effective_state_generation_id, schema_version: 1 };
}
```
### 8.2 Card attribution decision
```ts
export function exclusionReasonFromSuppressionKind(kind: "authority_blocked" | "policy_excluded" | "budget_blocked"): "matrix.policy_excluded" | "matrix.authority_excluded" | "matrix.budget_excluded" {
switch (kind) {
case "authority_blocked": return "matrix.authority_excluded";
case "policy_excluded": return "matrix.policy_excluded";
case "budget_blocked": return "matrix.budget_excluded";
}
}
export function decideCardAttribution(input: {
card_id: string;
packet_card_ids: Set<string>;
final_prompt_card_ids: Set<string>;
packet_card_presence: "included_inline" | "included_reference_only" | "excluded";
packet_exclusion_reason?: "matrix.policy_excluded" | "matrix.authority_excluded" | "matrix.budget_excluded";
reconciliation_outcome: "used" | "ignored" | "corrected" | "negative" | "unknown";
exposure_evidence: CardExposureEvidence[];
render_variant_id?: string;
attribution_decision_id: string;
policy_decision_id: string;
ec_effective_state_generation_id: string;
}): CardAttributionDecision {
if (!input.packet_card_ids.has(input.card_id)) {
return { kind: "invariant_failure", card_id: input.card_id, reason_code: "matrix.card_not_in_packet_manifest", metadata: { card_id: input.card_id }, schema_version: 1 };
}
if (input.packet_card_presence === "excluded") {
return { kind: "no_signal", card_id: input.card_id, reason_code: input.packet_exclusion_reason ?? "matrix.policy_excluded", schema_version: 1 };
}
if (!input.final_prompt_card_ids.has(input.card_id)) {
return { kind: "no_signal", card_id: input.card_id, reason_code: "matrix.dropped_by_final_prompt_owner", schema_version: 1 };
}
if (input.packet_card_presence === "included_reference_only") {
return { kind: "no_signal", card_id: input.card_id, reason_code: "matrix.reference_only_delivered", schema_version: 1 };
}
if (input.exposure_evidence.length === 0 || input.reconciliation_outcome === "unknown") {
return { kind: "no_signal", card_id: input.card_id, reason_code: "matrix.no_exposure_evidence", schema_version: 1 };
}
const signal = input.reconciliation_outcome === "used" ? "positive" : input.reconciliation_outcome;
return { kind: "ledger_signal", card_id: input.card_id, signal, evidence: input.exposure_evidence, render_variant_id: input.render_variant_id, attribution_decision_id: input.attribution_decision_id, policy_decision_id: input.policy_decision_id, ec_effective_state_generation_id: input.ec_effective_state_generation_id, schema_version: 1 };
}
```
### 8.3 Absence-signal opportunity state
```ts
export type MatrixAbsenceSignal = {
absent_target_id: string;
context_signature: LearningContextSignature;
opportunity_state:
| "not_candidate"
| "candidate_not_selected"
| "selected_in_packet"
| "dropped_by_final_prompt_owner"
| "policy_excluded"
| "authority_excluded"
| "reference_only_delivered";
may_update_matrix_relevance: boolean;
may_update_policy_or_authority: false;
no_signal_reason?: string;
observed_at: string;
schema_version: 1;
};
export function mayUpdateMatrixFromAbsence(state: MatrixAbsenceSignal["opportunity_state"]): boolean {
return state === "not_candidate" || state === "candidate_not_selected";
}
```
### 8.4 Tokenizer drift consumer contract
Tokenizer drift component and decision types are DOC24-owned lifecycle types. BDSM records DOC24 drift decisions for attribution diagnostics; tokenizer drift does not by itself create a utility signal.
```ts
import type { TokenizerDriftComponent, TokenizerDriftDecision } from "@elnor/doc24-runtime-lifecycle";
export type MatrixTokenizerDriftDiagnosticReceipt = {
packet_id: string;
component: TokenizerDriftComponent;
doc24_decision: TokenizerDriftDecision;
attribution_effect: "none";
schema_version: 1;
};
export function recordTokenizerDriftForMatrixDiagnostics(input: {
packet_id: string;
component: TokenizerDriftComponent;
decision: TokenizerDriftDecision;
}): MatrixTokenizerDriftDiagnosticReceipt {
return { packet_id: input.packet_id, component: input.component, doc24_decision: input.decision, attribution_effect: "none", schema_version: 1 };
}
```
---
## 9. Learning partitions, context signatures, and counters
### 9.1 Learning context signature
Raw `context_class_key` is not sufficient in V6.5. Every ledger, aggregate, canary counter, and rendering specialization outcome references a `LearningContextSignature`.
```ts
export type MatrixModelClass =
| "cheap_local"
| "cheap_api"
| "medium"
| "expensive_frontier"
| "unknown";
export type MatrixLedgerPartition = {
principal_id: string;
learning_visibility_scope:
| "global_shared"
| "matter_scoped"
| "personal"
| "private"
| "sealed"
| "firewalled";
firewall_scope_id?: string;
matter_id?: string;
model_class: MatrixModelClass;
policy_generation_id: string;
schema_version: 1;
};
export type LearningContextSignature = {
domain: string;
task_type: string;
artifact_class: string;
matter_id?: string;
work_context_id?: string;
model_class: MatrixModelClass;
learning_visibility_scope: MatrixLedgerPartition["learning_visibility_scope"];
firewall_scope_id?: string;
destination_class: "same_machine_local_runtime" | "cloud_api" | "external_share" | "audit_only";
normalized_key: string;
schema_version: 1;
};
export type PartitionedUtilityLedgerKey = {
target_ledger: "knowledge" | "tool_procedure" | "question" | "phrasing" | "task_design";
target_id: string;
context_signature: LearningContextSignature;
partition: MatrixLedgerPartition;
schema_version: 1;
};
```
### 9.2 Signal acceptance by partition
```ts
export type MatrixSignalPersistenceDecision =
| {
kind: "persist_for_learning";
policy_decision_id: string;
allowed_ledgers: Array<"knowledge" | "tool_procedure" | "question" | "phrasing" | "task_design">;
schema_version: 1;
}
| {
kind: "audit_only_no_learning";
policy_decision_id?: string;
reason_code:
| "matrix.signal_data_class_blocks_learning"
| "matrix.signal_matter_firewall_blocks_promotion"
| "matrix.signal_pattern_promotion_not_eligible"
| "matrix.incognito_blocks_optional_learning"
| "matrix.sealed_source_audit_only";
schema_version: 1;
};
export function acceptLearningSignalForPartition(input: {
partition: MatrixLedgerPartition;
policy_decision_id?: string;
pattern_promotion_eligible: boolean;
policy_allows_learning: boolean;
}): MatrixSignalPersistenceDecision {
if (input.partition.learning_visibility_scope === "sealed") {
return { kind: "audit_only_no_learning", policy_decision_id: input.policy_decision_id, reason_code: "matrix.sealed_source_audit_only", schema_version: 1 };
}
if (!input.policy_allows_learning) {
return { kind: "audit_only_no_learning", policy_decision_id: input.policy_decision_id, reason_code: "matrix.signal_data_class_blocks_learning", schema_version: 1 };
}
if (!input.pattern_promotion_eligible) {
return { kind: "audit_only_no_learning", policy_decision_id: input.policy_decision_id, reason_code: "matrix.signal_pattern_promotion_not_eligible", schema_version: 1 };
}
if (!input.policy_decision_id) {
return { kind: "audit_only_no_learning", reason_code: "matrix.signal_data_class_blocks_learning", schema_version: 1 };
}
return { kind: "persist_for_learning", policy_decision_id: input.policy_decision_id, allowed_ledgers: ["knowledge", "tool_procedure", "question", "phrasing", "task_design"], schema_version: 1 };
}
```
### 9.3 Scoped aggregate counters
```ts
export type ScopedAggregateCounter = {
counter_name: string;
value: number;
scope: MatrixLedgerPartition;
exportable_to_global: boolean;
schema_version: 1;
};
export function assertAggregateCounterExportable(counter: ScopedAggregateCounter): void {
const scope = counter.scope.learning_visibility_scope;
if (scope === "sealed" || scope === "firewalled" || scope === "private" || scope === "personal" || scope === "matter_scoped") {
if (counter.exportable_to_global) {
throw new MatrixValidationError({
reason_code: "matrix.aggregate_counter_scope_not_exportable",
metadata: { scope },
});
}
}
}
```
---
## 10. Signal inventory and typed ingestion
### 10.1 Single Matrix signal input union
```ts
export type MatrixSignalInput =
| { kind: "legacy_feedback_event"; event: FeedbackEventV1 }
| { kind: "evaluation_learning_signal"; envelope: EvaluationLearningSignalEnvelope }
| { kind: "doc23_task_invocation_signal"; signal: TaskInvocationLearningSignal }
| { kind: "doc73_special_signal"; signal: MatrixSpecialSignal };
export type MatrixSignalReceipt = {
signal_receipt_id: string;
input_kind: MatrixSignalInput["kind"];
persistence_decision: MatrixSignalPersistenceDecision;
learning_policy_decision: MatrixLearningPolicyDecision;
emitted_at: string;
schema_version: 1;
};
```
### 10.2 Payload registry
Every ledger-significant signal class is typed. Generic `Record<string, unknown>` payloads are debug-only and cannot enter ledger update functions.
```ts
export type MatrixSignalPhase =
| "legacy_runtime_feedback"
| "evaluation_learning_envelope"
| "doc73_special_signal"
| "debug_only"
| "aggregate_output";
export type MatrixSignalLedgerTarget =
| "knowledge"
| "tool_procedure"
| "question"
| "phrasing"
| "task_design"
| "none";
export type MatrixSignalPayloadRegistryEntry = {
signal_class: string;
phase: MatrixSignalPhase;
ledger_eligible: boolean;
default_target_ledger: MatrixSignalLedgerTarget;
payload_schema_ref: string;
requires_delivery_learning_boundary: boolean;
requires_policy_persistence_decision: boolean;
requires_partition: boolean;
schema_version: 1;
};
export const MATRIX_V64_LEGACY_SIGNAL_REGISTRY_ENTRIES: MatrixSignalPayloadRegistryEntry[] = [
{ signal_class: "explicit_rating", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "ExplicitRatingPayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "explicit_why_text", phase: "legacy_runtime_feedback", ledger_eligible: false, default_target_ledger: "none", payload_schema_ref: "WhyTextPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "knowledge_inbox_review", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "ReviewActionPayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "knowledge_inbox_why_text", phase: "legacy_runtime_feedback", ledger_eligible: false, default_target_ledger: "none", payload_schema_ref: "WhyTextPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "doc8_feedback_request_answered", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "question", payload_schema_ref: "DOC8FeedbackRequestAnsweredPayloadV3", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "procedure_outcome", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "ProcedureOutcomePayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "procedure_step_outcome", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "ProcedureStepOutcomePayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "tool_failure", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "ToolFailurePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "tool_wrong_output", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "ToolFailurePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "tool_access_gap", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "ToolFailurePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "constraint_violation", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "ConstraintViolationPayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "citation_provenance_mismatch", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "CitationProvenanceMismatchPayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "search_miss", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "SearchMissPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "near_miss_needed", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "NearMissNeededPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "fact_repetition", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "FactRepetitionPayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "routing_override", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "RoutingOverridePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "routing_mismatch", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "RoutingMismatchPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "question_outcome", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "question", payload_schema_ref: "QuestionOutcomePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "question_phrasing_outcome", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "phrasing", payload_schema_ref: "QuestionPhrasingOutcomePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "onboarding_probe_outcome", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "question", payload_schema_ref: "OnboardingProbeOutcomePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "standing_procedure_false_positive", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "question", payload_schema_ref: "StandingProcedureOutcomePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "standing_procedure_false_negative", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "question", payload_schema_ref: "StandingProcedureOutcomePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "user_override_action", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "UserOverrideActionPayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "model_override", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "ModelOverridePayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "card_inspection_action", phase: "legacy_runtime_feedback", ledger_eligible: false, default_target_ledger: "none", payload_schema_ref: "CardInspectionActionPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "inbox_suggestion_action", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "ReviewActionPayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "manual_recovery_after_miss", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "ManualRecoveryAfterMissPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "artifact_reopen_revision", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "ArtifactReopenRevisionPayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "external_truth_outcome", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "ExternalTruthOutcomePayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "terminal_action", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "TerminalActionPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "artifact_rework", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "ArtifactReworkPayloadV2", requires_delivery_learning_boundary: true, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "latency_to_first_token", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "tool_procedure", payload_schema_ref: "LatencyToFirstTokenPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "nightly_judgment", phase: "legacy_runtime_feedback", ledger_eligible: true, default_target_ledger: "knowledge", payload_schema_ref: "NightlyJudgmentPayloadV2", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
];
export const MATRIX_SIGNAL_PAYLOAD_REGISTRY: MatrixSignalPayloadRegistryEntry[] = [
...MATRIX_V64_LEGACY_SIGNAL_REGISTRY_ENTRIES,
// DOC23 Common Contracts envelope signals
{ signal_class: "OutcomeEvaluationSignal", phase: "evaluation_learning_envelope", ledger_eligible: true, default_target_ledger: "task_design", payload_schema_ref: "EvaluationLearningSignalEnvelope<OutcomeEvaluationSignal>", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "RepairCycleSignal", phase: "evaluation_learning_envelope", ledger_eligible: true, default_target_ledger: "task_design", payload_schema_ref: "EvaluationLearningSignalEnvelope<RepairCycleSignal>", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "TaskProcessGapSignal", phase: "evaluation_learning_envelope", ledger_eligible: true, default_target_ledger: "task_design", payload_schema_ref: "EvaluationLearningSignalEnvelope<TaskProcessGapSignal>", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "TaintClearanceSignal", phase: "evaluation_learning_envelope", ledger_eligible: true, default_target_ledger: "task_design", payload_schema_ref: "EvaluationLearningSignalEnvelope<TaintClearanceSignal>", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "HardCallResolutionSignal", phase: "evaluation_learning_envelope", ledger_eligible: true, default_target_ledger: "task_design", payload_schema_ref: "EvaluationLearningSignalEnvelope<HardCallResolutionSignal>", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "PromptComparisonSignal", phase: "evaluation_learning_envelope", ledger_eligible: true, default_target_ledger: "task_design", payload_schema_ref: "EvaluationLearningSignalEnvelope<PromptComparisonSignal>", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "UserActionSignal", phase: "evaluation_learning_envelope", ledger_eligible: true, default_target_ledger: "task_design", payload_schema_ref: "EvaluationLearningSignalEnvelope<UserActionSignal>", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
{ signal_class: "PatternPerformanceSignal", phase: "evaluation_learning_envelope", ledger_eligible: true, default_target_ledger: "task_design", payload_schema_ref: "EvaluationLearningSignalEnvelope<PatternPerformanceSignal>", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: true, requires_partition: true, schema_version: 1 },
// Debug-only and audit-only records
{ signal_class: "debug_note", phase: "debug_only", ledger_eligible: false, default_target_ledger: "none", payload_schema_ref: "DebugOnlyPayload", requires_delivery_learning_boundary: false, requires_policy_persistence_decision: false, requires_partition: false, schema_version: 1 },
];
export type DebugOnlyPayload = {
note: string;
source_surface: "inspector" | "developer_fixture" | "audit";
schema_version: 1;
};
export function registryEntryForMatrixSignal(signal_class: string): MatrixSignalPayloadRegistryEntry | null {
return MATRIX_SIGNAL_PAYLOAD_REGISTRY.find((r) => r.signal_class === signal_class) ?? null;
}
```
Add the following normative rule after the registry:
```text
A Matrix signal that is absent from `MATRIX_SIGNAL_PAYLOAD_REGISTRY` SHALL NOT update utility ledgers. It may be stored as audit-only only if EC policy permits audit persistence. Generic `Record<string, unknown>` payloads are ledger-ineligible unless wrapped by a typed registry entry.
Every `payload_schema_ref` in `MATRIX_V64_LEGACY_SIGNAL_REGISTRY_ENTRIES` SHALL resolve to a typed payload schema in BDSM §10, DOC8, or DOC72 before a signal is ledger-eligible. If the payload schema is unavailable at implementation time, EC may persist the signal as audit-only, but the signal MUST NOT update ledgers.
```
---
### 10.2A Feedback classification result and ledger routing
Feedback classification converts accepted raw feedback/signal input into typed ledger-routing decisions. Classification is owned by DOC8; EC writes classification artifacts; BDSM defines this contract and its fixture gate.
```ts
export type MatrixFailureCategory =
| "knowledge_accuracy_failure"
| "relevance_selection_failure"
| "procedure_quality_failure"
| "tool_selection_failure"
| "tool_reliability_failure"
| "routing_failure"
| "question_should_have_been_asked"
| "question_should_not_have_been_asked"
| "phrasing_clarity_issue"
| "style_preference_issue"
| "onboarding_probe_worthwhile"
| "onboarding_probe_wasted"
| "sequencing_failure"
| "incomplete_response"
| "unclassifiable";
export type MatrixFeedbackClassificationResult = {
classification_id: string;
signal_id: string;
signal_class: string;
failure_categories: MatrixFailureCategory[];
classifier_confidence: number;
purity: "clean" | "mixed" | "contaminated";
ledger_targets: MatrixSignalLedgerTarget[];
may_update_gate1_confidence: boolean;
may_update_utility_ledgers: boolean;
matrix_learning_policy_decision_id: string;
matrix_signal_persistence_decision_id: string;
partition: MatrixLedgerPartition;
context_signature: LearningContextSignature;
classified_at: string;
schema_version: 1;
};
export function classifyFeedbackForLedgerRouting(input: {
signal_class: string;
payload: unknown;
registry_entry: MatrixSignalPayloadRegistryEntry;
classifier_confidence: number;
purity: "clean" | "mixed" | "contaminated";
partition: MatrixLedgerPartition;
context_signature: LearningContextSignature;
learning_policy: MatrixLearningPolicyDecision;
persistence_decision: MatrixSignalPersistenceDecision;
}): MatrixFeedbackClassificationResult {
const confidence = clampUnitInterval(Number.isFinite(input.classifier_confidence) ? input.classifier_confidence : 0);
const ledger_targets = input.registry_entry.ledger_eligible
? [input.registry_entry.default_target_ledger].filter((x) => x !== "none")
: [];
const may_update_utility_ledgers =
input.registry_entry.ledger_eligible &&
input.learning_policy.kind === "on_policy_learning" &&
input.persistence_decision.kind === "persist_for_learning" &&
input.purity !== "contaminated" &&
confidence >= MIN_CLASSIFIER_CONFIDENCE_FOR_LEDGER_UPDATE;
const may_update_gate1_confidence =
may_update_utility_ledgers &&
["knowledge_accuracy_failure"].some((cat) => inferFailureCategories(input.signal_class, input.payload).includes(cat));
return {
classification_id: newId("matrix_feedback_classification"),
signal_id: extractSignalId(input.payload),
signal_class: input.signal_class,
failure_categories: inferFailureCategories(input.signal_class, input.payload),
classifier_confidence: confidence,
purity: input.purity,
ledger_targets,
may_update_gate1_confidence,
may_update_utility_ledgers,
matrix_learning_policy_decision_id: input.learning_policy.decision_id,
matrix_signal_persistence_decision_id: input.persistence_decision.decision_id,
partition: input.partition,
context_signature: input.context_signature,
classified_at: new Date().toISOString(),
schema_version: 1,
};
}
```
Rules:
- `may_update_gate1_confidence` SHALL be true only for proposition-level reliability failures linked to node/proposition evidence.
- `contaminated` feedback SHALL NOT update ledgers.
- Classification artifacts are append-only derived learning inputs written by EC.
- DOC8 implements `inferFailureCategories(...)` and `extractSignalId(...)`; BDSM defines the required input/output contract and fixtures.
### 10.2B Procedure outcome verification
Procedure verification records distinguish execution failure from stale or wrong procedure knowledge. They prevent raw tool errors from corrupting knowledge confidence or utility ledgers.
```ts
export type ProcedureVerificationOutcome =
| "verified_success"
| "verified_failure_node_content_wrong"
| "verified_failure_node_content_stale"
| "verified_failure_tool_or_environment"
| "unable_to_determine";
export type ProcedureVerificationRecordV2 = {
verification_id: string;
signal_id: string;
procedure_id: string;
step_id?: string;
outcome: ProcedureVerificationOutcome;
verification_method:
| "tool_receipt"
| "mcp_tool_result"
| "visual_confirmation"
| "user_confirmation"
| "deterministic_state_check"
| "llm_review_off_hot_path";
verified_evidence_refs: string[];
may_update_gate1_confidence: boolean;
may_update_tool_procedure_utility: boolean;
no_update_reason_code?:
| "matrix.procedure_verification_unable_to_determine"
| "matrix.procedure_failure_environment_only"
| "matrix.procedure_verification_policy_blocked";
partition: MatrixLedgerPartition;
context_signature: LearningContextSignature;
model_class: MatrixModelClass;
policy_decision_id: string;
observed_at: string;
schema_version: 2;
};
export function decideProcedureVerificationUpdate(input: {
outcome: ProcedureVerificationOutcome;
learning_policy: MatrixLearningPolicyDecision;
persistence_decision: MatrixSignalPersistenceDecision;
}): Pick<ProcedureVerificationRecordV2, "may_update_gate1_confidence" | "may_update_tool_procedure_utility" | "no_update_reason_code"> {
if (input.learning_policy.kind !== "on_policy_learning" || input.persistence_decision.kind !== "persist_for_learning") {
return {
may_update_gate1_confidence: false,
may_update_tool_procedure_utility: false,
no_update_reason_code: "matrix.procedure_verification_policy_blocked",
};
}
if (input.outcome === "unable_to_determine") {
return {
may_update_gate1_confidence: false,
may_update_tool_procedure_utility: false,
no_update_reason_code: "matrix.procedure_verification_unable_to_determine",
};
}
if (input.outcome === "verified_failure_tool_or_environment") {
return {
may_update_gate1_confidence: false,
may_update_tool_procedure_utility: true,
no_update_reason_code: "matrix.procedure_failure_environment_only",
};
}
return {
may_update_gate1_confidence:
input.outcome === "verified_failure_node_content_wrong" ||
input.outcome === "verified_failure_node_content_stale",
may_update_tool_procedure_utility: true,
};
}
```
Rules:
- `unable_to_determine` SHALL NOT update any ledger.
- Tool/environment-only failures MAY update tool/procedure utility but SHALL NOT update DOC72 confidence.
- Node-content-wrong/stale failures MAY feed Gate 1 only when classified and evidence-linked.
- Procedure verification records are append-only derived learning artifacts written by EC.
---
### 10.3 DOC73 / PropA special signal union
```ts
export type SyntheticGoldProvenanceClass =
| "synthetic_gold_human_verified"
| "synthetic_gold_automated_only"
| "synthetic_gold_mixed";
export type AvapoTierClassification = "tier_a" | "tier_b" | "tier_c" | "rejected";
export type MatrixSpecialSignal =
| { signal_kind: "avapo_c1_schema_fixture_diff"; result: "pass" | "fail"; fixture_id: string; scope: MatrixLedgerPartition }
| { signal_kind: "avapo_c2_exposure_normalized_rejection"; rejection_rate: number; scope: MatrixLedgerPartition }
| { signal_kind: "avapo_c3_synthetic_gold_provenance"; provenance_class: SyntheticGoldProvenanceClass; scope: MatrixLedgerPartition }
| { signal_kind: "avapo_c4_counterfactual_integration"; counterfactual_delta: number; scope: MatrixLedgerPartition }
| { signal_kind: "avapo_c5_tier_classification"; tier: AvapoTierClassification; scope: MatrixLedgerPartition }
| { signal_kind: "cluster_thrash_rate"; cluster_id: string; thrash_rate: number; scope: MatrixLedgerPartition }
| { signal_kind: "critique_utility"; outcome: "true_positive" | "false_positive" | "false_negative"; scope: MatrixLedgerPartition }
| { signal_kind: "cross_firewall_identity_attempt"; source_firewall_id: string; target_firewall_id: string; scope: MatrixLedgerPartition }
| { signal_kind: "ema_threshold_update"; threshold_name: string; old_value: number; new_value: number; alpha: number; scope: MatrixLedgerPartition };
export function validateMatrixSpecialSignal(s: MatrixSpecialSignal): void {
if (s.signal_kind === "avapo_c2_exposure_normalized_rejection" && (s.rejection_rate < 0 || s.rejection_rate > 1)) {
throw new MatrixValidationError({ reason_code: "matrix.special_signal_rate_out_of_range", metadata: { signal_kind: s.signal_kind, observed_rate: s.rejection_rate } });
}
if (s.signal_kind === "cluster_thrash_rate" && (s.thrash_rate < 0 || s.thrash_rate > 1)) {
throw new MatrixValidationError({ reason_code: "matrix.special_signal_rate_out_of_range", metadata: { signal_kind: s.signal_kind, observed_rate: s.thrash_rate } });
}
if (s.signal_kind === "avapo_c4_counterfactual_integration" && (s.counterfactual_delta < -1 || s.counterfactual_delta > 1)) {
throw new MatrixValidationError({ reason_code: "matrix.special_signal_delta_out_of_range", metadata: { signal_kind: s.signal_kind, observed_delta: s.counterfactual_delta } });
}
if (s.signal_kind === "ema_threshold_update" && (s.alpha < 0 || s.alpha > 1)) {
throw new MatrixValidationError({ reason_code: "matrix.ema_alpha_out_of_range", metadata: { threshold_name: s.threshold_name, alpha: s.alpha } });
}
}
```
### 10.4 DOC23 envelope consumption
BDSM/DOC8 consumes all eight Phase-1 signal types through `EvaluationLearningSignalEnvelope`, produces utility bundles, threshold-gates pattern surfacing, and emits aggregate `TaskDesignCorrelationSignal`. BDSM MUST NOT emit runtime `TaskProcessGapSignal`; runtime process-gap signals are emitted by Task Agent or Revisor.
```ts
export const MatrixEvaluationSignalConsumerConfig = {
consume_signal_types: [
"OutcomeEvaluationSignal",
"RepairCycleSignal",
"TaskProcessGapSignal",
"TaintClearanceSignal",
"HardCallResolutionSignal",
"PromptComparisonSignal",
"UserActionSignal",
"PatternPerformanceSignal",
],
emit_aggregate_signal: "TaskDesignCorrelationSignal",
emit_runtime_task_process_gap_signal: false,
schema_version: 1,
} as const;
```
### 10.5 Domain signal profile binding
Terminal outcome mappings and work-domain interpretations are DOC72/DOC8-owned domain signal profiles. BDSM records references, not ad hoc domain-specific enums.
```ts
export type DomainSignalProfileRef = {
profile_id: string;
owner_doc: "DOC72" | "DOC8";
version: string;
schema_version: 1;
};
export type MatrixDomainSignalBinding = {
context_signature: LearningContextSignature;
terminal_outcome_mapping_ref: DomainSignalProfileRef;
work_domain: string;
schema_version: 1;
};
export function assertDomainSignalProfileResolved(ref: DomainSignalProfileRef): void {
if (!ref.profile_id || !ref.version) {
throw new MatrixValidationError({
reason_code: "matrix.domain_signal_profile_unresolved",
metadata: { profile_id: ref.profile_id, version: ref.version },
});
}
}
```
---
### 10.6 EMA threshold registry and receipts
```ts
export type MatrixEmaThresholdKey =
| "intent_disclosure_confidence_threshold"
| "question_expected_information_value_threshold"
| "feedback_request_priority_threshold"
| "pattern_auto_apply_lower95_threshold";
export type MatrixEmaThresholdRegistryEntry = {
threshold_key: MatrixEmaThresholdKey;
current_value: number;
min_value: number;
max_value: number;
alpha: number;
partition: MatrixLedgerPartition;
model_class: MatrixModelClass;
updated_at: string;
policy_decision_id: string;
schema_version: 1;
};
export type MatrixEmaThresholdUpdateReceipt = {
receipt_id: string;
threshold_key: MatrixEmaThresholdKey;
previous_value: number;
observed_signal_value: number;
next_value: number;
alpha: number;
partition_hash: string;
policy_decision_id: string;
reason_code: "matrix.ema_threshold_updated" | "matrix.ema_threshold_update_policy_blocked";
updated_at: string;
schema_version: 1;
};
export function updateMatrixEmaThreshold(input: {
entry: MatrixEmaThresholdRegistryEntry;
observed_signal_value: number;
policy_allows_update: boolean;
}): MatrixEmaThresholdUpdateReceipt {
if (!input.policy_allows_update) {
return {
receipt_id: newId("matrix_ema_update_receipt"),
threshold_key: input.entry.threshold_key,
previous_value: input.entry.current_value,
observed_signal_value: input.observed_signal_value,
next_value: input.entry.current_value,
alpha: input.entry.alpha,
partition_hash: hashPartition(input.entry.partition),
policy_decision_id: input.entry.policy_decision_id,
reason_code: "matrix.ema_threshold_update_policy_blocked",
updated_at: new Date().toISOString(),
schema_version: 1,
};
}
const alpha = clampUnitInterval(input.entry.alpha);
const observed = clampUnitInterval(input.observed_signal_value);
const next = clampUnitInterval((alpha * observed) + ((1 - alpha) * input.entry.current_value));
return {
receipt_id: newId("matrix_ema_update_receipt"),
threshold_key: input.entry.threshold_key,
previous_value: input.entry.current_value,
observed_signal_value: observed,
next_value: Math.max(input.entry.min_value, Math.min(input.entry.max_value, next)),
alpha,
partition_hash: hashPartition(input.entry.partition),
policy_decision_id: input.entry.policy_decision_id,
reason_code: "matrix.ema_threshold_updated",
updated_at: new Date().toISOString(),
schema_version: 1,
};
}
```
Add rule:
```text
BDSM defines the EMA threshold registry and receipts. DOC8 implements threshold update computation. EC writes updated registry entries and receipts. EMA threshold updates SHALL be partitioned by learning visibility scope and model class unless a PropA/EC approval receipt permits broader aggregation.
```
---
## 11. Reward, metrics, and explicit feedback
### 11.1 Reward contribution contract
No-signal rows do not enter denominators.
```ts
export type RewardContribution = {
source: "explicit_feedback" | "answer_quality" | "downstream_result" | "terminal_outcome" | "latency";
weight: number;
value: number | null;
status: "signal" | "no_signal";
};
export type WeightedRewardResult =
| { kind: "computed"; reward: number; signal_weight: number; schema_version: 1 }
| { kind: "no_signal"; reward: 0; schema_version: 1 };
export function weightedReward(contribs: RewardContribution[]): WeightedRewardResult {
const signal = contribs.filter(c =>
c.status === "signal" &&
c.value !== null &&
Number.isFinite(c.value) &&
Number.isFinite(c.weight) &&
c.weight > 0,
);
if (signal.length === 0) return { kind: "no_signal", reward: 0, schema_version: 1 };
const weighted = signal.reduce((s, c) => s + c.weight * clampSigned(c.value as number), 0);
const denom = signal.reduce((s, c) => s + c.weight, 0);
return { kind: "computed", reward: Math.tanh(weighted / denom), signal_weight: denom, schema_version: 1 };
}
```
Unknown, no-effect, no-observable-outcome, not-measured, and dropped-by-final-prompt-owner outcomes SHALL be represented as `status: "no_signal"` unless a spec section explicitly assigns a nonzero weak positive or weak negative value.
### 11.2 Structured metric values
```ts
export type MatrixMetricValue = {
value: number | null;
numerator: number | null;
denominator: number | null;
formula_id: string;
range: "signed_unit" | "unit_interval" | "unbounded";
status: "computed" | "undefined_denominator" | "invalid_input" | "not_computed";
null_reason?: "denominator_zero" | "denominator_negative" | "non_finite_input" | "invalid_latency_budget";
schema_version: 1;
};
export function matrixSafeRatio(input: {
numerator: number;
denominator: number;
formula_id: string;
range: "signed_unit" | "unit_interval" | "unbounded";
}): MatrixMetricValue {
if (!Number.isFinite(input.numerator) || !Number.isFinite(input.denominator)) {
return { value: null, numerator: Number.isFinite(input.numerator) ? input.numerator : null, denominator: Number.isFinite(input.denominator) ? input.denominator : null, formula_id: input.formula_id, range: input.range, status: "invalid_input", null_reason: "non_finite_input", schema_version: 1 };
}
if (input.denominator === 0) {
return { value: null, numerator: input.numerator, denominator: input.denominator, formula_id: input.formula_id, range: input.range, status: "undefined_denominator", null_reason: "denominator_zero", schema_version: 1 };
}
if (input.denominator < 0) {
return { value: null, numerator: input.numerator, denominator: input.denominator, formula_id: input.formula_id, range: input.range, status: "invalid_input", null_reason: "denominator_negative", schema_version: 1 };
}
const raw = input.numerator / input.denominator;
const value = input.range === "signed_unit" ? clampSigned(raw) : input.range === "unit_interval" ? clampUnit(raw) : raw;
return { value, numerator: input.numerator, denominator: input.denominator, formula_id: input.formula_id, range: input.range, status: "computed", schema_version: 1 };
}
export function matrixSafeLatencyScore(input: { latency_ms: number | null; budget_ms: number; formula_id: string }): MatrixMetricValue {
if (!Number.isFinite(input.budget_ms) || input.budget_ms <= 0) {
return { value: null, numerator: null, denominator: Number.isFinite(input.budget_ms) ? input.budget_ms : null, formula_id: input.formula_id, range: "signed_unit", status: "invalid_input", null_reason: input.budget_ms === 0 ? "denominator_zero" : "invalid_latency_budget", schema_version: 1 };
}
if (input.latency_ms === null || !Number.isFinite(input.latency_ms) || input.latency_ms < 0) {
return { value: null, numerator: null, denominator: input.budget_ms, formula_id: input.formula_id, range: "signed_unit", status: "not_computed", null_reason: "invalid_latency_budget", schema_version: 1 };
}
return matrixSafeRatio({ numerator: input.budget_ms - input.latency_ms, denominator: input.budget_ms, formula_id: input.formula_id, range: "signed_unit" });
}
```
### 11.3 Strict explicit rating parser
```ts
export type ExplicitRatingResult =
| { kind: "computed"; value: number; original: string; scale_min: number; scale_max: number; schema_version: 1 }
| { kind: "invalid"; reason_code: "matrix.invalid_explicit_rating"; original: string; schema_version: 1 };
export function parseStrictDecimal(text: string): number | null {
const trimmed = text.trim();
if (!/^[+-]?(?:\d+|\d+\.\d+|\.\d+)$/.test(trimmed)) return null;
const parsed = Number(trimmed);
return Number.isFinite(parsed) ? parsed : null;
}
export function normalizeExplicitRating(input: { rating_text: string; scale_min: number; scale_max: number }): ExplicitRatingResult {
const raw = parseStrictDecimal(input.rating_text);
if (raw === null) return { kind: "invalid", reason_code: "matrix.invalid_explicit_rating", original: input.rating_text, schema_version: 1 };
if (!Number.isFinite(input.scale_min) || !Number.isFinite(input.scale_max) || input.scale_max <= input.scale_min) {
return { kind: "invalid", reason_code: "matrix.invalid_explicit_rating", original: input.rating_text, schema_version: 1 };
}
if (raw < input.scale_min || raw > input.scale_max) {
return { kind: "invalid", reason_code: "matrix.invalid_explicit_rating", original: input.rating_text, schema_version: 1 };
}
return { kind: "computed", value: (raw - input.scale_min) / (input.scale_max - input.scale_min), original: input.rating_text, scale_min: input.scale_min, scale_max: input.scale_max, schema_version: 1 };
}
```
### 11.4 Canonical semantic hashes
BDSM hash inputs SHALL use pinned canonical JSON, never raw `JSON.stringify`.
```ts
export type CanonicalJsonFunction = (value: unknown) => string;
export declare const canonicalJson: CanonicalJsonFunction;
export declare function sha256Hex(s: string): string;
export function stableSemanticHash(value: unknown): string {
return sha256Hex(canonicalJson(value));
}
export type CriterionHashMaterialV2 = {
criterion_text_normalized_preserving_negation: string;
scoring_basis: string;
scoring_scale?: string;
pass_threshold?: number;
target_scope_kind?: string;
criterion_version: string;
normalization_version: string;
producer_kind: string;
required_claim_types: string[];
evidence_requirements: string[];
model_class?: "cheap_local" | "cheap_api" | "medium" | "expensive_frontier" | "unknown";
schema_version: 2;
};
export function computeCriterionSemanticsHashV2(material: CriterionHashMaterialV2): string {
return stableSemanticHash({
...material,
required_claim_types: [...material.required_claim_types].sort(),
evidence_requirements: [...material.evidence_requirements].sort(),
});
}
```
---
## 12. Math contracts
### 12.0 Implementation owner for Matrix math
This section defines the normative Matrix math contracts, function signatures, edge-case behavior, and acceptance fixtures. **DOC8 implements the learning computation.** EC writes derived learning artifacts and compiled views. BDSM does not become an implementation runtime or durable writer.
The implementation owner split is:
| Surface | Contract owner | Implementation owner | Durable writer |
|---|---|---|---|
| reward/no-signal denominator | BDSM | DOC8 | EC |
| beta-like utility updates | BDSM | DOC8 | EC |
| attribution confidence | BDSM | DOC8 | EC |
| Shapley/top-K/residual attribution | BDSM | DOC8 | EC |
| hierarchical smoothing | BDSM | DOC8 | EC |
| compiled bundle generation | BDSM | DOC8 + EC orchestrator | EC |
Coding agents SHALL implement the formulas in DOC8 or a DOC8-owned package and SHALL import them into EC compilation jobs. They SHALL NOT create a second BDSM-owned computation service.
---
### 12.1 Beta-like update and safe decay
```ts
export type MatrixRewardObservation =
| { kind: "observed_reward"; reward: number }
| { kind: "observed_neutral"; reward: 0 }
| { kind: "no_signal" }
| { kind: "decay_only" };
export type MatrixBetaLikePair = {
positive_evidence: number;
negative_evidence: number;
prior_positive: number;
prior_negative: number;
max_evidence_mass: number;
degraded_reason_codes?: string[];
schema_version: 2;
};
export function matrixDecayFactor(input: { delta_ms: number; half_life_ms: number }): number {
if (!Number.isFinite(input.delta_ms) || !Number.isFinite(input.half_life_ms)) return 1;
if (input.delta_ms <= 0) return 1;
if (input.half_life_ms <= 0) return 0;
return Math.exp((-Math.log(2) * input.delta_ms) / input.half_life_ms);
}
export function updateMatrixBetaLikePair(input: {
current: MatrixBetaLikePair;
observation: MatrixRewardObservation;
decay: number;
}): MatrixBetaLikePair {
const decay = Number.isFinite(input.decay) ? clampUnit(input.decay) : 1;
let pos = Math.max(0, input.current.positive_evidence);
let neg = Math.max(0, input.current.negative_evidence);
const degraded_reason_codes = [...(input.current.degraded_reason_codes ?? [])];
if (input.observation.kind === "no_signal") {
return { ...input.current, positive_evidence: pos, negative_evidence: neg, degraded_reason_codes, schema_version: 2 };
}
pos *= decay;
neg *= decay;
if (input.observation.kind === "observed_reward") {
if (!Number.isFinite(input.observation.reward)) {
degraded_reason_codes.push("matrix.reward_nonfinite_input");
return { ...input.current, positive_evidence: pos, negative_evidence: neg, degraded_reason_codes, schema_version: 2 };
}
const r = clampSigned(input.observation.reward);
pos += Math.max(0, r);
neg += Math.max(0, -r);
}
const cap = Math.max(1, Number.isFinite(input.current.max_evidence_mass) ? input.current.max_evidence_mass : 1);
const mass = pos + neg;
if (mass > cap) {
const scale = cap / mass;
pos *= scale;
neg *= scale;
}
return { ...input.current, positive_evidence: pos, negative_evidence: neg, degraded_reason_codes, schema_version: 2 };
}
export type BetaLikeMeanResult =
| { kind: "computed"; mean: number; support: number; schema_version: 1 }
| { kind: "no_evidence"; schema_version: 1 };
export function betaLikeMean(pair: MatrixBetaLikePair): BetaLikeMeanResult {
const a = Math.max(0, pair.prior_positive) + Math.max(0, pair.positive_evidence);
const b = Math.max(0, pair.prior_negative) + Math.max(0, pair.negative_evidence);
const support = a + b;
if (!Number.isFinite(support) || support <= 0) return { kind: "no_evidence", schema_version: 1 };
return { kind: "computed", mean: a / support, support, schema_version: 1 };
}
```
### 12.2 Attribution confidence
```ts
export const ATTRIBUTION_VARIANCE_NORM_DIVISOR = 0.75;
export const ATTRIBUTION_SUPPORT_SATURATION_HALF_LIFE = 10;
export type AttributionConfidenceResult =
| { kind: "computed"; confidence: number; sign_stability: number; support_term: number; schema_version: 1 }
| { kind: "insufficient_phi_history"; confidence: 0; schema_version: 1 };
export function computeAttributionConfidence(input: {
support_count: number;
mean_purity_multiplier: number;
mean_classifier_confidence: number;
recent_phi_values: number[];
}): AttributionConfidenceResult {
const values = input.recent_phi_values.filter(Number.isFinite).map(clampSigned);
if (values.length === 0) return { kind: "insufficient_phi_history", confidence: 0, schema_version: 1 };
const support_term = 1 - Math.exp(-Math.max(0, input.support_count) / ATTRIBUTION_SUPPORT_SATURATION_HALF_LIFE);
const purity = clampUnit(Number.isFinite(input.mean_purity_multiplier) ? input.mean_purity_multiplier : 0);
const classifier = clampUnit(Number.isFinite(input.mean_classifier_confidence) ? input.mean_classifier_confidence : 0);
const non_zero_signs = values.filter(v => Math.abs(v) >= 0.05).map(v => Math.sign(v));
const sign_stability = non_zero_signs.length === 0
? 0
: Math.max(non_zero_signs.filter(s => s > 0).length, non_zero_signs.filter(s => s < 0).length) / non_zero_signs.length;
const mean = values.reduce((a, b) => a + b, 0) / values.length;
const variance = values.reduce((s, v) => s + Math.pow(v - mean, 2), 0) / values.length;
const magnitude_consistency = 1 - Math.min(1, Math.sqrt(variance) / ATTRIBUTION_VARIANCE_NORM_DIVISOR);
return { kind: "computed", confidence: clampUnit(support_term * purity * classifier * sign_stability * magnitude_consistency), sign_stability, support_term, schema_version: 1 };
}
```
### 12.3 Shapley top-K approximation
```ts
export type ShapleyRewardBasis =
| { kind: "raw_reward"; reward: number }
| { kind: "advantage_over_baseline"; observed_reward: number; baseline_expected_reward: number };
export type ShapleyFeatureInput = {
feature_id: string;
feature_value: number;
pinned?: boolean;
};
export type ShapleyApproximationResult = {
phi_by_feature_id: Record<string, number>;
residual_phi: number;
residual_feature_ids: string[];
residual_ledger_attributable: false;
degraded_reason_codes: string[];
reward_basis: ShapleyRewardBasis;
schema_version: 1;
};
export function rewardFromBasis(basis: ShapleyRewardBasis): number {
if (basis.kind === "raw_reward") return basis.reward;
return basis.observed_reward - basis.baseline_expected_reward;
}
export function approximateShapleyTopK(input: {
features: ShapleyFeatureInput[];
reward_basis: ShapleyRewardBasis;
top_k: number;
}): ShapleyApproximationResult {
const reward = rewardFromBasis(input.reward_basis);
if (!Number.isFinite(reward)) {
return { phi_by_feature_id: {}, residual_phi: 0, residual_feature_ids: [], residual_ledger_attributable: false, degraded_reason_codes: ["matrix.shapley_nonfinite_reward"], reward_basis: input.reward_basis, schema_version: 1 };
}
const dynamic = input.features.filter(f => !f.pinned && Number.isFinite(f.feature_value));
if (dynamic.length === 0 || dynamic.every(f => Math.abs(f.feature_value) < 1e-9)) {
return { phi_by_feature_id: {}, residual_phi: 0, residual_feature_ids: [], residual_ledger_attributable: false, degraded_reason_codes: ["matrix.shapley_empty_or_pinned_zero_context"], reward_basis: input.reward_basis, schema_version: 1 };
}
const ranked = [...dynamic].sort((a, b) => {
const magnitude = Math.abs(b.feature_value) - Math.abs(a.feature_value);
return magnitude !== 0 ? magnitude : a.feature_id.localeCompare(b.feature_id);
});
const k = Math.max(0, Math.floor(input.top_k));
const top = ranked.slice(0, k);
const residual = ranked.slice(k);
if (top.length === 0) {
return { phi_by_feature_id: {}, residual_phi: reward, residual_feature_ids: residual.map(f => f.feature_id), residual_ledger_attributable: false, degraded_reason_codes: ["matrix.shapley_top_k_zero"], reward_basis: input.reward_basis, schema_version: 1 };
}
const top_mass = top.reduce((s, f) => s + Math.abs(f.feature_value), 0);
const residual_mass = residual.reduce((s, f) => s + Math.abs(f.feature_value), 0);
const total_mass = top_mass + residual_mass;
const phi_by_feature_id: Record<string, number> = {};
for (const f of top) {
phi_by_feature_id[f.feature_id] = total_mass === 0 ? 0 : reward * (Math.abs(f.feature_value) / total_mass);
}
const residual_phi = total_mass === 0 ? 0 : reward * (residual_mass / total_mass);
return {
phi_by_feature_id,
residual_phi,
residual_feature_ids: residual.map(f => f.feature_id),
residual_ledger_attributable: false,
degraded_reason_codes: residual.length > 0 ? ["matrix.shapley_residual_bucket_not_ledger_attributable"] : [],
reward_basis: input.reward_basis,
schema_version: 1,
};
}
```
Residual `residual_phi` MAY be used for diagnostics. It MUST NOT write to per-node, per-card, per-procedure, per-question, or per-phrasing utility ledgers unless a later governed formula explicitly redistributes it.
### 12.4 True hierarchical smoothing
```ts
export type HierarchicalUtilityLayer = {
u?: number;
n: number;
k: number;
layer_name: "global" | "domain" | "task" | "exact";
};
export type HierarchicalSmoothedUtilityResult = {
value: number;
layer_values: Record<string, number>;
degraded_reason_codes: string[];
schema_version: 1;
};
function validLayer(layer: HierarchicalUtilityLayer): boolean {
return Number.isFinite(layer.u ?? NaN) && Number.isFinite(layer.n) && layer.n > 0 && Number.isFinite(layer.k) && layer.k > 0;
}
const SMOOTHING_INSUFFICIENT_SUPPORT_REASON: Record<HierarchicalUtilityLayer["layer_name"], string> = {
global: "matrix.smoothing_global_insufficient_support",
domain: "matrix.smoothing_domain_insufficient_support",
task: "matrix.smoothing_task_insufficient_support",
exact: "matrix.smoothing_exact_insufficient_support",
};
function shrinkTowardParent(parent: number, layer: HierarchicalUtilityLayer, degraded: string[]): number {
if (!validLayer(layer)) {
degraded.push(SMOOTHING_INSUFFICIENT_SUPPORT_REASON[layer.layer_name]);
return parent;
}
const w = layer.n / (layer.n + layer.k);
return clampSigned((layer.u as number) * w + parent * (1 - w));
}
export function computeHierarchicalSmoothedUtility(input: {
neutral_prior?: number;
global: HierarchicalUtilityLayer;
domain: HierarchicalUtilityLayer;
task: HierarchicalUtilityLayer;
exact: HierarchicalUtilityLayer;
}): HierarchicalSmoothedUtilityResult {
const degraded_reason_codes: string[] = [];
const neutral = Number.isFinite(input.neutral_prior ?? NaN) ? clampSigned(input.neutral_prior as number) : 0;
const global = shrinkTowardParent(neutral, input.global, degraded_reason_codes);
const domain = shrinkTowardParent(global, input.domain, degraded_reason_codes);
const task = shrinkTowardParent(domain, input.task, degraded_reason_codes);
const exact = shrinkTowardParent(task, input.exact, degraded_reason_codes);
return { value: exact, layer_values: { neutral, global, domain, task, exact }, degraded_reason_codes: [...new Set(degraded_reason_codes)], schema_version: 1 };
}
```
### 12.5 DOC24 confidence adjustment adoption
BDSM does not define a divergent confidence-adjustment function. Where Matrix adjusts DOC24/DOC72 confidence-like effective evidence for runtime ranking, it SHALL consume DOC24's safe clamp semantics. If a local fixture reimplements the body for testability, it must preserve finite guards, epsilon floor after scaling, evidence-mass cap, and the `matrix.effective_beta_clamped` reason code.
---
## 13. Utility records and aggregates
### 13.1 Utility records
```ts
export type KnowledgeUtilityRecordV3 = {
node_id: string;
ledger_key: PartitionedUtilityLedgerKey;
utility_pair: MatrixBetaLikePair;
support_count: number;
last_updated_at: string;
schema_version: 3;
};
export type ToolProcedureUtilityRecordV2 = {
target_kind: "tool" | "procedure";
target_id: string;
ledger_key: PartitionedUtilityLedgerKey;
utility_pair: MatrixBetaLikePair;
support_count: number;
doc24_tool_experience_view_ref?: string;
reliability_snapshot?: ToolReliabilitySnapshot;
selection_decision_ids: string[];
last_updated_at: string;
schema_version: 2;
};
export type QuestionUtilityRecordV2 = {
question_kind: SharedQuestionKind;
ledger_key: PartitionedUtilityLedgerKey;
utility_pair: MatrixBetaLikePair;
support_count: number;
last_updated_at: string;
schema_version: 2;
};
export type PhrasingUtilityRecordV2 = {
profile_id: string;
question_kind: SharedQuestionKind;
ledger_key: PartitionedUtilityLedgerKey;
utility_pair: MatrixBetaLikePair;
support_count: number;
last_updated_at: string;
schema_version: 2;
};
```
### 13.2 Tool reliability snapshots
BDSM does not update DOC24 tool reliability counters. It may store a derived snapshot for compiler use.
```ts
export type ToolReliabilitySnapshot = {
source_view_ref: string;
lower_bound_reliability: number | null;
support_count: number;
snapshotted_at: string;
may_update_doc24_reliability: false;
schema_version: 1;
};
export const DEFAULT_RELIABILITY_PRIOR_POSITIVE = 1;
export const DEFAULT_RELIABILITY_PRIOR_NEGATIVE = 1;
export const MIN_SNAPSHOT_SUPPORT = 5;
export const MIN_LOCAL_SUPPORT = 5;
export const DEFAULT_COLD_START_RELIABILITY_PENALTY = 0.25;
export const MIN_SNAPSHOT_SUPPORT_FOR_RELIABILITY = MIN_SNAPSHOT_SUPPORT;
export const MIN_LOCAL_SUPPORT_FOR_RELIABILITY = MIN_LOCAL_SUPPORT;
export declare function computeBetaLowerBound95(input: {
positive_evidence: number;
negative_evidence: number;
prior_positive: number;
prior_negative: number;
}): number;
export function computeLowerBoundReliability(record: ToolProcedureUtilityRecordV2): number | null {
if (record.reliability_snapshot && record.reliability_snapshot.support_count >= MIN_SNAPSHOT_SUPPORT_FOR_RELIABILITY) {
return record.reliability_snapshot.lower_bound_reliability;
}
if (record.support_count < MIN_LOCAL_SUPPORT_FOR_RELIABILITY) return null;
return computeBetaLowerBound95({
positive_evidence: record.utility_pair.positive_evidence,
negative_evidence: record.utility_pair.negative_evidence,
prior_positive: DEFAULT_RELIABILITY_PRIOR_POSITIVE,
prior_negative: DEFAULT_RELIABILITY_PRIOR_NEGATIVE,
});
}
export function computeReliabilityPenalty(input: {
lower_bound_reliability: number | null;
support_count: number;
explicitly_requested: boolean;
cold_start_penalty?: number;
}): number {
if (input.explicitly_requested) return 0;
if (input.support_count === 0 || input.lower_bound_reliability === null) {
return input.cold_start_penalty ?? DEFAULT_COLD_START_RELIABILITY_PENALTY;
}
return clampUnit(Math.max(0, 0.5 - input.lower_bound_reliability) * 2);
}
```
### 13.3 Utility aggregate record
```ts
export type UtilityAggregateRecordV3 = {
target_ledger: "knowledge" | "tool_procedure" | "question" | "phrasing" | "task_design";
target_id: string;
context_signature: LearningContextSignature;
partition: MatrixLedgerPartition;
exact?: { key: string; u: number; n: number };
task?: { key: string; u: number; n: number };
domain?: { key: string; u: number; n: number };
global?: { key: "global"; u: number; n: number };
contribution_summary_hash: string;
last_updated_at: string;
schema_version: 3;
};
export function assertUtilityAggregateGlobalExportable(record: UtilityAggregateRecordV3): void {
if (record.global && record.partition.learning_visibility_scope !== "global_shared") {
throw new MatrixValidationError({
reason_code: "matrix.global_aggregate_requires_global_shared_scope",
metadata: { target_ledger: record.target_ledger, scope: record.partition.learning_visibility_scope },
});
}
}
```
---
## 14. Compiled bundle generation and EC orchestration
### 14.1 Required bundle kinds
```ts
export const RequiredMatrixBundleKinds = [
"interaction_policy",
"knowledge_utility",
"tool_procedure_utility",
"phrasing_policy",
] as const;
export type MatrixBundleKind = typeof RequiredMatrixBundleKinds[number];
export type MatrixBundleGenerationState = "staged" | "active" | "failed" | "degraded_inactive" | "superseded" | "staged_inactive";
export type MatrixBundleGenerationManifest = {
bundle_generation_id: string;
matrix_compiler_version: string;
compiled_from: {
policy_generation_id: string;
ec_settings_generation_id: string;
propA_policy_snapshot_id: string;
doc72_graph_snapshot_id: string;
kda_registry_generation_id: string;
ledger_snapshot_hash: string;
};
bundle_hashes: Record<MatrixBundleKind, string>;
bundle_refs: Record<MatrixBundleKind, string>;
state: MatrixBundleGenerationState;
activated_at?: string;
blocked_reason_codes: string[];
schema_version: 1;
};
export function validateBundleGenerationCanActivate(input: {
manifest: MatrixBundleGenerationManifest;
current: MatrixBundleGenerationManifest["compiled_from"];
}): void {
const m = input.manifest;
if (m.state !== "staged") {
throw new MatrixValidationError({ reason_code: "matrix.bundle_activation_requires_staged_state", metadata: { state: m.state } });
}
for (const kind of RequiredMatrixBundleKinds) {
if (!m.bundle_hashes[kind]) {
throw new MatrixValidationError({ reason_code: "matrix.bundle_missing_required_kind", metadata: { bundle_kind: kind } });
}
if (!m.bundle_refs[kind]) {
throw new MatrixValidationError({ reason_code: "matrix.bundle_missing_required_kind", metadata: { bundle_kind: kind } });
}
}
for (const k of Object.keys(input.current) as Array<keyof MatrixBundleGenerationManifest["compiled_from"]>) {
if (m.compiled_from[k] !== input.current[k]) {
throw new MatrixValidationError({ reason_code: "matrix.bundle_activation_stale_snapshot", metadata: { snapshot_field: k } });
}
}
}
```
### 14.1A Concrete compiled bundle payload schemas
```ts
export type MatrixCompiledBundleKind =
| "interaction_policy"
| "knowledge_utility"
| "tool_procedure_utility"
| "phrasing_policy";
export type CompiledInteractionPolicyBundleV2 = {
bundle_kind: "interaction_policy";
bundle_generation_id: string;
context_signature_key: string;
optional_question_mean: number;
suppressible_question_kinds: SharedQuestionKind[];
non_suppressible_blocking_kinds: SharedQuestionKind[];
recommended_action: "ask_normally" | "defer_non_blocking" | "suppress_non_blocking";
supporting_question_record_ids: string[];
policy_generation_id: string;
schema_version: 2;
};
export type CompiledKnowledgeUtilityBundleV2 = {
bundle_kind: "knowledge_utility";
bundle_generation_id: string;
entries: Array<{
card_stable_key?: string;
node_id?: string;
context_signature_key: string;
normalized_utility: number;
attribution_confidence: number;
directive_floor?: ForceLevel;
directive_rationale_code?: string;
support_count: number;
partition_hash: string;
}>;
policy_generation_id: string;
schema_version: 2;
};
export type CompiledToolProcedureUtilityBundleV2 = {
bundle_kind: "tool_procedure_utility";
bundle_generation_id: string;
entries: Array<{
target_kind: "tool" | "procedure" | "capability";
target_id: string;
context_signature_key: string;
utility_mean: number;
utility_lower95?: number;
reliability_snapshot_ref?: string;
cold_start_penalty_applies: boolean;
support_count: number;
partition_hash: string;
}>;
policy_generation_id: string;
schema_version: 2;
};
export type CompiledPhrasingPolicyBundleV2 = {
bundle_kind: "phrasing_policy";
bundle_generation_id: string;
entries: Array<{
question_kind: SharedQuestionKind;
context_signature_key: string;
preferred_profile_id?: string;
preferred_shell_id?: string;
utility_mean: number;
support_count: number;
partition_hash: string;
}>;
policy_generation_id: string;
schema_version: 2;
};
export type MatrixCompiledBundlePayloadV2 =
| CompiledInteractionPolicyBundleV2
| CompiledKnowledgeUtilityBundleV2
| CompiledToolProcedureUtilityBundleV2
| CompiledPhrasingPolicyBundleV2;
export function assertBundlePayloadMatchesKind(input: {
expected_kind: MatrixCompiledBundleKind;
payload: MatrixCompiledBundlePayloadV2;
}): void {
if (input.payload.bundle_kind !== input.expected_kind) {
throw new MatrixValidationError({
reason_code: "matrix.bundle_payload_kind_mismatch",
metadata: { expected_kind: input.expected_kind, observed_kind: input.payload.bundle_kind },
});
}
}
```
Add rule:
```text
`MatrixBundleGenerationManifest` validates generation identity and activation preconditions. The four compiled bundle payload schemas above define the runtime read-model shapes consumed by DOC24/DOC23/DOC3. A generation cannot become active unless all four required payloads validate and their `bundle_generation_id` equals the manifest generation ID.
```
---
### 14.2 EC task registry entry
```ts
export const SatisfactionBundleCompilationTask: BackgroundTaskTypeRegistryEntry = {
task_type_id: "satisfaction_bundle_compilation",
owner_doc: "DOC24_Addendum_A_BDSM",
execution_trust_class: "same_machine_local_only",
default_priority: "low",
pause_control_key: "processing_controls.per_task_type.satisfaction_bundle_compilation",
budget_pool: "background_learning",
dlq_policy: {
max_retries: 3,
circuit_breaker_after_failures: 5,
circuit_breaker_window_hours: 24,
},
produces_read_models: [
"matrix_effective_runtime_state",
"compiled_bundle_generation_status",
"matrix_compiler_health",
],
schema_version: 1,
};
```
### 14.2A Background job policy controls and accuracy mapping
```ts
export type SatisfactionMatrixBackgroundJobPolicyV3 = {
accuracy: "low" | "medium" | "high";
idle_minutes_required: number;
cpu_threshold_pct: number;
require_screen_off_or_locked: boolean;
poll_interval_minutes: number;
checkpoint_interval_pct: number;
ec_task_type_id: "satisfaction_bundle_compilation";
schema_version: 3;
};
export const DEFAULT_SATISFACTION_MATRIX_BACKGROUND_JOB_POLICY_V3: SatisfactionMatrixBackgroundJobPolicyV3 = {
accuracy: "medium",
idle_minutes_required: 10,
cpu_threshold_pct: 20,
require_screen_off_or_locked: true,
poll_interval_minutes: 30,
checkpoint_interval_pct: 10,
ec_task_type_id: "satisfaction_bundle_compilation",
schema_version: 3,
};
export type SatisfactionMatrixBackgroundJobStatusV3 = {
state:
| "idle"
| "eligible_waiting"
| "running"
| "paused_by_user"
| "paused_by_activity"
| "checkpointing"
| "degraded";
pause_pending: boolean;
last_started_at?: string;
last_completed_at?: string;
last_checkpoint_at?: string;
last_error?: string;
progress_pct?: number;
active_stage?: MatrixCompilationStage;
schema_version: 3;
};
export const AccuracyToSamplesMapV3: Record<"low" | "medium" | "high", number> = {
low: 100,
medium: 200,
high: 500,
};
export function getShapleyPermutationsForMatrixJob(accuracy: "low" | "medium" | "high"): number {
return AccuracyToSamplesMapV3[accuracy];
}
```
Rules:
```text
These are EC-owned desired processing policy inputs. EC owns effective queue truth and may diverge from the desired policy for global pause, LLM pause, incognito, capacity lease, or user-activity reasons. The effective-state inspector SHALL show divergence reason codes.
The job SHALL start only when EC reports idle/user-activity/capacity eligibility. Pause takes effect at the next stage/checkpoint boundary. In-flight Matrix LLM/off-hot-path calls MAY finish, but no new Matrix compilation calls begin after `pause_pending = true`.
Routes are EC-owned topic-level routes:
- `GET /api/settings/satisfaction-matrix/background-job-policy`
- `POST /api/settings/satisfaction-matrix/background-job-policy`
- `GET /api/settings/satisfaction-matrix/background-job-status`
- `POST /api/settings/satisfaction-matrix/background-job-control`
```
---
### 14.3 Compiler stage state machine
```ts
export type MatrixCompilerState =
| "idle"
| "queued"
| "loading_inputs"
| "compiling_ledgers"
| "building_bundles"
| "validating"
| "staged"
| "active"
| "failed"
| "degraded_inactive"
| "superseded";
export type MatrixCompilationStageBudget = {
stage: Exclude<MatrixCompilerState, "idle" | "queued" | "active" | "failed" | "superseded">;
max_wall_clock_ms: number;
max_input_records?: number;
max_output_bytes?: number;
schema_version: 1;
};
export type MatrixCompilerStateTransitionReceipt = {
receipt_id: string;
bundle_generation_id: string;
from_state: MatrixCompilerState;
to_state: MatrixCompilerState;
state: "completed" | "failed" | "skipped_noop";
reason_code?:
| "matrix.compilation_state_transition_completed"
| "matrix.compilation_stage_budget_exceeded"
| "matrix.compilation_no_inputs"
| "matrix.compilation_illegal_state_transition";
metadata?: Record<string, string | number | boolean | null>;
emitted_at: string;
schema_version: 1;
};
export function assertMatrixCompilerTransition(from: MatrixCompilerState, to: MatrixCompilerState): void {
const allowed: Record<MatrixCompilerState, MatrixCompilerState[]> = {
idle: ["queued"],
queued: ["loading_inputs", "failed"],
loading_inputs: ["compiling_ledgers", "failed", "degraded_inactive"],
compiling_ledgers: ["building_bundles", "failed", "degraded_inactive"],
building_bundles: ["validating", "failed", "degraded_inactive"],
validating: ["staged", "failed", "degraded_inactive"],
staged: ["active", "failed", "degraded_inactive"],
active: ["superseded"],
failed: [],
degraded_inactive: [],
superseded: [],
};
if (!allowed[from].includes(to)) {
throw new MatrixValidationError({ reason_code: "matrix.compilation_illegal_state_transition", metadata: { from_state: from, to_state: to } });
}
}
```
---
### 14.4 Compiler stage runner and no-op/error receipts
```ts
export type MatrixCompilationStage =
| "load_new_signals"
| "classify_feedback"
| "verify_procedure_outcomes"
| "update_ledgers"
| "update_aggregates"
| "detect_patterns"
| "compile_bundle_payloads"
| "validate_generation"
| "stage_generation"
| "activate_generation";
export type MatrixCompilationStageReceipt = {
receipt_id: string;
job_id: string;
stage: MatrixCompilationStage;
state: "completed" | "no_op" | "failed" | "budget_exhausted" | "paused";
input_count: number;
output_count: number;
started_at: string;
completed_at: string;
budget: {
max_wall_ms: number;
max_records?: number;
max_tokens?: number;
max_cost_usd?: number;
};
reason_code?:
| "matrix.compiler_no_new_signals"
| "matrix.compiler_stage_budget_exhausted"
| "matrix.compiler_paused_by_ec"
| "matrix.compiler_stage_failed";
schema_version: 1;
};
export const MATRIX_COMPILATION_ALLOWED_TRANSITIONS: Record<MatrixCompilationStage, MatrixCompilationStage[]> = {
load_new_signals: ["classify_feedback"],
classify_feedback: ["verify_procedure_outcomes"],
verify_procedure_outcomes: ["update_ledgers"],
update_ledgers: ["update_aggregates"],
update_aggregates: ["detect_patterns"],
detect_patterns: ["compile_bundle_payloads"],
compile_bundle_payloads: ["validate_generation"],
validate_generation: ["stage_generation"],
stage_generation: ["activate_generation"],
activate_generation: [],
};
export async function runMatrixCompilationStageWithBudget(input: {
job_id: string;
stage: MatrixCompilationStage;
budget: MatrixCompilationStageReceipt["budget"];
ec_pause_requested: boolean;
run: () => Promise<{ input_count: number; output_count: number }>;
}): Promise<MatrixCompilationStageReceipt> {
const started_at = new Date().toISOString();
if (input.ec_pause_requested) {
return {
receipt_id: newId("matrix_compilation_stage_receipt"),
job_id: input.job_id,
stage: input.stage,
state: "paused",
input_count: 0,
output_count: 0,
started_at,
completed_at: new Date().toISOString(),
budget: input.budget,
reason_code: "matrix.compiler_paused_by_ec",
schema_version: 1,
};
}
const start_ms = Date.now();
try {
const result = await input.run();
const elapsed_ms = Date.now() - start_ms;
if (elapsed_ms > input.budget.max_wall_ms) {
return {
receipt_id: newId("matrix_compilation_stage_receipt"),
job_id: input.job_id,
stage: input.stage,
state: "budget_exhausted",
input_count: result.input_count,
output_count: result.output_count,
started_at,
completed_at: new Date().toISOString(),
budget: input.budget,
reason_code: "matrix.compiler_stage_budget_exhausted",
schema_version: 1,
};
}
return {
receipt_id: newId("matrix_compilation_stage_receipt"),
job_id: input.job_id,
stage: input.stage,
state: result.input_count === 0 ? "no_op" : "completed",
input_count: result.input_count,
output_count: result.output_count,
started_at,
completed_at: new Date().toISOString(),
budget: input.budget,
reason_code: result.input_count === 0 ? "matrix.compiler_no_new_signals" : undefined,
schema_version: 1,
};
} catch (error) {
return {
receipt_id: newId("matrix_compilation_stage_receipt"),
job_id: input.job_id,
stage: input.stage,
state: "failed",
input_count: 0,
output_count: 0,
started_at,
completed_at: new Date().toISOString(),
budget: input.budget,
reason_code: "matrix.compiler_stage_failed",
schema_version: 1,
};
}
}
```
---
## 14A. Pattern detection and scoped auto-apply
Pattern detection is a derived learning surface. It SHALL remain reversible, scoped, inspectable, and partitioned. Pattern detection MUST NOT silently create durable standing rules, graph mutations, policy changes, or prompt-control language.
```ts
export type MatrixPatternKind =
| "knowledge_utility_pattern"
| "tool_procedure_selection_pattern"
| "question_policy_pattern"
| "phrasing_pattern"
| "task_design_correlation_pattern"
| "doc73_special_signal_pattern";
export type MatrixPatternRecordV2 = {
pattern_id: string;
pattern_kind: MatrixPatternKind;
target_refs: Array<{ target_kind: MatrixSignalLedgerTarget; target_id: string }>;
context_signature: LearningContextSignature;
partition: MatrixLedgerPartition;
model_class: MatrixModelClass;
support_count: number;
positive_utility_rate: number;
lower95_positive_utility_rate: number;
regression_rate: number;
first_observed_at: string;
last_observed_at: string;
lifecycle_state:
| "candidate"
| "review_required"
| "scoped_auto_apply_active"
| "approved"
| "reverted"
| "rejected"
| "expired";
auto_apply_scope?: {
scope_kind: "exact_context" | "same_matter" | "same_firewall" | "same_model_class";
scope_key: string;
expires_at: string;
};
approval_receipt_id?: string;
revert_receipt_id?: string;
policy_decision_id: string;
schema_version: 2;
};
export type MatrixScopedAutoApplyDecision =
| {
kind: "allow_scoped_auto_apply";
pattern_id: string;
auto_apply_scope: NonNullable<MatrixPatternRecordV2["auto_apply_scope"]>;
reason_code: "matrix.pattern_scoped_auto_apply_allowed";
schema_version: 1;
}
| {
kind: "requires_review";
pattern_id: string;
reason_code:
| "matrix.pattern_scope_broadening_requires_review"
| "matrix.pattern_low_support_requires_review"
| "matrix.pattern_policy_requires_review";
schema_version: 1;
}
| {
kind: "block_auto_apply";
pattern_id: string;
reason_code:
| "matrix.pattern_privacy_partition_blocks_auto_apply"
| "matrix.pattern_regression_rate_blocks_auto_apply";
schema_version: 1;
};
export function decideScopedPatternAutoApply(input: {
pattern: MatrixPatternRecordV2;
min_support: number;
min_lower95_positive_rate: number;
max_regression_rate: number;
requested_scope_kind: "exact_context" | "same_matter" | "same_firewall" | "same_model_class" | "global";
policy_allows_auto_apply: boolean;
}): MatrixScopedAutoApplyDecision {
if (!input.policy_allows_auto_apply) {
return { kind: "requires_review", pattern_id: input.pattern.pattern_id, reason_code: "matrix.pattern_policy_requires_review", schema_version: 1 };
}
if (input.pattern.regression_rate > input.max_regression_rate) {
return { kind: "block_auto_apply", pattern_id: input.pattern.pattern_id, reason_code: "matrix.pattern_regression_rate_blocks_auto_apply", schema_version: 1 };
}
if (input.pattern.support_count < input.min_support || input.pattern.lower95_positive_utility_rate < input.min_lower95_positive_rate) {
return { kind: "requires_review", pattern_id: input.pattern.pattern_id, reason_code: "matrix.pattern_low_support_requires_review", schema_version: 1 };
}
if (input.requested_scope_kind === "global" || input.requested_scope_kind !== "exact_context") {
return { kind: "requires_review", pattern_id: input.pattern.pattern_id, reason_code: "matrix.pattern_scope_broadening_requires_review", schema_version: 1 };
}
return {
kind: "allow_scoped_auto_apply",
pattern_id: input.pattern.pattern_id,
auto_apply_scope: {
scope_kind: "exact_context",
scope_key: input.pattern.context_signature.normalized_key,
expires_at: addDaysIso(new Date().toISOString(), 30),
},
reason_code: "matrix.pattern_scoped_auto_apply_allowed",
schema_version: 1,
};
}
```
Rules:
- Auto-apply overlays SHALL be reversible derived overlays, not durable graph mutations.
- Auto-apply SHALL default to exact observed context only.
- Any scope broadening requires explicit PropA/EC approval and a Knowledge Manager review item.
- Regression detection SHALL revert active scoped auto-apply and emit `revert_receipt_id`.
---
## 15. Question and phrasing policy
### 15.1 Question kinds
```ts
export type SharedQuestionKind =
| "blocking_missing_input"
| "blocking_ambiguity"
| "blocking_tool_confirmation"
| "blocking_destructive_action_confirmation"
| "blocking_external_send_confirmation"
| "onboarding_bootstrap"
| "onboarding_enrichment"
| "knowledge_write_confirmation"
| "standing_procedure_offer"
| "skill_learning_probe"
| "debug_review_prompt"
| "inbox_followup";
```
### 15.1A Question trace, options, and decision utility
```ts
export type QuestionOptionV2 = {
option_id: string;
label: string;
value_ref?: string;
schema_version: 1;
};
export type QuestionTraceV4 = {
question_trace_id: string;
trace_id: string;
question_kind: SharedQuestionKind;
question_text_hash: string;
options: QuestionOptionV2[];
asked_inline: boolean;
blocking: boolean;
phrasing_profile_id?: string;
phrasing_shell_id?: string;
ec_effective_state_generation_id: string;
learning_policy_decision_id: string;
partition: MatrixLedgerPartition;
context_signature: LearningContextSignature;
asked_at: string;
answered_at?: string;
answer_quality?: "complete" | "partial" | "ambiguous" | "declined";
downstream_result?:
| "enabled_success"
| "enabled_partial_success"
| "prevented_failure"
| "no_effect"
| "caused_abandonment"
| "caused_annoyance";
schema_version: 4;
};
export type QuestionDecisionInput = {
question_kind: SharedQuestionKind;
missing_required_input: boolean;
destructive_or_external_side_effect: boolean;
normalized_question_utility: number;
attribution_confidence: number;
ec_escape_state: EffectiveQuestionEscapeState;
explicit_user_requested_question: boolean;
schema_version: 1;
};
export type QuestionDecision =
| { kind: "ask_inline"; reason_code: "matrix.question_blocking_required" | "matrix.question_utility_positive"; schema_version: 1 }
| { kind: "defer"; reason_code: "matrix.question_nonblocking_deferred"; schema_version: 1 }
| { kind: "suppress"; reason_code: "matrix.question_suppressed_by_policy" | "matrix.question_negative_utility"; schema_version: 1 };
export function decideQuestionInline(input: QuestionDecisionInput): QuestionDecision {
if (input.missing_required_input || input.destructive_or_external_side_effect) {
return { kind: "ask_inline", reason_code: "matrix.question_blocking_required", schema_version: 1 };
}
if (input.explicit_user_requested_question) {
return { kind: "ask_inline", reason_code: "matrix.question_utility_positive", schema_version: 1 };
}
if (input.ec_escape_state.suppress_all_optional_questions || input.ec_escape_state.suppress_non_blocking_questions) {
return { kind: "suppress", reason_code: "matrix.question_suppressed_by_policy", schema_version: 1 };
}
const score = clampSignedUnit(input.normalized_question_utility) * clampUnitInterval(input.attribution_confidence);
if (score < -0.2) return { kind: "suppress", reason_code: "matrix.question_negative_utility", schema_version: 1 };
if (score < 0.1) return { kind: "defer", reason_code: "matrix.question_nonblocking_deferred", schema_version: 1 };
return { kind: "ask_inline", reason_code: "matrix.question_utility_positive", schema_version: 1 };
}
```
Rules:
```text
Question traces are derived learning artifacts. They SHALL NOT become a second conversation transcript. They store hashes and outcome metadata sufficient for question-utility learning and inspectability. Blocking question kinds and knowledge-write confirmations remain non-suppressible under all optional-question escape hatches.
```
### 15.1B Phrasing profiles and deterministic shell registry
```ts
export type QuestionStyleProfileV3 = {
profile_id: string;
display_name: string;
tone: "plain" | "concise" | "warm" | "formal" | "direct";
verbosity: "minimal" | "standard" | "detailed";
default_for_question_kinds: SharedQuestionKind[];
scope: {
context_signature_key?: string;
matter_id?: string;
principal_id?: string;
};
lifecycle_state: "active" | "inactive" | "retired";
schema_version: 3;
};
export type DeterministicQuestionShellV3 = {
shell_id: string;
question_kind: SharedQuestionKind;
shell_text_template: string;
required_placeholders: string[];
mandatory: boolean;
profile_id?: string;
schema_version: 3;
};
export type PhrasingSelectionInput = {
question_kind: SharedQuestionKind;
candidate_profiles: QuestionStyleProfileV3[];
candidate_shells: DeterministicQuestionShellV3[];
phrasing_bundle?: CompiledPhrasingPolicyBundleV2;
explicit_shell_required: boolean;
schema_version: 1;
};
export type PhrasingSelection = {
profile_id?: string;
shell_id?: string;
selection_reason_code:
| "matrix.phrasing_policy_selected"
| "matrix.mandatory_shell_selected"
| "matrix.default_shell_selected"
| "matrix.no_phrasing_policy_available";
schema_version: 1;
};
export function choosePhrasingSelection(input: PhrasingSelectionInput): PhrasingSelection {
const mandatory = input.candidate_shells.find((s) => s.question_kind === input.question_kind && s.mandatory);
if (mandatory) {
return { profile_id: mandatory.profile_id, shell_id: mandatory.shell_id, selection_reason_code: "matrix.mandatory_shell_selected", schema_version: 1 };
}
const bundle_entry = input.phrasing_bundle?.entries
.filter((e) => e.question_kind === input.question_kind)
.sort((a, b) => b.utility_mean - a.utility_mean)[0];
if (bundle_entry) {
return {
profile_id: bundle_entry.preferred_profile_id,
shell_id: bundle_entry.preferred_shell_id,
selection_reason_code: "matrix.phrasing_policy_selected",
schema_version: 1,
};
}
const default_shell = input.candidate_shells.find((s) => s.question_kind === input.question_kind);
if (default_shell) {
return { profile_id: default_shell.profile_id, shell_id: default_shell.shell_id, selection_reason_code: "matrix.default_shell_selected", schema_version: 1 };
}
return { selection_reason_code: "matrix.no_phrasing_policy_available", schema_version: 1 };
}
```
Add rule:
```text
Phrasing utility affects shell/profile selection only. It MUST NOT alter epistemic hedge mode, policy state, or delivery eligibility.
```
---
### 15.2 Suppression rules
Blocking confirmations and knowledge-write confirmations are not optional.
```ts
export const OPTIONAL_QUESTION_SUPPORT_SATURATION_HALF_LIFE = 20;
const suppressibleNonBlockingQuestionKinds = new Set<SharedQuestionKind>([
"onboarding_bootstrap",
"onboarding_enrichment",
"standing_procedure_offer",
"skill_learning_probe",
"debug_review_prompt",
"inbox_followup",
]);
const nonSuppressibleBlockingQuestionKinds = new Set<SharedQuestionKind>([
"blocking_missing_input",
"blocking_ambiguity",
"blocking_tool_confirmation",
"blocking_destructive_action_confirmation",
"blocking_external_send_confirmation",
"knowledge_write_confirmation",
]);
export function maySuppressByOptionalEscapeHatch(kind: SharedQuestionKind): boolean {
if (nonSuppressibleBlockingQuestionKinds.has(kind)) return false;
return suppressibleNonBlockingQuestionKinds.has(kind);
}
export function weightedOptionalQuestionMean(rows: Array<{
question_kind: SharedQuestionKind;
smoothed_utility: number;
attribution_confidence: number;
support_count: number;
}>): number {
const usable = rows.filter(r => maySuppressByOptionalEscapeHatch(r.question_kind));
const weighted = usable.map(r => {
const confidence = clampUnit(r.attribution_confidence);
const support_weight = 1 - Math.exp(-Math.max(0, r.support_count) / OPTIONAL_QUESTION_SUPPORT_SATURATION_HALF_LIFE);
return { score: clampSigned(r.smoothed_utility) * confidence, weight: support_weight * confidence };
}).filter(x => x.weight > 0);
if (weighted.length === 0) return 0;
return clampSigned(weighted.reduce((s, x) => s + x.score * x.weight, 0) / weighted.reduce((s, x) => s + x.weight, 0));
}
```
### 15.3 EC behavioral escape hatches
BDSM consumes EC effective escape hatches:
- `suppress_onboarding_questions`
- `suppress_doc8_feedback_requests`
- `suppress_standing_procedure_offers`
- `suppress_non_blocking_questions`
- `suppress_all_optional_questions`
BDSM MUST NOT store a local durable `suppress_optional_questions` toggle. It MAY expose derived `suppress_optional_questions_effective` in read models.
---
## 15A. Interaction protocol and execution preference cards
V6.5 preserves the v6.4 distinction between interaction protocol cards and execution preference cards, but replaces local tag/force vocabulary with DOC24 canonical directive constraints and resolved delivery directives.
```ts
export type MatrixInteractionProtocolCardV2 = {
protocol_card_id: string;
context_signature_key: string;
interaction_preference:
| "ask_before_acting"
| "default_to_action"
| "prefer_brief_clarification"
| "avoid_nonblocking_questions"
| "prefer_status_summary";
directive_constraints: DirectiveConstraint[];
learned_from_pattern_ids: string[];
support_count: number;
attribution_confidence: number;
partition_hash: string;
policy_generation_id: string;
schema_version: 2;
};
export type MatrixExecutionPreferenceCardV2 = {
execution_preference_card_id: string;
target_kind: "tool" | "procedure" | "capability";
target_id: string;
context_signature_key: string;
preference:
| "prefer"
| "avoid_when_alternative_available"
| "require_confirmation"
| "prefer_local_only"
| "prefer_manual_path";
directive_constraints: DirectiveConstraint[];
supporting_tool_procedure_record_ids: string[];
support_count: number;
attribution_confidence: number;
partition_hash: string;
policy_generation_id: string;
schema_version: 2;
};
```
Rules:
- These cards are compiled read models only; they are not prompt-control language.
- DOC24 owns final packet delivery and directive reconciliation.
- Execution preference cards MUST NOT override policy, PropA sharing decisions, EC effective state, or DOC11/OpenClaw runtime truth.
---
## 16. KDA R3 interaction and variant attribution
### 16.1 KDA result consumption
BDSM consumes KDA R3 render metadata only through DOC24 manifests and KDA R3 `KdaRenderResult` objects already bound to those manifests. BDSM MUST NOT infer rendering variant IDs from text.
The following fields are learning-significant:
- `render_variant_id`
- `rendering_specialization_id`
- `template_version`
- `card_presence`
- `rendering_tier` when `card_presence = included_inline`
- `rendered_token_count`
- `tokenizer_ref`
- `degraded_reason_codes`
### 16.2 Positive attribution blocks KDA drop
KDA owns `KdaNeuroplasticityAction` and `constrainKdaActionByAttribution`. BDSM supplies attribution outcomes and MUST NOT define a local KDA action enum or local arbitration function.
```ts
import type { KdaNeuroplasticityAction } from "@elnor/kda-r3";
import { constrainKdaActionByAttribution } from "@elnor/kda-r3";
export function constrainKdaActionByMatrixAttribution(input: {
proposed_action: KdaNeuroplasticityAction;
bdsm_attribution: BdsmAttributionOutcome;
}): KdaNeuroplasticityAction {
return constrainKdaActionByAttribution({
proposed_action: input.proposed_action,
bdsm_attribution: input.bdsm_attribution === "invariant_failure" ? "unknown" : input.bdsm_attribution,
});
}
```
---
---
## 17. Proactive feedback and RecentActivityRollup
BDSM proactive feedback requests consume DOC73 `RecentActivityRollup` freshness state. They MUST NOT treat RecentActivityRollup as evidence and MUST NOT ask feedback questions when EC suppresses DOC8 feedback requests.
```ts
export type ProactiveFeedbackDecision =
| { kind: "show"; schema_version: 1 }
| { kind: "show_with_badge"; badge_reason_code: "matrix.recent_activity_rollup_stale"; schema_version: 1 }
| { kind: "refresh_then_show"; refresh_reason_code: "matrix.recent_activity_rollup_refresh_required"; schema_version: 1 }
| { kind: "suppress"; reason_code: "matrix.proactive_feedback_no_candidate" | "matrix.recent_activity_rollup_stale" | "matrix.doc8_feedback_requests_suppressed"; schema_version: 1 };
export function decideProactiveFeedbackFromRollup(input: {
rollup_freshness: "fresh" | "stale" | "missing";
stale_action: "suppress" | "show_with_stale_badge" | "refresh_rollup_then_show";
feedback_candidate_available: boolean;
suppress_doc8_feedback_requests: boolean;
}): ProactiveFeedbackDecision {
if (input.suppress_doc8_feedback_requests) {
return { kind: "suppress", reason_code: "matrix.doc8_feedback_requests_suppressed", schema_version: 1 };
}
if (!input.feedback_candidate_available) {
return { kind: "suppress", reason_code: "matrix.proactive_feedback_no_candidate", schema_version: 1 };
}
if (input.rollup_freshness === "fresh") return { kind: "show", schema_version: 1 };
if (input.stale_action === "suppress") return { kind: "suppress", reason_code: "matrix.recent_activity_rollup_stale", schema_version: 1 };
if (input.stale_action === "show_with_stale_badge") return { kind: "show_with_badge", badge_reason_code: "matrix.recent_activity_rollup_stale", schema_version: 1 };
return { kind: "refresh_then_show", refresh_reason_code: "matrix.recent_activity_rollup_refresh_required", schema_version: 1 };
}
```
---
### 17.1 DOC8 proactive feedback request records and routes
```ts
export type FeedbackRequestCategoryV3 =
| "knowledge_gap"
| "routing_gap"
| "tool_gap"
| "question_gap"
| "phrasing_gap"
| "onboarding_probe";
export type FeedbackRequestPolicyV3 = {
min_expected_information_value: number;
category_extinction_floor_multiplier: number;
max_visible_open_requests: number;
max_new_requests_per_day: number;
max_primary_queue_items_per_day: number;
category_cooldown_days: number;
dismiss_same_category_backoff_multiplier: number;
visible_ttl_days: number;
schema_version: 3;
};
export const DEFAULT_FEEDBACK_REQUEST_POLICY_V3: FeedbackRequestPolicyV3 = {
min_expected_information_value: 0.35,
category_extinction_floor_multiplier: 0.5,
max_visible_open_requests: 25,
max_new_requests_per_day: 10,
max_primary_queue_items_per_day: 5,
category_cooldown_days: 14,
dismiss_same_category_backoff_multiplier: 2,
visible_ttl_days: 14,
schema_version: 3,
};
export type DOC8FeedbackRequestV3 = {
request_id: string;
category: FeedbackRequestCategoryV3;
context_signature?: LearningContextSignature;
partition?: MatrixLedgerPartition;
prompt_summary: string;
options: QuestionOptionV2[];
expected_information_value: number;
attribution_confidence: number;
priority_score: number;
created_at: string;
expires_at: string;
version: number;
schema_version: 3;
};
export type DOC8FeedbackRequestAnswerRequestV3 = {
request_id: string;
answer_text?: string;
selected_option_id?: string;
expected_version?: number;
schema_version: 3;
};
export type DOC8FeedbackRequestDismissRequestV3 = {
request_id: string;
expected_version?: number;
schema_version: 3;
};
export type DOC8FeedbackRequestResultV3 = {
request_id: string;
action: "answer" | "dismiss";
result_state: "applied" | "queued" | "degraded_noop" | "failed";
receipt_id?: string;
emitted_signal_id?: string;
updated_version?: number;
reason_code?:
| "matrix.doc8_feedback_suppressed_by_ec"
| "matrix.doc8_feedback_policy_audit_only"
| "matrix.doc8_feedback_request_expired"
| "matrix.doc8_feedback_invalid_answer";
schema_version: 3;
};
export type DOC8FeedbackRequestListQueryV3 = {
category?: FeedbackRequestCategoryV3;
state?: "open" | "answered" | "dismissed" | "expired";
page: number;
page_size: number;
schema_version: 3;
};
export type DOC8FeedbackRequestListResultV3 = {
items: DOC8FeedbackRequestV3[];
total_count: number;
page: number;
page_size: number;
schema_version: 3;
};
export function computeFeedbackRequestPriorityV3(input: {
expected_information_value: number;
answer_rate_score: number;
recency_factor: number;
user_tolerance_factor: number;
}): number {
return clampUnitInterval(
(0.55 * clampUnitInterval(input.expected_information_value)) +
(0.20 * clampUnitInterval(input.answer_rate_score)) +
(0.15 * clampUnitInterval(input.recency_factor)) +
(0.10 * clampUnitInterval(input.user_tolerance_factor)),
);
}
```
Rules:
```text
DOC8 proactive feedback requests SHALL read EC `suppress_doc8_feedback_requests`. When suppressed, the system may retain audit-only candidate records if policy permits, but MUST NOT surface proactive requests.
Answer/dismiss routes are EC command routes. A feedback answer becomes a ledger-eligible signal only when `decideMatrixLearningPolicy` and `decideMatrixSignalPersistence` allow learning persistence.
Routes:
- `GET /api/knowledge/doc8-feedback-requests`
- `GET /api/knowledge/doc8-feedback-requests/:request_id`
- `POST /api/knowledge/doc8-feedback-requests/:request_id/answer`
- `POST /api/knowledge/doc8-feedback-requests/:request_id/dismiss`
- `GET /api/knowledge/doc8-feedback-requests/results/:receipt_id`
```
## 17A. Knowledge Manager direct feedback channel
Knowledge Manager remains the highest-quality direct feedback surface for Matrix learning. It is a user-facing review/read surface; EC remains the sole durable writer. Actions from this channel may produce Matrix signals only after EC effective-state, PropA/EC policy, and Matrix learning-policy gates accept them.
```ts
export type KnowledgeReviewItemKindV3 =
| "new_node"
| "procedure_update"
| "pattern_detection"
| "tool_failure"
| "standing_procedure_misfire"
| "doc8_feedback_request"
| "matrix_pattern_review"
| "matrix_scope_broadening_review";
export type KnowledgeReviewActionV3 =
| "approve"
| "edit_approve"
| "reject"
| "star_pin"
| "unpin"
| "scope_restrict"
| "dismiss"
| "discuss";
export type KnowledgeReviewQueueItemV3 = {
queue_item_id: string;
node_id?: string;
pattern_id?: string;
signal_id?: string;
item_kind: KnowledgeReviewItemKindV3;
item_subtype?: string;
rationale_summary: string;
confidence: number;
recommended_action?: KnowledgeReviewActionV3;
partition?: MatrixLedgerPartition;
context_signature?: LearningContextSignature;
policy_decision_id: string;
learning_policy_decision_id?: string;
created_at: string;
expires_at: string;
version: number;
schema_version: 3;
};
export type KnowledgeReviewActionRequestV3 = {
queue_item_id: string;
action: KnowledgeReviewActionV3;
why_text?: string;
edit_payload?: Record<string, unknown>;
scope_payload?: Record<string, unknown>;
expected_version?: number;
schema_version: 3;
};
export type KnowledgeReviewActionResultV3 = {
queue_item_id: string;
action: KnowledgeReviewActionV3;
result_state: "applied" | "queued" | "degraded_noop" | "failed";
receipt_id?: string;
emitted_signal_id?: string;
updated_version?: number;
reason_code?:
| "matrix.knowledge_review_policy_blocked"
| "matrix.knowledge_review_action_unsupported"
| "matrix.knowledge_review_learning_audit_only";
schema_version: 3;
};
export type KnowledgeReviewQueueQueryV3 = {
item_kind?: KnowledgeReviewItemKindV3;
state?: "open" | "resolved" | "dismissed" | "expired";
visible_only: boolean;
page: number;
page_size: number;
schema_version: 3;
};
export type KnowledgeReviewQueueResultV3 = {
items: KnowledgeReviewQueueItemV3[];
total_count: number;
visible_count: number;
page: number;
page_size: number;
degraded_state: "none" | "partial" | "unavailable";
schema_version: 3;
};
export type ConversationLauncherRequestV2 = {
queue_item_id: string;
schema_version: 2;
};
export type ConversationLauncherResultV2 = {
conversation_id: string;
conversation_url: string;
preloaded_node_ids: string[];
context_tag: "knowledge_review";
result_state: "created" | "degraded_noop" | "failed";
schema_version: 2;
};
```
Rules:
- `dismiss` is a null signal for the item unless DOC8 meta-learning separately aggregates repeated dismissals.
- `approve` semantics vary by `item_kind`; unsupported `approve × item_kind` combinations fail with `matrix.knowledge_review_action_unsupported`.
- Direct user feedback from Knowledge Manager has higher evidence priority than ambient implicit signals, subject to policy/capture gates.
- Routes are EC-owned command/read-model routes:
- `GET /api/knowledge/review-queue`
- `GET /api/knowledge/review-queue/:queue_item_id`
- `POST /api/knowledge/review-queue/:queue_item_id/action`
- `GET /api/knowledge/review-actions/:receipt_id`
- `POST /api/knowledge/review-queue/:queue_item_id/discuss`
---
## 18. Invalidation, restore, and migration
### 18.1 PropA reclassification invalidation
```ts
export type PolicyReclassificationInvalidationEvent = {
event_id: string;
node_id: string;
changed_policy_fields: string[];
invalidate: {
pending_packet_manifests: boolean;
kda_execution_strategy_cache: boolean;
kda_rendering_specializations: boolean;
matrix_ledgers: boolean;
matrix_compiled_bundles: boolean;
matrix_attribution_index: boolean;
};
emitted_at: string;
schema_version: 1;
};
export const POLICY_RECLASSIFICATION_INVALIDATION_ORDER = [
"abort_pending_packet_manifests",
"invalidate_kda_execution_strategy_cache",
"invalidate_kda_rendering_specializations",
"mark_matrix_compiled_bundles_inactive",
"invalidate_matrix_attribution_index",
"rebuild_scoped_utility_views",
] as const;
```
### 18.2 Backup/export/import restore order
Restoration SHALL follow this order:
1. DOC72 graph + node lifecycle state.
2. PropA sensitivity metadata + policy generations.
3. KDA rendering template registry + specialization registry.
4. KDA execution-strategy cache as inactive until validation.
5. DOC24 packet/final-prompt manifests + reconciliation events.
6. BDSM raw signal logs + attribution index.
7. BDSM utility ledgers.
8. BDSM compiled bundles as `staged_inactive`.
9. Rebuild active Matrix bundle generation from restored ledgers.
10. Activate only if generation hashes and policy/settings/source snapshots validate.
### 18.3 V6.4 → V6.5 migration steps
```ts
export const BDSM_V64_V65_MIGRATION_STEPS = [
{ step_id: "BDSM-MIG-001", action: "replace_context_class_key_with_learning_context_signature" },
{ step_id: "BDSM-MIG-002", action: "replace_scalar_force_level_with_doc24_directive_constraints" },
{ step_id: "BDSM-MIG-003", action: "delete_local_satisfaction_matrix_settings_routes" },
{ step_id: "BDSM-MIG-004", action: "retire_unified_injection_manifest_and_reconciliation_overlay_names" },
{ step_id: "BDSM-MIG-005", action: "rekey_ledgers_by_visibility_scope_model_class_policy_generation" },
{ step_id: "BDSM-MIG-006", action: "rebuild_compiled_bundles_from_ledgers_and_do_not_trust_restored_active_pointer" },
{ step_id: "BDSM-MIG-007", action: "generate_closed_reason_code_registry_and_fail_on_dynamic_codes" },
{ step_id: "BDSM-MIG-008", action: "run_v65_fixture_gate_before_accepting_migrated_artifacts" },
] as const;
```
---
## 19. Inspectability
### 19.1 Delivery-learning inspector row
DOC24 owns the canonical `DeliveryLearningInspectorRow`. BDSM MUST NOT define a local row type. BDSM supplies attribution/no-signal/ledger-decision data that DOC24 writes into the canonical inspector row.
```ts
import type { DeliveryLearningInspectorRow } from "@elnor/doc24-runtime-lifecycle";
export type MatrixInspectorAttributionProjection = Pick<
DeliveryLearningInspectorRow,
| "join_decision_id"
| "attribution_decision_id"
| "reconciliation_outcome"
| "bdsm_attribution"
| "attribution_decision_kind"
| "no_signal_reason_code"
>;
export function projectMatrixAttributionForInspector(input: {
join_decision_id: string;
decision: CardAttributionDecision;
reconciliation_outcome: DeliveryLearningInspectorRow["reconciliation_outcome"];
}): MatrixInspectorAttributionProjection {
if (input.decision.kind === "ledger_signal") {
return { join_decision_id: input.join_decision_id, attribution_decision_id: input.decision.attribution_decision_id, reconciliation_outcome: input.reconciliation_outcome, bdsm_attribution: bdsmInspectorLabel(input.decision.signal), attribution_decision_kind: "ledger_signal" };
}
if (input.decision.kind === "no_signal") {
return { join_decision_id: input.join_decision_id, attribution_decision_id: `${input.join_decision_id}:${input.decision.card_id}:no_signal`, reconciliation_outcome: input.reconciliation_outcome, bdsm_attribution: "no_signal", attribution_decision_kind: "no_signal", no_signal_reason_code: input.decision.reason_code };
}
return { join_decision_id: input.join_decision_id, attribution_decision_id: `${input.join_decision_id}:${input.decision.card_id}:invariant_failure`, reconciliation_outcome: input.reconciliation_outcome, bdsm_attribution: "invariant_failure", attribution_decision_kind: "invariant_failure", no_signal_reason_code: input.decision.reason_code };
}
```
### 19.2 Inspector requirements
The inspector SHALL answer for each card:
- Was it a candidate?
- Was it packeted?
- Was it delivered to the final prompt?
- Was it inline, reference-only, or excluded?
- Which final-prompt spans contained it?
- Which KDA variant rendered it?
- Was it used, ignored, corrected, negative, or unobservable?
- Did BDSM emit a signal?
- If not, why not?
---
## 20. Reason-code registry and validation errors
### 20.1 Closed reason-code model
```ts
export type ReasonCodeNamespace =
| "matrix"
| "kda"
| "doc24"
| "propa"
| "doc23"
| "doc8"
| "doc72"
| "ec"
| "doc11"
| "openclaw"
| "doc25"
| "tokenizer";
export type ClosedReasonCode = string & { readonly __closed_reason_code: unique symbol };
export type ReasonCodeEmission = {
reason_code: ClosedReasonCode;
metadata?: Record<string, string | number | boolean | null>;
};
export type ReasonCodeRegistryEntry = {
code: ClosedReasonCode;
namespace: ReasonCodeNamespace;
owner_doc: "BDSM" | "KDA" | "DOC24" | "PropA" | "DOC23" | "DOC8" | "DOC72" | "EC" | "DOC11" | "OpenClaw" | "DOC25";
producer_function: string;
display_template: string;
fixture_id: string;
schema_version: 1;
};
export class MatrixValidationError extends Error {
reason_code: ClosedReasonCode;
metadata: Record<string, unknown>;
constructor(input: { reason_code: string; metadata?: Record<string, unknown> }) {
super(input.reason_code);
if (/[{}:$]/.test(input.reason_code)) {
throw new Error("reason_code_must_be_closed_literal");
}
this.reason_code = input.reason_code as ClosedReasonCode;
this.metadata = input.metadata ?? {};
}
}
```
### 20.2 Generated registry rule
The BDSM build SHALL generate a reason-code registry from emitted literals and fail build when:
- a reason code includes interpolation characters (`{`, `}`, `:`, `$`) or embedded runtime values;
- a code lacks owner namespace;
- a code lacks a producer function;
- a code lacks a fixture;
- a code is emitted but absent from the registry;
- a registry code is declared but has no producer fixture.
---
## 21. Schema versioning and derived artifact parsing
```ts
export type DerivedArtifactKind =
| "matrix_utility_ledger"
| "matrix_compiled_bundle_index"
| "matrix_attribution_index"
| "matrix_signal_receipt_log"
| "matrix_reason_code_registry";
export type SchemaVersionPolicy = {
artifact_kind: DerivedArtifactKind;
min_supported: number;
max_supported: number;
migration_available_from: number[];
schema_version: 1;
};
export type DerivedArtifactParseError =
| { kind: "missing_schema_version"; artifact_kind: DerivedArtifactKind }
| { kind: "future_schema_version"; artifact_kind: DerivedArtifactKind; observed: number; max_supported: number }
| { kind: "deprecated_schema_version"; artifact_kind: DerivedArtifactKind; observed: number; min_supported: number }
| { kind: "migration_required"; artifact_kind: DerivedArtifactKind; observed: number; target: number };
export function parseDerivedArtifactVersion(input: {
artifact_kind: DerivedArtifactKind;
observed_schema_version?: number;
policy: SchemaVersionPolicy;
}): "accepted" | DerivedArtifactParseError {
const observed = input.observed_schema_version;
if (observed === undefined) return { kind: "missing_schema_version", artifact_kind: input.artifact_kind };
if (observed > input.policy.max_supported) return { kind: "future_schema_version", artifact_kind: input.artifact_kind, observed, max_supported: input.policy.max_supported };
if (observed < input.policy.min_supported) {
if (input.policy.migration_available_from.includes(observed)) {
return { kind: "migration_required", artifact_kind: input.artifact_kind, observed, target: input.policy.max_supported };
}
return { kind: "deprecated_schema_version", artifact_kind: input.artifact_kind, observed, min_supported: input.policy.min_supported };
}
return "accepted";
}
```
---
## 22. Conformance fixtures and acceptance gate
```ts
export type MatrixConformanceFixtureEntry = {
fixture_id: string;
target_adj_id: string;
target_section: string;
owner_doc: "BDSM" | "KDA" | "BDSM+KDA" | "DOC8" | "EC" | "DOC24";
fixture_kind: "unit" | "integration" | "schema" | "lifecycle" | "cross_doc";
expected_result: string;
schema_version: 1;
};
export const BDSM_V65_FIXTURE_GATE: MatrixConformanceFixtureEntry[] = [
{ fixture_id: "matrix_3way_manifest_join_no_signal_for_dropped_card", target_adj_id: "ADJ-002", target_section: "§8", owner_doc: "BDSM+KDA", fixture_kind: "integration", expected_result: "packeted-but-not-final card emits no utility signal", schema_version: 1 },
{ fixture_id: "matrix_boundary_normalized_relevance_is_0_1", target_adj_id: "ADJ-004", target_section: "§7", owner_doc: "BDSM", fixture_kind: "unit", expected_result: "normalized relevance clamps to [0,1] without crossing structural floor", schema_version: 1 },
{ fixture_id: "matrix_no_signal_rows_excluded_from_reward_denominator", target_adj_id: "ADJ-052", target_section: "§11", owner_doc: "BDSM", fixture_kind: "unit", expected_result: "no-signal entries do not dilute reward", schema_version: 1 },
{ fixture_id: "matrix_shapley_residual_not_ledger_attributable", target_adj_id: "ADJ-021", target_section: "§12", owner_doc: "DOC8", fixture_kind: "unit", expected_result: "residual bucket appears in diagnostics only", schema_version: 1 },
{ fixture_id: "matrix_hierarchical_smoothing_parent_chain", target_adj_id: "ADJ-022", target_section: "§12", owner_doc: "DOC8", fixture_kind: "unit", expected_result: "exact shrinks to task, task to domain, domain to global, global to neutral", schema_version: 1 },
{ fixture_id: "matrix_compiler_noop_receipt_for_empty_input", target_adj_id: "ADJ-015", target_section: "§14", owner_doc: "EC", fixture_kind: "lifecycle", expected_result: "stage emits no-op receipt", schema_version: 1 },
{ fixture_id: "doc8_feedback_request_suppressed_by_ec", target_adj_id: "ADJ-014", target_section: "§17", owner_doc: "BDSM", fixture_kind: "integration", expected_result: "proactive feedback is not surfaced when EC escape hatch active", schema_version: 1 },
{ fixture_id: "knowledge_manager_dismiss_is_null_signal", target_adj_id: "ADJ-041", target_section: "§17A", owner_doc: "BDSM", fixture_kind: "integration", expected_result: "dismiss does not update ledgers", schema_version: 1 },
{ fixture_id: "compiled_bundle_activation_requires_four_payloads", target_adj_id: "ADJ-010", target_section: "§14", owner_doc: "BDSM", fixture_kind: "lifecycle", expected_result: "active generation fails if any payload missing", schema_version: 1 },
{ fixture_id: "interaction_protocol_card_uses_canonical_constraints", target_adj_id: "ADJ-001", target_section: "§15A", owner_doc: "BDSM", fixture_kind: "schema", expected_result: "no local tag/force enum", schema_version: 1 },
];
export const BDSMKdaJointConformanceFixtures: MatrixConformanceFixtureEntry[] = [
...BDSM_V65_FIXTURE_GATE,
{ fixture_id: "kda_manifest_patch_union_blocks_invalid_presence_state", target_adj_id: "ADJ-006", target_section: "KDA §3.2B", owner_doc: "KDA", fixture_kind: "schema", expected_result: "excluded card cannot carry positive token count", schema_version: 1 },
{ fixture_id: "kda_variant_id_round_trips_to_matrix_attribution", target_adj_id: "ADJ-008", target_section: "KDA §3.2", owner_doc: "BDSM+KDA", fixture_kind: "integration", expected_result: "render_variant_id available in attribution decision", schema_version: 1 },
];
```
Add rule:
```text
The fixture gate is incomplete if any accepted BDSM-facing adjudication package lacks at least one fixture entry or an explicit owner-doc reason for not needing a BDSM fixture. KDA-only fixtures live in KDA R3 but are referenced here in the joint list where BDSM consumes their output.
```
A V6.5 draft SHALL NOT move to implementation handoff until the fixture gate and joint BDSM/KDA fixture set pass.
---
## 23. Safeguards and governance
1. Matrix influence can be disabled instantly through EC effective state.
2. Matrix capture can remain enabled when influence is disabled, but outputs go to off-policy shadow ledgers only.
3. Sealed learning signals are audit-only.
4. Firewalled signals require same `firewall_scope_id`.
5. Utility signals require delivery proof and exposure evidence.
6. Matrix cannot suppress directly requested or directly matched high-confidence knowledge by utility alone.
7. Matrix cannot make a below-structural-floor candidate eligible unless DOC24 separately marks a direct/pinned/explicit-request override.
8. Matrix cannot downgrade epistemic confidence or hedge mode based on preference utility.
9. Matrix cannot activate partial bundle generations.
10. Matrix cannot write DOC24 tool reliability counters.
---
## 23A. Coding-agent implementation checklist
Coding agents implementing BDSM V6.5 SHALL verify all of the following before marking the revision build-ready:
1. **No local manifest vocabulary.** No use of `UnifiedInjectionManifestSchema` or `PacketReconciliationOverlay` outside the retired-name declaration block.
2. **No local directive vocabulary.** BDSM emits DOC24 canonical `DirectiveConstraint[]` only.
3. **No hot-path LLM calls.** Matrix runtime influence reads active compiled bundles only.
4. **EC durable writer only.** All Matrix artifacts are written through EC commands/jobs.
5. **No generic ledger payloads.** Ledger update functions accept only registry-typed payloads.
6. **No signal without policy/capture decision.** Every learning write has `MatrixLearningPolicyDecision` and `MatrixSignalPersistenceDecision` references.
7. **No utility signal without delivery proof.** Card attribution requires the 3-way manifest join and exposure evidence.
8. **Privacy partitions on every counter.** Support counts, aggregate counts, canary counts, and utility records all carry partition/model-class scope.
9. **No dynamic reason codes.** Runtime detail goes in metadata.
10. **No active partial bundle generation.** All four bundle payloads validate before activation.
11. **No silent v6.4 deletion.** Any v6.4 surface not restored must be explicitly superseded by named owner-doc text.
12. **KDA R3 surface consumed, not redefined.** BDSM never declares `KdaManifestPatch` or `KdaRenderResult`.
---
## 24. Cross-doc obligations summary
BDSM V6.5 depends on OP-A V3.15 rows that already track DOC24 R3.1.1 manifest/join obligations, KDA R3 variant-tracking obligations, DOC23 task-system signal obligations, DOC73 RecentActivityRollup consumer obligations, and EC effective-state/background-orchestrator obligations.
New or updated obligation rows implied by this draft remain as listed in the V1.3.1-OPA15 adjudication card Part F. BDSM V6.5 SHALL NOT create new undocumented obligations in prose. Any newly discovered cross-doc requirement must be added to OP-A before implementation handoff.
---
## 25. Implementation priority
1. EC effective-state / cold-start / learning-policy gate (§5).
2. Delivery directive + relevance boundary (§6–§7).
3. 3-way manifest attribution boundary (§8).
4. Privacy partitions and signal ingestion (§9–§10).
5. Math contracts and utility ledgers (§11–§13).
6. Bundle compiler and EC orchestrator (§14).
7. Question/phrasing policy (§15).
8. KDA variant attribution seam (§16).
9. Proactive feedback / RecentActivityRollup (§17).
10. Invalidation, restore, migration, inspectability, reason-code generation (§18–§22).
11. Feedback classification / procedure verification / pattern detection restored surfaces (§10.2A–§10.2B, §14A).
12. Knowledge Manager and DOC8 proactive request surfaces (§17–§17A).
13. Background job policy and staged activation controls (§5.6, §14.2A).
---
## 26. Future-only / non-operative notes
The following remain non-operative in V6.5:
- KTO export. Any KTO export requires a separate redaction/security/export spec and MUST NOT be implemented from this BDSM revision.
- Product shadow-mode UI polish.
- AVAPO dashboard visualization polish.
- Unified `KnowledgeCard` product refactor.
- Any slot ID invention for DOC23 TaskOpportunityPacket. BDSM consumes DOC24/Core-registered slots once the owner specs register them; BDSM does not invent slot IDs.
---
## Appendix Z — v0.2 patch-plan application note
This v0.2 draft applies the twenty rows in `DOC24_BDSM_V6_5_DRAFT_V0_2_PATCH_PLAN.md`. The patch restores or explicitly supersedes the v6.4 surfaces identified by the audit while preserving the V6.5 adjudication-card redesign. Where v6.4 local settings, local privacy policy, local force/tag vocabulary, or retired manifest constructs conflict with DOC24 R3.1.1 / EC Core / PropA / KDA R3, V6.5 supersedes them explicitly rather than silently carrying them forward.