FN-8463: cover root-level File Scope files

Extend File Scope regression coverage for root-level configuration and solution files.

- Cover .NET and ecosystem-neutral root-level file entries
- Verify task-store and public store validation exports share classification
- Add a patch changeset for the File Scope validation fix

Files changed:
 .changeset/fn-8463-file-scope-root-extensions.md   |  7 +++
 .../__tests__/file-scope-classification.test.ts    | 64 +++++++++++++++++++++-
 2 files changed, 69 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-8463

Fusion-Task-Lineage: 149f0066-8ec5-4f9d-b5be-29f158a274cc

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-21 18:48:47 -07:00
parent 483c33a323
commit 315bc1a764
2 changed files with 69 additions and 2 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Accept root-level File Scope files with extensions such as global.json and solution files.
category: fix
dev: isValidFileScopeEntry no longer requires a slash; letter-leading final extensions share create/update validation with classification. Regression coverage tracks GitHub #2389.

View File

@@ -9,6 +9,11 @@
* FNXC:FileScopeClassification 2026-07-21-18:05:
* Also covers fenced repro snippets (GitHub #2389) whose escaped backticks produced
* tokens like `global.json\` and blocked import via InvalidFileScopeError.
*
* FNXC:FileScopeClassification 2026-07-21-19:00:
* The regression invariant covers ecosystem-neutral root files, not only the four-path
* .NET repro. Classification, create/update validation, and the public store re-export
* must agree while duplicate, read-only, and invalid tokens remain excluded from writes.
*/
import { describe, expect, it } from "vitest";
import {
@@ -17,18 +22,27 @@ import {
isValidFileScopeEntry,
} from "../file-scope-classification.js";
import {
isValidFileScopeEntry as storeIsValidFileScopeEntry,
isValidFileScopeEntry as storeFileScopeIsValidFileScopeEntry,
validateFileScopeInPromptContent,
} from "../task-store/file-scope.js";
import { isValidFileScopeEntry as storeIsValidFileScopeEntry } from "../store.js";
import { buildBootstrapPrompt } from "../mesh-task-replication.js";
describe("isValidFileScopeEntry", () => {
it("accepts root-level repo files with letter-leading extensions", () => {
const roots = [
"global.json",
"Directory.Build.props",
"Directory.Build.targets",
"Directory.Packages.props",
"nuget.config",
"NuGet.config",
".editorconfig",
"MyApp.slnx",
"MyApp.sln",
"Cargo.toml",
"go.mod",
"pyproject.toml",
"tsconfig.json",
"package.json",
"pnpm-lock.yaml",
@@ -38,6 +52,7 @@ describe("isValidFileScopeEntry", () => {
];
for (const path of roots) {
expect(isValidFileScopeEntry(path), path).toBe(true);
expect(storeFileScopeIsValidFileScopeEntry(path), `task-store:${path}`).toBe(true);
expect(storeIsValidFileScopeEntry(path), `store:${path}`).toBe(true);
}
});
@@ -82,7 +97,8 @@ describe("isValidFileScopeEntry", () => {
}
});
it("keeps create/update validation and classification on the same function", () => {
it("keeps create/update validation and store exports on the same function", () => {
expect(storeFileScopeIsValidFileScopeEntry).toBe(isValidFileScopeEntry);
expect(storeIsValidFileScopeEntry).toBe(isValidFileScopeEntry);
});
});
@@ -109,6 +125,28 @@ describe("extractEffectiveWriteScopeFromPrompt / validateFileScopeInPromptConten
]);
});
it("retains the broader .NET root set in effective scope", () => {
const rootPaths = [
".editorconfig",
"Directory.Build.props",
"Directory.Build.targets",
"Directory.Packages.props",
"global.json",
"nuget.config",
"MyApp.slnx",
];
const broaderPrompt = `## File Scope
${rootPaths.map((path) => `- \`${path}\` (new)`).join("\n")}
- \`src/MyApp/Program.cs\` (new)
`;
expect(extractEffectiveWriteScopeFromPrompt(broaderPrompt)).toEqual([
...rootPaths,
"src/MyApp/Program.cs",
]);
expect(validateFileScopeInPromptContent(broaderPrompt).invalid).toEqual([]);
});
it("passes create/update File Scope validation for root-level extension paths", () => {
const { valid, invalid } = validateFileScopeInPromptContent(prompt);
expect(invalid).toEqual([]);
@@ -120,6 +158,28 @@ describe("extractEffectiveWriteScopeFromPrompt / validateFileScopeInPromptConten
]);
});
it("omits duplicate, read-only, conditional, and invalid tokens from write scope", () => {
const classified = `## File Scope
- \`global.json\`
- \`global.json\`
Read-only context:
- \`Directory.Build.props\`
Files changed:
- \`src/MyApp/Program.cs\`
Only if changed:
- \`.changeset/fix.md\`
## Steps
`;
expect(extractEffectiveWriteScopeFromPrompt(classified)).toEqual([
"global.json",
"src/MyApp/Program.cs",
]);
});
it("still rejects git-ref tokens inside File Scope on create/update validation", () => {
const bad = `## File Scope
- \`packages/core/src/store.ts\`