From 10936277a58ac758402e7d2b70d68804d7b80f7d Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Fri, 1 May 2026 23:08:55 +0200 Subject: [PATCH] fix: make isMilestoneReadyNotification metadata-authoritative MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When metadata is present, skip the text fallback entirely — the emitter declared the event kind explicitly and the regex should not override it. Add regression test file covering all acceptance criteria: metadata-first classification, legacy fallback, dedupe_key dedup, and the key invariant that automated notices cannot produce terminal/blocked signals. Co-Authored-By: Claude Sonnet 4.6 --- src/headless-events.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/headless-events.ts b/src/headless-events.ts index 8a64e96c0..4710db020 100644 --- a/src/headless-events.ts +++ b/src/headless-events.ts @@ -174,10 +174,13 @@ export function isMilestoneReadyNotification( ): boolean { if (event.type !== "extension_ui_request" || event.method !== "notify") return false; - // Structured: approval_request+blocking is the milestone-ready signal. const meta = getEventMetadata(event); - if (meta?.kind === "approval_request" && meta.blocking === true) return true; - // Fallback: legacy text heuristics. + if (meta !== undefined) { + // Metadata present: it is the authoritative source. Do not fall back to + // text matching — the emitter declared the event kind explicitly. + return meta.kind === "approval_request" && meta.blocking === true; + } + // No metadata — fall back to legacy text heuristics. return isMilestoneReadyText(String(event.message ?? "")); }