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:
7
.changeset/fn-8463-file-scope-root-extensions.md
Normal file
7
.changeset/fn-8463-file-scope-root-extensions.md
Normal 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.
|
||||||
@@ -9,6 +9,11 @@
|
|||||||
* FNXC:FileScopeClassification 2026-07-21-18:05:
|
* FNXC:FileScopeClassification 2026-07-21-18:05:
|
||||||
* Also covers fenced repro snippets (GitHub #2389) whose escaped backticks produced
|
* Also covers fenced repro snippets (GitHub #2389) whose escaped backticks produced
|
||||||
* tokens like `global.json\` and blocked import via InvalidFileScopeError.
|
* 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 { describe, expect, it } from "vitest";
|
||||||
import {
|
import {
|
||||||
@@ -17,18 +22,27 @@ import {
|
|||||||
isValidFileScopeEntry,
|
isValidFileScopeEntry,
|
||||||
} from "../file-scope-classification.js";
|
} from "../file-scope-classification.js";
|
||||||
import {
|
import {
|
||||||
isValidFileScopeEntry as storeIsValidFileScopeEntry,
|
isValidFileScopeEntry as storeFileScopeIsValidFileScopeEntry,
|
||||||
validateFileScopeInPromptContent,
|
validateFileScopeInPromptContent,
|
||||||
} from "../task-store/file-scope.js";
|
} from "../task-store/file-scope.js";
|
||||||
|
import { isValidFileScopeEntry as storeIsValidFileScopeEntry } from "../store.js";
|
||||||
import { buildBootstrapPrompt } from "../mesh-task-replication.js";
|
import { buildBootstrapPrompt } from "../mesh-task-replication.js";
|
||||||
|
|
||||||
describe("isValidFileScopeEntry", () => {
|
describe("isValidFileScopeEntry", () => {
|
||||||
it("accepts root-level repo files with letter-leading extensions", () => {
|
it("accepts root-level repo files with letter-leading extensions", () => {
|
||||||
const roots = [
|
const roots = [
|
||||||
"global.json",
|
"global.json",
|
||||||
|
"Directory.Build.props",
|
||||||
|
"Directory.Build.targets",
|
||||||
"Directory.Packages.props",
|
"Directory.Packages.props",
|
||||||
|
"nuget.config",
|
||||||
|
"NuGet.config",
|
||||||
|
".editorconfig",
|
||||||
"MyApp.slnx",
|
"MyApp.slnx",
|
||||||
"MyApp.sln",
|
"MyApp.sln",
|
||||||
|
"Cargo.toml",
|
||||||
|
"go.mod",
|
||||||
|
"pyproject.toml",
|
||||||
"tsconfig.json",
|
"tsconfig.json",
|
||||||
"package.json",
|
"package.json",
|
||||||
"pnpm-lock.yaml",
|
"pnpm-lock.yaml",
|
||||||
@@ -38,6 +52,7 @@ describe("isValidFileScopeEntry", () => {
|
|||||||
];
|
];
|
||||||
for (const path of roots) {
|
for (const path of roots) {
|
||||||
expect(isValidFileScopeEntry(path), path).toBe(true);
|
expect(isValidFileScopeEntry(path), path).toBe(true);
|
||||||
|
expect(storeFileScopeIsValidFileScopeEntry(path), `task-store:${path}`).toBe(true);
|
||||||
expect(storeIsValidFileScopeEntry(path), `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);
|
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", () => {
|
it("passes create/update File Scope validation for root-level extension paths", () => {
|
||||||
const { valid, invalid } = validateFileScopeInPromptContent(prompt);
|
const { valid, invalid } = validateFileScopeInPromptContent(prompt);
|
||||||
expect(invalid).toEqual([]);
|
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", () => {
|
it("still rejects git-ref tokens inside File Scope on create/update validation", () => {
|
||||||
const bad = `## File Scope
|
const bad = `## File Scope
|
||||||
- \`packages/core/src/store.ts\`
|
- \`packages/core/src/store.ts\`
|
||||||
|
|||||||
Reference in New Issue
Block a user