diff --git a/docs/branch-review-records/5eb916e08284d9e70c23f302afa79bbc6efea6e39677c3a3a6e4006b3b749075.record.md b/docs/branch-review-records/5eb916e08284d9e70c23f302afa79bbc6efea6e39677c3a3a6e4006b3b749075.record.md new file mode 100644 index 0000000000..c4cf7fbb93 --- /dev/null +++ b/docs/branch-review-records/5eb916e08284d9e70c23f302afa79bbc6efea6e39677c3a3a6e4006b3b749075.record.md @@ -0,0 +1 @@ +| 2026-08-14 | claude/ledger-reconcile-batch-1 | 00c09a30f91427b3154b64812b73294847265778 | docs/outstanding-issues.md + inbox — serial reconciliation of 75 queued requests | Applied 63 active mutations (14 add, 23 done, 26 update) plus 6 cancellation decisions. Ledger 115 open/199 archived -> 106/222; inbox 0 pending/94 applied. Verified zero live same-target collisions before applying, since planRequestBatch dedupes on request UUID not canonical row id. | issues:reconcile --dry-run; verify:pr-local (11 completed, 0 failed); check:ledger-write-discipline | diff --git a/docs/outstanding-issues-inbox/02879c2f-f7c7-4698-bc2e-cd5555cc2f37.json b/docs/outstanding-issues-inbox/applied/02879c2f-f7c7-4698-bc2e-cd5555cc2f37.json similarity index 100% rename from docs/outstanding-issues-inbox/02879c2f-f7c7-4698-bc2e-cd5555cc2f37.json rename to docs/outstanding-issues-inbox/applied/02879c2f-f7c7-4698-bc2e-cd5555cc2f37.json diff --git a/docs/outstanding-issues-inbox/04470779-8c8d-4c90-ad04-bc6d613fd73a.json b/docs/outstanding-issues-inbox/applied/04470779-8c8d-4c90-ad04-bc6d613fd73a.json similarity index 100% rename from docs/outstanding-issues-inbox/04470779-8c8d-4c90-ad04-bc6d613fd73a.json rename to docs/outstanding-issues-inbox/applied/04470779-8c8d-4c90-ad04-bc6d613fd73a.json diff --git a/docs/outstanding-issues-inbox/05b0c32b-07f7-4ca2-9361-4d7bc5490661.json b/docs/outstanding-issues-inbox/applied/05b0c32b-07f7-4ca2-9361-4d7bc5490661.json similarity index 100% rename from docs/outstanding-issues-inbox/05b0c32b-07f7-4ca2-9361-4d7bc5490661.json rename to docs/outstanding-issues-inbox/applied/05b0c32b-07f7-4ca2-9361-4d7bc5490661.json diff --git a/docs/outstanding-issues-inbox/09b3e12b-c171-49a6-b9d3-3ff627c8f2cd.json b/docs/outstanding-issues-inbox/applied/09b3e12b-c171-49a6-b9d3-3ff627c8f2cd.json similarity index 100% rename from docs/outstanding-issues-inbox/09b3e12b-c171-49a6-b9d3-3ff627c8f2cd.json rename to docs/outstanding-issues-inbox/applied/09b3e12b-c171-49a6-b9d3-3ff627c8f2cd.json diff --git a/docs/outstanding-issues-inbox/0d8735c6-1950-4fef-8880-007063bbf662.json b/docs/outstanding-issues-inbox/applied/0d8735c6-1950-4fef-8880-007063bbf662.json similarity index 100% rename from docs/outstanding-issues-inbox/0d8735c6-1950-4fef-8880-007063bbf662.json rename to docs/outstanding-issues-inbox/applied/0d8735c6-1950-4fef-8880-007063bbf662.json diff --git a/docs/outstanding-issues-inbox/0e47904b-f354-4795-a4fc-dcf8b91c1790.json b/docs/outstanding-issues-inbox/applied/0e47904b-f354-4795-a4fc-dcf8b91c1790.json similarity index 100% rename from docs/outstanding-issues-inbox/0e47904b-f354-4795-a4fc-dcf8b91c1790.json rename to docs/outstanding-issues-inbox/applied/0e47904b-f354-4795-a4fc-dcf8b91c1790.json diff --git a/docs/outstanding-issues-inbox/1752caad-7fac-4089-a415-d20a26e5984a.json b/docs/outstanding-issues-inbox/applied/1752caad-7fac-4089-a415-d20a26e5984a.json similarity index 100% rename from docs/outstanding-issues-inbox/1752caad-7fac-4089-a415-d20a26e5984a.json rename to docs/outstanding-issues-inbox/applied/1752caad-7fac-4089-a415-d20a26e5984a.json diff --git a/docs/outstanding-issues-inbox/1bfaf0ef-e169-4a0c-8ad0-ca391f5f6024.json b/docs/outstanding-issues-inbox/applied/1bfaf0ef-e169-4a0c-8ad0-ca391f5f6024.json similarity index 100% rename from docs/outstanding-issues-inbox/1bfaf0ef-e169-4a0c-8ad0-ca391f5f6024.json rename to docs/outstanding-issues-inbox/applied/1bfaf0ef-e169-4a0c-8ad0-ca391f5f6024.json diff --git a/docs/outstanding-issues-inbox/210e3db5-f863-4dc8-8d6e-4d75d044e661.json b/docs/outstanding-issues-inbox/applied/210e3db5-f863-4dc8-8d6e-4d75d044e661.json similarity index 100% rename from docs/outstanding-issues-inbox/210e3db5-f863-4dc8-8d6e-4d75d044e661.json rename to docs/outstanding-issues-inbox/applied/210e3db5-f863-4dc8-8d6e-4d75d044e661.json diff --git a/docs/outstanding-issues-inbox/24586190-0756-488d-941e-70a970c13cce.json b/docs/outstanding-issues-inbox/applied/24586190-0756-488d-941e-70a970c13cce.json similarity index 100% rename from docs/outstanding-issues-inbox/24586190-0756-488d-941e-70a970c13cce.json rename to docs/outstanding-issues-inbox/applied/24586190-0756-488d-941e-70a970c13cce.json diff --git a/docs/outstanding-issues-inbox/2a2200d7-83ea-4cff-9969-ae682bad740c.json b/docs/outstanding-issues-inbox/applied/2a2200d7-83ea-4cff-9969-ae682bad740c.json similarity index 100% rename from docs/outstanding-issues-inbox/2a2200d7-83ea-4cff-9969-ae682bad740c.json rename to docs/outstanding-issues-inbox/applied/2a2200d7-83ea-4cff-9969-ae682bad740c.json diff --git a/docs/outstanding-issues-inbox/2a5aadb2-ab0e-4d89-8653-7a704933d533.json b/docs/outstanding-issues-inbox/applied/2a5aadb2-ab0e-4d89-8653-7a704933d533.json similarity index 100% rename from docs/outstanding-issues-inbox/2a5aadb2-ab0e-4d89-8653-7a704933d533.json rename to docs/outstanding-issues-inbox/applied/2a5aadb2-ab0e-4d89-8653-7a704933d533.json diff --git a/docs/outstanding-issues-inbox/31650c87-cfea-4a98-88e1-bd41b44c12b2.json b/docs/outstanding-issues-inbox/applied/31650c87-cfea-4a98-88e1-bd41b44c12b2.json similarity index 100% rename from docs/outstanding-issues-inbox/31650c87-cfea-4a98-88e1-bd41b44c12b2.json rename to docs/outstanding-issues-inbox/applied/31650c87-cfea-4a98-88e1-bd41b44c12b2.json diff --git a/docs/outstanding-issues-inbox/38e25384-5dcb-463d-b1b7-e0caea60bb83.json b/docs/outstanding-issues-inbox/applied/38e25384-5dcb-463d-b1b7-e0caea60bb83.json similarity index 100% rename from docs/outstanding-issues-inbox/38e25384-5dcb-463d-b1b7-e0caea60bb83.json rename to docs/outstanding-issues-inbox/applied/38e25384-5dcb-463d-b1b7-e0caea60bb83.json diff --git a/docs/outstanding-issues-inbox/3f1a672f-9038-4a36-897f-5286e4fa028d.json b/docs/outstanding-issues-inbox/applied/3f1a672f-9038-4a36-897f-5286e4fa028d.json similarity index 100% rename from docs/outstanding-issues-inbox/3f1a672f-9038-4a36-897f-5286e4fa028d.json rename to docs/outstanding-issues-inbox/applied/3f1a672f-9038-4a36-897f-5286e4fa028d.json diff --git a/docs/outstanding-issues-inbox/439cd410-9311-4d22-8888-9fc15948fea0.json b/docs/outstanding-issues-inbox/applied/439cd410-9311-4d22-8888-9fc15948fea0.json similarity index 100% rename from docs/outstanding-issues-inbox/439cd410-9311-4d22-8888-9fc15948fea0.json rename to docs/outstanding-issues-inbox/applied/439cd410-9311-4d22-8888-9fc15948fea0.json diff --git a/docs/outstanding-issues-inbox/43a11115-c75f-4402-a83c-7a309b3a76c4.json b/docs/outstanding-issues-inbox/applied/43a11115-c75f-4402-a83c-7a309b3a76c4.json similarity index 100% rename from docs/outstanding-issues-inbox/43a11115-c75f-4402-a83c-7a309b3a76c4.json rename to docs/outstanding-issues-inbox/applied/43a11115-c75f-4402-a83c-7a309b3a76c4.json diff --git a/docs/outstanding-issues-inbox/47ba09d2-64f6-47ab-8ad6-380fc3f93eee.json b/docs/outstanding-issues-inbox/applied/47ba09d2-64f6-47ab-8ad6-380fc3f93eee.json similarity index 100% rename from docs/outstanding-issues-inbox/47ba09d2-64f6-47ab-8ad6-380fc3f93eee.json rename to docs/outstanding-issues-inbox/applied/47ba09d2-64f6-47ab-8ad6-380fc3f93eee.json diff --git a/docs/outstanding-issues-inbox/4b95979b-65e8-474d-bc53-6f69e7eb5acf.json b/docs/outstanding-issues-inbox/applied/4b95979b-65e8-474d-bc53-6f69e7eb5acf.json similarity index 100% rename from docs/outstanding-issues-inbox/4b95979b-65e8-474d-bc53-6f69e7eb5acf.json rename to docs/outstanding-issues-inbox/applied/4b95979b-65e8-474d-bc53-6f69e7eb5acf.json diff --git a/docs/outstanding-issues-inbox/52929edc-53b9-4694-a0e2-1bd3c4b0a41b.json b/docs/outstanding-issues-inbox/applied/52929edc-53b9-4694-a0e2-1bd3c4b0a41b.json similarity index 100% rename from docs/outstanding-issues-inbox/52929edc-53b9-4694-a0e2-1bd3c4b0a41b.json rename to docs/outstanding-issues-inbox/applied/52929edc-53b9-4694-a0e2-1bd3c4b0a41b.json diff --git a/docs/outstanding-issues-inbox/5a2b5bda-41b7-45e9-921b-d87df7ac6af0.json b/docs/outstanding-issues-inbox/applied/5a2b5bda-41b7-45e9-921b-d87df7ac6af0.json similarity index 100% rename from docs/outstanding-issues-inbox/5a2b5bda-41b7-45e9-921b-d87df7ac6af0.json rename to docs/outstanding-issues-inbox/applied/5a2b5bda-41b7-45e9-921b-d87df7ac6af0.json diff --git a/docs/outstanding-issues-inbox/5af9a8bf-6136-4347-bcae-8433c8c9e686.json b/docs/outstanding-issues-inbox/applied/5af9a8bf-6136-4347-bcae-8433c8c9e686.json similarity index 100% rename from docs/outstanding-issues-inbox/5af9a8bf-6136-4347-bcae-8433c8c9e686.json rename to docs/outstanding-issues-inbox/applied/5af9a8bf-6136-4347-bcae-8433c8c9e686.json diff --git a/docs/outstanding-issues-inbox/5bf830df-f89b-4789-a6dc-48284d212171.json b/docs/outstanding-issues-inbox/applied/5bf830df-f89b-4789-a6dc-48284d212171.json similarity index 100% rename from docs/outstanding-issues-inbox/5bf830df-f89b-4789-a6dc-48284d212171.json rename to docs/outstanding-issues-inbox/applied/5bf830df-f89b-4789-a6dc-48284d212171.json diff --git a/docs/outstanding-issues-inbox/5c830d16-5076-4435-9903-6f3e7a71daa4.json b/docs/outstanding-issues-inbox/applied/5c830d16-5076-4435-9903-6f3e7a71daa4.json similarity index 100% rename from docs/outstanding-issues-inbox/5c830d16-5076-4435-9903-6f3e7a71daa4.json rename to docs/outstanding-issues-inbox/applied/5c830d16-5076-4435-9903-6f3e7a71daa4.json diff --git a/docs/outstanding-issues-inbox/67352cea-8989-4e12-b50d-5a775664d8a7.json b/docs/outstanding-issues-inbox/applied/67352cea-8989-4e12-b50d-5a775664d8a7.json similarity index 100% rename from docs/outstanding-issues-inbox/67352cea-8989-4e12-b50d-5a775664d8a7.json rename to docs/outstanding-issues-inbox/applied/67352cea-8989-4e12-b50d-5a775664d8a7.json diff --git a/docs/outstanding-issues-inbox/6a90b0f5-e5e2-46a1-9981-d5886d0e41b8.json b/docs/outstanding-issues-inbox/applied/6a90b0f5-e5e2-46a1-9981-d5886d0e41b8.json similarity index 100% rename from docs/outstanding-issues-inbox/6a90b0f5-e5e2-46a1-9981-d5886d0e41b8.json rename to docs/outstanding-issues-inbox/applied/6a90b0f5-e5e2-46a1-9981-d5886d0e41b8.json diff --git a/docs/outstanding-issues-inbox/721e629e-15df-4b77-a25c-c18200bdca3f.json b/docs/outstanding-issues-inbox/applied/721e629e-15df-4b77-a25c-c18200bdca3f.json similarity index 100% rename from docs/outstanding-issues-inbox/721e629e-15df-4b77-a25c-c18200bdca3f.json rename to docs/outstanding-issues-inbox/applied/721e629e-15df-4b77-a25c-c18200bdca3f.json diff --git a/docs/outstanding-issues-inbox/72929356-5ff3-47da-89a9-82de09602e73.json b/docs/outstanding-issues-inbox/applied/72929356-5ff3-47da-89a9-82de09602e73.json similarity index 100% rename from docs/outstanding-issues-inbox/72929356-5ff3-47da-89a9-82de09602e73.json rename to docs/outstanding-issues-inbox/applied/72929356-5ff3-47da-89a9-82de09602e73.json diff --git a/docs/outstanding-issues-inbox/75a2729c-b0b0-4144-8d99-2c3014d4bcf1.json b/docs/outstanding-issues-inbox/applied/75a2729c-b0b0-4144-8d99-2c3014d4bcf1.json similarity index 100% rename from docs/outstanding-issues-inbox/75a2729c-b0b0-4144-8d99-2c3014d4bcf1.json rename to docs/outstanding-issues-inbox/applied/75a2729c-b0b0-4144-8d99-2c3014d4bcf1.json diff --git a/docs/outstanding-issues-inbox/75df9b82-7ecc-4aa2-bdac-653da976fe53.json b/docs/outstanding-issues-inbox/applied/75df9b82-7ecc-4aa2-bdac-653da976fe53.json similarity index 100% rename from docs/outstanding-issues-inbox/75df9b82-7ecc-4aa2-bdac-653da976fe53.json rename to docs/outstanding-issues-inbox/applied/75df9b82-7ecc-4aa2-bdac-653da976fe53.json diff --git a/docs/outstanding-issues-inbox/7f9de4f4-73ca-4ab2-946c-b2ee3c27d708.json b/docs/outstanding-issues-inbox/applied/7f9de4f4-73ca-4ab2-946c-b2ee3c27d708.json similarity index 100% rename from docs/outstanding-issues-inbox/7f9de4f4-73ca-4ab2-946c-b2ee3c27d708.json rename to docs/outstanding-issues-inbox/applied/7f9de4f4-73ca-4ab2-946c-b2ee3c27d708.json diff --git a/docs/outstanding-issues-inbox/7fc8d67c-e2e4-4e6c-9bcf-6ac65fe9adc7.json b/docs/outstanding-issues-inbox/applied/7fc8d67c-e2e4-4e6c-9bcf-6ac65fe9adc7.json similarity index 100% rename from docs/outstanding-issues-inbox/7fc8d67c-e2e4-4e6c-9bcf-6ac65fe9adc7.json rename to docs/outstanding-issues-inbox/applied/7fc8d67c-e2e4-4e6c-9bcf-6ac65fe9adc7.json diff --git a/docs/outstanding-issues-inbox/821d7e63-4bf3-4af7-abfb-071f916b847c.json b/docs/outstanding-issues-inbox/applied/821d7e63-4bf3-4af7-abfb-071f916b847c.json similarity index 100% rename from docs/outstanding-issues-inbox/821d7e63-4bf3-4af7-abfb-071f916b847c.json rename to docs/outstanding-issues-inbox/applied/821d7e63-4bf3-4af7-abfb-071f916b847c.json diff --git a/docs/outstanding-issues-inbox/82377462-2da9-4f8e-b0f5-a421a1ab5fdd.json b/docs/outstanding-issues-inbox/applied/82377462-2da9-4f8e-b0f5-a421a1ab5fdd.json similarity index 100% rename from docs/outstanding-issues-inbox/82377462-2da9-4f8e-b0f5-a421a1ab5fdd.json rename to docs/outstanding-issues-inbox/applied/82377462-2da9-4f8e-b0f5-a421a1ab5fdd.json diff --git a/docs/outstanding-issues-inbox/829597d4-698b-4cc2-9bf4-65310504cba3.json b/docs/outstanding-issues-inbox/applied/829597d4-698b-4cc2-9bf4-65310504cba3.json similarity index 100% rename from docs/outstanding-issues-inbox/829597d4-698b-4cc2-9bf4-65310504cba3.json rename to docs/outstanding-issues-inbox/applied/829597d4-698b-4cc2-9bf4-65310504cba3.json diff --git a/docs/outstanding-issues-inbox/8621298b-db72-4960-818a-66ae49a3977f.json b/docs/outstanding-issues-inbox/applied/8621298b-db72-4960-818a-66ae49a3977f.json similarity index 100% rename from docs/outstanding-issues-inbox/8621298b-db72-4960-818a-66ae49a3977f.json rename to docs/outstanding-issues-inbox/applied/8621298b-db72-4960-818a-66ae49a3977f.json diff --git a/docs/outstanding-issues-inbox/89ac3ee8-550a-44aa-b411-88687e5935e4.json b/docs/outstanding-issues-inbox/applied/89ac3ee8-550a-44aa-b411-88687e5935e4.json similarity index 100% rename from docs/outstanding-issues-inbox/89ac3ee8-550a-44aa-b411-88687e5935e4.json rename to docs/outstanding-issues-inbox/applied/89ac3ee8-550a-44aa-b411-88687e5935e4.json diff --git a/docs/outstanding-issues-inbox/8c2d3fea-e7a9-41af-a825-62c8109d0248.json b/docs/outstanding-issues-inbox/applied/8c2d3fea-e7a9-41af-a825-62c8109d0248.json similarity index 100% rename from docs/outstanding-issues-inbox/8c2d3fea-e7a9-41af-a825-62c8109d0248.json rename to docs/outstanding-issues-inbox/applied/8c2d3fea-e7a9-41af-a825-62c8109d0248.json diff --git a/docs/outstanding-issues-inbox/8ef1a871-8573-4eab-83fd-213734efd82c.json b/docs/outstanding-issues-inbox/applied/8ef1a871-8573-4eab-83fd-213734efd82c.json similarity index 100% rename from docs/outstanding-issues-inbox/8ef1a871-8573-4eab-83fd-213734efd82c.json rename to docs/outstanding-issues-inbox/applied/8ef1a871-8573-4eab-83fd-213734efd82c.json diff --git a/docs/outstanding-issues-inbox/9424f687-997a-4b52-ab31-b49e462168b6.json b/docs/outstanding-issues-inbox/applied/9424f687-997a-4b52-ab31-b49e462168b6.json similarity index 100% rename from docs/outstanding-issues-inbox/9424f687-997a-4b52-ab31-b49e462168b6.json rename to docs/outstanding-issues-inbox/applied/9424f687-997a-4b52-ab31-b49e462168b6.json diff --git a/docs/outstanding-issues-inbox/9ae893ef-7073-4dfa-8602-899d53b177b7.json b/docs/outstanding-issues-inbox/applied/9ae893ef-7073-4dfa-8602-899d53b177b7.json similarity index 100% rename from docs/outstanding-issues-inbox/9ae893ef-7073-4dfa-8602-899d53b177b7.json rename to docs/outstanding-issues-inbox/applied/9ae893ef-7073-4dfa-8602-899d53b177b7.json diff --git a/docs/outstanding-issues-inbox/9c89036c-733b-41ed-b1f7-92f3daa2940b.json b/docs/outstanding-issues-inbox/applied/9c89036c-733b-41ed-b1f7-92f3daa2940b.json similarity index 100% rename from docs/outstanding-issues-inbox/9c89036c-733b-41ed-b1f7-92f3daa2940b.json rename to docs/outstanding-issues-inbox/applied/9c89036c-733b-41ed-b1f7-92f3daa2940b.json diff --git a/docs/outstanding-issues-inbox/9db75fae-07e6-454d-b2c4-eb88b0e8ff06.json b/docs/outstanding-issues-inbox/applied/9db75fae-07e6-454d-b2c4-eb88b0e8ff06.json similarity index 100% rename from docs/outstanding-issues-inbox/9db75fae-07e6-454d-b2c4-eb88b0e8ff06.json rename to docs/outstanding-issues-inbox/applied/9db75fae-07e6-454d-b2c4-eb88b0e8ff06.json diff --git a/docs/outstanding-issues-inbox/9dd78494-88b6-4d8e-b5c3-28caf91acaba.json b/docs/outstanding-issues-inbox/applied/9dd78494-88b6-4d8e-b5c3-28caf91acaba.json similarity index 100% rename from docs/outstanding-issues-inbox/9dd78494-88b6-4d8e-b5c3-28caf91acaba.json rename to docs/outstanding-issues-inbox/applied/9dd78494-88b6-4d8e-b5c3-28caf91acaba.json diff --git a/docs/outstanding-issues-inbox/9fded706-51d9-4d8f-9aa4-c57a6b0ac7aa.json b/docs/outstanding-issues-inbox/applied/9fded706-51d9-4d8f-9aa4-c57a6b0ac7aa.json similarity index 100% rename from docs/outstanding-issues-inbox/9fded706-51d9-4d8f-9aa4-c57a6b0ac7aa.json rename to docs/outstanding-issues-inbox/applied/9fded706-51d9-4d8f-9aa4-c57a6b0ac7aa.json diff --git a/docs/outstanding-issues-inbox/a101203a-ae13-46e0-9f76-6e91adf77c9a.json b/docs/outstanding-issues-inbox/applied/a101203a-ae13-46e0-9f76-6e91adf77c9a.json similarity index 100% rename from docs/outstanding-issues-inbox/a101203a-ae13-46e0-9f76-6e91adf77c9a.json rename to docs/outstanding-issues-inbox/applied/a101203a-ae13-46e0-9f76-6e91adf77c9a.json diff --git a/docs/outstanding-issues-inbox/a51fd616-bb58-4a1b-9009-21686eea2a84.json b/docs/outstanding-issues-inbox/applied/a51fd616-bb58-4a1b-9009-21686eea2a84.json similarity index 100% rename from docs/outstanding-issues-inbox/a51fd616-bb58-4a1b-9009-21686eea2a84.json rename to docs/outstanding-issues-inbox/applied/a51fd616-bb58-4a1b-9009-21686eea2a84.json diff --git a/docs/outstanding-issues-inbox/a5c8654c-f466-49b5-86ec-f783c15fc6e7.json b/docs/outstanding-issues-inbox/applied/a5c8654c-f466-49b5-86ec-f783c15fc6e7.json similarity index 100% rename from docs/outstanding-issues-inbox/a5c8654c-f466-49b5-86ec-f783c15fc6e7.json rename to docs/outstanding-issues-inbox/applied/a5c8654c-f466-49b5-86ec-f783c15fc6e7.json diff --git a/docs/outstanding-issues-inbox/a8a73f5a-f4c5-4c97-8c5d-bc93215469bc.json b/docs/outstanding-issues-inbox/applied/a8a73f5a-f4c5-4c97-8c5d-bc93215469bc.json similarity index 100% rename from docs/outstanding-issues-inbox/a8a73f5a-f4c5-4c97-8c5d-bc93215469bc.json rename to docs/outstanding-issues-inbox/applied/a8a73f5a-f4c5-4c97-8c5d-bc93215469bc.json diff --git a/docs/outstanding-issues-inbox/abd14d84-e4d6-4f8c-8384-a97817aafcb7.json b/docs/outstanding-issues-inbox/applied/abd14d84-e4d6-4f8c-8384-a97817aafcb7.json similarity index 100% rename from docs/outstanding-issues-inbox/abd14d84-e4d6-4f8c-8384-a97817aafcb7.json rename to docs/outstanding-issues-inbox/applied/abd14d84-e4d6-4f8c-8384-a97817aafcb7.json diff --git a/docs/outstanding-issues-inbox/b278a991-3122-4662-a835-37dd1d6645cc.json b/docs/outstanding-issues-inbox/applied/b278a991-3122-4662-a835-37dd1d6645cc.json similarity index 100% rename from docs/outstanding-issues-inbox/b278a991-3122-4662-a835-37dd1d6645cc.json rename to docs/outstanding-issues-inbox/applied/b278a991-3122-4662-a835-37dd1d6645cc.json diff --git a/docs/outstanding-issues-inbox/b78e551e-280b-4b9c-bdc5-37a79daeb4cf.json b/docs/outstanding-issues-inbox/applied/b78e551e-280b-4b9c-bdc5-37a79daeb4cf.json similarity index 100% rename from docs/outstanding-issues-inbox/b78e551e-280b-4b9c-bdc5-37a79daeb4cf.json rename to docs/outstanding-issues-inbox/applied/b78e551e-280b-4b9c-bdc5-37a79daeb4cf.json diff --git a/docs/outstanding-issues-inbox/bb14f53c-d36f-48ce-a02d-836aaa107008.json b/docs/outstanding-issues-inbox/applied/bb14f53c-d36f-48ce-a02d-836aaa107008.json similarity index 100% rename from docs/outstanding-issues-inbox/bb14f53c-d36f-48ce-a02d-836aaa107008.json rename to docs/outstanding-issues-inbox/applied/bb14f53c-d36f-48ce-a02d-836aaa107008.json diff --git a/docs/outstanding-issues-inbox/bce5b6bd-4727-4ad5-ac5f-d05a24df43cc.json b/docs/outstanding-issues-inbox/applied/bce5b6bd-4727-4ad5-ac5f-d05a24df43cc.json similarity index 100% rename from docs/outstanding-issues-inbox/bce5b6bd-4727-4ad5-ac5f-d05a24df43cc.json rename to docs/outstanding-issues-inbox/applied/bce5b6bd-4727-4ad5-ac5f-d05a24df43cc.json diff --git a/docs/outstanding-issues-inbox/bf12d7c9-04f8-4095-ba68-0cf6f5736501.json b/docs/outstanding-issues-inbox/applied/bf12d7c9-04f8-4095-ba68-0cf6f5736501.json similarity index 100% rename from docs/outstanding-issues-inbox/bf12d7c9-04f8-4095-ba68-0cf6f5736501.json rename to docs/outstanding-issues-inbox/applied/bf12d7c9-04f8-4095-ba68-0cf6f5736501.json diff --git a/docs/outstanding-issues-inbox/bfc60ffa-e648-47e1-8270-bc1056516239.json b/docs/outstanding-issues-inbox/applied/bfc60ffa-e648-47e1-8270-bc1056516239.json similarity index 100% rename from docs/outstanding-issues-inbox/bfc60ffa-e648-47e1-8270-bc1056516239.json rename to docs/outstanding-issues-inbox/applied/bfc60ffa-e648-47e1-8270-bc1056516239.json diff --git a/docs/outstanding-issues-inbox/c1218846-c99f-4d49-a44c-a97d642926aa.json b/docs/outstanding-issues-inbox/applied/c1218846-c99f-4d49-a44c-a97d642926aa.json similarity index 100% rename from docs/outstanding-issues-inbox/c1218846-c99f-4d49-a44c-a97d642926aa.json rename to docs/outstanding-issues-inbox/applied/c1218846-c99f-4d49-a44c-a97d642926aa.json diff --git a/docs/outstanding-issues-inbox/c15dfcea-2b4e-4c25-983e-c699ef7d1a52.json b/docs/outstanding-issues-inbox/applied/c15dfcea-2b4e-4c25-983e-c699ef7d1a52.json similarity index 100% rename from docs/outstanding-issues-inbox/c15dfcea-2b4e-4c25-983e-c699ef7d1a52.json rename to docs/outstanding-issues-inbox/applied/c15dfcea-2b4e-4c25-983e-c699ef7d1a52.json diff --git a/docs/outstanding-issues-inbox/c2c0104b-02e3-4ae3-8f8a-706c421ea4ce.json b/docs/outstanding-issues-inbox/applied/c2c0104b-02e3-4ae3-8f8a-706c421ea4ce.json similarity index 100% rename from docs/outstanding-issues-inbox/c2c0104b-02e3-4ae3-8f8a-706c421ea4ce.json rename to docs/outstanding-issues-inbox/applied/c2c0104b-02e3-4ae3-8f8a-706c421ea4ce.json diff --git a/docs/outstanding-issues-inbox/cafca1f3-5985-4d5e-9a5e-47cb6d5ffafb.json b/docs/outstanding-issues-inbox/applied/cafca1f3-5985-4d5e-9a5e-47cb6d5ffafb.json similarity index 100% rename from docs/outstanding-issues-inbox/cafca1f3-5985-4d5e-9a5e-47cb6d5ffafb.json rename to docs/outstanding-issues-inbox/applied/cafca1f3-5985-4d5e-9a5e-47cb6d5ffafb.json diff --git a/docs/outstanding-issues-inbox/ce7dfda5-ea3b-4cb8-a992-aec759367f9d.json b/docs/outstanding-issues-inbox/applied/ce7dfda5-ea3b-4cb8-a992-aec759367f9d.json similarity index 100% rename from docs/outstanding-issues-inbox/ce7dfda5-ea3b-4cb8-a992-aec759367f9d.json rename to docs/outstanding-issues-inbox/applied/ce7dfda5-ea3b-4cb8-a992-aec759367f9d.json diff --git a/docs/outstanding-issues-inbox/d34ff313-231a-4e65-93d6-806f5b910846.json b/docs/outstanding-issues-inbox/applied/d34ff313-231a-4e65-93d6-806f5b910846.json similarity index 100% rename from docs/outstanding-issues-inbox/d34ff313-231a-4e65-93d6-806f5b910846.json rename to docs/outstanding-issues-inbox/applied/d34ff313-231a-4e65-93d6-806f5b910846.json diff --git a/docs/outstanding-issues-inbox/da9b65c7-7245-4878-8b75-56c4cbe211cb.json b/docs/outstanding-issues-inbox/applied/da9b65c7-7245-4878-8b75-56c4cbe211cb.json similarity index 100% rename from docs/outstanding-issues-inbox/da9b65c7-7245-4878-8b75-56c4cbe211cb.json rename to docs/outstanding-issues-inbox/applied/da9b65c7-7245-4878-8b75-56c4cbe211cb.json diff --git a/docs/outstanding-issues-inbox/dcb1ba9e-18ea-4ae4-9594-8b7cbb6cbbcc.json b/docs/outstanding-issues-inbox/applied/dcb1ba9e-18ea-4ae4-9594-8b7cbb6cbbcc.json similarity index 100% rename from docs/outstanding-issues-inbox/dcb1ba9e-18ea-4ae4-9594-8b7cbb6cbbcc.json rename to docs/outstanding-issues-inbox/applied/dcb1ba9e-18ea-4ae4-9594-8b7cbb6cbbcc.json diff --git a/docs/outstanding-issues-inbox/e1506952-64c6-472f-9da4-812f8d69b483.json b/docs/outstanding-issues-inbox/applied/e1506952-64c6-472f-9da4-812f8d69b483.json similarity index 100% rename from docs/outstanding-issues-inbox/e1506952-64c6-472f-9da4-812f8d69b483.json rename to docs/outstanding-issues-inbox/applied/e1506952-64c6-472f-9da4-812f8d69b483.json diff --git a/docs/outstanding-issues-inbox/e2b81b96-5063-429c-866f-3d42549bd1c8.json b/docs/outstanding-issues-inbox/applied/e2b81b96-5063-429c-866f-3d42549bd1c8.json similarity index 100% rename from docs/outstanding-issues-inbox/e2b81b96-5063-429c-866f-3d42549bd1c8.json rename to docs/outstanding-issues-inbox/applied/e2b81b96-5063-429c-866f-3d42549bd1c8.json diff --git a/docs/outstanding-issues-inbox/e7d125c0-98a4-45db-883e-b19937bd4550.json b/docs/outstanding-issues-inbox/applied/e7d125c0-98a4-45db-883e-b19937bd4550.json similarity index 100% rename from docs/outstanding-issues-inbox/e7d125c0-98a4-45db-883e-b19937bd4550.json rename to docs/outstanding-issues-inbox/applied/e7d125c0-98a4-45db-883e-b19937bd4550.json diff --git a/docs/outstanding-issues-inbox/e7fe0e34-3ff3-48d0-878c-2d7a7fe792f1.json b/docs/outstanding-issues-inbox/applied/e7fe0e34-3ff3-48d0-878c-2d7a7fe792f1.json similarity index 100% rename from docs/outstanding-issues-inbox/e7fe0e34-3ff3-48d0-878c-2d7a7fe792f1.json rename to docs/outstanding-issues-inbox/applied/e7fe0e34-3ff3-48d0-878c-2d7a7fe792f1.json diff --git a/docs/outstanding-issues-inbox/ee6875e6-62ad-4f2e-8644-6f3b7a973d87.json b/docs/outstanding-issues-inbox/applied/ee6875e6-62ad-4f2e-8644-6f3b7a973d87.json similarity index 100% rename from docs/outstanding-issues-inbox/ee6875e6-62ad-4f2e-8644-6f3b7a973d87.json rename to docs/outstanding-issues-inbox/applied/ee6875e6-62ad-4f2e-8644-6f3b7a973d87.json diff --git a/docs/outstanding-issues-inbox/f1673818-e4fe-4733-9db4-312b11d0279c.json b/docs/outstanding-issues-inbox/applied/f1673818-e4fe-4733-9db4-312b11d0279c.json similarity index 100% rename from docs/outstanding-issues-inbox/f1673818-e4fe-4733-9db4-312b11d0279c.json rename to docs/outstanding-issues-inbox/applied/f1673818-e4fe-4733-9db4-312b11d0279c.json diff --git a/docs/outstanding-issues-inbox/f23c14ec-e386-4910-ba60-fd488cc0a83b.json b/docs/outstanding-issues-inbox/applied/f23c14ec-e386-4910-ba60-fd488cc0a83b.json similarity index 100% rename from docs/outstanding-issues-inbox/f23c14ec-e386-4910-ba60-fd488cc0a83b.json rename to docs/outstanding-issues-inbox/applied/f23c14ec-e386-4910-ba60-fd488cc0a83b.json diff --git a/docs/outstanding-issues-inbox/f6953f9c-671e-4bd3-82a5-d5825046cd95.json b/docs/outstanding-issues-inbox/applied/f6953f9c-671e-4bd3-82a5-d5825046cd95.json similarity index 100% rename from docs/outstanding-issues-inbox/f6953f9c-671e-4bd3-82a5-d5825046cd95.json rename to docs/outstanding-issues-inbox/applied/f6953f9c-671e-4bd3-82a5-d5825046cd95.json diff --git a/docs/outstanding-issues-inbox/f6e5fe24-02f4-4a49-beea-2f8bf20e0a0e.json b/docs/outstanding-issues-inbox/applied/f6e5fe24-02f4-4a49-beea-2f8bf20e0a0e.json similarity index 100% rename from docs/outstanding-issues-inbox/f6e5fe24-02f4-4a49-beea-2f8bf20e0a0e.json rename to docs/outstanding-issues-inbox/applied/f6e5fe24-02f4-4a49-beea-2f8bf20e0a0e.json diff --git a/docs/outstanding-issues-inbox/fa725f37-2d49-480e-83bc-23e8bca4c232.json b/docs/outstanding-issues-inbox/applied/fa725f37-2d49-480e-83bc-23e8bca4c232.json similarity index 100% rename from docs/outstanding-issues-inbox/fa725f37-2d49-480e-83bc-23e8bca4c232.json rename to docs/outstanding-issues-inbox/applied/fa725f37-2d49-480e-83bc-23e8bca4c232.json diff --git a/docs/outstanding-issues-inbox/fc44a290-5b8f-4ee4-b233-516774792802.json b/docs/outstanding-issues-inbox/applied/fc44a290-5b8f-4ee4-b233-516774792802.json similarity index 100% rename from docs/outstanding-issues-inbox/fc44a290-5b8f-4ee4-b233-516774792802.json rename to docs/outstanding-issues-inbox/applied/fc44a290-5b8f-4ee4-b233-516774792802.json diff --git a/docs/outstanding-issues.md b/docs/outstanding-issues.md index 60f77cad92..a08da1fee5 100644 --- a/docs/outstanding-issues.md +++ b/docs/outstanding-issues.md @@ -74,67 +74,51 @@ removed after current-main verification; it is not missing recommended work. | 19 | `#027` | Optional | Operator — SRE/provider | When an owned external alert path is wanted | 1–2 hours | Decide vendor/cost/privacy/owner; if accepted, prove one non-PHI outage and recovery alert. Stop when no responder owns it. | | 20 | `#039` | Optional | High — frontend architecture | During a concrete catalogue-toolbar project | 0.5–1 day inventory; 1–3 days code | Converge only repeated toolbar behavior without flattening search semantics. Stop when there is no bounded implementation target. | | 21 | `#079` | Optional | High — repository hygiene | In explicitly scheduled batches | 30–60 minutes per batch | Disposition at most ten retained worktrees per pass using owner, PR, review-ledger, ancestry, and patch evidence. Preserve every dirty, active, secret-bearing, post-freeze, or ambiguous worktree and stop rather than broad-cleaning. | -| 22 | `#086` | A3 | High — repository structure + Specialist | On explicit go-ahead for X3; later packages own their gates | 1 PR per work order | Ship remaining maturity backlog (X3 rag.ts; X7 src/lib reorg; X6 coverage floors; X5 ACL consolidation; L1 one-shot archive; M1 host hardening) as verified draft PRs from `docs/maturity-backlog-workorders.md`. L4 ledger rotation shipped in #1418. Start with X3 after go-ahead; stop before RAG edits without the flag or X5 without live-DB approval. | -| 23 | `#098` | A3 | High — test infrastructure | Before `#099` or `#101`; it is their enabler | 2–4 hours | Generalise the answer-route preamble guard into a counting-proxy round-trip budget harness over the existing offline fixtures. Must enforce admission-before-scope, never the reverse. No providers, no DB. Stop if it would require live credentials. | -| 24 | `#102` | A3 | Operator — Supabase + Specialist | Next approved index window, after the ordering question is settled | 1–2 hours plus apply | Author the migration (operator SQL alone never reaches staging/DR/local replay), then apply → mirror `schema.sql` → regenerate drift manifest → register `required_indexes`. **Stop:** the RAG-path index is canary-gated, and ordering `fetchDocumentTitleAliasRows`'s unordered `.limit(12)` does not lift that — an imposed order can select a different twelve, so it is a second canary-gated change, not a way out of the first. The byte-identical claim was retracted. | -| 25 | `#099` | A3 | Specialist — answer path | After `#098` | Half a day per sub-item | Remaining fixed per-request round trips: the 8 `setCachedSearch` deferrals (abort semantics + mutation window), the anonymous subject+global limiter pair (needs a new atomic RPC first), and proxy→route identity duplication. Stop before hand-authoring locking SQL. | -| 26 | `#162` | A3 | High — frontend/UI | When starting the mode search redesign package | 0.5–1.5 days | Redesign `/tools?q=` as Compact Results Instrument (direction A): query-as-H1, one composer, dense tool rows, demote cross-mode cards, remove success-green filter banner and home hero on results. Comps in `public/mockups/mode-page-redesign-2026-07/tools-search/`. Verify phone+desktop chrome ownership and `verify:phone-chrome` / focused UI. Stop if scope expands into Tools home redesign without an explicit ask. | -| 27 | `#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. | -| 28 | `#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. | -| 29 | `#090` | A3 | High — eslint toolchain | When ESLint 10 plugin peers are compatible | blocked; revisit monthly | Upgrade the eslint ecosystem to clear remaining dev-scoped high advisories — full `npm audit` reports zero high advisories from the eslint toolchain. | -| 30 | `#100` | A3 | Specialist — answer streaming | After offline Phase 0/1 design proof | provider-gated rollout | Buffered answer generation has no incremental verified delivery — [`verified-answer-incremental-delivery-design.md`](verified-answer-incremental-delivery-design.md) records the clinical-governance decision and staged co… | -| 31 | `#150` | Optional | Operator — review tooling | Next CodeRabbit billing/policy decision | 30–60 min decision | CodeRabbit reviewed none of a full day's PRs; spending cap reached — the repo's second automated reviewer is either funded or acknowledged as absent, rather than appearing to review while skipping. | -| 32 | `#152` | A2 | High — worktree hygiene | Next cleanup batch with #079 | 1–2 hours | Uncommitted work sits in worktrees whose branches are already merged — work that exists in no branch and no PR is either committed or knowingly discarded, not lost to a disk reclaim. | -| 33 | `#165` | A2 | High — clinical UI | Next answer-home UX pass | 0.5–1 day | Adopt a consolidated answer-home notice block — the studies exist, nothing adopts them — the answer hero states its safety obligation, its scope, and its verification requirement as one block in one voice. | -| 34 | `#168` | A3 | High — ledger architecture | With #156 / id-scheme redesign | design first | Sequential issue ids force every concurrent append to conflict — two sessions can append to this ledger at the same time without conflicting. | -| 35 | `#169` | A3 | High — git hygiene | Next branch cleanup batch | 1–2 hours | Local branches carry work that exists on no remote — committed work is not lost when a machine or worktree is reclaimed. | -| 36 | `#175` | A2 | Operator — clinical data + Standard | Next therapy catalogue curation window | 2–4 hours | Therapy modality is now null on all 205 records and needs curation or removal — the Therapy detail and recommend screens either show a curated modality or stop carrying the field at all. | -| 37 | `#178` | A3 | High — PR policy | Next pr-policy change | 1–2 hours | pr-policy does not flag operational risk bundled with clinical or UI risk — 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-it… | -| 38 | `#189` | A2 | Specialist — search/RAG budgets | After #098 route residual; before collapsing RPCs | 2–4 hours + canary if behaviour | Pin /api/search route-level round trips and disposition the x3 text RPC probes — a counting-proxy budget drives `POST` `/api/search` (auth/ratelimit/scope/enrichment/telemetry), and the retrieval-core finding that `matc… | -| 39 | `#036` | Optional | Specialist — privacy/schema | When visibility model is redesigned | design + migration | No explicit `is_public` visibility flag on documents — Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the prom… | -| 40 | `#101` | A3 | Specialist — RAG/retrieval | After #098 harness + canary approval | canary-gated | Canary-gated retrieval parallelisation candidates — metadata and memory hydration shipped in PR #1474; visual hydration, scope enumeration, typeahead caching, and universal-search coalescing remain, each behind the RAG flag and live-canary criteria. | -| 41 | `#156` | A3 | High — ledger architecture | With #168 id-scheme work | design first | Outstanding-issues ids are still allocated read-modify-write, and Update-branch corrupts the merge — two branches cannot silently claim the same outstanding-issues id, and no merge path can commit a file where they have. | -| 42 | `#188` | A3 | Operator — DR/SRE | After any schema restore drill, or next DR review | checklist-owned | Document and track disaster-recovery re-creation checklist as ledger work — the five DR items that do not survive a schema restore are tracked with owners and verify steps, not only in `docs/operator-backlog.md`. | -| 43 | `#190` | A3 | Specialist — RAG structure | On explicit X3 go-ahead | 1 PR per extraction unit | X3: Finish rag.ts monolith decomposition — `src/lib/rag/rag.ts` is decomposed into focused modules per `docs/maturity-backlog-workorders.md` X3, with existing offline RAG contracts green. | -| 44 | `#191` | A3 | Operator — DB + Specialist | Approved live-DB window only | provider-gated | X5: ACL-migration consolidation (provider-gated) — ACL-related migrations are consolidated per maturity work-order X5 without weakening owner-scope/RLS. | -| 45 | `#192` | A3 | High — test coverage | Next coverage-floor pass | 0.5–1 day | X6: Raise clinical/retrieval/answer coverage floors — coverage floors for clinical, retrieval, and answer domains meet the maturity X6 targets with CI enforcing them. | -| 46 | `#193` | A3 | High — src/lib structure | After/with X3 non-protected clusters | 1 PR per cluster | X7: Complete the remaining src/lib domain-directory reorg — remaining `src/lib` clusters sit in their domain directories per X7 follow-on to X2. | -| 47 | `#194` | A3 | High — scripts/docs hygiene | Next scripts archive pass | 1–2 hours | L1: Archive retired backfill one-shots and dead ci-change-scope token — retired `backfill:*` one-shots and the dead `ci-change-scope` token are archived/removed with docs/script index updated. | -| 48 | `#195` | A3 | Operator — GitHub maintainer | Maintainer UI window | 30–60 min | M1: Repo-host hardening (branch protection and required checks) — GitHub branch-protection rulesets and required checks match audit §8 / maturity M1. | -| 49 | `#196` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-create pg_cron schedules after schema restore — ingestion/retention and related pg_cron schedules exist on the target DB after any schema restore. | -| 50 | `#197` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-add Vault secrets including cron_ingestion_jwt — required Vault secrets (at least `cron_ingestion_jwt`) are present after schema restore. | -| 51 | `#198` | A3 | Operator — DR/SRE | After schema restore drill | 30–60 min | DR: Re-set custom database GUCs after schema restore — custom `app.*` GUCs required by the app/worker are set on the restored database. | -| 52 | `#199` | A3 | Operator — DR/SRE | After schema restore; Deno v2 available | 1–2 hours | DR: Redeploy Supabase edge functions (Deno v2.x) — required edge functions are deployed to the target project with Deno v2.x. | -| 53 | `#200` | A3 | Operator — DR/SRE | After schema restore drill | 1–2 hours | DR: Re-enter dashboard config after schema restore — auth providers/SSO redirect URLs, connection-pool caps, per-project keys, and `E2E_USER_*` are re-entered in the Supabase/Railway dashboards after restore. | -| 54 | `#183` | A2 | Operator — Sentry + Specialist | Next approved observability window with SENTRY_AUTH_TOKEN | 1–2 hours | Create Sentry metric alert for production DB span p95 > 500ms (`span.op:db`, environment production). **Stop:** no secret printing; blocked until token/env available. | -| 55 | `#206` | A2 | Specialist — answer UI contract | With AnswerState producer work (`#207`) | 2–4 hours | `partial_retrieval` has no app-facing producer — decide RAG contract vs UI-only mapping before AnswerCard. **Stop:** no retrieval behaviour change without RAG flag. | -| 56 | `#209` | A3 | High — design tokens / contrast | Next Gate 1 / warning-token pass | 1–2 hours | Add contrast pair for `--warning` used as body text (VerificationNotice / DoseLine). **Gate:** design-system contrast checks. **Stop:** do not invent a new status token without TOKENS.md. | -| 57 | `#211` | A3 | High — TypeScript strictness | Dedicated migration branch | multi-PR | Plan and start `noUncheckedIndexedAccess` migration (1266 errors); highest-risk files first. **Stop:** do not flip the flag on main without a staged plan. | -| 58 | `#212` | A3 | High — runtime validation | After highest-risk cast inventory | multi-PR | Replace `as unknown as` and unvalidated `JSON.parse` with Zod/guards at trust boundaries. **Stop:** RAG/provider boundaries need clinical/privacy care. | -| 59 | `#213` | A3 | High — error handling | Next fetch/stream hardening pass | 0.5–1 day | Stop swallowing fetch/stream errors with empty catches; check `response.ok`. **Stop:** do not change telemetry contracts silently. | -| 60 | `#215` | Optional | High — image perf | Next image/PWA pass | 2–4 hours | Image-optimization basics for lightbox, PWA lifecycle, demo PNGs. **Stop:** optional until measured need. | -| 61 | `#221` | A3 | High — design-system convergence | After `#218` cn() decision | 0.5–1 day | Converge remaining local EmptyState/LoadingState/Chip duplicates. **Stop:** not piecemeal before cn()/Chip decisions. | -| 62 | `#222` | A3 | High — headers / search chrome | During headers redesign decision | 2–4 hours | Decide whether mode-home-template / search-results-header-band are in PageHeader scope or permanently out. **Stop:** do not flatten phone composer ownership. | -| 63 | `#233` | A3 | High — design-system docs | Next COMPONENTS.md docs PR | 1–2 hours | Refresh section 0 maturity matrix and document FormField optionality-marker contract. **Gate:** docs checks. **Stop:** docs-only; no product behaviour change. | -| 64 | `#234` | A3 | High — design-system docs | With answer-surface docs | 30–60 min | Document `answer-copy-payload.ts` as the clipboard contract for three surfaces. **Stop:** do not add a second copy builder. | -| 65 | `#235` | A3 | High — design-system evidence | Next warmed local proof-shot pass | 1–2 hours | Capture missing ADOPTION.md §7 proof shots for adopted surfaces. **Stop:** not visual-baseline PNGs (`#118`); no Playwright snapshot commit. | -| 66 | `#236` | Optional | High — branch hygiene | Next cleanup batch with `#079` | 30–60 min | Dispose orphan DS V2 builder branches and leftover wave-5 dev servers. **Stop:** content-verify before delete; no force-clean. | -| 67 | `#237` | A3 | High — design-system a11y | Before freezing Linux visual baselines (#242) | 30–60 min | Eyeball low-confidence AccessibleTable densities at 320px; MissingValue phrases must remain readable. **Gate:** visual spot-check only. **Stop:** do not abbreviate MissingValue to a dash. | -| 68 | `#238` | A3 | High — overlays/UI | After Sheet portal default change (#1616) | 30–60 min | Visual pass for Sheet portal default on settings, sidebar, and answer overlays under OverlayRoot. **Stop:** do not revert portal default without evidence. | -| 69 | `#239` | Optional | High — phone chrome | When phone orientation QA is available | 15–30 min | Manual phone rotation check for ResizeObserver-only phone chrome reserve. **Gate:** `verify:phone-chrome` still owns automated coverage. **Stop:** do not widen reserve heuristics without reproduction. | -| 70 | `#240` | Optional | High — design tokens | Next design-owner review | 15–30 min | Confirm tooltip visual hard-clip asymmetry with design owner (sr-only keeps full text). **Stop:** no product change without that confirmation. | -| 71 | `#241` | A3 | High — therapy catalogue | Standing; with any therapy-home change | 15–30 min | Therapy home summary count/slugs remain build-time; keep `build-therapies-index --check` load-bearing. **Stop:** do not bypass the check. | -| 72 | `#242` | A2 | High — design-system baselines | After human review of Linux baselines | 1–2 hours | Commit approved Linux visual baselines and promote adoption not-committed → committed. **Stop:** never commit baselines from an unreviewed machine run. | -| 73 | `#244` | A3 | High — design tokens / forced-colors | With any ckb-v2 forced-colours edit | 15–30 min | Keep grouped dark selectors in the forced-colours media block so specificity matches dark rules. **Stop:** do not trim to a single `.ckb-v2.ckb-v2` selector. | -| 74 | `#245` | A3 | High — cross-mode links | Next CrossModeLinks / analytics pass | 30–60 min | responsive-compact CrossModeLinks keeps duplicate rails in the DOM; prefer one mount or accept test double-counts. **Stop:** do not break phone-only rail contract. | -| 75 | `#248` | A2 | Operator — Supabase + Specialist | After PR #1614 symptom repair; approved live/history window | 1–2 hours | Investigate why 20260705180000 search-health indexes were missing on live despite applied history; decide if drift checks should catch this class. **Stop:** no hosted mutation without approval. | -| 76 | `#250` | A2 | High — multi-agent execution | After Wave 0 queue repair on main (done in this capture); run remaining Wave 0/#202 process gates next on the engineering track | multi-wave | Execute the fastest-wins multi-wave plan (Waves 0–4 + operator track) with parallel agents and per-PR gates. Waves do not outrank A1 acuity. **Stop:** provider/RAG approvals still required where flagged. | -| 77 | `#253` | A2 | High — phone results UI | Next open-PR sweep | 15–30 min | Decide #1606's fate: the `MobileResultFilterControl` it rewrites was deleted by #247, so there is nothing left to hand-merge. Verify keyboard parity of the replacement sheet on a real device, then close #1606 as superseded. **Stop:** the decision is a human's; do not close #1606 automatically. | -| 78 | `#254` | A2 | Operator — Codex Cloud | Before #1617 leaves draft | 1–2 hours | Re-run Codex Cloud acceptance at the exact current head or mark head-independent evidence. **Stop:** do not treat stale pins as coverage. | -| 79 | `#257` | Optional | High — formulation/specifiers flake | Standing until second reproduction | 15–30 min | Single unreproduced ui-formulation flake when run with ui-specifiers — record a second sighting only; do not quarantine until three on the same SHA. **Stop:** do not weaken assertions. | +| 22 | `#098` | A3 | High — test infrastructure | Before `#099` or `#101`; it is their enabler | 2–4 hours | Generalise the answer-route preamble guard into a counting-proxy round-trip budget harness over the existing offline fixtures. Must enforce admission-before-scope, never the reverse. No providers, no DB. Stop if it would require live credentials. | +| 23 | `#102` | A3 | Operator — Supabase + Specialist | Next approved index window, after the ordering question is settled | 1–2 hours plus apply | Author the migration (operator SQL alone never reaches staging/DR/local replay), then apply → mirror `schema.sql` → regenerate drift manifest → register `required_indexes`. **Stop:** the RAG-path index is canary-gated, and ordering `fetchDocumentTitleAliasRows`'s unordered `.limit(12)` does not lift that — an imposed order can select a different twelve, so it is a second canary-gated change, not a way out of the first. The byte-identical claim was retracted. | +| 24 | `#099` | A3 | Specialist — answer path | After `#098` | Half a day per sub-item | Remaining fixed per-request round trips: the 8 `setCachedSearch` deferrals (abort semantics + mutation window), the anonymous subject+global limiter pair (needs a new atomic RPC first), and proxy→route identity duplication. Stop before hand-authoring locking SQL. | +| 25 | `#162` | A3 | High — frontend/UI | When starting the mode search redesign package | 0.5–1.5 days | Redesign `/tools?q=` as Compact Results Instrument (direction A): query-as-H1, one composer, dense tool rows, demote cross-mode cards, remove success-green filter banner and home hero on results. Comps in `public/mockups/mode-page-redesign-2026-07/tools-search/`. Verify phone+desktop chrome ownership and `verify:phone-chrome` / focused UI. Stop if scope expands into Tools home redesign without an explicit ask. | +| 26 | `#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. | +| 27 | `#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. | +| 28 | `#090` | A3 | High — eslint toolchain | When ESLint 10 plugin peers are compatible | blocked; revisit monthly | Upgrade the eslint ecosystem to clear remaining dev-scoped high advisories — full `npm audit` reports zero high advisories from the eslint toolchain. | +| 29 | `#100` | A3 | Specialist — answer streaming | After offline Phase 0/1 design proof | provider-gated rollout | Buffered answer generation has no incremental verified delivery — [`verified-answer-incremental-delivery-design.md`](verified-answer-incremental-delivery-design.md) records the clinical-governance decision and staged co… | +| 30 | `#150` | Optional | Operator — review tooling | Next CodeRabbit billing/policy decision | 30–60 min decision | CodeRabbit reviewed none of a full day's PRs; spending cap reached — the repo's second automated reviewer is either funded or acknowledged as absent, rather than appearing to review while skipping. | +| 31 | `#165` | A2 | High — clinical UI | Next answer-home UX pass | 0.5–1 day | Adopt a consolidated answer-home notice block — the studies exist, nothing adopts them — the answer hero states its safety obligation, its scope, and its verification requirement as one block in one voice. | +| 32 | `#168` | A3 | High — ledger architecture | With #156 / id-scheme redesign | design first | Sequential issue ids force every concurrent append to conflict — two sessions can append to this ledger at the same time without conflicting. | +| 33 | `#169` | A3 | High — git hygiene | Next branch cleanup batch | 1–2 hours | Local branches carry work that exists on no remote — committed work is not lost when a machine or worktree is reclaimed. | +| 34 | `#175` | A2 | Operator — clinical data + Standard | Next therapy catalogue curation window | 2–4 hours | Therapy modality is now null on all 205 records and needs curation or removal — the Therapy detail and recommend screens either show a curated modality or stop carrying the field at all. | +| 35 | `#178` | A3 | High — PR policy | Next pr-policy change | 1–2 hours | pr-policy does not flag operational risk bundled with clinical or UI risk — 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-it… | +| 36 | `#189` | A2 | Specialist — search/RAG budgets | After #098 route residual; before collapsing RPCs | 2–4 hours + canary if behaviour | Pin /api/search route-level round trips and disposition the x3 text RPC probes — a counting-proxy budget drives `POST` `/api/search` (auth/ratelimit/scope/enrichment/telemetry), and the retrieval-core finding that `matc… | +| 37 | `#036` | Optional | Specialist — privacy/schema | When visibility model is redesigned | design + migration | No explicit `is_public` visibility flag on documents — Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the prom… | +| 38 | `#101` | A3 | Specialist — RAG/retrieval | After #098 harness + canary approval | canary-gated | Canary-gated retrieval parallelisation candidates — metadata and memory hydration shipped in PR #1474; visual hydration, scope enumeration, typeahead caching, and universal-search coalescing remain, each behind the RAG flag and live-canary criteria. | +| 39 | `#190` | A3 | Specialist — RAG structure | On explicit X3 go-ahead | 1 PR per extraction unit | X3: Finish rag.ts monolith decomposition — `src/lib/rag/rag.ts` is decomposed into focused modules per `docs/maturity-backlog-workorders.md` X3, with existing offline RAG contracts green. | +| 40 | `#191` | A3 | Operator — DB + Specialist | Approved live-DB window only | provider-gated | X5: ACL-migration consolidation (provider-gated) — ACL-related migrations are consolidated per maturity work-order X5 without weakening owner-scope/RLS. | +| 41 | `#192` | A3 | High — test coverage | Next coverage-floor pass | 0.5–1 day | X6: Raise clinical/retrieval/answer coverage floors — coverage floors for clinical, retrieval, and answer domains meet the maturity X6 targets with CI enforcing them. | +| 42 | `#193` | A3 | High — src/lib structure | After/with X3 non-protected clusters | 1 PR per cluster | X7: Complete the remaining src/lib domain-directory reorg — remaining `src/lib` clusters sit in their domain directories per X7 follow-on to X2. | +| 43 | `#194` | A3 | High — scripts/docs hygiene | Next scripts archive pass | 1–2 hours | L1: Archive retired backfill one-shots and dead ci-change-scope token — retired `backfill:*` one-shots and the dead `ci-change-scope` token are archived/removed with docs/script index updated. | +| 44 | `#195` | A3 | Operator — GitHub maintainer | Maintainer UI window | 30–60 min | M1: Repo-host hardening (branch protection and required checks) — GitHub branch-protection rulesets and required checks match audit §8 / maturity M1. | +| 45 | `#183` | A2 | Operator — Sentry + Specialist | Next approved observability window with SENTRY_AUTH_TOKEN | 1–2 hours | Create Sentry metric alert for production DB span p95 > 500ms (`span.op:db`, environment production). **Stop:** no secret printing; blocked until token/env available. | +| 46 | `#206` | A2 | Specialist — answer UI contract | With AnswerState producer work (`#207`) | 2–4 hours | `partial_retrieval` has no app-facing producer — decide RAG contract vs UI-only mapping before AnswerCard. **Stop:** no retrieval behaviour change without RAG flag. | +| 47 | `#209` | A3 | High — design tokens / contrast | Next Gate 1 / warning-token pass | 1–2 hours | Add contrast pair for `--warning` used as body text (VerificationNotice / DoseLine). **Gate:** design-system contrast checks. **Stop:** do not invent a new status token without TOKENS.md. | +| 48 | `#211` | A3 | High — TypeScript strictness | Dedicated migration branch | multi-PR | Plan and start `noUncheckedIndexedAccess` migration (1266 errors); highest-risk files first. **Stop:** do not flip the flag on main without a staged plan. | +| 49 | `#212` | A3 | High — runtime validation | After highest-risk cast inventory | multi-PR | Replace `as unknown as` and unvalidated `JSON.parse` with Zod/guards at trust boundaries. **Stop:** RAG/provider boundaries need clinical/privacy care. | +| 50 | `#213` | A3 | High — error handling | Next fetch/stream hardening pass | 0.5–1 day | Stop swallowing fetch/stream errors with empty catches; check `response.ok`. **Stop:** do not change telemetry contracts silently. | +| 51 | `#215` | Optional | High — image perf | Next image/PWA pass | 2–4 hours | Image-optimization basics for lightbox, PWA lifecycle, demo PNGs. **Stop:** optional until measured need. | +| 52 | `#221` | A3 | High — design-system convergence | After `#218` cn() decision | 0.5–1 day | Converge remaining local EmptyState/LoadingState/Chip duplicates. **Stop:** not piecemeal before cn()/Chip decisions. | +| 53 | `#222` | A3 | High — headers / search chrome | During headers redesign decision | 2–4 hours | Decide whether mode-home-template / search-results-header-band are in PageHeader scope or permanently out. **Stop:** do not flatten phone composer ownership. | +| 54 | `#233` | A3 | High — design-system docs | Next COMPONENTS.md docs PR | 1–2 hours | Refresh section 0 maturity matrix and document FormField optionality-marker contract. **Gate:** docs checks. **Stop:** docs-only; no product behaviour change. | +| 55 | `#234` | A3 | High — design-system docs | With answer-surface docs | 30–60 min | Document `answer-copy-payload.ts` as the clipboard contract for three surfaces. **Stop:** do not add a second copy builder. | +| 56 | `#235` | A3 | High — design-system evidence | Next warmed local proof-shot pass | 1–2 hours | Capture missing ADOPTION.md §7 proof shots for adopted surfaces. **Stop:** not visual-baseline PNGs (`#118`); no Playwright snapshot commit. | +| 57 | `#237` | A3 | High — design-system a11y | Before freezing Linux visual baselines (#242) | 30–60 min | Eyeball low-confidence AccessibleTable densities at 320px; MissingValue phrases must remain readable. **Gate:** visual spot-check only. **Stop:** do not abbreviate MissingValue to a dash. | +| 58 | `#238` | A3 | High — overlays/UI | After Sheet portal default change (#1616) | 30–60 min | Visual pass for Sheet portal default on settings, sidebar, and answer overlays under OverlayRoot. **Stop:** do not revert portal default without evidence. | +| 59 | `#239` | Optional | High — phone chrome | When phone orientation QA is available | 15–30 min | Manual phone rotation check for ResizeObserver-only phone chrome reserve. **Gate:** `verify:phone-chrome` still owns automated coverage. **Stop:** do not widen reserve heuristics without reproduction. | +| 60 | `#240` | Optional | High — design tokens | Next design-owner review | 15–30 min | Confirm tooltip visual hard-clip asymmetry with design owner (sr-only keeps full text). **Stop:** no product change without that confirmation. | +| 61 | `#242` | A2 | High — design-system baselines | After human review of Linux baselines | 1–2 hours | Commit approved Linux visual baselines and promote adoption not-committed → committed. **Stop:** never commit baselines from an unreviewed machine run. | +| 62 | `#245` | A3 | High — cross-mode links | Next CrossModeLinks / analytics pass | 30–60 min | responsive-compact CrossModeLinks keeps duplicate rails in the DOM; prefer one mount or accept test double-counts. **Stop:** do not break phone-only rail contract. | +| 63 | `#248` | A2 | Operator — Supabase + Specialist | After PR #1614 symptom repair; approved live/history window | 1–2 hours | Investigate why 20260705180000 search-health indexes were missing on live despite applied history; decide if drift checks should catch this class. **Stop:** no hosted mutation without approval. | - + ## Open items > **Merged-main canary update (2026-07-23, run `30018289898`):** the new structured report correctly recorded evaluated tree `c24f2e8f2d30d0c59fc1eba025d3dcd63478137e`, run/attempt identity and `cross-region-runner` latency context. Golden retrieval remained 36/36 with document/content recall 1.0 and no failed cases. The 44-case answer gate had grounded-supported and unsupported-correct rates of 1.0, but failed because `neuroleptic-side-effect-escalation` again returned one citation where two are required (citation-failure rate 0.0227). `admission-discharge-comparison` again omitted the specific AKG admission document after `comparison_source_extractive_fallback`; `admission-discharge-coverage-paraphrase` was advisory-only at 24,870 ms. Answer cost was reported as `$0.234736`. Do not retry immediately: retain this as the first structured datapoint, compare it with the scheduled 2026-07-26 report, and keep retrieval/ranking unchanged. @@ -160,113 +144,104 @@ removed after current-main verification; it is not missing recommended work. | #057 | P2 | task | Complete staging soak and rollback rehearsal | After #056, run the documented soak and rollback against an exact candidate; retain latency/error/rollback evidence. Stop on unsafe data, identity mismatch, or an unowned rollback decision. | `docs/launch-operator-runbook.md`; `docs/audit/capacity-review.md` | 2026-07-24 | | #011 | P3 | task | Auth DB-connection allocation is operator-only | Supabase Auth (GoTrue) is capped at ~10 absolute DB connections (Supabase perf advisor). Switch to **percentage-based** allocation in the Supabase **dashboard** before the first compute scale-up — **not settable via SQL/MCP** (operator-owned). Verify via a staging soak + an approval-gated read-only advisor re-check. | `docs/auth-connection-cap-runbook.md`; `docs/process-hardening.md` (Known follow-up debts) | 2026-07-21 | | #013 | P3 | rec | Route catalogue weight remains measurement-gated; public field INP is unavailable | Keep the payload work open, but correct the measurement state: on 2026-08-13 the official Chrome UX Report current-record API returned 404 no-data for the psychiatry.tools origin and every reviewed URL (root, Therapy, Documents search, DSM, Forms, Services, Specifiers, and Formulation). Field INP is therefore unavailable because the site does not meet CrUX eligibility/coverage, not unverified and not a pass. The production app deliberately has no browser Sentry bundle, so adding RUM would expand the approved telemetry and privacy envelope. Next: continue lab LCP/TBT and interaction traces; request a separate privacy/operator decision before adding browser RUM, or recheck CrUX after traffic eligibility changes. Do not block route payload fixes waiting for a field dataset that does not exist, and do not infer an INP pass from absence. | session 2026-08-13 official CrUX current-record queries; https://cruxvis.withgoogle.com/ | 2026-07-21 | -| #016 | P3 | rec | "Big but not easy" structural + motion perf | Deferred larger levers: (a) nonce-CSP forces every product route to `╞Æ Dynamic` (zero static generation) — evaluate Partial Prerendering / static shells for the static clinical catalogues (DSM/differentials/therapy/specifiers/formulation); (b) sidebar expand/collapse animates `grid-template-columns` (biggest smoothness cost, motion-gated — needs a transform-overlay rethink); (c) Therapy Compass fetches 692 KB / 2.5 MB JSON client-side (defer until interaction + confirm brotli); (d) settings/setup/admin dialogs static-imported into the home chunk (`next/dynamic` them); (e) **DONE 2026-08-01 in PR-T (ds-v2 therapy teardown):** deleted `therapy-compass.css` and removed its route-group layout import — no longer render-blocking on `/`, `/documents`, `/forms`, `/dsm` and every mode home; (f) `shared-search-app-shell.tsx:8` statically imports the `therapy-compass` barrel, pulling `workspace.tsx` + `bindings.tsx` + `nav.tsx` into every `(search-app)` route; (g) three client waterfalls (`use-app-preferences.ts:156-182`, `ClinicalDashboard.tsx:977-1069`, `signed-image.tsx:60-84` + `use-signed-image-url.ts:39`) and the paint offenders in `globals.css` beyond the sidebar grid — three stacked `backdrop-filter` passes on an always-mounted translating element (`:709-748`), `box-shadow` inside a `transition` list (`:677-684`), and `@keyframes shimmer` animating `background-position` on the shared `Skeleton` (`:2289-2296`). **CORRECTED 2026-07-29 on (c):** the Therapy Compass filenames are unversioned and Next serves `/public` with an ETag, so only the FIRST visit pays 690.6 KB / 2,470 KB — repeat visits pay ~4 revalidation round trips. The fix is content-hashed filenames + `immutable` (touching `scripts/build-therapies-index.mjs` and `check:therapy-data-index`), NOT a bare `Cache-Control` line. See `docs/audit/latency-audit-2026-07-28.md` L3-1/L3-2/L3-3/L3-6/L3-7. | session 2026-07-21 (build route table + design audit) | 2026-07-21 | +| #016 | P3 | rec | "Big but not easy" structural + motion perf | **DEPRIORITISED 2026-08-12 (yield review against current main).** A grab-bag of five deferred perf levers, each individually gated on a measurement or a rethink. Split it or leave it parked; as one row it cannot be started. Deferred larger levers: (a) nonce-CSP forces every product route to `╞Æ Dynamic` (zero static generation) — evaluate Partial Prerendering / static shells for the static clinical catalogues (DSM/differentials/therapy/specifiers/formulation); (b) sidebar expand/collapse animates `grid-template-columns` (biggest smoothness cost, motion-gated — needs a transform-overlay rethink); (c) Therapy Compass fetches 692 KB / 2.5 MB JSON client-side (defer until interaction + confirm brotli); (d) settings/setup/admin dialogs static-imported into the home chunk (`next/dynamic` them); (e) **DONE 2026-08-01 in PR-T (ds-v2 therapy teardown):** deleted `therapy-compass.css` and removed its route-group layout import — no longer render-blocking on `/`, `/documents`, `/forms`, `/dsm` and every mode home; (f) `shared-search-app-shell.tsx:8` statically imports the `therapy-compass` barrel, pulling `workspace.tsx` + `bindings.tsx` + `nav.tsx` into every `(search-app)` route; (g) three client waterfalls (`use-app-preferences.ts:156-182`, `ClinicalDashboard.tsx:977-1069`, `signed-image.tsx:60-84` + `use-signed-image-url.ts:39`) and the paint offenders in `globals.css` beyond the sidebar grid — three stacked `backdrop-filter` passes on an always-mounted translating element (`:709-748`), `box-shadow` inside a `transition` list (`:677-684`), and `@keyframes shimmer` animating `background-position` on the shared `Skeleton` (`:2289-2296`). **CORRECTED 2026-07-29 on (c):** the Therapy Compass filenames are unversioned and Next serves `/public` with an ETag, so only the FIRST visit pays 690.6 KB / 2,470 KB — repeat visits pay ~4 revalidation round trips. The fix is content-hashed filenames + `immutable` (touching `scripts/build-therapies-index.mjs` and `check:therapy-data-index`), NOT a bare `Cache-Control` line. See `docs/audit/latency-audit-2026-07-28.md` L3-1/L3-2/L3-3/L3-6/L3-7. | session 2026-07-21 (build route table + design audit) | 2026-07-21 | | #018 | P2 | task | Split the lithium, ADHD and metabolic residuals by mechanism | Current evidence keeps the mechanisms separate. **Lithium — closed within this item:** the row/atom-aware subject guard, foreign-parameter rejection and query-specific range promotion returned `0.5–1.0 mmol/L` with correct targeting/citation; the full retrieval canary remained 36/36 with recall 1.0 and zero per-case RR regressions, and the full answer canary passed every blocking gate. **ADHD — open corpus debt:** `CG.MHSP.ADHD.pdf` is absent from the hosted corpus and the retrieved chart exposes `accessible_table_count=0`; repair corpus/fixture or ingestion evidence rather than weakening extractive budgets. **Metabolic — open structured-evidence debt:** the standalone plural classifier worsened the live answer and was reverted; obtain auditable schedule text/table evidence before another candidate. | targeted live lithium/ADHD/metabolic evidence 2026-07-27; `docs/evidence/rag-reliability-evidence-2026-07-27.md`; refuted approaches | 2026-07-21 | | #022 | P2 | task | Source-governance metadata refresh (operator) | The selected policy is now encoded locally as auditable `third_party_reference_attested` evidence with policy version, reviewer qualification, evidence references and append-only review history. It deliberately preserves `clinical_validation_status=unverified`; malformed, stale or non-BMJ evidence remains review debt. Migration `20260727010000_bmj_third_party_source_attestation.sql` is prepared but was **not applied**. The ten most visible local-document candidates are captured in `docs/evidence/rag-top-local-review-manifest-2026-07-26.json` with `attestation_applied=false`; qualified human review, deliberate hosted apply/attestation, and warning-rate remeasurement remain operator work. | governance worklist; local policy/migration tests; top-ten evidence manifest | 2026-07-21 | | #023 | P2 | task | Complete scheduled browser and labeling disposition | **Partial 2026-07-30:** `release-browser-matrix` no longer depends on `pr-required`, so a blocking scheduled dependency audit cannot skip Firefox/WebKit. Still need one green matrix datapoint + human irrelevant-at-10 disposition. The 2026-07-26 retrieval and answer artifacts are read and compared under resolved #051. Scheduled CI run `30216361999` failed its existing production dependency audit before Firefox/WebKit, while production Chromium passed. After that audit is green, capture one scheduled/manual browser-matrix datapoint; separately record the human decision for the stable irrelevant-at-10 set. #084 now makes each top-10 grade and matched signal reproducible, but it does not substitute for the human disposition. Do not rerun or spend on RAG for this item. | runs `30216191889`/`30216361999`; per-rank diagnostics #084; session 2026-07-27 | 2026-07-21 | | #024 | P2 | issue | WebKit e2e `_rsc` prefetch access-control errors | PR #1205 narrowed catch-all interception and duplicate navigation, but Next 16.2.11 still raises `_rsc` access-control `pageerror`s after document-source fallbacks: `/documents/source?id=&page=2&chunk=safety%20plan` → `/documents/?page=2&chunk=safety+plan`; `/documents/source/evidence?id=not-a-uuid&page=2` → `/documents/search`. The invalid-id failure survived removing every Playwright route; Chromium passed both. **Next:** on a provider-free macOS host, run both URLs in stable Safari and Safari Technology Preview without interception, capture console text plus `_rsc` status/access-control headers, and compare Playwright WebKit with routing on/off. Treat as an app defect only if native Safari reproduces; otherwise return to the harness. Never suppress `pageerror` or change CORS without native evidence. | PRs #1179/#1205; current-main local WebKit evidence; session 2026-07-28 | 2026-07-28 | | #025 | P2 | task | Activate the three webhooks (operator secrets) | Merged (#968/#1100) + deployed but inert — verified live: `POST /api/webhooks/railway` returns `503 webhook_not_configured`; the Supabase document-change trigger exists but lacks both activation inputs. To turn on: (1) Railway ΓåÆ set `RAILWAY_WEBHOOK_SECRET` + add the `?token=…` webhook URL; (2) set `SLACK_WEBHOOK_URL`/`DISCORD_WEBHOOK_URL` in BOTH the Railway **app/server env** and **GitHub repo secrets**; (3) set one matching document-change secret in the Railway app env as `SUPABASE_INGESTION_WEBHOOK_SECRET` and in Supabase Vault as `ingestion_webhook_secret`, then set the per-environment database GUC `app.ingestion_webhook_base_url` to the deployed app origin. Each path fails closed until fully configured, so this is pure ops. See `docs/webhooks.md` for verification and rotation. | sessions 2026-07-22/24; PRs #968/#1100; docs/webhooks.md | 2026-07-22 | -| #027 | P3 | rec | External uptime monitor independent of GitHub/Railway | `live-domain-monitor.yml` runs on GitHub's cron, so it won't run in exactly the outage it should catch (Actions or the deploy itself down). Add an off-platform synthetic monitor (UptimeRobot / Better Stack / Checkly) hitting `/api/health` with a webhook alert. Provider setup, not code. | session 2026-07-22 webhook review | 2026-07-22 | -| #033 | P3 | rec | Source governance metadata absent from the LLM prompt | `buildRagSourceBlock` omits `document_status`, `clinical_validation_status`, and `extraction_quality`, so the model cannot self-caveat during generation and governance is enforced only post-hoc. Generation-surface change: needs `eval:rag` plus `eval:quality --rag-only` (grounded-supported must not drop, citation-failure 0) and explicit approval. Carries the same "unknown Γëá bad" hazard as #032 — on a partially-enriched corpus the model would likely over-caveat correct sources, so design the prompt wording before spending an eval. | `src/lib/rag/rag-source-block.ts:126-198`; PR #1051 audit item 8 | 2026-07-22 | -| #035 | P3 | rec | Threshold-conflict detection covers only 3 params | `detectThresholdDisagreements` checks only ANC, WBC, and platelets paired with withholding verbs, so cross-source conflicts on medication doses, lithium/thyroid levels, or vital signs go undetected. Deliberately narrow (see the comment at `:469-474`). Broadening changes when an answer is classified `conflicting` and adds warnings — real false-positive risk. Needs new fixtures plus a behaviour review before any change. | `src/lib/evidence.ts:469-574`; PR #1051 audit item 7 | 2026-07-22 | -| #036 | P3 | rec | No explicit `is_public` visibility flag on documents | Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the promotion migrations but never used as a retrieval filter. Promotion is unconditional on `clinical_validation_status`, so unverified documents are publicly searchable — compensated by keeping `unverified_source` in the frontend-visible warning set. A hard schema flag touches RLS and the clinical-risk-gated retrieval RPCs; weigh against the existing compensating control before acting. | `supabase/schema.sql:61-108`; `src/lib/search-scope.ts:181-236`; PR #1051 audit item 3 | 2026-07-22 | -| #039 | P3 | rec | Consolidate catalogue toolbar patterns | Catalogue/search pages have independently evolved filter, sort, result-count and mobile toolbar behavior. Inventory the existing implementations and converge only the repeated interaction contract; do not flatten mode-specific search semantics. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | +| #027 | P3 | rec | External uptime monitor independent of GitHub/Railway | **DEPRIORITISED 2026-08-12 (yield review against current main).** Off-platform uptime monitoring for a single-user prototype that is not in clinical use and has no availability commitment to anyone. Revisit when there is a user who would notice an outage. `live-domain-monitor.yml` runs on GitHub's cron, so it won't run in exactly the outage it should catch (Actions or the deploy itself down). Add an off-platform synthetic monitor (UptimeRobot / Better Stack / Checkly) hitting `/api/health` with a webhook alert. Provider setup, not code. | session 2026-07-22 webhook review | 2026-07-22 | +| #033 | P3 | rec | Source governance metadata absent from the LLM prompt | **DEPRIORITISED 2026-08-12 (yield review against current main).** The row's own analysis argues against acting: on a partially-enriched corpus the model would likely over-caveat correct sources, and finding out costs a provider eval. Keep as a finding. `buildRagSourceBlock` omits `document_status`, `clinical_validation_status`, and `extraction_quality`, so the model cannot self-caveat during generation and governance is enforced only post-hoc. Generation-surface change: needs `eval:rag` plus `eval:quality --rag-only` (grounded-supported must not drop, citation-failure 0) and explicit approval. Carries the same "unknown Γëá bad" hazard as #032 — on a partially-enriched corpus the model would likely over-caveat correct sources, so design the prompt wording before spending an eval. | `src/lib/rag/rag-source-block.ts:126-198`; PR #1051 audit item 8 | 2026-07-22 | +| #035 | P3 | rec | Threshold-conflict detection covers only 3 params | **DEPRIORITISED 2026-08-12 (yield review against current main).** Deliberately narrow by design, with a code comment saying so, and broadening it carries real false-positive risk on a clinical warning path. Keep as a finding, not queued work. `detectThresholdDisagreements` checks only ANC, WBC, and platelets paired with withholding verbs, so cross-source conflicts on medication doses, lithium/thyroid levels, or vital signs go undetected. Deliberately narrow (see the comment at `:469-474`). Broadening changes when an answer is classified `conflicting` and adds warnings — real false-positive risk. Needs new fixtures plus a behaviour review before any change. | `src/lib/evidence.ts:469-574`; PR #1051 audit item 7 | 2026-07-22 | +| #036 | P3 | rec | No explicit `is_public` visibility flag on documents | **DEPRIORITISED 2026-08-12 (yield review against current main).** A compensating control already exists (unverified_source stays in the frontend-visible warning set) and the change touches RLS plus the clinical-risk-gated retrieval RPCs. Cost and blast radius exceed the residual risk. Public-corpus visibility is implicit: `owner_id IS NULL` on an `indexed` document (`resolveSearchScope`). The `metadata.public_corpus` marker is written by the promotion migrations but never used as a retrieval filter. Promotion is unconditional on `clinical_validation_status`, so unverified documents are publicly searchable — compensated by keeping `unverified_source` in the frontend-visible warning set. A hard schema flag touches RLS and the clinical-risk-gated retrieval RPCs; weigh against the existing compensating control before acting. | `supabase/schema.sql:61-108`; `src/lib/search-scope.ts:181-236`; PR #1051 audit item 3 | 2026-07-22 | +| #039 | P3 | rec | Consolidate catalogue toolbar patterns | **DEPRIORITISED 2026-08-12 (yield review against current main).** States an intent (converge repeated toolbar behaviour) with no measured defect and no named surfaces. Needs a concrete inventory before it is work. Catalogue/search pages have independently evolved filter, sort, result-count and mobile toolbar behavior. Inventory the existing implementations and converge only the repeated interaction contract; do not flatten mode-specific search semantics. | design audit reconciliation; session 2026-07-22 | 2026-07-22 | | #079 | P3 | task | Disposition retained worktrees in bounded cleanup batches | **Outcome:** the retained reconciliation tail is gradually classified without another disruptive all-worktree sweep. **Next:** after the primary checkout is clean and `npm run check:primary-checkout-lease` allows writes, revalidate and remove the twenty clean redundant candidates recorded on 2026-07-30 with `branch-cleanup-deletion-pending`; then process no more than ten further worktrees per explicitly scheduled pass using current owner/process metadata, open-PR state, exact review-ledger coverage, ancestry, and cherry-pick-aware content proof. **Success:** remove only clean, inactive, bundled worktrees whose content is merged or explicitly rejected; record every disposition and retain recovery evidence. **Stop:** preserve dirty, active, secret-bearing, post-freeze, paused, or ambiguous work and never use reset, force deletion, broad clean, or process killing. | final reconciliation inventory retained 104 independent worktrees; session 2026-07-24; 2026-07-30 bounded review found 20 redundant candidates across two bounded batches but the primary-dirty write lease blocked removal | 2026-07-30 | -| #086 | P3 | task | Repository maturity backlog — remaining structural work | **Outcome:** the deferred repository-maturity backlog ships as verified draft PRs, one per structural change. **Canonical runbook:** [`docs/maturity-backlog-workorders.md`](maturity-backlog-workorders.md). **Split into concrete tasks 2026-07-31:** `#190` X3 rag.ts; `#191` X5 ACL consolidation; `#192` X6 coverage floors; `#193` X7 src/lib reorg; `#194` L1 one-shot archive; `#195` M1 host hardening. Keep this umbrella only as the index; do the work under those ids. **Stop:** RAG/retrieval items need the flag + go-ahead; X5 is live-DB provider-gated. | `docs/maturity-backlog-workorders.md`; audit §8/§10; session 2026-07-28 | 2026-07-28 | -| #090 | P2 | task | Upgrade the eslint ecosystem to clear remaining dev-scoped high advisories | **Outcome:** full `npm audit` reports zero high advisories from the eslint toolchain. **Blocked 2026-07-30:** the stable ecosystem still has no compatible ESLint 10 set. `eslint-config-next@16.2.12` permits ESLint 10 but bundles `eslint-plugin-react@7.37.5`, `eslint-plugin-import@2.32.0`, and `eslint-plugin-jsx-a11y@6.10.2`; each plugin's published peer range still ends at ESLint 9, and the React plugin retains the previously reproduced removed-context-API crash. Keep the Dependabot major hold and ESLint `9.39.5`; do not force an invalid peer graph merely to make the audit report green. **Next:** recheck after those three plugins publish stable ESLint 10 support, then upgrade eslint and the complete plugin/config set together. Residual highs (`@eslint/config-array`, `@eslint/eslintrc`, `eslint`, `eslint-config-next`, `eslint-plugin-import`, `eslint-plugin-jsx-a11y`, plus the advisory's numeric `<=5.0.7` hit on the unused `brace-expansion@1.1.16` / `2.1.2` maintenance lines that still ship an unpatched `main`) cascade from this dev-only toolchain. **Success:** peer-valid install, `npm run lint` clean, `verify:cheap` green, full-audit highs cleared, no rule-config regressions. **Stop:** do not use `npm audit fix --force` or override plugin peer ranges. Production `npm audit --omit=dev` is already clean after the exceljs `archiver@8` / `unzipper@0.12.5` overrides on PR #1314. | stable npm metadata recheck 2026-07-30; session 2026-07-28 brace-expansion triage (PR #1314) | 2026-07-30 | +| #090 | P3 | task | Upgrade the eslint ecosystem to clear remaining dev-scoped high advisories | **DEPRIORITISED 2026-08-12 (yield review against current main).** Blocked upstream on three plugins publishing ESLint 10 support, and the advisories are dev-scoped only with no runtime or user impact. A recheck reminder, not work. **Outcome:** full `npm audit` reports zero high advisories from the eslint toolchain. **Blocked 2026-07-30:** the stable ecosystem still has no compatible ESLint 10 set. `eslint-config-next@16.2.12` permits ESLint 10 but bundles `eslint-plugin-react@7.37.5`, `eslint-plugin-import@2.32.0`, and `eslint-plugin-jsx-a11y@6.10.2`; each plugin's published peer range still ends at ESLint 9, and the React plugin retains the previously reproduced removed-context-API crash. Keep the Dependabot major hold and ESLint `9.39.5`; do not force an invalid peer graph merely to make the audit report green. **Next:** recheck after those three plugins publish stable ESLint 10 support, then upgrade eslint and the complete plugin/config set together. Residual highs (`@eslint/config-array`, `@eslint/eslintrc`, `eslint`, `eslint-config-next`, `eslint-plugin-import`, `eslint-plugin-jsx-a11y`, plus the advisory's numeric `<=5.0.7` hit on the unused `brace-expansion@1.1.16` / `2.1.2` maintenance lines that still ship an unpatched `main`) cascade from this dev-only toolchain. **Success:** peer-valid install, `npm run lint` clean, `verify:cheap` green, full-audit highs cleared, no rule-config regressions. **Stop:** do not use `npm audit fix --force` or override plugin peer ranges. Production `npm audit --omit=dev` is already clean after the exceljs `archiver@8` / `unzipper@0.12.5` overrides on PR #1314. | stable npm metadata recheck 2026-07-30; session 2026-07-28 brace-expansion triage (PR #1314) | 2026-07-30 | | #098 | P2 | task | Offline round-trip budget harness for the hot routes | **Outcome:** per-scenario Supabase round-trip counts are pinned by a test, so an extra round trip on a hot path is a red gate rather than an inference. **Done 2026-07-29:** the measurement gap is closed — `Server-Timing` now covers `auth`/`ratelimit`/`scope` on `/api/answer`, `auth`/`ratelimit`/`search`/`total` on `/api/search`, and `auth`/`ratelimit` on `/api/answer/stream` (previously the route the UI actually calls emitted no header at all). Headers flush before the first SSE frame, so in-stream stages cannot reach a header and must NOT be routed through the governed `progress`/`final` contract. `tests/answer-route-preamble.test.ts` pins admission-before-scope (no scope call while the limiter is pending or after a deny) and the client-disconnect abort signal. **Done 2026-07-30 (PR #1450, `1bff4c78`):** the counting proxy exists and the answer path is budgeted. `tests/helpers/supabase-round-trip-counter.ts` counts on **execution, not construction** — a builder that is never awaited costs zero, one awaited twice costs two — which is the distinction that makes the count mean "requests issued". `tests/rag-round-trip-budget.test.ts` pins two offline answer-path scenarios (a single-source source-only answer, and that trips do not scale with the number of retrieved sources) plus three self-tests of the counter, and is registered in `scripts/fixtures/rag-offline-contract-tests.json` so it runs inside the offline contract rather than only on demand. Verified locally, provider-free: `Test Files 1 passed (1)`, `Tests 5 passed (5)`. Its documented blind spot is worth repeating before anyone cites a budget as total cost: it sees only traffic through the wrapped client, so a trip issued via another client instance, a direct `fetch`, or a provider SDK is invisible to it. **Done 2026-07-30 (search *retrieval core*, not the endpoint):** `tests/search-round-trip-budget.test.ts` pins `searchChunksWithTelemetry` — what `/api/search` calls to retrieve — registered in both the contract fixture and `scripts/rag-offline-contract.mjs`. **Corrected after Codex review on PR #1464:** an earlier version of this row and the test itself claimed to pin `/api/search`. They do not. The route's auth, rate limiting, scope resolution, related-document enrichment and telemetry write are all invisible to this suite, so a round trip added to any of them leaves it green — and the refusal budget below is about *retrieval*, not about an adversarial HTTP request, which still pays the route preamble. **The measured shape is itself the finding:** one search costs **11 round trips** — `rag_aliases` 1, `match_document_chunks_text_v2` **3**, `match_document_table_facts_text_v2` **3**, `get_related_document_metadata_v2` 1, `document_index_quality` 1, `document_images` 2 — so the two text RPCs are each issued three times per search. Pinned by total *and* breakdown, because a refactor swapping one probe for an unrelated query would keep the total at 11 while changing the traffic. Deterministic across three consecutive runs. The refusal budget asserts **zero** Supabase traffic, matching `rag.ts`'s claim that prompt-injection intent is refused before any query issues, and was proven against the broken shape: with a non-refused query it fails on the round-trip assertion (`expected 11 to be +0`), which is why that assertion is ordered ahead of the results assertion. **Note 2026-07-30 (corrected):** a work-branch experiment on `origin/work` (`1f52c704`, reverted in `a0cd00ba`) collapsed both text surfaces offline (budget 11→7) but never reached `main` and was never canaried on those SHAs. Live canaries `30579804611` and `30580564419` ran on unrelated `main` docs commits (`fde68ed4` / `4312a214`) and must not be cited as probe-collapse evidence. Next (b) remains open and still needs a real canary pair before any collapse. **Next:** (a) add the route-level budget this suite does not provide — drive `POST` from `src/app/api/search/route.ts` with counted clients, following the `tests/answer-route-preamble.test.ts` pattern, so a round trip added to the route preamble or post-processing is a red gate; (b) do **not** wholesale-collapse the ×3 sibling lexical variants as the next step — the offline collapse on `origin/work` never landed on `main` with a canary on those SHAs (see Note above), so that avenue is not an approved follow-up; any later latency work must use a materially different approach (for example overlap/parallelism that preserves all three variants) or a real canary pair on the changed tip under the usual RAG gate. (c) `scripts/eval-rag-offline.mjs` and `scripts/test-rag-offline.mjs` remain unwired; the offline contract runner is now the de-facto single home for budgets, so either adopt that explicitly here or wire them. | `docs/audit/latency-audit-2026-07-28.md` measurement plan; `src/lib/server-timing.ts`; `src/lib/answer-stream-contract.ts:18-21` | 2026-07-29 | | #099 | P2 | task | Remove the remaining fixed per-request round trips | **Outcome:** the answer path stops paying avoidable per-request Supabase round trips. **Done 2026-07-29:** shared-cache-hit promotion deferred off the response path with its mid-request staleness guard intact and documented (`rag.ts:3234`, `rag-cache.ts`); scope resolution overlapped with the rate-limit RPC, signal threaded so a client disconnect finally cancels its paginated queries (`answer/route.ts`). **REFUTED on PR #1377 review — do not retry:** the same pass also overlapped scope with the rate-limit RPC and aborted it on deny, claiming the limiter could "deny for free". It cannot. With caller-supplied `filters` or explicit ids, scope passes its zero-query early returns (`search-scope.ts:242,253`) into the paginated `documents` loop at `:269`, and an `AbortSignal` cancels the client request without un-executing a statement Postgres already began — so throttled traffic kept burning database capacity while collecting 429s, against `capacity-review.md:106-113`'s first-soft-failure warning. Scope is behind admission again, pinned by `tests/answer-route-preamble.test.ts`. Re-attempting the overlap requires a non-database admission gate ahead of the durable limiter first. **Remaining:** (a) the 8 `setCachedSearch` awaits — deferring changes `throwIfAborted` semantics and widens a real mutation window because the clone happens after an `await`, so each branch needs discharging individually; (b) batch the anonymous subject+global rate-limit pair, which needs a NEW atomic RPC modelled on `consume_summary_rate_limits_atomic` and cannot be called until the operator applies it — `Promise.all` is the WRONG fix because it consumes the global bucket even when the subject bucket already denied; (c) stop the proxy and route handler resolving identity twice per authenticated request — no in-process memo can do this (different `Request` objects), so the proxy must forward unspoofable verified claims via a header it controls. Cross-references #011: halving auth resolutions eases the ~10-connection Auth cap that `capacity-review.md:106-113` calls the first hard failure. | `docs/audit/latency-audit-2026-07-28.md` L1-1/L1-3/L1-4; `src/lib/api-rate-limit.ts:276-282`; `src/proxy.ts:125` | 2026-07-29 | -| #100 | P2 | rec | Buffered answer generation has no incremental verified delivery | **Design complete; runtime work remains provider-gated.** [`verified-answer-incremental-delivery-design.md`](verified-answer-incremental-delivery-design.md) records the clinical-governance decision and staged contract: keep the `progress`/`final`/`error` allowlist; disclose bounded, owner-scoped evidence only after the canonical danger-level source-governance refusal permits it, then emit complete answer sections only after each reuses the full production verification boundary; reconcile every preview byte-for-byte with the authoritative `final`; discard all previews on error/cancel/retry; deploy behind separate parse/emission/render flags. Phase 0 contract proof and Phase 1 evidence preview can be developed offline, but visible rollout still needs clinical/browser proof. Phase 2 changes generation architecture and requires explicit approval for answer-quality evals plus a baseline/post live canary pair. **Naive token streaming remains REFUTED:** never re-land `token`, `revising`, provisional prose, or a weaker stream-only verifier. Cross-references #021. | `docs/verified-answer-incremental-delivery-design.md`; `docs/audit/latency-audit-2026-07-28.md` L0-1; `src/lib/answer-stream-contract.ts:18-21` | 2026-07-30 | -| #101 | P3 | rec | Canary-gated retrieval parallelisation candidates | **Outcome:** remaining retrieval parallelisation candidates are explicit after PR #1474 shipped the metadata and memory parallelisation. **Remaining:** visual hydration triples (each migrated path still calls `attachPageVisualEvidence` after hydration — not yet parallelised, see `rag.ts:1442,1811,1857,1959,2194,2281`); nested `await`-in-loop scope enumeration (`search-scope.ts:202,328`); uncached typeahead results (`rag.ts:2698-2711`); and universal-search coalescing (`/api/search` has it; `/api/search/universal` does not). Each changes candidate assembly, truncation, or what the next keystroke returns, so each requires the #098 harness, the RAG flag, explicit live-canary approval, 36/36 retrieval, recall 1.0, and zero per-case reciprocal-rank regressions. Distinct from #001 (semantic rerank). **Completed:** PR #1474 parallelised metadata and memory (`hydrateCandidatesWithMetadataAndMemory`); do not propose that specific change again. **Stop:** no remaining candidate proceeds without its canary gate. | `docs/audit/latency-audit-2026-07-28.md` L2-1/L2-2/L2-8/L1-5; PR #1474 | 2026-07-29 | +| #100 | P2 | rec | Buffered answer generation has no incremental verified delivery | UPDATE 2026-08-13 (PR #1909): Phase 0 offline contract proof and flag-gated Phase 1 server emission implemented (RAG_INCREMENTAL_EVIDENCE_PREVIEW, default false). Remaining: client parsing/rendering phase behind its own flag + verify:ui, then the design's provider-backed acceptance gates before production enablement; Phase 2 stays provider-gated. **Design complete; runtime work remains provider-gated.** [`verified-answer-incremental-delivery-design.md`](verified-answer-incremental-delivery-design.md) records the clinical-governance decision and staged contract: keep the `progress`/`final`/`error` allowlist; disclose bounded, owner-scoped evidence only after the canonical danger-level source-governance refusal permits it, then emit complete answer sections only after each reuses the full production verification boundary; reconcile every preview byte-for-byte with the authoritative `final`; discard all previews on error/cancel/retry; deploy behind separate parse/emission/render flags. Phase 0 contract proof and Phase 1 evidence preview can be developed offline, but visible rollout still needs clinical/browser proof. Phase 2 changes generation architecture and requires explicit approval for answer-quality evals plus a baseline/post live canary pair. **Naive token streaming remains REFUTED:** never re-land `token`, `revising`, provisional prose, or a weaker stream-only verifier. Cross-references #021. | `docs/verified-answer-incremental-delivery-design.md`; `docs/audit/latency-audit-2026-07-28.md` L0-1; `src/lib/answer-stream-contract.ts:18-21` | 2026-07-30 | +| #101 | P3 | rec | Canary-gated retrieval parallelisation candidates | **DEPRIORITISED 2026-08-12 (yield review against current main).** Every candidate needs the #098 harness, the RAG flag, live-canary approval, 36/36 retrieval and recall 1.0 — provider spend and clinical-surface risk for latency nobody is currently waiting on. **Outcome:** remaining retrieval parallelisation candidates are explicit after PR #1474 shipped the metadata and memory parallelisation. **Remaining:** visual hydration triples (each migrated path still calls `attachPageVisualEvidence` after hydration — not yet parallelised, see `rag.ts:1442,1811,1857,1959,2194,2281`); nested `await`-in-loop scope enumeration (`search-scope.ts:202,328`); uncached typeahead results (`rag.ts:2698-2711`); and universal-search coalescing (`/api/search` has it; `/api/search/universal` does not). Each changes candidate assembly, truncation, or what the next keystroke returns, so each requires the #098 harness, the RAG flag, explicit live-canary approval, 36/36 retrieval, recall 1.0, and zero per-case reciprocal-rank regressions. Distinct from #001 (semantic rerank). **Completed:** PR #1474 parallelised metadata and memory (`hydrateCandidatesWithMetadataAndMemory`); do not propose that specific change again. **Stop:** no remaining candidate proceeds without its canary gate. | `docs/audit/latency-audit-2026-07-28.md` L2-1/L2-2/L2-8/L1-5; PR #1474 | 2026-07-29 | | #102 | P3 | task | Apply the additive `documents` index debt (operator) | **Outcome:** bare-column `ILIKE` and the paged status scan on `documents` are index-served on hosted. `documents_title_trgm_idx` indexes a CONCATENATED expression, so the bare-column predicates in `api/documents/route.ts:193` and `rag-candidate-sources.ts:477` (RAG path) cannot use it and fall back to scanning; `search-scope.ts:271-277` sorts per page against the single-column `documents_status_idx`. **Runbook prepared 2026-07-29 — NOT applied, item stays open:** three `CREATE INDEX CONCURRENTLY` statements authored and reviewed in `docs/operator-apply-performance-latency-remediation.md` — additive, though **the "recall is byte-identical" claim was RETRACTED on 2026-07-29 review**: `fetchDocumentTitleAliasRows` (`rag-candidate-sources.ts:482`) applies `.limit(12)` with no `ORDER BY`, so a new index can change which title-alias documents feed candidate assembly. Only the documents-list use stays ordering-safe; `(status,id)` is canary-gated too — see runbook, and making that `.limit(12)` deterministic first does **not** lift the gate — an unordered `LIMIT` has no stable selection to preserve, so imposing an order can pick a different twelve and is itself an ordering behaviour change on a retrieval surface, which AGENTS.md requires a canary pair for. Sequencing the ordering fix first is worthwhile (unordered `LIMIT` on a retrieval input is latent nondeterminism regardless) but yields two canary-gated changes, not one (PR #1377 review). **Deliberately NO migration file:** an additive-index migration without a synchronized `schema.sql` mirror and regenerated drift manifest is exactly what closed PR #1312, and the mirror cannot come first because `required_indexes` in `search_schema_health()` (`schema.sql:3178`) runs against live. **Next (operator):** **author the migration first** — `supabase/migrations/` is the source of truth and `schema.sql` only a mirror, so hand-run operator SQL never reaches staging, disaster-recovery replay, or a local `supabase db reset`, and a `required_indexes` registration would fail there (PR #1377 review); follow the `20260717170000_registry_projection_cleanup.sql` idempotent pattern. **That migration must also carry the health-function change** — `required_indexes` lives inside `search_schema_health()`, which is redefined by `create or replace function` in eleven migrations (copy `20260705180000_reconcile_search_health_indexes.sql:62`); editing `schema.sql:3177` alone moves only the mirror and leaves the indexes unmonitored on hosted (PR #1377 review). Then apply concurrently, confirm `indisvalid`, mirror both the index statements and the identical function body into `schema.sql`, run `npm run drift:manifest` (Docker), and deploy the migration LAST — in that order, in one change. Expect `check:drift` to report them as unexpected between steps 1 and 2. **Rollback is three deployed phases, not the reverse of one:** retract `required_indexes` via its own `create or replace function` migration and deploy → drop concurrently live → only then deploy the `schema.sql` removal plus an idempotent forward `drop index if exists` migration, because Supabase wraps migrations in a transaction and a plain `DROP INDEX` there takes the lock the concurrent procedure exists to avoid (PR #1377 review). | `docs/audit/latency-audit-2026-07-28.md` L2-3/L2-5; `docs/operator-apply-performance-latency-remediation.md` | 2026-07-29 | -| #117 | P2 | rec | All live mobile routes breach LCP; shared render-blocking CSS and font are the current bottleneck | PR #1915 is merged and deployed at exact Railway SHA ca788d41e1e6b64dc6b9bc63609074d92470d7e7. Three-sample live medians on that SHA are Documents 3611 ms, DSM 3600 ms, Forms 3715 ms, root 3948 ms, Therapy 3543 ms, and Services 3791 ms; desktop LCP is 584-691 ms and CLS is within the mobile rule. Trace attribution shows LCP equals FCP, TTFB is only 267-316 ms, and the shared render-blocking stylesheet plus preloaded 28 KB Geist font dominate cold mobile paint. The follow-up branch removes mockup-only Tailwind vocabulary from production CSS (367,050 to 302,113 raw bytes; 55,140 to 46,152 gzip) while retaining a complete route-only mockup sheet. It keeps the display-swap body font preloaded because two hosted Lighthouse runs showed that removing it delayed text LCP, while the non-LCP mono font remains on-demand. Next: merge/deploy that exact SHA, rerun the complete live matrix, then attribute remaining shared JS/hydration cost if any route still exceeds 2500 ms. Therapy field safety review remains required for search/pathways. INP remains unverified because Lighthouse does not measure it and no usable CrUX result exists. Stop: do not strip clinical fields, weaken the Lighthouse budget, or claim an INP pass. | PR #1915; live Web Vitals runs 31704500966 and 31704504389; codex/performance-css-delivery | 2026-07-30 | +| #117 | P2 | rec | All live mobile routes breach LCP; shared render-blocking CSS and font are the current bottleneck | **Outcome:** `/therapy-compass` mobile LCP lands near the other mobile routes instead of double them. **Measured 2026-07-30** by the new pre-merge Lighthouse budget: mobile LCP 5229 ms, TBT 612 ms, CLS 0.142, against 2123-2460 ms on every other mobile route and 826 ms on desktop — so it is client-side work under mobile CPU/network throttling, not server latency. **Cause before this PR:** `useTherapyData` fetched `/therapy-compass-data/therapies-index.json` (the stable public alias served by a Next rewrite to the thin browse index; 205 records) for the home/search/pathways screens, so the download plus JSON parse sat on the critical path before content painted. **Current split:** home now fetches `public/therapy-compass-data/therapies-home.211dab554c4ec62d.json` (136,288 bytes raw), pathways use the thin browse index, and search loads the full prose corpus (#1471). 90% of the index weight is long-form clinical prose — indications 159 KB (26%), contraindicationsOrCautions 139 KB (23%), bestUsedFor 73 KB (12%), clinicalSummary 67 KB (11%), patientPopulation 59 KB (10%), targetSymptoms 48 KB (8%) — while name, slug, category, tags and setting together are 54 KB (7%). **Remaining decision for search/pathways: rendered on the card, matched by search, or neither.** `therapy-card.tsx` references five of those prose fields and the same index feeds the search screen, so stripping fields could silently change clinical display or search recall. **Next:** settle that per-field question, then either pre-truncate prose that only feeds card display, or move search matching server-side / load prose on first keystroke. **Gate:** `check:therapy-data-index` plus the therapy Playwright journeys; re-measure with `npm run verify:lighthouse`. **Stop:** do not drop a field from the catalogue payload without confirming no card renders it and no search path matches on it. Same class as #013 (route-chunk / catalogue JSON weight), different route and now measured. | PR #1915; live Web Vitals runs 31704500966 and 31704504389; codex/performance-css-delivery | 2026-07-30 | | #118 | P2 | task | Adopt the remaining visual baselines; Lighthouse now gates regressions | Lighthouse half resolved in PR #1915: authorized CI refresh run 31697669596 on current main produced all 10 route/strategy cells with one pinned HeadlessChrome/151 identity. The reviewed artifact was committed, lighthouse-budget.json enforce is true, the job no longer uses continue-on-error, merge_group coverage is restored, and pr-required now fails on a selected Lighthouse failure. The 2026-08-08 and 2026-08-13 complete baselines stayed within tolerance; the latter puts mobile LCP at 2357-2388 ms and Therapy is no longer an outlier. This relative local-production gate does not close #117 deployed-origin LCP work. Remaining #118 scope: adopt the CI-generated Linux visual snapshots and promote visual-baseline only after design-owner review and stable reruns. Stop: never use developer-machine snapshots or let a workflow update its own gate. | PR #1915; CI run 31697669596 artifact lighthouse-baseline-refresh-31697669596 | 2026-07-30 | | #150 | P2 | issue | CodeRabbit reviewed none of a full day's PRs; spending cap reached | IN FLIGHT 2026-08-12 in PR #1836 (finalize tooling follow-through notes). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. **Outcome:** the repo's second automated reviewer is either funded or acknowledged as absent, rather than appearing to review while skipping. **Evidence 2026-07-30:** CodeRabbit posted "Review limit reached … Your organization has reached its usage spending cap" on **every** PR opened that day — #1404, #1430, #1444, #1445, #1479 — reviewing none of them. Each notice renders as an ordinary bot comment, so a skimming reader sees reviewer activity where there was no review. The Codex connector was the sole substantive reviewer across those PRs and found three real defects that had survived local gates and self-review: a proxy-variable inference in #1430, an `unset` vs `unspecified` git-attribute conflation in #1444, and an earlier P1 recursive-delete on an unvalidated `--dir`. **Next:** decide whether to raise the cap, switch to label-based opt-in so the budget lands on PRs that need it, or accept single-reviewer coverage explicitly. **ESCALATED 2026-07-30 — both reviewers are now capped, so this row's premise no longer holds.** The analysis above rests on the Codex connector being the surviving reviewer. On PR #1505 the Codex connector posted "You have reached your Codex usage limits for code reviews" while CodeRabbit posted its own spending-cap notice on the same PR. **That PR therefore received zero automated review**, and so will anything opened while both caps hold. This is not a second issue — it is the same one, with the fallback removed. **Why it is worth more than a status note:** on 2026-07-30 the single Codex finding on PR #1459 was correct and changed the outcome — it showed that a claimed `LoadingPanel` verification had matched `ModeHomePageSkeleton` instead, which caused `#105` to be closed on wrong evidence. Local gates did not catch it and neither did self-review; the review did. A window in which neither reviewer runs is a window in which that class of error lands. **Next:** the three options above now need deciding rather than deferring, because "accept single-reviewer coverage" is no longer one of them. Until then, treat any PR merged during a cap window as locally-gated only. **Stop:** do not read a CodeRabbit *or* Codex comment as a completed review without checking it is not a usage-limit notice — during this window both bots posted comments on every PR while reviewing none of them. | PRs #1404/#1430/#1444/#1445/#1479; `.coderabbit.yaml` | 2026-07-30 | -| #152 | P2 | issue | Uncommitted work sits in worktrees whose branches are already merged | **Outcome:** work that exists in no branch and no PR is either committed or knowingly discarded, not lost to a disk reclaim. **Inventory 2026-07-30**, all from worktrees whose pre-snapshot branch tips were fully merged into `origin/main`, so the uncommitted changes existed nowhere else: `codex/reconcile-immediate-20260730` — 21 files, +395/-200 across 19 tracked, including `.github/workflows/ci.yml`, `package.json` and `docs/scripts-index.md`; `codex/document-results-mockup-20260730` — 8 files (+13/-3 tracked) plus an untracked `document-search-results/page.tsx` under `src/app/mockups/` (named without a full path here because it does not resolve in this repo); `codex/chat-ledger-triage-d344` — `docs/outstanding-issues.md` +59/-61; `claude/section-spy-browser-coverage` — `tests/ui-smoke.spec.ts` +51. A fifth (`claude/frosty-mayer-2c6167`) self-resolved to clean during the session. **Preserved 2026-07-30, not reviewed:** each was committed on its own branch as an unpushed `wip: preserve uncommitted work before worktree cleanup` snapshot, so the work now survives a worktree reclaim — `codex/reconcile-immediate-20260730` `748ef018f`, `codex/document-results-mockup-20260730` `5dbd9f965`, `codex/chat-ledger-triage-d344` `b7eae51a4`, `claude/section-spy-browser-coverage` `d949859c3`. All four worktrees are now clean. None is pushed and none is reviewed or verified; the snapshots exist to stop silent loss, not to endorse the content. First captured in PR #1490, which was closed unmerged; re-landed on `main` by PR #1508. **Next:** per snapshot, review and either promote it to a real branch/PR or `git reset --hard HEAD~1` to drop it. Do not bulk-delete worktrees without this check — it is why only one of the two "safe" candidates was removed in the 2026-07-30 cleanup. NOT VERIFIABLE FROM A CLOUD SESSION (checked 2026-08-12): this row describes machine-local git or process state. Claude Code on the web runs in a container cloned fresh at session start, so it reports 1 worktree, 0 upstream-less branches and no running dev servers no matter what is true on the machine that raised this. Do NOT close this row from a cloud session — that reading is an artifact of the container, not evidence. Verify from the originating machine. | session 2026-07-30 worktree cleanup; PR #1490 (closed); PR #1508 | 2026-07-30 | -| #156 | P3 | issue | Outstanding-issues ids are still allocated read-modify-write, and Update-branch corrupts the merge | **Outcome:** two branches cannot silently claim the same outstanding-issues id, and no merge path can commit a file where they have. **Detail:** Residual of archived `#112` (gate landed; underlying read-modify-write race explicitly left open) plus post-`#133` evidence. `#133` fixed the two causes of *conflict frequency* — `#1444` removed `merge=union` and `#1479` excluded the ledger from Prettier so a maximum-width row stops re-padding the whole table. Neither touches **id allocation**, which is still read-modify-write against the `issues:next-id` marker, so two branches open at the same time still pick the same number. Measured on PR #1451 (2026-07-30): one P3 row was renumbered `#135` -> `#141` -> `#145` -> `#147` -> `#149` across four sync cycles, because `main` had taken each id in turn — every renumber was manual. This capture itself demonstrates the same hazard: first written as `#151` on PR #1506, then `#154` / `#155` after earlier main syncs, then `#156` here because `main` had already taken `#154` and `#155` for unrelated rows. Both renumbers happened *after* `#133` was closed, which is the point: the fixes in `#1444` and `#1479` were real, but they were fixes to conflict frequency, and allocation was never the same problem. The sharper finding is the resolution path: the GitHub **Update branch** button pushed a sync to that PR head (`df3f3aeed`) whose auto-merge produced **two rows numbered `#141` and two `next-id` markers**, leaving the marker at `142` — below `main`'s highest id, so the next allocation would have reused a live number. `git merge` reported success; only `npm run check:outstanding-issues` caught it. That guard runs in `verify:cheap` and `static-pr`, so such a head cannot merge — but the corruption is produced by a one-click path that runs no guard, and the cost lands on whoever notices. A second session on the same branch later dropped an entire appended evidence block while resolving this file, which the guard cannot detect at all: it validates ids and structure, not whether a merge kept both sides' prose. **Next:** cheapest first — document that Update branch must not be used on PRs touching this file (prefer `npm run sync:pr-branches:apply`, which the repo already prefers for other reasons), then consider allocating ids from a source that cannot collide (per-row files, or a date-plus-slug id) so concurrent branches never contend. **Stop:** do not reintroduce a merge driver here — `#133` settled that; this is about allocation and about merges that silently drop rows, not about the driver. Do not reopen archived `#112` — keep the gate outcome there and track the residual race here. | PR #1451 sync cycles; `df3f3aeed`; archived `#112`; session 2026-07-30; PR #1506 | 2026-07-31 | | #162 | P2 | task | Redesign Tools search results state (Compact Results Instrument) | IN FLIGHT 2026-08-12 in PR #1839 (three runnable directions for the Tools search results state). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. **Outcome:** `/tools?q=` is a committed results page: query-as-H1, one composer, dense tool rows; cross-mode demoted; no home hero / green filter banner / dual H1. **Product pick:** direction A from comps in `public/mockups/mode-page-redesign-2026-07/tools-search/`. **Next:** implement A on production Tools search; verify desktop+phone chrome ownership. **Stop:** do not redesign Tools home in the same PR unless asked. Renumbered after `main` took `#161` for mockup hover-token residue. | session 2026-07-31 mode-page design audit | 2026-07-31 | | #163 | P2 | task | Redesign Services search results (Progressive Referral Workflow) | **Outcome:** `/services?q=` uses query-as-H1 (not match-count), progressive shortlist/compare, no always-on decision panel or giant step rail. **Product pick:** direction B from comps in `public/mockups/mode-page-redesign-2026-07/services-search/`. **Next:** implement B; keep referral shortlist behaviour. **Stop:** do not change Services ModeHome in the same PR. | session 2026-07-31 mode-page design audit | 2026-07-31 | | #164 | P2 | task | Redesign Favourites as hybrid dashboard + search (no ModeHome) | **Outcome:** `/favourites` is one dashboard+search workspace; empty query shows Continue/recent/sets/table; typed query filters in place; no ModeHome hero. **Product pick:** Search-Led Workspace (direction B) from comps in `public/mockups/mode-page-redesign-2026-07/favourites-hybrid/`. User rejected ModeHome for Favourites. **Next:** implement B; retire command-library marketing H1 and redundant dual search. **Stop:** do not reintroduce ModeHome or a separate Favourites home route. | session 2026-07-31 mode-page design audit; user Favourites hybrid decision | 2026-07-31 | | #165 | P2 | task | Adopt a consolidated answer-home notice block — the studies exist, nothing adopts them | **Outcome:** the answer hero states its safety obligation, its scope, and its verification requirement as one block in one voice. **Detail:** `/mockups/warning-consolidation` (PR #1437) diagnoses today's three stacked notices — the APP-5 privacy warning at 11px muted, a bare `/privacy` link, and an accent-blue `ShieldCheck` capability claim at 14px semibold — and shows the hierarchy is inverted: the least important line is the loudest, and two shields with opposite meanings sit ~40px apart. Three consolidations are drawn at 1440px and 390px. Recommended: **02 Safety card** on the hero (obligation on a warning-tinted top row, everything descriptive in one grey voice below) and **01 Assurance bar** on the docked composer — the same content model at two densities, so one component with a `density` prop covers both. **This is a governance change, not just a design one:** `PrivacyInputNotice` is the single site-wide APP-5 line and renders on the answer, documents and calculators composers, so all three move together; `tests/privacy-ui.test.ts`, `tests/ui-accessibility.spec.ts` and the phone-chrome reserve coverage all assert against the current markup and must change in the same commit; and the PR will need a full `## Clinical Governance Preflight` (the mockup PR correctly did not). **Third study (before/after):** `/mockups/answer-home-proposal` draws the concrete D-direction proposal as a full hero before/after rather than an isolated notice. **Second study (words only):** `/mockups/warning-line` answers a narrower brief — no icon, border, tint or background, one line where width allows. Six variants A-F; line counts measured from the rendered DOM, not asserted. Only B (middot clauses), D (obligation + verify) and F (compressed obligation) hold one line at desktop width, and **none fit one line on a 390px phone while the pinned APP-5 sentence stays verbatim** — 46 characters of obligation plus the 27-character link exceeds the ~60 available at 11px. Recommended there: **D**, the only compliant variant that is both one line and keeps weight-only hierarchy, reached by dropping the scope claim (a capability statement already visible on the answer itself). F fits best but rewrites the pinned obligation to \|No patient-identifiable information.\| and so needs the same privacy sign-off as `#166` plus a matching `tests/privacy-ui.test.ts` update. **Status:** PR #1437 was closed unmerged on 2026-07-30 as a deliberate pause during an owner-authorized ordered merge sweep, to be reopened at its queued place; branch `claude/warning-consolidation-mockups-09jyj7` is preserved and merged onto current `main`; these follow-up rows have been renumbered on each sync because `main` kept claiming the next ids while the PR was paused; the superseded numbers are deliberately not listed, since they now belong to unrelated rows. **Next:** decide block (02 + 01) versus line (D) direction, get wording sign-off for `#166`, then implement behind one component and run `verify:phone-chrome` before `verify:ui`. | session 2026-07-30; PR #1437; `/mockups/warning-consolidation`; `/mockups/warning-line` | 2026-07-30 | | #168 | P2 | rec | Sequential issue ids force every concurrent append to conflict | **Outcome:** two sessions can append to this ledger at the same time without conflicting. **Detail:** ids are allocated read-modify-write against the `issues:next-id` marker inside the file being edited, so two branches both read N and both write N. Because duplicate ids are unacceptable, a union merge driver is unsafe — .gitattributes says so explicitly — which is why this file deliberately has no driver and every overlapping append conflicts by hand. Manual resolution is where rows get dropped: PR #1490 was closed during one and took the only record of four snapshots with it (#152), and ids were renumbered under in-flight work three times in one session (#154, #155). The new writer (`scripts/outstanding-issues.mjs`) removes the mechanical errors but explicitly not this one. **Next:** replace the counter with a collision-free id (ULID, timestamp+suffix, or a content hash), keeping a short display number derived at render time if `#151` reads better than 01JQ…; then a union driver becomes safe to reinstate and concurrent appends stop conflicting at all. A larger variant is one row per file under `docs/issues` with the table generated, which the repo already does for `site-map.md`. **Stop:** do not reinstate `merge=union` while ids are sequential — that combination was tried in PR #1416 and removed for duplicating rows and the marker. Renumbered from this PR's original `#159` because `main` already used `#159` for the duplicated test-file-list finding. | session 2026-07-31; .gitattributes; #154/#155; PR #1524 sync | 2026-07-31 | -| #169 | P2 | issue | Local branches carry work that exists on no remote | **Outcome:** committed work is not lost when a machine or worktree is reclaimed. **Detail 2026-07-31:** six `claude/*` branches in this checkout have commits and no `origin/` counterpart. Verified real for `claude/clinical-kb-design-system-333a69` — 57 files / +4069, tip `feat(design-system): v2 token layer, 26 components, browser-crash fix` dated 2026-07-31 17:40, whose added `.design-sync/previews/*.tsx` files are absent from main. Others unverified: `design-sync-db0a54`, `fable-implementation-fc937c`, `frosty-mayer-2c6167`, `issues-133-evidence`. **How to check, because the obvious measure lies:** `git rev-list --count origin/main..` and a three-dot diff both report landed work as unmerged, since this repo squash-merges and the original commits never become ancestors — my own merged branch reported 1 commit and +476 by that measure. Test instead whether files the branch adds exist on main (`git ls-tree origin/main `). **Next:** per branch, push it for review or confirm it is superseded and delete it; do not bulk-delete on the commit count. Sibling of #152, which covers uncommitted work in worktrees rather than unpushed commits on branches. NOT VERIFIABLE FROM A CLOUD SESSION (checked 2026-08-12): this row describes machine-local git or process state. Claude Code on the web runs in a container cloned fresh at session start, so it reports 1 worktree, 0 upstream-less branches and no running dev servers no matter what is true on the machine that raised this. Do NOT close this row from a cloud session — that reading is an artifact of the container, not evidence. Verify from the originating machine. | session 2026-07-31; local branch audit | 2026-07-31 | +| #169 | P2 | issue | Machine-local branches, snapshots, worktrees, and dev servers remain at risk | **CONSOLIDATED 2026-08-13 from #152, #236, and #260 before those source rows are archived by PR #1920. Outcome:** every branch, snapshot, worktree, or process that exists on only one machine remains recoverable and receives an explicit owner disposition before machine or worktree cleanup. **Original unpushed branches:** `claude/clinical-kb-design-system-333a69` was verified to contain 57 files / +4069 at tip `feat(design-system): v2 token layer, 26 components, browser-crash fix`, including `.design-sync/previews/*.tsx` absent from main. Also inspect `design-sync-db0a54`, `fable-implementation-fc937c`, `frosty-mayer-2c6167`, and `issues-133-evidence`. **Preserved WIP snapshots from #152, all unpushed, unreviewed, and unverified:** `codex/reconcile-immediate-20260730` at `748ef018f` (21 files, +395/-200 across 19 tracked, including `.github/workflows/ci.yml`, `package.json`, and `docs/scripts-index.md`); `codex/document-results-mockup-20260730` at `5dbd9f965` (8 tracked files, +13/-3, plus an untracked `document-search-results/page.tsx` mockup); `codex/chat-ledger-triage-d344` at `b7eae51a4` (`docs/outstanding-issues.md` +59/-61); and `claude/section-spy-browser-coverage` at `d949859c3` (`tests/ui-smoke.spec.ts` +51). **Wave-5 inventory from #236:** content-compare `claude/ds-v2-builder-a` and `claude/ds-v2-builder-b` with current `origin/main` because squash merges make ancestry checks unreliable; retain the associated process evidence for ports 3258 (`Database-wt-ds-v2-capture`), 3135 (`Database-wt-ds-v2-correctness`), and 3672 (`Database-wt-ds-v2-empty-state-heading`) until the owner confirms each process is no longer needed. **Stranded Sentry work from #260:** on the originating Windows machine, inspect branch `claude/cloud-pr-loop-prevention-bc052b` commits `c3c9d6a31` and `abbcdc8e9` (~389 lines across `src/sentry.*.config.ts`, `src/lib/env.ts`, `src/lib/supabase/client.tsx`, and `src/components/ui-primitives.tsx`) plus the same four uncommitted files in `.claude/worktrees/pensive-borg-6be2f0`; content-compare them with remote branches `claude/sentry-nextjs-sdk-setup-2v24q5` and `cursor/sentry-nextjs-sdk-7cee`, then record whether the work is unique, remotely preserved, or proven superseded. **Verification rule:** do not use `git rev-list` counts, three-dot diff, or ancestry alone to declare squash-merged work represented; verify the branch-added files or content against current main. **Cloud-session stop:** fresh cloud containers cannot observe the originating machine's local branches, worktrees, or processes, so never close this row from a cloud inventory that reports them absent. **Next:** complete and record each disposition from the originating machine. **Stop:** retain every listed branch, snapshot, worktree, and process record until content proof and owner disposition exist. | sessions 2026-07-30/31 and 2026-08-04/07; #152/#169/#236/#260; PR #1920 review | 2026-07-31 | | #175 | P2 | task | Therapy modality is now null on all 205 records and needs curation or removal | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: src/data/therapies-source.json holds 205 records and 0 carry a modality value, exactly as described. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. **Outcome:** the Therapy detail and recommend screens either show a curated modality or stop carrying the field at all. **Detail:** the source catalogue derived `modality` from each record's own tag list — all 205 records had one, every value was also present in that record's `tags`, and the whole catalogue collapsed to CBT/ACT/DBT. It mislabelled the treatments it could not describe: ECT and rTMS as "ACT", Psychoanalysis and Psychodynamic Psychotherapy as "CBT", MBT and TFP as "DBT". Pre-existing on main, surfaced by the PR #1489 review. The generator emits it only when the source curates a value that is not already a tag, which today means null for 205/205 on the index projections *and* the full catalogue the detail/recommend screens load (`catalogue: "full"`), so the two chips (`detail-screen.tsx:49`, `recommend-screen.tsx:115`) never render and `select.ts:117` contributes no same-modality point. Removal was provably search-neutral: `src/lib/therapies.ts` scores with boolean `haystack.includes(token)`, not term frequency, and every modality value was already contributed by `tags.join(" ")` in the same haystack. **Next:** one of two — curate real modality values in `src/data/therapies-source.json` (clinical work, needs the psychiatrist), or drop the field from `types.ts`, `src/lib/therapies.ts`, the two chips and `select.ts`. **Stop:** do not reinstate the tag-derived value to make the chips reappear; a guess rendered as curated fact is the defect. `tests/therapy-compass-pathways.test.ts` pins the echo invariant on both the index and the full catalogue asset. Renumbered from this PR's original `#169` because `main` claimed `#169`–`#174` while the branch was open. | 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 | IN FLIGHT 2026-08-12 in PR #1837 (harden verification & PR policy guards). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. **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 | -| #183 | P2 | task | Create Sentry metric alert for production DB span p95 > 500ms | Still blocked 2026-08-01 closeout: SUPABASE_ACCESS_TOKEN and SENTRY_AUTH_TOKEN missing from session env; Sentry MCP OAuth can list/get alerts but has no create tool; browser hits login wall; no metric rules exist yet on clinibase-xz. Create Metric Alert: p95(span.duration), filter span.op:db, environment production, threshold >500ms, notify Active Members. Provide SENTRY_AUTH_TOKEN in session to finish via sentry alert metrics create. | session 2026-07-31 db-query-perf follow-up | 2026-07-31 | -| #188 | P3 | task | Document and track disaster-recovery re-creation checklist as ledger work | **Outcome:** the five DR items that do not survive a schema restore are tracked with owners and verify steps. **Split into concrete tasks 2026-07-31:** `#196` pg_cron; `#197` Vault secrets; `#198` custom GUCs; `#199` edge functions; `#200` dashboard config. Keep this umbrella as the index; execute the child tasks. Distinct from `#056` (staging schema) and resolved `#054` (secrets reconciliation). **Stop:** do not treat a schema restore as complete until children are green. | docs/operator-backlog.md Disaster-recovery re-creation; session 2026-07-31 | 2026-07-31 | +| #183 | P3 | task | Create Sentry metric alert for production DB span p95 > 500ms | **DEPRIORITISED 2026-08-12 (yield review against current main).** A production DB p95 latency alert for a system with one user; the alert has nobody to wake. Revisit alongside #027 when real usage exists. Still blocked 2026-08-01 closeout: SUPABASE_ACCESS_TOKEN and SENTRY_AUTH_TOKEN missing from session env; Sentry MCP OAuth can list/get alerts but has no create tool; browser hits login wall; no metric rules exist yet on clinibase-xz. Create Metric Alert: p95(span.duration), filter span.op:db, environment production, threshold >500ms, notify Active Members. Provide SENTRY_AUTH_TOKEN in session to finish via sentry alert metrics create. | session 2026-07-31 db-query-perf follow-up | 2026-07-31 | | #189 | P2 | task | Pin /api/search route-level round trips and disposition the x3 text RPC probes | **Outcome:** a counting-proxy budget drives `POST` `/api/search` (auth/ratelimit/scope/enrichment/telemetry), and the retrieval-core finding that `match_document_chunks_text_v2` and `match_document_table_facts_text_v2` each issue three times per search is either documented as intentional or collapsed under the RAG canary gate. **Source:** residual next actions on `#098` after answer-path and retrieval-core budgets landed. **Next:** (a) route-level budget following `tests/answer-route-preamble.test.ts`; (b) decide probe vs collapse — behaviour change needs RAG flag + canary. **Stop:** do not change retrieval assembly without approval. | session 2026-07-31; #098 residual; tests/search-round-trip-budget.test.ts | 2026-07-31 | -| #190 | P3 | task | X3: Finish rag.ts monolith decomposition | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: src/lib/rag/rag.ts measures 4,362 lines — still the monolith this row describes; the decomposition has not started. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. **Outcome:** `src/lib/rag/rag.ts` is decomposed into focused modules per `docs/maturity-backlog-workorders.md` X3, with existing offline RAG contracts green. **Status:** IN PROGRESS (DocumentViewer/Dashboard extractions done; rag.ts remains). **Next:** continue safe extractions only with the RAG flag before editing protected surfaces; one verified draft PR per unit. **Stop:** no behaviour change without canary when retrieval/answer paths move. | docs/maturity-backlog-workorders.md X3; #086 | 2026-07-31 | +| #190 | P3 | task | X3: Finish rag.ts monolith decomposition | **DEPRIORITISED 2026-08-12 (yield review against current main).** Structural churn on the most safety-critical and most protected file in the repo, with no user-facing benefit and real behaviour-drift risk on a live-validated clinical answer path. Do the extractions opportunistically when a feature change already requires being inside a region, not as a standalone project. VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: src/lib/rag/rag.ts measures 4,362 lines — still the monolith this row describes; the decomposition has not started. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. **Outcome:** `src/lib/rag/rag.ts` is decomposed into focused modules per `docs/maturity-backlog-workorders.md` X3, with existing offline RAG contracts green. **Status:** IN PROGRESS (DocumentViewer/Dashboard extractions done; rag.ts remains). **Next:** continue safe extractions only with the RAG flag before editing protected surfaces; one verified draft PR per unit. **Stop:** no behaviour change without canary when retrieval/answer paths move. | docs/maturity-backlog-workorders.md X3; #086 | 2026-07-31 | | #191 | P3 | task | X5: ACL-migration consolidation (provider-gated) | **Outcome:** ACL-related migrations are consolidated per maturity work-order X5 without weakening owner-scope/RLS. **Next:** DB-owner approved window only; live-DB provider confirmation required before apply. **Stop:** no hosted apply from an agent session without explicit approval. | docs/maturity-backlog-workorders.md X5; #086 | 2026-07-31 | | #192 | P3 | task | X6: Raise clinical/retrieval/answer coverage floors | **Outcome:** coverage floors for clinical, retrieval, and answer domains meet the maturity X6 targets with CI enforcing them. **Next:** set floors from current honest baselines; expand tests only where gaps are real. **Stop:** do not lower floors to pass. | docs/maturity-backlog-workorders.md X6; #086 | 2026-07-31 | -| #193 | P3 | task | X7: Complete the remaining src/lib domain-directory reorg | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: Six domain directories exist under src/lib (extractors, observability, rag, supabase, validation, webhooks); the reorg is genuinely partial, as the row says. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. **Outcome:** remaining `src/lib` clusters sit in their domain directories per X7 follow-on to X2. **Next:** move non-protected clusters first; answer/retrieval clusters need the RAG flag. **Stop:** no drive-by behaviour edits inside moves. | docs/maturity-backlog-workorders.md X7; #086 | 2026-07-31 | +| #193 | P3 | task | X7: Complete the remaining src/lib domain-directory reorg | **DEPRIORITISED 2026-08-12 (yield review against current main).** Mechanical directory moves with import-graph risk and no user-facing benefit. Same reasoning as #190: fold into work already touching the files. VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: Six domain directories exist under src/lib (extractors, observability, rag, supabase, validation, webhooks); the reorg is genuinely partial, as the row says. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. **Outcome:** remaining `src/lib` clusters sit in their domain directories per X7 follow-on to X2. **Next:** move non-protected clusters first; answer/retrieval clusters need the RAG flag. **Stop:** no drive-by behaviour edits inside moves. | docs/maturity-backlog-workorders.md X7; #086 | 2026-07-31 | | #194 | P3 | task | L1: Archive retired backfill one-shots and dead ci-change-scope token | VERIFIED CORRECT 2026-08-12 — re-checked against merged main and left open: Still open: five backfill one-shots remain under scripts/ (backfill-document-covers.mjs, backfill-document-tags.ts, backfill-enrichment.ts, backfill-gold-document-labels.ts, backfill-smart-index.ts). No dead ci-change-scope token was found, so that half may already be gone — confirm before archiving the row. **Outcome:** retired `backfill:*` one-shots and the dead `ci-change-scope` token are archived/removed with docs/script index updated. **Status:** IN PROGRESS (#1033 archived m13/july8; backfills still open). **Next:** finish backfill archive + token cleanup in a docs/scripts PR. **Stop:** do not break CI classifiers. | docs/maturity-backlog-workorders.md L1; #086 | 2026-07-31 | | #195 | P3 | task | M1: Repo-host hardening (branch protection and required checks) | **Outcome:** GitHub branch-protection rulesets and required checks match audit §8 / maturity M1. **Next:** maintainer GitHub UI work; not a repo-file change. Record evidence in the ledger when done. **Stop:** agents must not weaken required checks. | docs/maturity-backlog-workorders.md M1; #086 | 2026-07-31 | -| #196 | P3 | task | DR: Re-create pg_cron schedules after schema restore | **Outcome:** ingestion/retention and related pg_cron schedules exist on the target DB after any schema restore. **Next:** follow `docs/operator-backlog.md` Disaster-recovery checklist; verify schedules are active. Parent umbrella `#188`. **Stop:** do not skip after a restore. | docs/operator-backlog.md; #188 | 2026-07-31 | -| #197 | P3 | task | DR: Re-add Vault secrets including cron_ingestion_jwt | **Outcome:** required Vault secrets (at least `cron_ingestion_jwt`) are present after schema restore. **Next:** operator Vault write + names-only verify. Parent `#188`. **Stop:** never print secret values into git/chat. | docs/operator-backlog.md; #188 | 2026-07-31 | -| #198 | P3 | task | DR: Re-set custom database GUCs after schema restore | **Outcome:** custom `app.*` GUCs required by the app/worker are set on the restored database. **Next:** apply from the operator runbook; verify with a read-only show/settings check. Parent `#188`. | docs/operator-backlog.md; #188 | 2026-07-31 | -| #199 | P3 | task | DR: Redeploy Supabase edge functions (Deno v2.x) | **Outcome:** required edge functions are deployed to the target project with Deno v2.x. **Next:** operator deploy after restore; confirm function list/health. Parent `#188`. **Stop:** needs Deno toolchain and explicit approval for hosted deploy. | docs/operator-backlog.md; #188 | 2026-07-31 | -| #200 | P3 | task | DR: Re-enter dashboard config after schema restore | **Outcome:** auth providers/SSO redirect URLs, connection-pool caps, per-project keys, and `E2E_USER_*` are re-entered in the Supabase/Railway dashboards after restore. **Next:** operator checklist in `docs/operator-backlog.md`. Parent `#188`. **Stop:** do not commit dashboard secrets. | docs/operator-backlog.md; #188 | 2026-07-31 | | #206 | P2 | task | AnswerState partial_retrieval has no app-facing producer | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: `partial_retrieval` is declared in src/lib/answer-state-types.ts:63 and handled in answer-clipboard.ts:75, but nothing in src/app or the retrieval path produces it — still no app-facing producer, as the row says. Do not synthesise it from candidate counts. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. PR-E step 0 found nothing in the client payload names which expected sources were unavailable (retrievalDiagnostics = candidate counts; conflictsOrGaps = prose). RetrievalStateBanner supports the state but PR-J adoption can only emit ready/stale_evidence/source_only. Next action: decide whether a separate RAG contract PR should add a named missing-source signal (governance preflight + RAG impact line + offline eval); until then do not synthesise the state from counts. Pinned by tests/answer-state-contract.test.ts and SPEC 13 / COMPONENTS 2. | PR-E step 0, session 2026-08-02 | 2026-08-02 | | #209 | P3 | task | DS V2 Gate 1: add contrast pair for --warning used as body text | IN FLIGHT 2026-08-12 in PR #1841 (adds an explicit --warning body-text contrast assertion in tests/design-token-contract.test.ts). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. VerificationNotice's caution variant and DoseLine's overdue label use --warning at text tier — the only place a status hue is used as body-text colour rather than a --text-* token. Gate 1's contrast checking must add that pair explicitly rather than assuming the text tiers cover it. Also note: the logged-once Sets in missing-value, date-display, verification-notice, answer-state and retrieval-state-banner are module-level, so on the server they are per-process and unbounded; a persistent data defect logs once at boot then is swallowed. Acceptable while unregistered. | clinical-governance-reviewer P3 findings on PR 6; recorded in docs/design-system/SPEC.md PR 6 clinical review note | 2026-08-02 | -| #210 | P2 | task | npm run ensure generates .next/dev types that break typecheck and every Playwright build | RETITLED AND RE-SCOPED 2026-08-12 — the original title 'Restore the npm run typecheck gate' is wrong and cost this row its clarity: the gate was never missing. `npm run typecheck` IS in `verify:cheap:internal` (package.json:71) and runs today. The live defect is the generated-types include: tsconfig.json:28 still lists `.next/dev/types/**/*.ts` in `include`, so running `npm run ensure` — which the repo's own docs tell you to do before any browser work — makes the dev server write .next/dev/types/validator.ts, and that file then breaks BOTH repo-wide `npm run typecheck` AND every Playwright production build, because scripts/run-playwright.mjs writes an isolated tsconfig extending the root one ('Type error: Cannot find name __IsExpected'). `rm -rf .next/dev` restores both. Next: stop including dev-generated types in the checked project (drop `.next/dev/types/**/*.ts` from include, or give the Playwright isolated tsconfig its own include list), then confirm typecheck stays clean after `npm run ensure`. Stop: do not 'fix' this by removing typecheck from the gate — the gate is not the problem. | session 2026-08-02 /ledger sweep; docs/review-findings-2026-08-02.md | 2026-08-02 | -| #211 | P2 | task | Plan and start the noUncheckedIndexedAccess migration | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: `noUncheckedIndexedAccess` is absent from tsconfig.json — the migration has not begun. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. Enable noUncheckedIndexedAccess in a branch and remediate the 1,266 errors, starting with the 15-20 highest-risk source files. Hot spots include worker/main.ts:901-942, src/lib/rag/rag-extractive-answer.ts, and src/lib/answer-verification.ts. Prefer ?. or ?? guards, or non-null assertions only where invariants are provable. Re-run npm run test and npm run typecheck before merge. See docs/review-findings-2026-08-02.md section 6. | session 2026-08-02 /ledger sweep — docs/review-findings-2026-08-02.md | 2026-08-02 | -| #212 | P2 | task | Replace as unknown as casts and unvalidated JSON.parse with Zod or runtime guards | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: 40 `as unknown as` casts remain under src/ — the row's population is intact. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. 48 as unknown as casts and ~24 unvalidated JSON.parse calls across src/ trust Supabase, OpenAI, localStorage, file metadata and extraction boundaries. Start with src/lib/rag/rag.ts and src/app/api/* routes, mirroring existing Zod use in src/lib/validation/body.ts and src/lib/extractors/document.ts. See docs/review-findings-2026-08-02.md sections 2.2, 2.3 and 8. | session 2026-08-02 /ledger sweep — docs/review-findings-2026-08-02.md | 2026-08-02 | +| #210 | P2 | task | npm run ensure generates .next/dev types that break typecheck and every Playwright build | RE-SCOPED AGAIN 2026-08-13 (re-filed: the 2026-08-12 correction was lost when PR #1880 landed under the inbox architecture without a request being written for it). Half of this row is already fixed and its prescribed fix is REFUTED — do not apply the first suggestion. (1) FIXED: `npm run typecheck` runs `tsconfig.typecheck.json` (added in 450690f citing this row), which sets its own include and excludes `.next/**`; verified green with `.next/dev/types/validator.ts` present. (2) REFUTED: dropping `.next/dev/types/**/*.ts` from tsconfig.json does NOT hold. Next 16 emits that glob itself — `getTypeDefinitionGlobPatterns` (node_modules/next/dist/lib/typescript/type-paths.js) adds both `.next/types` and `.next/dev/types` deliberately 'to avoid tsconfig churn when switching between dev/build modes', and `writeConfigurationDefaults` adds a missing glob back when Next reads the root config directly. Deleting the line only re-creates an uncommitted change. (3) STILL OPEN, narrower than originally written: `scripts/run-playwright.mjs` writes an isolated tsconfig with `extends: '../../tsconfig.json'` and no include of its own, so it inherits the repo-root globs. The recorded `tsc --showConfig` probe resolved `../../.next/dev/types/**/*.ts`, and `--listFilesOnly` pulled in the root dev types including validator.ts. Next's API checker filters dev types with `getDevTypesPath`, but the default `experimental.useTypeScriptCli: true` path uses `runTypeCheckCli` to invoke `tsc --project` against the child config, so it honours the inherited include verbatim. Next: give the isolated tsconfig its own include/exclude (its run root is `.next-playwright/`, not under `.next/`, so excluding the repo-root `.next` keeps the run's own dist types). NOT PROVEN end-to-end: the failing Playwright build was not reproduced. Correcting the previous explanation, `next build` does not mutate this child config: Next 16.3 `writeConfigurationDefaults` returns immediately when the parsed config contains `extends` or `references`, and this config always contains `extends`. Confirm the remaining inherited-include hypothesis with one focused `verify:ui` build before and after the child include/exclude change, and hash the child tsconfig immediately before and after the build to prove it remains byte-identical. Stop: do not remove typecheck from the gate, and do not retry the include deletion. | session 2026-08-02 /ledger sweep; docs/review-findings-2026-08-02.md | 2026-08-02 | +| #211 | P3 | task | Plan and start the noUncheckedIndexedAccess migration | **DEPRIORITISED 2026-08-12 (yield review against current main).** 1,266 sites, each a local judgment, and no open ledger row traces a defect to unchecked indexed access. Real hardening, but speculative against this repo's measured failure history, and the diff conflicts with every open PR. Do it in scoped batches after the clinical and CI-trust work. VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: `noUncheckedIndexedAccess` is absent from tsconfig.json — the migration has not begun. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. Enable noUncheckedIndexedAccess in a branch and remediate the 1,266 errors, starting with the 15-20 highest-risk source files. Hot spots include worker/main.ts:901-942, src/lib/rag/rag-extractive-answer.ts, and src/lib/answer-verification.ts. Prefer ?. or ?? guards, or non-null assertions only where invariants are provable. Re-run npm run test and npm run typecheck before merge. See docs/review-findings-2026-08-02.md section 6. | session 2026-08-02 /ledger sweep — docs/review-findings-2026-08-02.md | 2026-08-02 | +| #212 | P3 | task | Replace as unknown as casts and unvalidated JSON.parse with Zod or runtime guards | **DEPRIORITISED 2026-08-12 (yield review against current main).** 40 casts at trust boundaries. Same reasoning as #211: worth doing, no measured defect traces to it, and it competes with clinical work for review attention. VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: 40 `as unknown as` casts remain under src/ — the row's population is intact. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. 48 as unknown as casts and ~24 unvalidated JSON.parse calls across src/ trust Supabase, OpenAI, localStorage, file metadata and extraction boundaries. Start with src/lib/rag/rag.ts and src/app/api/* routes, mirroring existing Zod use in src/lib/validation/body.ts and src/lib/extractors/document.ts. See docs/review-findings-2026-08-02.md sections 2.2, 2.3 and 8. | session 2026-08-02 /ledger sweep — docs/review-findings-2026-08-02.md | 2026-08-02 | | #213 | P2 | task | Stop swallowing fetch and stream errors with empty catch handlers | SCOPE RE-MEASURED 2026-08-12 on merged main: only **3** empty catch handlers remain under src/ (`catch {}` / `catch (e) {}`), down from the audit population this row was opened against. The principle is unchanged and the remaining three still need dispositioning — each should either handle, log through the observability path, or carry a comment saying why swallowing is correct — but this is now a small, closeable job rather than a sweep. Companion rows measured in the same pass for sequencing: #212 has 40 `as unknown as` casts left, #211's `noUncheckedIndexedAccess` is still absent from tsconfig.json. Do the three catches first; it is the cheapest of the three and no longer blocked behind the other two. | session 2026-08-02 /ledger sweep — docs/review-findings-2026-08-02.md | 2026-08-02 | -| #215 | P3 | task | Add image-optimization basics for lightbox, PWA lifecycle and demo PNGs | PARTIALLY DELIVERED, RE-MEASURED 2026-08-12. One of the three items is done: src/components/clinical-dashboard/image-lightbox.tsx now carries `decoding="async"` (note the path — the row's 'image-lightbox.tsx' is under clinical-dashboard/, and a search at the repo root finds nothing, which reads misleadingly as 'already fixed'). Still outstanding: src/components/pwa-lifecycle.tsx has NO `decoding="async"`; public/demo-documents/*.png are still PNG-only with no WebP/AVIF alternative; and SignedImage still has no `priority` prop for above-fold evidence images. Next: those three, in that order — the pwa-lifecycle attribute is a one-line change. Related image/perf work stays tracked under #016, #013, #117 and #147. | session 2026-08-02 /ledger sweep — docs/audit/performance-image-cwv-audit-2026-08-02.md | 2026-08-02 | +| #215 | P3 | task | Add image-optimization basics for lightbox, PWA lifecycle and demo PNGs | **Outcome:** two of the four image-only findings from the 2026-08-02 audit are shipped; two remain open for an explicit implementation-or-drop decision. **RESTATED 2026-08-13 after inspection against main 2d270392 — two of the four items already shipped and the row no longer describes them as open.** DONE: src/components/clinical-dashboard/image-lightbox.tsx carries decoding="async" (Phase 0, PR #1660), asserted by tests/signed-image.dom.test.tsx. DONE: SignedImage has the priority prop for above-fold evidence — it also skips the IntersectionObserver deferral entirely — and document viewer Phase 3 (PR #1772) added the other half of that pair: an explicit fetchPriority of high when priority is set and low otherwise, so a deferred rail figure does not contend with the page's own above-the-fold work. The document rail additionally passes a 240px observer root margin against the shared 640px default. REMAINING, both confirmed by inspection rather than inferred: (a) src/components/pwa-lifecycle.tsx still has no decoding attribute; (b) public/demo-documents/ still contains no .webp — the PNGs are ~80 KB each and served as-is, so the conversion with a PNG fallback has not been done. **Next:** apply decoding=async in pwa-lifecycle.tsx, and either convert the demo PNGs to WebP with a PNG fallback or record that an ~80 KB synthetic demo asset is not worth the build step. **Stop:** do not treat this row as covering the broader performance findings — those live under #016, #013, #117 and #147. | session 2026-08-02 /ledger sweep — docs/audit/performance-image-cwv-audit-2026-08-02.md | 2026-08-02 | | #221 | P3 | task | Local EmptyState, LoadingState and Chip duplicates still unconverged after PR-J | IN FLIGHT 2026-08-12 in PR #1841 and #1842 (cn()/LinkAction contracts and the EmptyState/Chip convergence). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. PR-J converged what it could inside its allowlists and left four known duplicates, each blocked for a stated reason rather than missed. therapy-compass/ui.tsx defines its own LoadingState AND its own EmptyState used across nine screens (whole-module job, not a one-call-site conversion). mode-home-template.tsx ModeHomeStatusNotice is an EmptyState duplicate that four catalogue homes delegate to, which is why those four files show no diff. differentials-home.tsx has a local two-density Chip blocked by the cn() tailwind-merge gap. favourites-command-library-page.tsx SmallChip is driven by an eight-entry type-token map that Chip's five-tone vocabulary cannot express. Next action: take these as one convergence PR after the cn() decision lands, not piecemeal. Found during PR-J adoption, 2026-08-03. | session 2026-08-03 (PR-J Wave 5, Builder B) | 2026-08-02 | | #222 | P3 | task | Headers surface only partially converged in PR-J: mode-home-template and search-results-header-band untouched | VERIFIED CORRECT 2026-08-12 — re-checked against merged main and left open: Still unconverged: src/components/mode-home-template.tsx defines ModeHomeStatusNotice locally (:232) and imports neither PageHeader nor the DS EmptyState; search-results-header-band.tsx is likewise untouched. Note the adjacency — in-flight PR #1842 delegates ModeHomeStatusNotice to the DS EmptyState under #221, which is a different conversion from the PageHeader question this row asks. Re-check after #1842 merges. Builder A converged DsmPageHeader, InformationPageHeader and InformationPageBreadcrumbs onto PageHeader plus Breadcrumb, and declined two files with reasons. mode-home-template.tsx ModeHomeHero is a centred display hero on the fluid text-hero token and is the slot the in-flow phone composer sits in, so converging it onto a left-aligned PageHeader is a redesign of 13 mode homes that collides with the one-composer-per-page contract. search-results-header-band.tsx is a results spine carrying status, counts and filters, not a page-title stack, so its pin tests/search-results-header-band.dom.test.tsx remains unflipped. Both are defensible; both leave the headers surface partially adopted. Next action: decide whether either is in scope at all, or record them as permanently out of the PageHeader vocabulary. Found during PR-J adoption, 2026-08-03. | session 2026-08-03 (PR-J Wave 5, Builder A) | 2026-08-02 | | #231 | P1 | issue | Generation fallbacks no longer stick in answer cache; lithium generation quality still falls back safely | PARTIAL 2026-08-12: This PR fixes the clinically consequential stale-fallback path: every answer whose routing or degraded reason contains generation_fallback is excluded from rag_response_cache. Offline evidence: 96 focused answer-route tests and 574 RAG fixture/contract tests passed. Approved live baseline/final canaries preserved 36/36 document and content recall at 1.0 with zero per-case reciprocal-rank regressions; the final 44-case answer gate had zero citation or numeric-grounding failures. A budget extension was tested and rejected: four cache-bypassed 'Lithium dosing?' probes remained grounded, cited safe extractive fallbacks at 35-40 second candidate budgets; the decisive 40-second probe completed generation in 25.272 seconds and 27.237 seconds total with route_deadline_exceeded=false, but failed generation quality. Therefore OPENAI_ANSWER_TIMEOUT_MS and the route budget are not the current residual binding cause. Next: instrument and reproduce the structured generation-quality failure using provider-safe metadata, then make a separate bounded output-quality fix with an offline fixture and live canary. Stop: do not increase route/provider timeouts or cache any generation fallback. | session 2026-08-04 (production triage, live /api/search + /api/answer) | 2026-08-04 | | #233 | P3 | task | COMPONENTS.md section 0 describes the pre-adoption world, and the optionality-marker contract change is undocumented | IN FLIGHT 2026-08-12 in PR #1842 (records DS adoption evidence and convergence state). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. Two documentation debts left by PR-J, both in docs/design-system/COMPONENTS.md, naturally one PR. First: section 0's maturity matrix is stale. FormField, TextField, SearchField, Select, Checkbox, RadioGroup, PageHeader and Breadcrumb now have real product mounts, so 0.1 and 0.2 misdescribe what is registered versus built-but-unregistered, and 0.4's field-shell defects are closed by the five-control fold. A reader deciding whether a component is safe to adopt is reading the wrong answer. Second: FormField now marks only the requirement and leaves optional fields unmarked - (optional) was removed app-wide by design decision and is pinned by tests/ui-v2-form-field.dom.test.tsx - which is a design-system contract change that appears in no document. It belongs in COMPONENTS.md section 4 and probably DECISIONS.md. Next action: one docs PR updating section 0 from the actual mount list and recording the optionality rule with its rationale. Stop: do not re-add (optional) markers to satisfy a generic form-accessibility rule - the removal was deliberate and is test-pinned. | session 2026-08-04 (DS V2 Wave 5 close-out capture) | 2026-08-04 | | #234 | P3 | task | answer-copy-payload.ts is the single clipboard payload builder for three surfaces and has no documentation | IN FLIGHT 2026-08-12 in PR #1842 (publishes the answer-copy clipboard contract). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. src/lib/answer-copy-payload.ts arrived in PR-J exporting answerStateForAnswer, buildAnswerClipboardText, resolveAnswerSources, citedSourcesOnly and singleDocumentClipboardMetadata. It is now the one place three product surfaces build a clipboard payload, which makes it a contract rather than a helper: a future caller that bypasses it can reintroduce the false-attribution defect the module exists to prevent (see #228). Nothing in docs/design-system mentions it. Next action: document the module and its five exports where the answer surface's copy contract is described, and state that new copy paths route through it rather than composing their own text. Found during PR-J close-out, 2026-08-04. | session 2026-08-04 (DS V2 Wave 5 close-out capture) | 2026-08-04 | | #235 | P3 | task | ADOPTION.md section 7 proof shots exist for only four of the adopted surfaces | IN FLIGHT 2026-08-12 in PR #1842 (records adoption evidence). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. The adoption contract asks for a proof shot per adopted surface. The Wave 5 adoption captured four - DSM header, settings rows, patient panel, answer surface - and none for the forms fold, the catalogue and docs surfaces, the headers convergence, or the empty states adopted since. Section 7 therefore reads as complete while most of the adoption is unevidenced, which matters because the proof shot is what a later reader uses to tell an intended restyle from a regression (the #229 DSM eyebrow was almost rediscovered as a defect for exactly this reason). Next action: capture the missing shots against a warmed local server and attach them to section 7. Cheap and mechanical - no gate, no provider access. Stop: this is not the visual-baseline harness (#118) - do not commit Playwright snapshot PNGs or flip that job to blocking. | session 2026-08-04 (DS V2 Wave 5 close-out capture) | 2026-08-04 | -| #236 | P3 | task | Wave 5 left two orphan builder branches and three dev servers running | Housekeeping from the DS V2 wave. Branches claude/ds-v2-builder-a and claude/ds-v2-builder-b still exist locally with their worktrees removed; their content is inside the PR-J squash but squash merge means they are not ancestors of main, so ordinary merged-branch detection will not offer them for cleanup and a content check is required before deleting either. Dev servers were left running on port 3258 (Database-wt-ds-v2-capture), port 3135 (Database-wt-ds-v2-correctness) and port 3672 (Database-wt-ds-v2-empty-state-heading, this session). Next action: verify each branch is content-identical to what landed (git diff --stat origin/main branch must be empty) before deleting, and stop the servers whose worktrees are finished. Stop: do not delete a branch on ancestry alone - squash merge breaks ancestry, which is the trap this row exists to flag. NOT VERIFIABLE FROM A CLOUD SESSION (checked 2026-08-12): this row describes machine-local git or process state. Claude Code on the web runs in a container cloned fresh at session start, so it reports 1 worktree, 0 upstream-less branches and no running dev servers no matter what is true on the machine that raised this. Do NOT close this row from a cloud session — that reading is an artifact of the container, not evidence. Verify from the originating machine. | session 2026-08-04 (DS V2 Wave 5 close-out capture) | 2026-08-04 | | #237 | P2 | rec | Eyeball low-confidence AccessibleTable densities at 320px before freezing Linux visual baselines | IN FLIGHT 2026-08-12 in PR #1841 (renders empty dense cells wrapping rather than truncated, with a 320px jsdom assertion). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. PR #1616 clinical MissingValue phrases increase text volume in sparse OCR grids. Contract forbids abbreviating to a dash. Next: open one real lowConfidence extraction at 320px phone width and accept or adjust dense preview column widths before committing Linux screenshots (#118). | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | | #238 | P2 | rec | Visual pass for Sheet portal default on settings, sidebar, and answer overlays | IN FLIGHT 2026-08-12 in PR #1842 (exercises the Sheet portal default and adds tests/sheet.dom.test.tsx). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. PR #1616 flips Sheet portal default to true, moving ~10 product overlays into OverlayRoot. Token inheritance is safe; residual risk is ancestor-scoped CSS / contain / transform. Next: one visual pass of settings-dialog, ClinicalSidebar, answer-result sheets, launcher sheet, section-nav. | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | | #239 | P3 | rec | Manual phone rotation check for ResizeObserver-only phone chrome reserve | PR #1616 phone overlay reserve publishes only from ResizeObserver quiet-window deliveries. Desktop↔phone and late-mount recovery are covered; orientation that does not change stack height is a narrower trigger. Next: rotate a physical phone on a chrome-overlay route and confirm --phone-overlay-chrome-h updates. | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | | #240 | P3 | rec | Confirm tooltip visual hard-clip asymmetry with design owner | Tooltip keeps overflow-hidden visual clamp while sr-only/aria-label retain full text. Design contract says supplementary-only. Next: design-owner confirmation that sighted users losing the clipped tail is acceptable, or allow overflow-y-auto for long clinical strings. | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | -| #241 | P3 | rec | Therapy home summary count/slugs remain build-time; keep --check load-bearing | VERIFIED CORRECT 2026-08-12 — re-checked against merged main and left open: `check:therapy-data-index` runs `node scripts/build-therapies-index.mjs --check` (package.json:111) and is wired into verify:cheap:internal (:71), so the --check path is load-bearing exactly as this row asks. Standing caution, no action. Home paints THERAPY_CATALOGUE_SUMMARY with catalogue I/O disabled. CI --check pins totalCount and defaultBrief/Sheet slugs. Next: do not bypass scripts/build-therapies-index.mjs --check; any home UI that lists real therapies must re-enable useTherapyData. | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | | #242 | P2 | task | Commit approved Linux visual baselines and promote adoption not-committed → committed | VERIFIED CORRECT 2026-08-12 — re-checked against merged main during the full ledger sweep and left unchanged: Six linux/ PNGs are committed, but the adoption manifest still carries 68 `not-committed` entries — the surfaces flip is the remaining work, as stated. This stamp exists so a later reader can tell "checked and still true" from "never looked at"; the two were indistinguishable before. Baselines and provenance are DONE as of PR #1729 (branch claude/ds-adopt-visual-baselines): all six linux/ PNGs committed from ubuntu artifact visual-baseline-31251091603 (main @ bc33d414e), AWAITING_BASELINE emptied, and tests/__screenshots__/linux/provenance.json written with per-candidate SHA-256 + dimensions and an approved human review. Proven by that PR's own run: visual-junit tests=9 failures=0 skipped=0, and no visual-candidates/ directory, i.e. all six compared rather than skipped. REMAINING: only the surfaces flip to baseline.status committed. Blocked on ordering, measured 2026-08-08: validateLinuxVisualBaselineSet short-circuits on declaredPaths.length===0, so declaring files activates its rule that no non-allowlisted path may change since candidateSourceHead — and PR #1729 necessarily changed tests/design-system-adoption.test.ts, whose initialiseCandidateRepository seeded fixtures from the LIVE spec and so failed the moment AWAITING_BASELINE emptied. The two cannot land together. Next: after #1729 merges, re-capture candidates from a main run that already contains that fixture fix, then flip the surfaces against that head. Note this does not affect whether pixels compare — Playwright compares because the goldens exist on disk. | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | -| #244 | P3 | rec | Forced-colours v2 mapping depends on grouped dark selectors staying in the media block | IN FLIGHT 2026-08-12 in PR #1841 (forced-colours selector specificity). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. ckb-v2-tokens.css forced-colours block lists .ckb-v2.ckb-v2, .dark .ckb-v2.ckb-v2, and .ckb-v2.dark.ckb-v2 so specificity matches dark rules. Trimming to a single .ckb-v2.ckb-v2 silently drops dark HCM. Next: keep the contract test pin; never trim that selector group. | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | | #245 | P3 | rec | responsive-compact CrossModeLinks keeps duplicate rails in the DOM | IN FLIGHT 2026-08-12 in PR #1842 (CrossModeLinks rail behaviour). Checked against the open-PR list during the full ledger sweep. Do NOT start this row while that PR is open — duplicating a queued conversion is the exact failure #292 records, and it has happened twice. Re-verify this row against main after that PR merges, and close it there rather than here. Phone chip rail and md+ card rail both mount; display:none removes the inactive from the a11y tree. Tests/analytics counting role=link see doubles; cross-mode-links-rail is phone-only. Next: prefer the variant test ids; do not collapse to one rail with JS breakpoints (hydration risk). | PR #1616 review findings; session 2026-08-05 | 2026-08-05 | | #248 | P2 | issue | Investigate why 20260705180000 search-health indexes were missing on live despite applied history | APPEND 2026-08-13: the prior closure is withdrawn. Repository and live-drift evidence establishes that 20260705180000_reconcile_search_health_indexes.sql is recorded as applied while documents_title_trgm_idx and document_chunks_content_trgm_idx are missing on live. Supabase transaction semantics exclude a persisted partial migration, but the present record does not distinguish skipped DDL/history repair from indexes created and later dropped. In an approved read-only window, query supabase_migrations.schema_migrations for the 20260705180000 statements fingerprint and inspect the relevant audit/history evidence; retain both hypotheses until that evidence establishes the cause. Separately, scheduled check:drift did detect the missing indexes, but red runs were not routed. | PR #1614 review / session 2026-08-05 (renumbered on main merge) | 2026-08-05 | -| #250 | P2 | task | Execute the fastest-wins multi-wave plan (Wave 0–4) | SUPERSEDED IN LARGE PART 2026-08-12 — this row is a wave plan whose contents have been overtaken, and as written it now misdirects. Rows it names as live A1 work are CLOSED: #207 and #226 were archived on main, and #166 (its sibling in that cluster) archived in the 2026-08-12 sweep. Of its engineering waves: 1B's gate-integrity set is done (#149, #210 re-scoped, #204/#167 covered by in-flight PR #1837); 1C's hygiene set is largely done or in flight (#232, #151, #154, #187, #142, #156, #186 all now carry IN FLIGHT notes against PR #1835/#1836); Wave 0/#202 is in flight in PR #1840. What genuinely remains of the plan is Wave 1A (#147 phone CLS, still open and still reproducible offline at zero provider cost), Wave 2 (#117 then #118), and Wave 3 (#098 then #189). The A1 track is now just #059, #053 and #231 — two operator rows and one live investigation. Next: either re-cut this row against that much smaller remainder, or close it and let #147/#117/#118/#098/#189 stand on their own, which is probably the honest move now that the multi-agent framing has served its purpose. Stop unchanged: no RAG behaviour change without flag plus canary, no provider gates without approval, and do not mix operationalRisk with clinical or UI in one squash. | session 2026-08-05 fastest-wins plan | 2026-08-05 | -| #253 | P3 | task | #1606 needs a hand-merge against merged PR #1615, not a rebase | SUPERSEDED IN PART 2026-08-07: the component both PRs rewrite no longer exists. `MobileResultFilterControl` — the native `