Twistris uses Vitest for typed puzzle rules, session economy, first-Bit crafting, Gravity Module repair, first-sector state, local profiles, profile storage, and application-state tests; TypeScript for compile-time checks; Vite for development and production builds; and the retained browser smoke harness for legacy controller characterization.
Do not remove the smoke harness merely because the new runner exists. Its controller coverage remains required until equivalent automated coverage and runtime parity are approved.
Run npm run dev, then open /tests/smoke.html from the URL printed by Vite.
The harness loads the production runtime into a hidden test DOM and currently runs 63 checks covering:
- test API availability
- DOM-free rules API availability and independent board creation
- quarter-turn rounding
- center-distance calculation
- symmetric and off-center balance profiles plus direction decisions
- seeded center creation
- direct attached and detached placement detection
- deterministic piece selection
- successful lock and detached-piece retry behavior
- bottom-edge exit retry before side-adjacency evaluation
- direct non-mutating rotation plus controller clockwise and counterclockwise rotation
- direct centered-square detection plus controller
3x3and5x5growth - charge awards across multiple growth layers
- harvest phase startup
- direct harvest classification plus exact Dud, charge, and outer-block totals
- immutable harvest results and exact atomic banking
- duplicate-result protection
- completed and skipped harvest-presentation banking
- presentation counter animation without inventory mutation
- final harvest run-value reset
- restart preservation of session bank values
- session, run, lifecycle, and presentation state ownership
- title, launch, play, pause, harvest, and return phase behavior
- repeated-action and invalid-phase guards
The harness is a characterization safety net, not complete unit coverage.
Run npm test. The focused typed suite verifies:
- page-session inventory starts empty and immutable
- a harvest result is applied atomically with frozen before/after values
- duplicate harvest IDs do not change inventory
- title, launch, puzzle, pause, harvest, and return transitions reject invalid order
- typed mode, restart, and harvest events are emitted from the application boundary
- banked inventory survives puzzle-run restart and is committed before harvest presentation
- version-one profiles create, normalize, migrate, recover, save, and reset through fake storage
- loaded inventory seeds the application, applied harvests update profile statistics, and save failure does not stop gameplay
- the first Bit recipe spends exactly eight Duds and one Pulse charge, rejects shortages and duplicates, and preserves immutable input
- Gravity Module repair consumes exactly one ordinary Bit, rejects missing or duplicate installation, and sets both repair flags
- the application persists crafting and repair before presentation and rejects progression commands in invalid modes
- the first sector remains locked before Gravity repair, derives as compromised afterward, and reconstructs secured state without active Bugs
- Board mode opens only after Gravity repair and returns through a validated application transition
- puzzle behavior remains covered independently by
PuzzleRunand the pure rules module
Open index.html and verify:
- title and Pulse presentation load correctly
- Start begins a run and keyboard focus works
- active-piece movement, rotation, soft drop, and hard drop work
- detached pieces retry correctly
- every tetromino that reaches the bottom edge passes through and retries even when its final cells sit beside settled structure
- side adjacency never locks a piece while its downward path remains clear
- a floor-bound miss has no ghost suggesting that it can lock to the bottom edge
- attached pieces update the central structure
- left-heavy and right-heavy structures twist in the expected direction
- Pulse charges update when the centered square grows
- next-piece preview remains centered
- pause and restart work
- reaching capacity starts harvest automatically
- Dud and charge counters animate and the game returns to the Pulse screen
- desktop, mobile-width, and embedded sizing remain readable
Add focused coverage as the corresponding state boundaries are introduced:
- pure crafting, mission, and first-sector transactions
- tutorial event ordering without waiting for real animation durations
- platform-adapter contracts using browser-safe fakes
- Vitest, active: pure puzzle rules now; economy, profile, mission, demo-board, migration, and transaction rules as those modules arrive.
- TypeScript, active: compile-time checks for migrated source, tests, and future typed contracts.
- Vite, active: development server and production asset/module validation.
- Playwright, planned: title-to-run flow, keyboard and touch input, scene transitions, responsive layouts, tutorial checkpoints, and selected visual comparisons.
The browser smoke harness may be retired only after its characterization cases have equivalent coverage and the Phaser runtime has demonstrated parity.
Run npm run dev, then open /proofs/phaser.html.
Verify:
- the falling T piece loops without resizing the layout
- arrow keys or
A/Dmove the piece - Up or
Wrotates the piece - Space or the action control starts the walking-Bit sequence
- the Bit emerges from the Pulse, walks with two visible feet, and hops into the connected socket
- every copper trace physically connects to the Pulse
- landing and installation trigger brief camera responses
- the scene reports stable frame pacing without browser warnings
- the square scene remains correctly framed at desktop and
390x844mobile viewport sizes
The proof is not the playable game and must remain isolated until its presentation patterns are ported through slice 7.
Run npm run dev, then compare /next/ with /.
For a repeatable, non-persistent Slice 10 QA profile containing exactly eight Duds and one Pulse charge, open /next/?testProfile=slice10 in development. This fixture never reads from or writes to the player's saved profile.
For a repeatable, non-persistent Slice 11 QA profile with the Gravity Module already repaired, open /next/?testProfile=slice11. It exposes the compromised first sector without changing the player's saved profile.
Current World-scene title parity checks:
- the block-built TWISTRIS logo retains its cyan and magenta treatment
- the close Pulse spins over the faint grid with heartbeat particles
- active, settled, preview, and harvested colored Bits retain diagonal depth, inner glow, and animated edge shimmer
- Start begins one transition and ignores repeated activation
- the transition takes approximately 2.05 seconds and settles on a quarter turn
- the logo and Start control fade while the board pulls back
- the normal board framing and Pulse charge HUD appear at completion
- keyboard focus moves to the game surface
- desktop and
390x844mobile layouts remain square, centered, and free of overlap - the scene holds stable frame pacing without browser warnings
- diagnostics report one active World scene, the expected camera mode, and only the Pulse sector mounted
- diagnostics report the application-owned lifecycle mode rather than scene-owned lifecycle state
Current /next/ puzzle checks:
- Enter starts the same guarded launch as the Start button
- Start finishes in the
puzzlecamera mode without replacing the World scene - the Pulse seed, falling piece, ghost, and next-piece preview share the mounted Pulse sector
- Arrow keys or WASD move, rotate, and soft-drop the active piece
- Space hard-drops and locks an attached piece
- a detached piece retries with the same shape and does not alter settled cells
- normal and soft gravity retain the characterized legacy timing
- next-piece state advances only after a successful lock
- puzzle input does not scroll the browser page
- an off-balance lock stages the same direction chosen by the pure balance rule
- the settled mass and Pulse rotate while the active piece, ghost, grid, and preview stay upright
- puzzle input and gravity pause throughout the 340 ms rotation
- the rotated board commits only when the visual quarter-turn completes
- completing a centered
3x3turns its enclosed Bits neutral and reveals the Pulse field - one run Charge is awarded for each newly completed centered layer
- a multi-layer completion updates the Charge total atomically before presentation
- core growth draws an inward white energy burst and briefly expands the Pulse
- Charge feedback can overlap a staged twist without changing the awarded result
- the bottom Charge HUD updates once and remains readable at desktop and mobile widths
- older outer Bits fade while the newest placement remains readable
- secured Bits retain their neutral treatment and light agitation
- the colored mass outline follows the exposed boundary of the settled structure
Ppauses gravity and input with visible feedback, then resumes the same runRstarts a fresh run while preserving page-session bank values- misses display feedback without changing the settled board
- a piece locking above the top boundary enters
harvestinginstead of leaving the run frozen with no active piece - capacity creates one immutable result and banks its Duds and Charges exactly once
CAPACITY REACHEDremains readable over the occupied top of the stack- outer blocks fall away before secured Duds stream to the right counter
- run Charges transfer to the left counter after the Dud transfer
- harvest completion restores the title and Start control with a fresh run ready
- a second run does not reapply the previous harvest result
- reloading
/next/restores banked Duds and Pulse charges from the local profile - starting or restarting a run keeps banked Pulse charges visible and adds newly earned run charges without changing the bank
- profile creation, migration, or recovery completes before Phaser starts
- blocked storage leaves the game playable and reports a non-persisted profile diagnostic
- a completed run presents its earned Duds and Pulse charges before returning to hub actions
- Craft exposes only Craft Bit, then updates Duds, Pulse charges, and Bits through the already-committed transaction
- the fabricated Bit stages beside the Pulse while the display-only Bit counter updates, and the only next progression action is the Gravity Module
- repair removes the Bit before presentation, visibly starts it at the Pulse rather than the HUD, walks it along the connected trace, stabilizes the compact module, and survives reload
- ordinary traveling and installed Bits render at roughly half their earlier Slice 10 scale
- the copper trace remains visibly connected and readable as the Bit's path without dominating the board
Current continuous-Board checks:
- selecting BOARD animates outward from the current Pulse-home framing
- desktop drag pans and the mouse wheel zooms without page scrolling
- one-finger drag pans and pinch gestures zoom on touch screens
- camera bounds prevent navigation beyond intentionally visible world space
- Return to Pulse restores the Pulse home framing
- camera movement does not alter puzzle, mission, economy, or persistence outcomes
- only the Pulse and
first-firewallsectors are mounted after the nearby sector is discovered - returning to the Pulse keeps
first-firewallmounted and visibly preserves its northbound trace while the camera eases inward - the broken firewall, active Bugs, northern fog boundary, and upward Pulse-connected trace remain readable with upright labels at desktop and
390x844 - the Board warning and Return control do not overlap at phone width
The route is not a replacement for / until all gameplay and presentation parity checks pass.
The release candidate must verify the complete public demo:
- First-run reveal completes without unintended input.
- The first Bit is crafted and walks into the Gravity Module upgrade slot.
- The gyro stabilizes before the firewall failure and Bug reveal.
- FEED THE PULSE starts the four-charge mission with full controls.
- Harvested resources are banked exactly once.
- The player can obtain the demo Charged Bit and secure the first firewall sector.
- The demo-complete result unlocks Endless Feed.
- Endless Feed remains replayable across save and reload.
- The selected demo upgrades unlock, equip, save, and affect only their documented rules.
- No inaccessible second sector or unfinished full-game surface appears in the release.
- Target smooth 60 FPS presentation on desktop and modern mobile hardware.
- Test at desktop, mobile-width, high-density, and reduced-effects settings.
- Include at least one modest Android-class device or profile and one older supported iPhone-class device before a mobile release.
- Check long tutorial sequences, harvest particles, walking Bits, Bug swarms, fog, blur, and Board camera movement.
- Pool repeated objects, cull off-camera and fog-hidden entities, and cap resolution and effect counts.
- Pause unnecessary simulation and animation when the page or packaged app enters the background.
- Performance reductions may change visual density but must never change economy, mission, repair, or demo-board outcomes.
- Logic changes: run the browser smoke harness.
- Pure-rule changes: also run
npm testandnpm run typecheck. - Visual or interaction changes: run the smoke harness and inspect the playable page.
- Balance changes: perform repeated left-heavy, right-heavy, early-run, and late-run manual scenarios.
- Persistence changes: test missing, valid, malformed, and older-version saves.
- Harvest changes: verify inventory awards remain correct if animation is skipped, interrupted, or reduced.
- Stack migration changes: run
npm run build,npm test,npm run typecheck, the 63-check browser harness, and a manual playable-page check until parity is approved.