feat: breadbug keybind and crash/freeze notification module #89

Merged
Breadway merged 3 commits from feature/breadbug-integration into main 2026-10-16 17:15:31 +08:00
Owner

Part of BOS-86 (card stays in review until fresh-install acceptance). Part of ECO-EP1.

Dependencies outside this PR:

  • Packaging: Breadway/bread PR #30 (open, base feature/breadbug-status) ships breadbug and the bug.* IPC. Not merged, so iso/bread-lockfile.toml has no breadbug pin yet.
  • Freeze events: Breadway/bread PR #24 (freeze watchdog), open.
  • Consent copy: first-run notice is a follow-up in Breadway/bread, not in this PR.

What

  • Keybind: Super+Shift+B opens breadbug in a terminal (kitty --class breadbug). It uses the TTY UI until the breadbug GUI exists. It is a normal binds.json entry, editable from bos-settings.
  • breadbug-notify module (auto-discovered, shipped active like low-battery-warning): reacts to bread.bug.panic, crash, freeze and recovered. It shows a notification with a "Review report" action where the daemon supports actions. The text always names SUPER + SHIFT + B and says "Nothing is sent unless you choose to." A crash loop is rate limited per app (60 s). A freeze notifies once until the app recovers. App ids are validated before they reach the shell command.
  • Nothing is enabled that sends data. Sending is off by default. The smoke test asserts upload_enabled is not set in ~/.config/bread/breadbug.toml.
  • Smoke test: the breadbug binary check runs only when breadbug is installed. When it is absent, the smoke test records a skipped feature check, which is not a pass. Keybind and module checks always run.
  • README keybind note and CHANGELOG entry.
  • Branch merged with origin/main at ee448e4 (BOS-87 hypridle eval). iso/airootfs/etc/skel/.config/hypr/hypridle.conf is identical to main.

Tested

  • binds.json parses as JSON. bash -n passes for scripts/smoke-test.sh and scripts/test-breadbug-notify.sh.
  • scripts/test-breadbug-notify.sh on the local host: skipped. The installed breadd answers bug.status with "unknown method", so the script's own skip branch ran. The real-breadd marker path was verified by reviewer on head b369af0 (card BOS-86 comment 2271); it has not been re-run on this head.
  • No fresh install was run. The binary, consent and freeze checks are unverified.

Card gates still open (BOS-86)

  • Fresh install shows the keybind and the breadbug binary.
  • First run shows consent copy.
  • Nothing leaves the machine until the user clicks Send.
Part of BOS-86 (card stays in review until fresh-install acceptance). Part of ECO-EP1. Dependencies outside this PR: - Packaging: Breadway/bread PR #30 (open, base feature/breadbug-status) ships breadbug and the bug.* IPC. Not merged, so iso/bread-lockfile.toml has no breadbug pin yet. - Freeze events: Breadway/bread PR #24 (freeze watchdog), open. - Consent copy: first-run notice is a follow-up in Breadway/bread, not in this PR. ## What - Keybind: Super+Shift+B opens breadbug in a terminal (kitty --class breadbug). It uses the TTY UI until the breadbug GUI exists. It is a normal binds.json entry, editable from bos-settings. - breadbug-notify module (auto-discovered, shipped active like low-battery-warning): reacts to bread.bug.panic, crash, freeze and recovered. It shows a notification with a "Review report" action where the daemon supports actions. The text always names SUPER + SHIFT + B and says "Nothing is sent unless you choose to." A crash loop is rate limited per app (60 s). A freeze notifies once until the app recovers. App ids are validated before they reach the shell command. - Nothing is enabled that sends data. Sending is off by default. The smoke test asserts upload_enabled is not set in ~/.config/bread/breadbug.toml. - Smoke test: the breadbug binary check runs only when breadbug is installed. When it is absent, the smoke test records a skipped feature check, which is not a pass. Keybind and module checks always run. - README keybind note and CHANGELOG entry. - Branch merged with origin/main at ee448e4 (BOS-87 hypridle eval). iso/airootfs/etc/skel/.config/hypr/hypridle.conf is identical to main. ## Tested - binds.json parses as JSON. bash -n passes for scripts/smoke-test.sh and scripts/test-breadbug-notify.sh. - scripts/test-breadbug-notify.sh on the local host: skipped. The installed breadd answers bug.status with "unknown method", so the script's own skip branch ran. The real-breadd marker path was verified by reviewer on head b369af0 (card BOS-86 comment 2271); it has not been re-run on this head. - No fresh install was run. The binary, consent and freeze checks are unverified. ## Card gates still open (BOS-86) - Fresh install shows the keybind and the breadbug binary. - First run shows consent copy. - Nothing leaves the machine until the user clicks Send.
Super+Shift+B opens breadbug to report a problem. A new breadbug-notify
module (auto-discovered, shipped active) reacts to the bread.bug.* events
breadd publishes on a panic, crash or freeze and offers to review the
report; a crash loop is rate limited per app and a freeze notifies once
until it recovers. It only shows a notification: nothing is sent unless the
user chooses to, and sending stays off by default.

