Skip to content

§1: reroute bare rocm/chat → dash chat + parity gate (tui.rs RETAINED) - #37

Merged
michaelroy-amd merged 7 commits into
mainfrom
supergoal/phase-9-reroute-and-gate
Jun 26, 2026
Merged

§1: reroute bare rocm/chat → dash chat + parity gate (tui.rs RETAINED)#37
michaelroy-amd merged 7 commits into
mainfrom
supergoal/phase-9-reroute-and-gate

Conversation

@michaelroy-amd

Copy link
Copy Markdown
Member

Stack 9/10 · base: supergoal/phase-8-provider-anthropic

  • Bare rocm and interactive rocm chat now route to the dash chat (dash::run_chat); --chat-mock + prompt passthrough preserved.
  • tui.rs RETAINED (parity then gate) via a single #[allow(dead_code)] retention anchor — deletion is a human step in docs/tui-retirement-checklist.md (NOT performed here).
  • Finalizes docs/dash-parity-map.md (30 covered) + adds docs/dash-parity-checklist.md.

Reviewed: rust + maintainability. All cargo gates green.

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve with minor comments. Reroute + tui.rs retention is well-documented (single dead-code anchor, retirement checklist).

Notes:

  • Compat change: interactive rocm chat --provider/--model no longer presets the session (dash auto-detects + /provider switches). Recoverable, but worth a changelog/user-doc line rather than only a code comment. Same for --chat-mock being interactive-only (silently ignored on the render path).
  • The parity-gate tests assert on main.rs source text (brace-slicing + .contains) — genuine (fail on revert) but brittle: a rustfmt reformat or rename breaks them with no behavioral regression. Acceptable stopgap given the paths need a TTY; flagging as test debt. assert_ne!(target as usize, 0) is a tautology — the compile-time signature coercion above it is the real guard.

@michaelroy-amd
michaelroy-amd force-pushed the supergoal/phase-8-provider-anthropic branch from de9b2e8 to c217073 Compare June 23, 2026 03:41
@michaelroy-amd
michaelroy-amd force-pushed the supergoal/phase-9-reroute-and-gate branch from 488ca6a to 11f5683 Compare June 23, 2026 03:41

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@michaelroy-amd
michaelroy-amd force-pushed the supergoal/phase-8-provider-anthropic branch from c217073 to ba10a67 Compare June 23, 2026 17:39
@michaelroy-amd
michaelroy-amd force-pushed the supergoal/phase-9-reroute-and-gate branch from 11f5683 to 05085b1 Compare June 23, 2026 17:39

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signing gate cleared — all commits on this PR now show Verified (verification.verified = true, reason valid). Re-reviewed the current diff against this PR's own base after the re-sign; it's substantively unchanged from my prior review.

Approving on both grounds: code is sound and commits are signed.

@michaelroy-amd
michaelroy-amd force-pushed the supergoal/phase-8-provider-anthropic branch from ba10a67 to 0cf26f3 Compare June 24, 2026 16:53
@michaelroy-amd
michaelroy-amd force-pushed the supergoal/phase-9-reroute-and-gate branch from 05085b1 to 4c515e0 Compare June 24, 2026 16:53
@rominf
rominf requested a review from Copilot June 24, 2026 17:50

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Routes the default interactive rocm entrypoint and interactive rocm chat (no --prompt) to the unified dash chat (dash::run_chat focusing ActiveTab::Chat), while intentionally retaining the legacy tui.rs behind a single retention anchor and documenting the human deletion gate once parity is accepted.

Changes:

  • Reroutes bare rocm and interactive rocm chat to dash::run_chat, adds --chat-mock passthrough, and pins tui.rs reachability via a retention anchor.
  • Adds dash::run_chat and threads initial_tab into the dash async launcher to support chat-first launches.
  • Adds/updates parity + retirement documentation (parity map, parity checklist, and a human-only deletion checklist).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
docs/tui-retirement-checklist.md Adds the human-only go/no-go procedure and explains the retention anchor.
docs/dash-parity-map.md Updates parity map with Phase 9 routing note and links to the retirement checklist.
docs/dash-parity-checklist.md Adds the accept/retire gate checklist summarizing parity evidence.
apps/rocm/src/main.rs Reroutes interactive entrypoints to dash chat, adds --chat-mock, adds retention anchor + routing verification tests.
apps/rocm/src/dash.rs Adds run_chat entrypoint and plumbs initial_tab into run_async/resolved_args.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/rocm/src/main.rs
Comment thread apps/rocm/src/main.rs
Comment thread apps/rocm/src/dash.rs Outdated

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approving the current head after the cascade rebase onto the updated base — refreshing my prior approval onto this exact commit.

Re-reviewed: reroute bare rocm + interactive rocm chat → dash::run_chat; legacy tui.rs retained behind a single anchor with a human-only deletion gate.

Copilot re-reviewed this same commit (COMMENTED, no blocking findings). No clear issues; LGTM.

@michaelroy-amd

