From 767c499d9a58f0e6fda0f5e07380a3fb558acd01 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 16 May 2026 21:40:52 +0200 Subject: [PATCH] fix(markdown-renderer): collapse 3+ consecutive newlines in slice plan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task descriptions in slice plans sometimes contained double-blanks (model emits multi-paragraph content with its own paragraph padding, which survives normalizeMarkdownBlockSpacing's heading-only padding logic). The double blanks tripped MD012/no-multiple-blanks in pre-execution checks and blocked the autonomous loop at the execute-task phase. Live observation today: SF iter2 completed research-slice and plan-slice for M006/S01 cleanly, then pre-execution checks failed on the generated S01-PLAN.md with two MD012 violations at lines 99-100 and 126-127 (both inside task description paragraphs). SF paused "Autonomous mode paused (Escape)" awaiting user — autonomous loop stalled. auto_fix_check_failures: true in prefs should have handled this but doesn't run for files under .sf/milestones/ (separate bug worth filing). Fix at source: collapse runs of 3+ newlines to 2 in the final rendered slice plan. Surgical, no semantic change, defensive against future model-quirks too. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/resources/extensions/sf/markdown-renderer.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/resources/extensions/sf/markdown-renderer.js b/src/resources/extensions/sf/markdown-renderer.js index a0e3cae7d..9297d957b 100644 --- a/src/resources/extensions/sf/markdown-renderer.js +++ b/src/resources/extensions/sf/markdown-renderer.js @@ -587,7 +587,14 @@ function renderSlicePlanMarkdown(slice, tasks, gates = []) { } } lines.push(""); - return `${lines.join("\n").trimEnd()}\n`; + // Collapse 3+ consecutive newlines down to 2. Task descriptions sometimes + // contain double blanks (model-emitted multi-paragraph content + paragraph + // padding) that survive normalizeMarkdownBlockSpacing's heading-only logic. + // Without this, the rendered S0?-PLAN.md trips MD012/no-multiple-blanks in + // pre-execution checks and blocks the autonomous loop at execute-task. + // auto_fix_check_failures was supposed to handle this but doesn't run for + // .sf/milestones/ files — quicker to emit clean markdown at the source. + return `${lines.join("\n").trimEnd()}\n`.replace(/\n{3,}/g, "\n\n"); } export async function renderPlanFromDb(basePath, milestoneId, sliceId) { const slice = getSlice(milestoneId, sliceId);