chore(deps): cover .mts/.cts in the shared-source scan, fix the README validate row

Both from Copilot's review of #1962.

The candidate-set enumeration matched only `.ts`/`.tsx`, while
`verify:format-coverage` and `verify:typecheck-coverage` both gate all four
TypeScript extensions. No `.mts`/`.cts` exists under `core/` or
`test-servers/src` today, which is exactly why the omission would have gone
unnoticed until a shared dependency arrived through one and skewed — the
failure this guard exists to prevent. Extract the rule as the exported
`isSharedSourceFile` predicate, cover all four extensions, and pin it with
regression tests (including path-boundary anchoring, so a `core-internal/`
sibling isn't swept in).

The README's `validate` row also omitted `test:scripts` and claimed typecheck
was cli/tui only; launcher runs one too, and web typechecks via `tsc -b`
inside its `build`. Restate the row to match the actual script chain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3
This commit is contained in:
cliffhall 2026-08-10 20:38:41 -04:00
commit e480fdc49d
2 changed files with 51 additions and 3 deletions

View file

@ -170,14 +170,30 @@ export function partitionSkew(skewed, tolerated = TOLERATED_SKEW) {
};
}
/** Tracked `.ts`/`.tsx` files under the shared first-party source trees. */
// The TypeScript extensions the shared trees can hold. Deliberately the same
// four `verify:format-coverage` and `verify:typecheck-coverage` gate on: a
// `.mts`/`.cts` under `core/` or `test-servers/src` is typechecked like any
// other source, so its imports must reach the candidate set too. None exist
// under those trees today, which is exactly why omitting them would go
// unnoticed until a new shared dependency arrived through one and skewed.
const SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts"];
/** Whether a repo-relative path is a TypeScript source of a shared tree. */
export function isSharedSourceFile(file) {
if (!SOURCE_EXTENSIONS.some((ext) => file.endsWith(ext))) return false;
// Anchored on a path boundary so a sibling whose name merely starts with a
// shared dir (`core-internal/`, `test-servers/src-legacy/`) isn't swept in.
return SHARED_SOURCE_DIRS.some((dir) => file.startsWith(`${dir}/`));
}
/** Tracked TypeScript files under the shared first-party source trees. */
function sharedSourceFiles() {
const out = execFileSync(
"git",
["ls-files", "--", ...SHARED_SOURCE_DIRS.map((d) => `${d}/**`)],
{ cwd: repoRoot, encoding: "utf8" },
);
return out.split("\n").filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
return out.split("\n").filter(isSharedSourceFile);
}
/**

View file

@ -9,11 +9,43 @@ import assert from "node:assert/strict";
import {
findSkew,
importedPackageNames,
isSharedSourceFile,
packageNameOf,
partitionSkew,
topLevelLockVersions,
} from "./verify-dep-lockstep.mjs";
test("isSharedSourceFile: all four TS extensions, not just .ts/.tsx", () => {
// `.mts`/`.cts` are gated by `verify:format-coverage` and
// `verify:typecheck-coverage` too. None exist under the shared trees today,
// so dropping them here would go unnoticed until a new shared dependency
// arrived through one — the skew this guard exists to catch (Copilot, #1962).
for (const ext of [".ts", ".tsx", ".mts", ".cts"]) {
assert.equal(isSharedSourceFile(`core/mcp/thing${ext}`), true, ext);
assert.equal(
isSharedSourceFile(`test-servers/src/thing${ext}`),
true,
`test-servers ${ext}`,
);
}
});
test("isSharedSourceFile: non-TS files and other trees are excluded", () => {
const rejected = [
"core/README.md", // not TypeScript
"core/mcp/data.json",
"clients/web/src/App.tsx", // a client's own sources resolve from the client
"scripts/verify-dep-lockstep.mjs",
"test-servers/configs/modern-http.json",
// Path-boundary anchoring: a sibling dir whose name merely starts with a
// shared dir's name must not be swept in.
"core-internal/thing.ts",
"test-servers/src-legacy/thing.ts",
];
for (const file of rejected)
assert.equal(isSharedSourceFile(file), false, file);
});
test("packageNameOf: bare names, scopes, and subpaths", () => {
const cases = [
["zod", "zod"],