Skip to content

Plan: Implement repository contracts and Alembic migrations (2.6.2) - #281

Draft
leynos wants to merge 5 commits into
mainfrom
2-6-2-repository-contracts-and-alembic-migrations.md
Draft

Plan: Implement repository contracts and Alembic migrations (2.6.2)#281
leynos wants to merge 5 commits into
mainfrom
2-6-2-repository-contracts-and-alembic-migrations.md

Conversation

@leynos

@leynos leynos commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Summary

Execution plan for roadmap item 2.6.2 — Implement repository contracts and Alembic migrations.

Plan document: docs/execplans/2-6-2-repository-contracts-and-alembic-migrations.md

This PR contains no code changes — only the plan.

Why this supersedes #142

PR #142 carries an earlier draft of the same plan, authored against a tree that predated commit 5af0638 (roadmap 4.3.2). That draft asserted "there is no durable persistence for generation runs" — no longer true — and targeted migration head 20260601_000009, three revisions stale. This branch rebases onto origin/main and rewrites the plan against the current tree. #142 should be closed.

Scope

4.3.2 already landed durable persistence for runs and events, the 20260624_000010 migration, and sequencing property tests. The genuine remaining gap is:

  • reviewer-checkpoint persistence (table, ORM model, reversible migration, SQL adapter);
  • a production implementation of the composite GenerationRunPort;
  • cross-adapter contract equivalence for the checkpoint surface;
  • the lease-reclamation primitive that 4.3.2 deferred here by name (gated milestone).

The plan is explicit that this is an enabling slice with zero production consumers until 2.6.3: InMemoryGenerationRunStore is instantiated nowhere in episodic/, and no production path calls create_checkpoint today.

Design review

The plan was reviewed by a six-lens expert panel (structure, alternatives, scaling, contracts, failure modes, viability) after a Wyvern reconnaissance pass. Two reviewers independently probed the test database and converged on a finding that invalidated a core axiom of the first draft:

py-pglite serializes all transactions globally. It is a single WebAssembly PostgreSQL backend, so an open transaction blocks every other session, related or not. A holder with an open transaction blocked an unrelated connection's SELECT 42.

Consequences, now reflected in the plan:

  • The previous AXIOM-3 (py-pglite reproduces PostgreSQL locking semantics) was false. INV-SEQ-1's concurrency obligation was unfalsifiable in the harness that would run it, and its negative control provably could not fire.
  • Every asyncio.gather test in tests/canonical_storage/ measures sequential replay, not concurrency. They remain valid evidence for compare-and-set predicates; the plan no longer cites them as lock evidence.
  • The concurrency claim moves to Residual gaps, with a real-PostgreSQL opt-in tier named as the only way to discharge it.

Other substantive revisions:

  • D-3 rebuilt. The API requires prefix stability, not gaplessness; the true argument for row locking is that allocation order equals commit order. All five sequencing alternatives are recorded with verdicts — BIGSERIAL as incorrect rather than inelegant, and a per-run counter column as the identified strict improvement, deferred.
  • D-2 reversed from a mixin to composition, and now answers a question the first draft never asked: whether a checkpoint table is needed at all.
  • D-7 renames the table to review_checkpoints. ADR-007 is titled "Durable generation checkpoints", so the original name walked into the exact confusion Risk 1 predicts.
  • Purpose overclaim corrected, and the users' guide deliberately left unchanged as a result.
  • New risks for the silent no-commit window, the reaper failing runs awaiting review, editing an already-applied migration revision, and a shared creation-precondition blind spot that the contract suite would otherwise certify as correct.
  • Test strategy trimmed: plain scenario functions rather than an abstract base class; folded into the existing SQL suite and feature file rather than duplicating them; snapshot and xfail dance dropped; max_examples raised 5-6 → 25 on measured cost.
  • Tolerances re-based on measurement: 130.58 s for 1227 tests, 0.20-0.38 s per database test.

Validation

make fmt, make markdownlint, and make nixie all pass. No code gates apply; this is a documentation-only change.

References

Summary by Sourcery

Define the implementation plan for completing generation-run repository contracts and durable reviewer-checkpoint persistence without introducing production consumers.

Enhancements:

  • Add a comprehensive execution plan for durable reviewer-checkpoint persistence, repository contract completion, migration work, adapter equivalence, and gated lease reclamation.

