v0.4.0 — architecture deepening + lockstep versioning
First release of the architecture-deepening work — giving domain concepts named in CONTEXT.md a single home, validated under worktree isolation and adversarial QA.
Highlights
- ADR-0011 — single-home Claim eligibility.
app.eligible_simulations(version, cap)is the one home of the per-User claim-eligibility predicate, consumed by the Runner's claim (as a membership gate that preservesFOR UPDATE OFrow locking — verified against PG16) and the KEDA scaler. - ADR-0013 — guarded lifecycle SQL.
claim_simulation/renew_lease/finish_simulation/reap_expired_leasesown each running-phase transition and itsrunner_idfence; the Runner and Reaper are thin callers. Hard per-User cap verified (cap=1, 6 concurrent claimers → 1 wins). - ADR-0012 — Content Hash pinned. A shared test vector asserts the Go and Python hashers can't drift.
- ADR-0014 — auth seam. API-key hash pinned by a shared vector across TS/Go; domain allow-list single-sourced.
- ADR-0015 — version source. Runner image tag derives from
spec.EngineVersion; aconfig/versions.yamlcatalog with a CI drift check.
Release engineering
- New
hack/bump-version.sh(make bump-version V=/make check-package-versions) bumps the portal, Runner, and Python SDK in lockstep and fails loudly on a missed pattern. - All packages unified at 0.4.0 (from 0.2.0 / 0.1.0 / 0.3.1).
Design rationale: ADRs docs/adr/0011–0015 and docs/architecture-review-2026-06.md.
Migrations: adds 0006_eligible_simulations.sql and 0007_lifecycle_functions.sql (idempotent; apply in order).
🤖 Generated with Claude Code