TDD: MigrationRunner.get_pending_migrations() uses get_current_revision() which misses multi-head database states and returns incorrect pending list #10450

Open
opened 2026-04-18 09:42:53 +00:00 by HAL9000 · 0 comments
Owner

Summary

Write a failing BDD test that captures the bug in MigrationRunner.get_pending_migrations() where get_current_revision() is used instead of get_current_heads(), causing incorrect pending migration detection when the database has been migrated along a branched revision path.

Bug Being Captured

MigrationRunner.get_pending_migrations() in src/cleveragents/infrastructure/database/migration_runner.py (lines 162–192) uses MigrationContext.get_current_revision() which returns only a single revision string. When the Alembic migration graph has multiple heads (which occurs in this codebase — e.g., m8_002_merge_profile_rename_and_corrections merges three branches), a database that has been migrated along one branch will have its current revision correctly identified. However, the subsequent walk logic:

pending: list[str] = []
for rev in script_dir.walk_revisions():
    if rev.revision == current_rev:
        break
    pending.append(rev.revision)
return list(reversed(pending))

…walks from head to base and stops when it finds current_rev. If current_rev is on a branch that was merged (e.g., m5_001_rename_profile_fields), the walk may encounter sibling branches before reaching current_rev, incorrectly including those sibling revisions in the "pending" list even though they are also already applied (or should not be applied independently).

Additionally, get_current_revision() returns None when the database has multiple current revisions (multi-head state), causing the method to incorrectly report ALL migrations as pending.

Subtasks

  • Add a Behave scenario in features/steps/migration_runner_steps.py (or create features/migration_runner.feature) that:
    • Creates an in-memory SQLite database
    • Stamps the database at a branched intermediate revision (e.g., m5_001_rename_profile_fields) using Alembic's stamp command
    • Calls MigrationRunner.get_pending_migrations()
    • Asserts that the returned list contains only the revisions that are genuinely pending (i.e., those after the merge point, not sibling branches)
    • Asserts that sibling branch revisions (m8_001_correction_attempts, m8_001_align_plans_schema) are NOT incorrectly included as pending when they should be
  • Tag the scenario with @tdd_issue, @tdd_issue_<N> (where N is the bug issue number), and @tdd_expected_fail
  • Verify the test fails with the current implementation (proving the bug exists)
  • Verify the test passes CI with @tdd_expected_fail tag present

Acceptance Criteria

  • A Behave scenario exists that proves get_pending_migrations() returns incorrect results for branched migration states
  • The scenario is tagged @tdd_issue, @tdd_issue_<N>, and @tdd_expected_fail
  • CI passes with the tag present

Background and Context

MigrationRunner.get_pending_migrations() is responsible for determining which Alembic migrations are pending for a given database. The current implementation uses get_current_revision(), which only returns a single revision string and fails in multi-head scenarios. This codebase uses branched Alembic migration graphs (e.g., m8_002_merge_profile_rename_and_corrections merges three branches), making this a real-world risk for incorrect migration state detection. This TDD issue captures the bug with a failing test before the fix is implemented.

Expected Behavior

MigrationRunner.get_pending_migrations() should correctly identify pending migrations even when the database is in a branched or multi-head Alembic state. It should use get_current_heads() (which returns a set of all current revision heads) rather than get_current_revision(), and the walk logic should account for all applied revisions — not just a single linear path.

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.

Metadata

  • Branch: tdd/mX-migration-runner-pending-multi-head
  • Commit Message: test(migration): add tdd test for MigrationRunner.get_pending_migrations() multi-head bug

Automated by CleverAgents Bot
Agent: new-issue-creator


Automated by CleverAgents Bot
Supervisor: Bug Hunt Pool | Agent: bug-hunt-pool-supervisor
Tag: [AUTO-BUG-4]

## Summary Write a failing BDD test that captures the bug in `MigrationRunner.get_pending_migrations()` where `get_current_revision()` is used instead of `get_current_heads()`, causing incorrect pending migration detection when the database has been migrated along a branched revision path. ## Bug Being Captured `MigrationRunner.get_pending_migrations()` in `src/cleveragents/infrastructure/database/migration_runner.py` (lines 162–192) uses `MigrationContext.get_current_revision()` which returns only a single revision string. When the Alembic migration graph has multiple heads (which occurs in this codebase — e.g., `m8_002_merge_profile_rename_and_corrections` merges three branches), a database that has been migrated along one branch will have its current revision correctly identified. However, the subsequent walk logic: ```python pending: list[str] = [] for rev in script_dir.walk_revisions(): if rev.revision == current_rev: break pending.append(rev.revision) return list(reversed(pending)) ``` …walks from head to base and stops when it finds `current_rev`. If `current_rev` is on a branch that was merged (e.g., `m5_001_rename_profile_fields`), the walk may encounter sibling branches before reaching `current_rev`, incorrectly including those sibling revisions in the "pending" list even though they are also already applied (or should not be applied independently). Additionally, `get_current_revision()` returns `None` when the database has multiple current revisions (multi-head state), causing the method to incorrectly report ALL migrations as pending. ## Subtasks - [ ] Add a Behave scenario in `features/steps/migration_runner_steps.py` (or create `features/migration_runner.feature`) that: - Creates an in-memory SQLite database - Stamps the database at a branched intermediate revision (e.g., `m5_001_rename_profile_fields`) using Alembic's `stamp` command - Calls `MigrationRunner.get_pending_migrations()` - Asserts that the returned list contains only the revisions that are genuinely pending (i.e., those after the merge point, not sibling branches) - Asserts that sibling branch revisions (`m8_001_correction_attempts`, `m8_001_align_plans_schema`) are NOT incorrectly included as pending when they should be - [ ] Tag the scenario with `@tdd_issue`, `@tdd_issue_<N>` (where N is the bug issue number), and `@tdd_expected_fail` - [ ] Verify the test fails with the current implementation (proving the bug exists) - [ ] Verify the test passes CI with `@tdd_expected_fail` tag present ## Acceptance Criteria - A Behave scenario exists that proves `get_pending_migrations()` returns incorrect results for branched migration states - The scenario is tagged `@tdd_issue`, `@tdd_issue_<N>`, and `@tdd_expected_fail` - CI passes with the tag present ## Background and Context `MigrationRunner.get_pending_migrations()` is responsible for determining which Alembic migrations are pending for a given database. The current implementation uses `get_current_revision()`, which only returns a single revision string and fails in multi-head scenarios. This codebase uses branched Alembic migration graphs (e.g., `m8_002_merge_profile_rename_and_corrections` merges three branches), making this a real-world risk for incorrect migration state detection. This TDD issue captures the bug with a failing test before the fix is implemented. ## Expected Behavior `MigrationRunner.get_pending_migrations()` should correctly identify pending migrations even when the database is in a branched or multi-head Alembic state. It should use `get_current_heads()` (which returns a set of all current revision heads) rather than `get_current_revision()`, and the walk logic should account for all applied revisions — not just a single linear path. ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional lines providing relevant details about the implementation. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly. - The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done. ## Metadata - **Branch:** `tdd/mX-migration-runner-pending-multi-head` - **Commit Message:** `test(migration): add tdd test for MigrationRunner.get_pending_migrations() multi-head bug` --- **Automated by CleverAgents Bot** Agent: new-issue-creator --- Automated by CleverAgents Bot Supervisor: Bug Hunt Pool | Agent: bug-hunt-pool-supervisor Tag: [AUTO-BUG-4]
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#10450
No description provided.