---
name: notif-truth-plan
# FIELD-VERIFY 2026-07-22 (hertz, HFENDULEAM, version-gated 0.40.0): observables 1/2/4/5 PASS
# (node-local uniqueness; stale migration INCL. 0.38.1 kitsubito N-1 leg + NOTIF_MIGRATE
# auto-dismissed 28+2; supersession 71 replicated rows active=0; seam dismissal both rows
# dismissed post-apply). Observable 3 (attached-session quiet delivery) NOT RUN — no newer
# signed offer exists post-apply, no public fixture path. Doyle-authorized path: hertz's
# isolated-SPT_HOME debug rig runs the DEBUG-ROLLOUT lab flow (keygen/pin/stage in the
# ISOLATED home only) → real signed debug offer exercises the true seam; fallback = accepted
# NOT RUN until next stable cut, doyle pings hertz pre-apply to arm an attached session.
#
# ⚠ DAY-ONE FIELD REGRESSION 2026-07-22 (operator, perri's node; perri field-confirmed rows
# dismissed + version 0.40.0): stale "update available" SPOOL COPIES deliver on updated nodes
# — self-clear is true at the ROW layer, false at the DELIVERY layer. Root + fix contract =
# ADR-0046 Amendment 1 + KH 7.53 + REQ-NOTIF-DRAIN-ROW-VALIDITY, riding DAEMON-LIFECYCLE W1
# ([[daemon-lifecycle-progress]]). Perri's standing falsifier: a copy surviving >1 drain =
# drain-not-consuming, a DIFFERENT defect — report immediately.
description: "NOTIF-TRUTH milestone (operator GO 2026-07-21, grill-ruled, ADR-0046): notif scope/supersession/TTL/quiet-delivery/migration → v0.40.0. Queued behind flynn shell mini-wave (v0.39.2). Doyle drives to release publish."
metadata: 
  node_type: memory
  type: project
  originSessionId: 8437132e-05e2-4e0f-9ded-92b344812dd0
  modified: 2026-07-22T10:01:27.778Z
---

**NOTIF-TRUTH — PLANNED MILESTONE (operator GO 2026-07-21: plan now, build after the flynn shell mini-wave publishes as v0.39.2, drive to v0.40.0 release publish).** Design fully ruled in a grill session same day — decisions + rejected shapes in **ADR-0046** (notif scope, supersession, quiet delivery; amends ADR-0007); canonical terms in CONTEXT.md §Subnet notifications (scope / coalesce key / dismissal-at-the-seam / TTL / delivery-window entries, all "ruled 2026-07-21").

**The six decisions (ADR-0046):** (1) scope = per-row producer-chosen `node`|`subnet` — update/consent/rollback node-scoped, never replicated; pairing/agent kinds stay subnet-scoped with cross-node dismiss. (2) coalesce key, REQUIRED namespaced form `<owner>:<key>` (front door rejects bare keys), latest-wins supersession. (3) dismissal-at-the-seam — producer dismisses by key at its own state transition (apply success; worker check running ≥ staged; later apply clears rollback); primitive stores NO predicates, evaluates NOTHING at surface. (4) notify kind rides `active_only` UNCONDITIONALLY incl rollback — loudness = resurface-until-dismissed persistence, never PTY interrupt; boundary resurface + adapter safe-point drain are the surfacing paths. (5) TTL producer-optional, expiry IGNORES seen-state, for seamless informational kinds only, no default. (6) migration = one-shot auto-dismiss of existing `from_id="spt-update"` rows (kinds consent/rollback); worker re-produces anything current within one cadence.

**REQ seeds (register at triage, activate on build branch per [[traceable-per-wave-activation]]):** REQ-NOTIF-SCOPE (scope attr + node rows never enter feeds + first-fire candidate set per scope) · REQ-NOTIF-COALESCE (namespaced-key enforcement + latest-wins) · REQ-NOTIF-SEAM-DISMISS (apply + worker-check + rollback-clear seams) · REQ-NOTIF-QUIET-DELIVERY (notify kind = active_only all producers; leg mirrors active_only_never_relay_e2e) · REQ-NOTIF-TTL (expired row auto-dismissed not surfaced; seen-independent) · REQ-NOTIF-MIGRATE (one-shot targeted dismiss, idempotent). N-1: additive serde-defaulted record fields (host_binaries pattern), schema regen drift-gated.

