Skip to content

Remove the Rust agent runtime from the TUI; keep /model via ACP config options - #7

Merged
zy1233 merged 54 commits into
mainfrom
codex/rust-agent-runtime-removal-plan
Oct 6, 2026
Merged

zy1233 merged 54 commits into
mainfrom
codex/rust-agent-runtime-removal-plan

Conversation

@zy1233

@zy1233 zy1233 commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Summary

Removes the in-process Rust agent runtime from the vendored TUI (tui/, binary zypi). The TUI is now a pure ACP client; the Python pi_agent_cli is the only agent. This clears the way for a future pi-rust agent, which only has to be a stdio ACP executable (PI_AGENT_COMMAND).

  • Runtime removal. pi-sampler, MvpAgent, the sampler/provider stack, the headless runner and the whole leader mode are gone (682 files in one compiler-verified commit).
  • /model is kept, through ACP Session Config Options: the Python agent advertises a category: "model" select option and routes session/set_config_option; the pager builds its model picker from it (legacy session/set_model stays as a fallback).
  • Stage 0 (Linux gate and baseline). tui-ci.yml + scripts/tui_baseline.py + manifest; enforced on Linux (toolchain pin, deny-list, dependency-graph ceiling, zero warnings, cargo check --workspace --tests, 8 test suites with floors). The report is docs/baselines/tui.md, now including the release-profile numbers.
  • Stage A (dead code first). The entry audit ran with the defaults in Appendix A of the plan (you approved "do not confirm item by item"): the UI entries that have no Python producer are deleted (dashboard, MCP, prompt queue / interject, subagents / workflows / goals, hooks / plugins, rewind / fork / jump / foreign sessions, feedback / consent / data sharing, changelog, session rename). The slash registry is down to 9 commands (settings, new, model, resume, theme, multiline, home, help, exit). The crate-root allow(unused, dead_code) attributes are gone and the consumer build has 0 warnings; unused dependencies and crates are deleted; two always-false worlds (chat_mode, cfg(feature = "local-workspace")) are removed; the welcome screen no longer offers the dead "New worktree" entry or advertises /feedback.
  • Stage 1, first slice (Python agent side).
    • session/list returns real titles (the first user message) and updated_at (file mtime); the resume picker used to show ISO timestamps. No cursor paging yet: the pager ignores nextCursor.
    • A stdio ACP contract suite (packages/pi-agent-cli/tests/test_acp_stdio_contract.py, runnable against another agent with PI_ACP_CONTRACT_COMMAND). It found that initialize advertised session/close and session/resume while the SDK answered "Method not found"; run_agent now runs with use_unstable_protocol=True.
    • Stopping the agent no longer orphans its tools. Measured: quitting zypi while the agent ran a bash call SIGKILLed the agent and the tool's process group kept running. stdin EOF already made the Python agent exit cleanly (exit 0, tools reaped) and SIGTERM did not (−15, tool left behind). Now SIGTERM and SIGHUP take the EOF path (SIGHUP is what the kernel sends to the foreground process group when the TUI, as session leader, is killed or the terminal closes), and the pager closes the agent's stdin, waits up to AGENT_EOF_GRACE (3 s), and only then kills it.
  • -p print mode refuses --sandbox (it used to run unsandboxed silently) and reports the flags it drops on stderr; the full flag contract is Stage 1 (1.P6).
  • Merged main. Another machine pushed 7 commits to main while this PR was open (the Phase 6/7 audit work: project trust, provider-scoped API keys, Model.reasoning, the workflow sandbox, queued prompts, 0.5.0). None of them touches tui/. Six files conflicted (17 hunks: agent.py, config.py, factory.py, __main__.py, pyproject.toml, packages/pi-agent-cli/AGENTS.md), all on the Python side, and both sides are kept. The seam: an api_key_env is the key of its own provider only, now for every /model choice too (api_key_getter(env_name, provider)); model_for_choice carries Model.reasoning, so switching model keeps it; _bind_session decides project trust first and then restores the saved model. One new test covers the seam. git show --remerge-diff 9b71a57 shows only what was resolved by hand.

Size: 1,485 files changed, +11,177 / −625,252 against main at 7e7d4d4. Tracked .rs lines under tui/: 1,650,814 → 1,049,333; .rs files 2,577 → 1,832; tui/Cargo.lock packages 1,311 → 1,257; codegen crates 74 → 65. Release-profile zypi: 422,034,120 B (−10.3 %), cold CI build 1,155 s (−29.9 %), --version median 23.1 ms (numbers at ac1d3ff).

Plan and execution record: docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md (r5, section 10.7 for this round). The merge of main is recorded in section 10.9, which lands with #8.

Review guide

54 commits (53 plus the merge of main). Most of the diff is deletion; the Rust commits are meant to be read one at a time and are mechanical (a compiler-driven cascade starting from one removed entry), so review them as "does it build, do the remaining tests pass, is what stays on the whitelist".

Commits What
9d3b4b3 Python only: model choices via ACP session config options. Independent of the rest.
9add266 The big runtime deletion + /model on the pager side.
bf4b79f, 0a081f4, 61126ed, cca48b0, d775a54, 259b056, 52a787f, 34fbd48, ea5a60d Plan r3 / r4 / r5 and the baseline report
3117858, e20af45, 56550ac, 27df705 Linux stage-0 gates and tooling; enforced; manifest ratcheted to the Linux numbers
a648e92, aeaf9b6, 0703d6a Leader UI residue, stale comments, agent-process lifecycle naming (behaviour unchanged)
0a58b81, 57c0d80 -p fails closed on --sandbox; changelog / release-notes removal
6983c99 ... 98e29c9, 7ff908b A.3: the entry audit deletions, in slices (slash commands, dashboard, tasks, subagents, queue, MCP, plugins, rewind / fork, hooks, rename)
5fcec14, ac1d3ff Always-false chat_mode world; dead worktree entries and the /feedback hint
4eb9f11, ab77740, fbcaf14, 886d5af, 86ea4c9, 0e71894, dbd4de6 A.4: crate-root allows, pub to pub(crate), unused dependencies and crates, local-workspace cfg
50ccfb1, 5163592, f14e321, 421d497, b617575 Python agent: session/list titles, stdio contract suite + use_unstable_protocol, SIGTERM / SIGHUP stop the agent like EOF, a repeated signal still kills (with the shutdown tests)
e8557e5 Rust: close the agent's stdin first, kill only after the grace (one file: acp/spawn.rs)
fe1fc65 rustfmt of the 216 files this branch touched. Skip it in review (git diff -w shows nothing).
9b71a57 Merge of main (Phase 6/7 audit work): the 6 conflicted files resolved by keeping both sides, plus one test for the /model seam. git show --remerge-diff 9b71a57 shows the hand-resolved part only.

Verification

  • Linux CI (ubuntu-24.04, rustc 1.94.0), TUI CI on b617575 (the last commit with code changes): PR run 37394965723 is green: dependency graph 980 packages (ceiling 980), 0 warnings, consumer build and cargo check --workspace --tests pass, 8 suites 7,439 passed / 0 failed / 20 ignored (enforced; the 2 more than at ac1d3ff are the unit tests of acp/spawn.rs). The manual release_baseline run 37389455793 on ac1d3ff is the source of the release-profile numbers. The docs-only head ea5a60d is green too: TUI CI run 37397452910, CI run 37397453697 (Ruff, gates + cargo check, cargo test, pytest on 3.11 / 3.12 / 3.13).
  • After the merge of main (head 9b71a57), Linux CI is green: TUI CI run 37436336738 (gates pass, 8 suites 7,439 passed / 0 failed / 20 ignored, the same as before the merge, as main touches nothing under tui/) and CI run 37436336636 (Ruff, pytest on Python 3.11 / 3.12 / 3.13). On macOS the full pytest run gives 1,999 passed and 6 failed; the same 6 fail on origin/main itself (7e7d4d4) and none fails on Linux CI. They are macOS-only: AGENTS.md and AGENTS.MD name the same file on a case-insensitive file system, so context_files.py loads it twice (a real bug since Phase 5, newly exposed by a test in main); macOS refuses a non-UTF-8 file name; and 4 workflow-sandbox tests fail because on macOS the sandbox reports no rlimit-as layer, a child process and a 1 GiB allocation are not stopped without the audit hook, and the child sees macOS's own __CF_USER_TEXT_ENCODING. I did not touch them. PTY smoke with the merged agent (mock LLM): boot, a turn, /model with two models, switching and back, /exit returning 0 with no leftover agent.
  • macOS, local (Homebrew cargo 1.96.1): pi-pager lib 5,307 passed / 0 failed / 13 ignored at ac1d3ff; settings_e2e 251; grok_home_paths 2; selection_model_public_api 2; --all-targets has 0 errors and 0 warnings. At e8557e5: cargo test -p pi-pager --lib acp::spawn 10 passed (2 new); the debug build has 0 warnings.
  • PTY smoke (debug zypi + the Python agent): boot (about 3 s to a ready session), command palette, /settings, /theme, /model, a turn, /exit and relaunch + /resume with history replay all work; /exit returns 0 and leaves no pi_agent_cli process behind. The later runs used the agent's mock LLM (PI_USE_MOCK=1): the free OpenRouter slug used earlier now answers 404. With a scripted agent running exec sleep 300: double Ctrl+Q exits zypi with 0 in 0.4 s and the old binary left the sleep running, the new one does not; kill -9 zypi (the agent then gets SIGHUP), kill -TERM zypi, closing the terminal and a plain Ctrl+C leave no sleep behind either. Full table in the plan, section 10.7.
  • Python: pytest -m "not real_llm" 621 passed (was 598 at r4: +11 session/list, +8 stdio contract, +4 shutdown); ruff check and ruff format --check pass. Linux CI (Python 3.11 to 3.13) on b617575: green, run 37394965713; it includes the stdio contract suite and all four shutdown tests. The shutdown tests were mutation-checked: without the SIGTERM handler that test fails with −15, without SIGHUP kill -9 zypi leaves the tool running, and without the handlers' self-removal the repeated-signal test times out.

Not verified

  • Windows, including pytest on Windows / WSL (waived for this push; the Python matrix on Linux covers 3.11 to 3.13). The shutdown tests are POSIX-only; on Windows the agent ends on stdin EOF only.
  • Exit behaviour on Linux end to end: the PTY checks ran on macOS only. Linux CI ran the Python shutdown tests and the Rust unit tests for stop_child, not a PTY smoke.
  • kill -9 zypi when zypi is not the session leader (the agent then only sees stdin EOF; covered by a test of the EOF path, not end to end), and MCP child processes.
  • Leftovers that are unreachable but still compiled: the CLI flags -w/--worktree, --worktree-ref, --restore-code, the /new worktree modes, the "remote restore" path and the welcome privacy banner (plan section 10.7, item 3). No UI entry reaches the worktree ones any more.
  • Known pre-existing failures and gaps are listed in plan section 10.4.

Next (follow-up work, not part of this PR)

  • Stage 1: cursor paging for session/list (the pager has to follow nextCursor first), the -p flag contract (1.P6), ADR1 (the pager stops reading session files from disk), stderr destination of the agent.
  • Branding: the user-visible Grok strings (/theme names, desktop notification title, zypi doctor, the disk-usage hint) are being renamed in a separate PR stacked on this one.
  • Stage C: user-guide rewrite, tui/NOTICE and third-party notices.
  • Optional Stage D: slimming the dependency graph further (async-openai out of the tree, session data layer, xAI auth layer).

zy1233 and others added 30 commits October 4, 2026 22:20
…tions

Publish a select config option (id "model", category "model") in the session/new, session/load and session/resume responses and route session/set_config_option to switch the active model. Choices come from the optional [[models]] tables in agent.toml (see agent.example.toml); the restored choice is derived from the session's recorded model. This is the standard ACP counterpart of the TUI /model picker, replacing the removed Rust runtime's model switching.

Tests: +3 in test_acp_agent.py (advertise, switch, restore on load), +2 in test_config.py (parsing/defaults).
Co-authored-by: Cursor <[email protected]>
…CP config options

zypi is now purely an ACP client. The agent (Python pi_agent_cli today, a future pi-rust) runs as a separate process behind standard ACP (stdio JSON-RPC); nothing in the TUI process can create an agent or call a model provider any more.

Removed: MvpAgent and the ACP server implementation in pi-shell, the pi-sampler crate (and 25 third-party crates only it used), headless mode, the leader process/protocol/cluster, the mcp/memory/plugin/sessions/share/trace/worktree/models subcommands, and the tests and helpers that only exercised them. pi-shell shrinks from ~404k to ~66k lines; the workspace loses ~366k lines of Rust (Cargo.lock 1311 -> 1285 packages).

Kept: pi-tools, pi-workspace, pi-agent and the rest of the non-runtime crates. pi_shell::sampling stays as a thin re-export of pi-sampling-types.

/model is restored through ACP Session Config Options: pi-acp-lib gains a typed SetSessionConfigOption request, and the pager builds the model catalogue from the agent's category=model config option and switches with session/set_config_option (session/set_model remains only as a fallback when the agent does not advertise the option).

Verified on macOS: cargo check (pager-bin, pager-minimal, update; workspace --tests), pi-shell lib 1136 passed, pi-pager lib 8606 passed, and a real zypi <-> pi_agent_cli <-> OpenRouter session in a PTY including a /model switch. Not verified: Linux/Windows cfg code. Known failures unrelated to this change are listed in docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md section 10.4.

Co-authored-by: Cursor <[email protected]>
Revised plan for removing the Rust agent runtime from the TUI (target architecture, ADRs, staged plan) plus section 10: what was executed, how /model was restored through ACP Session Config Options, verification results, known unrelated failures and remaining work. Also annotates the Phase 4 spec: /model has a standard ACP counterpart and is kept.

Co-authored-by: Cursor <[email protected]>
Plan stage 0 (0.1 / 0.2 / 0.4) of PLAN-RUST-AGENT-RUNTIME-REMOVAL.md.

- scripts/tui_baseline.py + scripts/tui_baseline.toml: one executor (gates | check | test |
  release) driven by a manifest. Blocking gates: toolchain pin == tui/rust-toolchain.toml,
  pi-sampler absent from Cargo.lock and the graph, no MvpAgent / acp_session_impl /
  SamplerActor under tui/crates, unique-package ceiling for pi-pager-bin on linux-gnu,
  consumer `cargo check --locked`, release binary not above the v0.4.0 size. Report-only
  until the first Linux run is triaged: 8 test suites, `cargo check --workspace --tests`,
  warning count. Ratchet semantics (ceilings only go down, floors only go up).
- .github/workflows/tui-ci.yml: `check` and `test` on ubuntu-24.04 (pinned toolchain,
  rust-cache, protoc), plus a manual `release-baseline` job; results go to the job summary and
  to artifacts.
- docs/baselines/tui.md: pre-removal (v0.4.0) vs post-removal (macOS) numbers, test table with
  the reason for every known failure / skip, deny-list with keep-or-shrink reasons, and the
  steps to finish once the Linux run exists.
- scripts/tests/test_tui_baseline.py (18 tests) is collected by pytest via `testpaths`.

The Linux job has not been run yet; the numbers in the report are macOS measurements.

Co-authored-by: Cursor <[email protected]>
Plan A.2 tail. The leader process, protocol and CLI flags were removed with the Rust runtime;
this removes what the pager still carried for them. Production behaviour is unchanged:
`leader_mode` and `reconnect_pending` were constant false, and the follow-up routing is now
exactly `[ui].follow_up_behavior = "steer"`.

pi-pager
- AppView: drop `leader_mode`, `leader_roster`, `reconnect_pending`, the roster upsert/remove
  helpers, `Effect::FetchRoster` and `TaskResult::Roster{Loaded,Failed}`. The dashboard roster is
  `dashboard_local_sessions` only (polled while open); `roster.rs` keeps just the row types.
- `immediate_server_send_eligible(agent)` loses its `leader_mode` argument; `handle_sessions_changed`,
  the reconnect guards in the drain paths and the `RestartSharedLeader` hint are gone.
- Startup failure screen is single-agent: no attempt counter or "longest step", no leader row, no
  `zypi leader kill` advice. `bounded_connect` and `ConnectFailure` shrink accordingly.
- Dashboard / queue / prompt doc comments and the embedded user guide no longer describe leader mode.
- Tests: fixtures use the production default (Queue); the 15 tests that cover the server-
  authoritative immediate send opt in with `SteerFollowUp` (agent_view::test_fixtures).
  Removed tests that exercised the reconnect guard; the announcements watermark test no longer
  simulates a reconnect reset that no longer exists.

other crates
- pi-telemetry: remove `StartupPhase::LeaderConnect` and `AgentKind::Leader`; `DashboardOpened`
  reports `{ agents, subagents }`. The process-identity vocabulary (`Entrypoint::Leader`,
  `LeaderMode`, `is_leader_mode`) is kept: it is a telemetry schema, not UI state.
