User reported `HTTP 400 — executed_at can only be set when state is
executed/reviewed_by_blue` when typing the timestamp inline in the new
scenario table. The state-gate predates the simplified UX — it made
sense back when the workflow was "Mark executed button + override
toggle", but the user has since asked for a single freely-typeable
datetime input.
- update_mission_test_fields drops the state check. Stamping a non-null
executed_at while state ∈ {pending, skipped, blocked} now auto-promotes
the state to `executed` in the same write. The promotion is gated by
the same mission.write_red_fields perm that executed_at already
required — no privilege escalation.
- MissionTestPage.tsx drops the state-based UI gate on canEditExecutedAt;
red perm alone now unlocks the input regardless of state.
- Replaced the old "rejection while pending" test with two new tests:
pending→executed via inline stamp + blue 403, and skipped→executed via
inline stamp.
- 139 pytest green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Metamorph backend
Flask 3 API. See repo root README.md for the big picture.
Layout
app/
├── api/ # HTTP layer (blueprints), versioned under /api/v1
├── core/ # config (env-driven), structured logging
├── db/ # SQLAlchemy session + Alembic (M1+)
├── models/ # ORM models (M1+)
├── services/ # domain logic (M2+)
└── i18n/ # message catalogs (M13)
tests/ # pytest
Local dev
Requires uv and a reachable Postgres (M1+; not needed yet for /health).
uv sync # install deps from pyproject.toml
uv run flask --app app.main run --debug --port 8000
curl http://localhost:8000/api/v1/health
Tests
uv run pytest
Lint
uv run ruff check .
uv run ruff format .