Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 28 additions & 13 deletions backend/tests/integration/test_migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,30 @@ def _sync_database_url() -> str:
return get_settings().database_url.replace("postgresql+asyncpg://", "postgresql://")


def _current_head() -> str:
"""Resolve the current Alembic head revision id at test-body execution time.

Invokes ``uv run alembic heads`` from the repo root and parses the first
whitespace-separated token of the single output line as the head id.
Asserts the chain has exactly one head — if a future branch lands and
Alembic emits multiple head lines, silently picking the first would mask
a real schema-design issue. Raise ``AssertionError`` instead, naming
``alembic merge`` as the canonical fix.

MUST be called inside test bodies, NOT at module import. The module-level
``pytestmark = pytest.mark.skipif(not _postgres_reachable(), ...)`` only
runs at collection time; an import-time invocation would bypass it and
fail on hosts where the integration tests are meant to skip.
"""
result = _alembic("heads").stdout
lines = [line for line in result.splitlines() if line.strip()]
assert len(lines) == 1, (
f"Expected exactly one Alembic head, got {len(lines)}: {lines!r}. "
"Run `alembic merge` to consolidate."
)
return lines[0].split()[0]


@pytest.fixture
def fresh_db() -> Iterator[None]:
"""Roll back to base, then leave whatever state the test ends in.
Expand Down Expand Up @@ -120,16 +144,8 @@ def test_upgrade_head_creates_alembic_version(self, fresh_db: None) -> None:
result = conn.execute(text("SELECT version_num FROM alembic_version"))
row = result.fetchone()
assert row is not None, "alembic_version table empty after upgrade head"
# Baseline is "0001" per migrations/versions/0001_baseline.py.
# Head extended by feat_data_table_primitive migrations
# 0008–0013 (search_vector columns + GIN indexes on 6 tables);
# 0014 adds clusters.target_filter (feat_cluster_target_filter);
# 0015 adds trials.per_query_metrics (feat_pr_metric_confidence);
# 0016 adds config_repos.last_merged_proposal_id
# (feat_config_repo_baseline_tracking);
# 0017 adds proposals.last_polled_at
# (chore_reconciler_terminal_closed_no_poll).
assert row[0] == "0017"
# Head is resolved dynamically via _current_head() — see helper docstring.
assert row[0] == _current_head()
finally:
engine.dispose()

Expand All @@ -152,9 +168,8 @@ def test_round_trip(self, fresh_db: None) -> None:
with engine.connect() as conn:
row = conn.execute(text("SELECT version_num FROM alembic_version")).fetchone()
assert row is not None
# Head: 0017 (chore_reconciler_terminal_closed_no_poll adds
# proposals.last_polled_at).
assert row[0] == "0017"
# Head is resolved dynamically via _current_head() — see helper docstring.
assert row[0] == _current_head()
finally:
engine.dispose()

Expand Down
2 changes: 1 addition & 1 deletion docs/00_overview/DASHBOARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ _Top-level index across MVP1 → GA v1+ as of **2026-05-23**. Click a release na

| Release | Theme | Progress | Status |
|---|---|---|---|
| [MVP1 / v0.1](MVP1_DASHBOARD.md) | The Loop | 69 / 69 scoped done · 5 remaining | **In progress** |
| [MVP1 / v0.1](MVP1_DASHBOARD.md) | The Loop | 69 / 70 scoped done · 6 remaining | **In progress** |
| [MVP1.5 / v0.1.5](MVP1_5_DASHBOARD.md) | Real Signals | 1 item(s) queued | **Held / queued** |
| [MVP2 / v0.2](MVP2_DASHBOARD.md) | Observable | 1 / 1 scoped done · 1 remaining | **In progress** |
| MVP3 / v0.3 | Production Stacks | — | **Not yet scoped** |
Expand Down
28 changes: 19 additions & 9 deletions docs/00_overview/MVP1_DASHBOARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@ _Reflects feature-folder state as of **2026-05-23** (latest mtime of any planned

## Next up

All scoped MVP1 features shipped 🎉
**[chore_migration_test_head_brittleness](../02_product/planned_features/chore_migration_test_head_brittleness/feature_spec.md)** — Chore, currently in **Plan**

Pull from the Idea backlog or capture a new feature spec.
> Replace the two hardcoded `"0017"` literals with a dynamic `_current_head()` helper that shells out to `uv run alembic heads` and returns the current head revision id.

Plan approved; run /impl-execute to ship

```bash
/impl-execute docs/02_product/planned_features/chore_migration_test_head_brittleness/implementation_plan.md --all
```

## MVP1 Progress

| Metric | Value |
|---|---|
| Scoped items done | **69 / 69** (100%) — feat_/infra_/chore_/epic_ past idea stage |
| Pending work | **12** items (every not-done feat/infra/chore/bug across all priorities) |
| Scoped items done | **69 / 70** (99%) — feat_/infra_/chore_/epic_ past idea stage |
| Pending work | **13** items (every not-done feat/infra/chore/bug across all priorities) |
| → P0 — do next | **0** unblocking / paying daily cost |
| → P1 | **0** high-value, ready when P0 clears |
| → P2 (default) | 11 important to file, not blocking |
| → P2 (default) | 12 important to file, not blocking |
| → Backlog | 1 captured for record, not planned |
| Open bugs | 0 |
| Legacy "Path to MVP1" | 5 items — scoped-not-done + bugs + chore-ideas only (excludes feat/infra ideas) |
| Legacy "Path to MVP1" | 6 items — scoped-not-done + bugs + chore-ideas only (excludes feat/infra ideas) |
| Backlog ideas | 7 idea-only feat/infra (not yet scoped into MVP1) |
| In flight | 0 feature(s) actively shipping |

Expand Down Expand Up @@ -122,9 +128,11 @@ Pull from the Idea backlog or capture a new feature spec.

_None._

### Plan (0)
### Plan (1)

_None._
| # | Priority | Feature | Type | One-liner | Depends on | Status |
|---|---|---|---|---|---|---|
| 1 | P2 | [chore_migration_test_head_brittleness](../02_product/planned_features/chore_migration_test_head_brittleness/feature_spec.md) | Chore | Replace the two hardcoded `"0017"` literals with a dynamic `_current_head()` helper that shells out to `uv run alembic heads` and returns the current head revision id. | — | — |

### Spec (0)

Expand All @@ -142,7 +150,7 @@ _None._
| 6 | P2 | [infra_agent_sibling_worktree_isolation](../02_product/planned_features/infra_agent_sibling_worktree_isolation/idea.md) | Infra | Running an autonomous agent in a sibling git worktree while the operator's main checkout has the Docker Compose stack up exposes two surfaces that aren't designed for parallel work: | — | Idea — tangential observations from the autonomous `chore_reconciler_terminal_closed_no_poll` agent run (PR #216, merged 2026-05-23) |
| 7 | P2 | [infra_study_preflight_real_engine_integration](../02_product/planned_features/infra_study_preflight_real_engine_integration/idea.md) | Infra | `feat_study_preflight_overlap_probe`'s integration tests (AC-1 through AC-4b in [`backend/tests/integration/test_studies_api.py`](../../backend/tests/integration/test_studies_api.py)) use… | — | Idea — surfaced during `feat_study_preflight_overlap_probe` (PR ___) phase-gate review |
| 8 | P2 | [chore_dashboard_pr_extraction_from_idea](../02_product/planned_features/chore_dashboard_pr_extraction_from_idea/idea.md) | Chore | Several early MVP1 features shipped before the `/pipeline` ceremony solidified, leaving them with only an `idea.md` in `implemented_features/<date>_<slug>/`. Examples (as of 2026-05-23): | — | Idea — surfaced during the tangential-observations sweep of `bug_dashboard_depends_on_column_bloat` (PR pending) |
| 9 | P2 | [chore_migration_test_head_brittleness](../02_product/planned_features/chore_migration_test_head_brittleness/idea.md) | Chore | [`backend/tests/integration/test_migrations.py`](../../backend/tests/integration/test_migrations.py) has two assertions: | — | Idea — surfaced during `chore_reconciler_terminal_closed_no_poll` implementation |
| 9 | P2 | [chore_e2e_seed_acme_idea_obsolete](../02_product/planned_features/chore_e2e_seed_acme_idea_obsolete/idea.md) | Chore | [`chore_e2e_seed_acme_helper_dead/idea.md`](../02_product/planned_features/chore_e2e_seed_acme_helper_dead/idea.md) (dated 2026-05-21) proposed two paths: | — | Idea — surfaced during `chore_migration_test_head_brittleness` `/idea-preflight` pick (2026-05-23) |
| 10 | P2 | [chore_studies_post_arq_spy_fixture](../02_product/planned_features/chore_studies_post_arq_spy_fixture/idea.md) | Chore | The studies POST handler at [`backend/app/api/v1/studies.py:307`](../../backend/app/api/v1/studies.py#L307) calls `await _enqueue_start_study(request, study_id)` after a successful create. The helper | — | Idea — surfaced during `feat_study_preflight_overlap_probe` (PR ___) phase-gate review |
| 11 | P2 | [chore_template_library_expansion](../02_product/planned_features/chore_template_library_expansion/idea.md) | Chore | Three connected gaps: | — | Idea — surfaced during a UX review of parameter-tuning ergonomics on 2026-05-19. |
| 12 | Backlog | [chore_e2e_seed_acme_helper_dead](../02_product/planned_features/chore_e2e_seed_acme_helper_dead/idea.md) | Chore | `seedAcmeProductsChain` is a 140-line helper that constructs a cluster + query_set + template + judgment_list + study + optional proposal/digest chain "Acme Products" demo scenario. The function is co | — | Idea — surfaced during `chore_e2e_test_rows_isolation` Story 1.2 coverage audit |
Expand All @@ -158,6 +166,8 @@ graph LR
classDef plan fill:#fef9c3,stroke:#854d0e,color:#854d0e;
classDef spec fill:#dbeafe,stroke:#1e40af,color:#1e40af;
classDef idea fill:#f1f5f9,stroke:#334155,color:#334155;
chore_migration_test_head_brittleness["migration test head brittleness"]
class chore_migration_test_head_brittleness plan;
infra_foundation["foundation"]
class infra_foundation done;
feat_study_lifecycle["study lifecycle"]
Expand Down
2 changes: 1 addition & 1 deletion docs/00_overview/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ <h2>Releases</h2>
<div class="roadmap-row">
<div class="release-name"><a href="mvp1_dashboard.html">MVP1 / v0.1</a></div>
<div class="theme">The Loop</div>
<div class="progress">69 / 69 scoped done · 5 remaining</div>
<div class="progress">69 / 70 scoped done · 6 remaining</div>
<span class="state-pill in_progress">In progress</span>
</div>

Expand Down
48 changes: 32 additions & 16 deletions docs/00_overview/mvp1_dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -382,28 +382,28 @@ <h1>RelyLoop MVP1 Dashboard</h1>
<main>

<section>
<div class="next-up done">
<div class="eyebrow">Next up</div>
<div class="title">All scoped MVP1 features shipped 🎉</div>
<div class="one-liner">
Pull from the Idea backlog or capture a new feature spec.
</div>
<div class="next-up">
<div class="eyebrow">Next up — Chore, currently in <strong>Plan</strong></div>
<div class="title"><a href="../../docs/02_product/planned_features/chore_migration_test_head_brittleness/feature_spec.md">Migration Test Head Brittleness</a></div>
<div class="one-liner">Replace the two hardcoded `&quot;0017&quot;` literals with a dynamic `_current_head()` helper that shells out to `uv run alembic heads` and returns the current head revision id.</div>
<div class="stage-hint">Plan approved; run /impl-execute to ship</div>
<code class="cmd">/impl-execute docs/02_product/planned_features/chore_migration_test_head_brittleness/implementation_plan.md --all</code>
</div>
</section>


<section>
<h2>MVP1 Progress</h2>
<div class="kpi-row">
<div class="kpi complete">
<div class="kpi ">
<div class="label">Scoped items done</div>
<div class="value">69 / 69</div>
<div class="sub">100% of feat_/infra_/chore_/epic_ items past idea stage</div>
<div class="bar"><span style="width:100%"></span></div>
<div class="value">69 / 70</div>
<div class="sub">99% of feat_/infra_/chore_/epic_ items past idea stage</div>
<div class="bar"><span style="width:99%"></span></div>
</div>
<div class="kpi warn">
<div class="label">Pending work</div>
<div class="value">12</div>
<div class="value">13</div>
<div class="sub">every not-done feat/infra/chore/bug across all priorities</div>
</div>
<div class="kpi ">
Expand All @@ -425,7 +425,7 @@ <h2>MVP1 Progress</h2>
</div>
<div class="kpi">
<div class="label">P2 (default)</div>
<div class="value">11</div>
<div class="value">12</div>
<div class="sub">important to file, not blocking</div>
</div>
<div class="kpi">
Expand All @@ -435,7 +435,7 @@ <h2>MVP1 Progress</h2>
</div>
<div class="kpi">
<div class="label">Legacy "Path to MVP1"</div>
<div class="value">5</div>
<div class="value">6</div>
<div class="sub">scoped not-done + bugs + chore-ideas only (excludes feat/infra ideas)</div>
</div>
</div>
Expand Down Expand Up @@ -570,13 +570,13 @@ <h3>Idea <span class="count">12</span></h3>


<div class="card chore" data-prefix="chore" data-priority="P2">
<div class="name"><a href="../../docs/02_product/planned_features/chore_migration_test_head_brittleness">Migration Test Head Brittleness</a></div>
<div class="name"><a href="../../docs/02_product/planned_features/chore_e2e_seed_acme_idea_obsolete">E2E Seed Acme Idea Obsolete</a></div>
<div class="meta">
<span class="badge chore">Chore</span>
<span class="badge priority" data-priority="P2">P2</span>

</div>
<div class="one-liner">[`backend/tests/integration/test_migrations.py`](../../backend/tests/integration/test_migrations.py) has two assertions:</div>
<div class="one-liner">[`chore_e2e_seed_acme_helper_dead/idea.md`](../02_product/planned_features/chore_e2e_seed_acme_helper_dead/idea.md) (dated 2026-05-21) proposed two paths:</div>


</div>
Expand Down Expand Up @@ -628,7 +628,19 @@ <h3>Spec <span class="count">0</span></h3>
</div>

<div class="col plan">
<h3>Plan <span class="count">0</span></h3>
<h3>Plan <span class="count">1</span></h3>

<div class="card chore" data-prefix="chore" data-priority="P2">
<div class="name"><a href="../../docs/02_product/planned_features/chore_migration_test_head_brittleness/feature_spec.md">Migration Test Head Brittleness</a></div>
<div class="meta">
<span class="badge chore">Chore</span>
<span class="badge priority" data-priority="P2">P2</span>

</div>
<div class="one-liner">Replace the two hardcoded `&quot;0017&quot;` literals with a dynamic `_current_head()` helper that shells out to `uv run alembic heads` and returns the current head revision id.</div>


</div>

</div>

Expand Down Expand Up @@ -1770,6 +1782,8 @@ <h2>Dependency graph (feat_ + infra_)</h2>
classDef plan fill:#fef9c3,stroke:#854d0e,color:#854d0e;
classDef spec fill:#dbeafe,stroke:#1e40af,color:#1e40af;
classDef idea fill:#f1f5f9,stroke:#334155,color:#334155;
chore_migration_test_head_brittleness[&quot;migration test head brittleness&quot;]
class chore_migration_test_head_brittleness plan;
infra_foundation[&quot;foundation&quot;]
class infra_foundation done;
feat_study_lifecycle[&quot;study lifecycle&quot;]
Expand Down Expand Up @@ -1961,6 +1975,8 @@ <h2>Dependency graph (feat_ + infra_)</h2>
classDef plan fill:#fef9c3,stroke:#854d0e,color:#854d0e;
classDef spec fill:#dbeafe,stroke:#1e40af,color:#1e40af;
classDef idea fill:#f1f5f9,stroke:#334155,color:#334155;
chore_migration_test_head_brittleness[&quot;migration test head brittleness&quot;]
class chore_migration_test_head_brittleness plan;
infra_foundation[&quot;foundation&quot;]
class infra_foundation done;
feat_study_lifecycle[&quot;study lifecycle&quot;]
Expand Down
Loading
Loading