core/docs/adr/ADR-0148-vault-promotion-policy-wiring.md
Shay 54e6bfc0d0
docs: reorganize docs landscape
Implements the 4-phase documentation reorganization master plan.

- Consolidation: Merged brief/, handoff/, planning/, and decisions/ into briefs/, handoffs/, plans/, and adr/ respectively (101 ADRs relocated)
- Root Cleanup: Relocated HANDOFF-gpt55-*.md and key top-level docs (runtime_contracts.md, etc.) to canonical folders. Added superseded alerts.
- Indices & Navigation: Created docs/README.md navigation document, docs/sessions/README.md index, docs/adr/README.md index
- Note: Also includes prior commit adding ADR-0200+ corpus hygiene governance (ADR-0225, dependency map, backfilled cross-references)
2026-06-30 16:59:36 -07:00

110 lines
3.8 KiB
Markdown

# ADR-0148 — Wire VaultPromotionPolicy into turn boundary
**Status:** Accepted
**Date:** 2026-05-25
**Work item:** W-003
---
## Context
`VaultPromotionPolicy` (introduced in ADR-0014, implemented at
`core/physics/learning.py`) decides whether a stored vault entry should be
promoted from `SPECULATIVE` to `COHERENT` based on its energy profile.
Prior to this ADR, the policy had **zero callers**. Every vault entry written
by `session/context.py` remained `SPECULATIVE` indefinitely, regardless of how
settled or coherent the underlying field region was.
This blocked W-007 (DerivedRecognizer derivation), which requires `COHERENT`
vault entries to serve as valid recognition anchors.
---
## Decision
### 1. Flag in `RuntimeConfig` (`core/config.py`)
```python
vault_promotion_enabled: bool = False
```
Default `False` enforces the **null-drop invariant**: zero behavior change when
disabled. Operators opt in explicitly.
### 2. Energy metadata persisted at store time (`session/context.py`)
In `finalize_turn()`, after `_anchor_pull()` resolves `oriented_state`, the
energy fields are written into the vault payload before `vault.store()`:
```python
if oriented_state.energy is not None:
payload["energy_raw"] = float(oriented_state.energy.raw)
payload["energy_class"] = oriented_state.energy.energy_class.value
payload["coherence_residual"] = float(oriented_state.energy.coherence_residual)
```
Storing raw scalars (not the `EnergyProfile` object) keeps the payload
JSON-serializable and avoids coupling the vault to the energy dataclass.
### 3. `VaultStore.promote_eligible_entries(policy)` (`vault/store.py`)
New method scans all SPECULATIVE entries. For each entry:
1. Parses the stored `epistemic_status` string.
2. If SPECULATIVE, reconstructs a minimal `EnergyProfile` from the stored
`energy_raw`, `energy_class`, `coherence_residual` fields.
3. Calls `policy.decide(energy)`.
4. If `decision.promote`, updates `epistemic_status` to `COHERENT` in-place.
**Versors are not touched.** `_matrix_cache` is not invalidated because no
versor changes — only metadata mutates. Deterministic recall is unaffected.
### 4. Promotion fires post-finalize in `chat/runtime.py`
After each `finalize_turn()` call in `chat()`:
```python
if self.config.vault_promotion_enabled:
self._context.vault.promote_eligible_entries(VaultPromotionPolicy())
```
**Why post-finalize, not at store time?**
A freshly stored entry is always `E2+` (new activation, high recency). The
`VaultPromotionPolicy` promotes only `E0`/`E1` entries
(`vault_candidate=True`). A just-written entry will not promote on the same
turn it was written — it needs to cool across subsequent turns. This is the
correct multi-turn crystallization behavior described in ADR-0014.
---
## Consequences
### Positive
- Vault entries can now crystallize: SPECULATIVE regions that settle over
multiple turns become COHERENT, making them admissible as evidence under
`min_status=EpistemicStatus.COHERENT` recall.
- W-007 (DerivedRecognizer derivation from promoted entries) is now unblocked.
- Zero coupling change when `vault_promotion_enabled=False` (default).
### Constraints preserved
- **versor_condition invariant**: no versor is modified during promotion.
`promote_eligible_entries` mutates only `_metadata` dicts.
- **No normalization**: `vault/store.py` is a forbidden normalization site
per `CLAUDE.md`. Promotion is a metadata-only operation — it does not
repair, reproject, or normalize any field.
- **No approximate recall**: CGA inner-product scoring is unchanged.
- **Reviewed learning path**: promotion upgrades `epistemic_status` on
already-stored entries; it does not inject new content or bypass the
teaching review gate.
---
## Unlocks
- **W-007** — DerivedRecognizer can now query the vault at
`min_status=EpistemicStatus.COHERENT` and receive crystallized entries as
recognition anchors.