Files
animaltrack/CLAUDE.md
Petru Paler 25a91c3322 fix: remove trailing newlines from migrations to prevent SQLITE_MISUSE errors
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>
2026-01-01 16:09:06 +00:00

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 AlertT enums for alerts, TextPresets for 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:

  1. Client sends filter + resolved_ids + roster_hash
  2. Server re-resolves at ts_utc
  3. On mismatch: return 409 with diff, require confirmed=true to 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.