# W20E6 E2E Recovery Plan

Status: design / coordination note. No production code or prompt pack changes in
this document.

## Context

The W20E5 Geumwoldo fresh-project E2E run proved the image-call cap path but
did not complete the W20 shot-aware background path.

Observed run:

- Project: `26ebc32a-9362-498d-83b7-01506e8ee2df`
- Episode: `255f2175-da33-40fa-9b7a-5fdaf8cec9d5`
- Run mode: `category=all`, `approve_image_generation=true`,
  `image_call_cap=15`
- Final status: `episode.status=error`
- Step summary: `38 completed`, `1 partial`, `1 failed`, `1 not_applicable`,
  `12 pending`, `11 blocked`
- `floor_plan_render`: completed, 10 floor-plan PNGs reported `ok`
- `floor_plan_geometry_readback`: partial, 5 ok / 5 failed, real VLM calls 10
- `shot_aware_bg_render_plan`: failed before planner LLM call
- Image cap: 9 used / 15 cap, no budget-exceeded exception

The cap gate is therefore not the blocker. W20E6 should focus on the W20A2
readback contract and the W20B staged-shot readiness contract.

## Locked Direction

W20E6 must preserve the original product direction:

1. Final BG images are generated after shot framing, camera requirements,
   visible space, subject position, and state cues are known.
2. Base location dossier remains separate from the shot-aware BG render plan.
3. Floor plans remain structural: rooms, openings, fixed furniture, fixtures.
   Transient state and event traces stay out of FP.
4. Code computes geometry candidates only: camera cells, look-at cells, view
   cones, visible-unit supersets. LLM chooses final camera/ref/anchor policy.
5. Reference selection remains LLM-owned. Code supplies candidates and
   structural validation only. Deterministic top-2 reference selection stays
   superseded.
6. The reference graph is planned globally from staged shot requirements before
   final BG image generation.
7. I2I is the renderer, VLM is an observer, code is the geometric/structural
   validator, and LLM owns semantic policy decisions.

## Evidence

### W20A2 Readback

`floor_plan_vlm_provider.validate_provider_output` currently rejects any two
observed markers sharing the same 10x10 cell:

- `backend/app/modules/pipeline/floor_plan_vlm_provider.py:333-370`

`floor_plan_geometry_readback._validate_readback_cells` repeats the same
hard-fail:

- `backend/app/modules/pipeline/floor_plan_geometry_readback.py:262-297`

The E2E failure mode matches those gates:

- `fp_l05_stairs`: marker cell collision
- `fp_l05_yard`: marker cell collision
- `fp_l06_interior_main`: marker cell collision
- `fp_l19_wheelhouse`: two marker cell collisions
- `fp_police_office_main`: empty response content

The collision failures do not prove invalid FP structure by themselves. A 10x10
grid is coarse, and two visible markers can legitimately land in one cell. The
strict invariants that must remain are marker number uniqueness, in-grid
coordinates, base-kind agreement, and exact missing/extra marker accounting.

### W20B Readiness

`shot_aware_bg_render_plan._consuming_shot_readiness` currently fails a BG if
any `applies_to_shots` item is not present in `shot_staging`:

- `backend/app/modules/pipeline/shot_aware_bg_render_plan.py:120-152`

`build_render_plan_for_fp` turns that into a hard pre-provider failure:

- `backend/app/modules/pipeline/shot_aware_bg_render_plan.py:970-992`

That is structurally too strict for the intended W20 flow. `shot_staging`
contains selected/renderable staged shots, while `background_master_plan`
`applies_to_shots` can include non-selected consuming shots. The shot-aware
planner should plan from staged shot requirements, not require every consumer
shot in the episode to be staged.

The graph completeness validator also currently expects every BG in the
dwelling's dossier graph:

- `backend/app/modules/pipeline/shot_aware_bg_render_plan.py:432-459`
- `backend/app/modules/pipeline/shot_aware_bg_render_plan.py:974-976`

For a staged-shot render plan, the graph completeness set should be the
renderable/staged BG subset for that FP, with omitted BGs surfaced as
diagnostics.

### Floor Plan ImageAsset Mismatch

The floor-plan checkpoint reports 10 ok floor plans, but DB `image_asset`
contains 9 floor-plan rows. The log identifies the skipped row:

- `floor_plan_render: fp_l10_exterior_entrance primary_location  has no canon`
- `floor_plan_render verify: 1 fp skipped (no_primary_loc_id)`

Code path:

