From 96c86c5a78b1368893045a192f925604dc048276 Mon Sep 17 00:00:00 2001 From: Lex Christopherson Date: Wed, 25 Mar 2026 22:52:26 -0600 Subject: [PATCH 1/2] refactor: extract planning-state validation helpers in detectRogueFileWrites Deduplicate near-identical "has any non-empty field" checks for milestone and slice planning state into a shared hasNonEmptyFields() helper with field-name arrays, reducing 8 repeated String(row.field||"").trim() calls to 2 declarative one-liners. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../extensions/gsd/auto-post-unit.ts | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/resources/extensions/gsd/auto-post-unit.ts b/src/resources/extensions/gsd/auto-post-unit.ts index 1aa4471ad..7a1e32c0e 100644 --- a/src/resources/extensions/gsd/auto-post-unit.ts +++ b/src/resources/extensions/gsd/auto-post-unit.ts @@ -84,6 +84,14 @@ export interface RogueFileWrite { * in postUnitPostVerification() eventually ingests rogue files, but explicit * detection provides immediate diagnostics so operators know the prompt failed. */ +function hasNonEmptyFields(row: Record | null, fields: string[]): boolean { + if (!row) return false; + return fields.some(f => String(row[f] || "").trim().length > 0); +} + +const MILESTONE_PLANNING_FIELDS = ["title", "vision", "requirement_coverage", "boundary_map_markdown"]; +const SLICE_PLANNING_FIELDS = ["title", "demo", "risk", "depends"]; + export function detectRogueFileWrites( unitType: string, unitId: string, @@ -124,12 +132,7 @@ export function detectRogueFileWrites( if (!roadmapPath || !existsSync(roadmapPath)) return []; const dbRow = getMilestone(mid); - const hasPlanningState = !!dbRow && ( - String(dbRow.title || "").trim().length > 0 || - String(dbRow.vision || "").trim().length > 0 || - String(dbRow.requirement_coverage || "").trim().length > 0 || - String(dbRow.boundary_map_markdown || "").trim().length > 0 - ); + const hasPlanningState = hasNonEmptyFields(dbRow, MILESTONE_PLANNING_FIELDS); if (!hasPlanningState) { rogues.push({ path: roadmapPath, unitType, unitId }); @@ -142,12 +145,7 @@ export function detectRogueFileWrites( if (!planPath || !existsSync(planPath)) return []; const dbRow = getSlice(mid, sid); - const hasPlanningState = !!dbRow && ( - String(dbRow.title || "").trim().length > 0 || - String(dbRow.demo || "").trim().length > 0 || - String(dbRow.risk || "").trim().length > 0 || - String(dbRow.depends || "").trim().length > 0 - ); + const hasPlanningState = hasNonEmptyFields(dbRow, SLICE_PLANNING_FIELDS); if (!hasPlanningState) { rogues.push({ path: planPath, unitType, unitId }); From cc47fb58b38f562ce6c1ac10d5688d4c5743e6b4 Mon Sep 17 00:00:00 2001 From: Lex Christopherson Date: Wed, 25 Mar 2026 23:08:05 -0600 Subject: [PATCH 2/2] fix: use Record for hasNonEmptyFields to accept typed DB rows MilestoneRow and SliceRow interfaces don't have index signatures, so they can't be assigned to Record. Using Record allows the helper to accept any typed row object. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/resources/extensions/gsd/auto-post-unit.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resources/extensions/gsd/auto-post-unit.ts b/src/resources/extensions/gsd/auto-post-unit.ts index 7a1e32c0e..bff928beb 100644 --- a/src/resources/extensions/gsd/auto-post-unit.ts +++ b/src/resources/extensions/gsd/auto-post-unit.ts @@ -84,7 +84,8 @@ export interface RogueFileWrite { * in postUnitPostVerification() eventually ingests rogue files, but explicit * detection provides immediate diagnostics so operators know the prompt failed. */ -function hasNonEmptyFields(row: Record | null, fields: string[]): boolean { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function hasNonEmptyFields(row: Record | null, fields: string[]): boolean { if (!row) return false; return fields.some(f => String(row[f] || "").trim().length > 0); }