From d052e16c1aa8026e3bc63a969515cce631786193 Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Fri, 10 Jul 2026 19:43:16 -0700 Subject: [PATCH 1/2] ci: cache TypeScript incremental build state across validate-code runs Enables tsc's own --incremental mode (tsBuildInfoFile: .tsbuildinfo) and caches it in validate-code across runs. Measured ~13.5s cold vs ~3.6s warm locally for this codebase's typecheck -- a real cut on a step that runs unconditionally on every backend-touching PR. Correctness verified directly: introduced a deliberate type error with a warm incremental cache present, confirmed tsc still reports it (exit 1); confirmed a clean tree still exits 0 after. tsc verifies each file's content hash before trusting cached state, so a fresh CI checkout's reset mtimes can't produce a false cache hit that masks a real error. Uses the run_id-suffixed-key + restore-keys-prefix pattern (not the hit-or-miss pattern node_modules caching uses elsewhere in this job), since .tsbuildinfo mutates every run rather than being immutable per lockfile -- this always creates a fresh entry to save into, restore falls back to the most recent matching prefix. --- .github/workflows/ci.yml | 20 ++++++++++++++++++++ tsconfig.json | 8 +++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f247036ec6..f485112d93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -271,9 +271,29 @@ jobs: env: NODE_OPTIONS: "" run: node scripts/validate-observability-configs.mjs + # .tsbuildinfo mutates every run (tsc's own incremental state), unlike node_modules above which is + # immutable per lockfile -- so this needs the run_id-suffixed-key + restore-keys-prefix pattern (always + # creates a new cache entry to save into, restore falls back to the most recent matching prefix) rather + # than the hit-or-miss pattern node_modules uses. Measured ~13.5s cold vs ~3.6s warm locally; tsc + # verifies each file's content hash before trusting cached state, so a fresh checkout's reset mtimes + # can't cause a false cache hit that would mask a real type error. + - name: Restore TypeScript incremental build cache + if: ${{ github.event_name == 'push' || needs.changes.outputs.backend == 'true' }} + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .tsbuildinfo + key: tsbuildinfo-${{ hashFiles('tsconfig.json', 'package-lock.json') }}-${{ github.run_id }} + restore-keys: | + tsbuildinfo-${{ hashFiles('tsconfig.json', 'package-lock.json') }}- - name: Typecheck if: ${{ github.event_name == 'push' || needs.changes.outputs.backend == 'true' }} run: npm run typecheck + - name: Save TypeScript incremental build cache + if: ${{ !cancelled() && (github.event_name == 'push' || needs.changes.outputs.backend == 'true') }} + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .tsbuildinfo + key: tsbuildinfo-${{ hashFiles('tsconfig.json', 'package-lock.json') }}-${{ github.run_id }} # Moved ahead of "Test with coverage" (#ci-engine-build-order): src/mcp/find-opportunities.ts (root # backend, since #2281/#3985) imports packages/gittensory-miner/lib/opportunity-fanout.js -- committed, # pre-built JS -- which itself imports @jsonbored/gittensory-engine. That package's dist/ is gitignored diff --git a/tsconfig.json b/tsconfig.json index b992c0df48..77ee066bed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,13 @@ "esModuleInterop": true, "resolveJsonModule": true, "forceConsistentCasingInFileNames": true, - "outDir": "dist" + "outDir": "dist", + // CI caches this across runs (validate-code job) to cut typecheck time on an unchanged/mostly-unchanged + // tree -- measured ~13.5s cold vs ~3.6s warm locally. Correctness is unaffected: tsc verifies each file's + // content hash before trusting the cached state, so a fresh checkout's reset mtimes can't cause a false + // cache hit. + "incremental": true, + "tsBuildInfoFile": ".tsbuildinfo" }, "include": ["src", "test", "scripts/check-engine-parity.ts", "worker-configuration.d.ts", "vitest.config.ts", "vitest.workers.config.ts", "drizzle.config.ts"] } From 5bc92bc02a1d18f76abf7da09f573c015d0ff53a Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Fri, 10 Jul 2026 19:44:02 -0700 Subject: [PATCH 2/2] ci: remove the CPU-count diagnostic now that it's answered nproc confirmed 4 on a real shard job -- --maxWorkers=4 is already exactly matched to the runner's real vCPU budget, no free parallelism available without more shards. Removing the temporary probe. --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f485112d93..d9498a63e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -455,8 +455,6 @@ jobs: run: npm run build --workspace @jsonbored/gittensory-engine - name: Prepare test reports dir run: mkdir -p reports/junit - - name: Report runner CPU count (temporary diagnostic) - run: nproc - name: Test with coverage (shard ${{ matrix.shard }}/6) id: coverage env: