From 6b2c744dbed4800332a94c75a155428ceb372464 Mon Sep 17 00:00:00 2001 From: SoundMindsAI Date: Sun, 17 May 2026 19:12:51 -0500 Subject: [PATCH] docs: capture chore_dropdown_primitive idea MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audit grounded during 2026-05-17 tutorial UX debugging — operator hit a UUID-paste text input at create-query-set-modal.tsx:86-92, the only such survivor in the UI. Proposes lifting DataTableFkSelect into a form-friendly primitive, extending the column-discipline lint guard to forms, and migrating six modals. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/00_overview/DASHBOARD.md | 4 +- docs/00_overview/MVP1_DASHBOARD.md | 26 +++-- docs/00_overview/MVP2_DASHBOARD.md | 2 +- docs/00_overview/dashboard.html | 4 +- docs/00_overview/mvp1_dashboard.html | 60 +++++++++--- docs/00_overview/mvp2_dashboard.html | 2 +- .../chore_dropdown_primitive/idea.md | 98 +++++++++++++++++++ 7 files changed, 166 insertions(+), 30 deletions(-) create mode 100644 docs/02_product/planned_features/chore_dropdown_primitive/idea.md diff --git a/docs/00_overview/DASHBOARD.md b/docs/00_overview/DASHBOARD.md index 57a33f7b..9cfa6714 100644 --- a/docs/00_overview/DASHBOARD.md +++ b/docs/00_overview/DASHBOARD.md @@ -1,12 +1,12 @@ # RelyLoop — Release Roadmap -_Top-level index across MVP1 → GA v1+ as of **2026-05-16**. Click a release name to drill into the per-release dashboard. Theme labels sourced from [`docs/01_architecture/tech-stack.md` §"Canonical release matrix"](../01_architecture/tech-stack.md). For the rich local view, open [`dashboard.html`](dashboard.html) in a browser._ +_Top-level index across MVP1 → GA v1+ as of **2026-05-18**. Click a release name to drill into the per-release dashboard. Theme labels sourced from [`docs/01_architecture/tech-stack.md` §"Canonical release matrix"](../01_architecture/tech-stack.md). For the rich local view, open [`dashboard.html`](dashboard.html) in a browser._ ## Releases | Release | Theme | Progress | Status | |---|---|---|---| -| [MVP1 / v0.1](MVP1_DASHBOARD.md) | The Loop | 38 / 38 scoped done · 2 remaining | **In progress** | +| [MVP1 / v0.1](MVP1_DASHBOARD.md) | The Loop | 39 / 39 scoped done · 2 remaining | **In progress** | | [MVP2 / v0.2](MVP2_DASHBOARD.md) | Observable | 1 / 1 scoped done · 1 remaining | **In progress** | | MVP3 / v0.3 | Production Stacks | — | **Not yet scoped** | | MVP4 / v0.4 | Multi-tenant, Multi-LLM | — | **Not yet scoped** | diff --git a/docs/00_overview/MVP1_DASHBOARD.md b/docs/00_overview/MVP1_DASHBOARD.md index 1c20fa25..4bf0151d 100644 --- a/docs/00_overview/MVP1_DASHBOARD.md +++ b/docs/00_overview/MVP1_DASHBOARD.md @@ -2,7 +2,7 @@ # RelyLoop MVP1 Dashboard -_Reflects feature-folder state as of **2026-05-16** (latest mtime of any planned/implemented feature `.md` file). Regenerated by `make dashboard` and the `mvp1-dashboard-regen` pre-commit hook. For the rich local view (filter chips, type colors), open [`mvp1_dashboard.html`](mvp1_dashboard.html) in a browser._ +_Reflects feature-folder state as of **2026-05-18** (latest mtime of any planned/implemented feature `.md` file). Regenerated by `make dashboard` and the `mvp1-dashboard-regen` pre-commit hook. For the rich local view (filter chips, type colors), open [`mvp1_dashboard.html`](mvp1_dashboard.html) in a browser._ ## Next up @@ -14,20 +14,20 @@ Pull from the Idea backlog or capture a new feature spec. | Metric | Value | |---|---| -| Scoped items done | **38 / 38** (100%) — feat_/infra_/chore_/epic_ past idea stage | +| Scoped items done | **39 / 39** (100%) — feat_/infra_/chore_/epic_ past idea stage | | Path to MVP1 | **2** items remaining (features + bugs + chores) | -| Open bugs | 1 | -| Open chores | 1 (idea-stage debt) | +| Open bugs | 0 | +| Open chores | 2 (idea-stage debt) | | Backlog ideas | 1 idea-only feat/infra (not yet scoped into MVP1) | | In flight | 0 feature(s) actively shipping | ## Pipeline -### Done (46) +### Done (48) | Feature | Type | One-liner | Depends on | Status | |---|---|---|---|---| -| [feat_chat_agent](implemented_features/2026_05_12_feat_chat_agent/feature_spec.md) | Feature | A chat surface at `/chat/{conversation_id}` streams OpenAI completions via SSE. | `feat_contextual_help` `feat_contextual_help_mvp2` `feat_data_table_primitive` `feat_digest_proposal` `feat_fts_rank_ordering_mvp2` `feat_github_pr_worker` `feat_github_webhook` `feat_judgments_periodic_resume_sweep` `feat_llm_judgments` `feat_proposals_ui` `feat_query_inline_crud` `feat_studies_ui` `feat_study_lifecycle` `infra_adapter_elastic` `infra_arq_subprocess_test_mvp2` `infra_ci_smoke_makeup` `infra_dashboard_regen_pre_commit_conflict` `infra_e2e_seed_completed_study` `infra_foundation` `infra_frontend_stack_refresh` `infra_make_targets_split_backend_only` `infra_nvmrc` `infra_optuna_eval` `infra_per_trial_timeout` `infra_structlog_test_helpers` | [PR #60](https://github.com/SoundMindsAI/relyloop/pull/60) merged 2026-05-12 | +| [feat_chat_agent](implemented_features/2026_05_12_feat_chat_agent/feature_spec.md) | Feature | A chat surface at `/chat/{conversation_id}` streams OpenAI completions via SSE. | `feat_contextual_help` `feat_contextual_help_mvp2` `feat_data_table_primitive` `feat_digest_proposal` `feat_fts_rank_ordering_mvp2` `feat_github_pr_worker` `feat_github_webhook` `feat_judgments_periodic_resume_sweep` `feat_llm_judgments` `feat_proposals_ui` `feat_query_inline_crud` `feat_studies_ui` `feat_study_lifecycle` `infra_adapter_elastic` `infra_arq_subprocess_test_mvp2` `infra_ci_smoke_makeup` `infra_dashboard_regen_pre_commit_conflict` `infra_e2e_seed_completed_study` `infra_e2e_wire_seed_helper_into_studies_spec` `infra_foundation` `infra_frontend_stack_refresh` `infra_make_targets_split_backend_only` `infra_nvmrc` `infra_optuna_eval` `infra_per_trial_timeout` `infra_structlog_test_helpers` | [PR #60](https://github.com/SoundMindsAI/relyloop/pull/60) merged 2026-05-12 | | [feat_contextual_help](implemented_features/2026_05_15_feat_contextual_help/feature_spec.md) | Feature | a relevance engineer can launch their second study and interpret its digest without re-reading the tutorial, because every domain-jargon label has a one-click contextual definition grounded in the sam | — | [PR #122](https://github.com/SoundMindsAI/relyloop/pull/122) merged 2026-05-15 | | [feat_data_table_primitive](implemented_features/2026_05_16_feat_data_table_primitive/feature_spec.md) | Feature | Complete (PR #126, merged 2026-05-16) | — | [PR #126](https://github.com/SoundMindsAI/relyloop/pull/126) merged 2026-05-16 | | [feat_digest_proposal](implemented_features/2026_05_11_feat_digest_proposal/feature_spec.md) | Feature | When a study transitions to `completed`, the digest worker generates: a narrative summary (LLM-authored), a parameter-importance map (computed by `optuna.importance`), and a recommended config. | `feat_study_lifecycle` `feat_llm_judgments` | [PR #41](https://github.com/SoundMindsAI/relyloop/pull/41) merged 2026-05-11 | @@ -42,6 +42,7 @@ Pull from the Idea backlog or capture a new feature spec. | [infra_adapter_elastic](implemented_features/2026_05_10_infra_adapter_elastic/feature_spec.md) | Infra | A single `ElasticAdapter` implements the `SearchAdapter` Protocol and serves both Elasticsearch (8.11+ / 9.x) and OpenSearch (2.x / 3.x), distinguished by a `engine_type` column. | — | [PR #16](https://github.com/SoundMindsAI/relyloop/pull/16) merged 2026-05-10 | | [infra_ci_smoke_makeup](implemented_features/2026_05_13_infra_ci_smoke_makeup/idea.md) | Infra | Complete | — | Complete | | [infra_dashboard_regen_pre_commit_conflict](implemented_features/2026_05_14_infra_dashboard_regen_pre_commit_conflict/idea.md) | Infra | Complete | — | Complete | +| [infra_e2e_seed_completed_study](implemented_features/2026_05_17_infra_e2e_seed_completed_study/idea.md) | Infra | Complete | — | Complete | | [infra_foundation](implemented_features/2026_05_09_infra_foundation/feature_spec.md) | Infra | A relevance engineer can `git clone`, `docker compose up`, see all subsystems healthy in <60s on a 16GB laptop, and have a CI pipeline that gates every PR on lint, type-check, test, and an 80% coverag | — | [PR #4](https://github.com/SoundMindsAI/relyloop/pull/4) merged 2026-05-09 | | [infra_frontend_stack_refresh](implemented_features/2026_05_12_infra_frontend_stack_refresh/idea.md) | Infra | Complete | — | Complete | | [infra_make_targets_split_backend_only](implemented_features/2026_05_14_infra_make_targets_split_backend_only/idea.md) | Infra | Complete | — | Complete | @@ -64,8 +65,9 @@ Pull from the Idea backlog or capture a new feature spec. | [chore_starlette_422_deprecation](implemented_features/2026_05_13_chore_starlette_422_deprecation/idea.md) | Chore | Complete | — | Complete | | [chore_test_both_engines](implemented_features/2026_05_13_chore_test_both_engines/idea.md) | Chore | Complete | — | Complete | | [chore_trial_summary_single_query](implemented_features/2026_05_13_chore_trial_summary_single_query/idea.md) | Chore | Complete | — | Complete | -| [chore_tutorial_polish](implemented_features/2026_05_12_chore_tutorial_polish/feature_spec.md) | Chore | The release tag `v0.1.0` is pushed with: a worked tutorial at `docs/08_guides/tutorial-first-study.md`, sample data (50-query set + sample ES index of ~1,000 docs from the Amazon ESCI subset), README | `feat_chat_agent` `feat_contextual_help` `feat_contextual_help_mvp2` `feat_data_table_primitive` `feat_digest_proposal` `feat_fts_rank_ordering_mvp2` `feat_github_pr_worker` `feat_github_webhook` `feat_judgments_periodic_resume_sweep` `feat_llm_judgments` `feat_proposals_ui` `feat_query_inline_crud` `feat_studies_ui` `feat_study_lifecycle` `infra_adapter_elastic` `infra_arq_subprocess_test_mvp2` `infra_ci_smoke_makeup` `infra_dashboard_regen_pre_commit_conflict` `infra_e2e_seed_completed_study` `infra_foundation` `infra_frontend_stack_refresh` `infra_make_targets_split_backend_only` `infra_nvmrc` `infra_optuna_eval` `infra_per_trial_timeout` `infra_structlog_test_helpers` | [PR #64](https://github.com/SoundMindsAI/relyloop/pull/64) merged 2026-05-12 | +| [chore_tutorial_polish](implemented_features/2026_05_12_chore_tutorial_polish/feature_spec.md) | Chore | The release tag `v0.1.0` is pushed with: a worked tutorial at `docs/08_guides/tutorial-first-study.md`, sample data (50-query set + sample ES index of ~1,000 docs from the Amazon ESCI subset), README | `feat_chat_agent` `feat_contextual_help` `feat_contextual_help_mvp2` `feat_data_table_primitive` `feat_digest_proposal` `feat_fts_rank_ordering_mvp2` `feat_github_pr_worker` `feat_github_webhook` `feat_judgments_periodic_resume_sweep` `feat_llm_judgments` `feat_proposals_ui` `feat_query_inline_crud` `feat_studies_ui` `feat_study_lifecycle` `infra_adapter_elastic` `infra_arq_subprocess_test_mvp2` `infra_ci_smoke_makeup` `infra_dashboard_regen_pre_commit_conflict` `infra_e2e_seed_completed_study` `infra_e2e_wire_seed_helper_into_studies_spec` `infra_foundation` `infra_frontend_stack_refresh` `infra_make_targets_split_backend_only` `infra_nvmrc` `infra_optuna_eval` `infra_per_trial_timeout` `infra_structlog_test_helpers` | [PR #64](https://github.com/SoundMindsAI/relyloop/pull/64) merged 2026-05-12 | | [bug_capability_check_test_isolation](implemented_features/2026_05_12_bug_capability_check_test_isolation/idea.md) | Bug | Complete | — | Complete | +| [bug_cursor_decode_value_validation](implemented_features/2026_05_17_bug_cursor_decode_value_validation/idea.md) | Bug | Complete | — | Complete | | [bug_digest_param_importance_seam](implemented_features/2026_05_13_bug_digest_param_importance_seam/idea.md) | Bug | Complete | — | Complete | | [bug_dockerfile_missing_prompts](implemented_features/2026_05_13_bug_dockerfile_missing_prompts/idea.md) | Bug | Complete | — | Complete | | [bug_env_file_corrupted_during_session](implemented_features/2026_05_13_bug_env_file_corrupted_during_session/idea.md) | Bug | Complete | — | Complete | @@ -90,9 +92,9 @@ _None._ | Feature | Type | One-liner | Depends on | Status | |---|---|---|---|---| -| [infra_e2e_seed_completed_study](../02_product/planned_features/infra_e2e_seed_completed_study/idea.md) | Infra | `feat_contextual_help` Phase 1 ships 7 InfoTooltip placements on the digest panel ([`ui/src/components/studies/digest-panel.tsx`](../../ui/src/components/studies/digest-panel.tsx)) — five section labe | — | Idea — deferred from `feat_contextual_help` Phase 1 Story 3.1 | -| [chore_data_table_primitive_followups](../02_product/planned_features/chore_data_table_primitive_followups/idea.md) | Chore | `feat_data_table_primitive` shipped with six known non-regression follow-up items captured only in chat transcripts. None block the PR but each is a real improvement that would otherwise evaporate whe | — | Idea — six discrete follow-ups deferred from `feat_data_table_primitive` GPT-5.5 review cycles (Epic 2 cycles 1/2/3 + Epic 3 cycle 1) and from Step 2.5 tangential-observations sweep. Each was correctly classified "defer as non-regression follow-up" at the time; capturing them now per the CLAUDE.md tangential-discoveries rule so they don't evaporate. | -| [bug_cursor_decode_value_validation](../02_product/planned_features/bug_cursor_decode_value_validation/idea.md) | Bug | `backend/app/db/repo/_sort.py:decode_cursor()` performs a `json.loads(base64.urlsafe_b64decode(raw))` round-trip and then takes `decoded[0]` + `str(decoded[1])` without validating the payload shape or | — | Idea — defense-in-depth concern surfaced by GPT-5.5 final review on PR #126 (feat_data_table_primitive). | +| [infra_e2e_wire_seed_helper_into_studies_spec](../02_product/planned_features/infra_e2e_wire_seed_helper_into_studies_spec/idea.md) | Infra | `infra_e2e_seed_completed_study` shipped `POST /api/v1/_test/studies/seed-completed` and the `seedStudyCompletedWithDigest` TypeScript helper. The two consuming E2E tests in `ui/tests/e2e/studies.spec | — | Idea — deferred from `infra_e2e_seed_completed_study` PR #130. The endpoint + helper landed cleanly; the 2 Playwright E2E tests that consume them caused the smoke CI lane to fail (root cause undiagnosed — agent environment had no access to GitHub Actions logs to debug the Playwright report). | +| [chore_data_table_primitive_followups](../02_product/planned_features/chore_data_table_primitive_followups/idea.md) | Chore | `feat_data_table_primitive` shipped with six known non-regression follow-up items captured only in chat transcripts. None block the PR but each is a real improvement that would otherwise evaporate whe | — | Partial — items 1, 2, 4, 6 shipped in PR (TBD) 2026-05-17. Items 3 + 5 remain open (larger refactors touching 8 DataTable consumers each). | +| [chore_dropdown_primitive](../02_product/planned_features/chore_dropdown_primitive/idea.md) | Chore | The DataTable primitive shipped with a lint-enforced source-of-truth discipline — every enum filter cites a backend `Literal[...]` and every FK filter cites a `useX()` hook. Forms didn't get the same | — | Idea — identified during tutorial UX debugging session 2026-05-17. | ## Dependency graph @@ -181,6 +183,8 @@ graph LR class feat_contextual_help done; feat_data_table_primitive["data table primitive"] class feat_data_table_primitive done; + infra_e2e_seed_completed_study["e2e seed completed study"] + class infra_e2e_seed_completed_study done; feat_study_lifecycle --> feat_digest_proposal feat_llm_judgments --> feat_digest_proposal infra_foundation --> feat_llm_judgments @@ -201,6 +205,7 @@ graph LR infra_adapter_elastic --> chore_tutorial_polish infra_ci_smoke_makeup --> chore_tutorial_polish infra_dashboard_regen_pre_commit_conflict --> chore_tutorial_polish + infra_e2e_seed_completed_study --> chore_tutorial_polish infra_foundation --> chore_tutorial_polish infra_frontend_stack_refresh --> chore_tutorial_polish infra_make_targets_split_backend_only --> chore_tutorial_polish @@ -222,6 +227,7 @@ graph LR infra_adapter_elastic --> feat_chat_agent infra_ci_smoke_makeup --> feat_chat_agent infra_dashboard_regen_pre_commit_conflict --> feat_chat_agent + infra_e2e_seed_completed_study --> feat_chat_agent infra_foundation --> feat_chat_agent infra_frontend_stack_refresh --> feat_chat_agent infra_make_targets_split_backend_only --> feat_chat_agent diff --git a/docs/00_overview/MVP2_DASHBOARD.md b/docs/00_overview/MVP2_DASHBOARD.md index dbd2cdd8..d44e129e 100644 --- a/docs/00_overview/MVP2_DASHBOARD.md +++ b/docs/00_overview/MVP2_DASHBOARD.md @@ -2,7 +2,7 @@ # RelyLoop MVP2 Dashboard -_Reflects feature-folder state as of **2026-05-16** (latest mtime of any planned/implemented feature `.md` file). Regenerated by `make dashboard` and the `mvp1-dashboard-regen` pre-commit hook. For the rich local view (filter chips, type colors), open [`mvp2_dashboard.html`](mvp2_dashboard.html) in a browser._ +_Reflects feature-folder state as of **2026-05-18** (latest mtime of any planned/implemented feature `.md` file). Regenerated by `make dashboard` and the `mvp1-dashboard-regen` pre-commit hook. For the rich local view (filter chips, type colors), open [`mvp2_dashboard.html`](mvp2_dashboard.html) in a browser._ ## Next up diff --git a/docs/00_overview/dashboard.html b/docs/00_overview/dashboard.html index c4ebbf57..ec9e1e41 100644 --- a/docs/00_overview/dashboard.html +++ b/docs/00_overview/dashboard.html @@ -355,7 +355,7 @@

