Skip to content

Sprint R13b — Phase 1 paired-memory inventory: upgrade (b) → (a) #128

Description

@juliatetrud

Sprint R13b: Phase 1 paired-memory inventory — upgrade (b) → (a)

Parent run: #108. Inserted between R16 and R17 on Julia's call. Supersedes the Phase 1 half
of #124. Design-call reversal logged to #110.

Why this sprint exists (the reversal)

R13 built Phase 1 with default (b) from #124 — the pragmatic path: reuse
giftMemoryFromJournal per round, with the correct warm answer plus 1–2 plausible
cross-chapter distractors written inline in shadowThrows. It delivers the verb
(choose the right warmth from the journal) without a memory inventory behind it.

Julia's call: upgrade to (a). Rationale — the paired-memory payoff is the climax's
design intent.
Under (b) the journal is a prop at the moment it is supposed to be the
payoff; the player spends memories they never actually collected. Canon is explicit that
this inventory is the point:

  • 01-story-bible.md:246 — "Every chapter from Chapter 2 onward produces two memories —
    one frightening (a 'monster moment') and one feeding (a 'meal moment'). Both go into Pip's
    collected memory inventory... Each chapter's pair is a unit. The pattern is locked."
  • 01-story-bible.md:285 / 02-game-design.md:389 — the override runs in reverse
    chronological order
    , each chapter's meal moment answering that same chapter's monster
    moment. "The notebook is no longer just a record. It is the inventory of what he has to
    spend. Every recipe he collected was secretly armor for this moment."
  • 02-game-design.md:437 — the paired memory inventory persists in save.
  • 03b-ui-spec.md:290 — from Ch5 onward a journal inventory spread surfaces the
    collected pairs (monster left, meal right). "This is the inventory the player will use at
    the climax."

Diagnosis (root cause, traced to code)

The collected-memory inventory does not exist as a system. Three concrete gaps:

  1. No inventory data structure. notebook (:1809) is { open, page, items }. items
    holds recipes/treats/objects (kind: 'recipe' | 'treat' | 'item' | 'object'). There is
    no memory kind and no memory array. Memories exist only as narrative booleans on the
    per-chapter state objects.

  2. No deposit points. The seven chapter taste beats each call an idempotent
    add*Recipe() that pushes a kind:'recipe' item and returns — nothing records the
    memory of the meal:

    Ch fn line recipe id
    1 addKitchenRecipe :11777 recipe-lefse-gravlaks
    2 addKotletidRecipe :14934 recipe-kotletid
    3 addCurryRecipe :16145 recipe-red-curry
    4 addMuhamamaraRecipe :17756 recipe-muhammara
    5 addPotjieRecipe :18971 recipe-potjie
    6 addLemperRecipe :20351 recipe-lemper
    7 addMoquecaRecipe :21468 recipe-moqueca
  3. The journal's Memories page is a hardcoded stub. renderNotebookPage() (:13397)
    page 1 sets the titles "Monster Moment" / "Meal Moment" and then writes the literal
    string 'No memory yet.' to both columns unconditionally — no data path at all. Page 0's
    right column is likewise the fixed string 'Memories collected here.' (:13364).
    So 03b-ui-spec.md §5's inventory spread was never built.

Consequence at the climax. runPhase1 (:22414) carries its own shadowThrows array
holding both the shadow's line and the three answer options with correct: true/false
baked in (:22420–22445). resolvePhase1Round (:22464) just reads mem.correct. The
"journal" the player opens is a literal array in the finale's own source. Nothing the player
did across seven chapters feeds it.

Note in (b)'s favour, which (a) must preserve: the round ordering is already canon-correct
— round 1 answers the Ch7 fire-serpent with Beatriz/moqueca, round 2 the Ch6 Pocong with the
Pocong's own release, round 3 the Ch5 Mamlambo with Iris. That is reverse chronological
(Ch7 → Ch6 → Ch5). (a) must not disturb this.

