Journal

The Planning Meta-Project

January 18, 2026 at 3:42 PM

It’s possible to one-shot just about anything using an AI coding assistant like Claude Code or Cursor. The problem is the result will almost surely be bad for anything but the simplest requests. What’s more, it will be bad as reckoned from just about any vantage point: correctness, performance, consistency, maintainability, etc. I suspect this is a major reason why many developers are quick to dismiss what AI can do to help them write code.

It’s as if someone walked up to a piano as a novice, banged on the keys for a few minutes, and then concluded that the thing is no good for making real music.

In the same way, getting good results from AI coding assistants takes time, effort, and practice. AI coding assistants are just another tool. It takes time to learn it, and yet it differs from any other tools that we developers ever have had available as a potential aid.

It can write the code for us.… but only if we are sufficiently clear in communicating what we want, and I think it’s now the job of real programmers to figure out how to do this.

This requires planning, and I mean this in a concrete way: a file containing an ordered list of specific design decisions, concepts, requests, constraints, explanations, tasks, tests, checkpoints, etc. For well over a year now, I’ve been informally evolving the way that I lay this out for the AI, so that it can write the code I want from it. Over the past few weeks, I’ve been trying to nail this down, to make this planning scheme more formal and well-defined—especially when it comes to defining cross-references and intralinks between the execution steps of the plan with the front matter and end matter.

A simple step in the plan looks like this:

## 2.0.5 Execution Steps {#execution-steps}

### Step 0: Preparation and Baseline {#step-0}

**Commit:** `chore: establish baseline metrics and API surface test before workspace migration`

**References:** [D01] Workspace structure, [D11] API surface guard, (#strategy, #success-criteria)

**Artifacts:**
- Baseline test count and coverage
- Baseline build times via `cargo build --timings`
- Verification that all tests pass
- `tests/api_surface.rs` - compile-time API contract

**Tasks:**
- [x] Run `cargo nextest run --workspace` and record pass/fail counts
- [x] Run `cargo build --timings` and save HTML report
- [x] Run `cargo clippy` and fix any warnings
- [x] Create `tests/api_surface.rs` with imports of all current public types (see [D11])
- [x] Ensure clean git status

**Tests:**
- [x] All existing tests pass
- [x] `tests/api_surface.rs` compiles with `--features full`

**Checkpoint:**
- [x] `cargo nextest run --workspace` - all tests pass
- [x] `cargo clippy -- -D warnings` - no warnings
- [x] `cargo fmt --check` - no formatting issues
- [x] `tests/api_surface.rs` exists and compiles with `cargo test -p tugtool --features full -- api_surface`

**Rollback:** N/A (no changes yet)

**Commit after all checkpoints pass.**

More complete examples are below.

I invoke my custom planning with a Claude Code custom subagent.

My experience tells me that the time I’ve spent developing this planning scheme has paid huge dividends in my productivity. I am probably 5–10× more productive using AI than I ever was coding by myself.

Check out the files. Read them over, copy them, change them, use them as you wish.

A built-out example of a planning file:

Files:

Plan Skeleton (845 lines)
## Phase X.Y: <Phase Title> {#phase-slug}

**Purpose:** <1–2 sentences. What capability ships at the end of this phase?>

---

### Plan Metadata {#plan-metadata}

| Field | Value |
|------|-------|
| Owner | <name> |
| Status | draft / active / done |
| Target branch | <branch> |
| Tracking issue/PR | <link or ID> |
| Last updated | <YYYY-MM-DD> |

---

### Phase Overview {#phase-overview}

#### Context {#context}

<1–2 paragraphs. What problem are we solving, and why now?>

#### Strategy {#strategy}

<3–7 bullets. The approach and sequencing philosophy for this phase.>

#### Stakeholders / Primary Customers {#stakeholders}

1. <customer or team>
2. <customer or team>

#### Success Criteria (Measurable) {#success-criteria}

> Make these falsifiable. Avoid “works well”.

- <criterion> (how to measure / verify)
- <criterion> (how to measure / verify)

#### Scope {#scope}

1. <Scope item>
2. <Scope item>
3. <Scope item>

#### Non-goals (Explicitly out of scope) {#non-goals}

- <Non-goal>
- <Non-goal>

#### Dependencies / Prerequisites {#dependencies}

- <Dependency>
- <Prerequisite>

#### Constraints {#constraints}

- <platform/tooling/perf/security constraints>

#### Assumptions {#assumptions}

- <assumption>
- <assumption>

---

### Section Numbering Convention {#section-numbering}

This skeleton uses `X.Y` placeholders. When writing a real plan, replace them with actual numbers:

| Placeholder | Meaning | Example |
|-------------|---------|---------|
| `X` | Major phase number | `1`, `2`, `3` |
| `Y` | Minor phase number (usually `0`) | `1.0`, `2.0` |
| `X.Y.N` | Numbered section within phase | `1.0.1`, `1.0.2` |
| `X.Y.N.M` | Subsection within a numbered section | `1.0.1.1`, `1.0.2.3` |

**Standard section numbers:**
- `X.Y.0` — Design Decisions (always `.0`)
- `X.Y.1` — Specification
- `X.Y.2` — Symbol Inventory
- `X.Y.3` — Documentation Plan
- `X.Y.4` — Test Plan Concepts
- `X.Y.5` — Execution Steps
- `X.Y.6` — Deliverables and Checkpoints

**Deep dives** are just numbered sections within the phase, typically starting at `X.Y.1` *after* `X.Y.0 Design Decisions` (e.g., `1.0.1 Refactoring Operations Analysis`, `1.0.2 Type Inference Roadmap`). Use `X.Y.N.M` for deep-dive subsections when needed.

---

### Document Size Guidance {#document-size}

Plans can grow large. When a plan exceeds **~100KB or ~2000 lines**, consider these strategies:

#### When to Split

| Symptom | Action |
|---------|--------|
| Deep dives exceed 50% of document | Extract to `phase-X-deepdives.md` |
| Multiple independent feature tracks | Split into `phase-X.1.md`, `phase-X.2.md` |
| Reference material dominates | Extract to `phase-X-reference.md` |

#### Navigation Aids for Large Documents

- Add a **Table of Contents** after the Purpose statement
- Use **collapsible sections** (if your renderer supports `<details>`)
- Add **"Back to top"** links after major sections

#### Cross-File References

When splitting across files, use relative links with anchors:

```markdown
See [Worker Protocol](./phase-1-deepdives.md#worker-protocol) for details.
```

Keep all **decisions** ([D01], [D02], ...) in the main plan file—they're the source of truth.

---

### Reference and Anchor Conventions (MANDATORY) {#reference-conventions}

This plan format relies on **explicit, named anchors** and **rich `References:` lines** in execution steps.

#### 1) Use explicit anchors everywhere you will cite later

- **Technique**: append an explicit anchor to the end of a heading using `{#anchor-name}`.
  - Example:
    - `### X.Y.0 Design Decisions {#design-decisions}`
    - `#### [D01] Workspace snapshots are immutable (DECIDED) {#d01-snapshots-immutable}`
- **Why**: do not rely on auto-generated heading slugs; explicit anchors are stable when titles change.

#### 2) Anchor naming rules (lock these in)

- **Allowed characters**: lowercase `a–z`, digits `0–9`, and hyphen `-` only.
- **Style**: short, semantic, **kebab-case**, no phase numbers (anchors should survive renumbering).
- **Prefix conventions (use these consistently)**:
  - **`dNN-...`**: design decisions (`[D01]`) anchors, e.g. `{#d01-sandbox-copy}`
  - **`qNN-...`**: open questions (`[Q01]`) anchors, e.g. `{#q01-import-resolution}`
  - **`rNN-...`**: risk notes (`Risk R01`) anchors, e.g. `{#r01-perf-regression}`
  - **`cNN-...`**: concepts (`Concept C01`) anchors, e.g. `{#c01-type-inference-wall}`
  - **`diagNN-...`**: diagrams (`Diagram Diag01`) anchors, e.g. `{#diag01-rename-flow}`
  - **`op-...`**: refactor operations, e.g. `{#op-rename}`, `{#op-extract-fn}`
  - **`cmd-...`**: CLI commands, e.g. `{#cmd-run}`
  - **`type-...`**: schema types, e.g. `{#type-span}`
  - **`seq-...`**: sequence diagrams, e.g. `{#seq-rename-python}`
  - **`fixture-...`**: fixture sections, e.g. `{#fixture-py-rename-fn}`
  - **Domain anchors**: for major concepts/sections, use a clear noun phrase, e.g. `{#cross-platform}`, `{#config-schema}`, `{#error-scenarios}`

#### 3) Stable label conventions (for non-heading artifacts)

Use stable labels so steps can cite exact plan artifacts even when prose moves around:

- **Design decisions**: `#### [D01] <Title> (DECIDED) {#d01-...}`
- **Open questions**: `#### [Q01] <Title> (OPEN) {#q01-...}`
- **Specs**: `**Spec S01: <Title>** {#s01-slug}` (or make it a `####` heading if you prefer)
- **Tables**: `**Table T01: <Title>** {#t01-slug}`
- **Lists**: `**List L01: <Title>** {#l01-slug}`
- **Risks**: `**Risk R01: <Title>** {#r01-slug}`
- **Milestones**: `**Milestone M01: <Title>** {#m01-slug}`
- **Concepts**: `**Concept C01: <Title>** {#c01-slug}` (for key conceptual explanations)
- **Diagrams**: `**Diagram Diag01: <Title>** {#diag01-slug}` (for ASCII diagrams, sequence flows, architecture visuals)

Numbering rules:
- Always use **two digits**: `D01`, `Q01`, `S01`, `T01`, `L01`, `R01`, `M01`, `C01`, `Diag01`.
- Never reuse an ID within a plan. If you delete one, leave the gap.

#### 4) `**References:**` lines are required for every execution step

Every step must include a `**References:**` line that cites the plan artifacts it implements.

Rules:
- Cite **decisions** by ID: `[D05] ...`
- Cite **open questions** by ID when the step resolves/de-risks them: `[Q03] ...`
- Cite **specs/lists/tables/risks/milestones/concepts/diagrams** by label: `Spec S15`, `List L03`, `Tables T27-T28`, `Risk R02`, `Milestone M01`, `Concept C01`, `Diagram Diag01`, etc.
- Cite **anchors** for deep links in parentheses using `#anchor` tokens (keep them stable).
- **Do not cite line numbers.** If you find yourself writing "lines 5–10", add an anchor and cite that instead.
- Prefer **rich, exhaustive citations**. Avoid `N/A` unless the step is truly refactor-only.

**Good References examples:**

```
**References:** [D05] Sandbox verification, [D12] Git-based undo, Spec S15, Tables T21-T25,
(#session-lifecycle, #worker-process-mgmt, #config-precedence)
```

```
**References:** [D01] Refactoring kernel, [D06] Python analyzer, Concept C01, List L04,
Table T05, (#op-rename, #fundamental-wall)
```

**Bad References examples (avoid these):**

```
**References:** Strategy section (lines 5–10)     ← uses line numbers
**References:** See design decisions above        ← vague, no specific citations
**References:** N/A                               ← only acceptable for pure refactor steps
```

---

### Open Questions (MUST RESOLVE OR EXPLICITLY DEFER) {#open-questions}

> Open questions are tracked work. If a question remains open at phase-end, explicitly defer it with a rationale and a follow-up plan.

#### [Q01] <Question title> (OPEN) {#q01-question-slug}

**Question:** <what is unknown / undecided?>

**Why it matters:** <what breaks or becomes expensive if we guess wrong?>

**Options (if known):**
- <option>
- <option>

**Plan to resolve:** <prototype / benchmark / spike / research / decision meeting>

**Resolution:** OPEN / DECIDED (see [DNN]) / DEFERRED (why, and where it will be revisited)

---

### Risks and Mitigations {#risks}

| Risk | Impact | Likelihood | Mitigation | Trigger to revisit |
|------|--------|------------|------------|--------------------|
| <risk> | low/med/high | low/med/high | <mitigation> | <trigger> |

**Risk R01: <Title>** {#r01-risk-slug}

- **Risk:** <1 sentence>
- **Mitigation:** <1–3 bullets>
- **Residual risk:** <what remains true even after mitigation>

---

### X.Y.0 Design Decisions {#design-decisions}

> Record *decisions* (not options). Each decision includes the “why” so later phases don’t reopen it accidentally.

#### [D01] <Decision Name> (DECIDED) {#d01-decision-slug}

**Decision:** <One sentence decision statement>

**Rationale:**
- <Why>
- <Why>

**Implications:**
- <What this forces in APIs / storage / tests>

---

### Deep Dives (Optional) {#deep-dives}

> Use this section for structured analysis that is not quite “decision” or “spec”, but is critical for implementation alignment.
>
> Examples: operation analysis, end-to-end flows, protocols, schemas, sequence diagrams, CI/CD shape, cross-platform strategy, perf notes, rejection rationale.

#### <Topic Title> {#topic-slug}

<Write-up, diagrams, tables, and any referenced specs/lists/tables.>

---

### X.Y.1 Specification {#specification}

> This section is the contract. It should be complete enough that implementation work can proceed without inventing semantics.

#### X.Y.1.1 Inputs and Outputs (Data Model) {#inputs-outputs}

**Inputs:**
- <Input artifact(s) and supported formats>

**Outputs:**
- <Output artifact(s), return types, side effects>

**Key invariants:**
- <Invariant>
- <Invariant>

#### X.Y.1.2 Terminology and Naming {#terminology}

- **<Term>**: <Definition>
- **<Term>**: <Definition>

#### X.Y.1.3 Supported Features (Exhaustive) {#supported-features}

> Be explicit. Avoid “etc.” and “and more”.

- **Supported**:
  - <Feature>
  - <Feature>
- **Explicitly not supported**:
  - <Feature>
  - <Feature>
- **Behavior when unsupported is encountered**:
  - <Policy-specific or mode-specific behavior>

#### X.Y.1.4 Modes / Policies (if applicable) {#modes-policies}

| Mode/Policy | Applies to | Behavior | Result |
|------------|------------|----------|--------|
| `<mode>` | <where> | <what happens> | <what is returned> |

#### X.Y.1.5 Semantics (Normative Rules) {#semantics}

> Write this like a spec: bullet rules, deterministic ordering, and edge-case behavior.

- **Traversal / evaluation order**: <rule>
- **Ordering guarantees**: <rule>
- **Stopping conditions**: <rule>
- **Null vs missing**: <rule>
- **Coercion rules (if any)**:
  - <rule>

#### X.Y.1.6 Error and Warning Model {#errors-warnings}

> Errors and warnings are the developer UI—be precise.

**Error fields (required):**
- <field>: <meaning>

**Warning fields (required):**
- <field>: <meaning>

**Path formats (if any):**
- Data path format: <e.g., RFC 6901 JSON Pointer>
- Schema path format: <e.g., keyword-level paths>
- Escaping rules: <e.g., "~" and "/">

#### X.Y.1.7 Public API Surface {#public-api}

> Provide Rust + Python signatures at the level needed to implement bindings and stubs.

**Rust:**
```rust
// Core types (enums, structs)
// Public functions / methods
```

**Python:**
```python
# Enums, dataclasses, methods
```

**<Language>:**
```<language>
# <Appropriate language constructs to define>
```

#### X.Y.1.8 Internal Architecture {#internal-architecture}

> Explain how components fit together so work doesn't fork midstream.

- **Single source of truth**: <what>
- **Compilation / interpretation pipeline**:
  - <step>
  - <step>
- **Where code lives**:
  - <crate/module ownership>
- **Non-negotiable invariants to prevent drift**:
  - <e.g., shared keyword list, shared $ref resolver, golden tests>

#### X.Y.1.9 Output Schemas (if applicable) {#output-schemas}

> Use this section when your phase defines CLI output, API responses, or wire formats. These schemas are the **contract**—changes require versioning.

##### Common Types {#schema-common-types}

Define reusable types that appear in multiple responses:

###### `<TypeName>` {#type-typename}

```json
{
  "field1": "string",
  "field2": 123,
  "nested": { ... }
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `field1` | string | yes | <description> |
| `field2` | integer | no | <description> |
| `nested` | object | no | <description> |

##### Response Envelope {#response-envelope}

> Define the standard wrapper for all responses.

```json
{
  "status": "ok" | "error",
  "schema_version": "1",
  ...response-specific fields...
}
```

##### Command Responses {#command-responses}

For each command, define success and error response schemas:

###### Command: `<command-name>` {#cmd-command-name}

**Spec S01: <command-name> Response Schema** {#s01-command-response}

**Success response:**

```json
{
  "status": "ok",
  "schema_version": "1",
  ...
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `field` | type | yes/no | <description> |

##### Error Codes (Exhaustive) {#error-codes}

> List all error codes by category. This table is the contract for error handling.

**Table T01: Error Codes** {#t01-error-codes}

###### <Category> Errors (exit code N)

| Code | Meaning | `details` fields |
|------|---------|------------------|
| `ErrorCode` | <what went wrong> | `field1`, `field2` |

##### Exit Codes {#exit-codes}

| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | General error |
| 2 | Invalid arguments |
| N | <category-specific> |

#### X.Y.1.10 Configuration Schema (if applicable) {#config-schema}

> Use this section when your phase introduces configuration options.

##### Configuration Precedence (highest to lowest) {#config-precedence}

1. CLI flags (`--flag=value`)
2. Environment variables (`PREFIX_KEY`)
3. Project config file (`pyproject.toml`, `Cargo.toml`, etc.)
4. Built-in defaults

##### Config File Schema {#config-file-schema}

```toml
[tool.<name>]
# <category>
field = "default"           # <description>
another_field = true        # <description>

[tool.<name>.<subsection>]
nested_field = "value"      # <description>
```

##### CLI Flag Mapping {#cli-flag-mapping}

| Config Key | CLI Flag | Environment Variable | Default |
|------------|----------|---------------------|---------|
| `field` | `--field=<value>` | `PREFIX_FIELD` | `"default"` |

---

### Compatibility / Migration / Rollout (Optional) {#rollout}

> Use this section when you are changing public APIs, config formats, CLI contracts, or anything that affects adopters.

- **Compatibility policy**: <semver? schema versioning?>
- **Migration plan**:
  - <what changes>
  - <who is impacted>
  - <how to migrate, and how to detect breakage>
- **Rollout plan**:
  - <opt-in flag / staged rollout / canary / feature gate>
  - <rollback strategy>

---

### X.Y.2 Definitive Symbol Inventory {#symbol-inventory}

> A concrete list of new crates/files/symbols to add. This is what keeps implementation crisp.

#### X.Y.2.1 New crates (if any) {#new-crates}

| Crate | Purpose |
|-------|---------|
| `<crate>` | <purpose> |

#### X.Y.2.2 New files (if any) {#new-files}

| File | Purpose |
|------|---------|
| `<path>` | <purpose> |

#### X.Y.2.3 Symbols to add / modify {#symbols}

| Symbol | Kind | Location | Notes |
|--------|------|----------|-------|
| `<Name>` | enum/struct/fn | `<path>` | <notes> |

---

### X.Y.3 Documentation Plan {#documentation-plan}

- [ ] <Docs update>
- [ ] <Examples / schema examples / API docs>

---

### X.Y.4 Test Plan Concepts {#test-plan-concepts}

> Describe the kinds of tests that prove the spec. Leave the actual enumeration of tests to the Execution Steps below.

#### Test Categories {#test-categories}

| Category | Purpose | When to use |
|----------|---------|-------------|
| **Unit** | Test individual functions/methods in isolation | Core logic, edge cases, error paths |
| **Integration** | Test components working together | End-to-end operations, CLI commands |
| **Golden / Contract** | Compare output against known-good snapshots | Schemas, APIs, parsers, serialization |
| **Drift Prevention** | Detect unintended behavior changes | Regression testing, API stability |

#### Test Fixtures (if applicable) {#test-fixtures}

> Use this section when your phase requires structured test data. Fixtures provide reproducible, self-contained test scenarios.

##### Fixture Directory Structure {#fixture-structure}

```
tests/fixtures/
├── <language>/                     # Language-specific fixtures
│   ├── <scenario>/                 # Scenario directory
│   │   ├── <input-files>           # Test input files
│   │   └── expected/               # Expected outputs (optional)
│   └── manifest.json               # Test case manifest
└── golden/                         # Golden output files
    └── <language>/
        └── <scenario>.{json,patch,txt}
```

##### Fixture Manifest Format {#fixture-manifest}

Each fixture directory should have a `manifest.json` describing test cases:

```json
{
  "fixtures": [
    {
      "name": "<test_name>",
      "description": "<what this tests>",
      "path": "<relative_path_to_input>",
      "operation": "<operation_being_tested>",
      "args": { "<arg>": "<value>" },
      "expected": {
        "status": "ok|error",
        "edits": 3,
        "files_changed": 1
      },
      "golden_output": "golden/<language>/<test_name>.json"
    }
  ]
}
```

##### Fixture Requirements {#fixture-requirements}

- **Self-contained**: Each fixture must be runnable/compilable on its own
- **Deterministic**: No randomness, timestamps, or environment-dependent behavior
- **Minimal**: Just enough code to exercise the scenario
- **Documented**: Include comments explaining what's being tested
- **Valid**: All fixtures must pass basic validation (syntax check, type check, etc.)

##### Golden Test Workflow {#golden-workflow}

```bash
# Run golden tests (compare against snapshots)
<test-command> golden

# Update golden files after intentional changes
<UPDATE_ENV_VAR>=1 <test-command> golden
```

---

### X.Y.5 Execution Steps {#execution-steps}

> Execution comes last. Each step should be executable, with a clear commit boundary and a checkpoint.
>
> **Patterns:**
> - Use **Step 0** for prep/bootstrapping that unblocks everything else.
> - If a step is big, split into **substeps** (`Step 2.1`, `Step 2.2`, …) with separate commits and checkpoints.
> - After completing a multi-substep step, add a **Step N Summary** block that consolidates what was achieved and provides an aggregate checkpoint.
>
> **References are mandatory:** Every step must cite specific plan artifacts ([D01], Spec S01, Table T01, etc.) and anchors (#section-name). Never cite line numbers—add an anchor instead.

#### Step 0: <Prep Step Title> {#step-0}

**Commit:** `<conventional-commit message>`

**References:** [D01] <decision name>, (#strategy, #context)

**Artifacts:** (what this step produces/changes)
- <new files / new commands / new schema fields / new docs>

**Tasks:**
- [ ] <task>
- [ ] <task>

**Tests:** (where T is one of: unit, integration, golden / contract, drift prevention)
- [ ] <T test>
- [ ] <T test>

**Checkpoint:**
- [ ] <command>
- [ ] <command>

**Rollback:** (how to undo if this step goes sideways)
- <e.g., revert commit, delete temp dirs, remove config entries>

**Commit after all checkpoints pass.**

---

#### Step 1: <Step Title> {#step-1}

**Commit:** `<conventional-commit message>`

**References:** [D02] <decision>, [D03] <decision>, Spec S01, List L01, (#terminology, #semantics)

**Artifacts:** (what this step produces/changes)
- <new files / new commands / new schema fields / new docs>

**Tasks:**
- [ ] <task>
- [ ] <task>

**Tests:** (where T is one of: unit, integration, golden / contract, drift prevention)
- [ ] <T test>
- [ ] <T test>

**Checkpoint:**
- [ ] <command>
- [ ] <command>

**Rollback:** (how to undo if this step goes sideways)
- <e.g., revert commit, delete temp dirs, remove config entries>

**Commit after all checkpoints pass.**

---

#### Step 2: <Big Step Title> {#step-2}

> If this step is large, break it into substeps with separate commits and checkpoints.
> The parent step explains the structure; each substep has its own commit and checkpoint.

##### Step 2.1: <Substep Title> {#step-2-1}

**Commit:** `<conventional-commit message>`

**References:** [D04] <decision>, Spec S02, Table T01, (#inputs-outputs)

**Artifacts:** (what this substep produces/changes)
- <artifact>

**Tasks:**
- [ ] <task>

**Tests:** (unit / integration / golden / drift prevention)
- [ ] <test>

**Checkpoint:**
- [ ] <command>

**Rollback:**
- <rollback>

**Commit after all checkpoints pass.**

---

##### Step 2.2: <Substep Title> {#step-2-2}

**Commit:** `<conventional-commit message>`

**References:** [D05] <decision>, Concept C01, (#public-api)

**Artifacts:** (what this substep produces/changes)
- <artifact>

**Tasks:**
- [ ] <task>

**Tests:** (unit / integration / golden / drift prevention)
- [ ] <test>

**Checkpoint:**
- [ ] <command>

**Rollback:**
- <rollback>

**Commit after all checkpoints pass.**

---

#### Step 2 Summary {#step-2-summary}

> After a multi-substep step, add a summary block to consolidate what was achieved.

After completing Steps 2.1–2.N, you will have:
- <capability or artifact 1>
- <capability or artifact 2>
- <capability or artifact 3>

**Final Step 2 Checkpoint:**
- [ ] `<aggregate verification command covering all substeps>`

---

#### Step N: Audit / Improvement Round (Optional Pattern) {#step-audit}

> Use this pattern for code review, audit, or cleanup steps. Organize issues by priority and track them systematically.

##### Priority-Based Issue Tracking {#audit-issues}

Organize findings by priority:

###### P0 (Critical): Bugs Causing Incorrect Behavior {#audit-p0}

| ID | File | Issue | Fix | Status |
|----|------|-------|-----|--------|
| S2-R2-01 | path.rs:L | <issue description> | <fix approach> | ✅ / ⏳ / ❌ |

**Tests added:**
- [ ] test: `<test_name_describing_fix>`

###### P1 (High): Security, Race Conditions, Missing Validation {#audit-p1}

| ID | File | Issue | Fix | Status |
|----|------|-------|-----|--------|
| S2-R2-04 | module.rs:L | <issue description> | <fix approach> | ✅ / ⏳ / ❌ |

###### P2 (Medium): API Inconsistencies, Error Handling {#audit-p2}

| ID | File | Issue | Fix | Status |
|----|------|-------|-----|--------|
| S2-R2-09 | api.rs:L | <issue description> | <fix approach> | ✅ / ⏳ / ❌ |

###### P3 (Low): Code Quality, Documentation {#audit-p3}

| ID | File | Issue | Fix | Status |
|----|------|-------|-----|--------|
| S2-R2-16 | lib.rs:L | <issue description> | <fix approach> | ✅ / ⏳ / ❌ |

##### Test Coverage Gaps {#audit-test-gaps}

List missing tests discovered during audit:

**<module>.rs:**
- [ ] `<scenario not currently tested>`
- [ ] `<edge case missing coverage>`

##### Architectural Concerns {#audit-arch-concerns}

> Capture structural issues that don't fit into bug fixes but affect long-term maintainability.

| ID | Concern | Recommendation | Priority |
|----|---------|----------------|----------|
| A1 | <pattern that may cause issues> | <recommended fix or refactor> | P1/P2/P3 |
| A2 | <missing abstraction or API gap> | <suggested approach> | P1/P2/P3 |

##### Dependency Concerns {#audit-dep-concerns}

| ID | Concern | Fix |
|----|---------|-----|
| D1 | <dependency with issues> | <alternative or mitigation> |
| D2 | <missing platform support> | <what to add> |

**Checkpoint:**
- [ ] All P0 issues resolved
- [ ] All P1 issues resolved or explicitly deferred with rationale
- [ ] `<verification command>`

---

### X.Y.6 Deliverables and Checkpoints {#deliverables}

> This is the single place we define “done” for the phase. Keep it crisp and testable.

**Deliverable:** <One sentence deliverable>

#### Phase Exit Criteria (“Done means…”) {#exit-criteria}

- [ ] <criterion> (verification)
- [ ] <criterion> (verification)

**Acceptance tests:** (where T is one of: unit, integration, golden / contract, drift prevention)
- [ ] <T test>
- [ ] <T test>

#### Milestones (Within Phase) (Optional) {#milestones}

**Milestone M01: <Title>** {#m01-milestone-slug}
- [ ] <what becomes true at this point>

#### Roadmap / Follow-ons (Explicitly Not Required for Phase Close) {#roadmap}

- [ ] <follow-on item>
- [ ] <follow-on item>

| Checkpoint | Verification |
|------------|--------------|
| <checkpoint> | <command/test/proof> |

**Commit after all checkpoints pass.**
Plan Subagent (69 lines)
---
name: code-planner
description: "Use this agent when the user needs to plan a new feature, refactoring task, or significant code change before implementation. This agent excels at breaking down complex requirements into actionable implementation steps. Examples of when to invoke this agent:\\n\\n<example>\\nContext: The user wants to add a new feature to the codebase.\\nuser: \"I want to add support for TypeScript in tugtool\"\\nassistant: \"This is a significant feature that requires careful planning. Let me use the code-planner agent to analyze the codebase and create a detailed implementation plan.\"\\n<Task tool invocation to launch code-planner agent>\\n</example>\\n\\n<example>\\nContext: The user wants to refactor an existing system.\\nuser: \"The error handling in this project is inconsistent, can we clean it up?\"\\nassistant: \"Refactoring error handling across the codebase requires understanding the current patterns and planning the migration. I'll use the code-planner agent to create a structured plan.\"\\n<Task tool invocation to launch code-planner agent>\\n</example>\\n\\n<example>\\nContext: The user describes a complex task that spans multiple files or modules.\\nuser: \"We need to add MCP tools for the new workspace management features\"\\nassistant: \"Adding new MCP tools involves changes across multiple files and requires understanding the existing patterns. Let me invoke the code-planner agent to create an implementation plan.\"\\n<Task tool invocation to launch code-planner agent>\\n</example>\\n\\n<example>\\nContext: The user explicitly asks for a plan before coding.\\nuser: \"Before we start coding, can you write up a plan for how we'll implement the caching layer?\"\\nassistant: \"Absolutely. I'll use the code-planner agent to analyze the requirements and create a detailed implementation plan.\"\\n<Task tool invocation to launch code-planner agent>\\n</example>"
model: opus
color: yellow
---

You are an expert software architect and technical planner specializing in codebase analysis and implementation planning. You possess deep knowledge of software design patterns, system architecture, and effective decomposition of complex tasks into manageable implementation steps.

## Your Core Responsibilities

1. **Codebase Investigation**: Thoroughly explore and understand the existing codebase structure, patterns, conventions, and architectural decisions before proposing changes.

2. **Requirement Analysis**: Parse user requests to identify explicit requirements, implicit needs, potential edge cases, and dependencies on existing code.

3. **Plan Creation**: Produce detailed, actionable implementation plans following the structure defined in @plans/plan-skeleton.md.

## Planning Process

### Phase 1: Discovery
- Read and understand the project's CLAUDE.md and any relevant documentation
- Explore the directory structure to understand the codebase organization
- Identify relevant files, modules, and patterns that relate to the requested work
- Note existing conventions for naming, error handling, testing, and code organization

### Phase 2: Analysis
- Break down the user's request into discrete, implementable units
- Identify dependencies between tasks and determine optimal ordering
- Anticipate potential challenges, edge cases, and integration points
- Consider testing requirements for each component

### Phase 3: Plan Composition
- Structure the plan according to @plans/plan-skeleton.md
- Write clear, specific implementation steps that another developer (or AI agent) could follow
- Include file paths, function signatures, and specific code locations where relevant
- Note any decisions that need user input or clarification
- Specify verification steps and success criteria for each major milestone

## Plan Quality Standards

- **Specificity**: Reference exact file paths, function names, and line numbers when relevant
- **Completeness**: Cover all aspects including implementation, testing, documentation, and integration
- **Sequencing**: Order tasks logically, respecting dependencies
- **Testability**: Include specific test cases or verification steps for each component
- **Reversibility**: Note any changes that might need rollback strategies

## Output Requirements

- Always write plans to files in the @plans directory
- Use descriptive filenames that reflect the feature or task (e.g., `plan-typescript-support.md`, `plan-error-handling-refactor.md`)
- If a plan file location is specified by the user, use that location
- After writing the plan, summarize the key milestones and estimated complexity

## Interaction Guidelines

- If the skeleton template at @plans/plan-skeleton.md is not found, ask the user to provide it or create a sensible default structure
- Ask clarifying questions when requirements are ambiguous, but batch questions together rather than asking one at a time
- If the scope seems too large for a single plan, propose breaking it into multiple related plans
- Flag any architectural concerns or potential conflicts with existing patterns you discover

## Self-Verification

Before finalizing any plan, verify:
- [ ] All referenced files and modules actually exist in the codebase
- [ ] The plan follows the project's established conventions (from CLAUDE.md)
- [ ] Each step is actionable and specific enough to implement
- [ ] Dependencies between steps are clearly stated
- [ ] Testing and verification criteria are included
- [ ] The plan has been written to the appropriate file in @plans````
Phase 2.0: Workspace Reorganization (1898 lines)
# Phase 2.0: Workspace Reorganization {#phase-2}

**Purpose:** Reorganize tugtool into a Cargo workspace with separate crates for core infrastructure, the main binary, and language-specific modules, enabling parallel compilation, feature-flag-based language inclusion, and easier contribution of new language support.

---

## Plan Metadata {#plan-metadata}

| Field | Value |
|------|-------|
| Owner | TBD |
| Status | ready |
| Target branch | main |
| Tracking issue/PR | TBD |
| Last updated | 2026-01-17 |

---

## Plan Audit History {#plan-audit}

### Audit 2026-01-17: Critical Flaw in Migration Strategy {#audit-2026-01-17}

**Problem Identified:** The original Step 1 converted the root Cargo.toml to a virtual workspace (removing the `[package]` section) BEFORE migrating any code. This immediately orphaned the `src/` directory, breaking all 639 tests.

**Root Cause:** The plan confused the **end state** (virtual workspace, [D07]) with the **migration strategy**. Decision [D07] correctly describes the final structure but was incorrectly implemented as the starting point of Step 1.

**Contradiction:** The Strategy section promised "maintaining a working build at each step" but Step 1 as written immediately broke the build.

**Symptoms:**
- `cargo nextest run` shows "0 tests to run" after Step 1
- Root `src/` code is orphaned (no package compiles it)
- Empty crate skeletons in `crates/` don't help

**Current State (as of audit):**
- Git status shows Step 1 was partially executed with the WRONG approach
- Root Cargo.toml is a virtual workspace (no `[package]`)
- `crates/` exist with empty skeletons
- All code still in `src/` but orphaned
- **ACTION REQUIRED:** Revert to pre-Step-1 state and re-execute with corrected plan

**Resolution Applied:**
1. Updated [D07] to clarify it describes the END STATE, not the starting point
2. Rewrote Step 1 to use a **hybrid workspace** (both `[workspace]` AND `[package]` sections)
3. Updated all Step 2 substeps to require `cargo nextest run` at each checkpoint
4. Rewrote Step 6.1 to handle the final conversion from hybrid to virtual workspace
5. Added critical warnings throughout to prevent this mistake
6. Added Milestone M00 to verify hybrid workspace is established correctly
7. Added test count verification (639 tests) at all milestones

**Key Insight:** Incremental migration requires maintaining the existing compilation path until the new path is ready. You cannot delete the old structure until the new structure can build everything.

**Rollback Instructions (if Step 1 was already executed incorrectly):**
```bash
# Revert to state before Step 1
git checkout HEAD~1 -- Cargo.toml
git checkout HEAD~1 -- Cargo.lock
rm -rf crates/

# Verify tests pass again
cargo nextest run --workspace  # Should show 639 tests
```

---

## Phase Overview {#phase-overview}

### Context {#context}

Tugtool is currently structured as a single crate with all functionality in `src/`. As the project grows to support multiple languages (Python now, Rust planned), this monolithic structure creates challenges:

1. **Compilation time**: Any change recompiles everything
2. **Coupling**: Language-specific code can accidentally depend on other language modules
3. **Feature management**: No clean way to build without certain language support
4. **Contributor friction**: New language support requires understanding the entire codebase

A workspace structure with separate crates addresses all these concerns while maintaining the existing API surface.

### Strategy {#strategy}

- **Incremental migration**: Move code in phases, maintaining a working build at each step
- **Hybrid workspace during migration**: Root Cargo.toml has BOTH `[workspace]` AND `[package]` sections until Step 6
- **Core-first approach**: Extract the shared infrastructure first (`tugtool-core`), then build language crates on top
- **Preserve public API**: The `tugtool` crate re-exports everything users currently depend on
- **Feature flags for languages**: Each language crate is an optional dependency, controlled by features
- **Test migration alongside code**: Move tests with their corresponding modules to maintain coverage
- **No functional changes**: This is purely a structural refactor; behavior remains identical
- **Virtual workspace as END STATE**: Convert to virtual workspace only in Step 6 after all code is migrated

> **CRITICAL INVARIANT**: `cargo nextest run --workspace` must pass at every checkpoint. If tests fail after a step, do NOT proceed - fix the issue first. **Always use `--workspace`** to ensure tests in all crates are included - without it, tests in newly created crates won't run!

### Stakeholders / Primary Customers {#stakeholders}

1. Tugtool developers contributing new language support
2. Users who want minimal builds (core + specific languages only)
3. CI/CD pipelines benefiting from parallel compilation

### Success Criteria (Measurable) {#success-criteria}

- All existing tests pass (`cargo nextest run --workspace`)
- Clean incremental builds after touching only language-specific code
- `cargo build --no-default-features` produces a working binary (core only)
- `cargo build --features python` includes Python support
- No changes to CLI interface or JSON output schemas
- Build time improvement measurable via `cargo build --timings`

### Scope {#scope}

1. Create workspace structure with `crates/` directory
2. Extract `tugtool-core` crate (shared infrastructure)
3. Extract `tugtool-python` crate (Python language support)
4. Create placeholder `tugtool-rust` crate (future)
5. Refactor main `tugtool` crate to compose the above
6. Add feature flags for language inclusion/exclusion

### Non-goals (Explicitly out of scope) {#non-goals}

- Adding new functionality or refactoring operations
- Changing any public API signatures
- Implementing Rust language support (placeholder only)
- Breaking changes to CLI or JSON output
- Changing the `.tug/` session directory structure

### Dependencies / Prerequisites {#dependencies}

- All existing tests must pass before starting
- Understanding of current module interdependencies (analyzed below)

### Constraints {#constraints}

- Must maintain backwards compatibility with existing `cargo install tugtool` (from crates.io)
- Feature names must be stable for downstream users
- Workspace must work with existing CI configuration
- CI/scripts assuming root is a package must be updated (virtual workspace has no root package)
- Local development install changes from `cargo install --path .` to `cargo install --path crates/tugtool`

### Assumptions {#assumptions}

- Cargo workspace member ordering allows parallel compilation
- No circular dependencies exist between proposed crate boundaries
- All integration tests can run against the composed binary

---

## Open Questions (MUST RESOLVE OR EXPLICITLY DEFER) {#open-questions}

### [Q01] MCP server crate placement (DECIDED) {#q01-mcp-placement}

**Question:** Should MCP server code live in `tugtool-core` or remain in the main `tugtool` crate?

**Why it matters:** MCP depends on `rmcp` which is a heavy dependency. Placing it in core means core carries that weight even for non-MCP builds.

**Options:**
- Keep MCP in main `tugtool` crate (current plan)
- Create separate `tugtool-mcp` crate
- Include in `tugtool-core` behind a feature flag

**Plan to resolve:** Start with MCP in main crate; evaluate if extraction needed based on build times.

**Resolution:** DECIDED - MCP stays in main `tugtool` crate, controlled by existing `mcp` feature flag.

### [Q02] Test organization strategy (OPEN) {#q02-test-organization}

**Question:** Should integration tests remain in the workspace root or move to individual crates?

**Why it matters:** Integration tests that exercise the full stack need access to all crates. Moving them complicates the test setup.

**Options:**
- Keep all integration tests in `tests/` at workspace root
- Move unit tests to crates, keep integration tests at root
- Each crate has its own `tests/` directory

**Plan to resolve:** Evaluate during Step 2; document chosen approach.

**Resolution:** OPEN - Will decide during implementation.

---

## Risks and Mitigations {#risks}

| Risk | Impact | Likelihood | Mitigation | Trigger to revisit |
|------|--------|------------|------------|--------------------|
| Circular dependencies discovered | high | low | Analyze deps before moving; refactor if found | Build fails during migration |
| Test coverage gaps after migration | med | med | Run coverage before/after; diff reports | Coverage drops >1% |
| Build time regression | low | low | Measure with `--timings`; revert if slower | Build time increases |

**Risk R01: Hidden coupling in current code** {#r01-hidden-coupling}

- **Risk:** Unexpected dependencies between modules may prevent clean separation
- **Mitigation:**
  - Analyze `use` statements before migration
  - Create abstraction traits if coupling found
  - Document any necessary restructuring
- **Residual risk:** Some coupling may require interface changes

---

## 2.0.0 Design Decisions {#design-decisions}

### [D01] Workspace structure with crates/ directory (DECIDED) {#d01-workspace-structure}

**Decision:** Use a `crates/` directory to hold all workspace members.

**Rationale:**
- Clean separation from workspace root files (Cargo.toml, README, etc.)
- Follows common Rust workspace conventions (rustc, cargo, ripgrep)
- Easy to glob for CI/tooling (`crates/*/Cargo.toml`)

**Implications:**
- Main binary moves from `src/` to `crates/tugtool/src/`
- Workspace Cargo.toml at root defines members

### [D02] Core crate contains shared infrastructure (DECIDED) {#d02-core-crate}

**Decision:** `tugtool-core` contains all language-agnostic infrastructure: session, workspace, patch, sandbox, output, error, facts, text, diff, util.

**Rationale:**
- These modules have no language-specific dependencies
- Forms the stable foundation for all language adapters
- Smaller core = faster compilation for language-specific changes

**Implications:**
- Core has no feature flags for languages
- Language crates depend on core, not vice versa
- `facts/` module provides language-agnostic symbol/reference types

### [D03] Language crates are optional dependencies (DECIDED) {#d03-optional-languages}

**Decision:** Each language crate (`tugtool-python`, `tugtool-rust`) is an optional dependency of the main `tugtool` crate, controlled by feature flags.

**Rationale:**
- Users can build minimal binaries
- Clear compilation boundaries
- Each language can have isolated dependencies

**Implications:**
- Default features include all supported languages
- Feature names: `python`, `rust` (short, clear)
- Conditional compilation in CLI dispatch code

### [D04] Main crate composes and re-exports (DECIDED) {#d04-main-crate}

**Decision:** The `tugtool` crate contains: main.rs, cli.rs, mcp.rs, and re-exports from core/language crates.

**Rationale:**
- Single binary entry point
- CLI logic dispatches to language crates based on file types
- MCP server stays here (depends on multiple languages)

**Implications:**
- `tugtool` crate has `[dependencies]` on core and language crates
- Public API surface maintained via `pub use` re-exports
- Version numbers stay synchronized

### [D05] Testcmd module stays in main crate (DECIDED) {#d05-testcmd}

**Decision:** `testcmd.rs` (test command resolution) remains in the main `tugtool` crate.

**Rationale:**
- Test command resolution may need to know about multiple languages
- It's primarily used by CLI and MCP, not by core or language crates

**Implications:**
- testcmd can import from language crates if needed
- Future: may spawn language-specific test runners

### [D06] Synchronized versions across workspace (DECIDED) {#d06-versions}

**Decision:** All crates in the workspace share the same version number, maintained in workspace Cargo.toml.

**Rationale:**
- Simplifies release process
- Clear compatibility guarantees
- Workspace inheritance makes this easy

**Implications:**
- Use `version.workspace = true` in member Cargo.toml files
- Bump all versions together on release

### [D07] Virtual workspace (no root package) - END STATE (DECIDED) {#d07-virtual-workspace}

**Decision:** The **final** root `Cargo.toml` is a **virtual workspace** with no `[package]` section. All crates live in `crates/`.

**CRITICAL: Migration Path**
This is the **end state**, not the starting point. The migration MUST use a **hybrid workspace** approach:

1. **During migration:** Root Cargo.toml has BOTH `[workspace]` AND `[package]` sections. This keeps `src/` compiled and all tests running throughout migration.
2. **After migration complete:** Remove `[package]` section from root, making it a pure virtual workspace.

Converting to virtual workspace **before** migrating code would orphan `src/` and break all tests.

**Rationale:**
- Matches Rust ecosystem conventions (rustc, ripgrep, cargo itself)
- Clean separation between workspace metadata and crate code
- Avoids confusion about "which crate am I building?"

**Implications:**
- `cargo build` from root builds all crates (or default members)
- `cargo install tugtool` works from crates.io (publishes from `crates/tugtool`)
- Local install requires: `cargo install --path crates/tugtool`
- CI scripts must be updated if they assume root is a package

### [D08] Naming and packaging contract (DECIDED) {#d08-naming-contract}

**Decision:** Maintain current naming for compatibility.

| Item | Value |
|------|-------|
| Package name | `tugtool` |
| Binary name | `tug` |
| Library name | `tugtool` |
| crates.io install | `cargo install tugtool` |
| Local dev install | `cargo install --path crates/tugtool` |
| Library usage | `use tugtool::*` (unchanged) |

**Rationale:**
- Users expect `cargo install tugtool` to continue working
- Binary name `tug` is already established
- Library re-exports maintain API compatibility

**Implications:**
- `crates/tugtool/Cargo.toml` publishes as `tugtool` on crates.io
- README and docs must clarify local vs crates.io install paths

### [D09] Feature-gated CLI behavior (DECIDED) {#d09-feature-gated-cli}

**Decision:** When a language feature is not compiled in, language-specific commands fail gracefully with a clear error message.

**Core-only build (`--no-default-features`) must support:**
- `tug --help`, `tug --version`
- `tug snapshot` (file scanning is language-agnostic)
- `tug session status`

**Language commands without the feature:**
```
$ tug run rename-symbol --at foo.py:1:1 --to bar
error: Python support not compiled in

To enable: cargo install tugtool --features python
```
Exit code: 2 (invalid arguments / unsupported operation)

**Rationale:**
- Users get actionable feedback instead of cryptic errors
- Core functionality remains useful for inspection/snapshot workflows
- Clear path to enable missing features

**Implications:**
- CLI dispatch code must check feature availability
- Error messages must include remediation instructions
- Exit code 2 for "feature not available" aligns with existing error codes

### [D10] MCP decoupled from language features (DECIDED) {#d10-mcp-decoupling}

**Decision:** The `mcp` feature is independent of language features. MCP server starts regardless of which languages are compiled in; individual tools check feature availability at runtime.

**Behavior:**
- `tug_snapshot` → always works
- `tug_rename_symbol` → returns error "Python support not compiled" if `!cfg!(feature = "python")`

**Rationale:**
- MCP server is useful even with partial language support
- Allows agents to discover available capabilities
- Simpler feature matrix (no `mcp-python` combo features)

**Implications:**
- MCP tool implementations must have feature guards
- Tool list/schema should indicate which tools are available
- Default features still include both `python` and `mcp`

### [D11] API surface compile-time guard (DECIDED) {#d11-api-surface-guard}

**Decision:** Add `tests/api_surface.rs` that imports all public types, serving as a compile-time contract for the public API. The test must be **feature-aware** to handle conditional re-exports.

**Implementation:**
```rust
//! Compile-only test to verify public API surface.
//! If this file fails to compile, the public API has regressed.
//!
//! Run with: cargo test -p tugtool --features full -- api_surface

use tugtool::{
    // Core types (always available)
    patch::{Span, FileId, Edit, PatchSet, ContentHash, /* ... */},
    facts::{FactsStore, Symbol, SymbolKind, ReferenceKind, /* ... */},
    error::TugError,
    output::{Location, ReferenceInfo, SymbolInfo},
    session::Session,
    workspace::WorkspaceSnapshot,
    // ... exhaustive list of core types
};

// Feature-gated re-exports
#[cfg(feature = "python")]
use tugtool::python;

#[cfg(feature = "rust")]
use tugtool::rust;

#[test]
fn api_surface_compiles() {
    // This test exists only to verify imports compile.
    // If you're here because this test broke, you may have
    // accidentally removed a public re-export.
}
```

**Rationale:**
- Catches accidental API breakage during refactoring
- Low maintenance cost (just a list of imports)
- Fails fast in CI if re-exports are missing
- Feature-aware structure prevents false failures on minimal builds

**Implications:**
- Must be created before migration begins (baseline)
- Must be updated when intentionally adding/removing public types
- Part of phase exit criteria
- **Must be tested with `--features full`** to validate all re-exports

---

## Deep Dives {#deep-dives}

### Current Module Dependency Analysis {#module-deps}

Analysis of `use` statements in the current codebase reveals the following dependency graph:

**Diagram Diag01: Current Module Dependencies** {#diag01-module-deps}

```
                    +-------------+
                    |   main.rs   |
                    +------+------+
                           |
                    +------v------+
                    |   cli.rs    |<------------+
                    +------+------+             |
                           |                    |
         +-----------------+---------------+    |
         |                 |               |    |
    +----v----+      +-----v-----+   +-----v----+--+
    | mcp.rs  |      | python/   |   | session     |
    +----+----+      +-----+-----+   +-----+-------+
         |                 |               |
         |           +-----v-----+   +-----v-------+
         |           | analyzer  |   | workspace   |
         |           |  worker   |   +-----+-------+
         |           |   ops/    |         |
         |           +-----+-----+         |
         |                 |               |
    +----v-----------------v---------------v------+
    |                  CORE LAYER                  |
    |  +--------+  +--------+  +--------+         |
    |  | patch  |  | facts  |  |sandbox |         |
    |  +--------+  +--------+  +--------+         |
    |  +--------+  +--------+  +--------+         |
    |  | output |  | error  |  |  text  |         |
    |  +--------+  +--------+  +--------+         |
    |  +--------+  +--------+                     |
    |  |  diff  |  |  util  |                     |
    |  +--------+  +--------+                     |
    +---------------------------------------------+
```

**Key observations:**

1. `patch.rs` is the foundation - used by facts, sandbox, output, diff, text, python
2. `facts/` depends only on patch (for Span, FileId, ContentHash)
3. `sandbox.rs` depends on patch and workspace
4. `output.rs` depends on patch (for Span) and facts (for SymbolKind)
5. `python/` depends on facts, patch, output, text, session, diff, util
6. `mcp.rs` depends on cli, error, output (and indirectly on python via cli)
7. `session.rs` depends on workspace
8. No circular dependencies detected

### Proposed Crate Boundaries {#crate-boundaries}

**Table T01: Module to Crate Mapping** {#t01-module-mapping}

| Current Module | Target Crate | Rationale |
|---------------|--------------|-----------|
| `patch.rs` | tugtool-core | Foundation types, no deps |
| `facts/mod.rs` | tugtool-core | Language-agnostic symbol model |
| `error.rs` | tugtool-core | Shared error types |
| `output.rs` | tugtool-core | Shared JSON output types |
| `text.rs` | tugtool-core | Text utilities |
| `diff.rs` | tugtool-core | Diff generation |
| `util.rs` | tugtool-core | General utilities |
| `workspace.rs` | tugtool-core | Workspace snapshots |
| `sandbox.rs` | tugtool-core | Sandboxed operations |
| `session.rs` | tugtool-core | Session management |
| `python/` (all) | tugtool-python | Python language support |
| `rust/mod.rs` | tugtool-rust | Rust placeholder |
| `main.rs` | tugtool | Binary entry point |
| `cli.rs` | tugtool | CLI implementation |
| `mcp.rs` | tugtool | MCP server |
| `testcmd.rs` | tugtool | Test command resolution |
| `lib.rs` | tugtool | Re-exports |

### Target Directory Structure {#target-structure}

**List L01: Final Directory Layout** {#l01-directory-layout}

```
tugtool/
+-- Cargo.toml              # workspace root
+-- Cargo.lock
+-- CLAUDE.md
+-- README.md
+-- crates/
|   +-- tugtool/            # main binary crate
|   |   +-- Cargo.toml
|   |   +-- src/
|   |       +-- main.rs     # CLI entry point
|   |       +-- lib.rs      # re-exports for library usage
|   |       +-- cli.rs      # CLI command implementations
|   |       +-- mcp.rs      # MCP server
|   |       +-- testcmd.rs  # test command resolution
|   |
|   +-- tugtool-core/       # shared infrastructure
|   |   +-- Cargo.toml
|   |   +-- src/
|   |       +-- lib.rs      # module exports
|   |       +-- patch.rs    # Patch IR
|   |       +-- error.rs    # TugError
|   |       +-- output.rs   # JSON output types
|   |       +-- session.rs  # Session management
|   |       +-- workspace.rs # Workspace snapshots
|   |       +-- sandbox.rs  # Sandboxed operations
|   |       +-- text.rs     # Text utilities
|   |       +-- diff.rs     # Diff generation
|   |       +-- util.rs     # General utilities
|   |       +-- facts/
|   |           +-- mod.rs  # Symbol/reference model
|   |
|   +-- tugtool-python/     # Python language support
|   |   +-- Cargo.toml
|   |   +-- src/
|   |       +-- lib.rs      # module exports (replaces mod.rs)
|   |       +-- analyzer.rs
|   |       +-- bootstrap.rs
|   |       +-- dynamic.rs
|   |       +-- env.rs      # Python environment resolution
|   |       +-- files.rs
|   |       +-- libcst_worker.py  # Embedded Python worker script
|   |       +-- lookup.rs
|   |       +-- test_helpers.rs
|   |       +-- type_tracker.rs
|   |       +-- validation.rs
|   |       +-- verification.rs
|   |       +-- worker.rs
|   |       +-- ops/
|   |           +-- mod.rs
|   |           +-- rename.rs
|   |
|   +-- tugtool-rust/       # Rust language support (placeholder)
|       +-- Cargo.toml
|       +-- src/
|           +-- lib.rs      # placeholder
|
+-- tests/                  # workspace-level integration tests
|   +-- integration/
+-- .tug/                   # session directory (unchanged)
+-- plans/                  # planning documents
```

### Feature Flag Design {#feature-flags}

**Table T02: Feature Flags** {#t02-feature-flags}

| Feature | Crate | Description | Dependencies |
|---------|-------|-------------|--------------|
| `default` | tugtool | Full build | `python`, `mcp` |
| `python` | tugtool | Python support | tugtool-python |
| `rust` | tugtool | Rust support (future) | tugtool-rust |
| `mcp` | tugtool | MCP server | rmcp, schemars |
| `full` | tugtool | All languages + MCP | `python`, `rust`, `mcp` |

**Spec S01: Feature Flag Usage** {#s01-feature-flags}

```toml
# crates/tugtool/Cargo.toml
[features]
default = ["python", "mcp"]
python = ["dep:tugtool-python"]
rust = ["dep:tugtool-rust"]
mcp = ["dep:rmcp", "dep:schemars"]
full = ["python", "rust", "mcp"]

[dependencies]
tugtool-core = { path = "../tugtool-core" }
tugtool-python = { path = "../tugtool-python", optional = true }
tugtool-rust = { path = "../tugtool-rust", optional = true }

# MCP dependencies (optional) - versions must match current Cargo.toml
rmcp = { version = "...", features = ["server", "transport-io"], optional = true }
schemars = { version = "...", optional = true }
```

**Note:** All dependency versions in this plan are illustrative. During implementation, use the exact versions from the current `Cargo.toml` to avoid version conflicts.

### Dependency Flow {#dependency-flow}

**Diagram Diag02: Crate Dependency Graph** {#diag02-crate-deps}

```
     +---------------------------------------+
     |              tugtool                   |
     |  (main binary, CLI, MCP)              |
     +-------------------+-------------------+
                         |
           +-------------+-------------+
           |             |             |
           v             v             v
+-------------+ +-------------+ +-------------+
|tugtool-python| |tugtool-rust | |  (MCP deps) |
|  (optional) | | (optional)  | | (optional)  |
+------+------+ +------+------+ +-------------+
       |               |
       +-------+-------+
               |
               v
     +-------------------+
     |   tugtool-core    |
     |  (always present) |
     +-------------------+
               |
               v
     +-------------------+
     |  External crates  |
     | (serde, sha2, etc)|
     +-------------------+
```

---

## 2.0.1 Specification {#specification}

### 2.0.1.1 Inputs and Outputs {#inputs-outputs}

**Inputs:**
- Current single-crate tugtool source code
- Existing Cargo.toml configuration

**Outputs:**
- Cargo workspace with 4 member crates
- Updated CLAUDE.md with new structure documentation
- All tests passing

**Key invariants:**
- Public API surface unchanged (same re-exports from `tugtool`)
- CLI behavior identical
- JSON output schemas unchanged

### 2.0.1.2 Terminology {#terminology}

- **Workspace root**: The top-level `tugtool/` directory containing `Cargo.toml`
- **Member crate**: Each crate in `crates/` directory
- **Core crate**: `tugtool-core`, the shared infrastructure
- **Language crate**: `tugtool-python`, `tugtool-rust`, etc.
- **Main crate**: `tugtool`, the binary and re-export crate

### 2.0.1.3 Public API Surface {#public-api}

**Spec S02: Re-exports from tugtool crate** {#s02-reexports}

The main `tugtool` crate must re-export all types currently accessible via `tugtool::*`:

```rust
// crates/tugtool/src/lib.rs

// Re-export core types
pub use tugtool_core::{
    // patch module
    patch::{
        Anchor, AnchorResolution, ApplyContext, ApplyResult, Conflict,
        ContentHash, Edit, EditKind, EditLabels, FileId, MaterializedPatch,
        OutputEdit, PatchSet, Precondition, Span, WorkspaceSnapshotId,
    },
    // facts module
    facts::{
        FactsStore, FileEntry, ImportEntry, ImportId, Language, ModuleEntry,
        ModuleId, ModuleKind, ReferenceEntry, ReferenceId, ReferenceKind,
        ScopeEntry, ScopeId, ScopeKind, Symbol, SymbolId, SymbolKind,
    },
    // other modules
    error::TugError,
    output::{Location, ReferenceInfo, SymbolInfo},
    session::Session,
    workspace::WorkspaceSnapshot,
    sandbox::{SandboxConfig, SandboxHandle, VerificationResult},
    text, diff, util,
};

// Re-export language modules (conditional)
#[cfg(feature = "python")]
pub use tugtool_python as python;

#[cfg(feature = "rust")]
pub use tugtool_rust as rust;

// CLI and MCP are internal (not re-exported)
```

---

## 2.0.2 Symbol Inventory {#symbol-inventory}

### 2.0.2.1 New crates {#new-crates}

| Crate | Purpose |
|-------|---------|
| `tugtool-core` | Shared infrastructure: patch, facts, session, workspace, sandbox, output, error, text, diff, util |
| `tugtool-python` | Python language support: analyzer, worker, ops |
| `tugtool-rust` | Rust language support (placeholder) |

### 2.0.2.2 New files {#new-files}

| File | Purpose |
|------|---------|
| `Cargo.toml` (root) | Workspace definition |
| `crates/tugtool/Cargo.toml` | Main binary crate manifest |
| `crates/tugtool-core/Cargo.toml` | Core crate manifest |
| `crates/tugtool-python/Cargo.toml` | Python crate manifest |
| `crates/tugtool-rust/Cargo.toml` | Rust crate manifest |
| `crates/*/src/lib.rs` | Module root for each crate |

### 2.0.2.3 Moved files {#moved-files}

**Table T03: File Movement Map** {#t03-file-moves}

| Current Location | New Location |
|-----------------|--------------|
| `src/patch.rs` | `crates/tugtool-core/src/patch.rs` |
| `src/facts/mod.rs` | `crates/tugtool-core/src/facts/mod.rs` |
| `src/error.rs` | `crates/tugtool-core/src/error.rs` |
| `src/output.rs` | `crates/tugtool-core/src/output.rs` |
| `src/session.rs` | `crates/tugtool-core/src/session.rs` |
| `src/workspace.rs` | `crates/tugtool-core/src/workspace.rs` |
| `src/sandbox.rs` | `crates/tugtool-core/src/sandbox.rs` |
| `src/text.rs` | `crates/tugtool-core/src/text.rs` |
| `src/diff.rs` | `crates/tugtool-core/src/diff.rs` |
| `src/util.rs` | `crates/tugtool-core/src/util.rs` |
| `src/python/*` | `crates/tugtool-python/src/*` |
| `src/rust/mod.rs` | `crates/tugtool-rust/src/lib.rs` |
| `src/main.rs` | `crates/tugtool/src/main.rs` |
| `src/cli.rs` | `crates/tugtool/src/cli.rs` |
| `src/mcp.rs` | `crates/tugtool/src/mcp.rs` |
| `src/testcmd.rs` | `crates/tugtool/src/testcmd.rs` |
| `src/lib.rs` | `crates/tugtool/src/lib.rs` |

---

## 2.0.3 Documentation Plan {#documentation-plan}

- [ ] Update CLAUDE.md with new directory structure
- [ ] Add workspace-level README explaining crate organization
- [ ] Document feature flags in main crate README
- [ ] Add inline documentation to each crate's lib.rs

---

## 2.0.4 Test Plan Concepts {#test-plan-concepts}

### Test Categories {#test-categories}

| Category | Purpose | When to use |
|----------|---------|-------------|
| **Unit** | Test individual functions in isolation | Each crate's internal logic |
| **Integration** | Test crates working together | Full rename/analyze flows |
| **Golden** | Compare output against snapshots | JSON schemas, patch output |

### Test Migration Strategy {#test-migration}

1. **Unit tests**: Move with their modules (embedded `#[cfg(test)]` modules stay in place)
2. **Integration tests**: Keep in workspace root `tests/` directory
3. **Golden tests**: Remain in current location, update paths as needed

### Verification Commands {#test-verification}

```bash
# Run all tests (from workspace root) - ALWAYS use --workspace during migration!
cargo nextest run --workspace

# Run only core tests
cargo nextest run -p tugtool-core

# Run only Python tests
cargo nextest run -p tugtool-python

# Run with specific features
cargo nextest run --workspace --no-default-features --features python
```

---

## 2.0.5 Execution Steps {#execution-steps}

### Step 0: Preparation and Baseline {#step-0}

**Commit:** `chore: establish baseline metrics and API surface test before workspace migration`

**References:** [D01] Workspace structure, [D11] API surface guard, (#strategy, #success-criteria)

**Artifacts:**
- Baseline test count and coverage
- Baseline build times via `cargo build --timings`
- Verification that all tests pass
- `tests/api_surface.rs` - compile-time API contract

**Tasks:**
- [x] Run `cargo nextest run --workspace` and record pass/fail counts
- [x] Run `cargo build --timings` and save HTML report
- [x] Run `cargo clippy` and fix any warnings
- [x] Create `tests/api_surface.rs` with imports of all current public types (see [D11])
- [x] Ensure clean git status

**API surface test template:**
```rust
//! Compile-only test to verify public API surface.
//! Run with: cargo test -p tugtool --features full -- api_surface

use tugtool::{
    // Core types (always available)
    patch::{Span, FileId, Edit, PatchSet, ContentHash, OutputEdit, /* ... */},
    facts::{FactsStore, Symbol, SymbolKind, ReferenceKind, /* ... */},
    error::TugError,
    // ... exhaustive list of core types
};

// Feature-gated re-exports
#[cfg(feature = "python")]
use tugtool::python;

#[test]
fn api_surface_compiles() {
    // Intentionally empty - this test verifies imports compile
}
```

**Tests:**
- [x] All existing tests pass
- [x] `tests/api_surface.rs` compiles with `--features full`

**Checkpoint:**
- [x] `cargo nextest run --workspace` - all tests pass
- [x] `cargo clippy -- -D warnings` - no warnings
- [x] `cargo fmt --check` - no formatting issues
- [x] `tests/api_surface.rs` exists and compiles with `cargo test -p tugtool --features full -- api_surface`

**Rollback:** N/A (no changes yet)

**Commit after all checkpoints pass.**

---

### Step 1: Create Hybrid Workspace Structure {#step-1}

**Commit:** `refactor: create cargo workspace structure with crates directory`

**References:** [D01] Workspace structure, [D07] Virtual workspace (end state), Table T01, List L01, (#target-structure)

**CRITICAL: Hybrid Workspace Approach**

This step creates a **hybrid workspace** where the root is BOTH a workspace AND a package.
This keeps the existing `src/` code compiling and all 639 tests running throughout migration.

**DO NOT** convert to a virtual workspace (removing `[package]`) until Step 6 after all code is migrated.

**Artifacts:**
- `crates/` directory with empty crate skeletons
- **Hybrid** Workspace Cargo.toml at root (has BOTH `[workspace]` AND `[package]` sections)
- Each crate has minimal Cargo.toml and empty lib.rs
- Existing `src/` code continues to compile and run tests

**Tasks:**
- [x] Create `crates/` directory
- [x] Create `crates/tugtool/` with minimal Cargo.toml (empty, for future main crate)
- [x] Create `crates/tugtool-core/` with minimal Cargo.toml
- [x] Create `crates/tugtool-python/` with minimal Cargo.toml
- [x] Create `crates/tugtool-rust/` with minimal Cargo.toml
- [x] Add `[workspace]` section to root Cargo.toml **WHILE KEEPING THE EXISTING `[package]` SECTION**
- [x] Add workspace-level settings (resolver, lints, profile)

**Cargo.toml structure (HYBRID - note both [workspace] AND [package]):**

```toml
# Root Cargo.toml - HYBRID WORKSPACE
# Has both [workspace] and [package] so src/ keeps compiling

[workspace]
resolver = "2"
members = [
    "crates/tugtool-core",
    "crates/tugtool-python",
    "crates/tugtool-rust",
    # NOTE: Do NOT include "crates/tugtool" yet - root IS the tugtool package during migration
]

[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Ken Kocienda"]
license = "MIT"
repository = "https://github.com/tugtool/tugtool"

[workspace.lints.rust]
warnings = "deny"

[workspace.lints.clippy]
all = { level = "deny", priority = -1 }
collapsible_if = "allow"

# KEEP THE EXISTING [package] SECTION - this is what makes src/ compile!
[package]
name = "tugtool"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
# ... keep all existing package configuration ...

# KEEP THE EXISTING [dependencies] - required for src/ to compile
[dependencies]
# ... all existing dependencies stay here ...

# KEEP THE EXISTING [[bin]], [features], etc.

[profile.release]
lto = "thin"
debug = "line-tables-only"

[profile.dev]
debug = 1
incremental = true
codegen-units = 256
lto = false
panic = "unwind"

[profile.test]
debug = 1
incremental = true
lto = false
```

**Tests:**
- [x] `cargo nextest run --workspace` - ALL 639 TESTS STILL PASS (critical!)
- [x] `cargo check -p tugtool-core` succeeds (empty crate compiles)

**Checkpoint:**
- [x] `cargo nextest run --workspace` - **all existing tests pass** (this is the critical checkpoint!)
- [x] `cargo clippy -- -D warnings` - no warnings
- [x] All four crate directories exist with Cargo.toml and src/lib.rs
- [x] Root Cargo.toml has both `[workspace]` AND `[package]` sections

**Rollback:**
- Remove `crates/` directory
- Restore original Cargo.toml from git

**Commit after all checkpoints pass.**

---

> **WARNING: Common Mistake**
>
> Do NOT remove the `[package]` section from root Cargo.toml during this step!
> Doing so creates a "virtual workspace" which orphans `src/` and breaks all tests.
> The conversion to virtual workspace happens in Step 6 AFTER all code is migrated.

---

### Step 2: Extract tugtool-core {#step-2}

This step is large and broken into substeps.

**CRITICAL: Two-Phase Migration Per Module**

For each module migration, you must:

1. **Copy** the module to the target crate
2. **Wire up imports** in the source crate to use the new location
3. **Verify tests pass** before proceeding

The root package (`src/lib.rs`) must be updated to re-export from `tugtool-core` so that:
- External code using `tugtool::patch::*` continues to work
- Internal code in `src/` can gradually migrate to `use tugtool_core::*`

After Step 2 completes:
- `tugtool-core` contains the migrated modules
- Root `src/lib.rs` re-exports from `tugtool-core`
- Original files in `src/` may be deleted OR kept as thin re-export wrappers (decide per substep)
- All tests continue to pass

#### Step 2.1: Move patch.rs and text.rs to tugtool-core {#step-2-1}

**Commit:** `refactor(core): move patch and text modules to tugtool-core`

**References:** [D02] Core crate, Table T03, Diagram Diag01, (#module-deps)

> **Why these modules move together:**
>
> `patch.rs` and `text.rs` have a mutual dependency that requires them to migrate as a unit:
> - `patch.rs` imports `crate::text::byte_offset_to_position` (used in `materialize()`)
> - `text.rs` imports `crate::patch::Span` (used in span utilities)
>
> Moving them separately would create either a broken build or messy inter-crate dependencies.
> By moving both together, the `crate::` imports resolve correctly within `tugtool-core`.

**Artifacts:**
- `crates/tugtool-core/src/patch.rs` with full implementation
- `crates/tugtool-core/src/text.rs` with full implementation
- Updated `crates/tugtool-core/Cargo.toml` with required dependencies
- Updated `crates/tugtool-core/src/lib.rs` with module exports
- Updated root `Cargo.toml` with `tugtool-core` dependency
- Updated root `src/lib.rs` to re-export from tugtool-core

**Tasks:**
- [x] Add `tugtool-core` as a dependency in root `Cargo.toml`:
      ```toml
      [dependencies]
      tugtool-core = { path = "crates/tugtool-core" }
      ```
- [x] Add dependencies to `crates/tugtool-core/Cargo.toml`: `serde`, `sha2`, `hex`
- [x] Copy `src/patch.rs` to `crates/tugtool-core/src/patch.rs`
- [x] Copy `src/text.rs` to `crates/tugtool-core/src/text.rs`
- [x] Update `crates/tugtool-core/src/lib.rs`:
      ```rust
      pub mod patch;
      pub mod text;
      ```
- [x] Verify `crate::` imports in both files resolve correctly (no changes needed - they now refer to tugtool-core)
- [x] Update root `src/lib.rs` to re-export:
      ```rust
      pub use tugtool_core::patch;
      pub use tugtool_core::text;
      ```
- [x] Delete `src/patch.rs` and `src/text.rs`
- [x] Verify BOTH core crate AND root package compile
- [x] Verify all tests pass

**Dependencies for tugtool-core/Cargo.toml:**
```toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
sha2 = "0.10"
hex = "0.4"

[dev-dependencies]
serde_json = "1.0"
```

**Tests:**
- [x] `cargo check -p tugtool-core` succeeds
- [x] `cargo nextest run --workspace` - all 639 tests pass (critical! use --workspace to include crate tests)

**Checkpoint:**
- [x] `cargo check -p tugtool-core` compiles without errors
- [x] `cargo nextest run --workspace` - **all 639 tests still pass** (use --workspace!)
- [x] `use tugtool::patch::Span` still works (API compatibility)
- [x] `use tugtool::text::byte_offset_to_position` still works (API compatibility)

**Rollback:**
- `git checkout -- crates/tugtool-core/ src/patch.rs src/text.rs src/lib.rs Cargo.toml`

**Commit after all checkpoints pass.**

---

#### Step 2.2: Move util.rs, diff.rs to tugtool-core {#step-2-2}

**Commit:** `refactor(core): move util and diff modules to tugtool-core`

**References:** [D02] Core crate, Table T03

**Artifacts:**
- `crates/tugtool-core/src/util.rs`
- `crates/tugtool-core/src/diff.rs`
- Updated root `src/lib.rs` re-exports

**Tasks:**
- [x] Copy `src/util.rs` to `crates/tugtool-core/src/util.rs`
- [x] Copy `src/diff.rs` to `crates/tugtool-core/src/diff.rs`
- [x] Add `pub mod util; pub mod diff;` to core lib.rs
- [x] Update diff.rs imports to use `crate::patch::OutputEdit`
- [x] Update root `src/lib.rs` to re-export: `pub use tugtool_core::{util, diff};`
- [x] Delete or convert `src/util.rs` and `src/diff.rs` to re-export wrappers
- [x] Verify BOTH crates compile and all tests pass

**Tests:**
- [x] `cargo check -p tugtool-core`
- [x] `cargo nextest run --workspace` - all 639 tests pass

**Checkpoint:**
- [x] Core crate compiles
- [x] `cargo nextest run --workspace` - **all 639 tests still pass**

**Rollback:**
- `git checkout -- crates/tugtool-core/ src/util.rs src/diff.rs src/lib.rs`

**Commit after all checkpoints pass.**

---

#### Step 2.3: Move facts/ to tugtool-core {#step-2-3}

**Commit:** `refactor(core): move facts module to tugtool-core`

**References:** [D02] Core crate, Table T03, Diagram Diag01

**Artifacts:**
- `crates/tugtool-core/src/facts/mod.rs`
- Updated core lib.rs
- Updated root `src/lib.rs` re-exports

**Tasks:**
- [x] Copy `src/facts/mod.rs` to `crates/tugtool-core/src/facts/mod.rs`
- [x] Add `pub mod facts;` to core lib.rs
- [x] Update imports in core: `use crate::patch::{ContentHash, FileId, Span}`
- [x] Update root `src/lib.rs` to re-export: `pub use tugtool_core::facts;`
- [x] Delete or convert `src/facts/` to re-export wrapper
- [x] Verify BOTH crates compile and all tests pass

**Tests:**
- [x] `cargo check -p tugtool-core`
- [x] `cargo nextest run --workspace` - all 639 tests pass

**Checkpoint:**
- [x] Core crate compiles
- [x] `cargo nextest run --workspace` - **all 639 tests still pass**

**Rollback:**
- `git checkout -- crates/tugtool-core/ src/facts/ src/lib.rs`

**Commit after all checkpoints pass.**

---

#### Step 2.4: Move error.rs and output.rs to tugtool-core {#step-2-4}

**Commit:** `refactor(core): move error and output modules to tugtool-core`

**References:** [D02] Core crate, Table T03

**Artifacts:**
- `crates/tugtool-core/src/error.rs`
- `crates/tugtool-core/src/output.rs`
- Updated root `src/lib.rs` re-exports

**Tasks:**
- [x] Copy `src/error.rs` to `crates/tugtool-core/src/error.rs`
- [x] Copy `src/output.rs` to `crates/tugtool-core/src/output.rs`
- [x] Add `pub mod error; pub mod output;` to core lib.rs
- [x] Add `thiserror` to core dependencies
- [x] Update output.rs imports for patch and facts (use `crate::` for core-internal refs)
- [x] Update root `src/lib.rs` to re-export: `pub use tugtool_core::{error, output};`
- [x] Delete or convert `src/error.rs` and `src/output.rs` to re-export wrappers
- [x] Verify BOTH crates compile and all tests pass

**Note:** During implementation, we created a new `types.rs` module in tugtool-core to hold shared types (`Location`, `SymbolInfo`) used by both error and output modules, avoiding circular dependencies. The `error_bridges.rs` module was created in the root crate to hold Python-specific error conversions (`From<RenameError>`, `From<WorkerError>`, `From<SessionError>`) that depend on language-specific types.

**Core dependencies update:**
```toml
thiserror = "2.0"
```

**Tests:**
- [x] `cargo check -p tugtool-core`
- [x] `cargo nextest run --workspace` - all 647 tests pass

**Checkpoint:**
- [x] Core crate compiles
- [x] `cargo nextest run --workspace` - **all 647 tests pass**

**Rollback:**
- `git checkout -- crates/tugtool-core/ src/error.rs src/output.rs src/lib.rs`

**Commit after all checkpoints pass.**

---

#### Step 2.5: Move workspace.rs and session.rs to tugtool-core {#step-2-5}

**Commit:** `refactor(core): move workspace and session modules to tugtool-core`

**References:** [D02] Core crate, Table T03

**Artifacts:**
- `crates/tugtool-core/src/workspace.rs`
- `crates/tugtool-core/src/session.rs`
- Updated root `src/lib.rs` re-exports

**Tasks:**
- [x] Copy `src/workspace.rs` to `crates/tugtool-core/src/workspace.rs`
- [x] Copy `src/session.rs` to `crates/tugtool-core/src/session.rs`
- [x] Add `pub mod workspace; pub mod session;` to core lib.rs
- [x] Add dependencies: `walkdir`, `chrono`
- [x] Update imports for workspace and session modules (use `crate::` for core-internal refs)
- [x] Update root `src/lib.rs` to re-export: `pub use tugtool_core::{workspace, session};`
- [x] Delete or convert `src/workspace.rs` and `src/session.rs` to re-export wrappers
- [x] Verify BOTH crates compile and all tests pass

**Note:** During implementation, we also:
- Added `libc` as a unix-only dependency for process checking functions
- Added `tempfile` as a dev-dependency for tests
- Moved `impl From<SessionError> for TugError` from `src/error_bridges.rs` to `crates/tugtool-core/src/error.rs` since both types are now in the same crate
- Removed SessionError bridge tests from `error_bridges.rs`

**Core dependencies update:**
```toml
chrono = { version = "0.4", default-features = false, features = ["std"] }
walkdir = "2"

[target.'cfg(unix)'.dependencies]
libc = "0.2"

[dev-dependencies]
tempfile = "3"
```

**Tests:**
- [x] `cargo check -p tugtool-core`
- [x] `cargo nextest run --workspace` - all 643 tests pass

**Checkpoint:**
- [x] Core crate compiles
- [x] `cargo nextest run --workspace` - **all 643 tests still pass**

**Rollback:**
- `git checkout -- crates/tugtool-core/ src/workspace.rs src/session.rs src/lib.rs`

**Commit after all checkpoints pass.**

---

#### Step 2.6: Move sandbox.rs to tugtool-core {#step-2-6}

**Commit:** `refactor(core): move sandbox module to tugtool-core`

**References:** [D02] Core crate, Table T03

**Artifacts:**
- `crates/tugtool-core/src/sandbox.rs`
- Updated root `src/lib.rs` re-exports

**Tasks:**
- [x] Copy `src/sandbox.rs` to `crates/tugtool-core/src/sandbox.rs`
- [x] Add `pub mod sandbox;` to core lib.rs
- [x] Add dependencies: `tempfile`, `tracing`, `wait-timeout`
- [x] Add target-specific dependency: `libc` (unix) - already added in Step 2.5
- [x] Update imports for sandbox module (use `crate::` for core-internal refs) - no changes needed, already uses crate::patch and crate::workspace
- [x] Update root `src/lib.rs` to re-export: `pub use tugtool_core::sandbox;`
- [x] Delete or convert `src/sandbox.rs` to re-export wrapper
- [x] Verify BOTH crates compile and all tests pass

**Core dependencies update:**
```toml
tempfile = "3"
tracing = "0.1"
wait-timeout = "0.2"

[target.'cfg(unix)'.dependencies]
libc = "0.2"
```

**Tests:**
- [x] `cargo check -p tugtool-core`
- [x] `cargo nextest run --workspace` - all 643 tests pass

**Checkpoint:**
- [x] Core crate compiles
- [x] `cargo nextest run --workspace` - **all 643 tests still pass** (including sandbox tests)

**Note:** During implementation, we also:
- Fixed several rustdoc warnings that were treated as errors due to `-D warnings`:
  - Escaped `[D05]` references in doc comments with `\[D05\]`
  - Escaped `List[int]` in type documentation with `List\[int\]`
  - Wrapped `<id>` in backticks to prevent HTML tag interpretation
  - Wrapped `<name>` in backticks to prevent HTML tag interpretation

**Rollback:**
- `git checkout -- crates/tugtool-core/ src/sandbox.rs src/lib.rs`

**Commit after all checkpoints pass.**

---

#### Step 2 Summary {#step-2-summary}

After completing Steps 2.1-2.6, you will have:
- Complete `tugtool-core` crate with all shared infrastructure
- All core modules migrated: patch, facts, error, output, session, workspace, sandbox, text, diff, util
- Root `src/lib.rs` re-exports everything from `tugtool-core`
- Original module files in `src/` either deleted or converted to re-export wrappers
- **All 639 tests still passing** (critical!)
- Clean dependency boundaries

**Final Step 2 Checkpoint:**
- [x] `cargo nextest run --workspace` - **all 643 tests pass** (not just core tests!)
- [x] `cargo test -p tugtool-core` - core tests pass independently
- [x] `cargo clippy -p tugtool-core -- -D warnings` - no warnings
- [x] `cargo clippy -- -D warnings` - no warnings on root package
- [x] Core crate can be used as dependency (verify with `cargo doc -p tugtool-core`)
- [x] `tests/api_surface.rs` still compiles (API contract preserved)

---

### Step 3: Extract tugtool-python {#step-3}

#### Step 3.1: Create tugtool-python crate skeleton {#step-3-1}

**Commit:** `refactor(python): create tugtool-python crate with dependency on core`

**References:** [D03] Optional languages, Table T01, (#crate-boundaries)

**Artifacts:**
- `crates/tugtool-python/Cargo.toml` with core dependency
- Basic lib.rs structure

**Tasks:**
- [x] Configure Cargo.toml with tugtool-core dependency
- [x] Set up lib.rs module structure matching python/ layout
- [x] Verify crate compiles (empty modules)

**Python crate Cargo.toml:**
```toml
[package]
name = "tugtool-python"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true

[dependencies]
tugtool-core = { path = "../tugtool-core" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "2.0"
tempfile = "3"
tracing = "0.1"

[lints]
workspace = true
```

**Checkpoint:**
- [x] `cargo check -p tugtool-python` succeeds

**Rollback:**
- `git checkout -- crates/tugtool-python/`

**Commit after all checkpoints pass.**

---

#### Step 3.2: Move Python modules to tugtool-python {#step-3-2}

**Commit:** `refactor(python): move all python modules to tugtool-python crate`

**References:** [D03] Optional languages, Table T03

**Artifacts:**
- All files from `src/python/` moved to `crates/tugtool-python/src/`
- Updated imports throughout
- Root `src/lib.rs` updated to re-export from `tugtool-python`
- Root `Cargo.toml` updated with `tugtool-python` dependency

**Tasks:**
- [ ] Add `tugtool-python` as a dependency in root `Cargo.toml`:
      ```toml
      [dependencies]
      tugtool-python = { path = "crates/tugtool-python" }
      ```
- [ ] Copy all files from `src/python/` to `crates/tugtool-python/src/`
- [ ] Update lib.rs in tugtool-python to export all public items
- [ ] Update imports: `use crate::` -> `use tugtool_core::`
- [ ] Fix any module path references
- [ ] Update root `src/lib.rs` to re-export: `pub use tugtool_python as python;`
- [ ] Delete `src/python/` directory (or convert mod.rs to re-export wrapper)
- [ ] Verify BOTH crates compile and all tests pass

**Import pattern changes:**
```rust
// Before (in python/ops/rename.rs)
use crate::facts::{FactsStore, ReferenceKind};
use crate::patch::{FileId, Span};

// After
use tugtool_core::facts::{FactsStore, ReferenceKind};
use tugtool_core::patch::{FileId, Span};
```

**Tests:**
- [ ] `cargo check -p tugtool-python`
- [ ] `cargo nextest run --workspace` - **all tests pass** (not just Python crate tests!)

**Checkpoint:**
- [ ] Python crate compiles
- [ ] `cargo nextest run --workspace` - **all tests still pass**
- [ ] `use tugtool::python::*` still works (API compatibility)

**Rollback:**
- `git checkout -- crates/tugtool-python/ src/python/ src/lib.rs Cargo.toml`

**Commit after all checkpoints pass.**

---

### Step 4: Create tugtool-rust placeholder {#step-4}

**Commit:** `refactor(rust): create tugtool-rust placeholder crate`

**References:** [D03] Optional languages, Table T01

**Artifacts:**
- `crates/tugtool-rust/Cargo.toml`
- `crates/tugtool-rust/src/lib.rs` with placeholder

**Tasks:**
- [x] Configure Cargo.toml with tugtool-core dependency
- [x] Create lib.rs with placeholder comment
- [x] Move `src/rust/mod.rs` content (if any) to lib.rs
- [x] Verify crate compiles

**Rust crate lib.rs:**
```rust
//! Rust language support for tugtool.
//!
//! This crate provides Rust-specific refactoring operations using rust-analyzer.
//!
//! **Status:** Placeholder - implementation planned for future phases.

use tugtool_core as _core;

/// Placeholder for Rust analyzer adapter.
pub struct RustAdapter;

impl RustAdapter {
    /// Create a new Rust adapter (placeholder).
    pub fn new() -> Self {
        RustAdapter
    }
}

impl Default for RustAdapter {
    fn default() -> Self {
        Self::new()
    }
}
```

**Checkpoint:**
- [x] `cargo check -p tugtool-rust` succeeds

**Rollback:**
- `git checkout -- crates/tugtool-rust/`

**Commit after all checkpoints pass.**

---

### Step 5: Refactor main tugtool crate {#step-5}

**CRITICAL: Transitioning the Binary**

This step moves CLI/MCP code to `crates/tugtool/`. At this point:
- Core infrastructure is in `tugtool-core`
- Python support is in `tugtool-python`
- The root still has `src/main.rs`, `src/cli.rs`, etc.

After this step:
- `crates/tugtool/` becomes the main binary crate
- Root `src/` only has re-export lib.rs (will be removed in Step 6)
- All tests still pass

**Important:** During this step, we temporarily have TWO places that can build the `tug` binary (root and `crates/tugtool`). This is resolved in Step 6 when we convert to virtual workspace.

#### Step 5.1: Move CLI files to main crate {#step-5-1}

**Commit:** `refactor: move main, cli, mcp, testcmd to tugtool crate`

**References:** [D04] Main crate, [D05] Testcmd, Table T03

**Artifacts:**
- `crates/tugtool/src/main.rs`
- `crates/tugtool/src/cli.rs`
- `crates/tugtool/src/mcp.rs`
- `crates/tugtool/src/testcmd.rs`
- Updated `crates/tugtool/Cargo.toml` with all dependencies

**Tasks:**
- [ ] Copy `src/main.rs` to `crates/tugtool/src/main.rs`
- [ ] Copy `src/cli.rs` to `crates/tugtool/src/cli.rs`
- [ ] Copy `src/mcp.rs` to `crates/tugtool/src/mcp.rs`
- [ ] Copy `src/testcmd.rs` to `crates/tugtool/src/testcmd.rs`
- [ ] Update `crates/tugtool/Cargo.toml` with dependencies and features (see below)
- [ ] Update imports in all moved files to use `tugtool_core::` and `tugtool_python::`
- [ ] Verify `crates/tugtool` compiles independently
- [ ] Verify root package still compiles (tests still run against root)

**Main crate Cargo.toml:**
```toml
[package]
name = "tugtool"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
description = "AI-native code transformation engine for verified, deterministic refactors"
repository.workspace = true
readme = "../../README.md"
keywords = ["refactoring", "code-transformation", "ai", "mcp", "cli"]
categories = ["development-tools", "command-line-utilities"]

[[bin]]
name = "tug"
path = "src/main.rs"

[lib]
name = "tugtool"
path = "src/lib.rs"

[dependencies]
tugtool-core = { path = "../tugtool-core" }
tugtool-python = { path = "../tugtool-python", optional = true }
tugtool-rust = { path = "../tugtool-rust", optional = true }

# CLI
clap = { version = "4", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }

# MCP (optional)
rmcp = { version = "0.12", features = ["server", "transport-io"], optional = true }
schemars = { version = "1", optional = true }

[features]
default = ["python", "mcp"]
python = ["dep:tugtool-python"]
rust = ["dep:tugtool-rust"]
mcp = ["dep:rmcp", "dep:schemars"]
full = ["python", "rust", "mcp"]

[lints]
workspace = true
```

**Tests:**
- [ ] `cargo check -p tugtool` (the crates/tugtool package)
- [ ] `cargo nextest run --workspace` - all tests still pass (against root package)

**Checkpoint:**
- [ ] `crates/tugtool` compiles: `cargo build -p tugtool`
- [ ] Root package still works: `cargo nextest run --workspace` - **all tests pass**
- [ ] Binary works from new location: `cargo run -p tugtool -- --help`

**Rollback:**
- `git checkout -- crates/tugtool/`

**Commit after all checkpoints pass.**

---

#### Step 5.2: Create lib.rs with re-exports {#step-5-2}

**Commit:** `refactor: add re-exports to tugtool lib.rs for API compatibility`

**References:** [D04] Main crate, Spec S02, (#public-api)

**Artifacts:**
- `crates/tugtool/src/lib.rs` with all re-exports

**Tasks:**
- [x] Create `crates/tugtool/src/lib.rs` with public re-exports from core
- [x] Add conditional re-exports for language crates
- [x] Add re-exports for cli, mcp, testcmd modules
- [x] Verify all previously-public types are accessible via `tugtool::*`
- [x] Update main.rs to use new module paths

**Checkpoint:**
- [x] `cargo check -p tugtool` (the crates/tugtool package)
- [x] `cargo doc -p tugtool` - documentation builds
- [x] `cargo nextest run --workspace` - **all tests still pass**

**Rollback:**
- `git checkout -- crates/tugtool/src/lib.rs`

**Commit after all checkpoints pass.**

---

#### Step 5.3: Update CLI imports and conditional compilation {#step-5-3}

**Commit:** `refactor: update CLI with conditional language support`

**References:** [D03] Optional languages, Table T02, Spec S01

**Artifacts:**
- Updated `cli.rs` with feature-gated language dispatch
- Updated `mcp.rs` with feature-gated tools

**Tasks:**
- [x] Add `#[cfg(feature = "python")]` guards to Python-specific CLI code
- [x] Add `#[cfg(feature = "rust")]` guards to Rust-specific CLI code
- [x] Update MCP tool registration with feature guards
- [x] Verify build with default features
- [x] Verify build with `--no-default-features`

**Conditional compilation pattern:**
```rust
// In cli.rs
#[cfg(feature = "python")]
use tugtool_python::ops::rename::PythonRenameOp;

pub fn run_rename(args: &RenameArgs) -> Result<(), TugError> {
    match args.language {
        #[cfg(feature = "python")]
        Language::Python => {
            // Python rename logic
        }
        #[cfg(feature = "rust")]
        Language::Rust => {
            // Rust rename logic (placeholder)
        }
        _ => {
            return Err(TugError::unsupported_language(args.language));
        }
    }
}
```

**Tests:**
- [x] `cargo build -p tugtool` (default features)
- [x] `cargo build -p tugtool --no-default-features`
- [x] `cargo build -p tugtool --features python`
- [x] `cargo build -p tugtool --features mcp` (MCP without Python - verifies no accidental Python imports)
- [x] `cargo build -p tugtool --features full`

**Checkpoint:**
- [x] All feature combinations compile (including `--features mcp` alone)
- [x] `cargo run -p tugtool -- --help` works
- [x] MCP-only build has no Python dependencies (verify with `--features mcp` compile)
  - Violation: any `use tugtool_python::` or dependency edge to `tugtool-python` without `#[cfg(feature = "python")]` guard

**Rollback:**
- `git checkout -- crates/tugtool/src/`

**Commit after all checkpoints pass.**

---

### Step 6: Clean up and finalize {#step-6}

**CRITICAL: This step converts from hybrid to virtual workspace**

At this point:
- All code has been migrated to `crates/`
- `crates/tugtool/` is the new main binary crate with all CLI/MCP code
- Root `src/` is no longer needed
- We can now safely convert to a virtual workspace

#### Step 6.1: Convert to virtual workspace and remove old src/ {#step-6-1}

**Commit:** `refactor: convert to virtual workspace, remove old src/`

**References:** [D07] Virtual workspace, Table T03, (#success-criteria)

**Artifacts:**
- Virtual workspace Cargo.toml (no `[package]` section)
- Old `src/` directory removed
- `crates/tugtool` added to workspace members

**Tasks:**
- [x] Add `"crates/tugtool"` to workspace members list
- [x] Remove `[package]` section from root Cargo.toml
- [x] Remove `[dependencies]` section from root Cargo.toml (dependencies are now in crates)
- [x] Remove `[[bin]]`, `[lib]`, `[features]` sections from root Cargo.toml
- [x] Delete `src/` directory entirely
- [x] Update `tests/` directory to use `crates/tugtool` as the test target (may need to move to `crates/tugtool/tests/`)
- [x] Update any hardcoded paths in tests

**Final root Cargo.toml (virtual workspace):**
```toml
[workspace]
resolver = "2"
members = [
    "crates/tugtool",        # NOW INCLUDED
    "crates/tugtool-core",
    "crates/tugtool-python",
    "crates/tugtool-rust",
]

[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Ken Kocienda"]
license = "MIT"
repository = "https://github.com/tugtool/tugtool"

[workspace.lints.rust]
warnings = "deny"

[workspace.lints.clippy]
all = { level = "deny", priority = -1 }
collapsible_if = "allow"

# NO [package] section - this is now a virtual workspace
# NO [dependencies] section - dependencies are in individual crates

[profile.release]
lto = "thin"
debug = "line-tables-only"

[profile.dev]
debug = 1
incremental = true
codegen-units = 256
lto = false
panic = "unwind"

[profile.test]
debug = 1
incremental = true
lto = false
```

**Checkpoint:**
- [x] `cargo build` succeeds from workspace root
- [x] `cargo nextest run --workspace` - all tests pass
- [x] `src/` directory no longer exists
- [x] Root Cargo.toml has NO `[package]` section

**Rollback:**
- `git checkout HEAD~1 -- src/ Cargo.toml`

**Commit after all checkpoints pass.**

---

#### Step 6.2: Update documentation and CI {#step-6-2}

**Commit:** `docs: update CLAUDE.md, README, and CI for workspace structure`

**References:** (#documentation-plan), [D07] Virtual workspace

**Artifacts:**
- Updated CLAUDE.md with new architecture section
- Updated README.md if needed
- Updated CI workflows for workspace commands
- Updated Justfile (if present)

**Tasks:**
- [x] Update CLAUDE.md Architecture section with new structure
- [x] Update build commands to reference workspace
- [x] Document feature flags
- [x] Update any path references
- [x] Update `.github/workflows/*.yml` to use `-p tugtool` or `--workspace` as appropriate
- [x] Update `Justfile` commands (if present) for workspace structure
- [x] Verify `cargo install --path crates/tugtool` works (document in README)

**CLAUDE.md updates:**
```markdown
## Architecture

tugtool is organized as a Cargo workspace with the following crates:

crates/
+-- tugtool/        # Main binary and CLI
+-- tugtool-core/   # Shared infrastructure
+-- tugtool-python/ # Python language support
+-- tugtool-rust/   # Rust language support (planned)

### Build Commands

# Build all crates
cargo build

# Build specific crate
cargo build -p tugtool-core

# Build with specific features
cargo build --no-default-features --features python
```

**Checkpoint:**
- [x] CLAUDE.md reflects new structure
- [x] `cargo doc --workspace` succeeds

**Rollback:**
- `git checkout -- CLAUDE.md README.md`

**Commit after all checkpoints pass.**

---

#### Step 6.3: Verify full test suite and metrics {#step-6-3}

**Commit:** `test: verify workspace migration maintains test coverage`

**References:** (#success-criteria)

**Artifacts:**
- Test report showing all tests pass
- Build timing comparison

**Tasks:**
- [x] Run full test suite: `cargo nextest run --workspace`
- [x] Run clippy: `cargo clippy --workspace -- -D warnings`
- [x] Run fmt: `cargo fmt --all --check`
- [x] Compare build times with baseline from Step 0
- [x] Verify `cargo install --path crates/tugtool` works

**Checkpoint:**
- [x] `cargo nextest run --workspace` - all tests pass (643 tests)
- [x] `cargo clippy --workspace -- -D warnings` - no warnings
- [x] `cargo fmt --all --check` - no formatting issues
- [x] Build times similar or improved vs baseline (~8s clean build)

**Rollback:** N/A (verification step)

**Commit after all checkpoints pass.**

---

## 2.0.6 Deliverables and Checkpoints {#deliverables}

**Deliverable:** Tugtool restructured as Cargo workspace with 4 member crates (tugtool, tugtool-core, tugtool-python, tugtool-rust), feature flags for language selection, and preserved API compatibility.

### Phase Exit Criteria ("Done means...") {#exit-criteria}

- [x] Root Cargo.toml is a **virtual workspace** (no `[package]` section)
- [x] `src/` directory no longer exists
- [x] All 4 crates compile independently (`cargo check -p <crate>`)
- [x] Full test suite passes (`cargo nextest run --workspace`)
- [x] `cargo build -p tugtool --no-default-features` produces working binary
- [x] `cargo build -p tugtool --features python` includes Python support
- [x] CLAUDE.md updated with new structure
- [x] CLI and JSON output unchanged from pre-migration behavior
- [x] `tests/api_surface.rs` compiles (public API contract preserved)

**Acceptance tests:**
- [x] Integration test: Full rename operation works end-to-end
- [x] Integration test: MCP server starts and responds to tool calls
- [x] Golden test: JSON output schemas unchanged
- [x] API surface test: All public re-exports accessible

**CRITICAL: Test count verification**
- [x] Final test count: 643 tests (exceeds baseline of 639)

### Milestones (Within Phase) {#milestones}

**Milestone M00: Hybrid workspace established (Step 1)** {#m00-hybrid-workspace}
- [x] Root Cargo.toml has BOTH `[workspace]` AND `[package]` sections
- [x] All 639 tests still pass
- [x] Empty crate skeletons exist in `crates/`

**Milestone M01: Core crate complete (Step 2)** {#m01-core-complete}
- [x] tugtool-core contains all shared infrastructure
- [x] Root `src/lib.rs` re-exports from tugtool-core
- [x] **All 643 tests still pass** (critical!)
- [x] Core crate tests pass independently

**Milestone M02: Python crate complete (Step 3)** {#m02-python-complete}
- [x] tugtool-python contains all Python support
- [x] Root `src/lib.rs` re-exports from tugtool-python
- [x] **All 643 tests still pass** (critical!)
- [x] Python crate tests pass independently (188 tests)

**Milestone M03: Workspace integrated (Step 5)** {#m03-workspace-integrated}
- [x] Main tugtool crate in `crates/tugtool/` composes all pieces
- [x] Feature flags work correctly
- [x] **All 643 tests still pass** (critical!)

**Milestone M04: Virtual workspace complete (Step 6)** {#m04-virtual-workspace}
- [x] Root Cargo.toml has NO `[package]` section
- [x] `src/` directory removed
- [x] **All 643 tests still pass** (critical!)

### Roadmap / Follow-ons (Explicitly Not Required for Phase Close) {#roadmap}

- [ ] Implement actual Rust language support in tugtool-rust
- [ ] Consider extracting MCP to separate crate if build times warrant
- [ ] Add per-crate CI jobs for parallel testing
- [ ] Investigate dynamic plugin loading for languages

| Checkpoint | Verification |
|------------|--------------|
| Virtual workspace | Root Cargo.toml has no `[package]` section |
| src/ removed | `! -d src` (directory does not exist) |
| Workspace compiles | `cargo build --workspace` |
| All tests pass | `cargo nextest run --workspace` (must show 643 tests) |
| Features work | `cargo build -p tugtool --no-default-features --features python` |
| No regressions | Compare test counts and build times with baseline |
| API preserved | `tests/api_surface.rs` compiles |

**CRITICAL: If test count drops below baseline at any step, STOP and investigate before proceeding.**

**Commit after all checkpoints pass.**