scripts/test-breadbug-notify.sh runs the real module under a real breadd
with stub notify-send and kitty. The smoke test checks breadbug is
installed, the module and keybind ship, and sending is not enabled.

Refs BOS-86
Author
Owner

Independent exact-head review of b369af03 (BOS-86 BOS half). Verdict: changes requested. No merge on this head.

Verified with evidence:

  • Keybind: binds.json gains one SUPER+SHIFT+B exec entry (kitty breadbug). Valid JSON, schema matches neighbors, no duplicate bind. Static pass; live Hyprland launch not tested (no live desktop) and needs the breadbug binary.
  • breadbug-notify.lua: subscribes to bread.bug.panic/crash/freeze/recovered, 60s per-app crash cooldown, freeze notifies once until recovered, app ids validated to plain id shape before shell use, notify-send -A/-w recipe valid for notify-send 0.8.8, degrades gracefully when actions are unsupported. Event names and ms timestamps confirmed against merged bread main (PR22+PR23). Ran scripts/test-breadbug-notify.sh against breadd v0.9.0 built from bread origin/main: all checks passed (panic notifies naming app, shortcut and consent text; Review opens breadbug --app clip; cooldown holds; apps independent; freeze/recover cycle works; no breadd errors). Marker-driven path verified; watchdog-driven freeze needs bread PR24 (still open) so production freeze detection is unverified.
  • Sending off by default: module only notifies, never sends; smoke test asserts no upload_enabled. Pass.
  • Skel paths and modes correct: module 100644, test script 100755, live skel under iso/airootfs/etc/skel. No hyprctl/wtype/grim/network actions in new code. No em dashes or attribution in diff.
  • No CI applies to these paths (workflow triggers are packaging path filters and v* tags; ordinary PRs run nothing). No formal reviews outstanding. Formal REQUEST_CHANGES via the reviews API was rejected by the server (own-PR rule on this token), so this comment is the persisted verdict.

Required rework before merge:

  1. scripts/smoke-test.sh adds a hard breadbug found check, but breadbug ships only via bread PR30 (open) plus a lockfile bump not in this PR. Merging now turns the smoke gate red on every current install (the PR body admits this). Guard that check to note/skip when breadbug is absent, following the graceful-skip pattern in scripts/test-breadbug-notify.sh, or land this together with the lockfile bump.
  2. iso/airootfs/etc/skel/.config/hypr/hypridle.conf dpms syntax change is unrelated to BOS-86 and unjustified in the PR body. Split it into its own commit/PR or add the Hyprland-version justification and test evidence.

External (coordinator, not this PR): bread PR30 packaging plus iso/bread-lockfile.toml bump for the binary; bread PR24 watchdog for production freeze detection; first-run consent copy is an acknowledged bread-repo follow-up; real-install verification of keybind launch, notification action, and the BOS-86 acceptance line (fresh install has binary, consent shown, nothing sent until Send). BOS-86 stays in review, not done.

