From f57ef2ebac8f47ab2e2a5303c2d54c28cd1e81e8 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 05:41:39 +0000 Subject: [PATCH] issues: reconcile the nine queued ledger requests Applies the pending inbox queue to the canonical ledger, the deliberately serialized step the write-discipline gate requires. Nine immutable requests, all created today through the CLI: five verified closures (#0HYHTH, #PWZYY8, #XHADPV, #5YKAQ5, #DVRZA4) and four updates (#EG4Q7W, #59JT7W, #AGRAKQ, #YTR84P). Their audit records move to docs/outstanding-issues-inbox/applied/ unchanged. The #YTR84P update is this session's own, and is the reason the reconcile was run now: the row claimed the WardFlowProvider pinned-clock fix existed only on an unpushed branch and had to be carried across. It is on main, by two independent routes. The row also said 40 call sites all passing NOW_ANCHOR (it is 51, four of them deliberately not), that the Phase 6 morning page still carried the defect (it does not, and is not on main at all), and that the remaining work was a D5 test. That last one cannot be written as specified: WB-DB-11 removed the fixed/live split, so the component holding the sentence such a test would assert is rendered by nothing. The row stays open, now describing a decision rather than a task. Regenerated data/outstanding-issues-snapshot.json with the repo tooling rather than by hand, per #Y090R5. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NiskRgeXeNRiU3npGP4M9f --- data/outstanding-issues-snapshot.json | 69 ++++--------------- .../372fe535-7009-498c-bc6c-059d9ad66ce1.json | 0 .../59f035ef-381b-4877-b27a-fa26f1048a32.json | 0 .../74ec59ed-9eb1-411b-ab10-bcc9d5a67b84.json | 0 .../78886201-3f6e-4c1f-88df-cd2a97bf7bd1.json | 0 .../8ba1e31f-b2f4-4a95-a9f4-69ab4340b619.json | 0 .../8da94bed-bb86-433e-9ad3-c4ac92a7331b.json | 0 .../c49cd04d-6485-430a-9b70-3c10c7cb3e03.json | 0 .../da8e92bb-f6ac-40a5-a6d8-b2927997730f.json | 0 .../f20b4b9f-00bb-4f82-8958-ef926eedb5bd.json | 0 docs/outstanding-issues.md | 18 ++--- 11 files changed, 21 insertions(+), 66 deletions(-) rename docs/outstanding-issues-inbox/{ => applied}/372fe535-7009-498c-bc6c-059d9ad66ce1.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/59f035ef-381b-4877-b27a-fa26f1048a32.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/74ec59ed-9eb1-411b-ab10-bcc9d5a67b84.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/78886201-3f6e-4c1f-88df-cd2a97bf7bd1.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/8ba1e31f-b2f4-4a95-a9f4-69ab4340b619.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/8da94bed-bb86-433e-9ad3-c4ac92a7331b.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/c49cd04d-6485-430a-9b70-3c10c7cb3e03.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/da8e92bb-f6ac-40a5-a6d8-b2927997730f.json (100%) rename docs/outstanding-issues-inbox/{ => applied}/f20b4b9f-00bb-4f82-8958-ef926eedb5bd.json (100%) diff --git a/data/outstanding-issues-snapshot.json b/data/outstanding-issues-snapshot.json index 17be81b4ea..4e6582aa51 100644 --- a/data/outstanding-issues-snapshot.json +++ b/data/outstanding-issues-snapshot.json @@ -1,17 +1,17 @@ { "version": "outstanding-issues-snapshot-v1", "ledger_revision": { - "sha": "362cc5dd50698e44481cfff76f578e075fa6a2bf", - "committed_at": "2026-08-27T20:30:32+08:00" + "sha": "d247db72afce3d28a45d496334927a6c5cb5762e", + "committed_at": "2026-09-02T03:17:21+00:00" }, "counts": { - "open": 75, + "open": 70, "p1": 2, - "p2": 51, - "p3": 22, + "p2": 49, + "p3": 19, "queued": 7, "pending": 0, - "resolved": 454 + "resolved": 459 }, "queue": [ { @@ -309,33 +309,6 @@ "source": "repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f", "added": "2026-08-23" }, - { - "id": "#0HYHTH", - "priority": "P2", - "type": "issue", - "summary": "Caring Contacts: nothing checks screen wording against the prohibited-vocabulary list", - "detail": "The prohibited-language check runs against outgoing messages and the 24 frozen overlay rows only. No gate scans interface strings elsewhere in the workspace, so the ban on words such as high risk, safe, engagement score and risk score is currently policy held by people rather than by software. Copy review Part 5 item 2 records the same gap.", - "source": "docs/caring-contacts/copy-review.md Part 5; Phase 2A deferred list", - "added": "2026-08-24" - }, - { - "id": "#PWZYY8", - "priority": "P2", - "type": "issue", - "summary": "Icons passed as icon props render with no aria-hidden, and the lint rule is structurally blind to it", - "detail": "eslint-rules/require-lucide-icon-aria.mjs only inspects JSX whose tag name is a direct lucide-react import; its own doc comment scopes out icons passed as icon: LucideIcon values. At least nine production components destructure icon: Icon and render it with no aria-hidden, aria-label, role or title and no aria-hidden ancestor, so a bare svg reaches the accessibility tree. DocumentBadge, DocumentActionLink, DocumentActionAnchor and DocumentActionButton alone are used 62 times across 9 production files. evidence-panels.tsx:1185 omits it in the same ternary as a correctly marked Loader2 sibling, showing the omission is inconsistent rather than deliberate. FIX: add aria-hidden at the sites, or extend the rule to flag a tag name resolved from a destructured LucideIcon prop.", - "source": "repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f", - "added": "2026-08-23" - }, - { - "id": "#5YKAQ5", - "priority": "P3", - "type": "issue", - "summary": "sentry.server.config falls back to the service-role key when the publishable key is absent", - "detail": "src/sentry.server.config.ts:54 reads NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY or else SUPABASE_SERVICE_ROLE_KEY. Server-only file and sendOperationData is false, so no key or PHI reaches Sentry and the risk is contained - but the fallback broadens where the secret is instantiated for no benefit. If the publishable key is missing, tracing should be disabled rather than escalated. FIX: drop the fallback.", - "source": "repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f", - "added": "2026-08-23" - }, { "id": "#ZK460W", "priority": "P2", @@ -426,15 +399,6 @@ "source": "Caring Contacts doc repair 2026-08-19", "added": "2026-08-18" }, - { - "id": "#DVRZA4", - "priority": "P3", - "type": "rec", - "summary": "prettierignore lists one generated data snapshot but not its sibling", - "detail": "data/outstanding-issues-snapshot.json is in .prettierignore; data/repo-awareness-snapshot.json, added by Phase 2, is not. The repo-awareness file passes prettier --check today, so nothing is broken and this is latent rather than live. The asymmetry makes the one listed entry read as arbitrary. Resolve it in whichever direction is right - the answer may well be removing the older entry rather than adding the newer one.", - "source": "Phase 2 whole-branch review, PR #2359", - "added": "2026-08-25" - }, { "id": "#CJCH2E", "priority": "P3", @@ -449,7 +413,7 @@ "priority": "P3", "type": "rec", "summary": "Caring Contacts: postgres-repository.ts is ~2,080 lines and holds five self-contained clusters", - "detail": "src/lib/caring-contacts/db/postgres-repository.ts would split naturally into sibling modules under db/. Pure structure, no behaviour change; the file is the code half of row-level security so any split must keep every method's set_config/set local role preamble intact and be proved by the existing shared contract suite.", + "detail": "STILL OPEN AND LARGER THAN RECORDED; re-measured against origin/main d1bb2c197 on 2026-09-02. A done request for this row (97bbfd51) claimed the file had been modularized into five cohesive domain modules (core, plans, contacts, referrals-pathways, assignments). That claim is false against current main and the request was cancelled, so nothing was lost -- but the reason recorded on the cancellation ('target issue is no longer in Open items on current main') was also wrong, since the row was and remains open. Recording the measurement so a third attempt does not start from either error. MEASURED: src/lib/caring-contacts/db/postgres-repository.ts is a single file of 2,500 lines. It has not been split, and it is now about 420 lines LARGER than the ~2,080 this row recorded when it was filed. There are no sibling domain modules beside it under db/. The original recommendation is unchanged and still applies: the split is pure structure with no behaviour change, every method's set_config / set local role preamble must survive it intact because this file is the code half of row-level security, and the existing shared contract suite is the proof that it did.", "source": "docs/caring-contacts/phase-2a-build-record.md deferred list item 2", "added": "2026-08-24" }, @@ -458,7 +422,7 @@ "priority": "P2", "type": "issue", "summary": "Caring Contacts: the closing-message refusal is a guard that cannot fire", - "detail": "resolveClosingContactMessageBody in src/lib/caring-contacts/message-policy.ts was added 2026-08-24 for approved copy item A4 ('refuse loudly rather than send nothing when a plan reaches its end with no closing message'). It is a standalone function and nothing obliges any caller to use it, so unlike the A1 fictional-contact check -- which rides validateGovernedMessage, a chokepoint every future sender must pass -- it can be bypassed simply by resolving a closing body some other way. PlannedContact (schedule.ts) carries messageType and no body, so no seam exists to wire it to yet, and inventing one would require deciding where an authored closing body comes from. DO NOT record A4 as closed: the owner's approved outcome is not achieved today. When the message-body seam is built, this function must become the only path to a closing body.", + "detail": "RE-VERIFIED STILL OPEN against origin/main d1bb2c197 on 2026-09-02. A done request (c862828e) claimed this was resolved because resolveClosingContactMessageBody and tests/caring-contacts-message-policy.test.ts exist. It was cancelled, and cancelling it was correct -- but for a different reason than the one recorded, so pinning the measurement here to stop a third attempt closing the row on the same mistake. The function existing was never what this row asked for. MEASURED: grep for resolveClosingContactMessageBody across src/**/*.ts and src/**/*.tsx returns exactly two hits -- its own definition at src/lib/caring-contacts/message-policy.ts:208, and a prose mention in a doc comment at src/lib/caring-contacts/message-copy.ts:121. There is still no production caller. The tests at tests/caring-contacts-message-policy.test.ts:477 exercise the function directly, which proves the refusal is correct but not that anything is obliged to ask for it, so the guard remains one a future sender can bypass by resolving a closing body some other way. The row's own stop rule is unchanged and still binding: do not record approved copy item A4 as closed. The blocker is also unchanged -- PlannedContact in schedule.ts carries messageType and no body, so there is still no seam to wire this to, and inventing one means deciding where an authored closing body comes from.", "source": "Task C review finding, 2026-08-24; confirmed by controller and reviewer independently", "added": "2026-08-24" }, @@ -489,15 +453,6 @@ "source": "docs/caring-contacts/phase-2a-build-record.md (Rulings 25/27); Phase 2A deferred list", "added": "2026-08-24" }, - { - "id": "#XHADPV", - "priority": "P3", - "type": "issue", - "summary": "repo-awareness snapshot emits two per-section documentation counts that nothing reads", - "detail": "documentation.sections[] carries documents and uncatalogued for all 15 sections; grep shows only .name is consumed, by documentsBySection for ordering. The documentation page shows section.documents.length computed at render instead. Rendering the live list is arguably SAFER than trusting a stored number, but it means the plan's 'counts are computed once by the generator so a count and its own list cannot disagree' rule has an undocumented exception, and 30 dead numbers ride in the committed snapshot. Pick one deliberately: render the stored counts, or stop emitting them. Deferred from the Phase 2 fix wave because it changes either the data contract or a page, which is a design decision rather than a cleanup.", - "source": "Phase 2 whole-branch review, PR #2359", - "added": "2026-08-25" - }, { "id": "#1S81R8", "priority": "P1", @@ -548,7 +503,7 @@ "priority": "P3", "type": "issue", "summary": "Caring Contacts: the interface prohibited-language helper has the same 'lead' job-title collision B2 just fixed for messages", - "detail": "tests/helpers/caring-contacts-prohibited-language.ts matches /\\bleads?\\b/, so it flags the ordinary English job titles 'the incident lead' and 'the clinical programme lead' exactly as the message validator used to before item B2 narrowed it on 2026-08-24. Out of B2's scope, which was explicitly limited to the message rules. Apply the same commercial-sense narrowing, or record why interface copy should be stricter than message copy.", + "detail": "NARROWED TO PLURALS; re-measured against origin/main d1bb2c197 on 2026-09-02 by executing the pattern, not by reading it. A done request (461a406d) claimed the plural case was fixed and was cancelled; the cancellation's stated reason was wrong (the row is open), but the outcome is right, because the plural case is NOT fixed. HALF DONE: tests/helpers/caring-contacts-prohibited-language.ts now carries the same negative lookbehind the message rules use -- (? | P2 | task | Retrieval RPC still treats a null document owner as public, so the owner_id republication hole is only half closed | RE-VERIFIED against current main c944fcdad78e106a7fb7b9c99e5e3a829e022fc4 on 2026-09-01. CLOSED: the owner-deletion path is no longer outstanding. public.documents, document_labels, document_summaries and document_table_facts now use ON DELETE RESTRICT, with the live migration and a schema proof that pins the four visibility tables and their exact restrict action. CODE DEFECT (unresolved): public.retrieval_owner_matches treats the public sentinel as row_owner_id IS NULL, and retrieval_owner_matches_v2 treats include_public as row_owner_id IS NULL; neither predicate requires metadata.public_corpus = true. An ownerless row without the publication marker could therefore still enter a public retrieval result. EXPOSURE (measured 2026-09-01, owner-approved read-only GET against ref sjrfecxgysukkwxsowpy): total documents 2851; owner_id NOT NULL = 0; owner_id IS NULL = 2851; of those, metadata.public_corpus = true = 2851. EXPOSED COUNT = 0. The production corpus is deliberately published and has no privately owned or unmarked ownerless document. PRIORITY: P2, not P1. NEXT: change the retrieval contract so its public branch requires both owner_id IS NULL and metadata.public_corpus = true, preserving owned-document retrieval; this is a protected RAG behaviour change and requires the RAG flag, an offline regression proof, and a live eval-canary pair. | repo-wide audit at 3ed1932 (six domain reviewers); re-verified against current main c944fcdad78e106a7fb7b9c99e5e3a829e022fc4 and an owner-approved read-only production count, database coordination chat 2026-09-01 | 2026-08-23 | | #A3AHCX | P2 | issue | Caring Contacts: the governed-message validator has zero production callers | validateGovernedMessage in src/lib/caring-contacts/message-policy.ts enforces the segment limit, the prohibited vocabulary, the required support fragments and (from 2026-08-24) the fictional-contact check, but grep across src/ and worker/ finds NO production caller -- it is exercised only by tests. That is consistent with there being no send path in this prototype, and the checks are the mechanism a future sender must use. Recorded so nobody mistakes 'the validator refuses this' for 'the system refuses this'. When a dispatch or preview path is built, wiring it to this validator is the first requirement. | Task C implementer concern 1, independently confirmed by grep 2026-08-24 | 2026-08-24 | | #5PQRSW | P2 | issue | Patient-profile alerts surface unassessed gates only for contraindication rows, so 266 caution, dose-adjust and monitor gates silently no-op | medication-patient-alerts.ts:344-358 collects missingGates for every gated row but the consumer discards them unless patient.action is contraindication. Counted from data/medications-snapshot.json: 256 caution, 145 dose-adjust and 62 monitor rows, 266 of them gated on a numeric or categorical input (eGFR, CrCl, QTc, age, hepatic). A clinician entering a partial profile without eGFR gets no considerations from a renal DOSE REDUCTION row and an empty panel indistinguishable from an all-clear; the UI sentence that would warn is scoped to contraindications only. FIX: include dose-adjust at minimum in the unassessed set and widen the UI copy. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | -| #0HYHTH | P2 | issue | Caring Contacts: nothing checks screen wording against the prohibited-vocabulary list | The prohibited-language check runs against outgoing messages and the 24 frozen overlay rows only. No gate scans interface strings elsewhere in the workspace, so the ban on words such as high risk, safe, engagement score and risk score is currently policy held by people rather than by software. Copy review Part 5 item 2 records the same gap. | docs/caring-contacts/copy-review.md Part 5; Phase 2A deferred list | 2026-08-24 | -| #PWZYY8 | P2 | issue | Icons passed as icon props render with no aria-hidden, and the lint rule is structurally blind to it | eslint-rules/require-lucide-icon-aria.mjs only inspects JSX whose tag name is a direct lucide-react import; its own doc comment scopes out icons passed as icon: LucideIcon values. At least nine production components destructure icon: Icon and render it with no aria-hidden, aria-label, role or title and no aria-hidden ancestor, so a bare svg reaches the accessibility tree. DocumentBadge, DocumentActionLink, DocumentActionAnchor and DocumentActionButton alone are used 62 times across 9 production files. evidence-panels.tsx:1185 omits it in the same ternary as a correctly marked Loader2 sibling, showing the omission is inconsistent rather than deliberate. FIX: add aria-hidden at the sites, or extend the rule to flag a tag name resolved from a destructured LucideIcon prop. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | -| #5YKAQ5 | P3 | issue | sentry.server.config falls back to the service-role key when the publishable key is absent | src/sentry.server.config.ts:54 reads NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY or else SUPABASE_SERVICE_ROLE_KEY. Server-only file and sendOperationData is false, so no key or PHI reaches Sentry and the risk is contained - but the fallback broadens where the secret is instantiated for no benefit. If the publishable key is missing, tracing should be disabled rather than escalated. FIX: drop the fallback. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | | #ZK460W | P2 | issue | The extractive review fallback flips grounded false to true, which is the shared root cause behind the three tracked adversarial divergences | rag.ts:3107-3122 enters the branch BECAUSE the answer failed its quality gate (!finalizedAnswer.grounded) and then sets grounded: true with confidence re-derived from retrieval similarity alone. Claim support force-classifies every claim on this route as routine, so the authority gate passes vacuously and trust resolves to high, unlocking quote cards and suppressing the source-gap warning. The prose restates the clinician's own query as though the corpus confirmed it. This is the mechanism behind #NTAV3D, #C2D9JF and #VXB8XA; none of those rows names it. FIX: keep grounded false and confidence unsupported, retaining citations as review-only provenance (that reason string already exists in answer-render-policy). Protected RAG surface: own PR, RAG impact line, canary pair; flips the three divergence pins together. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | | #JFRCZ4 | P3 | issue | check:repo-awareness-snapshot needs git, and nobody has established what it does without it | The gate shells out to git ls-files and git log through the generator, and it now sits in verify:cheap, verify:pr-local and CI. What it does in a git-less checkout - a git archive export, which is the very technique the Phase 2 spec section 8.2 names as a required proof - has never been tested. The generator itself is proven to fail loudly there, which is correct under ruling R5 because it only ever runs from docs:update. The gate is different: it runs in three gate chains, so a git-less environment turns a safety check into an unexplained failure. Establish the behaviour and document it, or make the gate skip explicitly with a stated reason rather than erroring. | Phase 2 whole-branch review, PR #2359 | 2026-08-25 | | #9P4XAE | P2 | issue | Form 12A displays a false Password protected warning although its committed PDF is readable | Measured on 2026-08-24: public/forms-pdf/form-12a.pdf has no /Encrypt marker, while data/forms-pdf-manifest.json records passwordProtected true and the form page renders a warning from that flag. Before changing any data manifest, identify its canonical source and supported generation path; if none exists, introduce a measured manifest-generation or validation workflow that derives the flag from committed PDF bytes, then regenerate the manifest and make the form test assert the generated contract. This changes a clinician-facing statement and requires Clinical Governance Preflight. | Direct inspection of public/forms-pdf/form-12a.pdf; data/forms-pdf-manifest.json; src/lib/form-catalog.ts; src/components/forms/form-detail-page.tsx; tests/forms.test.ts | 2026-08-24 | @@ -117,20 +114,18 @@ removed after current-main verification; it is not missing recommended work. | #W98GR7 | P2 | issue | Enrichment artifact families can be permanently lost, the designed repair function is called by nothing, and no monitoring exists | supabase/functions/indexing-v3-agent deletes an artifact family (document_memory_cards, document_index_units, document_embedding_fields) BEFORE calling OpenAI and re-inserting, one family at a time, never staged-then-swapped. A provider outage spanning the retry and deferral budget leaves the family permanently empty, and both terminal states (failed, needs_enrichment_artifacts) are excluded from claim eligibility forever. repair_strict_enrichment_gate_batch (migration 20260625033425) is invoked by NOTHING in the codebase, and no script or query references needs_enrichment_artifacts for monitoring, so a stuck document reports as indexed with an empty artifact family. Silent corruption, not a crash. FIX: stage-then-swap per family, plus a re-queue path or wire the existing repair function into an ops script. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | | #DW3XK8 | P2 | issue | Four no-DDL migrations sit outside every history guard, and the drift allowlist was never reconciled against a live read | Sixteen migration files contain no executable DDL. tests/migration-history-placeholders.test.ts tracks only six of the ten select-1 files; untracked are 20260629100000, 20260702170000, 20260708160000 and 20260709150000. 20260702170000 is the sharpest: supabase/drift-allowlist.json allowlists every neighbour in its window (100000 through 180000) but not it, and every allowlisted neighbour carries real DDL while 20260702170000_fix_match_chunks_text_n1.sql is bare select 1. The allowlist header states it was seeded by name from the repo, NOT by a live read. NEXT (operator, read-only): select (public.schema_drift_snapshot() -> 'migration_history'); and check statement counts for those versions. Any zero-statement row without an allowlist entry needs a 20260804110240-pattern validation guard. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | | #ZM8902 | P3 | issue | docs:check-links passes over a broken relative markdown link in a spec, so a binding document went missing unnoticed | scripts/check-docs-links.mjs reported 1905 references resolving while docs/superpowers/specs/2026-08-15-caring-contact-coordination-design.md line 180 linked to ../../caring-contacts/design-handoff.md, a file that has never existed, and line 131 described it as binding. Found by hand on 2026-08-19 and repointed to interaction-matrix.md, which actually holds the 24-row modality matrix. The gate is a check that cannot fail for this class of link: verify whether relative markdown links outside a recognised prefix are skipped, and close the gap. Same family as the mutation-testing concerns already recorded for stderr hooks and tail-masked exit codes. | Caring Contacts doc repair 2026-08-19 | 2026-08-18 | -| #DVRZA4 | P3 | rec | prettierignore lists one generated data snapshot but not its sibling | data/outstanding-issues-snapshot.json is in .prettierignore; data/repo-awareness-snapshot.json, added by Phase 2, is not. The repo-awareness file passes prettier --check today, so nothing is broken and this is latent rather than live. The asymmetry makes the one listed entry read as arbitrary. Resolve it in whichever direction is right - the answer may well be removing the older entry rather than adding the newer one. | Phase 2 whole-branch review, PR #2359 | 2026-08-25 | | #CJCH2E | P3 | issue | Ingestion panel reports 'could not reach' when it reached the endpoint but could not parse the body | A malformed JSON body on a 200 response falls into the generic catch in IngestionPanel and produces 'The panel could not reach the ingestion jobs endpoint.' It did reach it. The adjacent parseReadyPayload path gets this right and says 'returned an unexpected shape'. Small, but the panel's whole purpose is telling a reader precisely what is and is not known, so a message that misattributes the failure is off-key. Found during the ingestion panel review. | Ingestion panel review, 2026-08-25 | 2026-08-25 | -| #EG4Q7W | P3 | rec | Caring Contacts: postgres-repository.ts is ~2,080 lines and holds five self-contained clusters | src/lib/caring-contacts/db/postgres-repository.ts would split naturally into sibling modules under db/. Pure structure, no behaviour change; the file is the code half of row-level security so any split must keep every method's set_config/set local role preamble intact and be proved by the existing shared contract suite. | docs/caring-contacts/phase-2a-build-record.md deferred list item 2 | 2026-08-24 | -| #59JT7W | P2 | issue | Caring Contacts: the closing-message refusal is a guard that cannot fire | resolveClosingContactMessageBody in src/lib/caring-contacts/message-policy.ts was added 2026-08-24 for approved copy item A4 ('refuse loudly rather than send nothing when a plan reaches its end with no closing message'). It is a standalone function and nothing obliges any caller to use it, so unlike the A1 fictional-contact check -- which rides validateGovernedMessage, a chokepoint every future sender must pass -- it can be bypassed simply by resolving a closing body some other way. PlannedContact (schedule.ts) carries messageType and no body, so no seam exists to wire it to yet, and inventing one would require deciding where an authored closing body comes from. DO NOT record A4 as closed: the owner's approved outcome is not achieved today. When the message-body seam is built, this function must become the only path to a closing body. | Task C review finding, 2026-08-24; confirmed by controller and reviewer independently | 2026-08-24 | +| #EG4Q7W | P3 | rec | Caring Contacts: postgres-repository.ts is ~2,080 lines and holds five self-contained clusters | STILL OPEN AND LARGER THAN RECORDED; re-measured against origin/main d1bb2c197 on 2026-09-02. A done request for this row (97bbfd51) claimed the file had been modularized into five cohesive domain modules (core, plans, contacts, referrals-pathways, assignments). That claim is false against current main and the request was cancelled, so nothing was lost -- but the reason recorded on the cancellation ('target issue is no longer in Open items on current main') was also wrong, since the row was and remains open. Recording the measurement so a third attempt does not start from either error. MEASURED: src/lib/caring-contacts/db/postgres-repository.ts is a single file of 2,500 lines. It has not been split, and it is now about 420 lines LARGER than the ~2,080 this row recorded when it was filed. There are no sibling domain modules beside it under db/. The original recommendation is unchanged and still applies: the split is pure structure with no behaviour change, every method's set_config / set local role preamble must survive it intact because this file is the code half of row-level security, and the existing shared contract suite is the proof that it did. | docs/caring-contacts/phase-2a-build-record.md deferred list item 2 | 2026-08-24 | +| #59JT7W | P2 | issue | Caring Contacts: the closing-message refusal is a guard that cannot fire | RE-VERIFIED STILL OPEN against origin/main d1bb2c197 on 2026-09-02. A done request (c862828e) claimed this was resolved because resolveClosingContactMessageBody and tests/caring-contacts-message-policy.test.ts exist. It was cancelled, and cancelling it was correct -- but for a different reason than the one recorded, so pinning the measurement here to stop a third attempt closing the row on the same mistake. The function existing was never what this row asked for. MEASURED: grep for resolveClosingContactMessageBody across src/**/*.ts and src/**/*.tsx returns exactly two hits -- its own definition at src/lib/caring-contacts/message-policy.ts:208, and a prose mention in a doc comment at src/lib/caring-contacts/message-copy.ts:121. There is still no production caller. The tests at tests/caring-contacts-message-policy.test.ts:477 exercise the function directly, which proves the refusal is correct but not that anything is obliged to ask for it, so the guard remains one a future sender can bypass by resolving a closing body some other way. The row's own stop rule is unchanged and still binding: do not record approved copy item A4 as closed. The blocker is also unchanged -- PlannedContact in schedule.ts carries messageType and no body, so there is still no seam to wire this to, and inventing one means deciding where an authored closing body comes from. | Task C review finding, 2026-08-24; confirmed by controller and reviewer independently | 2026-08-24 | | #KMM6R6 | P2 | task | The Caring Contacts service safety stop halts sending across every patient and team, but the rule that it is stored as ONE record rather than one row per team is currently carried only by a field name (reportedByTeamId) and a doc comment in src/lib/caring-contacts/service-state.ts. Migration 0003 (Phase 2A Task 11) must enforce it in the schema with a fixed-key singleton row plus a test, and every dispatch path must read that one record regardless of the dispatching team. Without it, a stop raised by one team would leave every other team still sending during an incident. | | session 2026-08-19 | 2026-08-19 | | #EWWJVX | P2 | task | Caring Contacts Phase 2B — the screens | Phase 2A closed at Task 19 with one production screen built (/caring-contacts, Today) plus the frozen 24-overlay renderer. Plan 2B builds the remaining screens: patients, patient overview, patient and agreement, pathway selection, personalisation, review and activation, plan detail, schedule, contact and delivery exception, governed templates, team, guidance, reports — plus the Today dashboard body itself (referral queue, needs-action list, sending windows, recent activity, summary counts). Their rules and data already exist from Phase 1 and Tasks 3-11; only the surfaces are missing. The visual specification for each is the committed mockup atlas: 26 of its 44 images have no production counterpart today, listed in docs/caring-contacts/phase-2a-visual-differences.md. Building a screen also means giving its rail/dock destination an href in shell.tsx (Ruling 52) and raising its overlays through openWorkspaceOverlay, which nothing yet does. | docs/caring-contacts/phase-2a-visual-differences.md; docs/caring-contacts/phase-2a-sdd-archive/task-19-report.md | 2026-08-22 | | #4VKAA1 | P2 | issue | Caring Contacts: four bare foreign keys onto plans/contacts predate the composite same-team rule | caring-contacts/supabase/migrations/0001_caring_contacts_foundation.sql lines 146, 172, 218 and 227 declare plan_id/contact_id references without the team_id composite that Rulings 25 and 27 later made mandatory, so a row written by one team can point at another team's plan or contact. Verified in the migration text 2026-08-24. Decide whether to add the composite keys by migration or record the exception; a bare key already caught one real cross-team defect. | docs/caring-contacts/phase-2a-build-record.md (Rulings 25/27); Phase 2A deferred list | 2026-08-24 | -| #XHADPV | P3 | issue | repo-awareness snapshot emits two per-section documentation counts that nothing reads | documentation.sections[] carries documents and uncatalogued for all 15 sections; grep shows only .name is consumed, by documentsBySection for ordering. The documentation page shows section.documents.length computed at render instead. Rendering the live list is arguably SAFER than trusting a stored number, but it means the plan's 'counts are computed once by the generator so a count and its own list cannot disagree' rule has an undocumented exception, and 30 dead numbers ride in the committed snapshot. Pick one deliberately: render the stored counts, or stop emitting them. Deferred from the Phase 2 fix wave because it changes either the data contract or a page, which is a design decision rather than a cleanup. | Phase 2 whole-branch review, PR #2359 | 2026-08-25 | | #1S81R8 | P1 | task | Caring Contacts: three unmitigated hazards block any real-patient pilot (safety officer, lived-experience review, Aboriginal health review) | docs/caring-contacts/hazard-log.md records H-00 (no named clinical safety officer, so nobody owns clinical risk), H-04 (the message set has never been read by anyone with lived experience) and H-05 (no Aboriginal cultural safety review, in a WA suicide-aftercare service). All three are Open with no control. None blocks the synthetic build; every one blocks a pilot. H-04 is ready to run today: docs/caring-contacts/message-review-pack.md is a complete facilitation pack. Owner Josh for H-00 and H-04; Aboriginal health governance for H-05. | Caring Contacts design session 2026-08-19; hazard log H-00/H-04/H-05 | 2026-08-18 | | #Q4Y7TR | P2 | issue | No automated reaper for abandoned reindex generations, so crashed mid-builds leak rows and storage indefinitely | cleanup_abandoned_document_index_generations is called from exactly one place, scripts/cleanup-abandoned-reindex-generations.ts via npm run reindex:cleanup-staged. No cron.schedule, Railway cron, or CI job references it. Any crash mid-build, or the empty-generation guard at worker/main.ts throwing, leaves staged rows under a never-committed index_generation_id and storage objects under {owner}/images/{doc}/{generation}/ that nothing revisits. Unbounded Postgres row and Storage growth, dependent on an operator remembering to run a janitor. FIX: write storage_cleanup_jobs rows for superseded generations at commit time, where the commit RPC already knows both generation ids. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | | #N8B176 | P3 | issue | isPublicDocument ranks caller-adjacent metadata above owner_id | src/lib/documents/is-public-document.ts:49-61 returns true for metadata.public_corpus, is_public, public, visibility === 'public' or source_kind === 'registry_record' BEFORE consulting owner_id at :64 - a second, weaker definition of public than the one the DB and publish_approved_documents use. Not exploitable today: upload metadata is server-constructed and bulk edit accepts only a fixed key schema. But the function is exported as a standardized predicate and documented for reuse, so the next access decision that adopts it inherits a metadata-driven publication bypass. FIX: make owner_id === null the first and authoritative check, or scope the helper to rendering only. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | | #J43Z6B | P2 | rec | The tenancy boundary is app-code only, so a single missing owner predicate has nothing behind it; make the property mechanical | 20260719070000_align_existing_acls revokes ALL on every public base table from public, anon and authenticated and re-grants only service_role, and roles.sql makes that the default for future objects. The roughly 30 policies written TO authenticated therefore can never be evaluated, and every read path uses the RLS-bypassing admin client across 37 API route files. This is deliberate and pinned by tests, not a bug - but it means RLS is a dead backstop and app code is the only tenancy boundary. The audit scanned all owner-bearing .from() calls under src/app/api and found no unscoped read, so the property holds today by review rather than by enforcement. FIX: a lint or contract test asserting that any admin-client query against an owner-bearing table in src/app/api is lexically accompanied by an owner predicate. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | | #QCNE6N | P2 | issue | Schema drift is gated against schema.sql only; the migration chain's semantics are never diffed against the mirror | generate-drift-manifest.ts replays supabase/schema.sql and embeds ITS sha256; CI's db-reset-verify runs supabase migration up --local, proving the chain APPLIES but never diffing the result against schema.sql. A migration whose function or policy body diverges from the mirror passes every gate. Object-NAME parity does hold today (all create targets across 211 migrations resolve into schema.sql) but it is enforced by hand-written per-object tests, not systematically. FIX: after migration up --local, replay schema.sql into a second scratch database and diff schema_drift_snapshot() between the two. Fully offline and containerised, no provider access. DEMONSTRATED 2026-09-01, no longer theoretical: live-drift run 33484535655 (red on main at d3074946a) reported its sole unexpected finding as public.correct_clinical_query_terms(text,real) def_hash manifest e2356565 vs live 2ebaf978. Cause was exactly this gap - migration 20260831100000 (PR #2477) redefined that function with a duplicated 'and length(canonical) between 4 and 40' predicate and supabase/schema.sql was never updated to match, so the manifest disagreed with live while every pre-merge gate stayed green. The db-reset-verify assertion that did run is committed.schema_sha256 === generated.schema_sha256 (.github/workflows/ci.yml ~L1082-1095), which only catches an unrefreshed manifest, not a chain/mirror divergence. Behaviour impact of that instance was nil (the duplicate predicate is a boolean no-op) but it cost a red daily alarm and a remediation PR, and it is the second occurrence of this failure class after the #316 root cause (SET work_mem absent from schema.sql). Strengthens the case for scheduling the containerised two-database diff above. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f; demonstrated by live-drift run 33484535655, classified by the database coordination chat 2026-09-01 | 2026-08-23 | -| #AGRAKQ | P3 | issue | Caring Contacts: the interface prohibited-language helper has the same 'lead' job-title collision B2 just fixed for messages | tests/helpers/caring-contacts-prohibited-language.ts matches /\bleads?\b/, so it flags the ordinary English job titles 'the incident lead' and 'the clinical programme lead' exactly as the message validator used to before item B2 narrowed it on 2026-08-24. Out of B2's scope, which was explicitly limited to the message rules. Apply the same commercial-sense narrowing, or record why interface copy should be stricter than message copy. | Task C implementer concern 3, 2026-08-24 | 2026-08-24 | +| #AGRAKQ | P3 | issue | Caring Contacts: the interface prohibited-language helper has the same 'lead' job-title collision B2 just fixed for messages | NARROWED TO PLURALS; re-measured against origin/main d1bb2c197 on 2026-09-02 by executing the pattern, not by reading it. A done request (461a406d) claimed the plural case was fixed and was cancelled; the cancellation's stated reason was wrong (the row is open), but the outcome is right, because the plural case is NOT fixed. HALF DONE: tests/helpers/caring-contacts-prohibited-language.ts now carries the same negative lookbehind the message rules use -- (? | P3 | issue | run-playwright reported a production build failure with no compile error, once, unreproduced | Seen during Task 19 on 2026-08-22. node scripts/run-playwright.mjs tests/ui-caring-contacts-workspace.spec.ts --project=chromium printed 'Playwright production build failed (status 1)' with no TypeScript or bundler error anywhere in its output; the identical command run immediately afterwards, against an unchanged tree, built and ran cleanly. It did not recur across roughly a dozen further invocations that session, including six mutation runs. Recorded rather than explained. Why it matters: the wrapper's own contract is that a non-zero exit is either a genuine red or the distinguishable exit 75 admission-busy case, and a silent build failure is neither - a session that hit it once and stopped would report a red gate that does not exist, and a session that retried without noticing would never learn the run had been lost. The machine was under heavy concurrent load at the time (other agents held Vitest and lint leases in the same repository), so a resource or lock interaction during the Next build is the first place to look. Next step if it recurs: capture the full unfiltered output of the failing invocation before retrying, and check whether scripts/run-playwright.mjs is swallowing the child build's stderr rather than the build having produced none. | docs/caring-contacts/phase-2a-sdd-archive/task-19-report.md (Concerns 5) | 2026-08-22 | | #B0530F | P2 | task | Caring Contacts database and row-level-security suite runs in no automated gate | The caring-contacts-db Vitest project is conditional on CARING_CONTACTS_DATABASE_URL, and the two database files are excluded from the default project unconditionally, so roughly 1,330 lines of row-level-security and cross-team-isolation proof are collected by no automated gate. grep over .github/ returns no reference to the suite. NOT unexecuted: it was run repeatedly during Phase 2A, most recently at 174 passed, so the evidence exists for that branch - what is missing is automation, so nothing would catch a future regression. An absent Vitest project prints nothing at all, which is worse than a skip. The fix is a CI job with a postgres:17 service container running npm run caring-contacts:db:test, plus a static contract test asserting the script is reachable from a workflow. Deliberately not done inside the Phase 2A final fix wave: wiring it into an offline verify chain would break every offline run because the suite hard-fails without a container, and a dedicated alias would be a check that cannot fail. Needs a repository-wide CI change with its own review. | session 2026-08-22 | 2026-08-22 | | #CTA8CR | P2 | issue | A stale 'D4 is OFF' production-deploy claim survives uncorrected in two places in the live-drift forensics doc | docs/audit/live-drift-forensics-2026-08.md:1243-1244 and :1720 both assert flatly, with no forward pointer, that D4 is OFF and nothing reaches production on merge. A later section at :2056-2072 corrects this to 'treat D4 as UNRESOLVED and assume merging a migration PR deploys it to production'. A third instance at :2024 DOES carry a superseded pointer, so the doc's own convention exists and simply was not applied to the earlier two. A future session jumping to the Phase 4 or #Q5JHBJ sections would reasonably read the stale claim as settled fact about a live clinical database. FIX: add the same one-line superseded pointer at both sites. Also check docs/database-remediation-coordination.md:159, which references the same ambiguity. | repo-wide audit at 3ed1932 (six domain reviewers), re-verified against main 1bb362f | 2026-08-23 | @@ -147,7 +142,7 @@ removed after current-main verification; it is not missing recommended work. | #MPZTBR | P3 | issue | The scope-statement footer contradicts itself: an audit says mount it on every mode home, the code says it was deliberately removed from all of them | #PM9SP1's FIX text said to 'relabel to Clinical reference - not validated decision support and mount the footer on the other mode homes'. The relabel half landed (PRs #2497, #2499). The mount half conflicts with a recorded decision in the code: src/components/mode-home-template.tsx:216-219 states 'No mode home renders this any more: the line under the composer was removed from every home page. The sole remaining call site is the therapy-compass page footer, which sits at the bottom of the sub-routes and is explicitly not rendered on the therapy home (showFooter={!isHome} in workspace.tsx).' So one source says mount it everywhere and the other says it was deliberately taken off everywhere. NEXT ACTION: owner ruling on which is current, then make the other match. If the footer stays off mode homes, amend the #PM9SP1 fix text so a future session does not re-add it; if it should return, that is a deliberate reversal of the recorded decision and the comment at mode-home-template.tsx:216-219 must be updated in the same change. Not urgent: every surface that renders retrieved clinical content already carries its own scope line (verified by a repo-wide sweep 2026-09-01) - the open question is the shared mode-home composer footer only. | Design-system + app review session 2026-09-01; conflict found while fixing #PM9SP1, verified against mode-home-template.tsx and therapy-compass/workspace.tsx | 2026-09-01 | | #J7PZQP | P3 | task | Caring Contacts: carry the retention clearance instant on Episode so a screen stops inferring clearance from a blank patient name | Raised by the Task 6b implementer on 2026-08-25 and confirmed by review. The patient overview decides 'a retention clearance removed this reason' from `episode.patientName === ""`. That inference is sound TODAY but the guarantee behind it lives in a Zod schema at the API edge (`plans/route.ts`, `patientName: z.string().min(1)`), not as a domain invariant: the repository's own `createPlan` does not validate it and the Postgres column is `not null`, which permits the empty string. Task 6b made a SECOND statement depend on that inference, which is why it is worth closing rather than leaving. The failure mode is a wrong sentence on an already de-identified record, not a leak, so it is not urgent. The durable fix is cheap and already half-built: `caring_contacts.retention_state.cleared_at` exists in `0001_caring_contacts_foundation.sql` with `retention_state_cleared_after_terminal` pinning it, so the work is to surface it on `Episode` and have the screen read the instant instead of guessing from a blank field. Crosses both stores. | session 2026-08-25 | 2026-08-25 | | #A95DRY | P3 | task | mode-home-page-skeleton still subtracts a chrome estimate from 100dvh, the pattern invariant 24 retired everywhere else | PR #2419 replaced every 'calc(100dvh - )' page-fill floor with flex growth (shell #main-content, the composer reserve pad, the page shells, and the dashboard mode-home canvas) and recorded the rule as invariant 24 in docs/search-chrome-behaviour.md: 'A page fills the box it is in; it never subtracts a chrome estimate from 100dvh.' Two sites in src/components/mode-home-page-skeleton.tsx were deliberately left behind and are now the only page-fill estimates left in src/: line 32 'sm:min-h-[calc(100dvh-var(--shell-header-h))]' and line 60 'h-[calc(100dvh-var(--shell-header-h))]'. They were skipped because this is a route-loading placeholder rendered inside a Suspense boundary whose parent chain does not resolve a grown height, so swapping the class alone would collapse it - the parent chain has to be given a definite height first. --shell-header-h (4rem) is exactly the estimate invariant 24 exists to remove: it covers the header's inner bar plus its pb-2 but NOT the bar's own pt-[max(0.5rem,var(--safe-area-top))], which is why every floor built on it ran short. Impact is very low: the skeleton is visible for a fraction of a second while a route opens, and being a few px short there is invisible. Next: determine whether the skeleton's Suspense parent can carry a definite height; if yes, convert both sites to grow and delete the estimate; if no, record the exception explicitly under invariant 24 so the next sweep does not re-flag it as an oversight. Stop: do not simply swap the class without fixing the parent chain, and do not widen invariant 24 to permit estimates generally. | PR #2419; docs/search-chrome-behaviour.md invariant 24; src/components/mode-home-page-skeleton.tsx:32,60 | 2026-08-27 | -| #YTR84P | P2 | task | Ward Flow pinned-clock fix is committed only to an unpushed local branch, and the Phase 6 morning page still carries the defect, its workaround, and an untested D5 branch | WardFlowProvider's initialNow prop silently discarded its value: the render body forced elapsed=0 when pinned, so now was always NOW_ANCHOR (642, 10:42) whatever instant was passed. Latent, never active - all 40 initialNow call sites pass NOW_ANCHOR, and the live app never passes the prop - but it made every time-of-day branch untestable through the real provider. FIXED and PROVEN in commit 62f798c2a on branch claude/serene-heyrovsky-7a5e53 (base be65b8a1b), worktree D:\Repos\Database\.claude\worktrees\nostalgic-vaughan-7ee231: 40/40 existing call sites stayed green (92 tests), whole ward suite 519 passed, typecheck/lint/prettier pass, and a mutation test showed 3 of 4 new tests going red with 'Expected 450 / Received 642'. RISK: that branch has never been pushed, has no upstream and no PR, so the work exists on one machine only. NEXT ACTIONS, all on claude/ward-flow-phases-6-7-design (which was ACTIVELY ADVANCING when this was written - a53a3a994 to 0d29dd734 in one session, so confirm ownership before editing): (1) cherry-pick 62f798c2a or re-apply the two-line change; (2) write the spec D5 pre-08:00 test through the real provider by rendering MorningPage inside WardFlowProvider initialNow={7*60+30} - today D5 is proven at the pure-function level only, not through the rendered page; (3) remove the now-misleading NullHandoverHarness and its doc comments in tests/ward-morning-page.dom.test.tsx, which describe the defect as present-tense fact (DirectFrozenHarness may still earn its place as a unit-level check - judgement call, not a mechanical delete); (4) decide whether claude/serene-heyrovsky-7a5e53 should be closed as redundant or pushed as its own low-risk two-file PR - owner's call. Full handover with evidence, traps and do-not-reopen list: docs/ward-flow-pinned-clock-handover.md (committed 3fd861418 on the same branch, so it is also unpushed). Offline only - no provider, Supabase, OpenAI or CI access needed for any step. | Claude Code session 2026-08-27; handover doc docs/ward-flow-pinned-clock-handover.md | 2026-08-27 | +| #YTR84P | P2 | task | Ward Flow pinned clock: the provider fix is on main; D5's rendered branch is untested AND unrendered, so it needs an owner decision before any test | CORRECTED 2026-09-02 from a cloud session, verified against origin/main 45a3dca and origin/claude/ward-flow-phases-6-7-design 1888ad1. Four of this row's claims were stale and its central NEXT ACTION was not executable as written. (1) THE FIX IS ON MAIN. ward-flow-provider.tsx uses the pinned instant verbatim, and tests/ward-flow-provider.dom.test.tsx already pins 450 (07:30) and 1265 (21:05) asserting now is not NOW_ANCHOR. Action (1) is done. (2) claude/serene-heyrovsky-7a5e53 is REDUNDANT - close it; that answers action (4). Do not cherry-pick 62f798c2a; it is already superseded. (3) The morning page is not on main at all, and the Phase 6 branch fixed the same defect independently on 2026-08-30, so 'still carries the defect' is false everywhere. That branch IS reachable from a cloud container: git fetch --depth=50 origin claude/ward-flow-phases-6-7-design. (4) The blast radius is 51 call sites across 20 test files (47 NOW_ANCHOR, 4 deliberately not), not 40. (5) NullHandoverHarness and DirectFrozenHarness NO LONGER EXIST on that branch; action (3) has no target and should not be searched for. THE ONE REMAINING ACTION IS NOT A TEST, IT IS A DECISION. Action (2) said to render MorningPage inside WardFlowProvider initialNow={7*60+30}. THAT TEST CANNOT PASS, for a reason unrelated to the clock: owner decision WB-DB-11 ('ONE VIEW, ALWAYS LIVE') removed the fixed/live split, so MorningPage renders MorningBody and nothing else. NoHandoverYet (which holds 'The 08:00 handover has not been taken for this day'), ViewControl, buildFrozenMorning and the FrozenMorning/MorningView types are all exported and NONE is rendered or called. Pinning before 08:00 renders the ordinary live page. So D5 must first be either retired with the fixed view, or the fixed view restored - only then is initialNow={7*60+30} the right mechanism, and the pinned-clock fix has already made it available. Coverage as measured: D5's clock rule is well covered (ward-morning-rollup.test.ts pins morningHandoverInstant null at 07:59 and 01:00) but its RENDERED failure branch has NO coverage of any kind. SEPARATE UNRECORDED BREAKAGE FOUND: tests/ui-ward-morning.spec.ts still clicks ward-morning-view-fixed/-live, which ViewControl holds and nothing renders, so that spec cannot pass against branch head. CONTEXT FOR WEIGHING ANY OF THIS: the branch's PR #2466 was CLOSED UNMERGED on 2026-08-31 - 299 commits ahead, 276 files, mergeable_state dirty, titled 'DRAFT, not for merge' - and all of it is design scratch under src/app/mockups/ward-flow/**, which 404s in production. DONE THIS PASS on main: correction blocks above the retained buggy snippet in docs/superpowers/plans/2026-08-19-ward-flow-phase-3-role-screens.md and docs/ward-flow-phase-3-workspace/task-4-brief.md (the only places that still TAUGHT the defect); docs/ward-flow-pinned-clock-handover.md rewritten to be true; and two mutation-proven screen-level tests in tests/ward-discharge-board.dom.test.tsx driving releaseBand's two now-dependent branches through a rendered board (both red under the reintroduced defect, all six pre-existing tests green). Still open because the D5 decision is real and untaken. Offline only. | Claude Code cloud session 2026-09-02; verified against origin/main 45a3dca, origin/claude/ward-flow-phases-6-7-design 1888ad1, and closed PR #2466 | 2026-08-27 | | #RBK2J7 | P2 | issue | Lighthouse desktop-root LCP reads 100-175ms above main on a feature branch, decays run over run, and reddened PR #2422 once | Six CI readings on 2026-08-27 against the same 786ms baseline (tolerance: breach needs BOTH >20% and >100ms). PR #2422 branch: 961 (FAILED, reported 2/3 samples breached), 925, 900, 852. main: 748 (603da96), 705 (be65b8a1). NOT ROOT-CAUSED; recorded so the next occurrence is not re-investigated from scratch. Evidence AGAINST a real code regression: (a) mobile-root - the SAME page - measured 2293/2305/2314 against its 2274 baseline across the same runs, i.e. unchanged, and a genuine slowdown in that page would show on both form factors; (b) desktop-documents-search measured FASTER than main on the same runners (745/806 vs main 828/841); (c) the monotone decay 961->925->900->852 is regression-to-mean of a noisy metric, not a fixed cost a diff adds; (d) reading the 11-file diff line by line, nothing alters the desktop root cold render - the only new guard (!showAnswerCancelledNotice on showAnswerPending) is a no-op when nothing was cancelled, and pb-4->pb-10 requires an answer to be present. ONE TESTABLE MECHANISM REMAINS: PR #2422 split src/components/clinical-dashboard/answer-cancelled-notice.tsx out of ClinicalDashboard (forced by the 4140-line maintainability budget), and a new client module can become a separate chunk on /. Next: rm -rf .next FIRST (AGENTS.md: a cached build reports stale byte-identical numbers and will wrongly pass), npm run build, then npm run check:bundle-budget, which attributes chunks per route and reports / separately; compare against main. If / gained a chunk, move AnswerCancelledNotice into src/components/clinical-dashboard/dashboard-notices.tsx, which ClinicalDashboard already imports - same budget relief, no new module boundary. GATE CAVEAT worth knowing: majorityBreachDecision in scripts/check-lighthouse-budget.mjs takes its 3 samples within ONE job on ONE runner, so it guards against a single outlier but not against a slow runner. Stop: do not raise the baseline or the tolerance to clear this; do not re-run the job to chase a better number. Related but distinct: #GQ5X8T (mobile-root timing, needs a synchronized control). | PR #2422 CI runs 33067093750 (attempts 1-2), 33070896021, 33072264164; main runs 33063088413, 33065297542 | 2026-08-27 | | #27TWKM | P2 | issue | CI never runs the caring-contacts Postgres suite, so every row-level-security and cross-team assertion it makes against real SQL fires only on a developer's machine | Found while reviewing Phase 2B Task 6b on 2026-08-25. `grep -rn "caring-contacts" .github/workflows/` returns zero hits, and every file in `caringContactsDbTestFiles` is unconditionally excluded from the offline `node` project in `vitest.config.mts`. So the whole caring-contacts Postgres project — the shared repository-contract suite run against real SQL, including the cross-team reads that prove a coordinator cannot see another team's patients, and the `set_config('caring_contacts.team_id', …)` / `set local role caring_contacts_app` preamble whose absence silently bypasses every row-level-security policy — runs ONLY when a human happens to have the Docker Postgres on 54329 up. This row is about that whole project, not about any one guard. What made it visible: Task 6b installed a source scan there proving `PLAN_COLUMNS` does not select a patient's free-text clinical note, and the scan needed no database at all. Its own review found it unreachable and moved it to `tests/caring-contacts-domain-isolation.test.ts`, which the default `npm run test` collects. Deliberately no test count here: the number moved by one within the same branch that filed the first version of this row, which is exactly why it is stated as an invariant instead. Two separable questions: (1) should CI run this project at all, which needs a Postgres service container and is a real CI-cost decision for the owner; (2) independently, no guard that needs no database should live in a database-only file — that half is a placement rule, not a cost decision, and could be enforced. | session 2026-08-25 | 2026-08-25 | | #BS3SN9 | P1 | task | Close remaining privacy provider, legal, and clinical approvals | The PsychSift owner approved the production HMAC control and verified retention schedules on 2026-09-01. Release remains blocked on six items: OpenAI ZDR and countersigned DPA evidence, Railway sensitive-health terms, APP 8 and APP 1/5 privacy-adviser approval, and clinical PHI-minimisation acceptance. Evidence authority: docs/governance/privacy-readiness.v1.json and docs/governance/privacy-closeout-2026-09-01.md. | session 2026-09-01 owner attestation closeout | 2026-09-01 | @@ -623,3 +618,8 @@ Move resolved rows here with the resolution date and a one-line outcome. Keep th | #0JJ4RD | issue | Two live overloads of purge_expired_rag_response_cache, one unbounded | Consolidated live purge_expired_rag_response_cache overloads into single bounded signature in migration 20260831100000_harden_security_definer_search_paths_and_purge_cache.sql and schema mirror. | 2026-08-31 | | #F5EYAJ | issue | Caring Contacts: connection-unavailable and permission-unavailable have no runtime caller | Created connection and permission unavailable fallback components, wired routing in app/caring-contacts/error.tsx, and dynamically supplied blockReason in workspace-overlays.tsx | 2026-08-27 | | #ZW43ZT | issue | Every developer hub page renders twice in the browser during development | Bypassed GlobalMockupSearchShell for /mockups/development in MockupsLayoutClient to eliminate duplicate dev main landmark collisions. | 2026-08-27 | +| #0HYHTH | issue | Caring Contacts: nothing checks screen wording against the prohibited-vocabulary list | Resolved; verified against origin/main d1bb2c197 on 2026-09-02. The row's claim was that the prohibited-language check ran only against outgoing messages and the 24 frozen overlay rows, so interface strings elsewhere in the workspace were governed by people rather than by software. tests/caring-contacts-interface-vocabulary.test.ts now exists (18 KB) and closes that: it walks SCAN_ROOTS on disk, strips comments and className values, and asserts that no remaining quoted literal or JSX text matches CARING_CONTACTS_PROHIBITED_LANGUAGE, the single shared vocabulary in tests/helpers/caring-contacts-prohibited-language.ts. The scan roots are deliberately narrow so that src/components/caring-contacts/mockups/** is excluded by construction rather than by a special-case skip, and the suite carries its own fixture-directory self-tests (a quoted-literal fixture and a JSX-text fixture) so the scan is proved able to fail rather than merely observed passing. THE KNOWN RESIDUE IS TRACKED SEPARATELY AND IS NOT LOST BY THIS CLOSE: a pending inbox add (ULID 01M0VR9XRYZ5P2BW2GHVKFHDKJ) records that this scan cannot express a job title and that the workaround pushes role copy into src/lib/caring-contacts/**, which no vocabulary scan reaches. Related open row #AGRAKQ covers the plural job-title collision in the shared helper. HISTORY: a done request for this row (b1630592) was cancelled on 2026-08-31 with the reason 'target issue is no longer in Open items on current main'. That premise was wrong -- the row was and remained open -- so the close was lost rather than applied. This request re-closes it on directly verified evidence. | 2026-09-02 | +| #PWZYY8 | issue | Icons passed as icon props render with no aria-hidden, and the lint rule is structurally blind to it | Resolved; verified against origin/main d1bb2c197 on 2026-09-02. Both halves of the row are closed. THE LINT RULE IS NO LONGER BLIND: eslint-rules/require-lucide-icon-aria.mjs:51-52 now resolves a JSX tag named Icon or ActiveIcon in addition to a direct lucide-react import, so an icon received as an icon: LucideIcon prop and rendered bare is flagged, with an autofix that inserts aria-hidden="true". THE CALL SITES ARE MARKED: all four components the row named by name in src/components/clinical-dashboard/document-ui.tsx (DocumentBadge:82, DocumentActionLink:132, DocumentActionAnchor:149, DocumentActionButton:166) carry aria-hidden="true", and evidence-panels.tsx:1257 -- the site the row cited as inconsistent with its own Loader2 sibling -- now matches that sibling. MEASURED, not sampled: a scan of every and element in non-mockup src/**/*.tsx finds 184 sites, 182 of which declare aria-hidden, aria-label, aria-labelledby, role or title. The two that do not are both in src/components/settings-search-mockups/settings-search-mockup-page.tsx, which is design scratch and outside the production gates. NOTE, not a blocker: the rule's own header comment (line 9) still reads that icons passed around as icon: LucideIcon values are out of scope, which line 51 has since falsified. A stale comment, not a coverage hole. HISTORY: a done request for this row (d6e41550) was cancelled on 2026-08-31 with the reason 'target issue is no longer in Open items on current main'. That premise was wrong -- the row was and remained open -- so the close was lost rather than applied. This request re-closes it on directly verified evidence rather than on the cancelled claim. | 2026-09-02 | +| #XHADPV | issue | repo-awareness snapshot emits two per-section documentation counts that nothing reads | Resolved; verified against origin/main d1bb2c197 on 2026-09-02. The row asked for a deliberate choice between rendering the stored per-section counts and no longer emitting them, and the second option was taken. Reading the committed artifact rather than the generator: data/repo-awareness-snapshot.json now carries 21 entries under documentation.sections, and the union of every key across all of them is exactly {name}. The documents and uncatalogued fields the row counted as 30 dead numbers are gone from the snapshot entirely, so the generator no longer emits a count that could disagree with its own list, and the documentation page's habit of computing section.documents.length at render is now the only source of a count rather than an undocumented exception to the plan's rule. Section count has grown from the 15 the row recorded to 21, which is ordinary growth in the documentation set and not a change of contract. | 2026-09-02 | +| #5YKAQ5 | issue | sentry.server.config falls back to the service-role key when the publishable key is absent | Resolved; verified against origin/main d1bb2c197 on 2026-09-02. The row asked for the fallback to be dropped so that a missing publishable key disables tracing rather than escalating to the service-role secret. That is exactly what the code now does. In src/sentry.server.config.ts, supabaseTracingIntegrations() reads NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY and returns an empty integration list when either is absent, so no Supabase client is constructed at all. Proved by absence as well as by reading: grep for SERVICE_ROLE across src/sentry.server.config.ts returns zero matches, so the secret is no longer instantiated in that file under any branch. The privacy posture the row noted as already correct is unchanged -- the integration still sets sendOperationData: false, so no PostgREST filter values or mutation bodies reach Sentry -- and the whole path stays inert unless a DSN is set and the tracing sample rate is above zero. | 2026-09-02 | +| #DVRZA4 | rec | prettierignore lists one generated data snapshot but not its sibling | Resolved; verified against origin/main d1bb2c197 on 2026-09-02. The asymmetry the row recorded is gone. .prettierignore now lists both generated data snapshots, each with its own reason comment: data/outstanding-issues-snapshot.json at line 53 (generated by scripts/generate-outstanding-issues-snapshot.mjs, ignored so regeneration stays churn-free and check:outstanding-issues-snapshot compares like with like) and data/repo-awareness-snapshot.json at line 57 (generated by scripts/generate-repo-awareness-snapshot.ts, ignored so the generator's own formatting is preserved for check:repo-awareness-snapshot). The row explicitly left the direction open -- add the newer entry or remove the older one -- and it was settled by adding the newer one, which is the direction that keeps both generators authoritative over their own output. | 2026-09-02 |