---
title: P2 — manager loop wedges on oversized ticket: re-iterates research/plan without ever delegating to spawn-morty for completion; worker either never starts or exits without producing artifacts (R-WMW)
status: Draft
filed: 2026-05-13
priority: P2 (recoverable by manual operator intervention but recurs every time refinement produces a still-oversized ticket; pair with R-RSU which prevents the precondition)
type: bug
r_codes:
  - R-WMW-1
  - R-WMW-2
  - R-WMW-3
  - R-WMW-4
  - R-WMW-5
  - R-WMW-6
related:
  - prds/p2-pickle-refine-section-umbrella-granularity-bug.md   # R-RSU — root-cause companion. R-RSU prevents oversized tickets at refinement time; R-WMW is the safety net for the case where refinement still produces a too-large ticket (e.g. an atomic R-code that is itself oversized).
  - prds/p1-iteration-cap-and-phantom-done-handshake.md   # R-ICP-3 — phantom-Done watcher. Fires correctly on certain wedge classes; misses R-WMW class because no "Done" claim is made — the manager just iterates without ever claiming completion.
  - prds/p1-mux-runner-no-claude-manager-relaunch-on-max-turns.md   # R-MMTR (Finding #19) — adjacent surface. R-MMTR addresses manager-level max-turns at the orchestrator level (whole pipeline); R-WMW addresses per-ticket manager budget exhaustion without forward progress (one ticket).
---

# P2 — worker-manager wedge on oversized ticket: no artifact progress, no fail-loud

## Problem (one paragraph)

When a single Morty ticket exceeds the manager's claude-max-turns budget (e.g. a section-umbrella ticket covering 7+ atomic R-codes), the manager loop iterates research → plan → research → plan → ... **without ever delegating to `spawn-morty` for completion**. The worker subprocess either never starts (manager exhausted its turn budget on research/plan) or starts and exits immediately without producing artifacts (e.g. only a `"no stdin data received"` warning in the worker log). The phantom-Done watcher from R-ICP-3 doesn't fire because no "Done" claim is ever made — the manager just iterates. Pattern is **per-ticket manager budget exhaustion without forward progress**, distinct from R-MMTR which addresses orchestrator-level max-turns across multiple tickets. Reproduced 2026-05-13 in session `2026-05-13-ba01c135` ticket `3ab68cdd` (R-ICDM umbrella): 67 minutes wall-clock, 4 `worker_spawn_backend_resolved` events, only ONE `worker_session_*.log` written (157 bytes — a "no stdin data received" warning), zero `code_review_*.md`, zero `conformance_*.md`. Operator unwedged by manually committing the work the manager had partially done in the dirty tree.

## Observed incident

**Session**: `~/.local/share/pickle-rick/sessions/2026-05-13-ba01c135/`
**Ticket**: `3ab68cdd` (R-ICDM umbrella, generated by the auto-refinement bug — see R-RSU companion PRD)
**Backend**: claude

**Activity event timeline** (from `state.json` activity log):

- `T+0s` — `worker_spawn_backend_resolved` (1st)
- `T+~15min` — `worker_spawn_backend_resolved` (2nd)
- `T+~35min` — `worker_spawn_backend_resolved` (3rd)
- `T+~55min` — `worker_spawn_backend_resolved` (4th)
- `T+67min` — operator SIGINT after seeing zero artifact progress

**Worker artifacts**:

- `<ticket>/worker_session_<pid>.log` — exactly **one** file written, 157 bytes, content: `[warn] no stdin data received`. The other 3 spawn events produced no log file (worker subprocess apparently exited before its stdout pipe was bound, or was never actually spawned past resolution).
- `<ticket>/code_review_*.md` — **zero** files. Review phase never reached.
- `<ticket>/conformance_*.md` — **zero** files. Conformance phase never reached.
- `<ticket>/research_*.md` — files exist (manager produced them inline).
- `<ticket>/plan_*.md` — files exist (manager produced them inline).

**Dirty tree state at SIGINT**:

The manager had implemented R-ICDM-1..7 in the working tree as inline edits (not via the spawn-morty path) — the helper repair in `mux-runner.ts`, the prompt template tweaks in `anatomy-park.md` and `szechuan-sauce.md`, the regression test scaffolding, the trap-door pin. All correct. The manager just never escaped the research/plan iteration loop to invoke `spawn-morty completion-commit` and ship them. Operator ran `git status`, validated the diff, and committed manually as `c23ab353`.

**Manager log signature** (paraphrased from `tmux_iteration_*.log`):

```
[manager] reading PRD R-ICDM umbrella
[manager] research R-ICDM-1 helper repair...
[manager] plan R-ICDM-1 helper repair...
[manager] research R-ICDM-2 reclassifier audit...
[manager] plan R-ICDM-2 reclassifier audit...
[manager] research R-ICDM-3 promise-token relax (anatomy)...
[manager] plan R-ICDM-3 promise-token relax (anatomy)...
[manager] research R-ICDM-3 promise-token relax (szechuan)...    ← re-research, no progress
[manager] plan R-ICDM-3 promise-token relax (szechuan)...        ← re-plan, no progress
... (looping) ...
[manager] turn budget exhausted, exiting        ← clean exit, no spawn-morty
```

The manager fans out research+plan per R-code but never reaches the delegation step that would invoke `spawn-morty` with a completion brief. Effectively: **the umbrella was too large to plan AND commit within the budget; the manager spent the whole budget on plan and ran out before delegating completion**.

## Root cause

The manager's per-ticket prompt (deployed at `~/.claude/commands/pickle.md` / pickle-tmux dispatch path) instructs the manager to:

1. Read the ticket file.
2. Optionally research dependencies.
3. Optionally produce a plan.
4. **Delegate to `spawn-morty` for code edits and completion-commit.**

For an umbrella ticket covering 7 atomic R-codes, step 2-3 alone can consume the entire turn budget — there's no checkpoint that says "you've spent >50% of your turns on research+plan; cut over to delegation NOW." The manager keeps iterating research/plan because that's what its prompt invites it to do, and the budget cuts it off cleanly.

Pattern is distinct from:

- **R-MMTR** (Finding #19) — orchestrator-level max-turns across many tickets in a single mux-runner subprocess. R-MMTR's relaunch hatch handles "we did N tickets, ran out of turns; relaunch and continue with ticket N+1." R-WMW handles "we did ZERO tickets, ran out of turns on this one ticket."
- **R-ICP-3 phantom-Done watcher** — fires when the manager *claims* "Done" without commit. Here the manager never claims Done — it just exits cleanly when out of turns.
- **R-APMW** (Finding #23, anatomy-park worker-mode subprocess error kills loop) — handles worker subprocess crashes. Here the worker either never spawned or spawned and warned about no stdin; neither is a subprocess crash.

The gap is: **no detection or remediation for "manager spawned worker N times for same ticket with zero artifact progress, then ran out of turns."**

## Source surface

**Files to touch**:

- `extension/src/bin/mux-runner.ts` — add per-ticket artifact-progress tracking; emit observability event when N spawn attempts yield zero artifact growth; auto-skip ticket to `Failed` after K spawn attempts without progress.
- `extension/src/services/state-manager.ts` — schema field for `worker_artifact_progress_zero_count` per ticket (for migration safety).
- `extension/tests/integration/worker-manager-wedge-oversized.test.js` (new) — deliberately-oversized fixture ticket; assert auto-skip fires at K=5 without progress.
- `extension/src/bin/CLAUDE.md` — trap-door pin.

## Atomic tickets — R-WMW family ("worker-manager wedge")

### R-WMW-1 — Forensic analysis of session `2026-05-13-ba01c135` ticket `3ab68cdd`

- Extract from session logs:
  - Exact iteration count and turn count per `worker_spawn_backend_resolved`.
  - Exact contents of the single 157-byte worker log.
  - The manager's `claude-code` json log (`tmux_iteration_*.log`) with `stop_reason` and `num_turns` for the 4 manager invocations.
  - The directory listing of `<ticket>/` showing only `research_*.md` + `plan_*.md` artifacts, no `code_review_*.md` or `conformance_*.md`.
- Output: a forensic markdown file at `prds/research-r-wmw-3ab68cdd-2026-05-13.md` summarizing the wedge mechanics.
- File: research artifact only; no source code touched.

### R-WMW-2 — Detect "spawn-without-artifact-progress" pattern in `mux-runner.ts`

- Per ticket, track:
  - `worker_spawn_count` (increments on each `worker_spawn_backend_resolved` event).
  - `worker_artifact_count_at_spawn` (snapshot of `code_review_*.md` + `conformance_*.md` count immediately BEFORE the spawn).
  - `worker_artifact_count_at_completion` (snapshot AFTER the worker exits / before the next spawn).
- Compute `artifact_delta = after - before`. If `artifact_delta === 0` for K=3 consecutive spawns on the same ticket, emit a new activity event `worker_artifact_progress_zero` with `{ ticket_id, spawn_count, last_artifact_count, manager_turn_budget_remaining }`.
- This event is **observability only** at K=3 — it surfaces the wedge but does not act on it.
- File: `extension/src/bin/mux-runner.ts`. ~80 LOC + tests.

### R-WMW-3 — Auto-skip ticket to `Failed` after K=5 spawns without artifact progress

- After K=5 `worker_artifact_progress_zero` events in a row on the same ticket, set the ticket's state to `Failed` with `reason: 'oversized_no_progress'` and emit `worker_auto_skip_oversized` activity event.
- The mux-runner main loop moves on to the next ticket (or relaunches the manager per R-MMTR if multi-ticket queue remains).
- Failed ticket is recoverable: the dirty tree (if any) is preserved; operator can manually commit + flip to Done via `setup.js --resume`.
- Surface to monitor pane: oversized-ticket auto-skips appear in the `Failed` count with reason `oversized_no_progress` for triage.
- File: `extension/src/bin/mux-runner.ts` + `extension/types/index.ts` (new `FAILURE_REASONS` entry). ~60 LOC.

### R-WMW-4 — Regression integration test with deliberately-oversized fixture

- New: `extension/tests/integration/worker-manager-wedge-oversized.test.js`.
- Fixture: a synthetic 12-R-code "umbrella" ticket at `extension/tests/fixtures/oversized-umbrella-ticket.md`.
- Mock the spawn-morty worker to always exit cleanly with zero artifact output (simulating the wedge).
- Run mux-runner against the fixture with a 5-spawn cap.
- Assert: 5 `worker_artifact_progress_zero` events emitted (one per spawn after the first), 1 `worker_auto_skip_oversized` event emitted, ticket state ends as `Failed` with `reason: 'oversized_no_progress'`.
- ~200 LOC.
- File: new test + fixture + fake-worker helper.

### R-WMW-5 — Trap-door pin

- INVARIANT: `mux-runner` MUST track per-ticket artifact-count snapshots across worker-spawn boundaries. If `code_review_*.md` + `conformance_*.md` count is unchanged across K=3 spawns for the same ticket, MUST emit `worker_artifact_progress_zero`. After K=5 such consecutive zero-progress spawns, MUST auto-skip the ticket to `Failed` with `reason: 'oversized_no_progress'`.
- INVARIANT: `'oversized_no_progress'` is a recoverable failure reason — the dirty tree is preserved for operator inspection; the ticket can be re-queued after manual triage.
- ENFORCE: `tests/integration/worker-manager-wedge-oversized.test.js` (R-WMW-4).
- PATTERN_SHAPE: ticket spawned N+1 times in mux-runner with zero `<ticket>/code_review_*.md` and zero `<ticket>/conformance_*.md` file count delta between spawns.
- File: `extension/src/bin/CLAUDE.md`.

### R-WMW-6 — Pair with R-RSU; document the safety-net relationship

- In this PRD's "Cross-references" section AND in R-RSU's PRD cross-references:
  - R-RSU prevents the precondition (oversized tickets get generated at refinement time).
  - R-WMW catches the rare case where refinement is correct but the atomic R-code itself happens to be oversized (e.g. a single ticket whose AC list is honestly too long for a single worker turn). Without R-WMW, that case still wedges silently. With R-WMW, it fail-louds and the operator can split the ticket.
- Add a comment block in `mux-runner.ts` at the R-WMW detection site explaining: "R-WMW is the safety net for R-RSU. If R-RSU lands and works, R-WMW should rarely fire. If R-WMW fires often, refinement is producing oversized tickets and R-RSU's classifier should be tightened."
- File: documentation only (this PRD + R-RSU's PRD + a code comment).

## Estimated scope

- R-WMW-1..6 total: ~500 LOC across `mux-runner.ts`, `state-manager.ts` (schema field), `types/index.ts` (new failure reason), new integration test + fixture + fake-worker helper, trap-door pin.
- Half-day to full-day single PR.
- Pairs cleanly with R-RSU in the next bundle. R-RSU lands first; R-WMW lands second as defense-in-depth.

## Reproduction (deterministic)

1. Create a synthetic 12-R-code ticket file (cover 12 atomic sub-tasks).
2. Launch `/pickle-tmux` against a target repo with that ticket.
3. Monitor `<session>/<ticket>/` directory — observe `research_*.md` and `plan_*.md` accumulate while `code_review_*.md` and `conformance_*.md` stay at zero.
4. After ~60-90 minutes (claude-max-turns budget exhausted), observe the manager exits cleanly with no commit and the next ticket starts.
5. After R-WMW-3 lands, observe `worker_auto_skip_oversized` activity event after K=5 spawn attempts and the ticket flipping to `Failed` instead of hanging the operator.

## Cross-references

- **R-RSU** (companion finding, this batch) — root cause. R-RSU prevents oversized tickets at refinement; R-WMW is the safety net.
- **R-ICP-3 phantom-Done watcher** (`prds/p1-iteration-cap-and-phantom-done-handshake.md`) — fires on a related wedge class (manager claims Done without commit). R-WMW catches the case R-ICP-3 misses: manager never claims Done, just iterates research/plan to exhaustion.
- **R-MMTR** (Finding #19, `prds/p1-mux-runner-no-claude-manager-relaunch-on-max-turns.md`) — orchestrator-level max-turns relaunch. After R-MMTR lands, the manager that ran out of turns on the oversized ticket gets relaunched and tries the same ticket again — at which point R-WMW's per-ticket spawn-counter (R-WMW-2) accrues across the relaunch boundary, not reset. **Confirm R-WMW-2 reads the persisted spawn count from state.json, not from in-process memory**, so it survives manager relaunch.
- **R-APMW** (Finding #23, `prds/p1-anatomy-park-worker-mode-subprocess-error-kills-loop.md`) — worker-mode subprocess errors. R-APMW handles crashes; R-WMW handles no-progress.

## Notes

- The "zero artifact progress" predicate is intentionally narrow: it requires `code_review_*.md` + `conformance_*.md` count to be unchanged. If the manager produces `research_*.md` or `plan_*.md` artifacts, those don't reset the counter — that's the wedge signature (lots of research+plan, no review or conformance).
- K=3 threshold for observability emission and K=5 for auto-skip are starting values; tune based on production data once R-WMW-2 is shipping. Configurable via env: `PICKLE_WMW_OBSERVE_K=3`, `PICKLE_WMW_SKIP_K=5`.
- Operator workaround for the 2026-05-13 incident: `git status` → validate the dirty tree → `git add -A && git commit` with a manually-authored message. This worked because the manager had actually implemented the work correctly inline; the wedge was purely in the commit-delegation path. Future wedges may have less-correct dirty trees — R-WMW's fail-loud surfaces that earlier rather than after a 67-min wait.