- pi-config-types / pi-shell: remove `RemoteSettings.leader_mode`, `[cli].use_leader` and
  `AgentMode::Leader`. Old config files and server payloads that still carry the keys are
  ignored (no `deny_unknown_fields`; `merge_section` keeps unmodelled keys; covered by tests).
- pi-http: drop the unused `MIN_CLIENT_CONNECT_TIMEOUT`.
- pi-test-support: delete the leader harness and the UDS fault proxy (no users left) together
  with their dependencies (clap, tokio-tungstenite, tracing-subscriber).

Verification (macOS): `cargo check --workspace --tests` (0 errors), consumer build (20 warnings,
unchanged), pi-pager lib + 3 integration targets 8,870 passed / 2 known failures, pi-shell 1,140,
pi-telemetry 244, pi-http 13, pi-acp-lib 21. Totals are 18 below the previous baseline because
tests of the deleted code went with it; scripts/tui_baseline.toml `reference_passed` is updated.
`rg -i leader --glob '*.rs' tui/crates` drops from 684 lines / 121 files to 279 lines / 84 files;
what is left is process-group "leader", update convergence, telemetry identity, config watcher
and the multi-client shared-session protocol (ADR4 / A.3).

Co-authored-by: Cursor <[email protected]>
…onstant

Follow-up to the leader UI residue cleanup. These still described the removed leader:

- pi-acp-lib/normalize.rs claimed a leader socket and a leader-bridge replay sniff in
  pi-pager-bin that no longer exist. Nothing in the workspace calls spawn_stdin_line_reader
  any more, so the module doc now says so and leaves the decision to plan A.3.
- pi-status-line: the doc of CLIENT_STATUS_LINE_META said a leader injects it. Nothing reads or
  writes it now; doc only, the public constants stay.
- pi-http: delete STARTUP_AUTH_TIMEOUT, the boot-auth cap of the leader process. It has no
  references anywhere in the repository.

No behaviour change. `rg -i leader --glob '*.rs' tui/crates`: 279 lines / 84 files -> 273 / 80.

Co-authored-by: Cursor <[email protected]>
Plan 1.R3, naming and wording only. The pager's agent lifecycle still used the vocabulary of the
removed in-process runtime ("session actors flush SessionEnd teardown"); the agent is a child
process that the stdio bridge kills and reaps.

Renames (pi-pager)
- SpawnedAgent -> AgentProcess (thread_handle -> bridge_thread)
- AgentShutdownGuard -> AgentProcessGuard
- spawn_grok_shell -> spawn_agent_process (drops the unused _memory_config parameter)
- AcpConnection.agent_thread -> bridge_thread
- SESSION_FLUSH_GRACE / AGENT_JOIN_SLACK -> AGENT_EXIT_GRACE / BRIDGE_JOIN_SLACK
  (still 10 s + 2 s; the 20 s budget in app::exit_timeout is unchanged)
- join_agent_thread -> join_bridge_thread

Wording
- The slow-exit notice reads "Stopping agent…" instead of "Finishing session…", and the timeout
  warning no longer claims SessionEnd teardown may be incomplete; it says the agent process may
  still be running.
- Docs describe what the code does: cancel -> SIGKILL -> reap, the guard is held by app::run
  only, the child inherits stderr.

Behaviour is unchanged: same timeouts, same kill/reap path. Not done, and recorded in the plan
under 1.R3: where the child's stderr goes, a graceful exit on stdin EOF (1.P2), and an experiment
on leftover bash/MCP grandchildren after SIGKILL / Ctrl-C / kill -9.

Verification (macOS): tui_baseline.py check (consumer build, 20 warnings as before,
`cargo check --workspace --tests` 0 errors) and tui_baseline.py test (8 suites, 10,839 passed /
2 known failures / 80 ignored).

Co-authored-by: Cursor <[email protected]>
…r the leader cleanup

