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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Implementation Plan — Baseline-phase unit tests depend on suite ordering

**Date:** 2026-06-02
**Status:** Ready for Execution
**Status:** Complete (PR #466, squash-merged `6298e77`, 2026-06-05)
**Primary spec:** [feature_spec.md](feature_spec.md)
**Policy source(s):** [CLAUDE.md §"Bug Fix Protocol"](../../../../../CLAUDE.md), [docs/05_quality/testing.md](../../../../05_quality/testing.md)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Pipeline Status — Baseline-phase unit tests depend on suite ordering

## Idea
- Status: Complete
- File: idea.md

## Spec
- Status: Approved
- Date: 2026-06-02
- File: feature_spec.md
- Cross-model review: GPT-5.5 passed (2 cycles, 2 findings — 2 accepted, both Low)
- Phases: 1 total, 1 covered by spec (single-phase)

## Plan
- Status: Approved
- Date: 2026-06-02
- File: implementation_plan.md
- Cross-model review: GPT-5.5 passed (2 cycles, 1 finding — rejected-with-counter-evidence, Low)
- Stories: 2 total across 1 epic
- Phases covered: 1 of 1 (single-phase)

## Implementation
- Status: Complete (PR #466, squash-merged `6298e77`, 2026-06-05)
- Release: mvp2
- Note: Backend test-only, no migration. Story 1.1 deferred the `get_settings()` call in `_compute_baseline_wait_s` (`backend/workers/orchestrator.py`) into the falsy-`trial_timeout_s` branch so explicit-timeout callers never construct `Settings` (return values unchanged). Story 1.2 added an autouse `_settings_env_and_restore` fixture + a `test_explicit_timeout_does_not_read_settings` regression to `test_orchestrator_baseline_phase.py`. Standalone run with secrets unset: 14 passed (pre-fix `3 failed, 1 passed`); full unit suite 2400 passed. No Gemini findings; final GPT-5.5 skipped (≤40 LOC, test-only, below threshold). All 19 CI checks green.

This file was deleted.

16 changes: 8 additions & 8 deletions state.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions state_history.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@

---

### `bug_baseline_phase_test_isolation` — hermetic baseline-wait unit tests via lazy settings read (PR #466, 2026-06-05)

**What shipped.** `backend/workers/orchestrator.py`'s `_compute_baseline_wait_s(study)` computed `min(600, max(60, trial_timeout_s + 30))`, reading `study.config["trial_timeout_s"]` or falling back to `get_settings().studies_default_timeout_s`. The bug: it called `get_settings()` **unconditionally** at the top, even when an explicit `trial_timeout_s` was present and the settings value was never used. `Settings` construction requires `DATABASE_URL_FILE` + `POSTGRES_PASSWORD_FILE` (CLAUDE.md Rule #2), so the three explicit-timeout `TestComputeBaselineWaitS` cases only passed when an *earlier* test module in the same session had already seeded those env vars into the process — they failed standalone (`3 failed, 1 passed`) with the secrets unset. **Backend test-only — no migration, no API, no runtime behavior change** (Alembic head stays `0023`). 2 stories / 1 epic.

**The fix (both layers, per Bug Fix Protocol "fix at the right layer").** Story 1.1 (FR-1) defers the `get_settings()` read into the missing/falsy-`trial_timeout_s` branch — `trial_timeout_s = study.config.get("trial_timeout_s"); if not trial_timeout_s: trial_timeout_s = get_settings().studies_default_timeout_s` — so explicit-timeout callers never construct `Settings`. The falsy-fallback semantics (`None`/`0`/missing all fall back) and the formula constants are byte-for-byte preserved; return values are unchanged for every input. Story 1.2 (FR-2/FR-3) adds an autouse `_settings_env_and_restore` fixture (mirrors `test_poll_cron_kwargs.py`) that seeds both secret env vars at `/dev/null` and clears the **canonical** `get_settings` lru_cache imported from `backend.app.core.settings` — NOT `orch.get_settings`, which `test_missing_trial_timeout_uses_settings_default` monkeypatches to a plain lambda with no `.cache_clear()` — making the module hermetic regardless of collection order. It also adds `test_explicit_timeout_does_not_read_settings`, which monkeypatches `orch.get_settings` to a `_boom()` that raises, asserting the explicit-timeout path never calls it.

**Verification.** Reproduced the failure first: `env -u DATABASE_URL_FILE -u POSTGRES_PASSWORD_FILE pytest test_orchestrator_baseline_phase.py -p no:randomly` → `3 failed, 1 passed` pre-fix. Post-fix: **14 passed** standalone with secrets unset (AC-1/AC-3). The new regression test was confirmed to **fail** against the pre-fix production code (stashed Story 1.1 → `1 failed`) and passes post-fix (AC-2). Full unit suite `make test-unit`: 2400 passed, 1 skipped (AC-4, coverage gate satisfied). `make lint` 0 errors + `mypy --strict` clean on both touched files. No Gemini findings; final GPT-5.5 review skipped (≤40 LOC, test-only, no studies/judgments/adapter/migration surface — below the threshold). All 19 `pr.yml` checks green.

**Context.** Iteration 3 of the operator's "repeat this process three more times" run. Selected after the user chose "next locally-verifiable item" over the mechanically-top backlog item: the priority-sorted top ungated plan-ready item was `chore_demo_seeding_integration_tests_rewrite` (a 19-story DB-only integration-test rewrite unverifiable in this no-Docker/no-Postgres environment → CI-only), and the two `feat_` siblings above it are product-gated (see below). `bug_baseline_phase_test_isolation` is the highest-priority item whose tests run locally without a database. **Drift correction made this iteration:** iteration-2's finalization had described the two normalizer siblings (`feat_apply_path_normalizer_declaration`, `feat_query_normalizer_typed_pipeline`) as "now UNGATED" after PR #459; that overstated — PR #459 only cleared their G-1 *dependency* gate. Both retain **product gates** (apply-path: G-2 operator-friction evidence; typed-pipeline: open Q-1 Product decision + a "Do NOT `/impl-execute`" design-ahead banner) and are NOT auto-executable. `state.md`'s plan-ready line was corrected accordingly.

### `chore_cluster_detail_rung_badge` — cluster-detail UBI readiness card with rung badge (PR #464, 2026-06-05)

**What shipped.** A new `ClusterDetailUbiReadinessCard`, unconditionally mounted on `/clusters/[id]` between `<ClusterActionBar>` and `<ClusterDetailIndicesCard>`, so an operator can read a cluster's UBI readiness rung directly on the detail page instead of having to open the generate-judgments dialog (the only prior surface for `<UbiRungBadge>`). **Frontend-only — no backend, no new endpoint, no migration** (Alembic head stays `0023`). 8 stories / 1 epic, executed 8 → 1 → 2 → … → 7 (Story 8 first so the shared-hook change lands before the AC-8 assertions depend on it).
Expand Down