| name | five-phase-pass |
| description | Propagate a foundational Snapmatch app, remote-state, Firebase Hosting, or deployment-policy change through the current app, generator template, rendered-template validation, root platform configuration, owning skills/docs, and a final cross-surface release audit. |
Five-phase pass
Use this workflow when a shared app convention must remain aligned between a generated app and
turbo/generators/templates/app/. If no real app exists yet, skip phase 1 and treat the generator
template as the implementation source of truth.
Classify the change before editing. A remote-boundary change (Firebase SDK/configuration, Firestore
schemas or rules, synchronization, hosting, or deploy credentials) crosses app, template, root
platform, and documentation surfaces. It is not complete when only the working app passes.
Phase 1 — current app
Resolve the selected workspace with bun scripts/resolve-app-filter.ts. Implement and validate the
change in that apps/<name> workspace. Do not assume a canonical app directory.
Phase 2 — generator template
Apply the reusable form of the change under turbo/generators/templates/app/. Keep domain mechanics,
content, branding, and app-specific assets out of the template.
Update root-owned platform files in the same phase when the convention needs them. For Firebase work,
audit firebase.json, .firebaserc, firestore.rules, firestore.indexes.json, root scripts,
.gitignore, and CI/deploy workflows. Firebase Hosting is the canonical static target and must serve
the selected app's apps/<app>/dist/client artifact at BASE_PATH=/; GitHub Pages may remain an
optional secondary mirror. Keep the entire Pages build/deploy chain behind the exact
PAGES_ENABLED=true opt-in, and keep scripts/check-deployment-policy.sh wired into both CLI gates.
Phase 3 — rendered template
Run:
bash scripts/check-template.sh
The script renders an ignored apps/test-project through Turbo's real generator semantics, runs
Biome and TypeScript, then removes the generated directory. Never commit apps/test-project.
When invoking Turbo directly, the generator receives positional answers after --args:
bunx turbo gen app --args test-project
Do not use the stale -- --args name=test-project form; Turbo treats it as a different argument
shape. Prefer bash scripts/check-template.sh for the disposable validation render.
When the change affects dependencies, generate a temporary app, run bun install, and validate its
typecheck and relevant tests before deleting the temporary workspace and restoring bun.lock to the
real workspace set.
Phase 4 — owning documentation
Update the README, AGENTS.md, and the focused skill that owns the changed convention. Examples must
use apps/<name> or a clearly generic sample app and the @snapmatch/* scope.
For Hosting or deployment changes, update nitro, its ownership-matrix entry, and both .agents
and .claude mirrors. State explicitly that .github/workflows/deploy.yml is the optional Pages
mirror, not Firebase deployment automation, and preserve the exact-main release proof protocol.
For remote-state changes, audit at least the t3-env, idb, jotai, nitro, and
five-phase-pass skills and their mirrors. State what is implemented separately from what is live in
production. Snapmatch implements browser-session anonymous Auth, domain schemas/Rules, a namespaced
IDB replica/outbox, and exact receipt reconciliation. Approved production releases enable domain
sync through a release-only environment override and provision anonymous Auth, Rules, and indexes
through separate commands. Do not infer App Check or a trusted game arbiter from that live boundary.
The current remote-state convention uses IDB version 5: version 4 introduced compound
replica/tombstone keys and its validating v3 rescue migration, while version 5 adds durable
retirement intents. It also includes exact namespace/entity/path refinements, compare-and-delete
repair, namespace-bound runtime quarantine, a projection-free prospective Game egress lane, a
rolling 50-document command feed whose live QueryDocumentSnapshot cursor is not a durable
checkpoint, all-store historical namespace retirement, hydration-time expiry deletion without
RecoveryRecord publication, and fail-closed identity teardown. Update the owning docs and skills
when any of those invariants changes.
Phase 5 — audit and gate
Search for stale names, deleted workspace references, and drift between the real app and template.
For a remote boundary, also compare Firebase client initialization, public env schemas, Firestore
paths and rules, Hosting artifact paths, build-time env injection, emulator/deploy scripts, and
secret-ignore policy across every owning surface. For remote state, compare the IDB version and
migrations, compound physical keys, canonical namespace/path refinements, prospective/confirmed lane
handoff, rolling command-feed restart behavior, retention sweep, retirement enumeration, and
teardown ordering. Confirm that retirement attempts every owned namespace, its durable intent is
cleared only after full success and drained before replacement bootstrap, and hard teardown replaces
the resumable claim with a strict sanitized retirement-pending marker that cannot resume and
clears only after retirement. Also confirm that row repair preserves a newer cross-tab replacement,
publishes only rows actually quarantined, and runtime quarantine remains namespace-owned. Root
hydration must union IDB and sessionStorage retirement markers, recover invalid-row ownership from
physical keys/tab hashes, suppress all retiring identity evidence, and keep RecoveryRecords out of
the published snapshot. Render the template and verify that it builds without inheriting
app-specific Firebase identifiers.
For remote state, also run the demo-project Auth/Firestore Rules and integration suites before the
general gate. The retained baseline is 35 of 35 Rules cases and one live Web SDK test with 280
assertions; report the actual rerun instead of assuming those counts. A Hosting deployment is not
approval to deploy domain Rules/indexes or enable domain traffic. Then run the appropriate gate:
bash scripts/check-deployment-policy.sh
bun run check:fast
bun run check
For the current Snapmatch room contract, the cross-surface audit must also keep these invariants
aligned: eight total seats (two performers plus at most six audience), automatic cached
PLAYER.##### browser-session identity, an authoritative 60-second pendingEndsAt, non-null
pre-scheduled active-document purgeAt with an exact terminal-time reset, and invite hydration through
Firestore → Zod → IDB → Jotai. Components and routes may invoke the application command or
hydration facade, but must not import Firestore. Auth, Hosting, Firestore configuration, and the
domain-sync build override remain independently deployed and evidenced.
When the task includes landing or deployment, do not collapse evidence across systems. Require the
PR gate, record the freshly fetched origin/main SHA, run the Hosting-only
firebase:deploy:production command from a clean worktree at that exact SHA, and smoke the canonical
Firebase URL in a real browser. A successful or skipped
optional-github-pages run is never evidence that Firebase Hosting was released.
Report any intentionally app-only or template-only differences. Do not copy game rules or content
into the reusable template merely to make trees identical. App-specific public Firebase project
identifiers may live in the selected app; the template must default to Firebase disabled and document
the required VITE_FIREBASE_* values. Admin SDK credentials and service-account material never
belong in either tree.