From 83b2c3ede186020533ac002690db979c441d880f Mon Sep 17 00:00:00 2001 From: Diego Saavedra Date: Mon, 15 Jun 2026 22:21:27 -0500 Subject: [PATCH] fix(sdd-status): skip pending/TODO blocker when line has explicit pass signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The verify-report parser treated any line containing TODO or PENDING as a blocker, even when the line also carried an explicit pass signal (✅ glyph, COMPLIANT status, or PASS verdict). This caused false negatives that blocked archive for fully passing reports. Fix: - reportLineHasBlocker: gate pending pattern on !reportLineHasPassSignal - reportLineHasPassSignal: detect ✅ glyph as pass signal in prose - Add 3 test cases covering compliant+TODO, standalone TODO, and mixed Closes #848 --- internal/sddstatus/status.go | 8 ++++++- internal/sddstatus/status_test.go | 38 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/internal/sddstatus/status.go b/internal/sddstatus/status.go index 41308539..1580deea 100644 --- a/internal/sddstatus/status.go +++ b/internal/sddstatus/status.go @@ -614,7 +614,10 @@ func reportLineHasBlocker(line string) bool { if line == "" { return false } - if reportPassNegationPattern.MatchString(line) || reportPendingPattern.MatchString(line) { + if reportPassNegationPattern.MatchString(line) { + return true + } + if reportPendingPattern.MatchString(line) && !reportLineHasPassSignal(line) { return true } if reportCriticalGlyphStatusPattern.MatchString(line) { @@ -647,6 +650,9 @@ func reportLineHasPassSignal(line string) bool { if line == "" { return false } + if strings.Contains(line, "✅") { + return true + } _, value, hasField := reportField(line) if hasField && reportPassValuePattern.MatchString(stripMarkdownSignal(value)) { return true diff --git a/internal/sddstatus/status_test.go b/internal/sddstatus/status_test.go index b6144e42..5c648ae1 100644 --- a/internal/sddstatus/status_test.go +++ b/internal/sddstatus/status_test.go @@ -402,6 +402,44 @@ func TestResolveApplyVerifyArchiveGates(t *testing.T) { wantNext: "verify", wantBlocked: "verify-report.md is not clearly passing.", }, + { + name: "archive ready when verify report has checkmark todo on same line", + seed: func(t *testing.T, root string) { + changeRoot := seedReadyChange(t, root, "thin", "- [x] 1.1 Work\n") + write(t, filepath.Join(changeRoot, "verify-report.md"), "# Verify\nStatus: PASS\n✅ TODO: finish audit\n✅ PENDING: test run\n") + }, + wantApply: ApplyAllDone, + wantApplyD: DependencyAllDone, + wantVerify: DependencyAllDone, + wantArchive: DependencyReady, + wantNext: "archive", + }, + { + name: "archive blocked when verify report has standalone todo without glyph", + seed: func(t *testing.T, root string) { + changeRoot := seedReadyChange(t, root, "thin", "- [x] 1.1 Work\n") + write(t, filepath.Join(changeRoot, "verify-report.md"), "# Verify\nStatus: PASS\ntodo: security review\n") + }, + wantApply: ApplyAllDone, + wantApplyD: DependencyAllDone, + wantVerify: DependencyReady, + wantArchive: DependencyBlocked, + wantNext: "verify", + wantBlocked: "verify-report.md is not clearly passing.", + }, + { + name: "archive blocked when verify report mixes checkmark todo and standalone todo", + seed: func(t *testing.T, root string) { + changeRoot := seedReadyChange(t, root, "thin", "- [x] 1.1 Work\n") + write(t, filepath.Join(changeRoot, "verify-report.md"), "# Verify\nStatus: PASS\n✅ TODO: finish audit\ntodo: security review\n") + }, + wantApply: ApplyAllDone, + wantApplyD: DependencyAllDone, + wantVerify: DependencyReady, + wantArchive: DependencyBlocked, + wantNext: "verify", + wantBlocked: "verify-report.md is not clearly passing.", + }, { name: "archive blocked when verify report says status not passed", seed: func(t *testing.T, root string) {