diff --git a/docs/00_overview/MVP2_DASHBOARD.md b/docs/00_overview/MVP2_DASHBOARD.md index 0a0ca9ff..98f9964c 100644 --- a/docs/00_overview/MVP2_DASHBOARD.md +++ b/docs/00_overview/MVP2_DASHBOARD.md @@ -20,16 +20,16 @@ Plan approved; run /impl-execute to ship | Metric | Value | |---|---| -| Filed under MVP2 | **40** folders total (done + specced not-done + idea backlog + bugs) | +| Filed under MVP2 | **41** folders total (done + specced not-done + idea backlog + bugs) | | Specced features done | **10 / 19** (53%) — of features *past the idea stage* (those with a spec); the idea backlog below is NOT in this denominator, so 100% ≠ release complete | -| Pending work | **28** items (every not-done feat/infra/chore/bug across all priorities) | +| Pending work | **29** items (every not-done feat/infra/chore/bug across all priorities) | | → P0 — do next | **0** unblocking / paying daily cost | | → P1 | **1** high-value, ready when P0 clears | -| → P2 (default) | 23 important to file, not blocking | +| → P2 (default) | 24 important to file, not blocking | | → Backlog | 4 captured for record, not planned | | Open bugs | 8 | | Legacy "Path to MVP2" | 24 items — scoped-not-done + bugs + chore-ideas only (excludes feat/infra ideas) | -| Backlog ideas | 4 idea-only feat/infra (not yet scoped into MVP2) | +| Backlog ideas | 5 idea-only feat/infra (not yet scoped into MVP2) | | In flight | 0 feature(s) actively shipping | ## Pipeline @@ -73,29 +73,30 @@ _None._ _None._ -### Idea (19) +### Idea (20) | # | Priority | Feature | Type | One-liner | Depends on | Status | |---|---|---|---|---|---|---| | 1 | P1 | [infra_smoke_reseed_runtime_budget](planned_features/02_mvp2/infra_smoke_reseed_runtime_budget/idea.md) | Infra | Idea — captured at `infra_solr_smoke_stability` PR #383 merge time | — | Idea — captured at `infra_solr_smoke_stability` PR #383 merge time | | 2 | P2 | [infra_openapi_types_freshness_gate](planned_features/02_mvp2/infra_openapi_types_freshness_gate/idea.md) | Infra | Idea — Phase 2 of [`infra_generated_artifact_freshness_gate`](../infra_generated_artifact_freshness_gate/feature_spec.md), extracted to its own folder | — | Idea — Phase 2 of [`infra_generated_artifact_freshness_gate`](../infra_generated_artifact_freshness_gate/feature_spec.md), extracted to its own folder | -| 3 | P2 | [chore_demo_reseed_partial_completion_fast_test](planned_features/02_mvp2/chore_demo_reseed_partial_completion_fast_test/idea.md) | Chore | `infra_solr_ci_readiness` made the demo reseed engine-tolerant: when an engine is unreachable, its scenario is skipped, the reseed completes with `status="complete"` and a non-empty `scenarios_skipped | — | Idea — tangential discovery during `infra_solr_ci_readiness` Story 1.2 implementation | -| 4 | P2 | [chore_solr_post_pipeline_followups](planned_features/02_mvp2/chore_solr_post_pipeline_followups/idea.md) | Chore | The 13-story `infra_adapter_solr` execution surfaced several follow-on items that fit neither the original spec nor any sister feature folder. None block the MVP2 Solr release — they're operator-exper | — | Idea — tangential observations from `infra_adapter_solr` end-to-end | -| 5 | P2 | [chore_studies_post_arq_spy_fixture](planned_features/02_mvp2/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 | -| 6 | P2 | [chore_template_library_expansion](planned_features/02_mvp2/chore_template_library_expansion/idea.md) | Chore | Three connected gaps: | — | Idea — surfaced during a UX review of parameter-tuning ergonomics on 2026-05-19. | -| 7 | P2 | [chore_ubi_hybrid_template_render](planned_features/02_mvp2/chore_ubi_hybrid_template_render/idea.md) | Chore | Idea — contract decision deferred (NOT a worker bug) | — | Idea — contract decision deferred (NOT a worker bug) | -| 8 | P2 | [chore_ubi_reader_search_after_pagination](planned_features/02_mvp2/chore_ubi_reader_search_after_pagination/idea.md) | Chore | `UbiReader._scan_ubi_events` / `_scan_ubi_queries` each issue ONE `search_batch` (a `size`-limited query). To stay under the engine result-window they now cap at 10000 rows per (target, window).… | — | Idea — deferred from `feat_ubi_judgments` (found during the rung-3 E2E) | -| 9 | P2 | [bug_baseline_phase_test_isolation](planned_features/02_mvp2/bug_baseline_phase_test_isolation/idea.md) | Bug | `backend/tests/unit/workers/test_orchestrator_baseline_phase.py::TestComputeBaselineWaitS::*` fail when run in isolation with: | — | Idea — pre-existing bug surfaced during `feat_ubi_judgments` PR #317 | -| 10 | P2 | [bug_e2e_teardown_chain_node_delete_500](planned_features/02_mvp2/bug_e2e_teardown_chain_node_delete_500/idea.md) | Bug | The E2E global-teardown deletes seeded rows in a fixed order (per `chore_e2e_test_rows_isolation` Story 1.2 cleanup registration). For auto-followup **chains**, the seeded nodes are `queued` studies c | — | Idea — tangential discovery during `feat_overnight_autopilot` (Story 4.2 E2E, PR forthcoming) | -| 11 | P2 | [bug_judgment_header_omits_click_bucket](planned_features/02_mvp2/bug_judgment_header_omits_click_bucket/idea.md) | Bug | A judgment list generated from UBI (or hybrid UBI+LLM) carries non-zero `source_breakdown.click`, but the header's source-breakdown card silently drops it. Operators reviewing a UBI list see only the | — | Idea — tangential discovery during `feat_overnight_autopilot` (Story 2.1, PR forthcoming) | -| 12 | P2 | [bug_relyloop_spec_ubi_section_drift](planned_features/02_mvp2/bug_relyloop_spec_ubi_section_drift/idea.md) | Bug | [`docs/00_overview/relyloop-spec.md`](relyloop-spec.md) §"Click-derived judgments — OpenSearch UBI as the engine-neutral primary path" (line ~706) carries two staleness bugs from the 2026-05-27 releas | — | Idea — captured during `feat_ubi_judgments` preflight (2026-05-29) | -| 13 | P2 | [bug_reseed_failure_blocks_retry_arq_singleton_dedup](planned_features/02_mvp2/bug_reseed_failure_blocks_retry_arq_singleton_dedup/idea.md) | Bug | `run_demo_reseed` is enqueued with a fixed Arq job id `demo_reseed:singleton` (the singleton concurrency guard). When a run reaches a terminal state, Arq stores its **result** under `arq:result:demo_r | — | Idea — tangential discovery while verifying `fix(demo): add Solr (8983) to the reseed engine host-URL mapping` (branch `feat_demo_reseed_solr_and_steplog`) | -| 14 | P2 | [bug_seed_meaningful_demos_silent_bulk_errors](planned_features/02_mvp2/bug_seed_meaningful_demos_silent_bulk_errors/idea.md) | Bug | [`scripts/seed_meaningful_demos.py:917-935`](../../scripts/seed_meaningful_demos.py#L917-L935) bulk-indexes 1000 Amazon ESCI products into a dedicated index per demo scenario: | — | Idea — captured during `bug_smoke_seed_es_unavailable_shards_race` Phase 2.5 tangential sweep | -| 15 | P2 | [bug_webhook_concurrent_merge_race_timing_sensitive](planned_features/02_mvp2/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 | [feat_fts_rank_ordering](planned_features/02_mvp2/feat_fts_rank_ordering/idea.md) | Feature | `feat_data_table_primitive` shipped filter-only FTS — `?q=foo` matches rows where `search_vector @@ plainto_tsquery('english', 'foo')` is true but orders results by `created_at DESC, id DESC` (the def | — | Idea — deferred from `feat_data_table_primitive` (MVP1) per spec §16. | -| 17 | Backlog | [infra_arq_subprocess_test](planned_features/02_mvp2/infra_arq_subprocess_test/idea.md) | Infra | Idea (deferred from `feat_study_lifecycle` Phase 2 / PR #25 final GPT-5.5 review). Still applicable as of 2026-05-14: the three in-process tests cited below still cover the resume contract correctly; | — | Idea (deferred from `feat_study_lifecycle` Phase 2 / PR #25 final GPT-5.5 review). Still applicable as of 2026-05-14: the three in-process tests cited below still cover the resume contract correctly; a subprocess test would add a narrow Arq-version-regression guard. | -| 18 | Backlog | [chore_auto_followup_parent_advisory_lock](planned_features/02_mvp2/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 | [bug_chat_long_conversation_truncation](planned_features/02_mvp2/bug_chat_long_conversation_truncation/idea.md) | Bug | [`backend/app/services/agent_chat.send_user_message`](../../backend/app/services/agent_chat.py) defensively caps the OpenAI history at the most recent `HISTORY_MAX_MESSAGES = 100` messages… | — | Held for MVP2 (decided 2026-05-13). Folder renamed with `_mvp2` suffix to make the deferral visible at-a-glance in `ls docs/00_overview/planned_features/`. Resume work when MVP2 starts — no technical dependency on MVP2 infra (audit_log is N/A; Langfuse is convenience only); the deferral is scope discipline + zero current impact (latent bug, no operator has hit the 100-message cap). | +| 3 | P2 | [infra_smoke_fork_pr_secret_skip](planned_features/02_mvp2/infra_smoke_fork_pr_secret_skip/idea.md) | Infra | `.github/workflows/pr.yml` triggers on `pull_request:` ([pr.yml:43](../.github/workflows/pr.yml)) — **not** `pull_request_target`. GitHub deliberately withholds repository secrets from workflows trigg | — | Idea — tangential discovery while merging PR #387 (`chore_arq_pool_aclose_deprecation`) | +| 4 | P2 | [chore_demo_reseed_partial_completion_fast_test](planned_features/02_mvp2/chore_demo_reseed_partial_completion_fast_test/idea.md) | Chore | `infra_solr_ci_readiness` made the demo reseed engine-tolerant: when an engine is unreachable, its scenario is skipped, the reseed completes with `status="complete"` and a non-empty `scenarios_skipped | — | Idea — tangential discovery during `infra_solr_ci_readiness` Story 1.2 implementation | +| 5 | P2 | [chore_solr_post_pipeline_followups](planned_features/02_mvp2/chore_solr_post_pipeline_followups/idea.md) | Chore | The 13-story `infra_adapter_solr` execution surfaced several follow-on items that fit neither the original spec nor any sister feature folder. None block the MVP2 Solr release — they're operator-exper | — | Idea — tangential observations from `infra_adapter_solr` end-to-end | +| 6 | P2 | [chore_studies_post_arq_spy_fixture](planned_features/02_mvp2/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 | +| 7 | P2 | [chore_template_library_expansion](planned_features/02_mvp2/chore_template_library_expansion/idea.md) | Chore | Three connected gaps: | — | Idea — surfaced during a UX review of parameter-tuning ergonomics on 2026-05-19. | +| 8 | P2 | [chore_ubi_hybrid_template_render](planned_features/02_mvp2/chore_ubi_hybrid_template_render/idea.md) | Chore | Idea — contract decision deferred (NOT a worker bug) | — | Idea — contract decision deferred (NOT a worker bug) | +| 9 | P2 | [chore_ubi_reader_search_after_pagination](planned_features/02_mvp2/chore_ubi_reader_search_after_pagination/idea.md) | Chore | `UbiReader._scan_ubi_events` / `_scan_ubi_queries` each issue ONE `search_batch` (a `size`-limited query). To stay under the engine result-window they now cap at 10000 rows per (target, window).… | — | Idea — deferred from `feat_ubi_judgments` (found during the rung-3 E2E) | +| 10 | P2 | [bug_baseline_phase_test_isolation](planned_features/02_mvp2/bug_baseline_phase_test_isolation/idea.md) | Bug | `backend/tests/unit/workers/test_orchestrator_baseline_phase.py::TestComputeBaselineWaitS::*` fail when run in isolation with: | — | Idea — pre-existing bug surfaced during `feat_ubi_judgments` PR #317 | +| 11 | P2 | [bug_e2e_teardown_chain_node_delete_500](planned_features/02_mvp2/bug_e2e_teardown_chain_node_delete_500/idea.md) | Bug | The E2E global-teardown deletes seeded rows in a fixed order (per `chore_e2e_test_rows_isolation` Story 1.2 cleanup registration). For auto-followup **chains**, the seeded nodes are `queued` studies c | — | Idea — tangential discovery during `feat_overnight_autopilot` (Story 4.2 E2E, PR forthcoming) | +| 12 | P2 | [bug_judgment_header_omits_click_bucket](planned_features/02_mvp2/bug_judgment_header_omits_click_bucket/idea.md) | Bug | A judgment list generated from UBI (or hybrid UBI+LLM) carries non-zero `source_breakdown.click`, but the header's source-breakdown card silently drops it. Operators reviewing a UBI list see only the | — | Idea — tangential discovery during `feat_overnight_autopilot` (Story 2.1, PR forthcoming) | +| 13 | P2 | [bug_relyloop_spec_ubi_section_drift](planned_features/02_mvp2/bug_relyloop_spec_ubi_section_drift/idea.md) | Bug | [`docs/00_overview/relyloop-spec.md`](relyloop-spec.md) §"Click-derived judgments — OpenSearch UBI as the engine-neutral primary path" (line ~706) carries two staleness bugs from the 2026-05-27 releas | — | Idea — captured during `feat_ubi_judgments` preflight (2026-05-29) | +| 14 | P2 | [bug_reseed_failure_blocks_retry_arq_singleton_dedup](planned_features/02_mvp2/bug_reseed_failure_blocks_retry_arq_singleton_dedup/idea.md) | Bug | `run_demo_reseed` is enqueued with a fixed Arq job id `demo_reseed:singleton` (the singleton concurrency guard). When a run reaches a terminal state, Arq stores its **result** under `arq:result:demo_r | — | Idea — tangential discovery while verifying `fix(demo): add Solr (8983) to the reseed engine host-URL mapping` (branch `feat_demo_reseed_solr_and_steplog`) | +| 15 | P2 | [bug_seed_meaningful_demos_silent_bulk_errors](planned_features/02_mvp2/bug_seed_meaningful_demos_silent_bulk_errors/idea.md) | Bug | [`scripts/seed_meaningful_demos.py:917-935`](../../scripts/seed_meaningful_demos.py#L917-L935) bulk-indexes 1000 Amazon ESCI products into a dedicated index per demo scenario: | — | Idea — captured during `bug_smoke_seed_es_unavailable_shards_race` Phase 2.5 tangential sweep | +| 16 | P2 | [bug_webhook_concurrent_merge_race_timing_sensitive](planned_features/02_mvp2/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. | +| 17 | Backlog | [feat_fts_rank_ordering](planned_features/02_mvp2/feat_fts_rank_ordering/idea.md) | Feature | `feat_data_table_primitive` shipped filter-only FTS — `?q=foo` matches rows where `search_vector @@ plainto_tsquery('english', 'foo')` is true but orders results by `created_at DESC, id DESC` (the def | — | Idea — deferred from `feat_data_table_primitive` (MVP1) per spec §16. | +| 18 | Backlog | [infra_arq_subprocess_test](planned_features/02_mvp2/infra_arq_subprocess_test/idea.md) | Infra | Idea (deferred from `feat_study_lifecycle` Phase 2 / PR #25 final GPT-5.5 review). Still applicable as of 2026-05-14: the three in-process tests cited below still cover the resume contract correctly; | — | Idea (deferred from `feat_study_lifecycle` Phase 2 / PR #25 final GPT-5.5 review). Still applicable as of 2026-05-14: the three in-process tests cited below still cover the resume contract correctly; a subprocess test would add a narrow Arq-version-regression guard. | +| 19 | Backlog | [chore_auto_followup_parent_advisory_lock](planned_features/02_mvp2/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. | +| 20 | Backlog | [bug_chat_long_conversation_truncation](planned_features/02_mvp2/bug_chat_long_conversation_truncation/idea.md) | Bug | [`backend/app/services/agent_chat.send_user_message`](../../backend/app/services/agent_chat.py) defensively caps the OpenAI history at the most recent `HISTORY_MAX_MESSAGES = 100` messages… | — | Held for MVP2 (decided 2026-05-13). Folder renamed with `_mvp2` suffix to make the deferral visible at-a-glance in `ls docs/00_overview/planned_features/`. Resume work when MVP2 starts — no technical dependency on MVP2 infra (audit_log is N/A; Langfuse is convenience only); the deferral is scope discipline + zero current impact (latent bug, no operator has hit the 100-message cap). | ## Dependency graph diff --git a/docs/00_overview/mvp2_dashboard.html b/docs/00_overview/mvp2_dashboard.html index 2e0c5471..e86964f8 100644 --- a/docs/00_overview/mvp2_dashboard.html +++ b/docs/00_overview/mvp2_dashboard.html @@ -398,12 +398,12 @@

MVP2 Progress

Specced features done
10 / 19
-
53% specced · 40 filed under MVP2
+
53% specced · 41 filed under MVP2
Pending work
-
28
+
29
every not-done feat/infra/chore/bug across all priorities
@@ -425,7 +425,7 @@

MVP2 Progress

P2 (default)
-
23
+
24
important to file, not blocking
@@ -442,7 +442,7 @@

MVP2 Progress

Backlog ideas: - 4 idea-only feat/infra folders (not yet scoped into MVP2) + 5 idea-only feat/infra folders (not yet scoped into MVP2) In flight: @@ -463,7 +463,7 @@

Pipeline

-

Idea 19

+

Idea 20

@@ -491,6 +491,19 @@

Idea 19

+
+ +
+ Infra + P2 + +
+
`.github/workflows/pr.yml` triggers on `pull_request:` ([pr.yml:43](../.github/workflows/pr.yml)) — **not** `pull_request_target`. GitHub deliberately withholds repository secrets from workflows trigg
+ + +
+ +
diff --git a/docs/00_overview/planned_features/02_mvp2/chore_arq_pool_aclose_deprecation/idea.md b/docs/00_overview/planned_features/02_mvp2/chore_arq_pool_aclose_deprecation/idea.md index f3a099ba..a5940a42 100644 --- a/docs/00_overview/planned_features/02_mvp2/chore_arq_pool_aclose_deprecation/idea.md +++ b/docs/00_overview/planned_features/02_mvp2/chore_arq_pool_aclose_deprecation/idea.md @@ -38,3 +38,8 @@ Replace both `await arq_pool.close()` calls with `await arq_pool.aclose()` — ` ## Relationship to other work - Pure maintenance; no feature dependency. Pick up with any other `backend/app/main.py` touch. +- **Surfaced a CI-policy gap:** this fix shipped via [PR #387](https://github.com/SoundMindsAI/relyloop/pull/387) (merged 2026-06-02 as `2e49ac99`), submitted by an external contributor. Its first run exposed that the `smoke` job hard-fails on every external-fork PR because forked PRs can't read `OPENAI_API_KEY_TEST` — captured as [`infra_smoke_fork_pr_secret_skip`](../infra_smoke_fork_pr_secret_skip/idea.md). + +## Postscript — shipped + +Shipped via PR #387 (squash commit `2e49ac99`, 2026-06-02). Both call sites converted to `aclose()`; regression tests added at both shutdown paths. The `fake_pool.close = AsyncMock(...)` stub flip predicted in "Scope signals" was applied, and `fake_pool.close` was additionally retained as an `AsyncMock` so the `close.assert_not_called()` negative assertion fails cleanly rather than crashing on a non-awaitable mock — a Gemini review point the contributor addressed before merge. diff --git a/docs/00_overview/planned_features/02_mvp2/infra_smoke_fork_pr_secret_skip/idea.md b/docs/00_overview/planned_features/02_mvp2/infra_smoke_fork_pr_secret_skip/idea.md new file mode 100644 index 00000000..8dea09ac --- /dev/null +++ b/docs/00_overview/planned_features/02_mvp2/infra_smoke_fork_pr_secret_skip/idea.md @@ -0,0 +1,56 @@ +# infra_smoke_fork_pr_secret_skip — smoke job hard-fails on every external-fork PR because forked PRs can't read `OPENAI_API_KEY_TEST` + +**Date:** 2026-06-01 +**Status:** Idea — tangential discovery while merging PR #387 (`chore_arq_pool_aclose_deprecation`) +**Tracking issue:** [#410](https://github.com/SoundMindsAI/relyloop/issues/410) (public — gives external contributors a findable "known fork limitation, not your code" explanation) +**Type:** `infra_` +**Priority:** P2 — does not block this repo's own branches (internal PRs see the secret), but every **external contributor** PR gets a permanent red ✗ on the `smoke` job for a reason unrelated to their code. Elevated by the OSS-launch posture (CHANGELOG + CITATION.cff just landed, #382): a red X on a first-time contributor's PR reads as "your change is broken" when it isn't. + +## Origin + +PR #387 (the `arq_pool.aclose()` deprecation fix from external contributor `Yashi248`, merged 2026-06-02 as `2e49ac99`). After updating the branch and approving the workflow run, **every code-validating job passed** (backend lint/typecheck/tests/coverage, frontend, docker buildx, license, DCO, secrets-defense) — the **only** red was `smoke (operator-path tutorial flow)`, run `26792913411`, job result `failure`. + +The smoke job failed on its very first real step, **"Sanity-check OPENAI_API_KEY_TEST is populated"**, before running a single test: + +``` +##[error]OPENAI_API_KEY_TEST secret is empty — smoke gate requires it + (per chore_tutorial_polish §3 + decision log M5) +##[error]Process completed with exit code 1. +``` + +## Problem + +`.github/workflows/pr.yml` triggers on `pull_request:` ([pr.yml:43](../../../../.github/workflows/pr.yml)) — **not** `pull_request_target`. GitHub deliberately withholds repository secrets from workflows triggered by a PR opened from a **fork** (security: prevents secret exfiltration by an attacker's PR). So for any external-contributor PR: + +1. `OPENAI_API_KEY_TEST: ${{ secrets.OPENAI_API_KEY_TEST }}` ([pr.yml:536](../../../../.github/workflows/pr.yml)) resolves to the empty string. +2. It is written to `./secrets/openai_key` ([pr.yml:553](../../../../.github/workflows/pr.yml)) as a zero-byte file. +3. The "Sanity-check OPENAI_API_KEY_TEST is populated" step greps for non-whitespace content and `exit 1`s with the message above ([pr.yml:565–571](../../../../.github/workflows/pr.yml)). + +This is **structural**, not a code defect — it reproduces identically on every fork PR regardless of the diff. The smoke job's existing `if: ${{ vars.SKIP_HEAVY_CI != 'true' }}` ([pr.yml:492](../../../../.github/workflows/pr.yml)) is a blunt global kill-switch; it is not fork-aware, so when heavy CI is on, fork PRs always go red here. + +## Proposed capability + +Make the `smoke-test` job **fork-aware**: when the PR head repo is a fork (the runner therefore has no secret), skip the smoke job gracefully with an explicit notice instead of hard-failing. Candidate shapes (pick at spec time): + +- **A — Job-level `if` guard (preferred).** Add a fork condition to the existing `if:` so the whole job is skipped on fork PRs: + `if: ${{ vars.SKIP_HEAVY_CI != 'true' && github.event.pull_request.head.repo.full_name == github.repository }}`. + A skipped job shows neutral (not red). Pair with a tiny always-green "smoke (skipped — fork PR, no secrets)" notice job so the status is self-explanatory to the contributor. +- **B — In-step soft skip.** Change the sanity-check step to detect the empty secret on a fork and `echo "::notice::"` + early-`exit 0` (with a guard that it ONLY soft-skips for forks, never for the upstream repo where an empty secret is a real misconfiguration). +- **C — `pull_request_target`.** Rejected by default — runs in the base-repo context with secret access against untrusted fork code; a known RCE-class footgun. Out of scope unless paired with a vetted checkout-and-sandbox pattern. + +Whichever is chosen, the upstream-repo path MUST keep failing loudly on a genuinely empty secret (the original M5 intent) — the soft-skip applies **only** to the fork case. + +## Scope signals + +- **CI / infra:** one YAML job in `pr.yml` (`if:` guard + optional notice job). No backend/frontend/migration changes. +- **Test coupling:** none — no application code touched. +- **Decision surface:** which approach (A/B/C), and whether to add the always-green notice job so a skipped status is legible to contributors. One forcing decision: confirm the soft-skip is fork-gated and never weakens the upstream empty-secret guard. + +## Why deferred (not fixed inline) + +Surfaced at PR #387 merge time; that PR was an external contributor's two-line deprecation fix and the right call was to merge it (the failure was provably environmental — see the adjudication comment on #387). Editing `pr.yml`'s smoke job inside #387 would have mixed an unrelated CI-policy change into a contributor's narrow diff, and the contributor can't even test a secrets-dependent workflow change from their fork. Belongs in its own maintainer-owned infra PR. + +## Relationship to other work + +- **[`infra_smoke_reseed_runtime_budget`](../infra_smoke_reseed_runtime_budget/idea.md)** — the **sibling "smoke job stays red" issue**, but an **independent** failure mode. That one is about the Playwright demo-ubi reseed blowing the 25-min wall-clock budget once Solr boots; this one is about fork PRs lacking the secret. They fail at different steps and neither fix resolves the other: even with the runtime budget fixed, a fork PR still dies at the secret sanity-check; even with the secret present, an internal PR can still hit the runtime cap. Both must ship for `smoke` to be green on **every** PR. Coordinate so the two `pr.yml` edits don't collide. +- **[`chore_arq_pool_aclose_deprecation`](../chore_arq_pool_aclose_deprecation/idea.md)** — the shipped fix (PR #387, `2e49ac99`) whose CI run surfaced this. That work is done; this is the CI-policy gap its first external-contributor run exposed. diff --git a/docs/00_overview/planned_features/02_mvp2/infra_smoke_reseed_runtime_budget/idea.md b/docs/00_overview/planned_features/02_mvp2/infra_smoke_reseed_runtime_budget/idea.md index 6d2c487e..65afbbe6 100644 --- a/docs/00_overview/planned_features/02_mvp2/infra_smoke_reseed_runtime_budget/idea.md +++ b/docs/00_overview/planned_features/02_mvp2/infra_smoke_reseed_runtime_budget/idea.md @@ -61,6 +61,7 @@ Per CLAUDE.md "fix inline by default" rule, options A and B are both inline-chea - **Sibling of** `infra_solr_smoke_stability` ([PR #383](https://github.com/SoundMindsAI/relyloop/pull/383), merged with smoke red per D-6). The smoke-solr-stability runbook (`docs/03_runbooks/smoke-solr-stability.md`) needs a new lever-cascade entry after this work ships, OR a section noting that reseed-runtime is a separate concern from Solr stability. - **Independent of** `infra_solr_ci_readiness` Phase 1 (backend half — shipped, unaffected). +- **Sibling "smoke job stays red" issue:** [`infra_smoke_fork_pr_secret_skip`](../infra_smoke_fork_pr_secret_skip/idea.md) — a **separate, independent** smoke-red failure mode (external-fork PRs can't read `OPENAI_API_KEY_TEST`, so smoke hard-fails at the secret sanity-check). Neither fix resolves the other: this idea fixes the reseed wall-clock; that one fixes the fork-secret gate. Both must ship for `smoke` to be green on every PR — and both edit the same `pr.yml` smoke-test job, so coordinate the YAML changes to avoid collisions. ## Scope signals diff --git a/website/docs/roadmap.md b/website/docs/roadmap.md index 6f8b7228..3970a7b1 100644 --- a/website/docs/roadmap.md +++ b/website/docs/roadmap.md @@ -180,7 +180,7 @@ - 🟡 [Query Normalizer Typed Pipeline](https://github.com/SoundMindsAI/relyloop/tree/main/docs/00_overview/planned_features/02_mvp2/feat_query_normalizer_typed_pipeline) - 🟡 [UBI LLM Study Comparison](https://github.com/SoundMindsAI/relyloop/tree/main/docs/00_overview/planned_features/02_mvp2/feat_ubi_llm_study_comparison) -??? note "Infrastructure & tooling (7)" +??? note "Infrastructure & tooling (8)" - ✅ [Adapter Solr](https://github.com/SoundMindsAI/relyloop/tree/main/docs/00_overview/implemented_features/2026_05_31_infra_adapter_solr) · [#336](https://github.com/SoundMindsAI/relyloop/pull/336) - ✅ [Solr CI Readiness](https://github.com/SoundMindsAI/relyloop/tree/main/docs/00_overview/implemented_features/2026_06_01_infra_solr_ci_readiness) · [#367](https://github.com/SoundMindsAI/relyloop/pull/367) @@ -188,6 +188,7 @@ - 🟡 [Arq Subprocess Test](https://github.com/SoundMindsAI/relyloop/tree/main/docs/00_overview/planned_features/02_mvp2/infra_arq_subprocess_test) - 🟡 [Generated Artifact Freshness Gate](https://github.com/SoundMindsAI/relyloop/tree/main/docs/00_overview/planned_features/02_mvp2/infra_generated_artifact_freshness_gate) - 🟡 [Openapi Types Freshness Gate](https://github.com/SoundMindsAI/relyloop/tree/main/docs/00_overview/planned_features/02_mvp2/infra_openapi_types_freshness_gate) + - 🟡 [Smoke Fork PR Secret Skip](https://github.com/SoundMindsAI/relyloop/tree/main/docs/00_overview/planned_features/02_mvp2/infra_smoke_fork_pr_secret_skip) - 🟡 [Smoke Reseed Runtime Budget](https://github.com/SoundMindsAI/relyloop/tree/main/docs/00_overview/planned_features/02_mvp2/infra_smoke_reseed_runtime_budget) ??? note "Maintenance & fixes (21)"