Implementation Spec · PLAN Phase · Visual Review

WICK_STORE_FILL_LIVE_WIRE_v1

Wire the existing eval-only createEmitExpandSession + EvictionAsPut into the live api-bridge path so real usage fills the canonical W5b store — gated default-OFF, byte-identical-off. The #1 lever: filling the store makes recall measurable and re-fires the parked TDA.

PLAN CONVERGED 3/3 steelman 8 11 panel rounds · every finding real frozen base 5877660c3 plan @ a9647f354 touches_security · touches_contract

01 The Problem

The canonical store is empty because the eviction-capable session and EvictionAsPut exist but are eval-only — no live consumer imports them. Recall can't be measured because nothing fills the store.

● Before — store empty, recall unmeasurable

  1. Live api-bridge turn runs with no emit-expand session mounted.
  2. Context overflows → handled by today's behavior, nothing stored.
  3. EvictionAsPut + createEmitExpandSession sit in tests only.
  4. W5b store stays empty → recall has nothing to address → TDA parked.

● After — real usage fills the store (flag ON)

  1. Gated mount puts real request/context through the emit-expand session.
  2. Window overflow → eviction → admission-gated content.
  3. Durable putTx commit → content keyed by sha256 = W5b address.
  4. Store fills from real usage → recall measurable → TDA re-fires.
Gating modelFlag-OFF ⇒ no session / eviction / store mounted — byte-identical to pre-store-fill HEAD. Flag-ON ⇒ the full additive capability. "Byte-identical" is strictly a flag-OFF-vs-today property; flag-ON is a new capability, never claimed identical to OFF.

02 Composition Seam — one boundary, decorator chain

Every unit stacks at the single seam SubstrateAdapter.put(raw) → {contentHash}. No unit reaches into another — a clean decorator chain across three owners.

LIVE api-bridge model turn   (flag ON)
        │
        ▼
createEmitExpandSession({ fetcher, eviction })     ← Layer 1 · gated mount   (session.ts:30)
        │   window overflow → eviction.onWindowOverflow (session.ts:140)
        ▼
EvictionAsPut({ substrate: gated, carryPolicy })   ← Layer 2 · my construction (eviction-as-put.ts:93)
        │   cleanly-evictable span → putTx commit → sha256 wick = W5b address
        ▼
gated = wrapAdmission( adaptStoreToSubstrate(realStore), revive-build P1/P2, claude-revive ledger )
        │        string-P1 (surrogate/secret/PII)   │ byte-P2 (dedup/provenance)  │ content-free terminal
        ▼
realStore.put(raw) → { contentHash }               ← the canonical W5b store recall reads

My deliverables (Layer 1 + 2)

adaptStoreToSubstrate (commit-only boundary) · flag-gated mount · PIN-B code-point boundary snap · bounded-retry ≤1 · construct EvictionAsPut + carryPolicy.

Composed at the seam

revive-build's wrapAdmission (store-agnostic P1/P2) + claude-revive's ledger (content-free terminals) + carryPolicy — stack without touching EvictionAsPut or the transport.

03 Abstain-Disposition Taxonomy — the correctness backbone

Store-fill is purely additive: it never owns window liveness (the session does), only STORE-or-NOT. Disposition branches on the admission's abstain reason — and store-availability is an orthogonal axis, not a fourth content category.

CONTENT · #1 transient

Surrogate boundary-split

Clean content sliced at a bad code-unit boundary. PIN-B re-slices once on a code-point boundary → stores. PIN A stays-live applies HERE ONLY. ≤1 retry; still-broken ⇒ #3.

CONTENT · #2 policy

Secret / PII — deliberate non-persist

NOT pinned live. Session drains normally; store-fill declines to persist + records a content-free redaction terminal. Deliberate ledgered non-store ≠ data loss.

CONTENT · #3 residual

Intrinsic-broken / all-abstain

Yields to the session's own non-storing eviction; never force-store-at-broken-key, never a new blocking error. Ledgered dropped-unstorable, bounded + visible.

ORTHOGONAL AXIS · store

Store-availability

A durable-commit failure can strike valid content — a separate axis with its own terminal dropped-store-unavailable. Kept distinct so telemetry says "fix the store" vs "content unrepresentable".

Unifying invariantNever changes flag-OFF window behavior; never SILENTLY loses STORABLE content. No abstain reason is both "pin-live-forever" AND "must-drain" — the contradiction is dissolved.

04 PUT-BEFORE-POINTER — one law, two faces

The deepest finding (Codex + Gemini): the wick / recall-address is an effect of a successful durable put, never a promise of a future one. It is contentHash-identity applied to time, and it closes the mirror of the original surrogate hole.

◧ Spatial face

The address's hash-domain must equal the store's key-domain: sha256(UTF-8(string)) on both sides. (store-without-recallable-address = orphan content)

◪ Temporal face

Content is durably stored BEFORE its address is recall-visible. (emit-address-without-durable-store = dangling address, recall-resolves-missing)

ConsequenceA dropped / abandoned / hung / crashed flush emits NO recall-visible reference → nothing dangles → recall never resolves-missing. Disk-verified: consolidate already does put(:151) → hash-check(:156) → carry-pointer(:164), with an alignment guard that throws (never corrupts) on concurrent index-shift.

