---
name: counter38-field-bug-milestone
description: "counter-38 field-bug milestone (18 bugs/13 REQs) — branch field-bug-milestone, wave progress + remaining order"
metadata: 
  node_type: memory
  type: project
  originSessionId: 7b460723-7bdb-4e3f-970f-3e7ef0cc79ee
---

**LATEST-8 2026-07-01 (todlando) — perri consumer-loop CLOSED (claude-spt v0.10.0) + spt-core docs follow-up PR#42:** perri shipped **claude-spt v0.10.0** (fetcher migration + min_spt_core 0.19.0), applied on-node 0.9.2→0.10.0 (#18 rename-tolerant update re-confirmed, NO skew brick on 0.1.9 dispatch + unchanged hook_cmd). #17 EMPIRICALLY CLOSED on the 0.19.0 binary: [digest] strategy="fetcher" extractor=`claude-spt digest --session {session_id} --config-dir {CLAUDE_CONFIG_DIR}`, [env.CLAUDE_CONFIG_DIR] direction="read" fallback ~/.claude, precedence --config-dir→inherited-env→legacy --in→~/.claude/projects; digest-proof DIGEST_PROOF_OK 5/0 + real ccs transcript located via --config-dir (133 records). ONE deferred E2E: daemon-context ccs digest (`spt endpoint digest` on a live spt-core-hosted ccs endpoint) — none exists on this node (live agent rides legacy owl) → perri proves on first real spt-core ccs bringup (flag me). SKEW-BRICK triaged NOT spt-core: stale cplugs dispatch.sh(0.1.8) execs `"$bin" <event>` vs new bare-path hook_cmd → unknown-subcommand → CC tool-block+Stop-loop until /reload-plugins (fired here); adapter/plugin forward-compat gap, perri added to their KNOWN-HAZARDS. **SPT-CORE DOCS GAP perri flagged (real):** published `docs-site/src/harness-contract/manifest.md` §[digest] (source of llms-full.txt) documented ONLY locate_normalize — the v0.19.0 [digest] `fetcher` strategy + [env] read-fill shipped in the SCHEMA + internal docs/MANIFEST.md (W6b) but NOT the published PROSE ([history] fetcher WAS documented, [digest] wasn't). FIX = **PR#42** `docs/digest-fetcher-published-prose` (off HEAD a5129cd, ancestor of main): ported fetcher prose to docs-site [digest] (strategy field+table, adapter-locates/no-source/harness-neutral {session_id}+{cwd}+read-vars, cross-field rule scoped to locate_normalize). [doc->REQ-DIGEST-FETCHER-STRATEGY], traceable exit 0, docs-only (llms-full.txt regen at docs-publish), no bump/no [twohost]. **MERGED @296a0a9 → main aebabaa** (verified clean: merge landed ONLY docs-site/manifest.md +33/-7; the gh-merge-tail "create mode" lines were misleading, not the real diff). One hfenduleam multi_subnet_bringup E2E flake cleared on rerun. docs-publish fires on v* TAGS / manual dispatch ONLY (not push-to-main) → prose auto-publishes on the v0.19.1 tag (counter-39). perri told: merged, publishes with v0.19.1, not blocked (v0.10.0 shipped). Consumer loop + docs follow-up BOTH closed. GOTCHA REINFORCED: internal docs/MANIFEST.md ≠ published docs-site/src/harness-contract/manifest.md — a shipped seam needs the PUBLISHED surface updated too or the blind adapter-builder (perri) can't discover it; [history] had it, [digest] didn't = asymmetric drift the drift-gate (xtask check = links+CLI-ref only, not prose-completeness) doesn't catch.

