Originally built following Harry Percival's *Test-Driven Development with Python* (3rd ed., complete through ch. 25). Now an ongoing game app — EarthmanRPG — extended well beyond the book.
MCP servers load at session startup only. **Start a new Claude Code conversation** (hit "+" in the sidebar) — no need to reboot VSCode, just open a fresh chat. Always call `firefox_diagnose` first to confirm the connection is live.
### Correct startup sequence
1. Firefox open with Claudezilla extension active (native host must be running)
2. Open a new Claude Code conversation → tools appear as `mcp__claudezilla__firefox_*`
3. Call `firefox_diagnose` to confirm before depending on any tool
**Config file gotcha:** The Claude Code CLI and VSCode extension read user-level MCP servers from `~/.claude.json` (home dir, single file) — NOT from `~/.claude/settings.json` or `~/.claude/mcp.json`. Always use `claude mcp add --scope user` to register; never hand-edit. Verify registration with `claude mcp list`.
**BOM gotcha:** PowerShell writes JSON files with a UTF-8 BOM, which causes `JSON.parse` to throw. Never use PowerShell `Set-Content` to write any Claude config JSON — use the Write tool or the CLI instead.
# Integration + unit tests only (from project root — targets src/apps, skips src/functional_tests)
python src/manage.py test src/apps
# Functional tests only
python src/manage.py test src/functional_tests
# All tests (integration + unit + FT)
python src/manage.py test src
```
**Test tags:** The only tag in use is `channels` — for async consumer tests that require a live Redis channel layer.
- Exclude channels tests: `python src/manage.py test src/apps --exclude-tag=channels`
- Channels tests only: `python src/manage.py test src/apps --tag=channels`
FTs are isolated by **directory path** (`src/functional_tests/`), not by tag.
Test runner is `core.runner.RobustCompressorTestRunner` — handles the Windows compressor PermissionError by deleting stale CACHE at startup + monkey-patching retry logic.
FTs use `TransactionTestCase` which flushes migration-seeded rows. Any IT/FT `setUp` that needs `Applet` rows must call `Applet.objects.get_or_create(slug=..., defaults={...})` — never rely on migration data surviving.
### Static files in tests
`StaticLiveServerTestCase` serves via `STATICFILES_FINDERS` only — NOT from `STATIC_ROOT`. JS/CSS that lives only in `src/static/` (STATIC_ROOT, gitignored) is a silent 404 in CI. All app JS must live in `src/apps/<app>/static/` or `src/static_src/`.
### msgpack integer key bug (Django Channels)
`channels_redis` uses msgpack with `strict_map_key=True` — integer dict keys in `group_send` payloads raise `ValueError` and crash consumers. Always use `str(slot_number)` as dict keys.
### Multi-browser FTs in CI
Any FT opening a second browser must pass `FirefoxOptions` with `--headless` when `HEADLESS` env var is set. Bare `webdriver.Firefox()` crashes in headless CI with `Process unexpectedly closed with status 1`.
### Selenium + CSS text-transform
Selenium `.text` returns visually rendered text. CSS `text-transform: uppercase` causes `assertIn("Test Room", body.text)` to fail. Assert against the uppercased string or use `body.text.upper()`.
### Tooltip portal pattern
`mask-image` on grid containers clips `position: absolute` tooltips. Use `#id_tooltip_portal` (`position: fixed; z-index: 9999`) at page root. See `gameboard.js` + `wallet.js`.
### Applet menus + container-type
`container-type: inline-size` creates a containing block for all positioned descendants — applet menus must live OUTSIDE `#id_applets_container` to be viewport-fixed.
### ABU session auth
`create_pre_authenticated_session` must set `HASH_SESSION_KEY = user.get_session_auth_hash()` and hardcode `BACKEND_SESSION_KEY` to the passwordless backend string.
- Task unit tests: `apps.lyric.tasks.requests.post`
- FTs: mock both with `side_effect=send_login_email_task`
## Teaching Style
User prefers guided learning: describe what to type and why, let them write the code, then review together. But for now, given user's accelerated timetable, please respond with complete code snippets for user to transcribe directly.