**Wave shape (2 waves, minimal overlap):** W1 store+wire — scope/key/TTL fields, coalesce+TTL semantics in NotifStore, migration pass, schema/N-1 legs (spt-store/notif.rs + spt-net/net/notif.rs + notifsync gate). W2 producers+delivery — update/rollback seam dismissal (pump/update.rs, applyhost), active_only window on the notify envelope (notif.rs delivery), scope-aware first-fire. Seam tests: notifsync + twohost notif legs + active_only e2e all run at gate ([[shared-seam-change-run-all-seam-tests]]).

**Sequence:** v0.39.1 published (c69) → flynn shell mini-wave = **v0.39.2** (todlando building now: frame vocab docs + {perch_dir} key) → NOTIF-TRUTH triage (doyle: REQs + dispatch doc) → todlando W1/W2 → doyle gates → deployah **v0.40.0** (minor bump — behavior change; counter from published at release time, never pinned early) → hertz field-verify (the observable: no duplicate update notifs, stale rows gone post-migration, no PTY interrupts, rollback surfaces at next boundary). **Doyle drives the whole lane to publish (operator order).**

**Status (2026-07-22): W1 MERGED (#53 @850d9b2), W2 DISPATCHED.** Triage @9a299f9 (6 REQs minted [doc], `docs/NOTIF-TRUTH-DISPATCH.md` = contract). W1 gate history: first push @8a9cc1a BOUNCED on 2 findings — (1) `expire_due` had ZERO production callers ("instead of surfaced" unenforced — resurface_at_boundary + first_fire_at both blind to expiry; fix wired sweep at per-subnet loop top + typed `FirstFireOutcome::Expired` guard covering already-expired replicated rows); (2) doc-comment misattachment in pump/mod.rs (new fn's doc fused onto reconcile's block). Fix @d289787 verified + full battery GREEN (build/clippy/nextest 2043/2043/traceable 0). SCOPE/COALESCE/TTL/MIGRATE now [doc,impl,unit]. W2 (producers+delivery, `build/notif-truth-w2` off @850d9b2) dispatched to todlando: node-scoped producers w/ coalesce keys, seam dismissal (apply + worker-check + rollback-clear), unconditional active_only, scope-aware first-fire; grows SEAM-DISMISS/QUIET-DELIVERY to [doc,impl,unit,int] + SCOPE +int. Then doyle gates → deployah v0.40.0 (migration = Changed entry) → hertz field-verify. ⚠ gate-observed once: `fork_forks_homes_and_deletes_exactly_the_source` bare exit-1 under first cold parallel run, 0/20 loop + full rerun green — seeded [[spt-core-findings-backlog]]. Related: [[rc-render-truth-progress]] · [[v0394-published]]

## OBS-3 ROOT (found at v0.41.0, 2026-07-22 — moved from index) ⭐⭐
**DO NOT SCHEDULE OBS 3 AGAINST A STABLE CUT AGAIN.** The v0.40.0 "no peer-pump ingestion route" explanation is SUPERSEDED. `notif.rs`: `ConsentDecision::AutoApproved => Ok(None)` — on a **full-auto node the update-available notice is NEVER CONSTRUCTED**, so obs 3 has no subject and is STRUCTURALLY UNOBSERVABLE there. v0.41.0 supplied a real update, a real continuously-attached pre-apply viewer, and perfect arm discipline, and it still could not fire. PRECONDITION for any future attempt: an observing node where consent is REQUIRED (NOT full-auto). Needs no release — a non-full-auto node + any staged update reproduces it, taking obs 3 off the release clock. The old "ping hertz before apply" standing order is necessary but NOT sufficient and misleads as written.
