Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/review/content-lane/registry-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,13 +679,17 @@ export interface RegistryScopeResult {

/**
* Generic surface-model scope classifier: in scope when the PR edits exactly ONE entry file (or, entry-free,
* one flat provider file); the spec's provider + artifact files are allowed companions; any other path —
* including a retired candidate path the registry no longer accepts — makes it mixed-files.
* one flat provider file); the spec's provider + artifact files are allowed companions. A registry-looking
* submission with too many entry/provider files is malformed and stays in the lane as mixed-files; an unrelated
* PR with no direct registry files remains not-direct-submission.
*/
export function classifyRegistryPrScope(spec: RegistryLaneSpec, changedFiles: string[]): RegistryScopeResult {
const files = (changedFiles ?? []).map((f) => String(f || "").trim()).filter(Boolean);
const entryFiles = files.filter((f) => spec.entryFilePattern.test(f));
const providerFiles = spec.providerFilePattern ? files.filter((f) => spec.providerFilePattern!.test(f)) : [];
if (entryFiles.length > 1 || (entryFiles.length === 0 && providerFiles.length > 1)) {
return { scope: "mixed-files", directFile: null, isProvider: false };
}
const isEntryPr = entryFiles.length === 1;
const isProviderPr = entryFiles.length === 0 && providerFiles.length === 1;
if (!isEntryPr && !isProviderPr) {
Expand Down
14 changes: 14 additions & 0 deletions test/unit/content-lane-orchestrator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ describe("runSurfaceReview (deterministic + decisive: merge/close, rarely manual
expect((await review([SUBNET, "src/index.ts"], {}))?.verdict).toBe("close");
});

it("closes a multi-entry registry submission instead of deferring to the generic gate", async () => {
const calls: string[] = [];
const result = await runSurfaceReview(METAGRAPHED_LANE_SPEC, {
changedFiles: [SUBNET, "registry/subnets/bar.json"],
loadFile: (path, ref) => {
calls.push(`${ref}:${path}`);
return Promise.resolve(null);
},
});

expect(result?.verdict).toBe("close");
expect(calls).toEqual([]);
});

it("merges a valid provider submission and CLOSES an invalid one (never manual)", async () => {
const okProvider = { [`head:${PROVIDER}`]: JSON.stringify({ provider: { id: "acme", name: "Acme", website_url: "https://acme.example" } }) };
expect((await review([PROVIDER], okProvider))?.verdict).toBe("merge");
Expand Down
7 changes: 4 additions & 3 deletions test/unit/content-lane-registry-logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,10 @@ describe("classifyRegistryPrScope (generic surface model, metagraphed spec)", ()
expect(classifyRegistryPrScope(spec, ["registry/subnets/actual.json", "registry/candidates/community/foo.json"]).scope).toBe("mixed-files");
});

it("is not-direct for no submission, and >1 entry file", () => {
it("is not-direct for no submission, but mixed for too many direct registry files", () => {
expect(classifyRegistryPrScope(spec, ["README.md"]).scope).toBe("not-direct-submission");
expect(classifyRegistryPrScope(spec, ["registry/subnets/a.json", "registry/subnets/b.json"]).scope).toBe("not-direct-submission");
expect(classifyRegistryPrScope(spec, ["registry/subnets/a.json", "registry/subnets/b.json"]).scope).toBe("mixed-files");
expect(classifyRegistryPrScope(spec, ["registry/providers/a.json", "registry/providers/b.json"]).scope).toBe("mixed-files");
expect(isRegistrySubmissionScope("not-direct-submission")).toBe(false);
});

Expand All @@ -314,7 +315,7 @@ describe("classifyRegistryPrScope (generic surface model, metagraphed spec)", ()
it("works for a minimal spec with no provider/artifact patterns (a bare registry)", () => {
const bare: RegistryLaneSpec = { entryFilePattern: /^data\/[a-z]+\.json$/, collectionField: "entries" };
expect(classifyRegistryPrScope(bare, ["data/x.json"]).scope).toBe("entry-submission");
expect(classifyRegistryPrScope(bare, ["data/x.json", "data/y.json"]).scope).toBe("not-direct-submission");
expect(classifyRegistryPrScope(bare, ["data/x.json", "data/y.json"]).scope).toBe("mixed-files");
expect(classifyRegistryPrScope(bare, ["data/x.json", "other.json"]).scope).toBe("mixed-files");
});
});
Expand Down
Loading