Merge pull request 'Release: test infrastructure, rules overhaul, and design refresh' (#30) from develop into main

Reviewed-on: #30
This commit was merged in pull request #30.
This commit is contained in:
2026-02-21 16:58:14 +01:00
362 changed files with 45471 additions and 18064 deletions

View File

@@ -1,19 +0,0 @@
---
# nuzlocke-tracker-009n
title: Add CLI export for all seed data types
status: completed
type: feature
priority: normal
created_at: 2026-02-08T11:37:27Z
updated_at: 2026-02-08T11:38:48Z
---
Add export functions for games, pokemon, routes/encounters, and evolutions to the seed CLI, matching the existing export API endpoints. Consolidate with the existing --export-bosses into a single --export flag that dumps everything.
## Checklist
- [x] Add export_games() to run.py — writes games.json
- [x] Add export_pokemon() to run.py — writes pokemon.json
- [x] Add export_routes() to run.py — writes {game_slug}.json per game (routes + encounters)
- [x] Add export_evolutions() to run.py — writes evolutions.json
- [x] Replace --export-bosses with --export flag that exports all data types
- [x] Update __main__.py docstring

View File

@@ -1,10 +1,11 @@
---
# nuzlocke-tracker-0arz
title: Integration tests for Runs & Encounters API
status: draft
status: completed
type: task
priority: normal
created_at: 2026-02-10T09:33:21Z
updated_at: 2026-02-10T09:33:21Z
updated_at: 2026-02-21T11:54:42Z
parent: nuzlocke-tracker-yzpb
---
@@ -12,15 +13,15 @@ Write integration tests for the core run tracking and encounter API endpoints. T
## Checklist
- [ ] Test run CRUD operations (create, list, get, update, delete)
- [ ] Test run creation with rules configuration (JSONB field)
- [ ] Test encounter logging on a run (create encounter on a route)
- [ ] Test encounter status changes (alive → dead, alive → retired, etc.)
- [ ] Test duplicate encounter prevention (dupes clause logic)
- [ ] Test shiny encounter handling
- [ ] Test egg encounter handling
- [ ] Test ending a run (completion/failure)
- [ ] Test error cases (encounter on invalid route, duplicate route encounters, etc.)
- [x] Test run CRUD operations (create, list, get, update, delete)
- [x] Test run creation with rules configuration (JSONB field)
- [x] Test encounter logging on a run (create encounter on a route)
- [x] Test encounter status changes (alive → dead, faintLevel, deathCause)
- [x] Test route-lock enforcement (duplicate sibling encounter → 409)
- [x] Test shiny encounter handling (shinyClause bypasses route-lock)
- [x] Test gift clause bypass (giftClause=true, origin=gift bypasses route-lock)
- [x] Test ending a run (completion/failure, completed_at set, 400 on double-end)
- [x] Test error cases (404 for invalid run/route/pokemon, 400 for parent route, 422 for missing fields)
## Notes

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-0pas
title: Extract version groups and route ordering to JSON files
status: completed
type: task
priority: normal
created_at: 2026-02-07T13:27:37Z
updated_at: 2026-02-07T13:32:30Z
---
Move VERSION_GROUPS and ROUTE_ORDER from fetch_pokeapi.py into separate JSON files (version_groups.json, route_order.json) for easier editing. Update the script to load from the JSON files.

View File

@@ -1,28 +0,0 @@
---
# nuzlocke-tracker-0q8f
title: Encounter Tracking Interface
status: completed
type: task
priority: normal
created_at: 2026-02-04T15:44:37Z
updated_at: 2026-02-05T14:21:54Z
parent: nuzlocke-tracker-f5ob
---
Build the main interface for tracking encounters on each route/area.
## Checklist
- [x] Create route list component showing all areas in the game
- [x] Display encounter status per route (uncaught, caught, failed, skipped)
- [x] Build encounter modal/form:
- [x] Select Pokémon from route's available encounters
- [x] Enter nickname for caught Pokémon
- [x] Mark as caught, failed (ran/KO'd), or skipped (duplicates clause)
- [x] Show route progression (e.g., 15/45 routes completed)
- [x] Allow editing/updating existing encounters
- [x] Support marking gift/static encounters separately (deferred to nuzlocke-tracker-rxrt)
## UX Considerations
- Quick entry flow - minimize clicks to log an encounter
- Visual distinction between route types (grass, water, cave, etc.)
- Mobile-friendly for tracking while playing

View File

@@ -1,50 +0,0 @@
---
# nuzlocke-tracker-1e9k
title: Populate encounter data for Gen 8+ stub games
status: completed
type: task
priority: normal
created_at: 2026-02-10T08:59:02Z
updated_at: 2026-02-11T13:39:09Z
parent: nuzlocke-tracker-rzu4
---
Fill in encounter data for games that currently have null/stub seed files. These games are not covered by PokeAPI and require manual curation or an alternative data source.
## Games with null/stub data:
- [ ] Sword (null)
- [ ] Shield (null)
- [ ] Brilliant Diamond (null)
- [ ] Shining Pearl (null)
- [ ] Scarlet (null)
- [ ] Violet (null)
- [ ] Legends Arceus (null)
## Format requirements:
Each game's JSON file must follow the existing structure:
\`\`\`json
[
{
"name": "Route Name",
"order": 1,
"encounters": [
{
"pokeapi_id": 25,
"pokemon_name": "pikachu",
"method": "walk",
"encounter_rate": 10,
"min_level": 5,
"max_level": 8
}
],
"children": []
}
]
\`\`\`
## Notes:
- This is likely the largest manual task unless the "explore automated sources" task finds a viable alternative
- Depends on findings from the automated data sources task — if automation is viable, this becomes much easier
- Sword/Shield and Scarlet/Violet use open-world/Wild Area mechanics that may need special handling
- Legends Arceus has a fundamentally different encounter system (overworld encounters, alpha Pokémon, space-time distortions)
- BD/SP are remakes of Diamond/Pearl — existing D/P data could serve as a starting point

View File

@@ -1,30 +1,21 @@
---
# nuzlocke-tracker-1guz
title: Component tests for key frontend components
status: draft
status: completed
type: task
priority: normal
created_at: 2026-02-10T09:33:45Z
updated_at: 2026-02-10T09:33:45Z
updated_at: 2026-02-21T12:53:51Z
parent: nuzlocke-tracker-yzpb
---
Write component tests for the most important frontend React components, focusing on user interactions and rendering correctness.
Write component tests for key frontend React components, focusing on user interactions and rendering correctness.
Test components with no external hook dependencies directly; mock `useTheme` where needed. Use @testing-library/user-event for interactions.
## Checklist
- [ ] Test `EncounterModal`form submission, validation, Pokemon selection
- [ ] Test `StatusChangeModal` — status transitions, confirmation flow
- [ ] Test `EndRunModal` — run completion/failure flow
- [ ] Test `GameGrid` — game selection rendering, click handling
- [ ] Test `RulesConfiguration` — rules toggle interactions, state management
- [ ] Test `Layout` — navigation rendering, responsive behavior
- [ ] Test admin form modals (GameFormModal, RouteFormModal, PokemonFormModal) — CRUD form flows
- [ ] Test `AdminTable` — sorting, filtering, action buttons
## Notes
- Focus on user-facing behavior, not implementation details
- Use @testing-library/user-event for simulating clicks, typing, etc.
- Mock API responses for components that fetch data
- Don't aim for 100% coverage — prioritise the most complex/interactive components
- Page components (RunEncounters, RunDashboard, etc.) are large and complex — consider testing their sub-components instead
- [x] Test `EndRunModal`Victory/Defeat/Cancel button callbacks, genlocke description text, disabled state
- [x] Test `GameGrid` — renders games, generation filter, region filter, onSelect callback
- [x] Test `RulesConfiguration` — renders rule sections, toggle calls onChange, type restriction toggle, reset button
- [x] Test `Layout` — nav links present, mobile menu toggle, theme toggle button

View File

@@ -1,34 +0,0 @@
---
# nuzlocke-tracker-1iuh
title: Project Setup & Tech Stack
status: completed
type: task
priority: normal
created_at: 2026-02-04T15:43:55Z
updated_at: 2026-02-04T15:55:46Z
parent: nuzlocke-tracker-f5ob
blocking:
- nuzlocke-tracker-sm1b
- nuzlocke-tracker-k5lm
- nuzlocke-tracker-uw2j
- nuzlocke-tracker-dgax
- nuzlocke-tracker-0q8f
- nuzlocke-tracker-hm6t
- nuzlocke-tracker-8tuw
- nuzlocke-tracker-8fcj
---
Set up the initial project structure and choose the technology stack.
## Checklist
- [x] Initialize the project (e.g., Vite + React/Vue/Svelte or Next.js)
- [x] Set up TypeScript configuration
- [x] Configure linting (ESLint) and formatting (Prettier)
- [x] Set up basic folder structure (components, types, utils, data)
- [x] Add Tailwind CSS or chosen styling solution
- [x] Create basic app shell with routing
## Considerations
- Should be a web app for accessibility
- Consider PWA capabilities for offline use
- Keep bundle size small for fast loading

View File

@@ -0,0 +1,9 @@
---
# nuzlocke-tracker-1qzo
title: Fix WCAG AA color contrast violations
status: completed
type: bug
priority: high
created_at: 2026-02-20T19:19:32Z
updated_at: 2026-02-20T19:20:25Z
---

View File

@@ -1,89 +0,0 @@
---
# nuzlocke-tracker-25mh
title: Genlocke tracking
status: completed
type: epic
priority: normal
created_at: 2026-02-08T12:17:19Z
updated_at: 2026-02-09T11:07:51Z
---
Track a **genlocke** — a series of linked nuzlocke runs, typically one per generation or region. The player picks one game per generation/region and surviving Pokemon carry over between legs.
## User Flow
### 1. Create Genlocke
The user starts a new genlocke and gives it a name.
### 2. Select Games (Legs)
The user picks which games to play, in order. The UI offers **preset templates** to speed this up, but the user can always customize:
- **True Genlocke** — One original game per generation (Red/Blue/Yellow → Gold/Silver/Crystal → Ruby/Sapphire/Emerald → ...). Uses the original releases only.
- **Normal Genlocke** — Uses the latest remake or enhanced version for each region (FireRed/LeafGreen → HeartGold/SoulSilver → Emerald → Platinum → ...). This is the most common format.
- **Custom** — The user picks any games in any order. No restrictions on which games or how many.
For the preset templates, the user still picks *which* game within each generation/region slot (e.g., FireRed vs LeafGreen for Gen 1). The template just determines which slots are shown. The user can add/remove/reorder legs after selecting a template.
Games are grouped by **region** (not release generation) for the purpose of presets, since that's how genlocke players think about it (e.g., FireRed is a "Kanto" leg, not a "Gen 3" leg).
### 3. Configure Rules
Two categories of rules are configured:
**Per-game nuzlocke rules** — The standard nuzlocke ruleset (first encounter only, permadeath, duplicates clause, level caps, etc.). These are set once and apply uniformly to all legs. Uses the existing `NuzlockeRules` interface.
**Genlocke-specific rules** — Overarching rules that govern how legs connect:
- **Keep HoF** (default) — Pokemon that enter the Hall of Fame at the end of a leg are transferred to the next leg as eggs (breed at level 1). This is the standard genlocke mechanic.
- **Retire HoF** — Pokemon that enter the Hall of Fame are retired. They (and their evolutionary families) become unavailable in future legs (added to a cumulative dupe list). This is also known as the "Gauntlet" rule. Increases difficulty by forcing new Pokemon each leg.
- Potentially more rules in the future (e.g., item carry-over restrictions, level scaling).
### 4. Sequential Run Progression
- When the genlocke is created, the **first leg** is automatically started as a new nuzlocke run.
- Each leg is a full nuzlocke run, tracked exactly like any standalone run (encounters, team, bosses, graveyard, etc.).
- When a leg is marked as **completed** (Hall of Fame), the next leg is started. A transfer step happens between legs where the user selects which surviving Pokemon to carry forward.
- When a leg is marked as **failed** (wipe), the genlocke itself is marked as failed (game over).
- The final leg's completion marks the entire genlocke as completed.
### 5. Genlocke Overview Page
A dedicated page showing:
- **Progress** — Which leg is active, which are completed, which are upcoming. Visual timeline or step indicator.
- **Configuration** — Selected games, rules, genlocke-specific rules.
- **Cumulative Stats** — Total encounters, total deaths, total HoF entries across all legs.
- **Lineage Tracking** — Show Pokemon that have carried over across multiple legs (their journey through the genlocke).
- **Cumulative Graveyard** — All deaths across all legs in one view.
## Data Model
### New entities:
- **`Genlocke`** — Top-level entity: name, status (active/completed/failed), genlocke rules (JSONB), created_at.
- **`GenlockeLeg`** — Join table linking a Genlocke to a NuzlockeRun: genlocke_id, run_id, leg_order. Defines the sequence.
### Changes to existing entities:
- **`NuzlockeRun`** — No schema changes needed. A run that's part of a genlocke is just a normal run that happens to be referenced by a GenlockeLeg. The genlocke-level rules are stored on the Genlocke, not duplicated per run.
### Transfer tracking:
- **`GenlockeTransfer`** — Records which Pokemon were carried between legs: from_leg_id, to_leg_id, encounter_id (the source encounter from the completed leg), to_encounter_id (the egg/gift encounter created in the next leg).
## Child Features (suggested breakdown)
1. **Genlocke creation wizard** — Multi-step UI: name → game selection (with presets) → rules → confirm
2. **Genlocke overview page** — Dashboard with progress, stats, configuration
3. **Leg progression** — Auto-start next leg when current completes, transfer step
4. **Transfer UI** — Select surviving Pokemon to carry forward between legs
5. **Lineage tracking** — Show a Pokemon's journey across legs
6. **Cumulative graveyard** — Deaths across all legs in one view
7. **Gauntlet/Retire HoF rule** — Enforce the "retire" mechanic with cumulative dupe list
## Success Criteria
- [x] A user can create a new genlocke via a multi-step wizard (name, game selection with presets, rules)
- [x] Games can be selected using True Genlocke, Normal Genlocke, or Custom presets, grouped by region
- [x] Nuzlocke rules are configured once and applied uniformly to all legs
- [x] Genlocke-specific rules (Keep HoF / Retire HoF) can be selected
- [x] The first leg starts automatically upon genlocke creation
- [x] Each leg is a full nuzlocke run, tracked identically to standalone runs
- [x] Completing a leg triggers a transfer step where surviving Pokemon can be carried forward
- [x] Failing a leg marks the entire genlocke as failed
- [x] Completing the final leg marks the genlocke as completed
- [x] A genlocke overview page shows progress, configuration, cumulative stats, lineage, and graveyard
- [x] Transferred Pokemon appear as eggs (base form, level 1) in the next leg
- [x] Pokemon lineage is trackable across multiple legs
- [x] A cumulative graveyard shows all deaths across the entire genlocke
- [x] The Retire HoF / Gauntlet rule correctly retires HoF Pokemon and adds their families to the dupe list

View File

@@ -1,19 +0,0 @@
---
# nuzlocke-tracker-29oy
title: Boss seed data pipeline
status: completed
type: feature
priority: normal
created_at: 2026-02-08T11:33:33Z
updated_at: 2026-02-08T11:35:13Z
---
Export boss data from admin, save as seed JSON files, and load them during seeding.
## Checklist
- [x] Add unique constraint to boss_battles (version_group_id, order) + Alembic migration
- [x] Add upsert_bosses to seed loader (loader.py)
- [x] Add boss loading step to seed runner (run.py)
- [x] Add boss count to verify() function
- [x] Add export_bosses() function to run.py
- [x] Add --export-bosses flag to __main__.py

View File

@@ -1,36 +0,0 @@
---
# nuzlocke-tracker-2b4r
title: Add route ordering for Gen 5+ games
status: scrapped
type: task
priority: normal
created_at: 2026-02-10T08:58:55Z
updated_at: 2026-02-11T13:54:47Z
parent: nuzlocke-tracker-rzu4
---
Add route progression ordering in `route_order.json` for all games that currently lack it. Routes should be ordered to match the typical in-game progression (main story first, post-game after).
## Games needing route ordering:
- [ ] Diamond/Pearl (could alias to Platinum if progression is similar enough)
- [ ] Black/White
- [ ] Black 2/White 2
- [ ] X/Y
- [ ] Sun/Moon
- [ ] Ultra Sun/Ultra Moon (could alias to Sun/Moon if similar enough)
- [ ] Sword/Shield
- [ ] Brilliant Diamond/Shining Pearl (could alias to Platinum/Diamond-Pearl)
- [ ] Scarlet/Violet
- [ ] Legends Arceus
- [ ] Legends Z-A
- [ ] Let's Go Pikachu/Eevee (currently aliased to firered-leafgreen — verify this is correct)
## Approach:
- Reference Bulbapedia "walkthrough" or "appendix" pages for progression order
- Consider aliasing games that share the same region and route progression
- Mark a clear divider between main story and post-game routes where applicable
- The format is an array of route name strings in `route_order.json`, keyed by version group slug
## Notes:
- This is primarily manual work — play order guides are widely available online
- Verify route names match exactly what's in the encounter data files (case-sensitive)

View File

@@ -1,17 +0,0 @@
---
# nuzlocke-tracker-2zc9
title: Add zizmor and actionlint to CI
status: completed
type: task
priority: normal
created_at: 2026-02-16T19:13:50Z
updated_at: 2026-02-16T19:26:23Z
parent: nuzlocke-tracker-a5es
---
Global standards require scanning GitHub Actions workflows with zizmor (security audit) and actionlint (linter).
## Checklist
- [ ] Add actionlint check to CI
- [ ] Add zizmor scan to CI
- [ ] Fix any issues found

View File

@@ -1,23 +0,0 @@
---
# nuzlocke-tracker-338l
title: Verify fetch_pokeapi.py rewrite
status: scrapped
type: task
priority: normal
created_at: 2026-02-07T15:08:10Z
updated_at: 2026-02-07T19:05:29Z
---
All code changes are complete. Run verification:
1. Run the script to completion
2. Verify output (pokemon.json has forms, correct IDs, etc.)
3. Verify cache speedup on second run
4. Frontend build check
## Checklist
- [ ] Run fetch_pokeapi.py to completion
- [ ] Verify pokemon.json has more entries with forms (megas, gmax, regionals)
- [ ] Verify pokeapi_id/national_dex split is correct
- [ ] Verify route/encounter JSON files
- [ ] Verify cache speedup on second run
- [ ] Frontend build passes

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-36wg
title: Make footer stick to bottom of viewport
status: completed
type: bug
priority: normal
created_at: 2026-02-13T07:47:48Z
updated_at: 2026-02-13T12:59:22Z
---
On pages with little content, the footer appears right after the content instead of staying at the bottom of the viewport. The footer should always be at the bottom of the browser window, pushing down when there's enough content but not floating in the middle of the page when content is short (sticky footer pattern).

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-37gk
title: Curate route ordering to match game progression
status: completed
type: feature
priority: normal
created_at: 2026-02-07T12:25:05Z
updated_at: 2026-02-07T12:27:11Z
---
Implement ROUTE_ORDER in fetch_pokeapi.py for progression-based sorting, and add Export Order button to AdminGameDetail. See bean j28y for full plan.

View File

@@ -1,16 +0,0 @@
---
# nuzlocke-tracker-3c9l
title: Set up branching structure
status: completed
type: task
priority: normal
created_at: 2026-02-09T15:30:35Z
updated_at: 2026-02-10T10:49:55Z
parent: nuzlocke-tracker-ahza
---
Create the `develop` branch from `main` and establish the `main`/`develop`/`feature/*` branching workflow.
- Create `develop` branch from current `main`
- Push `develop` to remote
- Set `develop` as the default working branch

View File

@@ -1,29 +0,0 @@
---
# nuzlocke-tracker-3el1
title: Run progression dividers (main story / endgame)
status: completed
type: feature
priority: normal
created_at: 2026-02-08T13:40:14Z
updated_at: 2026-02-08T20:25:59Z
---
Add support for dividing a run's boss battle progression into sections like "Main Story" and "Endgame" (e.g., post-Elite Four content). This helps players visually distinguish where the main campaign ends and optional/endgame content begins.
## Context
Currently boss battles are displayed as a flat ordered list. In many Pokemon games there's a clear distinction between the main story (up through the Champion) and endgame content (rematches, Battle Frontier, Kanto in GSC/HGSS, etc.). A visual divider would make it easier to track progress through each phase.
## Scope
- **Admin side**: Allow marking boss battles or defining breakpoints that separate progression phases (e.g., "everything after this boss is endgame")
- **Run side**: Render a visual divider/section header between main story and endgame boss battles
- Should support at minimum two sections (main story, endgame), but consider whether the design should be flexible enough for arbitrary sections (e.g., "Kanto" in HGSS)
## Checklist
- [ ] Decide on data model approach (e.g., a `section` field on boss battles, or a separate progression divider entity tied to the version group)
- [ ] Add backend models and migrations
- [ ] Add API support for managing sections/dividers
- [ ] Update admin UI to allow assigning bosses to sections or inserting dividers
- [ ] Update run-side boss progression display to render section headers/dividers

View File

@@ -1,18 +0,0 @@
---
# nuzlocke-tracker-3lfw
title: Configure Nginx Proxy Manager for nuzlocke-tracker
status: completed
type: task
priority: normal
created_at: 2026-02-09T15:30:50Z
updated_at: 2026-02-10T08:44:18Z
parent: nuzlocke-tracker-ahza
blocking:
- nuzlocke-tracker-vpn5
---
Add a proxy host entry in Nginx Proxy Manager on Unraid to route LAN traffic to the app.
- Add a proxy host (e.g., `nuzlocke.local`) pointing to the frontend/API containers
- Configure appropriate ports and forwarding rules
- Test access from other devices on the LAN

View File

@@ -1,24 +0,0 @@
---
# nuzlocke-tracker-44ps
title: Replace ESLint/Prettier with oxlint/oxfmt
status: completed
type: task
priority: normal
created_at: 2026-02-16T19:13:49Z
updated_at: 2026-02-16T19:24:15Z
parent: nuzlocke-tracker-a5es
---
Global standards require oxlint and oxfmt instead of ESLint and Prettier. This involves:
## Checklist
- [ ] Install oxlint as devDependency
- [ ] Configure oxlint with typescript, import, unicorn plugins
- [ ] Install oxfmt as devDependency (or use oxlint --fix for formatting)
- [ ] Remove ESLint and all ESLint plugins/configs
- [ ] Remove Prettier and eslint-config-prettier
- [ ] Update package.json scripts
- [ ] Update pre-commit hooks
- [ ] Update CI workflow
Note: oxfmt may not be stable yet — check current status before proceeding.

View File

@@ -1,17 +0,0 @@
---
# nuzlocke-tracker-48ds
title: Database backup strategy
status: completed
type: task
priority: normal
created_at: 2026-02-09T15:30:55Z
updated_at: 2026-02-10T10:55:15Z
parent: nuzlocke-tracker-ahza
---
Set up a simple scheduled backup for the production PostgreSQL database.
- Create a backup script using `pg_dump` that runs inside or against the PostgreSQL container
- Set up a cron job on Unraid to run the backup on a schedule (e.g., daily)
- Store backups in a designated location on Unraid with rotation (keep last N backups)
- Document the restore procedure

View File

@@ -0,0 +1,53 @@
---
# nuzlocke-tracker-49xj
title: Overhaul Nuzlocke Rules System
status: completed
type: epic
priority: normal
created_at: 2026-02-20T13:22:23Z
updated_at: 2026-02-21T11:23:31Z
---
Audit and overhaul the nuzlocke rules configuration. The current rules are a flat collection of boolean settings, some of which don't meaningfully affect tracker behavior. This epic cleans up existing rules and adds new rules for popular variants with actual tracker logic.
## Scope
### Rules to REMOVE (5)
These rules either define what a nuzlocke is (always true) or don't affect tracker behavior at all:
- `firstEncounterOnly` — implicit; it's a nuzlocke tracker
- `permadeath` — implicit; it's a nuzlocke tracker
- `nicknameRequired` — not enforced or tracked
- `setModeOnly` — not enforced or tracked
- `postGameCompletion` — not enforced or tracked
### Rules to KEEP (5)
These actively affect tracker logic:
- `duplicatesClause` — used in encounter creation and bulk randomization
- `shinyClause` — used in encounter creation (bypass route-lock)
- `pinwheelClause` — used for zone-based encounter logic
- `hardcoreMode` — used in BossDefeatModal (auto-win, 1 attempt)
- `levelCaps` — displayed in sticky bar on encounters page
### New rules to ADD (4)
These are boolean flags with real tracker logic:
- `egglocke` — all caught Pokemon are replaced with traded eggs. When enabled, encounter Pokemon selection should allow picking from ALL Pokemon (not just the game's regional dex), similar to the admin panel encounter creation / boss team creation flow.
- `wonderlocke` — all caught Pokemon are Wonder Traded away. Same as egglocke: encounter Pokemon selection allows picking from ALL Pokemon.
- `randomizer` — the run uses a randomized ROM. Same behavior: encounter Pokemon selection allows picking from ALL Pokemon since the dex is randomized.
- `giftClause` — in-game gift Pokemon are free and do not count against the area's encounter limit. When enabled, gift-origin encounters should bypass the route-lock check (similar to how shinyClause bypasses it for shinies).
### Complex rules (need design work)
These need more complex logic and are tracked as draft sub-tasks:
- Type Restrictions (Monolocke) — bs0y
- Team Size Limit — fv7w
- Static/Legendary Clause — knnc
## Children
Work is tracked in sub-tasks:
- **o7r8** — Remove unused nuzlocke rules
- **fitk** — Add egglocke, wonderlocke, and randomizer rules
- **sij8** — Add gift clause rule
- **bs0y** — Add type restriction rules (monolocke) *(draft)*
- **fv7w** — Add team size limit rule *(draft)*
- **knnc** — Add static/legendary clause rule *(draft)*

View File

@@ -0,0 +1,25 @@
---
# nuzlocke-tracker-4a6i
title: Replace CI pipeline with test suite
status: completed
type: task
priority: normal
created_at: 2026-02-21T13:01:01Z
updated_at: 2026-02-21T13:10:15Z
---
Replace the current `.github/workflows/ci.yml` with a workflow that runs the actual test suites. The existing jobs (lint, format, type check) are already enforced by pre-commit hooks (prek), so CI should focus on test execution instead.
## Context
- **Backend integration tests**: pytest with `TEST_DATABASE_URL` pointing at a postgres service container. Default URL: `postgresql+asyncpg://postgres:postgres@localhost:5433/nuzlocke_test`. Tests live in `backend/tests/`.
- **Frontend unit tests**: vitest (`npm run test -- --run`). No external services needed.
- **E2e tests**: Playwright. `e2e/global-setup.ts` uses `docker compose -p nuzlocke-test -f docker-compose.test.yml up -d --build` to start a test API + DB, then seeds data via the API. `playwright.config.ts` spins up `npm run dev` as the webServer. Need to install Chromium via `npx playwright install --with-deps chromium`.
## Checklist
- [x] Add `backend-tests` job: postgres service container (image postgres:16-alpine, user/pass/db matching conftest defaults), install deps with `uv`, run `pytest backend/tests/ -q`
- [x] Add `frontend-tests` job: node 24, `npm ci` in `frontend/`, run `npm run test -- --run`
- [x] Add `e2e-tests` job: install Docker Compose, install Playwright + Chromium deps, run `npx playwright test` from `frontend/`; upload HTML report as artifact on failure
- [x] Keep the `actions-lint` job (actionlint + zizmor); remove `backend-lint` and `frontend-lint` jobs
- [x] Pin all action versions to SHA with version comments; pass `zizmor` audit

View File

@@ -1,30 +0,0 @@
---
# nuzlocke-tracker-4c31
title: Backend Setup & Framework
status: completed
type: task
priority: normal
created_at: 2026-02-04T15:46:49Z
updated_at: 2026-02-04T16:10:13Z
parent: nuzlocke-tracker-f5ob
blocking:
- nuzlocke-tracker-l7e3
- nuzlocke-tracker-bkhs
---
Set up the backend API project with chosen framework and structure.
## Checklist
- [x] Choose backend framework (Node.js/Express, Fastify, NestJS, Go, Python/FastAPI, etc.) - **Python/FastAPI**
- [x] Initialize backend project
- [x] Set up TypeScript (if applicable) - N/A (Python with type hints via Pydantic)
- [x] Configure linting and formatting - **ruff**
- [x] Set up project structure (routes, controllers, services, models)
- [x] Add environment configuration (.env handling) - **pydantic-settings**
- [x] Set up development server with hot reload - **uvicorn --reload**
- [x] Add basic health check endpoint - `/health` and `/`
## Considerations
- Should match team's expertise
- Consider ease of deployment
- TypeScript preferred for type safety with frontend

View File

@@ -1,44 +0,0 @@
---
# nuzlocke-tracker-4ph2
title: Fix and complete route ordering for all games
status: completed
type: task
priority: normal
created_at: 2026-02-11T13:55:09Z
updated_at: 2026-02-11T14:21:24Z
parent: nuzlocke-tracker-rzu4
---
Audit existing route ordering and add missing ordering for all games in `route_order.json`. Use Bulbapedia walkthrough/appendix pages as the primary reference for game progression order.
## Approach:
- Reference Bulbapedia walkthrough pages (e.g. `Appendix:FireRed_and_LeafGreen_walkthrough`) to determine correct progression order
- Cross-reference route names from walkthroughs against actual route names in encounter data files
- Consider building a script to semi-automate this (scrape walkthrough pages → match against encounter data → generate ordering)
## Games to fix (existing but incorrect):
- [x] Platinum — fixed ordering to start with Twinleaf Town, matched names to encounter data
## Games to add ordering for:
- [x] Diamond/Pearl (separate ordering, slight differences from Platinum)
- [x] Black/White
- [x] Black 2/White 2
- [x] X/Y
- [x] Sun/Moon
- [x] Ultra Sun/Ultra Moon (separate ordering, has additional areas)
- [x] Sword/Shield
- [x] Brilliant Diamond/Shining Pearl (separate ordering with Grand Underground)
- [x] Scarlet/Violet
- [x] Legends Arceus
- [x] Legends Z-A
## Games to audit (existing, likely correct):
- [x] FireRed/LeafGreen (and aliases: Red/Blue, Yellow, Let's Go) — rewritten with correct encounter data names
- [x] HeartGold/SoulSilver (and aliases: Gold/Silver, Crystal) — rewritten with correct encounter data names
- [x] Emerald (and aliases: Ruby/Sapphire, ORAS) — rewritten with correct encounter data names
## Notes:
- Route names must match exactly what's in the encounter data files (case-sensitive)
- "Starter" should be the first entry for each game
- Post-game areas placed after main story areas
- Replaces beans nuzlocke-tracker-6lud and nuzlocke-tracker-2b4r

View File

@@ -1,26 +0,0 @@
---
# nuzlocke-tracker-55kp
title: Docker Development Environment Setup
status: completed
type: task
priority: normal
created_at: 2026-02-04T16:07:25Z
updated_at: 2026-02-04T16:13:04Z
parent: nuzlocke-tracker-f5ob
---
Set up Docker and docker-compose for local development environment.
## Checklist
- [x] Create Dockerfile for backend API
- [x] Create Dockerfile for frontend (if needed)
- [x] Create docker-compose.yml with services (api, db, frontend)
- [x] Add PostgreSQL service configuration
- [x] Configure volume mounts for hot reload
- [x] Add environment variable handling
- [x] Create .dockerignore files
- [x] Document usage in README
## Notes
- Should support hot reload for development
- Database data should persist between restarts

View File

@@ -1,31 +0,0 @@
---
# nuzlocke-tracker-5bez
title: Non-evolution form changes
status: completed
type: feature
priority: normal
created_at: 2026-02-07T13:40:00Z
updated_at: 2026-02-08T11:57:36Z
---
Some Pokemon can change form without evolving, using items or abilities. These form changes affect types, stats, and appearance but are not part of the evolution chain.
## Examples
- **Oricorio**: Changes form (Baile/Pom-Pom/Pa'u/Sensu) by using nectar items from different islands. Each form has a different type (Fire, Electric, Psychic, Ghost + Flying).
- **Darmanitan**: Has a Zen Mode ability that changes it to Darmanitan (Zen) in battle (Fire/Psychic). Galarian Darmanitan Zen Mode is Ice/Fire.
- **Rotom**: Changes form by interacting with appliances (Heat/Wash/Frost/Fan/Mow), each with different secondary types.
- **Shaymin**: Changes between Land and Sky forme using the Gracidea flower.
- **Tornadus/Thundurus/Landorus**: Incarnate vs Therian forms via the Reveal Glass.
- **Hoopa**: Confined vs Unbound via the Prison Bottle.
## Scope
This is lower priority than basic form support (bean f44d) and submodule update (bean 6aje). It matters for tracking because a player might catch an Oricorio in one form and change it to another — the tracker should reflect the current form's types.
## Design considerations
- Should the tracker allow manually changing a caught Pokemon's form?
- Or should it just track the form as encountered and leave it static?
- How to represent form-change items/methods in the data model?
- This may not need seed data support — could be a manual UI action on a caught Pokemon

View File

@@ -1,28 +0,0 @@
---
# nuzlocke-tracker-5o1v
title: Improve encounter method input in route encounter form
status: completed
type: feature
priority: normal
created_at: 2026-02-08T19:06:10Z
updated_at: 2026-02-08T19:17:14Z
parent: nuzlocke-tracker-iu5b
---
Replace the free-text encounter method input in the route encounter form (RouteEncounterFormModal) with a smarter selector that leverages the known encounter methods already defined in the codebase.
## Current behavior
- The encounter method field in RouteEncounterFormModal is a plain `<input type="text">` with a placeholder "e.g. Walking, Surfing, Fishing"
- Easy to introduce typos or inconsistent naming (e.g. "walking" vs "walk" vs "Grass")
- The app already has a well-defined set of encounter methods in `EncounterMethodBadge.tsx` with METHOD_CONFIG and METHOD_ORDER (starter, gift, fossil, trade, walk, headbutt, surf, rock-smash, old-rod, good-rod, super-rod)
- The backend stores this as a `String(30)` column, so it's not strictly enum-constrained
## Desired behavior
- Replace the free-text input with a dropdown/select that lists the known encounter methods from METHOD_ORDER, using the human-readable labels from getMethodLabel()
- Include an "Other" option that reveals a text input for custom methods not in the predefined list
- When editing an existing encounter, pre-select the correct method
- Consider showing the colored badge preview next to each option for visual consistency with how methods appear elsewhere in the app
## Files
- `frontend/src/components/admin/RouteEncounterFormModal.tsx` — replace the text input with new selector
- `frontend/src/components/EncounterMethodBadge.tsx` — export METHOD_CONFIG or add a helper to get the list of known methods

View File

@@ -1,66 +0,0 @@
---
# nuzlocke-tracker-5tac
title: Enable naming generator for Genlockes
status: completed
type: task
priority: normal
created_at: 2026-02-11T21:14:21Z
updated_at: 2026-02-14T08:52:16Z
---
## Overview
Genlockes are multiple nuzlocke runs played back-to-back. Currently, naming scheme selection is only available per-run, meaning genlocke runs don't get naming schemes at all (they're created automatically during genlocke creation and leg advancement). This task adds genlocke-level naming scheme selection and lineage-aware name suggestions.
## Key Behaviors
### 1. Genlocke-Level Naming Scheme
- When creating a genlocke, the user selects a naming scheme (same categories as standalone runs)
- This scheme is stored on the `Genlocke` model and automatically applied to every leg's `NuzlockeRun`
- Both the initial run (created in `create_genlocke`) and subsequent runs (created in `advance_leg`) inherit the genlocke's naming scheme
### 2. Name Suggestions (Current Leg Only)
- Duplicate name checking stays scoped to the current run (already the case)
- Transferred pokemon carry their nicknames forward, so they naturally occupy names in the current run's used-name set
### 3. Lineage-Aware Name Suggestions (Roman Numerals)
- When catching a pokemon in a genlocke leg (leg 2+), the system checks if any pokemon from the same **evolution family** was caught in a previous leg
- If so, the original nickname is suggested with a roman numeral suffix (e.g., "Heracles II", "Heracles III")
- The numeral represents the Nth distinct leg where this evolution family was originally caught (not transferred)
- Leg 1: Magikarp → "Heracles" (no numeral, first appearance)
- Leg 2: Magikarp or Gyarados caught → suggest "Heracles II"
- Leg 3: Magikarp caught again → suggest "Heracles III"
- Transferred pokemon don't count as new appearances (they're the same individual)
- The "base name" is taken from the first original encounter of that family across all legs
- The lineage suggestion appears as a **priority suggestion** alongside regular naming scheme suggestions
- The user can always choose a different name
### 4. How the API Changes
- `GET /runs/{run_id}/name-suggestions` gains an optional `pokemon_id` query param
- When `pokemon_id` is provided AND the run belongs to a genlocke:
- Determine the pokemon's evolution family
- Query previous legs' encounters (excluding transfer-target encounters) for matching family members
- If matches found: compute the roman numeral and prepend "{base_name} {numeral}" to the suggestions list
- Regular naming scheme suggestions are returned as before
## Checklist
### Backend
- [x] Add `naming_scheme` column to `genlockes` table (Alembic migration)
- [x] Update `Genlocke` model with `naming_scheme: Mapped[str | None]`
- [x] Update `GenlockeCreate` schema to accept optional `naming_scheme: str | None`
- [x] Update `GenlockeResponse` and `GenlockeDetailResponse` to include `naming_scheme`
- [x] Update `create_genlocke` endpoint: pass `naming_scheme` to the first leg's `NuzlockeRun`
- [x] Update `advance_leg` endpoint: pass the genlocke's `naming_scheme` to the new leg's `NuzlockeRun`
- [x] Add roman numeral helper function (e.g., in `services/naming.py`)
- [x] Update `get_name_suggestions` endpoint to accept optional `pokemon_id` param
- [x] Implement lineage lookup: when in genlocke context with `pokemon_id`, query prior legs for evolution family matches (excluding transfers) and compute suggestion with roman numeral
- [ ] Add tests for lineage-aware name suggestions
### Frontend
- [x] Update `CreateGenlockeInput` type to include `namingScheme?: string | null`
- [x] Add naming scheme selector to genlocke creation wizard (in the Rules step or as a new step)
- [x] Update `GenlockeResponse` / `GenlockeDetailResponse` types to include `namingScheme`
- [x] Update `EncounterModal` to pass selected `pokemonId` to name suggestions API when in genlocke context
- [x] Update `getNameSuggestions` API client to accept optional `pokemonId` param
- [x] Display lineage suggestion prominently in the suggestions UI (e.g., first pill with distinct styling)

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-5uoz
title: Fix webp sprites not loading in production nginx
status: completed
type: bug
priority: normal
created_at: 2026-02-11T12:21:29Z
updated_at: 2026-02-11T12:24:02Z
---
Sprites (.webp) don't load in prod while .png images work fine. The files are in the container but nginx/proxy isn't serving them correctly. Fix by adding explicit webp MIME type to nginx config.

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-5wsn
title: Add run management to admin panel
status: completed
type: feature
priority: normal
created_at: 2026-02-08T09:53:01Z
updated_at: 2026-02-08T09:53:25Z
---
Create AdminRuns page with table listing all runs (name, game, status, started date) with delete functionality. Wire it into routing and admin navigation.

View File

@@ -1,37 +0,0 @@
---
# nuzlocke-tracker-66hg
title: Auto-select boss team variant based on starter choice
status: completed
type: feature
priority: normal
created_at: 2026-02-08T20:21:40Z
updated_at: 2026-02-08T20:34:35Z
---
When a run's starter Pokemon is known, automatically match it against boss battle condition labels (e.g., "Chose Bulbasaur") and pre-select the matching variant instead of showing the pill selector.
## Context
Currently, bosses with variant teams (condition_label) display a pill selector on the run page (BossDefeatModal and RunEncounters BossTeamPreview) so the user can manually toggle between team variants. However, if the run's starter choice is known, the correct variant can be inferred automatically.
## Behavior
- If the run has a starter Pokemon recorded, check each variant's condition label for a match (e.g., starter name "Bulbasaur" matches condition label "Chose Bulbasaur")
- If exactly one condition label matches, auto-select that variant and hide the pill selector entirely
- If no match is found (or the run has no starter), fall back to showing the pill selector as today
- Matching should be case-insensitive and check if the starter name appears anywhere in the condition label (substring match)
## Affected Components
- `BossDefeatModal` — auto-select variant, hide pills when matched
- `BossTeamPreview` in `RunEncounters` — same logic
- May need to check how/where the run's starter Pokemon is stored and accessible
## Checklist
- [x] Determine how the starter Pokemon is stored/accessible from the run data
- [x] Add matching logic to find the right variant from condition labels
- [x] Update BossDefeatModal to auto-select and hide pills when starter matches
- [x] Update BossTeamPreview in RunEncounters with same logic
- [ ] Test with variant bosses where starter matches a condition
- [ ] Test fallback behavior when no starter is set or no match found

View File

@@ -1,30 +0,0 @@
---
# nuzlocke-tracker-6aje
title: Update PokeAPI data submodule to latest version
status: completed
type: task
priority: high
created_at: 2026-02-06T10:53:45Z
updated_at: 2026-02-07T14:35:41Z
---
The local PokeAPI data repository we use as a submodule is outdated. It's missing data for newer Pokemon forms and potentially newer games.
## Why this is important now
After adding form support to seeding (bean f44d), we discovered that the submodule is missing many form entries. Currently only 16 forms are included — all from Gen 7 (Alolan + Oricorio/Lycanroc). Missing forms include:
- **Galarian forms** (Gen 8): Galarian Meowth, Ponyta, Rapidash, Slowpoke, Farfetch'd, Weezing, Mr. Mime, Corsola, Zigzagoon, Linoone, Darumaka, Darmanitan, Stunfisk, Yamask
- **Hisuian forms** (Gen 8/Legends Arceus): Hisuian Growlithe, Voltorb, Typhlosion, Samurott, Decidueye, Zorua, Zoroark, Braviary, Goodra, Avalugg, Sneasel, Lilligant, Qwilfish, Sliggoo
- **Paldean forms** (Gen 9): Paldean Wooper, Tauros
- **Other missing forms**: Basculin (White-Striped), regional bird variants, etc.
These forms have different types and stats from their base species and appear in encounter data for their respective games. Without them in the submodule, the seeding script can't create Pokemon records for them.
## Action
- Check for updates to the PokeAPI data repository
- Update the submodule to the latest version
- Re-run fetch_pokeapi.py to regenerate seed data
- Add any new games to VERSION_GROUPS in fetch_pokeapi.py
- Verify that form count increases significantly after update

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-6kux
title: 'Admin Panel: Tabs for Routes/Bosses + Boss Export'
status: completed
type: feature
priority: normal
created_at: 2026-02-08T10:49:47Z
updated_at: 2026-02-08T10:51:25Z
---
Add tabbed UI to AdminGameDetail (Routes/Bosses tabs) and boss battle export endpoint

View File

@@ -1,31 +0,0 @@
---
# nuzlocke-tracker-6lud
title: Audit and fix route ordering for Gen 1-4 games
status: scrapped
type: task
priority: normal
created_at: 2026-02-10T08:59:16Z
updated_at: 2026-02-11T13:54:47Z
parent: nuzlocke-tracker-rzu4
---
Review the existing route ordering for Gen 1-4 games that already have ordering defined. The current ordering may not accurately reflect typical game progression.
## Games to audit:
- [ ] FireRed/LeafGreen (and aliased: Red/Blue, Yellow, Let's Go)
- [ ] HeartGold/SoulSilver (and aliased: Gold/Silver, Crystal)
- [ ] Emerald (and aliased: Ruby/Sapphire, Omega Ruby/Alpha Sapphire)
- [ ] Platinum
## What to check:
- Routes are in correct progression order (match typical walkthrough)
- No routes are missing from the ordering
- No routes are listed that don't exist in the encounter data
- Aliases make sense (e.g. Red/Blue may have slightly different progression from FR/LG)
- Post-game areas are placed after main story areas
- Sub-areas (children) inherit parent ordering correctly
## Notes:
- Cross-reference with Bulbapedia walkthrough pages
- The Platinum ordering currently starts with cities and mines rather than the starting town/routes — this may be incorrect
- Diamond/Pearl currently aliases to nothing — check if it should alias to Platinum or needs its own ordering

View File

@@ -1,12 +0,0 @@
---
# nuzlocke-tracker-6r4z
title: 'Admin table improvements: Routes and Bosses'
status: completed
type: task
priority: normal
created_at: 2026-02-13T13:01:55Z
updated_at: 2026-02-13T13:06:08Z
---
1. Routes table: add column for 'pinwheel close' and a column for quicklink to encounters
2. Bosses table: add column for Position after route column, ideally as an inline dropdown for mass editing

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-704x
title: Run progression dividers (main story / endgame)
status: completed
type: feature
priority: normal
created_at: 2026-02-08T13:46:12Z
updated_at: 2026-02-08T13:48:28Z
---
Add section field to boss battles to enable visual dividers between game progression phases (Main Story, Endgame, etc.) in both admin and run views.

View File

@@ -1,20 +0,0 @@
---
# nuzlocke-tracker-765i
title: Update CLAUDE.md with branching rules
status: completed
type: task
priority: normal
created_at: 2026-02-09T15:30:38Z
updated_at: 2026-02-10T10:49:56Z
parent: nuzlocke-tracker-ahza
blocking:
- nuzlocke-tracker-3c9l
---
Once the branching structure is in place, add instructions to CLAUDE.md enforcing the branching strategy:
- Never commit directly to `main`
- Day-to-day work happens on `develop`
- New work is done on `feature/*` branches off `develop`
- Merge flow: `feature/*``develop``main`
- `main` is always production-ready

View File

@@ -1,27 +0,0 @@
---
# nuzlocke-tracker-7jba
title: Review and complete special encounters for all games
status: completed
type: task
priority: normal
created_at: 2026-02-10T08:59:24Z
updated_at: 2026-02-11T13:52:06Z
parent: nuzlocke-tracker-rzu4
---
Only starters were missing from the encounter data. Gifts, trades, and fossils are already present in the per-game encounter JSON files.
The `special_encounters.json` file previously contained starters, gifts, and fossils for Gen 1-3 — but only the starters were actually needed (the rest was redundant). Additionally, starters were missing for Gen 4+ games. Yellow and Let's Go were incorrectly aliased to firered-leafgreen (wrong starters).
## Checklist:
- [x] Remove non-starter entries from special_encounters.json
- [x] Add Yellow starter (Pikachu) — was incorrectly aliased to firered-leafgreen
- [x] Add Let's Go starters (Pikachu, Eevee) — was incorrectly aliased to firered-leafgreen
- [x] Add Gen 4 starters (Diamond/Pearl/Platinum/BD/SP)
- [x] Add Gen 5 starters (Black/White/B2/W2)
- [x] Add Gen 6 starters (X/Y)
- [x] Add Gen 7 starters (Sun/Moon/USUM)
- [x] Add Gen 8 starters (Sword/Shield)
- [x] Add Gen 9 starters (Scarlet/Violet)
- [x] Add Legends Arceus starters
- [x] Add Legends Z-A starters

View File

@@ -1,37 +0,0 @@
---
# nuzlocke-tracker-8fcj
title: Frontend API Integration
status: completed
type: task
priority: normal
created_at: 2026-02-04T15:44:42Z
updated_at: 2026-02-05T13:55:52Z
parent: nuzlocke-tracker-f5ob
blocking:
- nuzlocke-tracker-uw2j
- nuzlocke-tracker-0q8f
- nuzlocke-tracker-hm6t
- nuzlocke-tracker-8tuw
---
Implement frontend services to communicate with the backend API.
## Checklist
- [x] Create API client/service layer
- [x] Implement API calls for:
- [x] Fetch available games
- [x] Fetch routes for a game
- [x] Fetch Pokémon data
- [x] Create/update/delete Nuzlocke runs
- [x] Create/update encounters
- [x] Update Pokémon status
- [x] Add loading states and error handling
- [x] Add retry logic for failed requests
## Technical Notes
- Using native `fetch` via `src/api/client.ts` wrapper
- Using TanStack Query for caching, loading states, and retry
- All API responses typed with TypeScript
- Vite dev proxy configured for `/api` → backend
- Query hooks in `src/hooks/` for each domain (games, pokemon, runs, encounters)
- Mutations auto-invalidate relevant query caches

View File

@@ -1,28 +0,0 @@
---
# nuzlocke-tracker-8tuw
title: Run Dashboard/Overview
status: completed
type: task
priority: normal
created_at: 2026-02-04T15:44:38Z
updated_at: 2026-02-05T14:16:36Z
parent: nuzlocke-tracker-f5ob
---
Create the main dashboard showing the current state of the Nuzlocke run.
## Checklist
- [x] Show run statistics:
- [x] Total encounters (caught/failed/skipped)
- [x] Total deaths
- [x] Routes completed
- [x] Quick navigation to:
- [x] Route list / encounter tracking
- [x] Graveyard (fallen Pokémon)
- [x] Show active rules as badges/icons
- [x] Display game name and run start date
## UX Considerations
- This is the home screen users return to most
- Keep it clean and informative at a glance
- Easy access to add new encounters

View File

@@ -1,40 +0,0 @@
---
# nuzlocke-tracker-8w9s
title: Gauntlet rule option for genlockes
status: completed
type: feature
priority: normal
created_at: 2026-02-08T19:15:43Z
updated_at: 2026-02-09T09:05:12Z
parent: nuzlocke-tracker-25mh
---
Add the **Retire HoF** (aka Gauntlet) rule as a genlocke-specific rule option. When enabled, Pokemon that enter the Hall of Fame at the end of a leg are NOT transferred to the next game — instead, they and their entire evolutionary families are added to a cumulative dupe list for all subsequent legs.
## Behavior
- Toggled as a genlocke-specific rule during genlocke creation (step 3 of the wizard)
- Mutually exclusive with "Keep HoF" — you pick one or the other
- When a leg is completed:
- Surviving HoF Pokemon are marked as "retired" rather than being available for transfer
- Their evolutionary families (full chain: e.g., Charmander/Charmeleon/Charizard) are added to a cumulative dupe list
- In subsequent legs, the duplicates clause treats these families as already caught/dead
- The cumulative dupe list grows with each completed leg, making later legs increasingly restrictive
- The genlocke overview page should display the cumulative retired/duped families
## Dependencies
- Requires the genlocke creation wizard (to set the rule)
- Requires the leg progression system (to trigger retirement on leg completion)
- Should integrate with the existing duplicates clause enforcement in the encounter system
## Notes
- This is a popular variant that increases difficulty by forcing entirely new Pokemon each generation
- The dupe list should be visible somewhere in the genlocke dashboard so the player knows which families are off-limits
## Checklist
- [x] Add a `retireHoF` boolean (or equivalent) to the genlocke rules JSONB schema
- [x] On leg completion with Retire HoF enabled: resolve the full evolutionary families of all surviving HoF Pokemon
- [x] Store the cumulative retired families list (could be a JSONB field on the Genlocke, or derived from completed legs)
- [x] Implement `GET /api/v1/genlockes/{id}/retired-families` — return the list of retired evolutionary families with which leg they were retired in
- [x] Integrate with the encounter system's duplicates clause: when logging an encounter in a genlocke leg, check the cumulative retired list and flag duplicates
- [ ] Build a "Retired Families" display on the genlocke overview page showing all off-limits Pokemon with their sprites
- [x] Ensure the creation wizard's genlocke rules step correctly toggles between Keep HoF and Retire HoF

View File

@@ -1,29 +0,0 @@
---
# nuzlocke-tracker-94hx
title: Add sort options to run team overview
status: completed
type: feature
priority: normal
created_at: 2026-02-09T10:03:49Z
updated_at: 2026-02-09T11:09:33Z
---
The Active Team / Final Team section on the run dashboard (`RunDashboard.tsx`) currently displays Pokemon in whatever order encounters arrive from the backend — there is no explicit sorting. Add a sort dropdown so the user can choose how their team is ordered.
## Sort options
- **Route order** (default) — sort by the route's `order` field, matching game progression
- **Catch level** — sort by `catchLevel`, ascending
- **Species name** — sort alphabetically by the display Pokemon's name (accounting for evolutions via `currentPokemon`)
- **National Dex** — sort by the display Pokemon's `nationalDex` number
## Scope
- Frontend-only change — all data is already available in the `EncounterDetail` objects
- Add a small sort control (dropdown or segmented buttons) above the team grid
- Persist the selected sort in component state (no need for localStorage)
- Apply the same sort options to both the Active Team and Graveyard sections
## Checklist
- [x] Add sort state and sort logic to `RunDashboard.tsx`
- [x] Add sort dropdown/control above the team grid
- [x] Apply sorting to both `alive` and `dead` encounter arrays
- [x] Verify sort works correctly with evolved Pokemon (use `currentPokemon ?? pokemon` for name/dex)

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-94v0
title: Handle Pokemon Forms properly
status: scrapped
type: task
priority: normal
created_at: 2026-02-05T17:47:30Z
updated_at: 2026-02-07T13:21:32Z
---
Some pokemon have different forms, either regional forms or based on other criteria. They behave differently, are different encounters and might have different evolutions. This needs to be handled.

View File

@@ -1,10 +1,11 @@
---
# nuzlocke-tracker-9c66
title: Integration tests for Genlockes & Bosses API
status: draft
status: completed
type: task
priority: normal
created_at: 2026-02-10T09:33:26Z
updated_at: 2026-02-10T09:33:26Z
updated_at: 2026-02-21T12:20:37Z
parent: nuzlocke-tracker-yzpb
---
@@ -12,14 +13,14 @@ Write integration tests for the genlocke challenge and boss battle API endpoints
## Checklist
- [ ] Test genlocke CRUD operations (create, list, get, update, delete)
- [ ] Test leg management (add/remove legs to a genlocke)
- [ ] Test Pokemon transfers between genlocke legs
- [ ] Test boss battle CRUD (create, list, update, delete per game)
- [ ] Test boss battle results per run (record win/loss)
- [ ] Test stats endpoint for run statistics
- [ ] Test export endpoint
- [ ] Test error cases (invalid transfers, boss results for wrong game, etc.)
- [x] Test genlocke CRUD operations (create, list, get, update, delete)
- [x] Test leg management (add/remove legs to a genlocke)
- [x] Test Pokemon transfers between genlocke legs
- [x] Test boss battle CRUD (create, list, update, delete per game)
- [x] Test boss battle results per run (record win/loss)
- [x] Test stats endpoint for run statistics
- [x] Test export endpoint
- [x] Test error cases (invalid transfers, boss results for wrong game, etc.)
## Notes

View File

@@ -1,11 +1,11 @@
---
# nuzlocke-tracker-9c8d
title: Rebrand to Another Nuzlocke Tracker (ANT)
status: todo
status: completed
type: task
priority: normal
created_at: 2026-02-10T14:46:09Z
updated_at: 2026-02-10T14:46:56Z
updated_at: 2026-02-17T19:17:22Z
---
Adopt the new branding: **Another Nuzlocke Tracker**, abbreviated **ANT**.
@@ -18,8 +18,8 @@ Adopt the new branding: **Another Nuzlocke Tracker**, abbreviated **ANT**.
## Checklist
- [ ] Update project name in package.json / config files
- [ ] Update page titles, meta tags, and any visible app name references
- [ ] Update README and any documentation with the new name
- [ ] Design or source a Durant-themed logo/icon
- [ ] Update favicon and app icons
- [x] Update project name in package.json / config files
- [x] Update page titles, meta tags, and any visible app name references
- [x] Update README and any documentation with the new name
- [x] Design or source a Durant-themed logo/icon
- [x] Update favicon and app icons

View File

@@ -1,25 +0,0 @@
---
# nuzlocke-tracker-9cx2
title: Drag-and-drop reordering for boss battles
status: completed
type: feature
priority: normal
created_at: 2026-02-08T12:33:18Z
updated_at: 2026-02-08T13:11:50Z
parent: nuzlocke-tracker-iu5b
---
Add drag-and-drop reordering for boss battles, matching the existing pattern used for routes.
## Current behavior
- Boss battles have a manual 'order' field that must be edited individually
- Routes already have drag-and-drop reordering via @dnd-kit
## Desired behavior
- Boss battles in the AdminGameDetail bosses tab support drag-and-drop reordering
- Same visual pattern as routes: drag handle on the left, auto-recalculate order on drop
- Reuse the same @dnd-kit setup already in place for routes
## Files
- frontend/src/pages/admin/AdminGameDetail.tsx — add DnD to bosses tab
- Backend may need a reorder endpoint for bosses (similar to routes reorder)

View File

@@ -1,67 +0,0 @@
---
# nuzlocke-tracker-9ngw
title: Stats Screen
status: completed
type: feature
priority: normal
created_at: 2026-02-07T19:19:40Z
updated_at: 2026-02-07T19:45:55Z
---
A dedicated stats page aggregating data across all runs. Accessible from the main navigation.
## Sections
### 1. Run Overview
- Total runs, active runs, completed runs, failed runs
- Win rate (completed / (completed + failed), excluding active)
- Average run duration (started_at → completed_at for finished runs)
- Pie or bar chart: runs by game (using game.name / game.color)
- Pie or bar chart: runs by region (game.region)
- Pie or bar chart: runs by generation (game.generation)
### 2. Encounter Stats
- Total encounters across all runs
- Breakdown by status: caught / fainted / missed (counts + percentages)
- Catch rate: caught / total encounters
- Average encounters per run
### 3. Pokemon Rankings
- Top N most-caught Pokemon (by pokemon_id frequency across encounters with status=caught), showing sprite + name + count
- Top N most-encountered Pokemon (any status), same display
- Configurable N (default 5, expandable to 10/all)
### 4. Team & Deaths
- Total Pokemon caught, total deaths (caught with faint_level != null)
- Mortality rate: deaths / caught
- Most common death causes (death_cause field, grouped + counted, top 5)
- Average catch level, average faint level
- Type distribution of caught Pokemon (bar chart of pokemon.types, counted across all caught encounters)
## Data Access
### Option A — Frontend-only (compute from existing data)
The list-runs endpoint already returns all runs with encounters. Stats can be computed client-side by fetching all runs. This is simpler but won't scale well with many runs.
### Option B — Dedicated backend endpoint (recommended for scale)
Add `GET /api/stats` that runs aggregate queries server-side and returns pre-computed stats. This is more work but performs better and keeps the frontend thin.
**Recommendation:** Start with Option A for simplicity. If performance becomes an issue, migrate to Option B.
## UI Notes
- New route: `/stats`
- Add "Stats" link to the main nav/sidebar
- Use the existing `StatCard` component for top-level numbers
- Charts: consider a lightweight library (e.g., recharts) or simple CSS bar charts to avoid heavy dependencies
- Responsive grid layout matching the existing design system (dark mode support)
## Checklist
- [x] Add `/stats` route and page component
- [x] Add "Stats" navigation link
- [x] Fetch all runs with encounters (or add backend stats endpoint)
- [x] Run Overview section (counts, win rate, duration)
- [x] Encounter Stats section (caught/fainted/missed breakdown)
- [x] Pokemon Rankings section (top caught, top encountered, expandable)
- [x] Team & Deaths section (mortality, death causes, type distribution)
- [x] Charts for region/generation/type breakdowns
- [x] Responsive layout + dark mode styling

View File

@@ -1,18 +0,0 @@
---
# nuzlocke-tracker-9vny
title: Add Dependabot config
status: completed
type: task
priority: normal
created_at: 2026-02-16T19:13:50Z
updated_at: 2026-02-16T19:24:15Z
parent: nuzlocke-tracker-a5es
---
No .github/dependabot.yml exists. Global standards require Dependabot with 7-day cooldowns and grouped updates.
## Checklist
- [ ] Create .github/dependabot.yml
- [ ] Configure for npm (frontend), pip (backend), and github-actions
- [ ] Set 7-day schedule intervals
- [ ] Group minor/patch updates

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-9z2k
title: Implement non-evolution form changes
status: completed
type: feature
priority: normal
created_at: 2026-02-08T11:51:18Z
updated_at: 2026-02-08T11:52:36Z
---
Add ability to change Pokemon forms (e.g. Rotom appliances, Oricorio nectar forms) without evolving. Mirror existing evolution UI pattern with a new 'Change Form' button in StatusChangeModal.

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-a5es
title: Align repo config with global dev standards
status: completed
type: epic
priority: normal
created_at: 2026-02-16T19:13:24Z
updated_at: 2026-02-16T19:26:23Z
---
Audit found multiple deviations from the global CLAUDE.md development standards. This epic tracks all the fixes needed.

View File

@@ -1,19 +0,0 @@
---
# nuzlocke-tracker-a7q2
title: Handle pokemon evolutions
status: completed
type: feature
priority: normal
created_at: 2026-02-05T14:27:07Z
updated_at: 2026-02-05T18:26:11Z
parent: nuzlocke-tracker-f5ob
---
Add support for pokemon evolutions in the tracker. When a pokemon evolves during a Nuzlocke run, the tracker should reflect the new species while preserving the encounter history (original catch route, catch level, nickname, etc.).
## Implementation
- **Data model**: `Evolution` table with from/to pokemon, trigger, level, item, and condition fields. `Encounter.current_pokemon_id` tracks evolved species separately from original.
- **Seed data**: Evolution chains fetched from PokeAPI submodule data, with an overrides file for manual corrections.
- **API**: `GET /pokemon/{id}/evolutions` returns available evolutions. `PATCH /encounters/{id}` accepts `current_pokemon_id` to record an evolution.
- **Frontend**: "Evolve" button in StatusChangeModal shows available evolutions with trigger details. PokemonCard displays the current (evolved) species with "Originally: {name}" label.

View File

@@ -1,58 +0,0 @@
---
# nuzlocke-tracker-ahza
title: Deployment Strategy
status: completed
type: epic
priority: normal
created_at: 2026-02-09T14:03:53Z
updated_at: 2026-02-10T11:36:07Z
---
Define and implement a deployment strategy for running the nuzlocke-tracker in production on a local Unraid server while keeping laptop/PC as the development environment.
## Context
- **Components:** API (Python/FastAPI), Frontend (Vite/React), PostgreSQL database
- **Dev environment:** Laptop/PC — continue using the existing `docker-compose.yml` for local development
- **Production host:** Unraid server running Docker containers
- **Networking:** LAN-only access, Nginx Proxy Manager already in place on Unraid
- **Orchestration:** Docker Compose for production (matching dev workflow). Deploy via SSH from the dev machine.
## Decided Approach
**Docker Compose + SSH + Gitea (source hosting, container registry)**
1. **Gitea** runs on Unraid behind Nginx Proxy Manager with SSL (e.g., `gitea.nerdboden.de`). It serves as the self-hosted Git remote and container registry.
2. **Images are built on the dev machine** (podman or docker, cross-compiled for linux/amd64) and pushed to Gitea's container registry as **user-level packages** (e.g., `gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest`, `gitea.nerdboden.de/thefurya/nuzlocke-tracker-frontend:latest`).
3. **Production runs docker compose** on Unraid at `/mnt/user/appdata/nuzlocke-tracker/`, pulling images from the Gitea container registry instead of mounting source.
4. **A deploy script** on the dev machine automates the full flow: build images → push to Gitea registry → SCP compose file to Unraid → generate `.env` if missing → SSH to pull images and (re)start containers.
5. **Nginx Proxy Manager** handles routing on the LAN (e.g., `nuzlocke.nerdboden.de` → frontend container, `gitea.nerdboden.de` → Gitea).
6. **Database** uses a bind mount (`./data/postgres`) for persistence on the Unraid disk; migrations run automatically on API container startup.
## Branching Strategy
**`main` + `develop` + feature branches**
- **`main`** — always production-ready. Only receives merges from `develop` when ready to deploy. The deploy script builds from `main`.
- **`develop`** — integration branch for day-to-day work. Features are merged here and tested before promoting to `main`.
- **`feature/*`** — short-lived branches off `develop` for individual features/fixes. Merged back into `develop` via PR or direct merge when complete.
**Workflow:**
1. Create `feature/xyz` from `develop`
2. Work on the feature, commit, merge into `develop`
3. When ready to deploy: merge `develop``main`
4. Run `./deploy.sh` (builds from `main`, pushes to Gitea registry, deploys to Unraid via SSH)
## Checklist
- [x] **Set up branching structure** — create `develop` branch from `main`, establish the `main`/`develop`/`feature/*` workflow
- [x] **Update CLAUDE.md with branching rules** — once the branching structure is in place, add instructions to CLAUDE.md that the branching strategy must be adhered to (always work on feature branches, never commit directly to `main`, merge flow is `feature/*``develop``main`)
- [x] **Configure Gitea container registry** — create an access token with `read:package` and `write:package` scopes, verify `docker login gitea.nerdboden.de` works, test pushing and pulling an image as a user-level package
- [x] **Create production docker-compose file** (`docker-compose.prod.yml`) — uses images from the Gitea container registry, production env vars, no source volume mounts, proper restart policies
- [x] **Create production Dockerfiles (or multi-stage builds)** — ensure frontend is built and served statically (e.g., via the API or a lightweight nginx container), API runs without debug mode
- [x] **Create deploy script**`./deploy.sh` builds images (podman/docker, linux/amd64), pushes to Gitea registry, SCPs compose file, generates `.env` if needed, pulls and starts containers via SSH
- [x] **Configure Nginx Proxy Manager** — add proxy host entries for Gitea and the nuzlocke-tracker frontend/API on the appropriate ports
- [x] **Environment & secrets management** — deploy script auto-generates `.env` with `POSTGRES_PASSWORD` on Unraid if missing; file lives at `/mnt/user/appdata/nuzlocke-tracker/.env`
- [x] **Implement Gitea Actions CI/CD pipeline** — set up Gitea Actions runner on Unraid, create CI workflow (lint/test on `develop`) and deploy workflow (build/push/deploy on `main`); uses GitHub Actions-compatible syntax for portability
- [x] **Database backup strategy** — set up a simple scheduled backup for the PostgreSQL data (e.g., cron + `pg_dump` script on Unraid)
- [x] **Document the deployment workflow** — README or docs covering how to deploy, redeploy, rollback, and manage the production instance

View File

@@ -1,24 +0,0 @@
---
# nuzlocke-tracker-aiw6
title: Create deploy script
status: completed
type: task
priority: normal
created_at: 2026-02-09T15:30:48Z
updated_at: 2026-02-09T17:28:22Z
parent: nuzlocke-tracker-ahza
blocking:
- nuzlocke-tracker-izf6
- nuzlocke-tracker-vpn5
- nuzlocke-tracker-xmyh
- nuzlocke-tracker-jzqz
---
Create a `./deploy.sh` script for the dev machine that automates the full deployment flow.
- Ensure the script runs from `main` branch (or warns if not)
- Build Docker images for API and frontend
- Tag images for the local registry (e.g., `unraid:5000/nuzlocke-api:latest`)
- Push images to the local registry
- Trigger the Portainer webhook to redeploy the stack
- Print status/confirmation of each step

View File

@@ -1,24 +0,0 @@
---
# nuzlocke-tracker-b9oj
title: Implement Dupes Clause & Shiny Clause Enforcement
status: completed
type: feature
priority: normal
created_at: 2026-02-07T20:03:12Z
updated_at: 2026-02-07T20:07:50Z
---
Add enforcement for duplicatesClause and shinyClause rules:
- Dupes Clause: Grey out Pokemon in encounter modal whose evolution family is already caught
- Shiny Clause: Dedicated Shiny Box for bonus shiny catches that bypass route locks
## Checklist
- [x] Migration: Add is_shiny column to encounters table
- [x] Backend model + schema: Add is_shiny field
- [x] Backend: Shiny route-lock bypass in create_encounter
- [x] Backend: Evolution families endpoint (GET /pokemon/families)
- [x] Frontend types + API: Add isShiny fields and fetchPokemonFamilies
- [x] Frontend: Dupes Clause greying in EncounterModal
- [x] Frontend: ShinyEncounterModal + ShinyBox components
- [x] Frontend: RunEncounters orchestration (split encounters, duped IDs, shiny box)
- [x] TypeScript type check passes

View File

@@ -1,35 +0,0 @@
---
# nuzlocke-tracker-bi4e
title: Integrate name suggestions into encounter registration UI
status: completed
type: task
priority: normal
created_at: 2026-02-11T15:56:44Z
updated_at: 2026-02-11T20:48:02Z
parent: nuzlocke-tracker-igl3
---
Show name suggestions in the encounter registration flow so users can pick a nickname with a single click.
## Requirements
- When a user registers a new Pokemon encounter, display 5-10 name suggestions below/near the nickname input
- Each suggestion is a clickable chip/button that fills in the nickname field
- Include a "regenerate" button to get a fresh batch of suggestions
- Only show suggestions if the run has a naming scheme selected
- The nickname input should still be editable for manual entry
## Implementation Notes
- **Data fetching**: Call `GET /api/v1/runs/{run_id}/name-suggestions?count=10` to get suggestions from the backend.
- **Regeneration**: Each call to the endpoint returns a fresh random batch (backend handles exclusion of used names).
- **No dictionary data in frontend**: All suggestion logic lives in the backend.
## Checklist
- [x] Add a name suggestions component (chips/buttons with regenerate)
- [x] Integrate the component into the encounter registration modal/form
- [x] Wire up the backend API endpoint to the component via React Query
- [x] Ensure clicking a suggestion populates the nickname field
- [x] Ensure regenerate fetches a new batch from the API
- [x] Hide suggestions gracefully if no naming scheme is set on the run

View File

@@ -1,40 +0,0 @@
---
# nuzlocke-tracker-bkhs
title: API Endpoints Implementation
status: completed
type: task
priority: normal
created_at: 2026-02-04T15:46:59Z
updated_at: 2026-02-05T13:47:57Z
parent: nuzlocke-tracker-f5ob
blocking:
- nuzlocke-tracker-8fcj
- nuzlocke-tracker-hy41
---
Implement the REST/GraphQL API endpoints for the tracker.
## Checklist
- [x] Reference Data endpoints (read-only for tracker):
- [x] GET /api/v1/games - List all games
- [x] GET /api/v1/games/:id - Get game details with routes
- [x] GET /api/v1/games/:id/routes - List routes for a game
- [x] GET /api/v1/routes/:id/pokemon - List available Pokémon for a route
- [x] GET /api/v1/pokemon/:id - Get Pokémon details
- [x] Run Management endpoints:
- [x] POST /api/v1/runs - Create new run
- [x] GET /api/v1/runs - List all runs
- [x] GET /api/v1/runs/:id - Get run details with encounters
- [x] PATCH /api/v1/runs/:id - Update run (settings, status)
- [x] DELETE /api/v1/runs/:id - Delete a run
- [x] Encounter endpoints:
- [x] POST /api/v1/runs/:id/encounters - Log new encounter
- [x] PATCH /api/v1/encounters/:id - Update encounter (status, nickname)
- [x] DELETE /api/v1/encounters/:id - Remove encounter
- [x] Add request validation
- [x] Add proper error responses
## Notes
- Follow REST conventions
- Return appropriate HTTP status codes
- Include pagination for list endpoints if needed

View File

@@ -1,108 +0,0 @@
---
# nuzlocke-tracker-bs05
title: Build PokeDB.org data import tool
status: completed
type: feature
priority: normal
created_at: 2026-02-10T14:04:11Z
updated_at: 2026-02-11T10:54:04Z
parent: nuzlocke-tracker-rzu4
blocking:
- nuzlocke-tracker-spx3
---
Build a standalone Python tool that converts PokeDB.org's JSON data export into our existing seed JSON format. This replaces PokeAPI as the single source of truth for ALL games (Gen 1-9).
Python was chosen over Go because:
- The backend is already Python, so the team is familiar with it
- We're processing local JSON files — no need for Go's concurrency
- Remains a standalone tool in `tools/import-pokedb/`, not part of the backend
## Data source
PokeDB.org provides a full data export at https://pokedb.org/data-export with JSON downloads:
- `encounters.json` (69MB, 37,724 records) — all encounter data across all games
- `locations.json` — 839 locations
- `location_areas.json` — 2,672 location areas
- `encounter_methods.json` — 73 encounter methods
- `versions.json` — 82 game versions
- `pokemon_forms.json` — Pokemon forms with identifiers
**No scraping required.** Just download the JSON files and process them locally.
**Terms of use:** "Data is provided for educational, research, and non-commercial purposes." Attribution to PokeDB requested.
## Encounter data coverage
Encounter counts by version:
- Sword: 10,160 / Shield: 10,144
- Scarlet: 4,135 / Violet: 4,101
- SoulSilver: 2,492 / HeartGold: 2,475
- Shining Pearl: 2,021 / Brilliant Diamond: 2,013
- Legends Arceus: 1,756
- Black 2: 1,418 / White 2: 1,418
- Crystal: 1,375 / Alpha Sapphire: 1,338 / Platinum: 1,337
- Diamond: 1,292 / Pearl: 1,289 / Silver: 1,284 / Gold: 1,282
- LeafGreen: 987 / FireRed: 985 / White: 981 / Black: 947
- Ultra Moon: 886 / Ultra Sun: 885 / X: 880 / Y: 879
- Emerald: 763 / Let's Go Eevee: 710 / Sun: 709 / Moon: 707
- Sapphire: 707 / Ruby: 707 / Let's Go Pikachu: 690
- Blue: 528 / Red: 526 / Yellow: 496
## Data format details
Each encounter record has:
- `pokemon_form_identifier` — e.g. "pidgey-default", "mr-mime-default"
- `version_identifiers` — array of game version IDs (e.g. ["sword", "shield"])
- `location_area_identifier` — e.g. "route-01-kanto", "axews-eye"
- `encounter_method_identifier` — e.g. "walking-tall-grass", "surfing", "npc-trade"
- `levels` — string like "2 - 4" or "67"
- Rate fields vary by game generation:
- Gen 1/3/6: `rate_overall` (single percentage)
- Gen 2/4: `rate_morning`, `rate_day`, `rate_night` (time-of-day percentages)
- Gen 5: `rate_spring`, `rate_summer`, `rate_autumn`, `rate_winter` (seasonal)
- Gen 8 Sw/Sh: `weather_*_rate` fields (per-weather percentages, e.g. "40%")
- Gen 8 Legends Arceus: `during_*` and `while_*` booleans (time+weather conditions)
- Gen 9 Sc/Vi: `probability_*` fields (overworld probability weights)
- `trade_for` — Pokemon form identifier for NPC trades
- `alpha_levels` — for Legends Arceus alpha encounters
- `visible` — overworld vs hidden encounter
- Max Raid and Tera Raid fields for special encounters
## Subtasks
Work is broken into child task beans:
- [ ] **Set up Python tool scaffold** — project structure, CLI entry point, PokeDB JSON file loading
- [ ] **Build reference data mappings** — pokemon_form → pokeapi_id, location_area → name/region, encounter method mapping
- [ ] **Core encounter processing** — filter by game version, parse levels, handle rate variants, group by location area
- [ ] **Output seed JSON** — produce per-game JSON in existing format, integrate route ordering + special encounters
- [ ] **Validation & full generation** — compare against existing data, run for all games, fix discrepancies
## Encounter method mapping (draft)
PokeDB method → Our seed method:
- `walking-tall-grass`, `walking-*` → "walk"
- `surfing`, `surfing-*` → "surf"
- `fishing-old-rod` → "old-rod"
- `fishing-good-rod` → "good-rod"
- `fishing-super-rod` → "super-rod"
- `fishing` → "fishing"
- `rock-smash` → "rock-smash"
- `headbutt-*` → "headbutt"
- `npc-gift`, `egg`, `revive` → "gift"
- `npc-trade` → "trade"
- `symbol-encounter` → "walk" (overworld, Gen 8+)
- `wanderer` → "walk" (overworld visible)
- `fixed-encounter`, `static-encounter` → "static"
- `swarm` → "swarm"
- `poke-radar` → "pokeradar"
- `dual-slot-mode` → "dual-slot"
- Others: TBD based on relevance
## Notes
- This tool replaces `tools/fetch-pokeapi/` as the primary data source for all games
- Pokemon form identifiers need mapping to pokeapi IDs — may need a fuzzy match since naming conventions differ
- The existing `pokemon.json` has names and pokeapi IDs we can use as a lookup
- S/V probability weights are not percentages — they represent relative spawn weights
- Legends Arceus uses boolean conditions (during_night + while_clear) rather than rates

View File

@@ -0,0 +1,33 @@
---
# nuzlocke-tracker-bs0y
title: Add type restriction rules (monolocke)
status: completed
type: feature
priority: normal
created_at: 2026-02-20T19:56:16Z
updated_at: 2026-02-21T11:22:16Z
parent: nuzlocke-tracker-49xj
---
Restrict team composition to specific types (monolocke and similar variants).
## Design Decisions
**Type selection:** Multi-select from the 18 standard Pokemon types. A monolocke selects one type; multi-type variants (e.g., "fire and water only") select multiple.
**Dual-type matching:** A Pokemon qualifies if at least one of its types is in the allowed set. This matches the community standard for monolocke — e.g., in a Fire monolocke, Charizard (Fire/Flying) is allowed because it has Fire.
**Enforcement:** Soft enforcement via UI warnings, not hard blocks. The tracker warns when a caught Pokemon doesn't match the allowed types but doesn't prevent logging it. Reason: players sometimes need to use HM slaves or have edge cases the tracker shouldn't block.
**Data model:** Add `allowedTypes: string[]` to `NuzlockeRules`. Empty array means no restriction (disabled). This keeps it in the existing JSONB rules blob on the run.
**UI:** Add a "Type Restrictions" section to `RulesConfiguration` with a multi-select type picker (reuse the type badge styling from `TypeBadge`). Show a warning badge on encounters that don't match.
## Checklist
- [x] Add `allowedTypes: string[]` to `NuzlockeRules` interface (default: `[]`)
- [x] Add a new `BooleanRuleKeys` type to `RuleDefinition` to exclude non-boolean fields
- [x] Add type multi-select UI to `RulesConfiguration` (shown when allowedTypes toggle is on)
- [x] Show warning indicator on `PokemonCard` and encounter list for Pokemon that don't match allowed types
- [x] Add `RuleBadge` display for active type restriction (e.g., "Monolocke: Fire")
- [x] Update `RuleBadges` to handle `allowedTypes` separately from boolean rules

View File

@@ -1,36 +0,0 @@
---
# nuzlocke-tracker-c6ly
title: Build name suggestion engine
status: completed
type: task
priority: normal
created_at: 2026-02-11T15:56:44Z
updated_at: 2026-02-11T20:44:27Z
parent: nuzlocke-tracker-igl3
blocking:
- nuzlocke-tracker-bi4e
---
Build the backend service and API endpoint that picks random name suggestions from the dictionary based on a selected naming scheme.
## Requirements
- Given a category and a run ID, return 5-10 unique suggestions
- The engine queries the run's existing encounter nicknames and excludes them from suggestions
- Support regeneration (return a fresh batch, avoiding previously shown suggestions where possible)
- Handle edge case where category is nearly exhausted gracefully (return fewer suggestions)
## Implementation Notes
- **Backend service**: A Python module that loads the dictionary JSON, filters by category, excludes used names, and picks random suggestions.
- **API endpoint**: `GET /api/v1/runs/{run_id}/name-suggestions?count=10` — reads the run's `naming_scheme`, fetches encounter nicknames, returns suggestions.
- **No new DB tables needed**: Used names come from `Encounter.nickname` on the run's encounters.
- **Caching**: Load the dictionary once and cache in memory (it's static data).
## Checklist
- [x] Create a service module for name suggestion logic (e.g. `services/name_suggestions.py`)
- [x] Implement dictionary loading with in-memory caching
- [x] Implement random selection from a category with exclusion of used names
- [x] Add API endpoint for fetching suggestions
- [x] Add unit tests for the suggestion logic

View File

@@ -1,29 +0,0 @@
---
# nuzlocke-tracker-cdmx
title: 'Clean up Sword/Shield encounter data: wild Pokemon incorrectly listed as gifts'
status: completed
type: bug
priority: normal
created_at: 2026-02-14T19:56:12Z
updated_at: 2026-02-14T21:15:04Z
---
## Problem
In the Sword and Shield seed data, wild Pokemon on almost all encounter locations are also duplicated as gift encounters. This appears to be an issue with the PokeDB export process.
## Goal
Investigate why the PokeDB export produced duplicate gift entries for wild encounters, then clean up the data so that only genuine gifts are listed as gifts.
## Findings
The PokeDB export duplicated walk encounters as gift encounters on 6 locations in each game. The affected locations were: Route 1 (Galar), Galar Mine, Motostoke Outskirts, Route 6 (Galar), Glimwood Tangle, and Route 7 (Galar). Route 6 also had legitimate fossil gifts (Arctovish, Arctozolt, Dracovish, Dracozolt) which were preserved.
## Checklist
- [x] Investigate the PokeDB export logic to understand why wild Pokemon are being duplicated as gifts
- [x] Identify which encounters are legitimately gifts vs incorrectly tagged
- [x] Remove erroneous gift entries from Sword seed data (39 removed)
- [x] Remove erroneous gift entries from Shield seed data (40 removed)
- [x] Verify remaining gift encounters are accurate

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-ceeh
title: Clean up old seed generation tools
status: completed
type: task
priority: normal
created_at: 2026-02-11T12:52:31Z
updated_at: 2026-02-11T13:55:49Z
---
Remove the old Go fetch-pokeapi tool from tools/fetch-pokeapi/ and update README.md references to point to the new import-pokedb tool instead.

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-cftf
title: Filter out routes with no encounters for active game
status: completed
type: task
priority: normal
created_at: 2026-02-14T14:38:05Z
updated_at: 2026-02-14T14:38:19Z
---
Route orders are per version group, so both games in a pair share the same route list. Routes with no encounters for the active game should be filtered out in the list_game_routes endpoint.

View File

@@ -1,26 +1,31 @@
---
# nuzlocke-tracker-ch77
title: Integration tests for Games & Routes API
status: draft
status: completed
type: task
priority: normal
created_at: 2026-02-10T09:33:13Z
updated_at: 2026-02-10T09:33:13Z
updated_at: 2026-02-21T11:48:10Z
parent: nuzlocke-tracker-yzpb
---
Write integration tests for the games and routes API endpoints in `backend/src/app/api/games.py`.
Write integration tests for the games and routes API endpoints in backend/src/app/api/games.py.
## Key behaviors to test
- Game CRUD: create (201), list, get with routes, update, delete (204)
- Slug uniqueness enforced at create and update (409)
- 404 for missing games
- 422 for invalid request bodies
- Route operations require version_group_id on the game (need VersionGroup fixture via db_session)
- list_game_routes only returns routes with encounters (or parents of routes with encounters)
- Game detail (GET /{id}) returns all routes regardless
- Route create, update, delete, reorder
## Checklist
- [ ] Test CRUD operations for games (create, list, get, update, delete)
- [ ] Test route management within a game (create, list, reorder, update, delete)
- [ ] Test route encounter management (add/remove Pokemon to routes)
- [ ] Test bulk import functionality
- [ ] Test region grouping/filtering
- [ ] Test error cases (404 for missing games, validation errors, duplicate handling)
## Notes
- Use the httpx AsyncClient fixture from the test infrastructure task
- Each test should be independent — use fixtures to set up required data
- Test both success and error response codes and bodies
- [x] Test CRUD operations for games (create, list, get, update, delete)
- [x] Test route management within a game (create, list, update, delete, reorder)
- [x] Test error cases (404, 409 duplicate slug, 422 validation)
- [x] Test list_game_routes filtering behavior (empty routes excluded)
- [x] Test by-region endpoint structure

View File

@@ -1,29 +0,0 @@
---
# nuzlocke-tracker-czeh
title: Improve admin panel UX
status: completed
type: feature
priority: normal
created_at: 2026-02-05T18:28:04Z
updated_at: 2026-02-07T11:59:49Z
parent: nuzlocke-tracker-f5ob
---
Improve the admin panel with better interactions and missing management capabilities.
## Checklist
- [x] Evolution data management:
- [x] List evolutions (searchable/filterable)
- [x] Add new evolution
- [x] Edit evolution details (trigger, level, item, conditions)
- [x] Delete evolution
- [x] Route reordering:
- [x] Replace up/down buttons with drag-and-drop
- [x] Table sorting (Games table):
- [x] Add sortable column support to AdminTable component
- [x] Enable sorting by Region, Generation, and Release Year on Games table
- [x] General UX improvements:
- [x] Improve table layouts and spacing
- [x] Add loading states and better error feedback
- [x] Add confirmation toasts for successful actions
- [x] Improve mobile responsiveness of admin views

View File

@@ -1,11 +1,11 @@
---
# nuzlocke-tracker-d8cp
title: Set up frontend test infrastructure
status: draft
status: completed
type: task
priority: normal
created_at: 2026-02-10T09:33:33Z
updated_at: 2026-02-10T09:34:00Z
updated_at: 2026-02-21T12:32:34Z
parent: nuzlocke-tracker-yzpb
blocking:
- nuzlocke-tracker-ee9s
@@ -16,14 +16,14 @@ Set up the test infrastructure for the React/TypeScript frontend. No testing too
## Checklist
- [ ] Install Vitest, @testing-library/react, @testing-library/jest-dom, @testing-library/user-event, jsdom
- [ ] Configure Vitest in `vite.config.ts` or a dedicated `vitest.config.ts`
- [ ] Set up jsdom as the test environment
- [ ] Create a test setup file (e.g. `src/test/setup.ts`) that imports @testing-library/jest-dom matchers
- [ ] Create test utility helpers (e.g. render wrapper with providers — QueryClientProvider, BrowserRouter)
- [ ] Add a \`test\` script to package.json
- [ ] Verify the setup by writing a simple smoke test
- [ ] Set up MSW (Mock Service Worker) or a similar API mocking strategy for hook/component tests
- [x] Install Vitest, @testing-library/react, @testing-library/jest-dom, @testing-library/user-event, jsdom
- [x] Configure Vitest in `vite.config.ts` or a dedicated `vitest.config.ts`
- [x] Set up jsdom as the test environment
- [x] Create a test setup file (e.g. `src/test/setup.ts`) that imports @testing-library/jest-dom matchers
- [x] Create test utility helpers (e.g. render wrapper with providers — QueryClientProvider, BrowserRouter)
- [x] Add a \`test\` script to package.json
- [x] Verify the setup by writing a simple smoke test
- [x] Set up MSW (Mock Service Worker) or a similar API mocking strategy for hook/component tests — using `vi.mock` instead; MSW deferred until needed
## Notes

View File

@@ -1,30 +0,0 @@
---
# nuzlocke-tracker-dgax
title: Nuzlocke Rules Configuration
status: completed
type: task
priority: normal
created_at: 2026-02-04T15:44:22Z
updated_at: 2026-02-04T15:58:43Z
parent: nuzlocke-tracker-f5ob
---
Create the rules/difficulty settings screen for customizing the Nuzlocke run.
## Checklist
- [x] Create rules configuration component
- [x] Implement core Nuzlocke rules toggles:
- [x] Standard rules (catch first encounter only, permadeath)
- [x] Nickname requirement toggle
- [x] Duplicates clause (can skip already-caught species)
- [x] Shiny clause (can catch shinies regardless)
- [x] Add optional difficulty modifiers:
- [x] Hardcore mode (no items in battle)
- [x] Level caps (can't overlevel gym leaders)
- [x] Set mode only (no switch after KO)
- [x] Persist selected rules to run state (local state; backend API TBD)
- [x] Show rule explanations/tooltips
## Notes
- Rules should be toggleable after run starts (user preference)
- Default to standard Nuzlocke rules

View File

@@ -0,0 +1,127 @@
---
# nuzlocke-tracker-dpw7
title: Modernize website design and look-and-feel
status: completed
type: feature
priority: normal
created_at: 2026-02-17T19:16:39Z
updated_at: 2026-02-20T19:05:21Z
---
Overhaul the UI to a dark-first, techy aesthetic with a cohesive brand identity derived from the ANT steel ant logo.
## Design direction
**Dark & techy** — dark-first surfaces, subtle glow/accent effects, code-editor-influenced aesthetic. Think GitHub dark, Discord, or Linear dark mode. Light mode becomes the secondary theme.
## 1. Brand palette + Tailwind theme
Define custom Tailwind v4 theme tokens in `index.css` using `@theme`:
- **Surfaces:** dark navy/charcoal base (`#0f1117`, `#161b22`, `#1c2128`) with layered elevation (darker = further back, lighter = elevated)
- **Accent:** steel blue from the logo (`#395E73`, `#7EB0CE`) as the primary interactive color
- **Text:** off-white primary (`#e6edf3`), muted gray secondary (`#7d8590`)
- **Status colors:** keep green/red/blue semantics but shift to darker, more saturated variants that work on dark surfaces
- **Borders:** subtle (`rgba(255,255,255,0.08)`) instead of gray-200/700
Replace ad-hoc Tailwind color classes throughout all components with theme tokens.
## 2. Typography
Self-host **Geist** (or Inter/JetBrains Mono pairing):
- Geist Sans for UI text (headings, labels, body)
- Geist Mono for data-heavy elements (stats numbers, encounter rates, levels)
- Set up via `@font-face` in `index.css`, configure in Tailwind `@theme`
- Establish clear size/weight hierarchy: page titles (2xl bold), section headers (lg semibold), body (sm regular), labels (xs medium)
## 3. Navigation redesign
- Add the ant SVG logo mark next to "ANT" in the nav
- Active route indicator (accent-colored underline or background highlight)
- Subtle bottom border glow or gradient accent line
- Slightly translucent/backdrop-blur nav background for depth
- Better mobile menu transitions (slide or fade instead of instant toggle)
## 4. Home page hero
- Full-width dark gradient hero section with the ant logo as a subtle watermark/background element
- Tagline with stronger typography hierarchy
- Stats summary (total runs, completion rate) as glowing stat pills if the user has data
- CTA button with accent glow/gradient
## 5. Cards & surfaces
- Dark elevated cards (`bg-[#161b22]`) with subtle border (`border-white/[0.06]`)
- Hover state: slight border brightness increase + subtle shadow glow in accent color
- Active/selected states with accent border
- Pokemon cards: dark backgrounds make sprites pop better, accent ring on hover
- Stat cards: accent-colored left border or top gradient
- Modals: dark overlay with backdrop-blur, card-style modal surface
## 6. Status indicators & badges
- Status badges: more vibrant on dark backgrounds (alive=emerald glow, dead=red glow, caught=blue)
- Type badges: use the established Pokemon type colors but tuned for dark surfaces
- Encounter method badges: same treatment
- Pulse animation on active run indicators
## 7. Micro-interactions
- Smooth transitions on all interactive elements (`transition-all duration-150`)
- Hover lift on cards (`hover:-translate-y-0.5`)
- Button press feedback (`active:scale-[0.98]`)
- Loading spinners in accent color
- Skeleton loading states for data-heavy pages
## 8. Dark/light mode
- Dark is the default and primary design target
- Light mode: invert surfaces to white/gray-50, keep accent colors, adjust contrast
- Toggle in nav (sun/moon icon)
- Persist preference in localStorage, respect `prefers-color-scheme`
## Checklist
- [x] Define Tailwind v4 `@theme` tokens (colors, fonts, spacing) in `index.css`
- [x] Self-host Geist font family, configure in theme
- [x] Redesign nav bar (logo mark, active states, backdrop blur, dark surface)
- [x] Redesign home page hero section
- [x] Update card/surface styles globally (Layout, PokemonCard, StatCard, GameCard)
- [x] Update all page-level backgrounds and containers
- [x] Update modal styles (EncounterModal, StatusChangeModal, etc.)
- [x] Update badge/indicator styles (TypeBadge, RuleBadges, EncounterMethodBadge)
- [x] Add dark/light mode toggle to nav
- [x] Polish hover states and transitions across all interactive elements
- [x] Add automated Playwright accessibility and mobile layout tests
- [x] Verify accessibility (contrast ratios, focus indicators)
- [x] Verify mobile layout and touch targets
## Automated verification approach
Add a Playwright test suite that covers both accessibility and mobile layout:
### Accessibility (axe-core + Playwright)
- Install `@axe-core/playwright` as a dev dependency
- Write a test that visits each major page and runs axe-core
- Pages to cover: Home, RunList, RunDashboard, RunEncounters, Stats, NewRun, GenlockeList, GenlockeDetail, NewGenlocke, admin pages
- Check for: color contrast (WCAG AA), missing ARIA labels, heading hierarchy, focus indicators, form label associations
- Run as part of CI
### Mobile layout (Playwright viewports)
- Test each major page at 3 viewport sizes: mobile (375x667), tablet (768x1024), desktop (1280x800)
- Assert no horizontal overflow (`document.documentElement.scrollWidth <= window.innerWidth`)
- Assert touch targets are at least 44x44px (axe-core `target-size` rule)
- Screenshot each page at each viewport for visual review
### Implementation
- Add test file: `frontend/e2e/accessibility.spec.ts`
- Add Playwright config if not present
- Add `test:a11y` script to `package.json`
## Constraints
- Tailwind-only (no additional CSS frameworks or component libraries)
- Self-hosted fonts only (no Google Fonts CDN)
- Maintain accessibility (WCAG AA contrast ratios, visible focus indicators)
- No performance regression (fonts loaded with `font-display: swap`, no layout shift)

View File

@@ -1,30 +0,0 @@
---
# nuzlocke-tracker-dqyb
title: Set up Python tool scaffold
status: completed
type: task
priority: normal
created_at: 2026-02-11T08:42:58Z
updated_at: 2026-02-11T08:49:55Z
parent: nuzlocke-tracker-bs05
blocking:
- nuzlocke-tracker-zno2
---
Set up the standalone Python tool project in `tools/import-pokedb/`.
## Checklist
- [x] Create `tools/import-pokedb/` directory structure
- [x] Set up `pyproject.toml` with dependencies (just stdlib should suffice for JSON processing, maybe `click` for CLI)
- [x] Create CLI entry point (`__main__.py` or similar) that accepts:
- Path to directory containing PokeDB JSON export files
- Target output directory (default: `backend/src/app/seeds/data/`)
- Optional: specific game version to generate (default: all)
- [x] Load and parse all PokeDB JSON files: `encounters.json`, `locations.json`, `location_areas.json`, `encounter_methods.json`, `versions.json`, `pokemon_forms.json`
- [x] Basic validation that all expected files are present and parseable
## Notes
- Keep it as a standalone tool, not part of the backend
- The PokeDB JSON files are downloaded manually from https://pokedb.org/data-export — no need to automate the download
- Model the CLI similarly to how `tools/fetch-pokeapi/` works (cd into dir, run the tool)

View File

@@ -1,40 +0,0 @@
---
# nuzlocke-tracker-dyzh
title: Click-to-edit pattern across admin tables
status: completed
type: feature
priority: high
created_at: 2026-02-08T12:32:53Z
updated_at: 2026-02-08T12:45:17Z
parent: nuzlocke-tracker-iu5b
blocking:
- nuzlocke-tracker-fxi7
- nuzlocke-tracker-mg46
---
Replace the current pattern of separate Edit/Delete action buttons with an inline click-to-edit pattern across all admin pages.
## Current behavior
- Table rows show data with Edit and Delete buttons on the right
- Clicking Edit opens a modal form
- Clicking Delete opens a confirmation dialog
## Desired behavior
- Clicking a table row opens an edit card/panel inline or as a detail view
- The edit card shows all fields in editable form
- A Delete button is available within the edit card
- Save/Cancel buttons to confirm or discard changes
- Clicking outside or pressing Escape cancels
## Affected pages
- AdminPokemon (pokemon table)
- AdminEvolutions (evolutions table)
- AdminGames (games table)
- AdminGameDetail routes tab (routes table)
- AdminGameDetail bosses tab (boss battles table)
- AdminRouteDetail (route encounters table)
- AdminRuns (runs table — delete only, no edit)
## Notes
- Some pages already have row click handlers (Games navigates to detail). Preserve that where it makes sense — the edit card could be the detail page itself.
- Keep modal forms for Create (adding new entities) since there's no row to click yet.

View File

@@ -1,18 +0,0 @@
---
# nuzlocke-tracker-ecij
title: Add vitest for frontend testing
status: completed
type: task
priority: normal
created_at: 2026-02-16T19:13:50Z
updated_at: 2026-02-16T19:26:23Z
parent: nuzlocke-tracker-a5es
---
No frontend test runner is configured. Global standards require vitest.
## Checklist
- [ ] Install vitest as devDependency
- [ ] Create vitest.config.ts
- [ ] Add test script to package.json
- [ ] Add test step to CI workflow

View File

@@ -1,31 +1,27 @@
---
# nuzlocke-tracker-ee9s
title: Unit tests for frontend utilities and hooks
status: draft
status: completed
type: task
priority: normal
created_at: 2026-02-10T09:33:38Z
updated_at: 2026-02-10T09:33:38Z
updated_at: 2026-02-21T12:47:19Z
parent: nuzlocke-tracker-yzpb
---
Write unit tests for the frontend utility functions and custom React hooks.
All API modules are mocked with `vi.mock`. Hooks are tested with `renderHook` from @testing-library/react, wrapped in `QueryClientProvider`. Mutation tests spy on `queryClient.invalidateQueries` to verify cache invalidation.
## Checklist
- [ ] Test `utils/formatEvolution.ts`evolution chain formatting logic
- [ ] Test `utils/download.ts`file download utility
- [ ] Test `hooks/useRuns.ts`run CRUD hook with mocked API
- [ ] Test `hooks/useGames.ts`game fetching hook
- [ ] Test `hooks/useEncounters.ts`encounter operations hook
- [ ] Test `hooks/usePokemon.ts`pokemon data hook
- [ ] Test `hooks/useGenlockes.ts`genlocke operations hook
- [ ] Test `hooks/useBosses.ts`boss operations hook
- [ ] Test `hooks/useStats.ts` — stats fetching hook
- [ ] Test `hooks/useAdmin.ts`admin operations hook
## Notes
- Utility functions are pure functions — straightforward to test
- Hooks wrap React Query — test that they call the right API endpoints, handle loading/error states, and invalidate queries correctly
- Use `@testing-library/react`'s `renderHook` for hook testing
- Mock the API client (from `src/api/`) rather than individual fetch calls
- [x] Test `utils/formatEvolution.ts`done in smoke test
- [x] Test `utils/download.ts`blob URL creation, filename, cleanup
- [x] Test `hooks/useGames.ts`query hooks and disabled state
- [x] Test `hooks/useRuns.ts`query hooks + mutations with cache invalidation
- [x] Test `hooks/useEncounters.ts`mutations and conditional queries
- [x] Test `hooks/usePokemon.ts`conditional queries
- [x] Test `hooks/useGenlockes.ts`queries and mutations
- [x] Test `hooks/useBosses.ts`queries and mutations
- [x] Test `hooks/useStats.ts` — single query hook
- [x] Test `hooks/useAdmin.ts`representative subset (usePokemonList, useCreateGame, useDeleteGame)

View File

@@ -1,13 +0,0 @@
---
# nuzlocke-tracker-eltb
title: Deduplicate case-only duplicate sub-areas in seed data
status: completed
type: bug
priority: normal
created_at: 2026-02-08T12:21:44Z
updated_at: 2026-02-08T12:22:37Z
---
PokeAPI returns both lowercase and uppercase floor names (e.g. '1f' vs '1F') as separate sub-areas with identical encounter data. This causes duplicate routes in the UI. Fix by merging case-insensitive duplicate children in all seed JSON files, keeping the uppercase name and combining encounters.
649 duplicates across all 22 seed files.

View File

@@ -1,28 +0,0 @@
---
# nuzlocke-tracker-em40
title: Add filtering to admin tables
status: completed
type: feature
priority: low
created_at: 2026-02-08T12:33:46Z
updated_at: 2026-02-08T19:28:04Z
parent: nuzlocke-tracker-iu5b
---
Add filter controls to admin tables beyond the existing text search.
## Current state
- Pokemon and Evolutions have text search
- No other filter dimensions available
## Desired filters
- **Pokemon**: Filter by type, generation (derived from dex range)
- **Evolutions**: Filter by trigger type (level-up, trade, item, etc.)
- **Games**: Filter by region, generation
- **Routes**: Filter by encounter method availability
- **Runs**: Filter by status (active, completed, failed), game
## UX
- Filter chips or dropdown selectors above the table, next to the existing search bar
- Filters combine with search (AND logic)
- Clear all filters button

View File

@@ -1,10 +0,0 @@
---
# nuzlocke-tracker-ey30
title: Reorder Crystal routes to match Gold ordering
status: completed
type: task
created_at: 2026-02-13T14:23:40Z
updated_at: 2026-02-13T14:23:40Z
---
Adjusted the route ordering of crystal.json to match gold.json. The sequence is identical — only the order field was changed, encounters were preserved. Crystal is missing Cerulean City (not present in Crystal) so orders are offset by -1 from that point.

View File

@@ -1,32 +0,0 @@
---
# nuzlocke-tracker-f44d
title: Add Pokemon forms support to seeding
status: completed
type: task
priority: normal
created_at: 2026-02-06T10:11:23Z
updated_at: 2026-02-07T13:30:57Z
---
The current seeding only fetches base Pokemon species. It should also include alternate forms (Alolan, Galarian, Mega, regional variants, etc.) which have different types and stats.
## Scope
1. **Data model evaluation**: Determine if Pokemon forms need a separate table or can be handled with additional fields on the existing Pokemon model (e.g., `form_name`, `base_pokemon_id`)
2. **PokeAPI structure**: Investigate how forms are represented in PokeAPI data:
- `pokemon-form` endpoint
- `pokemon` endpoint (forms like `pikachu-alola` have separate entries)
- Relationship between species and forms
3. **Seed data updates**: Update `fetch_pokeapi.py` to:
- Fetch all forms for Pokemon that appear in encounter tables
- Include form-specific data (types, sprites)
- Handle form naming consistently
4. **Frontend considerations**: Ensure Pokemon selector in encounter modal can distinguish forms when relevant
## Questions to resolve
- Should forms be stored as separate Pokemon records or as variants of a base Pokemon?
- How do encounter tables reference forms vs base species in PokeAPI?
- Which games have form-specific encounters that need to be supported?

View File

@@ -1,35 +0,0 @@
---
# nuzlocke-tracker-f5ob
title: MVP - Core Nuzlocke Tracker
status: completed
type: epic
priority: normal
created_at: 2026-02-04T15:43:34Z
updated_at: 2026-02-07T12:31:15Z
blocking:
- nuzlocke-tracker-f0fm
---
The minimum viable product for the Nuzlocke Tracker application. This epic covers the essential features needed for a user to track a basic Nuzlocke run.
## Architecture
- **Frontend**: Web application for users to track their runs
- **Backend API**: RESTful or GraphQL API for data persistence
- **Database**: Persistent storage for game data, user runs, and encounters
- **Admin Panel**: Interface for managing reference data (games, routes, Pokémon)
## Goals
- Allow users to select a Pokémon game to play
- Support optional difficulty/rule settings
- Track encounters per route/area
- Track Pokémon status (alive, dead, boxed)
- Provide a clear overview of the current run
- Admin can manage game/route/Pokémon reference data
## Out of Scope for MVP
- User authentication (single user assumed)
- Multiple simultaneous runs
- Cloud sync across devices
- Team builder/planner
- Detailed statistics and analytics
- Social features

View File

@@ -0,0 +1,10 @@
---
# nuzlocke-tracker-fitk
title: Add egglocke, wonderlocke, and randomizer rules
status: completed
type: feature
priority: normal
created_at: 2026-02-20T19:56:05Z
updated_at: 2026-02-20T20:31:29Z
parent: nuzlocke-tracker-49xj
---

View File

@@ -0,0 +1,29 @@
---
# nuzlocke-tracker-fv7w
title: Add boss team match rule
status: completed
type: feature
priority: normal
created_at: 2026-02-20T19:56:22Z
updated_at: 2026-02-20T21:03:20Z
parent: nuzlocke-tracker-49xj
---
When enabled, hint to the player that they should limit their active party to the same number of Pokemon as the next boss fight. This is a self-imposed difficulty rule — the tracker cannot enforce it since it doesn't track the active party, but it can surface the information.
## Design
**Rule:** Add `bossTeamMatch: boolean` to `NuzlockeRules` (default: `false`, category: `playstyle`).
**Display:** When enabled and the sticky boss banner is shown, add a hint next to the boss name showing their team size, e.g. "Next: Brock (2 Pokemon — match their team)". This reuses the existing `nextBoss` and its `pokemon` array.
**Variant bosses:** Some bosses have conditional teams (e.g. rival starter choice). Use the same logic as `BossTeamPreview`: count pokemon without a `conditionLabel` plus those matching the auto-detected variant (via `matchVariant`). Falls back to first variant if no match is detected.
**Scope:** Frontend-only. No backend or data model changes needed.
## Checklist
- [x] Add `bossTeamMatch: boolean` to `NuzlockeRules` interface and `DEFAULT_RULES` (default: false)
- [x] Add `RuleDefinition` entry (category: `playstyle`)
- [x] Show boss team size hint in the sticky level cap banner when the rule is enabled
- [x] Handle variant boss teams (use auto-matched variant count when available)

View File

@@ -1,43 +0,0 @@
---
# nuzlocke-tracker-fxi7
title: Pokemon detail card with encounters and evolution chain
status: completed
type: feature
priority: high
created_at: 2026-02-08T12:33:05Z
updated_at: 2026-02-08T12:53:13Z
parent: nuzlocke-tracker-iu5b
---
When viewing/editing a Pokemon in the admin panel, show contextual information about where it can be encountered and its evolution chain.
## Desired behavior
- The pokemon edit card (from click-to-edit) shows:
1. Editable fields (name, types, dex number, sprite, etc.)
2. **Encounter locations**: A list of routes/games where this pokemon appears as a route encounter. Grouped by game, showing route name + encounter method + levels.
3. **Evolution chain**: Visual display of the pokemon's evolution family — predecessors and successors with triggers (level, item, trade, etc.)
- Encounter locations link to the route detail page in admin for quick navigation
- Evolution chain entries are clickable to open the EvolutionFormModal for direct editing
## Implementation
### Tabbed modal (edit mode)
In edit mode, the PokemonFormModal uses three tabs instead of a single scrolling view:
- **Details** — the form fields (PokeAPI ID, name, types, etc.) with Save/Delete/Cancel footer
- **Evolutions** — clickable evolution chain rows that open a stacked EvolutionFormModal for direct editing
- **Encounters** — encounter locations grouped by game, with route names linking to admin route detail pages
In create mode, no tabs are shown (just the form fields).
### Backend endpoints
- `GET /pokemon/{id}/encounter-locations` — returns encounters grouped by game with route/game names eagerly loaded
- `GET /pokemon/{id}/evolution-chain` — BFS to find full evolution family, returns all edges with from/to Pokemon
### Frontend
- New types: `PokemonEncounterLocationItem`, `PokemonEncounterLocation`
- New API functions: `fetchPokemonEncounterLocations`, `fetchPokemonEvolutionChain`
- New hooks: `usePokemonEncounterLocations`, `usePokemonEvolutionChain`
- Extracted `formatEvolutionMethod` to shared `utils/formatEvolution.ts`
## Notes
- This helps the admin quickly verify data completeness — 'is this pokemon assigned to the right routes?' and 'are its evolutions set up correctly?'

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-g57d
title: Implement conditional boss battle teams
status: completed
type: feature
priority: normal
created_at: 2026-02-08T19:51:02Z
updated_at: 2026-02-08T19:54:52Z
---
Wire up the existing condition_label column on boss_pokemon to support variant teams in the UI. This includes: admin BossTeamEditor with variant tabs, BossDefeatModal with variant selector, RunEncounters boss card variant display, and schema additions for BulkBossPokemonItem and frontend BossPokemonInput.

View File

@@ -1,49 +0,0 @@
---
# nuzlocke-tracker-g8zi
title: 'Fix Pokemon form identification: separate PokeAPI ID from national dex'
status: completed
type: bug
priority: high
created_at: 2026-02-07T13:44:25Z
updated_at: 2026-02-07T13:54:29Z
blocking:
- 6aje
---
## Problem
Pokemon forms (Alolan, Galarian, etc.) share a national dex number with their base species (e.g., Alolan Rattata is still national dex #19, same as regular Rattata). However, the current data model uses the PokeAPI internal ID (e.g., 10091 for Alolan Rattata) as the `national_dex` field. This is semantically wrong and causes:
1. **Wrong display**: Frontend shows "#10091" next to Alolan Rattata instead of "#19"
2. **Broken sorting**: Forms sort at the end of the Pokemon list instead of next to their base species
3. **Misleading data**: The field name implies a national Pokedex number but contains an internal API ID
## Current architecture
The `national_dex` field is deeply embedded as the unique Pokemon identifier:
- **Database**: `SmallInteger` column with `UNIQUE` constraint (`alembic/versions/03e5f186a9d5`)
- **Models**: `pokemon.py``mapped_column(SmallInteger, unique=True)`
- **Seeder**: `loader.py` — upsert conflict resolution on `national_dex`, builds `{national_dex: id}` mapping for linking encounters and evolutions
- **Seed data**: `fetch_pokeapi.py` — uses PokeAPI pokemon ID as `national_dex` for both base species and forms
- **API**: All CRUD operations key on `national_dex`, returned as `nationalDex` in JSON
- **Frontend**: Displayed as `#nationalDex` in Pokemon selector, admin table, encounter modals
## Proposed fix
Add a `pokemon_id` (or similar) field as the true unique identifier (the PokeAPI pokemon ID), and keep `national_dex` as the real national dex number (shared between forms). This requires changes across every layer.
## Checklist
- [ ] Add new migration: add `pokemon_id` column (unique, not null), change `national_dex` unique constraint to non-unique
- [ ] Update Pokemon model to add `pokemon_id` field
- [ ] Update seed data: `pokemon.json` entries get both `pokemon_id` (PokeAPI ID) and `national_dex` (real dex number, from species endpoint)
- [ ] Update `fetch_pokeapi.py`: for forms, look up the species to get the real national dex, store PokeAPI ID separately
- [ ] Update `loader.py`: upsert on `pokemon_id` instead of `national_dex`, update encounter/evolution linking
- [ ] Update API schemas and endpoints to expose both fields
- [ ] Update frontend to display real `national_dex` but use `pokemon_id` internally for uniqueness
- [ ] Update encounter seed data to reference `pokemon_id` instead of `national_dex`
## Impact
Touches almost every layer: migration, model, seeder, API, frontend. Should be done before more forms are added (bean 6aje) to avoid migrating bad data.

View File

@@ -1,31 +0,0 @@
---
# nuzlocke-tracker-gkcy
title: Output seed JSON
status: completed
type: task
priority: normal
created_at: 2026-02-11T08:43:21Z
updated_at: 2026-02-11T10:00:00Z
parent: nuzlocke-tracker-bs05
blocking:
- nuzlocke-tracker-vdks
---
Generate the final per-game JSON files in the existing seed format.
## Checklist
- [x] **Apply route ordering**: Use the existing `backend/src/app/seeds/route_order.json` to assign `order` values to routes. Handle aliases (e.g. "red-blue" → "firered-leafgreen"). Log warnings for routes not in the order file.
- [x] **Merge special encounters**: Integrate starters, gifts, fossils, and trades from `backend/src/app/seeds/special_encounters.json` into the appropriate routes. Pokemon names are resolved to proper display names via PokemonMapper.
- [x] **Output per-game JSON**: Write `{game-slug}.json` files matching the existing format:
```json
[{"name": "Route 1", "order": 3, "encounters": [...], "children": []}]
```
- [x] **Output games.json**: Generate the global games list from `version_groups.json` — 38 games written, matching existing count.
- [x] **Output pokemon.json**: Generate the global pokemon list including all pokemon referenced in any encounter. Include pokeapi_id, national_dex, name, types, sprite_url.
- [x] **Handle version exclusives**: Encounters are filtered by `version_identifiers` per game — verified FireRed vs LeafGreen have 18 exclusives each.
## Notes
- The output must be a drop-in replacement for the existing files in `backend/src/app/seeds/data/`
- Boss data (`{game}-bosses.json`) is NOT generated by this tool — it's manually curated
- Evolutions data is also separate (currently from PokeAPI) — out of scope for this task

View File

@@ -1,43 +0,0 @@
---
# nuzlocke-tracker-glh8
title: Gather generation metadata (games, regions)
status: completed
type: task
priority: normal
created_at: 2026-02-08T19:20:49Z
updated_at: 2026-02-09T08:06:19Z
parent: nuzlocke-tracker-25mh
blocking:
- nuzlocke-tracker-kz5g
---
Collect and store metadata about each Pokemon generation to support genlocke features. This data is needed so the app can present generation-aware options when setting up a genlocke (e.g. "pick one game per generation").
## Data to gather per generation
- **Generation number** (19)
- **Main region** (e.g. Gen 1 → Kanto, Gen 2 → Johto, etc.)
- **Games in the generation** — including remakes and enhanced versions:
- Original titles (e.g. Red, Blue, Yellow)
- Remakes that belong to the generation's region (e.g. FireRed/LeafGreen are Gen 1 region but Gen 3 era)
- Consider whether remakes should be listed under their original generation (by region) or their release generation (by engine/dex) — genlocke players typically organize by region
## Notes
- This could be stored as seed data (JSON) or as a database table
- The existing Game model already has `generation` and `region` fields, so some of this data may already be derivable from existing games in the DB
- However, a dedicated generations reference would be useful for UI purposes (showing all generations even if not all games are seeded yet)
- Check if `backend/src/app/seeds/data/generations.json` already exists or if this needs to be created from scratch
## Decisions
- Legends games (Hisui, Lumiose) are excluded from genlocke presets — available via Custom only
- Black 2/White 2 are grouped with Black/White in the same Unova slot (category: sequel)
- Normal Genlocke defaults use best mainline remake: FireRed, HeartGold, Alpha Sapphire, Platinum, Ultra Sun
- Metadata stored as `category` field on Game model + standalone `regions.json` seed file
## Checklist
- [x] Define the generation-to-region mapping (Gen 1 = Kanto, Gen 2 = Johto, ..., Gen 9 = Paldea)
- [x] Determine how to group games by region (use `region` field on existing Game model, or create a dedicated lookup)
- [x] Create a `regions.json` seed file (or equivalent) with: generation number, region name, region order, and which games belong to each region
- [x] Categorize each game as "original", "remake", "enhanced", "sequel", or "spinoff" so presets can filter appropriately
- [x] Define which game is the "default" pick per region for the Normal Genlocke preset (e.g., FireRed for Kanto, HeartGold for Johto)
- [x] Add an API endpoint or extend the games endpoint to return games grouped by region with generation metadata
- [x] Verify all seeded games are correctly tagged with their region

View File

@@ -1,27 +0,0 @@
---
# nuzlocke-tracker-gvom
title: Boss Battles, Level Caps & Badges
status: completed
type: feature
priority: normal
created_at: 2026-02-08T10:09:33Z
updated_at: 2026-02-08T10:15:38Z
---
Add boss battle data models, API endpoints, and UI for gym leaders, elite four, champion, etc. Includes:
- Backend models (BossBattle, BossPokemon, BossResult)
- Database migration
- API endpoints for CRUD and run tracking
- Frontend types, API client, hooks
- Sticky level cap bar on run page
- Boss battle cards interleaved in encounter list
- Admin panel for managing boss battles
## Checklist
- [x] Phase 1: Backend models & migration
- [x] Phase 2: Backend schemas
- [x] Phase 3: Backend API endpoints
- [x] Phase 4: Frontend types, API & hooks
- [x] Phase 5: Frontend run page (level cap bar + boss cards)
- [x] Phase 6: Frontend admin panel

View File

@@ -1,18 +0,0 @@
---
# nuzlocke-tracker-h0dr
title: Fix route deletion failing due to missing cascade on route_encounters
status: completed
type: bug
priority: normal
created_at: 2026-02-14T14:19:56Z
updated_at: 2026-02-14T14:23:10Z
---
Deleting a route returns 500 due to two FK constraint issues:
1. `route_encounters.route_id` — missing cascade on the relationship (SQLAlchemy tried to NULL a NOT NULL column)
2. `boss_battles.after_route_id` — references the route being deleted
## Fix
- Added `cascade="all, delete-orphan"` to `Route.route_encounters` relationship
- Added `update(BossBattle).where(...).values(after_route_id=None)` before deleting the route in the delete endpoint

View File

@@ -1,40 +0,0 @@
---
# nuzlocke-tracker-h3fw
title: Use HoF team for genlocke retirement instead of all alive Pokemon
status: completed
type: task
priority: normal
created_at: 2026-02-09T09:15:00Z
updated_at: 2026-02-09T09:26:54Z
parent: nuzlocke-tracker-8w9s
blocking:
- nuzlocke-tracker-25mh
---
The current retireHoF implementation in `advance_leg()` retires ALL alive, non-shiny, caught Pokemon from the completed run. This is incorrect — only the Hall of Fame team (up to 6 Pokemon selected by the player) should be retired.
## Current behavior
```python
# genlockes.py — retires everything alive
select(Encounter.pokemon_id).where(
Encounter.run_id == current_leg.run_id,
Encounter.status == "caught",
Encounter.faint_level.is_(None),
Encounter.is_shiny.is_(False),
)
```
## Desired behavior
- If the run has `hof_encounter_ids` set, use those encounter IDs to determine which Pokemon to retire
- If `hof_encounter_ids` is not set (legacy/skipped), fall back to the current "all alive" behavior for backwards compatibility
- Only the HoF team members and their evolutionary families get added to the retired list
## Changes needed
- Update `advance_leg()` in `backend/src/app/api/genlockes.py` to check `current_run.hof_encounter_ids` first
- If present, query only those encounters for retirement
- If absent, keep the current fallback query
- Update the frontend `RunEncounters.tsx` to also scope `retiredPokemonIds` correctly (no change needed — it reads from the backend response)
## Checklist
- [x] Update `advance_leg()` to prefer `hof_encounter_ids` over all-alive query
- [x] Verify backwards compatibility when `hof_encounter_ids` is null

View File

@@ -1,19 +0,0 @@
---
# nuzlocke-tracker-hit0
title: Group parent/child routes in admin route table
status: completed
type: feature
priority: normal
created_at: 2026-02-13T13:33:36Z
updated_at: 2026-02-13T13:34:42Z
---
Visually indent child routes under their parent in the admin route table. Dragging a parent route moves all its children with it. Children cannot be independently dragged to a new position.
## Checklist
- [x] Add organizeRoutes() function to AdminGameDetail.tsx
- [x] Replace SortableRouteRow with SortableRouteGroup using multiple tbody elements
- [x] Update SortableContext to only track group IDs
- [x] Update handleDragEnd for group-aware reordering
- [x] Handle edge cases (standalone routes, orphan children)
- [x] Verify frontend build passes

View File

@@ -1,10 +1,11 @@
---
# nuzlocke-tracker-hjkk
title: Unit tests for Pydantic schemas and model validation
status: draft
status: completed
type: task
priority: normal
created_at: 2026-02-10T09:33:03Z
updated_at: 2026-02-10T09:33:03Z
updated_at: 2026-02-21T11:39:58Z
parent: nuzlocke-tracker-yzpb
---
@@ -12,14 +13,14 @@ Write unit tests for the Pydantic schemas in `backend/src/app/schemas/`. These a
## Checklist
- [ ] Test `CamelModel` base class (snake_case → camelCase alias generation)
- [ ] Test run schemas — creation validation, required fields, optional fields, serialization
- [ ] Test game schemas — validation rules, field constraints
- [ ] Test encounter schemas — status enum validation, field dependencies
- [ ] Test boss schemas — nested model validation
- [ ] Test genlocke schemas — complex nested structures
- [ ] Test stats schemas — response model structure
- [ ] Test evolution schemas — validation of evolution chain data
- [x] Test `CamelModel` base class (snake_case → camelCase alias generation)
- [x] Test run schemas — creation validation, required fields, optional fields, serialization
- [x] Test game schemas — validation rules, field constraints
- [x] Test encounter schemas — status enum validation, field dependencies
- [x] Test boss schemas — nested model validation
- [x] Test genlocke schemas — complex nested structures
- [x] Test evolution schemas — validation of evolution chain data
- [x] Test Pokemon create schema (types list, required fields)
## Notes

View File

@@ -1,39 +0,0 @@
---
# nuzlocke-tracker-hm6t
title: Pokemon Status Management
status: completed
type: task
priority: normal
created_at: 2026-02-04T15:44:37Z
updated_at: 2026-02-05T16:47:18Z
parent: nuzlocke-tracker-f5ob
---
Implement the system for tracking Pokémon status (alive, dead, boxed).
## Checklist
- [x] Create Pokémon card/tile component showing:
- [x] Sprite, name, nickname
- [x] Current status with visual indicator (green/red dot)
- [x] Location caught
- [x] Implement status transitions:
- [x] Alive → Dead (fainted in battle) via StatusChangeModal with confirmation
- [ ] Alive → Boxed (stored in PC) — deferred, no boxed tracking yet
- [ ] Boxed → Alive (added to party) — deferred, no boxed tracking yet
- [x] Add death recording:
- [x] Optional: record cause of death (free text, max 100 chars)
- [x] Optional: record level at death
- [x] Create "Graveyard" view for fallen Pokémon (on RunDashboard)
- [ ] Create "Box" view for stored Pokémon — deferred, no boxed tracking yet
## Implementation (death_cause feature)
- Backend: Alembic migration adds `death_cause` VARCHAR(100) to encounters
- Backend: Model + schemas updated with `death_cause` field
- Frontend: `StatusChangeModal` for recording death with confirmation from RunDashboard
- Frontend: `PokemonCard` now clickable with status indicator dot and death cause display
- Frontend: `EncounterModal` includes death cause input alongside faint level
- Frontend: `RunEncounters` shows death cause in route list
## Notes
- Status changes should be confirmable (prevent accidental deaths) ✓
- Consider undo functionality for misclicks — not implemented (Nuzlocke rules: death is permanent)

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-huag
title: Populate boss Pokemon teams from Bulbapedia
status: completed
type: task
priority: normal
created_at: 2026-02-16T19:53:15Z
updated_at: 2026-02-16T20:07:17Z
---
Populate all 318 boss entries across 21 *-bosses.json seed files with their actual in-game Pokemon teams (pokeapi_id, level, order, condition_label). Data sourced from Bulbapedia. Also fix _export_bosses in run.py to include condition_label.

View File

@@ -1,17 +0,0 @@
---
# nuzlocke-tracker-hwyk
title: Set up Portainer on Unraid
status: completed
type: task
priority: normal
created_at: 2026-02-09T15:30:44Z
updated_at: 2026-02-09T16:53:41Z
parent: nuzlocke-tracker-ahza
---
Install Portainer CE on the Unraid server for container/stack management.
- Run Portainer CE as a Docker container on Unraid
- Configure it to manage the local Docker environment
- Import/create a stack from the production docker-compose file
- Verify the stack can be deployed and managed through the Portainer UI

View File

@@ -1,43 +0,0 @@
---
# nuzlocke-tracker-hy41
title: Admin Panel
status: completed
type: task
priority: normal
created_at: 2026-02-04T15:47:05Z
updated_at: 2026-02-05T18:22:09Z
parent: nuzlocke-tracker-f5ob
---
Build an admin interface for managing reference data.
## Checklist
- [x] Create admin routes/pages separate from tracker
- [x] Game Management:
- [x] List all games
- [x] Add new game
- [x] Edit game details
- [x] Delete game (with cascade warning)
- [x] Route Management:
- [x] List routes for a game
- [x] Add new route
- [x] Edit route details (name, order)
- [x] Delete route
- [x] Reorder routes via drag-and-drop (implemented as up/down buttons)
- [x] Route Pokémon Assignment:
- [x] View Pokémon available on a route
- [x] Add Pokémon to route
- [x] Remove Pokémon from route
- [x] Set encounter rates/methods
- [x] Pokémon Management:
- [x] List all Pokémon
- [x] Add new Pokémon
- [x] Edit Pokémon details
- [x] Bulk import from CSV/JSON
- [x] Admin API endpoints:
- [x] POST/PUT/DELETE for games, routes, pokemon, route_pokemon
## Notes
- Can be a simple CRUD interface
- Consider using a UI library for tables/forms
- No auth required for MVP (assume local/trusted use)

View File

@@ -1,25 +0,0 @@
---
# nuzlocke-tracker-i08l
title: Implement Pinwheel Clause support
status: completed
type: feature
priority: normal
created_at: 2026-02-07T19:18:34Z
updated_at: 2026-02-07T19:21:45Z
---
Add pinwheel_zone column to routes, pinwheelClause toggle to NuzlockeRules, zone-aware encounter locking on frontend and backend.
## Checklist
- [x] Alembic migration for pinwheel_zone column
- [x] SQLAlchemy model update
- [x] Pydantic schema updates
- [x] Route list API helper update
- [x] Encounter creation API zone-aware sibling check
- [x] Seed loader update
- [x] Seed data for Pinwheel Forest zones
- [x] NuzlockeRules per-run toggle
- [x] Frontend types (game.ts, admin.ts)
- [x] Admin route form pinwheelZone input
- [x] Encounter page zone-aware locking, counts, and filtering
- [x] getZoneEncounters helper

View File

@@ -1,10 +1,11 @@
---
# nuzlocke-tracker-iam7
title: Unit tests for services layer
status: draft
status: completed
type: task
priority: normal
created_at: 2026-02-10T09:33:08Z
updated_at: 2026-02-10T09:33:08Z
updated_at: 2026-02-21T12:01:23Z
parent: nuzlocke-tracker-yzpb
---
@@ -12,10 +13,13 @@ Write unit tests for the business logic in `backend/src/app/services/`. Currentl
## Checklist
- [ ] Test family resolution with simple linear evolution chains (e.g. A → B → C)
- [ ] Test family resolution with branching evolutions (e.g. Eevee)
- [ ] Test family resolution with region-specific evolutions
- [ ] Test edge cases: single-stage Pokemon, circular references (if possible), missing data
- [x] Test family resolution with simple linear evolution chains (e.g. A → B → C)
- [x] Test family resolution with branching evolutions (e.g. Eevee / Shedinja)
- [x] Test disjoint chains remain separate families
- [x] Test edge cases: empty list, single-stage Pokemon, base form, middle form
- [x] Test resolve_base_form: linear, branching, Shedinja, not-in-any-evolution
- [x] Test to_roman: parametrized 1100, genlocke sequence IV
- [x] Test strip_roman_suffix: II/III/IV/X, no suffix, round-trip with to_roman
## Notes

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-idh2
title: Add PokeDB.org data attribution
status: completed
type: task
priority: normal
created_at: 2026-02-10T14:52:22Z
updated_at: 2026-02-10T14:56:25Z
---
PokeDB.org requires attribution when their data is used. Implement visible attribution in the application UI. This is both a requirement of their terms of use and good practice.

View File

@@ -1,34 +0,0 @@
---
# nuzlocke-tracker-igl3
title: Name Generation
status: completed
type: epic
priority: normal
created_at: 2026-02-05T13:45:15Z
updated_at: 2026-02-11T20:48:02Z
---
Implement a dictionary-based nickname generation system for Nuzlocke runs. Instead of using an LLM API to generate names on the fly, provide a static dictionary of words categorised by theme. A word can belong to multiple categories, making it usable across different naming schemes.
## Architecture Decisions
- **Dictionary storage**: Static JSON file in `backend/src/app/seeds/data/`, alongside other seed data. Not exposed to frontend directly.
- **Dictionary format**: Category-keyed structure (`{ "mythology": ["Apollo", ...], "space": ["Nova", ...] }`) for fast lookup by naming scheme. Words may appear in multiple categories.
- **Suggestion logic**: Backend service with API endpoint. Frontend calls the backend to get suggestions.
- **Used-name tracking**: No new storage needed. The existing `Encounter.nickname` field already tracks assigned names. The suggestion engine queries encounter nicknames for the current run and excludes them.
- **Naming scheme per run**: Dedicated nullable `naming_scheme` column on `NuzlockeRun` (not in the `rules` JSONB).
## Approach
- **Static dictionary**: A local data file (JSON) containing words organised by category (e.g. mythology, food, space, nature, warriors, music, etc.)
- **~150-200 words per category**: A typical Nuzlocke has ~100 encounters, so this provides ample variety without repetition.
- **Name suggestion UX**: When registering a new encounter, the user is shown 5-10 suggested names from their chosen naming scheme. They can click one to select it, or regenerate for a fresh batch.
- **Naming scheme selection**: Users pick a naming scheme (category) per run, either at run creation or in run settings.
## Success Criteria
- [x] Word dictionary data file exists with multiple categories, each containing 150-200 words
- [x] Name suggestion engine picks random names from the selected category, avoiding duplicates already used in the run
- [x] Encounter registration UI shows 5-10 clickable name suggestions
- [x] User can regenerate suggestions if none fit
- [x] User can select a naming scheme per run

View File

@@ -1,18 +0,0 @@
---
# nuzlocke-tracker-iu5b
title: Admin UX improvement round
status: completed
type: epic
priority: normal
created_at: 2026-02-08T12:32:43Z
updated_at: 2026-02-08T19:32:28Z
---
A round of UX improvements to the admin panel to make data editing more intuitive and efficient. The admin panel is the primary tool for populating game data (routes, encounters, bosses, evolutions), so friction here directly slows down adding support for new games.
## Key themes
- **Consistency**: Unify interaction patterns across all admin pages (click-to-edit, reordering, selectors)
- **Efficiency**: Reduce clicks and navigation depth for common workflows
- **Discoverability**: Surface related data (where a pokemon appears, its evolution chain) directly in context
- **Bulk operations**: Enable importing all entity types, not just Pokemon

View File

@@ -1,20 +0,0 @@
---
# nuzlocke-tracker-izf6
title: Configure Gitea container registry
status: completed
type: task
priority: normal
created_at: 2026-02-09T15:30:40Z
updated_at: 2026-02-09T16:53:09Z
parent: nuzlocke-tracker-ahza
---
Set up and verify the Gitea container registry for hosting Docker images as user-level packages.
## Checklist
- [ ] Create a Gitea access token with `read:package` and `write:package` scopes
- [ ] Verify `docker login gitea.nerdboden.de` works from the dev machine
- [ ] Test pushing a Docker image as a user-level package (e.g., `gitea.nerdboden.de/thefurya/nuzlocke-tracker-api:latest`)
- [ ] Verify the image appears under the user's Packages tab in Gitea
- [ ] Test pulling the image back (from Unraid or dev machine)

View File

@@ -1,11 +0,0 @@
---
# nuzlocke-tracker-j02g
title: Fix duplicate Alembic migration revision IDs
status: completed
type: bug
priority: normal
created_at: 2026-02-14T12:13:08Z
updated_at: 2026-02-14T12:14:34Z
---
Two migration files share revision ID f7a8b9c0d1e2: add_game_id_to_boss_battles and add_naming_scheme_to_genlockes. Fix by giving one a new unique ID and chaining them sequentially.

View File

@@ -1,53 +0,0 @@
---
# nuzlocke-tracker-j28y
title: Curate route ordering to match game progression
status: completed
type: task
priority: normal
created_at: 2026-02-05T13:37:39Z
updated_at: 2026-02-07T12:30:17Z
parent: nuzlocke-tracker-f5ob
---
Routes are currently in alphabetical order from PokeAPI. Update the order field in each game's JSON seed file to reflect actual game progression (e.g., Pallet Town → Route 1 → Viridian City → Route 2 → ...).
## Automated Approach: Bulbapedia Walkthrough Scraping
We already have all location/encounter data from PokeAPI. The missing piece is progression order, which can be extracted from Bulbapedia walkthroughs.
### Data Source
Every game has a Bulbapedia walkthrough with routes listed in progression order:
- https://bulbapedia.bulbagarden.net/wiki/Walkthrough:Pokémon_FireRed_and_LeafGreen
- https://bulbapedia.bulbagarden.net/wiki/Walkthrough:Pokémon_Emerald
- https://bulbapedia.bulbagarden.net/wiki/Walkthrough:Pokémon_HeartGold_and_SoulSilver
- (and so on for all games through Gen 8)
### Implementation Plan
1. **Scrape walkthrough TOCs** - Parse the section headings from each game's walkthrough page to get route order
2. **Normalize names** - Map Bulbapedia location names to PokeAPI location names (handle differences like "Route 1" vs "Kanto Route 1")
3. **Generate ordering** - Create a JSON mapping of `{game: {location_name: order_number}}`
4. **Update fetch_pokeapi.py** - Apply ordering when generating seed data
### Benefits
- Automatable for all games (Gen 1-8)
- Bulbapedia walkthroughs are community-maintained and accurate
- Scales as we add more games
- Only needs to run once per game (or when walkthroughs update)
### Considerations
- Gen 9 (Scarlet/Violet) is open-world so ordering is less meaningful
- Some games have branching paths - may need to pick a canonical order
- Name matching between Bulbapedia and PokeAPI may need fuzzy matching
## Current Scope
- FireRed and LeafGreen share the same route progression (Kanto)
- HeartGold and SoulSilver share the same route progression (Johto + Kanto)
- Emerald has its own progression (Hoenn)
- So effectively 3 unique orderings to define for current games
## Files
- `backend/src/app/seeds/data/firered.json`
- `backend/src/app/seeds/data/leafgreen.json`
- `backend/src/app/seeds/data/emerald.json`
- `backend/src/app/seeds/data/heartgold.json`
- `backend/src/app/seeds/data/soulsilver.json`

Some files were not shown because too many files have changed in this diff Show More