Files
undercooked/levels/02_double_shift.yaml
ducoterra 3426264ffb phase: 02_level_content_and_validation
Phase `02_level_content_and_validation` — final verification **green**.

- **Gate:** `bash .agents/validate.sh` → ruff ✅ · pyright 0 errors ✅ · `undercooked-levels validate` OK (2 levels) ✅ · **668 passed** (incl. 10 Playwright e2e) ✅ · coverage **100%** ✅ · repo hygiene ✅
- **Criteria:** `validate` exit 0, one line per level; `list` = `01_first_kitchen`, `02_double_shift` in campaign order; live `:8000` `/healthz` 200, `/api/levels` (+detail) carries both levels with numeric `target_score`/`time_limit` and no grid/station leakage, `/api/config` correct, unknown id → 404, startup log `content loaded: 2 level(s), 4 recipe(s)`; `validate --dir tests/fixtures/levels/_bad/` → exit 1 with named `RECIPE_STATION_MISSING` / `SERVE_REACHABLE` / `TARGET_IMPLAUSIBLE`; A8 lock test proves a YAML-only level reaches loader+API+CLI.
- **Defect fixed (pre-existing path, phase-02-triggered):** `undercooked` with unusable content printed the report then a raw traceback and exited `1`. `src/undercooked/__main__.py` now catches `ContentError` → one closing line, exit `2` (like a bad setting); 2 new tests in `tests/unit/test_main.py` (incl. non-content failures still propagate).
- **Docs pass (full, project-wide):** README (content gate + exit `2`, config section), AGENTS.md §3, `docs/LEVELS.md` (startup refusal + exact CLI exit codes), `docs/DEPLOYMENT.md` (crash-loop diagnosis bullet); DESIGN/TESTING re-read, consistent.
- **Container re-verified (no phase-01 regression):** `podman build -f Containerfile` succeeds; the image ships `levels/` and serves `/healthz` 200, `/api/levels` (2 levels), `/` 200 as non-root.
- **Deviation to note:** the phase overview names `tests/fixtures/levels/bad/`; the actual broken-fixture dir is `tests/fixtures/levels/_bad/` (the `_` prefix is what keeps it out of normal discovery). Overview files are read-only to me — README/LEVELS.md document the real path.
- No git index/history commands run; all changes left unstaged for the harness. ntfy notification sent.
2026-10-06 11:03:28 -04:00

50 lines
1.6 KiB
YAML

# levels/02_double_shift.yaml — campaign level 2: two dishes at once.
# Schema: docs/LEVELS.md · validated at startup and by `undercooked-levels
# validate`. The counter bands pinch the kitchen into three rooms joined by
# one narrow walk-through.
id: 02_double_shift
name: Double Shift
order: 2
difficulty: 2
time_limit: 130
target_score: 80
blurb: >-
Salads join the soup and the orders overlap now. Two counter bands pinch the
floor, so someone is always walking the long way through the gap — plan who
crosses and who stays on their side.
grid:
- "#############"
- "#T....C....O#"
- "#.....S.....#"
- "####=...=####"
- "#....P.X....#"
- "#...........#"
- "####=...=####"
- "#..K.....L..#"
- "#############"
tiles:
"#": {kind: wall}
".": {kind: floor}
"=": {kind: counter}
"T": {kind: floor, station: spawn_shelf, item: tomato}
"O": {kind: floor, station: spawn_shelf, item: onion}
"L": {kind: floor, station: spawn_shelf, item: lettuce}
"C": {kind: floor, station: chop_board}
"S": {kind: floor, station: stove}
"P": {kind: floor, station: prep}
"X": {kind: floor, station: serve}
"K": {kind: floor, station: sink}
spawns: [[2, 2], [10, 5]]
orders:
pool: [soup, salad]
concurrent: 2
spawn_interval: [12, 16]
deadline: 52
hazards: []
escalation_notes: >-
Moves four escalation axes vs 01_first_kitchen (PLAN §8 requires two):
recipe_complexity 1 -> 2 (salad joins), order_pressure 0.056 -> 0.143
(2 concurrent at shorter intervals), time_budget 0.30 -> 0.62 target/s,
and layout_hostility 0.35 -> 0.48 blocking tiles with the counter pinch.