Copy link
Copy Markdown
Member Author

Reviewed the Copilot suggestions here (simplicity-first / ponytail pass) — dismissing all three; nothing to change on this PR.

  • --provider ignored in interactive rocm chatintentional design. Interactive chat routes to the unified dash where /provider is the switch; --provider is honored on the non-interactive render path. Documented in the code comment at the routing site.
  • Routing tests string-match main.rs source — valid smell, but rewriting passing tests mid-merge is churn/risk for little gain. Deferred to a dedicated test-improvement pass, not this stack.
  • run_chat duplicates the AppPaths/runtime build — legitimate dedup, but it's a refactor and out of scope for landing this stack. Captured as a follow-up cleanup (a shared runtime-builder helper).

@volen-silo volen-silo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed from the diff — clean, well-documented reroute. One real UX regression to address, plus a test-robustness suggestion. Not blocking.

🟡 --provider is silently ignored on the interactive path

Previously the interactive launch honored it: tui::run(provider.map(provider_name).map(str::to_owned)). Now the branch calls dash::run_chat(chat_mock), whose signature is run_chat(chat_mock: bool)provider is dropped. So rocm chat --provider anthropic (interactive) starts on auto-detect, not anthropic. clap still accepts the flag, so it's silently ignored — no error, no effect. The code comment documents this intentionally, but a user can't see a comment.

Pick one:

  • Thread it through: run_chat(provider, chat_mock) to set the starting backend (the dash already supports /provider to switch afterward), or
  • If intentionally deferred, emit a one-line notice when --provider is passed interactively ("ignoring --provider; use /provider in the dash").

Same question for --tools on the interactive branch — confirm it isn't silently dropped too.

🟡 Source-introspection tests are brittle

launch_default_routes_to_dash_run_chat_not_tui et al. read main.rs at test time and hand-roll brace balancing to extract handler bodies, then string-match. Clever given the TTY constraint, but it breaks on any harmless refactor (e.g. extracting the arm into a fn) even when behavior is unchanged, and the brace counter doesn't account for braces inside string/char literals (fine today, latent).

More robust: extract the routing decision into a pure function returning an enum (e.g. chat_route(interactive, prompt, chat_mock) -> ChatRoute), call it from both entrypoints, and unit-test that — assertions key on real logic, not source text. Worth doing before the eventual tui.rs deletion churns these strings.

🟢 Good

  • Retention anchor (_RETAINED_TUI_ENTRY) is a clean way to keep the frozen module reachable under -D warnings without a dead_code cascade, and tui_run_is_referenced_only_by_the_retention_anchor locks it to exactly one reference.
  • run_chat reuses the same run_async path (replay=None ⇒ live executor + embedded daemon), so chat parity inherits the Phase 2/3 seam correctly.
  • The retirement checklist is excellent — compiler-driven pruning, "don't run automatically," rollback section. Right way to stage a risky deletion.

Notes

  • CI hasn't reported on this branch yet. With the license-header hawkeye hook now on main (#51), rebase this stack so hawkeye/fmt run clean here too.
  • Reviewed from the diff only; I didn't check out the phase-8 stack, so I haven't run its tests or verified the parity-checklist's cross-phase evidence pointers.

@michaelroy-amd
michaelroy-amd force-pushed the supergoal/phase-8-provider-anthropic branch 2 times, most recently from 1dac2c7 to ae78484 Compare June 26, 2026 15:55
Base automatically changed from supergoal/phase-8-provider-anthropic to main June 26, 2026 17:01
Bare `rocm` and interactive `rocm chat` now open the unified dash with the
Chat tab focused via dash::run_chat, instead of the legacy tui::run assistant.
run_async takes an initial_tab; run_chat is a thin wrapper. Adds --chat-mock to
the Chat command. Prompt/non-interactive passthrough (render_chat_*) unchanged.

tui.rs is RETAINED and compiling via a single dead_code-allowed retention
anchor (_RETAINED_TUI_ENTRY) that keeps its call graph reachable under
-D warnings without deleting the module.

Signed-off-by: Michael Roy <michael.roy@amd.com>
… checklist

- dash-parity-map.md: header note that all 30 commands are covered as of Phase 9
  and bare rocm/chat route to the dash (30 covered, 0 pending-status).
- dash-parity-checklist.md: accept/retire gate (ROADMAP §1 step 1) — 6 capability
  buckets, all ACCEPTED with concrete test/artifact pointers, zero unaccepted.
- tui-retirement-checklist.md: exact human go/no-go deletion steps (not executed)
  with a compiler-driven dead-code discovery procedure and verify command.

Signed-off-by: Michael Roy <michael.roy@amd.com>
Source-level dispatch tests (interactive branches need a TTY, untestable in CI):
- launch_default and the Command::Chat interactive arm call dash::run_chat and
  no longer call tui::run;
- --chat-mock is forwarded to run_chat(chat_mock); --prompt still routes to
  render_chat_prompt_text / render_chat_text (scriptable passthrough);
