diff --git a/extensions/pi-workflow/ui-policy.test.ts b/extensions/pi-workflow/ui-policy.test.ts index cd823b0..a73b886 100644 --- a/extensions/pi-workflow/ui-policy.test.ts +++ b/extensions/pi-workflow/ui-policy.test.ts @@ -76,6 +76,23 @@ test("backend workflow terminology does not imply a UI prototype", () => { assert.equal(result.impact, "none"); }); +test("configuration files named settings do not imply a UI prototype", () => { + const result = enforceUiImpact({ + plannerImpact: "none", + plannerEvidence: ["Configuration inventory only"], + affectedSurfaces: [], + facts: facts({ + candidatePaths: [ + "/home/user/.pi/agent/settings.json", + "config/settings.yaml", + ], + tags: ["configuration", "security"], + }), + }); + assert.equal(result.impact, "none"); + assert.equal(result.objectiveImpact, "none"); +}); + test("generic frontend evidence does not inflate a minor change", () => { const result = enforceUiImpact({ plannerImpact: "minor", diff --git a/extensions/pi-workflow/ui-policy.ts b/extensions/pi-workflow/ui-policy.ts index c4f113e..927cefb 100644 --- a/extensions/pi-workflow/ui-policy.ts +++ b/extensions/pi-workflow/ui-policy.ts @@ -41,6 +41,18 @@ const NON_TRIVIAL_PATH_PARTS = [ "/settings/", ]; +const UI_SOURCE_SUFFIXES = [ + ".astro", + ".css", + ".html", + ".jsx", + ".less", + ".scss", + ".svelte", + ".tsx", + ".vue", +]; + export interface UiImpactPolicyInput { plannerImpact: UiImpact; plannerEvidence: string[]; @@ -64,7 +76,13 @@ function objectiveEvidence(facts: WorkflowFacts) { } for (const candidatePath of facts.candidatePaths) { const normalized = `/${candidatePath.toLowerCase().replaceAll("\\", "/")}`; - if (NON_TRIVIAL_PATH_PARTS.some((part) => normalized.includes(part))) { + const isUiSource = UI_SOURCE_SUFFIXES.some((suffix) => + normalized.endsWith(suffix), + ); + if ( + isUiSource && + NON_TRIVIAL_PATH_PARTS.some((part) => normalized.includes(part)) + ) { evidence.push(`Affected path has non-trivial UI responsibility: ${candidatePath}`); } }