Scope (Julia's, verbatim)

  1. Each chapter's taste-memory beat deposits a discrete warm journal entry (Ch1–Ch7),
    additive save fields only.
  2. Phase 1's picker reads that real inventory instead of the synthesized answer+distractor
    sets.
  3. Guarantee a minimum-memory completion path for sparse saves.
  4. Update R15's save-coverage matrix rows for the new fields.

Design calls needed (defaults marked; flagging per protocol rather than guessing)

  1. Ch1 inclusion vs. the bible's "Chapter 2 onward." Julia's scope says Ch1–Ch7; the
    story bible says pairs start at Ch2. These are reconcilable — Ch1 has a real taste beat
    (Henrik's first taste, addKitchenRecipe) and R13's Phase 1 already uses Henrik as a
    distractor. Default: follow Julia's Ch1–Ch7 (Ch1 deposits a meal moment only, no
    monster pair), and log it as a deliberate, additive extension of the bible's pattern
    rather than a contradiction of it. Flagging because it touches locked canon.

  2. How many options the picker shows. The full inventory is up to 7 entries; the choice
    UI (showChoices :4930) is an unpadded numbered list at 480×270 and R13 shipped 3.
    Default: cap at 4 — the correct memory plus 3 drawn from the player's real collected
    entries, chosen deterministically (by round, not Math.random) so a gentle retry re-shows
    the same set rather than reshuffling under the player. Still reads the real inventory;
    just doesn't dump all of it on one screen.

  3. Sparse and legacy saves — the minimum-memory path. The sharp case: SAVE_VERSION
    stays 2 and the new fields are additive, so an existing mid-Ch8 save arrives at Phase 1
    with an empty inventory
    and, without a guarantee, no correct answer exists. Default:
    ensureMinimumMemories() runs at Phase 1 entry and back-fills any missing Ch1–Ch7 meal
    memory from the same canonical table the deposits use
    , marked backfilled: true. One
    source of truth, every save completable, no fallback to (b)'s inline sets. The player who
    collected everything sees their own entries; the sparse save is quietly made whole rather
    than blocked.

  4. Does R13b build the journal Memories page? Not in Julia's four scope items, but a
    "discrete warm journal entry" that is invisible in the journal is half a feature — gap 3
    above means the page is a stub today. Default: build the minimal read path (render the
    deposited meal moments on page 1's right column, monster moments left where known) per
    03b-ui-spec.md §5, and defer the full spread polish — the Ch5-onward gating, the
    faint connecting line between paired entries, icons — to a follow-up. Flagging as a scope
    addition so Julia can cut it if she'd rather keep R13b tight.

Out of scope

  • Phase 2 (riddle, verified) and Phase 3 (arrow-key trace) — untouched.
  • "It's not fair." stays a single choice (design call Epic 4 — Chapter 2: Tallinn #4, Sprint R13: Ch8 finale mechanics (override phases) #124). Never a puzzle.
  • Round count, round order, shadow lines, and all resolve/pong narration — byte-identical.
  • All post-override content: Babcia gift, pierogi fold, parents through veil, the locked
    final line, both epilogues.
  • No SAVE_VERSION bump — additive fields only.

Test checklist

  1. Full-collection playthrough: all 7 meal moments deposit; journal page 1 shows them.
  2. Phase 1 rounds 1–3 pull real entries; correct answers remain Ch7 → Ch6 → Ch5.
  3. Sparse save (legacy R16-era save loaded mid-Ch8): back-fill fires, Phase 1 completable.
  4. Wrong pick → gentle static + retry, same option set, no game-over.
  5. Save/load round-trip: memory fields persist; a v2 save without them still loads.
  6. "It's not fair." still a single choice; final line + epilogues unchanged.
  7. node --check clean; push verified with git log origin/main..HEAD empty.
  8. Julia plays the finale in-browser and signs off on feel — the ending's feel outranks
    the mechanics checklist (R13 spec rule, still binding).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    chapter-8Chapter 8: Greenpointr-seriesR-series release run (R01-R18)sprintIndividual sprint issue

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions