From 038938f2acbdfe447bb8e60c7bc9b0de0ee3b3ef Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 2 May 2026 01:52:29 +0200 Subject: [PATCH] fix: headless EXIT_RELOAD case + notification dedup boundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - src/headless-events.ts: add case "reload" → EXIT_RELOAD (12). EXIT_RELOAD sentinel was defined but unused — "reload" status fell through to EXIT_ERROR (1). - src/resources/extensions/sf/notification-store.ts:109: use <= for dedup window so a second identical notification at exactly DEDUP_WINDOW_MS still gets suppressed (was off-by-one at boundary). - src/resources/extensions/sf/definition-loader.ts: pending docstring tweaks from autonomous sweep. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/headless-events.ts | 2 ++ src/resources/extensions/sf/definition-loader.ts | 6 ++++++ src/resources/extensions/sf/notification-store.ts | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/headless-events.ts b/src/headless-events.ts index 4710db020..272fb9843 100644 --- a/src/headless-events.ts +++ b/src/headless-events.ts @@ -43,6 +43,8 @@ export function mapStatusToExitCode(status: string): number { return EXIT_BLOCKED; case "cancelled": return EXIT_CANCELLED; + case "reload": + return EXIT_RELOAD; default: return EXIT_ERROR; } diff --git a/src/resources/extensions/sf/definition-loader.ts b/src/resources/extensions/sf/definition-loader.ts index c18718567..f7c7b911e 100644 --- a/src/resources/extensions/sf/definition-loader.ts +++ b/src/resources/extensions/sf/definition-loader.ts @@ -19,6 +19,9 @@ import { parse } from "yaml"; // ─── Public TypeScript Types (camelCase) ───────────────────────────────── +/** + * Step verification policy: content-heuristic, shell-command, prompt-verify, or human-review. + */ export type VerifyPolicy = | { policy: "content-heuristic"; minSize?: number; pattern?: string } | { policy: "shell-command"; command: string } @@ -32,6 +35,9 @@ export interface IterateConfig { pattern: string; } +/** + * Workflow step definition with prompt, dependencies, produced artifacts, and optional verification. + */ export interface StepDefinition { /** Unique step identifier within the workflow. */ id: string; diff --git a/src/resources/extensions/sf/notification-store.ts b/src/resources/extensions/sf/notification-store.ts index ed740f233..a1a9da0f2 100644 --- a/src/resources/extensions/sf/notification-store.ts +++ b/src/resources/extensions/sf/notification-store.ts @@ -106,7 +106,7 @@ export function appendNotification( : `${_basePath}:${severity}:${source}:${persistedMessage}`; const now = Date.now(); const lastSeen = _recentMessageTimestamps.get(dedupKey); - if (lastSeen !== undefined && now - lastSeen < DEDUP_WINDOW_MS) return; + if (lastSeen !== undefined && now - lastSeen <= DEDUP_WINDOW_MS) return; _recentMessageTimestamps.set(dedupKey, now); if (_recentMessageTimestamps.size > DEDUP_PRUNE_THRESHOLD) { for (const [key, ts] of _recentMessageTimestamps) { @@ -347,7 +347,7 @@ function _withLock(basePath: string, fn: () => T): T { try { const stat = readFileSync(lockPath, "utf-8"); const lockTime = parseInt(stat, 10); - if (Date.now() - lockTime > 5000) { + if (Number.isFinite(lockTime) && Date.now() - lockTime > 5000) { try { unlinkSync(lockPath); } catch {