docs(mystery): spec for engine prereqs (verbs, disambiguation, ending UI) #1

Merged
ejlewis merged 19 commits from feat/engine-prereqs into main 2026-05-09 15:10:20 -05:00

19 Commits

Author SHA1 Message Date
ejlewis 5f8e3b1a34 fix(ui): keep input typable post-end so player can type restart/undo
Disabling the input via the disabled attribute blocks keydown events
entirely, so players couldn't type 'restart' or 'undo' after reaching an
ending. Switch to a CSS class for the faded visual state; the keydown
handler already restricts post-end input to those two commands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 15:06:37 -05:00
ejlewis e167979fa7 feat(ui): render ending lines distinctly and lock input on end-state
Ending-kind lines get a separator and italic styling. Once endedWith is
set, the terminal disables the input and rejects all commands except
restart and undo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 14:57:52 -05:00
ejlewis 19d1efc586 feat(engine): detect endings on every successful turn
After each state-mutating dispatch, evaluate world.endings in priority
order (true > wrong > bad). The first whose whenFlags are all satisfied
sets state.endedWith and appends a kind:'ending' transcript line. Once
ended, further dispatches return a "story has ended" narration.

Also update test-world fixtures and placeholder ending markdown files
to use whenFlags: { _never: true } instead of {} so that vacuously-true
empty flags don't accidentally fire on every successful turn.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 14:53:07 -05:00
ejlewis 0d9db9bb55 test(engine): self-contained locked-exit fixture replaces the stub
Verifies blocked movement, key-permitted passage, and that the key is
not consumed by passing through.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 14:42:22 -05:00
ejlewis b870d884ef feat(engine): wire verb-target-prep — explicit \light X with Y\ and \use\ routing
light X with Y validates the named instrument and reuses handleLight.
use X / use X on Y route through the encounter dispatcher; if no encounter
consumes it, the dispatcher narrates the fallback. The encounter matcher
also rejects transitions whose required item doesn't match the typed
instrument, so a mistyped instrument fails cleanly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 14:22:03 -05:00
ejlewis 8401e7d281 feat(engine): light/extinguish verbs with implicit lighter selection
\`light X\` finds a lighter (item with lighter:true and remaining state.uses)
in inventory, decrements its charges, and toggles target.state.lit. The
target's litText / extinguishedText / the lighter's lighterEmptyText
provide narration. Refuses politely on each error path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 14:18:54 -05:00
ejlewis dac8487dbe feat(engine): read verb narrates item.readableText
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 14:15:35 -05:00
ejlewis 2fecc7878d feat(world): annotate lamp/matches/letter for read/light/extinguish
Adds the new schema flags and per-state body sections so the dispatcher's
new verb handlers have content to narrate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 14:12:10 -05:00
ejlewis ee3cfcc00d feat(world): parseItem extracts optional ## read / lit / extinguished / lighter-empty sections
Existing items with no body sections continue to load unchanged. New items
can author per-state prose in dedicated sections; the dispatcher will read
these in subsequent commits.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 14:08:33 -05:00
ejlewis df50afa479 feat(world): item schema — readable, lightable, lighter, lighterUses
Optional fields used by the new read/light/extinguish dispatcher branches.
Loader updates and dispatcher logic follow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 14:01:31 -05:00
ejlewis ab8c17fdd5 feat(engine): dispatcher handles ambiguous parses with a disambiguation prompt
Sets pendingDisambiguation on state and emits "Which X — A, or B?" using
each candidate item's short text. The existing disambiguation reply path
then re-issues the original verb against the chosen target.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:58:32 -05:00
ejlewis b318747840 feat(parser): emit verb-target-prep on 'with'/'on'/'in'/'to' separators
Enables `light lamp with matches`, `use shears on vines`, and similar
multi-noun forms. Both the target and indirect noun must resolve;
otherwise the command falls back to unknown-noun.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:56:19 -05:00
ejlewis 46f851bc3a feat(parser): return ambiguous variant when noun matches multiple aliases
Replaces the previous behavior of returning unknown-noun. The dispatcher
will use this in the next commit to prompt the player to disambiguate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:53:38 -05:00
ejlewis b325f04b02 feat(parser): strip leading stop-words (at, the, a, an) from noun phrase
Allows `look at lamp`, `examine the letter`, `take a key`, `take an oil lamp`.
Stop-words are only removed from the head of the noun phrase, not from
anywhere in the middle.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:36:34 -05:00
ejlewis 14a58481b1 refactor(engine): theme is a UI preference; remove it from GameState
Previously, clicking the theme button updated localStorage and the DOM but
not state.theme, so the engine's `theme` meta-verb toggled from stale state.
Theme is now exclusively UI/storage concern. Old saves with the field still
load; the field is silently ignored.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:33:57 -05:00
ejlewis 657ed22b48 refactor(engine): drop redundant string[] casts now that RoomState includes arrays
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:30:35 -05:00
ejlewis 6cffb87a63 feat(engine): widen state value unions and add 'ending' transcript kind
Drops redundant string[] casts in dispatcher paths and prepares the
TranscriptLine kind for the ending-screen render path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:28:14 -05:00
ejlewis e21a308e9d docs(mystery): implementation plan for engine prereqs
17 tasks covering type widening, theme removal, parser stop-words +
ambiguous + verb-target-prep variants, dispatcher handlers for
read/light/extinguish/use, ending detection, and UI ending screen.
Includes self-contained locked-exit fixture and manual playthrough.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:21:13 -05:00
ejlewis bcff8a42f9 docs(mystery): spec for engine prereqs (verbs, disambiguation, ending UI)
Hard prereqs from halfstreet-followon-notes plus should-fix items.
Polish items deferred. Phase 2 (full bible content draft) follows after
this lands.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:07:24 -05:00