- `backend/app/core/steps/floor_plan_render_step.py:551-566`

This is not the run blocker because downstream W20A/W20B consume checkpoint
paths, not floor-plan ImageAsset rows. It should still be kept visible as a
separate cleanup/audit item.

## W20E6-A: Readback Robustness

Goal: let real VLM readback represent coarse-grid marker collisions without
silently accepting wrong markers.

Proposed contract:

- Keep hard failures for:
  - non-dict provider output
  - status/fp_id/grid mismatch
  - duplicate marker numbers
  - out-of-grid cells
  - unknown/state-overlay kind
  - kind mismatch against dossier `base_layer_decision`
  - missing/extra marker self-report mismatch
- Change duplicate 10x10 cell from hard blocker to structured diagnostic:
  - provider normalized readback includes collision diagnostics
  - geometry candidate output carries `cell_collision_diagnostics`
  - marker numbers remain distinct; `marker_cells[number]` remains exact
- Deduplicate derived candidate lists where needed, but do not discard marker
  observations.
- Empty content remains fail-closed for that FP. No automatic retry unless a
  separate explicit real-API budget policy is approved.

Expected behavior:

- Collision FPs become usable if all exact-ID/kind/missing/extra checks pass.
- A failed/empty-content FP remains failed and is omitted from W20B planning.
- No scenario-specific exceptions.

Suggested no-network tests:

- provider validator accepts two different marker numbers in the same cell and
  surfaces collision diagnostics.
- duplicate marker number still fails.
- missing/extra mismatch still fails.
- geometry candidates accept colliding cells and keep both marker numbers.
- candidate cell arrays are deduped where a consumer expects unique cells.

## W20E6-B: Staged-Shot Readiness Contract

Goal: plan final BGs only for staged/renderable shot requirements, while still
recording omitted non-staged consumers.

Proposed contract:

- For each BG, split `applies_to_shots` into:
  - `surfaced_shots`: exact shot IDs found in `shot_staging`
  - `omitted_unstaged_shots`: exact shot IDs missing from `shot_staging`
- A BG is renderable for W20B if `surfaced_shots` is non-empty.
- An FP is renderable if it has readback `ok` and at least one renderable BG.
- A BG/FP with no staged shots is `not_applicable`, not `failed`.
- Planner input must expose the omitted list as diagnostics, not as a blocker.
- `same_fp_bg_ids` passed to graph completeness should be the staged/renderable
  BG set, not all dossier BGs.
- Prompt/user template should make clear that the LLM must plan from staged
  shots only and must not invent requirements for omitted shots.

Non-goals:

- Do not let code choose anchor/camera/ref policy.
- Do not classify same-space semantics in code.
- Do not reintroduce deterministic top-K references.

Suggested no-network tests:

- BG with some staged and some unstaged shots passes readiness and records
  omitted IDs.
- BG with zero staged shots is excluded from the planner target set.
- FP with no staged BGs returns `not_applicable`.
- LLM provider is invoked only for renderable FPs.
- Graph completeness validates against staged BG IDs only.
- Existing DAG, max-ref, camera candidate, same-fp, and two-ref validators
  remain enforced.

## W20E6-C: Floor-Plan ImageAsset Audit

Goal: keep the 10 ok floor-plan PNGs visible even when one FP has no
`primary_loc_id`.

Recommended classification:

- Not a W20E6 blocker for shot-aware BG planning.
- Add or preserve explicit diagnostics for skipped ImageAsset registration.
- Do not invent an entity/canon fallback in W20E6 unless a later consumer
  requires floor-plan ImageAsset rows rather than checkpoint PNG paths.

Suggested tests:

- floor-plan render manifest can contain an ok FP whose ImageAsset registration
  is skipped with a reason.
- verify/report path names skipped FPs by exact `fp_id`.

## Rerun Gate

After W20E6-A/B land and no-network tests pass:

1. Do not rerun real E2E automatically.
2. First run focused tests for readback, geometry, shot-aware planner, step
   wrappers, and catalog/manifest count if changed.
3. Then request explicit approval for a bounded rerun:
   - either resume from `floor_plan_geometry_readback`
   - or create a fresh project/episode if checkpoint state is too contaminated
4. Real VLM/image/LLM calls require an explicit user trigger.

## Claude Coordination Rule

Claude should implement code/prompt changes only after Codex approves the
patch plan. Codex should not edit production code or prompt packs for this
wave. Codex may maintain this document and review Claude's proposed patches.