Independent exact-head review of b369af03 (BOS-86 BOS half). Verdict: changes requested. No merge on this head. Verified with evidence: - Keybind: binds.json gains one SUPER+SHIFT+B exec entry (kitty breadbug). Valid JSON, schema matches neighbors, no duplicate bind. Static pass; live Hyprland launch not tested (no live desktop) and needs the breadbug binary. - breadbug-notify.lua: subscribes to bread.bug.panic/crash/freeze/recovered, 60s per-app crash cooldown, freeze notifies once until recovered, app ids validated to plain id shape before shell use, notify-send -A/-w recipe valid for notify-send 0.8.8, degrades gracefully when actions are unsupported. Event names and ms timestamps confirmed against merged bread main (PR22+PR23). Ran scripts/test-breadbug-notify.sh against breadd v0.9.0 built from bread origin/main: all checks passed (panic notifies naming app, shortcut and consent text; Review opens breadbug --app clip; cooldown holds; apps independent; freeze/recover cycle works; no breadd errors). Marker-driven path verified; watchdog-driven freeze needs bread PR24 (still open) so production freeze detection is unverified. - Sending off by default: module only notifies, never sends; smoke test asserts no upload_enabled. Pass. - Skel paths and modes correct: module 100644, test script 100755, live skel under iso/airootfs/etc/skel. No hyprctl/wtype/grim/network actions in new code. No em dashes or attribution in diff. - No CI applies to these paths (workflow triggers are packaging path filters and v* tags; ordinary PRs run nothing). No formal reviews outstanding. Formal REQUEST_CHANGES via the reviews API was rejected by the server (own-PR rule on this token), so this comment is the persisted verdict. Required rework before merge: 1. scripts/smoke-test.sh adds a hard `breadbug found` check, but breadbug ships only via bread PR30 (open) plus a lockfile bump not in this PR. Merging now turns the smoke gate red on every current install (the PR body admits this). Guard that check to note/skip when breadbug is absent, following the graceful-skip pattern in scripts/test-breadbug-notify.sh, or land this together with the lockfile bump. 2. iso/airootfs/etc/skel/.config/hypr/hypridle.conf dpms syntax change is unrelated to BOS-86 and unjustified in the PR body. Split it into its own commit/PR or add the Hyprland-version justification and test evidence. External (coordinator, not this PR): bread PR30 packaging plus iso/bread-lockfile.toml bump for the binary; bread PR24 watchdog for production freeze detection; first-run consent copy is an acknowledged bread-repo follow-up; real-install verification of keybind launch, notification action, and the BOS-86 acceptance line (fresh install has binary, consent shown, nothing sent until Send). BOS-86 stays in review, not done.
breadbug is not in iso/bread-lockfile.toml until bread PR 30 ships, so
the smoke test records the binary check as skipped instead of failing.
This is not a pass: the feature is unverified on installs without it.

Part of BOS-86
Author
Owner

Independent exact-head review of 26557240e6 (base main ee448e445a). Verdict: PASS for this slice of BOS-86. BOS-86 is NOT done and stays in review.

Prior change requests (comment 5079, head b369af03):

  1. Smoke gate went red without breadbug: FIXED. scripts/smoke-test.sh:55-59 runs the "breadbug found" check only when breadbug is on PATH, otherwise note() "skipped ... not a pass". Ran the extracted branch: no breadbug gives pass=0 fail=0 plus the skip note; with a fake breadbug on PATH gives PASS. So absent is an explicit skip, never a pass or a failure.
  2. Unrelated hypridle delta: FIXED. git diff origin/main HEAD on hypridle.conf is empty, blob f52fd78 identical on both sides. Branch merges origin/main ee448e4 (BOS-87) inline (0c5682f); PR touches exactly 6 files (CHANGELOG, README, breadbug-notify.lua, binds.json, smoke-test.sh, test-breadbug-notify.sh).

Re-run on this head (not carried over from b369af0):

  • Built isolated modern breadd: copy of a bread-worktree debug build at bread main d7add44 (has bug.marker/bug.status), sha256 957db8b35b69a6db..., placed at /tmp/opencode/bos89/breadd. Never used /usr/local/bin/breadd and never ran breadd --help. Host breadd pid 3449 (started before this review) compared before/after: unchanged.
  • Sandbox: env -i, private HOME/XDG_CONFIG_HOME/XDG_RUNTIME_DIR/XDG_STATE_HOME in a mktemp dir, DBUS_SESSION_BUS_ADDRESS pointed at a nonexistent path, stub notify-send and kitty on PATH, all adapters disabled in breadd.toml. No host desktop, no real notification, no window.
  • scripts/test-breadbug-notify.sh: "breadbug-notify: all checks passed", exit 0. Covers panic notification naming app, SUPER + SHIFT + B and "Nothing is sent unless you choose to"; Review action runs "kitty ... breadbug --app clip"; 60 s per-app crash cooldown; other apps independent; freeze once until recovered then notifies again; no breadd errors in log.
  • Event names match the bread.bug contract in bread main Documentation.md:1444 (bread.bug.panic/crash/freeze/recovered, data { app, kind, t }) and the bug.marker IPC (Documentation.md:1774).
  • Shell safety: module-level safe_app() ^[a-z][a-z0-9_-]*$, max 32 chars, verified with 14 hostile values (quotes, $(), backticks, newline, trailing newline, spaces, uppercase, length 33, non-strings) rejected and legit ids accepted. Also sent hostile app ids through the real breadd: breadd itself rejects them as not a registered app, and a touch-canary file was never created.
  • Lua syntax ok (lua5.4 loadfile). bash -n ok on both scripts. binds.json parses; one new SUPER+SHIFT+B entry, no duplicate.
  • No sending by default: module only calls notify-send and kitty; smoke asserts no upload_enabled = true in breadbug.toml. No hyprctl, network or upload code added. No em dashes or attribution in the diff or in the PR body.

