Skip to content
Merged
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
4 changes: 3 additions & 1 deletion docs/outstanding-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ removed after current-main verification; it is not missing recommended work.
| 34 | `#163` | A3 | High — frontend/UI | After or with `#162` | 0.5–1.5 days | Redesign `/services?q=` as Progressive Referral Workflow (direction B): H1 = query (not match count), progressive shortlist/compare (no always-on decision panel or giant step rail). Comps in `public/mockups/mode-page-redesign-2026-07/services-search/`. Verify referral shortlist still works; stop before changing Services home ModeHome. |
| 35 | `#164` | A3 | High — frontend/UI | Product confirmed Favourites is hybrid dashboard+search (no ModeHome) | 1–2 days | Redesign Favourites as one dashboard + search page: recommended Search-Led Workspace (direction B) — persistent search, sets as chips, Continue + recent + table on empty query, in-place filter on typed query. Comps in `public/mockups/mode-page-redesign-2026-07/favourites-hybrid/`. Do not reintroduce ModeHome for Favourites. Verify desktop+phone; stop before splitting into separate ModeHome routes. |

<!-- issues:next-id=179 -->
<!-- issues:next-id=181 -->

## Open items

Expand Down Expand Up @@ -158,6 +158,8 @@ removed after current-main verification; it is not missing recommended work.
| #176 | P3 | issue | sourceChanged in use-hide-on-scroll has the ordering defect just fixed for viewportHeightChanged | **Outcome:** no early-return guard in `computeScrollHideUpdate` can strand phone chrome hidden at the top of the scroll range. **Detail:** PR #1489's remediation moved the `viewportHeightChanged` guard below the `offset <= topRevealOffset` branch, because the top reveal band is an absolute layout contract and a resize does not stop the reader being at the top. The `sourceChanged` guard immediately above it still returns `{ hidden: currentlyHidden }` before that branch is reached, so a scroll-source switch that lands while the chrome is hidden and the scroller is clamped to the top leaves the chrome off-screen until the next scroll. Left alone deliberately during the #1489 remediation: different failure mode (container handoff rather than viewport geometry), and offsets from a different container genuinely are not comparable, so the fix is not simply reordering. **Next:** decide whether a fresh source at `offset <= topRevealOffset` should reveal — it almost certainly should, since the new container's offset is meaningful even though the delta is not — then reorder and add a case at offset 0 alongside the one added for `viewportHeightChanged`. **Gate:** `npm run verify:phone-chrome`; read `docs/search-chrome-behaviour.md` first. **Stop:** do not widen this into a rewrite of the guard chain; one branch, one test. Renumbered from this PR's original `#170`. | PR #1489 review remediation; PR #1532; session 2026-07-31 | 2026-07-31 |
| #177 | P3 | rec | Therapy catalogue aliases duplicate 2.53 MB of bytes instead of pointing at the hashed file | **Outcome:** the unversioned catalogue aliases stop costing a second copy of every payload in the repo and the image. **Detail:** `public/therapy-compass-data/` ships each catalogue twice — `therapies.json` (2,529,429 bytes) alongside `therapies.<hash>.json` with identical content, and the same for the index and home projections. Git stores one blob for each identical pair, so history is unaffected, but the working tree and every Docker image carry both. PR #1489's remediation adds a one-deploy grace generation, so a stem whose content changed now holds three copies until the next regeneration. **Next:** serve the alias as a Next rewrite to the current hashed filename instead of writing a duplicate file — the alias would keep its `max-age=0, must-revalidate` header (the hashed target keeps `immutable`, so the rewrite must not inherit it) and the generator would stop copying bytes. Check that a rewrite still satisfies the alias-equality assertions in `check:therapy-data-index` and `tests/therapy-compass-mode-wiring.test.ts`, which currently compare file bytes. **Stop:** do not drop the aliases outright — they are the fallback `useTherapyData` uses when a pre-deploy bundle names a hash that no longer exists. Renumbered from this PR's original `#171`. | PR #1489 review remediation; PR #1532; session 2026-07-31 | 2026-07-31 |
| #178 | P2 | rec | pr-policy does not flag operational risk bundled with clinical or UI risk | **Outcome:** a PR that mixes operational-risk paths with clinical or UI risk is called out before it merges, because squash-merging that mix destroys per-item revert. **Detail:** `classifyPullRequestFiles` already computes `operationalRisk`, `clinicalRisk`, `ragRanking` and `ui` independently, but nothing reacts to the combination. AGENTS.md's "PR bundling" section forbids bundling anything once `operationalRisk` is true; the classifier is where that could be enforced. PR #1489 is the worked example: 33 files spanning `.github/workflows/ci.yml`, both Dockerfiles, a rewrite of the bundle-budget gate, a phone-chrome scroll change and a therapy data restructure, merged as one squash (945148251). Reverting any single item now means hand-reverting hunks out of the squash commit, because the branch commits are unreachable. The remediation PR for that review repeats the pattern on a smaller scale (clinical data plus a one-line ci.yml timeout), which is why this is a recommendation rather than a hard gate — the right severity is probably a warning that names the mixed classes, not a merge block. **Next:** emit an advisory line from `evaluatePullRequestPolicy` when `operationalRisk` coincides with `clinicalRisk` or `ui`, listing which paths drove each; decide separately whether it ever blocks. Cover it in the `--self-test` block. **Stop:** do not make it a hard failure in the same change that introduces it — land the signal first and see how often it fires. Renumbered from this PR's original `#172`. | PR #1489 review remediation; PR #1532; session 2026-07-31 | 2026-07-31 |
| #179 | P3 | issue | The full therapy catalogue silently switched from minified to pretty-printed | **Outcome:** the full catalogue's on-disk format is a decision someone made, not a side effect. **Detail:** `public/therapy-compass-data/therapies.json` was a single minified line (2,529,429 bytes) until PR #1532 routed it through `syncTarget` to scrub the tag-echo `modality`. `syncTarget` serialises with `JSON.stringify(records, null, 2)`, so the file is now 18,400 pretty-printed lines at 2,652,783 bytes. **Measured cost:** +123,354 bytes raw (+4.9%), but only +3,409 bytes gzipped (333,428 -> 336,837, +1.0%) — gzip absorbs almost all the added whitespace, and the file is not JavaScript so `check:bundle-budget` never sees it. The visible cost is git churn: each future data revision now produces ~18,400 changed lines in this file plus another ~18,400 in its content-addressed twin, which is why PR #1532 read as 42,071 insertions when only 381 of them were hand-written. **The benefit is real and probably wins:** a 205-record clinical dataset with a one-line diff is unreviewable, and a reviewer needs to see which clinical field changed. The two index projections were already pretty-printed, so this is now consistent. **Next:** confirm the format deliberately, then either leave it (recommended — 1% wire cost buys reviewable clinical diffs) or pass a minified serialiser for the full payload only, which would land below main's original size now that `modality` is gone. Either way record the reason next to `syncTarget`. **Stop:** do not minify the index or home projections to "match" — they are small and their diffs are the review surface for catalogue changes. | PR #1532; session 2026-07-31 | 2026-07-31 |
| #180 | P3 | issue | build-therapies-index now overwrites its own source input | **Outcome:** the therapy catalogue generator has a source it does not also destroy. **Detail:** `scripts/build-therapies-index.mjs` reads `public/therapy-compass-data/therapies.json` as `source`, and since PR #1532 also writes the curated full payload back to that same path (`syncTarget(legacyFullTarget, curatedFull)`). Input and output are now the same file, so the generator is no longer idempotent with respect to its own input in the strict sense: it is stable in practice only because `curatedModality` is a fixed point (a null modality stays null, a genuinely curated non-tag value survives a second pass). The original tag-echo values are now recoverable only from git history. **Why it matters beyond tidiness:** `#175` asks someone to curate real modality values in this file. They will be editing the generator's output, and any field the generator ever decides to rewrite will silently discard their edit on the next run — with no gate to catch it, because `--check` compares the file against what the generator would produce, so a clobbered hand edit reads as "current". **Next:** separate the two roles — keep an unprocessed source (`therapies.source.json`, or move curation upstream of `public/`) and let `therapies.json` be purely generated, or at minimum add a header comment to the file stating it is generated and hand edits to processed fields will not survive. **Stop:** do not fix this by reverting the full-catalogue scrub; detail and recommend load `catalogue: "full"`, so the scrub is what keeps the ECT/rTMS mislabel off the only screens that render the chip. | PR #1532; session 2026-07-31 | 2026-07-31 |


## Resolved / archive
Expand Down
Loading