Python's sqlite3.executescript() has a bug where trailing newlines after the final semicolon create empty statements. When APSW's log_sqlite() is enabled (via apswutils, imported by fastmigrate), these cause visible "API called with NULL prepared statement" errors during interpreter shutdown. - Strip trailing newlines from all 9 existing migration files - Update migration template to end with semicolon, no trailing newline - Document the requirement in CLAUDE.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
3.1 KiB
3.1 KiB
AnimalTrack Project Instructions
Project Status
Check PLAN.md for current progress. Each step has a checkbox to track completion.
Critical Workflow Rules
1. Commit Frequently
ALWAYS commit after completing any step. Don't let changes accumulate.
2. Update Plan
After completing work, update PLAN.md:
- Check off completed items with
[x] - Add commit hash if significant
3. Run Tests
pytest tests/ -v
Framework Documentation
ALWAYS consult docs before implementing UI. Use vendored documentation.
| Framework | Main Context | Examples |
|---|---|---|
| FastHTML | docs/vendor/fasthtml/llms-ctx.txt |
API refs in docs/vendor/fasthtml/ |
| MonsterUI | docs/vendor/monsterui/llms.txt |
docs/vendor/monsterui/examples/ |
Idiomatic Patterns
- Always check docs for the idiomatic way before implementing anything with FastHTML/MonsterUI
- MonsterUI components:
Card,Alert,Grid(cols_sm/md/lg),DivFullySpaced - Use
AlertTenums for alerts,TextPresetsfor typography - Forms: Use MonsterUI form components, not raw HTML
Quick Reference
Database (APSW + FastLite)
This project uses APSW (not standard sqlite3) via fastlite. Key patterns:
from fastlite import database
db = database('animaltrack.db')
# db.execute() returns APSW cursor
# Use db.t for table access
Timestamps
All timestamps are ms since Unix epoch, stored as INTEGER:
import time
ts_utc = int(time.time() * 1000) # Current time in ms
IDs
All entity IDs are ULIDs (26 chars):
from ulid import ULID
id = str(ULID()) # "01ARZ3NDEKTSV4RRFFQ69G5FAV"
Money
Store all prices as integer cents. Display prices with 2 decimals, cost/egg with 3.
Project-Specific Rules
Event Sourcing
- Every state change is an event
- Events are immutable (edits create revisions, deletes create tombstones)
- Projections are updated synchronously in the same transaction
Selection Context
When handling animal selections:
- Client sends filter + resolved_ids + roster_hash
- Server re-resolves at ts_utc
- On mismatch: return 409 with diff, require
confirmed=trueto proceed
Feed Costing
- Block FeedGiven if no FeedPurchased exists <= ts_utc
- Store feed amounts in grams (INTEGER) for precision
- Display as kg with 3 decimals
Migrations
- Migration files must end with
;and NO trailing newline - Python's sqlite3.executescript() has a bug: trailing newlines after the final
;create empty statements that cause SQLITE_MISUSE errors when APSW logging is enabled - Use
animaltrack create-migration "description"to create new migrations (template handles this correctly)
Refreshing Docs
# FastHTML
curl -s https://www.fastht.ml/docs/llms-ctx.txt -o docs/vendor/fasthtml/llms-ctx.txt
# MonsterUI
curl -s https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/llms.txt -o docs/vendor/monsterui/llms.txt
E2E Tests
The spec defines 8 authoritative acceptance tests in §21. These are the source of truth for correct behavior. When in doubt, refer to the spec.
Numeric comparisons on REAL values use tolerance ±0.001.