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
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
outputs:
backend: ${{ steps.filter.outputs.backend }}
backendConfig: ${{ steps.filter.outputs.backendConfig }}
cocoDev: ${{ steps.filter.outputs.cocoDev }}
ui: ${{ steps.filter.outputs.ui }}
uiContract: ${{ steps.filter.outputs.uiContract }}
mcp: ${{ steps.filter.outputs.mcp }}
Expand Down Expand Up @@ -160,6 +161,11 @@ jobs:
observability:
- 'grafana/dashboards/**'
- 'prometheus/rules/**'
# #9649: the coco-dev version manifest and the KBS kustomization that must stay in lockstep, plus the
# checker itself. Nothing else in the workflow references k8s/, so this is its own narrow filter.
cocoDev:
- 'k8s/coco-dev/**'
- 'scripts/check-coco-dev-versions*.ts'
# The UI's own app code -- triggers the FULL toolchain (lint/typecheck/test/build).
# A dependency bump (package.json/package-lock.json) stays here too since it can break the UI
# build or types in ways only that full toolchain would catch.
Expand Down Expand Up @@ -406,6 +412,18 @@ jobs:
- name: Re-gate sort-key check
if: ${{ github.event_name == 'push' || needs.changes.outputs.backend == 'true' }}
run: npm run regate-sort-key:check
# #9649: coco-dev-versions:check keeps k8s/coco-dev/versions.json and the KBS kustomization in lockstep, but
# was wired into test:ci only -- no CI job ran it, and nothing in this workflow references k8s otherwise.
# Gated on the new cocoDev path filter plus the standard push clause every drift step above uses.
- name: Coco-dev versions drift check
if: ${{ github.event_name == 'push' || needs.changes.outputs.cocoDev == 'true' }}
run: npm run coco-dev-versions:check
# #9649: import-specifiers:check (#9221) scans import specifiers across src/scripts/test and every
# packages/* workspace (BUNDLER_ROOTS/NODENEXT_ROOTS), but no CI job ran it -- #9240/#9249 both drifted in
# after it shipped. Gate on every filter covering one of those scanned roots.
- name: Import-specifiers drift check
if: ${{ github.event_name == 'push' || needs.changes.outputs.backend == 'true' || needs.changes.outputs.mcp == 'true' || needs.changes.outputs.engine == 'true' || needs.changes.outputs.miner == 'true' || needs.changes.outputs.discoveryIndex == 'true' }}
run: npm run import-specifiers:check

# #9563: every webhook-owned handler must decide about #9312's redelivery guard out loud. Three had
# silently skipped it -- one writing duplicate permanent suppression rows, two spending a second paid
Expand Down
7 changes: 6 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,12 @@ 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);
// #9667: entryFilePattern/providerFilePattern are compiled by globToRegExp against a CANONICALIZED path (no `i`
// flag), so they only match lowercase. Canonicalize each issue-body token before testing it, exactly as the
// changedFiles side already does below — otherwise an issue naming `Registry/Subnets/Foo.json` produces no
// mentionedPath and the deliverable gate silently doesn't run. `.find` still returns the ORIGINAL token, so the
// "missing" verdict's mentionedPath stays what the contributor/maintainer see in the issue body.
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
6 changes: 6 additions & 0 deletions test/unit/check-import-specifiers-script.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,10 @@ describe("check-import-specifiers script", () => {
expect(multi.map((v) => v.file)).toEqual(["src/a.ts", "src/z.ts", "src/z.ts"]);
expect(multi.map((v) => v.specifier)).toEqual(["./c.js", "./a.js", "./b.js"]);
});

it("REGRESSION (#9649): the real repo tree has zero import-specifier violations (the guard was wired into test:ci only, so #9240 and #9249 both reached main while it was local-only)", () => {
// No injected listSourceFiles/readFile: runs the real gate against the real src/scripts/test/packages tree,
// matching the real-tree regression guards on validate-no-hand-written-js and coverage-bolt-on-filenames.
expect(findImportSpecifierViolations()).toEqual([]);
});
});
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 @@ -676,6 +676,32 @@ describe("checkContentLaneDeliverable (generic, spec-driven — #content-lane-de
expect(checkContentLaneDeliverable(entryOnlySpec, issueText, ["tests/foo.test.mjs"]).verdict).toBe("missing");
});

it("REGRESSION (#9667): is 'missing' for a MIXED-CASE issue-body path, preserving the original case in mentionedPath", () => {
// The spec patterns are compiled against a canonicalized (lowercased) path, so before the fix a mixed-case
// issue path produced no mentionedPath and the gate silently returned not-applicable.
const issueText = "Add the missing surfaces to Registry/Subnets/Foo.json before merging.";
expect(checkContentLaneDeliverable(spec, issueText, ["tests/foo-verify.test.mjs"])).toEqual({
verdict: "missing",
mentionedPath: "Registry/Subnets/Foo.json", // original case, as the contributor/maintainer see it
});
});

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

it("#9667: all-lowercase issue-path behaviour is unchanged (delivered when the file is touched, missing otherwise)", () => {
const issueText = "Add registry/subnets/foo.json.";
expect(checkContentLaneDeliverable(spec, issueText, ["registry/subnets/foo.json"])).toEqual({ verdict: "delivered" });
expect(checkContentLaneDeliverable(spec, issueText, ["tests/foo.test.mjs"])).toEqual({ verdict: "missing", mentionedPath: "registry/subnets/foo.json" });
});

it("#9667: an issue path matching NO spec pattern still returns not-applicable (the fix does not over-broaden)", () => {
// A mixed-case path that is not a registry entry/provider file must not now spuriously match.
expect(checkContentLaneDeliverable(spec, "See Docs/README.md for details.", ["docs/readme.md"])).toEqual({ verdict: "not-applicable" });
});

// #content-lane-deliverable follow-up (metagraphed #7060-class gap): the literal-path scan alone is BLIND
// to metagraphed's own ~120 "MCP execute: verify + wire SN*" issues, whose bodies write the registry path
// with a generic `<slug>` documentation placeholder rather than the real resolved filename — confirmed
Expand Down
Loading