### Phase 3: Dev (Sonnet)

> **TLDR**  -  Sonnet executes the plan task-by-task with TDD (red→green→refactor). Required: issue-tracker status moved to "In Progress" before any code, with a post-mutation verify step (re-reads the field, retries once on silent VALIDATION failures). Build verification after each task (up to 3 retries). Build-queue lock serializes concurrent xcodebuild. A Short run (`state.onlyDevelop`) uses Opus self-contained, with no Phase 2 plan. `taskType === component` **short-circuits the TDD path** and delegates the whole phase to the enabled `ai-<platform>-toolkit` marketplace plugin's component skill (`create-component`, fallback `create-ui-component`)  -  see next subsection.

## Phase 3 Pre-flight (BLOCKING, v9.0.0)

Per Locked decision 30, Phase 3 Dev consumes the analysis document as the sole design source. MCP / Figma REST / Figma URL fetches are forbidden.

Pre-flight steps (run in order, abort on failure).

**Steps 1, 2, 3, 5 and 6 apply only when Phase 1 ran.** In a Short run (`state.onlyDevelop === true`) there is no analysis doc by design, so they are recorded `not-applicable (no Phase 1 in this mode)` and skipped  -  an unconditional abort there would make every fast mode impossible. Steps 4, 7, 8 and 9 apply in every mode.

1. **Analysis document presence** (Phase 1 modes only): read `state.analysis.docStatus` and `state.analysis.docPath[]`, both set by Phase 1 Step 4.
   - `produced` | `reused` -> read the active platform's file; multi-repo runs need one per selected repo.
   - `not-applicable` -> no document by design; record it for steps 1, 2, 3, 5, 6 and skip them, as a Short run does.
   - **Abort**: `produced` but unreadable -> `ERR: analysis doc at <path> unreadable. Resume with /multi-agent:resume #N.` Producing it is Phase 1's job.

2. **Parse YAML front-matter** into `state.analysis.frontMatter`: `feature`, `platform`, `language`, `mode`, `evidence_digest`, `template_version`. **Abort** when `template_version` < `v3`; there is no degraded mode.

3. **Spec freshness**: two checks, both non-blocking, both flagged for Phase 4.
   - **Digest**: `state.run.lastAnalysisDigest` against `frontMatter.evidence_digest`. Mismatch = written from different evidence. Key absent -> `not-verifiable`, never `fresh`.
   - **Repo drift**: `git diff --name-only <state.run.analysisBaseCommit>..HEAD` intersected with Section 14 paths. Non-empty = the repo moved under the spec. This is the one that fires on a `reused` doc, where the digest still matches because the evidence inputs did not change.

4. **Code Connect mapping lookup**: for each component named in analysis Section 6 (Bileşen Envanteri), search the repo for matching `*.figma.swift` / `*.figma.kt` files. Persist hits to `state.dev.codeConnect[<componentName>]`. Missing mappings are not blockers but the Phase 4 reviewer will flag them.

5. **Standards binding citations**: read `analysis Section 21 References` table. For each row with `Rol: bağlayıcı / binding`, persist the source path into `state.dev.standardsBindings[]`. Phase 3 Dev tasks MUST cite at least one binding source per architectural decision (pre-existing Locked decision 8).

5b. **Test plan handoff (the RED input)**: read `analysis Section 15` into `state.dev.testPlan[]` (15.1 unit rows with name/arrange/act/expected/`BR-` id, 15.2 snapshot variants, 15.6 UI flows, 15.7 manual scenarios). **RED writes these tests, not invented ones** - development is TDD, so the analysis matrix is literally the first thing written. A row too vague to write from is an analysis defect: open a Section 20 question, do not improvise.

6. **Conventions handoff**: read `analysis Section 13.1 Concept Table` (Pass B output with footnotes). Persist concept-to-realization mapping into `state.dev.conventions[<concept>]`. Phase 3 implementation uses these names verbatim, and a non-empty `sharedUtilities` bucket is binding: bind those formatters/rule-facades/tokens, never hand-roll a duplicate (e.g., if Section 13.1 says "State holder: PassengerFlightViewModel", Phase 3 names the class exactly `PassengerFlightViewModel`).

7. **MCP forbidden**: calling `mcp__claude_ai_Figma__*` in Phase 3 is a violation. `smoke-no-mcp-in-dev-phases.sh` reads `state.telemetry.mcpCalls[]` after the run and fails if Phase 3 contributed an entry.

8. **Criteria ledger (required, every mode)**: the moment this phase consults a skill, a marketplace plugin skill, a stack guide or a module `CLAUDE.md` in order to write code, append an entry to `state.telemetry.skillCalls[]`:

   ```json
   {"skill": "ios-coding-standard", "phase": 3, "targetFiles": ["Sources/Login/LoginViewModel.swift"], "timestamp": "<ISO-8601>"}
   ```

   `targetFiles` is required  -  without it a skill applied to the wrong files still reads as "applied". Append at the moment of consultation, not at the end of the phase. Phase 4 Step 1.78 treats this as self-report only and resolves criteria independently; it is the one signal separating "applied to the wrong files" from "never opened".

9. **Stack skill routing (every `taskType`, when a stack toolkit plugin is enabled)**: ask each enabled toolkit's own `index` skill which skills govern this task, load them BEFORE writing code, and record each into `state.telemetry.skillCalls[]` with `routedBy: "<toolkit>:index@<version>"`. Candidates are the effective `enabledPlugins`, not a stack table. The routing table stays in the plugin  -  a copy here would be the stale one, and `rules/outside-the-pipeline.md` runs the same routing outside a run. A screen-creation task loads the routed toolkit's `workflow/create-screen` when one exists. No toolkit, or none enabled, is a recorded no-op, not a halt. Contract: [`features/stack-skill-routing.md`]($HOME/.claude/multi-agent-refs/features/stack-skill-routing.md).

The analysis document is the SOLE design source in Phase 3. Variant choices, padding values, color tokens, copy strings, accessibility identifiers, and test method names all come from the rendered Pass B cells. If something is missing in the analysis doc, the fix is to re-run `/multi-agent:analysis`, not to fetch from Figma.

<!-- progress-contract: applied -->

**Progress (per `$HOME/.claude/multi-agent-refs/progress-contract.md`):** every non-trivial step emits one `→ <verb> <object>` line at immediate flush. Canonical set for standard TDD: `→ reading <file>`, `→ writing test <name>`, `→ running xcodebuild RED`, `→ writing code <file>`, `→ running xcodebuild GREEN`, `→ committing WIP <summary>`. Component-task dispatch emits the line `→ dispatching create-component <componentName>` immediately before the Skill-tool call and then relays the plugin skill's progress lines verbatim under indent `    →`.

#### Input contract

Phase 3 consumes the Phase 2 output object conforming to `$HOME/.claude/schemas/planning-output.schema.json`  -  the task graph (`tasks[]` with `id`, `subject`, `targetFiles`, `complexity`, `blockedBy`) plus the architecture review notes. Tasks execute in dependency order; the schema's `blockedBy` field drives the ready-task picker. In a Short run (no Phase 2), Opus generates the equivalent task list inline before entering the loop below.

**Plan Todo iteration (opt-in)**: gated by `prefs.global.planTodos.enabled` (default: `false`). When enabled and Phase 2 Step 4.5 emitted a `plan.todos[]`, Phase 3 iterates via `$HOME/.claude/lib/plan-todos.sh next/start/complete/fail` instead of walking `tasks[]` directly. When disabled, the loop walks `tasks[]` from `planning-output`  -  TDD contract is unchanged. Full helper loop + state semantics: `$HOME/.claude/multi-agent-refs/features/plan-todos.md`. A todo with `sourceTag: Reuse` binds the file analysis already found; `Modify` edits in place. Writing a new file over a `Reuse` step is a Locked 11 violation and Phase 4 flags it.

**Shadow-Git checkpoints (opt-in)**: gated by `prefs.global.shadowGit.enabled` (default: `false`). When enabled, the orchestrator snapshots the worktree via `$HOME/.claude/lib/shadow-git.sh` so sub-phase rollback is possible without touching the project's real `.git` history. Lifecycle: `shadow-git.sh init` (Phase 0 baseline), `shadow-git.sh snapshot` (per step after `plan-todos complete`), `shadow-git.sh restore <sha> --files` (rollback). Modes: `per-todo-step` (default) or `per-tool-call`. Full wiring + storage cap: `$HOME/.claude/multi-agent-refs/features/shadow-git.md`.

#### Component tasks  -  delegated dispatch (taskType === "component")

When Phase 0 Step 7 classified the task as `component`, Phase 3 delegates the entire phase to the enabled `ai-<platform>-toolkit` marketplace plugin's component skill (`create-component`, fallback `create-ui-component`) via the Skill tool and does NOT run the TDD loop below. The dispatch layer passes the plugin skill the analysis Section 6 (Bileşen Envanteri) entry + Section 13.1 conventions for the named component as context. Because plugin skills do not write pipeline state, the **dispatch layer** (not the skill) owns `state.phases["3"].subphases[]`, recording a coarse component-build row - multi-agent's `phase-tracker` reads that array with no special case. Plugin resolution (dual-name), dispatch call, failure/resume, multi-repo, Short-run elisions, and the intentional cross-CLI divergence live in `$HOME/.claude/multi-agent-refs/component-dispatch.md` - read it before editing component-task behaviour here. Phase 3 still owns: progress line `-> dispatching create-component <name>`, `retryCount` cap at 3, and fallthrough to the TDD path when dispatch prerequisites are missing (`taskType` absent OR the plugin is not enabled in this repo -> log anomaly, halt or run TDD per component-dispatch.md).

For non-component taskTypes (`bugfix`, `feature`, `refactor`, `chore`), continue with the standard TDD section below.

#### Design fidelity contract (BLOCKING when the task carries a Figma reference)

Applies to EVERY task whose analysis doc carries design ground truth (Section 6 rows, node IDs, screenshots)  -  component-dispatch AND TDD-path UI work alike. MCP stays forbidden here (analysis-only rule); the analysis doc IS the design:

1. **1:1, not interpretation.** The captured frames are the reference for every UI line. Layout, variant states, copy, and colours come from the analysis doc's measured values  -  never eyeballed, never "close enough".
2. **Code Connect decides the component.** A mapped component (`CodeConnectSnippet` / repo `*.figma.swift` / `*.figma.kt` row) MUST be used verbatim  -  sound-alike substitutes are forbidden; a missing modifier is added to the component's `+Modifiers` extension, never forked. No mapping exists -> write a NEW component per the Configuration/View/Modifiers architecture; never inline ad-hoc UI into the consumer screen.
3. **Inter-component spacing is part of the design.** Gaps, paddings, and alignment BETWEEN components must match the frame's measured values, mapped to spacing tokens (`Spacing*`)  -  never invented numbers. A spacing/layout deviation is a `review_blocking` finding in Phase 4 Step 1.8, not a nitpick.

Missing design data (variant, padding, copy) → HALT and instruct the user to re-run `/multi-agent:analysis`; never guess and never call Figma MCP from this phase.

#### Re-entry from Phase 4 triage

Phase 3 runs twice in the pipeline lifetime: first for initial development, then optionally for rework after Phase 4 review. **Phase 3 never acts on raw reviewer output.** It only consumes `triage.accepted` findings  -  Fable triage in Phase 4 already filtered false-positives, deferred out-of-scope items, and rejected noise.

When re-entering from Phase 4:

1. Read `state.reviewIterations[-1]`  -  the latest iteration's **accepted** list.
2. For each `accepted` finding where `severity === "blocking"` or `severity === "important"`:
   - Treat it exactly like a Phase 2 plan task (TDD cycle, build verify).
   - Quote the original `issue` + `fix` text in the task description so the dev agent has full context.
3. `accepted.suggestion` items are applied opportunistically (no TDD loop required) unless the user asked for suggestions to be treated strictly.
4. `deferred` items are NOT actioned in this re-entry  -  they surface in Phase 7's "Follow-up items" section.
5. `rejected` items are never touched. Log their IDs + triage reasons for audit only.
6. After rework, increment `state.phases["3"].retryCount`; hard-kill at `retryCount === 3` and escalate to the user. Do not loop indefinitely.

If the latest iteration has `triage.approved === true` AND `accepted === []`, Phase 3 was entered by mistake  -  log the anomaly and return to Phase 5.

**Telemetry**: at the start of every re-entry, emit:

```bash
$HOME/.claude/scripts/log-metric.sh "$TASK_ID" 3 rework.started \
  iteration=$ITERATION accepted_blocking=$BLOCKING accepted_important=$IMPORTANT
```

---

For each task (respecting dependency order):

1. **Update task status: `in_progress` (required  -  do not skip or defer)**
   - **Why mandatory**: the board/tracker visually stays in "Todo" until this runs. Skipping it means reviewers and the rest of the team cannot see that work has started, and automations downstream (status reports, kanban swimlanes) will be wrong.
   - **Where it applies**:
     - Jira: transition issue to "In Progress" via REST API (`/rest/api/2/issue/{id}/transitions`)
     - GitHub Projects V2: `updateProjectV2ItemFieldValue` on the Status field (singleSelectOptionId)
     - Internal state: `agent-state.json` → `phases["3"].status = "in_progress"`
   - **Post-update verify** (catches silent VALIDATION errors from stale option IDs after board rebuilds):
     - Re-read the Status field after mutation.
     - If the value is NOT `In Progress` (Jira) / `In Develop` (Projects V2), re-fetch current option IDs and retry ONCE.
     - If verify still fails after retry → log error, do NOT proceed to TDD. The mutation silently failed and continuing would leave the task mis-tracked.
2. **Multi-submodule awareness**: If the project has submodules (detected in Phase 0 Step 7), development may span multiple submodules. For example, a UI component might need:
   - New types/tokens in the **common** submodule
   - The component itself in the **uicomponents** submodule
   - Work in BOTH submodules in the same task  -  this is normal and expected
3. **TDD cycle** (Launch Agent with `model: "sonnet"`)  -  gated by `state.testPolicy`: `tdd` = the loop below; `tests-after` = skip RED, author the same tests once green; `none` = author no tests (existing ones never weakened), report "tests not written - project policy" rather than a gap.

   **RED  -  Write ONE failing test first:**
   - **Rework re-entry**: if `state.reviewIterations[-1].verifyByTest.redTests[]` exists (Phase 4 Step 3.7 ran), those failing repro tests ARE the RED step for their findings  -  make them green, do not write a duplicate failing test and do not delete or weaken them. See `$HOME/.claude/multi-agent-refs/features/verify-by-test.md`.
   - Test framework: use whatever the project already uses. Detect from existing test files:
     - `@Test` / `#expect` → Swift Testing
     - `XCTestCase` / `XCTAssert` → XCTest
     - `describe` / `it` → Quick/Nimble
   - Test naming: `test{Scenario}_{Expected}` (e.g. `testKeychainReturnsNil_doesNotCrash`)
   - One test per behavior change  -  not one test per file
   - Run test to confirm RED  -  the command is the platform's, one arm per stack:
     ```bash
     case "$STACK" in
       ios)     acquire_build_lock "$TASK_ID"
                xcodebuild test -scheme "{scheme}" -destination "platform=iOS Simulator,name={simulator}" \
                  -derivedDataPath "{worktreePath}/.DerivedData" -only-testing:"{testTarget}/{testClass}/{testMethod}" 2>&1 | tail -5
                release_build_lock ;;
       android) ./gradlew test --tests "{testClass}.{testMethod}" 2>&1 | tail -5 ;;
       backend) pytest "{test_file}::{test_name}" 2>&1 | tail -5 ;;
       frontend) npm test -- --testPathPattern="{file}" 2>&1 | tail -5 ;;
     esac
     ```
   - Must fail for the RIGHT reason (expected assertion, not compilation error)

   **GREEN  -  Minimal code to pass:**
   - Smallest change that makes the test green  -  no extras
   - **Existing tests are immutable**: deleting, renaming, skipping, or weakening an existing assertion to reach green is a violation. A test may change only when the task itself changes the spec that test encodes, and the commit body must name the changed test and the spec change. (Deterministic backstop: the `test_lines_removed` signal in Phase 4 Step 1.75 flags test files that shrink.)
   - Run the same test again → must PASS
   - Run full test suite → no regressions:
     ```bash
     acquire_build_lock "$TASK_ID"
     xcodebuild test \
       -scheme "{scheme}" \
       -destination "platform=iOS Simulator,name={simulator}" \
       -derivedDataPath "{worktreePath}/.DerivedData" \
       2>&1 | tail -20
     release_build_lock
     ```

   **REFACTOR (if needed):**
   - Only if duplication or naming is poor
   - Re-run tests after refactor → still GREEN

   **Target resolution** (auto-detect once per project, cache in `agent-state.json`; ios resolves scheme + simulator, android resolves module + variant, backend/frontend need none):
   ```bash
   case "$STACK" in
     ios)     xcodebuild -list -json -project "{projectPath}" 2>/dev/null || xcodebuild -list -json -workspace "{workspacePath}" 2>/dev/null
              # prefer the scheme matching the project name, then the first non-test scheme
              xcrun simctl list devices available -j | jq '.devices | to_entries[] | select(.key | contains("iOS")) | .value[0].name' ;;
     android) ./gradlew projects 2>/dev/null | grep -E "^\+--- Project" ; ./gradlew tasks --all 2>/dev/null | grep -m5 "assemble.*Debug" ;;
   esac
   ```

4. **Build verification** (per stack; ios/android under the build queue lock, see below):
   - **ios, preferred (MCP, multi-agent-toolkit >= 3.0.0)**: `acquire_build_lock` → `mcp__multi-agent-toolkit__ios_xcodebuild({project|workspace, scheme, configuration: "Release", destination: "generic/platform=iOS", derived_data_path: "{worktreePath}/.DerivedData"})` → `release_build_lock`. Returns one line `Build: SUCCESS|FAILURE (E errors, W warnings) [xcresult-<id>]`; on failure drill in via `mcp__multi-agent-toolkit__ios_xcresult({id, mode: "errors"})`, never dump the full log.
   - **ios, fallback (raw)**: same lock pair around `xcodebuild build -scheme "{scheme}" -destination "generic/platform=iOS" -derivedDataPath "{worktreePath}/.DerivedData" 2>&1 | tail -5`.
   - **android**: lock pair around `./gradlew assembleDebug 2>&1 | tail -5` (the Gradle daemon and `build/` outputs contend across parallel worktrees exactly as DerivedData does  -  the lock applies).
   - **backend / frontend**: `python -m compileall .` / `npm run build --if-present 2>&1 | tail -5`; no lock.
5. If build fails → fix → rebuild (max 3 attempts, track `retryCount` in state).
6. **Intermediate commit** (after each completed task in the plan):
   ```bash
   git -C "{worktreePath}" add -A
   git -C "{worktreePath}" commit -m "wip({scope}): {task summary} [{jiraId}]"
   ```
   WIP commits are squashed in Phase 6 before PR. This prevents work loss if a later task fails or the session crashes.
7. Update task status: `completed` (apply the same verify rule  -  re-read the Status field and retry once on mismatch)
8. Log: "Phase 3: {task}  -  Test + Build passed"

---

#### Build Queue (parallel task serialization)

**Problem**: Multiple parallel worktrees may reach build/test phase simultaneously. `xcodebuild` cannot run in parallel  -  DerivedData, simulators, and project locks cause conflicts.

**Solution**: Lock-file based queue using `mkdir` atomicity. Before ANY `xcodebuild` call, acquire the lock; release after completion.

```bash
BUILD_LOCK="/tmp/claude-xcodebuild.lock"

acquire_build_lock() {
  local TASK_ID="${1:-unknown}"
  while ! mkdir "$BUILD_LOCK" 2>/dev/null; do
    OWNER=$(cat "$BUILD_LOCK/owner" 2>/dev/null || echo "unknown")
    LOCK_AGE=$(( $(date +%s) - $(stat -f "%m" "$BUILD_LOCK/owner" 2>/dev/null || echo 0) ))
    [ "$LOCK_AGE" -gt 900 ] && { echo "Stale lock (${LOCK_AGE}s)  -  removing"; rm -rf "$BUILD_LOCK"; continue; }
    echo "Build queue: waiting ($OWNER, ${LOCK_AGE}s)"; sleep 5
  done
  echo "$TASK_ID" > "$BUILD_LOCK/owner"
}

release_build_lock() { rm -rf "$BUILD_LOCK"; }
```

**Usage**: `acquire_build_lock "{jiraId}"` → `xcodebuild -derivedDataPath "{worktreePath}/.DerivedData" ...` → `release_build_lock`

**Key details:**

- Lock path: `/tmp/claude-xcodebuild.lock` (shared across all Claude instances)
- Stale lock timeout: 15 minutes (auto-cleanup if a session crashes)
- Each worktree uses its OWN `-derivedDataPath` to avoid cache poisoning
- `sleep 5` between retries  -  not aggressive polling
- Lock owner tracked for visibility: which task is currently building

**This applies to ALL xcodebuild calls in the pipeline:**

- Phase 3 Step 4 (build after development)
- Phase 4 Step 1 Gate 1 (build gate before review)
- Phase 4 Step 1 Gate 3 (test gate before review)

**Android**: same lock discipline  -  the Gradle daemon and `build/` outputs contend across parallel worktrees. **Backend/frontend** (Python, Node.js): no lock needed  -  these build/test in parallel without conflicts.

---

#### Step 3.5  -  Dev Critic (Evaluator-Optimizer, opt-in)

Gated by `prefs.global.devCritic.enabled` (default: `false`). When enabled, after the generator's last edit and BEFORE Phase 4: dispatch `dev-critic` sub-agent (Sonnet), run 4 deterministic gates (build / lint / test / secrets), then platform checklist. STRICT loop cap  -  **max 2 iterations** (round 2 re-checks round-1 failures only); round 3+ returns `escalate: true`. Severity routing: `blocking` → generator must fix; `important` → SHOULD fix or pass through to Phase 4; `suggestion` → generator's judgement. Full agent contract, schema, telemetry, when-to-enable: `$HOME/.claude/multi-agent-refs/features/dev-critic.md` + `$HOME/.claude/agents/dev-critic.md`.

---

#### Step 3.6  -  Code-simplifier pass (required diff shrink, before Phase 4 handoff)

After the build/test green step and BEFORE Phase 4 handoff, run one diff-shrink round (bloated diffs waste Phase 4 reviewer tokens and unfocus the PR).

1. **Dispatch ONE subagent** (`subagent_type: "general-purpose"`, model: `sonnet`); input is ONLY the working diff (`git -C "$WORKTREE" diff "origin/$BASE_BRANCH"...HEAD`) + task summary. No correctness re-review (Phase 4's job), no new features. Exactly four smells:
   - **Comment bloat**  -  comments restating code, scaffolding notes, TODO chatter
   - **Unrelated rewrites**  -  hunks outside task scope (re-formatting, unrequested rename sweeps)
   - **Dead code**  -  unused symbols, unreachable branches, commented-out blocks from this diff
   - **Over-abstraction**  -  single-call-site protocols/wrappers/helpers from this diff

   Progress line: `    → dispatching code-simplifier diff-shrink`
2. **Return contract**  -  a list of shrink edits `{ "file", "lines", "edit", "rationale", "risk": "safe|unsafe" }`; the subagent never writes files.
3. **Apply safe edits only**  -  skip `unsafe` (logged). Zero edits is normal  -  log and continue. Progress line: `    → applying shrink edits ({N} applied, {M} skipped)`
4. **Re-run build + tests** (same build-queue lock + evidence-gate rule as Step 4). Any breakage -> revert shrink edits wholesale and proceed pre-shrink; the simplifier must never cost a green state.
5. **Record tokens in the cost ledger** so Phase 7's Cost Breakdown captures the pass:

```bash
LOG_METRIC_FORWARD_TO_TRACKER=1 $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 3 dev.simplifier_pass \
  model=sonnet tokens_in=$IN tokens_out=$OUT duration_ms=$DUR \
  edits_returned=$RET edits_applied=$APPLIED edits_skipped=$SKIPPED
```

Scope guard: a single pass, never looped. Runs in a Short run too; component tasks (`taskType === "component"`) skip it (the figma skill owns its own checklist).

---

#### Short pipeline (`state.onlyDevelop === true`)

Set by the Phase 0 Step 7.5 depth picker, or by autopilot never (autopilot always runs Full). When it is true, Phase 3 runs self-contained with **Opus** (not Sonnet). No Phase 2 plan exists  -  the agent creates its own scope.

**Flow:**
1. Read task description (from Jira, GitHub issue, or free-text)
2. Lightweight file scan  -  grep/glob for relevant code (not full Explore agents)
3. Determine scope autonomously  -  no task breakdown, no user confirmation
4. Implement with TDD cycle (same RED→GREEN→REFACTOR as normal mode)
5. Build verification (same lock, same retry logic)
6. Intermediate commits (same WIP pattern)

**Key differences from normal mode:**

| Aspect | Full | Short |
|--------|--------|----------|
| Model | Sonnet | **Opus** |
| Plan source | Phase 2 task list | Self-determined |
| Task granularity | Per-plan-item | Agent decides |
| Status updates | Per task item | Single in_progress → completed |
| Scope confirmation | Phase 2 user approval | None (agent autonomous) |
| Review of the result | Phase 4 | Phase 4 (same) |

Because the agent determines its own scope here, Phase 4 is the only place that checks the result against anything external. Record every skill, plugin skill and guide consulted during this phase into `state.telemetry.skillCalls[]` with the files it was applied to  -  Phase 4 resolves the criteria set independently, and this record is what lets it tell "applied and honoured" from "never opened".

**Never combined with autopilot.** Autopilot skips the depth question and runs Full, so `onlyDevelop` is false in every unattended run. "Fast plus unattended" was `--dev autopilot` until v16.0.0 and no longer exists: something has to choose when nobody is asked, and unattended is the worst place to drop analysis and planning.

**Tracker visibility during Opus dispatch**: on Claude Code the model switch to Opus happens via subagent dispatch, and the parent widget cannot move while an Agent call is in flight. Dispatch per task from the self-generated task list (never one monolithic call for the whole phase), set the pre-dispatch `activeForm` marker, and record tokens between chunks  -  full rules in `$HOME/.claude/multi-agent-refs/tracker-contract.md` section "Delegated phases".

---

#### v2.1.0+ Multi-Repo Mode

Active when `state.projects[].length > 1` (set by Phase 0 multi-select). Single-repo flow above is preserved verbatim  -  this section adds the deltas.

**Todo tagging**: Phase 2 plan items in multi-repo mode carry a `repo` field naming the target project root. The dev agent uses this tag to dispatch each todo:

```json
{
  "id": "T-3",
  "title": "Add tokenResolver public API",
  "repo": "common",                         // ← matches state.projects[i].name
  "files": ["Sources/Common/TokenResolver.swift"],
  "tdd": { "test": "...", "code": "..." }
}
```

If a todo has no `repo` tag in multi-repo mode → log warning + ask user, do not auto-pick.

**Per-todo worktree switch**: Before each todo's TDD cycle, resolve `WT_PATH = state.projects[ findIndex(name === todo.repo) ].worktreePath` and run **all** subsequent commands with `git -C "$WT_PATH"` and `cd "$WT_PATH"` for build/test. Identity (`user.name`/`user.email`) was already pinned per worktree by Phase 0 Step 8  -  no re-config needed here.

**Status update (single ticket, multi-repo)**: The Jira/GitHub issue is shared across all repos in the group  -  call the status-update API exactly **once per todo** (not once per repo per todo). The post-mutation verify rule still applies.

**Build serialization**:
- Xcode worktrees still share `BUILD_LOCK = /tmp/claude-xcodebuild.lock`  -  multi-repo just means more callers contending for the same lock; the existing `mkdir`-based queue handles it correctly.
- Non-Xcode repos may build in parallel across worktrees  -  but ONLY when builds are independent. If repo B's build depends on repo A's freshly-published artifact (e.g. SPM local path dependency), serialize them in dependency order. Plan items declare dependencies via Phase 2's `dependsOn` field; multi-repo dispatch respects that order.

**Failure isolation**: A build/test failure in one repo's worktree must NOT silently leave the other repos in a half-built state. On failure:
1. Capture the failure into `state.projects[i].buildStatus = { ok: false, attempts: N, lastError: "..." }`
2. The ENTIRE Phase 3 retries within that repo's worktree (max 3, same as single-repo)
3. After 3 attempts, escalate; do NOT proceed to Phase 4 with any unbuilt repo

**Recording a pass (default-FAIL evidence gate):** before setting `buildStatus.ok = true`, the build output must be tee'd to a log and that log must substantiate the success  -  a zero exit code alone is not trusted. Run the evidence gate; on exit 1, do NOT record a pass:
```bash
<build-command> 2>&1 | tee "$WORKTREE/.build.log" \
  | bash $HOME/.claude/scripts/offload-ref.sh --phase 3 --label build --root "$WORKTREE"
node $HOME/.claude/scripts/evidence-gate.mjs --claim build --status passed --evidence "$WORKTREE/.build.log" \
  || { echo "build pass unverified  -  treat as failure"; /* keep buildStatus.ok=false */ }
```
This closes the gap where an agent records "built" without ever producing build output.

**Why the pipe (opt-in via `prefs.global.contextOffload.enabled`).** `tee` decides where the log is written, not how much of it the model reads. The filter parks the full text at `.multi-agent/refs/<node_id>.md` and prints a stub plus the tail, where a failing build's error already is; read that file before re-running a failed build. The evidence gate still reads the whole `.build.log`, so what counts as a verified pass is unchanged. Pref off = pass-through.

**Telemetry**: Per-repo metrics in addition to per-task metrics:
```bash
$HOME/.claude/scripts/log-metric.sh "$TASK_ID" 3 build.completed repo=common duration_ms=$D status=ok
$HOME/.claude/scripts/log-metric.sh "$TASK_ID" 3 build.completed repo=uicomponents duration_ms=$D status=ok
```

**Token forwarding:** every TDD round (red, green, refactor) that hits the dev model MUST forward token totals into the tracker so Phase 7's Cost Breakdown captures Phase 3:

```bash
LOG_METRIC_FORWARD_TO_TRACKER=1 $HOME/.claude/scripts/log-metric.sh "$TASK_ID" 3 dev.tdd_round \
  model=<sonnet|opus> step=<red|green|refactor> \
  tokens_in=$IN tokens_out=$OUT duration_ms=$DUR
```

Model resolves from the active mode: a Full run uses Sonnet, a Short run uses Opus. Best-effort. See `$HOME/.claude/multi-agent-refs/progress-contract.md#token-telemetry-forwarding`.

---

## Token telemetry  -  invoke after every LLM call

```bash
bash $HOME/.claude/scripts/phase-tracker.sh tokens 3 <input_count> <output_count>
```

Contract and rationale: `progress-contract.md` -> Token telemetry forwarding.


#### Generated trees are not yours to edit

Many repos generate part of their source: a service client from an OpenAPI spec, mock
scenario indexes, localization keys, testing identifiers, design tokens. A generated
file is regenerated on the next build, so an edit there is lost silently, and the
matching hand-authored tree is the one that takes the change.

Before writing into any path, check whether it is generated:

```bash
# a Generated/ segment, or a header saying so, is the signal
find . -type d -name Generated -not -path './.*' | head
grep -rl "DO NOT EDIT\|auto-generated\|Generated by" --include="*.swift" --include="*.kt" . | head
```

The pairing is usually `Generated/<x>` for output and `Custom<X>/` or
`CustomSources/` for input. Two concrete shapes seen in the wild:

| Want to | Wrong place | Right place |
|---|---|---|
| add a mock fixture / named scenario | a `Fixtures/` file under a generated tree | the repo's custom fixture tree, plus registering the scenario in the generated index the build reads |
| add or change a service endpoint | the generated client method | the OpenAPI source the generator consumes, then regenerate |

One run wrote a mock fixture into the generated fixtures tree; the fix commit moved it
to the custom tree and registered the scenario in the generated index. Same content,
wrong side of the generator, and the Debug menu never showed it.

When the analysis doc has not recorded which trees are generated, that is a Phase 1
gap  -  say so rather than guessing, since guessing wrong is invisible until the next
regeneration.
