Skip to content
Closed
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
6 changes: 5 additions & 1 deletion src/review/content-lane/registry-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,11 @@ export function checkContentLaneDeliverable(
issueTitle?: string,
): ContentLaneDeliverableCheck {
const matchesSpec = (candidate: string): boolean => spec.entryFilePattern.test(candidate) || (spec.providerFilePattern?.test(candidate) ?? false);
const mentionedPath = extractPathTokens(issueText).find(matchesSpec);
// Test each token CANONICALIZED (globToRegExp compiles the spec patterns against a canonicalized path with no
// `i` flag, so an uppercase / `./`-prefixed / `\`-separated body token would otherwise never match), exactly
// as classifyRegistryPrScope's matchesPattern does — while `.find` still returns the ORIGINAL token so the
// `mentionedPath` rendered into the public PR comment quotes the issue body verbatim.
const mentionedPath = extractPathTokens(issueText).find((token) => matchesSpec(canonicalize(token)));
const titleImplies = !mentionedPath && Boolean(issueTitle && spec.issueTitleImpliesEntryPattern?.test(issueTitle));
if (!mentionedPath && !titleImplies) return { verdict: "not-applicable" };
const delivered = changedFiles.some((file) => matchesSpec(canonicalize(file)));
Expand Down
26 changes: 26 additions & 0 deletions test/unit/content-lane-registry-logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,32 @@ describe("checkContentLaneDeliverable (generic, spec-driven — #content-lane-de
expect(checkContentLaneDeliverable(spec, issueText, ["registry\\subnets\\foo.json"]).verdict).toBe("delivered");
});

it("regression (#9667): canonicalizes issue-BODY path tokens — a mixed-case path is 'missing' when undelivered, quoting the body verbatim", () => {
const issueText = "Add the missing surfaces to Registry/Subnets/Foo.json.";
// No matching changed file ⇒ missing; the reported mentionedPath stays the ORIGINAL mixed-case token so the
// public PR comment quotes the issue as written (canonicalizing it would corrupt that text).
expect(checkContentLaneDeliverable(spec, issueText, ["tests/foo-verify.test.mjs"])).toEqual({
verdict: "missing",
mentionedPath: "Registry/Subnets/Foo.json",
});
});

it("regression (#9667): the same mixed-case body is 'delivered' when the PR changes the (lowercase) file", () => {
const issueText = "Add the missing surfaces to Registry/Subnets/Foo.json.";
expect(checkContentLaneDeliverable(spec, issueText, ["registry/subnets/foo.json"]).verdict).toBe("delivered");
});

it("all-lowercase body behaviour is byte-identical — delivered when the file is touched, missing (quoting the token) when it is not", () => {
const issueText = "Add registry/subnets/foo.json.";
expect(checkContentLaneDeliverable(spec, issueText, ["registry/subnets/foo.json"]).verdict).toBe("delivered");
expect(checkContentLaneDeliverable(spec, issueText, ["tests/foo.test.mjs"])).toEqual({ verdict: "missing", mentionedPath: "registry/subnets/foo.json" });
});

it("does not over-broaden: a real body path token matching NO spec pattern is still not-applicable", () => {
const result = checkContentLaneDeliverable(spec, "See docs/architecture/overview.md for context.", ["registry/subnets/foo.json"]);
expect(result).toEqual({ verdict: "not-applicable" });
});

it("is not-applicable for a spec with no providerFilePattern configured, even if the issue mentions an entry-shaped path (guards the optional-chaining branch)", () => {
const entryOnlySpec: RegistryLaneSpec = { entryFilePattern: SUBNET_ENTRY_PATTERN, collectionField: "surfaces" };
const issueText = "Add registry/subnets/foo.json.";
Expand Down
Loading