# PRD — Pickle Pipeline Pre-flight Gates Ergonomics (Readiness + Ticket-Audit False Positives)

**Author:** Gregory Dickson (via Claude/Pickle Rick session 2026-05-14-9d491b00)
**Date:** 2026-05-14
**Severity:** P1 (operational tax — blocks ALL ticket bundles until two undocumented `state.flags.skip_*_reason` overrides are set)
**Discovered during:** LOA-791 refinement → pickle-pipeline launch with codex backend on `loanlight-api` branch `gregory/loa-775-...` (110 commits ahead of `main`)
**Session:** `/Users/gregorydickson/.local/share/pickle-rick/sessions/2026-05-14-9d491b00`

## Problem

Brand-new ticket bundles (refined cleanly, all_success=true, 19 well-formed `linear_ticket_*.md` files written by the refinement team) **fail to enter Phase-1/pickle even once** because **two pre-flight gates** (`check-readiness.js` and `audit-ticket-bundle.js`) emit fatal exit codes on findings that are structurally false-positive for greenfield bundles.

**Observed failure modes (all on the same session):**

### Failure class A — Readiness check (`check-readiness.js` → exit 2)

1. **`source_prd` absolute path strip-and-resolve.** Ticket frontmatter generated by `/pickle-refine-prd` Step 7c uses absolute paths (e.g. `source_prd: /Users/.../sessions/.../prd_refined.md`). The readiness resolver strips the leading `/` and looks up `Users/.../sessions/.../prd_refined.md` relative to cwd. Result: ~17 findings of `Referenced ticket file path does not resolve: Users/.../prd_refined.md` per session. Verbatim from `readiness_2026-05-14.md`.

2. **Files-to-create are flagged as missing.** The forward-reference annotation syntax exists (`` `path` (forward-created)`` or `` `path` (created by ticket <hash>)``) per `check-readiness.js:25-30`, but `/pickle-refine-prd` Step 7c template does NOT teach Mortys to use it. So `Implementation Details > Files to modify/create` blocks reference files like `packages/api/scripts/check-rule-frontend-sync.ts`, `packages/app/src/lib/__tests__/operators-mirror.test.ts`, `packages/api/src/modules/portal-appraisal-rules/agency-template.service.ts` without the annotation — readiness treats them as broken refs.

3. **scope-resolver grep timeouts under `scope=branch`.** When pipeline.json carries `scope: "branch"`, `scope-setup` resolves to `base=refs/remotes/origin/<current-branch>` (the REMOTE of the same branch, not `main`). On a branch already pushed with most work present, the diff is ~1 file → `allowed=1`. The resolver then tries to walk imports for the entire repo to build the one-hop graph and hits `grep timeout status=null signal=SIGTERM error=ETIMEDOUT` 24× per readiness run, exceeding `DEFAULT_MAX_WALL_MS=60_000`. Surfaces as `performance` findings (`Contract resolution wall budget exceeded; remaining refs were not checked`).

### Failure class B — Ticket audit (`audit-ticket-bundle.js` → exit 1)

4. **`cross-doc-naming-drift` warnings treated as fatal-equivalent.** With NO disposition table present (`src/data/bundle-disposition-2026-05-04.json` / `-05-07-deferred-slots.json` / `-05-08-mega.json` — only valid for specific historical bundles), `audit-ticket-bundle.js:600` exits 1 if any finding is `severity: 'fatal'` OR `severity: 'warning'`. The warning class `cross-doc-naming-drift` flags **basename collisions between current ticket bodies and unrelated historical PRDs under `docs/superpowers/specs/`** — even when the current ticket cites the correct full path and the historical doc only used the basename. Result: 804+ findings on a 19-ticket bundle (logged: *"ticket cites `packages/api/src/lib/appraisal-pipeline/rules/condition-schema.ts` but `docs/superpowers/specs/2026-04-13-compound-rules-prd.md` uses `condition-schema.ts` (same basename, path differs)"*).

### Mux-runner halts on both

`mux-runner.js:3082` (`READINESS HALT`) and `:3110` (`TICKET AUDIT HALT`) → `recordExitReason` → pipeline phase exits 1 → pipeline-runner aborts entire phase chain.

### Operational tax measured

- **Run 1**: readiness halt after **78s** (24× grep timeouts + 33 path-resolution findings + 2 perf findings).
- **Run 2** (after `state.flags.skip_readiness_reason` set): audit halt after **1s** with 804+ `cross-doc-naming-drift` findings.
- **Run 3** (after both `skip_readiness_reason` + `skip_ticket_audit_reason` set): pipeline enters iteration 1, worker spawns. 

Until the operator sets BOTH undocumented flags, the pipeline cannot run for any greenfield ticket bundle. The flags are documented internally in `extension/CLAUDE.md` and `mux-runner.js:1733` (`skip_ticket_audit_reason`) and `:runMuxReadinessGate` (`skip_readiness_reason`) — but neither `/pickle`, `/pickle-tmux`, `/pickle-pipeline`, nor `/pickle-refine-prd` skill prompts surface them. Discovery requires grepping `mux-runner.js` for the flag name.

## Goal

Make the readiness + audit gates pass on a clean, freshly-refined ticket bundle WITHOUT requiring the operator to discover and set `state.flags.skip_*_reason` overrides. Greenfield bundles SHOULD pass both gates by construction.

## Acceptance Criteria