Plan (docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md)
- Status r4 with the list of local commits; nothing is pushed.
- Stage 0: 0.4 (deny-list) done; 0.1/0.2 stay open until the workflow has run on Linux.
- A.2 tail done. 1.R3: naming and wording done; the open items are spelled out (where the
  child's stderr goes, graceful exit 1.P2, leftover bash/MCP grandchildren after a kill).
- Section 10.4: leader residue rewritten. 273 lines / 80 files still say "leader"; none of it
  is UI state (shared-session protocol vocabulary awaiting ADR4/A.3, process-group leaders,
  update convergence, telemetry identity). Suggested order of next steps updated.
- New section 10.6: what r4 changed and how it was verified (macOS only); revision log r4.

Baseline report (docs/baselines/tui.md)
- Test counts after the leader cleanup: pi-shell 1,140, pi-pager 8,870, total 10,839. That is
  18 fewer than first measured because the tests of the deleted code went with it.
- Line count after the cleanup (1,252,374); dependency graph (995), warnings (20) and
  `cargo check --workspace --tests` (0 errors) re-measured and unchanged.

Co-authored-by: Cursor <[email protected]>
An earlier exploratory `cargo tree --no-dedupe` for native -sys crates never finished, so the
question "does the Linux job need apt packages?" was left implicit. Answer it with the plain
(deduplicated) tree, which has the same 995 packages the gates count: the -sys crates are
bundled or fall back to a source build, and there is no alsa/openssl/udev/dbus -sys crate, so
the workflow installing only protoc (like release.yml, which built v0.4.0 on ubuntu-24.04) is
expected to be enough. Noted that this covers the pi-pager-bin graph only, and what to do if
the test job fails to link.

Co-authored-by: Cursor <[email protected]>
… the default text encoding

On Windows (and with LC_ALL=C elsewhere) Python's default text encoding is not UTF-8. The tool
wrote its Markdown report, which contains an em dash and status emoji, with the default encoding
and crashed at the end of every run with UnicodeEncodeError. The tests read the vendored
Cargo.toml files (one has a non-ASCII description) the same way and failed on the first of them.
That would break the cross-platform pytest run the repository requires before a push.

- Read and write every file with encoding="utf-8"; decode cargo/rustc child output as UTF-8.
- Reconfigure stdout/stderr to UTF-8 (errors=replace) at startup.
- New test: publish() in a child interpreter with PYTHONUTF8=0, LC_ALL=C and
  PYTHONCOERCECLOCALE=0, which reproduces the legacy-codec behaviour on POSIX.

Reproduced before the fix with those settings (`tui_baseline.py gates` crashed in publish(), the
manifest test failed on a Cargo.toml). After it, 19 tests pass in both modes and `gates` exits 0.
ruff check and ruff format --check pass for the whole repository. Not run on a real Windows host.
The baseline report no longer claims the script was verified on every platform.

Co-authored-by: Cursor <[email protected]>
… it drops

`zypi -p` is handed to the Python agent in main(), before async_main(), which is where the OS
sandbox is applied. A sandbox that was asked for (--sandbox, GROK_SANDBOX, [sandbox].profile in
config.toml, or a deployment requirement) was therefore silently skipped, and every flag the agent
does not receive was silently ignored. ADR7 of the plan does not allow running unsandboxed without
saying so.

- pi-shell: requested_sandbox_profile() resolves the profile exactly like apply_sandbox()
  (requirement > --sandbox > GROK_SANDBOX > [sandbox].profile) without applying anything.
- pi-pager-bin: -p exits with status 2 and an explanation when that profile is not `off`. Passing
  --sandbox off is the way out, unless a deployment requirement fixes the profile.
- pi-pager-bin: the 24 user-facing flags that are not forwarded to the agent (--model,
  --output-format, --always-approve, --allow, --resume, ...) each print one warning on stderr.
  The full flag contract stays plan item 1.P6.

Tests: 3 in pi-shell (which layer wins, explicit off, requirement cannot be lifted), 6 in
pi-pager-bin. Also run against a debug zypi with PI_AGENT_COMMAND=/bin/echo: --sandbox workspace,
GROK_SANDBOX=strict and a config.toml profile exit 2 without starting the agent; --sandbox off
starts it and prints the warnings.

Co-authored-by: Cursor <[email protected]>
….ai fetch

The welcome screen fetched https://x.ai/cli/changelogs/<version>.external.{md,json} at
every startup (cached under $GROK_HOME, offline switch GROK_CHANGELOG_OFFLINE) to draw a
"Changelog" block, a "Changelog" menu row and the /release-notes command. zypi is not
that CLI, and a phone-home to a third-party host at startup is not something a local
coding agent should do. A PTY probe with a logging proxy used to show `CONNECT x.ai:443`
right after launch; after this change nothing reaches the proxy in 45 seconds.

Removed:
- pi-shell-base `util::changelog` (ChangelogManager, cache, parsing) and the
  `Effect::FetchChangelog` / `TaskResult::ChangelogFetched` plumbing,
- the welcome "Changelog" block (hero box and stacked layout), the menu row and its
  hit-testing/hover state, `/release-notes` (+ `/changelog` alias),
- the unreferenced upstream release-note data under pi-shell (CHANGELOG.md and
  changelogs/*.{md,json}, 1 MB, versions 0.2.0 - 1.0.8) and the offline-only scenario
  release_notes_scroll.yaml.

`Action::ShowReleaseNotes` doubled as the generic markdown viewer for /docs, so it is kept
as `Action::ShowDoc`. The welcome layout's "changelog" slot was shared with remote
announcements; it is now the announcement slot (`WelcomeLayout::info`,
`effective_info`), and the layout tests that used a fixed changelog height now use an
announcement (stacked-slot boundary, tip squeeze, compact, hero/stacked exclusivity).

Verified: cargo check --workspace --tests (minus the already-broken pi-fast-worktree),
pi-pager 8,581 lib tests (-8 net), pi-shell, pi-pager-bin; PTY probe shows the welcome
screen without the Changelog row and zero outbound requests.
TUI CI run 37263077643 (ubuntu-24.04, rust 1.94.0, PR #7 at e20af45): gates pass,
dependency graph 995 packages, consumer build 20 dead_code warnings, `cargo check
--workspace --tests` 0 errors, 8 suites 10,851 passed / 0 failed / 75 ignored.

Following docs/baselines/tui.md section 5:
- [test] enforce = true and [check] enforce_workspace_tests = true,
- max_warnings = 20, graph ceiling 995 (was the 1000 placeholder),
- reference_passed refreshed to the macOS numbers at this tip,
- unset_env also clears PI_HOME / GROK_HOME / PI_AGENT_COMMAND / PI_PYTHON / GROK_SANDBOX:
  a leftover PI_HOME (it outranks the GROK_HOME the config-isolation tests set) made
  test_config_update_isolation fail on a developer machine only,
- the report gets a Linux column, per-suite timings and the corrected status.

min_passed floors stay unset until the entry-audit deletions (plan A.3 / A.4) have landed,
because deleting dead code legitimately deletes its tests. The release baseline (size, cold
build time, --version latency) is still to be run, after the last push.
…reachable

The registry filtered `builtin_commands()` at runtime down to nine names
(`PI_STANDARD_SLASH_NAMES`); the other ~60 command modules compiled but could not be
listed, completed or dispatched. This turns the runtime filter into a deletion (plan A.3,
first slice).

Kept: new, resume, quit (exit), help, theme, settings, multiline, model, home, plus
`effort_levels` (helper of /model). Deleted: 57 modules and `workflow_tests.rs`
(9.1k lines): always-approve, announcements, auto, btw, cd, compact, compact-mode,
config-agents, context, copy, dashboard, debug, delete, docs, doctor, edit-prompt, effort,
expand, export, feedback, find, fork, gboom, history, imagine, imagine-video,
import-claude, jump, login, logout, loop, mcps, personas, plan, plugin (hooks / plugins /
marketplace / skills), privacy, queue, recap, remember, rename, rewind, minimal/fullscreen
switchers, scroll-debug, session-info, share, tasks, timeline, timestamps, mouse
reporting, transcript, tutorial, usage, view-plan, vim-mode, voice, workflow(s).

Registry: the `pi_standard_slash` switch and its three callers are gone, because the
builtin set is the whitelist now; a test pins `builtin_commands()` to
`PI_STANDARD_SLASH_NAMES` (test-only constant) and another asserts the removed names do not
resolve. The command palette is rebuilt from the rows that survived the old runtime filter
(New Session, Resume, Model, Multiline, Theme, Settings, Shortcuts, Quit); seven dead
`PaletteCommand` variants and their handlers are removed.

Helpers that other code still needed move to their callers instead of keeping a command
module alive: `ForkArgs` (app::actions, until the fork flow itself goes), the doctor
report builder (dispatch::prompt) and `detect_external_auth_provider` (event_loop). Those
three are the entry points the next slices (fork, doctor, account/tier) will remove.

Tests: tests that exercised deleted commands are deleted or pointed at a kept command
(/model, /theme, /settings); the pinned mode-refusal list now has only /theme; the
dashboard-surface slash tests go with the dashboard in the next commit.

Verified (no NO_COLOR/FORCE_COLOR): pi-pager lib 8383 passed / 2 failed (the two known
Alt/Opt footer tests), settings_e2e 276 passed, other integration targets ok.
The multi-session dashboard was the leader/roster front end: with a single
agent process per `zypi` there is nothing to list, peek into or dispatch to.
Entry audit (docs/PLAN/PLAN-RUST-AGENT-RUNTIME-REMOVAL.md, Appendix A):
delete.

- views/dashboard/, dispatch/dashboard.rs, dashboard_telemetry.rs, roster.rs
  and the dashboard dispatch tests are gone.
- `ActiveView` is now `{ Welcome, Agent(AgentId) }`; `VoiceTarget` has a
  single variant; `AgentView::draw` loses the overlay parameters;
  `SlashCommand::{session_scoped, offered_when_session_less, dashboard_only}`
  and `SlashController::hide_session_scoped` are removed;
  `command_offered` takes two arguments.
- Actions, keymap entries, shortcut-help rows, startup flags
  (`deferred_startup.open_dashboard`), telemetry events and config keys that
  only served the dashboard are removed. The queue-drain helper is renamed
  `drain_queue_for_agent`.
- 112 dashboard/overlay-only tests deleted; the rest were rewritten to use
  `Welcome` / `Agent` views. Two scenario YAMLs and the `23-dashboard.md`
  embedded guide are removed.

pi-pager lib tests: 7601 passed / 0 failed / 56 ignored (macOS, 2 threads).
…llow

Restore point for the dead-code cascade (A.4). 62 modules stay `pub` (the
ones pi-pager-minimal, pi-pager-bin, the integration tests, benches, examples
and playground bins import); the other 151 are `pub(crate)`. The lib now
compiles with warnings visible (to be cleared by the following commits).
…vealed

With the crate-root `#![allow(dead_code, unused_*)]` gone and modules narrowed to
`pub(crate)`, rustc now reports what nothing reaches. This commit deletes it, driven
by the compiler's diagnostics and repeated to a fixed point:

- legacy ext-notification / ext-method handlers (follow_ups, settings, queue, MCP,
  interjection, plan mode, ...) that only a `#[cfg(test)]` dispatcher still routed,
  together with that dispatcher and the tests that exercised it
- unused slash plumbing left behind by the deleted commands (`CommandResult` variants,
  `usage()`, `ModeSupport::MinimalOnly`, `SlashController`'s cwd, ...)
- empty / vestigial modules: `tool_usage`, `diagnostics::doctor_format`, four
  `acp_handler` submodules
- unused parameters and variables

Vendor `x.ai/` ext methods are now ignored unconditionally (no `cfg!(test)` bypass).
pi-pager lib tests: 7069 passed, 0 failed, 51 ignored.
…askResult

Temporarily narrowing the three action enums to pub(crate) let rustc report
every never-constructed variant and never-read field once the slash modules,
the dashboard and the legacy ext dispatchers were gone. The cascade removed
~11.8k lines: dead variants and their handlers, now-unused imports and
helpers, and the tests that only exercised those paths.

Also deletes 32 tests that were already #[ignore]d as "grok-specific
feature not supported" and target removed features (plan, compact,
queue-edit-into-builtin, hooks, voice, remote restore, plugin CTA), plus 7
tests (plugin CTA, coding-data-sharing) whose effect handlers the cascade
removed. Visibility of app::actions is restored to pub because
tests/settings_e2e.rs consumes Action.
…xtensions/rewind (A.3-3 part 1)

Remove the keyboard entry points whose backing features are being stripped
from the Rust client (ToggleTasks, SendToBackground, InterjectPrompt,
OpenExtensions, Rewind, KillBgTask, ExitSession, NewSessionInWorktree):
ActionId variants, default ActionDefs, input routing, hints, test-only
registries, and the tests that exercised the deleted chords. Ctrl+G stays the
external-editor chord in minimal mode only; restored/rewritten the tests that
covered still-live behaviour (minimal registry surfaces, Ctrl+G mode split,
shortcuts-help Ctrl+G rows, minimal palette registry).

pi-pager lib tests: 6814 passed / 0 failed / 13 ignored.
…3 part 2)

Remove the hooks / plugins / marketplace / skills / workflows / MCP-servers
extensions modal, the /config-agents agents modal and the persona detail view,
together with everything that only existed to drive them:

- views: extensions_modal (+ workflows_picker_rows), agents_modal,
  persona_detail; agent_view/modals.rs (key/mouse/paste handlers)
- Action/Effect/TaskResult variants for hooks/plugins/marketplace/MCP/skills
  management and their stub effect handlers, router arms and task-result
  handlers; ConfigFile editor requests; OpenManagedConnectors
- ext-notification arms for HooksChanged / PluginsChanged, the workflow
  catalog refresh plumbing and the deferred extensions-fetch flag
- pi-pager-minimal: the /mcps below-prompt panel and the extensions-modal live
  render (the /resume panel stays); minimal_api accessors for the modal

Tests that only exercised those surfaces are deleted with them; the 247
removed tests all belong to the removed modules (checked name by name).
`Action`/`Effect`/`TaskResult` stay `pub` because tests/settings_e2e.rs uses
them. pi-pager lib tests: 6557 passed / 0 failed / 13 ignored.
…(A.3-3 3b-1)

Remove the tasks pane, bg-task and scheduled-task state, the kill/demote/
cancel actions and effects, the bg-task block, viewer and status chips, the
background and scheduled-task ACP handlers, and the subagent lifecycle arms
that depended on them. Keep the bash highlighter (views/bash_highlight.rs)
that lived in the tasks pane. Adapt pi-pager-minimal's commit frontier and
drop tests that only exercised deleted features.
…e (A.3-3 3b-2)

Remove the subagent transcript views, workflow runs overlay, goal detail/status
UI, subagent catalog pane and child-session routing. Notifications now route to
the root agent only (find_session_match returns an AgentId); the turn-status,
timeline rail, send-now and cancel paths lose their subagent/goal conditions.

Restored live-feature tests that the cascade dropped with the subagent fixtures
(bind_session_id cursor reset, resize tip gate, reseed round trip, resume focus).
…bagent wait plumbing (A.3-3 3b-3)

- remove the cancel-turn subagent choice panel (BlockingCard::CancelTurn,
  EscStep::KeepRunning, Action::CancelTurnChoice, minimal Modal::Cancel),
  the cancel_subagents preference/wire meta and its setting handling
- remove turn-status Watchers / bg button / still-running watcher cue,
  has_running_execute, and the related hit areas and hover plumbing
- drop VerbGroupKind::Subagent, BgTaskState/BgTaskStatus/ScheduledTaskInfo
  and other dead bg-task state, plus the vestigial wire-compat tests
- tests: 6071 pi-pager lib / 80 pi-pager-minimal pass; 39 tests removed,
  all tied to the deleted behaviour
…d steer (A.3-3 3c)

The Python agent keeps no server-side prompt queue, never adopts a running
prompt from another client and never emits interjection notifications, so the
server-authoritative half of the queue machinery was dead by data.

Removed:
- shared_prompt_queues, optimistic echoes, the merged queue view, queue
  mutation RPCs (remove / edit / reorder), send-now / interject / force-interject
  actions, effects and key chords, and the parked-wait look in the turn status;
- the [ui].follow_up_behavior (queue | steer) setting and the
  contextual_hints.send_now tip, including their cross-crate plumbing
  (pi-shared, pi-config-types, pi-shell, pi-telemetry, pi-pager-render).
  Old config files that still contain these keys keep parsing: the config
  structs do not deny unknown fields.

Kept: the local drip-feed queue (pending_prompts, QueueEntryKind, drain,
queue pane delete / reorder / edit, combined bubbles), which is what the
Python agent actually relies on while a turn is running.

Tests: removed the tests of the deleted behaviour, ported the ones that
describe retained local-queue behaviour (drain, FIFO, combined bubbles,
notification suppression, stale prompt-RPC error gate, queue pane keys and
mouse) and added bash_while_running_is_queued_locally.

Verified locally under low resources (jobs=2, test-threads=2):
pi-pager lib 5960 passed / 0 failed / 13 ignored; pi-pager-minimal 80;
pi-shell lib 1138; pi-shared 101; pi-telemetry 244; pi-config-types 94;
pi-pager-render 1084 (+ tmux_probe timing test passes in isolation).
cargo check --all-targets for pi-pager and pi-pager-minimal is clean.
…metry stubs (A.3-3 3d-1)

The inline plugin-install CTA (keyword-matched marketplace upsell above the
prompt) and its marketplace-update check had no producer after the Rust agent
runtime removal; every Effect was executed by a stub. Remove:

- pi-pager: dispatch/cta.rs, tests/cta_e2e.rs, the CTA Effect/TaskResult
  variants, CtaPhase/PluginCtaState and layout row, event-loop marketplace
  override readers, their tests
- pi-shell: dismissed-plugin-CTA config helpers, MarketplaceConfig.plugin_cta_marketplace
- pi-config-types: RemoteSettings.plugin_cta
- pi-telemetry: PluginCta* events
- pi-pager: drop the unused pi-plugin-marketplace dependency

Lib tests: pi-pager 5882 passed / 0 failed; settings_e2e 266 passed.
…and Claude-import flow (A.3-3 3d-2)

All four had no producer after the Rust agent runtime removal:

- views/mcps_modal.rs: unreferenced outside its own module
- McpInitProgress: seeded on every new session and only cleared by an
  mcp_initialized notification the Python agent never sends, so the idle row
  showed 'Starting session...' for 30 s and kept the tick loop on the fast path
- MCP elicitation card (views/elicitation_view, agent_view/elicitation.rs,
  BlockingCard::McpElicitation): no code ever set elicitation_view /
  pending_elicitation outside tests
- Claude-settings import (import_claude_modal, dispatch/import_claude.rs,
  welcome menu row, Action::{ImportClaudeSettings,ImportClaudeConfirm,
  ImportClaudeCancel,DismissClaudeImport}): has_claude_import was never set

Also restores the AgentView doc comment that had been glued to McpInitProgress.

Tests: pi-pager lib 5809 passed / 0 failed; settings_e2e 266; pi-pager-minimal 80.
Lost tests (73) all belong to the deleted features.
…ng-data-sharing (A.3-3 3d-3a)

Compiler-driven removal of ACP ext calls the Python agent never serves:
info/compact/history/agent-name/bundle/usage-modal effects, shell and
prompt suggestions, feedback pane and trace offer, remember note, recap,
doctor-fix, consent notice, privacy/announcement plumbing in the effect
layer, and the coding_data_sharing setting (Privacy category, lock
plumbing, SetCodingDataSharing).

Tests: lib 5621 passed, settings_e2e 253 passed (low-resource run).
…ssion-picker source machinery (A.3-3 3d-3b/c)

The Python agent advertises no rewind, fork, jump or foreign-session
capability and every session it lists has source "local", so all of the
following were dead by data (not by rustc): rewind/jump/memory modals and
their dispatch, fork and foreign-session resume (with the pi-foreign-sessions
scan, compat and launch plumbing), inline prompt edit, content / deep search,
the source filter, lanes and card-detail fetches of the session picker, and
the restore-progress channel.

- Session picker: one FetchSessionList { seq } -> SessionListLoaded /
  SessionListFailed round trip, single-variant PickerItem, stale-seq drop,
  "No sessions found for this directory" / "Couldn't load sessions: ..."
  toasts; filter indicator and header notes removed from the shared picker.
- Settings: ui.confirm_before_rewind removed end to end.
- Billing: Effect::FetchBilling / TaskResult::BillingFetched lose the nonce;
  the handler stays a stub until the xAI auth layer goes (Stage D).
- pi-pager-minimal: follows the deleted Remember mode and /feedback card
  (placeholder override, feedback editor, question-view feedback_input).
- pi-pager-bin: print mode no longer lists the removed --fork-session flag.
- Tests: recovered the live-feature tests (picker delete, navigation,
  stale seq, list toasts, tick demand, expand toggle) in adapted form and
  rewrote the Esc tests for the removed rewind arm; dead-feature tests are
  deleted.

Verified under low resources (CARGO_BUILD_JOBS=2, CARGO_INCREMENTAL=0,
--test-threads=2): pi-pager --lib 5393 passed / 0 failed / 13 ignored,
settings_e2e 251 passed; cargo check --all-targets is clean for pi-pager,
pi-pager-bin and pi-pager-minimal (pi-shell keeps its 20 dead_code warnings).
pi-foreign-sessions (foreign coding-agent session scan; its last user went
with the foreign-resume flow), pi-agent-lifecycle, pi-plugin-marketplace,
pi-session-search and pi-shell-session-support have no dependents in the
workspace and are not reachable from pi-pager-bin. pi-file-utils is dropped
from pi-pager's direct dependencies (unreferenced there).

The workspace goes from 90 to 85 members; Cargo.lock shrinks accordingly.
…h and the pi-hooks-plugins-types crate (A.3-3 3d-5)

The Python agent emits no hook/plugin notifications and the /hooks and
/plugins commands are already gone, so everything that rendered or
routed them was dead-by-data:

- pi-shell: SessionUpdate::{HookAnnotation, HookExecution, HooksChanged,
  PluginsChanged, PluginUpdatesInstalled} + their DTOs, the
  session::commands husk (HOOK_DENIED_CATEGORY)
- pi-pager scrollback: ScrollbackEntry.hook_data, tool/hook.rs,
  tool/lifecycle.rs (ToolCallBlock::Lifecycle), verb-group hook counts,
  hook hover popup, stop-hook folding into turn markers
  (SessionEventBlock.stop_hooks/prompt_id, TurnBlockedByHook,
  HookAnnotation), PendingStopHooks stash, the cancellationCategory
  plumbing (TerminalSignal / PendingTurnEnd / cancelled_turn_event),
  push_turn_terminal_marker's ending-prompt-id argument
- ToolCallBlock::label_kind() is now total (VerbGroupKind) and
  verb_group_kind() derives from it
- CommandRegistry::set_plugins_visible + appearance.disable_plugins
  (the commands it toggled are not registered)
- docs: hooks/plugins guides (09-plugins, 10-hooks, hooks-and-plugins,
  custom-hooks) and their docs.rs / README entries
- workspace: pi-hooks-plugins-types is now orphaned and deleted; pi-pager
  drops the dependency (85 -> 84 members)

40 tests removed with the code they covered (stop-hook stash/merge,
HookDenied marker, hook aggregation in verb-group headers); one rewritten
(estimate_uses_foldability_for_collapsed_shortcut) and one added
(no_event_pushes_no_marker).

Verified (low-resource): pi-pager --lib 5353 passed / 0 failed / 13
ignored, --test settings_e2e 251 passed; cargo check --all-targets clean
for pi-pager, pi-pager-bin and pi-pager-minimal.
zy1233 added 22 commits October 6, 2026 04:44
… and the dead code they hid (A.4-1)

Nine crates carried #![allow(unused_imports, unused_variables, unused_mut,
unreachable_code, dead_code)] at the crate root. Removing them shows 9 dead
functions in pi-workspace (fs_notify adapters, hub_transfer_client) and nothing
elsewhere; the functions and the tests that only exercised them are deleted.
File-level allow(dead_code) in pi-workspace is gone as well.
…behind them (A.4-2)

The crate-root allow(dead_code) attributes are gone (4eb9f11), but rustc
still cannot see dead items that are reachable through a `pub mod`. Lower
pi-shell's module tree to `pub(crate)` (compile-driven: modules the
consumers need are restored on E0603), then let the warnings drive the
deletion of what is left:

- 12 husk modules (session_registry_client, extensions/{billing,mcp,
  session_search,task}, session/{mcp_dispatcher,mcp_dispatcher_e2e_tests,
  mcp_restart,persistence_tests,restore_stub,memory}, resolve/system_prompt),
  config/watcher.rs and claude_import_state.rs
- BackendClient husk and the remote SessionUpdate DTO, ConsumerKind and
  RequirementSource variants nobody builds, SessionDirMode (only one mode
  is ever constructed), the SyncOutcome/GoalPauseReason leftovers, the
  replay-preparation passes whose outputs nobody reads
- the tests that only exercised the deleted code (checked with vac2/empties:
  no vacuous or empty test is left behind)

`cargo check` of pi-pager, pi-pager-bin, pi-pager-minimal, pi-update and
pi-shell with --all-targets reports 0 errors and 0 warnings. pi-shell lib
tests: 1052 passed, 0 failed, 1 ignored; sandbox_requirements_pin and
test_config_update_isolation pass.
Found with `-W unused-crate-dependencies` over `cargo check --workspace
--tests` (separate target dir, JSON diagnostics attributed per unit), then
cross-checked against the sources for cfg-gated uses. A dependency was only
removed when it is unused in every unit of its package and its name does not
appear in any source file; optional dependencies that no feature enables went
the same way. Kept on purpose: the `cargo-shear` ignores (`pi-ratatui-inline`
in pi-pager-minimal, `tokio-tungstenite` in pi-workspace, `serde` / `tonic` in
pi-tools-api), `zstd` (behind pi-workspace's `compression` feature) and the
Linux / macOS-x86_64 / Windows-only uses (`nix`, `libc`, `pi-sandbox`,
`pi-tty-utils`, `image`).

- pi-env: tracing, url
- pi-file-utils: dunce, indexmap
- pi-pager: obfstr, together with the dead `obf` module (its only macro had
  no caller left)
- pi-shell: notify, notify-debouncer-mini, pi-mcp, pi-memory (also dev),
  sha2, plus the never-enabled optional tonic / tonic-prost / prost and the
  windows-only siphasher
- pi-shell-base: anyhow, pi-extra-ca, pi-version
- pi-telemetry: pi-env, obfstr (optional, never enabled)
- pi-tools: pi-env, pi-workspace-types, tonic, and the never-enabled optional
  encoding_rs, kamadak-exif, unicode-normalization
- pi-workspace: humantime-serde, pi-env, pi-fsnotify, tonic, the optional
  sha1 / zip, and the flate2 dev-dependency

Cargo.lock loses 16 packages (aes, bzip2, bzip2-sys, cipher, deflate64,
humantime, humantime-serde, inout, kamadak-exif, lzma-rs, lzma-sys,
mutate_once, notify-debouncer-mini, pbkdf2, xz2, zopfli); the dependency
graph of pi-pager-bin drops from 992 to 980 packages on x86_64-linux and
pi-memory is no longer reachable from it. The consumer build and
`cargo check --workspace --tests` (minus pi-fast-worktree) stay clean.
…dencies nobody uses (A.4-3)

The dependency sweep left two library crates without a single dependent:
pi-memory (10.7k lines, was only used by pi-shell) and pi-fsnotify (7.0k
lines, was only used by pi-workspace). Like the crates removed in 86ea4c9,
they go. Cargo.lock also loses file-id, notify-debouncer-full and sqlite-vec.

Twenty `[workspace.dependencies]` entries were not referenced by any member
any more (eight of them because of the previous commit: encoding_rs,
humantime-serde, kamadak-exif, notify-debouncer-mini, sha1, siphasher,
unicode-normalization, zip; the rest were already orphaned: alacritty_terminal,
cryptify, env_logger, eventsource-stream, gethostname, glob, jsonschema, md5,
moka, rsa, terminput, tokio-retry). They had no effect on the build; they are
removed so that the next sweep does not trip over them.
…tures (A.4-4)

The `local-workspace` feature was never enabled by any consumer build, so
everything behind `#[cfg(feature = "local-workspace")]` was code rustc never
compiled and no lint could flag. It is gone together with the feature:

- `rsrm cfgfeat` deleted the 197 nodes whose cfg is false with the feature
  off (3.1k lines across 23 pi-pager files) and stripped the redundant
  `#[cfg(not(feature = "local-workspace"))]` attributes;
- `views/welcome/workspace_mode.rs`, the `--local-workspace*` CLI flags, the
  per-session "on your machine" permission-title qualifier and the
  `session_flags` parameter of `finalize_chat_session_meta` /
  `session_flags_for_effects` went with it;
- the feature lines and `default-bazel` entries are removed from the
  pi-shell-base, pi-shell, pi-pager and pi-pager-bin manifests.

`cargo check --all-targets` on the consumer set is clean (0 warnings) and
the touched test filters pass (358 passed).
`PagerArgs::chat()` and `process_chat_mode_enabled()` can never be true in
this fork (the gateway "chat" product is not reachable through standard
ACP), so everything keyed on them was dead by data, not by reachability, and
rustc could not see it:

- the `--chat` flag, `AppView.chat_mode`, `AgentView.chat_kind` /
  `conversation_entry`, `Effect::{CreateSession,CreateWorktreeSession,
  LoadSession}.chat_kind`, `DeferredStartupActions.pending_chat`,
  `MaterializeCtx.chat_mode` and the `_meta.kind = "chat"` stamping /
  scrubbing helpers
- the chat branches of the session picker, context bar, credit bar, welcome
  screen and model setter; `source == "conversation"` picker rows and the
  conversation delete path
- `pi-shell::agent::chat_modes`
- 20-odd tests that only asserted the chat behaviour

Folded with a syn-based constant folder, then pruned by the compiler: the
whole pager + shell set builds without warnings for all targets;
pi-pager lib 5327 passed, settings_e2e 251, grok_home_paths 2,
selection_model_public_api 2.
TUI CI run 37379203134 (Linux x86_64, rustc 1.94.0): graph 980 packages,
0 warnings, `--workspace --tests` clean, 8 suites 7478 passed / 0 failed.

- graph ceiling 995 -> 980, warning ceiling 20 -> 0
- `reference_passed` is now the Linux result (it used to be the macOS one) and
  every suite gets a `min_passed` floor a few percent below it, so a drop in
  the test count has to be a deliberate edit of the manifest
Mechanical: `rustfmt --edition 2024 --config-path tui/rustfmt.toml` on the 216
files that this branch changed and that were not formatted. It repairs the
indentation fragments that the dead-code removal left behind (`use a::{b, };`,
blocks that lost their `if`) and normalises a few lines that were already
unformatted. Files the branch never touched are left alone, so the diff stays
reviewable on its own; skip this commit when reading the removal commits.
The agents modal was the last non-test user of pi_agent in pi-pager; keep the
dev-dependency and drop the normal one.
- plan: status r5; 0.1 / 0.6 / A.3 / A.4 ticked with what was done and what was
  left on purpose; new section 10.7 (commits, audit outcome, scale, validation,
  leftovers); appendix A results; revision log
- docs/baselines/tui.md: Linux numbers of TUI CI run 37379203134 (graph 980, 0
  warnings, 7478 tests), five-triple graph sizes, min_passed floors, dependents
  of the tracked crates
- baseline manifest: drop the two known_failures that died with the dashboard
- Welcome screen: drop the "New worktree" menu item, the ctrl+w
  shortcut and the New Worktree dialog (it could only fail with "Git
  worktree sessions are not supported in standard ACP mode"); menu is
  now Resume session / Quit.
- Session picker: drop ctrl+w "resume in worktree" (PickSessionInWorktree,
  session_picker_worktree_selection, dispatch_pick_session_in_worktree)
  and its "worktree" shortcut hint.
- Hero subtitle no longer advertises the deleted /feedback command.
- Remove cloud_modal_open (const false) from is_scroll_blocking_modal_open
  and the now-unused LineEditor::insert_paste_with_byte_limit.

Verified: pi-pager lib 5307 passed (was 5327; 20 worktree-dialog tests
removed), settings_e2e 251, grok_home_paths 2, selection_model_public_api 2,
0 errors / 0 warnings on --all-targets; PTY smoke (boot/worktree/ui/resume)
OK against the debug zypi.
…, part)

The resume picker showed "<ISO timestamp> (<short id>)" for every session
because the agent had no title to offer. session/list now returns:

- title: the first user message of the session (whitespace collapsed, 100
  characters at most, like pi's session selector), or "(no messages)";
- updated_at: the session file's modification time (every entry is an
  append), falling back to the header's created-at stamp.

Only a bounded prefix (200 lines) of each file is read, off the event loop.
No cursor pagination: the pager does not follow nextCursor, so everything
stays in one page.

Tests: 11 new (helper unit tests + an agent-level one); 609 passed in the
mock suite. Verified in the TUI with a PTY run of /resume.
…stable methods

The new tests/test_acp_stdio_contract.py spawns the agent process and talks
JSON-RPC over its stdio like the TUI does: initialize, session/new, streamed
prompt, the model config option, session/list titles, session/load replay and
session/resume across a restart, session/close, unknown methods, exit 0 on
stdin EOF. PI_ACP_CONTRACT_COMMAND points the same suite at another stdio ACP
agent (for example a future pi-rust).

It found a real bug: initialize advertises session/close and session/resume,
but run_agent() ran without use_unstable_protocol, so the SDK answered both
with "Method not found" (plus a UserWarning on stderr). __main__ now passes
use_unstable_protocol=True.

Plan items 0.3 / 1.P7 (partly). 617 passed in the mock suite.
…Stage 1 start

- docs/baselines/tui.md: Linux column for ac1d3ff (TUI CI runs 37389447358
  and 37389455793): 980 packages, 0 warnings, 7,437 passed; release-dist
  zypi 422,034,120 B (-10.3 %), cold build 1,155 s (-29.9 %), --version
  23.1 ms. Step 5 notes that a manual run is not cancelled by PR pushes.
- plan: 0.2 done; r5 verification rows, PTY findings, the leftover list
  (worktree flags, privacy banner, branding, graceful exit) and the
  Stage 1 items that started (1.P1 half, 1.P5 route bug, 0.3 / 1.P7 Python).
- scripts/tui_baseline.toml: pi-pager reference_passed 5593 -> 5552.
…ing tools are reaped (1.P2)

Measured with a scripted bash call: closing stdin already ended the agent
with exit 0 and no tool left behind (asyncio.run cancels the turns, the
bash tool kills its process group), but SIGTERM killed the process at once
and the tool survived.

- __main__.serve(): SIGTERM cancels the main task, the same path as EOF
  (POSIX only; Windows has no add_signal_handler and ends on EOF).
- tests/_tool_agent.py + tests/test_acp_shutdown.py: the real serve() with
  a scripted long bash call; EOF and SIGTERM must exit 0 and reap the tool.
  Mutation-checked: without the handler the SIGTERM test fails (-15).
- AGENTS.md: a 'Stopping the agent' section.
…a grace (1.P2, 1.R3)

Quitting the TUI while the agent ran a bash tool used to SIGKILL the agent
at once; the tool's process group then outlived it (PTY check: double
Ctrl+Q, the 'sleep' kept running). The bridge now closes the agent's stdin,
which the Python agent treats as 'the client is gone', waits up to
AGENT_EOF_GRACE (3 s) for it to exit and reaps its tools, and kills it
only if it does not. Stdout is drained while the agent stops so it never
meets a closed pipe.

- spawn.rs: stop_child() + StopOutcome, AGENT_EOF_GRACE (< AGENT_EXIT_GRACE,
  checked at compile time); two unit tests (exits on EOF / ignores EOF).
- PTY check with the scripted agent: tool reaped after quit, quit still
  0.3 s, mock /exit leaves no process and no stderr noise.
Found by killing the TUI in a PTY (kill -9 while a bash tool ran): the TUI
was the session leader, so the kernel sent SIGHUP to its foreground process
group, the agent died at once on the default action, and the tool's process
group was left running. A closing terminal does the same.

- __main__.serve(): SIGHUP takes the same path as SIGTERM and EOF (the
  signal set is looked up by name; Windows has neither handlers nor SIGHUP).
- test_acp_shutdown.py: the signal test is parametrised over SIGTERM and
  SIGHUP.
- PTY check, scripted agent running 'sleep 300': kill -9 zypi leaves the
  tool running with a SIGTERM-only handler and reaps it with SIGHUP too;
  closing the PTY master and SIGTERM to zypi reap it either way.
The stop-signal handlers made SIGTERM/SIGHUP harder to act on: if the
graceful stop hangs (asyncio.run waits for a thread that cannot be
cancelled) the agent could only be killed with SIGKILL. The handlers now
remove themselves on the first signal, so a second one terminates the
process as before.

test_acp_shutdown.py: a stuck-thread mode of the scripted agent and a test
that the second SIGTERM kills it (-15). Mutation-checked: without the
self-removal the test times out.
main gained the Phase 6/7 audit work (project trust, provider-scoped API keys,
Model.reasoning, the workflow sandbox, prompts queued behind background turns) while this
branch removed the Rust runtime and restored /model. Six files conflicted (17 hunks), all in
packages/pi-agent-cli and pyproject.toml. Both sides' features are kept:

- agent.py: main's deferred session setup (the trust question, advertising commands, the
  notices) replaces the plain deferred advertise. Session responses still carry configOptions
  and the per-session pi/* hints. _bind_session decides about trust first, then restores the
  persisted model choice and hands both to create_session_harness.
- config.py: the trust fields and model_reasoning sit next to models, default_choice and
  model_choices. api_key_getter(env_name, provider) is main's provider-scoped hook, now for
  any model; make_get_api_key(config) is the [model] default.
- factory.py: create_session_harness takes both trust and model_choice. model_for_choice
  carries Model.reasoning (CliConfig.model_reasoning, the same for every choice), and
  api_key_for_choice scopes a choice's key to its provider.
- __main__.py, pyproject.toml, AGENTS.md: both sides' additions.

New test: a chosen model keeps Model.reasoning and gets only its own provider's key.
@zy1233
zy1233 merged commit 455527c into main Oct 6, 2026
7 checks passed
@zy1233
zy1233 deleted the codex/rust-agent-runtime-removal-plan branch October 6, 2026 16:10
zy1233 added a commit that referenced this pull request Oct 10, 2026
…ds the merge order

The ADR1 PR 1 branch and the mode_a branch were local when the plan and the baseline report were
written, and said "not pushed" in eight places. They are pushed now: #11 (codex/adr1-pr1-python) on
top of #10, #12 (codex/phase1-mode-a) on top of #11. Those places name the PRs instead; the r9
statements that were true when they were written stay, as the r10 entry already says.

The r10 entry in the revision log gets the last step. The user asked to sync main to origin and
merge the processed PRs. The stack has four (#9, #10, #11, #12), and the upper ones have nothing to
merge into until the lower ones are in, so all four are merged, bottom-up, one squash each (the
convention of #7 and #8), with the base moved to main before each merge and the tree of main compared
with the branch's afterwards. Local main is 9 commits ahead of origin/main; all of them except one
older docs commit (superseded by the revised one in #9) are already in #9's branch, so main is not
pushed: after the merges it is reset to origin/main, and the old main stays on a backup branch.

Docs only: the plan, docs/baselines/tui.md.
zy1233 added a commit that referenced this pull request Oct 20, 2026
…used in pi-shell (plan decision 10) (#27)

* refactor(tui): delete the goal prompt templates and the research script that nothing loads

Nothing in the tree names these 15 files any more (git grep by file name: no hit).
The last code that loaded them was removed with the Rust agent runtime in
455527c (#7); the files stayed, and rustc cannot see a file that no `include_str!`
names, so no warning reported them.

pi-shell/src/session/templates/ (14 Markdown prompt templates for the goal
mode) and pi-shell/src/session/workflows/deep_research.rhai (584 lines).
15 files, 1,033 lines. No Rust code changes.

* refactor(tui): delete the dead units session::worktree and session::helpers of pi-shell

The call graph (scripts/tui_callgraph.py units) reports both as dead: nothing in the
consumer build (pi-pager-bin, pi-pager-minimal, pi-update) reaches them. rustc does not
warn, because what they hold is `pub(crate)` or a re-export.

session::worktree (359 lines): a glob re-export of pi_workspace::worktree, two `From`
impls between the shell's WorktreeType and the workspace's (nothing calls them), and 24
tests of types and functions that live in pi-workspace (serde shapes, build_candidate_list,
BackgroundCopyContext, resolve_worktree_by_id_or_path).

session::helpers (3 files, 61 lines): comments, and two tests of string shapes.

The two `resolve_worktree_by_id_or_path` tests were the ones that opened worktrees.db in
whatever GROK_HOME held; they are gone with the unit. pi-shell tests: 864 -> 838.

pi_workspace::worktree has no user left in pi-shell after this; that is left for plan D.8.

* refactor(tui): remove the [storage] section from the pi-shell config

`[storage] cleanup_ttl_days` set how long old session files were kept. The code that
read it went with the session store (#26), and the pager keeps no session file now.
`StorageConfig` and the `storage` field of `Config` are deleted.

A config file that still has the section loads. The section is reported as
unrecognized, like any other unknown section: a new test pins that, and the
"valid config" test no longer lists the section.

`storage_mode` (the `--storage-mode` flag and `Config.storage_mode`) is a different
setting. This change does not touch it.

* refactor(tui): delete util::config::worktree of pi-shell

`util::config::worktree` holds a `WorktreeType` enum (with `FromStr` and a `From` impl for
`CreationMode`). Its one user was `session::worktree`, which the previous commit deletes. The
baseline `check` shows it: the glob re-export of the module is an unused import (1 warning;
the ceiling is 0).

`pi-workspace` and `pi-workspace-types` have their own `WorktreeType` and do not use this
one. The module has one test, `test_worktree_type_fromstr`; it goes with it. `pi-shell` has
no other reference to `pi_fast_worktree` now, so that dependency goes in the next commit.

* refactor(tui): remove the dependencies of pi-shell that no source file names

Ten dependencies of `pi-shell` are not named in any source file of the crate outside comments
(a text scan of `src/`, `tests/` and `build.rs`; the compiler has the last word):

- `[dependencies]`: `urlencoding`, `base64`, `pi-fast-worktree`, `pi-compaction-transcript`,
  `pi-workflow`, `tdigests`, and `nix` (unix only).
- `[dev-dependencies]`: `pretty_assertions`, `pi-test-utils`, `tokio-util`.

Eight lost their last user in #26 (the session store). `pi-fast-worktree` lost its last users in
`session::worktree` and `util::config::worktree` (this branch), and `tokio-util` in the tests of
`session::worktree`.

The dependency graph of `pi-pager-bin` goes from 980 to 970 packages on Linux: `pi-workflow` (a
crate of this workspace; no other crate depends on it now) and nine packages from the registry
(`tdigests`, `rhai`, `rhai_codegen`, `smartstring`, `thin-vec`, `const-random`,
`const-random-macro`, `crunchy`, `tiny-keccak`). `Cargo.lock` loses `tdigests` and ten
dependency lines. The other packages stay in the lock file, because `pi-workflow` is still a
member of the workspace. `THIRD-PARTY-NOTICES` loses 5 entries (it lists the packages that ship on
any platform; `tui_notices.py check`: 984 of 984).

* chore(tui): lower the ceilings of the baseline after the cleanup of pi-shell

- `[gates.residue]`: `leader` 245 -> 243 and `x.ai` 304 -> 297. The deletions of #26 removed
  those lines; `check` has said "lower the ceiling" since then.
- `[graph.max_nodes]`: 980 -> 970 packages on Linux (the dependency commit of this branch).
- `pi-shell` `reference_passed`: 864 -> 838. The tests of `session::worktree` (24, Unix only),
  `session::helpers` (2) and `util::config::worktree` (1) are gone, and one test is new (a
  `[storage]` section is reported as unrecognized). The ten references add up to 8,488 (8,514
  before).

A local run on Linux (WSL2) gives: `check` with 0 warnings, 970 packages, `leader` 243 and `x.ai`
297; the ten test suites 8,487 passed and 1 failed (the known `tmux_probe` test of
`pi-pager-render`); `tui_notices.py check` 984 of 984.

* docs: record plan r16 (the merge of #25 and #26, and what the session store left in pi-shell)

- CHANGELOG.md: the entry for the third cleanup of pi-shell (15 files, three modules, the
  [storage] setting, 10 dependencies). It also says that the earlier entry left
  `[storage] cleanup_ttl_days` for this one.
- docs/baselines/tui.md: current baseline r16 (tests 8,488, graph 970, `leader` 243, `x.ai` 297)
  and a column r16 in the table of sizes. The numbers are from local runs (WSL2); the CI numbers
  of PR #27 follow in a later commit.
- docs/baselines/archive/tui-history.md: the r16 paragraph, with the CI numbers of `7d82c27`.
- docs/baselines/tui-support.md: made again (`pi-shell` has 7 dead units, 1,008 lines, not 9 and
  1,388).
- Plan: status r16, next steps, decision 10, D.2, D.8 (new candidates: 7 dead units, the crate
  `pi-workflow`, `Config.storage_mode`, about 88 files that no code loads), D.9, a new open item
  "races between tests", the index and the revision list.
- Execution log: section 10.19, B.11 (how to repeat the checks) and the r16 revision entry.

* docs: record the CI result of PR 27 in the plan, the log and the baselines

`CI` (run 39193752450) and `TUI CI` (run 39193752451) pass on 6bd7db5.
`TUI CI`: 8,488 passed, 0 failed, 21 ignored; 970 packages in the graph;
`leader` 243, `x.ai` 297; 0 warnings. These are the numbers that section
10.19 of the execution log expected, so only the "to be added" line and
the timing bullet change, plus one sentence in each r16 entry.

The cargo test job took 17 m 34 s and the gates job 4 m 24 s. The tests
themselves took about 54 s of that time.
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.

1 participant