05 Synchronous Commit-Gated Eviction

Gemini R2 proved onWindowOverflow is synchronous (must free tokens now) — so fire-and-forget is impossible. Overwatch traced the disk: the durable commit putTx (store.ts:347) is synchronous and completes before store.put's only await (the best-effort vectorize tail, :408). Gate on the sync commit; detach the tail.

onWindowOverflow · synchronousselect span (PIN-B code-point snapped) → admission (string-P1)
putTx · synchronous better-sqlite3 WAL commit (:347)durable commit runs to completion BEFORE the async vectorize tail
▼ branch on OUTCOME
putTx SUCCESSsplice CARRY pointer (wick+gist) → tokens freed + recoverable. Vectorize detached (fire-and-forget, .catch swallow+log — no unhandledRejection crash). Slow/failing tail is irrelevant.
putTx THROWSstore failure (DbClosed/Storage/VerifyMismatch) → NO pointer → non-storing evict through valid coords → dropped-store-unavailable. Caught; no new live-path error.
alignment-guard THROWSwindow mutated, coords stale → synchronous bounded RE-DERIVE on current window → then session non-storing fallback. Tokens ALWAYS freed this turn. eviction-stale-window.
Liveness guardrail — architectural, not a noteputTx is a synchronous single-thread call; a JS timeout can't preempt it. The live-path eviction uses its OWN short busy_timeout (tens of ms, distinct from the 5000ms batch default) → writer contention fails fast → fallback, never a multi-second serving stall. Degradation is metered (dropped-store-unavailable rate) so operators see it before users do.

06 Three Failure-Domain Terminals — telemetry-separable

Conflating failure domains blinds audit. Three distinct, content-free terminals keep store-health, concurrency bugs, and content limits separable — plus an independent-domain drop signal so a store-substrate failure never silently swallows its own record.

TerminalDomainTriggerDisposition
dropped-unstorablecontent-intrinsicintrinsic-broken / all-abstainsession non-storing eviction
dropped-store-unavailablestore healthputTx throwsdrop through valid coords + in-process counter + stderr signal
eviction-stale-windowconcurrencyalignment-guard throwbounded re-derive → session fallback (tokens freed this turn)
abstained-secretpolicysecret/PII on span textdeliberate non-persist, content-free redaction terminal
Independent-domain drop signalA store-failure drop is only "non-silent" if its record survives the same failure — so the primary signal is an in-process counter + synchronous stderr line, NOT the shared SQLite substrate that may be down. The ledger terminal is best-effort on top. Under total failure the process is dying → in-memory window co-dies (moot).

07 L0–L4 Test Pyramid touches_security · all required · real content, never fixtures

L0 · e2e — real turn → mount → overflow → evict → putTx → store has content (flag ON) vs no-op + byte-identical (flag OFF).
L1 · unit — adaptStoreToSubstrate; PIN-B code-point snap (flag-gated); bounded-retry ≤1; abstain-stays-live (#1); carryPolicy.
L2 · integration — turn → admission → sync commit → outcome branch; put-before-pointer ordering; putTx-throw → content-free fallback; short busy_timeout bound.
L3 · adversarial (overwatch) — gated-off byte-identity; put-before-pointer negative property (kill mid-commit → zero dangling); commit-vs-vectorize no-false-drop; throw-safety (3 domains); detached-vectorize crash-safety; secret/PII no-store + no-leak; independent-domain drop signal.
L4 · fix-verification — each finding closed by a test.

08 Convergence Record — 11 rounds, every finding real

The panel (Grok mandatory + Codex + Gemini + steelman) recorded each reviewer's actual verdict and refused to transition on any request_changes. No rubber-stamps — each round hardened a real defect before a line of code was written.

R1 secret/PII NC · bounded-overflow AC R2 put-before-pointer / fire-and-forget temporal contradiction R3 better-sqlite3 sync timeout · fallback · throw-handling R4 internal consistency · byte-identical scope R5 store-failure terminal · ledger-visibility R6 stays-live-vs-drop · liveness guardrail R7 commit-only boundary · large-span compute R8 independent-domain audit signal R9 alignment-guard vs store-failure domains R10 stale-window liveness · detached-vectorize crash R11 ✓ CONVERGED 3/3 + steelman 8

09 DESIGN Pins — deferred, cite-don't-assert

PinOwnerNote
api-bridge injection pointcoord-build-recall-b + claude-revivethe exact request-handler mount line — joint disk-trace (greenfield; no live consumer today)
commit-only store surfaceclaude-revive (store owner)thin method / signal exposing the sync putTx result with vectorize detached
terminal enumclaude-revive (B3_PROVENANCE_WIRE_v1)3 failure-domain terminals + abstained-secret vs an abstainReason field
store durability = FULLclaude-revive · ruled + implementedwick-coord ruling faaabd67 — pragma + power-fail test + SLA (already measured)
short busy_timeout value + span capcoord-build-recall-bthe exact tens-of-ms number + a span-size cap for the bounded-compute case