• v0.2.0 c5499728be

    v0.2.0 — Phase 1 completion
    All checks were successful
    CI / frontend (push) Successful in 1m18s
    CI / e2e (push) Successful in 2m27s
    CI / backend (push) Successful in 12m59s
    Stable

    claude-bot released this 2026-08-03 18:34:16 +00:00 | 50 commits to main since this release

    Phase 1 is finished and verified. All 34 issues on the milestone are closed.

    Exit criteria met: every Phase 1 acceptance criterion is verified by an automated test, projections are provably derivable from history, and no known silently-wrong-data path remains.

    1109 backend tests · 58 component tests · 7 end-to-end journeys.

    This tag also carries the v0.1.1 security milestone, which closed on 2026-07-30 and was never tagged separately — the unauthenticated-to-RCE chain, revocable sessions, the Pillow sandbox, rate limiting, and the audit ledger's append-only triggers. The changelog splits the two.

    What landed

    Things that had nothing behind them. ai-rerun and ocr-rerun created jobs from v0.1.0 onward and the Jobs page rendered them, but app/workers/ held only an __init__.py — nothing ever claimed a job. The worker runtime, the Claude vision backend with its cost ledger and spend ceilings, and Tesseract OCR now make those buttons do something.

    Bugs that were writing wrong data. Scanner timestamps recorded as capture dates. A review_version guard that raced. Pagination that silently dropped photographs from a bulk-ingested collection. A filename parser that read a year as a sequence number and accepted 3999-01-01 as a day-precision date at medium reliability — which is what puts a photo in front of a reviewer.

    Silent-failure paths closed. patchNotes and patchFlags had no error handling at all, so a refused save left the reviewer's text on screen with nothing to say it had not been written. A failed evidence fetch rendered as "No evidence yet." — a claim about the archive that nothing had established.

    The contract. Every route returned an untyped dict; the frontend's types were hand-maintained and had drifted twice. Now Pydantic response models end to end, with openapi.json and the generated TypeScript regenerated and diffed in CI.

    Security. Production now refuses to start on an insecure configuration, and CIRCA_ENVIRONMENT defaults to production so an unconfigured deployment fails closed rather than serving family photographs behind a cookie signed with the key printed in the README. Storage keys gained a containment check. A development login bypass exists, with three independent guards keeping it out of production.

    Performance, measured. The photo list went from 15.2 ms to 0.150 ms with the right index. Media was never cacheable — every revisit re-sent a multi-megabyte original. Ingest was the only async data handler in the codebase, blocking the event loop on every hash and copy.

    Tests. 1109 of them, including an isolation failure they were hiding: 257 scan files had accumulated in the real backend/storage/ because three suites called ingest_photo() without redirecting storage. Nothing had failed.

    Full detail in CHANGELOG.md.

    Downloads