§1+§2: polish, harden, invariant re-proofs + ROADMAP update - #38
Conversation
Launch-panic fix: provider key resolution moved off the runtime threadSymptom: bare Root cause (from the real backtrace):
Fix: hoist Verification:
Audited all other keyring call sites ( |
Chat endpoint detection: use the real launched port (managed-services registry)Problem: the dash chat only auto-detected the well-known local ports Fix (
Behavior
Tests/lint: new pure picker Verify on an MI3xx node rocm services # status: ready, endpoint: http://127.0.0.1:11435/v1
rocm # chat tab auto-binds to :11435 |
rominf
left a comment
There was a problem hiding this comment.
LGTM. Verified the 767 deletions are all verbatim moves of app/mod.rs into app/{slash,chat,summary}.rs with visibility narrowed to pub(super) — no behavior, error handling, or validators removed; approval-gating intact. The launch-panic fix is type-level enforced (hoists the keyring probe out of the async runtime), and the "invariant re-proofs" are real assertions (no-double-spawn guard, recoverable seam errors, honest provider revert) rather than comment-claims.
Two minor notes: the architectural-purity invariants are compiler-enforced but have no guard test (a cargo tree CI check would catch a future accidental dep); and resolved_args() now probes the keyring unconditionally including on --demo/--replay paths (harmless, wasted round-trip).
488ca6a to
11f5683
Compare
115f322 to
ed7442b
Compare
rominf
left a comment
There was a problem hiding this comment.
Requesting changes on signing grounds — this is separate from the code review above, which still stands.
Every commit on this PR is unsigned: GitHub reports verification.verified = false (reason unsigned) for all of them. Signed commits are required before this can merge.
To fix, configure commit signing and re-sign the branch history, e.g. with SSH signing:
git config gpg.format ssh
git config user.signingkey <your-signing-key.pub>
git config commit.gpgsign true
git rebase --exec 'git commit --amend --no-edit -S' <base-branch>
git push --force-with-lease
Since this is a stacked PR, re-signing is easiest from the bottom of the stack upward — re-signing a base branch rewrites its SHAs and the dependent branches will need rebasing/re-pushing on top.
Once the commits show as Verified I'll clear this.
11f5683 to
05085b1
Compare
ed7442b to
42f262b
Compare
rominf
left a comment
There was a problem hiding this comment.
Signing gate cleared — all commits now show Verified. Re-reviewed the current diff against supergoal/phase-9-reroute-and-gate after the re-sign.
Approving. The headline honest provider revert is present and correct — ProviderSwitch { previous, target } snapshots the pre-switch provider and reverts to previous (not hardcoded Local) on build failure, fixing the #36 desync; test-covered. The app.rs → app/{mod,slash,chat,summary}.rs split is a faithful file move (bodies byte-identical), and the pre-runtime keyring resolution correctly avoids the nested-runtime panic.
Non-blocking follow-up (unchanged): a CI check asserting the acyclic rocm → rocmd cargo-tree invariant would be nice but isn't in this diff and doesn't gate merge.
05085b1 to
4c515e0
Compare
42f262b to
a8d4367
Compare
There was a problem hiding this comment.
Pull request overview
Polishes and hardens the unified dashboard chat/daemon flows, re-proving key invariants while refactoring the rocm-dash TUI app module into cohesive submodules and updating the project roadmap to reflect §1/§2 completion status.
Changes:
- Refactors
rocm-dash-tuiapp logic by splitting chat backend construction, slash-command routing, and summary rendering into dedicated modules. - Improves chat/provider robustness (managed-service endpoint detection, actionable “no backend” empty state, provider-switch revert correctness) and deduplicates agent completion tail logic.
- Hardens daemon/background-helper behavior (no double-spawn guard) and avoids tokio runtime nesting issues by resolving keyring-dependent args before starting the dashboard runtime.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| ROADMAP.md | Updates roadmap narrative/status for §1 parity and §2 daemon wiring regression. |
| crates/rocm-dash-tui/src/app/summary.rs | Adds pure, concise rendering/parsing helpers for slash-tool outcomes and /plan results. |
| crates/rocm-dash-tui/src/app/slash.rs | Moves slash-command parsing/routing out of app/mod.rs into an I/O-free reducer helper. |
| crates/rocm-dash-tui/src/app/mod.rs | Wires new submodules, improves provider-switch edge data, adds actionable no-backend message, and updates chat detection to consider managed endpoints. |
| crates/rocm-dash-tui/src/app/chat.rs | Centralizes provider agent construction, adds managed-service endpoint picking/detection, and keeps config persistence logic cohesive. |
| crates/rocm-dash-tui/src/agent.rs | Deduplicates agent complete() tail (timeout/error/annotation), centralizes token budget, and adds seam-error recoverability tests. |
| apps/rocm/src/main.rs | Adds background-helper liveness guard helper + tests; prints a note when interactive rocm chat ignores --provider (rerouted to dash chat). |
| apps/rocm/src/dash.rs | Extracts dashboard runtime builder and resolves args (incl. keyring lookup) before entering tokio runtime to avoid nested-runtime panics. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pub(super) fn scalar_or_shape(v: &serde_json::Value) -> String { | ||
| use serde_json::Value; | ||
| match v { | ||
| Value::String(s) => s.clone(), |
| eprintln!( | ||
| "note: launching the dash chat; switch providers with /provider <name>" | ||
| ); |
rominf
left a comment
There was a problem hiding this comment.
Re-approving the current head after the cascade rebase onto the updated base — refreshing my prior approval onto this exact commit.
Re-reviewed: §1/§2 polish: app module split (chat/slash/summary), provider-switch revert correctness, daemon no-double-spawn guard, nested-runtime fix.
Copilot re-reviewed this same commit (COMMENTED, no blocking findings). No clear issues; LGTM.
4c515e0 to
2c8d3c8
Compare
…ing tools
A read-only and a mutating ROCm tool whose injected executor returns
RocmToolOutcome::Error("boom") return a Value carrying an `error` key
(recoverable) and never panic; the mutating error path surfaces no
approval intent. Complements the existing None-executor graceful tests.
Signed-off-by: Michael Roy <michael.roy@amd.com>
…t edge states
- /provider revert: capture the provider active BEFORE the optimistic
switch (new ProviderSwitch{previous,target} edge) so a failed build
(missing key) reverts active_provider to the prior provider, not
unconditionally Local (Phase-8 review deferral).
- No-backend submit: emit an actionable empty-state (NO_CHAT_BACKEND_MSG)
naming the two recovery paths (detect / /provider) instead of a terse
"chat backend unavailable".
- Tests: provider-switch captures previous; no-provider/no-key chat
surfaces an actionable error turn (no panic); approval modal Esc and 'n'
both yield a closing verdict that clears the modal with the covered tab
preserved (no focus trap).
Signed-off-by: Michael Roy <michael.roy@amd.com>
…interactive chat - background_helper_already_running(&paths): factor the "should spawn?" decision out of ensure_background_helper_running so it is testable hermetically with no spawn side effect. Hermetic offline tests prove it is true for a live recorded pid + running=true (no double spawn) and false for a missing state file, a dead/zero pid, or running=false. - Interactive `rocm chat` with --provider now emits a one-line note pointing at /provider for live switching instead of dropping the flag silently; still reroutes to dash::run_chat. Test reads the handler body. Signed-off-by: Michael Roy <michael.roy@amd.com>
…n_chat Dedup the identical multi-thread tokio runtime construction in dash::run and dash::run_chat into a tiny private build_dashboard_runtime() -> Result<Runtime>. Behavior identical. Signed-off-by: Michael Roy <michael.roy@amd.com>
…on) done S1: parity REACHED, bare rocm/chat REROUTED to the dash, tui.rs deletion GATED (retained behind _RETAINED_TUI_ENTRY); add a what-shipped note (read-only tools, approval-gated mutating, /plan, /provider+anthropic, 30-command parity map) linking the parity map + checklists. S2: DONE — rocm daemon runs the real loop in-process via the rocmd lib with on-demand double-spawn-guarded autostart; render_daemon_text retained as --status. Add a brief Supergoal S1+S2 summary. Signed-off-by: Michael Roy <michael.roy@amd.com>
…plete() tail HIGH-1: convert the oversized app.rs (~2813 production lines) into a module directory. Extract handle_slash_command into app/slash.rs, the chat-backend construction/detection/persistence group (build_chat_agent, detect_local_chat, persist_chat_endpoint, config_with_chat, fetch_first_model, set_chat_config) into app/chat.rs, and the pure display utilities (summarize_slash_tool, summarize_json_value, scalar_or_shape, parse_plan_result, SUMMARY_MAX_FIELDS) into app/summary.rs. AppState struct, core constructor, and the event loop stay in app/mod.rs (now ~2122 production lines). Pure code movement, no behavior change; a file->dir module move keeps crate::app::* paths valid. HIGH-2: dedup the 6-line complete() tail copy-pasted across RigAgentClient, ChatGptAgentClient, and AnthropicAgentClient into one generic helper finish_agent_request<F, E>, generic over IntoFuture<Output = Result<String, E>> to bridge rig's per-backend typestate. Also add const MAX_AGENT_TOKENS = 1024 (u64, to match rig's AgentBuilder::max_tokens) used by all three .max_tokens() calls (rust-reviewer LOW). Behavior identical; 1708 tests pass single-threaded; clippy -D warnings + fmt --check clean; agent.rs remains the sole rig crate namer. Signed-off-by: Michael Roy <michael.roy@amd.com>
Bare `rocm` and `rocm chat` panicked at launch with "Cannot start a runtime from within a runtime" whenever ANTHROPIC_API_KEY was unset. run_async built ResolvedArgs on a dash multi-thread runtime worker via resolved_args(), whose Anthropic-key lookup falls through to the OS secret-service store when the env var is absent. That store uses zbus::blocking, which builds its own runtime and block_on's internally — illegal while a runtime is already entered on the thread. Hoist resolved_args() to the synchronous run/run_chat entry points, before rt.block_on, and pass the prebuilt ResolvedArgs into run_async. The keyring lookup now runs on the plain main thread where zbus::blocking is legal. run_async drops its initial_tab param and takes args instead, so it is type-level impossible to re-resolve keys on a runtime thread. Root cause confirmed deterministically: the keyring call panics inside block_on and is panic-free outside it. Signed-off-by: Michael Roy <michael.roy@amd.com>
… launched port) The dash chat only auto-detected the well-known local ports (vLLM 8000, Lemonade 13305), so an engine launched via the tool on a non-default port (e.g. vLLM on 11435) was invisible to chat detection. Make detection registry-first: query the read-only `services` tool and use the actual `endpoint_url` of a ready, OpenAI-compatible managed service (vllm/lemonade/llama.cpp/sglang/pytorch), preferring the most recently created. "ready" mirrors the bin's own HTTP readiness check, so a picked endpoint is one already verified to serve; a best-effort /v1/models fetch confirms liveness and supplies the served model id. Falls back to the well-known TCP probe when no managed service exists, and never overrides an explicitly configured chat_url/env URL (config precedence preserved). Wired into both the startup backend resolution and the live "press d to detect" path. New pure picker `pick_managed_chat_endpoint` with unit tests (incl. the headline non-default-port case, ready-only gating, newest-wins, engine filtering, and model_ref fallback). Signed-off-by: Michael Roy <michael.roy@amd.com>
Signed-off-by: Michael Roy <michael.roy@amd.com>
Signed-off-by: Michael Roy <michael.roy@amd.com>
…headers) The phase-10 app.rs split predates #36/#37 and the repo's MIT relicense, so the extracted modules carried stale logic and license headers that the rebase's "keep the split structure" resolution preserved. Restore main's behavior: - build_chat_agent's OpenAI arm requires a non-empty key and returns None otherwise (#36); the older split passed the raw Option through, building a dead sk-no-key backend that 401s at request time. - /update --apply is position-independent (scan all tokens), matching /uninstall, not only the second token. - /uninstall rejects conflicting --apply + --dry-run with a guided error. - app/{chat,slash,summary}.rs SPDX headers switched Apache-2.0 → MIT to match the relicensed tree (licenserc.toml / hawkeye gate). All dash-tui tests pass; hawkeye clean. Signed-off-by: Michael Roy <michael.roy@amd.com>
a8d4367 to
c9c8648
Compare
Stack 10/10 · base:
supergoal/phase-9-reroute-and-gateapp.rsintoapp/{slash,chat,summary}.rs; dedups the agentcomplete()tail. Updates repoROADMAP.md(§2 done, §1 parity-reached + gated).Reviewed: final cumulative rust + security + maintainability over phases 1-10, zero unresolved CRITICAL/HIGH. Full suite green (1708 tests,
--test-threads=1).