Skip to content

Pin renderedstep/engine#99's head (e5c28ab): stock a floor with safe rooms and healing things when it is laid out - #375

Merged
calebl merged 2 commits into
mainfrom
fm/ta-floors-s7
Oct 30, 2026
Merged

calebl merged 2 commits into
mainfrom
fm/ta-floors-s7

Conversation

@calebl

@calebl calebl commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

Stage 7 of the dungeon plan, server half: floor supplies, healing items and safe rooms. The engine half is renderedstep/engine#99. This PR pins its head (e5c28ab) and does every item of its "What the server pin PR must change" section.

  • A floor's row names its supplies. floors.safe_rooms, plus floors.healing_count with healing_name and healing_description.
  • They are placed once, when the engine lays the floor out. A re-seed never writes, moves or removes them.
  • A safe room has locations.sanctuary set, and nobody is born in it.
  • A healing thing is a world row with items.supply = 'floor'. Neither the room's cap nor the story's cap counts it.
  • The player learns a room is safe from the engine's line on the room panel: glance.room.safe. No model is told, and no prompt template changed.
  • A world without the new keys plays as before. No checked-in story world turns them on; only the new sweep world does.

No model was called. Every command ran with OPENROUTER_API_KEY and TYPESAFE_API_KEY unset.

What changed

  1. Migration 20261031150000 AddSuppliesToFloors. The version and declarations are the engine's, so its world dumps stay byte-true. db/schema.rb moves to 2026_10_31_150000. That version is still the latest against a freshly fetched main.
  2. Models.
    • Floor validates safe_rooms and healing_count as whole numbers from 0. It requires healing_name and healing_description when healing_count > 0.
    • Item gains SUPPLIES = %w[floor], validates supply against it (nil allowed), and gains #supply?.
    • Factory traits :supply (item) and :sanctuary (location). Neither rolls dice.
  3. Loader.
    • Reads floor: safe_rooms: and floor: healing: {count, name, description} in both directions on every load. An absent key writes 0, or 0, nil, nil.
    • Reads a generic sanctuary: true on any location; absent writes false.
    • Refuses each case in the engine PR's list:
      • a non-integer or negative safe_rooms;
      • safe_rooms above rooms − 2;
      • a healing: that is not a mapping, or that has another key;
      • a non-integer or negative count;
      • a blank name or description when count > 0;
      • a non-boolean sanctuary.
    • Re-seed rule. The item finders (find_item, find_renamed_item, room_item) skip supply rows, and a file's item is always written with supply: nil. So a file thing that shares a draught's name is a different thing, and the supply row stays where the layout put it. A laid-out floor room keeps its sanctuary.
    • Item::TemplateRefresh skips copies of a supply.
  4. Exporter. Writes safe_rooms and healing: only when their counts are non-zero, and sanctuary: true on any location that has it. A floor's laid-out rooms are still not written. Round-trip test included.
  5. The caps.
    • Item.bespoke is now portable.where(kit_key: nil, supply: nil), so Item::Registry#room_for_items and #story_item_count leave supplies out.
    • Character::Registry#slots draws nobody for a sanctuary and throws no die, as the engine's slots does.
    • Registry tests cover the default limit and things_limit: 1, with two supply rows lying in the room.
    • The copiers.
      • Story::Fork and Story::Snapshot go through the exporter's document, so they carry the floor's keys and sanctuary; there is a new fork test.
      • Playthrough::SqlitePackage copies rows with SELECT *; its test now pins both columns.
      • Item::NOT_COPIED leaves supply out, so a game's copy keeps the marker.
  6. Doctor findings, all reported and never repaired:
    • sanctuary_with_a_hostile_resident: a living hostile person in the world, or someone a game has provoked (Playthrough#foes_in).
    • supply_with_a_kit_key.
    • floor_stocked_to_another_count: compares what is on record with the row's counts, clamped the way the engine's floor.rs clamps them.
  7. Protocol and glance.
    • The extension's glance_json adds "safe" to room.
    • Playthrough::Glance::Room gains :safe, and the console prints it on its own safe line.
    • Protocol::V1#glance sends room.safe.
    • openapi.json: safe is required on the room object, appears in every example, and is described as the engine PR specifies. v1.md adds it as revision 1.8.
    • A contract test and a glance test cover it.
  8. Sweep.
    • World a-stocked-floor and script a-floor-stocked-at-layout are copied from the engine PR with their comments and why: notes. The script's stripped form matches the engine's parity/scripts/ byte for byte.
    • The golden is vendored, and engine:vendored passes.
    • The checks file was written with engine:checks.
    • test/ruby_loop_coverage.yml did not need to change.
    • test/engine_vectors was re-exported. The recorded rows only gain "supply": null and "sanctuary": false, as the stage 3 pin re-export did with posted_to_id.

Things a reviewer should know

  • The doctor no longer counts supply rows in duplicate_items or the story-cap count. This is the one change beyond the engine PR's list. On the first engine:checks run, the new script's two draughts were reported as duplicate_items ("one thing to a re-seed"). That stopped being true once the loader stopped matching supply rows by name. Kit rows were already left out of both for the same reason. The checks file now says "playable, 2 warnings" (no_arc, room_without_a_layout), the same as every other floor sweep world.
  • There is no error severity in Story::Doctor (SEVERITIES is fatal and warning). So supply_with_a_kit_key, which the engine PR calls an error, is a warning here. It is not fatal: the story still plays, its room just reads as furnished.
  • parity/worlds/a-stocked-floor.sql does not need rebuilding. I rebuilt it with this branch's loader (scratch copy, db:schema:load, the README's load_world.rb, sqlite3 .dump). Every row matches the engine's dump. The only differences are created_at/updated_at timestamps and where the schema_migrations row sits in the dump (a schema load puts it in order; the engine migrated its dump, which appends it).
  • The engine PR's flag for the owner still stands: the "already spoken for" list names a draught once per row. It is out of scope here, because it is model-read text.

