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>
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>
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>
Mirrors the ethanjlewis.com pipeline structure:
- single combined build step (npm ci + test + build)
- deploy via npx wrangler to project halfstreet-io
- notify-success at priority 0, notify-failure at priority 1
- triggers on push to main + manual
Secret names are lowercase (cloudflare_api_token, cloudflare_account_id,
pushover_token, pushover_user) to match what's configured on the
halfstreet repo in Woodpecker.
Needed by the deploy step (npx wrangler pages deploy). Keeping it as a
local devDep means CI doesn't re-download wrangler on every run, matching
how ethanjlewis.com is set up.
Single notify step now runs for both success and failure
(when.status: [success, failure]), branching on
CI_PIPELINE_STATUS for the title. Success uses Pushover
priority -1 (quiet) so passing-build pings don't alert at
night; failures stay at priority 0 (default).
Uses pushover_token and pushover_user secrets configured in Woodpecker.
Step runs only on failure (when.status: failure) so successful runs stay
quiet. Message includes branch, commit message, and a link back to the
pipeline.
Halfstreet now lives at halfstreet.io as the entire site, so the game
serves at / instead of /mystery. After git-filter-repo lifted
src/mystery/ to src/, the page's css/ts imports need to drop the
mystery/ segment.
Substantial bible expansion (frontmatter, Obsidian list/heading formatting,
expanded core themes section, target length 25–28 rooms).
Adds .obsidian/ vault config (app/appearance/core-plugins) and gitignore
rules for the per-machine workspace cache, mirroring the world vault setup.
Done before extracting Halfstreet to its own repo so this work travels with
the filter-repo extraction.
Move Halfstreet game content (rooms, items, encounter narration, endings)
from TypeScript object literals into markdown files editable in Obsidian.
Engine, UI, and the public World type are unchanged. Three-room prototype
verified end-to-end via tests and manual playthrough.
Includes code-review followups: locked-exit requires validation,
endings-completeness check, and clear errors for malformed section headers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Validate lockedExits[*].requires resolves to a known item or flag
- Throw if any of true/wrong/bad ending markdown files are missing
- Detect malformed ## headers (spaces, dots, etc.) and throw a clear
error rather than silently dropping the section
Tests: 86 passing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gray-matter eagerly loads Node's Buffer API path even when only
matter(rawString) is called, crashing browser bundles. Replace it with
an inline frontmatter parser backed by the browser-safe yaml package.
All 84 mystery tests pass; build is clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rooms, items, and endings now come from .md files under world/{rooms,items,endings}/.
Encounters still come from encounters.ts (Tasks 11–12 will complete that leg).
Cross-reference validation at module init ensures exits, item refs, and encounter
refs are all consistent. Deletes rooms.ts, items.ts, roundtrip.test.ts, and the
one-shot migration script (whose output is already committed).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds scripts/migrate-mystery-content.ts which reads rooms, items,
encounters, and endings from TypeScript source and emits byte-identical
markdown files under src/mystery/world/{rooms,items,encounters,endings}/.
Installs tsx as a devDep to support .ts imports across src/ during the
one-shot run.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
14-task plan covering: gray-matter+zod setup, schemas, four pure
parsers (room/item/ending/encounter), narration() helper with
auto-registration, one-shot migration script, round-trip
verification, world assembly cutover, encounters.ts refactor,
manual playthrough, and Obsidian vault config.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Address spec review feedback: switch frontmatter convention from snake_case
to camelCase to match existing TS field names; surface the working
three-room prototype as the explicit deliverable; rephrase the Astro
content collections out-of-scope item to clarify the import.meta.glob
choice is a tooling decision, not a feature exclusion.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move rooms, items, encounter narration, and endings from TypeScript
object literals to markdown files editable in Obsidian. Engine code,
tests, and the World shape are unchanged. Tonal refinement is a
separate spec that follows.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
appendLines() only updated the DOM, never state.transcript, so any
UI-originated line (player input, restart/undo/quit messages) vanished
on page reload. Engine narration was unaffected because dispatch()
already adds its lines to state.transcript.
Fix: appendLines() now pushes into state.transcript (capped at
TRANSCRIPT_CAP) and renders. Engine output uses renderAll() directly
since dispatch already added its lines to state.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Captures the final code reviewer's recommendations: hard prerequisites
(read/light/extinguish/use verbs; disambiguation; endedWith ending screen),
should-fix items (look-at parser polish, theme-state divergence, roomState
type lie), and a polish backlog. Read this before generating the next plan.
Pure computeChips function (TDD, 4 tests) generates context-aware direction/item/encounter/meta chips from game state; chip-render.ts wires chips to DOM; terminal.ts calls refreshChips on init, each Enter dispatch, restart, and undo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>