Skip to content

iter-101: daemon session correctness — re-watch, concurrent clients, type-aware buffer, --since parity#141

Merged
ractive merged 6 commits into
mainfrom
iter-101/daemon-session-correctness
Jul 9, 2026
Merged

iter-101: daemon session correctness — re-watch, concurrent clients, type-aware buffer, --since parity#141
ractive merged 6 commits into
mainfrom
iter-101/daemon-session-correctness

Conversation

@ractive

@ractive ractive commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

Closes the daemon session-correctness gaps from the 2026-07 deep review. Five themes, all with deterministic unit/e2e coverage:

  • A — Target-switch re-watch. handle_target_event now branches on is_top_level; a cross-process top-level switch purges the destroyed target's stale buffered resources via ResourceBuffer::purge_destroyed_target. Tab-scoped watchResources makes per-target re-watch implicit — documented (with what the watcher does/does not re-deliver) in kb/rdp/actors/watcher.md.
  • B — Concurrent-client safety. The single RPC-writer slot is now claimed lazily (try_claim_rpc_slot) only when a client forwards its first Firefox-bound message; a second concurrent client gets a structured daemon_busy frame and is not forwarded, so RDP responses (no correlation IDs) can never cross-deliver. Stream-only --follow clients never touch the slot. Daemon-local responses now route to the requesting client's own connection. recv_reply_from/recv_event_from surface a from:"daemon" control-error promptly instead of hanging. The unused DemuxReader/split_demux/Packet pub API and the _demux decoy that gamed check-dead-primitives are deleted.
  • C — Type-aware buffering. ResourceBuffer tracks per-type counts and evicts type-aware with a TYPE_RESERVED_FLOOR (500) so a network burst can't evict console/error entries below their floor. The floor is a soft reservation (a single type can still fill the buffer).
  • D — --since parity. One-shot network --since now fails loudly with AppError::Unsupported { error_type: "since_requires_daemon" } (exit 1) instead of a silent no-op; the refusal fires before any connection opens. --since -1 parses as space-separated via allow_hyphen_values.
  • E — Atomic registry + parity tests. Registry::register uses the DashMap::entry API so a concurrent invalidate_target can no longer revive a dead actor. Added e2e_error_shape_parity_daemon asserting byte-identical error_type/exit code (connection-refused) daemon vs --no-daemon.

Design decisions recorded in the plan: busy over queue for Theme B; delete the DemuxReader pub API for Theme B task 2.

Carry-over (live cross-process follow test + extended daemon error-parity scenarios) filed as kb/iterations/iteration-102-daemon-live-coverage.md.

Test plan

  • cargo fmt --check clean
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo test --workspace green (1551 tests, 0 failures)
  • check-iteration-ready 10/10 PASS (with FF_RDP_LIVE_TESTS=1)
  • unit_registry_register_atomic_no_revive — hammer register/invalidate from two threads + observer asserting monotonic-death
  • unit_buffer_eviction_per_type — 50 console msgs survive a 10× MAX_EVENTS network flood
  • unit_concurrent_clients_no_cross_delivery + recv_reply_from_surfaces_daemon_busy_control_error
  • top_level_switch_purges_buffer — purge only on a genuine cross-process switch
  • e2e_network_since_no_daemon_explicit — exits 1 with since_requires_daemon, no results payload
  • e2e_error_shape_parity_daemon — identical error shape daemon vs --no-daemon
  • deferred to iter-102: live_daemon_follow_survives_cross_process_nav (Firefox-only)

🤖 Generated with Claude Code## Claims vs code
<generated 2026-07-09T11:42:40Z by ralph-loop>

  • ResourceBuffer::purge_destroyed_target → ✅ matched in diff
  • AppError::Unsupported → ✅ matched in diff
  • Registry::register → ✅ matched in diff
  • TYPE_RESERVED_FLOOR → ✅ matched in diff
  • target-switch → ✅ matched in diff

ractive and others added 6 commits July 9, 2026 13:39
Theme A — target-switch re-watch: branch on is_top_level in
handle_target_event; on a cross-process top-level switch, purge the
destroyed target's stale buffered resources via
ResourceBuffer::purge_destroyed_target. Tab-scoped watchResources makes
per-target re-watch implicit (documented in kb/rdp/actors/watcher.md).

Theme B — concurrent-client safety: replace eager RPC-writer-slot claim
with lazy claiming (try_claim_rpc_slot); a second concurrent client that
tries to forward gets a structured daemon_busy frame and is not
forwarded, so RDP responses never cross-deliver. Route daemon-local
responses to the requesting client's own connection. recv_reply_from /
recv_event_from surface a daemon control-error frame promptly. Delete the
unused DemuxReader / split_demux / Packet pub API and the _demux decoy.

Theme C — type-aware buffering: ResourceBuffer tracks per-type counts and
evicts type-aware with a TYPE_RESERVED_FLOOR so a network burst cannot
evict console/error entries below their floor.

Theme D — --since parity: one-shot network --since fails with
AppError::Unsupported { error_type: "since_requires_daemon" } instead of
a silent no-op; --since -1 parses via allow_hyphen_values.

Theme E — atomic registry + parity tests: Registry::register uses the
DashMap entry API so a concurrent invalidate_target cannot revive a dead
actor; add e2e error-shape/exit-code parity (connection-refused) daemon
vs --no-daemon.

Tests: unit_registry_register_atomic_no_revive, unit_buffer_eviction_per_type,
unit_concurrent_clients_no_cross_delivery, top_level_switch_purges_buffer,
recv_reply_from_surfaces_daemon_busy_control_error,
e2e_network_since_no_daemon_explicit, e2e_error_shape_parity_daemon.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…idelity

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
iter-101 deleted the dead DemuxReader/split_demux/Packet pub API (425
lines) from transport.rs, shifting the two production .expect()/request()
line citations in iter-102's plan (450-453 -> 433-436, 480 -> 463).
Corrected against the current file so check-firefox-refs style line
citations stay accurate when iter-102 starts.
… iter-101 review

test (windows-latest) fails 5 tests (install_skill::* x4, nav_action::
reload_wait_idle_no_traffic_returns_idle_quickly) on PR #141, but neither
failing file is touched by iter-101's diff and the identical failures
reproduce on prior unrelated branches (iter-100, iter-100b CI runs before
iter-101 existed) — confirmed pre-existing, not a regression. Tracked as
its own plan per the iteration-99 precedent rather than blocking this PR.
…-sweep)

main gained kb/iterations/iteration-107-post-105-live-sweep.md (a different,
unrelated plan) between when this branch was cut and now. Renumber the
Windows-CI-preexisting-reds plan filed in the previous commit to the next
free slot to avoid a duplicate iteration-107.
@ractive ractive merged commit 5ce31ef into main Jul 9, 2026
8 of 10 checks passed
@ractive ractive deleted the iter-101/daemon-session-correctness branch July 9, 2026 12:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant