Skip to content

Add durable run persistence behind a RunRepository port (Epic 2) - #13

Merged
Lvvphole merged 2 commits into
mainfrom
feat/durable-run-persistence
Jun 23, 2026
Merged

Add durable run persistence behind a RunRepository port (Epic 2)#13
Lvvphole merged 2 commits into
mainfrom
feat/durable-run-persistence

Conversation

@Lvvphole

Copy link
Copy Markdown
Owner

Epic 2 — Durable Run and Evidence Persistence

Replaces the MVP in-memory run registry with a port + adapters so run state can be durable, auditable, and multi-worker-ready — without changing any runtime authority rule. A repository only stores/retrieves runs; the verifier and gates still decide PASS/FAIL/BLOCKED.

Acceptance criteria — all met

  • RunRegistry accessed through an interface/port (RunRepository ABC)
  • In-memory implementation remains for tests/dev (default; RunRegistry alias preserved)
  • Postgres implementation persists and retrieves runs (verified against real PostgreSQL)
  • Run state survives process restart (restart-survival test, real PostgreSQL)
  • No verifier authority rules weakened

Changes

  • PortRunRepository (add/save/get/list). Routers call save() after every state/verifier/result mutation so out-of-process stores stay durable.
  • In-memory adapterInMemoryRunRepository, default for dev/tests; kept as RunRegistry with public .runs for back-compat (existing tests unchanged).
  • Postgres adapterPostgresRunRepository: authoritative JSON snapshot (lossless round-trip) + projected audit rows (handler_results, gate_results, verifier_decisions). psycopg is an optional, lazily-imported dependency (requirements-postgres.txt); core install/tests need no DB.
  • Durable schemastorage/sql/0001_init.sql (idempotent): runs, run_attempts, handler_results, gate_results, verifier_decisions, evidence_artifacts. tenant_id reserved for Epic 5; run_attempts/evidence_artifacts defined for Epics 3/6.
  • Adapter selection — bound at startup from AGENT_ANALYSIS_DATABASE_URL (in-memory default).
  • Docsdocs/persistence.md; README updated (status, layout, test counts).

Verification

Default (no DB):        262 passed, 6 skipped
Real PostgreSQL (DSN):  268 passed, 0 skipped   # threw up a throwaway PG cluster; all 6 tables + durable rows confirmed
git diff --check:       clean
forbidden endpoints:    none (no merge/deploy/force-pass/bypass)

Scope honesty

run_attempts (Epic 3) and evidence_artifacts (Epic 6) are defined in the schema but populated by their own epics. No later epic was started in this PR. Merge remains human-controlled.

🤖 Generated with Claude Code


Generated by Claude Code

Replace the MVP in-memory run registry with a port + adapters so run state
can be durable, auditable, and multi-worker-ready — without changing any
runtime authority rule. A repository only stores/retrieves runs; the verifier
and gates still decide PASS/FAIL/BLOCKED.

- RunRepository port (add/save/get/list); routers now call save() after every
  mutation so out-of-process stores stay durable.
- InMemoryRunRepository: default for dev/tests; kept as `RunRegistry` alias with
  a public `.runs` dict for back-compat (existing tests unchanged).
- PostgresRunRepository: persists each run as an authoritative JSON snapshot
  (lossless round-trip) and projects queryable audit rows (handler_results,
  gate_results, verifier_decisions). psycopg is an optional, lazily-imported
  dependency (requirements-postgres.txt); core install/tests need no DB.
- Durable schema sql/0001_init.sql (idempotent): runs, run_attempts,
  handler_results, gate_results, verifier_decisions, evidence_artifacts.
  tenant_id reserved for Epic 5; run_attempts/evidence_artifacts defined for
  Epics 3/6.
- Adapter selected at startup from AGENT_ANALYSIS_DATABASE_URL (in-memory
  default).
- Tests: serialization round-trip (no DB) + repository contract parametrized
  over in-memory (always) and Postgres (when AGENT_ANALYSIS_TEST_DATABASE_URL
  is set) + Postgres-only restart-survival and projection tests.

Verification: 262 passed, 6 skipped (default, no DB); 268 passed, 0 skipped
against a real PostgreSQL. No merge/deploy/force-pass endpoint added; no gate
weakened.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQW4FXw46KBvMCVc2EduXT

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4666b67e46

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/app/storage/postgres_run_repository.py Outdated
…dapter

PostgresRunRepository.save derived runs.final_status from
chain_execution_result.final_status but runs.verifier_decision from
verifier_report.decision when a report existed. After POST /runs/{id}/verify
this could persist skewed rows (final_status=PASS, verifier_decision=FAIL).

Derive both columns from a single source of truth: the verifier report when
present (both columns = report.decision), otherwise the chain execution result.
No verifier/gate logic or API route changed.

Adds Postgres-gated regression test
test_verifier_report_overrides_chain_final_status[FAIL|BLOCKED]: a run with
chain final_status=PASS, re-saved with verifier_report.decision FAIL/BLOCKED,
persists both runs columns as FAIL/BLOCKED.

Verification: default 262 passed, 8 skipped; PostgreSQL 270 passed, 0 skipped;
git diff --check clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQW4FXw46KBvMCVc2EduXT
@Lvvphole
Lvvphole merged commit 908b9a9 into main Jun 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants