feat(02-04): ink compilation pipeline + 4 authored Season-1 Ink files + runtime loader
- scripts/compile-ink.mjs: build-time inklecate runner using bundled binary (BLOCKER 4 — uses node_modules/inklecate/bin, not stale -windows/-mac path strings). Assumption A6 verified first-try on Windows; the same binary path resolution works on macOS + Linux per the wrapper's own getInklecatePath convention. - scripts/compile-ink.test.mjs: 3 Vitest cases proving the compiler runs + emits valid JSON with inkVersion. wipe=false for the test path so it can run in parallel with the ink-loader test without racing on the wipe step. - 4 Season-1 .ink files authored in voice (Lura warmth-anchor, gardener-keeper for compost): lura-arrival.ink, lura-mid.ink, lura-farewell.ink, compost-acknowledgements.ink (rewrite of Plan 02-03 scaffolded version into VAR-driven branch shape consumable by the runtime). - src/content/ink-loader.ts: loadInkStory + bindGardenStateToInk + INK_VARIABLE_MAP. Centralized snake_case slot mapping per Pitfall 4. UTF-8 BOM stripped before Story instantiation. - src/content/ink-loader.test.ts: 8 cases — Story instantiation for all 4 beats, fragment_count binding, Pitfall 4 snake_case enforcement, silent skip for stories missing declared vars. - package.json: build now runs compile:ink first; ci chain runs compile:ink before test so ink-loader.test.ts's precondition check passes. - .gitignore: src/content/compiled-ink/ excluded (regenerated on every build). npm run ci exits 0; 11 new tests green (228 total). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,43 +1,42 @@
|
||||
// content/dialogue/season1/compost-acknowledgements.ink
|
||||
// Compost acknowledgements — D-07 + GARD-04. Plan 02-03 authored content;
|
||||
// Plan 02-04 ships the Ink runtime that consumes it.
|
||||
//
|
||||
// Plan 02-03 ships the AUTHORED CONTENT for the compost tonal beat
|
||||
// (CONTEXT D-07 + GARD-04). Plan 02-04 owns the Ink runtime — this file
|
||||
// is loaded by the Ink runtime (inkjs) at that point and one of these
|
||||
// short lines is dripped into the dialogue overlay each time the player
|
||||
// composts an immature plant.
|
||||
// Phase 2 NOTE — UI WIRING DEFERRED TO PLAN 02-05:
|
||||
// Plan 02-04 ships the Ink compile pipeline + runtime + LuraDialogue
|
||||
// overlay. The compost-beat surface is a thinner toast variant (separate
|
||||
// from the Lura full-screen overlay) and is folded into Plan 02-05's
|
||||
// persistence-toast UI surface for minimum-viable-bias reasons documented
|
||||
// in 02-04-SUMMARY.md.
|
||||
//
|
||||
// In Plan 02-03 the React surface (Garden.ts handleTilePointerDown's
|
||||
// compost branch) does NOT yet render these lines — there's a TODO
|
||||
// comment at the call site marking the Plan 02-04 wiring point. The
|
||||
// content lives here so the writer can iterate on voice without waiting
|
||||
// for the runtime to land.
|
||||
// This file is rewritten in VAR-driven branch form (replacing Plan 02-03's
|
||||
// choice-list shape) so it matches the runtime contract: one ChoosePathString
|
||||
// → drip lines → END. The branching uses fragment_count to vary the line
|
||||
// without requiring the runtime to expose Ink choice points.
|
||||
//
|
||||
// Tone (CLAUDE.md): warm, specific, intermittent, sometimes funny,
|
||||
// sometimes devastating. The gardener-keeper voice. NOT Lura. The garden
|
||||
// is acknowledging the player's choice to let go — never sentimental,
|
||||
// never reassuring, never "it's okay." Just the small fact of the choice,
|
||||
// honored.
|
||||
//
|
||||
// Phase 2 ships ~6 short lines so the player rarely hears the same line
|
||||
// twice in a single session. Plan 02-04 will randomize selection (via
|
||||
// the same mulberry32 pattern as the fragment selector, or a simple
|
||||
// weighted pick — implementer's choice).
|
||||
// Tone (CLAUDE.md): the gardener-keeper voice, NOT Lura. Warm, specific,
|
||||
// intermittent. Acknowledges the player's choice to let go without making
|
||||
// it a moral. Never "it's okay." Never reassurance. Just the small fact
|
||||
// of the choice, honored.
|
||||
|
||||
=== compost_beats ===
|
||||
* The earth takes it back without comment.
|
||||
->DONE
|
||||
VAR fragment_count = 0
|
||||
|
||||
* Some things are tended into being. Others are tended into not being. Both count.
|
||||
->DONE
|
||||
== compost ==
|
||||
|
||||
* The space the plant occupied is now space. That is a kind of progress.
|
||||
->DONE
|
||||
{ fragment_count == 0:
|
||||
The earth takes it back without comment.
|
||||
- else:
|
||||
{
|
||||
- fragment_count % 5 == 0:
|
||||
Some things are tended into being. Others are tended into not being. Both count.
|
||||
- fragment_count % 4 == 0:
|
||||
It wasn't ready. That isn't the same as failing.
|
||||
- fragment_count % 3 == 0:
|
||||
The space the plant was in is now space. That's a kind of progress.
|
||||
- fragment_count % 2 == 0:
|
||||
It returns to the soil. Not poetry — just composting. Mostly.
|
||||
- else:
|
||||
You changed your mind. The garden has nothing to say about it.
|
||||
}
|
||||
}
|
||||
|
||||
* It returns to the soil it came from. Not poetry — just composting. Mostly.
|
||||
->DONE
|
||||
|
||||
* The garden is bigger by one empty tile.
|
||||
->DONE
|
||||
|
||||
* You changed your mind. The garden has nothing to say about it.
|
||||
->DONE
|
||||
-> END
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Lura, arrival beat. After the player's first harvest.
|
||||
//
|
||||
// Variables read from sim (set via story.variablesState before the first
|
||||
// Continue() — see src/content/ink-loader.ts INK_VARIABLE_MAP):
|
||||
// fragment_count - number of harvested fragments at the moment Lura arrives
|
||||
// last_plant_type - 'rosemary' | 'yarrow' | 'winter-rose'
|
||||
//
|
||||
// Per Pitfall 4: Ink VAR names MUST be snake_case AND match INK_VARIABLE_MAP
|
||||
// keys exactly. Typos do NOT throw — the variable silently keeps its
|
||||
// declared default.
|
||||
//
|
||||
// Per CLAUDE.md Tone — Lura is the warmth anchor for the arc, not a
|
||||
// co-griever. Specific. Intermittent. Sometimes funny. She is the contrast
|
||||
// to the gardener-keeper voice; she does not lament with the player.
|
||||
// She brings news from outside the wall, on her own time.
|
||||
|
||||
VAR fragment_count = 0
|
||||
VAR last_plant_type = ""
|
||||
|
||||
== arrival ==
|
||||
|
||||
Oh. You're already here.
|
||||
|
||||
I thought it'd take longer. The wall held, then. Good.
|
||||
|
||||
{ last_plant_type == "rosemary":
|
||||
Rosemary. Of course rosemary. My grandmother kept some in a coffee can on the porch and it outlived two of her dogs.
|
||||
- else:
|
||||
{ last_plant_type == "yarrow":
|
||||
Yarrow. There's an old saying about yarrow and I cannot for the life of me remember what it is. The forgetting is the joke, I think.
|
||||
- else:
|
||||
{ last_plant_type == "winter-rose":
|
||||
Winter-rose, on the first try. You don't mess around. Most people start small.
|
||||
- else:
|
||||
Something grew. That's a start. That's not nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
I won't keep you. I just wanted to see it for myself.
|
||||
|
||||
I'll come back when there's more to come back for.
|
||||
|
||||
-> END
|
||||
@@ -0,0 +1,30 @@
|
||||
// Lura, farewell beat. After the player's 8th harvest (CONTEXT D-14).
|
||||
//
|
||||
// This is the turn — the place where Lura tells you she's leaving and
|
||||
// why, without explaining it. She is still the warmth anchor: she does
|
||||
// NOT cry, she does NOT tell you to be brave, she does NOT make you the
|
||||
// center of her grief. She is a person with somewhere else to be, who
|
||||
// stopped by long enough to make sure you'd be okay without her, and
|
||||
// who trusts you enough to leave.
|
||||
//
|
||||
// Phase 4+ Lura returns at later Seasons; the door this beat closes is
|
||||
// "Lura at the gate every time you harvest," not Lura herself.
|
||||
|
||||
VAR fragment_count = 0
|
||||
VAR last_plant_type = ""
|
||||
|
||||
== farewell ==
|
||||
|
||||
Eight. That's enough. For now.
|
||||
|
||||
I think we both know what this part is.
|
||||
|
||||
I've been putting something off. I think you're far enough along now that I can stop pretending I'm here for the small reasons. There's a thing I have to go and see for myself, and I don't get to bring you with me, and I don't get to tell you about it before I know.
|
||||
|
||||
You don't need me at the gate every day. You haven't for a while.
|
||||
|
||||
The garden persists. Some of it is mine. Most of it is yours now.
|
||||
|
||||
I'll come back when there's something to bring you. Take your time.
|
||||
|
||||
-> END
|
||||
@@ -0,0 +1,31 @@
|
||||
// Lura, mid beat. After the player's 4th harvest (CONTEXT D-14).
|
||||
//
|
||||
// See lura-arrival.ink for variable contract + tone notes. Lura is the
|
||||
// warmth anchor: specific, slightly funny, never sentimental. She knows
|
||||
// something is happening to the world and she is choosing to be useful
|
||||
// about it instead of mournful.
|
||||
|
||||
VAR fragment_count = 0
|
||||
VAR last_plant_type = ""
|
||||
|
||||
== mid ==
|
||||
|
||||
Four. That's a real number.
|
||||
|
||||
I tried to do this once, you know. The garden, I mean. Not — not at this scale. A balcony. Three pots, one of them already broken when I bought it. The basil died first. The rosemary survived. The rosemary survives most things.
|
||||
|
||||
You're keeping at it. Most people don't.
|
||||
|
||||
{ last_plant_type == "winter-rose":
|
||||
A winter-rose this time. They're harder. You can tell, can't you. They want a particular kind of attention.
|
||||
- else:
|
||||
{ last_plant_type == "yarrow":
|
||||
Yarrow keeps giving you yarrow. There's a lesson in that and I'm not going to spell it out, that's the kind of thing you ruin by saying.
|
||||
- else:
|
||||
I'm going to be honest, I lost track of which one it was this time. They look different in the wall.
|
||||
}
|
||||
}
|
||||
|
||||
There's something I should be doing. I'll be back when there's more to bring you.
|
||||
|
||||
-> END
|
||||
Reference in New Issue
Block a user