RelyLoop — Release Roadmap

- Top-level index across MVP1 → GA v1+ as of 2026-05-16. Click a release name to + Top-level index across MVP1 → GA v1+ as of 2026-05-18. Click a release name to drill into the per-release dashboard. Theme labels sourced from tech-stack.md §"Canonical release matrix". See state.md for @@ -371,7 +371,7 @@

Releases

The Loop
-
38 / 38 scoped done · 2 remaining
+
39 / 39 scoped done · 2 remaining
In progress
diff --git a/docs/00_overview/mvp1_dashboard.html b/docs/00_overview/mvp1_dashboard.html index 69524f8d..e898712e 100644 --- a/docs/00_overview/mvp1_dashboard.html +++ b/docs/00_overview/mvp1_dashboard.html @@ -356,7 +356,7 @@

RelyLoop MVP1 Dashboard

- Reflects feature-folder state as of 2026-05-16 (latest mtime of any + Reflects feature-folder state as of 2026-05-18 (latest mtime of any docs/02_product/planned_features/ or docs/00_overview/implemented_features/ file). See state.md for the active branch context, @@ -384,7 +384,7 @@

MVP1 Progress

Scoped items done
-
38 / 38
+
39 / 39
100% of feat_/infra_/chore_/epic_ items past idea stage
@@ -393,14 +393,14 @@

MVP1 Progress

2
items left = features + bugs + chores
-
+
Open bugs
-
1
+
0
tracked bug_* idea files
Open chores
-
1
+
2
idea-stage chore_* (debt)
@@ -431,12 +431,12 @@

Pipeline

Idea 3

- +
Infra
-
`feat_contextual_help` Phase 1 ships 7 InfoTooltip placements on the digest panel ([`ui/src/components/studies/digest-panel.tsx`](../../ui/src/components/studies/digest-panel.tsx)) — five section labe
+
`infra_e2e_seed_completed_study` shipped `POST /api/v1/_test/studies/seed-completed` and the `seedStudyCompletedWithDigest` TypeScript helper. The two consuming E2E tests in `ui/tests/e2e/studies.spec
@@ -454,13 +454,13 @@

Idea 3

-
- +
+
- Bug + Chore
-
`backend/app/db/repo/_sort.py:decode_cursor()` performs a `json.loads(base64.urlsafe_b64decode(raw))` round-trip and then takes `decoded[0]` + `str(decoded[1])` without validating the payload shape or
+
The DataTable primitive shipped with a lint-enforced source-of-truth discipline — every enum filter cites a backend `Literal[...]` and every FK filter cites a `useX()` hook. Forms didn't get the same
@@ -483,7 +483,7 @@

Implementing 0

-

Done 46

+

Done 48

@@ -493,7 +493,7 @@

Done 46

A chat surface at `/chat/{conversation_id}` streams OpenAI completions via SSE.
-
depends on: feat_contextual_helpfeat_contextual_help_mvp2feat_data_table_primitivefeat_digest_proposalfeat_fts_rank_ordering_mvp2feat_github_pr_workerfeat_github_webhookfeat_judgments_periodic_resume_sweepfeat_llm_judgmentsfeat_proposals_uifeat_query_inline_crudfeat_studies_uifeat_study_lifecycleinfra_adapter_elasticinfra_arq_subprocess_test_mvp2infra_ci_smoke_makeupinfra_dashboard_regen_pre_commit_conflictinfra_e2e_seed_completed_studyinfra_foundationinfra_frontend_stack_refreshinfra_make_targets_split_backend_onlyinfra_nvmrcinfra_optuna_evalinfra_per_trial_timeoutinfra_structlog_test_helpers
+
depends on: feat_contextual_helpfeat_contextual_help_mvp2feat_data_table_primitivefeat_digest_proposalfeat_fts_rank_ordering_mvp2feat_github_pr_workerfeat_github_webhookfeat_judgments_periodic_resume_sweepfeat_llm_judgmentsfeat_proposals_uifeat_query_inline_crudfeat_studies_uifeat_study_lifecycleinfra_adapter_elasticinfra_arq_subprocess_test_mvp2infra_ci_smoke_makeupinfra_dashboard_regen_pre_commit_conflictinfra_e2e_seed_completed_studyinfra_e2e_wire_seed_helper_into_studies_specinfra_foundationinfra_frontend_stack_refreshinfra_make_targets_split_backend_onlyinfra_nvmrcinfra_optuna_evalinfra_per_trial_timeoutinfra_structlog_test_helpers
@@ -665,6 +665,18 @@

Done 46

+
+ +
+ Infra + merged 2026-05-17 +
+
Complete
+ + +
+ +
@@ -937,7 +949,7 @@

Done 46

The release tag `v0.1.0` is pushed with: a worked tutorial at `docs/08_guides/tutorial-first-study.md`, sample data (50-query set + sample ES index of ~1,000 docs from the Amazon ESCI subset), README
-
depends on: feat_chat_agentfeat_contextual_helpfeat_contextual_help_mvp2feat_data_table_primitivefeat_digest_proposalfeat_fts_rank_ordering_mvp2feat_github_pr_workerfeat_github_webhookfeat_judgments_periodic_resume_sweepfeat_llm_judgmentsfeat_proposals_uifeat_query_inline_crudfeat_studies_uifeat_study_lifecycleinfra_adapter_elasticinfra_arq_subprocess_test_mvp2infra_ci_smoke_makeupinfra_dashboard_regen_pre_commit_conflictinfra_e2e_seed_completed_studyinfra_foundationinfra_frontend_stack_refreshinfra_make_targets_split_backend_onlyinfra_nvmrcinfra_optuna_evalinfra_per_trial_timeoutinfra_structlog_test_helpers
+
depends on: feat_chat_agentfeat_contextual_helpfeat_contextual_help_mvp2feat_data_table_primitivefeat_digest_proposalfeat_fts_rank_ordering_mvp2feat_github_pr_workerfeat_github_webhookfeat_judgments_periodic_resume_sweepfeat_llm_judgmentsfeat_proposals_uifeat_query_inline_crudfeat_studies_uifeat_study_lifecycleinfra_adapter_elasticinfra_arq_subprocess_test_mvp2infra_ci_smoke_makeupinfra_dashboard_regen_pre_commit_conflictinfra_e2e_seed_completed_studyinfra_e2e_wire_seed_helper_into_studies_specinfra_foundationinfra_frontend_stack_refreshinfra_make_targets_split_backend_onlyinfra_nvmrcinfra_optuna_evalinfra_per_trial_timeoutinfra_structlog_test_helpers
@@ -953,6 +965,18 @@

Done 46

+
+ +
+ Bug + merged 2026-05-17 +
+
Complete
+ + +
+ +
@@ -1125,6 +1149,8 @@

Dependency graph (feat_ + infra_)

class feat_contextual_help done; feat_data_table_primitive["data table primitive"] class feat_data_table_primitive done; + infra_e2e_seed_completed_study["e2e seed completed study"] + class infra_e2e_seed_completed_study done; feat_study_lifecycle --> feat_digest_proposal feat_llm_judgments --> feat_digest_proposal infra_foundation --> feat_llm_judgments @@ -1145,6 +1171,7 @@

Dependency graph (feat_ + infra_)

infra_adapter_elastic --> chore_tutorial_polish infra_ci_smoke_makeup --> chore_tutorial_polish infra_dashboard_regen_pre_commit_conflict --> chore_tutorial_polish + infra_e2e_seed_completed_study --> chore_tutorial_polish infra_foundation --> chore_tutorial_polish infra_frontend_stack_refresh --> chore_tutorial_polish infra_make_targets_split_backend_only --> chore_tutorial_polish @@ -1166,6 +1193,7 @@

Dependency graph (feat_ + infra_)

infra_adapter_elastic --> feat_chat_agent infra_ci_smoke_makeup --> feat_chat_agent infra_dashboard_regen_pre_commit_conflict --> feat_chat_agent + infra_e2e_seed_completed_study --> feat_chat_agent infra_foundation --> feat_chat_agent infra_frontend_stack_refresh --> feat_chat_agent infra_make_targets_split_backend_only --> feat_chat_agent @@ -1276,6 +1304,8 @@

Dependency graph (feat_ + infra_)

class feat_contextual_help done; feat_data_table_primitive["data table primitive"] class feat_data_table_primitive done; + infra_e2e_seed_completed_study["e2e seed completed study"] + class infra_e2e_seed_completed_study done; feat_study_lifecycle --> feat_digest_proposal feat_llm_judgments --> feat_digest_proposal infra_foundation --> feat_llm_judgments @@ -1296,6 +1326,7 @@

Dependency graph (feat_ + infra_)

infra_adapter_elastic --> chore_tutorial_polish infra_ci_smoke_makeup --> chore_tutorial_polish infra_dashboard_regen_pre_commit_conflict --> chore_tutorial_polish + infra_e2e_seed_completed_study --> chore_tutorial_polish infra_foundation --> chore_tutorial_polish infra_frontend_stack_refresh --> chore_tutorial_polish infra_make_targets_split_backend_only --> chore_tutorial_polish @@ -1317,6 +1348,7 @@

Dependency graph (feat_ + infra_)

infra_adapter_elastic --> feat_chat_agent infra_ci_smoke_makeup --> feat_chat_agent infra_dashboard_regen_pre_commit_conflict --> feat_chat_agent + infra_e2e_seed_completed_study --> feat_chat_agent infra_foundation --> feat_chat_agent infra_frontend_stack_refresh --> feat_chat_agent infra_make_targets_split_backend_only --> feat_chat_agent diff --git a/docs/00_overview/mvp2_dashboard.html b/docs/00_overview/mvp2_dashboard.html index 155f835d..1084cb39 100644 --- a/docs/00_overview/mvp2_dashboard.html +++ b/docs/00_overview/mvp2_dashboard.html @@ -356,7 +356,7 @@

RelyLoop MVP2 Dashboard

- Reflects feature-folder state as of 2026-05-16 (latest mtime of any + Reflects feature-folder state as of 2026-05-18 (latest mtime of any docs/02_product/planned_features/ or docs/00_overview/implemented_features/ file). See state.md for the active branch context, diff --git a/docs/02_product/planned_features/chore_dropdown_primitive/idea.md b/docs/02_product/planned_features/chore_dropdown_primitive/idea.md new file mode 100644 index 00000000..5202a9d9 --- /dev/null +++ b/docs/02_product/planned_features/chore_dropdown_primitive/idea.md @@ -0,0 +1,98 @@ +# Dropdown primitive + cross-app form-select discipline + +**Date:** 2026-05-17 +**Status:** Idea — identified during tutorial UX debugging session 2026-05-17. +**Origin:** Operator hit the "type a cluster UUID" form field in [`create-query-set-modal.tsx:86-92`](../../../../ui/src/components/query-sets/create-query-set-modal.tsx) while following the [first-study tutorial](../../../08_guides/tutorial-first-study.md) Step 5. Cluster-id text input is the only UUID-paste survivor — every other FK in the UI already uses a `` is hand-rolled, and the enum/FK source-of-truth discipline that `feat_data_table_primitive` baked into column-configs (per [CLAUDE.md "Enumerated Value Contract Discipline"](../../../../CLAUDE.md)) does not apply to form-level dropdowns. +**Depends on:** None. [`feat_data_table_primitive`](../../../00_overview/implemented_features/2026_05_16_feat_data_table_primitive/) (PR #126, merged 2026-05-16) established the pattern this idea generalizes. + +## Problem + +The DataTable primitive shipped with a lint-enforced source-of-truth discipline — every enum filter cites a backend `Literal[...]` and every FK filter cites a `useX()` hook. Forms didn't get the same treatment, and three concrete consequences are now visible: + +1. **One UUID-paste field still ships** at [`create-query-set-modal.tsx:86-92`](../../../../ui/src/components/query-sets/create-query-set-modal.tsx) — ``. The tutorial's curl example even uses a `` placeholder that operators copy-paste literally, leading to a delayed `CLUSTER_NOT_FOUND` and a confusing `QUERY_SET_NOT_FOUND` on the next call. (Documented this session — see chat transcript 2026-05-17.) + +2. **FK selects are duplicated, not shared.** Six form components hand-roll the "load entities, render `` with "Loading clusters…" placeholder. Error state shows a disabled `` and `` with ``. **<60min — implement inline in this PR** (per CLAUDE.md implement-over-defer rule). +- [`create-study-modal.tsx`](../../../../ui/src/components/studies/create-study-modal.tsx) — replace four hand-rolled FK selects with `` instances. Enum selects (metric, k, direction, sampler, pruner) move to `@/lib/enums` imports. +- [`register-cluster-modal.tsx`](../../../../ui/src/components/clusters/register-cluster-modal.tsx) — repo `` → ``. +- [`create-template-modal.tsx`](../../../../ui/src/components/templates/create-template-modal.tsx) — engine enum → `@/lib/enums`. +- [`override-popover.tsx`](../../../../ui/src/components/judgments/override-popover.tsx) — rating enum → `@/lib/enums`. + +### 4. Tutorial doc cleanup + +[`tutorial-first-study.md`](../../../08_guides/tutorial-first-study.md) Step 5 currently shows a `` placeholder that operators paste literally. After the modal switches to a dropdown, the curl example can drop the inline UUID and lead with the `LOCAL_ES_ID=$(curl ... | jq -r ...)` pattern from this session's debug transcript, with the modal as the primary path and curl as the API-equivalent. + +## Scope signals + +- **Backend:** none. +- **Frontend:** + - 1 new primitive (`entity-select.tsx`) — ~150 LOC + co-located test. + - 1 new directory (`lib/enums/`) — ~50 LOC total across ~6 enum files. + - 1 lint guard test extension — ~80 LOC in `__tests__/components/common/form-select-discipline.test.tsx` (new file, mirrors the DataTable pattern). + - 6 form components migrated — ~30-60 LOC delta per component (mostly deletions; the inline `` — so the "UUID text input" failure is a one-component bug, not a system-wide regression. The reason to bundle this as a primitive rather than patch one modal: + +- The primitive lets the status-dot + inline-warning UX (proposed during the same debug session) ship once and apply uniformly. Otherwise every modal re-implements it slightly differently. +- The enum-discipline lint extension is the cheap insurance — without it, enum drift is a known-and-documented failure mode that the codebase has not yet been bitten by, but only because every enum has stayed small. The first time someone adds a new `study_status` value, the inline `` blocks across 3 components will silently miss it. + +Picking it up is a one-PR job once a contributor has the bandwidth — no upstream dependencies, no spec gates, no operator coordination. + +## Relationship to other work + +- **Parent pattern:** [`feat_data_table_primitive`](../../../00_overview/implemented_features/2026_05_16_feat_data_table_primitive/) — same playbook, applied to form-level dropdowns. The lint-guard extension lives in the same test directory as the DataTable guard. +- **Sibling follow-ups:** [`chore_data_table_primitive_followups`](../chore_data_table_primitive_followups/idea.md) — independent (different surface). No ordering constraint. +- **Tutorial impact:** [`docs/08_guides/tutorial-first-study.md`](../../../08_guides/tutorial-first-study.md) Step 5 and Step 7 both reference UUID-paste patterns that the modal-via-dropdown path supersedes. Update those examples in the same PR as the modal migration. +- **Future:** when MVP4 adds the multi-tenant `tenants` table, the tenant picker is another `` consumer — having the primitive ready means MVP4 doesn't re-invent it.