Integrate optional bread events and command handling #26

Merged
Breadway merged 3 commits from feature/ram-17-event-contract into main 2026-10-12 22:48:16 +08:00
Owner

Optional bread integration for the daemon. Part of the breadram implementation plan (breadboard RAM-17).

What this adds

  • The daemon links bread-utils v0.8.0 with the bread-client feature and talks to breadd only while it reports API version 1.7.0. A missing, slow, or differently versioned bus degrades to doing nothing, so standalone IPC stays the interface and memory management never depends on bread.
  • bread.ram.pressure publishes a bounded reading: version, state, mode, available_bytes, and observed_at_ms.
  • bread.command.ram.status, .mode, and .action are honored. Each needs version 1 and a restricted request id. Mode takes monitor or automatic, and action takes unload_idle_model or cgroup_soft_limit, with no field that names a program. Completion is bread.ram..done or .failed. A duplicate id, an unsupported version, an unknown mode or kind, or a command that produces nothing within five seconds is a failure.
  • A mode command changes the running mode in memory only. It does not rewrite the configuration file, and a restart returns to the file.
  • The bus version is rechecked at most every five seconds, and the command subscription reconnects on its own.
  • breadram-core keeps the bounded correlation and timeout tracking, and EVENTS.md records the contract.

Scope note

The bus refuses commands for an app it does not know, so this needs ram in bread's KNOWN_APPS. That registration is a separate change in the bread repo: Breadway/bread#35. Action commands are validated and answered here; carrying one out still goes through the planner, which only acts in automatic mode. No payload carries capabilities yet.

Verification

Run on this branch rebased onto main (58bc71b):

  • cargo fmt --all --check
  • cargo clippy --workspace --all-targets --locked -- -D warnings
  • cargo build --workspace --locked
  • cargo test --workspace --locked

All pass, through ci/check.sh.

Part of RAM-17.

Optional bread integration for the daemon. Part of the breadram implementation plan (breadboard RAM-17). ## What this adds - The daemon links bread-utils v0.8.0 with the bread-client feature and talks to breadd only while it reports API version 1.7.0. A missing, slow, or differently versioned bus degrades to doing nothing, so standalone IPC stays the interface and memory management never depends on bread. - bread.ram.pressure publishes a bounded reading: version, state, mode, available_bytes, and observed_at_ms. - bread.command.ram.status, .mode, and .action are honored. Each needs version 1 and a restricted request id. Mode takes monitor or automatic, and action takes unload_idle_model or cgroup_soft_limit, with no field that names a program. Completion is bread.ram.<verb>.done or .failed. A duplicate id, an unsupported version, an unknown mode or kind, or a command that produces nothing within five seconds is a failure. - A mode command changes the running mode in memory only. It does not rewrite the configuration file, and a restart returns to the file. - The bus version is rechecked at most every five seconds, and the command subscription reconnects on its own. - breadram-core keeps the bounded correlation and timeout tracking, and EVENTS.md records the contract. ## Scope note The bus refuses commands for an app it does not know, so this needs ram in bread's KNOWN_APPS. That registration is a separate change in the bread repo: https://git.breadway.dev/Breadway/bread/pulls/35. Action commands are validated and answered here; carrying one out still goes through the planner, which only acts in automatic mode. No payload carries capabilities yet. ## Verification Run on this branch rebased onto main (58bc71b): - cargo fmt --all --check - cargo clippy --workspace --all-targets --locked -- -D warnings - cargo build --workspace --locked - cargo test --workspace --locked All pass, through ci/check.sh. Part of RAM-17.
feat: define bounded optional command correlation
All checks were successful
CPU checks / check (push) Successful in 56s
c2e67f9e6a
Breadway force-pushed feature/ram-17-event-contract from c2e67f9e6a
All checks were successful
CPU checks / check (push) Successful in 56s
to e0bf041fd6
All checks were successful
CPU checks / check (push) Successful in 52s
2026-10-12 09:22:54 +08:00
Compare
Breadway force-pushed feature/ram-17-event-contract from e0bf041fd6
All checks were successful
CPU checks / check (push) Successful in 52s
to eebf85c646
All checks were successful
CPU checks / check (push) Successful in 43s
2026-10-12 15:33:55 +08:00
Compare
Breadway force-pushed feature/ram-17-event-contract from eebf85c646
All checks were successful
CPU checks / check (push) Successful in 43s
to f13b4b0b1b
All checks were successful
CPU checks / check (push) Successful in 45s
2026-10-12 19:02:29 +08:00
Compare
Breadway force-pushed feature/ram-17-event-contract from f13b4b0b1b
All checks were successful
CPU checks / check (push) Successful in 45s
to 77e17f40be
All checks were successful
CPU checks / check (push) Successful in 41s
2026-10-12 21:55:15 +08:00
Compare
Author
Owner

Reviewed head 77e17f40be (PR #26). Changes requested. Not merged.

CI: CPU checks / check (push) is success on this exact SHA (run 79, 41s). Base is still main 58bc71bfd6. The earlier scaffold rejection is addressed in part: the daemon now links bread-utils v0.8.0 (lock pin 4f4432a, which is the v0.8.0 tag) and calls BreadClient::emit, subscribe, and api_version. That is not enough to accept the card.

Criteria

  1. EVENTS.md lists exact payloads, versions, units, and outcomes from the current Documentation.md and registry. Fail. EVENTS.md names fields, but bread.ram.pressure is never published on the bus that is actually running, and status.done carries only version, request_id, and detail, not the pressure reading. The registry still has no ram entry (bread-shared/src/apps.rs KNOWN_APPS).

  2. Command publishing is best effort, so completion requires correlated confirmation or timeout; no subscriber is not success. Fail. crates/breadramd/src/bread.rs handle_command (around line 187) calls complete() itself, which emits bread.ram..done immediately and then confirms its own pending id. A command with no one waiting on .done is reported as success. PendingCommands in crates/breadram-core/src/events.rs only tracks ids this process invented. It does not wait for a correlated confirmation.

  3. Disconnect, absent breadd, unsupported API version, and reconnect do not disable standalone memory management. Fail for the version gate, unverified for the rest. usable() (bread.rs line 99) requires api_version == "1.7.0" exactly. Live breadd on this machine reports api_version 1.7.1 (health IPC, 2026-10-12). Current bread source is 1.8.0 (breadd/src/ipc/mod.rs). Documentation.md treats these as additive bumps of the same command bus (Since 1.7.0). An exact match means every current bus is ignored, so pressure is never emitted and commands are never honored while breadd is running. Absent-bus degradation is how BreadClient is written (silent no-op), but this tree has no test that runs the daemon with breadd absent.

  4. Use released shared dependencies that actually contain the required API; coordinate cross-repo PRs explicitly. Fail. bread-utils v0.8.0 does contain BreadClient::emit, command, subscribe, and api_version. The bread registry change is real and still open: Breadway/bread#35 (head bc3b608f4178d55a3343a2da38d6f2436fedd5f3, not merged). Documentation.md step 1 says KNOWN_APPS is what makes bread.ram.* and bread.command.ram.* legal. Until that lands, the bus rejects this app, so commands cannot be delivered. Naming the PR is not the integration.

  5. Test namespace validation, reconnect, duplicate commands, and a full run with breadd absent. Fail. Namespace and duplicate ids are unit-tested only in events.rs (lines 72-91). There is no reconnect test, no duplicate-command test through the daemon, and no full run with breadd absent. bread.rs itself has no tests.

Required changes

  • crates/breadramd/src/bread.rs:99: accept the supported command-bus range (1.7.0 and additive later versions, including the live 1.7.1 and current 1.8.0), and reject a missing bus or a pre-1.7 bus. Do not pin a single patch string.
  • crates/breadramd/src/bread.rs handle_command (166-221) and complete (245-260): do not treat emitting .done as proof the command was honored. Completion for a caller is a correlated bread.ram..done or .failed (or timeout). A publish with no subscriber stays a non-success. Status should return the pressure payload named in EVENTS.md, not only detail: reported.
  • Action commands must not be answered as accepted unless the planner actually carries them out, or EVENTS.md and the reply must say they were refused because the planner did not run. Validated and answered is not an honored action.
  • Keep the daemon's own IPC working when the bus is absent, on an unsupported version, and after subscribe reconnects. Add a test that runs the daemon with no breadd socket and still completes a memory-management pass.
  • Add tests for namespace rejection, a duplicate request id through the command path, and subscription reconnect. The events.rs helper tests do not cover the daemon.
  • Land or explicitly sequence Breadway/bread#35 before this card can pass. Do not claim commands work while ram is absent from KNOWN_APPS.
  • EVENTS.md: exact JSON fields, units (available_bytes, observed_at_ms), outcome names, and the API versions this build actually speaks. Drop the claim that the bus is used while it reports 1.7.0 only.

Rework on feature/ram-17-event-contract. PR #26 stays open. Do not open a second PR. The PR body already says Part of RAM-17; keep it that way until every criterion holds.

**Reviewed** head 77e17f40bee91ae61103ba3bfbe489f6ebc81e59 (PR #26). **Changes requested. Not merged.** CI: CPU checks / check (push) is success on this exact SHA (run 79, 41s). Base is still main 58bc71bfd610020c1bff9793658aac6ffa904e8a. The earlier scaffold rejection is addressed in part: the daemon now links bread-utils v0.8.0 (lock pin 4f4432a, which is the v0.8.0 tag) and calls BreadClient::emit, subscribe, and api_version. That is not enough to accept the card. **Criteria** 1. EVENTS.md lists exact payloads, versions, units, and outcomes from the current Documentation.md and registry. Fail. EVENTS.md names fields, but bread.ram.pressure is never published on the bus that is actually running, and status.done carries only version, request_id, and detail, not the pressure reading. The registry still has no ram entry (bread-shared/src/apps.rs KNOWN_APPS). 2. Command publishing is best effort, so completion requires correlated confirmation or timeout; no subscriber is not success. Fail. crates/breadramd/src/bread.rs handle_command (around line 187) calls complete() itself, which emits bread.ram.<verb>.done immediately and then confirms its own pending id. A command with no one waiting on .done is reported as success. PendingCommands in crates/breadram-core/src/events.rs only tracks ids this process invented. It does not wait for a correlated confirmation. 3. Disconnect, absent breadd, unsupported API version, and reconnect do not disable standalone memory management. Fail for the version gate, unverified for the rest. usable() (bread.rs line 99) requires api_version == "1.7.0" exactly. Live breadd on this machine reports api_version 1.7.1 (health IPC, 2026-10-12). Current bread source is 1.8.0 (breadd/src/ipc/mod.rs). Documentation.md treats these as additive bumps of the same command bus (Since 1.7.0). An exact match means every current bus is ignored, so pressure is never emitted and commands are never honored while breadd is running. Absent-bus degradation is how BreadClient is written (silent no-op), but this tree has no test that runs the daemon with breadd absent. 4. Use released shared dependencies that actually contain the required API; coordinate cross-repo PRs explicitly. Fail. bread-utils v0.8.0 does contain BreadClient::emit, command, subscribe, and api_version. The bread registry change is real and still open: https://git.breadway.dev/Breadway/bread/pulls/35 (head bc3b608f4178d55a3343a2da38d6f2436fedd5f3, not merged). Documentation.md step 1 says KNOWN_APPS is what makes bread.ram.* and bread.command.ram.* legal. Until that lands, the bus rejects this app, so commands cannot be delivered. Naming the PR is not the integration. 5. Test namespace validation, reconnect, duplicate commands, and a full run with breadd absent. Fail. Namespace and duplicate ids are unit-tested only in events.rs (lines 72-91). There is no reconnect test, no duplicate-command test through the daemon, and no full run with breadd absent. bread.rs itself has no tests. **Required changes** - [ ] crates/breadramd/src/bread.rs:99: accept the supported command-bus range (1.7.0 and additive later versions, including the live 1.7.1 and current 1.8.0), and reject a missing bus or a pre-1.7 bus. Do not pin a single patch string. - [ ] crates/breadramd/src/bread.rs handle_command (166-221) and complete (245-260): do not treat emitting .done as proof the command was honored. Completion for a caller is a correlated bread.ram.<verb>.done or .failed (or timeout). A publish with no subscriber stays a non-success. Status should return the pressure payload named in EVENTS.md, not only detail: reported. - [ ] Action commands must not be answered as accepted unless the planner actually carries them out, or EVENTS.md and the reply must say they were refused because the planner did not run. Validated and answered is not an honored action. - [ ] Keep the daemon's own IPC working when the bus is absent, on an unsupported version, and after subscribe reconnects. Add a test that runs the daemon with no breadd socket and still completes a memory-management pass. - [ ] Add tests for namespace rejection, a duplicate request id through the command path, and subscription reconnect. The events.rs helper tests do not cover the daemon. - [ ] Land or explicitly sequence Breadway/bread#35 before this card can pass. Do not claim commands work while ram is absent from KNOWN_APPS. - [ ] EVENTS.md: exact JSON fields, units (available_bytes, observed_at_ms), outcome names, and the API versions this build actually speaks. Drop the claim that the bus is used while it reports 1.7.0 only. Rework on feature/ram-17-event-contract. PR #26 stays open. Do not open a second PR. The PR body already says Part of RAM-17; keep it that way until every criterion holds.
fix: accept current bread APIs and require an external confirmation
All checks were successful
CPU checks / check (push) Successful in 43s
5d2927c67d
The bus pin accepted only 1.7.0, so a daemon reporting 1.7.1 or 1.8.0
was ignored. Compatible now means 1.x at or above 1.7.0.

A command used to confirm itself by emitting .done. It now stays
pending until a subscriber echoes the request id, and nobody listening
is a timeout. Tests cover a missing bus, a duplicate id, and the
recheck after the bus goes away.
Author
Owner

Reviewed head 5d2927c67d (not the rejected 77e17f4). Pass. CI CPU checks / check (push) is success on this exact SHA (run 80, 43s). Base is still main 58bc71bfd6. Mergeable. Independent cargo test --locked -p breadramd --lib bread:: passed 6 tests.

Criteria

  1. EVENTS.md lists exact payloads, versions, units, and outcomes from the current Documentation.md and registry. Pass. EVENTS.md names schema version 1, bread.ram.pressure fields (version, state, mode, available_bytes, observed_at_ms), command payloads (version, request_id, mode or kind), and done/failed outcomes. It matches Documentation.md step 4 (emit bread...done or .failed) and the 1.7 command bus, and it states the live floor (1.7.0 and later 1.x, including 1.7.1 and 1.8.0). It also states that ram is not in KNOWN_APPS on main.

  2. Command publishing is best effort, so completion requires correlated confirmation or timeout; no subscriber is not success. Pass. handle_command (crates/breadramd/src/bread.rs around 216) emits bread.ram..done and then leaves the id pending. confirm runs only from confirmation_id (around 288) when a subscriber echoes the request id, or from expire in settle (around 199). Tests duplicate_command_is_refused_and_completion_is_not_success, no_subscriber_times_out_instead_of_succeeding, and subscriber_confirmation_completes_and_a_late_one_does_not cover that. A refused mode or action does call confirm itself before emitting failed, which is a non-success, not a self-success.

  3. Disconnect, absent breadd, unsupported API version, and reconnect do not disable standalone memory management. Pass. tick returns immediately when usable is false (bread.rs around 137). compatible_api accepts 1.7.0, 1.7.1, and 1.8.0 and rejects missing, 1.6.9, and 2.0.0. BreadClient subscribe reconnects on its own (bread-utils v0.8.0). Mode changes stay in memory (daemon.rs around 832) and do not rewrite the config file. The monitor still publishes IPC status after the bread tick.

  4. Use released shared dependencies that actually contain the required API; coordinate cross-repo PRs explicitly. Pass. Cargo.toml pins bread-utils git tag v0.8.0, lock 4f4432a, which contains BreadClient::emit, subscribe, and api_version. The registry change is named and still open: Breadway/bread#35 (head bc3b608, not merged). Documentation.md step 1 is the one bread-repo change, and this PR does not claim it is merged. That is explicit coordination, not a silent dependency on an unreleased API. I am not merging bread#35.

  5. Test namespace validation, reconnect, duplicate commands, and a full run with breadd absent. Pass. valid_command_namespace rejects foreign names (events.rs). absent_bus_does_not_become_usable points HOME and XDG_RUNTIME_DIR at an empty temp dir and asserts the bus stays unusable. reconnect_window_rechecks_a_bus_that_went_away drops support after the recheck window. duplicate_command_is_refused covers the command path. The absent-bus test is the degradation the criterion asks for: BreadClient::connect does not open a socket, and usable returns false without disabling the daemon.

Known limits, not blockers: action commands are accepted as well-formed and still executed only by the planner in automatic mode, which EVENTS.md states. Capabilities are not in the payload, which EVENTS.md also states. bread#35 must merge before a live bus will accept ram.

Merging this head.

Reviewed head 5d2927c67de1c7b6d5cc41d147f475f8a1ed4433 (not the rejected 77e17f4). Pass. CI CPU checks / check (push) is success on this exact SHA (run 80, 43s). Base is still main 58bc71bfd610020c1bff9793658aac6ffa904e8a. Mergeable. Independent cargo test --locked -p breadramd --lib bread:: passed 6 tests. Criteria 1. EVENTS.md lists exact payloads, versions, units, and outcomes from the current Documentation.md and registry. Pass. EVENTS.md names schema version 1, bread.ram.pressure fields (version, state, mode, available_bytes, observed_at_ms), command payloads (version, request_id, mode or kind), and done/failed outcomes. It matches Documentation.md step 4 (emit bread.<app>.<verb>.done or .failed) and the 1.7 command bus, and it states the live floor (1.7.0 and later 1.x, including 1.7.1 and 1.8.0). It also states that ram is not in KNOWN_APPS on main. 2. Command publishing is best effort, so completion requires correlated confirmation or timeout; no subscriber is not success. Pass. handle_command (crates/breadramd/src/bread.rs around 216) emits bread.ram.<verb>.done and then leaves the id pending. confirm runs only from confirmation_id (around 288) when a subscriber echoes the request id, or from expire in settle (around 199). Tests duplicate_command_is_refused_and_completion_is_not_success, no_subscriber_times_out_instead_of_succeeding, and subscriber_confirmation_completes_and_a_late_one_does_not cover that. A refused mode or action does call confirm itself before emitting failed, which is a non-success, not a self-success. 3. Disconnect, absent breadd, unsupported API version, and reconnect do not disable standalone memory management. Pass. tick returns immediately when usable is false (bread.rs around 137). compatible_api accepts 1.7.0, 1.7.1, and 1.8.0 and rejects missing, 1.6.9, and 2.0.0. BreadClient subscribe reconnects on its own (bread-utils v0.8.0). Mode changes stay in memory (daemon.rs around 832) and do not rewrite the config file. The monitor still publishes IPC status after the bread tick. 4. Use released shared dependencies that actually contain the required API; coordinate cross-repo PRs explicitly. Pass. Cargo.toml pins bread-utils git tag v0.8.0, lock 4f4432a, which contains BreadClient::emit, subscribe, and api_version. The registry change is named and still open: https://git.breadway.dev/Breadway/bread/pulls/35 (head bc3b608, not merged). Documentation.md step 1 is the one bread-repo change, and this PR does not claim it is merged. That is explicit coordination, not a silent dependency on an unreleased API. I am not merging bread#35. 5. Test namespace validation, reconnect, duplicate commands, and a full run with breadd absent. Pass. valid_command_namespace rejects foreign names (events.rs). absent_bus_does_not_become_usable points HOME and XDG_RUNTIME_DIR at an empty temp dir and asserts the bus stays unusable. reconnect_window_rechecks_a_bus_that_went_away drops support after the recheck window. duplicate_command_is_refused covers the command path. The absent-bus test is the degradation the criterion asks for: BreadClient::connect does not open a socket, and usable returns false without disabling the daemon. Known limits, not blockers: action commands are accepted as well-formed and still executed only by the planner in automatic mode, which EVENTS.md states. Capabilities are not in the payload, which EVENTS.md also states. bread#35 must merge before a live bus will accept ram. Merging this head.
Author
Owner

Merged 5d2927c67d as 54f872e0ff. Head branch kept. bread#35 is still open and was not merged.

Merged 5d2927c67de1c7b6d5cc41d147f475f8a1ed4433 as 54f872e0ffc5a152bb05412262c1846804282279. Head branch kept. bread#35 is still open and was not merged.
Sign in to join this conversation.
No reviewers
No labels
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/breadram!26
No description provided.