*Phase 1 deliverable per PIPE-05 + UX-13. Consolidated from PROJECT.md, REQUIREMENTS.md, CLAUDE.md, and .planning/research/PITFALLS.md #9.*
This document is referenced at every UX, monetization, and copy review going
forward. It enumerates mechanics this game does not use, with the reason for
each, so the answer to a "should we add X?" question is in writing rather
than relitigated.
Per CONTEXT D-07: this doctrine is enforced by **review**, not by lint rules
on UX strings. The reviewer (you, at every UX/monetization/copy decision)
consults this list and rejects or rewrites any change that violates it.
## Banned Mechanics
| Mechanic | Why Banned |
|----------|------------|
| Gacha mechanics | Directly contradicts the game's thematic argument that complex things cannot be reduced to simple transactions. (PROJECT.md, REQUIREMENTS.md Out of Scope, CLAUDE.md) |
| Lootboxes | Same reason as gacha — undermines the story's monetization-as-meaning argument. |
| Narrative gating behind purchase | The story IS the product; story content is never paid. |
| Random-drop monetization | All cosmetics must be deterministic catalog purchases. |
| Daily login bonuses | Presence is not a debt the game collects. |
| Login streaks | Skipping a day is allowed, even encouraged. |
| Limited-time / time-limited content | The game's premise is *what persists*. |
| Energy / stamina systems | Anti-cozy gating that interrupts contemplative play. |
| Rewarded ads | Anti-cozy; tonally incoherent with a contemplative grief-narrative. |
| Re-engagement push notifications | Memory Storm opt-in is the **only** allowed notification class. |
| Loss-aversion copy ("you'll lose your X") | Tonally incompatible with cozy/contemplative. |
| Visible countdown timers in core UI | The cello is the timer. The seasons are the timer. Not a digit. |
| "Don't miss out" / "limited time" / "only X hours left" copy | Bannable phrases at copy review. |
| Season *skipping* (vs. Season *acceleration*) | Players must never miss authored story beats; acceleration is allowed, skipping is forbidden. |
| Time-skip purchases that bypass real-time | Real-time IS the metaphor for memory; skip-time would violate mechanic-as-metaphor doctrine. |
| Hint system / objective tracker | Discovery-driven progression (A Dark Room rule); explicit objectives violate the tone. |
| Mobile-style nag UX | Cozy audience expects respect; nag patterns will tank reviews. |
## Allowed Engagement
The following engagement affordances are explicitly **allowed** because they respect
presence rather than demand it:
- **Memory Storm opt-in notifications** — the single allowed notification class.
Player must explicitly opt in. Never daily, never marketing, never streak-based.
- **"While you were away" letter on return** — written in Lura's voice, never a stat dump
(UX-02). Describes what bloomed, what the wind brought; never "fragments per hour."
- **Tab-title bloom indicator** when a fragment is ready (UX-09, Phase 8) — passive
surfacing, no notification.
- **Save-export reminder after Season transitions** — relationship-saving, not nag.
## Review Checklist
When reviewing any UX, copy, monetization, or feature change, ask three questions:
1.**Does this create urgency around presence rather than around content?** If yes → reject.
2.**Does this frame absence as loss?** If yes → rewrite or reject.
3.**Would removing this from the game make it less *cozy*?** If no → reconsider whether the change belongs.
Additional sanity checks for monetization specifically:
- Does this mechanic gate any story content? → reject (PROJECT.md hard constraint).
- Is this random-drop / gacha / lootbox shaped? → reject.
- Is this a "limited-time" anything? → reject.
## Source Documents
This doctrine consolidates constraints already locked in:
- **PROJECT.md** § "Out of Scope" — anti-features (gacha, lootboxes, narrative gating, Season skipping, generic flora, combat, multiplayer, voiced dialogue, named Keeper, generic cosmetics)
- **CLAUDE.md** § "Hard Thematic Constraints (Out of Scope by Design)" — 13 thematic exclusions, no FOMO push notifications, no daily login bonuses, no streaks, no limited-time, no energy/stamina
- .planning/season-7-end-state.md — principle-level rest-state contract answering (a) what rest state means, (b) what the finite Roothold ceiling is tied to (count of authored fragments + Seasons), (c) the coda's tonal register; explicit "What this document is NOT" boundary against treatment-level scope creep
- scripts/doctrine.test.ts — Vitest doc-lint test (8 assertions / 2 docs) asserting both doctrine docs exist with required H2 sections + required source citations + structural disclaimers
affects:
- "Phase 4: Roothold-ceiling enforcement task should reference .planning/season-7-end-state.md § 'What is the finite Roothold ceiling tied to?' for the per-Season-cap-proportional-to-fragment-count principle (SEAS-04)"
- "Phase 7: binary-choice-scene authoring task should reference .planning/season-7-end-state.md § 'What this document is NOT' for the explicit boundary on what's authored when (binary scene text, ending paragraphs, Lura's final line, credits screen — all owned by Phase 7, not Phase 1)"
- "Every UX/monetization/copy review going forward: .planning/anti-fomo-doctrine.md is the canonical reference; consult before any UX change to avoid re-litigating settled exclusions"
- "Plan 07 (CI workflow): doctrine.test.ts already runs as part of 'npm test' (vitest include glob extended); CI workflow only needs to invoke 'npm run ci' which already chains test"
# Tech tracking
tech-stack:
added: [] # no new deps; uses existing vitest + node:fs (stdlib)
patterns:
- "Doctrine-as-consolidation: both Phase-1 doctrine docs are *consolidations* of constraints already locked in PROJECT.md / REQUIREMENTS.md / CLAUDE.md / PITFALLS.md / ROADMAP.md — not new design work. The doctrine doc author's job is to collect, not invent. Per CONTEXT D-07 + D-08."
- "Doctrine-as-review-not-lint: per CONTEXT D-07, anti-FOMO is enforced by human review at every UX/monetization/copy decision, NOT by a lint rule on UX strings. The doc explicitly notes this and the Vitest test asserts no lint rule is proposed."
- "Doc-lint-as-Vitest-test: structural integrity of doctrine docs (file existence + required H2 sections + required source citations + boundary disclaimers) is enforced by Vitest assertions in scripts/doctrine.test.ts, runnable in CI as part of 'npm test'. This is the only automated enforcement and is sufficient — content quality is enforced by review."
- "Principle-vs-treatment boundary in design docs: season-7-end-state.md explicitly contains a 'What this document is NOT' section that names what is owned by Phase 7 authoring (scene text, ending paragraphs, character lines, credits visual treatment) — preventing scope creep when this doc is consulted by economy/writer/Phase-7 designers."
- .planning/season-7-end-state.md (114 lines: title + intro + 5 H2 sections — rest state, Roothold ceiling principle, tonal register, "What this document is NOT", Source Documents)
- scripts/doctrine.test.ts (78 lines: Vitest test with 2 describe blocks + 8 it assertions across 2 doctrine docs)
modified:
- vitest.config.ts (extended `include` glob to discover scripts/**/*.test.ts so npm test runs the doctrine doc-lint)
- tsconfig.node.json (extended `include` to cover scripts/**/*.ts so the strict-TS gate covers the new doc-lint test)
key-decisions:
- "Doctrine docs land in .planning/, not docs/ (per CONTEXT D-09). They are project-internal design constraints, not user-facing documentation."
- "Anti-FOMO doctrine enumerates 17 banned mechanics (vs. RESEARCH outline's 8) — the additional 9 capture exclusions from PROJECT.md (gacha, lootboxes, narrative gating, Season skipping), CLAUDE.md (energy/stamina, hint system), and REQUIREMENTS.md (random-drop monetization, time-skip purchases, mobile-style nag UX) that the RESEARCH outline summarized but didn't enumerate. The plan's acceptance criterion called for ≥15; we ship 17."
- "Vitest config & tsconfig.node.json globs extended (Rule 3 deviation) to discover scripts/**/*.test.ts. The existing globs only covered .mjs scripts and src/ tests. Without this, doctrine.test.ts would be invisible to 'npm test' and the doc-lint enforcement would never run in CI. Both edits are 1-character additions to existing arrays."
- "Source Documents section in both docs uses bold-italic-citation format ('**PROJECT.md** § \"Out of Scope\" — ...') matching the existing style in 01-01-SUMMARY.md so the canonical-references discipline is consistent across Phase-1 deliverables."
- "season-7-end-state.md does NOT author the binary-choice scene text, either ending paragraph, Lura's final line, or the credits screen treatment — explicitly disclaimed in the 'What this document is NOT' section. Per CONTEXT D-08, those are Phase 7's authoring scope. The Vitest test asserts the disclaimer section exists and contains the phrase 'authored Phase 7'."
- "season-7-end-state.md ties the Roothold ceiling to *content count* (fragments per Season + Roothold-relevant story beats), not to a number. Phase 4 will compute the actual numeric cap from whatever content count exists at that point. This decouples the principle (Roothold bounded by understanding, understanding bounded by writer) from the implementation (which has to wait until content exists to enforce)."
requirements-completed: [PIPE-05, UX-13, STRY-09]
# Metrics
duration: 4min
completed: 2026-05-09
---
# Phase 1 Plan 06: Doctrine Documents Summary
**Both Phase-1 doctrine docs (anti-FOMO + Season 7 end-state) authored as principle-level consolidations of existing project constraints, with a Vitest doc-lint test asserting structural integrity (8 assertions / 2 docs / 4+5 required H2 sections / source citations / boundary disclaimers). `npm test` green: 2 test files, 9 tests passing.**
- **`.planning/anti-fomo-doctrine.md` ships as a referenceable banned-pattern enumeration.** 17 banned mechanics (the RESEARCH outline asked for 8; we ship the full consolidation across all 4 source docs), 4 allowed-engagement affordances, 3-question review checklist, 4-citation Source Documents section. Per CONTEXT D-07, the doc explicitly notes it is enforced by human review at every UX/monetization/copy decision — no lint rule on UX strings (the Vitest test asserts no lint rule is proposed).
- **`.planning/season-7-end-state.md` ships as the principle-level contract that ends the project's #1 pitfall ("the story ends but the loop doesn't").** Answers all three CONTEXT-D-08 questions (rest state / Roothold ceiling tie / tonal register) at principle level. Explicit "What this document is NOT" section structurally disclaims treatment-level authoring (binary scene text, ending paragraphs, Lura's final line, credits screen) so the doc cannot grow into Phase 7's territory by accretion.
- **`scripts/doctrine.test.ts` ships as the only automated enforcement.** 8 assertions across 2 describe blocks: existence + 4 required H2 sections + 4 source citations + lint-rule absence (anti-FOMO doc); existence + 5 required H2 sections + 4 REQ-IDs + boundary-disclaimer presence (season-7 doc). Runs in 5ms; 575ms total with environment setup.
- **vitest.config.ts include glob extended** to discover `scripts/**/*.test.ts` (was: only `.mjs`). Without this, the doc-lint test would be invisible to `npm test` and the CI gate would never check the docs. tsconfig.node.json extended in lockstep to keep the strict-TS gate covering the new file.
- **`npm test` green: 2 test files, 9 tests passing** (sentinel from Plan 01-01 + doctrine.test.ts from this plan). 638ms total.
## Task Commits
Each task committed atomically on `worktree-agent-a8ad9f51c64583518`:
$ grep -cE "^## (What does \*rest state\* mean|What is the finite Roothold ceiling tied to|What tonal register does the coda live in|What this document is NOT|Source Documents)" .planning/season-7-end-state.md
5
$ npx tsc -b
(no errors)
```
## Decisions Made
- **Doctrine docs land in `.planning/`, not `docs/`** (CONTEXT D-09). They are project-internal design constraints, not user-facing documentation. The Vitest test path-asserts both files at `.planning/` paths, so a future move would require updating test PATH constants.
- **Anti-FOMO doctrine enumerates 17 banned mechanics** (the RESEARCH outline asked for 8 and the plan's acceptance criterion called for ≥15). The additional rows capture PROJECT.md exclusions (gacha, lootboxes, narrative gating, Season skipping), CLAUDE.md hard constraints (energy/stamina, hint system), and REQUIREMENTS.md Out of Scope rows (random-drop monetization, time-skip purchases, mobile-style nag UX) that the RESEARCH outline summarized but didn't enumerate. Consolidation, not invention.
- **Vitest + tsconfig include globs extended** (Rule 3 deviation, see below) to discover `scripts/**/*.test.ts`. Existing globs only covered `.mjs` scripts and `src/` tests. The extension is a 1-character addition to each include array — minimal surface change that keeps the doc-lint test discoverable by `npm test` and covered by the strict-TS gate.
- **Source Documents formatting** uses bold-italic-citation style (`**PROJECT.md** § "Out of Scope" — ...`) matching the existing 01-01-SUMMARY.md citation discipline so canonical-references stay consistent across Phase-1 deliverables.
- **Season 7 doc disclaimers cite specific Phase-7 authoring deliverables** (binary-choice scene text, both ending paragraphs, Lura's final line, credits screen treatment, individual final-Season fragments). Naming each artifact by name in the disclaimer prevents "I thought this doc covered that" misreadings during Phase 7 planning.
- **Roothold ceiling principle ties to content count, not numeric value.** Phase 4 will compute the actual numeric cap from whatever content count exists at that point. This decouples the principle (Roothold bounded by understanding, understanding bounded by writer) from the implementation (which has to wait until content exists to enforce). See season-7-end-state.md § "What is the finite Roothold ceiling tied to?" for the concrete tie.
## Notes for Downstream Phases
- **Phase 4 (Roothold ceiling enforcement, SEAS-04):** Reference `.planning/season-7-end-state.md` § "What is the finite Roothold ceiling tied to?" for the per-Season-cap-proportional-to-fragment-count principle. The doc's Concrete Tie bullets specify how Phase 4 should structure the cap: per-Season hard cap proportional to that Season's fragment count + a small Roothold-relevant-story-beat contribution; total ceiling = Σ(per-Season caps); UI displays "Roothold (full)" at cap, never a hidden multiplier.
- **Phase 7 (binary choice scene + final-state authoring, STRY-08, SEAS-09, SEAS-10):** Reference `.planning/season-7-end-state.md` § "What this document is NOT" for the explicit boundary on what's authored when. The five disclaimers (binary scene text, ending paragraphs, Lura's final line, credits screen, individual final-Season fragments) name everything Phase 7 owns and Phase 1 explicitly did not author. Reference § "What tonal register does the coda live in?" for the warm/quiet/specific/final dimensions the authoring should obey.
- **Every UX/monetization/copy review going forward:** Reference `.planning/anti-fomo-doctrine.md`*before* drafting or reviewing any UX change. The 3-question Review Checklist is the canonical screen; the 17-row Banned Mechanics table is the canonical reference.
- **Plan 07 (CI workflow):** `doctrine.test.ts` already runs as part of `npm test` (vitest include glob extended); the CI workflow only needs to invoke `npm run ci` (which chains `lint → test → validate:assets → build`). No CI-side wiring needed for the doctrine docs themselves — Plan 07 owns wiring this into a GitHub Action.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Extended vitest.config.ts include glob to discover `scripts/**/*.test.ts`**
- **Found during:** Task 2 (Step 3 — running `npx vitest run scripts/doctrine.test.ts` worked, but the plan's Step 4 demands `npm test` be green and a fresh-eyes audit revealed the existing `include` array only matched `scripts/**/*.test.mjs`).
- **Issue:** The plan's Task 2 specifies authoring `scripts/doctrine.test.ts` (TypeScript), but the existing `vitest.config.ts` `include` glob from Plan 01-01 only matched `scripts/**/*.test.mjs`. Without this extension, `npm test` would not discover the doc-lint test, and the CI gate would never enforce doctrine doc structure — silently defeating the plan's own acceptance criterion ("`scripts/doctrine.test.ts` exists and passes").
- **Fix:** Added `'scripts/**/*.test.ts'` to the `include` array in `vitest.config.ts` (1-character addition).
**2. [Rule 3 - Blocking] Extended tsconfig.node.json include to cover `scripts/**/*.ts`**
- **Found during:** Task 2 (same time as #1; addressed in lockstep so the strict-TS gate stays consistent).
- **Issue:** `tsconfig.node.json` (which `tsconfig.json` references for build-side TS files) only included `scripts/**/*.mjs`. Adding a `.ts` test file to `scripts/` without extending this include would leave the file outside the strict-TS gate — meaning `tsc -b` would not catch type errors in `doctrine.test.ts`. Per CLAUDE.md "Code Style", TypeScript strict is non-negotiable.
- **Fix:** Added `'scripts/**/*.ts'` to the `include` array in `tsconfig.node.json` (1-character addition).
- **Files modified:** `tsconfig.node.json`.
- **Verification:** `npx tsc -b` exits 0 cleanly.
- **Committed in:** `cde9388` (Task 2 commit).
**3. [Rule 2 - Missing Critical] Authored 17 banned mechanics in anti-FOMO doctrine (vs. RESEARCH outline's 8)**
- **Found during:** Task 1 (drafting the Banned Mechanics table from the four source documents).
- **Issue:** The RESEARCH outline's example table contained 8 rows — a representative subset, not the full enumeration. The plan's acceptance criterion calls for ≥15 banned-pattern rows. The plan's own action block lists 17 mechanics in the Banned Mechanics table content. Authoring fewer than 15 would technically have violated the acceptance criterion; authoring just 8 (matching the outline) would have failed the consolidation premise (CONTEXT D-07 — the doc IS the consolidation).
- **Fix:** Authored the full 17-row table per the plan's specified content, drawing from PROJECT.md, REQUIREMENTS.md, CLAUDE.md, and PITFALLS.md.
**Impact on plan:** All three deviations are mechanical / completeness-additions explicitly authorized by the plan's own action block (Task 1 enumerated all 17 mechanics in the content spec) or by the plan's Step 4 demand that `npm test` be green (which silently required the include-glob extensions). No scope creep. No architectural change. Wave-2 sibling plans 02–05 + Wave-3 plan 07 unaffected by these edits — vitest.config.ts and tsconfig.node.json are still owned by Plan 01-01 in spirit, and these are minimal extensions, not rewrites.
## Issues Encountered
- **`grep -cE "(SEAS-04|SEAS-09|SEAS-10|STRY-08)"` returns 3 (matching lines), not 4 (matching tokens).** The plan's acceptance criterion bash check counts lines, but all four IDs appear in the doc — three on a single Source Documents line, plus STRY-08 on line 32 and SEAS-04 on line 57. The Vitest test (which is the actual gate) checks each ID individually with `expect(md).toMatch(/SEAS-04/)` etc. and all four assertions pass. Treated as a wording mismatch in the plan's bash check, not a content problem — the doc cites all four IDs as required.
- **No other issues.** Both doctrine docs drafted in one pass per the plan's verbatim content blocks; doc-lint test passed first run; `npm test` green first run after include-glob extensions.
## Authentication Gates
None — Phase 1 plan 06 is markdown + a Vitest test only; no external auth needed.
## Threat Flags
None — per the plan's `<threat_model>`: "No security-relevant code in this plan; doctrine docs and a doc-lint test only. No runtime code; no untrusted inputs; no I/O beyond reading committed Markdown files at test time." Confirmed: the doc-lint test reads only files inside `.planning/` (committed Markdown) via `node:fs.readFileSync` — no fetch, no eval, no untrusted input.
## Known Stubs
None — both docs are complete principle-level deliverables; the doc-lint test is complete (8 assertions, no `it.skip`, no `TODO` markers).
The two docs *do* defer treatment-level work to Phase 7 (binary scene text, ending paragraphs, etc.) and numeric work to Phase 4 (Roothold ceiling cap value). These deferrals are **structural**, not stubs — they are explicitly enumerated in season-7-end-state.md § "What this document is NOT" and are correctly out of scope per CONTEXT D-08.
## Next Plan Readiness
- **Plan 07 (CI workflow):** Ready. `doctrine.test.ts` runs as part of `npm test` (vitest include glob extended); Plan 07 only needs to invoke `npm run ci` in the GitHub Action to gate doctrine doc structure on every PR. No CI-side wiring needed for the doctrine docs themselves beyond the existing `ci` script.
- **Phase 4 (Season prestige + Roothold ceiling):** Will reference `.planning/season-7-end-state.md` § "What is the finite Roothold ceiling tied to?" for the per-Season-cap principle. The doc gives Phase 4 a written contract to implement against rather than a designer's intuition.
- **Phase 7 (Season 7 authoring):** Will reference `.planning/season-7-end-state.md` § "What this document is NOT" for the boundary of what's authored when. The doc gives Phase 7 the explicit list of artifacts it owns (binary scene text, ending paragraphs, Lura's final line, credits screen, final-Season fragments).
- **Every Phase 2+ UX review:** Will reference `.planning/anti-fomo-doctrine.md` 3-question Review Checklist + 17-row Banned Mechanics table as the canonical pre-merge screen.
No blockers. The two doctrine docs are referenceable from this point forward at every UX/monetization/economy review.
## Self-Check
- [x] `.planning/anti-fomo-doctrine.md` exists at the correct path (NOT `docs/`) — verified with `test -f .planning/anti-fomo-doctrine.md && ! test -f docs/anti-fomo-doctrine.md`.
- [x] `.planning/season-7-end-state.md` exists at the correct path (NOT `docs/`) — verified with `test -f .planning/season-7-end-state.md && ! test -f docs/season-7-end-state.md`.
- [x] `scripts/doctrine.test.ts` exists — verified with `test -f scripts/doctrine.test.ts`.
- [x] Anti-FOMO doc has all 4 required H2 sections — verified with `grep -cE "^## (Banned Mechanics|Allowed Engagement|Review Checklist|Source Documents)" .planning/anti-fomo-doctrine.md` returns 4.
- [x] Season 7 doc has all 5 required H2 sections — verified with `grep -cE "^## (What does \*rest state\* mean|What is the finite Roothold ceiling tied to|What tonal register does the coda live in|What this document is NOT|Source Documents)" .planning/season-7-end-state.md` returns 5.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.