Gates

All ran with both provider keys unset, at b696798:

  • bin/rails test (includes game:sweep): 5487 runs, 0 failures, 0 errors, 0 skips.
  • bin/rails engine:rust_gates: PASSED for all 113 scripts (goldens, invariants, doctor and audit).
  • bin/rails engine:vendored: VENDORED, at e5c28ab.
  • bin/rails engine:kept_requests: the engine reproduces every golden vector and kept request this checkout exports (45 passed).
  • bundle exec rubocop, bin/rails zeitwerk:check and bin/brakeman: clean.
  • In ext/renderedstep, cargo fmt --check and cargo clippy --release --locked -- -D warnings: clean.

Post-update: no new Update::REGISTRY step. bin/update already migrates and rebuilds the extension when the pin moves.

After renderedstep/engine#99 merges, this PR moves the pin to its merge commit.

…rooms and healing things when it is laid out

Stage 7 of the dungeon plan, server half ("Floor supplies: healing items
and safe rooms"). A floor that says `safe_rooms:` and `healing:` is stocked
when the engine lays it out, once, for the world (VISION.md principle 7): its
safe rooms are laid out safe, with no hazard and nobody born in them
(`locations.sanctuary`), and its healing things are world rows lying one to a
room, with the supply marker and no `kit_key`. The player learns a room is
safe from the engine's line on the room panel; no model is told. A world
without the keys plays as before; no checked-in story world turns them on.

- Migration 20261031150000 AddSuppliesToFloors, the engine's version and
  declaration. schema.rb moves to 2026_10_31_150000.
- Floor validates safe_rooms and healing_count as whole numbers from 0, and
  requires healing_name and healing_description when healing_count > 0.
  Item gains SUPPLIES, validates supply in it or nil, and #supply?;
  Item.bespoke leaves supply rows out, so neither registry cap counts them.
  Factory traits :supply (item) and :sanctuary (location), neither rolling.
- Character::Registry#slots draws nobody for a sanctuary and throws no die,
  the engine's realize.rs `slots`.
- Loader reads floor `safe_rooms` and `healing: {count, name, description}`
  in both directions (absent writes 0 / 0, nil, nil), and a generic
  `sanctuary: true` on any location (absent writes false). It refuses a
  non-integer or negative safe_rooms, safe_rooms above rooms - 2, a healing
  that is not a map or has another key, a non-integer or negative count, and
  a blank name or description when count > 0. A re-seed never matches,
  moves or removes a supply row (the item finders skip them, and a file row
  is written with no supply), nor unsets a laid-out floor room's sanctuary.
  Item::TemplateRefresh skips copies of a supply.
- Exporter writes safe_rooms and healing when their counts are not zero, and
  sanctuary: true on a location that has it; a floor's laid-out rooms are
  still not written.
- Story::Doctor: sanctuary_with_a_hostile_resident (world or game),
  supply_with_a_kit_key and floor_stocked_to_another_count, all warnings,
  reported and never repaired. Supply rows join kit rows outside
  duplicate_items and the story cap count: the layout leaves one name in
  several rooms by design.
- The extension's glance_json, Playthrough::Glance::Room (and its console
  line) and Protocol::V1#glance carry the room's `safe`; openapi.json and
  v1.md add it as revision 1.8.
- Sweep: world a-stocked-floor, script a-floor-stocked-at-layout, golden
  vendored and checks file written. test/engine_vectors re-exported: the
  recorded rows gain items.supply and locations.sanctuary.
calebl added a commit to renderedstep/engine that referenced this pull request Oct 30, 2026
… b696798

The game's pin PR (renderedstep/server#375) reads safe_rooms: and healing:,
so the dump is now the one its own loader writes from a schema load: the
supplies are written by the loader rather than by hand. The rows are the
same; only timestamps, the schema digest and the order the dump lists them
in moved. No golden or records file moves.
@calebl
calebl merged commit 8d334c1 into main Oct 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant