Skip to content

Keimgarten (Game 002) — implementation PRD #13

Description

@DanielBonaker

Keimgarten — PRD (Game 002)

Field Value
Slug 002-keimgarten
Date 2026-05-05
Source spec docs/superpowers/specs/2026-05-05-keimgarten-design.md
Source bestiary docs/franchise/bestiary.jsx + docs/franchise/franchise-design.md
Layer-A source Established by docs/prds/2026-05-05-drop-deck.md (Game 001)
Status Ready for /to-issues

Problem Statement

I'm building 100g — a 100-day challenge to ship a different mini-game every day at https://danielbonaker.github.io/100g/. Game 001 (Drop Deck) is the vertical slice that proves the engine + cross-game services + shape bestiary contract end-to-end. Game 002 needs to be intentionally simpler than Drop Deck, charming and heart-warming in tone, and — crucially — the first game whose primary subject is the cross-100g creature franchise itself rather than a puzzle that happens to use the franchise's shapes. Keimgarten is also the project's first "spend hub": it gives the cross-game currency that other 100g games yield a meaningful place to land, so every other game in the project gains a clear answer to "where do my points go?".

The design has been brainstormed and specified. What I need now is implementation: Keimgarten depends on Layer A (engine + services + shared franchise bestiary + tier metadata) being in place from Drop Deck's slices, then layers its own pure domain and Pixi integration on top. As with Drop Deck, every public surface is a deep module and TDD is non-negotiable per CLAUDE.md.

Solution

Keimgarten is a mobile-first, non-scrolling pixel-art caretaker vivarium.

The player watches creatures from the franchise's 167-shape bestiary live, hop, and play autonomously on a single fixed 48 × 80 native-pixel screen. The garden is the player's permanent collection — once a creature is acquired, it stays forever. There is no decay, no real-time pressure, no "needs"; per franchise canon, creatures live persistently regardless of player attention.

Acquisition is exclusively through cross-game currency earned in other 100g games. The shop offers a tap-to-roll random-creature draw within a chosen size tier (sizes 1–5 only, prices 3 / 8 / 20 / 50 / 120). Sizes 6–9 (Titan / Apex / Archon / Absolut, the franchise's higher tiers) are obtainable only through fusion of two owned creatures whose sizes sum to the target output size. Fusion is gated by a tier-unlock chain (must have ever owned a size-(N-1) creature to fuse to size N) and an escalating currency cost (0 / 100 / 500 / 2,000 for sizes 6 / 7 / 8 / 9). The cost curve is calibrated so that the unique size-9 Vollkommen arrives at roughly day 100 of casual play (~30 currency/day baseline).

Three achievements track the headline milestones (first-fusion, first-archon, vollkommen). Keimgarten itself yields cross-game currency proportional to bestiary breadth and depth, capped at 50 per session.

The implementation produces three layers, mirroring Drop Deck's pattern: Layer A (already shipped by Drop Deck slices: engine, services, shared franchise bestiary), Layer B (Keimgarten's pure domain — garden, shop, fusion, currency yield, run state, creature behaviour), and Layer C (Pixi rendering, input controller, manifest, index).

User Stories

A. Day-1 onboarding and the starter

  1. As a new player, I want to find Keim — the unique size-1 creature — alone in my garden on first launch, so that I have something to look at and tap from minute one without paying anything.
  2. As a new player, I want the empty garden to make it self-evident that I need to play other 100g games to acquire more creatures, so that I understand the cross-game economy by experience rather than by tutorial.
  3. As a new player, I want Keim to wander, hop, and respond to taps even when I have no currency, so that the day-1 experience is charming on its own merits.
  4. As a player, I want my garden state to persist forever — the same Keim is there next time, the same tap counts retained — so that the relationship feels real.

