fix(gsd): fix pre-execution-checks false positives from backticks and task.files

Three fixes:
1. Strip backtick wrapping in normalizeFilePath — LLM-generated paths
   like \`src/foo.ts\` resolve to nonexistent paths, causing false blocks
2. Exclude task.files from existence checks — it includes files the task
   will create, so they legitimately don't pre-exist
3. Lower minimum task count from 2 to 1 — single-task slices are valid
   per the planning prompt

Closes #3649
Closes #3626

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tibsfox 2026-04-06 19:00:47 -07:00
parent b4c6229360
commit 08a79875bb
2 changed files with 14 additions and 8 deletions

View file

@ -237,9 +237,12 @@ export async function checkPackageExistence(
*/
export function normalizeFilePath(filePath: string): string {
if (!filePath) return filePath;
// Strip backtick wrapping from LLM-generated paths (#3649)
let normalized = filePath.replace(/`/g, "");
// Normalize path separators to forward slashes
let normalized = filePath.replace(/\\/g, "/");
normalized = normalized.replace(/\\/g, "/");
// Remove leading ./
while (normalized.startsWith("./")) {
@ -272,10 +275,13 @@ function getExpectedOutputsUpTo(tasks: TaskRow[], taskIndex: number): Set<string
}
/**
* Check that all files referenced in task.files and task.inputs either:
* Check that all files referenced in task.inputs either:
* 1. Exist on disk, OR
* 2. Are in a prior task's expected_output
*
*
* task.files ("files likely touched") is excluded it intentionally includes
* files the task will create, so they don't need to pre-exist (#3626).
*
* All paths are normalized before comparison to ensure ./src/a.ts matches src/a.ts.
*/
export function checkFilePathConsistency(
@ -287,7 +293,7 @@ export function checkFilePathConsistency(
for (let i = 0; i < tasks.length; i++) {
const task = tasks[i];
const priorOutputs = getExpectedOutputsUpTo(tasks, i);
const filesToCheck = [...task.files, ...task.inputs];
const filesToCheck = [...task.inputs];
for (const file of filesToCheck) {
// Skip empty strings

View file

@ -54,12 +54,12 @@ const VALIDATORS: Record<string, ContentValidatorFn> = {
function validatePlanSlice(content: string): ContentViolation[] {
const violations: ContentViolation[] = [];
// Must have at least 2 task entries (checkbox pattern)
// Must have at least 1 task entry — single-task slices are valid (#3649)
const taskCount = (content.match(/- \[[ x]\] \*\*T\d+/g) || []).length;
if (taskCount < 2) {
if (taskCount < 1) {
violations.push({
severity: "warning",
reason: `Slice plan has only ${taskCount} task(s) — expected at least 2`,
reason: `Slice plan has ${taskCount} task(s) — expected at least 1`,
});
}