### AC-1 — Refinement teaches forward-reference annotation
- **AC-1.a** `spawn-refinement-team.js` Step 7c template (Mortys' ticket-writing prompt) instructs: *"For any path in `Implementation Details > Files to modify/create` that does NOT exist at HEAD yet, append the canonical forward-reference annotation: `` `path` (created by ticket <hash>)`` or `` `path` (forward-created)``."* Verify: refinement run on the LOA-791 PRD produces tickets with annotations on `check-rule-frontend-sync.ts`, `operators-mirror.test.ts`, `agency-template.service.ts`, `llm-prompts.spec.ts`.

### AC-2 — `source_prd` path handling
- **AC-2.a** Either `/pickle-refine-prd` Step 7c writes `source_prd:` as a path relative to repo root (e.g. `docs/prd-loa-791.md` if the PRD lives in the target repo) OR `check-readiness.js` recognizes the session-dir prefix and exempts paths to `${SESSION_ROOT}/*`. Pick one — implementer's call; relative-to-repo is simpler.
- **AC-2.b** Spec test in `check-readiness.test.js`: a frontmatter `source_prd: <absolute session path>` does NOT produce a `file_path` finding (assuming the file exists).

### AC-3 — `scope=branch` base-ref correctness
- **AC-3.a** `scope-resolver` MUST default `base` to `origin/main` (or `origin/<default-branch>`) when scope=branch, NOT `origin/<current-branch>`. Existing `--scope-base` flag remains an override.
- **AC-3.b** Regression in `scope-resolver.test.js`: on a feature branch with 100+ commits ahead of main, `mode=branch` returns the full feature-branch diff (`allowed=N >> 1`), not 1.
- **AC-3.c** Once base-ref is correct, `DEFAULT_MAX_WALL_MS` for `check-readiness.js`'s contract resolver should comfortably finish without `Contract resolution wall budget exceeded` perf findings on a 19-ticket bundle. If still over budget, raise the default to 120_000 or make it explicitly configurable via `pickle_settings.json`.

### AC-4 — `cross-doc-naming-drift` defect class triage
- **AC-4.a** The `cross-doc-naming-drift` defect class (currently `severity: 'warning'`, comparing ticket paths to ALL `docs/**/*.md` basenames) is either:
  - **(option a)** demoted to `severity: 'info'` so it doesn't trip the exit-1 logic at `audit-ticket-bundle.js:600`, OR
  - **(option b)** scoped to compare only against the *session's own PRD* (not historical doc archives).
  - **Recommended: option a** — historical drift signal is useful as audit logging, not as a blocking gate.
- **AC-4.b** Spec: run audit-ticket-bundle on a bundle that cites a file by full path while a historical PRD cites the same file by basename — no fatal-or-warning finding emitted.

### AC-5 — Surface the skip flags in skill prompts
- **AC-5.a** `/pickle`, `/pickle-tmux`, `/pickle-pipeline` skill help text documents `--skip-readiness-reason` and `--skip-ticket-audit-reason` flags (or equivalent), wired to set the respective `state.flags.skip_*_reason` field before mux-runner reads them.
- **AC-5.b** When either gate halts, the operator-facing error message names the override flag explicitly: *"To bypass with audit trail, set state.flags.skip_readiness_reason in state.json."* — printed by `mux-runner.js:3082` / `:3110`.

### AC-6 — Activity-event audit trail for skipped gates
- **AC-6.a** When `skip_*_reason` is honored, both gates emit a `readiness_skipped` / `ticket_audit_skipped` activity event with `gate_payload.reason` + ISO timestamp (already implemented per `extension/CLAUDE.md`). Add a CI test that this event fires on skip path.

## Risks

| ID | Risk | Severity | Mitigation |
|----|------|----------|------------|
| R-01 | Demoting `cross-doc-naming-drift` to info hides a genuine cross-doc drift class (e.g. two different files with the same basename being confused). | LOW | The check still logs the finding; only the exit-code logic changes. A future tightening can re-promote selectively after a disposition table is curated. |
| R-02 | Fixing `scope=branch` base-ref might be load-bearing for some existing flow that expected the current-branch-as-base. | MEDIUM | Grep `scope-resolver.ts` callers; cite which were OK with the bug. Verify with R-TSPF and other recent bundles that ran successfully under scope=branch. |
| R-03 | AC-3.c wall-budget raise might mask a real perf regression in the resolver. | LOW | Make it configurable per `pickle_settings.json` rather than a default bump. Add p99 wall-clock telemetry to readiness manifests. |

## Implementation Order (suggested)

| Order | AC | Effort | Notes |
|-------|----|--------|-------|
| 10 | AC-1.a refinement template forward-ref instruction | XS | One-line addition to `extension/src/refinement/team-spawn-prompt.md` (or wherever the Step 7c template lives). |
| 20 | AC-4.a demote `cross-doc-naming-drift` severity | XS | Single line change in `audit-ticket-bundle.js`. |
| 30 | AC-3.a base-ref fix | S | `scope-resolver.ts` default change + spec. |
| 40 | AC-2.a `source_prd` handling | S | Either refinement template change OR readiness resolver exemption. |
| 50 | AC-5 surface skip flags in skill help | XS | Append to skill prompts. |
| 60 | AC-6.a CI test for activity events | S | Tests exist; just verify the skip path. |

## Verification

```bash
# After fix, run on a fresh ticket bundle without any state.flags overrides:
node extension/bin/setup.js --tmux --resume "<session>" --max-iterations 500 --backend codex
# Pipeline should enter iteration 1 of phase pickle WITHOUT halt.
# Manifests should show: readiness exit 0, ticket-audit exit 0 (or honored skip-with-reason).
```

## Cross-references

- **`extension/CLAUDE.md`** — documents `state.flags.skip_readiness_reason` invariant (R-RTRC-skip).
- **`extension/bin/mux-runner.js:1697-1759`** — `runMuxReadinessGate` + `runTicketAuditGate`.
- **`extension/bin/check-readiness.js:53,891`** — usage + exit-2 trigger.
- **`extension/bin/audit-ticket-bundle.js:600`** — exit-1 trigger on any fatal-or-warning finding.
- **`extension/services/scope-resolver.ts`** — base-ref defaulting bug (AC-3).
- **`prds/MASTER_PLAN.md`** — file an entry under "Open ergonomics issues" (parent ticket).

## Acceptance verification on LOA-791 session

This PRD was filed from session `2026-05-14-9d491b00` where the bug was discovered. Acceptance can be verified by:
1. Resetting `state.flags.skip_*_reason` to empty.
2. Re-running pickle phase on the same 19-ticket bundle.
3. Confirming readiness exits 0, audit exits 0, iteration 1 spawns workers without operator intervention.
