From 460e5da5566cddc5f8b814008437f8adf5f8962e Mon Sep 17 00:00:00 2001 From: BigSimmo Date: Sat, 17 Oct 2026 21:39:39 +0800 Subject: [PATCH 1/6] chore(ledger): queue reopen of #WFYDV6 by owner decision and a #3570 record branch-name correction Queued through the ledger inbox (no hand edits): a P1 add re-tracking the WA MHA Form 6A/6B statutory-text issue until legal and clinical sign-off, an amend-outcome on the archived #WFYDV6 row recording the reopen, and a P3 ledger-correction task for the #3570 prlanded record branch name. They apply at the next serialized issues:reconcile. --- .../15a11955-9ee3-4c86-ad8f-faa0af2a342c.json | 14 ++++++++++++++ .../a5c25ca4-4fa3-421f-a944-35621169e55e.json | 14 ++++++++++++++ .../f8628e25-fe07-442f-9221-119b40e34213.json | 14 ++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 docs/outstanding-issues-inbox/15a11955-9ee3-4c86-ad8f-faa0af2a342c.json create mode 100644 docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json create mode 100644 docs/outstanding-issues-inbox/f8628e25-fe07-442f-9221-119b40e34213.json diff --git a/docs/outstanding-issues-inbox/15a11955-9ee3-4c86-ad8f-faa0af2a342c.json b/docs/outstanding-issues-inbox/15a11955-9ee3-4c86-ad8f-faa0af2a342c.json new file mode 100644 index 0000000000..0580b3e7ba --- /dev/null +++ b/docs/outstanding-issues-inbox/15a11955-9ee3-4c86-ad8f-faa0af2a342c.json @@ -0,0 +1,14 @@ +{ + "version": 2, + "id": "15a11955-9ee3-4c86-ad8f-faa0af2a342c", + "createdOn": "2026-10-17", + "action": "add", + "payload": { + "pri": "P3", + "type": "task", + "summary": "Ledger correction: PR #3570 prlanded record names the wrong branch", + "detail": "docs/branch-review-records/a7be2760026da4bc040fa90bf847382bdcded2a8a7510ceaf7be4ac49c042211.record.md (added by PR #3609) records branch claude/reconcile-inbox-20261016 for PR #3570, but gh shows #3570's head branch was claude/reconcile-ledger-20261016 (merge fa526775c6, head d8eefbb91e). The SHAs and merge state are correct. Correct the branch name through the sanctioned branch-review-ledger tooling only; never hand-edit the record.", + "source": "PR #3609 sweep proof verification, 17 Oct 2026", + "issueUlid": "01M551BST62X90V6VK3DEP4C7S" + } +} diff --git a/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json b/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json new file mode 100644 index 0000000000..2e31d23619 --- /dev/null +++ b/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json @@ -0,0 +1,14 @@ +{ + "version": 2, + "id": "a5c25ca4-4fa3-421f-a944-35621169e55e", + "createdOn": "2026-10-17", + "action": "add", + "payload": { + "pri": "P1", + "type": "issue", + "summary": "Reopened #WFYDV6: unwired WA MHA Form 6A/6B print component carries unverified statutory text; keep unwired until legal and clinical sign-off", + "detail": "Owner decision 17 Oct 2026 21:37 AWST: #WFYDV6 must stay open; PR #3609 archived it without the required sign-off. src/components/forms/wa-mha-form-print.tsx (PR #3538) was reworded on 16 Oct (Form 6B now a report to the Chief Psychiatrist; Non-Statutory Study Layout header), but section lists, the 7-day pre-expiry examination and copy recipients remain unverified and no legal or clinical sign-off is recorded. Nothing imports or renders it; it must stay unwired. Delete it once the 30-day dead-code window passes (after 13 Nov 2026) unless legal and clinical sign-off is obtained first.", + "source": "PR 3538 review, 15 Oct 2026; owner decision on PR #3609, 17 Oct 2026", + "issueUlid": "01M551BSHWZ12CBC9F0K21CPEN" + } +} diff --git a/docs/outstanding-issues-inbox/f8628e25-fe07-442f-9221-119b40e34213.json b/docs/outstanding-issues-inbox/f8628e25-fe07-442f-9221-119b40e34213.json new file mode 100644 index 0000000000..79e3afccf3 --- /dev/null +++ b/docs/outstanding-issues-inbox/f8628e25-fe07-442f-9221-119b40e34213.json @@ -0,0 +1,14 @@ +{ + "version": 2, + "id": "f8628e25-fe07-442f-9221-119b40e34213", + "createdOn": "2026-10-17", + "action": "amend-outcome", + "payload": { + "id": "#WFYDV6", + "outcome": "Reopened 17 Oct 2026 by owner decision (21:37 AWST): archived by PR #3609 without the required legal and clinical sign-off; tracked again as an open P1 row. The form stays unwired until sign-off.", + "baseRowFingerprint": "7557eae4330cfdd49d1530a684e58d5668cd895b9f73047e23be2dc8a3fb7531", + "allowArchived": true, + "targetTable": "archive", + "targetUlid": "01M4ZM793BWFYDV65NNBHBP53K" + } +} From 6099ccb1ef7316cb46f9384e33ce749c7df5f118 Mon Sep 17 00:00:00 2001 From: BigSimmo Date: Sat, 17 Oct 2026 22:13:35 +0800 Subject: [PATCH 2/6] docs(decisions): record the #WFYDV6 owner decision; queue the #8Y9169 correction and a ledger reopen task - docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md anchors the owner decision to the queued reopen request - queue a P3 ledger-tooling task for a reopen action that keeps ID and ULID - queue the #8Y9169 inventory correction through issues:update --- data/repo-awareness-snapshot.json | 5 +++++ .../2026-10-17-wfydv6-stays-open-until-sign-off.md | 14 ++++++++++++++ .../177b7474-70f8-47a9-8299-8fdaf811c419.json | 14 ++++++++++++++ .../c455b372-522a-4894-b87c-4fd7827b61bb.json | 12 ++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md create mode 100644 docs/outstanding-issues-inbox/177b7474-70f8-47a9-8299-8fdaf811c419.json create mode 100644 docs/outstanding-issues-inbox/c455b372-522a-4894-b87c-4fd7827b61bb.json diff --git a/data/repo-awareness-snapshot.json b/data/repo-awareness-snapshot.json index ab9d70907c..7df206910a 100644 --- a/data/repo-awareness-snapshot.json +++ b/data/repo-awareness-snapshot.json @@ -3969,6 +3969,11 @@ "section": "decisions", "catalogued": true }, + { + "path": "docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md", + "section": "decisions", + "catalogued": true + }, { "path": "docs/agents/native-startup/external-skill-precedence.md", "section": "agents", diff --git a/docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md b/docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md new file mode 100644 index 0000000000..98269f3c14 --- /dev/null +++ b/docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md @@ -0,0 +1,14 @@ +# Decision: The WA MHA Form 6A/6B statutory-text issue stays open until legal and clinical sign-off + +- **Status:** decided 2026-10-17 +- **Source:** `docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json`, find "Owner decision 17 Oct 2026 21:37 AWST" + +PR #3609 archived ledger issue #WFYDV6 (the unwired `src/components/forms/wa-mha-form-print.tsx` with +unverified statutory text) without the legal and clinical sign-off the issue required. The owner decided on +17 Oct 2026 at 21:37 AWST that the issue stays open and the form stays unwired until that sign-off is +recorded. The ledger tooling has no reopen action, so the issue is tracked again as a new open P1 row +queued through the ledger inbox, and the archived #WFYDV6 row's outcome records the reopen. A reconciliation +must not archive that row again without the recorded sign-off, or the deletion of the file after the +30-day dead-code window (13 Nov 2026). + +If this summary and the source ever differ, the source wins. diff --git a/docs/outstanding-issues-inbox/177b7474-70f8-47a9-8299-8fdaf811c419.json b/docs/outstanding-issues-inbox/177b7474-70f8-47a9-8299-8fdaf811c419.json new file mode 100644 index 0000000000..a313d9757b --- /dev/null +++ b/docs/outstanding-issues-inbox/177b7474-70f8-47a9-8299-8fdaf811c419.json @@ -0,0 +1,14 @@ +{ + "version": 2, + "id": "177b7474-70f8-47a9-8299-8fdaf811c419", + "createdOn": "2026-10-17", + "action": "add", + "payload": { + "pri": "P3", + "type": "task", + "summary": "Ledger tooling: add a reopen action that moves an archived issue back to the open table with its original ID and ULID", + "detail": "There is no sanctioned reopen in scripts/ledger-inbox.mjs (actions: add, done, update, amend-outcome, queue, cancel). Reopening #WFYDV6 by owner decision on 17 Oct 2026 therefore created a new open P1 row and amended the archived row, splitting the issue's history across two identities. Add a reopen request kind that the reconciler applies by moving the archive row back to the open table with its original ID and ULID, then use it to fold the new P1 row back into #WFYDV6.", + "source": "PR #3616 review (chatgpt-codex-connector), 17 Oct 2026", + "issueUlid": "01M552JNXFGYR7Q31XTTJSNQZD" + } +} diff --git a/docs/outstanding-issues-inbox/c455b372-522a-4894-b87c-4fd7827b61bb.json b/docs/outstanding-issues-inbox/c455b372-522a-4894-b87c-4fd7827b61bb.json new file mode 100644 index 0000000000..bea2f72ae1 --- /dev/null +++ b/docs/outstanding-issues-inbox/c455b372-522a-4894-b87c-4fd7827b61bb.json @@ -0,0 +1,12 @@ +{ + "version": 2, + "id": "c455b372-522a-4894-b87c-4fd7827b61bb", + "createdOn": "2026-10-17", + "action": "update", + "payload": { + "id": "#8Y9169", + "detail": "#08216B was archived on 17 Oct with an outcome covering only the clinical-math calculators. Still open: src/app/api/proxy/route.ts (nothing calls it), scripts/recalculate-answer-support-labels.ts (no production caller; reassess it as a deletion candidate) and its test (covered by the normal test suite; do not treat it as unused), and the developer tool pages under src/app/mockups/development (unreachable duplicates of /admin/developer-hub) - remove after the 30-day dead-code window (13 Nov 2026) via npm run check:dead-code-candidate. The safe-number editor and clinical note editor are unwired: wire or remove each; the note editor needs a privacy review before any use (the attendance roster and QR check-in helper are wired through organise-sheets.tsx and teaching-code-sheet.tsx). The 16 Oct clinical-math calculator changes (IBW guidance, lithium boundaries, dose rounding) need clinician verification before any clinical use.", + "baseRowFingerprint": "556db211f1a3e8c8cad29cb6d400fe4b85d2f1058dc98d465eae5d8be84370ce", + "targetUlid": "01M54YEVKA8Y91694B291WKTQS" + } +} From 9f5dd9ee993e92e8ca8d6bfac615400995ab511a Mon Sep 17 00:00:00 2001 From: BigSimmo Date: Sat, 17 Oct 2026 22:13:47 +0800 Subject: [PATCH 3/6] docs(decisions): state that the Form 6A/6B wording still needs owner, legal and clinical sign-off --- .../2026-10-17-wfydv6-stays-open-until-sign-off.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md b/docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md index 98269f3c14..fe6472137c 100644 --- a/docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md +++ b/docs/decisions/2026-10-17-wfydv6-stays-open-until-sign-off.md @@ -1,4 +1,4 @@ -# Decision: The WA MHA Form 6A/6B statutory-text issue stays open until legal and clinical sign-off +# Decision: The WA MHA Form 6A/6B statutory-text issue stays open until owner, legal and clinical sign-off - **Status:** decided 2026-10-17 - **Source:** `docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json`, find "Owner decision 17 Oct 2026 21:37 AWST" @@ -6,9 +6,9 @@ PR #3609 archived ledger issue #WFYDV6 (the unwired `src/components/forms/wa-mha-form-print.tsx` with unverified statutory text) without the legal and clinical sign-off the issue required. The owner decided on 17 Oct 2026 at 21:37 AWST that the issue stays open and the form stays unwired until that sign-off is -recorded. The ledger tooling has no reopen action, so the issue is tracked again as a new open P1 row +recorded. The Form 6A/6B wording still needs owner, legal and clinical sign-off; nothing in this decision signs it off. The ledger tooling has no reopen action, so the issue is tracked again as a new open P1 row queued through the ledger inbox, and the archived #WFYDV6 row's outcome records the reopen. A reconciliation -must not archive that row again without the recorded sign-off, or the deletion of the file after the +must not archive that row again without the recorded owner, legal and clinical sign-off, or the deletion of the file after the 30-day dead-code window (13 Nov 2026). If this summary and the source ever differ, the source wins. From e127e1ee879b93fde49ea084cf9beb79a8080176 Mon Sep 17 00:00:00 2001 From: "qodo-code-review[bot]" <151058649+qodo-code-review[bot]@users.noreply.github.com> Date: Sat, 17 Oct 2026 14:24:22 +0000 Subject: [PATCH 4/6] fix: Correct the clinical editor inventory --- .../c455b372-522a-4894-b87c-4fd7827b61bb.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/outstanding-issues-inbox/c455b372-522a-4894-b87c-4fd7827b61bb.json b/docs/outstanding-issues-inbox/c455b372-522a-4894-b87c-4fd7827b61bb.json index bea2f72ae1..aa0e26ea0f 100644 --- a/docs/outstanding-issues-inbox/c455b372-522a-4894-b87c-4fd7827b61bb.json +++ b/docs/outstanding-issues-inbox/c455b372-522a-4894-b87c-4fd7827b61bb.json @@ -5,7 +5,7 @@ "action": "update", "payload": { "id": "#8Y9169", - "detail": "#08216B was archived on 17 Oct with an outcome covering only the clinical-math calculators. Still open: src/app/api/proxy/route.ts (nothing calls it), scripts/recalculate-answer-support-labels.ts (no production caller; reassess it as a deletion candidate) and its test (covered by the normal test suite; do not treat it as unused), and the developer tool pages under src/app/mockups/development (unreachable duplicates of /admin/developer-hub) - remove after the 30-day dead-code window (13 Nov 2026) via npm run check:dead-code-candidate. The safe-number editor and clinical note editor are unwired: wire or remove each; the note editor needs a privacy review before any use (the attendance roster and QR check-in helper are wired through organise-sheets.tsx and teaching-code-sheet.tsx). The 16 Oct clinical-math calculator changes (IBW guidance, lithium boundaries, dose rounding) need clinician verification before any clinical use.", + "detail": "#08216B was archived on 17 Oct with an outcome covering only the clinical-math calculators. Still open: src/app/api/proxy/route.ts (nothing calls it), scripts/recalculate-answer-support-labels.ts (no production caller; reassess it as a deletion candidate) and its test (covered by the normal test suite; do not treat it as unused), and the developer tool pages under src/app/mockups/development (unreachable duplicates of /admin/developer-hub) - remove after the 30-day dead-code window (13 Nov 2026) via npm run check:dead-code-candidate. The safe-number editor remains to be assessed for wiring or removal. ClinicalNoteEditor is already wired into the on-call handover shift scratchpad with persistence callbacks (handover-page.tsx); any privacy review should address that existing de-identified use rather than precede it. The attendance roster and QR check-in helper are wired through organise-sheets.tsx and teaching-code-sheet.tsx. The 16 Oct clinical-math calculator changes (IBW guidance, lithium boundaries, dose rounding) need clinician verification before any clinical use.", "baseRowFingerprint": "556db211f1a3e8c8cad29cb6d400fe4b85d2f1058dc98d465eae5d8be84370ce", "targetUlid": "01M54YEVKA8Y91694B291WKTQS" } From dc4265446bc7b916cbd1b0a115c1e0c924ac7001 Mon Sep 17 00:00:00 2001 From: "qodo-code-review[bot]" <151058649+qodo-code-review[bot]@users.noreply.github.com> Date: Sat, 17 Oct 2026 14:24:26 +0000 Subject: [PATCH 5/6] fix: Reopen the original ledger identity --- .../6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json | 10 +++++ .../a5c25ca4-4fa3-421f-a944-35621169e55e.json | 9 +++-- scripts/ledger-inbox.mjs | 38 +++++++++++++++--- scripts/outstanding-issues.mjs | 40 +++++++++++++++++++ 4 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 docs/outstanding-issues-inbox/6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json diff --git a/docs/outstanding-issues-inbox/6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json b/docs/outstanding-issues-inbox/6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json new file mode 100644 index 0000000000..9a98cd01b1 --- /dev/null +++ b/docs/outstanding-issues-inbox/6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "id": "6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a", + "createdOn": "2026-10-17", + "action": "cancel", + "payload": { + "requestId": "f8628e25-fe07-442f-9221-119b40e34213", + "reason": "Replaced by the sanctioned reopen request for #WFYDV6, which preserves the original ledger identity" + } +} diff --git a/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json b/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json index 2e31d23619..9d15e88d4a 100644 --- a/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json +++ b/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json @@ -2,13 +2,14 @@ "version": 2, "id": "a5c25ca4-4fa3-421f-a944-35621169e55e", "createdOn": "2026-10-17", - "action": "add", + "action": "reopen", "payload": { + "id": "#WFYDV6", "pri": "P1", - "type": "issue", - "summary": "Reopened #WFYDV6: unwired WA MHA Form 6A/6B print component carries unverified statutory text; keep unwired until legal and clinical sign-off", "detail": "Owner decision 17 Oct 2026 21:37 AWST: #WFYDV6 must stay open; PR #3609 archived it without the required sign-off. src/components/forms/wa-mha-form-print.tsx (PR #3538) was reworded on 16 Oct (Form 6B now a report to the Chief Psychiatrist; Non-Statutory Study Layout header), but section lists, the 7-day pre-expiry examination and copy recipients remain unverified and no legal or clinical sign-off is recorded. Nothing imports or renders it; it must stay unwired. Delete it once the 30-day dead-code window passes (after 13 Nov 2026) unless legal and clinical sign-off is obtained first.", "source": "PR 3538 review, 15 Oct 2026; owner decision on PR #3609, 17 Oct 2026", - "issueUlid": "01M551BSHWZ12CBC9F0K21CPEN" + "baseRowFingerprint": "7557eae4330cfdd49d1530a684e58d5668cd895b9f73047e23be2dc8a3fb7531", + "targetTable": "archive", + "targetUlid": "01M4ZM793BWFYDV65NNBHBP53K" } } diff --git a/scripts/ledger-inbox.mjs b/scripts/ledger-inbox.mjs index 8eb73d752b..28193a3b67 100644 --- a/scripts/ledger-inbox.mjs +++ b/scripts/ledger-inbox.mjs @@ -12,7 +12,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, s import path from "node:path"; import { fileURLToPath } from "node:url"; -import { addIssue, resolveIssue, updateArchivedIssue, updateIssue, updateQueueRow } from "./outstanding-issues.mjs"; +import { addIssue, reopenIssue, resolveIssue, updateArchivedIssue, updateIssue, updateQueueRow } from "./outstanding-issues.mjs"; import { ISSUES_PATH, archiveRowFingerprint, @@ -34,7 +34,7 @@ import { const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); const INBOX_DIR = "docs/outstanding-issues-inbox"; const APPLIED_DIR = path.posix.join(INBOX_DIR, "applied"); -const ACTIONS = new Set(["add", "done", "update", "amend-outcome", "queue", "cancel"]); +const ACTIONS = new Set(["add", "done", "update", "amend-outcome", "reopen", "queue", "cancel"]); // Fields a `queue` request may carry, mirroring updateQueueRow's editable map. const QUEUE_FIELDS = ["acuity", "capability", "when", "estimate", "outcome"]; const REQUEST_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; @@ -93,6 +93,20 @@ function validateDoneRequest(payload, problems) { } } +function validateReopenRequest(payload, problems) { + if (!isIssueDisplayId(payload?.id)) problems.push("reopen requires a canonical issue display id"); + for (const field of ["pri", "detail", "source"]) { + if (!payload?.[field]) problems.push(`reopen requires ${field}`); + } + if (payload?.pri && !["P1", "P2", "P3"].includes(String(payload.pri))) { + problems.push("reopen pri must be P1, P2, or P3"); + } + validateFingerprint(payload, "reopen", problems); + if (payload?.targetUlid !== undefined && !isIssueUlid(payload.targetUlid)) { + problems.push("reopen targetUlid must be a valid ULID"); + } +} + function validateUpdateRequest(action, payload, problems) { if (!isIssueDisplayId(payload?.id)) { problems.push(`${action} requires a canonical issue display id`); @@ -144,10 +158,11 @@ export function validateRequest(request) { if (![1, 2].includes(request.version)) problems.push("version must be 1 or 2"); if (!REQUEST_ID.test(request.id ?? "")) problems.push("id must be a UUID"); if (!/^\d{4}-\d{2}-\d{2}$/.test(request.createdOn ?? "")) problems.push("createdOn must be YYYY-MM-DD"); - if (!ACTIONS.has(request.action)) problems.push("action must be add, done, update, queue, or cancel"); + if (!ACTIONS.has(request.action)) problems.push("action must be add, done, update, amend-outcome, reopen, queue, or cancel"); if (!request.payload || typeof request.payload !== "object") problems.push("payload must be an object"); if (request.action === "add") validateAddRequest(request, problems); if (request.action === "done") validateDoneRequest(request.payload, problems); + if (request.action === "reopen") validateReopenRequest(request.payload, problems); if (request.action === "update" || request.action === "amend-outcome") { validateUpdateRequest(request.action, request.payload, problems); } @@ -211,7 +226,7 @@ function shouldKeepArchivedMarkdown(markdown, request, options, allowArchived) { } function verifyIssueFingerprint(markdown, request, allowArchived) { - if (!["done", "update", "amend-outcome"].includes(request.action) || !request.payload?.baseRowFingerprint) { + if (!["done", "update", "amend-outcome", "reopen"].includes(request.action) || !request.payload?.baseRowFingerprint) { return allowArchived; } const id = request.payload.id; @@ -239,6 +254,9 @@ function applyValidatedRequest(markdown, request, options, dateOptions, allowArc const durableId = request.payload.issueUlid ?? issueUlidFromRequest(request.createdOn, request.id); return addIssue(markdown, request.payload, { ...dateOptions, issueUlid: durableId }); } + if (request.action === "reopen") { + return reopenIssue(markdown, request.payload.id, request.payload, dateOptions); + } if (request.action === "done") { return resolveIssue(markdown, request.payload.id, request.payload.outcome, { ...dateOptions, @@ -1247,6 +1265,16 @@ function createRequestPayload(action, argv) { issueUlid: issueUlid(), }; } + if (action === "reopen") { + const payload = { + id: targetId, + pri: argValue(argv, "pri"), + detail: argValue(argv, "detail"), + source: argValue(argv, "source"), + }; + if (targetUlid) payload.targetUlid = targetUlid; + return payload; + } if (action === "done" || action === "amend-outcome") { const payload = { id: targetId, outcome: argValue(argv, "outcome") ?? argValue(argv, "reason") }; if (targetUlid) payload.targetUlid = targetUlid; @@ -1287,7 +1315,7 @@ function createRequestPayload(action, argv) { } function attachIssueFingerprint(action, payload) { - if (!["done", "update", "amend-outcome"].includes(action) || typeof payload.id !== "string") return; + if (!["done", "update", "amend-outcome", "reopen"].includes(action) || typeof payload.id !== "string") return; const pending = loadPending(); // If explicit targetUlid identifies a pending add, retain it without borrowing diff --git a/scripts/outstanding-issues.mjs b/scripts/outstanding-issues.mjs index 44f3ebff21..3d4196d0ac 100644 --- a/scripts/outstanding-issues.mjs +++ b/scripts/outstanding-issues.mjs @@ -306,6 +306,46 @@ export function resolveIssue(markdown, id, outcome, options = {}) { }); } +/** + * Reopen an archived issue without allocating a second durable identity. + * The archived row supplies the original ID, type, summary, and ULID; the + * request supplies the open-row triage fields that are not retained in the + * archive table. + */ +export function reopenIssue(markdown, id, fields, options = {}) { + if (!fields?.pri || !fields?.detail || !fields?.source) { + throw new Error("reopen requires pri, detail, and source"); + } + if (!PRIORITIES.has(String(fields.pri))) throw new Error(`--pri must be one of ${[...PRIORITIES].join(", ")}`); + + return guarded(markdown, (current) => { + const parsed = parseIssues(current); + const targetId = normalizeIssueDisplayId(id); + const row = parsed.rows.find((entry) => entry.table === "archive" && normalizeIssueDisplayId(entry.id) === targetId); + if (!row || !row.ulid) throw new Error(`${id} is not an archived durable issue in ${ISSUES_PATH}`); + + const archiveCells = splitCells(row.raw); + const openRow = buildRow([ + issueIdCell(row.id, row.ulid), + String(fields.pri), + archiveCells[1], + archiveCells[2], + escapeCell(fields.detail), + escapeCell(fields.source), + today(options), + ]); + const lines = current.split("\n"); + lines.splice(row.line - 1, 1); + const next = lines.join("\n"); + const afterRemoval = parseIssues(next); + const anchor = lastRowIndex(afterRemoval, "open"); + if (anchor === null) throw new Error("the open-items table has no rows to append after"); + const nextLines = next.split("\n"); + nextLines.splice(anchor + 1, 0, openRow); + return nextLines.join("\n"); + }); +} + export function updateIssue(markdown, id, fields) { const editable = { pri: 1, summary: 3, detail: 4, source: 5 }; const requested = Object.keys(editable).filter((key) => fields[key] !== undefined); From 5385ab9b3e8f31b1cd218b6fec1a3e172bb22f2e Mon Sep 17 00:00:00 2001 From: BigSimmo Date: Sat, 17 Oct 2026 23:13:36 +0800 Subject: [PATCH 6/6] Revert "fix: Reopen the original ledger identity" This reverts commit dc4265446, a review bot's unreviewed reconciler change. It added a 'reopen' request kind to scripts/ledger-inbox.mjs and scripts/outstanding-issues.mjs without tests, rewrote the pending a5c25ca4 request into that new kind, and failed the changed-file format check. The #WFYDV6 identity fix stays tracked by the queued P3 reopen-action task (177b7474), which can land with tests in its own PR. --- .../6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json | 10 ----- .../a5c25ca4-4fa3-421f-a944-35621169e55e.json | 9 ++--- scripts/ledger-inbox.mjs | 38 +++--------------- scripts/outstanding-issues.mjs | 40 ------------------- 4 files changed, 9 insertions(+), 88 deletions(-) delete mode 100644 docs/outstanding-issues-inbox/6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json diff --git a/docs/outstanding-issues-inbox/6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json b/docs/outstanding-issues-inbox/6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json deleted file mode 100644 index 9a98cd01b1..0000000000 --- a/docs/outstanding-issues-inbox/6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 2, - "id": "6e1a6d7a-6e6d-4e5c-9dd0-1c0f4f7f6d4a", - "createdOn": "2026-10-17", - "action": "cancel", - "payload": { - "requestId": "f8628e25-fe07-442f-9221-119b40e34213", - "reason": "Replaced by the sanctioned reopen request for #WFYDV6, which preserves the original ledger identity" - } -} diff --git a/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json b/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json index 9d15e88d4a..2e31d23619 100644 --- a/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json +++ b/docs/outstanding-issues-inbox/a5c25ca4-4fa3-421f-a944-35621169e55e.json @@ -2,14 +2,13 @@ "version": 2, "id": "a5c25ca4-4fa3-421f-a944-35621169e55e", "createdOn": "2026-10-17", - "action": "reopen", + "action": "add", "payload": { - "id": "#WFYDV6", "pri": "P1", + "type": "issue", + "summary": "Reopened #WFYDV6: unwired WA MHA Form 6A/6B print component carries unverified statutory text; keep unwired until legal and clinical sign-off", "detail": "Owner decision 17 Oct 2026 21:37 AWST: #WFYDV6 must stay open; PR #3609 archived it without the required sign-off. src/components/forms/wa-mha-form-print.tsx (PR #3538) was reworded on 16 Oct (Form 6B now a report to the Chief Psychiatrist; Non-Statutory Study Layout header), but section lists, the 7-day pre-expiry examination and copy recipients remain unverified and no legal or clinical sign-off is recorded. Nothing imports or renders it; it must stay unwired. Delete it once the 30-day dead-code window passes (after 13 Nov 2026) unless legal and clinical sign-off is obtained first.", "source": "PR 3538 review, 15 Oct 2026; owner decision on PR #3609, 17 Oct 2026", - "baseRowFingerprint": "7557eae4330cfdd49d1530a684e58d5668cd895b9f73047e23be2dc8a3fb7531", - "targetTable": "archive", - "targetUlid": "01M4ZM793BWFYDV65NNBHBP53K" + "issueUlid": "01M551BSHWZ12CBC9F0K21CPEN" } } diff --git a/scripts/ledger-inbox.mjs b/scripts/ledger-inbox.mjs index 28193a3b67..8eb73d752b 100644 --- a/scripts/ledger-inbox.mjs +++ b/scripts/ledger-inbox.mjs @@ -12,7 +12,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, s import path from "node:path"; import { fileURLToPath } from "node:url"; -import { addIssue, reopenIssue, resolveIssue, updateArchivedIssue, updateIssue, updateQueueRow } from "./outstanding-issues.mjs"; +import { addIssue, resolveIssue, updateArchivedIssue, updateIssue, updateQueueRow } from "./outstanding-issues.mjs"; import { ISSUES_PATH, archiveRowFingerprint, @@ -34,7 +34,7 @@ import { const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); const INBOX_DIR = "docs/outstanding-issues-inbox"; const APPLIED_DIR = path.posix.join(INBOX_DIR, "applied"); -const ACTIONS = new Set(["add", "done", "update", "amend-outcome", "reopen", "queue", "cancel"]); +const ACTIONS = new Set(["add", "done", "update", "amend-outcome", "queue", "cancel"]); // Fields a `queue` request may carry, mirroring updateQueueRow's editable map. const QUEUE_FIELDS = ["acuity", "capability", "when", "estimate", "outcome"]; const REQUEST_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; @@ -93,20 +93,6 @@ function validateDoneRequest(payload, problems) { } } -function validateReopenRequest(payload, problems) { - if (!isIssueDisplayId(payload?.id)) problems.push("reopen requires a canonical issue display id"); - for (const field of ["pri", "detail", "source"]) { - if (!payload?.[field]) problems.push(`reopen requires ${field}`); - } - if (payload?.pri && !["P1", "P2", "P3"].includes(String(payload.pri))) { - problems.push("reopen pri must be P1, P2, or P3"); - } - validateFingerprint(payload, "reopen", problems); - if (payload?.targetUlid !== undefined && !isIssueUlid(payload.targetUlid)) { - problems.push("reopen targetUlid must be a valid ULID"); - } -} - function validateUpdateRequest(action, payload, problems) { if (!isIssueDisplayId(payload?.id)) { problems.push(`${action} requires a canonical issue display id`); @@ -158,11 +144,10 @@ export function validateRequest(request) { if (![1, 2].includes(request.version)) problems.push("version must be 1 or 2"); if (!REQUEST_ID.test(request.id ?? "")) problems.push("id must be a UUID"); if (!/^\d{4}-\d{2}-\d{2}$/.test(request.createdOn ?? "")) problems.push("createdOn must be YYYY-MM-DD"); - if (!ACTIONS.has(request.action)) problems.push("action must be add, done, update, amend-outcome, reopen, queue, or cancel"); + if (!ACTIONS.has(request.action)) problems.push("action must be add, done, update, queue, or cancel"); if (!request.payload || typeof request.payload !== "object") problems.push("payload must be an object"); if (request.action === "add") validateAddRequest(request, problems); if (request.action === "done") validateDoneRequest(request.payload, problems); - if (request.action === "reopen") validateReopenRequest(request.payload, problems); if (request.action === "update" || request.action === "amend-outcome") { validateUpdateRequest(request.action, request.payload, problems); } @@ -226,7 +211,7 @@ function shouldKeepArchivedMarkdown(markdown, request, options, allowArchived) { } function verifyIssueFingerprint(markdown, request, allowArchived) { - if (!["done", "update", "amend-outcome", "reopen"].includes(request.action) || !request.payload?.baseRowFingerprint) { + if (!["done", "update", "amend-outcome"].includes(request.action) || !request.payload?.baseRowFingerprint) { return allowArchived; } const id = request.payload.id; @@ -254,9 +239,6 @@ function applyValidatedRequest(markdown, request, options, dateOptions, allowArc const durableId = request.payload.issueUlid ?? issueUlidFromRequest(request.createdOn, request.id); return addIssue(markdown, request.payload, { ...dateOptions, issueUlid: durableId }); } - if (request.action === "reopen") { - return reopenIssue(markdown, request.payload.id, request.payload, dateOptions); - } if (request.action === "done") { return resolveIssue(markdown, request.payload.id, request.payload.outcome, { ...dateOptions, @@ -1265,16 +1247,6 @@ function createRequestPayload(action, argv) { issueUlid: issueUlid(), }; } - if (action === "reopen") { - const payload = { - id: targetId, - pri: argValue(argv, "pri"), - detail: argValue(argv, "detail"), - source: argValue(argv, "source"), - }; - if (targetUlid) payload.targetUlid = targetUlid; - return payload; - } if (action === "done" || action === "amend-outcome") { const payload = { id: targetId, outcome: argValue(argv, "outcome") ?? argValue(argv, "reason") }; if (targetUlid) payload.targetUlid = targetUlid; @@ -1315,7 +1287,7 @@ function createRequestPayload(action, argv) { } function attachIssueFingerprint(action, payload) { - if (!["done", "update", "amend-outcome", "reopen"].includes(action) || typeof payload.id !== "string") return; + if (!["done", "update", "amend-outcome"].includes(action) || typeof payload.id !== "string") return; const pending = loadPending(); // If explicit targetUlid identifies a pending add, retain it without borrowing diff --git a/scripts/outstanding-issues.mjs b/scripts/outstanding-issues.mjs index 3d4196d0ac..44f3ebff21 100644 --- a/scripts/outstanding-issues.mjs +++ b/scripts/outstanding-issues.mjs @@ -306,46 +306,6 @@ export function resolveIssue(markdown, id, outcome, options = {}) { }); } -/** - * Reopen an archived issue without allocating a second durable identity. - * The archived row supplies the original ID, type, summary, and ULID; the - * request supplies the open-row triage fields that are not retained in the - * archive table. - */ -export function reopenIssue(markdown, id, fields, options = {}) { - if (!fields?.pri || !fields?.detail || !fields?.source) { - throw new Error("reopen requires pri, detail, and source"); - } - if (!PRIORITIES.has(String(fields.pri))) throw new Error(`--pri must be one of ${[...PRIORITIES].join(", ")}`); - - return guarded(markdown, (current) => { - const parsed = parseIssues(current); - const targetId = normalizeIssueDisplayId(id); - const row = parsed.rows.find((entry) => entry.table === "archive" && normalizeIssueDisplayId(entry.id) === targetId); - if (!row || !row.ulid) throw new Error(`${id} is not an archived durable issue in ${ISSUES_PATH}`); - - const archiveCells = splitCells(row.raw); - const openRow = buildRow([ - issueIdCell(row.id, row.ulid), - String(fields.pri), - archiveCells[1], - archiveCells[2], - escapeCell(fields.detail), - escapeCell(fields.source), - today(options), - ]); - const lines = current.split("\n"); - lines.splice(row.line - 1, 1); - const next = lines.join("\n"); - const afterRemoval = parseIssues(next); - const anchor = lastRowIndex(afterRemoval, "open"); - if (anchor === null) throw new Error("the open-items table has no rows to append after"); - const nextLines = next.split("\n"); - nextLines.splice(anchor + 1, 0, openRow); - return nextLines.join("\n"); - }); -} - export function updateIssue(markdown, id, fields) { const editable = { pri: 1, summary: 3, detail: 4, source: 5 }; const requested = Object.keys(editable).filter((key) => fields[key] !== undefined);