refactor(workspace): Phase 6/7 scaffold + AppState/Tool trait extraction (#75 #76) - #86
Merged
Merged
Conversation
…rsei#76) Lands the Phase 6 / Phase 7 crate scaffold and the last cycle-breaking work needed before the physical source move. ## Scaffold (9 new workspace members) Phase 6 (issue Crsei#75): - cc-engine target for src/engine/ - cc-query target for src/query/ - cc-tools target for src/tools/ (minus lsp, send_message, team_spawn, system_status) - cc-lsp-service target for src/lsp_service/ + tools/lsp.rs Phase 7 (issue Crsei#76): - cc-plugins target for src/plugins/ - cc-teams target for src/teams/ (+ tools/send_message.rs, tools/team_spawn.rs) - cc-daemon target for src/daemon/ - cc-commands target for src/commands/ - cc-ipc target for src/ipc/ (+ tools/system_status.rs) Each crate publishes a stub lib.rs documenting its target scope and the remaining blockers. Description comments note which cycle-causing tool files travel with which crate when the source move lands. ## Cycle-breaking (done in this PR) 1. query -> tools: eliminated. The query loop now drives hooks via the cc_types::hooks::HookRunner trait through a new QueryDeps::hook_runner() accessor. HookRunner gained a run_stop_hooks method, implemented by NoopHookRunner (cc-types) and ShellHookRunner (root crate's tools::hooks). 2. query -> tools (background_agents): eliminated. CompletedBackgroundAgent and PendingBackgroundResults moved from tools/background_agents.rs to cc-types::background_agents. Root tools/background_agents.rs is now a thin re-export so existing call sites compile unchanged. 3. engine -> ipc (agent events): eliminated. The agent IPC type trio moved from src/ipc/ to cc-types: agent_types -> cc-types::agent_types (AgentNode, AgentInfo, TeamMemberInfo) agent_events -> cc-types::agent_events (AgentEvent, TeamEvent, commands) agent_channel -> cc-types::agent_channel (AgentSender, AgentIpcEvent) Root src/ipc/agent_*.rs are now thin re-exports. The engine sdk_to_agent_event helper now imports only from cc-types. 4. types -> ipc: eliminated from the ToolUseContext surface. ToolUseContext::bg_agent_tx is now Option<cc_types::agent_channel::AgentSender> instead of Option<crate::ipc::agent_channel::AgentSender>. 5. tools::lsp <-> lsp_service: eliminated. The three LSP shared types (HoverInfo, SourceLocation, SymbolInfo) moved from tools/lsp.rs to a new lsp_service/types.rs. tools/lsp.rs re-exports them. Next pass moves tools/lsp.rs itself into cc-lsp-service, making the crate fully self-contained. ## Remaining before the source move (documented in design doc) - Hoist types/tool.rs (Tool trait, ToolUseContext) to cc-types. Blocker: ToolUseContext references AppState, which reaches into teams/ui. Either (a) abstract AppState behind a trait, or (b) move TeamContext and StatusLineRunner to cc-types first. - Physically move src/engine/, src/query/, src/tools/, src/lsp_service/ into their respective crates and rewire imports (follow-up PR). - Move cycle-causing tool files to their natural homes: tools/lsp.rs -> cc-lsp-service tools/send_message.rs -> cc-teams tools/team_spawn.rs -> cc-teams tools/system_status.rs -> cc-ipc See docs/superpowers/specs/2026-04-20-workspace-split-design.md for the full plan and remaining work breakdown. ## Verification - cargo check --workspace: clean (2 pre-existing warnings, unrelated). - cargo test --lib -p cc-types: 2/2 passing. - cargo test --lib --bin claude-code-rs ipc::agent: 8/8 passing. - cargo test --lib --bin claude-code-rs query: 35/35 passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…i#75) Removes the `types/app_state.rs -> teams::types::*` edge — the last run-time-free leaf dependency blocking `AppState` from moving into cc-types (which in turn blocks Phase 6 `types/tool.rs` extraction). ## What moves Only the two pure data types `AppState` actually consumes: - `TeamContext` (team_name, paths, self-identity, teammates map) - `TeammateInfo` (per-teammate runtime info) Both are plain field bags with no internal deps beyond `String` / `Option` / `HashMap`. They now live in `cc-types/src/teams.rs`. ## What stays in root Everything else in `src/teams/types.rs` — `TeamFile`, `TeamMember`, `BackendType`, `TeammateMessage`, `TeammateSpawnConfig`, `TeammateSpawnResult`, `TaskStatus`, `TeammateIdentity`, `InProcessTeammateTaskState`, `IdleReason`, `SystemPromptMode` — is used only by the team runtime modules (backend, mailbox, runner, protocol, in_process). Moving them here would pull `tokio::task::AbortHandle` and team-specific impls into cc-types, which violates the leaf-crate invariant. They'll travel with the full `src/teams/` tree into cc-teams in Phase 7. ## Backward compatibility `src/teams/types.rs` keeps `pub use cc_types::teams::{TeamContext, TeammateInfo};` re-exports, so the ~7 call sites (`commands/agents_cmd.rs`, `commands/team_cmd.rs`, `teams/identity.rs`, `tools/send_message.rs`, `tools/team_spawn.rs`) compile unchanged. `types/app_state.rs` switches from `crate::teams::types::TeamContext` to `cc_types::teams::TeamContext` directly, breaking the paper dependency. ## Verification - `cargo check --workspace`: clean (2 pre-existing warnings unrelated). - `cargo test -p cc-types`: 4/4 passing (added 2 new teams tests). - `cargo test --bin claude-code-rs teams -- --test-threads=1`: 58/58 passing. - Parallel test-runner interference on 5 mailbox/helpers tests is pre-existing (shared `~/.cc-rust/teams/` paths); not caused by this change. Next step: abstract `StatusLineRunner` behind a `StatusLineHandle` trait in cc-types so `AppState` itself can move. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First real source move of Phase 6: the scriptable status-line payload
and runner (~1k LOC across 3 files) leave the root crate and land in
`cc-engine`. This is the lightest-weight step toward moving `AppState`
out of root, since `AppState::status_line_runner: StatusLineRunner`
will now resolve to `cc_engine::status_line::StatusLineRunner`.
## What moves
- `src/ui/status_line/{mod.rs, payload.rs, runner.rs}` -> `crates/cc-engine/src/status_line/`
## Decoupling the two root-crate back-edges
The payload used to call into the root crate for two things:
- `crate::engine::output_style::resolve(style, cwd)` -> canonical style name
- `crate::tools::worktree::get_current_worktree_session()` -> active worktree
Both live deep in the root crate and can't follow status_line yet. Rather
than introduce callback trait objects, the contract now inverts: callers
pre-resolve the two values and pass them into `StatusLineSnapshot`:
- removed fields: `output_style: Option<&'a str>`
- added fields: `resolved_output_style_name: Option<String>`,
`worktree: Option<WorktreeStatus>`
A tiny helper module `src/ui/status_line_resolver.rs` in the root crate
does the resolution once per call site, so each of the three call sites
(commands/statusline_cmd, ipc/sdk_mapper, ui/app) just plugs it in.
## Call-site compatibility
`src/ui/mod.rs` re-exports the module (`pub use cc_engine::status_line;`),
so existing `crate::ui::status_line::*` import paths keep compiling
without touching the ~5 consumer files.
## Dependency envelope (cc-engine Cargo.toml)
tokio, futures, async-trait, serde, serde_json, anyhow, tracing, chrono,
uuid, parking_lot, git2, cc-types, cc-config, cc-bootstrap, cc-compact,
cc-keybindings. All already-extracted workspace siblings — no cycle.
## Verification
- `cargo check --all-targets`: clean (2 pre-existing warnings).
- `cargo test -p cc-engine`: 12/12 passing (status_line payload + runner).
- `cargo test --bin claude-code-rs statusline`: 7/7 passing.
- `cargo test --bin claude-code-rs ipc::sdk_mapper`: 5/5 passing.
Next step: move `types/{tool, app_state, config}.rs` into cc-engine so
the Tool trait leaves the root crate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rsei#75) The last runtime-adjacent type files finally leave the root crate: - `src/types/app_state.rs` -> `crates/cc-engine/src/types/app_state.rs` - `src/types/tool.rs` -> `crates/cc-engine/src/types/tool.rs` - `src/types/config.rs` -> `crates/cc-engine/src/types/config.rs` With the `Tool` trait and `ToolUseContext` now in cc-engine, Phase 6's `cc-tools` can finally import them from a workspace crate rather than from the root bin crate — the core architectural goal of the phase. ## Why cc-engine, not cc-types `AppState` transitively holds runtime handles (`StatusLineRunner` in cc-engine, `KeybindingRegistry` in cc-keybindings) and `ToolUseContext` exposes `get_app_state: Arc<dyn Fn() -> AppState>` — both pull in things that cc-types (a pure leaf) deliberately refuses to carry. cc-engine is the natural home: it's where `QueryEngine` lives and it already owns the status-line runner after the previous commit. ## Import rewiring inside the moved files - `use super::message::{...}` -> `use cc_types::message::{...}` - `crate::keybindings::KeybindingRegistry` -> `cc_keybindings::KeybindingRegistry` - `crate::ui::status_line::StatusLineRunner` -> `crate::status_line::StatusLineRunner` (since the files now live in cc-engine, `crate` means cc-engine) `super::tool::*` and `super::app_state::*` work unchanged — the modules are siblings in `cc-engine/src/types/`. ## Back-compat shim Root `src/types/mod.rs` becomes a re-export: pub use cc_engine::types::{app_state, config, tool}; pub use cc_types::{message, state, transitions}; So every `crate::types::{app_state, tool, config}::*` path across the root crate still resolves. No call sites changed. ## Verification - `cargo check --all-targets`: clean (2 pre-existing warnings). - `cargo test -p cc-types`: 4/4 passing. - `cargo test -p cc-engine`: 12/12 passing. - `cargo test --bin claude-code-rs -- types`: 18/18 passing. - `cargo test --bin claude-code-rs -- engine query`: 181/181 passing. - Full `--test-threads=1` run: only the pre-existing `sink::tests::off_mode_skips_redaction` in cc-observability fails (flaky on HEAD too, unrelated). ## What this unblocks - `cc-tools`, `cc-plugins`, `cc-teams`, `cc-daemon`, `cc-commands`, `cc-ipc`, `cc-lsp-service` can all now import the `Tool` trait and `ToolUseContext` via `cc_engine::types::tool`, paving the way for the full Phase 6/7 source moves. - Next: move `src/engine/` and `src/query/` into cc-engine (the bulk of Phase 6) — these are lifecycle code, not just types. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Accidentally created during incremental-build benchmarking in F:/AIclassmanager/cc/rust/.claude/worktrees/modest-morse-592f2d when I mistook the file's canonical path (real file lives at tools/fs/file_read.rs). The stray file had no module declaration referencing it so it was dead weight; deleting it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Integrates the fork-agent infrastructure and ancillary command/IPC updates that landed on rust-lite after this branch opened: - PR Crsei#85: fork-agent infra + /btw /simplify /advisor (Crsei#33 Crsei#37 Crsei#62) - PR Crsei#87: MCP/plugin/reload-plugins/ide/reload-plugins commands (Crsei#41 Crsei#44 Crsei#47 Crsei#49) - PR Crsei#88: /loop /schedule /team-onboarding (Crsei#43 Crsei#58 Crsei#60 Crsei#63) ## Conflict resolution Git's recursive merge handled every hunk on its own — each of the five files flagged as overlapping had non-overlapping line ranges between my workspace-split edits and rust-lite's feature edits: - `crates/cc-engine/src/types/app_state.rs` — my move to cc-engine vs. advisor/fork-related AppState fields. Clean auto-merge. - `crates/claude-code-rs/src/engine/agent/mod.rs` — my `cc_types::agent_*` import rewrites vs. rust-lite's `pub mod fork;` addition. Clean. - `crates/claude-code-rs/src/engine/lifecycle/deps.rs` — my `cc_types::background_agents` retyping and `hook_runner()` impl vs. rust-lite's `advisor_model` stripping logic. Clean. - `crates/claude-code-rs/src/query/deps.rs` — my `hook_runner()` / `drain_background_results` signature changes vs. rust-lite's new `ModelCallParams::advisor_model` field. Clean. - `crates/claude-code-rs/src/query/loop_impl.rs` — my hook-runner-trait rewiring vs. rust-lite's advisor model plumbing. Clean. All 40+ other touched files (api/*, commands/*, ide/*, ipc/*, services/*, etc.) merged without conflict — they're in regions my branch didn't touch. ## Verification - `cargo check --workspace`: clean (2 pre-existing warnings). - `cargo test -p cc-types`: 4/4 passing (teams + background_agents). - `cargo test -p cc-engine`: status_line + types suite unchanged. - `cargo test --bin claude-code-rs engine::agent query::loop_impl`: 69/69 passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lands Phase 6/7 of the workspace split (issues #75, #76) in five incremental commits:
ToolUseContext::bg_agent_txwiring to cc-types.TeamContext/TeammateInfoland in cc-types soAppStatestops reaching intocrate::teams::types::*.src/ui/status_line/moves tocc-engine/src/status_line/. Two root-crate back-edges (engine::output_style::resolve,tools::worktree::get_current_worktree_session) inverted via pre-resolved fields onStatusLineSnapshot.Tooltrait andToolUseContextfinally leave the root bin crate. Every downstream Phase 6/7 crate can now import them viacc_engine::types::tool.Why
Without moving
AppStateand theTooltrait out of the root bin crate, Phase 6/7 physical source moves are blocked —cc-tools,cc-plugins,cc-teams,cc-daemon,cc-commands,cc-ipc,cc-lsp-serviceall needToolandToolUseContextfrom somewhere other than the root bin crate they'd be pulled out of.The chosen solution puts
AppState+Tooltrait in cc-engine (rather than abstracting them behind trait objects in cc-types) because both transitively hold runtime handles (StatusLineRunner,KeybindingRegistry) that don't belong in the leaf-types crate. cc-engine is their semantic home — it already ownsQueryEngineand the newstatus_linesubmodule.Key architectural notes
teams,agent_channel,agent_events,agent_types,background_agentsmodules only add plain-data structs/enums plus a couple tokio-backed mpsc channel types.src/types/mod.rsandsrc/ui/mod.rsre-export the moved modules so the ~200+ existingcrate::types::{app_state, tool, config}::*andcrate::ui::status_line::*call sites compile unchanged.Incremental build speed (measured)
Against the design-doc baseline of 7.5s after a
main.rstouch:main.rstools/fs/file_read.rs)cc-engine/src/status_line/runner.rscc-engine/src/types/tool.rscc-typesleaf file~20% win on
main.rstouch already. The full <3s target arrives whensrc/tools/(~16.8k LOC) physically moves into cc-tools — documented in the follow-up section of the design doc.Phase 6 remaining
Documented in docs/superpowers/specs/2026-04-20-workspace-split-design.md:
src/engine/lifecycle code into cc-engine (the bulk; ~5k LOC)src/query/into cc-engine (or independent cc-query depending on cc-engine; 2.3k LOC)src/tools/minus the cycle-causing files into cc-tools (16.8k LOC — biggest single-step win)src/lsp_service/plustools/lsp.rsinto cc-lsp-serviceTest plan
cargo check --all-targetsclean (only the 2 pre-existingweb/handlers.rsdead-field warnings)cargo test -p cc-types: 4/4 passingcargo test -p cc-engine: 12/12 passing (status_line payload + runner)cargo test --bin claude-code-rs -- types: 18/18 passingcargo test --bin claude-code-rs -- engine query: 181/181 passingcargo test --bin claude-code-rs -- statusline: 7/7 passingcargo test --bin claude-code-rs -- ipc::sdk_mapper: 5/5 passingcargo test --bin claude-code-rs teams -- --test-threads=1: 58/58 passing--test-threads=1run: only pre-existingcc-observability::sink::off_mode_skips_redactionflaky failure (fails on HEAD too; unrelated)🤖 Generated with Claude Code