PR body / parser: body says "Part of BOS-86" with no close/fix/resolve keyword (scanned with the same keyword set as breadboard sync.rs CLOSE_KEYWORDS). It cannot falsely close BOS-86 via parse_closes_refs or parse_closes_key_refs. Caveat: the card currently still carries a stale closed-by link to this PR from an earlier body (card BOS-86 links: closed-by 752). sync_pr_closes cascades done only when the PR is done and a keyword links it, and only re-adds links on body keywords, so treat any premature BOS-86 done after merge as the known sync limitation and restore review.

CI: nothing applies to this diff. .forgejo/workflows triggers are packaging/** path filters (bibata, calamares, powerlevel10k, yay-bin), v* tags (release-iso), schedule/dispatch (zen-browser, signed-repo). None match these paths. API: 0 actions runs for this head, 0 commit statuses, no branch protection rule found for main. Absence is expected, not a pass signal by itself. Formal reviews: API returns 0 reviews, no outstanding change requests (own-PR rule prevents a formal APPROVE from this token, so this comment is the persisted verdict).

Provider slice judgement: this PR delivers the BOS side only (keybind, notify module, smoke guards). It does not claim and does not provide the card acceptance. Verified NOT done and still required before BOS-86 can close: (a) breadbug binary: bread PR30 plus an iso/bread-lockfile.toml pin; (b) production freeze detection: bread PR24 watchdog (only marker-driven freeze verified); (c) first-run consent copy: bread follow-up; (d) real fresh-install run showing keybind, binary, consent, and nothing sent until Send. No fresh install was faked here.

Residual risk, non-blocking for merge but blocking for any ISO or release: until (a) lands the keybind opens a terminal that cannot find breadbug. No ISO build, tag or version bump should be cut from main until the lockfile pin is in.

Merge: authorized by coordinator for this PR; no tags, versions or releases.

Independent exact-head review of 26557240e65e7deb0a704de3c3c20e99e406cade (base main ee448e445a581e132472c35aa08e3036903e253f). Verdict: PASS for this slice of BOS-86. BOS-86 is NOT done and stays in review. Prior change requests (comment 5079, head b369af03): 1. Smoke gate went red without breadbug: FIXED. scripts/smoke-test.sh:55-59 runs the "breadbug found" check only when breadbug is on PATH, otherwise note() "skipped ... not a pass". Ran the extracted branch: no breadbug gives pass=0 fail=0 plus the skip note; with a fake breadbug on PATH gives PASS. So absent is an explicit skip, never a pass or a failure. 2. Unrelated hypridle delta: FIXED. git diff origin/main HEAD on hypridle.conf is empty, blob f52fd78 identical on both sides. Branch merges origin/main ee448e4 (BOS-87) inline (0c5682f); PR touches exactly 6 files (CHANGELOG, README, breadbug-notify.lua, binds.json, smoke-test.sh, test-breadbug-notify.sh). Re-run on this head (not carried over from b369af0): - Built isolated modern breadd: copy of a bread-worktree debug build at bread main d7add44 (has bug.marker/bug.status), sha256 957db8b35b69a6db..., placed at /tmp/opencode/bos89/breadd. Never used /usr/local/bin/breadd and never ran breadd --help. Host breadd pid 3449 (started before this review) compared before/after: unchanged. - Sandbox: env -i, private HOME/XDG_CONFIG_HOME/XDG_RUNTIME_DIR/XDG_STATE_HOME in a mktemp dir, DBUS_SESSION_BUS_ADDRESS pointed at a nonexistent path, stub notify-send and kitty on PATH, all adapters disabled in breadd.toml. No host desktop, no real notification, no window. - scripts/test-breadbug-notify.sh: "breadbug-notify: all checks passed", exit 0. Covers panic notification naming app, SUPER + SHIFT + B and "Nothing is sent unless you choose to"; Review action runs "kitty ... breadbug --app clip"; 60 s per-app crash cooldown; other apps independent; freeze once until recovered then notifies again; no breadd errors in log. - Event names match the bread.bug contract in bread main Documentation.md:1444 (bread.bug.panic/crash/freeze/recovered, data { app, kind, t }) and the bug.marker IPC (Documentation.md:1774). - Shell safety: module-level safe_app() ^[a-z][a-z0-9_-]*$, max 32 chars, verified with 14 hostile values (quotes, $(), backticks, newline, trailing newline, spaces, uppercase, length 33, non-strings) rejected and legit ids accepted. Also sent hostile app ids through the real breadd: breadd itself rejects them as not a registered app, and a touch-canary file was never created. - Lua syntax ok (lua5.4 loadfile). bash -n ok on both scripts. binds.json parses; one new SUPER+SHIFT+B entry, no duplicate. - No sending by default: module only calls notify-send and kitty; smoke asserts no upload_enabled = true in breadbug.toml. No hyprctl, network or upload code added. No em dashes or attribution in the diff or in the PR body. PR body / parser: body says "Part of BOS-86" with no close/fix/resolve keyword (scanned with the same keyword set as breadboard sync.rs CLOSE_KEYWORDS). It cannot falsely close BOS-86 via parse_closes_refs or parse_closes_key_refs. Caveat: the card currently still carries a stale closed-by link to this PR from an earlier body (card BOS-86 links: closed-by 752). sync_pr_closes cascades done only when the PR is done and a keyword links it, and only re-adds links on body keywords, so treat any premature BOS-86 done after merge as the known sync limitation and restore review. CI: nothing applies to this diff. .forgejo/workflows triggers are packaging/** path filters (bibata, calamares, powerlevel10k, yay-bin), v* tags (release-iso), schedule/dispatch (zen-browser, signed-repo). None match these paths. API: 0 actions runs for this head, 0 commit statuses, no branch protection rule found for main. Absence is expected, not a pass signal by itself. Formal reviews: API returns 0 reviews, no outstanding change requests (own-PR rule prevents a formal APPROVE from this token, so this comment is the persisted verdict). Provider slice judgement: this PR delivers the BOS side only (keybind, notify module, smoke guards). It does not claim and does not provide the card acceptance. Verified NOT done and still required before BOS-86 can close: (a) breadbug binary: bread PR30 plus an iso/bread-lockfile.toml pin; (b) production freeze detection: bread PR24 watchdog (only marker-driven freeze verified); (c) first-run consent copy: bread follow-up; (d) real fresh-install run showing keybind, binary, consent, and nothing sent until Send. No fresh install was faked here. Residual risk, non-blocking for merge but blocking for any ISO or release: until (a) lands the keybind opens a terminal that cannot find breadbug. No ISO build, tag or version bump should be cut from main until the lockfile pin is in. Merge: authorized by coordinator for this PR; no tags, versions or releases.
Author
Owner

Merged reviewed head 26557240e6 into main (base ee448e445a) as merge commit 9c259b015b. Tree is identical to the reviewed head, 6 files, 199 additions, 1 deletion. Head, base, reviews (0) and mergeability were re-read immediately before the merge. The merge API has no expected-head condition, so the residual race window was seconds and the post-merge tree check closed it. No tag, version or release. Feature branch kept. BOS-86 is not done: bread PR30 packaging plus lockfile pin, bread PR24 freeze watchdog, consent copy and fresh-install acceptance remain open.

Merged reviewed head 26557240e65e7deb0a704de3c3c20e99e406cade into main (base ee448e445a581e132472c35aa08e3036903e253f) as merge commit 9c259b015b3ba78a3fed3d36cae46186b9577cfc. Tree is identical to the reviewed head, 6 files, 199 additions, 1 deletion. Head, base, reviews (0) and mergeability were re-read immediately before the merge. The merge API has no expected-head condition, so the residual race window was seconds and the post-merge tree check closed it. No tag, version or release. Feature branch kept. BOS-86 is not done: bread PR30 packaging plus lockfile pin, bread PR24 freeze watchdog, consent copy and fresh-install acceptance remain open.
Sign in to join this conversation.
No description provided.