Skip to content

Rest an hour in a safe room with no foe in it, and mend eight - #100

Merged
calebl merged 3 commits into
mainfrom
fm/ta-floors-s8-engine
Oct 30, 2026
Merged

calebl merged 3 commits into
mainfrom
fm/ta-floors-s8-engine

Conversation

@calebl

@calebl calebl commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Stage 8 of the dungeon plan, engine half: rest.

  • A rest is a wait, with no new verb. It is a rest when three things hold:
    • the room is a sanctuary (locations.sanctuary);
    • nobody in it is a foe (foes_in is empty);
    • the player is alive and has a stat block.
  • What a rest does:
    • it mends 8 hit points, one healing thing's worth, up to the player's maximum;
    • on the told path, its scene spends 60 minutes instead of 5.
  • With a foe in the room, or outside a sanctuary, it is the plain five-minute wait it was, and mends nothing.
  • A rest is never refused, even across a floor's countdown. One that runs past the countdown is played, and stage 6's after-time check takes the player with the floor.
  • Both numbers are one table in code: turn::REST = Rest { minutes: 60, mends: HEALING_POINTS }.
  • The notice is engine copy, for the player only. It is kept in a new per-game table, playthrough_rests. The turn's scene claims the row as it claims a toll, so the game's log entry can show it. No model is told of it.

A world with no sanctuary plays and prompts byte for byte as before. No model was called; every command ran with OPENROUTER_API_KEY and TYPESAFE_API_KEY unset.

The words

The plan's §4.3 draft (turn::rested, said from the row by Game::rest_to_s):

{fullname} rested for an hour in {room} and recovered {n} hit points ({hp} of {max}).

