Approve MVP technical and acceptance plans

This commit is contained in:
2026-08-17 01:08:23 -07:00
parent b08fea918c
commit f3688cd62b
10 changed files with 1929 additions and 9 deletions
+42
View File
@@ -0,0 +1,42 @@
# Instructions for Labyricorn publishing content
These instructions apply to the entire `.labyricorn/` directory. More specific
instructions in `project/AGENTS.md` and `devlog/AGENTS.md` also apply within
those directories.
## Purpose and ownership
- This directory is the repository-owned source for the project's exhibition
page and development log on Labyricorn.
- This project repository is authoritative for the content. The Labyricorn site
imports it as read-only content.
- Read `.labyricorn/README.md` and the nearest scoped `AGENTS.md` before editing
a publishing record.
- Keep publishing changes focused. Do not alter application code merely to
support an exhibition or devlog edit unless the user separately requests it.
## Authorization and content contract
- Preserve unrelated and uncommitted work. Never discard changes to obtain a
clean working tree.
- Do not commit or push unless the user explicitly requests publication.
- Use native Lektor records named `contents.lr` and preserve `schema_version: 1`
until a coordinated schema migration is approved.
- Keep attachments inside the record subtree that owns them.
- Use UTF-8 text, `YYYY-MM-DD` dates, and stable lowercase hyphenated slugs.
- Do not add templates, models, plugins, workflows, executable files, builds,
application state, credentials, raw HTML, scripts, or active third-party
content below `.labyricorn/`.
- Treat published URLs as durable. Ask before renaming or removing a published
record; redirects or archival behavior may be required first.
## Security, synchronization, and review
- Never store or print passwords, API tokens, refresh credentials, private keys,
`.netrc` contents, or other secrets in this directory.
- Do not claim content is public merely because a push succeeded. Report push
status and Labyricorn synchronization status separately.
- Parse changed records, confirm referenced source commits exist, run
`git diff --check`, and review the exact publishing diff before committing.
- At handoff, report changed publishing records, validation performed, commit
and push status, and synchronization status if known.
+6 -3
View File
@@ -6,6 +6,9 @@ This repository owns the public project page and development log for Twungeon.
- `.labyricorn/devlog/contents.lr` is the development-log index.
- Each directory below `.labyricorn/devlog/` is one chronological entry.
The project is currently in pre-development. The PRD and MVP definition are
complete; future work should add dated devlog entries as the project moves
through planning, implementation, testing, release, and maintenance.
The project is currently in pre-implementation planning. The PRD and MVP
definition are complete, the technical specification is approved, and a
traceable acceptance-test/build checklist now defines the route into
implementation. Runtime behavior and live Twitch integration are not yet
claimed complete. Future work should add dated devlog entries as the project
moves through implementation, testing, release, and maintenance.
+57
View File
@@ -0,0 +1,57 @@
# Instructions for the Labyricorn development log
These instructions apply to `.labyricorn/devlog/` and help coding assistants
maintain the project's public development narrative safely.
## Format and ownership
- Keep the devlog index at `devlog/contents.lr` using `_model: devlog` and
`schema_version: 1`.
- Store each entry at `devlog/<stable-slug>/contents.lr` using
`_model: devlog-entry` and `schema_version: 1`.
- Use lowercase hyphenated entry slugs. Published slugs are durable public URLs;
do not rename or delete them without explicit approval and a redirect or
archival decision.
- Every entry must contain `title`, `date`, `author`, `summary`, `tags`,
`source_commit`, and Markdown `body` fields.
- Keep entry attachments in that entry's directory. Only PNG, JPEG, and WebP
images are accepted by the Labyricorn importer.
- Do not add templates, models, plugins, executable code, builds, application
state, credentials, or active third-party content below this directory.
## Content guidance
- Base entries on verifiable repository history, project documentation, and
implemented behavior. Do not invent motivations, results, release status,
user feedback, or completion claims.
- Explain the milestone, why it mattered, and what changed. Keep one main
milestone per entry and write a summary that works in a chronological list.
Do not merely expand a commit message or enumerate every changed file.
- Distinguish design and schema contracts from completed runtime behavior.
- Link to the relevant canonical commit using the repository's HTTPS web URL.
- Use the referenced commit's actual `YYYY-MM-DD` calendar date for retrospective
entries, not the date on which the prose was drafted.
- Set `source_commit` to the full lowercase 40-character commit ID most directly
associated with the milestone. It must be an ancestor of the published branch.
- Topics are comma-separated display labels. Unknown topics are allowed: the
site keeps them visible but unlinked. Do not create or edit the site's tracked
tag registry from this project repository.
- Use Markdown without raw HTML, scripts, embedded credentials, or active
external content.
- Multiple entries may share a date. The site resolves ties from source history;
do not invent timestamps or manual next/previous links.
- Do not rewrite older entries merely because the implementation later changed.
Add a new entry or a clearly labelled correction when historical context is
needed.
## Safety and review
- Preserve unrelated project files and uncommitted work. The devlog owns only
`.labyricorn/devlog/`.
- Do not commit or push unless the user explicitly requests publication.
- Before publication, verify claims against the referenced commit, validate all
records, check that the source-commit link uses the same full commit ID, review
the exact devlog diff, and confirm unrelated files are not staged in the
publishing commit.
- Prefer the repository-root `devlog_editor.py` for routine validation and safe
publication when it is available.
@@ -51,3 +51,6 @@ This entry marks the end of product definition, not the beginning of
implementation. Future devlog entries will record technical planning, build
phases, validation evidence, scope changes, and what is learned from real
viewers.
The canonical source for this product-definition milestone is
[commit `d67d9fc5230985895de9dd8232525ea8ca474e05`](https://git.labyricorn.com/Labyricorn/Twungeon/commit/d67d9fc5230985895de9dd8232525ea8ca474e05).
+33
View File
@@ -0,0 +1,33 @@
# Instructions for the Labyricorn project record
These instructions supplement `.labyricorn/AGENTS.md` and apply to the
`project/` directory.
## Record structure
- Maintain exactly one project record at `project/contents.lr` using
`_model: project` and `schema_version: 1`.
- Keep `project_id` stable. It is the durable identity used by the importer and
public URL.
- Ask before changing the repository URL, default branch, project status,
author, start date, or project identity.
- Store project-owned images in this directory and reference them by filename
from `contents.lr`. Only PNG, JPEG, and WebP images are accepted.
## Editorial guidance
- Write the exhibition page as a concise project narrative, not as a copy of
the repository README or as release documentation.
- Verify technical and status claims against the repository before editing.
- Distinguish completed work, formal design work, experiments, and future work.
- Do not manually add repository-derived commit, license, language, release,
issue, star, or fork metadata. The Labyricorn importer owns those values.
- Keep the summary suitable for listings and social previews.
## Review requirements
- Confirm all required project fields are present and any image reference stays
inside `project/`.
- Check Markdown links are intentional HTTPS links and prose makes no unsupported
claims.
- Validate the record and run `git diff --check` before committing.
+12 -5
View File
@@ -18,7 +18,7 @@ repository_url: https://git.labyricorn.com/Labyricorn/Twungeon
---
default_branch: main
---
tags: Twitch, Twitch Extension, RPGJS, Twurple, TypeScript, game design, pre-development, PRD, MVP, planning, architecture, documentation
tags: Twitch, Twitch Extension, RPGJS, Twurple, TypeScript, game design, pre-development, PRD, MVP, planning, architecture, acceptance testing, documentation
---
body:
@@ -45,8 +45,15 @@ Twurple for Twitch integration, and a Twitch Extension for player controls.
The design keeps Twungeon's game rules separate from RPGJS where practical so
the prototype can be replaced without discarding the validated concept.
The current source of truth is the
The product source of truth is the
[Twungeon MVP Product Requirements Document](https://git.labyricorn.com/Labyricorn/Twungeon/blob/main/Twungeon_MVP_PRD_Current.md).
The project is in pre-development: the PRD and MVP definition are complete,
and this repository will document the transition through technical planning,
implementation, testing, release preparation, and maintenance.
The approved
[technical specification](https://git.labyricorn.com/Labyricorn/Twungeon/blob/main/Twungeon_MVP_Technical_Specification.md)
defines the authoritative architecture and game behavior. The
[acceptance-test and build checklist](https://git.labyricorn.com/Labyricorn/Twungeon/blob/main/Twungeon_MVP_Acceptance_Test_and_Build_Checklist.md)
sequences implementation and maps evidence to all 33 MVP success criteria.
The project remains in pre-implementation planning. These documents establish
the build baseline, but they do not claim that runtime behavior, Twitch
integration, or viewer validation is complete. Future records will document
implementation, testing, release preparation, and maintenance as they occur.
+32 -1
View File
@@ -1,3 +1,34 @@
# Twungeon
Twungeon as a Twitch-controlled cooperative dungeon game.
Twungeon is a Twitch-controlled cooperative dungeon game in which viewers watch
a shared world on stream, join through Twitch chat, and control individual
characters with a Twitch Extension.
## Project status
Twungeon is in pre-implementation planning. The MVP product requirements and
technical specification are approved, and the acceptance-test/build checklist
is ready to guide implementation. No runtime MVP behavior or live Twitch
integration is claimed complete yet.
## Planning documents
- [MVP Product Requirements Document](Twungeon_MVP_PRD_Current.md) — defines the
product boundary and 33 success criteria.
- [MVP Technical Specification](Twungeon_MVP_Technical_Specification.md) —
defines the approved architecture, authoritative state, rules, contracts, and
failure behavior.
- [MVP Acceptance-Test and Build Checklist](Twungeon_MVP_Acceptance_Test_and_Build_Checklist.md)
— sequences implementation and defines the evidence required for each success
criterion.
The PRD is authoritative for product scope. The specification and checklist
must be updated alongside any approved requirement change.
## Initial technical direction
The MVP is planned around TypeScript, RPGJS, Twurple, and a Twitch Extension,
with Twungeon's game rules isolated from framework and service adapters where
practical.
Public project and development-log records are maintained under `.labyricorn/`.
@@ -0,0 +1,740 @@
# Twungeon MVP Acceptance-Test and Build Checklist
**Status:** Approved execution plan
**Date:** 2026-08-17
**Product baseline:** [Twungeon MVP PRD](Twungeon_MVP_PRD_Current.md)
**Technical baseline:** [Twungeon MVP Technical Specification](Twungeon_MVP_Technical_Specification.md)
## 1. Purpose
This document converts the approved Twungeon MVP product requirements and
technical specification into a sequenced implementation plan with verification
checkpoints. It is both the build checklist and the acceptance-test plan.
The checklist is complete as a planning artifact. Runtime items remain unchecked
until implementation and evidence satisfy their stated exit criteria.
## 2. Execution Rules
- Work through build phases in order unless a dependency is explicitly mocked.
- Keep the backend domain core authoritative for every game-state transition.
- Do not move Twitch, RPGJS, browser, or transport logic into the domain package.
- Add or update tests in the same change as the behavior they verify.
- Do not mark a phase complete while any required test or review is failing.
- Do not mark a live Twitch criterion complete using only a mock or unit test.
- Record evidence for every acceptance-test ID.
- Treat a PRD behavior change as a requirements change, not an implementation
shortcut; update the PRD, specification, checklist, and affected tests
together.
- Preserve the MVP non-goals. New features require an explicit scope decision.
## 3. Status and Evidence Conventions
Checklist status:
- `[ ]` not started or not yet proven
- `[x]` completed and supported by evidence
- `[!]` blocked; include the blocker and owner beside the item
Acceptance evidence should be stored under:
```text
evidence/acceptance/AT-###/
```
Each acceptance evidence record must contain:
- build or commit identifier;
- date and tester;
- environment and relevant non-secret configuration;
- exact procedure or automated test name;
- expected and actual result;
- pass/fail status;
- relevant logs, screenshots, or video; and
- linked defect IDs for any failure.
Secrets, full authentication payloads, and private viewer information must not
be included in evidence.
## 4. Standard Quality Gates
Project scaffolding must expose equivalent commands for these gates:
```text
npm run lint
npm run typecheck
npm test
npm run test:integration
npm run build
```
Live Twitch and first-time-viewer tests are separate manual gates. Exact command
names may change with the selected workspace tooling, but the capabilities may
not be removed.
Every implementation phase closes with:
- [ ] lint passes;
- [ ] type checking passes;
- [ ] affected unit tests pass;
- [ ] affected integration tests pass;
- [ ] production build passes;
- [ ] no secrets or generated credentials appear in the diff;
- [ ] documentation matches implemented behavior; and
- [ ] evidence is recorded for any acceptance criterion claimed complete.
## 5. Build Sequence
### Phase 0: Planning baseline
**Goal:** Establish the approved source documents and prevent scope drift.
- [x] Record the current MVP behavior and 33 success criteria in the PRD.
- [x] Approve the technical specification as the implementation baseline.
- [x] Resolve tile occupancy, attack range, command ordering, timer-boundary,
restart, resurrection, presentation, and authority decisions.
- [x] Create this traceable acceptance-test and build checklist.
- [ ] Confirm the repository package manager and TypeScript workspace tooling.
- [ ] Confirm access to a Twitch development channel, developer application,
Extension configuration, and test viewer identities.
- [ ] Select a configurable Bits resurrection price for live testing.
- [ ] Record implementation-time Twitch/Twurple versions and verify their exact
scopes, event types, authentication requirements, and transport setup against
official documentation.
**Checkpoint P0:** The team can identify the PRD, specification, checklist, open
integration configuration, and the rule for changing each baseline.
### Phase 1: Repository and process scaffold
**Goal:** Produce a clean, repeatable build with safe configuration boundaries.
- [ ] Create the workspace structure described in specification section 18.
- [ ] Configure TypeScript, linting, formatting, unit tests, integration tests,
and production builds.
- [ ] Create backend, Extension, stream-view, domain, contracts,
dungeon-generator, Twitch-adapter, RPGJS-adapter, and shared-UI boundaries.
- [ ] Enforce dependency direction so the domain package cannot import adapter
or framework packages.
- [ ] Implement typed configuration loading and startup validation.
- [ ] Add `.env.example` containing variable names and safe descriptions only.
- [ ] Ensure real environment and credential files are ignored.
- [ ] Add `GET /health` with startup/readiness reporting that exposes no secrets.
- [ ] Draft installation and startup documentation as soon as commands exist.
- [ ] Verify a clean checkout can install dependencies, type-check, test, and
build without undocumented local files.
**Checkpoint P1:** Standard quality gates run from a clean checkout, missing
configuration fails clearly, and no secret is present in source or client
artifacts.
### Phase 2: Shared contracts and authoritative domain foundation
**Goal:** Establish deterministic, framework-independent state transitions.
- [ ] Define validated IDs, positions, commands, command envelopes, snapshots,
domain events, rejection reasons, and Twitch message contracts.
- [ ] Implement RunState, FloorState, PhaseState, PlayerState, GoblinState, and
ActionLogEntry invariants from specification section 6.
- [ ] Implement serialized command processing and atomic state commits.
- [ ] Implement request idempotency and stale run/floor/phase rejection.
- [ ] Inject monotonic clock, scheduler, ID generator, and random provider.
- [ ] Generate ordered structured action-log entries only from domain events.
- [ ] Add invariant tests that reject invalid HP, AP, Guard, identity, phase,
entity, and sequence states.
- [ ] Add concurrency tests proving two commands cannot spend the same AP or
apply after a floor transition.
**Checkpoint P2:** The domain package can process deterministic commands and
events without importing Twitch, RPGJS, HTTP, WebSocket, or browser code.
### Phase 3: Dungeon generation and read-only rendering
**Goal:** Repeatedly generate and display valid two-room floors.
- [ ] Implement seeded spawn-room and exit-room generation.
- [ ] Implement varied room sizes, positions, and corridor shapes.
- [ ] Place exactly one exit and one valid Goblin guard position.
- [ ] Validate orthogonal connectivity from spawn to exit.
- [ ] Bound invalid-generation retries and expose terminal generation failure.
- [ ] Test same-seed determinism and multi-seed variation.
- [ ] Test hundreds of generated seeds for required entities and connectivity.
- [ ] Implement the broadcast-mode three-section layout.
- [ ] Render players, Goblin, walls, paths, exit, shared status, and action log
with temporary but distinguishable visuals.
- [ ] Render the exact dormant banner text.
**Checkpoint P3:** A local read-only stream view can cycle through many valid,
visibly varied floors without a Twitch connection.
### Phase 4: Player actions, phases, combat, and AutoGuard
**Goal:** Complete the local turn-based player/enemy loop.
- [ ] Implement dormant, Player Phase, Enemy Phase, and transition states.
- [ ] Start Player Phase with 2 AP and zero Guard for each living participant.
- [ ] Implement 25 seconds per living player with a 120-second cap.
- [ ] End Player Phase early when its initial eligible set is finished or dead.
- [ ] Decide deadline acceptance using gateway arrival before the monotonic
deadline; reject arrivals at or after the deadline.
- [ ] Implement orthogonal movement, wall/Goblin blocking, shared player tiles,
and exit entry.
- [ ] Implement adjacent selected-target player attacks with 65% hit chance and
1 damage.
- [ ] Implement self-heal to 3 HP, once per floor, for 1 AP.
- [ ] Implement pass as exactly 1 AP.
- [ ] Reject invalid commands without consuming AP.
- [ ] Convert remaining AP to Guard when Player Phase ends.
- [ ] Block one successful hit per Guard; do not consume Guard on misses.
- [ ] Reset Guard at the next Player Phase.
- [ ] Emit every required movement, combat, heal, Guard, and phase log event.
**Checkpoint P4:** Local deterministic tests can run complete rounds, including
early completion, expiry, AFK AutoGuard, hits, misses, healing, and rejection.
### Phase 5: Goblin Guard state machine
**Goal:** Implement the complete one-enemy behavior without adding advanced AI.
- [ ] Spawn one passive Goblin with 2 HP at its guard position.
- [ ] Aggro on a valid attack whether it hits or misses.
- [ ] Retain the original attacker as target while that player lives.
- [ ] Implement two Enemy Phase AP for move and attack combinations.
- [ ] Implement 50% hit chance and 1 damage.
- [ ] Implement deterministic shortest-path pursuit with the approved tie order.
- [ ] On target death, clear the target and return toward guard position.
- [ ] Prevent attacks while returning and restore passive guarding on arrival.
- [ ] Remove blocking and actions when the Goblin dies.
- [ ] Allow floor completion while the Goblin remains alive.
- [ ] Log aggro, movement/return where useful, attacks, hits, misses, damage,
Guard blocks, and death.
**Checkpoint P5:** Automated scenarios cover guarding, aggro, pursuit, attacks,
target death, return, death, and escape with the Goblin alive.
### Phase 6: Player lifecycle, floor lifecycle, and run lifecycle
**Goal:** Complete the local gameplay loop before adding live Twitch input.
- [ ] Create one player record per stable synthetic Twitch user ID.
- [ ] Prevent duplicate characters and same-floor death bypass.
- [ ] Implement full death state, control disablement, and death markers.
- [ ] Implement late spawn with 0 AP until the next Player Phase.
- [ ] Implement resurrection at spawn with 3 HP, unchanged heal availability,
and 0 AP until the next Player Phase.
- [ ] Advance immediately when a living player enters the exit.
- [ ] Reject queued commands for the previous floor.
- [ ] Generate the next floor and Goblin.
- [ ] Revive, heal, reposition, and refresh self-heal for all participants on
floor advancement.
- [ ] Reset to a new Floor 1 run immediately after the last participant dies.
- [ ] Distinguish zero participants from a total-party wipe.
- [ ] Preserve character identity and state across simulated client reconnects.
- [ ] Verify expected new-run behavior after a simulated backend restart.
- [ ] Run a deterministic multi-floor soak test.
**Checkpoint P6:** A local driver can repeat the entire gameplay loop through
spawn, action, death, resurrection or advancement, new floors, and wipe reset.
### Phase 7: Backend API, state stream, and UIs
**Goal:** Make the authoritative local game usable through production-shaped
transport and presentation boundaries.
- [ ] Implement Extension session establishment without character creation.
- [ ] Implement authenticated command submission with stable rejection reasons.
- [ ] Implement read-only and personalized WebSocket connections.
- [ ] Send a complete snapshot on connection and ordered updates afterward.
- [ ] Detect sequence gaps and replace client state with a fresh snapshot.
- [ ] Reconnect without creating a player or replaying commands.
- [ ] Implement broadcast and Extension modes from shared UI components.
- [ ] Implement Up, Down, Left, Right, Attack, target selection, self-heal, and
pass controls.
- [ ] Display personalized HP, AP, Guard, heal, life, phase, binding, and disabled
reason state.
- [ ] Keep the three-section broadcast layout and chronological log readable.
- [ ] Verify two synthetic authenticated clients independently control two
characters in one dungeon.
**Checkpoint P7:** Two local clients can reconnect, receive ordered state, issue
independent controls, and observe the same authoritative dungeon and log.
### Phase 8: Twitch chat, follower eligibility, and identity binding
**Goal:** Replace synthetic identities with verified Twitch viewer identities.
- [ ] Configure Twurple through external environment values.
- [ ] Connect to the configured broadcaster/channel and expose connection state.
- [ ] Receive and normalize chat activity required for `!spawn`.
- [ ] Extract stable Twitch user ID rather than trusting display name.
- [ ] Verify follower eligibility against the configured broadcaster.
- [ ] Accept eligible `!spawn`; explain follower, duplicate, or death rejection.
- [ ] Verify the Twitch Extension credential server-side.
- [ ] Bind Extension controls only when its stable user ID equals the chat-spawn
owner ID.
- [ ] Reject unauthenticated, opaque, mismatched, and cross-player controls.
- [ ] Reconnect Twitch integrations with bounded backoff and event deduplication.
- [ ] Fail closed for new spawn/binding when Twitch verification is unavailable.
- [ ] Test two real Twitch viewers with distinct characters.
**Checkpoint P8:** A follower can spawn and control only their own character;
non-followers and identity mismatches cannot acquire control.
### Phase 9: Bits resurrection
**Goal:** Complete the Twitch-backed death recovery branch.
- [ ] Configure the approved positive Bits price outside domain code.
- [ ] Subscribe to and normalize the selected Twitch Bits event.
- [ ] Deduplicate by stable external event ID.
- [ ] Map the event's stable Twitch user ID to the correct dead character.
- [ ] Apply resurrection only when the configured amount/rule is satisfied.
- [ ] Reject duplicate, anonymous, insufficient, living-user, and unknown-user
events without mutating game state.
- [ ] Confirm resurrection position, HP, AP timing, and heal preservation.
- [ ] Log a successful resurrection and safe diagnostics for rejection.
**Checkpoint P9:** A real Bits event resurrects exactly the intended dead player
once, without permitting cross-user or duplicate resurrection.
### Phase 10: Operator documentation and acceptance campaign
**Goal:** Prove a clean checkout and real viewers can complete the MVP loop
without undocumented developer intervention.
- [ ] Complete `docs/twitch-setup.md` for every topic in PRD section 20.
- [ ] Complete `docs/testing.md` for local, integration, live-channel, and
troubleshooting verification.
- [ ] Verify all required software, dependencies, credentials, identifiers,
scopes, environment values, URLs, HTTPS/public endpoints, and startup order.
- [ ] Document safe secret separation and rotation.
- [ ] Document event-flow verification and common authentication, EventSub,
Extension, identity-binding, follower-check, and Bits failures.
- [ ] Have a second operator connect a clean checkout using only documentation.
- [ ] Run all automated quality gates from that clean checkout.
- [ ] Execute AT-001 through AT-030 and capture evidence.
- [ ] Run the first-time-viewer usability session for AT-031.
- [ ] Run the documentation troubleshooting review for AT-032.
- [ ] Conduct the real-viewer concept-validation session for AT-033.
- [ ] Triage failures; fix or explicitly reject them before MVP acceptance.
**Checkpoint P10:** Every PRD criterion has passing evidence, known failures are
resolved, and a real viewer can complete the loop without developer assistance.
## 6. Acceptance-Test Matrix
### AT-001 — Clean installation
**PRD criterion:** 1
- [ ] From a clean checkout on a supported machine, follow only repository
documentation to install dependencies and start all required components.
- [ ] Confirm no undocumented global tool, local file, or developer knowledge is
needed.
**Level:** Clean-environment manual test plus build automation
**Pass evidence:** Full command transcript, environment description, health
response, and screenshots of running backend, Extension, and stream view.
### AT-002 — External configuration and secret safety
**PRD criterion:** 2
- [ ] Run secret scanning and inspect built client artifacts.
- [ ] Confirm credentials, identifiers, and environment-specific URLs are
externally configured and no secret is hard-coded or returned to clients.
**Level:** Automated scan plus review
**Pass evidence:** Scan output, configuration test results, and reviewer signoff.
### AT-003 — Documented channel connection
**PRD criterion:** 3
- [ ] Have a second operator configure a Twitch channel from a clean checkout
using only `docs/twitch-setup.md`.
- [ ] Confirm application, Extension, authentication, event transport, URLs, and
broadcaster identifiers are correct.
**Level:** Independent operator test
**Pass evidence:** Operator checklist, redacted configuration inventory, and
successful connection logs.
### AT-004 — Required Twitch events and chat
**PRD criterion:** 4
- [ ] Generate the live chat and Twitch events used by the MVP.
- [ ] Confirm Twungeon receives each once, normalizes it, and reports connection
health without exposing credentials.
**Level:** Live integration
**Pass evidence:** Correlated redacted Twitch-adapter and backend logs.
### AT-005 — Extension-to-backend communication
**PRD criterion:** 5
- [ ] Load the configured Extension, establish an authenticated session, receive
a snapshot, submit a command, and observe an ordered result.
**Level:** Live integration
**Pass evidence:** Browser capture and correlated gateway/backend logs.
### AT-006 — Three-section presentation
**PRD criterion:** 6
- [ ] Confirm the stream view contains a compact upper-left control/status area,
the largest upper-right game area, and a full-width bottom action log.
- [ ] Confirm required entities and state are understandable with temporary art.
**Level:** Visual review
**Pass evidence:** Representative stream screenshot and reviewer signoff.
### AT-007 — Dormant behavior
**PRD criterion:** 7
- [ ] Start with zero participants and wait longer than a maximum Player Phase.
- [ ] Confirm the floor remains loaded, no timer or Enemy Phase runs, the Goblin
does not act, and the exact `Type !spawn to spawn in the Twungeon!` banner is
shown.
**Level:** Automated domain/integration plus visual review
**Pass evidence:** State assertions, elapsed-time logs, and screenshot.
### AT-008 — Follower spawn
**PRD criterion:** 8
- [ ] As a verified follower with no character or same-floor death, issue
`!spawn` and confirm one character appears in the current spawn room.
**Level:** Live integration
**Pass evidence:** Chat capture, follower result, spawn event, and stream capture.
### AT-009 — Non-follower rejection
**PRD criterion:** 9
- [ ] As a verified non-follower, issue `!spawn` and confirm no character or
identity binding is created.
**Level:** Live integration
**Pass evidence:** Chat capture, rejection result, and unchanged snapshot.
### AT-010 — Duplicate spawn prevention
**PRD criterion:** 10
- [ ] Issue repeated and concurrent `!spawn` requests for one Twitch user.
- [ ] Confirm exactly one character exists and duplicate events are harmless.
**Level:** Automated integration plus live spot check
**Pass evidence:** Concurrency test output and player-state snapshot.
### AT-011 — Chat-to-Extension identity binding
**PRD criterion:** 11
- [ ] Spawn through chat, authenticate the same viewer in the Extension, and
confirm both resolve to the same stable Twitch user ID before controls enable.
**Level:** Live integration
**Pass evidence:** Redacted identity-correlation logs and enabled controller.
### AT-012 — Cross-player control rejection
**PRD criterion:** 12
- [ ] Attempt to control viewer A's character from viewer B's authenticated
Extension and through client-tampered ownership fields.
- [ ] Confirm every attempt is rejected without state change or AP loss.
**Level:** Automated security integration plus live test
**Pass evidence:** Rejection responses, unchanged state, and audit logs.
### AT-013 — Multiple independent viewers
**PRD criterion:** 13
- [ ] Spawn at least two verified viewers and have each issue actions through
their own Extension during the same Player Phase.
**Level:** Live multi-viewer integration
**Pass evidence:** Stream recording, ordered commands, and per-player state.
### AT-014 — Reliable Extension commands
**PRD criterion:** 14
- [ ] Exercise Up, Down, Left, Right, Attack, target selection, self-heal, and
pass, including valid, invalid, duplicate, and stale requests.
- [ ] Confirm one authoritative result per request and correct UI feedback.
**Level:** Automated E2E plus live spot check
**Pass evidence:** Command suite output and controller recording.
### AT-015 — Phase cycle
**PRD criterion:** 15
- [ ] Run repeated Player Phase and Enemy Phase cycles.
- [ ] Confirm players always receive initiative and Goblin actions occur only in
Enemy Phase.
**Level:** Automated domain/integration
**Pass evidence:** State-transition assertions and ordered event log.
### AT-016 — Scaled timer and cap
**PRD criterion:** 16
- [ ] Verify durations of 25, 50, 75, 100, and 120 seconds for one through five
living players and 120 seconds for more than five.
**Level:** Automated fake-clock domain test plus one live timing check
**Pass evidence:** Parameterized test output and live timer recording.
### AT-017 — Early Player Phase completion
**PRD criterion:** 17
- [ ] Have every initially eligible living player spend/pass all AP before the
deadline and confirm Enemy Phase begins immediately.
- [ ] Confirm a late spawn or resurrection does not extend the completion set.
**Level:** Automated domain/integration
**Pass evidence:** Fake-clock transition assertions and event sequence.
### AT-018 — AP, Guard, combat, heal, HP, and death
**PRD criterion:** 18
- [ ] Exercise every valid AP combination, valid and invalid movement, player
and Goblin hits/misses, Guard conversion/blocks, heal use/refresh, damage, and
death.
- [ ] Confirm invalid actions cost no AP and unused AP blocks only successful
hits in the immediately following Enemy Phase.
**Level:** Automated deterministic domain suite
**Pass evidence:** Branch/parameterized test output and state snapshots.
### AT-019 — Accurate action log
**PRD criterion:** 19
- [ ] Trigger every log category required by PRD section 4.2.
- [ ] Confirm messages are chronological, accurate, backend-generated, restored
after reconnect, and ordered by sequence.
**Level:** Automated integration plus visual review
**Pass evidence:** Expected/actual event fixture and rendered log capture.
### AT-020 — Complete Goblin behavior
**PRD criterion:** 20
- [ ] Verify passive guarding, aggro on attack, target pursuit, move/attack AP,
target death, deterministic return, resumed guarding, and remaining behind on
transition.
**Level:** Automated deterministic domain suite
**Pass evidence:** State-machine test output and representative replay.
### AT-021 — Repeated valid floor generation
**PRD criterion:** 21
- [ ] Generate a large deterministic seed sample.
- [ ] Confirm every floor has exactly two rooms, a spawn, exit, guard, navigable
connection, and variation in room or corridor properties.
**Level:** Automated property/invariant test
**Pass evidence:** Seed count, invariant results, variation metrics, and samples.
### AT-022 — Escape without killing Goblin
**PRD criterion:** 22
- [ ] Aggro but do not kill the Goblin, move a living player onto the exit, and
confirm immediate group advancement and a new Goblin on the next floor.
**Level:** Automated E2E plus live scenario
**Pass evidence:** Before/after snapshots and stream recording.
### AT-023 — Dead players revive on advancement
**PRD criterion:** 23
- [ ] Kill one player, advance with another, and confirm the dead player returns
at spawn with full HP and refreshed self-heal on the next floor.
**Level:** Automated E2E plus live scenario
**Pass evidence:** Before/after player and floor state.
### AT-024 — Same-floor death cannot use `!spawn`
**PRD criterion:** 24
- [ ] Kill a player and repeat `!spawn` from that Twitch identity.
- [ ] Confirm rejection, no duplicate/replacement, and unchanged death state.
**Level:** Automated integration plus live spot check
**Pass evidence:** Rejection log and unchanged player record.
### AT-025 — Correct-player Bits resurrection
**PRD criterion:** 25
- [ ] With at least two players and one dead, send a qualifying live Bits event
from the dead viewer.
- [ ] Confirm exactly that character returns at spawn with full HP, preserved
heal availability, and AP beginning next Player Phase.
- [ ] Confirm duplicate, insufficient, living-user, and other-user events do not
mutate the dead character.
**Level:** Automated integration plus live Bits event
**Pass evidence:** Redacted event, deduplication log, and before/after state.
### AT-026 — Total-party wipe reset
**PRD criterion:** 26
- [ ] Kill the final living participant before exit.
- [ ] Confirm an immediate new run on Floor 1, new floor/Goblin, every participant
alive at full HP, refreshed heals, and one wipe/reset log sequence.
**Level:** Automated E2E plus live scenario
**Pass evidence:** Run/floor transition and participant snapshots.
### AT-027 — Zero players differs from wipe
**PRD criterion:** 27
- [ ] Compare a new game with no participants to a run where the final player
dies.
- [ ] Confirm the first remains dormant on its current floor and the second
resets immediately with participants restored.
**Level:** Automated domain/integration
**Pass evidence:** Side-by-side state-transition assertions.
### AT-028 — Spawn into an empty current floor
**PRD criterion:** 28
- [ ] From dormant state, issue an eligible `!spawn` for a viewer without a
current-floor death marker.
- [ ] Confirm the existing floor is retained, banner disappears, player spawns,
and a fresh Player Phase begins.
**Level:** Automated integration plus live scenario
**Pass evidence:** Matching floor ID before/after, spawn event, and timer start.
### AT-029 — Refresh and interruption safety
**PRD criterion:** 29
- [ ] Refresh and temporarily disconnect Extension and view clients during
Player Phase, Enemy Phase, death, and floor transition.
- [ ] Confirm identity re-verification, one character, preserved critical state,
full-snapshot recovery, and no replayed command.
**Level:** Automated fault-injection E2E plus live network test
**Pass evidence:** Reconnect traces, sequence-gap recovery, and invariant results.
### AT-030 — Repeated complete-loop stability
**PRD criterion:** 30
- [ ] Run a multi-floor soak containing spawns, concurrent actions, expiry,
combat, heal, death, resurrection, escape, advancement, and wipe reset.
- [ ] Confirm integration, phase, player, Goblin, generator, and log invariants
remain valid throughout.
**Level:** Automated soak plus live multi-floor session
**Pass evidence:** Duration/floor count, event totals, invariant report, and video.
### AT-031 — First-time-viewer usability
**PRD criterion:** 31
- [ ] Recruit a real viewer who has not received private operating instructions.
- [ ] Observe them watch, follow, use `!spawn`, discover controls, and perform a
meaningful action without developer intervention.
**Level:** Moderated usability test
**Pass evidence:** Consent-safe observation notes, completion result, and issues.
### AT-032 — Troubleshooting completeness
**PRD criterion:** 32
- [ ] Have a second operator use the documentation to diagnose controlled
failures in authentication, Twitch events/transport, Extension loading,
identity mismatch, configuration, follower verification, and Bits handling.
- [ ] Confirm each failure has a discoverable symptom, diagnostic, and remedy.
**Level:** Independent documentation fault-injection review
**Pass evidence:** Failure matrix, operator results, and documentation fixes.
### AT-033 — Concept-validation evidence
**PRD criterion:** 33
- [ ] Run the complete prototype with real viewers and collect structured
observations about joining, control comprehension, waiting, cooperation,
combat/escape choices, death/resurrection, and desire to continue.
- [ ] Record what worked, what blocked play, and whether evidence supports
expanding, revising, or stopping the concept.
**Level:** Real-viewer validation session
**Pass evidence:** Consent-safe session summary, observed metrics, issues, and an
explicit MVP learn/build decision. Passing means the prototype produced enough
reliable evidence to make that decision; it does not require positive feedback.
## 7. Acceptance Campaign Order
Run acceptance in increasing order of cost and external dependency:
1. Domain unit and invariant tests.
2. Generator property tests and deterministic multi-floor simulations.
3. Backend/API integration tests with fake Twitch adapters.
4. Browser E2E tests with synthetic authenticated identities.
5. Clean-checkout and independent-operator setup tests.
6. Twitch development-channel tests for chat, followers, Extension, and Bits.
7. Multi-viewer complete-loop and soak sessions.
8. First-time-viewer and concept-validation sessions.
A failure at an earlier layer should be corrected before repeating a more
expensive live test that depends on it.
## 8. MVP Acceptance Decision
The MVP is accepted only when:
- [ ] AT-001 through AT-033 each have passing evidence;
- [ ] all standard quality gates pass from a clean checkout;
- [ ] no unresolved defect breaks identity ownership, authoritative state,
required gameplay, setup reproducibility, or the complete viewer loop;
- [ ] known non-blocking defects and accepted MVP exploits are documented;
- [ ] setup and troubleshooting documentation passed independent review; and
- [ ] the real-viewer validation summary records an explicit decision about
whether and how to continue Twungeon.
## 9. Change Control
- PRD changes require a corresponding success-criteria and scope review.
- Technical-rule changes require specification, contract, and test updates.
- Checklist sequencing may change without altering requirements when the reason
and dependency impact are recorded.
- A checked item may be reopened when later evidence invalidates it.
- Deferred and non-goal features must not be added merely to close an MVP test.
This checklist is the execution index. The PRD defines what must be proven, and
the technical specification defines how the MVP is designed to prove it.
+12
View File
@@ -1,5 +1,17 @@
# Twungeon MVP Product Requirements Document
**Status:** Approved product baseline
**Baseline date:** 2026-08-17
**Implementation design:** [Twungeon MVP Technical Specification](Twungeon_MVP_Technical_Specification.md)
**Execution and verification:** [Twungeon MVP Acceptance-Test and Build Checklist](Twungeon_MVP_Acceptance_Test_and_Build_Checklist.md)
This document remains the source of truth for MVP product scope and acceptance.
The companion specification defines implementation behavior, and the checklist
tracks build sequencing and evidence without replacing these requirements.
## 1. Product Summary
**Twungeon** is a Twitch-controlled cooperative dungeon game in which viewers watch a shared game world on the Twitch stream and control their own characters through a Twitch Extension.
+992
View File
@@ -0,0 +1,992 @@
# Twungeon MVP Technical Specification
**Status:** Approved implementation baseline
**Date:** 2026-08-17
**Approved:** 2026-08-17
**Product source of truth:** [Twungeon MVP PRD](Twungeon_MVP_PRD_Current.md)
**Execution and verification:** [Twungeon MVP Acceptance-Test and Build Checklist](Twungeon_MVP_Acceptance_Test_and_Build_Checklist.md)
**Intended implementation:** TypeScript, RPGJS, Twurple, and a Twitch Extension
## 1. Purpose
This approved document translates the Twungeon MVP Product Requirements
Document into an implementation-ready system design. It defines component responsibilities,
authoritative state, command handling, game-state transitions, spatial rules,
integration boundaries, failure behavior, and verification requirements.
The PRD remains authoritative for product scope and acceptance. If this
technical specification conflicts with the PRD, the PRD wins and this document
must be corrected.
This specification intentionally does not add post-MVP features. Decisions made
here resolve behavior that the PRD requires but does not define precisely enough
for deterministic implementation.
## 2. Design Goals
The implementation must optimize for the following goals, in order:
1. Prove the complete Twitch viewer-to-gameplay loop with real viewers.
2. Keep all game and identity decisions authoritative on the backend.
3. Make command processing deterministic and testable.
4. Keep Twungeon domain rules independent of RPGJS where practical.
5. Make Twitch setup reproducible from a clean checkout.
6. Prefer observable, recoverable failure over silent state corruption.
7. Avoid systems outside the MVP, including long-term progression and
production-grade persistence.
## 3. Scope and Constraints
### 3.1 Included
- Twitch chat `!spawn` handling and follower validation
- Stable Twitch user identity binding between chat and Extension
- One character per Twitch user
- Twitch Extension controls
- Shared stream-facing game view and chronological action log
- Dormant, Player Phase, Enemy Phase, floor transition, and run reset states
- AP, movement, combat, AutoGuard, self-heal, death, and resurrection
- One Goblin Guard with guarding, pursuit, and return behavior
- Varied, navigable two-room floors
- Runtime reconnect and refresh handling
- Clean-checkout Twitch integration documentation
- Automated domain tests and live integration verification
### 3.2 Excluded
All non-goals in PRD section 22 remain excluded. In particular, this design does
not introduce accounts beyond Twitch identity, inventory, progression, multiple
classes or enemies, sophisticated AI, long-term persistence, or anti-exploit
systems.
### 3.3 Runtime persistence boundary
The authoritative run exists in backend memory. Browser refreshes and temporary
client disconnects do not remove or duplicate characters because clients
reconnect to that backend state using the same Twitch user ID.
Surviving a backend process restart is not an MVP requirement. On an intentional
or unintentional backend restart, Twungeon starts a new run on Floor 1 and logs
the reset. Configuration and secrets persist outside the run state.
## 4. System Architecture
Twungeon uses one authoritative backend process for game decisions. Other
components translate external input into authenticated commands or render
backend state; they never modify game state directly.
```text
Twitch chat ----> Twitch adapter -----------+
|
Twitch events --> Twitch adapter -----------+--> Authoritative backend
| | |
Extension ------> Extension API/WebSocket --+ | +--> Action log
|
+--> RPGJS adapter
|
+--> State snapshots
|
+------------------+----------------+
| |
Stream view Extension view
```
### 4.1 Authoritative backend
The backend owns:
- the current run, floor, map, phase, and phase deadline;
- all participating player records and character state;
- Twitch identity bindings and follower eligibility results;
- the Goblin state and AI decisions;
- command ordering, validation, and idempotency;
- random outcomes;
- floor generation and transitions;
- the canonical action log; and
- state snapshots distributed to clients.
Only this component may accept or reject a command or perform a state
transition.
### 4.2 Twitch adapter
The Twitch adapter uses Twurple to:
- receive chat messages needed for `!spawn`;
- extract the stable Twitch user ID and display data from the chat event;
- verify follower eligibility using the configured broadcaster identity;
- receive the Bits event selected for resurrection;
- normalize Twitch events into internal messages; and
- reconnect without submitting the same external event more than once.
Version-specific Twitch scopes, event types, and transport configuration must
be confirmed against the official Twitch and Twurple documentation during the
integration task and recorded in the setup guide. They must not be guessed or
hard-coded into domain logic.
### 4.3 Extension gateway
The Extension gateway:
- accepts Twitch-authenticated Extension sessions;
- verifies the credential server-side;
- resolves the credential to a stable Twitch user ID;
- binds that ID to the same player created from chat;
- accepts controller commands;
- adds a server-issued connection and request identity;
- forwards normalized commands to the game core; and
- distributes personalized state and command results.
The Extension never supplies an authoritative player ID, AP value, position,
target validity, damage result, or follower result.
### 4.4 Domain game core
The domain core is a framework-independent TypeScript module containing:
- state types;
- command validation and reducers;
- phase and floor state machines;
- movement and combat rules;
- Goblin AI;
- dungeon generation contracts;
- deterministic random interfaces; and
- structured domain events.
It must not import RPGJS, Twurple, browser APIs, or Twitch SDK types.
### 4.5 RPGJS adapter
The RPGJS adapter maps domain state and events to prototype rendering and any
engine-specific map representation. It must not decide whether movement,
attacks, spawning, resurrection, or floor completion are valid.
### 4.6 Views
The presentation is implemented as one shared UI system with two modes:
- **Broadcast mode:** a read-only stream source that renders the three-section
layout. Its upper-left area shows shared status and a visible controller
legend, the upper-right area shows the dungeon, and the bottom area shows the
running log.
- **Extension mode:** an authenticated controller using the same visual
language. It renders interactive controls and personalized player status,
while consuming the same authoritative shared state.
This resolves the difference between a personalized controller and a shared
broadcast: controls are visibly represented on the stream, but only an
authenticated viewer's Extension controls are interactive.
## 5. Trust and Security Model
### 5.1 Stable identity
`twitchUserId` is the canonical player key. Display name, chat name, Extension
connection ID, and browser storage are metadata only and cannot establish
ownership.
### 5.2 Identity-binding sequence
1. The Twitch adapter receives `!spawn` with a stable chat user ID.
2. The backend verifies that user follows the configured broadcaster.
3. The backend creates or rejects the character using `twitchUserId`.
4. The Extension sends its Twitch-authenticated credential to the gateway.
5. The gateway verifies the credential and extracts its stable user ID.
6. Controls become enabled only when that ID equals the character owner ID.
7. Every later control command uses the verified server session; client-sent
ownership fields are ignored.
### 5.3 Secrets and configuration
Secrets must be supplied through environment variables or ignored local secret
files. They must never be committed, logged, returned to clients, included in
URLs, or embedded in the Extension bundle.
Configuration validation must fail startup with a specific error when required
values are missing or inconsistent. Logs may name a missing variable but must
not print its value.
### 5.4 Input safety
All external messages are schema-validated. Invalid, unauthorized, stale, or
duplicate messages are rejected without changing game state. Display names and
other viewer text are escaped before rendering or logging.
## 6. Authoritative Data Model
The following types are conceptual contracts. Exact TypeScript syntax may vary,
but the fields and ownership boundaries must remain equivalent.
### 6.1 Run state
```ts
interface RunState {
runId: string
floorNumber: number
floor: FloorState
phase: PhaseState
players: Map<TwitchUserId, PlayerState>
goblin: GoblinState
actionLog: ActionLogEntry[]
nextEventSequence: number
}
```
### 6.2 Player state
```ts
interface PlayerState {
twitchUserId: string
displayName: string
followerVerified: boolean
characterCreated: boolean
extensionBound: boolean
connectionState: 'connected' | 'disconnected'
position: TilePosition
hp: 0 | 1 | 2 | 3
lifeState: 'alive' | 'dead'
ap: 0 | 1 | 2
guard: 0 | 1 | 2
healAvailable: boolean
participatingFloor: number
diedOnFloor: number | null
eligibleThisPhase: boolean
}
```
Connection state does not determine whether a living character participates in
the turn. A disconnected living character remains in the dungeon, receives AP,
and converts unused AP to AutoGuard.
### 6.3 Phase state
```ts
type PhaseState =
| { kind: 'dormant' }
| {
kind: 'player'
phaseId: string
startedAt: number
deadlineAt: number
initialEligiblePlayerIds: string[]
}
| { kind: 'enemy'; phaseId: string }
| { kind: 'transition'; reason: 'floor-advance' | 'party-wipe' }
```
The backend uses a monotonic clock for elapsed phase timing. Wall-clock values
may be included for display but cannot determine acceptance ordering.
### 6.4 Floor state
```ts
interface FloorState {
floorId: string
width: number
height: number
tiles: TileKind[][]
spawnTiles: TilePosition[]
exitPosition: TilePosition
goblinGuardPosition: TilePosition
generationSeed: string
}
type TileKind = 'wall' | 'floor' | 'exit'
```
### 6.5 Goblin state
```ts
interface GoblinState {
hp: 0 | 1 | 2
position: TilePosition
guardPosition: TilePosition
mode: 'guarding' | 'pursuing' | 'returning' | 'dead'
targetPlayerId: string | null
}
```
### 6.6 Structured action log
```ts
interface ActionLogEntry {
sequence: number
runId: string
floorNumber: number
phaseId: string | null
type: string
actorId: string | null
targetId: string | null
message: string
occurredAt: string
}
```
The backend creates log messages from domain events. Clients do not submit log
text. Sequence numbers establish canonical ordering.
## 7. Spatial and Interaction Rules
These rules make movement and combat deterministic while preserving the PRD's
simple cooperative intent.
### 7.1 Grid
- Movement is orthogonal: up, down, left, or right by one tile.
- Diagonal movement and attacks are not permitted.
- Walls are impassable.
- The exit tile is traversable by living players.
- A Goblin blocks player movement into its tile.
- A player blocks Goblin movement into its tile.
- Multiple players may share a tile. This prevents spawn-room crowding and
cooperative path blocking from becoming an accidental MVP system.
### 7.2 Spawn placement
New characters spawn on the first spawn tile chosen by deterministic ordering.
Players may share that tile. A late join never changes the current phase timer
or the set of players required to finish the phase.
### 7.3 Attack range and targeting
- Player and Goblin attacks target an orthogonally adjacent tile.
- A player attack requires a selected living Goblin adjacent to the player.
- A Goblin attack requires its living pursuit target to be adjacent.
- Invalid or stale targets do not consume AP.
- A valid attack consumes AP even when the hit roll misses.
- A successful unblocked hit applies exactly 1 HP of damage.
### 7.4 Exit behavior
When a living player successfully moves onto the exit tile, the move consumes
1 AP and the floor transition starts immediately after that command resolves.
No later queued command for the old floor is processed.
## 8. Command Model
### 8.1 Player commands
```ts
type PlayerCommand =
| { type: 'move'; direction: 'up' | 'down' | 'left' | 'right' }
| { type: 'attack'; targetId: string }
| { type: 'heal-self' }
| { type: 'pass' }
```
Each Extension request includes:
- a client-generated `requestId`;
- the current `runId`, `floorId`, and `phaseId` last observed by the client; and
- exactly one command payload.
The gateway supplies the authenticated `twitchUserId`; it is not accepted from
the payload.
### 8.2 Command ordering
The backend processes accepted messages serially in arrival order. Each message
is fully validated against the state produced by all earlier messages before it
is applied.
This rule resolves simultaneous actions:
- the first accepted player to enter the exit ends the floor;
- the first accepted attack may invalidate a later attack by killing the
target;
- duplicate request IDs return the original result without applying again; and
- commands for an old run, floor, or phase are rejected as stale.
### 8.3 Player command validation
A command is accepted only when:
- the session is authenticated and bound to the character owner;
- the character exists, is alive, and is eligible to act;
- the current phase is Player Phase;
- the command reaches the authoritative queue before the deadline;
- the player has at least 1 AP;
- the supplied run, floor, and phase IDs match current state; and
- command-specific spatial or resource rules pass.
Rejected commands consume no AP and return a machine-readable reason plus a
safe display message.
### 8.4 Pass semantics
Pass costs exactly 1 AP. A player with 2 AP who wants to finish immediately
must pass twice. There is no separate zero-cost end-turn command in the MVP.
## 9. Turn and State Machines
### 9.1 Dormant state
The game starts with a generated Floor 1 and no participating characters.
While no active character exists:
- phase is `dormant`;
- no phase deadline exists;
- no Enemy Phase can run;
- the Goblin cannot act; and
- the spawn banner is visible.
The first accepted `!spawn` creates a living character, removes the banner, and
starts a fresh Player Phase.
The MVP has no voluntary leave or character-removal command. Temporary
disconnects therefore do not create dormancy. A total-party death triggers a
run reset rather than dormancy.
### 9.2 Starting Player Phase
For every living participant present when the phase starts:
1. set AP to 2;
2. set Guard to 0;
3. set `eligibleThisPhase` to true; and
4. include the player in `initialEligiblePlayerIds`.
The duration is:
```text
min(living-player-count * 25 seconds, 120 seconds)
```
The count includes disconnected living characters, matching the PRD's AFK
AutoGuard behavior.
Players who spawn or resurrect after the phase starts receive 0 AP, are not
added to the phase completion set, and may act beginning with the next Player
Phase.
### 9.3 Ending Player Phase
The phase ends when either:
- every player in the initial eligible set has 0 AP or is dead; or
- the authoritative deadline is reached.
At the transition, each living player's remaining AP becomes Guard one-for-one,
then AP becomes 0. Dead players receive no Guard.
Commands already waiting in the serialized backend queue are accepted only if
the gateway recorded their arrival before the deadline. A command arriving at
or after the deadline is rejected even if the phase-transition task has not yet
run.
### 9.4 Enemy Phase
The living Goblin receives 2 AP and repeatedly selects one deterministic action
until it has no AP or no valid action.
After the Goblin finishes, Guard is discarded and a new Player Phase begins,
unless the floor advanced or the run reset during the Enemy Phase.
### 9.5 AutoGuard
When a Goblin attack hits:
1. if the target has Guard greater than 0, decrement Guard and apply no damage;
2. otherwise, subtract 1 HP;
3. if HP reaches 0, perform player death handling.
A miss never consumes Guard. Guard cannot carry into another round.
## 10. Player Lifecycle
### 10.1 `!spawn`
An accepted spawn requires:
- a stable Twitch chat user ID;
- verified follower status;
- no existing character for that ID; and
- no death marker for that ID on the current floor.
The backend creates one character at full HP with heal available. If the game is
already active, the player has 0 AP until the next Player Phase.
Repeated `!spawn` attempts for an existing character return an informative chat
response or log result and never create another character.
### 10.2 Heal
A valid self-heal:
- costs 1 AP;
- requires the player to be alive;
- requires `healAvailable` to be true;
- restores HP to 3 even if already full; and
- sets `healAvailable` to false.
### 10.3 Death
When HP reaches 0:
- life state becomes dead;
- AP and Guard become 0;
- `diedOnFloor` becomes the current floor number;
- controls are disabled; and
- a death event is logged.
The dead character remains associated with the Twitch user ID and cannot be
replaced with `!spawn`.
### 10.4 Bits resurrection
The Bits price is a required positive configuration value and may change
without changing domain code. An accepted resurrection event must identify the
same Twitch user ID as the dead character, satisfy the configured transaction
rule, and have a unique external event ID.
Resurrection:
- restores the player to 3 HP;
- places the player on the deterministic spawn tile;
- clears the current-floor death marker;
- preserves whether the floor heal was already used;
- grants 0 AP during the current phase; and
- enables action beginning with the next Player Phase.
Extra, duplicate, anonymous, insufficient, or non-dead-user events do not alter
game state and must produce a diagnostic result without exposing secrets.
### 10.5 Refresh and reconnect
On Extension refresh or reconnection, the gateway re-verifies identity and
returns the existing character and current personalized snapshot. It never
creates a character from browser state. Multiple connections for the same user
may display state, but all share one player command budget and idempotency
history.
## 11. Goblin Guard Behavior
### 11.1 Guarding
The Goblin begins at its guard position in `guarding` mode. It spends no AP and
does not attack until it is attacked by a player.
### 11.2 Aggro and pursuit
The first valid player attack changes the Goblin to `pursuing` and sets that
attacker as its target, whether the attack hits or misses. Later attacks do not
retarget it while the target lives.
During each Enemy Phase, the Goblin:
1. attacks its target if orthogonally adjacent;
2. otherwise moves one tile along a shortest path to the target; and
3. repeats until its 2 AP are spent or no path/action exists.
Shortest paths use breadth-first search. Equal choices use a fixed direction
priority of up, left, right, then down so tests are deterministic.
### 11.3 Target death and return
If the pursued player dies, the Goblin enters `returning`, clears its target,
and uses later Enemy Phases to take shortest-path movement toward its original
guard position. It does not attack players while returning. On reaching the
guard position, it becomes passive `guarding` again.
### 11.4 Goblin death
At 0 HP the Goblin becomes `dead`, is removed as a blocking entity, and takes no
further actions. The exit remains usable whether the Goblin is alive or dead.
## 12. Floor Generation and Progression
### 12.1 Generator contract
For a supplied seed, the generator must return the same floor and guarantee:
- exactly one rectangular spawn room;
- exactly one rectangular exit room that does not overlap the spawn room;
- one exit tile in the exit room;
- one Goblin guard position adjacent to or near the exit;
- an orthogonally navigable floor path between a spawn tile and the exit; and
- enclosing impassable walls.
Variation must affect room sizes, room positions, or connecting corridor shape
across seeds. Generation retries with a derived seed when validation fails and
fails startup/transition visibly after a bounded retry count rather than loading
an invalid map.
### 12.2 Floor advancement
When a living player enters the exit:
1. enter transition state and reject remaining old-floor commands;
2. increment the floor counter;
3. generate and validate a new floor and Goblin;
4. place every participating player on the new spawn tile;
5. restore every player to 3 HP and alive;
6. clear death markers, AP, and Guard;
7. refresh every self-heal;
8. log the new floor; and
9. begin a fresh Player Phase.
The previous Goblin and map are discarded.
### 12.3 Total-party wipe
After any death resolution, if participating characters exist and none is
alive, the backend immediately:
1. enters transition state;
2. creates a new run ID;
3. sets the floor counter to 1;
4. generates a new starting floor and Goblin;
5. restores all participating players alive at 3 HP;
6. clears death markers, AP, and Guard;
7. refreshes all self-heals;
8. places all players at the spawn tile;
9. logs the wipe and reset; and
10. begins a fresh Player Phase.
Because the reset is immediate, a Bits event cannot interrupt a completed
total-party wipe. Zero participating characters is explicitly not a wipe.
## 13. Randomness
All random behavior goes through an injected pseudo-random interface. The
backend is the sole random authority.
Randomness is used for:
- player 65% attack hit rolls;
- Goblin 50% attack hit rolls; and
- procedural floor parameters.
Tests use fixed sequences or seeds. Production logs record the run/floor seed
and outcome event, but clients never provide rolls or seeds used to resolve a
command.
## 14. Backend Interfaces
Exact framework routing may change, but equivalent contracts are required.
### 14.1 Health
`GET /health`
Returns process readiness without credentials or game secrets. Readiness is
false when required Twitch configuration or the game core failed to initialize.
### 14.2 Extension session
`POST /api/extension/session`
Accepts the Twitch Extension authentication credential, verifies it, and
returns a short-lived Twungeon session plus personalized state. It does not
create a character.
### 14.3 Commands
`POST /api/commands`
Accepts an authenticated player command envelope and returns:
```json
{
"requestId": "client-generated-id",
"accepted": true,
"eventSequence": 42,
"reason": null
}
```
Rejected responses use a stable reason such as `UNAUTHENTICATED`,
`IDENTITY_NOT_BOUND`, `CHARACTER_DEAD`, `WRONG_PHASE`, `STALE_PHASE`,
`NO_AP`, `INVALID_TARGET`, `BLOCKED_TILE`, or `DUPLICATE`.
### 14.4 State stream
`GET /ws` upgrades to an authenticated WebSocket when personalized state is
needed or a read-only broadcast connection otherwise. Messages include:
- complete snapshot on connection;
- ordered state/event updates;
- current server time and phase deadline;
- action-log entries; and
- explicit resynchronization after sequence gaps.
Clients must replace local state with a complete snapshot after reconnect or a
sequence gap. UI animation state is local; game state is not.
### 14.5 Internal Twitch messages
Twitch events normalize into idempotent internal messages:
```ts
type TwitchMessage =
| {
type: 'spawn-requested'
externalEventId: string
twitchUserId: string
displayName: string
broadcasterId: string
}
| {
type: 'bits-resurrection-received'
externalEventId: string
twitchUserId: string
amount: number
}
```
## 15. Presentation Requirements
### 15.1 Three-section layout
The default wide layout reserves:
- a compact upper-left control/status area;
- the largest upper-right area for the map; and
- a full-width bottom area for the action log.
Temporary shapes, colors, labels, and sprites are acceptable. Every required
entity, wall, traversable path, and exit must remain distinguishable.
### 15.2 Player status
The personalized Extension status includes at least identity/binding status,
HP, AP, Guard, heal availability, alive/dead status, current phase, and a clear
disabled reason when controls are unavailable.
### 15.3 Action log
The view displays canonical entries by sequence, oldest to newest, with the
latest entries visible. It must cover every event category required by PRD
section 4.2. Reconnection restores recent history from the backend rather than
starting an empty client-only log.
### 15.4 Dormant banner
When phase is dormant, both applicable views prominently display exactly:
`Type !spawn to spawn in the Twungeon!`
## 16. Failure and Recovery Behavior
### 16.1 Twitch unavailable
- Existing authenticated gameplay may continue if the backend remains healthy.
- New spawn, new identity binding, and Bits events fail closed when they cannot
be verified.
- The operator view and logs identify the unavailable integration.
- Reconnect uses bounded exponential backoff and does not duplicate events.
### 16.2 Extension disconnected
The character remains participating. The current phase continues, unused AP
becomes Guard, and reconnection restores authoritative state.
### 16.3 View disconnected
Game simulation continues. The view requests a full snapshot on return and does
not replay commands.
### 16.4 Invalid floor
An invalid generated floor is never activated. Generation retries a bounded
number of times. If all retries fail, phase progression stops with an operator-
visible error rather than corrupting the active run.
### 16.5 Unexpected domain exception
The command is not partially applied. Command handling must compute and commit
one atomic state transition. The error is correlated with request, run, floor,
phase, and event sequence identifiers without logging secrets.
## 17. Observability
Structured operational logs must distinguish:
- Twitch connection and subscription state;
- authentication and identity-binding results;
- follower checks;
- accepted and rejected spawn attempts;
- accepted and rejected controller commands;
- phase transitions and timer expiry;
- Bits-event deduplication and resurrection results;
- floor-generation seed and validation result;
- reconnect and snapshot resynchronization; and
- run reset or fatal errors.
Logs must use stable user IDs only where operationally required and must avoid
credentials and full authentication payloads.
## 18. Proposed Source Layout
```text
/
|-- apps/
| |-- backend/ # Process composition, APIs, WebSocket, scheduler
| |-- extension/ # Authenticated Twitch controller UI
| `-- stream-view/ # Read-only broadcast layout
|-- packages/
| |-- domain/ # Framework-independent game state and rules
| |-- dungeon-generator/ # Seeded two-room generation and validation
| |-- twitch-adapter/ # Twurple chat, follower, and Bits integration
| |-- rpgjs-adapter/ # Mapping between domain state and RPGJS
| |-- contracts/ # Shared validated API/event schemas
| `-- ui/ # Shared layout, status, controls, and log components
|-- tests/
| |-- domain/ # Deterministic rule and state-machine tests
| |-- integration/ # Backend, adapters, APIs, reconnect, deduplication
| `-- e2e/ # Multi-viewer and complete-loop scenarios
|-- docs/
| |-- twitch-setup.md # Clean-checkout Twitch setup and troubleshooting
| `-- testing.md # Local and live-channel verification procedure
|-- .env.example # Names and descriptions, never secret values
|-- Twungeon_MVP_PRD_Current.md
`-- Twungeon_MVP_Technical_Specification.md
```
The exact monorepo tooling may be selected during project scaffolding. Component
boundaries and dependency direction are mandatory even if folders are renamed.
## 19. Dependency Direction
Allowed dependency direction:
```text
apps -> adapters/contracts -> domain
UI apps -> contracts/UI components
domain -> no framework or external-service package
```
The domain package exposes commands, state, and events. Adapters translate
between those contracts and RPGJS, Twurple, HTTP, WebSocket, or browser APIs.
## 20. Verification Strategy
### 20.1 Domain unit tests
Deterministic tests must cover:
- all accepted and rejected command conditions;
- AP spending and early phase completion;
- timer duration and 120-second cap;
- deadline boundary behavior;
- AutoGuard conversion, hit blocking, misses, and reset;
- player and Goblin hit probabilities through injected rolls;
- heal availability and floor refresh;
- death, spawn lockout, resurrection, advancement, and wipe reset;
- Goblin guarding, aggro on hit or miss, pursuit, return, and death;
- exit entry with a living Goblin;
- dormant versus total-party-wipe conditions;
- serial ordering and stale/duplicate command handling; and
- deterministic floor generation and connectivity validation.
### 20.2 Integration tests
Integration tests use test doubles before live Twitch testing and cover:
- chat event to follower check to spawn;
- non-follower rejection;
- chat-to-Extension identity match and mismatch;
- two authenticated viewers controlling separate characters;
- refresh/reconnect without duplication;
- Twitch event and command deduplication;
- Bits event mapped to the correct dead character;
- backend snapshots, ordered events, and sequence-gap recovery; and
- RPGJS rendering updates without domain authority leakage.
### 20.3 End-to-end scenarios
At minimum, the live test plan must demonstrate:
1. clean setup and startup from repository documentation;
2. dormant banner before any player joins;
3. follower spawn and non-follower rejection;
4. matching Extension control and mismatched identity rejection;
5. two or more simultaneous viewer characters;
6. movement, two attacks, heal, pass, timer expiry, and AutoGuard;
7. Goblin aggro, pursuit, attack, target death, and return;
8. Bits resurrection of the correct dead viewer;
9. floor escape with the Goblin alive and dead-player revival;
10. total-party wipe and Floor 1 reset;
11. refresh and temporary disconnect recovery; and
12. repetition across multiple generated floors.
### 20.4 PRD success-criteria traceability
| PRD criteria | Primary verification |
|---|---|
| 1-6 | Clean-checkout, configuration, Twitch event, Extension, and layout E2E tests |
| 7-13 | Dormancy, spawn eligibility, duplicate prevention, and identity integration tests |
| 14-19 | Controller, phase, timer, AP, combat, heal, death, Guard, and log tests |
| 20-22 | Goblin AI, generator validation, and live-Goblin escape tests |
| 23-29 | Floor revival, death lockout, Bits, wipe, zero-player, late spawn, and reconnect tests |
| 30-33 | Multi-floor soak test, first-time-viewer test, setup troubleshooting review, and viewer-feedback session |
The test implementation should reference individual PRD criterion numbers in
test names or metadata so failures remain traceable to product acceptance.
## 21. Implementation Sequence
Implementation should proceed through vertical, independently verifiable
slices:
1. Scaffold the repository, shared contracts, configuration validation, and
framework-independent domain package.
2. Implement the deterministic floor generator and static stream rendering.
3. Implement player/Goblin state, commands, turn phases, timer, and action log
using local test drivers.
4. Implement death, resurrection messages, floor advancement, wipe reset,
dormancy, reconnect, and idempotency.
5. Add the RPGJS adapter without moving rules out of the domain package.
6. Add Twurple chat and follower verification for `!spawn`.
7. Add Extension authentication, identity binding, controller commands, and
personalized state.
8. Add the Twitch Bits adapter and configurable resurrection rule.
9. Complete clean-checkout Twitch setup and troubleshooting documentation.
10. Run automated, live-channel, multi-viewer, and repeated-floor acceptance
tests against all 33 PRD criteria.
## 22. Decisions Deferred to Integration Configuration
The following are intentionally configurable or version-dependent and do not
change the domain design:
- exact Bits resurrection price;
- concrete Twitch developer application and Extension identifiers;
- exact Twitch scopes, EventSub event names, and Twurple configuration required
by the versions selected at implementation time;
- callback, WebSocket, public endpoint, and HTTPS values for each environment;
- UI art, typography, and final dimensions; and
- retention limits for operational logs and the on-screen action-log window.
These values must be resolved and documented before live acceptance testing.
## 23. Definition of Ready for Implementation
Implementation may begin when:
- the PRD and this specification are accepted as the product and technical
baselines;
- the eight resolved behavior choices below are acknowledged;
- the initial repository/tooling scaffold is selected;
- a Twitch development channel and required developer access are available; and
- secrets can be supplied outside source control.
Resolved behavior choices:
1. Players may share tiles; the Goblin and players may not share a tile.
2. Movement and attacks are orthogonal, and attacks are adjacent-only.
3. The backend serializes commands by authoritative arrival order.
4. The arrival timestamp at the backend gateway decides timer-boundary commands.
5. Run state survives client reconnects but not backend process restarts.
6. Bits resurrection restores full HP at spawn and grants AP next Player Phase.
7. Broadcast and Extension modes share presentation, but only the authenticated
Extension is interactive.
8. The backend domain core is the sole authority for every state transition.
Changes to these decisions must update this specification and any affected
tests before or alongside implementation.