Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/branch-review-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -971,3 +971,4 @@ This file is append-only. Never rewrite or delete an existing review record; app
| 2026-07-25 | cursor/pr1190-dark-mode-salvage-f453 (PR #1214) | pending-ci-retrigger-2 | CI unblock + skip-branch-sync | Repeated pr-branch-sync bot merges left CI `action_required`. Applied `skip-branch-sync` label and agent retrigger so required checks can finish for squash-merge; then close #1190. | Hosted CI pending on tip; no provider-backed checks. |
| 2026-07-25 | cursor/pr1190-dark-mode-salvage-f453 (PR #1214) | `e19442240afbd7f28c321e399c6b4dcb0a7c9fdf` / squash `bb6b394617cbd906285ebe19e0e452912793320a` | prlanded after squash merge | MERGED. Two-dot content diff vs `origin/main` empty; clinical gate preserved; `no-hardcoded-hex` + theme CSS on main; PWA manifest theme colours intentionally absent. Hosted PR required SUCCESS (Unit/Build/Static/Production UI). Remote salvage branch deleted by squash `--delete-branch`. | `gh pr view` MERGED; `git diff origin/main e19442240` empty; trustGated grep on main; no provider-backed checks. |
| 2026-07-25 | PR #1190 / `remediate-dark-mode-audit` | `00eca49b9b0d7e5fbfa5703a15e9e930963984a6` | Close without merge after #1214 salvage | CLOSED (not merged). Unsafe tip superseded by #1214. Remote branch `remediate-dark-mode-audit` retained pending optional cleanup; do not merge. | `gh pr view` state=CLOSED mergedAt=null; no provider-backed checks. |
| 2026-07-25 | cursor/formulation-disabled-accessibility-14d4 | 5be3d6ccccc4c974539612fb6fbbbfb6dfb3d105 | #064 clean replacement PR-readiness review | Current-main clean-room replay contains only the formulation disabled-state improvement, focused browser coverage, and issue disposition. Product/test content is identical to the independently reviewed change; no P0-P2 findings. Conflicted PR #1219 is superseded and must not merge. | Focused Chromium 2/2; `verify:cheap` 3,421/3,421; `verify:pr-local` passed with build, client-secret scan, and offline RAG fixtures; no provider-backed checks. |
4 changes: 2 additions & 2 deletions docs/outstanding-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ removed after current-main verification; it is not missing recommended work.
| 9 | `#029` | A2 | Specialist — answer quality/clinical safety | After `#051`/`#023` and `#018` | 0.5–1 day inventory; 1–3 days per fix | Re-enumerate current fallback stubs and fix one causal cluster at a time without weakening grounding/citation gates. Stop if a change merely makes the metric easier to pass. |
| 10 | `#001` | A2 | Specialist — retrieval/ranking | After `#051`/`#023` and rollout approval | 0.5–1 day plus canary | Keep semantic reranking off unless an approved ambiguity comparison preserves 36/36, recall 1.0, zero per-case regressions, and shows measured gain; otherwise record keep-off and stop. |
| 11 | `#025` | A2 | Operator — Railway/GitHub/chat/Supabase | Next approved observability window | 1–3 hours/channel | Choose owned deployment, CI, ingestion, and SLO alerts; mock first, then one approved controlled provider event/channel. The merged Supabase trigger remains inert until its verified inputs are configured. Stop without an accountable responder. |
| 12 | `#064` | A2 | High — frontend/browser | After higher-acuity local fixes; before the release UI gate | 4–8 hours | Preserve the isolated dirty formulation/contrast patch, reconcile its intent against current `main`, and run focused Playwright plus `verify:ui`. Stop rather than overwriting unrelated work or weakening access-control assertions. |
| 12 | `#064` | A2 | High — frontend/browser | Clean replacement branch ready for hosted review | Merge only after required CI is green | `cursor/formulation-disabled-accessibility-14d4` contains only the reviewed formulation disabled-state fix, focused Playwright coverage, and consolidated ledger evidence on current `main`; the conflicted PR #1219 must not merge. |
| 13 | `#055` | A2 | Specialist release owner + Operator | Before next full-confidence release/handoff | 2–4 hours plus runtime | On one exact SHA, run local/provider gates, Firefox/WebKit, required hosted CI, and close actionable GitHub threads. Stop at first failure and rerun only the repaired smallest gate. |
| 14 | `#056` | A2 | Operator — Supabase/Railway + Specialist | After cost/ownership approval | 0.5–1 day | Provision isolated `Clinical KB Staging` with synthetic data and distinct secrets. Verify identity, schema, indexing, health, and data boundary; never copy production clinical documents. |
| 15 | `#057` | A2 | High — release/SRE + Operator | After `#056` | 2–4 hours plus soak | Run documented staging soak and rollback against an exact candidate. Retain latency/error/rollback evidence; stop on unsafe data, identity mismatch, or unowned rollback. |
Expand Down Expand Up @@ -94,7 +94,7 @@ removed after current-main verification; it is not missing recommended work.
| ID | Pri | Type | Summary | Detail / next action | Source | Added |
| ---- | --- | ----- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| #059 | P1 | task | Verify containment of every credential reported exposed in chat | **Outcome:** every reported exposed credential is rejected or retired. **Next:** in approved security windows, verify and revoke or rotate the GitHub token, OpenAI key, Supabase service-role JWT, database password, and E2E credential; create replacements only when required and update only intended secret stores. **Success:** provider evidence confirms the old credentials cannot authenticate, replacements are distinct and minimally scoped, presence/readiness checks pass, and secret scans remain clean. **Stop:** no provider or secret-store action without approval; never print or paste values into Git, logs, issues, or chat. | session 2026-07-24 security reconciliation; AI Agent Target Manifest | 2026-07-24 |
| #064 | P2 | task | Reconcile the preserved browser and contrast patch | **Outcome:** the isolated dirty formulation/contrast patch is safely landed or explicitly dispositioned. **Next:** rebase its intent against current `main` without overwriting the worktree, then run focused Playwright coverage and `verify:ui`. **Success:** intended disabled/contrast behavior is accessible, browser assertions remain meaningful, and unrelated work is preserved. **Stop:** do not discard or auto-merge the dirty worktree; pause on ambiguous ownership or scope. | `agent/formulation-disabled-contrast`; session 2026-07-24 | 2026-07-24 |
| #064 | P2 | task | Reconcile the preserved browser and contrast patch | **Clean replacement ready 2026-07-25:** commit `70dd8cd9` on `cursor/formulation-disabled-accessibility-14d4` applies the independently reviewed opacity-free disabled styling and native-disabled/focus/axe coverage directly to current `main`. The unavailable historical worktree remains untouched. The earlier PR #1219 passed hosted checks but became GitHub-conflicted after automated branch sync and must not merge. **Next:** open the clean replacement PR and require all hosted checks, especially Production UI, to pass before merge. **Stop:** do not merge #1219, weaken PWA/access-control assertions, or alter the historical worktree. | `agent/formulation-disabled-contrast`; clean replacement `cursor/formulation-disabled-accessibility-14d4`; session 2026-07-25 | 2026-07-24 |
| #065 | P2 | task | Complete the paused compact document source-text accordion | **Outcome:** the document viewer uses compact nested disclosures while retaining complete text, citation/search navigation, print behavior, and composer clearance. **Next:** only when the user explicitly resumes, reconcile `codex/chat-document-text-accordion-7cb4` with current `main` and complete the focused 320/390/1280 px tests. **Success:** default disclosures are closed; deep links and search open only the active passage; printing expands/restores state; no overflow. **Verify:** focused document-viewer Playwright, `verify:cheap`, `verify:ui`, and static production-readiness. **Stop:** remain paused until explicit user return; no provider calls. | paused document-viewer task; `codex/chat-document-text-accordion-7cb4` | 2026-07-24 |
| #051 | P2 | task | Stabilise the live answer-quality canary before more RAG tuning | Diagnostics landed in PR #1095: structured JSON/Markdown artifacts now record the actual checked-out SHA, run identity and latency context, and the offline trend tool separates content, provider-route and latency outcomes. First validating run `30018289898` recorded the expected tree and cost, with 36/36 retrieval green, but one report cannot establish variability; PR #1097 prevents a single failure being mislabeled as repeated. Next: compare the scheduled 2026-07-26 structured report with this run. Do not spend on an immediate retry or reapply the archived lithium guard before that comparison. | PR #1095; run `30018289898`; PR #1097; archive ref `refs/archive/rejected-rag/20260723/monitoring-subject-gate` | 2026-07-23 |
| #001 | P2 | task | Semantic reranking still gated off | `RAG_SEMANTIC_RERANK_ENABLED=false` from PR #901. Do not enable until the provider-backed 36/36 retrieval-quality gate **and** an ambiguity-focused canary are explicitly approved and recorded. | `docs/process-hardening.md` (Semantic reranking rollout debt); PR #901 | 2026-07-21 |
Expand Down
4 changes: 2 additions & 2 deletions src/components/formulation/formulation-builder-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ export function FormulationBuilderPage({
type="button"
onClick={() => move(-1)}
disabled={activeIndex === 0}
className="inline-flex min-h-tap items-center gap-2 rounded-lg border border-[color:var(--border-strong)] bg-[color:var(--surface)] px-4 text-sm font-bold text-[color:var(--text-muted)] disabled:opacity-40"
className="inline-flex min-h-tap items-center gap-2 rounded-lg border border-[color:var(--border-strong)] bg-[color:var(--surface)] px-4 text-sm font-bold text-[color:var(--text-muted)] disabled:cursor-not-allowed disabled:border-[color:var(--border)] disabled:bg-[color:var(--surface-inset)]"
>
<ArrowLeft className="h-4 w-4" aria-hidden />
Previous
Expand All @@ -688,7 +688,7 @@ export function FormulationBuilderPage({
type="button"
onClick={() => move(1)}
disabled={activeStep === "select" && selectedMechanisms.length === 0}
className="inline-flex min-h-tap items-center gap-2 rounded-lg bg-[color:var(--command)] px-4 text-sm font-bold text-[color:var(--command-contrast)] shadow-[var(--shadow-tight)] disabled:cursor-not-allowed disabled:opacity-45"
className="inline-flex min-h-tap items-center gap-2 rounded-lg border border-transparent bg-[color:var(--command)] px-4 text-sm font-bold text-[color:var(--command-contrast)] shadow-[var(--shadow-tight)] disabled:cursor-not-allowed disabled:border-[color:var(--border)] disabled:bg-[color:var(--surface-inset)] disabled:text-[color:var(--text-muted)] disabled:shadow-none"
>
{activeStep === "select"
? "Continue to framework"
Expand Down
40 changes: 33 additions & 7 deletions tests/ui-formulation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,42 @@ test("keeps long mobile formulation pages inside the app scrollport", async ({ p
expect(geometry.mainScrollHeight).toBeGreaterThan(geometry.mainClientHeight + 40);
});

test("keeps unavailable builder navigation natively disabled without fading its text", async ({ page }, testInfo) => {
await gotoApp(page, "/formulation/builder?template=5Ps");

const previousStep = page.getByRole("button", { name: "Previous", exact: true });
const continueStep = page.getByRole("button", { name: "Continue to framework", exact: true });

await expect(previousStep).toBeDisabled();
await expect(continueStep).toBeDisabled();
await expect(previousStep).toHaveCSS("opacity", "1");
await expect(continueStep).toHaveCSS("opacity", "1");

const disabledControlsAcceptedFocus = await page.evaluate(() => {
const controls = Array.from(document.querySelectorAll<HTMLButtonElement>("button:disabled")).filter((button) =>
["Previous", "Continue to framework"].includes(button.textContent?.trim() ?? ""),
);
return controls.map((control) => {
control.focus();
return document.activeElement === control;
});
});
expect(disabledControlsAcceptedFocus).toEqual([false, false]);
await expectNoBlockingAxeViolations(page, testInfo);
});

test("moves a selected mechanism through framework, quality review, and an editable draft", async ({
page,
}, testInfo) => {
await gotoApp(page, "/formulation/builder?mechanism=rumination&template=5Ps");

await expect(page.getByRole("checkbox", { name: /Rumination/ })).toBeChecked();
await page.getByRole("button", { name: /Continue to framework/ }).click();
const previousStep = page.getByRole("button", { name: "Previous", exact: true });
const continueStep = page.getByRole("button", { name: "Continue to framework", exact: true });
await expect(previousStep).toBeDisabled();
await expect(previousStep).toHaveCSS("opacity", "1");
await expect(continueStep).toBeEnabled();
await continueStep.click();
await expect(page.getByTestId("formulation-builder-structure")).toBeVisible();
const cbtCycle = page.getByRole("radio", { name: /CBT cycle/ });
await page.getByText("CBT cycle", { exact: true }).click();
Expand Down Expand Up @@ -173,12 +202,9 @@ test("moves a selected mechanism through framework, quality review, and an edita
await expect(draft).not.toHaveValue("Stale edited draft");
await expect(draft).toHaveValue(/Select mechanisms and add case evidence/);
await expectNoHorizontalOverflow(page);
// WebKit can serve a stale :disabled computed style (opacity .4) for the step-nav
// Previous button after the select->draft transition re-enables it; axe folds that
// opacity into the glyph color and reports a phantom color-contrast violation for a
// state WCAG 1.4.3 exempts anyway (matrix run 4012). Poll the computed style so axe
// measures the live enabled state; a failure HERE is direct proof of the stale style.
const previousStep = page.getByRole("button", { name: "Previous", exact: true });
// Regression guard for matrix run 4012: the old disabled opacity utility could
// remain stale in WebKit after this button re-enabled, which made axe measure a
// phantom low-contrast enabled state. Keep the live state explicit before scanning.
await expect(previousStep).toBeEnabled();
await expect(previousStep).toHaveCSS("opacity", "1");
await expectNoBlockingAxeViolations(page, testInfo);
Expand Down