- dash::run_chat exists with fn(bool) -> Result<()>;
- tui::run is referenced exactly once in production code (the retention anchor).

Signed-off-by: Michael Roy <michael.roy@amd.com>
cargo fmt wrapped the multi-arg run_async call; the dash::run_chat existence
test now asserts on the fn pointer address so the binding has an effect under
clippy::no_effect_underscore_binding (-D warnings).

Signed-off-by: Michael Roy <michael.roy@amd.com>
@michaelroy-amd
michaelroy-amd force-pushed the supergoal/phase-9-reroute-and-gate branch from 4c515e0 to 2c8d3c8 Compare June 26, 2026 18:10
… against nested tokio runtimes

The #37 reroute sends bare `rocm` / `rocm chat` to `dash::run_chat`, which
builds a tokio runtime and `block_on`s `run_async`. `run_async` called
`resolved_args`, which resolved the Anthropic key via the native secret store;
on Linux that store uses `zbus::blocking` (its own `block_on`). Running it with
a tokio runtime context already entered panics "Cannot start a runtime from
within a runtime" — so every Linux user launching `rocm`/`rocm chat` without
ANTHROPIC_API_KEY in env crashed at startup. The acceptance PTY harness caught
it; unit tests did not.

Two layers, fixing the instance and the class:

1. Resolve the Anthropic key off-runtime. `dash::run` and `dash::run_chat` now
   call `anthropic_api_key_for_dash()` before building the runtime and thread the
   resolved `Option<String>` through `run_async` -> `resolved_args` (new param).
   This keeps secret I/O off the async hot path and self-documents the intent.

2. Make `provider_keys::with_native_entry` runtime-safe. It is the single
   chokepoint for every store op (get/set/clear) and thus for
   `resolve_provider_api_key` / `provider_key_status`. When a tokio runtime is
   active (`Handle::try_current().is_ok()`), it now runs `native_entry` + the
   action on a fresh `std::thread::scope` thread that has no runtime entered, and
   `join()`s it (mapping a worker panic to an `Err` so `thread::scope` cannot
   re-propagate it into a process abort). `block_in_place` is deliberately not
   used: it keeps the runtime context entered, so the nested `block_on` would
   still panic. This also hardens the latent provider-assisted `/plan` path
   (run_internal_mcp_call -> provider_chat -> resolve_provider_api_key), which
   only avoids the panic today because it runs under spawn_blocking.

Adds `native_store_access_inside_tokio_runtime_does_not_panic`, which reproduces
the exact panic without the guard (verified RED) and passes with it. It asserts
the no-panic / graceful-Err contract (CI has no Secret Service), which is the
behavior that regressed.

Signed-off-by: Michael Roy <michael.roy@amd.com>
After the #37 reroute, `rocm chat` runs the dash event loop. Two paths could
turn a clean quit into a non-zero exit once the terminal/PTY closed:

- `event_loop` mapped a crossterm `EventStream` error to `return Err(..)`. When
  the controlling terminal goes away (the PTY/stdin closes), that read error is
  not a failure — the session is over. Now it breaks cleanly, mirroring the
  existing `None => break` EOF arm and the legacy blocking reader's behavior.
- `app::run` tore down the terminal with `?` on every step. If the terminal is
  already gone, those writes can fail with a broken pipe and flip the exit code.
  Teardown is now best-effort so it never overrides the session result.

This surfaced as the acceptance PTY smoke (`rocm chat --provider openai` driven
by a finite input pipe) exiting non-zero on Linux but not Windows: only Linux
takes the `maybe_spawn_embedded_daemon` path, whose startup delay let the input
pipe close before the first key was read, so the event stream errored. With a
live daemon (or on Windows, where embedded-daemon spawn is a no-op) startup is
instant and the bug is masked.

Also dump the captured TUI log on smoke failure so a non-zero exit is
debuggable in CI instead of failing opaquely.

Signed-off-by: Michael Roy <michael.roy@amd.com>
…GPIPE

The TUI smoke drives `rocm chat` by piping `q`/`y` keystrokes into `script`.
After the #37 reroute the dash quits immediately on `q`, so the feeder's
trailing `printf 'y'` races with the child exiting and takes a broken pipe
(non-zero). Under `set -o pipefail` that poisoned the pipeline status and failed
the smoke even though `script -e` reported the dash exited 0 — masked previously
by the startup panic. The verified-clean-exit signal is `script`'s child code,
so drop `pipefail` for just this pipeline. Real TUI failures (non-zero child)
are still caught.

Signed-off-by: Michael Roy <michael.roy@amd.com>
@michaelroy-amd
michaelroy-amd added this pull request to the merge queue Jun 26, 2026
Merged via the queue into main with commit 7d81a7a Jun 26, 2026
8 checks passed
@michaelroy-amd
michaelroy-amd deleted the supergoal/phase-9-reroute-and-gate branch June 26, 2026 21:34
michaelroy-amd added a commit that referenced this pull request Jun 26, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants