---
title: P3 — pipeline-runner dirty-tree guard fires before bundle's own cleanup ticket can run
status: Draft
date: 2026-05-04
priority: P3
type: bug
peer_prds:
  related:
    - prds/p1-bug-fix-bundle-2026-05-04.md          # observed here — R-BUNDLE-CLEANUP cannot self-execute
    - prds/p1-iteration-cap-and-phantom-done-handshake.md  # adjacent — pipeline-runner exit-code semantics
---

# PRD — pipeline-runner dirty-tree guard blocks self-healing cleanup tickets

## Symptom

Bundle session `2026-05-04-f416c6cc` (62 atomic tickets, codex-spark backend) launched 2026-05-04 13:18 local. pipeline-runner exited fatal at 13:19:

```
[2026-05-04T18:18:52.981Z] pipeline-runner started
[2026-05-04T18:18:53.064Z] ensureMonitorWindow: created 4-pane monitor (mode=pickle) on pipeline-f416c6cc
[2026-05-04T18:18:53.066Z] backend resolved: codex (source: state.json)
[FATAL] Working tree at /Users/gregorydickson/loanlight/pickle-rick/pickle-rick-claude is dirty
       (ignored prefixes: prds, docs).
       Commit, stash, or discard changes before starting the pipeline (git status).
```

The single dirty file was `bundle/ac-dr-02.json` — non-deterministic test debris regenerated by the `verify-recapture-fired` checker on every test run with random temp-dir paths in `state_path`. The bundle's own ticket order=220 (`R-BUNDLE-CLEANUP`) is *exactly* the ticket that gitignores + `git rm --cached` this file. But it cannot run because the pipeline-runner's pre-iteration guard fires first.

Workaround applied 2026-05-04: `git checkout -- bundle/ac-dr-02.json` (file is committed at HEAD, just timestamp-drifted) → tree clean → re-launch.

## Why this is a distinct bug

Two conditions compound:

1. `bundle-state-integrity.ts` (AC-BUNDLE-03 audit) **needs** `bundle/ac-dr-02.json` to be a tracked checked-in evidence file. The verifier rewrites the file on every test run.
2. `pipeline-runner.ts` startup **forbids** any dirty file outside the `prds`/`docs` prefix list. `bundle/` is not in the ignore list.

So every test run dirties a file that pipeline-runner refuses to launch over. **Any pipeline session that follows a recent test-run is one `git checkout`-or-`git stash` away from launching.** Today's 2026-05-04 PM session caught this on a fresh launch attempt because `bundle/ac-dr-02.json` was modified by a prior test run earlier in the session.

Beyond the recurring-friction angle, the bigger structural bug is **chicken-and-egg cleanup tickets**: any bundle that includes a "clean up state X" ticket where X is the very condition pipeline-runner refuses to start under is unrunnable without manual pre-launch work.

## Reproducer

1. `cd /Users/gregorydickson/loanlight/pickle-rick/pickle-rick-claude && cd extension && npm run test:fast` — modifies `bundle/ac-dr-02.json` with new ISO timestamp + random temp-dir hash in `state_path`.
2. `git status --short` → `M bundle/ac-dr-02.json`.
3. Launch any pipeline session via `/pickle-pipeline <prd-path>` or `bash $SESSION_ROOT/launch.sh $SESSION_ROOT`.
4. pipeline-runner exits FATAL within ~3 seconds with "Working tree dirty" message.
5. `git checkout -- bundle/ac-dr-02.json` → tree clean → re-launch succeeds.

## Root causes

### RC-1 — `verify-recapture-fired` writes ephemeral data into a tracked evidence file

`bundle/ac-dr-02.json` is supposed to be evidence that the AC-BUNDLE-03 (existing trap-door) audit ran, but the checker stores `checked_at: <ISO>` and `state_path: /var/folders/.../verify-recapture-missing-XXXX/state.json`. Both fields are unstable per-run. A canonical evidence file should be content-stable across runs, with checker metadata in a separate untracked artifact (or omitted entirely).

### RC-2 — pipeline-runner's dirty-tree guard ignore list is too narrow

The guard ignores `prds/` and `docs/` (both contain documentation that's expected to be modified during PRD refinement). It does NOT ignore:
- `bundle/` (test-debris evidence files like ac-dr-02.json)
- Any file matched by `.gitignore` (the canonical ignore list)
- Files modified by prior test runs in the same session (no per-pipeline-launch baseline)

A more correct guard would consult `.gitignore` directly (so newly-gitignored files are skipped without code change) AND exempt files that are tracked-but-only-have-runtime-mutation (a separate config list).

## Functional Requirements

| ID | Requirement | Priority |
|---|---|---|
| **R-PDT-1** | `extension/src/services/verify-recapture-fired.ts` (or wherever the AC-BUNDLE-03 evidence is written) MUST stop writing ephemeral data into the tracked evidence file. Either (a) write checker metadata to a separate untracked artifact (`bundle/ac-dr-02.runtime.json`, gitignored), or (b) compute a stable digest of state contents instead of pointing at the temp dir, or (c) move evidence creation entirely to `${SESSION_ROOT}` and out of the working tree. | P2 |
| **R-PDT-2** | pipeline-runner dirty-tree guard consults `.gitignore` + a config list `extension/.pipeline-runner-dirty-allowed.json` for tracked-but-runtime-mutated files. New gitignored files are auto-exempted. | P3 |
| **R-PDT-3** | When the dirty-tree guard fires, stderr message MUST list the dirty files explicitly (currently only a generic warning). Operators don't have to run `git status` to discover what blocked launch. | P3 |
| **R-PDT-4** | New trap-door entry in `extension/CLAUDE.md`: `src/bin/pipeline-runner.ts (dirty-tree guard)` — INVARIANT: guard ignore list = `prds/` + `docs/` + `.gitignore` patterns + `extension/.pipeline-runner-dirty-allowed.json`; ENFORCE: `extension/tests/integration/pipeline-runner-dirty-tree-guard.test.js`. | P3 |

## Acceptance Criteria

- **AC-PDT-01** — After R-PDT-1 lands, `bundle/ac-dr-02.json` is no longer modified by a clean test run. Verify: `cd extension && npm run test:fast && git diff --quiet bundle/ac-dr-02.json` — Type: integration.
- **AC-PDT-02** — Dirty-tree guard ignore list consults `.gitignore`. Verify: regression fixture adds a tracked file `foo.txt`, gitignores it, leaves it dirty, launches pipeline-runner; runner exits 0 (not FATAL). Type: test.
- **AC-PDT-03** — Stderr on FATAL dirty-tree includes specific file list (one filename per line). Type: test.
- **AC-PDT-04** — Trap-door enforced. Type: lint.

## Workaround until R-PDT-1..4 ship

```bash
# Before any pipeline launch:
cd /Users/gregorydickson/loanlight/pickle-rick/pickle-rick-claude
git checkout -- bundle/ac-dr-02.json   # if dirty
# OR a more aggressive option:
git stash --keep-index
# Launch.
# git stash pop afterwards if there were OTHER unrelated changes.
```

This workaround is documented in `prds/p1-bug-fix-bundle-2026-05-04.md` "Cleanup — pre-launch" section, but the documentation only helps operators who read it BEFORE attempting launch.

## Cross-references

- Empirical session: `~/.local/share/pickle-rick/sessions/2026-05-04-f416c6cc/`
- Pipeline-runner log: `${SESSION_ROOT}/pipeline-runner.log` (4 lines, exit at iteration 0)
- Bundle PRD that captures the cleanup ticket: `prds/p1-bug-fix-bundle-2026-05-04.md` Section "Cleanup — pre-launch" + ticket order=220
- Cycle-3 codebase-analyst NEW-P0 finding for the file's non-determinism: `~/.local/share/pickle-rick/sessions/2026-05-04-f416c6cc/refinement/analysis_codebase.md` "bundle/ac-dr-02.json is non-deterministic test debris committed to the tree"
- Pipeline-runner guard site: `extension/src/bin/pipeline-runner.ts` (dirty-tree check site — search for "Working tree" + "dirty")

— Pickle Rick out. *belch*
