Skip to content

Pin renderedstep/engine#97's head (e8a28d2): raise the game's level once on each floor that says so, and heal to full - #373

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

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

Conversation

@calebl

@calebl calebl commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

Stage 5 of the dungeon plan, server half: levelling stage 1. This PR pins renderedstep/engine#97 at its head (e8a28d27cc2308e3378103194c13db57fe0973ff) and does every item in that PR's "What the server pin PR must change" list.

  • A floor that says levels_up: true raises a game's level by one the first time that game stands on it. The engine heals the player to full on the same turn.
  • The level belongs to the game. characters.level never moves.
  • The level-up notice is written by the engine, for the player only. No model is told of it.

A world without levels_up plays exactly as before. No checked-in story world turns it on, no prompt changed, and no model was called.

What changed

  1. Migration 20261031090000_add_levels_up_and_create_playthrough_floors.rb uses the engine's version and declaration exactly. db/schema.rb moves to 2026_10_31_090000. I placed the lines by hand so the existing column order stays as it was. No later migration is on origin/main (86b9764).

  2. Models

    • Playthrough::FloorReached (table playthrough_floors) is written by the engine only.
      • It belongs to a playthrough, a location and an optional scene.
      • level must be in Character::LEVELS or nil, and a game has one row per floor.
      • #to_s is Game::floor_to_s word for word, and nil for a row with no level.
    • Associations: Playthrough has_many :floors_reached (destroy), Scene (nullify), Location (destroy).
    • Floor validates levels_up as a boolean.
    • The factory never rolls dice. Model tests added.
  3. One game-aware reader. Playthrough#level_for(character, at: nil) and #max_hp_for(character, at: nil) mirror Game::level_at / Game::max_hp_at. Character.hit_points is the one formula. Every reader of a maximum on a game now asks the game:

    • Playthrough::Vitals: .instantiate!, Condition.for, and #max, so the hp_within_the_stat_block validation uses it too;
    • Playthrough::Turn#mend!, the cap;
    • Blow#condition and Toll#condition, at their story_timestamp;
    • Mechanics::State#sheet, which prints the game's level, as the engine's glance does.

    Character#advance! is still never called, and its comment now says why: it writes the world, and a level belongs to the game.

  4. Story::Doctor#hp_above_maximum and Story::Repair#repair_hp_above_maximum now read row.playthrough.max_hp_for(row.character), and the finding's words name the game's level.

    • Test: a game raised to level 5 at 28 hp is neither reported nor clamped. One at 29 is reported and clamped to 28, not 18.
  5. Loader. floor: gains levels_up.

    • Anything but true or false is refused (test with "yes").
    • It is written in both directions on every load, and an absent key writes false (test).
  6. Exporter. Writes levels_up: true when it is set. Round-trip test.

  7. Protocol. Protocol::V1#entry appends this game's claimed floor rows that raised a level, after its tolls. The browser log (turns/_turn) shows the same notice after the tolls.

    • turn_log preloads what both read, so the "preloaded log reads no more rows" test still holds.
    • openapi.json does not describe what a notice is, so it is unchanged.
    • Test: a scene claiming a levelling row, a non-levelling row and another game's row.
  8. Copies of a game. Playthrough::SqlitePackage::PLAYTHROUGH_TABLES gains playthrough_floors (test).

    • Location::OneRoomFold::REFERENCES gains playthrough_floors.location_id, which its schema-walking test requires.
  9. New doctor finding floor_reached_that_is_no_floor (warning/manual, never repaired): a game's row for a place that has no floors row. Test: the game keeps its level after a repair.

  10. Sweep

    • World three-floors-that-raise-you and script a-floor-raises-a-level-once are copied from the engine PR body, with their comments and why: notes.
    • The golden is vendored, and test/engine_parity/a-floor-raises-a-level-once.checks.json is written. The doctor finds no hp_above_maximum after the walk.
    • Records files are not vendored here, and test/ruby_loop_coverage.yml lists no scripts, so neither changes.
    • bin/rails engine:vectors re-exports with no change.
  11. Pin: ext/renderedstep/Cargo.toml and Cargo.lock now point at e8a28d2.