B. The non-scrolling garden surface

  1. As a mobile player, I want the entire garden to render on one screen with no scrolling, so that the play surface is always visible at a glance.
  2. As a mobile player, I want the garden to render at 48 × 80 native pixels, scaled by the largest integer multiple that fits my device's viewport (7×–8× on standard phones, higher on tablets/desktops), so that creatures render at true pixel resolution per franchise canon and never blur.
  3. As a player, I want the layout to dedicate 48 × 56 native pixels (the central play area) to creature wandering, with a top bar (48 × 8) for the currency balance and a bottom bar (48 × 16) for Shop / Fusion / Bestiary buttons, so that the HUD is unobtrusive but always reachable.
  4. As a mobile player, I want every interactive UI element to meet a 44 × 44 px minimum hit target even at the lowest supported zoom (7×), so that touch is reliable.
  5. As a player, I want creatures to wander only inside the play-area rectangle (never overlapping HUD bars), so that the garden feels bounded and focused.
  6. As a player, I want the background to be a soft pastel gradient with no parallax, no clouds, and no animated chrome, so that the creatures themselves are the visual focus.

C. Watching creatures live (autonomous behaviour)

  1. As a player, I want each owned creature to wander autonomously through a state machine (idle / walk / hop / play / nap), so that the garden feels alive without my input.
  2. As a player, I want creatures to move at integer-pixel granularity — gentle stepping, no sub-pixel smoothing — so that the franchise's true-pixel-art identity is preserved.
  3. As a player, I want creatures to never collide or block each other (they pass through gracefully because they're pixel-shapes, not physics bodies), so that motion stays charming and unblocked.
  4. As a player, I want occasional "play" interactions where two nearby creatures pair up briefly and bob, so that the garden has emergent moments of charm beyond solo wandering.
  5. As a player, I want creatures to occasionally nap (curl up, slightly tinted), so that the garden has visual rhythm rather than constant motion.
  6. As a player, I want behaviour state transitions to be deterministic given a seeded RNG, so that the behaviour layer is unit-testable in isolation.

D. Tap-on-creature affection

  1. As a player, when I tap a creature, I want it to do a happy hop animation and a nameplate tooltip to appear for ~2 seconds showing its German name and tier (e.g., "Schmetterling — Wesen (5)"), so that I learn each creature's identity through play.
  2. As a player, I want the tap to have no mechanical effect (no currency, no progression) — it's purely affection — so that I'm never punished for ignoring it or rewarded for grinding it.
  3. As a player, after I've tapped a single creature 100 cumulative times, I want a tiny heart particle to begin appearing on subsequent taps for that creature only, so that long-loved creatures feel acknowledged. (Cosmetic only.)
  4. As a player, I want my tap counts persisted across sessions, so that the affection is durable.

E. Shop — tap-to-roll-random-by-size (sizes 1–5)

  1. As a player, I want the Shop overlay to show five buttons, one per buyable size (1–5), with a clear price label on each, so that size-tier choice is the only friction.
  2. As a player, when I tap a size button and have enough currency, I want a single random creature of that size to be added to my garden immediately and the cost deducted, so that purchase is a single tap with no further pickers.
  3. As a player, I want shop draws to be uniform within a size (every size-3 creature is equally likely from a size-3 buy), so that distribution is fair and grindable.
  4. As a player, I want the shop to allow duplicate draws (a creature I already own can be rolled again), so that duplicates are intentional fusion fuel rather than a frustration-by-design.
  5. As a player, I want size-1 to be the cheapest (3 currency) and size-5 the most expensive (120 currency) with intermediate sizes priced 8 / 20 / 50, so that the price curve maps to creature scarcity within a tier.
  6. As a player, I want the shop to refuse purchases when I don't have enough currency, with the relevant size button visibly disabled, so that I never see a deduction surprise.
  7. As a player, I want the shop overlay to scroll if its content ever exceeds the viewport (it shouldn't with five buttons, but the scroll-allowed policy is consistent with other overlays), so that overlay UX is consistent.
  8. As a player, I want the shop to be skippable by tapping outside or using a back gesture, so that opening it is never a commitment.

F. Fusion — the headline progression mechanic

  1. As a player, I want fusion to be the only path to creatures of sizes 6–9 (Titan / Apex / Archon / Absolut), so that those tiers feel earned rather than purchased.
  2. As a player, I want the Fusion overlay to first ask me to pick a target output size from {6, 7, 8, 9}, with locked tiers visible but disabled until I've earned the unlock, so that the progression chain is legible.
  3. As a player, I want a tier-unlock rule where size N output is fuseable only after I've ever owned a size-(N-1) creature, so that the chain forces step-by-step climbing through Titan → Apex → Archon → Vollkommen.
  4. As a player, I want tier unlocks to be permanent — I don't need to currently hold a size-(N-1) creature to fuse to N, only to have ever owned one — so that I'm never forced to babysit each climbed tier.
  5. As a player, after picking a target size, I want to pick exactly two of my owned creatures whose sizes sum to that target, so that valid input pairs are deterministic and the math is transparent.
  6. As a player, I want the fusion-confirm button enabled only when both inputs are picked, their sum equals the target, and I have enough currency for the fusion cost, so that bad combos can't be accidentally committed.
  7. As a player, I want fusion costs to escalate by tier (0 for size 6, 100 for size 7, 500 for size 8, 2,000 for size 9), so that each higher tier feels like a meaningful save-up.
  8. As a player, when I confirm a fusion, I want both input creatures consumed permanently and a single random creature of the target size added, so that fusion is a clean "two go in, one comes out" trade.
  9. As a player, I want the fusion output drawn uniformly within the target size's pool (one of 42 size-6, one of 21 size-7, one of 6 size-8; the unique Vollkommen for size 9), so that climbing the chain is deterministic but which creature lands is fate-driven.
  10. As a player, I want a 4 + 5 fusion to always produce Vollkommen (since size-9 has only one entry), so that the long save-up to the size-9 fusion-cost-2000 culminates in a guaranteed-but-rare event rather than another roll.
  11. As a player, I want the Fusion overlay to scroll vertically if my owned-creature list exceeds the viewport, so that picking inputs from a large garden is feasible.

G. The bestiary overlay

  1. As a player, I want a Bestiary overlay accessible from the bottom bar that lists all 167 slots in size order (Keim → Vollkommen), grouped by tier, so that my collection journey is always visible at a glance.
  2. As a player, I want unowned slots shown as greyed silhouettes and owned slots shown in full color with the creature's German name and tap-count, so that I see at a glance what I have and what I'm missing.
  3. As a player, I want the bestiary overlay to scroll vertically (since 167 entries don't fit on the play surface), so that the catalog is fully browsable on mobile.
  4. As a player, I want a small sticky header in the bestiary overlay showing my owned-vs-locked count (e.g., "47 / 167"), so that I always know my completion state.
  5. As a player, I want the bestiary overlay to close on tap-outside or back-gesture, so that I can browse without commitment.
  6. As a player, I want the bestiary's owned/locked status to derive from the same RunState as everything else, so that there's no possibility of desync between "I just acquired Schmetterling" and "the bestiary still shows Schmetterling locked".

H. Achievements and cross-game progression

  1. As a player, I want three achievements on milestone axes — first-fusion (Erste Verschmelzung: complete any fusion), first-archon (Archon erwacht: own at least one size-8), vollkommen (own the unique size-9) — so that the franchise canon's emotional peaks are recognised.
  2. As a player, I want achievements to fire only once per save (they are not per-run, since Keimgarten has no run/reset cycle), so that the first-time-ever moment is preserved as singular.
  3. As a player, I want my Keimgarten to yield cross-game currency at session-end via the formula min(50, floor(uniqueOwnedCount / 10) + highestTierReached * 2), so that growing the garden gently feeds the cross-game economy.
  4. As a player, I want the per-session yield cap of 50 to prevent tap-farming, so that Keimgarten contributes meaningfully but never dominates the cross-game economy.
  5. As a player, I want Keimgarten's yield to be deterministic for any given state, so that the economy is fair and auditable.

I. Persistence

  1. As a player, I want the entire garden state (owned creatures, tap counts, tier unlocks, achievement status, schema version) saved to IndexedDB after every action, so that I never lose progress to a closed tab or OS sleep.
  2. As a player, I want the cross-game currency balance and achievement set persisted by the cross-game services (not by Keimgarten itself), so that Keimgarten consistently observes the same balance other games modify.
  3. As a player, I want a single Keimgarten save per browser (no multi-save / multi-profile) at launch, so that the data model is simple and the player has one durable garden.
  4. As a player, I want the save format to include a schema version so that future migrations are possible without data loss, even though no migration is needed at v1.

J. Cross-game architecture (developer-as-platform-owner)

  1. As the project owner, I want Keimgarten to consume the shared franchise bestiary at src/shared/franchise/bestiary.ts (extracted by Drop Deck's Layer A slices) as read-only data, so that the 167-creature catalog stays consistent across all games using it.
  2. As the project owner, I want any tier-label metadata (Keim / Bund / Funke / … with their colors) to live in src/shared/franchise/types.ts (or a sibling file in the same directory), so that future franchise games inherit tier identity uniformly.
  3. As the project owner, I want Keimgarten's domain layer to contain zero Pixi imports and zero DOM access, so that game logic is fully unit-testable in pure node.
  4. As the project owner, I want Keimgarten to implement the same Game lifecycle contract from src/engine/Game.ts that Drop Deck implements, so that the platform shell mounts both games uniformly.
  5. As the project owner, I want every Keimgarten domain function implemented test-first per CLAUDE.md TDD invariant, so that the code is reliable from day one.
  6. As the project owner, I want no franchise-trademark names anywhere in code, paths, commits, docs, UI strings, or assets (per Anti-IP rules); the title "Keimgarten" and all creature names are from Daniel's original German bestiary so this is automatically satisfied.
  7. As the project owner, I want no new runtime dependencies introduced by Keimgarten (Pixi is already in package.json), so that bundle size stays controlled.
  8. As the project owner, I want TypeScript strict mode with noUncheckedIndexedAccess honoured, so that off-by-one and nullish access bugs are caught at compile time.
  9. As the project owner, I want creature behaviour (the idle/walk/hop/play/nap state machine) to live in domain/ despite "feeling like" a render concern, because it's pure logic and benefits from the same TDD discipline as the rest of the domain.

Implementation Decisions

Architecture

  • Three-layer split, identical pattern to Drop Deck. Layer A: cross-game prerequisites (already shipped via Drop Deck PRD slices: engine Game contract; shared franchise bestiary at src/shared/franchise/bestiary.ts + types.ts; persistence, economy, achievements, input, audio services). Layer B: Keimgarten's pure domain. Layer C: Keimgarten's integration (Pixi rendering, input controller, manifest, index).
  • Layer A is a hard prerequisite already specified by Drop Deck's PRD. Keimgarten cannot ship until those slices land. The Drop Deck PRD's "prerequisite ordering" section governs Layer A. Keimgarten adds no new Layer A modules unless tier-label metadata wasn't included in Drop Deck's bestiary extraction — in that case, Keimgarten contributes a small extension to src/shared/franchise/types.ts.
  • Pure domain. Layer B contains zero Pixi imports and zero DOM access. All game logic is exercised through plain reducers over typed structures.
  • Behaviour state machine in domain/, not render/. Although creature behaviour is rendered visually, the state-machine logic itself is pure and is placed under domain/creatureBehavior.ts for testability symmetry with the rest of the domain.
  • DEEP MODULES contract. Every domain module exports a small interface with deep behavior. A change is "wide and shallow" if it adds many surface methods; the response is to refuse such changes and propose collapsing them into one deeper method first.

The shop (final algorithm)

  • Five buttons, one per buyable size (1–5), each with its current price labelled.
  • Tap a size button → if currency ≥ price, deduct price, draw a uniform-random creature ID from that size's pool in the bestiary, add a new owned-creature instance to the garden. Single tap, no picker.
  • If currency < price, the button is visually disabled.
  • Duplicates are intentional and accepted (used as fusion fuel).
  • Prices: size 1 = 3, size 2 = 8, size 3 = 20, size 4 = 50, size 5 = 120 (currency).

Fusion (final algorithm)

  • Tier-unlock chain (permanent). Output size 6 unlocks once the player has ever owned a size-5 creature. 7 unlocks on owning 6. 8 on owning 7. 9 on owning 8. Once unlocked, stays unlocked even if the player no longer holds a creature of that tier.
  • Cost (in cross-game currency). Output 6 = 0, 7 = 100, 8 = 500, 9 = 2,000. Deducted on confirm.
  • Input pair. Player picks target output size first (only unlocked sizes are pickable), then picks two owned creatures whose sizes sum exactly to the target. Confirm button enables when both inputs are picked, sum is correct, and currency ≥ cost.
  • On confirm. Both inputs are consumed permanently. A single random creature of the target size is drawn uniformly from that size's pool and added to the garden. For size 9, the pool has exactly one entry (Vollkommen) so the output is deterministic-by-pool.
  • No re-roll, no undo. Per franchise canon: permanent decisions, no exiting fate.

Pacing — Vollkommen at ~day 100

The fusion-cost curve is calibrated against an assumed cross-game currency yield of ~30/day baseline. Speedrun-optimal Vollkommen path costs 2,732 currency total (size 5 → fuse 1+5 → fuse 1+6 → fuse 1+7 → fuse 1+8) which is ~91 days at 30/day. Realistic play (duplicates, exploration, multiple fusions per tier) lands the first Vollkommen around days 100–150. Currency yield baseline is a tunable; size-9 fusion cost is the primary lever to push Vollkommen out further if real-world yield is higher.

Pixel canvas

  • Native world resolution: 48 × 80 native pixels. Logical canvas, immutable across devices.
  • Integer-zoom autoscale: each device renders at the largest integer multiple of native that fits its viewport. iPhone SE (375 × 667) → 7×; iPhone 11 (414 × 896) → 8×; tablets/desktops higher. The native grid never changes.
  • HUD allocation: top 48 × 8 (currency bar), middle 48 × 56 (play area), bottom 48 × 16 (Shop / Fusion / Bestiary button trio at 16 × 16 native each).
  • Pixi Application configured with roundPixels: true. Render hierarchy: ZoomContainer (scaled by integer zoom) → BackgroundLayer + PlayLayer (creatures) + HUDLayer.
  • All animation is translation-only and integer-pixel. No rotation, no scale animations, no skew, no sub-pixel positioning. Hop animations move whole pixels. This preserves franchise canon.

Currency yield formula

  • compute(state) = min(50, floor(state.uniqueOwnedCount / 10) + state.highestTierReached * 2)
  • Per-session cap of 50 prevents tap-farming.
  • Awarded to the cross-game economy service at session-end (boundary defined by the platform shell, not Keimgarten).

Achievements

  • Three IDs: first-fusion (Erste Verschmelzung), first-archon (Archon erwacht), vollkommen (Vollkommen).
  • Each fires once per save (Keimgarten has no run/reset cycle).
  • Yielded to the cross-game achievements service.

Persistence

  • Save the entire RunState to IndexedDB after every state-mutating action, with a small debounce (50 ms) to coalesce rapid taps.
  • A single Keimgarten save per browser at launch.
  • RunState.schemaVersion is included for future migrations even though v1 needs none.
  • Persistence goes through the cross-game persistence service (per Drop Deck PRD's Layer A interface).

Tech invariants honored (per CLAUDE.md)

  • TypeScript strict + noUncheckedIndexedAccess.
  • Vite build with base: "/100g/" for GitHub Pages.
  • Pixi for rendering; the engine's thin custom ECS is used for render-side animated entities only.
  • Domain layer is plain reducers over typed structures, no ECS.
  • Vitest with happy-dom for DOM-adjacent tests, node for pure tests.
  • typescript-eslint + Prettier + Husky/lint-staged.
  • pnpm package manager.
  • Daily auto-deploy to GitHub Pages on every merge to main.

Testing Decisions

What makes a good test (project-wide)

A good test exercises external behavior, never implementation details. It calls the deep module's public surface, asserts on the return value or observable side effects, and would still pass if the internals were rewritten. Tests live next to source. Pure tests run in node; DOM-adjacent tests run in happy-dom.

TDD coverage

  • Layer B is TDD non-negotiable per CLAUDE.md's iron law (no production code without a failing test first). Every public function in domain/garden, domain/shop, domain/fusion, domain/currencyYield, domain/runState, and domain/creatureBehavior is implemented test-first.
  • Layer C is mixed. Input controller is TDD with happy-dom. Manifest is TDD (just an export-shape assertion). Render layer (Pixi-based views) is NOT unit-tested — visual regression is human-reviewed during night-shift QA, matching Drop Deck's convention. A single integration smoke test mounts the game, simulates a starter Keim plus 5 size-2 purchases, and asserts the garden contains the expected creatures + the currency was deducted correctly.

Critical test specifications

  • Tier-unlock gate. Cannot fuse to size N if the player has never owned a size-(N-1) creature. Once owned even once, the unlock is permanent and survives consuming all instances of that tier. Test the full chain 5 → 6 → 7 → 8 → 9.
  • Fusion cost deduction. Fuse-to-7 deducts 100; fuse-to-8 deducts 500; fuse-to-9 deducts 2000; fuse-to-6 is free. Insufficient currency refuses the fusion cleanly without partial state mutation.
  • Fusion sum validation. Refuse pairs whose sizes don't sum to the chosen target; refuse same-instance picks (cannot pick instance A twice).
  • Fusion output uniformity. With seeded RNG and a tolerance bound, confirm fusion outputs are uniform across the target size's pool. Confirm that size 9 always returns the unique Vollkommen.
  • Shop draw uniformity. With seeded RNG and tolerance bounds, confirm size-N shop draws are uniform across size-N's pool. Confirm duplicates can occur (no de-duplication trick).
  • Currency yield formula. Boundary cases: 0 owned (yield = 0), 10 unique only at tier 1 (yield = 1 + 2 = 3), full collection (yield capped at 50), highest-tier-reached vs unique-count interactions.
  • Achievement firing. First fusion fires first-fusion once. Owning a size-8 fires first-archon once even if obtained via subsequent fuse. Owning Vollkommen fires vollkommen once. None re-fire on subsequent identical events.
  • Behaviour state machine. With seeded RNG, transitions idle → walk → hop → play → nap follow the spec's weight distribution within tolerance bounds. Position updates are integer-pixel only.
  • Persistence round-trip. Save after every action; reload; assert RunState (owned, uniqueOwnedIds, tier-unlocks, tap counts, achievements) is bit-identical. Confirm 50 ms debounce coalesces rapid taps to one write.
  • Mobile viewport smoke. Manual pass on 375 × 667 (iPhone SE) and 414 × 896 (iPhone 11) before merging: 7×/8× integer-zoom letterboxes cleanly with no fractional-pixel artefacts; HUD readable; tap-targets ≥ 44 × 44; tap-on-creature dispatches correctly.

What is NOT tested

  • Pixi visual rendering (no snapshot tests; visual regression is night-shift QA, matching Drop Deck's prior art).
  • Audio playback timing (audio service has its own light tests; game-level audio is fire-and-forget chimes/whooshes).
  • Statistical convergence of RNG over millions of rolls — one focused statistical test per RNG-using function with tolerance bounds is sufficient; long-running stress tests are not part of CI.

Out of Scope

The following were considered and explicitly cut, either in the spec or during the 2026-05-05 brainstorming:

  • Pixel types (Puls / Phase / Funke / Void / Blüte / Kristall). Franchise-canonical, deferred to a future game (likely game 003+ with pixel types as its headline mechanic).
  • Archon-Zerfall (splitting an Archon into smaller creatures). Franchise-canonical but tied to a monetisation flow that doesn't apply to 100g; deferred indefinitely.
  • Land ownership / territory expansion / multi-room gardens. Franchise-canonical for the eventual MMO; not for 100g — Keimgarten is one fixed 48 × 80 screen per player.
  • ML-trained creatures playing 2D action levels. A long-term franchise idea, post-100g.
  • Real-time decay / Tamagotchi needs (Q5 lock). Contradicts franchise canon's persistent-living-world; cut.
  • In-garden currency separate from cross-game currency (Q4 lock). Only cross-game currency drives acquisition.
  • 3-of-N shop pickers / multi-button selection in the garden (Q9 lock). Tap-to-roll only.
  • Fusion recipe determinism (Q8 lock). Output is random within target size; no input-pair → output-pair table.
  • English translation of creature names. German verbatim from the franchise bestiary at v1; localisation pass deferred.
  • Multiple gardens or saves per browser. Single save at launch.
  • Reset / new-game / hard-restart / "rebirth". No reset path at launch.
  • Trading / sharing / online features. Per ADR 0001 — no server, no networking.
  • Decoration system / placeable props in the garden. The current spec's garden is intentionally empty (pastel gradient only). A decoration mechanic would be a future game's headline mechanic, not a Keimgarten v1 feature.
  • Energy-level "platformer" motion. The play area at 48 × 56 native pixels is sized for gentle wandering, not vigorous jump-and-run. Energetic motion would require a larger native canvas and was explicitly traded against scope; out of scope for v1.
  • A second acquisition path via a future "shop game". Mentioned during brainstorming as a possibility; the spec deliberately couples acquisition only to cross-game currency (a stable interface), so a future game can layer on alternative acquisition paths without rewriting Keimgarten. No work needed in this PRD.
  • Server-backed anything. All state IndexedDB; no fetch calls. Per ADR 0001.
  • Bestiary re-extraction. The shared franchise bestiary is extracted by Drop Deck's Layer A slices; Keimgarten consumes it read-only. Re-extraction or fork is out of scope.

Further Notes

Calibration items deferred to playtesting

These are tuning concerns, not architecture, and should be revisited after the first playable build is shipped:

  • Currency yield baseline (~30/day). Depends on which 100g games actually ship and what they typically yield.
  • Shop prices (3 / 8 / 20 / 50 / 120).
  • Fusion costs (0 / 100 / 500 / 2,000), especially the size-9 fusion cost as the lever for Vollkommen-arrival pacing.
  • Currency yield cap (50 per session).
  • Affection-heart unlock threshold (currently 100 taps per creature).
  • Behaviour state-transition weights (the idle/walk/hop/play/nap mix).
  • Background palette (currently #FFE8D6#FFD6C4 warm-dawn).
  • Whether the "ever owned" tier-unlock should instead be "currently own" for added collection texture.

Decomposition strategy for /to-issues

This PRD should decompose into independently-grabbable vertical slices. Suggested grain:

  • Layer A extension (only if needed): tier-label metadata in src/shared/franchise/types.ts (Keim / Bund / Funke / Gestalt / Wesen / Titan / Apex / Archon / Absolut + colors). Skip if Drop Deck already included this.
  • Keimgarten domain/runState.ts (master RunState + apply reducer + schema version).
  • Keimgarten domain/garden.ts (own / tap / transfer reducers).
  • Keimgarten domain/shop.ts (price lookup + shop-roll RNG with seeded-RNG tests).
  • Keimgarten domain/fusion.ts (tier-unlock + cost + sum-check + output RNG with seeded tests).
  • Keimgarten domain/currencyYield.ts (formula + cap + boundary tests).
  • Keimgarten domain/creatureBehavior.ts (state machine; pure RNG-driven).
  • Keimgarten render/stage.ts (Pixi Application + ZoomContainer + integer-zoom autoscale + layer scaffolding).
  • Keimgarten render/creatureSprite.ts (RenderTexture rasterisation + sprite-per-instance + tap routing).
  • Keimgarten render/hudView.ts (top bar + bottom button trio).
  • Keimgarten render/shopOverlay.ts (buy-size-N tap-to-roll wiring through domain/shop).
  • Keimgarten render/fusionOverlay.ts (target-pick → input-pair-pick → confirm wiring through domain/fusion).
  • Keimgarten render/bestiaryOverlay.ts (vertical-scrollable 167-slot list, owned/locked silhouettes).
  • Keimgarten input/controller.ts (pointer normalisation, hit-testing, command dispatch).
  • Keimgarten manifest.ts + index.ts (Game contract conformance).
  • Integration smoke test (mount-and-buy + mount-and-fuse).
  • Mobile viewport smoke (cross-cuts everything; 375 × 667 manual pass).

Each slice should produce a passing test suite plus minimal implementation, mergeable independently to main. The night-shift workflow (subagent-driven-development per CLAUDE.md) picks up issues with the night-shift label after /triage clears them.

Prerequisite ordering

Layer A from Drop Deck's PRD MUST land first — engine Game contract, shared franchise bestiary extraction, persistence/economy/achievements/input/audio services. Once Layer A is green, Keimgarten domain modules can run in parallel where they don't depend on each other (runState is the convergence point — most others depend on its types). Render slices follow domain. Manifest and index are the final glue.

Source of truth

The design spec at docs/superpowers/specs/2026-05-05-keimgarten-design.md is the authoritative specification — including specific algorithms, dimensions, formulas, and module layout. This PRD describes WHAT to build and WHY; the spec describes HOW. If the two ever conflict, the spec wins and the PRD should be updated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationgameA daily mini-gamepriority:p2Normal

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions