# Task 8: RoomCarver Pass

**Depends on:** Task 6

## Context Files
- `.dev/2026-05-28-procgen-map-core/spec/design.md` — §4.7 (Shaper role), §5 (data flow).
- `.dev/2026-05-28-procgen-map-core/README.md` — inter-pass data contract (RoomCarver row).
- `reikhelm-core/src/pass.rs` — `Pass`, `GenContext` (Task 6).
- `reikhelm-core/src/region.rs`, `reikhelm-core/src/map.rs` — `Region`, `RegionKind::Room`, `Tile::Floor`.
- `reikhelm-core/src/passes/mod.rs` — append the module declaration here.

## Files
- Create: `reikhelm-core/src/passes/room.rs` — `RoomConfig`, `RoomCarver`.
- Modify: `reikhelm-core/src/passes/mod.rs` — append `pub mod room;` and re-export `RoomCarver`, `RoomConfig`.

## What to Build
A shaper (spec §4.7) that turns BSP leaves into actual rooms. For each existing `Room` region (the placeholders from `BspPartition`), it picks a room rectangle that fits inside the leaf `bounds` (random size within `RoomConfig` limits, leaving a margin from the leaf edge so corridors and walls have room), carves `Tile::Floor` into `ctx.tiles` over that rectangle, and updates the region's `bounds` to the actual room rect and fills `cells` with the carved points. A leaf too small to hold a `min_size` room is skipped gracefully: its region is **left in place with empty `cells` (never removed)** — removal would break the `RegionId`-as-index invariant (Task 5). A Room region with empty `cells` denotes "not actually a room"; downstream room consumers (MstConnect, CorridorCarver) treat only Room regions with non-empty `cells` as real rooms. (In the assembled dungeon recipe this case never fires — Task 12's config validation guarantees every leaf fits a room — but the pass must handle it without panicking per spec §8.)

This pass reads/writes `ctx.regions` and `ctx.tiles` only; it does **not** depend on `BspPartition`'s code, only on the documented "placeholder Room regions" precondition — so tests construct that precondition directly.

## Interface Dependencies
- **Consumes:** `Pass`, `GenContext`, `Rng` (Task 6); `Region`, `Tile` (Task 5).
- **Produces:**
  - `struct RoomConfig { min_size: i32, max_size: i32, margin: i32 }` (+ `Default`).
  - `struct RoomCarver { /* holds RoomConfig */ }` — `new(cfg) -> Self`; `impl Pass` with `name() == "room_carver"`.
  - **Writes to context:** `Floor` tiles; finalized Room region `bounds`/`cells`.

## Tests
- Given a `GenContext` with two placeholder Room regions over a `Wall` grid, after running: each carved room's `bounds` lies within its original leaf bounds and within the map; the floor cell count matches `bounds` area.
- Carved floors do not extend outside the originating leaf (margin respected).
- A leaf smaller than `min_size` is skipped without panicking.
- Determinism: same seed/sub-stream → identical carved rooms.

## Success Criteria
- [ ] `RoomCarver` carves `Floor` rooms inside leaf bounds, respecting size/margin, and finalizes region `bounds`/`cells`.
- [ ] Undersized leaves are handled gracefully (no panic).
- [ ] `pub mod room;` appended to `passes/mod.rs`.
- [ ] Tests pass: `cargo test -p reikhelm-core room`

## Commit
`"feat(core): RoomCarver pass"`
