From c8f0b551237549b96c3ab19f73a3944719dcfbfc Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Sun, 5 Jul 2026 22:26:16 -0700 Subject: [PATCH] fix(signals): move isTestFile/isCodeFile out of local-branch.ts to unbreak ui:typecheck path-matchers.ts importing isCodeFile/isTestFile from local-branch.ts pulled the whole review-scoring/Gittensor-API subsystem into any caller's import graph. focus-manifest.ts (which PR #3690 made import classifyChangedFile from path-matchers.ts) is reachable from the UI via a long-standing cross-boundary import, so ui:typecheck started failing repo-wide with Env/D1Database ambient-type errors it has no way to resolve. Both functions were only ever thin, self-contained path-matching helpers incidentally defined in local-branch.ts. Move them into path-matchers.ts (zero remaining dependency on local-branch.ts), with local-branch.ts importing them back and re-exporting for its existing importers. Add a structural test asserting path-matchers.ts never imports from local-branch.ts again. --- src/signals/local-branch.ts | 28 +++++++--------------------- src/signals/path-matchers.ts | 26 +++++++++++++++++++++++++- test/unit/path-matchers.test.ts | 15 +++++++++++++++ 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/signals/local-branch.ts b/src/signals/local-branch.ts index 028380311a..125bd53916 100644 --- a/src/signals/local-branch.ts +++ b/src/signals/local-branch.ts @@ -26,7 +26,7 @@ import { deriveEligibilityPlan } from "../services/eligibility-plan"; import { scenarioInputFromLocalBranchMetadata } from "../scenarios/input-model"; import { renderPublicScenarioSummary, type PublicScenarioSummary, type ScenarioSummaryInput } from "../scenarios/scenario-summary"; import { simulateOpenPrPressure } from "../services/open-pr-pressure-scenarios"; -import { isTestPath } from "./test-evidence"; +import { isCodeFile, isTestFile } from "./path-matchers"; export type LocalBranchChangedFile = { path: string; @@ -1261,26 +1261,12 @@ function safeRepoPath(path: string): string { return PUBLIC_LOCAL_PATH_PREFIX_PATTERN.test(String(path).replace(/\\/g, "/")) ? "[local path hidden]" : String(path || "(unknown path)").replace(/\\/g, "/"); } -export function isTestFile(file: string): boolean { - // Keep local scoring aligned with slop/test-evidence matchers (#561 / #1046). - return isTestPath(file); -} - -export function isCodeFile(file: string): boolean { - // cs/swift/groovy/php plus C/C++/Objective-C round out the native/JVM/.NET/Swift/PHP set: isTestPath already - // recognizes their `SomethingTest(s)`/`Spec` test files, so their source must - // count as code too — otherwise a C#/Swift/Groovy/PHP/native source file is neither test - // nor code in the local scorer. vue/svelte/astro align with review/rag.ts CODE_EXT_RE, - // review/visual/paths.ts, and rules/advisory.ts isCodePath so every classifier agrees. - // cc/hpp round out the C++ set alongside cpp/c/h (rag.ts already indexes all four). - // dart aligns with rag.ts and test-evidence's *_test.dart convention (hand-authored - // .dart is source; generated .g.dart/.freezed.dart stay non-code via isGeneratedFile). - return ( - /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|cc|c|h|hpp|m|vue|svelte|astro|dart)$/i.test( - file, - ) && !isTestFile(file) - ); -} +// isTestFile/isCodeFile now live in path-matchers.ts (#3690-followup: path-matchers.ts must never import +// FROM local-branch.ts -- it is reachable from apps/gittensory-ui/src/lib/registration-workspace.ts via +// focus-manifest.ts, and local-branch.ts pulls in the whole review-scoring/Gittensor-API subsystem, which +// breaks `ui:typecheck` under the UI's tsconfig (no Workers ambient types there). Re-exported here so this +// file's own many existing importers of isTestFile/isCodeFile don't need to change their import path. +export { isCodeFile, isTestFile }; function sameRepo(left: string, right: string): boolean { return left.toLowerCase() === right.toLowerCase(); diff --git a/src/signals/path-matchers.ts b/src/signals/path-matchers.ts index 104ee26e1c..dd89572411 100644 --- a/src/signals/path-matchers.ts +++ b/src/signals/path-matchers.ts @@ -1,10 +1,34 @@ -import { isCodeFile, isTestFile } from "./local-branch"; import { isTestPath } from "./test-evidence"; // Pure, deterministic path matchers for slop classification (#561). Siblings to `isTestFile` / // `isTestPath`: they identify changed files that are NOT genuine hand-authored effort — machine- // generated output, vendored/imported third-party code, minified bundles, dependency lockfiles, and // docs — so slop signals can tell a padded diff from real work. Path-only and side-effect-free. +// +// MUST NOT import from local-branch.ts (#3690-followup): this file is reachable from +// apps/gittensory-ui/src/lib/registration-workspace.ts via focus-manifest.ts, and local-branch.ts pulls in +// the whole review-scoring/Gittensor-API subsystem, which breaks `ui:typecheck` under the UI's tsconfig (no +// Workers ambient types there). local-branch.ts imports isTestFile/isCodeFile back FROM here instead. + +/** Keep local scoring aligned with slop/test-evidence matchers (#561 / #1046). */ +export function isTestFile(file: string): boolean { + return isTestPath(file); +} + +/** cs/swift/groovy/php plus C/C++/Objective-C round out the native/JVM/.NET/Swift/PHP set: isTestPath already + * recognizes their `SomethingTest(s)`/`Spec` test files, so their source must count as code too — + * otherwise a C#/Swift/Groovy/PHP/native source file is neither test nor code in the local scorer. + * vue/svelte/astro align with review/rag.ts CODE_EXT_RE, review/visual/paths.ts, and rules/advisory.ts + * isCodePath so every classifier agrees. cc/hpp round out the C++ set alongside cpp/c/h (rag.ts already + * indexes all four). dart aligns with rag.ts and test-evidence's *_test.dart convention (hand-authored + * .dart is source; generated .g.dart/.freezed.dart stay non-code via isGeneratedFile). */ +export function isCodeFile(file: string): boolean { + return ( + /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|py|rb|rs|kt|scala|java|go|sql|cs|swift|groovy|php|cpp|cc|c|h|hpp|m|vue|svelte|astro|dart)$/i.test( + file, + ) && !isTestFile(file) + ); +} function normalize(path: string): string { return String(path ?? "") diff --git a/test/unit/path-matchers.test.ts b/test/unit/path-matchers.test.ts index 3d9cb9dd5b..a2aa2a2c37 100644 --- a/test/unit/path-matchers.test.ts +++ b/test/unit/path-matchers.test.ts @@ -1,3 +1,6 @@ +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; import { classifyChangedFile, @@ -11,6 +14,18 @@ import { isVendoredFile, } from "../../src/signals/path-matchers"; +// Structural guard (#3690-followup): this file is reachable from apps/gittensory-ui/src/lib/ +// registration-workspace.ts via focus-manifest.ts's classifyChangedFile import. local-branch.ts pulls in +// the whole review-scoring/Gittensor-API subsystem, so an import from it here breaks `ui:typecheck` under +// the UI's tsconfig (no Workers ambient types there) -- confirmed by a real ~35-file "Cannot find name +// Env/D1Database" break when path-matchers.ts briefly imported isCodeFile/isTestFile from local-branch.ts. +describe("path-matchers.ts never imports from local-branch.ts", () => { + it("has no import statement referencing ./local-branch", () => { + const source = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "../../src/signals/path-matchers.ts"), "utf8"); + expect(source).not.toMatch(/from\s+["']\.\/local-branch["']/); + }); +}); + describe("isGeneratedFile", () => { it("matches generated output by directory, suffix, codegen, and source maps", () => { for (const path of [