From f183bd80a6819e519d1ed7a137596c33c4e9007e Mon Sep 17 00:00:00 2001 From: "itarun.p" Date: Sun, 26 Jul 2026 05:01:46 +0700 Subject: [PATCH] chore(ci): the product gate ran a tool the product does not ship MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses #107's "at minimum" path. `scripts/graph/` is a SCIP code-graph indexer for the maintainer's own agent tooling; it is not part of @ipv9/tokentracker-cli and ships to nobody, yet its 12 test files ran in every ci:local, on every PR, for every contributor. The cost that matters is not the runtime. It is that a tenth of the suite tested something users never run, so the repo looked well tested while four product parsers (hermes, codebuddy, craft, kimi) have no test at all — the inversion #104 is about. test/graph-auto-index-*.test.js -> test/graph/, which the flat `test/*.test.js` glob no longer picks up. Product suite drops 909 -> 893. Two path fixes the move required: the `../scripts/graph/` requires, and one path.join(__dirname, "..", "scripts", ...) in parse-scip that assumed the old depth. Caught by running them, not by reading them. They still run. A new workflow does it in its own job, triggered only on scripts/graph/** or test/graph/**. Deliberately a real blocking job rather than continue-on-error: a gate that cannot fail reports success it did not earn. Path filtering makes it absent when irrelevant, not green when broken. The trade, stated in the workflow file: a change elsewhere that breaks the indexer is caught on the next graph-touching PR rather than immediately. The tool has no runtime dependency on the product. Nothing in the product imports scripts/graph — only the graph:auto-index and graph:scip npm scripts reference it. Full move to its own repository is still open on #107; this is the "if it must stay for now" half. ci:local exit 0: 893 root, 256 dashboard. test:graph: 16 pass. --- .github/workflows/graph-tools.yml | 52 +++++++++++++++++++ CONTRIBUTING.md | 10 +++- package.json | 1 + .../graph-auto-index-config.test.js | 2 +- .../graph-auto-index-domains.test.js | 2 +- .../graph-auto-index-import.test.js | 2 +- .../graph-auto-index-metrics.test.js | 2 +- .../graph-auto-index-multi-scip.test.js | 2 +- .../graph-auto-index-orchestrator.test.js | 2 +- .../graph-auto-index-parse-scip.test.js | 2 +- .../{ => graph}/graph-auto-index-plan.test.js | 2 +- .../graph-auto-index-scip-runner.test.js | 2 +- .../graph-auto-index-split.test.js | 2 +- ...-auto-index-validate-relative-path.test.js | 2 +- .../graph-auto-index-validation.test.js | 2 +- 15 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/graph-tools.yml rename test/{ => graph}/graph-auto-index-config.test.js (89%) rename test/{ => graph}/graph-auto-index-domains.test.js (84%) rename test/{ => graph}/graph-auto-index-import.test.js (97%) rename test/{ => graph}/graph-auto-index-metrics.test.js (93%) rename test/{ => graph}/graph-auto-index-multi-scip.test.js (93%) rename test/{ => graph}/graph-auto-index-orchestrator.test.js (87%) rename test/{ => graph}/graph-auto-index-parse-scip.test.js (92%) rename test/{ => graph}/graph-auto-index-plan.test.js (85%) rename test/{ => graph}/graph-auto-index-scip-runner.test.js (86%) rename test/{ => graph}/graph-auto-index-split.test.js (82%) rename test/{ => graph}/graph-auto-index-validate-relative-path.test.js (87%) rename test/{ => graph}/graph-auto-index-validation.test.js (92%) diff --git a/.github/workflows/graph-tools.yml b/.github/workflows/graph-tools.yml new file mode 100644 index 00000000..f31f1779 --- /dev/null +++ b/.github/workflows/graph-tools.yml @@ -0,0 +1,52 @@ +name: Graph tools + +# `scripts/graph/` is a SCIP code-graph indexer built for the maintainer's own +# agent tooling. It is not part of @ipv9/tokentracker-cli and ships to nobody, +# but its 12 test files were a tenth of the suite and ran in every `ci:local` +# on every PR — distorting the coverage picture while four product parsers have +# no test at all (#104), and charging every contributor for it (#107). +# +# So the product gate no longer runs them. This workflow does, and ONLY when +# the graph code or its tests change. +# +# Deliberately a real, blocking job rather than a `continue-on-error` one: a +# gate that cannot fail is a gate that reports success it did not earn. Path +# filtering makes it absent when irrelevant, not green when broken. +# +# Note this does NOT make the tests run on unrelated PRs, so a change elsewhere +# that breaks the indexer would be caught only on the next graph-touching PR. +# That is the accepted trade for taking it off the product's critical path; the +# tool has no runtime dependency on the product. + +on: + pull_request: + branches: [main] + paths: + - "scripts/graph/**" + - "test/graph/**" + - ".github/workflows/graph-tools.yml" + +concurrency: + group: graph-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test-graph: + name: test:graph + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v6 + with: + node-version: 24 + cache: npm + cache-dependency-path: package-lock.json + + - name: Install root dependencies + run: npm ci + + - name: Run the graph-tool tests + run: npm run test:graph diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 84f9a71d..2db7e708 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,11 +25,19 @@ node bin/tracker.js doctor # Health check ## Tests ```bash -npm test # Full suite (node --test over test/*.test.js) +npm test # Product suite (node --test over test/*.test.js) node --test test/rollout-parser.test.js # A single test file npm run ci:local # Tests + validations + builds (everything CI runs) +npm run test:graph # Maintainer tooling only — see below ``` +`test/graph/` covers `scripts/graph/`, a SCIP code-graph indexer built for the +maintainer's own agent tooling. It is not part of the published package and +ships to nobody, so it is **not** in `npm test` or `ci:local` — its 12 files +were a tenth of the suite, which made the coverage picture look better than it +is while four product parsers still have no test at all. CI runs it in its own +job, triggered only when `scripts/graph/**` or `test/graph/**` changes. + If you're touching the dashboard: ```bash diff --git a/package.json b/package.json index 449d2ecb..6f0f827a 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "audit:tokens": "node scripts/audit-token-correctness.cjs", "graph:auto-index": "node scripts/graph/auto-index.cjs", "graph:scip": "node scripts/graph/generate-scip.cjs", + "test:graph": "node --test test/graph/*.test.js", "pricing:build-seed": "node scripts/build-pricing-seed.cjs", "prepublishOnly": "node scripts/build-pricing-seed.cjs", "test": "node --test test/*.test.js", diff --git a/test/graph-auto-index-config.test.js b/test/graph/graph-auto-index-config.test.js similarity index 89% rename from test/graph-auto-index-config.test.js rename to test/graph/graph-auto-index-config.test.js index e1536196..63fada61 100644 --- a/test/graph-auto-index-config.test.js +++ b/test/graph/graph-auto-index-config.test.js @@ -1,7 +1,7 @@ const test = require("node:test"); const assert = require("node:assert"); const path = require("node:path"); -const { loadGraphConfig } = require("../scripts/graph/lib/config.cjs"); +const { loadGraphConfig } = require("../../scripts/graph/lib/config.cjs"); test("loadGraphConfig collects tsconfig paths and defaults", () => { const fakeFs = { diff --git a/test/graph-auto-index-domains.test.js b/test/graph/graph-auto-index-domains.test.js similarity index 84% rename from test/graph-auto-index-domains.test.js rename to test/graph/graph-auto-index-domains.test.js index 1b63084e..ce812500 100644 --- a/test/graph-auto-index-domains.test.js +++ b/test/graph/graph-auto-index-domains.test.js @@ -1,7 +1,7 @@ const test = require("node:test"); const assert = require("node:assert"); const path = require("node:path"); -const { discoverDomains } = require("../scripts/graph/lib/domain-discovery.cjs"); +const { discoverDomains } = require("../../scripts/graph/lib/domain-discovery.cjs"); test("discoverDomains picks known roots that exist", () => { const fakeFs = { diff --git a/test/graph-auto-index-import.test.js b/test/graph/graph-auto-index-import.test.js similarity index 97% rename from test/graph-auto-index-import.test.js rename to test/graph/graph-auto-index-import.test.js index e7cadfaa..7fe93bb0 100644 --- a/test/graph-auto-index-import.test.js +++ b/test/graph/graph-auto-index-import.test.js @@ -4,7 +4,7 @@ const path = require("node:path"); const os = require("node:os"); const fs = require("node:fs"); const { execFileSync } = require("node:child_process"); -const { importScip } = require("../scripts/graph/lib/importer.cjs"); +const { importScip } = require("../../scripts/graph/lib/importer.cjs"); test("importScip writes sqlite with documents, symbols, and occurrences", () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "graph-import-")); diff --git a/test/graph-auto-index-metrics.test.js b/test/graph/graph-auto-index-metrics.test.js similarity index 93% rename from test/graph-auto-index-metrics.test.js rename to test/graph/graph-auto-index-metrics.test.js index 0f7973a5..a3709933 100644 --- a/test/graph-auto-index-metrics.test.js +++ b/test/graph/graph-auto-index-metrics.test.js @@ -1,7 +1,7 @@ const test = require("node:test"); const assert = require("node:assert"); const path = require("node:path"); -const { scanDomainMetrics } = require("../scripts/graph/lib/metrics.cjs"); +const { scanDomainMetrics } = require("../../scripts/graph/lib/metrics.cjs"); test("scanDomainMetrics counts files and noise", () => { const fakeFs = { diff --git a/test/graph-auto-index-multi-scip.test.js b/test/graph/graph-auto-index-multi-scip.test.js similarity index 93% rename from test/graph-auto-index-multi-scip.test.js rename to test/graph/graph-auto-index-multi-scip.test.js index eb6325f8..6bc4ee57 100644 --- a/test/graph-auto-index-multi-scip.test.js +++ b/test/graph/graph-auto-index-multi-scip.test.js @@ -1,7 +1,7 @@ const test = require("node:test"); const assert = require("node:assert"); const path = require("node:path"); -const { runScipForPlan } = require("../scripts/graph/lib/scip-runner.cjs"); +const { runScipForPlan } = require("../../scripts/graph/lib/scip-runner.cjs"); test("runScipForPlan invokes scip-typescript per domain", () => { const calls = []; diff --git a/test/graph-auto-index-orchestrator.test.js b/test/graph/graph-auto-index-orchestrator.test.js similarity index 87% rename from test/graph-auto-index-orchestrator.test.js rename to test/graph/graph-auto-index-orchestrator.test.js index a6aa07a8..c93e800b 100644 --- a/test/graph-auto-index-orchestrator.test.js +++ b/test/graph/graph-auto-index-orchestrator.test.js @@ -1,7 +1,7 @@ const test = require("node:test"); const assert = require("node:assert"); const path = require("node:path"); -const { buildPlan } = require("../scripts/graph/auto-index.cjs"); +const { buildPlan } = require("../../scripts/graph/auto-index.cjs"); test("buildPlan returns decision and domains", () => { const plan = buildPlan({ diff --git a/test/graph-auto-index-parse-scip.test.js b/test/graph/graph-auto-index-parse-scip.test.js similarity index 92% rename from test/graph-auto-index-parse-scip.test.js rename to test/graph/graph-auto-index-parse-scip.test.js index f1d598ea..50cfa30e 100644 --- a/test/graph-auto-index-parse-scip.test.js +++ b/test/graph/graph-auto-index-parse-scip.test.js @@ -14,7 +14,7 @@ test("parseScipFile deserializes scip index", () => { const index = new scip.Index({ documents: [] }); fs.writeFileSync(scipPath, index.serializeBinary()); - const autoIndexPath = path.join(__dirname, "..", "scripts", "graph", "auto-index.cjs"); + const autoIndexPath = path.join(__dirname, "..", "..", "scripts", "graph", "auto-index.cjs"); const code = `${fs.readFileSync(autoIndexPath, "utf8")}\nmodule.exports._test = { parseScipFile };`; const localRequire = createRequire(autoIndexPath); const sandbox = { diff --git a/test/graph-auto-index-plan.test.js b/test/graph/graph-auto-index-plan.test.js similarity index 85% rename from test/graph-auto-index-plan.test.js rename to test/graph/graph-auto-index-plan.test.js index 675e3e32..31411e9b 100644 --- a/test/graph-auto-index-plan.test.js +++ b/test/graph/graph-auto-index-plan.test.js @@ -1,6 +1,6 @@ const test = require("node:test"); const assert = require("node:assert"); -const { writePlan } = require("../scripts/graph/lib/plan-writer.cjs"); +const { writePlan } = require("../../scripts/graph/lib/plan-writer.cjs"); test("writePlan writes graph.plan.json", () => { let written = null; diff --git a/test/graph-auto-index-scip-runner.test.js b/test/graph/graph-auto-index-scip-runner.test.js similarity index 86% rename from test/graph-auto-index-scip-runner.test.js rename to test/graph/graph-auto-index-scip-runner.test.js index 5b3fb102..47dc8b83 100644 --- a/test/graph-auto-index-scip-runner.test.js +++ b/test/graph/graph-auto-index-scip-runner.test.js @@ -1,7 +1,7 @@ const test = require("node:test"); const assert = require("node:assert"); const path = require("node:path"); -const { runScipForPlan } = require("../scripts/graph/lib/scip-runner.cjs"); +const { runScipForPlan } = require("../../scripts/graph/lib/scip-runner.cjs"); test("runScipForPlan throws when no tsconfig is available", () => { const fakeFs = { diff --git a/test/graph-auto-index-split.test.js b/test/graph/graph-auto-index-split.test.js similarity index 82% rename from test/graph-auto-index-split.test.js rename to test/graph/graph-auto-index-split.test.js index 83cb724e..32940b60 100644 --- a/test/graph-auto-index-split.test.js +++ b/test/graph/graph-auto-index-split.test.js @@ -1,6 +1,6 @@ const test = require("node:test"); const assert = require("node:assert"); -const { decideSplit } = require("../scripts/graph/lib/split-decision.cjs"); +const { decideSplit } = require("../../scripts/graph/lib/split-decision.cjs"); test("decideSplit chooses split when size threshold reached", () => { const decision = decideSplit({ diff --git a/test/graph-auto-index-validate-relative-path.test.js b/test/graph/graph-auto-index-validate-relative-path.test.js similarity index 87% rename from test/graph-auto-index-validate-relative-path.test.js rename to test/graph/graph-auto-index-validate-relative-path.test.js index e97b3264..c75bcddb 100644 --- a/test/graph-auto-index-validate-relative-path.test.js +++ b/test/graph/graph-auto-index-validate-relative-path.test.js @@ -1,7 +1,7 @@ const assert = require("node:assert/strict"); const test = require("node:test"); -const { validateScipCoverage } = require("../scripts/graph/lib/validate.cjs"); +const { validateScipCoverage } = require("../../scripts/graph/lib/validate.cjs"); test("validateScipCoverage accepts relative_path fields", () => { const result = validateScipCoverage({ diff --git a/test/graph-auto-index-validation.test.js b/test/graph/graph-auto-index-validation.test.js similarity index 92% rename from test/graph-auto-index-validation.test.js rename to test/graph/graph-auto-index-validation.test.js index ae01ba1f..0e7b0f22 100644 --- a/test/graph-auto-index-validation.test.js +++ b/test/graph/graph-auto-index-validation.test.js @@ -1,6 +1,6 @@ const test = require("node:test"); const assert = require("node:assert"); -const { validateScipCoverage } = require("../scripts/graph/lib/validate.cjs"); +const { validateScipCoverage } = require("../../scripts/graph/lib/validate.cjs"); test("validateScipCoverage fails when domain has zero docs", () => { const deps = {