Gates (both provider keys unset)

  • bin/rails test: 5466 runs, 0 failures, 0 errors. This includes the game sweep.
  • bin/rails engine:rust_gates: PASSED, 110 scripts: goldens, invariants, doctor and audit.
  • bin/rails engine:vendored: VENDORED, byte for byte against e8a28d2.
  • bin/rails engine:kept_requests: the engine reproduces every golden vector and kept request.
  • bundle exec rubocop: no offenses. bin/rails zeitwerk:check: all good. bin/brakeman: no warnings.

Follow-up on the engine side

The engine PR asks for parity/worlds/three-floors-that-raise-you.sql to be rebuilt from this branch's loader. That file lives in the engine repository and is not changed here.

I built it following parity/README.md, in a scratch copy of this commit: db:schema:load, then the load_world.rb runner. Its rows are identical to the engine PR's dump. The differences are:

  • table order (schema load against migrate);
  • schema_sha1;
  • a pinned sqlite_sequence row for playthrough_floors.

After engine#97 merges, this PR re-pins to the merge commit.

…nce on each floor that says so, and heal to full

Stage 5 of the dungeon plan, server half ("Levelling stage 1"). A floor that
says `levels_up: true` raises a game's level by one the first time that game
stands on it, and the engine heals the player to full on the same turn. The
level is the game's: `characters.level` never moves. A world without
`levels_up` plays as before; no checked-in story world turns it on.

- Migration 20261031090000 AddLevelsUpAndCreatePlaythroughFloors, the
  engine's version and declaration. schema.rb moves to 2026_10_31_090000.
- Playthrough::FloorReached (table playthrough_floors), written by the engine
  only: level in Character::LEVELS or nil, one row per game and floor. #to_s
  is the engine's notice word for word. Playthrough destroys, Location
  destroys, Scene nullifies. Floor validates levels_up as a boolean.
- Playthrough#level_for / #max_hp_for mirror Game::level_at / max_hp_at, and
  every max-HP reader on a game asks them: Vitals (.instantiate!,
  Condition.for, #max and so its validation), Turn#mend!, Blow#condition
  and Toll#condition at their story_timestamp, and Mechanics::State#sheet.
  Character.hit_points is the one formula; Character#advance! stays uncalled
  and says why.
- Story::Doctor#hp_above_maximum and Story::Repair#repair_hp_above_maximum
  read the game's maximum, so a routine repair never clamps a levelled
  player back to the world's. New finding floor_reached_that_is_no_floor
  (manual, never repaired).
- Loader reads floor `levels_up` (true or false only, absent writes false);
  the exporter writes it when true.
- Protocol::V1#entry and the browser log show this game's claimed levelling
  rows after its tolls. turn_log preloads what they read.
- SqlitePackage carries playthrough_floors; OneRoomFold moves its location.
- Sweep: world three-floors-that-raise-you and script
  a-floor-raises-a-level-once, golden vendored and checks file written.
calebl added a commit to renderedstep/engine that referenced this pull request Oct 30, 2026
…dstep/server 132b475

The game's pin PR (renderedstep/server#373) reads levels_up:, so the dump
is now the one its own loader writes from a schema load: levels_up is
written by the loader rather than by hand. The rows are the same; only
timestamps, the order playthrough_floors and its indexes are dumped in,
and the walk's id pin on its sequence moved. No golden or records file
moves.
9bbff37 adds to the head pinned before only the engine's CI and
parity/worlds/three-floors-that-raise-you.sql rebuilt from this branch's
loader (132b475). No golden, script or vector portion changed: bin/rails
engine:vendored passes, engine:rust_gates passes all 110 scripts.
@calebl
calebl merged commit a69297a 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