ROCMAI-83: extract webhook.rs from apps/rocmd/src/lib.rs - #480
jussielo-amd wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The architecture documentation inaccurately claims independent sibling modules are already present.
Review effort: Balanced
Findings: 1
What changed in this PR
Extracts the rocmd local webhook implementation into a dedicated module without intended behavior changes.
Changes:
- Moves webhook routing, validation, and tests into
webhook.rs. - Repoints daemon and test call sites.
- Updates architecture documentation, though it prematurely lists sibling extractions as complete.
| File | Description |
|---|---|
apps/rocmd/src/webhook.rs |
Contains extracted webhook logic and tests. |
apps/rocmd/src/lib.rs |
Registers and calls the webhook module. |
docs/architecture.md |
Documents modularization progress. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| ### `apps/rocmd` — background daemon | ||
|
|
||
| `lib.rs` is **not yet modularized** — see EAI-7768. | ||
| `lib.rs` modularization is in progress (ROCMAI-83, Phase 5 of EAI-7768's sequencing). Extracted so far: `persistence.rs` (`record_event`/`load_managed_services`, the automation-event/audit-log and managed-service-registry I/O shared across the daemon's sandbox, MCP, service-lifecycle, and watcher code), `common.rs` (helpers shared across ≥2 of those remaining clusters: GPU/amd-smi snapshotting, the bridge-snapshot diagnostic, `CommandCapture`/command-timeout plumbing, and small arg/healthcheck/endpoint-key utilities), and `webhook.rs` (the local webhook source: its axum routes, request validation, and watcher-kind allow-list). Still pending: `cli.rs`, `sandbox.rs`, `mcp.rs`, `service.rs`, and `watchers.rs` — each landing as its own PR. |
There was a problem hiding this comment.
Resolved by restacking rather than rewording: PR #480 is now based on rocmai-83-common (which carries #477's persistence.rs and #479's common.rs), per this repo's AGENTS.md §11 stacked-PR convention — merge order 477 → 479 → 480. The architecture.md hunk is now a genuinely incremental diff on top of the already-landed predecessor wording (adds webhook.rs, drops it from "still pending"), not a claim that can go stale out of order, since #480's base branch doesn't exist upstream until #479 merges.
r0x0r
left a comment
There was a problem hiding this comment.
Review: code is clean; holding approval on one doc line
The extraction verified as pure code motion, including the security-relevant parts, which I checked closely because this module is a network-facing endpoint that turns JSON into automation triggers. One issue in docs/architecture.md is the only thing between this and an approval.
The doc hunk describes a state this branch is not in
The new text claims persistence.rs and common.rs are extracted alongside webhook.rs, but neither file exists in this branch — only lib.rs, main.rs and webhook.rs are present.
This is worth more than a doc nit because of how the five PRs in this phase interact, which is invisible from inside any one of them:
- All five rewrite the same single line of
docs/architecture.md, and all five branch independently frommain. Four are therefore guaranteed to conflict there. - Each one's text is a cumulative prefix assuming the earlier sequence has merged, so it is only true if they land in exactly the order 477 → 479 → 480 → 481 → 483.
- The failure mode is quiet: if this merges before #477 and #479, there is nothing to conflict with, so it applies cleanly and
mainasserts two files that do not exist.
docs/architecture.md says of itself that it is updated in the same PR as the code it documents and that "a stale-but-plausible-looking note is worse than an explicit prompt to check". Either fix works:
- Scope the hunk to
webhook.rsonly, letting each sibling append its own module as it lands — self-consistent at any merge order, and turns the conflicts into trivial appends. - Keep the cumulative text and gate merge order via
Depends on #477, #479in the body.
(1) is more robust; (2) depends on whoever merges remembering the order.
Verified clean
- Validation logic is identical — this was my main concern and I went through it guard by guard against
main: thewatcher_hintempty/unknown check, thekindempty check, every arm of thelocal_webhook_kind_allowedmatch (all six watcher IDs and their allowed kinds), theservice_idpath-separator rejection viarocm_core::ServiceId::new,server-recoverrequiringservice_id,cache-warmrequiringpayload.artifact_ref, anddriver-upgraderequiringpayload.component == "driver". Order of checks preserved. The only textual change in that region is thedriver-upgradeguard being rewrapped across two lines, which is formatting. A reordered or dropped guard here would be a security regression that no amount of test-count arithmetic would catch, so it is worth stating explicitly that there isn't one. - The three expected deltas and nothing else —
fn→pub(crate) fnon exported items,payload_string(...)→crate::payload_string(...)in two guards, and that one rewrap. payload_stringback-reference is sound: it ispub(crate)inlib.rs:4516, and the moved tests reach it only indirectly throughlocal_webhook_event_from_request, so there is no test-scope visibility problem. Worth flagging for whoever writeswatchers.rs: that PR has to repoint this call site whenpayload_stringmoves.- Call sites — all four repointed (
start_local_webhook_source,receive_local_webhook_event,local_webhook_event_from_request+LocalWebhookEventRequest). - Imports —
builtin_watcherandDeserializecorrectly dropped fromlib.rs;Serializecorrectly kept, sinceprint_json<T: Serialize>andcall_enginestill need it — that is the easy mistake in this diff and it was not made.TcpListener,mpscandgetlooked like survivors on a mechanical pass, but all remaining uses are fully-qualified (std::net::TcpListener,std::sync::mpsc), so nothing fails under-D warnings. I confirmed these by hand. - Field visibility —
LocalWebhookSource'sendpoint,receiverandtaskarepub(crate)andrun_daemonreads all three by name. - Tests — 130 attributes conserved (113 + 17), and the claimed 17 is exactly right: 15
#[test]plus 2#[tokio::test(flavor = "multi_thread")]. The three left behind do genuinely exercise other things —local_webhook_help_mentions_loopback_only_bindingandlocal_webhook_port_rejects_out_of_range_valueshitCli,local_webhook_requires_enabled_automation_loophitsrun_daemon— so the shared name prefix was correctly treated as a red herring rather than a sorting key. - Blast radius — no reference to any moved symbol outside
apps/rocmd/src/, and no e2e scenario exercises the webhook endpoint. - Leak scan (AGENTS.md §2) — clean.
🤖 by agent-hub on AMD AgentHub
b080766 to
dd6307e
Compare
|
Re the review withholding approval on the Resolved by restacking (your option framed closest to "(2)", but structural rather than a body note): #480 is now rebased onto Everything else in the review (validation logic, import deltas, |
195678b to
b843952
Compare
Third PR of Phase 5 (rocmd modularization, ROCMAI-27): pull the local webhook source (axum routes, request validation, watcher-kind allow-list) into its own module. Fully self-contained aside from one call into payload_string, which stays at the crate root until watchers.rs is extracted in a later PR (PR8 will need to re-point that one call site once it moves). No behavior change; call sites repointed to webhook::. The 17 tests that exercise this module's own logic directly (not mixed with run_daemon/Cli-parsing/watcher-domain logic that happens to share the local_webhook_* name prefix) moved into webhook.rs's own Signed-off-by: Jussi Elo <jussi.elo@amd.com>
dd6307e to
4491bf6
Compare

Summary
Third PR of Phase 5 (ROCMAI-83, part of the modularization epic ROCMAI-27). Pulls the local webhook source — axum routes, request validation, watcher-kind allow-list — into its own
webhook.rsmodule. Fully self-contained except one call intopayload_string, which stays at the crate root untilwatchers.rsis extracted in a later PR (that PR will need to re-point this one call site once it moves).webhook::....webhook.rs's own#[cfg(test)] modin this same PR. Three other tests that happen to share thelocal_webhook_*name prefix but actually exerciseCliparsing /run_daemonwere left in place — they'll move withcli.rs/service.rsrespectively.docs/architecture.mdupdated in this PR.Independent of #477 (
persistence.rs) and #479 (common.rs) — branched separately frommain, 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)