Watchdog live capture targets stale processes and emits excluded-app freeze events #45

Closed
opened 2026-10-14 18:16:53 +08:00 by Breadway · 4 comments
Owner

Found by independent exact-head review of Breadway/bread PR24 at c4e0277713. Confidence high: real daemon, real eu-stack and CLI probes reproduce. Repair on existing PR24, preserve feature/breadbug-watchdog for stacked PR25.

  1. bread-bug/src/livestack.rs:22-24 trusts mutable /proc/PID/comm prefix, not app executable or process start identity. An unrelated /usr/bin/python3.14 with comm=bread-unrelated is captured for a pad freeze marker. Record process identity/start time and bind capture to the recorded app and instance, revalidate after consent and before attaching.
  2. breadbug/src/lib.rs:217-228 skips Recovered markers. Seed freeze then recovered for that PID; --app pad --type freeze --live-stack --dry-run still captures it. Latest recovery/stop/death must invalidate live capture.
  3. livestack.rs:40-58 parses eu-stack default output as absolute PC under offset and sets module/build_id=None. Real capture reports offset=139859864734315 with no module/build-id. Resolve to module-relative offset plus build-id, never substitute virtual addresses or function-relative displacements. Add real-tool fixtures, stripped binary coverage and explicit no-memory payload assertions.
  4. breadd/src/bugstore.rs:507-528,574-577 ignores exclusions and record_marker(false/error) before publishing. With excluded_apps=[clip], heartbeat silence emits bread.bug.freeze while no marker file exists. Honor opt-out before tracking and suppress excluded-app freeze/recovery publication; define failure behavior.
  5. breadd/src/watchdog.rs:147-153 skips liveness once frozen. Pure immutable-source probe freezes PID42, alive=false produces no Gone, then heartbeat PID43 returns recovered=true. Retire dead frozen instances and do not attribute recovery/stop from another instance.

Evidence: /tmp/opencode/watchdog-review/{repro.py,repro.log,stale-capture-report.json,lifecycle-probe.rs,lifecycle.log}. Existing 51 bread-bug,29 scrub,13 CLI,11 watchdog,3 journal tests and2 daemon IPC cases pass but miss these behaviors. Full BREAD-18 blocked-main-loop/notification and actual suspend/resume acceptance stays open.

Found by independent exact-head review of Breadway/bread PR24 at c4e02777139a3cd28cdc999c325e6c0756769f58. Confidence high: real daemon, real eu-stack and CLI probes reproduce. Repair on existing PR24, preserve feature/breadbug-watchdog for stacked PR25. 1. bread-bug/src/livestack.rs:22-24 trusts mutable /proc/PID/comm prefix, not app executable or process start identity. An unrelated /usr/bin/python3.14 with comm=bread-unrelated is captured for a pad freeze marker. Record process identity/start time and bind capture to the recorded app and instance, revalidate after consent and before attaching. 2. breadbug/src/lib.rs:217-228 skips Recovered markers. Seed freeze then recovered for that PID; --app pad --type freeze --live-stack --dry-run still captures it. Latest recovery/stop/death must invalidate live capture. 3. livestack.rs:40-58 parses eu-stack default output as absolute PC under offset and sets module/build_id=None. Real capture reports offset=139859864734315 with no module/build-id. Resolve to module-relative offset plus build-id, never substitute virtual addresses or function-relative displacements. Add real-tool fixtures, stripped binary coverage and explicit no-memory payload assertions. 4. breadd/src/bugstore.rs:507-528,574-577 ignores exclusions and record_marker(false/error) before publishing. With excluded_apps=[clip], heartbeat silence emits bread.bug.freeze while no marker file exists. Honor opt-out before tracking and suppress excluded-app freeze/recovery publication; define failure behavior. 5. breadd/src/watchdog.rs:147-153 skips liveness once frozen. Pure immutable-source probe freezes PID42, alive=false produces no Gone, then heartbeat PID43 returns recovered=true. Retire dead frozen instances and do not attribute recovery/stop from another instance. Evidence: /tmp/opencode/watchdog-review/{repro.py,repro.log,stale-capture-report.json,lifecycle-probe.rs,lifecycle.log}. Existing 51 bread-bug,29 scrub,13 CLI,11 watchdog,3 journal tests and2 daemon IPC cases pass but miss these behaviors. Full BREAD-18 blocked-main-loop/notification and actual suspend/resume acceptance stays open.
Author
Owner

