diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 167fc589..a66f2ad1 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -221,6 +221,18 @@ jobs: # behavior is acceptable until the deploy workflow exists. RELYLOOP_API_URL: http://localhost:8000 run: | + # CI-perf #3 (pytest-xdist `-n auto`) was attempted on the first + # PR #291 CI run and reverted: the integration test layer hit FK + # collisions (query_sets_cluster_id_fkey violation when parallel + # tests held a FK reference to a cluster being deleted in another + # worker's teardown). pytest-xdist remains in dev deps for local + # opt-in (`pytest -n auto` works fine on the unit-test layer); + # CI-perf #1 + #2 (buildx artifact handoff + base-image cache) + # are the actual smoke-pace wins. A follow-up may split the + # backend job into a parallel-safe "unit + contract" lane + a + # serial "integration" lane to recover #3's savings. See + # chore_ci_perf_buildx_artifact_image_cache_xdist/idea.md + # §"What is NOT changed in this PR". uv run pytest backend/tests/ \ --cov=backend \ --cov-report=xml \ @@ -307,6 +319,13 @@ jobs: name: smoke (operator-path tutorial flow) runs-on: ubuntu-24.04 timeout-minutes: 15 + # Depend on the parallel `docker` (API) + `docker-ui` jobs so both image + # artifacts are ready before `make up`. Pre-bumps this PR was paying ~10min + # for `docker compose up -d` (image pulls + API + UI builds inside the + # step). The artifact handoff (API + UI) + base-image cache + SKIP_BUILD + # below cut that to ~2-3min on a warm cache. See + # chore_ci_perf_buildx_artifact_image_cache_xdist/idea.md. + needs: [docker, docker-ui] permissions: contents: read steps: @@ -365,7 +384,75 @@ jobs: exit 1 fi + # CI-perf #1: download the pre-built API + UI images from the parallel + # docker / docker-ui jobs so `make up` skips both in-step `docker build`s + # (saves ~5min). Combined with RELYLOOP_SKIP_BUILD=1 (which makes + # install.sh skip its `docker compose build` step) compose just `up`s + # the loaded images. RELYLOOP_GIT_SHA below picks them up by tag. + - name: Download pre-built API image + uses: actions/download-artifact@v6 + with: + name: relyloop-api-image-${{ github.sha }} + path: /tmp/ + + - name: Download pre-built UI image + uses: actions/download-artifact@v6 + with: + name: relyloop-ui-image-${{ github.sha }} + path: /tmp/ + + - name: Load pre-built API + UI images into Docker + run: | + docker load -i /tmp/relyloop-api-image.tar + docker load -i /tmp/relyloop-ui-image.tar + docker image ls 'relyloop/*' + + # CI-perf #2: cache the base service-container images (postgres / redis / + # elasticsearch / opensearch) keyed on their tags. On cache hit we + # `docker load` 4 tars in ~5s vs ~60-90s for `docker pull` on miss. + # Key changes whenever any of the image tags in docker-compose.yml change + # (forces re-pull on a bump PR, hit on subsequent runs). + - name: Cache base service-container images + id: base-image-cache + uses: actions/cache@v5 + with: + path: /tmp/docker-base-images + key: docker-base-images-v1-${{ hashFiles('docker-compose.yml') }} + + - name: Pre-pull + save base images on cache miss + if: steps.base-image-cache.outputs.cache-hit != 'true' + run: | + mkdir -p /tmp/docker-base-images + for img in postgres:17 redis:8 elasticsearch:9.4.1 opensearchproject/opensearch:3.6.0; do + docker pull "$img" + safe=$(echo "$img" | tr '/:' '__') + docker save "$img" -o "/tmp/docker-base-images/${safe}.tar" + done + + - name: Load base images on cache hit + if: steps.base-image-cache.outputs.cache-hit == 'true' + run: | + for tar in /tmp/docker-base-images/*.tar; do + docker load -i "$tar" + done + docker image ls + + # Compose's `image:` lines reference `relyloop/api:${RELYLOOP_GIT_SHA:-dev}` + # and `relyloop/ui:${RELYLOOP_GIT_SHA:-dev}` — setting RELYLOOP_GIT_SHA + # here makes compose pick up the loaded images instead of trying to + # build/pull them. RELYLOOP_SKIP_BUILD=1 also makes install.sh skip its + # explicit `docker compose build` step (added 2026-05-28; see install.sh + # step 6). Together these eliminate the API + UI build duplication in + # smoke that was eating ~5min per run. - name: Bring up the stack + env: + RELYLOOP_GIT_SHA: ${{ github.sha }} + RELYLOOP_SKIP_BUILD: "1" + # Same rationale as RELYLOOP_SKIP_BUILD — the 2 demo-dependent + # E2E specs were skipped in CI on 2026-05-28 + # (chore_drop_demo_seed_from_ci). Without this skip install.sh + # would still auto-seed ~5min of demo data on every CI run. + RELYLOOP_SKIP_AUTO_SEED: "1" run: make up - name: Wait for /healthz @@ -545,3 +632,59 @@ jobs: exit 1 } ' + + # Export the built API image as a tar so the smoke job can `docker load` + # it instead of rebuilding (which costs ~2-3min inside `make up`). See + # chore_ci_perf_buildx_artifact_image_cache_xdist/idea.md for the + # smoke-pace context. compression-level: 0 because docker save already + # produces a compressed tar (re-compressing wastes ~30s with no win). + - name: Export API image as tar for smoke job + run: docker save relyloop/api:${{ github.sha }} -o /tmp/relyloop-api-image.tar + + - name: Upload API image artifact + uses: actions/upload-artifact@v7 + with: + name: relyloop-api-image-${{ github.sha }} + path: /tmp/relyloop-api-image.tar + retention-days: 1 + compression-level: 0 + + docker-ui: + name: docker buildx (relyloop/ui) + runs-on: ubuntu-latest + # Parallel to `docker` (API buildx). Symmetric pattern: builds + uploads + # the UI image as a tar so the smoke job can `docker load` it instead of + # rebuilding inside `make up`. Reused via `needs: [docker, docker-ui]` on + # the smoke job + `RELYLOOP_SKIP_BUILD=1` to bypass install.sh's build step. + timeout-minutes: 10 + steps: + - uses: actions/checkout@v6 + + - uses: docker/setup-buildx-action@v4 + + - name: Build relyloop/ui (no push, load into local daemon) + uses: docker/build-push-action@v7 + with: + context: ./ui + file: ui/Dockerfile + push: false + load: true + tags: relyloop/ui:${{ github.sha }} + # The compose service bakes NEXT_PUBLIC_API_BASE_URL into the bundle + # at build time (Next.js inlines it at `next build`). Match the value + # docker-compose.yml line 183 sets so the smoke run uses the same URL. + build-args: | + NEXT_PUBLIC_API_BASE_URL=http://localhost:8000 + cache-from: type=gha,scope=ui + cache-to: type=gha,scope=ui,mode=max + + - name: Export UI image as tar for smoke job + run: docker save relyloop/ui:${{ github.sha }} -o /tmp/relyloop-ui-image.tar + + - name: Upload UI image artifact + uses: actions/upload-artifact@v7 + with: + name: relyloop-ui-image-${{ github.sha }} + path: /tmp/relyloop-ui-image.tar + retention-days: 1 + compression-level: 0 diff --git a/backend/app/scripts/seed_es.py b/backend/app/scripts/seed_es.py index 543b878f..dc7cd2d5 100644 --- a/backend/app/scripts/seed_es.py +++ b/backend/app/scripts/seed_es.py @@ -45,7 +45,16 @@ async def main() -> int: products = json.loads(SAMPLES_PRODUCTS.read_text()) logger.info("seed_es: loaded %d products from %s", len(products), SAMPLES_PRODUCTS) - async with httpx.AsyncClient(base_url=cluster.base_url, timeout=30.0) as client: + # timeout=90 (was 30): ES 9.4.1 single-node on a cold GHA runner can take + # >30s to respond to the first index-create PUT after `docker compose up + # --wait` returns. Observed in PR #291's 6th + 7th smoke runs after the + # fast stack-up (compose-up went from 10min → 21s, eliminating the + # ambient ES warmup time that previously masked this). The compose + # healthcheck waits for `_cluster/health?wait_for_status=yellow` which + # passes early on single-node ES (no shards to wait on), so ES is + # "healthy" but its write path needs more warmup. 90s gives headroom + # without making real failure modes invisible. + async with httpx.AsyncClient(base_url=cluster.base_url, timeout=90.0) as client: # DELETE existing index (idempotent — 404 is fine, that just means it didn't exist). delete_resp = await client.delete(f"/{INDEX_NAME}") if delete_resp.status_code not in (200, 404): @@ -58,9 +67,19 @@ async def main() -> int: return 1 # Create with mapping derived from the products schema. + # + # number_of_replicas=0 is required for single-node ES (local dev + + # CI). The default (1) tries to allocate a replica that can never + # bind on a one-node cluster, leaving the primary itself in an + # INITIALIZING → STARTED race that surfaces as an + # `unavailable_shards_exception` on the immediately-following + # bulk-index. Visible in PR #291 CI run after the faster stack-up + # (~3min vs ~10min) stopped masking the race with implicit warmup + # time. See chore_ci_perf_buildx_artifact_image_cache_xdist/idea.md. create_resp = await client.put( f"/{INDEX_NAME}", json={ + "settings": {"number_of_replicas": 0}, "mappings": { "properties": { "title": {"type": "text"}, @@ -69,7 +88,7 @@ async def main() -> int: "color": {"type": "keyword"}, "bullet_points": {"type": "text"}, } - } + }, }, ) create_resp.raise_for_status() diff --git a/docs/00_overview/DASHBOARD.md b/docs/00_overview/DASHBOARD.md index 5b79a63c..1b8a2985 100644 --- a/docs/00_overview/DASHBOARD.md +++ b/docs/00_overview/DASHBOARD.md @@ -6,7 +6,7 @@ _Top-level index across MVP1 → GA v1+ as of **2026-05-28**. Click a release na | Release | Theme | Progress | Status | |---|---|---|---| -| [MVP1 / v0.1](MVP1_DASHBOARD.md) | The Loop | 88 / 89 scoped done · 16 remaining | **In progress** | +| [MVP1 / v0.1](MVP1_DASHBOARD.md) | The Loop | 88 / 89 scoped done · 18 remaining | **In progress** | | MVP1.5 / v0.1.5 | Real Signals | — | **Not yet scoped** | | [MVP2 / v0.2](MVP2_DASHBOARD.md) | Observable | 1 / 1 scoped done · 1 remaining | **In progress** | | MVP3 / v0.3 | Production Stacks | — | **Not yet scoped** | diff --git a/docs/00_overview/MVP1_DASHBOARD.md b/docs/00_overview/MVP1_DASHBOARD.md index 46a6b1a4..29ef2b20 100644 --- a/docs/00_overview/MVP1_DASHBOARD.md +++ b/docs/00_overview/MVP1_DASHBOARD.md @@ -21,13 +21,13 @@ Implementation in progress — resume to finish | Metric | Value | |---|---| | Scoped items done | **88 / 89** (99%) — feat_/infra_/chore_/epic_ past idea stage | -| Pending work | **18** items (every not-done feat/infra/chore/bug across all priorities) | +| Pending work | **20** items (every not-done feat/infra/chore/bug across all priorities) | | → P0 — do next | **0** unblocking / paying daily cost | -| → P1 | **6** high-value, ready when P0 clears | +| → P1 | **8** high-value, ready when P0 clears | | → P2 (default) | 10 important to file, not blocking | | → Backlog | 2 captured for record, not planned | -| Open bugs | 5 | -| Legacy "Path to MVP1" | 16 items — scoped-not-done + bugs + chore-ideas only (excludes feat/infra ideas) | +| Open bugs | 6 | +| Legacy "Path to MVP1" | 18 items — scoped-not-done + bugs + chore-ideas only (excludes feat/infra ideas) | | Backlog ideas | 2 idea-only feat/infra (not yet scoped into MVP1) | | In flight | 1 feature(s) actively shipping | @@ -171,27 +171,29 @@ _None._ _None._ -### Idea (17) +### Idea (19) | # | Priority | Feature | Type | One-liner | Depends on | Status | |---|---|---|---|---|---|---| | 1 | P1 | [feat_ubi_judgments](../02_product/planned_features/feat_ubi_judgments/idea.md) | Feature | MVP1 ships with **LLM-as-judge** as the only authoritative judgment source. The architecture anticipated this would change — the `judgments.source` CHECK already accepts `click`… | — | Idea — bundled with [`infra_adapter_solr`](../infra_adapter_solr/idea.md) into MVP2 / v0.2 "Three-Engine + Real Signals" | | 2 | P1 | [infra_smoke_job_chronic_flake](../02_product/planned_features/infra_smoke_job_chronic_flake/idea.md) | Infra | Recent `pr.yml` runs on `main` (newest first): | — | Idea — captured during feat_index_document_browser CI watch (PR #285) | -| 3 | P1 | [chore_drop_demo_seed_from_ci](../02_product/planned_features/chore_drop_demo_seed_from_ci/idea.md) | Chore | The smoke job in `.github/workflows/pr.yml` ran three seed steps before the smoke test + Playwright E2E suite: | — | Idea — landed bundled with PR #290 (docker-image-bumps) | -| 4 | P1 | [chore_drop_fusion_scope](../02_product/planned_features/chore_drop_fusion_scope/idea.md) | Chore | The prior umbrella spec ([`docs/00_overview/relyloop-spec.md`](relyloop-spec.md)) planned Lucidworks Fusion as the MVP3 engine target and Apache Solr as a v2+ "architectural reference, not v1 scope" a | — | Idea — scope decision, paired with [`infra_adapter_solr`](../infra_adapter_solr/idea.md) | -| 5 | P1 | [chore_oss_public_launch_punchlist](../02_product/planned_features/chore_oss_public_launch_punchlist/idea.md) | Chore | The `chore_oss_launch_prep` PR adds the foundational governance / security / contributor files that prospective contributors and enterprise reviewers look for first. Three remaining items are gates on | — | Idea — captured during `chore_oss_launch_prep` (the PR that added SECURITY.md / GOVERNANCE.md / MAINTAINERS.md / CODEOWNERS / issue + PR templates and replaced the Code of Conduct) | -| 6 | P1 | [bug_demo_reseed_button_silent_enqueue_failure](../02_product/planned_features/bug_demo_reseed_button_silent_enqueue_failure/idea.md) | Bug | There is at least one untrapped exception path in `backend/workers/demo_reseed.py:run_demo_reseed`'s pre-main-body initialization that: | — | Idea — bug captured during PR #286 first-run testing | -| 7 | P2 | [chore_demo_seeding_integration_tests_rewrite](../02_product/planned_features/chore_demo_seeding_integration_tests_rewrite/idea.md) | Chore | The async flow's contract: | — | Idea — chore captured during PR #286 | -| 8 | P2 | [chore_e2e_api_base_url_construction](../02_product/planned_features/chore_e2e_api_base_url_construction/idea.md) | Chore | Five sites in three e2e specs concatenate `API_BASE` with a path string: | — | Idea — surfaced during Gemini Code Assist review on PR #273 (`chore_clone_narrow_bounds_full_roundtrip_e2e`). | -| 9 | P2 | [chore_state_md_size_compression](../02_product/planned_features/chore_state_md_size_compression/idea.md) | Chore | `state.md` is structured around two concerns conflated into one file: | — | Idea — tangential observation surfaced during `/impl-execute` for `infra_agent_sibling_worktree_isolation` (Phase 1, this PR). | -| 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 | P2 | [bug_ceiling_badge_assumes_maximize_direction](../02_product/planned_features/bug_ceiling_badge_assumes_maximize_direction/idea.md) | Bug | The `CEILING` badge in [`studies-table.column-config.tsx:METRIC_CEILING_THRESHOLD`](../ui/src/components/studies/studies-table.column-config.tsx) flags rows where `best_metric >= 0.99`. The threshold | — | — | -| 13 | P2 | [bug_smoke_studies_data_table_search_flake](../02_product/planned_features/bug_smoke_studies_data_table_search_flake/idea.md) | Bug | [`ui/tests/e2e/studies-data-table.spec.ts:20-40`](../../ui/tests/e2e/studies-data-table.spec.ts#L20-L40): | — | Idea — surfaced during PR #273 CI watch. | -| 14 | P2 | [bug_starlette_request_poisons_fastapi_depends_tests](../02_product/planned_features/bug_starlette_request_poisons_fastapi_depends_tests/idea.md) | Bug | There is shared state somewhere in starlette / FastAPI that is mutated by `Request(scope={"type": "http", ...})` and breaks subsequent `Depends` resolution. Possible suspects: | — | Idea — bug captured during feat_index_document_browser Story 2.1 | -| 15 | P2 | [bug_webhook_concurrent_merge_race_timing_sensitive](../02_product/planned_features/bug_webhook_concurrent_merge_race_timing_sensitive/idea.md) | Bug | Idea — surfaced during `bug_demo_clusters_unreachable_in_healthz` PR #236 CI. | — | Idea — surfaced during `bug_demo_clusters_unreachable_in_healthz` PR #236 CI. | -| 16 | Backlog | [chore_auto_followup_parent_advisory_lock](../02_product/planned_features/chore_auto_followup_parent_advisory_lock/idea.md) | Chore | The shipped `feat_auto_followup_studies` worker uses a two-layer idempotency scheme: | — | Idea — captured as a standalone file to resolve broken cross-references in `feat_auto_followup_studies` D-11 + plan F2 + `bug_auto_followup_completed_parent_stop_chain_race/idea.md`. The slug was coined 2026-05-24 in D-11 but only existed as descriptive prose across other documents until now. | -| 17 | 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 | — | Closed (2026-05-25) — superseded by guide-06 spec wiring (commit `2cbcb93b`, 2026-05-22). Real caller: `ui/tests/e2e/guides/06_create_and_monitor_study.spec.ts`. No further action beyond the coverage-audit refresh that ships in the same PR. | +| 3 | P1 | [chore_ci_perf_buildx_artifact_image_cache_xdist](../02_product/planned_features/chore_ci_perf_buildx_artifact_image_cache_xdist/idea.md) | Chore | PR #290's smoke job ran for 15m 22s and was killed by `timeout-minutes: 15`. Per-step breakdown: | — | Idea — landed as the next PR after PR #290 (docker-image-bumps) | +| 4 | P1 | [chore_drop_demo_seed_from_ci](../02_product/planned_features/chore_drop_demo_seed_from_ci/idea.md) | Chore | The smoke job in `.github/workflows/pr.yml` ran three seed steps before the smoke test + Playwright E2E suite: | — | Idea — landed bundled with PR #290 (docker-image-bumps) | +| 5 | P1 | [chore_drop_fusion_scope](../02_product/planned_features/chore_drop_fusion_scope/idea.md) | Chore | The prior umbrella spec ([`docs/00_overview/relyloop-spec.md`](relyloop-spec.md)) planned Lucidworks Fusion as the MVP3 engine target and Apache Solr as a v2+ "architectural reference, not v1 scope" a | — | Idea — scope decision, paired with [`infra_adapter_solr`](../infra_adapter_solr/idea.md) | +| 6 | P1 | [chore_oss_public_launch_punchlist](../02_product/planned_features/chore_oss_public_launch_punchlist/idea.md) | Chore | The `chore_oss_launch_prep` PR adds the foundational governance / security / contributor files that prospective contributors and enterprise reviewers look for first. Three remaining items are gates on | — | Idea — captured during `chore_oss_launch_prep` (the PR that added SECURITY.md / GOVERNANCE.md / MAINTAINERS.md / CODEOWNERS / issue + PR templates and replaced the Code of Conduct) | +| 7 | P1 | [bug_demo_reseed_button_silent_enqueue_failure](../02_product/planned_features/bug_demo_reseed_button_silent_enqueue_failure/idea.md) | Bug | There is at least one untrapped exception path in `backend/workers/demo_reseed.py:run_demo_reseed`'s pre-main-body initialization that: | — | Idea — bug captured during PR #286 first-run testing | +| 8 | P1 | [bug_smoke_seed_es_unavailable_shards_race](../02_product/planned_features/bug_smoke_seed_es_unavailable_shards_race/idea.md) | Bug | `backend/app/scripts/seed_es.py` creates the `products` index then immediately bulk-indexes 1000 docs against it. On cold GHA runners with ES 9.4.1 (bumped from 9.4.0 in PR #290), the bulk call someti | — | Idea — captured as part of PR #291 admin-merge | +| 9 | P2 | [chore_demo_seeding_integration_tests_rewrite](../02_product/planned_features/chore_demo_seeding_integration_tests_rewrite/idea.md) | Chore | The async flow's contract: | — | Idea — chore captured during PR #286 | +| 10 | P2 | [chore_e2e_api_base_url_construction](../02_product/planned_features/chore_e2e_api_base_url_construction/idea.md) | Chore | Five sites in three e2e specs concatenate `API_BASE` with a path string: | — | Idea — surfaced during Gemini Code Assist review on PR #273 (`chore_clone_narrow_bounds_full_roundtrip_e2e`). | +| 11 | P2 | [chore_state_md_size_compression](../02_product/planned_features/chore_state_md_size_compression/idea.md) | Chore | `state.md` is structured around two concerns conflated into one file: | — | Idea — tangential observation surfaced during `/impl-execute` for `infra_agent_sibling_worktree_isolation` (Phase 1, this PR). | +| 12 | 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 | +| 13 | 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. | +| 14 | P2 | [bug_ceiling_badge_assumes_maximize_direction](../02_product/planned_features/bug_ceiling_badge_assumes_maximize_direction/idea.md) | Bug | The `CEILING` badge in [`studies-table.column-config.tsx:METRIC_CEILING_THRESHOLD`](../ui/src/components/studies/studies-table.column-config.tsx) flags rows where `best_metric >= 0.99`. The threshold | — | — | +| 15 | P2 | [bug_smoke_studies_data_table_search_flake](../02_product/planned_features/bug_smoke_studies_data_table_search_flake/idea.md) | Bug | [`ui/tests/e2e/studies-data-table.spec.ts:20-40`](../../ui/tests/e2e/studies-data-table.spec.ts#L20-L40): | — | Idea — surfaced during PR #273 CI watch. | +| 16 | P2 | [bug_starlette_request_poisons_fastapi_depends_tests](../02_product/planned_features/bug_starlette_request_poisons_fastapi_depends_tests/idea.md) | Bug | There is shared state somewhere in starlette / FastAPI that is mutated by `Request(scope={"type": "http", ...})` and breaks subsequent `Depends` resolution. Possible suspects: | — | Idea — bug captured during feat_index_document_browser Story 2.1 | +| 17 | P2 | [bug_webhook_concurrent_merge_race_timing_sensitive](../02_product/planned_features/bug_webhook_concurrent_merge_race_timing_sensitive/idea.md) | Bug | Idea — surfaced during `bug_demo_clusters_unreachable_in_healthz` PR #236 CI. | — | Idea — surfaced during `bug_demo_clusters_unreachable_in_healthz` PR #236 CI. | +| 18 | Backlog | [chore_auto_followup_parent_advisory_lock](../02_product/planned_features/chore_auto_followup_parent_advisory_lock/idea.md) | Chore | The shipped `feat_auto_followup_studies` worker uses a two-layer idempotency scheme: | — | Idea — captured as a standalone file to resolve broken cross-references in `feat_auto_followup_studies` D-11 + plan F2 + `bug_auto_followup_completed_parent_stop_chain_race/idea.md`. The slug was coined 2026-05-24 in D-11 but only existed as descriptive prose across other documents until now. | +| 19 | 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 | — | Closed (2026-05-25) — superseded by guide-06 spec wiring (commit `2cbcb93b`, 2026-05-22). Real caller: `ui/tests/e2e/guides/06_create_and_monitor_study.spec.ts`. No further action beyond the coverage-audit refresh that ships in the same PR. | ## Dependency graph diff --git a/docs/00_overview/dashboard.html b/docs/00_overview/dashboard.html index b4b1eccd..0f5765f5 100644 --- a/docs/00_overview/dashboard.html +++ b/docs/00_overview/dashboard.html @@ -384,7 +384,7 @@