**LATEST-7 2026-07-01 (todlando) — v0.19.0 PUBLISHED + VERIFIED, perri kicked, MILESTONE SHIPPED END-TO-END. DONE.** doyle final independent gate-verify (pulled spt-releases himself): release v0.19.0 draft=false prerelease=false published 21:16:11Z, all 8 assets; linux .release.json version=38/product 0.19.0 sha=7aff6be3 MATCHES SHA256SUMS; windows version=38/product 0.19.0 sha=3671533d MATCHES; update-set.json version=38; signed rel-primary-2026 channel=stable; MONOTONIC 37→38 no collision, rollback floor intact. Clean SINGLE-PASS publish (no assemble-TLS flake; runner-contention queue wait behind backstop ci on shared runners). Publish tail was deployah's step, doyle the FINAL independent gate (avoid split-brain — todlando held relay until DOYLE's published-confirmed, not release.yml-green nor deployah's tail-report). perri KICKED (v0160-release-ping-perri pattern): min_spt_core→0.19.0, cluster-A consumable; perri ACK'd + in-flight (node binary 0.18.0→0.19.0 → min_spt_core bump → #17 [digest] fetcher migration strategy="fetcher"+extractor-as-locator+[env]read for CLAUDE_CONFIG_DIR; perri confirmed #18 rename-tolerant update-in-place works LIVE, found separate plugin-side mid-session skew brick — asked perri to route to triage if it touches spt-core update/register seam). GOTCHA (owl send body): `printf '%s' "..."` DOUBLE-quoted body with `spt ...` substrings → bash command-substitution EXECUTED them (leaked ADAPTER_UPDATE_UPTODATE/rc-usage, corrupted the sent body); ALWAYS use `"$OWL" send <t> <<'EOF'` single-quoted heredoc for message bodies (zero expansion). Ledger: counter 38 @bac50fc = v0.19.0 (v0190-published.md + MEMORY.md index already added). Counter-38 field-bug milestone (18 bugs, #4/#12/#6/#2/#3/#9/#10/#16/#1/#17/#18 + picker #5/#11/#13/#14/#15) COMPLETE. Perch armed (Monitor b4dh1ilze, revived gen). NEXT: nothing pending my side — stand by for perri digest-proof questions or operator's next milestone.

**LATEST-6 2026-07-01 (todlando) — OPERATOR RELEASE GO → v0.19.0 MERGED+TAGGED, release.yml building:** Operator (SaberMage) gave release GO (relayed via doyle high-pri owl msg; doyle revived this session per operator, perch re-armed via `$LIVE revive todlando` Monitor task). Operator MERGED PR#41 himself via GitHub UI @20:52:20Z → merge commit **bac50fc** = origin/main HEAD; **tag v0.19.0 → bac50fc** pushed @20:52:40Z → **release.yml run 28547039496 building** (deployah has doyle's GO, drives build+sign+publish + spt-releases metadata, counter from PUBLISHED metadata monotonic NOT memory). VERIFIED tagged commit sound: bac50fc Cargo.toml=0.19.0 + Cargo.lock spt-daemon=0.19.0 (first-party bump present at tag; noq untouched; vte 0.13.1); contains d819a52(bump+CHANGELOG) + a5129cd(env-gate, CI 7/7). NOTHING for me to merge/tag — operator did it UI-side; my role = verify (done) + watch release run + coordinate deployah + confirm on publish-success. AFTER publish: perri manifest min_spt_core→0.19.0 rides (ping perri GO per [[v0160-release-ping-perri]] pattern). Counter 38 = v0.19.0 (add to published ledger [[v0121-published]] on confirm). Watching run 28547039496.

**LATEST-5 2026-07-01 (todlando) — PR#41 ALL 7 GREEN @a5129cd, doyle pinged for deployah GO:** Executed doyle's Path-1 env-gate @a5129cd (SPT_FORKPTY_BACKPRESSURE_HARD opt-in: unix→hard only if env set else eprintln capture-not-assert mirroring windows arm; 3 containment asserts + broker unit UNTOUCHED; std::env already fully-qualified in-file; tags/traceable unchanged; test-only, no source). Push → fresh run 28513985474. Sequence to green: (1) Linux test PASS (paste-wedge now capture-not-assert on kitsubito). (2) Windows test FLAKED once = `seedmap::tests::stop_op_acks_then_serve_returns` TIMEOUT@240s (1335 pass, 1 timed out) — the documented [[seedmap-test-collides-live-daemon]] loaded-box starvation (hfenduleam busy: long queue+fresh rebuild+doyle active), NOT disk (164G free), NOT code → `gh run rerun --failed` cleared it (11m30s PASS). (3) twohost-a + twohost-b PASS = [twohost] cross-machine leg exercised #9/#10 WAN reply-leg + #4 cross-node across BOTH real hosts. FINAL 7/7: traceability + n1-gate(L+W) + test(L+W) + twohost-a + twohost-b all PASS. PR#41 HEAD @a5129cd = d819a52(bump+CHANGELOG) + a5129cd(env-gate). `$OWL send doyle` all-green ping SENT. AWAITING doyle deployah publish GO on operator release GO → then merge/tag mechanics (bump-in-PR: PR CI validated the tagged commit; tag on merge). DISK NOTE for future cuts: hfenduleam recurring disk-full — the hogs are stale SIBLING WORKTREE TARGETS (../spt-core-*/target, ~15-30G each) + TEMP gate scratch (cv-gate7*, 177G), not just the one CI target [[hfenduleam-disk-full-ci]]; reclaim worktree targets + TEMP scratch when C: tightens.

**LATEST-4 2026-07-01 (doyle) — PR#41 paste-wedge kitsubito hazard-gate RULING = Path 1 (env-gate), NO regression hunt:** kitsubito `test(Linux)` failed 3/3 on ONLY `p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker` (inject_control_wedge.rs:2510), `backpressured=false`. RULED test-env calibration not code — verified whole chain: (1) the failing `#[cfg(unix)] assert!(backpressured)` is NON-VACUITY (did the depth-4 input FIFO saturate), NOT the hazard guarantee; the 3 CONTAINMENT asserts (subscribed/got_output/keystroke_accepted = REQ-HAZARD-PTY-INPUT-WRITER-WEDGE) are hard-every-platform + PASS on kitsubito. (2) failure direction backpressured=false = FIFO never saturated = writer drained FAST = OPPOSITE of a wedge/starvation regression → affirmatively no regression. (3) DROP+stamp+HEAL mechanism deterministically UNIT-covered broker.rs:3255-3305 (forced DROP leg, no forkpty timing) → gating the int reproduction loses ZERO coverage. (4) milestone touches nothing on input path (#6 OUTPUT-only per ADR-0031, #16 journal-recovery, #4/#12/#2 unrelated). (5) cfg(unix) too broad: kitsubito forkpty drains faster than gravity → can't deterministically saturate depth-4 FIFO (passed@v0.18.0 by timing-luck); comment L2510 already says "gravity-linux CI carries the backpressure assertion" + Windows arm ALREADY capture-not-assert. MECHANISM ordered: replace hard `#[cfg(unix)] assert!(backpressured)` with OPT-IN env gate SPT_FORKPTY_BACKPRESSURE_HARD (set→hard for gravity manual/acceptance; else eprintln capture like windows); DO NOT touch the 3 containment asserts or the broker unit; tag/traceable unchanged. NEW REUSABLE LESSON: a real-forkpty SATURATION reproduction assert is env-calibrated to the SLOW-forkpty host (gravity); kitsubito(fast-drain)+windows(ConPTY-absorb) can't saturate → gate reproduction by ENV not cfg(unix); hazard MECHANISM stays covered by the deterministic broker unit + CONTAINMENT stays hard everywhere. doyle reclaimed own 31G gate scratch (gate-bug2+gate-bug6) from TEMP. AWAITING todlando env-gate+rerun → all-green → doyle deployah GO on operator release GO.

**LATEST-0 2026-07-01 (todlando) — v0.19.0 CUT, PR#41 OPEN, CI env-flakes cleared, rerun in flight:** doyle COMBINED GREEN @b67d23d received. Cut release: bump workspace + 11 first-party Cargo.lock entries 0.18.0→0.19.0 (third-party `noq` coincidentally @0.18.0 LEFT untouched per first-party discipline; `vte` bug6 pinned 0.13.1), CHANGELOG v0.19.0 (12 field bugs → user surfaces, v0.18.0..HEAD), commit **@d819a52** (`Co-authored by: todlando`), pushed field-bug-milestone (origin was behind @ec360f1). **PR#41** title carries `[twohost]`. FIRST CI RUN (28510414012): traceability PASS, n1-gate BOTH hosts PASS, but `test` FAILED both legs + twohost-a/b SKIPPED. DIAGNOSIS — both env, NOT my commit (version+CHANGELOG only, zero runtime effect): (a) **Windows LNK1318/LNK1201** = hfenduleam DISK-FULL ([[hfenduleam-disk-full-ci]]) — C: was 3.7G free/100%; freed by `rm -rf /c/actions-runner/_work/spt-core/spt-core/target` (21G rebuildable CI target, scoped per [[no-machinewide-killon-shared-runner]]) → 25G free. (b) **Linux paste-wedge flake** `p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker` @inject_control_wedge.rs:2512 (`backpressured=false` — parked-writer FIFO never saturated, load-dependent on hammered box) → rerun on quiet box. (c) **twohost SKIP is NOT a tag miss** — both jobs `needs: test`; failed dep → skipped; title tag correct, twohost auto-runs once test greens. MULTI-RERUN OUTCOME: Windows test PASS after disk fix (12m14s cold rebuild). Linux test FAIL x3 — ONLY `p0_paste_wedge_parked_write_does_not_starve_attach_or_wedge_broker` @inject_control_wedge.rs:2512 (94/95 pass; 3 core asserts subscribed/got_output/keystroke_accepted PASS; #[cfg(unix)] `backpressured` assert fails — depth-4 input FIFO never saturates in 6s poll window). **NOT a load-flake:** kitsubito load=2.25/16cores (~14% QUIET) yet still fails. Assert commit **0675b37 PREDATES v0.18.0**; v0.18.0 @461e6fe kitsubito CI ran GREEN 2026-06-30 → passed there by ENV-LUCK, fails now 3/3. Assert comment L2510 "gravity-linux CI carries the backpressure assertion" = calibrated for gravity forkpty; kitsubito drains FIFO faster → never saturates even idle (Windows arm already CAPTURED-not-asserted for same ConPTY-absorbs reason). **ESCALATED to doyle (`$OWL send doyle`, SENT) — hazard test = his gate.** Two options given: (1 my lean) TEST-ROBUSTNESS: gate backpressure hard-assert to gravity via env guard (e.g. SPT_FORKPTY_BACKPRESSURE_HARD) or capture-not-assert on kitsubito like Windows arm; (2) REGRESSION HUNT #6-broker-drain/#16-journal if he suspects milestone shifted input-writer timing (nothing in milestone touches input FIFO/child-drain). DISK ROOT (handled, recurring hazard): hfenduleam C: filled to <1G by 21G CI target + 119G STALE SIBLING WORKTREE TARGETS (../spt-core-{bug2,bug6,delivery,translate-proof,ufx,wedgeval}/target) + 177G TEMP cv-gate7 gate scratch — freed ~130G (worktree targets + CI target); C: now 131G free. spt-core-combined/crates/spt-daemon = Device-busy (doyle's worktree, left it). TEMP 177G gate scratch still reclaimable. AWAITING doyle assert-ruling → execute + rerun → all-green → doyle deployah GO on operator release GO. Everything else (6/7 checks) GREEN + ready.

**LATEST-3 2026-07-01 (doyle) — FOLD-IN GATED, v0.19.0 PR handed to todlando:** todlando merged all 3 branches into field-bug-milestone (bug4→bug6→bug2, all CLEAN, traceable union auto-resolved), added the #4×#6 intersection int (cross_node_cold_attach_to_alt_screen_gets_clean_repaint, cfg(unix)→Linux CI), merged HEAD **@b67d23d**. doyle COMBINED RE-GATE (throwaway worktree ../spt-core-combined @b67d23d detached, fresh target): traceable exit 0, clippy --workspace --all-targets -D warnings 0, nextest 674/675 + seedmap::stop_op_acks_then_serve_returns passes 0.023s ISOLATED = 675/675 effective. GATE-FLAKE LESSON (reconfirmed): a hammered box (3 back-to-back gates) → (a) seedmap stop-barrier STARVATION timeout @240s [[seedmap-test-collides-live-daemon]] — clears isolated; (b) an FS-write flake on the fresh target's .fingerprint corrupted the target dir → clippy died TWICE on half-built artifacts (libsqlite3-sys bindgen.rs missing, aws-lc-sys NASM noise) — NOT lints; a PRISTINE fresh target (nuke + rebuild) gave clippy exit 0. Danger-zone static re-read PASS: #6 repaint_initial:322/become_controller:436 byte-identical through merge, #4 remote_session threads at serve_attach NOT into become_controller, intersection int asserts alt-present+scrollback-absent (raw wire AND ScreenGrid render). MERGE-CAUGHT by fresh-target combined gate: #4's 7th serve_attach arg remote_session:Option<u64> + request_attach_endpoint unused-import-on-windows → cfg(unix)-gated (validates the combined-gate requirement). **COMBINED GREEN issued** → todlando cutting v0.19.0 release PR (bump-in-PR, CHANGELOG v0.18.0..HEAD, [twohost] in PR TITLE, verify vte Cargo.lock first-party discipline, push field-bug-milestone origin-behind@ec360f1). AWAITING todlando PR-open+CI-green ping → doyle issues deployah publish GO on operator release GO. Perri manifest (min_spt_core) rides AFTER cut.

**LATEST-2 2026-07-01 (doyle) — MILESTONE COMPLETE, all builds gated:** #2-SECONDARY per-row Offline-TTL **GATED GREEN @a31667f** (bug2-offline-ttl, worktree ../spt-core-bug2, off @026a118, unmerged). Independent re-gate fresh target: traceable exit 0, clippy --workspace --all-targets -D warnings 0, nextest -p spt-net -p spt-daemon 626/626 (5 leaky, 1 skip), NO [twohost]. IMPL as locked: SubnetRegistry::evict_aged_offline rebuilds #[serde(skip)] offline_since each sweep (sticky first-seen carried fwd → fresh-epoch re-advert doesn't reset; own/routable skipped self-prune; aged dropped from map; saturating_sub + strict >grace); RegistryHost::evict_aged_offline_rows_at mirrors evict_silent_peers_at; pump pre_round wire after evict_silent_peers; REQ-HAZARD-REGISTRY-GHOST-ROWS broadened (trigger b), stays [doc,impl,unit]. WHOLE counter-38 now BUILT+GATED: acute W1-W6b + #4/#12 @9bb2da5 + #6 @dcbbf3d + #3 @41b169c + #2-primary @a93ddba + #2-secondary @a31667f. **NEXT = combined v0.19.0 release PR** (todlando fold-in): merge bug4+bug6+bug2 into field-bug-milestone (watch Cargo.lock vte add from bug6, first-party lock discipline), bump-in-PR, CHANGELOG v0.18.0..HEAD, **[twohost] in PR TITLE** (W5a #9/#10 + #4 want cross-machine CI; head_commit NULL on pull_request → tag in title), push (origin behind @ec360f1). doyle re-gates the COMBINED merged HEAD ONCE (fresh target) before PR to catch cross-branch interaction, then deployah GO on operator release GO.

**LATEST 2026-07-01 (doyle):** #6 REQ-BROKER-SCREEN-GRID **GATED GREEN @dcbbf3d** (bug6-screen-grid branch, worktree ../spt-core-bug6, unmerged). Independent re-gate fresh CARGO_TARGET_DIR: traceable exit 0, clippy --workspace --all-targets -D warnings 0, nextest -p spt-term -p spt-daemon 540/540 (5 leaky benign), NO [twohost]. Design refinement BLESSED — todlando caught the repaint pseudo-seq (next_seq-1) is a FORWARD JUMP the legacy brain strict reject-gap (B2) fatals; resolution = repaint scoped to COLD attach (from_seq==0), Brain::baseline_next_output accepts-then-strict; resume-from-floor (from_seq>0) raw-ring re-fetch + irrecoverable-behind UNCHANGED; CONTROLLER-GAP-RESUME re-repaint DEFERRED (recorded in design doc). Now the WHOLE design batch is BUILT: #4+#12 @9bb2da5 (gated), #6 @dcbbf3d (gated), #3 @41b169c (gated W4a), #2-primary @a93ddba (gated W2). **#2-SECONDARY Offline-TTL eviction DISPATCHED to todlando** = the ONLY remaining build (per-row receiver-side Offline-TTL, sticky wall-clock offline_since NOT epoch-keyed, mirror evict_silent_peers seam, broaden REQ-HAZARD-REGISTRY-GHOST-ROWS); its gate-green is the LAST build before the v0.19.0 release PR. NOTE: "#3-local-view" is a PARKED seed (REQ-GOSSIP-CONTROLLED-LOCAL-VIEW, required_stages=[], mint-only-if-picked-up) — NOT a committed batch item. Ground truth = traceable-reqs (triage STATUS lines are STALE).

**counter-38 field-bug milestone** (doyle-dispatched 2026-06-30). Branch `field-bug-milestone` off origin/main @461e6fe (=v0.18.0). Working in the SHARED main checkout `C:\Users\decid\Documents\projects\spt-core` (already on the branch). Ground truth = `docs/NEXT-MILESTONE-BUG-TRIAGE.md` (root cause + file:line + fix per bug; has doyle's live-verified corrections on #17/#18). 13 REQ seeds in traceable-reqs.toml; activate per-wave. Version = minor (v0.19.0 likely) — bump rides the release PR, not per-wave. Report per-wave, doyle gates on return.

**SHIPPED (pushed, doyle-ack pending on W2/W3 clean-target re-gate):**
- W1 @9216bfd — #16 REQ-BROKER-ATTACH-JOURNAL-RESILIENT (URGENT live regression, eel-a wedged after v0.18.0 self-update respawn). EffectJournal::lock_recover (PoisonError::into_inner, all 6 .expect sites) + loopback open_stream → bounded_block_on. doyle ACK'd green.
- W2 @a93ddba — #5 node_ident_display ("This node: <id>"), #14 STATUS_ROW_ENABLED=false (id-marker off), #2-PRIMARY routable-only denominator (raw count for all-Offline liveness). doyle confirmed clean.
- W3 @3bebb25 — picker pair: shared EndpointRow::from_resource_row + EpDisplay glyph/ansi_color_code/square (picker::model now pub(crate)); #13 REQ-PICKER-NODE-GROUPING (dedup per-machine, union subnets, most-alive); #11 REQ-ENDPOINT-LIST-PALETTE (colored square in format_subnet_rows) + #15 (Dormant→ONLINE, no bare-word leak, display-only). doyle GATE-GREEN (isolated-worktree reproduce).
- W4a @41b169c — #3 REQ-GOSSIP-CONTROLLED-ANY. InfoJson.controlled bool + set_controlled; broker stamp_driven_by ALSO stamps controlled=self.controller.is_some() (driven_by stays remote-only, KH 7.15 hazard test gap_b still green); registryhost advertise_local gossips controller_node via pure gossip_controller_node (remote origin → else local self.node_hex → else None). SCOPED to gossip/remote path; local picker rows unchanged. GOTCHA fixed: don't insert a fn between a doc comment and its fn — orphans the doc → clippy doc_lazy_continuation. doyle GATE-GREEN (isolated worktree @41b169c: clippy 0-warn, nextest 29/29 incl gossip_controller_node_covers_any_controller + KH-7.15 gap_b, traceable +impl+unit).

- W6b @b9e5ce9 — #17 CLOSE, REQ-DIGEST-FETCHER-STRATEGY (doyle {project} ruling = CONTEXT-symmetry, NOT a new invention). [digest] DigestStrategy{LocateNormalize(#[default],serde-default field→pre-W6b manifests parse unchanged)|Fetcher} mirrors [history] fetcher; validation gates source-resolvability on locate_normalize only; extract_digest branches (Fetcher = run extractor bounded, NO source/pre-read/stdin → records(stdout); LocateNormalize unchanged). Fetcher fed harness-NEUTRAL {session_id}+{cwd}(=info.json.cwd, threaded project_endpoint_digest→activity_spanned)+captured read-vars — extractor globs {session_id} under root, NO {project} slug (that was the charter violation). digest-proof: {cwd}=current_dir + --sample forces locate_normalize. MANIFEST.md doc + manifest.schema.json regen (SPT_BLESS=1 cargo test -p spt-runtime checked_in_schema_is_current — the drift gate). Gate (scratch target): clippy 0-warn, nextest 29/29 incl fetcher_digest_lets_the_adapter_locate_and_emit(int)+fetcher_digest_strategy_needs_no_source(unit)+spt digest-proof, traceable +doc+impl+unit+int. spt-core #17 DONE end-to-end; perri loop UNBLOCKED (strategy=fetcher + owlery::claude_projects_root locator fed {session_id}/{cwd}/{CLAUDE_CONFIG_DIR}). GOTCHA: adding a serde-default field to a schemars struct → regen manifest.schema.json via SPT_BLESS or the drift gate reddens CI.
- W6 @9305d19 — ADAPTER CLUSTER #18/#1/#17 (last acute wave, built fresh one pass). #18 REQ-ADAPTER-UPDATE-INPLACE: dest = adapter_update_install_dir(record)=source_dir (was _github/<safe> from [update].repo). #1 REQ-ADAPTER-ADD-SURFACE-ERRORS: conduct surfaces stderr via subprocess_detail + install_post_step runs [update.post] at add-time (Delegate + gh_release arms). #17 REQ-DIGEST-PROFILE-ENV: wired dormant `[env] direction="read"` — spt-runtime capture_read_env/inject_read_env_keys/expand_tilde + InfoJson.read_env (additive) + establish_perch capture-at-bind (both topologies) + daemon activity_spanned resolve + digest-proof live-resolve + MANIFEST.md doc. Gate (fresh scratch target $TEMP\spt-w6-gate): clippy --workspace --all-targets 0-warn, nextest spt-runtime/store/daemon 651/651 + new units + adapter_post_step int + digest-proof units, traceable exit 0. NO [twohost] (no cross-node seam). **KNOWN GAP (escalated to doyle, NOT in #17 scope):** perri's relocated [digest].source needs a per-session FILE path but spt-core provides NO {project} key (CC slug = harness-specific cwd munging) AND pre-reads {source} as a file → root-dir source can't resolve; eel-a end-to-end NOT closed until doyle design-rules (provide harness-neutral cwd key / stop pre-reading a dir / adapter carries slug). #17 delivered the env-read seam+docs as specified. GOTCHA confirmed: AdapterKind is in spt_runtime::manifest NOT ::registry (registry only private-`use`s it) — cargo check -p spt --bin spt does NOT compile cfg(test), so a bad test import passed check but failed nextest.
- W5a @ec360f1 — #9/#10 REQ-WAN-SEND-DELIVERY. (a) seed_first_addr = PeerAddrStore direct-addr FIRST (id-only fallback), all 3 wansend resolver sites. (b) reply-leg: WanReply record (spt-net wanmsg) + WanOutcome::token/WanRequestOutcome::from_token + request_wan round-trip (daemon wan.rs, mirrors request_rest) + serve_wan_feed writes reply pre-EOF (dispatch.rs) + wan_send_with blocks → classify_wan_reply. SENT(WAN) ONLY on Delivered/Spooled/Duplicate; Refused/NoPerch/NoReply → honest non-zero CLI. Proven in-proc by wan_send_ships_to_the_resolved_node (real two-broker round-trip, distinct node ids). TWOHOST: real cross-machine confirm = [twohost] CI, PR-TITLE tag (head_commit NULL on pull_request). GOTCHA: request_wan first-record via `if let ...next()` not `for` that always returns (clippy never_loop). doyle GATE-GREEN (worktree @ec360f1: clippy 0-warn, nextest 25/25 incl classify_wan_reply honest-SENT invariant + wan_send_ships_to_the_resolved_node in-proc two-broker round-trip + seed_first_addr, traceable +impl+unit+int). Real cross-machine = CI [twohost] PR-title tag.

- W7 @9bb2da5 (branch bug4-cross-node-attach, worktree ../spt-core-bug4 off 99e3ef7; AWAITING doyle gate) — #4 REQ-RC-CROSS-NODE-ATTACH + #12 REQ-RC-WIN-VT-OUTPUT, ONE commit. **#4** (doyle D1-D6): rc.rs run_attach_inner local session-miss → wansend::resolve_and_dial_owner (new; reuse resolve_across_visible/seed_first_addr/net_dial → OwnerDial{Dialed/NotFound/Ambiguous/Unreachable}) → request_attach_endpoint(endpoint_id, session_id=0). Additive AttachRecord::Request.endpoint_id (serde default+skip_serializing_if=none ⇒ local wire BYTE-IDENTICAL + N-1; request_attach kept as thin shim → request_attach_endpoint to avoid ~13 caller churn). SERVE: dispatch worker resolves endpoint_id→session (resolve_local_session, D5 presence/D6 stale) BEFORE serve_attach subscribes, passes remote_session:Option<u64> down; serve_attach uses it. Honest fail: PumpEnd::NoLiveSession (pump tracks rendered_any; serve-closed EOF w/ nothing rendered ⇒ "no live session on {node}, stale row") + resolve-miss copy. **CRITICAL GOTCHA (my first cut = 240s hang):** do NOT call brain.sessions() (or any request/reply) MID serve_attach event-loop — sessions() does `_ => continue` and SWALLOWS interleaved NetStreamData(Input)/Subscribed ⇒ typed bytes lost ⇒ no echo ⇒ hang. Resolve in the dispatcher (pre-subscribe, quiet brain) and pass the sid down. **#12** (doyle): rc RawGuard gains prior_out_mode; enable() cfg(windows)+windows_mouse_wanted() → enable_vt_output (GetStdHandle STD_OUTPUT_HANDLE→GetConsoleMode→SetConsoleMode with_vt_output(prior)=|VT_PROCESSING|PROCESSED_OUTPUT), restore on Drop. Piped stdout fails GetConsoleMode→None→no-op (clean e2e). spt already had windows-sys Win32_System_Console (no new dep). GATE (scratch target C:\...\Temp\spt-bug4-target): traceable exit 0 (#4 +doc+impl+unit+int, #12 +impl+unit), clippy --workspace --all-targets 0-warn, nextest spt-net attach 6/6 + spt wansend/rc 9/9 (resolve_and_dial + with_vt_output) + spt-daemon binary(dispatch)+binary(attach) 27/27 incl new int dispatcher_resolves_attach_by_endpoint_and_refuses_a_stale_endpoint. NO [twohost] (int = in-proc two-broker loopback; real cross-machine attach = later [twohost] add). serve_attach behaviourally UNTOUCHED (VIEWPORT-ONLY/two-conn-split/QUIC-origin inherited).

**BRANCH/PUSH STATE (2026-06-30, IMPORTANT for release):** the whole milestone since W5a is LOCAL-ONLY — origin/field-bug-milestone is BEHIND at ec360f1 (W5a). Local field-bug-milestone @026a118 (=acute W1-W6b + triage docs + my #6 design-doc commit). W7 (#4/#12) is on a SEPARATE local branch bug4-cross-node-attach @9bb2da5 (off 99e3ef7), NOT yet merged to field-bug-milestone — todlando's merge call. All worktrees share ONE .git (a commit on field-bug-milestone in the main checkout advances the ref everywhere). PUSH happens at the release-PR cut (bump-in-PR discipline). Gate protocol: gate each sha in a throwaway git worktree of the shared .git (works for local branches too) + fresh CARGO_TARGET_DIR.

**W7 @9bb2da5 (bug4 branch) — #4 + #12 doyle GATE-GREEN 2026-06-30.** #4 REQ-RC-CROSS-NODE-ATTACH: local session-miss → wansend::resolve_and_dial_owner (reuse resolve_across_visible/seed_first_addr/net_dial) → request_attach_endpoint(endpoint_id, session_id=0); additive AttachRecord::Request.endpoint_id (serde skip_if=none → local wire byte-identical + N-1); cross-node endpoint→session resolve in the DISPATCHER before subscribe (resolve_local_session) — NOT mid-serve (todlando's first cut did mid-loop sessions() → swallowed interleaved Input → 240s hang; fixed). Honest-fail PumpEnd::NoLiveSession + NotFound/Ambiguous/Unreachable. #12 REQ-RC-WIN-VT-OUTPUT: rc RawGuard SetConsoleMode STD_OUTPUT_HANDLE |= ENABLE_VIRTUAL_TERMINAL_PROCESSING|PROCESSED_OUTPUT, cfg(windows)+mouse-guard, restore-on-drop; piped-stdout GetConsoleMode-fail→no-op→clean e2e. GATE: clippy 0-warn, traceable +stages both, nextest 27/27 non-E2E incl remote_attach_drives_a_real_pty_cross_daemon + spt_hosted_bringup_then_cross_node_attach + dispatcher_resolves_attach_by_endpoint_and_refuses_a_stale + with_vt_output_enables_virtual_terminal_processing + HAZARDS HELD (input_ack_deadlock + inject_control_wedge x2). 4 E2E fails = live-box fs::copy(spt.exe) collision (W2 class, NOT diff).

**#6 DISPATCHED + docs committed @026a118 (field-bug-milestone).** V0.19.0-P6-SCREEN-GRID-DESIGN.md + docs/adr/0031. todlando building after W7. vte crate + clean-room current-screen ScreenGrid, render_repaint replaces raw-ring initial-batch, ADR-0008 reconciled (render-grid = render-reads-PTY side, content-parser stays retired). CONTEXT already carries both my grill amendments (committed in W6b).

**RELEASE SEQUENCING (operator ruled 2026-06-30): FOLD-IN.** NO v0.19.0 cut on the acute waves alone — do the DESIGN-PASS BATCH first, then ONE v0.19.0 = acute W1-W6b + #4 + #6-grid + #2-TTL + #3-local-view. deployah stays on-call, no release GO yet. #17/eel-a is closed spt-core-side but perri loop + the release all ride the eventual combined cut. Next work = the #4+#6 co-design (doyle drives, WITH operator; HOLD ALL BUILD until design ratified + dispatched — worst-hazard attach/serve path). doyle grounding the attach/serve seam via Explore (agent doyle-w420) for the design framing.

**REMAINING (after W6b — ALL ACUTE WAVES DONE + GATED; design-pass batch then combined v0.19.0):**
1. ~~ADAPTER cluster #18/#1/#17 (W6 @9305d19) + #17 {project} close (W6b @b9e5ce9)~~ — ALL SHIPPED, doyle gating both shas. #17 spt-core-side DONE end-to-end; perri loop unblocked. {project} DESIGN-GATE was RULED (doyle 2026-06-30) + BUILT as W6b: W6's env-read seam shipped but eel-a NOT closed because [digest] pre-reads a single {source} path + spt-core (correctly) provides NO {project} slug key. RULING = CONTEXT-symmetry: give [digest] a `fetcher` strategy like [history] ALREADY has (history.rs:4-10; CONTEXT amended "[digest] mirrors history's two strategies — locate ownership"). fetcher-digest: ADAPTER extractor LOCATES+reads+emits normalized records (spt-core runs bounded, NO pre-read, NO {project}); spt-core supplies harness-neutral {session_id}+{cwd}+captured read-vars; extractor globs UNIQUE {session_id} under {CLAUDE_CONFIG_DIR}/projects/ (no slug — un-deads owlery::claude_projects_root). REVISES the keep-stdin-bytes clause (fetcher doesn't pre-read; W6 env-read still needed — feeds the fetcher root). Keep source+pre-read locate_normalize (additive) for trivial single-file harnesses. → W6b: mint REQ-DIGEST-FETCHER-STRATEGY impl+unit+int (mock fetcher-digest int, mirror history fetcher), docs MANIFEST.md+harness-contract. Build W6b BEFORE the design batch (completes acute #17/eel-a). PERRI LOOP after W6b: strategy=fetcher + extractor-becomes-locator (resolver exists, mostly wiring). GATE LESSON (W6): `cargo check --bin spt` does NOT compile cfg(test) → a bad test import passes check, fails nextest; AdapterKind is in spt_runtime::manifest NOT ::registry. Always nextest, never bare check.
2. DESIGN-PASS BATCH (fresh context, WITH doyle — NOW the next work; do NOT build solo, these are doyle-gated design):
   - SEAM MAP (Explore agent doyle-w420, 2026-06-30) REFRAMED the co-design: #4 and #6 are SEPARABLE, NOT one machinery. serve_attach (dispatch.rs:335/attach.rs:205) is ALREADY transport-agnostic (serves QUIC Attach streams = loopback); #4 gap is PURELY the client dial (rc.rs). Wire (AttachRecord::Output{seq,bytes}) unchanged between them; #4 client renders whatever bytes arrive, #6 only changes what the initial batch CONTAINS. Operator ruled SPLIT: #4 first, then #6 deep.
   - #4 REQ-RC-CROSS-NODE-ATTACH — DISPATCHED to todlando 2026-06-30 (design ratified, building). Contained CLIENT-side only, NO serve change. D1 resolve-hop: local resolve_session MISS → resolve_across_visible + seed_first_addr/net_dial (reuse W5a wansend.rs:73/142/172) + send ENDPOINT_ID on AttachRecord::Request → OWNING node resolves endpoint→session server-side (authoritative). D2 honest-failure via WanOutcome/classify_wan_reply (no false attach/hang). D3 hazards: bounded_block_on (NO unbounded like #16 nethost.rs:1060), send_effect_no_ack (INPUT-ACK-BACKPRESSURE), two-conn split (ENDPOINT-RUN-ATTACH-OUTPUT), origin from stream-table not payload (WAN-ORIGIN-AUTH). D4 viewport-only (CONTEXT L621-624). D5 gate on remote SESSION PRESENCE not online (REQ-ENDPOINT-UNBOUND-ATTACH L637-639). D6 stale row→clean not-reachable (GHOST-ROWS). Reuse rc pump chain rc.rs:1105-1168 + serve_attach unchanged. [twohost] int PR-title tag. Activate impl+unit+int.
   - #6 REQ-BROKER-SCREEN-GRID — NEXT deep design pass WITH operator (after #4). Large NET-NEW: NO VT parser/grid exists anywhere (no vte/vt100/termwiz; ratatui picker-only; spt-term = byte-stream + log-projection, PTY-byte parser RETIRED M9/ADR-0008). Net-new = authoritative server-side VT/screen model (grid+alt/main+cursor) parsing PTY bytes, synthesize clean repaint at become_controller/add_viewer (broker.rs:411-416) replacing raw-ring replay. KEY DESIGN TENSION: reintroduces PTY-byte interpretation ADR-0008 RETIRED — reconcile: that was a CONTENT parser (fragile harness-specific extraction, CONTEXT L494 "content surfaces read logs"); #6 is a RENDER grid (universal VT semantics, OS-neutral replay the byte-stream can't give, CONTEXT L483). Needs an ADR. Hazard-dense: viewer-isolation 7.7 / skip-to-live / controller-writer-reorder 7.21 / NO inline write under log lock 7.12 / wedge family 7.11/7.14/7.19. Slots behind SessionSurface/OutputStream (surface.rs, designed OS-neutral for this). operator NON-NEGOTIABLE zero PTY artifacts (fixes #6/#12/#7/#8). Own [twohost]? (attach-render, likely single-host int suffices — decide at design).
   - #2-SECONDARY Offline-TTL: DESIGN GATE = TTL anchors on a STICKY wall-clock offline_since that ghost-heal's FRESH-epoch re-advertise does NOT reset (epoch-keyed decay defeated by ghost-heal by construction) + sweep-site + REQ-HAZARD-REGISTRY-GHOST-ROWS test (evict_nodes registry.rs:280 = whole-node silence only). NOT acute (#2 PRIMARY shipped W2).
   - #12 REQ-RC-WIN-VT-OUTPUT — FOLDED IN + DISPATCHED to todlando 2026-06-30 (operator ruled into v0.19.0). Small cfg(windows) client-side fix, INDEPENDENT of #6/#4. ROOT (doyle code-grounded): rc.rs RawGuard::enable (749) calls ONLY crossterm enable_raw_mode = INPUT raw, NEVER enables ENABLE_VIRTUAL_TERMINAL_PROCESSING on the OUTPUT handle → Win10 CONHOST (enlyzeam/raw-pwsh) prints ANSI LITERALLY (raw ←[K) = #12 garble. Win11 Windows-Terminal VT-output always-on = unaffected (the divergence). `endpoint run --attach` works same-env because it enters the console via crossterm's VT-enabling picker/alt-screen setup BEFORE attach; plain rc skips it. Original "VT-out-enable Win10" theory was RIGHT, mis-scoped global — it's the RC-ATTACH path; --attach-works CONFIRMS not refutes. FIX: enable ENABLE_VIRTUAL_TERMINAL_PROCESSING on STD_OUTPUT_HANDLE in RawGuard::enable (cfg(windows), interactive-only mirror the windows_mouse_wanted guard 755-758 so piped-stdin e2e stays clean), restore on Drop. Confirm by diffing --attach's console-VT-enable that rc skips. Mint REQ impl+unit, verify Win10/conhost repro.
   - #6 VT-ENGINE DECISION RATIFIED (operator 2026-06-30): use the `vte` crate (alacritty's byte→action state machine, tiny/battle-tested) for byte→Perform-actions + CLEAN-ROOM a thin current-screen ScreenGrid{cells,cursor,alt_buffer} impl vte::Perform on top. SCOPE = CURRENT SCREEN only (viewport grid+cursor+alt/main) — scrollback stays the raw ring; grid drives ONLY the attach repaint. ADR-0008 RECONCILIATION (clean, small companion ADR): ADR-0008's split = "render surfaces read the PTY / content surfaces read logs"; it retired the PTY-byte parser ONLY for CONTENT (digest). #6 is a RENDER grid = the "render reads PTY" side ADR-0008 EXPLICITLY PRESERVES + it names #6's exact problem (alt-screen PTY = rendering protocol, repaints corrupt raw replay). #6 COMPLETES that side (render-read-PTY needs interpretation for a clean repaint); content-parser stays retired. NOT a reversal. #6 fixes: scrollback-corrupt (#6 = raw-ring-replay corruption, current-screen grid eliminates the garbage-dump), #7 residual artifacts, #8 resize artifacts; unlocks sticky-marker redo (#8/#14 marker-half, FOLLOW-UP re-enable after grid). Placement: broker-side, grid updated on drain/append path, become_controller/add_viewer (broker.rs:411-416/459) emit grid.render_repaint() instead of raw-ring initial-batch; live frames still stream raw post-attach. HAZARD constraints (map §4): NO inline write under log lock (7.12), viewer-isolation 7.7 + skip-to-live, controller-writer-reorder 7.21 (single-live-writer/ascending-seq), wedge family. doyle writing the #6 design doc next → operator ratify → dispatch.
   - PARKED FOLLOW-UP: #3 local-view truth (locally-controlled endpoint shows "free" on its OWN node) — touches confirm/takeover-flow, same-node collision rare. Mint REQ-GOSSIP-CONTROLLED-LOCAL-VIEW seed (required_stages=[]) only if picked up.

**GATE PROTOCOL (doyle lessons this session):** (1) gate compile in a FRESH CARGO_TARGET_DIR (scratchpad/wf-target), NOT the shared main target\ — incremental reuses stale objects → false green (caught a missing-field WIP break). (2) main target\debug\spt.exe is LOCKED by live infra (os error 5) — can't build there; use fresh target. (3) fresh-target E2E needs fixture bins (`build the dummy harness` panics cold) — that's env, not the diff; doyle gates clean-target-with-fixtures. Per wave: nextest (fresh) + clippy --workspace --all-targets (fresh) + traceable exit 0 + xtask gen if CLI surface (none so far — my edits are runtime output/enums, reference.md unchanged; watch CRLF-only drift → git checkout it).

**Backlog logged in triage doc:** persist daemon stderr to a rotating log (detached launch = no panic backtrace; #16 couldn't be split poison-vs-runtime from live evidence). Messaging to doyle = `$OWL send doyle` ($OWL=owl.exe plugin path); `spt send doyle` bounces NO_PERCH (doyle on legacy owl infra). See [[owl-send-not-legacy-spt-send]] [[v0170-published]].
