[workspace-split] P2: extract cc-bootstrap, cc-auth, cc-skills leaf crates - #81
Merged
Merged
Conversation
… crates
Three Phase-2 extractions in one commit; each builds + tests green at
the boundary, and the root crate keeps every `crate::{auth,bootstrap,
skills}::…` path working via `use cc_X as X;` aliases at the crate root
(same pattern Crsei#79 used for `cc-keybindings` and `cc-observability`).
cc-bootstrap — 7 files, 922 LOC (clean leaf)
-------------------------------------------
`src/bootstrap/{diagnostics,ids,mod,model,signal,state,timing}.rs` →
`crates/cc-bootstrap/src/…` with a pure `git mv`. No dependency-injection
needed: `bootstrap/` was already a true import-DAG leaf per its own
module doc. Direct deps: `serde`, `parking_lot`, `tokio`, `uuid` (+
`serde_json` for the round-trip test in `ids.rs`).
cc-auth — 8 files, ~1,500 LOC (one tie broken)
---------------------------------------------
`src/auth/{api_key,codex_cli,mod,token}.rs` and
`src/auth/oauth/{mod,client,config,pkce}.rs` → `crates/cc-auth/src/…`.
The one reverse-dep was `token.rs:9` calling
`crate::config::paths::credentials_path()`. Broken by a global init
registered from the host at process startup (mirrors the `set_event_sender`
pattern already used by mcp/plugins/lsp/skills):
- `cc_auth::set_credentials_path(PathBuf)` — host calls this at the top
of `fn main()` with `crate::config::paths::credentials_path()`.
- `token::token_file_path()` reads it back via a private
`crate::credentials_path()` helper.
- Falls back to `{CC_RUST_HOME | ~/.cc-rust | $TMP/cc-rust}/credentials.json`
when the host hasn't registered one, so unit tests that exercise
`resolve_auth()` directly (doctor, logout, voice_cmd — 10 tests) keep
passing. The fallback duplicates ~10 LOC from `config::paths::data_root`
— a small price for full decoupling.
Keychain service name stays `"cc-rust"` (the original acceptance
criterion on Crsei#71). OAuth scopes, PKCE, and token-refresh flow are
untouched — only the storage path resolution changed.
Direct deps: `anyhow`, `serde`, `serde_json`, `chrono`, `tokio`,
`tracing`, `reqwest`, `keyring`, `base64`, `rand`, `sha2`, `dirs`,
`parking_lot`, `urlencoding`.
cc-skills — 3 files, ~1,000 LOC (two ties broken)
------------------------------------------------
`src/skills/{bundled,loader,mod}.rs` → `crates/cc-skills/src/…`.
Two reverse-deps broken:
1. **Event emission** — `emit_event` held a
`broadcast::Sender<crate::ipc::subsystem_events::SubsystemEvent>`,
which is a cycle the moment skills leaves the root crate.
Replaced with a minimal cc-skills-owned enum and a callback:
```rust
pub enum SkillSubsystemEvent { SkillsLoaded { count: usize } }
pub fn set_event_callback<F: Fn(SkillSubsystemEvent) + Send + Sync + 'static>(cb: F);
```
The host adapts it into `SubsystemEvent::Skill(SkillEvent::SkillsLoaded { .. })`
in `ipc/runtime.rs` (replaces the old `set_event_sender(bus.sender())`
line).
2. **User-skills directory** — `init_skills` used to resolve
`crate::config::paths::skills_dir_global()` internally. It now takes
the directory as its first parameter; `main.rs` and `ipc::subsystem_handlers`
pass `&crate::config::paths::skills_dir_global()` at the call site.
Direct deps: `serde`, `parking_lot` only.
Verification
------------
- `cargo build` : ok, 2 warnings (pre-existing
`web::handlers::session_id` dead_code, identical on `rust-lite@ab8a2fc`).
- `cargo build --release` : ok, same 2 warnings.
- `cargo test --workspace --lib --offline --no-fail-fast -- --test-threads=1`:
- `cc-auth` — 32 passed, 0 failed, 2 ignored
- `cc-bootstrap` — 28 passed, 0 failed
- `cc-keybindings` — 51 passed, 0 failed (unchanged from P1)
- `cc-observability` — 10 passed, 1 pre-existing flake
(`off_mode_skips_redaction`, tracked in Crsei#79)
- `cc-skills` — 21 passed, 0 failed
- `cc-types` — 0 tests
- `cargo test --bin claude-code-rs --offline -- --test-threads=1`
: **1713 / 1713 pass**. Baseline from PR Crsei#80 was 1794 — the 81-test
drop is the tests that moved out with their code (cc-bootstrap 28 +
cc-auth 32 + cc-skills 21 = 81, matches exactly).
- Smoke: `claude-code-rs --version` prints `claude-code-rs 0.1.0`.
Phase 2 checklist (Crsei#71)
-----------------------
- [x] `crates/cc-bootstrap/` created
- [x] `crates/cc-auth/` created
- [x] `crates/cc-skills/` created
- [x] `src/{bootstrap,auth,skills}/` removed from root crate
- [x] Keychain service name unchanged (`"cc-rust"`)
- [x] All tests pass (1713 bin + 142 extracted, one pre-existing
cc-observability flake unchanged)
- [ ] Manual OAuth login E2E (`/login 2` + `/login-code`) — not
reproducible in CI, flagged for maintainer verification.
Refs Crsei#71. Part of Crsei#68.
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.
Closes #71. Follow-up to #80 (P1.3). Part of #68.
Summary
Three Phase-2 extractions in one commit — each builds + tests green at
the boundary, and the root crate keeps every
crate::{auth, bootstrap, skills}::…path working viause cc_X as X;aliases (the pattern #79established for
cc-keybindingsandcc-observability).cc-bootstrap— 7 files, 922 LOC (clean leaf)src/bootstrap/{diagnostics,ids,mod,model,signal,state,timing}.rs→crates/cc-bootstrap/src/…. Puregit mv; no dependency injectionneeded — the directory was already a true leaf per its own module doc.
Direct deps:
serde,parking_lot,tokio,uuid(+serde_jsondev).cc-auth— 8 files, ~1.5k LOC (one tie broken)src/auth/{api_key,codex_cli,mod,token}.rsandsrc/auth/oauth/{mod,client,config,pkce}.rs→crates/cc-auth/src/….The one reverse-dep was
token.rs:9callingcrate::config::paths::credentials_path(). Broken via a global initregistered from the host at process startup (mirrors the
set_event_senderpattern thatmcp/plugins/lspalready use):credentials_path()falls back to{CC_RUST_HOME | ~/.cc-rust | \$TMP/cc-rust}/credentials.jsonwhen the hosthasn't registered one, so the 10 unit tests that exercise
resolve_authdirectly (
doctor,logout,voice_cmd) keep passing without a customsetup. That duplicates ~10 LOC from
config::paths::data_root— a smallprice for a zero-cycle extraction.
Keychain service name stays
\"cc-rust\"(the explicit acceptancecriterion on #71). OAuth scopes, PKCE, token-refresh flow are all
byte-identical — only storage-path resolution changed.
Direct deps:
anyhow,serde,serde_json,chrono,tokio,tracing,reqwest,keyring,base64,rand,sha2,dirs,parking_lot,urlencoding(+tempfiledev).cc-skills— 3 files, ~1k LOC (two ties broken)src/skills/{bundled,loader,mod}.rs→crates/cc-skills/src/….Tie #1 — event emission.
emit_eventheld abroadcast::Sender<crate::ipc::subsystem_events::SubsystemEvent>, whichis a cycle the moment
skillsleaves the root crate. Replaced with aminimal cc-skills-owned enum + callback:
Tie #2 — user-skills directory.
init_skillsused to resolvecrate::config::paths::skills_dir_global()internally. Now takes it asthe first parameter; both call sites (
main.rs:258andipc::subsystem_handlers.rs:163) pass&crate::config::paths::skills_dir_global()in.Direct deps:
serde,parking_lotonly — thetokio::sync::broadcastdep fell out when the event API switched to a plain callback.
Verification
cargo build(dev + release): ok, 2 warnings (pre-existingweb::handlers::session_iddead_code, identical onrust-lite@ab8a2fc).cargo test --bin claude-code-rs --offline -- --test-threads=1: 1713 / 1713 pass. Baseline from [workspace-split] P1.3: extract cc-types leaf crate (partial) #80 was 1794 — the 81-test drop
is the tests that moved out with their code (28 cc-bootstrap +
32 cc-auth + 21 cc-skills = 81, matches exactly).
cargo test --workspace --lib --offline --no-fail-fast -- --test-threads=1:cc-authcc-bootstrapcc-keybindingscc-observabilityoff_mode_skips_redactionflake, [workspace-split] Phase 0 + 1 (partial): Workspace skeleton, leaf extractions, CI adaptations #79)cc-skillscc-typesSmoke:
claude-code-rs --versionprintsclaude-code-rs 0.1.0.Phase 2 checklist (#71)
crates/cc-bootstrap/createdcrates/cc-auth/createdcrates/cc-skills/createdsrc/{bootstrap,auth,skills}/removed from root crate\"cc-rust\") — explicit [workspace-split] Phase 2: Extract leaf crates (auth, bootstrap, skills) #71acceptance criterion, no regression.
cc-observabilityflake unchanged)./login 2+/login-code) — notreproducible in CI, flagged for maintainer verification. The only
auth-flow change is storage-path resolution, which is exercised
by the save/load round-trip test in
cc-auth::token::tests.Follow-ups (out of scope)
cc-observability::sink::tests::off_mode_skips_redactionstill flakes —pre-existing
redact_valuebug tracked in [workspace-split] Phase 0 + 1 (partial): Workspace skeleton, leaf extractions, CI adaptations #79. Not touched here.Test plan
cargo build(dev + release)cargo test --bin claude-code-rs --offline -- --test-threads=1cargo test --workspace --libclaude-code-rs --versionsmoke test/login 2//login-coderound-trip on aclean profile.
🤖 Generated with Claude Code