You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
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.
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
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)
Each chapter's taste-memory beat deposits a discrete warm journal entry (Ch1–Ch7),
additive save fields only.
Phase 1's picker reads that real inventory instead of the synthesized answer+distractor
sets.
Guarantee a minimum-memory completion path for sparse saves.
Update R15's save-coverage matrix rows for the new fields.
Design calls needed (defaults marked; flagging per protocol rather than guessing)
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.
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.
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.
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.
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
giftMemoryFromJournalper round, with the correct warm answer plus 1–2 plausiblecross-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 reversechronological 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 thecollected 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:
No inventory data structure.
notebook(:1809) is{ open, page, items }.itemsholds recipes/treats/objects (
kind: 'recipe' | 'treat' | 'item' | 'object'). There isno memory kind and no memory array. Memories exist only as narrative booleans on the
per-chapter state objects.
No deposit points. The seven chapter taste beats each call an idempotent
add*Recipe()that pushes akind:'recipe'item and returns — nothing records thememory of the meal:
addKitchenRecipe:11777recipe-lefse-gravlaksaddKotletidRecipe:14934recipe-kotletidaddCurryRecipe:16145recipe-red-curryaddMuhamamaraRecipe:17756recipe-muhammaraaddPotjieRecipe:18971recipe-potjieaddLemperRecipe:20351recipe-lemperaddMoquecaRecipe:21468recipe-moquecaThe 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'sright 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 ownshadowThrowsarrayholding both the shadow's line and the three answer options with
correct: true/falsebaked in (
:22420–22445).resolvePhase1Round(:22464) just readsmem.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)
additive save fields only.
sets.
Design calls needed (defaults marked; flagging per protocol rather than guessing)
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 adistractor. 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.
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-showsthe same set rather than reshuffling under the player. Still reads the real inventory;
just doesn't dump all of it on one screen.
Sparse and legacy saves — the minimum-memory path. The sharp case:
SAVE_VERSIONstays 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 mealmemory from the same canonical table the deposits use, marked
backfilled: true. Onesource 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.
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, thefaint 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
final line, both epilogues.
SAVE_VERSIONbump — additive fields only.Test checklist
node --checkclean; push verified withgit log origin/main..HEADempty.the mechanics checklist (R13 spec rule, still binding).