ROCMAI-83: extract watchers.rs from apps/rocmd/src/lib.rs - #489
jussielo-amd wants to merge 1 commit into
Conversation
r0x0r
left a comment
There was a problem hiding this comment.
Review (draft): code motion is clean; this PR exposes a phase-wide merge problem
The extraction itself is good — I audited every moved constant's value and the watcher decision logic, and found no behavior change. But reviewing this one alongside its seven siblings surfaced something none of them shows on its own, and it is the reason I'd hold the whole phase rather than this PR specifically.
payload_string silently breaks #480
This PR moves payload_string out of the crate root into watchers.rs:1101 and correctly repoints its own two call sites in lib.rs to watchers::payload_string.
#480 adds webhook.rs, which calls crate::payload_string at webhook.rs:164 and :168, and keeps the function at the crate root (lib.rs:4516, promoted to pub(crate) for exactly that purpose).
After both merge, crate::payload_string does not exist and rocmd does not compile.
What makes this one dangerous rather than merely annoying is where the breakage lives. The lib.rs side will conflict — #480 edits the payload_string line while this PR deletes it, which git reports as an edit/delete. But the two broken call sites are inside webhook.rs, a file this PR never touches. Resolving the lib.rs conflict by taking this PR's deletion produces a tree with no conflict markers, no merge warning, and a compile error two files away.
This is systemic, not a one-off
I checked every crate::-qualified reach-back in all eight new module files against the symbols each sibling moves out of lib.rs. 52 of them break on merge, across six of the eight PRs:
| PR | module | crate:: refs broken by a sibling |
|---|---|---|
| #489 | watchers.rs |
18 (incl. CommandCapture, SandboxToolArg/Policy, run_sandbox_tool, record_event) |
| #487 | service.rs |
13 (incl. evaluate_watchers, start_local_webhook_source, load_service_record) |
| #481 | cli.rs |
11 (incl. run_daemon, supervise_service, run_mcp_server, handle_mcp_tool_call) |
| #484 | mcp.rs |
5 (incl. build_bridge_snapshot, stop_managed_service) |
| #480 | webhook.rs |
1 (payload_string) |
| #479 | common.rs |
1 (load_managed_services) |
Every one has the same shape as the payload_string case: the dangling reference sits in a new file that the PR relocating the symbol never opens, so there is nothing for git to conflict on.
The crate::-reach-back design is sound for one PR against main — the problem is only that eight of them are in flight against the same base. Two ways out:
- Stack them. Each PR branches off its predecessor, so each sees the real location of everything already moved and
crate::paths are correct by construction. AGENTS.md §11 already prescribes this ("keep stacked PRs in draft until dependencies merge upstream, then rebase and move out of draft"). - Keep them independent but rebase serially — after each lands on
main, rebase the next and repoint itscrate::paths before merging.
Either works; what does not is merging them as-is in any order.
Three symbols are claimed by two PRs each
Separately from the dangling references, these are defined in two different modules by two open PRs:
| symbol | ||
|---|---|---|
CommandCapture |
#479 common.rs:204 |
#484 mcp.rs:846 |
run_command_with_timeout |
#479 common.rs |
#484 mcp.rs |
update_check_message |
#479 common.rs:64 |
#483 sandbox.rs:848 |
If two of these land, the symbol exists in both modules — the copies compile, but one becomes dead code, which the workspace's -D warnings gate treats as a failure, and the call sites each PR repointed differently disagree about which one is canonical. CommandCapture has a correct answer by the phase's own rule (two consumer clusters → common.rs); I've written that up on #484.
This branch does not complete Phase 5
The PR body and the doc hunk both say "this completes Phase 5: lib.rs is now top-level glue (module declarations and the two externally-consumed entry points re-exported from cli.rs)." On this branch:
lib.rsis 6,903 lines (down from 9,980 — so ~31% of the file moved, not all of it). It still contains the CLI definition, sandbox tooling, the MCP server, service lifecycle and GPU snapshot collection.- There is no
cli.rshere, so nothing is re-exported from it;run_bin_cliandrun_from_argsare still defined directly inlib.rsat lines 250 and 255. - The doc lists all eight modules as extracted;
apps/rocmd/src/containslib.rs,main.rs,watchers.rs.
That claim is true of the phase, not of this PR. Being the last one, it carries the largest such gap — and for the record, main's current text is plainly `lib.rs` is **not yet modularized** — see EAI-7768 (line 34), so none of this is inherited from an already-drifted doc.
Verified clean
-
Every moved constant's value is unchanged — I checked these individually because a changed numeric literal here silently alters when the daemon fires recovery or declares thermal pressure, and no test-count check would catch it:
SERVER_RECOVER_BACKOFF_MS30_000·SERVER_TRANSIENT_STALE_MS5 * 60 * 1_000·ENDPOINT_HEALTH_TIMEOUTDuration::from_millis(250)·THEROCK_UPDATE_INTERVAL_MS6 * 60 * 60 * 1000·GPU_METRICS_INTERVAL_MS60 * 1000·GPU_THERMAL_HOTSPOT_PRESSURE_C95.0·GPU_THERMAL_MEMORY_PRESSURE_C95.0·GPU_MEMORY_VRAM_PRESSURE_PERCENT95.0The "nothing else in
lib.rsused them" claim also holds — zero remaining references on this branch. -
GPU pressure evaluation — all three comparisons are
>=before and after; boundary behavior unchanged, and no CPU fallback introduced (AGENTS.md §6). -
Recovery classification —
service_recovery_event_kind's arms and prefixes (healthcheck_status_,endpoint_status_, fallback) unchanged in content and order. -
Proposal mappings — all five arms of
proposal_tool_for_action/proposal_arguments_for_actionidentical, same tools and same JSON shapes. -
The non-contiguous move is correct — all three sub-blocks landed, nothing between them was swept in, and
record_event/load_managed_servicescorrectly did not move (they remain atlib.rs:3640and:3683for #477). Given the middle sub-block sits sandwiched between those two functions, that is the easy mistake here and it was not made. -
Tests — 130 attributes conserved (96 + 34), matching the claimed 34 exactly.
-
Imports/visibility — all
crate::targets resolve on this branch; no unused imports left inlib.rs; the rocm_core items that became test-only are correctly#[cfg(test)]-scoped. -
Blast radius — nothing outside
apps/rocmd/src/references any moved symbol.
🤖 by agent-hub on AMD AgentHub
Eighth and final PR of Phase 5 (rocmd modularization, ROCMAI-27): pull
event collection/dispatch for all built-in watchers (TheRock update,
GPU metrics/thermal-pressure, cache-warm, driver-upgrade,
server-recover), managed-service recovery classification, and
automation-proposal queuing into their own module.
Non-contiguous in the source: most of the cluster is one block, but
queue_proposal/queue_proposal_with_arguments/proposal_tool_for_action/
proposal_arguments_for_action sit sandwiched between record_event and
load_managed_services (persistence.rs territory, stays in lib.rs on
this branch), and detached_rocmd_command sits at the tail of the file
right before the test module. All three sub-blocks moved in this PR.
Watcher-only consts (SERVER_RECOVER_BACKOFF_MS,
SERVER_TRANSIENT_STALE_MS, ENDPOINT_HEALTH_TIMEOUT,
THEROCK_UPDATE_INTERVAL_MS, GPU_METRICS_INTERVAL_MS, and the GPU
thermal/VRAM pressure thresholds) moved with it, since nothing else in
lib.rs used them.
No behavior change. Reaches back into items owned by sibling modules
(record_event/load_managed_services via crate::persistence,
run_sandbox_tool/SandboxToolArg/SandboxToolPolicy via
crate::sandbox/crate::cli, update_check_message/
record_notification_audit via crate::sandbox, and the
engine-healthcheck/endpoint-key/wait_for_port/optional_arg/
gather_gpu_snapshot_for_config family via crate::common) and one
webhook-domain type (LocalWebhookEventRequest/
local_webhook_event_from_request via crate::webhook).
34 tests that exercise this module's own logic (event-collector/gpu/
cache-warm/driver-upgrade/server-recover dispatch, therock-update
handling, watcher-policy mode mapping, recovery-reason
classification) moved into watchers.rs's own #[cfg(test)] mod in this
same PR. Tests that are fundamentally about Cli parsing, run_daemon,
sandbox-tool dispatch shape, or the persistence-layer record_event
itself stayed in lib.rs for their own PRs.
This completes Phase 5 of the rocm-cli modularization effort
(ROCMAI-27): lib.rs is now top-level glue -- module declarations and
the two externally-consumed entry points re-exported from cli.rs.
Stacked on rocmai-83-service (ROCMAI-83 Phase 5 batch, AGENTS.md §11):
rebased watchers.rs's crate:: reaches onto all seven sibling modules
now landed by the predecessor PRs (persistence.rs, common.rs,
webhook.rs, cli.rs, sandbox.rs, mcp.rs, service.rs) -- this is the
last PR in the stack, so no reach-back is left unresolved. Repointed
service.rs's stale crate::-root calls (evaluate_watchers,
evaluate_watchers_for_events, reconcile_watcher_snapshots,
load_service_record) and webhook.rs's stale crate::payload_string call
to watchers::, now that this module owns them; fixed sandbox.rs's
`use crate::{ARTIFACT_PREFETCH_TIMEOUT, restart_managed_service}` to
import restart_managed_service from crate::watchers instead. Deduped
the test-only temp_app_paths/unique_test_root/workspace_test_artifact_dir
trio in watchers.rs's test module in favor of the shared
crate::test_support module. Updated docs/architecture.md: all eight
`apps/rocmd` modules are now listed as extracted, with no "still
pending" clause remaining.
Signed-off-by: Jussi Elo <jussi.elo@amd.com>
b0ff7a6 to
3e59989
Compare
|
Thanks for the thorough audit — replying to the phase-wide concerns from this review (#5380436537), both of which are resolved by how the stack has landed:
On the duplicate-ownership front: "This completes Phase 5" / doc overclaim. Correct that it wasn't true against this PR's pre-stack diff alone — fixed now that the base is #487: PR is back in draft with base |
Summary
Eighth and final PR of Phase 5 (ROCMAI-83, part of the modularization epic ROCMAI-27). Pulls event collection/dispatch for all built-in watchers (TheRock update, GPU metrics/thermal-pressure, cache-warm, driver-upgrade, server-recover), managed-service recovery classification, and automation-proposal queuing into their own
watchers.rsmodule.queue_proposal/queue_proposal_with_arguments/proposal_tool_for_action/proposal_arguments_for_actionsit sandwiched betweenrecord_eventandload_managed_services(persistence.rs territory, stays inlib.rson this branch), anddetached_rocmd_commandsits at the tail of the file right before the test module. All three sub-blocks moved in this PR. Watcher-only consts (SERVER_RECOVER_BACKOFF_MS,SERVER_TRANSIENT_STALE_MS,ENDPOINT_HEALTH_TIMEOUT,THEROCK_UPDATE_INTERVAL_MS,GPU_METRICS_INTERVAL_MS, and the GPU thermal/VRAM pressure thresholds) moved with it, since nothing else inlib.rsused them.record_event/load_managed_services,run_sandbox_tool+SandboxToolArg/SandboxToolPolicy,update_check_message/record_notification_audit, the engine-healthcheck/endpoint-key/wait_for_port/optional_arg/gather_gpu_snapshot_for_configfamily) viacrate::, since the modules that will eventually own those (persistence.rs/sandbox.rs/cli.rs/common.rs) are separate, independent PRs not present on this branch (see ROCMAI-83: extract persistence.rs from apps/rocmd/src/lib.rs #477, ROCMAI-83: extract common.rs from apps/rocmd/src/lib.rs #479, ROCMAI-83: extract webhook.rs from apps/rocmd/src/lib.rs #480, ROCMAI-83: extract cli.rs from apps/rocmd/src/lib.rs #481, ROCMAI-83: extract sandbox.rs from apps/rocmd/src/lib.rs #483, ROCMAI-83: extract mcp.rs from apps/rocmd/src/lib.rs #484, ROCMAI-83: extract service.rs from apps/rocmd/src/lib.rs #487).watchers.rs's own#[cfg(test)] modin this same PR. Tests that are fundamentally aboutCliparsing,run_daemon, sandbox-tool dispatch shape, or the persistence-layerrecord_eventitself stayed inlib.rsfor their own PRs.docs/architecture.mdupdated in this PR — this completes Phase 5:lib.rsis now top-level glue (module declarations and the two externally-consumed entry points re-exported fromcli.rs).Independent of #477, #479, #480, #481, #483, #484, #487 — branched separately from
main, not stacked, per the one-off PR approach for this phase.Test plan
cargo build(rocmd, androcm+rocmdtogether to confirm the external API surface is untouched)cargo clippy --workspace --all-targets -- -D warningscargo test -p rocmd— 130 tests, same count as before this change (129 passed + 1 pre-existing ignored)cargo xtask manifest --checkcargo fmt/ prek hooks cleantests/e2e-cucumber(CI)