For example: Odile rested for an hour in The Landing and recovered 8 hit points (14 of 18).

  • One change from verbatim: for n = 1 it reads recovered 1 hit point. This follows the toll notice (Playthrough::Toll#to_s), which also says point for one. A rest can recover 1 when the player is one short of full. recovered 0 hit points stays plural, as the toll notice has it.
  • "an hour" is fixed copy. It is not built from REST.minutes. If the owner changes the minutes, the words need changing with them.
  • Where it shows:
    • told path: the row's scene_id is the rest's scene, and the server's log entry and Protocol::V1#entry notices show it from there (server change below);
    • engine alone: a report note, the game: {notice}, as the level-up has.
  • Never in a request:
    • The narrator is asked exactly as for any wait (narrate(command, None, …)).
    • tests/rest.rs checks that no request of a rest contains rested, recovered, an hour, safe room or sanctuary.
    • The sweep's rest reply asserts prompt_excludes: [rested for an hour, hit points].
  • Where no narrator answers: the scene's words are You rest here a while. (turn::RESTED). They replace You let time pass where you stand. Nothing else changed., which would be false for a rest.

What changed

  • turn.rs.
    • REST, RESTED, rested: the table, the fallback words and the notice text.
    • Mechanics::resting: sanctuary, no foe, a living player with a condition.
    • apply_physical's WAIT: for a rest it calls rest, which does three things:
      • mends REST.mends;
      • writes this game's playthrough_rests row (location, recovered, hp_after, and story_timestamp, the story time the rest began);
      • returns RESTED.
    • Without the table the rest still mends and spends its hour, but keeps no row and says no notice.
    • answered_by_the_world: notes each new rest as the game: {notice}. It tracks rests_before exactly as it tracks floors_before.
  • turn/told.rs.
    • The scene's time: use_physical reads resting() before the effect commits. A rest sets the turn's spends to REST.minutes * 60. narrate and tell_in_engine_words stamp the scene with story_now() + spends instead of ACTION_SECONDS.
    • A resumed turn: a rest leaves the room and its foes as they were, so a turn resumed after physical_effect reads it as a rest again and still spends the hour (tested).
    • A new journal step, told_rests: it follows told_floors and claims the turn's rest rows for its scene. It comes after floor_fell_after, so the scene's time is already spent when the countdown is checked.
  • playthrough.rs. Game::rests and Game::rest_to_s. The maximum is the one at the rest's own time (max_hp_at), as a toll's is.
  • store.rs.
    • PLAYTHROUGH_RESTS joins OPTIONAL_LOADED with its whole shape in OPTIONAL_SHAPES, so a database opens with the table whole or without it.
    • SCHEMA_VERSION and shape.txt do not move.
  • Migration 20261031180000 (CreatePlaythroughRests), applied to all 36 world dumps in parity/worlds/ by the method of stages 5–7:
    • Each dump was first checked to re-dump byte-identically.
    • Then bin/rails db:migrate ran on a copy of it in a scratch checkout of the game at SERVER_COMMIT (b696798), holding only this migration.
    • For the 29 dumps without 20261029154235, its version row was added before the run and deleted after.
    • Each dump gains:
      • the table, with three foreign keys and three indexes;
      • the version row.
    • The version follows 20261031150000, the latest at SERVER_COMMIT, and is within the day ahead Rails accepts today. Check it against a freshly pulled main before the pin PR.
  • README. The playthrough row of the module table.

Things a reviewer should know

  • The engine alone spends no time on a wait, rest or not. That was already true of every wait in it, so a rest there mends and notes but moves no clock. On the told path the hour is spent.
    • Both loops agree on whether a wait is a rest, on what it mends, and on the row and the notice.
    • The told loop alone moves the clock, as it does for every wait.
  • A rest at full is still a rest. It takes the hour and keeps a row with recovered 0, and the notice says so. That is the plan's rule as written: sanctuary and no foe; it says nothing about the player's hit points.
  • A player with no stat block never rests: a wait stays a plain wait. There are no hit points to mend, and the notice has nothing to say.
  • The narrator is not told of the rest, but it reads the player's condition. The paragraph's moment is built after the effect, as it is for every act. So a rest's moment says is hurt (14 of 18) where a plain wait's would say (6 of 18). That is the player's state, the same as after a healing thing is consumed. No words about resting or recovering reach any request.
  • A rest's row keeps the time the rest began, not the end of its hour. Its notice's maximum is read at that time. No floor raises a level mid-rest, so the two cannot differ.

What did not move

  • No golden and no records file.
    • parity --check parity: AGREED 110 of 114. The other four are runner-only, as before.
    • No existing script waits in a sanctuary.
  • fight --check sim: AGREED, 10 fight goldens, unchanged. Nothing was re-blessed.

Sweep

New world a-place-to-rest and script resting-in-a-safe-room, in PASSING with a golden and a records file.

How the world was made. The world file below was loaded with the game's loader at SERVER_COMMIT (b696798). That loader already reads sanctuary:, safe_rooms: and minutes:. Then the migration ran. No row was set by hand. The rows will not change when the pin branch's own loader rebuilds the dump; only timestamps and the schema digest will.

The world.

  • Odile is level 3, d8: 18 hit points.
  • The Landing (the opening room) and The Shrine are sanctuaries the world file names. Brother Amal keeps The Shrine: not hostile, level 1, d6.
  • The Resting Floor is three rooms, safe_rooms: 1, minutes: 30. Its layout makes room 3 the safe room.

resting-in-a-safe-room (12 steps):

  1. harm 12: 6 of 18.
  2. Rest on the landing: +60 min, hp 14. The narrator's request excludes rested for an hour and hit points.
  3. Rest again: +60 min, hp 18 (it mended 4, to her maximum).
  4. Into The Shrine: Brother Amal is present.
  5. /attack Brother Amal: he is a foe. He strikes back: hp 14.
  6. /wait with the foe there: +5 min, no mending. He strikes again: hp 10.
  7. Back to the landing: he strikes once more as she leaves, and the fight closes. hp 5.
  8. Onto The Resting Floor: it is laid out, and its countdown starts.
  9. Room 2.
  10. Room 3, the safe room: its request carries Write NOBODY into this place.
  11. /wait: a rest of +60 min, past the floor's 30. It is played, not refused: dead.
  12. /wait: refused, dead.

I checked every expect: against the engine's dumps: 27 expectations, 0 mismatches.

What the records do not show. The records file does not hold playthrough_rests, because parity::WRITTEN_TABLES holds no playthrough_* table (nor does it for playthrough_floors). The rows and the notices are asserted in tests/rest.rs.

The world file (lib/engine_sweep/worlds/a-place-to-rest.yml)
# A wait in a safe room is a rest. The Landing and The Shrine are safe rooms
# the world file names (`sanctuary: true`); The Resting Floor is a FLOOR of
# three rooms whose `safe_rooms: 1` makes one of them, never the entry, a safe room
# when it is laid out, and whose countdown (`minutes: 30`) runs from the
# first step onto it. A rest is the `wait` every room offers: in a safe room
# with no foe in it, it takes an hour, not five minutes, and mends one
# healing thing's worth of hit points, up to the player's maximum. Brother
# Amal keeps The Shrine and is nobody's foe until he is struck.
format: 2
universe:
  physics: Ordinary physics.
  technology: Lanterns, iron keys and hand tools.
  weapons: Short blades and cudgels.
  geographies: A dungeon cut down into black stone under a lantern-lit landing.
  civilizations: Delvers who sell what they bring up.
  history: The dungeon was cut long ago by people nobody remembers.
  economics: Coin for what comes up from below.
  politics: Nobody governs the dungeon.
  religion: Delvers leave a coin at the shrine for luck.
  races:
  - name: Delvers
    description: People who go down into the dungeon for a living.
story:
  title: A Place To Rest
  genre: dungeon crawl
  start_time: '2026-10-31T08:00:00Z'
  preface: Delvers rest where nothing hostile is born, and the floor below does not wait for them.
  summary: Odile rests on the landing, in the shrine, and on the floor below.
opening_scene:
  location: The Landing
  characters: [Odile]
  description: You stand on the lantern-lit landing above the first floor.
  summary: Odile reaches the landing.
characters:
- fullname: Odile
  nickname: Odile
  race: Delvers
  age: 29
  sex: female
  is_protagonist: true
  is_companion: false
  backstory: A delver who has never been below the first floor.
  personality: Careful and stubborn.
  appearance: A lantern on a pole and a short blade.
  likes: A clear way back.
  dislikes: Doors she cannot open.
  fears: Resting too long below.
  stats: {level: 3, hit_die: 8, strength: 14, dexterity: 12, will: 12}
- fullname: Brother Amal
  nickname: Amal
  race: Delvers
  age: 63
  sex: male
  is_protagonist: false
  is_companion: false
  backstory: He has kept the shrine by the landing for thirty years and never been below.
  personality: Quiet and patient.
  appearance: A grey robe and a cudgel he leans on.
  likes: Coins left at the shrine.
  dislikes: Raised voices.
  fears: The floor below.
  location: The Shrine
  stats: {level: 1, hit_die: 6, strength: 6, dexterity: 8, will: 14}
locations:
- name: The Landing
  detail_level: realized
  population: nobody
  opening: true
  sanctuary: true
  description: A square landing of black stone lit by one lantern, a stair going down from it.
  teaser: A lantern-lit landing.
  lore: Delvers rest here before they go down.
- name: The Shrine
  detail_level: realized
  population: nobody
  sanctuary: true
  description: A niche cut off the landing, its shelf crowded with coins and candle stubs.
  teaser: A shrine off the landing.
  lore: Nothing hostile has ever been born here.
- name: The Resting Floor
  detail_level: stub
  kind: warren
  teaser: The first level of the dungeon, with one room nothing is born in.
  floor:
    rooms: 3
    danger: safe
    hazard: none
    safe_rooms: 1
    minutes: 30
connections:
- between: [The Landing, The Shrine]
  distance: adjacent
  travel_method: walking
- between: [The Landing, The Resting Floor]
  distance: adjacent
  travel_method: taking stairs
The script with its why: notes (lib/engine_sweep/scripts/resting-in-a-safe-room.yml)
# A WAIT IN A SAFE ROOM IS A REST. In a sanctuary (`locations.sanctuary`)
# with no foe in it, the `wait` every room offers takes an hour instead of
# five minutes and mends eight hit points, one healing thing's worth, up to
# the player's maximum. The game keeps a row for each rest
# (`playthrough_rests`), which the turn's scene claims and its log entry says
# in the engine's words; no model is told of it. With a foe in the room it is
# a plain five-minute wait and mends nothing. A rest is never refused: one
# that runs past a floor's countdown is played, and the player falls with the
# floor.
story: A Place To Rest
steps:
- id: hurt-on-the-landing
  why: Odile is level 3 on a d8, so 18 hit points; 12 of them are gone.
  type: harm 12
  expect:
    change: Odile unhurt -> badly hurt (6 of 18)
    hp: 6
- id: rest-on-the-landing
  why: >-
    The landing is a safe room the world file names, and nobody is fighting
    Odile: the wait is a rest. It takes an hour and mends eight.
  type: "/wait"
  browser:
    token: rest-on-the-landing
    replies:
    - purpose: narration
      prompt_excludes:
      - rested for an hour
      - hit points
      content: You sit with your back to the wall and let the lantern burn.
  expect:
    elapsed_minutes: 60.0
    hp: 14
    dead: false
- id: rest-again-mends-only-to-her-maximum
  why: A second rest mends only the four she is short of.
  type: "/wait"
  browser:
    token: rest-again
    replies:
    - purpose: narration
      content: You doze, and wake to the same lantern.
  expect:
    elapsed_minutes: 60.0
    hp: 18
- id: into-the-shrine
  type: "/move The Shrine"
  expect:
    location: The Shrine
    present:
    - Brother Amal
- id: strike-the-keeper
  why: Struck, Brother Amal is provoked, and so a foe in the room.
  type: "/attack Brother Amal"
  expect:
    foes:
    - Brother Amal
    hp: 14
    dead: false
- id: a-wait-with-a-foe-here-is-no-rest
  why: >-
    The shrine is a safe room, but somebody in it is fighting Odile: the wait
    is a plain five minutes and mends nothing. The keeper strikes back, for
    four.
  type: "/wait"
  browser:
    token: wait-with-a-foe
    replies:
    - purpose: narration
      content: You hold your ground and watch the old man's cudgel.
  expect:
    elapsed_minutes: 5.0
    hp: 10
    foes:
    - Brother Amal
- id: back-to-the-landing
  why: The keeper strikes once more as she goes, and the fight closes.
  type: "/move The Landing"
  expect:
    location: The Landing
    hp: 5
- id: onto-the-resting-floor
  why: The floor is laid out on this turn; its countdown runs from now.
  type: "/move The Resting Floor"
  browser:
    token: into-the-resting-floor
    realizes: The Resting Floor
    replies:
    - purpose: location
      content:
        description: A level of black stone passages.
        lore: The first floor was cut by people nobody remembers.
        place_kind: warren
    - purpose: location
      content:
        description: A low chamber at the foot of the stair.
        lore: Delvers pass through here on their way in.
        items: []
        people: []
    - purpose: arrival
      unavailable: true
  expect:
    location: The Resting Floor room 1 (realized)
- id: through-room-2
  type: "/move The Resting Floor room 2"
  browser:
    token: through-room-2
    realizes: The Resting Floor room 2
    replies:
    - purpose: location
      content:
        description: A bare chamber of black stone.
        lore: Delvers do not stop here.
        items: []
        people: []
    - purpose: arrival
      unavailable: true
  expect:
    location: The Resting Floor room 2 (realized)
- id: into-the-safe-room
  why: Room 3 is the floor's safe room, laid out so when the floor was.
  type: "/move The Resting Floor room 3"
  browser:
    token: into-the-safe-room
    realizes: The Resting Floor room 3
    replies:
    - purpose: location
      prompt_includes:
      - Write NOBODY into this place.
      content:
        description: A quiet chamber with a dry floor.
        lore: Nothing is born here.
        items: []
    - purpose: arrival
      unavailable: true
  expect:
    location: The Resting Floor room 3 (realized)
    hp: 5
    dead: false
- id: rest-past-the-countdown
  why: >-
    The hour runs past the floor's thirty minutes. The rest is not refused:
    it is played, and Odile falls with the floor.
  type: "/wait"
  browser:
    token: rest-past-the-countdown
    replies:
    - purpose: narration
      content: You close your eyes as the stone settles around you.
  expect:
    elapsed_minutes: 60.0
    dead: true
- id: nothing-after
  type: "/wait"
  expect:
    refused: true
    dead: true
    offers: is dead, and this playthrough is over

Tests

tests/rest.rs (new; the live path with replayed replies, and the engine alone):

  • A rest of an hour that mends eight.
    • hp 6 → 14, story time +60 min.
    • One row, claimed by the turn's scene: recovered 8, hp_after 14, story_timestamp the time the rest began.
    • The notice is exact.
    • No request says anything of the rest.
  • Up to the maximum. Three rests:
    • +8, then +4 to 18 of 18, then +0;
    • each one an hour;
    • each with its exact notice.
  • A foe in the safe room. After /attack Brother Amal, a wait is +5 min, mends nothing and keeps no row.
  • A room that is not safe. sanctuary cleared, a wait is +5 min, mends nothing and keeps no row.
  • Past the countdown.
    • Walking onto the floor and into its safe room, the rest is played, not refused. The game is over, the glance carries the collapse, and hp is 0.
    • The rest's row is kept: she rested before the floor fell.
  • Resumed after its effect. A turn stopped after physical_effect and delivered again mends once (hp 14), spends exactly one hour, and keeps one row.
  • The engine alone.
    • A rest's change is RESTED, with the note the game: {notice}; hp 14; no time spent.
    • With sanctuary cleared, the change is WAITED with no note.
  • The words. Over records, rest_to_s:
    • says 1 hit point in the singular;
    • reads the maximum at the rest's own time: (20 of 23) after a floor raised Odile to level 4.

Store (tests/engine.rs).

  • A database from before the migration opens. A rest there mends (6 → 14) and notes nothing.
  • A playthrough_rests missing an index is refused.
  • every_world_has_the_shape_the_engine_is_written_against holds for all 37 dumps.

Local runs, all with both provider keys unset:

  • cargo fmt --check, cargo clippy --workspace --all-targets --locked -- -D warnings and cargo test --workspace --locked: all pass.
  • parity --check parity: 110 of 114 agree (the four runner-only, as before).
  • fight --check sim: AGREED, 10 fight goldens.
  • CI's runner job, run locally against the game at SERVER_COMMIT (b696798), on its own schema (no playthrough_rests), with the extension built from this checkout:
    • parity/runner.sh: 113 of 114 agree; the new script is "not in the checkout yet";
    • engine:rust_gates: PASSED for 113 scripts (goldens, invariants, doctor and audit).

SERVER_COMMIT does not move. The game's extension builds against this checkout unchanged: only pub items were added, and no struct the extension builds gained a field.

What the server pin PR must change

Line numbers are at b696798.

  1. Migration db/migrate/20261031180000_create_playthrough_rests.rb, with exactly this version and these declarations, so the dumps here stay byte-true. store::OPTIONAL_SHAPES holds every fact of it. db/schema.rb moves to 2026_10_31_180000.
    class CreatePlaythroughRests < ActiveRecord::Migration[8.1]
      def change
        create_table :playthrough_rests do |t|
          t.references :playthrough, null: false, foreign_key: true
          t.references :location, null: false, foreign_key: true
          t.integer :recovered, null: false
          t.integer :hp_after, null: false
          t.datetime :story_timestamp, null: false
          t.references :scene, foreign_key: true
          t.timestamps
        end
      end
    end
    Give it the header comment the stage 5–7 migrations carry ("THE VERSION AND THE DECLARATION ARE THE ENGINE'S…").
  2. Model Playthrough::Rest (app/models/playthrough/rest.rb, self.table_name = "playthrough_rests"), on the pattern of Playthrough::FloorReached (app/models/playthrough/floor_reached.rb):
    • Associations: belongs_to :playthrough, belongs_to :location, belongs_to :scene, optional: true.
    • Validations: recovered an integer ≥ 0, hp_after an integer ≥ 0, story_timestamp present.
    • #to_s, the Ruby mirror, word for word the engine's Game::rest_to_s:
      def to_s
        player = playthrough.character
        return nil if player.nil? || !player.stat_block?
      
        max = playthrough.max_hp_for(player, at: story_timestamp)
        "#{player.fullname} rested for an hour in #{location.name} and recovered " \
          "#{recovered} hit point#{"s" unless recovered == 1} (#{hp_after} of #{max})."
      end
    • A header comment: written by the engine only; no model is told.
    • A model test with the exact sentence, 1 hit point in the singular, and a maximum read at the rest's time after a floor raised the level.
  3. Associations on the owners, each with the reasoning comment its floors_reached neighbour has:
    • Playthrough: has_many :rests, class_name: "Playthrough::Rest", dependent: :destroy, inverse_of: :playthrough (app/models/playthrough.rb:100).
    • Location: has_many :rests, …, dependent: :destroy, inverse_of: :location (app/models/location.rb:71).
    • Scene: has_many :rests, …, dependent: :nullify, inverse_of: :scene (app/models/scene.rb:198).
  4. The log.
    • Preload: Playthrough#turn_log (app/models/playthrough.rb:533-535) adds rests: [ :location, { playthrough: [ :character, :floors_reached ] } ].
    • View: app/views/turns/_turn.html.erb:55 adds one <p class="notice" id="<%= dom_id(rest) %>"> per rest of this game, after the floors, filtered by playthrough_id like the tolls and sorted by id.
    • Test: a scene that claimed a rest shows the sentence.
  5. Protocol.
    • Protocol::V1#entry (app/models/protocol/v1.rb:76) appends this game's rests the scene claimed, by id, after its floors:
      + scene.rests.select { |rest| rest.playthrough_id == playthrough.id }.sort_by(&:id).filter_map(&:to_s)
    • docs/protocol/v1/openapi.json: no change. notices (:783) is an array of strings with no description.
    • Test: an entry whose scene claimed a rest.
  6. Playthrough::SqlitePackage::PLAYTHROUGH_TABLES (app/models/playthrough/sqlite_package.rb:27) adds playthrough_rests, in alphabetical order after playthrough_passages.
  7. Vendor from this engine at the new pin:
    • The script: lib/engine_sweep/scripts/resting-in-a-safe-room.yml, with its why: notes, as above.
    • The world: lib/engine_sweep/worlds/a-place-to-rest.yml, as above.
    • The goldens and records: parity/goldens/resting-in-a-safe-room.json and parity/records/resting-in-a-safe-room.json, as bin/rails engine:vendored checks them.
    • The checks file: test/engine_parity/resting-in-a-safe-room.checks.json, written by the runner as for every new script.
  8. Then rebuild this engine's parity/worlds/a-place-to-rest.sql from the pin branch's own loader with the migration in its schema, as stages 2–7 did.
    • The rows will not move: this dump was already written by the loader at b696798.
    • Point this repository's SERVER_COMMIT at the pin branch in the same follow-up.

Nothing in the game's request builders reads the new table. No prompt template changes.

A wait in a sanctuary (locations.sanctuary) with no foe in it and the
player alive with a stat block is a rest. It mends REST.mends (8,
HEALING_POINTS: one healing thing's worth) up to the player's maximum,
and on the told path its scene spends REST.minutes (60) instead of an
action's five. With a foe in the room, or anywhere that is not a
sanctuary, it is the plain five-minute wait it was. A rest is never
refused: one that runs past a floor's countdown is played, and stage 6's
after-time check takes the player with the floor.

Each rest is kept as a row of the new per-game table playthrough_rests
(where, recovered, hp_after, the story time it began), which the turn's
scene claims as it claims a toll. Game::rest_to_s says it in the
engine's words: "{fullname} rested for an hour in {room} and recovered
{n} hit points ({hp} of {max})." The engine-alone report notes it as
"the game: ...". No request carries it: the narrator is asked as for any
wait, and where none answers the engine's words are "You rest here a
while." The engine alone spends no time on a wait, rest or not.

Migration 20261031180000 creates playthrough_rests, applied to every
world dump as Rails' SQLite adapter applies it. store::OPTIONAL_LOADED
and OPTIONAL_SHAPES hold it, so the game at its current pin still opens
and plays: there a rest mends and takes its hour and keeps no row.
SCHEMA_VERSION and shape.txt do not move. No golden or records file
moved, and the fight simulator's reference fights are unchanged.

New world a-place-to-rest (the game's loader at server b696798, then the
migration) and script resting-in-a-safe-room, in PASSING with a golden
and records.
… cfe509a

The game's pin PR (renderedstep/server#376) carries migration
20261031180000, so the dump is now the one its own loader writes from a
schema load, with playthrough_rests in the schema rather than migrated
on after. 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 66e8eb0 into main Oct 30, 2026
4 checks passed
calebl added a commit to renderedstep/server that referenced this pull request Oct 30, 2026
calebl added a commit to renderedstep/server that referenced this pull request Oct 30, 2026
Pin renderedstep/engine#100's head (6bf995e): rest an hour in a safe room with no foe in it, and mend eight
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