mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-06 02:49:56 +00:00
Some checks are pending
Scorecard / Scorecard analysis (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* feat(java): JLS 13.1 immediate-host naming for anonymous bodies + v9 schema window (#2555, step 1) `synthesizeJavaAnonymousClassName` generalizes to both anonymous-body shapes (`object_creation_expression` with a `class_body`; `enum_constant` with a `body:` field) and switches from topmost-host naming to JLS 13.1 binary names: the `$`-joined chain of enclosing host types (`EnumWrap$Mode$1`), numbered per IMMEDIATE host in source order across both shapes (javac's shared counter). Every existing fixture's immediate host is its top-level type, so existing names are unchanged — proven by the 11 #2550 tests passing untouched, not assumed. The owner walk's anonymous branch also fires on `enum_constant` now (the synthesis returns undefined for body-less constants, so the walk continues to `enum_declaration` as before). Identity window: INCREMENTAL_SCHEMA_VERSION 8→9, parse-cache SCHEMA_BUMP 18→19, U-C5 pin extended with the v8-stamp rejection (enum-constant methods re-key `E.hook`→`E$1.hook`; nested-host anons re-key `EnumWrap$1`→`EnumWrap$Mode$1`). Enum-constant Class-node emission and scope-side ownership land in the next commits per docs/plans/2026-07-18-gitnexus-plan-enum-constant-bodies.md (plan is local — docs/ gitignored). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(java): model enum constant bodies as first-class instances (#2555, steps 2-4) `enum E { A { void hook(){} } }` — javac's other anonymous-class shape — joins the #2550 instance model: - Structure: `(enum_constant body: (class_body)) @definition.class` in JAVA_QUERIES; `enum_constant` in javaClassConfig.typeDeclarationNodes with extractName synthesis. The shouldSkipClassCapture guard now also covers enum_constant — without it, extract()'s name fallback would fabricate a Class node from the constant's own identifier (`A`). - Scope: `(enum_constant body: (class_body) @scope.class)` + synthesized `@declaration.class`/`@declaration.name` anchored on the body, so the constant's methods are owned (`ownerId`) and re-keyed (`Method:...:EnumConst$1.hook#0`). - Inheritance: a body-anchored `@reference.inherits` naming the HOST ENUM (javac semantics: E$N extends E) — `mroFor(E$N) ∋ E`, so bare calls from the body to enum helpers pass the ownership gate's MRO arm while the same-file bare-call leak for constant-body method names is closed (discrimination evidence: the #2549 review's archived S1b probe showed the identical shape resolving `local-call` pre-fix). - Nested-host JLS naming verified end-to-end: `EnumWrap$Mode$1` (not `EnumWrap$1`). - Bench: java scope-capture fingerprint rebaselined (new captures + two fixtures), `measure.mjs --check` PASS across all 14 languages. Verified: full java.test.ts 230/230 twice sequentially; TS 254 + JS/ Kotlin 289 (shared-file spot set); schema/scope/owner unit suites 90. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(java): exempt $-chain anonymous class defs from nested-class qualification (#2555 review) Review lens probe caught a HIGH collapse: same-named methods across sibling enum constant bodies attributed to the FIRST body's Method node (`M3$1.hook -> M3.log` where the log() call lives in C's body; the same-target sibling edge vanished entirely under dedup). Root cause: `populateClassOwnedMembers`'s qualifier chains a constant-body class def to `M3.M3$2` — its Class scope's parent is the enum's Class scope, unlike OCE anons whose parent is a Function scope — and its methods to `M3.M3$2.hook`. The structure-phase node id encodes `M3$2.hook`, so the graph-bridge's qualified key misses and falls to the file-wide simple-name lookup: first-write-wins. Fix: `qualify()` now skips CLASS-LIKE defs whose name already carries a `$` chain — a synthesized anonymous binary name is complete by construction (JLS 13.1). Narrowly scoped: `$`-named MEMBERS (legal and real in JS/TS) still qualify against their class, and named nested classes (`Outer.Inner`, #1978) are untouched. Discriminating regression test: same-name/distinct-target sibling bodies must each own their edge, and the misattributed cross-edge must not exist. Verified: full java.test.ts 231/231; Python+Kotlin 459 (heaviest populateClassOwnedMembers consumers) — zero assertion failures. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(ci): prettier formatting + java bench rebaseline at the final corpus (#2555) Two CI reds from the review-fix commit landing AFTER the bench rebaseline: (1) prettier reformat of the new java.test.ts describe; (2) the java scope-capture fingerprint drifted again because the review fix added the java-enum-constant-same-name fixture to the corpus — rebaselined at the true final corpus (196 fixtures, ce104a76…, scaling 1.05 < 1.5), local `measure.mjs --check` PASS across all 14 languages. Lesson honored going forward: the bench rebaseline is the LAST artifact step — any post-review fixture addition reopens it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(java): strict JLS 13.1 chaining through anonymous enclosing types (#2555) Per review discussion: anonymous enclosing types now chain into the binary name instead of flattening to the nearest NAMED host — the immediately enclosing type per JLS 13.1 may itself be anonymous: - anon inside an anon: NestHost$1$1 (was NestHost$2) - anon inside an enum constant: N$1$1 (was N$2) - named nested hosts (unchanged): EnumWrap$Mode$1 `nearestJavaAnonHost` becomes `nearestJavaEnclosingType` (named hosts OR anonymous bodies); an anonymous enclosing type's prefix is its own synthesized name (memo-bounded recursion); numbering is per immediately enclosing type in source order. Top-level-hosted names are untouched — the full existing suite passes unchanged. New coverage: anon-in-anon chain, anon-in-constant-body chain (with ownership), and a bodied constant in a NESTED enum (EnumWrap2$Mode$1 — the one host combination previously untested). Rides the unreleased v9 identity window (doc wording tightened); java bench fingerprint rebaselined at the final corpus, `--check` PASS across 14 languages; prettier clean. Verified: full java.test.ts 234/234 (one worker-crash flake rerun green in isolation). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
114 lines
5.8 KiB
TypeScript
114 lines
5.8 KiB
TypeScript
/**
|
|
* PDG FU-C (U-C1 / U-C5) — CALL_SUMMARY relation-type posture + the v3→4
|
|
* incremental reuse gate.
|
|
*
|
|
* CALL_SUMMARY is an INTERNAL PDG-engine edge: like the taint substrate edges
|
|
* (TAINTED / TAINT_PATH / CDG / REACHING_DEF / CFG) it must stay OUT of
|
|
* `VALID_RELATION_TYPES` so it never enters impact-style symbol-space traversal,
|
|
* and the impact relType allowlists (local-backend.ts ~:4373 / ~:5674) that gate
|
|
* on `VALID_RELATION_TYPES` therefore never surface it. The v4 bump forces a
|
|
* full re-analyze on a pre-v4 index (which has no CALL_SUMMARY edges, so an
|
|
* incremental top-up would silently under-report return-value ascent).
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import path from 'node:path';
|
|
import {
|
|
VALID_RELATION_TYPES,
|
|
EPISTEMIC_HERITAGE_RELATION_TYPES,
|
|
EPISTEMIC_CONSUMER_RELATION_TYPES,
|
|
} from '../../src/mcp/local/local-backend.js';
|
|
import { INCREMENTAL_SCHEMA_VERSION } from '../../src/storage/repo-manager.js';
|
|
|
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
const repoRoot = path.resolve(here, '..', '..');
|
|
|
|
describe('CALL_SUMMARY relation-type exclusion (U-C1)', () => {
|
|
it('is NOT in VALID_RELATION_TYPES (never enters impact symbol-space traversal)', () => {
|
|
expect(VALID_RELATION_TYPES.has('CALL_SUMMARY')).toBe(false);
|
|
});
|
|
|
|
it('shares the internal-PDG-edge exclusion posture with the taint substrate edges', () => {
|
|
// The whole PDG/taint substrate stays out of the impact allowlist.
|
|
expect(VALID_RELATION_TYPES.has('TAINT_PATH')).toBe(false);
|
|
expect(VALID_RELATION_TYPES.has('TAINTED')).toBe(false);
|
|
expect(VALID_RELATION_TYPES.has('REACHING_DEF')).toBe(false);
|
|
expect(VALID_RELATION_TYPES.has('CFG')).toBe(false);
|
|
expect(VALID_RELATION_TYPES.has('CDG')).toBe(false);
|
|
// Sanity floor: the public callgraph edges ARE in the allowlist.
|
|
expect(VALID_RELATION_TYPES.has('CALLS')).toBe(true);
|
|
});
|
|
|
|
it('is absent from the epistemic-boundary relation sets', () => {
|
|
expect(EPISTEMIC_HERITAGE_RELATION_TYPES).not.toContain('CALL_SUMMARY');
|
|
expect(EPISTEMIC_CONSUMER_RELATION_TYPES).not.toContain('CALL_SUMMARY');
|
|
});
|
|
|
|
it('is absent from the impact relType default allowlists in local-backend (the ~:4373/~:5674 filters)', () => {
|
|
// The two impact relType filters first intersect with VALID_RELATION_TYPES
|
|
// (above) and otherwise fall back to a hardcoded public-edge default list.
|
|
// Assert CALL_SUMMARY appears in NEITHER default list's source text, so it
|
|
// can never be the relType an impact traversal walks.
|
|
const src = readFileSync(
|
|
path.join(repoRoot, 'src', 'mcp', 'local', 'local-backend.ts'),
|
|
'utf8',
|
|
);
|
|
// Every default relType array literal in the impact filters.
|
|
const defaultLists = src.match(/\[\s*\n\s*'CALLS',[\s\S]*?\]/g) ?? [];
|
|
expect(defaultLists.length).toBeGreaterThan(0);
|
|
for (const list of defaultLists) {
|
|
expect(list).not.toContain('CALL_SUMMARY');
|
|
}
|
|
});
|
|
|
|
it('the /api/graph relationship projection does not special-case (allow OR block) CALL_SUMMARY', () => {
|
|
// The /api/graph relationship query (api.ts GRAPH_RELATIONSHIP_QUERY) is an
|
|
// unfiltered MATCH used for visualization, not an impact surface — it must
|
|
// not name CALL_SUMMARY in either direction (no bespoke allow/deny clause).
|
|
const api = readFileSync(path.join(repoRoot, 'src', 'server', 'api.ts'), 'utf8');
|
|
expect(api).not.toContain('CALL_SUMMARY');
|
|
});
|
|
});
|
|
|
|
describe('CALL_SUMMARY incremental reuse gate (U-C5)', () => {
|
|
it('INCREMENTAL_SCHEMA_VERSION is bumped to 9 (Java enum-constant-body + JLS-naming re-index window)', () => {
|
|
expect(INCREMENTAL_SCHEMA_VERSION).toBe(9);
|
|
});
|
|
|
|
it('a pre-current stamp fails the `=== INCREMENTAL_SCHEMA_VERSION` reuse gate → forces full re-analyze', () => {
|
|
// The reuse gate at run-analyze.ts:920 is exactly this strict equality on
|
|
// the persisted `existingMeta.schemaVersion` (a plain number, possibly
|
|
// absent on a legacy stamp). Replicate it as a typed predicate.
|
|
const passesReuseGate = (stampedSchemaVersion: number | undefined): boolean =>
|
|
stampedSchemaVersion === INCREMENTAL_SCHEMA_VERSION;
|
|
// A pre-v4 (v3) index has no CALL_SUMMARY edges → must NOT reuse.
|
|
expect(passesReuseGate(3)).toBe(false);
|
|
// A pre-v5 (v4) index predates the multi-verb Route identity change → its
|
|
// persisted Route nodes use the old url-only ids, so an incremental top-up
|
|
// would strand them → must NOT reuse.
|
|
expect(passesReuseGate(4)).toBe(false);
|
|
// A legacy stamp with no schemaVersion at all is likewise rejected.
|
|
expect(passesReuseGate(undefined)).toBe(false);
|
|
// A pre-v6 (v5) index predates the uniform 0-based line-storage flip → its
|
|
// COBOL/JCL/markdown/scope rows are still 1-based, so an incremental top-up
|
|
// would mix bases → must NOT reuse.
|
|
expect(passesReuseGate(5)).toBe(false);
|
|
// A pre-v7 (v6) index predates the callable-value-flow edges (#2437/#2522)
|
|
// — new edges between unchanged files would never enter the incremental
|
|
// write set → must NOT reuse.
|
|
expect(passesReuseGate(6)).toBe(false);
|
|
// A pre-v8 (v7) index predates the Java anonymous-class instance model
|
|
// (#2550) — `Worker.run`-keyed Method nodes would be stranded alongside
|
|
// the re-keyed `Worker$N.run` ones on unchanged files → must NOT reuse.
|
|
expect(passesReuseGate(7)).toBe(false);
|
|
// A pre-v9 (v8) index predates enum constant bodies + JLS 13.1
|
|
// immediate-host naming (#2555) — `E.hook`-keyed Method nodes and
|
|
// topmost-anchored `EnumWrap$1`-style ids would be stranded alongside
|
|
// the re-keyed ones on unchanged files → must NOT reuse.
|
|
expect(passesReuseGate(8)).toBe(false);
|
|
// A current-version stamp passes the gate (incremental top-up eligible).
|
|
expect(passesReuseGate(9)).toBe(true);
|
|
});
|
|
});
|