import { createHash } from 'node:crypto'; import { writeFileSync } from 'node:fs'; import { link, mkdir, readFile, readdir, realpath, symlink, unlink, writeFile, } from 'node:fs/promises'; import { performance } from 'node:perf_hooks'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; import { describe, expect, it } from 'vitest'; import { _clearAnalyzerIdentityProcessCacheForTests, _hashAnalyzerIdentityFramesForTests, analyzerRunnerIdentitiesEqual, captureAnalyzerIdentityBeforeLoad, finalizeAnalyzerRunnerIdentity, normalizeAnalyzerRootPath, normalizeAnalyzerRunnerIdentityForComparison, resolveAnalyzerRunnerIdentity, } from '../../src/core/analyzer-identity.js'; import { getStoragePaths, loadMeta, saveMeta } from '../../src/storage/repo-manager.js'; import type { RepoMeta } from '../../src/storage/repo-manager.js'; import { setupMiniRepo } from '../helpers/mini-repo.js'; import { createTempDir } from '../helpers/test-db.js'; describe('analyzer runner identity', () => { it('is versioned, resolved, and changes when the analyzer build tree changes', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(path.join(fixture.dbPath, 'package-lock.json'), '{"lockfileVersion":3}\n'); await writeFile(modulePath, 'export const analyzer = 1;\n'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); const resolvedModulePath = await realpath(modulePath); const resolvedSourceRoot = await realpath(sourceRoot); const resolvedManifestPath = await realpath(path.join(fixture.dbPath, 'package.json')); const resolvedLockfilePath = await realpath(path.join(fixture.dbPath, 'package-lock.json')); const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(first).toMatchObject({ schemaVersion: 4, cliVersion: '9.8.7', runtime: { executablePath: expect.any(String), version: process.version, platform: process.platform, architecture: process.arch, modulesAbi: process.versions.modules ?? 'unknown', libc: expect.any(String), }, invokedArtifact: { path: resolvedModulePath, digest: expect.stringMatching(/^sha256:[a-f0-9]{64}$/), }, build: { kind: 'source', rootPath: resolvedSourceRoot, canonicalization: 'gitnexus-analyzer-build-v2', digest: expect.stringMatching(/^sha256:[a-f0-9]{64}$/), }, dependencyRuntime: { manifestPath: resolvedManifestPath, lockfilePath: resolvedLockfilePath, canonicalization: 'gitnexus-analyzer-dependency-runtime-v4', packageCount: 1, artifactCount: 0, digest: expect.stringMatching(/^sha256:[a-f0-9]{64}$/), }, }); await writeFile(path.join(sourceRoot, 'new-module.ts'), 'export const changed = true;\n'); const second = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(second.invokedArtifact.digest).toBe(first.invokedArtifact.digest); expect(second.build.digest).not.toBe(first.build.digest); expect(second.dependencyRuntime.digest).toBe(first.dependencyRuntime.digest); // THE #2798 INVARIANT: the build digest moved while nothing else did. // Node-id formats, wire formats, resolution tiers and emit ordering live in // analyzer code, not in DDL, so `SCHEMA_FINGERPRINT` (lbug/schema.ts) is // structurally incapable of firing on a change shaped like this one — it is // a digest of the node+relation DDL and of nothing else. #2798 deleted the // hand-incremented INCREMENTAL_SCHEMA_VERSION ladder, and roughly 30 of its // ~35 bumps were exactly this shape: semantic, no DDL. This receipt is their // only remaining cover, so a moved build digest MUST refuse index reuse. // (call-summary-schema-version.test.ts holds the DDL-blind half of the split.) expect(analyzerRunnerIdentitiesEqual(second, first)).toBe(false); } finally { await fixture.cleanup(); } }); it('rejects build-tree symlinks instead of trusting unchanged link metadata', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const importedTarget = path.join(fixture.dbPath, 'outside-build-input.ts'); const importedLink = path.join(sourceRoot, 'linked-input.ts'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile(importedTarget, 'export const imported = 1;\n'); try { await symlink(importedTarget, importedLink, 'file'); } catch (error) { if (['EPERM', 'EACCES'].includes((error as NodeJS.ErrnoException).code ?? '')) return; throw error; } const resolve = () => resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory: path.join(fixture.dbPath, 'identity-cache'), }); expect(resolve).toThrow(/build symbolic links are not supported/); // Changing only target bytes leaves the symlink inode/text unchanged. // The resolver must continue to fail closed, never return an old digest. await writeFile(importedTarget, 'export const imported = 200;\n'); expect(resolve).toThrow(/build symbolic links are not supported/); } finally { await fixture.cleanup(); } }); it('versions runtime semantics and rejects a cache from another runtime variant', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, Buffer.alloc(128 * 1024, 0x5a)); const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(first.runtime).toMatchObject({ version: process.version, platform: process.platform, architecture: process.arch, modulesAbi: process.versions.modules ?? 'unknown', libc: expect.stringMatching(/\S/), }); expect( analyzerRunnerIdentitiesEqual( { ...first, runtime: { ...first.runtime, platform: `${first.runtime.platform}-other` } }, first, ), ).toBe(false); expect( analyzerRunnerIdentitiesEqual( { ...first, runtime: { ...first.runtime, architecture: `${first.runtime.architecture}-other` }, }, first, ), ).toBe(false); expect( analyzerRunnerIdentitiesEqual( { ...first, runtime: { ...first.runtime, modulesAbi: `${first.runtime.modulesAbi}-other` }, }, first, ), ).toBe(false); expect( analyzerRunnerIdentitiesEqual( { ...first, runtime: { ...first.runtime, libc: `${first.runtime.libc}-other` } }, first, ), ).toBe(false); const [cacheFile] = await readdir(cacheDirectory); const cachePath = path.join(cacheDirectory, cacheFile); const envelope = JSON.parse(await readFile(cachePath, 'utf8')) as { payload: { schemaVersion: number; runtimeVariant: { nodeVersion: string; platform: string; architecture: string; modulesAbi: string; libc: string; }; }; checksum: string; }; expect(envelope.payload).toMatchObject({ schemaVersion: 6, runtimeVariant: { nodeVersion: process.version, platform: process.platform, architecture: process.arch, modulesAbi: process.versions.modules ?? 'unknown', libc: first.runtime.libc, }, }); envelope.payload.runtimeVariant.platform = `${process.platform}-stale-cache`; envelope.checksum = `sha256:${createHash('sha256') .update(JSON.stringify(envelope.payload)) .digest('hex')}`; await writeFile(cachePath, `${JSON.stringify(envelope)}\n`); _clearAnalyzerIdentityProcessCacheForTests(); let hashedBytes = 0; const afterIncompatibleCache = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onHashedInput: ({ bytes }) => { hashedBytes += bytes; }, }); expect(afterIncompatibleCache).toEqual(first); expect(hashedBytes).toBeGreaterThanOrEqual(128 * 1024); } finally { await fixture.cleanup(); } }); it('disables the default persistent cache without getuid but trusts an explicit override', async () => { const fixture = await createTempDir(); const originalGetuid = Object.getOwnPropertyDescriptor(process, 'getuid'); const originalEnvironment = { TMPDIR: process.env.TMPDIR, XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR, }; try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const tempRoot = path.join(fixture.dbPath, 'tmp'); const explicitCache = path.join(fixture.dbPath, 'operator-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(tempRoot, { mode: 0o700 }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, Buffer.alloc(64 * 1024, 0x33)); process.env.TMPDIR = tempRoot; delete process.env.XDG_RUNTIME_DIR; Object.defineProperty(process, 'getuid', { value: undefined, configurable: true, enumerable: true, writable: true, }); const hashedWith = (cacheDirectory?: string): number => { let bytes = 0; resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { ...(cacheDirectory ? { cacheDirectory } : {}), onHashedInput: (input) => { bytes += input.bytes; }, }); return bytes; }; expect(hashedWith()).toBeGreaterThanOrEqual(64 * 1024); // Cross-platform process-local reuse remains available even when secure // default persistence cannot be proved. expect(hashedWith()).toBe(0); expect(await readdir(tempRoot)).toEqual([]); _clearAnalyzerIdentityProcessCacheForTests(); expect(hashedWith(explicitCache)).toBeGreaterThanOrEqual(64 * 1024); _clearAnalyzerIdentityProcessCacheForTests(); expect(hashedWith(explicitCache)).toBe(0); } finally { if (originalGetuid) Object.defineProperty(process, 'getuid', originalGetuid); else Reflect.deleteProperty(process, 'getuid'); if (originalEnvironment.TMPDIR === undefined) delete process.env.TMPDIR; else process.env.TMPDIR = originalEnvironment.TMPDIR; if (originalEnvironment.XDG_RUNTIME_DIR === undefined) delete process.env.XDG_RUNTIME_DIR; else process.env.XDG_RUNTIME_DIR = originalEnvironment.XDG_RUNTIME_DIR; await fixture.cleanup(); } }); it('supports only an absolute, pre-provisioned, external operator-trusted cache directory', async () => { const fixture = await createTempDir(); const protectedCache = await createTempDir(); const previous = process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR; try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, Buffer.alloc(64 * 1024, 0x37)); const url = pathToFileURL(modulePath).href; const hashedBytes = (): number => { let bytes = 0; resolveAnalyzerRunnerIdentity(url, { onHashedInput: (input) => { bytes += input.bytes; }, }); return bytes; }; process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR = await realpath(protectedCache.dbPath); _clearAnalyzerIdentityProcessCacheForTests(); expect(hashedBytes()).toBeGreaterThanOrEqual(64 * 1024); _clearAnalyzerIdentityProcessCacheForTests(); expect(hashedBytes()).toBe(0); for (const invalid of [ 'relative/cache', path.join(fixture.dbPath, 'missing-cache'), fixture.dbPath, sourceRoot, ]) { process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR = invalid; _clearAnalyzerIdentityProcessCacheForTests(); expect(() => resolveAnalyzerRunnerIdentity(url)).toThrow( /GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR/, ); } const linkedCache = path.join(fixture.dbPath, 'cache-link'); try { await symlink(protectedCache.dbPath, linkedCache, 'dir'); process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR = linkedCache; _clearAnalyzerIdentityProcessCacheForTests(); expect(() => resolveAnalyzerRunnerIdentity(url)).toThrow(/non-symlink|symbolic links/); await unlink(linkedCache); } catch (error) { if (!['EPERM', 'EACCES'].includes((error as NodeJS.ErrnoException).code ?? '')) throw error; } } finally { if (previous === undefined) delete process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR; else process.env.GITNEXUS_ANALYZER_IDENTITY_CACHE_DIR = previous; _clearAnalyzerIdentityProcessCacheForTests(); await protectedCache.cleanup(); await fixture.cleanup(); } }); it('changes on lock and native/parser mutations while ignoring model caches', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const grammarRoot = path.join(fixture.dbPath, 'vendor', 'tree-sitter-fixture'); const nativePath = path.join( grammarRoot, 'prebuilds', `${process.platform}-${process.arch}`, 'tree-sitter-fixture.node', ); const sharedLibraryPath = path.join( path.dirname(nativePath), process.platform === 'win32' ? 'tree-sitter-fixture.dll' : process.platform === 'darwin' ? 'libtree-sitter-fixture.dylib' : 'libtree-sitter-fixture.so.1', ); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(path.dirname(nativePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); const originalLock = '{"name":"fixture-analyzer","lockfileVersion":3}\n'; await writeFile(path.join(fixture.dbPath, 'package-lock.json'), originalLock); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile( path.join(grammarRoot, 'package.json'), '{"name":"tree-sitter-fixture","version":"1.0.0"}\n', ); await writeFile(nativePath, 'native-v1'); await writeFile(sharedLibraryPath, 'shared-v1'); await writeFile(path.join(grammarRoot, 'tree-sitter-fixture.wasm'), 'wasm-v1'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); let hashedBytes = 0; let runtimeArtifactHashes = 0; const resolve = () => resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onHashedInput: (input) => { hashedBytes += input.bytes; if (input.kind === 'runtime-artifact') runtimeArtifactHashes += 1; }, }); const first = resolve(); expect(first.dependencyRuntime.artifactCount).toBe(3); expect(runtimeArtifactHashes).toBe(3); expect(hashedBytes).toBeGreaterThan(0); expect(analyzerRunnerIdentitiesEqual(structuredClone(first), first)).toBe(true); expect(analyzerRunnerIdentitiesEqual({ ...first, schemaVersion: 1 }, first)).toBe(false); // The second resolver call models a fresh status/analyze process: the // persistent cache is reloaded from disk, and unchanged payload bytes are // never read even though both build/dependency inventories are validated. hashedBytes = 0; runtimeArtifactHashes = 0; expect(resolve()).toEqual(first); expect(runtimeArtifactHashes).toBe(0); expect(hashedBytes).toBe(0); await writeFile( path.join(fixture.dbPath, 'package-lock.json'), '{"name":"fixture-analyzer","lockfileVersion":4}\n', ); const lockChanged = resolve(); expect(lockChanged.build.digest).toBe(first.build.digest); expect(lockChanged.dependencyRuntime.digest).not.toBe(first.dependencyRuntime.digest); expect(analyzerRunnerIdentitiesEqual(lockChanged, first)).toBe(false); expect(runtimeArtifactHashes).toBe(0); await writeFile(path.join(fixture.dbPath, 'package-lock.json'), originalLock); await writeFile(nativePath, 'native-v2'); runtimeArtifactHashes = 0; const nativeChanged = resolve(); expect(nativeChanged.dependencyRuntime.digest).not.toBe(first.dependencyRuntime.digest); expect(runtimeArtifactHashes).toBe(1); await writeFile(nativePath, 'native-v1'); await writeFile(sharedLibraryPath, 'shared-v2'); const sharedLibraryChanged = resolve(); expect(sharedLibraryChanged.dependencyRuntime.digest).not.toBe( first.dependencyRuntime.digest, ); const modelCache = path.join(fixture.dbPath, '.cache', 'models'); await mkdir(modelCache, { recursive: true }); await writeFile(path.join(modelCache, 'weights.bin'), 'large-model-placeholder'); runtimeArtifactHashes = 0; const cacheChanged = resolve(); expect(cacheChanged.dependencyRuntime).toEqual(sharedLibraryChanged.dependencyRuntime); expect(runtimeArtifactHashes).toBe(0); // A corrupt cache is fail-closed: valid inventory stats cannot rescue // unverifiable cached digests, so every expensive artifact is rehashed. const [cacheFile] = await readdir(cacheDirectory); await writeFile(path.join(cacheDirectory, cacheFile), '{"payload":{},"checksum":"bad"}\n'); _clearAnalyzerIdentityProcessCacheForTests(); runtimeArtifactHashes = 0; hashedBytes = 0; resolve(); expect(runtimeArtifactHashes).toBe(3); expect(hashedBytes).toBeGreaterThan(0); } finally { await fixture.cleanup(); } }); it('reuses the secure runtime cache across isolated HOME and GITNEXUS_HOME values', async () => { const fixture = await createTempDir(); const previous = { HOME: process.env.HOME, GITNEXUS_HOME: process.env.GITNEXUS_HOME, TMPDIR: process.env.TMPDIR, XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR, }; const restore = (name: keyof typeof previous): void => { const value = previous[name]; if (value === undefined) delete process.env[name]; else process.env[name] = value; }; try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const grammarRoot = path.join(fixture.dbPath, 'vendor', 'tree-sitter-fixture'); const nativePath = path.join( grammarRoot, 'prebuilds', `${process.platform}-${process.arch}`, 'tree-sitter-fixture.node', ); const tempRoot = path.join(fixture.dbPath, 'runtime-cache-root'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(path.dirname(nativePath), { recursive: true }); await mkdir(tempRoot, { mode: 0o700 }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile( path.join(grammarRoot, 'package.json'), '{"name":"tree-sitter-fixture","version":"1.0.0"}\n', ); await writeFile(nativePath, Buffer.alloc(2 * 1024 * 1024, 0x5a)); delete process.env.XDG_RUNTIME_DIR; process.env.TMPDIR = tempRoot; process.env.HOME = path.join(fixture.dbPath, 'home-a'); process.env.GITNEXUS_HOME = path.join(fixture.dbPath, 'gitnexus-home-a'); let runtimeHashes = 0; let hashedBytes = 0; const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { onHashedInput: (input) => { if (input.kind === 'runtime-artifact') runtimeHashes += 1; hashedBytes += input.bytes; }, }); expect(runtimeHashes).toBe(1); expect(hashedBytes).toBeGreaterThanOrEqual(2 * 1024 * 1024); process.env.HOME = path.join(fixture.dbPath, 'home-b'); process.env.GITNEXUS_HOME = path.join(fixture.dbPath, 'gitnexus-home-b'); _clearAnalyzerIdentityProcessCacheForTests(); runtimeHashes = 0; hashedBytes = 0; let cacheMissWalks = 0; let cacheMissReads = 0; const second = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { onHashedInput: (input) => { if (input.kind === 'runtime-artifact') runtimeHashes += 1; hashedBytes += input.bytes; }, onCacheMissWork: (input) => { if (input.kind === 'directory-walk') cacheMissWalks += 1; else cacheMissReads += 1; }, }); expect(second).toEqual(first); expect(runtimeHashes).toBe(0); expect(hashedBytes).toBe(0); expect(cacheMissWalks).toBe(0); expect(cacheMissReads).toBe(0); } finally { restore('HOME'); restore('GITNEXUS_HOME'); restore('TMPDIR'); restore('XDG_RUNTIME_DIR'); await fixture.cleanup(); } }); it('keeps warm identities stable when unrelated siblings churn in a shared parent', async () => { const fixture = await createTempDir(); let unrelated: Awaited> | null = null; try { const modulePath = path.join(fixture.dbPath, 'src', 'core', 'analyzer.ts'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); unrelated = await createTempDir(); _clearAnalyzerIdentityProcessCacheForTests(); let cacheMissWork = 0; const second = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onCacheMissWork: () => { cacheMissWork += 1; }, }); expect(second).toEqual(first); expect(cacheMissWork).toBe(0); } finally { if (unrelated) await unrelated.cleanup(); await fixture.cleanup(); } }); it('invalidates an absent path guard when a nearer ancestor package lock appears', async () => { const fixture = await createTempDir(); try { const packageRoot = path.join(fixture.dbPath, 'packages', 'fixture-analyzer'); const modulePath = path.join(packageRoot, 'src', 'core', 'analyzer.ts'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); const ancestorLock = path.join(fixture.dbPath, 'package-lock.json'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(packageRoot, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); const withoutLock = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(withoutLock.dependencyRuntime.lockfilePath).toBeNull(); await writeFile(ancestorLock, '{"lockfileVersion":3}\n'); const withLock = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(withLock.dependencyRuntime.lockfilePath).toBe(await realpath(ancestorLock)); expect(withLock.dependencyRuntime.digest).not.toBe(withoutLock.dependencyRuntime.digest); } finally { await fixture.cleanup(); } }); it('invalidates an absent path guard when a nearer dependency shadows a hoisted one', async () => { const fixture = await createTempDir(); try { const packageRoot = path.join(fixture.dbPath, 'packages', 'fixture-analyzer'); const modulePath = path.join(packageRoot, 'src', 'core', 'analyzer.ts'); const hoistedRoot = path.join(fixture.dbPath, 'node_modules', 'runtime-package'); const nearerRoot = path.join(fixture.dbPath, 'packages', 'node_modules', 'runtime-package'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(hoistedRoot, { recursive: true }); await writeFile( path.join(packageRoot, 'package.json'), JSON.stringify({ name: 'fixture-analyzer', version: '9.8.7', dependencies: { 'runtime-package': '1.0.0' }, }), ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile( path.join(hoistedRoot, 'package.json'), JSON.stringify({ name: 'runtime-package', version: '1.0.0' }), ); await writeFile(path.join(hoistedRoot, 'runtime.js'), 'export const source = "hoisted";\n'); const hoisted = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); await mkdir(nearerRoot, { recursive: true }); await writeFile( path.join(nearerRoot, 'package.json'), JSON.stringify({ name: 'runtime-package', version: '2.0.0' }), ); await writeFile(path.join(nearerRoot, 'runtime.js'), 'export const source = "nearer";\n'); const shadowed = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(shadowed.dependencyRuntime.digest).not.toBe(hoisted.dependencyRuntime.digest); } finally { await fixture.cleanup(); } }); it('invalidates a warm identity when an intermediate package symlink is retargeted', async () => { const fixture = await createTempDir(); try { const packageRoot = path.join(fixture.dbPath, 'fixture-analyzer'); const modulePath = path.join(packageRoot, 'src', 'core', 'analyzer.ts'); const nodeModulesRoot = path.join(packageRoot, 'node_modules'); const packageLink = path.join(nodeModulesRoot, 'runtime-package'); const storeA = path.join(fixture.dbPath, 'store-a'); const storeB = path.join(fixture.dbPath, 'store-b'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(nodeModulesRoot, { recursive: true }); await mkdir(storeA); await mkdir(storeB); await writeFile( path.join(packageRoot, 'package.json'), JSON.stringify({ name: 'fixture-analyzer', version: '9.8.7', dependencies: { 'runtime-package': '1.0.0' }, }), ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile( path.join(storeA, 'package.json'), JSON.stringify({ name: 'runtime-package', version: '1.0.0' }), ); // Keep the final candidate manifest's inode and stat state identical so // only an exact lexical-component guard can observe the retarget. await link(path.join(storeA, 'package.json'), path.join(storeB, 'package.json')); await writeFile(path.join(storeA, 'runtime.js'), 'export const source = "a";\n'); await writeFile(path.join(storeB, 'runtime.js'), 'export const source = "b changed";\n'); try { await symlink(storeA, packageLink, 'dir'); } catch (error) { if (['EPERM', 'EACCES'].includes((error as NodeJS.ErrnoException).code ?? '')) return; throw error; } const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); await unlink(packageLink); await symlink(storeB, packageLink, 'dir'); _clearAnalyzerIdentityProcessCacheForTests(); let cacheMissWork = 0; const retargeted = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onCacheMissWork: () => { cacheMissWork += 1; }, }); expect(retargeted.dependencyRuntime.digest).not.toBe(first.dependencyRuntime.digest); expect(cacheMissWork).toBeGreaterThan(0); } finally { await fixture.cleanup(); } }); it('invalidates direct-stat guards for build, topology, and artifact inventory changes', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const grammarRoot = path.join(fixture.dbPath, 'vendor', 'tree-sitter-fixture'); const artifactDir = path.join( grammarRoot, 'prebuilds', `${process.platform}-${process.arch}`, ); const nativePath = path.join(artifactDir, 'tree-sitter-fixture.node'); const addedNativePath = path.join(artifactDir, 'tree-sitter-extra.node'); const manifestPath = path.join(grammarRoot, 'package.json'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(artifactDir, { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile(manifestPath, '{"name":"tree-sitter-fixture","version":"1.0.0"}\n'); await writeFile(nativePath, 'native-v1'); const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(first.dependencyRuntime.artifactCount).toBe(1); let walks = 0; let reads = 0; let hashes = 0; const resolve = () => resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onCacheMissWork: (input) => { if (input.kind === 'directory-walk') walks += 1; else reads += 1; }, onHashedInput: () => { hashes += 1; }, }); const reset = () => { walks = 0; reads = 0; hashes = 0; }; expect(resolve()).toEqual(first); expect({ walks, reads, hashes }).toEqual({ walks: 0, reads: 0, hashes: 0 }); await writeFile(path.join(sourceRoot, 'added.ts'), 'export const added = true;\n'); reset(); const buildAdded = resolve(); expect(buildAdded.build.digest).not.toBe(first.build.digest); expect(walks).toBeGreaterThan(0); await writeFile(addedNativePath, 'native-extra'); reset(); const artifactAdded = resolve(); expect(artifactAdded.dependencyRuntime.artifactCount).toBe(2); expect(artifactAdded.dependencyRuntime.digest).not.toBe(buildAdded.dependencyRuntime.digest); expect(walks).toBeGreaterThan(0); expect(hashes).toBe(1); await writeFile(manifestPath, '{"name":"tree-sitter-fixture","version":"2.0.0"}\n'); reset(); const manifestChanged = resolve(); expect(manifestChanged.dependencyRuntime.digest).not.toBe( artifactAdded.dependencyRuntime.digest, ); expect(reads).toBeGreaterThan(0); await unlink(nativePath); reset(); const artifactDeleted = resolve(); expect(artifactDeleted.dependencyRuntime.artifactCount).toBe(1); expect(artifactDeleted.dependencyRuntime.digest).not.toBe( manifestChanged.dependencyRuntime.digest, ); expect(walks).toBeGreaterThan(0); } finally { await fixture.cleanup(); } }); it('keeps same-name/version dependency instances distinct by package-root locator', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const nestedA = path.join( fixture.dbPath, 'node_modules', 'parent-a', 'node_modules', 'duplicate', ); const nestedB = path.join( fixture.dbPath, 'node_modules', 'parent-b', 'node_modules', 'duplicate', ); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(nestedA, { recursive: true }); await mkdir(nestedB, { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), JSON.stringify({ name: 'fixture-analyzer', version: '9.8.7', dependencies: { 'parent-a': '1.0.0', 'parent-b': '1.0.0' }, }), ); await writeFile(modulePath, 'export const analyzer = 1;\n'); for (const parentName of ['parent-a', 'parent-b']) { await writeFile( path.join(fixture.dbPath, 'node_modules', parentName, 'package.json'), JSON.stringify({ name: parentName, version: '1.0.0', dependencies: { duplicate: '1.0.0' }, }), ); } for (const nestedRoot of [nestedA, nestedB]) { await writeFile( path.join(nestedRoot, 'package.json'), JSON.stringify({ name: 'duplicate', version: '1.0.0' }), ); await writeFile(path.join(nestedRoot, 'runtime.wasm'), 'same-runtime-bytes'); } const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(first.dependencyRuntime.packageCount).toBe(5); expect(first.dependencyRuntime.artifactCount).toBe(2); const [cacheFile] = await readdir(cacheDirectory); const envelope = JSON.parse(await readFile(path.join(cacheDirectory, cacheFile), 'utf8')) as { payload: { artifactEntries: Array<{ canonicalPath: string }> }; }; const artifactLocators = envelope.payload.artifactEntries.map((entry) => entry.canonicalPath); expect(artifactLocators).toEqual( expect.arrayContaining([ expect.stringContaining('node_modules/parent-a/node_modules/duplicate/runtime.wasm'), expect.stringContaining('node_modules/parent-b/node_modules/duplicate/runtime.wasm'), ]), ); expect(new Set(artifactLocators).size).toBe(2); await writeFile( path.join(nestedB, 'package.json'), JSON.stringify({ name: 'duplicate', version: '1.0.0', instance: 'parent-b' }), ); const changedOneInstance = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(changedOneInstance.dependencyRuntime.packageCount).toBe(5); expect(changedOneInstance.dependencyRuntime.digest).not.toBe(first.dependencyRuntime.digest); } finally { await fixture.cleanup(); } }); it('discovers runtime artifacts in every resolved package without an allowlist', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const dependencyRoot = path.join(fixture.dbPath, 'node_modules', 'ordinary-runtime'); const nativePath = path.join(dependencyRoot, 'build', 'addon.node'); const wasmPath = path.join(dependencyRoot, 'codec', 'runtime.wasm'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(path.dirname(nativePath), { recursive: true }); await mkdir(path.dirname(wasmPath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), JSON.stringify({ name: 'fixture-analyzer', version: '9.8.7', dependencies: { 'ordinary-runtime': '1.0.0' }, }), ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile( path.join(dependencyRoot, 'package.json'), JSON.stringify({ name: 'ordinary-runtime', version: '1.0.0' }), ); await writeFile(nativePath, 'native-v1'); await writeFile(wasmPath, 'wasm-v1'); const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(first.dependencyRuntime).toMatchObject({ packageCount: 2, artifactCount: 2 }); await writeFile(nativePath, 'native-v2-with-a-different-size'); const changed = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(changed.dependencyRuntime.digest).not.toBe(first.dependencyRuntime.digest); } finally { await fixture.cleanup(); } }); it('tracks generic runtime directories and filename-mismatched native payloads on cold and warm scans', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const dependencyRoot = path.join(fixture.dbPath, 'node_modules', 'runtime-package'); const foreignPlatform = process.platform === 'linux' ? 'darwin' : 'linux'; const foreignArchitecture = process.arch === 'x64' ? 'arm64' : 'x64'; const payloadPaths = [ path.join(dependencyRoot, '.cache', 'generated-loader.js'), path.join( dependencyRoot, 'cache', `${foreignPlatform}-${foreignArchitecture}`, 'addon.node', ), path.join(dependencyRoot, 'models', 'runtime-model.wasm'), path.join( dependencyRoot, 'prebuilds', `${foreignPlatform}-${foreignArchitecture}`, 'foreign-target.node', ), path.join( dependencyRoot, 'codec', `runtime-${foreignPlatform}-${foreignArchitecture}.wasm`, ), ]; await mkdir(path.dirname(modulePath), { recursive: true }); for (const payloadPath of payloadPaths) { await mkdir(path.dirname(payloadPath), { recursive: true }); } await mkdir(path.join(dependencyRoot, '.git'), { recursive: true }); await mkdir(path.join(dependencyRoot, '.hg'), { recursive: true }); await mkdir(path.join(dependencyRoot, '.svn'), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), JSON.stringify({ name: 'fixture-analyzer', version: '9.8.7', dependencies: { 'runtime-package': '1.0.0' }, }), ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile( path.join(dependencyRoot, 'package.json'), JSON.stringify({ name: 'runtime-package', version: '1.0.0' }), ); await writeFile(path.join(dependencyRoot, '.git', 'config'), 'ignored-vcs-state'); await writeFile(path.join(dependencyRoot, '.hg', 'dirstate'), 'ignored-vcs-state'); await writeFile(path.join(dependencyRoot, '.svn', 'wc.db'), 'ignored-vcs-state'); for (const payloadPath of payloadPaths) await writeFile(payloadPath, 'payload-v1'); const baseline = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory: path.join(fixture.dbPath, 'baseline-cache'), }); expect(baseline.dependencyRuntime.artifactCount).toBe(payloadPaths.length); for (const payloadPath of payloadPaths) { await writeFile(payloadPath, `payload-v2:${path.basename(payloadPath)}`); } const warmCacheDirectory = path.join(fixture.dbPath, 'warm-cache'); let runtimeHashes = 0; const coldAfterMutation = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory: warmCacheDirectory, onHashedInput: (input) => { if (input.kind === 'runtime-artifact') runtimeHashes += 1; }, }); expect(coldAfterMutation.dependencyRuntime.digest).not.toBe( baseline.dependencyRuntime.digest, ); expect(runtimeHashes).toBe(payloadPaths.length); runtimeHashes = 0; expect( resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory: warmCacheDirectory, onHashedInput: (input) => { if (input.kind === 'runtime-artifact') runtimeHashes += 1; }, }), ).toEqual(coldAfterMutation); expect(runtimeHashes).toBe(0); for (const payloadPath of payloadPaths) { await writeFile(payloadPath, `payload-v3-with-new-bytes:${path.basename(payloadPath)}`); } runtimeHashes = 0; const warmAfterMutation = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory: warmCacheDirectory, onHashedInput: (input) => { if (input.kind === 'runtime-artifact') runtimeHashes += 1; }, }); expect(warmAfterMutation.dependencyRuntime.digest).not.toBe( coldAfterMutation.dependencyRuntime.digest, ); expect(runtimeHashes).toBe(payloadPaths.length); } finally { await fixture.cleanup(); } }); it('fails closed when a resolved-package artifact walk exceeds its depth bound', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const dependencyRoot = path.join(fixture.dbPath, 'node_modules', 'deep-runtime'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(dependencyRoot, { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), JSON.stringify({ name: 'fixture-analyzer', version: '9.8.7', dependencies: { 'deep-runtime': '1.0.0' }, }), ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile( path.join(dependencyRoot, 'package.json'), JSON.stringify({ name: 'deep-runtime', version: '1.0.0' }), ); let cursor = dependencyRoot; for (let depth = 0; depth < 66; depth += 1) { cursor = path.join(cursor, 'd'); await mkdir(cursor); } expect(() => resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory: path.join(fixture.dbPath, 'identity-cache'), }), ).toThrow(/payload scan exceeded depth 64/); } finally { await fixture.cleanup(); } }); it('stable-reads symlinked package locks and rejects broken lock links', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const lockTarget = path.join(fixture.dbPath, 'actual-package-lock.json'); const lockLink = path.join(fixture.dbPath, 'package-lock.json'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile(lockTarget, '{"lockfileVersion":3}\n'); try { await symlink(lockTarget, lockLink, 'file'); } catch (error) { if (['EPERM', 'EACCES'].includes((error as NodeJS.ErrnoException).code ?? '')) return; throw error; } const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(first.dependencyRuntime.lockfilePath).toBe( path.join(await realpath(path.dirname(lockLink)), path.basename(lockLink)), ); await writeFile(lockTarget, '{"lockfileVersion":4,"changed":true}\n'); const targetChanged = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(targetChanged.dependencyRuntime.digest).not.toBe(first.dependencyRuntime.digest); await unlink(lockLink); await symlink(path.join(fixture.dbPath, 'missing-lock-target.json'), lockLink, 'file'); expect(() => resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory }), ).toThrow(/package lock symbolic link does not resolve to a file/); } finally { await fixture.cleanup(); } }); it('uses one final warm validation pass and notices immediate file and topology changes', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const addedPath = path.join(sourceRoot, 'added-at-boundary.ts'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); let validationPasses = 0; expect( resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onCacheValidationPass: () => { validationPasses += 1; }, }), ).toEqual(first); expect(validationPasses).toBe(1); validationPasses = 0; let changedFile = false; const fileChanged = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onCacheValidationPass: () => { validationPasses += 1; if (!changedFile) { changedFile = true; writeFileSync(modulePath, 'export const analyzer = 200;\n'); } }, }); expect(fileChanged.build.digest).not.toBe(first.build.digest); expect(validationPasses).toBe(2); validationPasses = 0; let changedTopology = false; const topologyChanged = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onCacheValidationPass: () => { validationPasses += 1; if (!changedTopology) { changedTopology = true; writeFileSync(addedPath, 'export const added = true;\n'); } }, }); expect(topologyChanged.build.digest).not.toBe(fileChanged.build.digest); expect(validationPasses).toBe(2); } finally { await fixture.cleanup(); } }); it('keeps warm-cache work at zero and materially below cold-path latency', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const payloadPath = path.join(sourceRoot, 'large-runtime-source.bin'); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); const payloadBytes = 32 * 1024 * 1024; await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile(payloadPath, Buffer.alloc(payloadBytes, 0x61)); let coldHashedBytes = 0; let coldTopologyWork = 0; let coldValidationPasses = 0; const coldStarted = performance.now(); const coldIdentity = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onHashedInput: ({ bytes }) => { coldHashedBytes += bytes; }, onCacheMissWork: () => { coldTopologyWork += 1; }, onCacheValidationPass: () => { coldValidationPasses += 1; }, }); const coldDurationMs = performance.now() - coldStarted; expect(coldHashedBytes).toBeGreaterThanOrEqual(payloadBytes); expect(coldTopologyWork).toBeGreaterThan(0); expect(coldValidationPasses).toBe(1); const warmDurationsMs: number[] = []; for (let iteration = 0; iteration < 5; iteration += 1) { let warmHashedBytes = 0; let warmTopologyWork = 0; let warmValidationPasses = 0; const warmStarted = performance.now(); const warmIdentity = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onHashedInput: ({ bytes }) => { warmHashedBytes += bytes; }, onCacheMissWork: () => { warmTopologyWork += 1; }, onCacheValidationPass: () => { warmValidationPasses += 1; }, }); warmDurationsMs.push(performance.now() - warmStarted); expect(warmIdentity).toEqual(coldIdentity); expect(warmHashedBytes).toBe(0); expect(warmTopologyWork).toBe(0); expect(warmValidationPasses).toBe(1); } warmDurationsMs.sort((a, b) => a - b); const medianWarmDurationMs = warmDurationsMs[Math.floor(warmDurationsMs.length / 2)]; expect(medianWarmDurationMs).toBeLessThan(coldDurationMs); } finally { await fixture.cleanup(); } }); it('uses non-ambiguous framing and treats the invoked entrypoint as diagnostic', async () => { const leftOldEncoding = Buffer.concat([ Buffer.from('a'), Buffer.from([0]), Buffer.from('b\0c'), Buffer.from([0]), ]); const rightOldEncoding = Buffer.concat([ Buffer.from('a\0b'), Buffer.from([0]), Buffer.from('c'), Buffer.from([0]), ]); expect(leftOldEncoding).toEqual(rightOldEncoding); expect(_hashAnalyzerIdentityFramesForTests([['entry', 'a', 'b\0c']])).not.toBe( _hashAnalyzerIdentityFramesForTests([['entry', 'a\0b', 'c']]), ); const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); const options = { cacheDirectory: path.join(fixture.dbPath, 'identity-cache') }; const identity = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, options); const alternateEntrypoint = { ...identity, invokedArtifact: { path: path.join(sourceRoot, 'server', 'analyze-worker.ts'), digest: `sha256:${'a'.repeat(64)}`, }, }; expect(analyzerRunnerIdentitiesEqual(alternateEntrypoint, identity)).toBe(true); expect(normalizeAnalyzerRunnerIdentityForComparison(alternateEntrypoint)).toEqual( normalizeAnalyzerRunnerIdentityForComparison(identity), ); expect(normalizeAnalyzerRunnerIdentityForComparison({ schemaVersion: 4 })).toBeNull(); expect( analyzerRunnerIdentitiesEqual( { ...alternateEntrypoint, invokedArtifact: { path: '', digest: 'bad' } }, identity, ), ).toBe(false); // Fail-closed on a receipt that cannot be read at all — the same posture as // an absent schemaFingerprint. An index predating the field stamps nothing // (undefined) and a cleared/legacy field reads back as null; neither is ever // grandfathered into an incremental top-up (#2798). expect(analyzerRunnerIdentitiesEqual(undefined, identity)).toBe(false); expect(analyzerRunnerIdentitiesEqual(null, identity)).toBe(false); await writeFile( path.join(sourceRoot, 'new-semantic-input.ts'), 'export const changed = 1;\n', ); expect(() => finalizeAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, identity, options), ).toThrow(/changed during analysis/); } finally { await fixture.cleanup(); } }); it('captures before loading and rejects a replacement that races module evaluation', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"9.8.7"}\n', ); await writeFile(modulePath, 'export const analyzer = 1;\n'); const options = { cacheDirectory: path.join(fixture.dbPath, 'identity-cache') }; const prepared = await captureAnalyzerIdentityBeforeLoad( pathToFileURL(modulePath).href, async () => { // Change the size as well as the bytes so filesystems with coarse // timestamp granularity cannot make this race regression flaky. await writeFile(modulePath, 'export const analyzer = 200;\n'); return 'loaded-after-replacement'; }, options, ); expect(prepared.loaded).toBe('loaded-after-replacement'); expect(() => finalizeAnalyzerRunnerIdentity( pathToFileURL(modulePath).href, prepared.runnerIdentity, options, ), ).toThrow(/changed during analysis/); } finally { await fixture.cleanup(); } }); it('content-addresses every resolved package payload and reuses it without byte reads', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const dependencyRoot = path.join(fixture.dbPath, 'node_modules', 'runtime-package'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(dependencyRoot, { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), JSON.stringify({ name: 'fixture-analyzer', version: '9.8.7', dependencies: { 'runtime-package': '1.0.0' }, }), ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile( path.join(dependencyRoot, 'package.json'), JSON.stringify({ name: 'runtime-package', version: '1.0.0' }), ); const payloadNames = [ 'index.js', 'legacy.cjs', 'module.mjs', 'data.json', 'addon.node', 'runtime.wasm', 'extensionless', 'runtime-config.txt', ]; for (const name of payloadNames) await writeFile(path.join(dependencyRoot, name), `${name}:v1`); const cacheDirectory = path.join(fixture.dbPath, 'identity-cache'); const first = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(first.dependencyRuntime.artifactCount).toBe(payloadNames.length); let warmBytes = 0; expect( resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, onHashedInput: ({ bytes }) => { warmBytes += bytes; }, }), ).toEqual(first); expect(warmBytes).toBe(0); let priorDigest = first.dependencyRuntime.digest; for (const name of payloadNames) { await writeFile(path.join(dependencyRoot, name), `${name}:v2-with-new-bytes`); const changed = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory, }); expect(changed.dependencyRuntime.digest).not.toBe(priorDigest); priorDigest = changed.dependencyRuntime.digest; } } finally { await fixture.cleanup(); } }); it('enforces iterative build, package, edge, entry, payload, byte, depth, and resolution bounds', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); const dependencyRoot = path.join(fixture.dbPath, 'node_modules', 'runtime-package'); await mkdir(path.dirname(modulePath), { recursive: true }); await mkdir(path.join(dependencyRoot, 'deep', 'deeper'), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), JSON.stringify({ name: 'fixture-analyzer', version: '9.8.7', dependencies: { 'runtime-package': '1.0.0', missing: '1.0.0' }, }), ); await writeFile(modulePath, 'export const analyzer = 1;\n'); await writeFile(path.join(sourceRoot, 'extra.ts'), 'export const extra = true;\n'); await mkdir(path.join(sourceRoot, 'nested', 'deeper'), { recursive: true }); await writeFile(path.join(sourceRoot, 'nested', 'deeper', 'leaf.ts'), 'export {};\n'); await writeFile( path.join(dependencyRoot, 'package.json'), JSON.stringify({ name: 'runtime-package', version: '1.0.0' }), ); await writeFile(path.join(dependencyRoot, 'a.js'), 'a'); await writeFile(path.join(dependencyRoot, 'b.json'), '{}'); await writeFile(path.join(dependencyRoot, 'deep', 'deeper', 'c.mjs'), 'c'); const url = pathToFileURL(modulePath).href; let sequence = 0; const bounded = (traversalLimits: Record) => () => resolveAnalyzerRunnerIdentity(url, { cacheDirectory: path.join(fixture.dbPath, `cache-${sequence++}`), traversalLimits, }); expect(bounded({ buildEntries: 1 })).toThrow(/build scan exceeded 1 entries/); expect(bounded({ buildDepth: 1 })).toThrow(/build scan exceeded depth 1/); expect(bounded({ buildBytes: 1 })).toThrow(/build scan exceeded 1 bytes/); expect(bounded({ runtimePackages: 1 })).toThrow(/dependency graph exceeded 1 packages/); expect(bounded({ runtimeEdges: 1 })).toThrow(/dependency graph exceeded 1 edges/); expect(bounded({ runtimeEntries: 1 })).toThrow(/payload scan exceeded 1 entries/); expect(bounded({ runtimeDepth: 1 })).toThrow(/payload scan exceeded depth 1/); expect(bounded({ runtimePayloads: 1 })).toThrow(/payload scan exceeded 1 payloads/); expect(bounded({ runtimeBytes: 1 })).toThrow(/runtime scan exceeded 1 bytes/); expect(bounded({ resolutionAncestors: 1 })).toThrow(/exceeded 1 ancestors/); } finally { await fixture.cleanup(); } }); it('persists the same receipt to both metadata mirrors on full and incremental runs', async () => { const repo = await setupMiniRepo(); try { const { runFullAnalysis } = await import('../../src/core/run-analyze.js'); await runFullAnalysis( repo.dbPath, { skipAgentsMd: true, skipSkills: true }, { onProgress: () => {} }, ); const { storagePath } = getStoragePaths(repo.dbPath); const first = await loadMeta(storagePath); const expectedIdentity = resolveAnalyzerRunnerIdentity( pathToFileURL(path.resolve(__dirname, '../../src/core/run-analyze.ts')).href, ); expect(first?.runnerIdentity).toEqual(expectedIdentity); expect(first?.runnerIdentity).toMatchObject({ schemaVersion: 4, cliVersion: expect.any(String), invokedArtifact: { digest: expect.stringMatching(/^sha256:[a-f0-9]{64}$/) }, build: { digest: expect.stringMatching(/^sha256:[a-f0-9]{64}$/) }, dependencyRuntime: { digest: expect.stringMatching(/^sha256:[a-f0-9]{64}$/) }, }); if (!first?.runnerIdentity) throw new Error('analysis did not persist a runner identity'); const legacyMeta = { ...first, runnerIdentity: { ...first.runnerIdentity, schemaVersion: 1 }, } as unknown as RepoMeta; await saveMeta(storagePath, legacyMeta); const upgraded = await runFullAnalysis( repo.dbPath, { skipAgentsMd: true, skipSkills: true }, { onProgress: () => {} }, ); expect(upgraded.alreadyUpToDate).toBeUndefined(); expect((await loadMeta(storagePath))?.runnerIdentity).toEqual(first.runnerIdentity); const changedPath = path.join(repo.dbPath, 'src', 'logger.ts'); const before = await readFile(changedPath, 'utf8'); await writeFile(changedPath, `${before}\n// force incremental identity restamp\n`, 'utf8'); const incremental = await runFullAnalysis( repo.dbPath, { skipAgentsMd: true, skipSkills: true }, { onProgress: () => {} }, ); expect(incremental.alreadyUpToDate).toBeUndefined(); const second = await loadMeta(storagePath); expect(second?.runnerIdentity).toEqual(first?.runnerIdentity); const primary = JSON.parse( await readFile(path.join(storagePath, 'gitnexus.json'), 'utf8'), ) as { runnerIdentity?: unknown }; const legacy = JSON.parse(await readFile(path.join(storagePath, 'meta.json'), 'utf8')) as { runnerIdentity?: unknown; }; expect(primary.runnerIdentity).toEqual(second?.runnerIdentity); expect(legacy.runnerIdentity).toEqual(second?.runnerIdentity); } finally { await repo.cleanup(); } }, 300_000); }); // #2668 threading guard: the produced identity's path fields must already be // normalizer-stable, i.e. resolveBuildRoot/resolveRuntimeVariant actually route // build.rootPath and runtime.executablePath through normalizeAnalyzerRootPath. // Ubuntu-only by necessity: this needs a real fixture identity, and the fixture // harness cannot run on the Windows matrix (the runner's repo is on D: while temp // is on C:, and isInside() misjudges cross-drive paths so resolveInvokedArtifact // picks the vitest fork worker). The pure-transform assertions that DO run on // windows-latest live in analyzer-identity-path-normalization.test.ts. describe('analyzer identity path threading (#2668)', () => { it('produces identity path fields that are already normalizer-stable', async () => { const fixture = await createTempDir(); try { const sourceRoot = path.join(fixture.dbPath, 'src'); const modulePath = path.join(sourceRoot, 'core', 'analyzer.ts'); await mkdir(path.dirname(modulePath), { recursive: true }); await writeFile( path.join(fixture.dbPath, 'package.json'), '{"name":"fixture-analyzer","version":"1.0.0"}\n', ); await writeFile(path.join(fixture.dbPath, 'package-lock.json'), '{"lockfileVersion":3}\n'); await writeFile(modulePath, 'export const analyzer = 1;\n'); const identity = resolveAnalyzerRunnerIdentity(pathToFileURL(modulePath).href, { cacheDirectory: path.join(fixture.dbPath, 'identity-cache'), }); expect(identity.build.rootPath).toBe( normalizeAnalyzerRootPath(identity.build.rootPath, process.platform), ); expect(identity.runtime.executablePath).toBe( normalizeAnalyzerRootPath(identity.runtime.executablePath, process.platform), ); } finally { await fixture.cleanup(); } }); });