Full exact-head changes-requested review with additional identity-redaction and unavailable-store grace requests persisted: #24 (comment) . BREAD-18 moved todo, confirmed; original full integration criteria preserved. Repair existing PR24, no duplicate PR. Keep feature/breadbug-watchdog for PR25. CI run146 check job2479 and all steps succeeded at c4e0277713; bugs reproduced despite green suites. No merge or done.

Full exact-head changes-requested review with additional identity-redaction and unavailable-store grace requests persisted: https://git.breadway.dev/Breadway/bread/pulls/24#issuecomment-4885 . BREAD-18 moved todo, confirmed; original full integration criteria preserved. Repair existing PR24, no duplicate PR. Keep feature/breadbug-watchdog for PR25. CI run146 check job2479 and all steps succeeded at c4e02777139a3cd28cdc999c325e6c0756769f58; bugs reproduced despite green suites. No merge or done.
Author
Owner

All five original items plus the identity-scrub and store-independent grace requests verified repaired at bbb12d6 (details: #24 (comment)). Not closed: exact-head CI is red pending a clean rerun, and the PR is unmerged. Note for follow-up: system.os_id exemption uses exact equality, so a composite value containing a configured identity survives in that single field (low risk, value comes from /etc/os-release).

All five original items plus the identity-scrub and store-independent grace requests verified repaired at bbb12d6 (details: https://git.breadway.dev/Breadway/bread/pulls/24#issuecomment-5169). Not closed: exact-head CI is red pending a clean rerun, and the PR is unmerged. Note for follow-up: system.os_id exemption uses exact equality, so a composite value containing a configured identity survives in that single field (low risk, value comes from /etc/os-release).
Author
Owner

Independent verification on merged main: PASS, no merge action (already merged)

Verified SHA: 90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036 (current origin/main tip, the PR24 merge; repair commits d2a588b, 875cb2c, 1317554). There is no PR left to merge: PR24 is already merged, so this verifies the content that is actually on main. Scratch worktree ~/.worktrees/bread/verify-bread46-90cd7b4 detached at 90cd7b4, clean tree, primary checkout untouched. Breadboard card BREAD-46 stays in review for coordinator verification, not done.

Locked workspace checks on the exact SHA

  • cargo fmt --check: pass
  • cargo clippy --workspace --all-targets --locked -- -D warnings: pass, 0 warnings
  • cargo test --workspace --locked: 594 passed, 0 failed, 21 suites. The four livestack_real tests ran against the real eu-stack 0.196 with real child processes (real_capture_is_module_relative_with_the_elf_build_id, real_stripped_binary_has_module_offset_and_build_id_but_no_symbol, real_unstripped_binary_names_its_functions_with_the_same_offsets, real_capture_refuses_an_unrelated_process_that_borrows_a_bread_name), zero "skipped" lines.
  • CI: the check workflow only triggers on feature/fix pushes, so main has no check run to read; the exact-SHA dev-release build (push) run 167 on 90cd7b4 is success.

Evidence obtained outside the test suite

  1. Real eu-stack -b -m -r -n 128 -p PID (elfutils 0.196) against a compiled PIE, the same binary after strip, and a --build-id=none build. Output form matches the committed fixtures exactly: [build-id]@0xBASE+0xOFFSET detail lines, and no detail line at all for the no-build-id module. Recorded under /tmp/opencode/bread46-probe.
  2. Real CLI end to end with the built breadbug binary, a hand-seeded store in scratch XDG dirs and a real breadpad-named target:
    • correct recorded identity: 8 frames, each with module + offset + build_id; app frames 4212, 4480, 4491, 4541, all below the 16080 byte module size; no 0x anywhere in the payload; no memory, register or environment text.
    • same pid, start ticks off by one: "pid ... now belongs to a different process than the one that froze", 0 frames, eu-stack not run.
    • a different executable recorded under app pad: "process ... does not run the pad executable", 0 frames.
    • freeze then recovered, and freeze then stopped: "the app's latest marker is not a freeze", 0 frames.
    • freeze then target killed: "process ... has exited", 0 frames.
    • offsets cross-checked against readelf/nm: breadpad park st_value 0x1149 vs frame offset 0x1180; libc clock_nanosleep 0xe4a20 vs frame 0xe4a6b, __nanosleep 0xf0ed0 vs frame 0xf0ee6. Module-relative values, not virtual addresses and not function displacements.

Per finding

  1. PASS. bread-bug/src/procid.rs:43,61,84,94,123 records and compares kernel start ticks, /proc/PID/exe and boot id; bread-bug/src/livestack.rs:74,78,126,130 verifies the instance before running eu-stack, again after it returns, and exe_matches_app ties the recorded executable to the app, so comm is never consulted. breadd/src/bugstore.rs:820 rejects a heartbeat whose pid executable does not match the app, and ipc_marker clears client-supplied pid/process, so a recorded identity is always daemon-observed. Tests: livestack::tests::comm_prefix_is_not_an_identity, same_app_with_a_reused_pid_is_refused, another_apps_executable_is_refused, the_tool_is_not_run_when_the_target_does_not_match (call counter proves eu-stack never ran), a_swap_between_the_check_and_the_result_discards_the_frames; daemon heartbeat_from_an_unrelated_executable_is_rejected; real tools real_capture_refuses_an_unrelated_process_that_borrows_a_bread_name. The old code at c4e0277 (livestack.rs:20-24) was a /proc/PID/comm prefix test only, so these tests cannot pass against it.
  2. PASS. breadbug/src/lib.rs:219 takes the single latest marker and requires it to be Freeze, refusing with NoActiveFreeze otherwise; Stopped is a new store-only kind written when a frozen instance ends or is replaced (bread-bug/src/store.rs:67-80, breadd/src/bugstore.rs:796). Capture is not invoked for recovery, stop or death (asserted with a call counter, not just absence from output). Tests: a_recovered_stopped_or_later_marker_invalidates_the_freeze (recovered, stopped, panic, crash), interactive_prompt_is_not_offered_after_recovery, a_new_freeze_after_recovery_is_capturable_again, plus the CLI probe above. Old code .rev().find(|m| m.kind != MarkerKind::Recovered) walked back past a recovery, which is the reported defect.
  3. PASS. bread-bug/src/livestack.rs:104 pins the argument list to -b -m -r -n 128 -p; parse_eu_stack and parse_module_line (171, 264) build a frame from the module line plus the [build-id]@base+offset detail line, cross-check the absolute pc against base+offset (accepting the return-address minus one), and emit a frame only when module, module-relative offset and build-id are all present. Absolute pcs and symbol + 0xdisp displacements are never stored (no_frame_carries_a_virtual_address, default_output_without_module_info_yields_no_offset, a_detail_line_that_does_not_match_its_frame_is_ignored, malformed_build_ids_and_modules_are_rejected). Stripped coverage: fixture eu-stack-pie-stripped.txt plus real real_stripped_binary_has_module_offset_and_build_id_but_no_symbol. No memory payload: memory_registers_and_environment_never_reach_the_payload and the exhaustive four-field destructor test a_frame_has_exactly_four_fields_and_nothing_else_can_be_added_silently; breadbug/src/report/tests.rs:73 keeps a username-bearing module path scrubbed out of the report while the build-id survives, so frames do not widen the privacy surface.
  4. PASS. Exclusions are honored before tracking (breadd/src/watchdog.rs:170, 217, breadd/src/bugstore.rs:820) and again before record/publish (breadd/src/bugstore.rs:770-793), so an excluded app produces no marker file and no bread.bug.freeze or bread.bug.recovered. Tests: watchdog::tests::excluded_apps_are_never_tracked, bugstore::watchdog_store_tests::excluded_apps_never_publish_watchdog_markers, recovered_marker_is_also_suppressed_for_an_excluded_app, real daemon configured_excluded_app_never_tracks_or_publishes_a_freeze (asserts no apps/clip directory and no bus event). Storage-failure behavior is defined and observable: an unstorable freeze is still published with "stored": false and a warning (an_unstorable_freeze_is_published_with_stored_false_and_warned, store_errors_publish_freeze_with_stored_false, missing_store_still_publishes_freeze_with_stored_false, positive a_stored_freeze_says_stored_true), and breadbug never offers a live stack without a stored marker. Reading note: the issue text's "(no bread.bug.freeze when no marker exists)" holds in the excluded case it describes, and the review's "define observable storage-failure behavior instead of pretending a stored marker exists" holds through the stored: false flag. The two differ for a genuine disk failure by design, documented in code and accepted in review comment 5169; flagged so the coordinator can say if total silence was intended instead.
  5. PASS. breadd/src/watchdog.rs:262 probes every silent instance and every frozen one, so a dead or replaced frozen instance emits Gone with was_frozen: true and is dropped from tracking; is_same (50) requires pid plus start ticks/exe, so a heartbeat (170) or a stop (217) from another instance retires the old instance instead of recovering it or untracking the current one. The retired frozen instance gets a store-only stopped marker (breadd/src/bugstore.rs:796), which makes breadbug refuse a live capture. Tests: frozen_instance_that_dies_is_retired_with_a_gone_event, new_instance_heartbeat_before_the_tick_retires_the_old_one_without_recovery, reused_pid_with_a_new_start_time_is_a_new_instance, a_replaced_pid_is_retired_by_the_tick, an_old_instance_cannot_stop_a_new_one, stop_and_replacement_retire_only_the_old_frozen_instance, real daemon frozen_dead_process_is_retired_and_next_instance_is_not_recovery; the CLI probe confirms a stopped marker makes the freeze uncapturable. Old code if silent <= limit || beat.frozen { continue; } (c4e0277 watchdog.rs:147-153) never re-probed a frozen app and attributed any later heartbeat to recovery.

Residual notes, not blockers for these five findings

  • Composite system.os_id exemption uses exact equality (carried over from the earlier review, low risk, the value comes from /etc/os-release).
  • exe_matches_app is a name-based identity (bread<app> / bread<app>d), a reuse and attribution guard for privacy and consent, not an anti-spoofing control. Out of scope here.
  • For a genuine storage failure the freeze is still published with stored: false (finding 4), so a consumer that ignores the flag sees an event with no stored marker. breadbug itself honors it.
  • Open and unverified on BREAD-18, explicitly out of scope: real blocked GUI main loop producing a marker and notification, actual suspend/resume and monitor hotplug acceptance, provider heartbeat adoption and rollout, and the BOS-86 notification/action.

No merge, tag, branch deletion or done transition. feature/breadbug-watchdog is untouched. Card BREAD-46 left in review for coordinator verification.

## Independent verification on merged main: PASS, no merge action (already merged) Verified SHA: `90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036` (current `origin/main` tip, the PR24 merge; repair commits `d2a588b`, `875cb2c`, `1317554`). There is no PR left to merge: PR24 is already merged, so this verifies the content that is actually on main. Scratch worktree `~/.worktrees/bread/verify-bread46-90cd7b4` detached at 90cd7b4, clean tree, primary checkout untouched. Breadboard card BREAD-46 stays in review for coordinator verification, not done. ### Locked workspace checks on the exact SHA - `cargo fmt --check`: pass - `cargo clippy --workspace --all-targets --locked -- -D warnings`: pass, 0 warnings - `cargo test --workspace --locked`: 594 passed, 0 failed, 21 suites. The four `livestack_real` tests ran against the real eu-stack 0.196 with real child processes (`real_capture_is_module_relative_with_the_elf_build_id`, `real_stripped_binary_has_module_offset_and_build_id_but_no_symbol`, `real_unstripped_binary_names_its_functions_with_the_same_offsets`, `real_capture_refuses_an_unrelated_process_that_borrows_a_bread_name`), zero "skipped" lines. - CI: the `check` workflow only triggers on feature/fix pushes, so main has no check run to read; the exact-SHA dev-release `build (push)` run 167 on 90cd7b4 is success. ### Evidence obtained outside the test suite 1. Real `eu-stack -b -m -r -n 128 -p PID` (elfutils 0.196) against a compiled PIE, the same binary after `strip`, and a `--build-id=none` build. Output form matches the committed fixtures exactly: `[build-id]@0xBASE+0xOFFSET` detail lines, and no detail line at all for the no-build-id module. Recorded under /tmp/opencode/bread46-probe. 2. Real CLI end to end with the built `breadbug` binary, a hand-seeded store in scratch XDG dirs and a real `breadpad`-named target: - correct recorded identity: 8 frames, each with module + offset + build_id; app frames 4212, 4480, 4491, 4541, all below the 16080 byte module size; no `0x` anywhere in the payload; no memory, register or environment text. - same pid, start ticks off by one: "pid ... now belongs to a different process than the one that froze", 0 frames, eu-stack not run. - a different executable recorded under app pad: "process ... does not run the pad executable", 0 frames. - freeze then `recovered`, and freeze then `stopped`: "the app's latest marker is not a freeze", 0 frames. - freeze then target killed: "process ... has exited", 0 frames. - offsets cross-checked against `readelf`/`nm`: breadpad `park` st_value 0x1149 vs frame offset 0x1180; libc `clock_nanosleep` 0xe4a20 vs frame 0xe4a6b, `__nanosleep` 0xf0ed0 vs frame 0xf0ee6. Module-relative values, not virtual addresses and not function displacements. ### Per finding 1. PASS. `bread-bug/src/procid.rs:43,61,84,94,123` records and compares kernel start ticks, `/proc/PID/exe` and boot id; `bread-bug/src/livestack.rs:74,78,126,130` verifies the instance before running eu-stack, again after it returns, and `exe_matches_app` ties the recorded executable to the app, so `comm` is never consulted. `breadd/src/bugstore.rs:820` rejects a heartbeat whose pid executable does not match the app, and `ipc_marker` clears client-supplied pid/process, so a recorded identity is always daemon-observed. Tests: `livestack::tests::comm_prefix_is_not_an_identity`, `same_app_with_a_reused_pid_is_refused`, `another_apps_executable_is_refused`, `the_tool_is_not_run_when_the_target_does_not_match` (call counter proves eu-stack never ran), `a_swap_between_the_check_and_the_result_discards_the_frames`; daemon `heartbeat_from_an_unrelated_executable_is_rejected`; real tools `real_capture_refuses_an_unrelated_process_that_borrows_a_bread_name`. The old code at c4e0277 (livestack.rs:20-24) was a `/proc/PID/comm` prefix test only, so these tests cannot pass against it. 2. PASS. `breadbug/src/lib.rs:219` takes the single latest marker and requires it to be `Freeze`, refusing with `NoActiveFreeze` otherwise; `Stopped` is a new store-only kind written when a frozen instance ends or is replaced (`bread-bug/src/store.rs:67-80`, `breadd/src/bugstore.rs:796`). Capture is not invoked for recovery, stop or death (asserted with a call counter, not just absence from output). Tests: `a_recovered_stopped_or_later_marker_invalidates_the_freeze` (recovered, stopped, panic, crash), `interactive_prompt_is_not_offered_after_recovery`, `a_new_freeze_after_recovery_is_capturable_again`, plus the CLI probe above. Old code `.rev().find(|m| m.kind != MarkerKind::Recovered)` walked back past a recovery, which is the reported defect. 3. PASS. `bread-bug/src/livestack.rs:104` pins the argument list to `-b -m -r -n 128 -p`; `parse_eu_stack` and `parse_module_line` (171, 264) build a frame from the module line plus the `[build-id]@base+offset` detail line, cross-check the absolute pc against base+offset (accepting the return-address minus one), and emit a frame only when module, module-relative offset and build-id are all present. Absolute pcs and `symbol + 0xdisp` displacements are never stored (`no_frame_carries_a_virtual_address`, `default_output_without_module_info_yields_no_offset`, `a_detail_line_that_does_not_match_its_frame_is_ignored`, `malformed_build_ids_and_modules_are_rejected`). Stripped coverage: fixture `eu-stack-pie-stripped.txt` plus real `real_stripped_binary_has_module_offset_and_build_id_but_no_symbol`. No memory payload: `memory_registers_and_environment_never_reach_the_payload` and the exhaustive four-field destructor test `a_frame_has_exactly_four_fields_and_nothing_else_can_be_added_silently`; `breadbug/src/report/tests.rs:73` keeps a username-bearing module path scrubbed out of the report while the build-id survives, so frames do not widen the privacy surface. 4. PASS. Exclusions are honored before tracking (`breadd/src/watchdog.rs:170`, `217`, `breadd/src/bugstore.rs:820`) and again before record/publish (`breadd/src/bugstore.rs:770-793`), so an excluded app produces no marker file and no `bread.bug.freeze` or `bread.bug.recovered`. Tests: `watchdog::tests::excluded_apps_are_never_tracked`, `bugstore::watchdog_store_tests::excluded_apps_never_publish_watchdog_markers`, `recovered_marker_is_also_suppressed_for_an_excluded_app`, real daemon `configured_excluded_app_never_tracks_or_publishes_a_freeze` (asserts no `apps/clip` directory and no bus event). Storage-failure behavior is defined and observable: an unstorable freeze is still published with `"stored": false` and a warning (`an_unstorable_freeze_is_published_with_stored_false_and_warned`, `store_errors_publish_freeze_with_stored_false`, `missing_store_still_publishes_freeze_with_stored_false`, positive `a_stored_freeze_says_stored_true`), and `breadbug` never offers a live stack without a stored marker. Reading note: the issue text's "(no bread.bug.freeze when no marker exists)" holds in the excluded case it describes, and the review's "define observable storage-failure behavior instead of pretending a stored marker exists" holds through the `stored: false` flag. The two differ for a genuine disk failure by design, documented in code and accepted in review comment 5169; flagged so the coordinator can say if total silence was intended instead. 5. PASS. `breadd/src/watchdog.rs:262` probes every silent instance and every frozen one, so a dead or replaced frozen instance emits `Gone` with `was_frozen: true` and is dropped from tracking; `is_same` (50) requires pid plus start ticks/exe, so a heartbeat (170) or a stop (217) from another instance retires the old instance instead of recovering it or untracking the current one. The retired frozen instance gets a store-only `stopped` marker (`breadd/src/bugstore.rs:796`), which makes `breadbug` refuse a live capture. Tests: `frozen_instance_that_dies_is_retired_with_a_gone_event`, `new_instance_heartbeat_before_the_tick_retires_the_old_one_without_recovery`, `reused_pid_with_a_new_start_time_is_a_new_instance`, `a_replaced_pid_is_retired_by_the_tick`, `an_old_instance_cannot_stop_a_new_one`, `stop_and_replacement_retire_only_the_old_frozen_instance`, real daemon `frozen_dead_process_is_retired_and_next_instance_is_not_recovery`; the CLI probe confirms a `stopped` marker makes the freeze uncapturable. Old code `if silent <= limit || beat.frozen { continue; }` (c4e0277 watchdog.rs:147-153) never re-probed a frozen app and attributed any later heartbeat to recovery. ### Residual notes, not blockers for these five findings - Composite `system.os_id` exemption uses exact equality (carried over from the earlier review, low risk, the value comes from /etc/os-release). - `exe_matches_app` is a name-based identity (`bread<app>` / `bread<app>d`), a reuse and attribution guard for privacy and consent, not an anti-spoofing control. Out of scope here. - For a genuine storage failure the freeze is still published with `stored: false` (finding 4), so a consumer that ignores the flag sees an event with no stored marker. `breadbug` itself honors it. - Open and unverified on BREAD-18, explicitly out of scope: real blocked GUI main loop producing a marker and notification, actual suspend/resume and monitor hotplug acceptance, provider heartbeat adoption and rollout, and the BOS-86 notification/action. No merge, tag, branch deletion or done transition. `feature/breadbug-watchdog` is untouched. Card BREAD-46 left in review for coordinator verification.
Author
Owner

Independent post-merge verification on current origin/main: PASS for all five findings. Full evidence: #45 (comment)

Verified SHA 90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036 (origin/main tip, PR24 merge; repairs d2a588b, 875cb2c, 1317554). Scratch worktree ~/.worktrees/bread/verify-bread46-90cd7b4, clean tree, primary checkout untouched. No PR to merge: PR24 is already merged, so this verifies merged main.

Checks on that SHA: cargo fmt --check pass; cargo clippy --workspace --all-targets --locked -- -D warnings pass with 0 warnings; cargo test --workspace --locked 594 passed / 0 failed over 21 suites, with all four real eu-stack tests executed and nothing skipped. Independent evidence beyond the suites: real eu-stack 0.196 output matches the committed fixtures; real breadbug CLI against a hand-seeded store refuses a wrong start time, a wrong executable, a recovered or stopped marker and a killed target with 0 frames each, and returns 8 module-relative frames with build ids (offsets cross-checked against readelf/nm) for a matching instance.

Findings: 1 PASS (identity + revalidation, comm never consulted), 2 PASS (latest-marker rule, capture not invoked), 3 PASS (module-relative offset + build-id, no addresses, stripped fixture, no memory payload), 4 PASS (exclusions before tracking and publishing, stored:false on storage failure), 5 PASS (dead frozen instance retired with Gone, cross-instance recovery/stop not attributed).

Not blockers, recorded: the os_id composite-equality note from review comment 5169; exe_matches_app is name-based; a genuine storage failure still publishes bread.bug.freeze with stored:false by design; and BREAD-18's blocked-main-loop, suspend/resume, provider rollout and BOS-86 gates stay open and out of scope.

{"key":"BREAD-46","repo":"Breadway/bread","pr":"https://git.breadway.dev/Breadway/bread/pulls/24","pr_number":24,"head_sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","verdict":"pass","criteria":[{"criterion":"Live-stack capture binds to recorded process identity and start time and revalidates; a bread-like comm on an unrelated process is not captured","result":"pass","evidence":"bread-bug/src/procid.rs:43,61,84,94,123; bread-bug/src/livestack.rs:74,78,126,130; tests comm_prefix_is_not_an_identity, the_tool_is_not_run_when_the_target_does_not_match, a_swap_between_the_check_and_the_result_discards_the_frames, real_capture_refuses_an_unrelated_process_that_borrows_a_bread_name; CLI probe refused a wrong start time and a wrong executable with 0 frames"},{"criterion":"Latest recovery, stop or death invalidates live capture","result":"pass","evidence":"breadbug/src/lib.rs:219; bread-bug/src/store.rs:67-80; test a_recovered_stopped_or_later_marker_invalidates_the_freeze (recovered, stopped, panic, crash) with call counter; CLI probe gave no frames for recovered, stopped and killed"},{"criterion":"eu-stack output resolves to module-relative offset plus build-id, never virtual addresses; stripped fixture and no-memory assertion","result":"pass","evidence":"bread-bug/src/livestack.rs:104,171,264; tests frames_are_module_relative_with_build_id, stripped_binary_keeps_module_and_offset_but_no_symbol, memory_registers_and_environment_never_reach_the_payload, a_frame_has_exactly_four_fields; livestack_real 4/4 executed against eu-stack 0.196; CLI payload had no 0x and offsets below module size, cross-checked with readelf/nm"},{"criterion":"Exclusions honored before tracking; excluded-app freeze/recovery publication suppressed; record failure behavior defined","result":"pass","evidence":"breadd/src/watchdog.rs:170,217; breadd/src/bugstore.rs:770-793,820; tests excluded_apps_are_never_tracked, excluded_apps_never_publish_watchdog_markers, recovered_marker_is_also_suppressed_for_an_excluded_app, configured_excluded_app_never_tracks_or_publishes_a_freeze; storage failure publishes with stored false plus warning (three tests plus a positive)"},{"criterion":"Dead frozen instances retired; recovery/stop not attributed across instances","result":"pass","evidence":"breadd/src/watchdog.rs:50,170,217,262; tests frozen_instance_that_dies_is_retired_with_a_gone_event, new_instance_heartbeat_before_the_tick_retires_the_old_one_without_recovery, an_old_instance_cannot_stop_a_new_one, stop_and_replacement_retire_only_the_old_frozen_instance, real daemon frozen_dead_process_is_retired_and_next_instance_is_not_recovery"}],"required_checks":[{"name":"cargo fmt --check","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"success"},{"name":"cargo clippy --workspace --all-targets --locked -- -D warnings","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"success"},{"name":"cargo test --workspace --locked","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"success"},{"name":"dev release build (push) run 167","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"success"},{"name":"check / check (push)","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"missing","note":"workflow triggers only on feature and fix branches, so main has no equivalent run"}],"review_comment":"https://git.breadway.dev/Breadway/bread/issues/45#issuecomment-5431","blockers":[],"next_action":"Coordinator verifies the merged behavior on main and records completion; leave feature/breadbug-watchdog for PR25; keep the BREAD-18 gates open","merged":true,"merge_sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036"}
Independent post-merge verification on current origin/main: PASS for all five findings. Full evidence: https://git.breadway.dev/Breadway/bread/issues/45#issuecomment-5431 Verified SHA `90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036` (origin/main tip, PR24 merge; repairs d2a588b, 875cb2c, 1317554). Scratch worktree ~/.worktrees/bread/verify-bread46-90cd7b4, clean tree, primary checkout untouched. No PR to merge: PR24 is already merged, so this verifies merged main. Checks on that SHA: cargo fmt --check pass; cargo clippy --workspace --all-targets --locked -- -D warnings pass with 0 warnings; cargo test --workspace --locked 594 passed / 0 failed over 21 suites, with all four real eu-stack tests executed and nothing skipped. Independent evidence beyond the suites: real eu-stack 0.196 output matches the committed fixtures; real breadbug CLI against a hand-seeded store refuses a wrong start time, a wrong executable, a recovered or stopped marker and a killed target with 0 frames each, and returns 8 module-relative frames with build ids (offsets cross-checked against readelf/nm) for a matching instance. Findings: 1 PASS (identity + revalidation, comm never consulted), 2 PASS (latest-marker rule, capture not invoked), 3 PASS (module-relative offset + build-id, no addresses, stripped fixture, no memory payload), 4 PASS (exclusions before tracking and publishing, stored:false on storage failure), 5 PASS (dead frozen instance retired with Gone, cross-instance recovery/stop not attributed). Not blockers, recorded: the os_id composite-equality note from review comment 5169; exe_matches_app is name-based; a genuine storage failure still publishes bread.bug.freeze with stored:false by design; and BREAD-18's blocked-main-loop, suspend/resume, provider rollout and BOS-86 gates stay open and out of scope. ```json {"key":"BREAD-46","repo":"Breadway/bread","pr":"https://git.breadway.dev/Breadway/bread/pulls/24","pr_number":24,"head_sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","verdict":"pass","criteria":[{"criterion":"Live-stack capture binds to recorded process identity and start time and revalidates; a bread-like comm on an unrelated process is not captured","result":"pass","evidence":"bread-bug/src/procid.rs:43,61,84,94,123; bread-bug/src/livestack.rs:74,78,126,130; tests comm_prefix_is_not_an_identity, the_tool_is_not_run_when_the_target_does_not_match, a_swap_between_the_check_and_the_result_discards_the_frames, real_capture_refuses_an_unrelated_process_that_borrows_a_bread_name; CLI probe refused a wrong start time and a wrong executable with 0 frames"},{"criterion":"Latest recovery, stop or death invalidates live capture","result":"pass","evidence":"breadbug/src/lib.rs:219; bread-bug/src/store.rs:67-80; test a_recovered_stopped_or_later_marker_invalidates_the_freeze (recovered, stopped, panic, crash) with call counter; CLI probe gave no frames for recovered, stopped and killed"},{"criterion":"eu-stack output resolves to module-relative offset plus build-id, never virtual addresses; stripped fixture and no-memory assertion","result":"pass","evidence":"bread-bug/src/livestack.rs:104,171,264; tests frames_are_module_relative_with_build_id, stripped_binary_keeps_module_and_offset_but_no_symbol, memory_registers_and_environment_never_reach_the_payload, a_frame_has_exactly_four_fields; livestack_real 4/4 executed against eu-stack 0.196; CLI payload had no 0x and offsets below module size, cross-checked with readelf/nm"},{"criterion":"Exclusions honored before tracking; excluded-app freeze/recovery publication suppressed; record failure behavior defined","result":"pass","evidence":"breadd/src/watchdog.rs:170,217; breadd/src/bugstore.rs:770-793,820; tests excluded_apps_are_never_tracked, excluded_apps_never_publish_watchdog_markers, recovered_marker_is_also_suppressed_for_an_excluded_app, configured_excluded_app_never_tracks_or_publishes_a_freeze; storage failure publishes with stored false plus warning (three tests plus a positive)"},{"criterion":"Dead frozen instances retired; recovery/stop not attributed across instances","result":"pass","evidence":"breadd/src/watchdog.rs:50,170,217,262; tests frozen_instance_that_dies_is_retired_with_a_gone_event, new_instance_heartbeat_before_the_tick_retires_the_old_one_without_recovery, an_old_instance_cannot_stop_a_new_one, stop_and_replacement_retire_only_the_old_frozen_instance, real daemon frozen_dead_process_is_retired_and_next_instance_is_not_recovery"}],"required_checks":[{"name":"cargo fmt --check","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"success"},{"name":"cargo clippy --workspace --all-targets --locked -- -D warnings","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"success"},{"name":"cargo test --workspace --locked","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"success"},{"name":"dev release build (push) run 167","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"success"},{"name":"check / check (push)","sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036","status":"missing","note":"workflow triggers only on feature and fix branches, so main has no equivalent run"}],"review_comment":"https://git.breadway.dev/Breadway/bread/issues/45#issuecomment-5431","blockers":[],"next_action":"Coordinator verifies the merged behavior on main and records completion; leave feature/breadbug-watchdog for PR25; keep the BREAD-18 gates open","merged":true,"merge_sha":"90cd7b4b6fae1b4dd99e2f9830d42a05e7f3c036"} ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Breadway/bread#45
No description provided.