Documentation:

  • Document the revised scope, architecture decisions, migration strategy, risks, verification obligations, and implementation milestones for roadmap item 2.6.2.

Tests:

  • Define cross-adapter contract, migration, durability, sequencing, and lease-reclamation validation strategies, including the limitations of the py-pglite test harness.

leynos and others added 4 commits August 23, 2026 03:29
…rations

This plan defines the scope, stages, and validation criteria for implementing
repository contracts and Alembic migrations for generation-run aggregates.
The plan covers:

- Verification of domain model from 2.6.1
- Definition of GenerationRunRepository and GenerationEventRepository protocols
- Alembic migration for generation_runs and generation_events tables
- SQLAlchemy ORM models and repository implementations
- Integration tests validating event ordering and persistence semantics
- Validation that all gates pass (check-fmt, typecheck, lint, test)

The plan is structured as 8 stages with explicit go/no-go validation points.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Replace the initial generation-run persistence execplan, which was
drafted without inspecting the codebase, with a version grounded in the
actual 2.6.1 contracts, prior art, and a community-of-experts design
review.

Corrections over the previous draft:

- Target the real ports in `episodic/canonical/generation_run_ports.py`
  (`create_run`, `get_run`, `list_runs`, `update_run_status`,
  `append_event`, `list_events`, and the checkpoint methods) rather than
  inventing `GenerationRunRepository.add`/`by_id` in a non-existent
  `episodic/generation/ports.py`. The ports already exist; this slice
  implements a PostgreSQL adapter for them.
- Chain the migration off the true head `20260601_000009`, not the stale
  `20260508_000008`, and note the single linear head despite two
  `000009` files.
- Mirror established storage patterns: `Base` declarative models, the
  history-table `UNIQUE(parent_id, seq)` + `CHECK (seq >= 1)` sequence
  pattern, `_RepositoryBase`, per-feature mappers, and `SqlAlchemyUnitOfWork`
  wiring.

Design decisions added after the Logisphere panel (proceed-with-conditions):

- Allocate per-run event `seq` as `MAX(seq)+1` under a unique constraint
  with a bounded retry inside a savepoint and a conflict metric, instead
  of escalating on first conflict; reject a global sequence (gaps) and a
  counter column (pattern divergence).
- Widen `seq` to `BIGINT`; add `(episode_id, created_at)` and
  `(episode_id, status, created_at)` indexes; specify deterministic
  `list_runs` ordering and FK `ON DELETE` semantics; add an `updated_at`
  trigger.
- Qualify behavioural equivalence to the single-writer case and document
  the py-pglite single-connection limitation; force the idempotency
  conflict branch in tests.

Status remains DRAFT pending approval before implementation.
The previous draft was authored before commit `5af0638` (roadmap 4.3.2)
landed durable generation-run and event persistence, the
`20260624_000010` migration, and Hypothesis event-ordering property
tests. It therefore asserted that no durable persistence existed and
targeted migration head `20260601_000009`, three revisions stale.

Rewrite the plan against the current tree and re-scope it to the genuine
remaining gap: reviewer-checkpoint persistence, a composite
`GenerationRunPort` implementation, cross-adapter contract equivalence,
and the lease-reclamation repository primitive that 4.3.2 deferred here
by name. Add the `Conformance basis` and `Verification plan` sections
the ExecPlan format requires, including per-obligation non-vacuity
checks and negative controls.
A six-lens design review found the plan promised evidence its test
harness cannot produce, and justified its central decision with a
requirement the API document does not state.

Two independent probes showed py-pglite serializes *all* transactions
globally, including unrelated sessions: it is a single WebAssembly
backend. The previous AXIOM-3 claimed it reproduces PostgreSQL locking
semantics. It does not, so INV-SEQ-1's concurrency obligation was
unfalsifiable and its negative control provably could not fire. Scope
the obligation to what is observable and move the concurrency claim to
`Residual gaps`.

Rebuild D-3 on prefix stability rather than gaplessness, which is what
`after_seq` replay actually requires, and record all five sequencing
alternatives with verdicts.

Other substantive changes:

- D-2 reverses the mixin decision in favour of composition, and answers
  whether a checkpoint table is needed at all.
- D-7 renames the table to `review_checkpoints`; ADR-007 is titled
  "Durable generation checkpoints", so the old name walked into the
  confusion Risk 1 predicts.
- Correct the Purpose overclaim: nothing creates a checkpoint in
  production today, so this is an enabling slice with zero consumers.
  Leave the users' guide unchanged accordingly.
- Add risks and obligations for the silent no-commit window, the reaper
  failing runs awaiting review, editing an applied migration revision,
  and the shared creation-precondition blind spot.
- Fold new tests into the existing SQL suite and feature file rather
  than duplicating them; drop the snapshot and the xfail dance; raise
  `max_examples` from 5-6 to 25 on measured cost.
- Re-base tolerances on measurement: 130.58s for 1227 tests, 0.20-0.38s
  per database test.
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Warning

Your free Security trial is over. An organization admin can activate billing to continue.

@sourcery-ai

sourcery-ai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a detailed execution plan document for roadmap item 2.6.2 describing how to implement generation-run repository contracts and Alembic migrations, including constraints, risks, verification strategy, schema design for a new review_checkpoints table, adapter architecture, and milestone breakdown, but no executable code changes.

Sequence diagram for durable review checkpoint response

sequenceDiagram
    participant Caller
    participant UOW as SqlAlchemyUnitOfWork
    participant Store as SqlAlchemyReviewCheckpointStore
    participant DB as PostgreSQL

    Caller->>UOW: create_checkpoint(...)
    UOW->>Store: create_checkpoint(...)
    Store->>DB: flush()
    Caller->>Store: respond_to_checkpoint(...)
    Store->>DB: with_for_update()
    Store->>DB: flush()
    Caller->>UOW: commit()
    UOW->>DB: COMMIT
    Caller->>UOW: read checkpoint in fresh unit of work
    UOW->>DB: SELECT review_checkpoints
    DB-->>Caller: responded checkpoint
Loading

ER diagram for review checkpoint persistence

erDiagram
    GENERATION_RUNS ||--o{ REVIEW_CHECKPOINTS : owns
    GENERATION_RUNS {
        uuid id PK
        enum status
    }
    REVIEW_CHECKPOINTS {
        uuid id PK
        uuid generation_run_id FK
        text prompt
        jsonb options
        enum status
        enum response_action
        timestamptz resolved_at
    }
Loading

File-Level Changes

Change Details Files
Introduce an execution plan document for implementing durable review checkpoint persistence, SQL adapters, migrations, and tests for generation runs.
  • Create docs/execplans/2-6-2-repository-contracts-and-alembic-migrations.md with a full execution plan for roadmap item 2.6.2.
  • Define constraints, tolerances, and risks around schema design, concurrency, migrations, and adapter behaviour.
  • Specify design decisions such as using a review_checkpoints table with PostgreSQL enums and JSONB, composition for the SQL checkpoint store, and continued pessimistic row locking for event sequencing.
  • Lay out a verification plan with concrete invariants (INV-SEQ, INV-CKPT, INV-MIG, INV-LEASE), how to test them, and explicit residual gaps (e.g. real PostgreSQL concurrency).
  • Describe a multi-milestone implementation path (M1–M5) covering schema, SQL checkpoint store, shared contract tests, lease reclamation primitive, and documentation updates, including guidance on tooling, test structure, and migration safety.
docs/execplans/2-6-2-repository-contracts-and-alembic-migrations.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f5ef0a28-dcbb-4851-a770-3f803c3802a1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

PR 278 takes ADR-018 and ADR-019, so this plan's ADR becomes 020. More
substantially, its ADR-018 is now the governing record for versioning,
concurrency shapes, immutability, and deletion policy, and it warns that
"applying the wrong [concurrency shape] produces either lost updates or
spurious conflicts". Three decisions change to conform:

- D-10 replaces `SELECT ... FOR UPDATE` on the checkpoint row with
  compare-and-set, the shape ADR-018 names for concurrent writers racing
  on one mutable row, mirroring `SqlAlchemyEpisodeRepository.update`.
  This also converts an obligation py-pglite cannot verify into one it
  can: a `WHERE status = 'created'` predicate is exercisable
  sequentially, whereas lock contention is not observable at all.
- The `review_checkpoints` foreign key becomes `ON DELETE RESTRICT`
  under ADR-018's audit-trail deletion policy.
- D-11 records why a review checkpoint is not a versioned aggregate and
  so needs no history table.

PR 277 edits the four files this plan changes most, so it is now a
stated dependency. It also establishes that persisted rows survive
between Hypothesis examples, which matters at `max_examples=25`; that
`uow.py` imports must stay outside `TYPE_CHECKING` because
`mock.create_autospec` evaluates annotations at runtime; and that
`integrity_helpers` already provides the constraint classification this
plan would otherwise hand-roll.

Add a `Pending pull requests` section, a risk for implementing ahead of
both, and the revised test baseline.
@leynos

leynos commented Aug 23, 2026

Copy link
Copy Markdown
Owner Author

Reconciled with PRs #278 and #277

Both were reviewed and the plan now depends on them. A new Pending pull requests this plan depends on section states the dependency and the rebase order.

PR #278 — versioning ADR (docs only)

  • ADR number moved 018 → 020. Record the versioning strategy and adopt episode TEI revision history (2.8) #278 takes 018 and 019.
  • adr-018-explicit-versioning-and-history-strategy.md is now a Conformance basis upstream item. It is the governing record for versioning, concurrency shapes, immutability, and deletion policy, and it warns that "applying the wrong [concurrency shape] produces either lost updates or spurious conflicts". Three decisions changed to conform:
    • D-10 (new): compare-and-set replaces SELECT ... FOR UPDATE for checkpoint transitions. ADR-018 names compare-and-set as the house shape for "concurrent writers racing on one mutable row" — exactly a checkpoint transition — with SqlAlchemyEpisodeRepository.update as the precedent. Beyond conformance this is a real improvement: a WHERE status = 'created' predicate is verifiable under py-pglite by mutating stored state between sequential calls, whereas FOR UPDATE blocking is unobservable (AXIOM-3). It converts an unverifiable design into a verifiable one, the same correction Risk 2 applies to INV-SEQ-1. INV-CKPT-2 gains a matching negative control.
    • Foreign key becomes ON DELETE RESTRICT under ADR-018's audit-trail deletion policy, rather than the bare FK the design review had settled on.
    • D-11 (new): a review checkpoint is not a versioned aggregate, so ADR-018's history-table requirement does not apply — it transitions to a terminal state once and the row is the audit record. Recorded to pre-empt the conformance question.
  • Roadmap 2.8 now exists; follow-ups from this plan must not claim that number. EP-M5 must rebase — Record the versioning strategy and adopt episode TEI revision history (2.8) #278 edits docs/contents.md, docs/roadmap.md, the design document, and the TUI API document, all of which EP-M5 also edits.

PR #277 — 4.3.2 alpha hardening (code + docs)

  • It edits uow.py, generation_run_ports.py, tests/canonical_storage/test_generation_runs.py, and test_sql_generation_run_property_contract.pythe four files this plan changes most. Rebase first.
  • Persisted rows survive between Hypothesis examples (the session factory outlives them); Harden the no-QA generation slice from local alpha testing (4.3.2) #277 scopes an idempotency key per example with uuid.uuid7(). This matters because this plan raises max_examples from 5-6 to 25, so the Tolerances entry now requires per-example data scoping.
  • uow.py imports were moved out of TYPE_CHECKING with # noqa: TC00x, because mock.create_autospec evaluates annotations at runtime. Stage C step 8 now requires any new import there to follow that convention, or the NameError Harden the no-QA generation slice from local alpha testing (4.3.2) #277 fixes returns.
  • count_events gained a Raises / RunNotFound docstring — precedent for Stage C step 7, and evidence the port file is where contracts are expected to live.
  • integrity_helpers.constraint_name already does the constraint classification INV-CKPT-5 needs; a new Constraints entry requires reusing it rather than hand-rolling.
  • Test baseline updated: 1237 passed / 3 skipped on Harden the no-QA generation slice from local alpha testing (4.3.2) #277 against 1227 on main; re-measure after rebasing.

New Risk 8 covers implementing ahead of either PR.

make fmt, make markdownlint, and make nixie pass.

codescene-access[bot]

This comment was marked as outdated.

@codescene-access codescene-access 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.

No quality gates enabled for this code.

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.

1 participant