mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-03 02:21:44 +00:00
10 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1029a8ddd7
|
feat: add Spring DI resolver for @Autowired List<T> injection (#2200)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
* feat: add Spring DI resolver for @Autowired List<T> injection Addresses all P0/P1 findings from tri-review (#2200): - P0: Register INJECTS in RelationshipType union (compiles) - P0: Rewrite execute() to emit consumer→implementation edges from graph data only - P1: Register in VALID_RELATION_TYPES, single-pass O(N) indexes - P1: Java-only gate with early exit on non-Java repos - P1: Update FULL_ORDER golden test - 8 unit tests covering all edge cases * test: make VALID_RELATION_TYPES size assertion array-driven (no hardcoded count) The security test hardcoded toBe(16) for the relation type count, but PR #2200 added INJECTS, bumping it to 17. Replace the magic number with an EXPECTED_RELATION_TYPES array whose .length drives the size assertion, so future additions only need to append to the list. Fixes CI failure on PR #2200. * fix(ingestion): thread raw generic field types onto Property nodes so Spring DI matching works (review 4616076037 P0) Production declaredType is generics-stripped by design (extractSimpleTypeName: List<Shape> -> "List"), so the spring-di phase's anchored regexes could never match real extraction output — the phase was a silent no-op on every real Java repository, while its unit tests passed against hand-built node shapes. Add FieldInfo.rawDeclaredType captured verbatim from the field's type node (.text, generics and qualifiers preserved — same precedent as the JVM method extractor), thread it through both parse-worker Property sites, add it to the shared NodeProperties contract, and match on rawDeclaredType ONLY (no declaredType fallback: it can never match real data and would mask future plumbing regressions as quiet no-ops). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ingestion): gate Spring DI on real injection annotations, honest edge reason (review 4616076037 P1) Extract Java field annotations (shared extractAnnotations helper, moved verbatim from the method extractor) onto Property nodes and require @Autowired or @Inject before a collection field becomes an INJECTS candidate. Previously every edge's reason string fabricated "@Autowired" without any annotation ever being checked, and any plain collection field would have fanned out false edges once matching worked. @Resource is deliberately excluded: JSR-250 resolves by bean name first (defaulting to the field name), injecting a single named collection bean — the opposite of the collect-all-implementers fan-out INJECTS models. Pinned by a test. An annotated candidate missing rawDeclaredType now logs an isDev warning (plumbing-contract breach signal) instead of vanishing silently. SCHEMA_BUMP 9 -> 10: Property nodes gained rawDeclaredType + annotations; warm parse caches must invalidate or the DI phase silently no-ops on replayed pre-upgrade nodes (the #2038 trap). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(ingestion): framework-neutral di phase + language-scoped Spring matcher registry (review 4616076037 P1) spring-di was the only pipeline phase naming a language in shared core/ingestion code (DoD.md language rule; the maintainer's direction is a generic DI solution). Split it: - di-extractors/spring.ts: the Spring matcher (annotation gate, collection type parse, @Resource exclusion rationale, framework-specific reason payload) — language-scoped home, mirroring route-extractors/. - di-extractors/index.ts: DI_MATCHERS, a single-valued ReadonlyMap<SupportedLanguages, DiFieldMatcher> mirroring the SCOPE_RESOLVERS registry shape sanctioned by AGENTS.md. Constructor injection deliberately out of scope; widen to arrays only when a second same-language framework lands. - pipeline-phases/di.ts (renamed from spring-di.ts): framework-neutral — routes Property nodes to registered matchers by node language via a typed guard, then runs the unchanged reverse-index fan-out. Zero language or framework names remain (grep-verified). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ingestion): language- and qualified-name-scoped interface resolution for DI fan-out (review 4616076037 P2) The interface index was built from ALL Interface nodes regardless of language, keyed by bare simple name with last-writer-wins overwrite — a polyglot repo with a TS and a Java 'Shape' could fan Java INJECTS edges into TypeScript classes, and two same-named Java interfaces in different packages silently collapsed to whichever parsed last (documented GitNexus bug class: #2054, PR #1956). Resolution is now per-language with qualifiedName as the primary key (Interface nodes already carry package-qualified qualifiedName); dotted element types resolve via qualifiedName, bare names via a per-language simple-name index that records ambiguity and fails CLOSED. Ambiguity skips are observable: DIOutput.ambiguousSkipped + an aggregated isDev debug log, so 'no DI fields' is distinguishable from 'all candidates ambiguous'. Same-package tiebreaking is a pinned, documented follow-up. Order-independence pinned by running collision tests in both insertion orders. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ingestion): depth-aware Spring collection-type parser for idiomatic generics (review 4616076037 P3) The two anchored regexes silently skipped idiomatic Spring shapes: Map<Pair<A,B>, IFoo> (nested-generic key broke the [^,]+ split), List<? extends IFoo> / List<? super IFoo> (bounded wildcards), java.util.List<IFoo> (qualified wrapper), and whitespace/multi-line declarations. Replace them with a small scanner: whitespace normalization, wrapper matched by last dotted segment, depth-aware top-level-comma split, wildcard bound stripping, and a final plain-dotted-type-name gate so anything else (nested-generic elements, arrays, unbounded wildcards, embedded comments, unbalanced brackets) fails closed. Every accept and reject is documented in the module docstring and pinned by 27 table-driven cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(integration): prove Spring DI end-to-end through the real pipeline (review 4616076037 P1) Both no-op incarnations of this feature shipped with a green unit suite because every test hand-built the exact graph shape the phase expected — no test ever ran real Java source through the actual extraction pipeline. Add test/integration/spring-di-pipeline.test.ts: real .java fixtures via runPipelineFromRepo, pinning (a) the extraction contract on the annotated field's Property node (declaredType 'List', rawDeclaredType 'List<IFoo>', annotations ['@Autowired']), (b) set-equality on ALL INJECTS edges (exactly Consumer->FooA and Consumer->FooB; the non-annotated 'plain' field of the same type contributes nothing; no self-edges), and (c) a negative-control fixture with no injection annotations producing zero INJECTS edges. Either historical regression fails at least one of these. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(incremental): register INJECTS across product surfaces + delete-before-writeback (review 4616076037 P2) INJECTS was allowlisted in VALID_RELATION_TYPES but invisible or unhandled everywhere else. Register it deliberately: - REL_TYPES (gitnexus-shared schema-constants): web-side validRelType() otherwise silently rejects INJECTS filters (CLI/web single source of truth). - mcp/tools.ts cypher edge list (agent-facing schema discovery). - isGraphWideRelType: INJECTS validity is a whole-program property — a change to a THIRD file (the interface, or a new/removed implementer) creates/invalidates edges between two untouched files (the TAINT_PATH / #2084 M4 U6 class), so incremental extraction must always re-include the full fresh set. - deleteAllInjects (lbug-adapter): mirrors deleteAllInterprocTaintPaths — COUNT-then-DELETE under withConnLock, benign missing-table carve-out, re-throw otherwise (CodeRelation has no PK and there is no read-side dedup; a fail-soft delete + re-add would silently duplicate rows). - run-analyze.ts: the delete is UNCONDITIONAL, next to the Communities delete — deliberately NOT inside the options.pdg block: the di phase runs on every persisting analyze while the graph-wide re-include is unconditional, so a pdg-gated delete would append without deleting on every non-pdg incremental run (N runs = N copies). - local-backend.ts comment: opt-in traversal by design (not in default impact()/context() lists; no IMPACT_RELATION_CONFIDENCE entry per the WRAPS/FETCHES precedent — edges carry their own 0.8). - ARCHITECTURE.md: 14 -> 15 phases, DAG diagram, phase table, skip-list. Note: the tools.ts edge list also predates WRAPS/QUERIES/USES — that drift is pre-existing and left for a follow-up. Idempotency pinned end-to-end: two successive incremental runs (real runFullAnalysis + real LadybugDB, unrelated-file touches) leave the INJECTS row count stable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: describe INJECTS' actual precondition; drop stale fixed-at-16 comments (review 4616076037 P3) The shared-schema doc for INJECTS claimed an @Autowired precondition the code (pre-fix) never checked, and hardwired Spring semantics into what is now a framework-neutral edge type. Reword: precondition is an injection annotation recognized by a per-language matcher in di-extractors/; framework specifics live in the reason payload, not the type contract. security.test.ts comments still said the allow-list size 'stays fixed at 16' (it is 17 and the assertion derives from EXPECTED_RELATION_TYPES). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor: simplify DI surfaces — narrow matcher contract, dedup delete-alls, derive tools edge list Post-implementation simplification pass (4 review angles): - DiFieldMatch/CandidateField carried collectionType + matchedAnnotation that no consumer read (the matcher bakes both into reason) — narrowed to {elementTypeName, reason}. - parseElementTypeName had two guard branches fully subsumed by the final plain-dotted-type-name gate — deleted, rationale folded into the regex comment. - The three byte-identical delete-all-by-rel-type functions in lbug-adapter (TAINT_PATH / CALL_SUMMARY / INJECTS) are now one parameterized helper + thin wrappers with identical names, signatures, and message text (character-diff verified) — the missing-table regex and abort policy now live in exactly one place. - The cypher tool's hand-maintained edge-type list (already missing WRAPS/QUERIES/USES) is now derived from the canonical REL_TYPES — the drift class is gone rather than patched. - di phase: interface indexes are built only for languages that actually have candidates; test builder gained a rawDeclaredType opt-out replacing a hand-rolled node. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: apply Tier-2 review findings — qualified-name fail-closed, honest cypher docs, pinned delete contract, hook isolation - byQualifiedName was last-writer-wins on duplicate qualified names (reproduced: order-dependent INJECTS edges with ambiguousSkipped 0 — same package+interface duplicated across monorepo modules/source roots; Java qualifiedName has no file-path component). Both indexes now share the AMBIGUOUS fail-closed sentinel; order-flip test added. - The REL_TYPES-derived cypher edge list advertised pdg-gated types with no caveat (LLM queries on them silently return zero rows on default indexes) — caveat appended, INJECTS example added, impact relationTypes description now names the DI fan-out opt-in. - The delete-all re-throw contract (only defense against duplicate CodeRelation rows) was untested — error classification extracted to a pure classifyDeleteAllError and pinned exhaustively. - extractRawType/extractAnnotations hooks lacked the per-hook try/catch the pipeline applies elsewhere (#2286 pattern): a throwing hook would silently drop every remaining file in the language group. Hardened, degradation tested. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
2870aa6248
|
fix(grammars): load vendored tree-sitter grammars from vendor/ by absolute path (#2111) (#2144)
* fix(grammars): load vendored tree-sitter grammars from vendor/ by absolute path (#2111) The recurring Windows `EPERM: operation not permitted, symlink` (errno -4048) when adding the MCP server to Antigravity is NOT the #2101/#2110 module-load crash — it is an install-time arborist failure during the `_npx` reify that the MCP client triggers on every `npx gitnexus` launch. Root cause: the `postinstall` materialize step copied each vendored grammar (`vendor/tree-sitter-{c,dart,proto,swift,kotlin}`) into `node_modules/gitnexus/node_modules/tree-sitter-*` as a real package so runtime `require('tree-sitter-dart')` would resolve. Those packages are in no dependency graph, so every subsequent npm/npx reify treats them as **extraneous** and prunes/relocates them — on Windows the relocation goes through `@npmcli/move-file`'s symlink path and throws EPERM (symlinks need Developer Mode/admin), and on every OS the 2nd run silently deletes the grammars. This is the same class as #1728, which the materialize step itself claimed to have fixed. Fix (the prebuildify + node-gyp-build ecosystem pattern): never copy grammars into node_modules. Load each by absolute path from `vendor/<name>` via the new `requireVendoredGrammar` helper — the grammar's own `bindings/node` runs `node-gyp-build(<dir>)` and loads the committed `vendor/<name>/prebuilds/ <platform>-<arch>/…` directly (all 5 ship all 6 tuples). vendor/ is inside the package but not a node_modules subtree, so arborist never sees the grammars and the reify is idempotent — no EPERM, no silent deletion. - new src/core/tree-sitter/vendored-grammars.ts (requireVendoredGrammar / vendoredGrammarDir / VENDORED_GRAMMAR_PACKAGES; VENDOR_ROOT stable in dev+dist) - route all consumers through it: parser-loader, parse-worker, grpc proto, include-extractor (C), http-patterns kotlin, cli optional-grammars probe - postinstall drops the materialize step; build-tree-sitter-grammars.cjs builds in-place under vendor/ (gitignored) and deletes materialize-vendor-grammars.cjs - tests + grammar-introspection helper load grammars from vendor/ too (single source of truth); new vendored-grammars.test.ts guards against reintroducing a bare `require('tree-sitter-<vendored>')` Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(grammars): throw on a non-vendored name in requireVendoredGrammar Drift guard (PR #2144 review, P3): validate the argument against VENDORED_GRAMMAR_PACKAGES and fail loudly on an unknown name, so the three grammar lists (package set / CLI probe / build registry) drifting out of sync surfaces as a clear error instead of a confusing absolute-path require miss. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(grammars): prepack guard against stray vendor/<g>/build/ shadowing prebuilds Publish hygiene (PR #2144 review, P2). Now that build-tree-sitter-grammars.cjs source-builds into vendor/<name>/build/, a stray build dir would ship in the tarball (files:["vendor"] overrides .gitignore/.npmignore) AND shadow the committed prebuild — node-gyp-build resolves build/Release before prebuilds/. assert-publish-grammar-coverage.cjs (prepack) now fails `npm pack` if any vendor/*/build exists (findStrayBuildArtifacts), with a clear `rm -rf` fix hint. Adds unit coverage for the new pure function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(grammars): harden the #2111 no-bare-require regression guard PR #2144 review (P2). The guard regex missed dynamic import(), side-effect `import 'x'`, /subpath, and backtick loads, and only scanned src/. It now covers every node_modules-forcing form (single/double/backtick quotes, optional subpath), scans test/ too (excluding fixtures and the guard file itself), drops the `//`-substring false-negative (leading-comment-only heuristic), and adds a self-test asserting every load form is caught while prose mentions and tree-sitter-cpp are ignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(grammars): correct stale vendored-grammar comments PR #2144 review (P3). kotlin/query.ts called tree-sitter-kotlin an "optionalDependency" — it is vendored and loaded from vendor/ by absolute path (#2111). proto.ts now states its remaining `_require` is only for the real `tree-sitter` dependency, not a vendored grammar (which goes through requireVendoredGrammar). Comment-only; no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
df5ce1f49b
|
fix(ingestion): close remaining open language parsing-layer coverage gaps (#1919) (#2072)
* fix(c): skip computed #include MACRO instead of emitting a garbage import source (F5) * fix(cpp): emit a Variable per name for structured-binding declarations (F9) * fix(dart): extract static const/final class fields (F26) * fix(dart): capture old-style function typedefs (F28) * fix(dart): read real top-level variable shape instead of a dead type field (F29) * fix(kotlin): capture callable references (F47) * fix(kotlin): anchor infix-call capture to the operator only (F49) * fix(kotlin): extract secondary constructors as members (F48) * fix(kotlin): capture destructuring declarations (F51) * fix(kotlin): index companion-object properties as fields (F52) * test(kotlin): assert callable-reference coverage runs on the worker path (F47) * fix(swift): extract protocol property requirements (F75) * fix(swift): recognize enum_class_body as a method body node (F79) * test(ingestion): rebaseline swift captures-golden + scope-capture fingerprints (#1919) * fix(kotlin): attribute secondary-constructor body calls to the Constructor node (#1919 review CF1) A Kotlin secondary constructor's body executes statements like a method body, but the registry-primary scope-resolution path had no Function scope or Constructor def for it. A call inside the body resolved its caller anchor up to the enclosing Class scope, mis-attributing the CALLS edge to the class rather than the Constructor. Add `(secondary_constructor) @scope.function` to the Kotlin scope query so the body becomes its own scope, and synthesize a `@declaration.constructor` (named `constructor`, qualified `<Class>.constructor`, with parameter metadata) so the scope owns a Constructor def that bridges to the structure-phase Constructor node. Also add an arity-disambiguating lookup key for overloadable callables: two same-name secondary constructors of different arity (e.g. a zero-arg vs a 2-arg) share the qualified key whose first-write-wins assignment is source-order- dependent — so a zero-arg overload could resolve to a sibling. The structure node id encodes `#<arity>`; mirror that in the bridge keyspace and match by the def's parameterCount. Same-arity overloads collapse onto one arity key exactly as before, so no regression there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(kotlin): do not own function-local property bindings under the enclosing class (#1919 review CF3) Kotlin emits destructuring / loop bindings (`val (a,b) = pair`, `for ((k,v) in m)`) as `@definition.property` to dodge the block-scope local-symbol pruner. When such a binding sits inside a method body of a class, the structure-phase owner walk found the enclosing class and emitted a spurious HAS_PROPERTY edge (e.g. `C -> k`), treating a function-local as a class member. Guard the Property owner resolution: if a function-like ancestor is reached before any class container, the property is function-local and gets no owner edge (it falls back to a File DEFINES edge). Language-agnostic — genuine class fields sit directly in the class body with no intervening function, so they keep their HAS_PROPERTY owner edge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(kotlin): guard non-companion property isStatic=false (#1919 review CF4) Add a field-extraction case for a plain non-companion class `class C { val x: Int = 1 }` asserting the property `x` has isStatic=false, guarding the `isInsideKotlinCompanion` walk against false-positives. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(kotlin): dedup type_identifier lookup in extractOwnerName (#1919 review CF5) The `node.namedChildren.find(c => c.type === 'type_identifier')?.text` lookup was duplicated across the companion and non-companion branches of the Kotlin field-extractor's extractOwnerName. Hoist it into a single local, preserving the existing behavior (anonymous companion falls back to "Companion"; other nodes prefer the `name` field, else the type_identifier text, else undefined). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(dart): capture generic old-style function typedefs (#1919 review CF2) * test(dart): guard multi-name field count and top-level-var labels (#1919 review CF4) * docs(swift): correct isStatic comment re multi-modifier hasKeyword (#1919 review CF5) * test(ingestion): rebaseline dart+kotlin scope-capture fingerprints after review remediation (#1919) * fix(ingestion): correct CF3 owner-strip boundary set for accessor/init bodies and Dart signatures (#1919 review) The CF3 property-ownership guard used FUNCTION_NODE_TYPES, which (a) includes Dart bare signatures (function_signature/method_signature) — over-stripping every Dart class getter/setter's HAS_PROPERTY owner — and (b) omits Kotlin anonymous_initializer/getter/setter and Swift computed accessors — under- stripping destructuring/locals inside init{} and accessor bodies, emitting spurious Class->local HAS_PROPERTY edges. Introduces a guard-specific LOCAL_SCOPE_BODY_NODE_TYPES set (signatures excluded, accessor/init bodies included). Adds Dart accessor-ownership + Kotlin init/accessor destructuring regression fixtures. Both confirmed on the worker pipeline; no cross-language regression (1597 cross-language tests green). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
3b43eb8b47
|
fix(go): capture multi-name declarations (#2032)
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
|
||
|
|
a94d6ef80b
|
Extract registries into model/ module with SemanticModel interface (#786)
* Initial plan * feat(SM-20): extract registries into model/ module with SemanticModel interface - Create model/type-registry.ts — TypeRegistry interface + factory - Create model/method-registry.ts — MethodRegistry interface + factory - Create model/field-registry.ts — FieldRegistry interface + factory - Create model/semantic-model.ts — SemanticModel interface + factory - Create model/heritage-map.ts — re-export HeritageMap types - Create model/binding-accumulator.ts — re-export BindingAccumulator types - Create model/resolve.ts — move lookupMethodByOwnerWithMRO from call-processor - Update symbol-table.ts — delegate to SemanticModel for registry ops - Update call-processor.ts — re-export lookupMethodByOwnerWithMRO from model/resolve No circular dependencies: model/resolve.ts does NOT import resolution-context.ts. All 775 related unit tests pass with no regressions. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/27ad2975-1a31-4f50-815b-178ee8a95277 * fix: clarify re-export comment per code review feedback Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/27ad2975-1a31-4f50-815b-178ee8a95277 * refactor(SM-20): wire up SemanticModel as first-class resolution input PR #786 extracted TypeRegistry/MethodRegistry/FieldRegistry into model/ behind SemanticModel, but consumers still routed through SymbolTable delegates. This change completes Phase 6 of the fuzzy-lookup elimination roadmap by making call-processor, resolution-context, type-env, and heritage-map query the model directly via `table.model.{types,methods,fields}`. Also absorbs the open PR #786 review findings so the branch lands clean: - Removed duplicate JSDoc block on lookupMethodByOwner (symbol-table.ts) - Added model/index.ts barrel for the public model/ surface - Fixed O(n) buildParentMapFromHeritage BFS via head-pointer queue - Clarified re-export facade framing on binding-accumulator.ts and heritage-map.ts inside model/ - Refined @internal JSDoc on lookupMethodByOwnerWithMRO Changes: - symbol-table.ts: expose `readonly model: SemanticModel` on the SymbolTable interface. SymbolTable delegate wrappers (lookupClassByName etc.) stay as thin pass-throughs for backward compat; deletion is a follow-up once all internal callers are migrated. - model/resolve.ts: lookupMethodByOwnerWithMRO now takes SemanticModel instead of SymbolTable, removing the last SymbolTable import from the model/ module. Preserves circular-dependency firewall. - call-processor.ts: 6 call sites in D0 member resolution, field resolution, ctor override, and ctor disambiguation migrated to model.types/methods/fields. - resolution-context.ts: tier 3 class+impl lookup migrated. - type-env.ts: 5 sites across lookupClassDefsByName, resolveFieldType, and resolveMethodReturnType migrated. - heritage-map.ts: parent/child class-name resolution migrated. Tests: - symbol-table.test.ts: +10 parity and feeding-audit tests covering every model.{types,methods,fields} path (Class, Method, Property, Impl, Function-with-ownerId, Property-without-ownerId skip, arity filtering, clear cascade). - call-processor.test.ts: classLookupSpy now targets ctx.symbols.model.types since the wrapper is bypassed. - type-env.test.ts: createMockSymbolTable and the destructured-call makeSymbolTable helpers gained a model shim that forwards to the (possibly overridden) top-level lookup stubs. Validation: full suite 5603 passed / 159 skipped, resolver integration suite (19 files, 1766 tests) clean, tsc --noEmit clean. * refactor(SM-21): invert ownership — SemanticModel contains SymbolTable Follow-up to SM-20. Previously SymbolTable owned a `model` subfield; this commit turns the ownership direction around so the SemanticModel is the top-level container and SymbolTable is nested as `.symbols`: SemanticModel (top-level, passed everywhere) ├── types (TypeRegistry) ├── methods (MethodRegistry) ├── fields (FieldRegistry) └── symbols (SymbolTable — file-indexed + callable-name index) The owner-scoped registries live directly on the model; file and callable-name lookups go through `.symbols`. Consumers receive a `SemanticModel` and reach into the appropriate field — no more `table.model.types.X` double-hop. Core changes: - symbol-table.ts: createSymbolTable now takes injected TypeRegistry/MethodRegistry/FieldRegistry via a SymbolTableDeps argument. When omitted (test fallback), it creates standalone registries locally and clears them in clear() — production callers always inject. The five registry convenience delegates (lookupClassByName, lookupMethodByOwner, lookupFieldByOwner, lookupClassByQualifiedName, lookupImplByName) remain as thin forwards to the injected registries so standalone SymbolTable use (chiefly tests) stays ergonomic. - model/semantic-model.ts: createSemanticModel() now creates the three registries AND a SymbolTable wired to them, exposing the SymbolTable as `.symbols`. clear() cascades through all four. - resolution-context.ts: `readonly symbols: SymbolTable` field is replaced with `readonly model: SemanticModel`. Internal factory builds a SemanticModel and keeps a local `symbols` alias for backward-compatible inner body. Consumer migrations (src/): - call-processor.ts: ctx.symbols.add/.lookupExactAll/ .lookupCallableByName → ctx.model.symbols.*; ctx.symbols.model.X → ctx.model.X. buildTypeEnv option key renamed symbolTable → model. - type-env.ts: symbolTable parameter renamed model (type SemanticModel), all internal call sites rewritten to use model.types.*, model.methods.*, model.fields.*, model.symbols.lookupExactAll / .lookupCallableByName. - heritage-map.ts: 2 class-lookup sites migrated. - pipeline.ts: ctx.symbols → ctx.model.symbols throughout. Test migrations: - symbol-table.test.ts: parity tests (which validated the old table.model.X hop) replaced with direct SemanticModel coverage via createSemanticModel(). New tests exercise types/methods/fields/ symbols feeding end-to-end. - type-env.test.ts: createMockSymbolTable rebuilt as a SemanticModel-shaped mock that still accepts the legacy flat override bag for backward compat; inline `makeSymbolTable` helpers for destructured-call and importedReturnTypes suites rewritten to match the new shape; buildTypeEnv options `symbolTable: X` and `{ symbolTable }` shorthand renamed to `model:`; one real createSymbolTable-based test rewritten to use createSemanticModel. - call-processor.test.ts, heritage-map.test.ts, heritage-processor.test.ts, symbol-resolver.test.ts: bulk sed `ctx.symbols.` → `ctx.model.symbols.`. call-processor.test.ts spy updated to target `ctx.model.types.lookupClassByName`. Validation: full test suite 5589 passed / 169 skipped / 0 failed; tsc --noEmit clean; pre-commit eslint + prettier + typecheck all green. CLAUDE.md / AGENTS.md stats bumped from an earlier `npx gitnexus analyze` refresh (3965 symbols / 10012 edges / 243 flows). * refactor(SM-22/SM-23): dispatch table + DAG rearchitecture SM-22: Extract registration dispatch table into model/registration-table.ts. Replaces the if/else ladder inside SymbolTable.add() with an O(1) Map<NodeLabel, RoutingDecision> fan-out. SemanticModel wires the table per-instance so hooks close over the correct registries. SM-23: DAG rearchitecture. symbol-table.ts is now a pure 2-index leaf (fileIndex + callableByName) with zero imports from model/. All type/method/field routing lives in the model/ layer. Tests migrated to createSemanticModel() + model.symbols access pattern. Tests: 5632 passed, 0 failures. * refactor: delete dead code (skipCallableIndex + model/ facades) Removes the unused skipCallableIndex flag from the registration dispatch table and deletes two facade files that had zero consumers. skipCallableIndex was declared on RoutingDecision and populated for all 10 entries but never read at runtime — semantic-model.ts explicitly documented that the flag was NOT consulted. The callable-index gate lives inside SymbolTable.add() via CALLABLE_TYPES.has(type), which is the single source of truth. Deleting the flag keeps SymbolTable as the sole decision point and removes documentation-as-data. model/binding-accumulator.ts and model/heritage-map.ts were facade pass-throughs of their parent-directory counterparts. Grep confirms no consumer imports either from the model/ path — all usage goes through ../binding-accumulator.js and ../heritage-map.js directly. model/index.ts was the only "user" and re-exported them with a note about unifying the import boundary, but that boundary has no actual consumers today. Resolves review findings M-01 and M-03 from .context/compound-engineering/ce-review/20260411-144641-59605d93/maintainability.json Tests: 5631 passed, 0 failures (1 less than pre-Unit-1: the skipCallableIndex-specific assertion was removed). * refactor: remove lookupMethodByOwnerWithMRO backward-compat shim call-processor.ts re-exported lookupMethodByOwnerWithMRO from ./model/resolve.js as a backward-compat shim for symbol-table.test.ts. The function already lives in model/resolve.ts and is re-exported properly from model/index.ts (the barrel) — the call-processor shim was a duplicate export path with no durable reason to exist. Migrated the test import from call-processor.js to model/index.js (the canonical barrel). Deleted the re-export statement and the stale "re-exported for backward compatibility" comment block. Hoisted the remaining import to the top of the file with the other imports; the bottom-of-file position was a relic of the shim pattern. Resolves review finding M-02 from .context/compound-engineering/ce-review/20260411-144641-59605d93/maintainability.json Tests: 5631 passed, 0 failures. * refactor: harden registration dispatch runtime safety Two hardening changes in semantic-model.ts, both closing silent-failure paths in the SM-series dispatcher-bypass failure mode. 1. model.symbols.clear() now cascades to the owner-scoped registries. Previously, the SymbolTable facade exposed rawSymbols.clear directly, which only emptied fileIndex + callableByName — the types/methods/ fields registries stayed populated. Any caller holding a SymbolTable reference that invoked .clear() left the model in a split state where subsequent .add() calls double-registered in the registries. No current caller exercises this path, but it was a latent phantom- resolution risk that didn't belong in a public API. Extracted the cascade into a single cascadeClear closure wired into both model.clear() and the facade's clear field. 2. runExhaustivenessGuard now throws instead of console.warn on drift. The production short-circuit via NODE_ENV === 'production' is preserved, so real users never see the throw — but CI and dev runs now fail loudly if a NodeLabel is added to gitnexus-shared without being placed in one of the three registration-table allowlists. The previous warn-only behavior was silent in test output volume; SM-19 already documented dispatcher-bypass as the dominant silent-failure mode in this codebase. Test-first: added test/unit/model/semantic-model.test.ts covering model.symbols.clear() cascade (4 registries × clear = 4 tests), the existing model.clear() cascade (regression guard), and a happy-path construction test that verifies the current allowlists have zero drift. Resolves correctness P2 finding (symbols.clear() partial clear), correctness P3 (exhaustiveness warn-only), and kieran-typescript KT-03 (same exhaustiveness finding, agreement boost). Tests: 5638 passed (+7 new), 0 failures. * docs: fix stale JSDoc references in resolveStaticCall call-processor.ts:2215-2216 referenced SymbolTable.lookupClassByName and SymbolTable.lookupMethodByOwner via {@link}. Both methods were removed from SymbolTable during SM-20 — they now live on TypeRegistry and MethodRegistry respectively, accessible via model.types and model.methods. Other SymbolTable.* references in the codebase (lookupExactFull, add, lookupCallableByName in call-processor.ts:593, symbol-table.ts:86, type-extractors/types.ts:57) target methods that are still on SymbolTable and remain valid. Resolves correctness P3 and kieran-typescript KT-02 (same finding, agreement boost). * refactor: deduplicate ALL_NODE_LABELS constant ALL_NODE_LABELS was private in semantic-model.ts and duplicated verbatim in registration-table.test.ts. Two hardcoded lists meant a new NodeLabel added to gitnexus-shared could land in one copy but not the other, silently drifting the exhaustiveness invariant. Exported ALL_NODE_LABELS from semantic-model.ts, re-exported through model/index.ts for barrel consistency, and switched the test to import it instead of redeclaring. The explanatory comment now describes the single-source-of-truth contract. Resolves maintainability M-04. Tests: 5638 passed, 0 failures. * refactor: add compile-time NodeLabel exhaustiveness check The runtime exhaustiveness guard in semantic-model.ts caught drift at test time. Added a type-level check in registration-table.ts that catches drift at BUILD time — if a new NodeLabel is added to gitnexus-shared without being classified into one of the three allowlists, TypeScript fails the _exhaustiveCheck assignment and names the missing label. The runtime guard stays as belt-and-suspenders: if a future contributor bypasses the type check with @ts-ignore, the runtime guard still fires in dev/test. Implementation: converted the three allowlist Set<NodeLabel> initializers to use `as const` tuples, then derived a union type from the tuples and asserted `Exclude<NodeLabel, union> extends never`. Zero runtime impact — the exported Sets are unchanged, Map.get hot-path performance is unchanged, the test API is unchanged. Resolves kieran-typescript KT-04. Tests: 21/21 registration-table tests pass with zero modifications. * refactor(test): restore type safety to createMockSymbolTable createMockSymbolTable was widened to (overrides: any = {}): any with an eslint-disable-next-line, and every buildTypeEnv call site passed the mock as `model: mockSymbolTable as any`. The widening masked silent false-green tests: buildTypeEnv accesses model.types/methods/fields, and a flat any-typed override could silently return undefined from a path that TypeScript should have caught at compile time. Defined LegacyMockOverrides interface with typed stubs for each method the mock can override (SymbolTable reads + TypeRegistry/MethodRegistry/ FieldRegistry lookups). Return type is now SemanticModel, so the mock object is compile-checked against the real interface — a missing registry method is a type error, not a silent runtime undefined. Removed the eslint-disable and all 9 `as any` casts at call sites (lines 1287, 1300, 1307, 2124, 2138, 5823, 5835, 5850, 5870). The mock's return value now flows through buildTypeEnv's typed `model` option without coercion. Resolves kieran-typescript KT-01 and testing gap TG-02. This was the highest-value cleanup in the plan — the only finding representing real hidden test weakness. Tests: 360 passed | 7 skipped (type-env.test.ts), typecheck clean. * test: close coverage gaps in model/ registries Added direct unit tests for the three owner-scoped registries that previously had only transitive coverage via symbol-table.test.ts and registration-table.test.ts. These new tests pin behaviors that were flagged by the testing reviewer as untested or undertested. method-registry.test.ts (14 tests): - T-01: arity-fallback branch — when argCount matches no overload, fall back to the full pool so fuzzy resolution still has candidates. Previously untested and would have returned undefined instead of a valid candidate if the branch regressed. - T-02: requiredParameterCount range filtering — methods with default parameters accept any argCount in [requiredParameterCount, parameterCount]. Previously untested at the registry level. - Variadic fallback (parameterCount=undefined is retained during arity narrowing, bypassing range check). - Return-type dedup paths: shared returnType → first wins, differing returnTypes → undefined, firstReturnType=undefined → undefined, single-overload skips dedup entirely. type-registry.test.ts (9 tests): - classByName homonym accumulation (two User classes in different packages both returned). - classByQualifiedName disambiguation — same simple name, different FQNs resolve independently. - Partial classes with identical simple + qualified name accumulate in both indexes. - registerImpl stores Rust impl blocks separately from classes. - Multiple impl blocks per type accumulate. field-registry.test.ts (6 tests): - register/lookup round-trip, owner-scope isolation, last-wins on duplicate key (flat map, not overload list). - clear + re-register round-trip. Extended symbol-table.test.ts cascade test (renamed from "both registries" to "all three registries and the nested symbol table") to also assert model.methods and model.fields are cleared — the test name previously implied full coverage but only asserted types + symbols. Resolves testing findings T-01, T-02, T-03, T-05. Tests: 5667 passed (+29 new), 0 failures. * refactor(test): replace brittle reference-equality tests + add intent comments Two cleanups flagged as low-severity P3 by the testing reviewer: 1. registration-table.test.ts: Replaced three reference-equality tests (hook identity via toBe) with behavioral tests that survive a future refactor to per-label closures. The new "class-like behavior group" describe iterates Class/Struct/Interface/Enum/Record/Trait and verifies each one writes to types.registerClass. Same pattern for Method/Constructor. A separate "behavior group isolation" describe verifies class-like hooks don't leak into methods/fields and Impl never pollutes registerClass. Strictly more coverage than the reference-equality tests provided and implementation-independent. 2. symbol-resolver.test.ts: Added a comment above the lookupExactFull and SM-16: getFiles() describes explaining why they intentionally use createSymbolTable() directly instead of createSemanticModel(). The DAG leaf-only behaviors they test do not involve registries, so testing the bare SymbolTable keeps the unit isolated. Prevents a future reader from "fixing" the inconsistency. 3. qualified-class-lookups.test.ts: Added a comment above `const symbolTable = model.symbols` explaining that processParsing writes still reach the owner-scoped registries via SemanticModel's fan-out — the alias is convenience, not a leaf in isolation. Resolves testing T-04, kieran-typescript KT-05, kieran-typescript KT-06. Tests: affected files all green (112 passed in registration-table + symbol-resolver + qualified-class-lookups). * refactor(model): collapse RoutingDecision wrapper and trim barrel surface Two cleanups against the advanced-review findings on post-Unit-9 state: S2 (cross-reviewer agreement — architecture-strategist + code-simplicity): Delete the RoutingDecision single-field wrapper interface. Post-Unit-1 it held exactly one field (hook: RegistrationHook) and added pure ceremony at every call site — `dispatchTable.get(key)!.hook(name, def)` vs the now-direct `dispatchTable.get(key)!(name, def)`. Change the Map type from Map<NodeLabel, RoutingDecision> to Map<NodeLabel, RegistrationHook>, drop the interface, and update 17 test call sites. A3 (architecture-strategist): Trim model/index.ts barrel surface. createRegistrationTable, RegistrationHook, and RegistrationTableDeps were re-exported from the barrel despite having zero legitimate consumers outside model/ itself. The only callers (semantic-model.ts and registration-table.test.ts) import directly from ./registration-table.js. Barrel exposure invited external callers to construct orphan dispatch tables with independent registries, weakening the SM-21 ownership inversion where SemanticModel is the composition root. Kept CALLABLE_ONLY_LABELS, INERT_LABELS, DISPATCH_LABELS exported since those remain useful for downstream resolution logic and have no construction risk. Resolves review findings: - S2 (code-simplicity P3, 0.85) + architecture-strategist residual - A3 (architecture-strategist P3, 0.82) Tests: 5674 passed, 0 failures. Typecheck clean. * refactor(model): replace runtime exhaustiveness guard with compile-time bijection Replace the three-layer drift protection (hardcoded ALL_NODE_LABELS array + 3 tuple consts + _ExhaustiveLabelCheck type + runExhaustivenessGuard runtime + CI taxonomy test) with a single Record<NodeLabel, LabelBehavior> map that structurally proves every invariant at compile time. ## Before - ALL_NODE_LABELS hardcoded in semantic-model.ts (36 entries, could drift) - DISPATCH_LABELS_TUPLE / CALLABLE_ONLY_LABELS_TUPLE / INERT_LABELS_TUPLE private tuples (36 more entries total, could overlap or miss) - _ClassifiedLabel / _UncoveredLabel type-level check (caught missing labels but NOT duplicates across tuples) - runExhaustivenessGuard runtime throw (only defense against duplicates) - NodeLabel taxonomy coverage test in CI (same check as runtime guard) Four defenses for invariants that the type system can express directly. ## After ```ts type LabelBehavior = 'dispatch' | 'callable-only' | 'inert'; const LABEL_BEHAVIOR = { Class: 'dispatch', // ...36 entries... Tool: 'inert', } as const satisfies Record<NodeLabel, LabelBehavior>; ``` The `as const satisfies Record<NodeLabel, LabelBehavior>` combo enforces: 1. **Every NodeLabel must be a key** — Record requires all K keys. Adding a NodeLabel to gitnexus-shared without classifying it here fails with "Property 'X' is missing in type ..." naming the drifted label. 2. **No non-NodeLabel keys allowed** — `satisfies` with object literals triggers excess-property checking. A typo'd key fails to compile. 3. **No duplicate classification** — impossible by construction; object keys are unique at the source level. 4. **Valid category** — LabelBehavior is a narrow union, typos caught. `ALL_NODE_LABELS`, `DISPATCH_LABELS`, `CALLABLE_ONLY_LABELS`, and `INERT_LABELS` are now derived via `Object.keys(LABEL_BEHAVIOR)` and `filter(l => LABEL_BEHAVIOR[l] === ...)` — single source of truth, structurally impossible to drift. ## Deleted - runExhaustivenessGuard() function in semantic-model.ts (~18 lines) - ALL_NODE_LABELS hardcoded array in semantic-model.ts (~38 lines) - DISPATCH_LABELS_TUPLE / CALLABLE_ONLY_LABELS_TUPLE / INERT_LABELS_TUPLE private consts in registration-table.ts (~30 lines) - _ClassifiedLabel / _UncoveredLabel / _exhaustiveCheck type machinery (~20 lines) ## Kept named proofs: none The `as const satisfies` on the object literal already catches all four drift modes. Named type-level proofs (_MissingFromMap / _ExtraKeysInMap) are pure duplication and were removed per review. ## Also in this commit - S6: trim wrappedAdd narration comments in semantic-model.ts (Step 1/2/3 block comments removed; kept the Function+ownerId WHY note) - A3: tighten model/index.ts barrel — createRegistrationTable, RegistrationHook, RegistrationTableDeps remain direct-imports only; ALL_NODE_LABELS and LabelBehavior re-exported from the new home in registration-table.ts ## Resolves - Advanced-review S4 (runtime guard per-call cost) — guard no longer exists - Advanced-review S1 (tuple three-defenses indirection) — single Record replaces all tuples - Correctness P3 (exhaustiveness warns-only) — structurally impossible to drift - Unit 6 type-level check — subsumed by the Record type - Unit 3 runtime throw — no longer needed Tests: 5674 passed, 0 failures. Typecheck clean. * test(model): delete duplicate closure-isolation spy tests S5 (code-simplicity P3): The 'closure isolation — each hook can only write to its registry' describe block duplicated the 'behavior group isolation' block's coverage via a different mechanism. Behavioral tests (lines 151-174, kept): table.get('Class')!('User', def); expect(deps.methods.lookupMethodByOwner('unrelated', 'User')).toBeUndefined(); expect(deps.fields.lookupFieldByOwner('unrelated', 'User')).toBeUndefined(); Spy tests (deleted, ~55 lines): vi.spyOn(deps.methods, 'register') table.get('Class')!('User', def); expect(methodsSpy).not.toHaveBeenCalled(); Both assert the same invariant — classHook does not touch the methods or fields registries. The behavioral form observes the END STATE of the registry (lookup returns undefined), which is the actual contract. The spy form asserts the IMPLEMENTATION (a specific method was not called), which couples to internal wiring — a refactor to a different register function name would break the spy test while the behavioral test would still pass. Also dropped the now-unused `vi` import from vitest. Tests: 24/24 registration-table.test.ts pass (-4 from spy deletion). * refactor(model): compile-time cross-invariant between CLASS_TYPES and dispatch classHook A1 (architecture-strategist P2, 0.90): CLASS_TYPES in symbol-table.ts and the class-like entries of the dispatch table were two independent hardcoded sets. Adding a new class-like label (e.g. Swift 'Extension') to one but not the other would silently degrade qualifiedName population — the symptom is subtle (partial qualified-name lookups) and no test asserted the co-extensive invariant. Fixed with a single source of truth and a two-layer compile-time enforcement: ## symbol-table.ts - Add `CLASS_TYPES_TUPLE` as `readonly [...] as const satisfies readonly NodeLabel[]`. The `satisfies` forces every tuple entry to be a valid NodeLabel at compile time. - Export derived type `ClassLikeLabel = typeof CLASS_TYPES_TUPLE[number]`. - Derive `CLASS_TYPES` Set from the tuple — same runtime shape as before, now typed `ReadonlySet<NodeLabel>`. ## registration-table.ts - Import `CLASS_TYPES_TUPLE` and `ClassLikeLabel` from symbol-table.ts. - Narrow the `satisfies` on `LABEL_BEHAVIOR` via intersection: Record<NodeLabel, LabelBehavior> & Record<ClassLikeLabel, 'dispatch'> This forces every class-like label to have value 'dispatch' at compile time. Adding a label to CLASS_TYPES_TUPLE without classifying it as dispatch in LABEL_BEHAVIOR fails to compile with a type error naming the drifted label. - Build the class-like entries of the dispatch Map by iterating `CLASS_TYPES_TUPLE` at factory time. Adding a label to the tuple automatically wires it to classHook — no second place to update. ## What the design prevents 1. Drift scenario A (A1 original): 'Extension' added to CLASS_TYPES_TUPLE but not to LABEL_BEHAVIOR → compile error on LABEL_BEHAVIOR's satisfies. 2. Drift scenario B: 'Extension' added to CLASS_TYPES_TUPLE but not wired to classHook → impossible because the Map is derived from the tuple. 3. Drift scenario C: class-like label classified as something other than 'dispatch' in LABEL_BEHAVIOR → compile error on the narrowed intersection. Runtime behavior unchanged: same 6 labels in CLASS_TYPES, same 6 class-like entries in the dispatch Map. Tests pin the behavior via the existing behavior-group tests in registration-table.test.ts. DAG unchanged: registration-table.ts already imported from symbol-table.ts (the allowed upward direction). symbol-table.ts still imports nothing from model/. Tests: 5670 passed, 0 failures. Typecheck clean. * test(field-extraction): use SemanticModel facade instead of raw SymbolTable A6 (architecture-strategist P3, 0.85): field-extraction.test.ts created its FieldExtractorContext fixture with `symbolTable: createSymbolTable()` — a raw SymbolTable leaf, not the facade. In production, the context's symbolTable field is always `model.symbols` (the SemanticModel-wrapped facade where .add() dispatches through the owner-scoped registries). The current field extractors don't call symbolTable.add() at all, so this change is behavior-neutral today. The value is architectural consistency — matching the test fixture to the production shape prevents silent drift if a future field extractor starts registering dynamically-discovered properties via the context. Without the fix, such writes would hit the raw leaf and skip the fan-out, and tests would pass even though the symptom (empty FieldRegistry) would manifest in production. Tests: 50/50 field-extraction.test.ts pass. Production tsc --noEmit clean. Test-tsconfig error count unchanged (634 pre-existing errors in unrelated test files, out of scope). * refactor(A5): decouple model/resolve.ts from language registry Move the MroStrategy type into gitnexus-shared and replace the language: SupportedLanguages parameter on lookupMethodByOwnerWithMRO with a direct mroStrategy: MroStrategy literal. Callers derive the strategy from their language provider before invoking the resolver. model/resolve.ts no longer imports from ../languages/index.js, so the model/ layer is free of cross-layer coupling with the language registry — this closes finding A5 from the SM-20/21/22/23 advanced review (plan 006). * feat(A4): add MethodRegistry.lookupMethodByName flat-by-name index Add a secondary `methodsByName: Map<string, SymbolDefinition[]>` index on MethodRegistry that returns every method with a given unqualified name, accumulated across owners and overloads. The new index shares SymbolDefinition references with methodByOwner — no duplication. This is step 1 of the A4 double-index removal (plan 006). Tier 3 global resolution will switch to this index in Unit 3 so Method and Constructor can be removed from CALLABLE_TYPES in Unit 4. * refactor(A4): extend Tier 3 + memberCallByFile to consult method registry Add model.methods.lookupMethodByName to Tier 3 global resolution in resolution-context.ts and to the callable-pool build in call-processor.ts (resolveMemberCallByFile + D2 widen path). Intentionally behavior-preserving: Method and Constructor are still in CALLABLE_TYPES so the new lookup returns identical candidates that already reach Tier 3 through callableByName. Both paths dedup by nodeId during this intermediate state — Unit 4 shrinks CALLABLE_TYPES and the dedup is removed. Part of plan 006 A4 step 2. * refactor(A4): shrink CALLABLE_TYPES to free callables only CALLABLE_TYPES = {Function, Macro, Delegate}. Method and Constructor are no longer double-indexed in callableByName — they reach resolvers through model.methods.lookupMethodByName instead. Companion changes: - Introduce CALL_TARGET_TYPES = CALLABLE_TYPES ∪ {Method, Constructor} for the resolver's kind filter (filterCallableCandidates, countCallableCandidates). Separates registration semantics (narrow) from the resolver's acceptable-target set (wide). - type-env.ts for-loop return-type inference consults both indexes, treating the union as the authoritative call pool. - resolveMemberCallByFile + D2 widen path keep the nodeId dedup in place: Python/Rust/Kotlin class methods emitted as Function+ownerId still land in both indexes until Unit 5 unblocks the normalization. - Tier 3 global resolution (resolution-context.ts) keeps the same dedup for the same reason. Test updates reflect the new contract: Method/Constructor live in methodsByName, not callableByName. Orphan Method-without-ownerId now lives only in the file index (no registry coverage). Part of plan 006 — closes A4 for strictly-labeled methods. Python/ Rust/Kotlin Function+ownerId normalization is tracked as Unit 5 (blocked). * refactor: rename CALLABLE_TYPES → FREE_CALLABLE_TYPES Pure rename. The constant's meaning changed in Unit 4 (free callables only — no methods, no constructors) so the name now reflects that scope: "callables that have no owner scope". Updates the constant declaration and every consumer in src/ and test/. Closes plan 006 Unit 6. * refactor(A2): strict SymbolTableReader (pure reads) + SymbolTableWriter (+add) Split the SymbolTable interface into three strictly layered surfaces: - SymbolTableReader: lookups + iteration. NO add, NO clear. Holders cannot mutate the table in any way. - SymbolTableWriter extends Reader: + add. NO clear. Holders can register new symbols but cannot trigger a leaf-index reset. - InternalSymbolTable (private, not exported): + clear. The cascading reset capability is reachable only through createSymbolTable's return type, held exclusively by SemanticModel.rawSymbols. SemanticModel.symbols is now typed as SymbolTableWriter — external consumers (workers, processors, pipelines) can register symbols and query them, but cannot reach .clear(). The A2 LSP fix holds: callers holding any public reference cannot desync the leaf indexes from the owner-scoped registries. Delete the transitional `type SymbolTable = SymbolTableReader` alias and migrate every consumer (src + test) to the explicit names: - Field and parameter annotations use SymbolTableReader by default; only code that calls .add() uses SymbolTableWriter. - parsing-processor (workers + sequential paths) takes SymbolTableWriter so it can register extracted symbols. - field-types, call-processor, named-binding-processor, workers/parse-worker: use SymbolTableReader (query-only). - Tests: drop the stale `clear` fields from mock factories and migrate the semantic-model cascade tests from the removed model.symbols.clear() path to model.clear(). Closes plan 006 Unit 7. Industry sources: TypeScript compiler API builder pattern, Salsa ParallelDatabase, .NET IReadOnlyList. See the a2-lsp-clear-contract-research artifact for full citations. * feat(A2): add SemanticModel.resetFileIndex() partial-reset entry point Add a named method that clears only the leaf file and callable indexes without cascading to the three owner-scoped registries (types, methods, fields). Replaces the rare partial-reset use case that was previously reachable via the now-removed symbols.clear() path from A2 (plan 006 Unit 7). JSDoc makes the semantic difference with model.clear() explicit so future readers don't have to guess which method to call for a given reingestion scenario. Test-first: three scenarios cover the partial-vs-full semantics, re-add after reset, and idempotency. Closes plan 006 Unit 8. * docs(S7): trim registration-table module JSDoc Remove the ~24 lines of design-provenance citations from the module JSDoc. The rust-analyzer, TypeScript-compiler, and Fowler references are preserved in git history via the original SM-22 commits and in plan 006 Unit 9. Keep the ownership diagram, behavior-group table, and the 'How to add a new NodeLabel' checklist — those are load-bearing for future contributors. Closes plan 006 Unit 9 (S7 advanced-review finding). * test(S3): migrate type-env.test.ts off LegacyMockOverrides Replace the createMockSymbolTable bridge and LegacyMockOverrides interface with real createSemanticModel() + add() calls across all 14 call sites. Where a test needs a specific registry lookup that can't be pre-populated cleanly, use vi.spyOn on the real registry instead. Pattern breakdown: - Pattern A (pre-populate via model.symbols.add): 13 sites - Pattern B (vi.spyOn on registry lookup): 1 site Deletes LegacyMockOverrides + createMockSymbolTable entirely. The real MethodRegistry arity/returnType semantics match the hand-rolled mock behavior in every migrated case, and no 'as any' casts remain in the file. Closes plan 006 Unit 10 (S3 advanced-review finding). * refactor: remove unused MroStrategy type exports from language-provider and resolve modules * refactor: relocate symbol-table, heritage-map, resolution-context into model/ Use git mv so blame and history follow each file: - gitnexus/src/core/ingestion/symbol-table.ts → model/symbol-table.ts - gitnexus/src/core/ingestion/heritage-map.ts → model/heritage-map.ts - gitnexus/src/core/ingestion/resolution-context.ts → model/resolution-context.ts These three files are part of the SemanticModel layer (file/callable indexes, heritage parent map, tiered resolver) and now sit alongside the registries they collaborate with. Updates every consumer import path across src/ and test/ to the new locations. * refactor(model): enforce pure-leaf DAG + delete legacy re-exports model/ is now a pure leaf: zero upward imports and zero compat shims in its parent processors. Completes the DAG cleanup started in the previous commit. 1. walkBindingChain — moved into model/resolution-context.ts; named-binding-processor.ts deleted. 2. NamedImportMap + NamedImportBinding + isFileInPackageDir — moved into model/resolution-context.ts. Every consumer now imports from the canonical location directly. Legacy re-exports in import-processor.ts deleted. 3. c3Linearize + gatherAncestors — moved into model/resolve.ts. mro-processor.ts imports them back for computeMRO. Legacy c3Linearize re-export from mro-processor.ts deleted. 4. ExtractedHeritage type — moved into model/heritage-map.ts. call-processor.ts, parsing-processor.ts, pipeline.ts, heritage-processor.ts, and the test files now import it from the canonical location. Legacy re-exports in parse-worker.ts and heritage-processor.ts deleted. 5. resolveExtendsType — rewritten in model/heritage-map.ts to take an explicit HeritageResolutionStrategy (A5-style DI). buildHeritageMap accepts an optional getHeritageStrategy callback; production uses getHeritageStrategyForLanguage from heritage-processor.ts. Legacy resolveExtendsType re-export from heritage-processor.ts deleted. Verified: - grep 'from "..' gitnexus/src/core/ingestion/model → empty - grep 'Re-export for legacy' gitnexus/src/core/ingestion → empty - npx tsc --noEmit → clean - npx vitest run → 5686 passing * docs(model): strip phase/plan references from module comments Remove SM-20/21/22/23, A2/A4/A5, plan 006, Unit N labels and historical phrasing ("previously", "legacy", "model-leaf DAG cleanup") from all 10 files in src/core/ingestion/model/. Preserve domain vocabulary (Tier 1/2/3), invariants, and caveats — only the plan archaeology is gone. * refactor(model): tighten interface segregation + compile-time invariants Apply four gated findings from branch-wide code review: - SemanticModel.symbols now typed as SymbolTableReader; MutableSemanticModel widens it back to SymbolTableWriter. ResolutionContext.model is typed as MutableSemanticModel since it owns the lifecycle. Resolvers that only query symbols can annotate their own fields as SemanticModel to drop write access at the type level. - Lookup methods (lookupExactAll, lookupCallableByName, lookupClassByName, lookupClassByQualifiedName, lookupImplByName) now return readonly SymbolDefinition[]. The returned arrays are live views into the internal indexes; the readonly marker prevents accidental caller mutation. walkBindingChain return type narrowed to match. - FREE_CALLABLE_TUPLE + FreeCallableLabel exported from symbol-table.ts as the single source of truth for free-callable labels. LABEL_BEHAVIOR now satisfies Record<FreeCallableLabel, 'callable-only'> as a second cross-invariant alongside Record<ClassLikeLabel, 'dispatch'>. Adding a label to the tuple without classifying it as 'callable-only' fails at build time. CALLABLE_ONLY_LABELS is now a re-export alias of FREE_CALLABLE_TYPES so the two sets cannot drift. - walkBindingChain fast-exits before allocating its cycle-detection Set when the caller's file has no named bindings. Skips ~200k transient Set allocations per large-repo resolution pass. Also fixes five stale comments flagged by the review: duplicate JSDoc block on RegistrationHook merged; resolve.ts "delegates to mro-processor" direction corrected; RegistrationTableDeps JSDoc names createRegistrationTable (not createSymbolTable); mro-processor.ts "re-exported at top" stale comment removed; gatherAncestors export comment matches reality. tsc --noEmit clean, full test suite green (5786 tests). * refactor(model): resolve four deferred P2 review findings Address the four gated items from the branch-wide review that needed design decisions before applying: F#3 — Method/Constructor without ownerId fallback to callable index. The dispatch hook silently skips owner-scoped labels that lack an owner (an extractor contract violation — AST-degraded parse, or a buggy language extractor). Pre-dispatch-table code let such defs fall through to callableByName and stay reachable at Tier 3 global resolution. This restores that fallback in SymbolTable.add so orphaned Methods and Constructors don't silently vanish. Property deliberately does NOT participate in the fallback to avoid polluting common names like id / name / type. F#4 — Delete MutableSemanticModel.resetFileIndex. The method had zero production callers (only three tests), documented a "rare partial- reingestion flow" that was never implemented, and contained the adversarial-reviewer's double-populate trap: calling resetFileIndex followed by re-adding the same class symbol would push a duplicate SymbolDefinition into TypeRegistry.classByName without ever clearing the first one. If incremental reingestion is ever needed, it can be designed properly with per-file TypeRegistry invalidation. For now, deleting the footgun is safer than documenting it. F#5 — Compile-time dispatch-table completeness check. `LABEL_BEHAVIOR` already enforces "every NodeLabel is classified" via `Record<NodeLabel, LabelBehavior>`, but the dispatch-table factory populated its Map with manual `table.set(...)` calls that TypeScript could not correlate back to the `'dispatch'` classification. Add a type-level `DispatchLabel` extracted from `LABEL_BEHAVIOR` via a conditional mapped type, and build the table from an object literal that satisfies `Record<DispatchLabel, RegistrationHook>`. Adding a new dispatch-classified label without wiring it to a hook now fails the build with a named-key error — no more silent no-op hooks. F#7 — Tier 3 dedup fast-path via MethodRegistry.hasFunctionMethods. The Set-based dedup between callableDefs and methodDefs is only needed when a Python/Rust/Kotlin class method (emitted as Function+ownerId by the worker) lands in both indexes. For TS/Java/C#/C++/Ruby-only repos — where the two indexes are disjoint by construction — the dedup was pure overhead on every global-tier hit. MethodRegistry now tracks whether any Function-typed def was ever registered, and resolution- context branches Tier 3 into a concat-only fast path when that flag is false. Slow path with dedup survives unchanged for mixed-language repos. New tests pin the invariants: hasFunctionMethods flag transitions, Method/Constructor orphan fallback, Property non-fallback, and the MethodRegistry clear() reset. Full test suite green (5756 tests). * refactor(model): close remaining P3 review findings + coverage gaps Address the remaining review items in one batch. Production refactors: - Rename classHook → classLikeHook (M05). The hook handles Class / Struct / Interface / Enum / Record / Trait; the vocabulary used in surrounding docs and the behavior-group table is "class-like". The rename makes the code match the taxonomy without forcing readers through a mental glossary. - Extract MAX_BINDING_CHAIN_DEPTH constant in resolution-context.ts and document it as a known silent false-negative source (ADV-003). Five hops cover the common TypeScript monorepo pattern; raising the cap is a one-line change if a real repo exceeds it. walkBindingChain consumes the constant so the 5 magic number no longer floats free. - Replace defs.filter() allocation in MethodRegistry.lookupMethodByOwner with a two-pass streaming count + conditional materialization (PERF-04). Pure-match and pure-reject arity paths now skip the filtered-array allocation entirely; only the discriminating case (at least one match AND at least one rejection) pays it. - Rewrite NOOP_SYMBOL_TABLE in parse-worker.ts and NOOP_SYMBOL_TABLE_SEQ in parsing-processor.ts to implement all six SymbolTableReader methods (ADV-005). The `as unknown as SymbolTableReader` cast is removed in favor of a direct SymbolTableReader annotation, so future additions to the interface surface as compile errors on the stubs instead of silently falling through. - type-env.ts getCallableUnionCount and getFirstCallable now take `model: SemanticModel` as an explicit argument instead of reaching into the enclosing `model!` non-null assertion (KT-003). Callers enter via an `if (model)` guard and pass the narrowed reference, so the non-null precondition is visible at the type level and the closures cannot be accidentally extracted into a context without the guard. - Tier 3 dedup in resolution-context.ts now covers all four index reads (classDefs, implDefs, callableDefs, methodDefs) via a pushUnique helper (C-03). Previously classDefs and implDefs were spread directly without dedup; any theoretical nodeId collision would have produced duplicates in globalDefs. Test infrastructure: - Extract makeDef / makeMethod factory helpers into test/unit/model/helpers.ts (T-07). The four registry/table test files now import the shared helper and specialize with overrides, removing ~25 lines of duplicated boilerplate and creating a single point of maintenance. New test coverage: - T-01: c3 BFS fallback — cyclic Python hierarchy that fails c3 linearization and must fall back to heritageMap.getAncestors() BFS order. Added to the lookupMethodByOwnerWithMRO describe block. - T-02: Tier 2a-named precedence — verifies the binding chain walker fires before Tier 2a import-scoped when an aliased import `import { User as U } from B` competes with a raw same-name Tier 2a hit. Also pins Tier 1 same-file precedence over Tier 2a-named. - T-03: Tier 3 Function+ownerId dedup — end-to-end test that a Python class method emitted as `Function + ownerId` yields exactly ONE Tier 3 candidate (not two). Companion test pins the fast-path branch for hasFunctionMethods === false repos. - T-06: walkBindingChain guards — circular re-export detection, depth-cap exceeded drop, and boundary case at exactly MAX_BINDING_CHAIN_DEPTH hops resolving successfully. All tests added to a new test/unit/model/resolution-context.test.ts dedicated to ResolutionContext.resolve() tier-precedence invariants. Full suite: 5708 passing (minus the known Windows LBUG lock flake that passes in isolation). --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> |
||
|
|
12be2025f1
|
feat(ts,js): TypeScript/JavaScript MethodExtractor config (#588)
* feat(ts,js): MethodExtractor config for TypeScript and JavaScript (#570) Add per-language method extraction config following the established JVM and C# patterns. Shared config base mirrors the field extractor's typescript-javascript.ts pattern — TS-only node types are harmless no-ops for JS. Key features: - isAbstract for abstract class methods and interface methods - Parameter extraction with isOptional (?:, defaults) and isVariadic (...) - Decorator extraction from preceding body-level siblings - isAsync and isOverride detection - Visibility via accessibility_modifier two-pass pattern - Return type extraction unwrapping type_annotation * test(ts,js): add override, getter/setter, destructured param tests Address code review findings: - Add override method detection test - Add getter/setter extraction test - Add destructured parameter with type annotation test - Tighten constructor and private method assertions * refactor(ts,js): address code review findings - Replace O(M*N) decorator index scan with previousNamedSibling walk - Remove dead findVisibility 'modifiers' fallback (TS uses accessibility_modifier, not a modifiers wrapper) - Document call_signature/construct_signature as known gaps - Document that TS constructors are method_definition nodes - Remove unused findVisibility import * fix(ts,js): type guard before cast, add generator/computed/overload tests - Use type guard pattern (Set.has check before as-cast) in visibility extraction to ensure string is validated before narrowing - Add generator method test (*items()) — confirms extraction works - Add computed property name test ([Symbol.iterator]) — documents bracket-in-name behavior as intentional - Add class-level method overload test — verifies overload signatures + implementation are all extracted * fix(ts,js): detect #private methods as visibility 'private' ES2022 private class methods (#name) use private_property_identifier as their name node type. Detect this and return 'private' visibility instead of the default 'public'. * fix(ts,js): address review findings + close ingestion gaps - hasKeyword/findVisibility: skip name field child to prevent false positives on soft-keyword method names (e.g. `abstract()`, `static()`) - extractTsJsParameters: filter TS `this` parameter (compile-time only) - extractMethodSignature: mirror `this`-param skip in fallback path - tree-sitter queries: capture abstract_method_signature, method_signature, and private_property_identifier for TS; add private_property_identifier for JS - Remove dead childForFieldName('name') fallbacks and typeFromAnnotation fallback - Add 10+ unit tests, 4 integration tests through query pipeline * test(ts): update HAS_METHOD count for interface method_signature capture The new method_signature query now captures ILogger.log() as a Method node with a HAS_METHOD edge, increasing the expected count from 4 to 5. * fix(ts,js): address second review — async generator test, declare module gap - Add async generator method test (async *values() → isAsync: true) - Document declare module/global augmentation as known gap |
||
|
|
c72890d59d
|
feat(csharp): C# MethodExtractor config (#582)
* feat(csharp): add C# MethodExtractor config (#573) Add C# method extraction config mirroring the JVM pattern from PR #576. Wire csharpMethodConfig into the C# language provider and add 18 tests covering classes, interfaces, abstract classes, structs, records, constructors, params/out/ref/optional parameters, sealed methods, attributes, and visibility modifiers. * fix(csharp): add destructor, operator, conversion operator, and in-param support - Add destructor_declaration, operator_declaration, and conversion_operator_declaration to methodNodeTypes - Custom extractName for operators (e.g., "operator +", "implicit operator double") - Fix extractReturnType for operator declarations (use type field, not returns) - Add in modifier to parameter extraction (alongside out/ref) - Add 4 new tests: destructor, operator+, implicit conversion, in parameter * fix(csharp): add ref param test and document compound visibility limitation - Add test for ref parameter modifier (was only testing out) - Document that protected internal / private protected resolve to first modifier * feat(csharp): support compound visibilities (protected internal, private protected) - Add 'protected internal' and 'private protected' to FieldVisibility union - Detect compound modifiers in both C# method and field extractors via collectModifierTexts helper scanning adjacent modifier nodes - Add 2 tests for compound visibility detection * feat(csharp): primary constructors, virtual/override/async, primary fields Address all known limitations from review: - Primary constructor support (C# 12): add extractPrimaryConstructor to MethodExtractionConfig and extractPrimaryFields to FieldExtractionConfig. Record params become public readonly properties; class params become private captured fields. - Add isVirtual, isOverride, isAsync optional fields to MethodInfo, MethodExtractionConfig, NodeProperties, and parse-worker propagation. - Detect virtual/override/async modifiers in C# method config. - Move collectModifierTexts to shared helpers.ts (deduplicate). - Fix destructor name to ~ClassName (disambiguates from constructor). - Add expression-bodied method test. - 118 tests total across method + field extraction suites, all passing. * fix(csharp): review round 2 — annotations, record_struct, grammar pin - Fix primary constructor annotations: use [] instead of extracting class-level attributes (C# has no syntax for ctor-specific attributes) - Add record_struct_declaration to typeDeclarationNodes in both method and field extractors, CLASS_CONTAINER_TYPES, and isRecord visibility check - Pin tree-sitter-c-sharp version (^0.23.1) in params comment * fix(csharp): complete record_struct query + label mapping, sealed override test - Add record_struct_declaration capture patterns to tree-sitter-queries.ts (type definition + primary constructor) - Add record_struct_declaration → 'Struct' in CONTAINER_TYPE_TO_LABEL - Assert isOverride: true alongside isFinal in sealed override test * fix(csharp): record_struct label mismatch, add record struct + documented limitation tests - Fix record_struct_declaration query tag: @definition.struct (not @definition.record) to match CONTAINER_TYPE_TO_LABEL and prevent broken HAS_METHOD edges - Add 3 record struct tests: isTypeDeclaration, method extraction, primary constructor - Add documented limitation tests: partial method (isAbstract: false), generic type parameter stripping (name excludes <T>) * fix(csharp): remove record_struct_declaration — not a real tree-sitter node type tree-sitter-c-sharp 0.23.1 parses 'record struct' as record_declaration (absorbs the 'struct' keyword as an unnamed child token). The non-existent record_struct_declaration in queries caused TSQueryErrorNodeType, breaking ALL C# file processing. Remove from: tree-sitter-queries.ts, typeDeclarationNodes in both extractors, CLASS_CONTAINER_TYPES, and CONTAINER_TYPE_TO_LABEL. Record struct types are already handled via record_declaration. * feat(csharp): add isPartial support, filter targeted attributes, static ctor test - Add isPartial optional field to MethodInfo, MethodExtractionConfig, NodeProperties, and parse-worker propagation pipeline - Detect partial modifier in C# config — marks both declaration-only and implemented partial methods - Filter targeted attribute lists (e.g. [return: MarshalAs(...)]) in extractCSharpAnnotations — only untargeted attributes collected - Add static constructor test (isStatic: true, same name as class) - Add 3 partial method tests: declaration-only, with body, coexisting pair - Document record_struct/record_class as defensive dead code in export-detection.ts (grammar absorbs keywords into record_declaration) * fix(csharp): this param for extension methods, dedup visibility, test fixes - Handle this modifier on extension method parameters (type prefixed as 'this string', consistent with out/ref/in handling) - Deduplicate visibility logic in extractPrimaryConstructor — reuse csharpMethodConfig.extractVisibility instead of inline compound check - Fix record struct test title to reflect actual grammar behavior - Add conversion operator returnType assertion - Add extension method this parameter test * fix(csharp): primary constructor line points to param list, empty name guard - Use paramList.startPosition instead of ownerNode.startPosition for primary constructor line number (avoids methodInfoCache key collision) - Guard against empty param names from tree-sitter error recovery nodes |
||
|
|
acf6fbdd39
|
feat: configure eslint with unused import removal (#564)
* feat: configure eslint with unused import removal Add ESLint v9 (flat config) for code quality: - eslint-plugin-unused-imports for auto-removing dead imports - @typescript-eslint for TypeScript-aware linting - eslint-plugin-react-hooks for React hooks rules - eslint-config-prettier to avoid formatting conflicts - lint-staged runs eslint --fix before prettier on .ts/.tsx - CI lint job added to ci-quality.yml * refactor: remove unused imports via eslint --fix Auto-fixed by eslint-plugin-unused-imports. No logic changes. * chore: add eslint fix commit to .git-blame-ignore-revs |
||
|
|
bf09eab95b
|
feat: configure prettier with pre-commit hook (#563)
* feat: configure prettier with pre-commit hook integration Add prettier, lint-staged, and prettier-plugin-tailwindcss at the repo root with husky pre-commit hook integration. Moves husky from gitnexus/ to root package.json for reliable hook installation. - Root package.json with prepare/format/format:check scripts - .prettierrc with endOfLine:lf and tailwindStylesheet for TW v4 - .prettierignore excluding fixtures, vendor, generated, *.d.ts, *.md - .gitattributes enforcing LF line endings for Windows consistency - Pre-commit hook uses direct node_modules/.bin/ paths (no npx) * style: apply prettier formatting to entire codebase One-time bulk format. No logic changes. Use .git-blame-ignore-revs to skip this commit in git blame. * chore: add .git-blame-ignore-revs for prettier format commit * perf: pre-commit hook runs only tests related to staged files Use vitest --related to scope test execution to tests that import the changed files, instead of running the full suite on every commit. * perf: remove vitest from pre-commit hook, keep in CI only Pre-commit now runs lint-staged + tsc only. Tests run in CI (ci-tests.yml) where they belong — keeps commits fast. * ci: add prettier format check to quality workflow PRs will now fail if code isn't formatted with prettier. |
||
|
|
cde3858e03
|
refactor: Phase 8 & 9 — Field Types and Return-Type Binding (#494)
* feat(phase8): add field type data structures and extractor interface
* feat(phase8): implement TypeScript field extractor
* feat-phase9-add-call-result-binding
* test-phase8-add-field-extraction-unit-tests
* docs: update documentation for Phase 8 and Phase 9
* feat(swift): Phase 8/9 integration tests for field-type and call-result binding
Add Swift field-type resolution and call-result binding integration tests
with fixtures, plus merge-conflict fixes for the FieldExtractor code.
**Swift integration tests:**
- `swift-field-types/` fixture (Models.swift + App.swift) — tests
HAS_PROPERTY edges, field-chain CALLS resolution (user.address.save()
→ Address#save), and ACCESSES edges for field reads.
- `swift-call-result-binding/` fixture — tests call-result binding
(let user = getUser(); user.save() → User#save).
- 2 new describe blocks in swift.test.ts with skipIf(!swiftAvailable).
**Swift arity fix:**
- extractMethodSignature fallback counts direct `parameter` children
when no wrapper list node exists (Swift's tree-sitter grammar places
parameters as direct children of function_declaration). Without this,
all Swift functions had parameterCount: 0 and the arity filter rejected
valid call targets.
**FieldExtractor merge-conflict fixes:**
- field-extractor.ts: update import from removed ./utils.js to
./utils/ast-helpers.js; use typeEnv.fileScope() instead of .get('').
- field-extractors/typescript.ts: same import fix.
- field-types.ts: alias TypeEnvironment as TypeEnv (renamed on main).
- field-extraction.test.ts: mock TypeEnvironment interface properly.
* feat(field-extractors): generic table-driven field extractors for all 14 languages, wired into pipeline
Implements field extractors for all supported languages and integrates
them into the ingestion pipeline as the single source of truth for
Property node metadata.
**Generic field extractor factory** — `field-extractors/generic.ts`
defines a `createFieldExtractor(config)` factory that generates
FieldExtractor instances from a per-language `FieldExtractionConfig`.
Each config specifies AST node types, name/type/visibility extraction
functions, and static/readonly detection — typically 20-40 lines per
language vs 300+ for a hand-written extractor.
**Per-language configs** — `field-extractors/configs/` has 11 config
files covering 13 languages (TS/JS share, Java/Kotlin share).
TypeScript keeps its hand-written extractor for richer handling.
**LanguageProvider integration** — New optional `fieldExtractor` property
on LanguageProviderConfig, set via defineLanguage() in each language
file. Follows the same strategy pattern as typeConfig, exportChecker,
and labelOverride. Removed the separate FieldExtractorRegistry class
and field-extractors/index.ts — extractors are accessed via
getProvider(lang).fieldExtractor.
**Pipeline wiring** — Both parse-worker.ts (worker pool) and
parsing-processor.ts (sequential fallback) now call the FieldExtractor
during Property node creation. Results are cached per class node.
Property nodes are enriched with: declaredType, visibility, isStatic,
isReadonly.
**extractPropertyDeclaredType removed** — The 100-line multi-strategy
function in type-extractors/shared.ts is replaced by the FieldExtractor.
All 14 languages register an extractor, eliminating the need for a
generic fallback. The Python config's extractType was fixed to handle
annotation-without-value patterns (address: Address).
**Integration tests** — Each language's resolver test file gains
pipeline-based assertions verifying visibility/isStatic/isReadonly on
Property nodes via getNodesByLabelFull. Tests run through
runPipelineFromRepo with real fixtures — no direct extractor calls.
* fix(type-env): thread enclosingFunctionFinder through scope resolution, unskip Dart ACCESSES test
The type-env's findEnclosingScopeKey had the same Dart sibling problem
as findEnclosingFunction — it walked parents but never found
function_signature because the call lives inside function_body (a
sibling). Instead of hardcoding a function_body check, thread the
provider's enclosingFunctionFinder hook through BuildTypeEnvOptions →
lookupInEnv → findEnclosingScopeKey. All three buildTypeEnv call sites
(call-processor, parsing-processor, parse-worker) now pass the hook.
This enables the type-env to resolve scoped parameter bindings for Dart
(e.g., `user: User` in processUser), which lets the chain-resolution
tier (Step 1c) walk `user.address` and emit ACCESSES edges.
Dart integration test unskipped — 10/10 passing including ACCESSES.
Reverted CHANGELOG.md to origin/main.
* fix: resolve all PR #494 review findings (10 items)
CRITICAL:
- parse-worker.ts: classNode: any → SyntaxNode on getFieldInfo
and findEnclosingClassNode; removed redundant as number casts
- parsing-processor.ts: classNode: any → SyntaxNode on seqGetFieldInfo
HIGH:
- ruby.ts: attr_accessor now extracts ALL symbol arguments via
extractNames hook in generic factory (was firstNamedChild only)
- typescript.ts: added JSDoc explaining why hand-written extractor
coexists with config-based typescript-javascript.ts
MEDIUM:
- field-types.ts: FieldVisibility union type replaces string
('public'|'private'|'protected'|'internal'|'package'|'fileprivate'|'open')
Propagated through field-extractor.ts, generic.ts, all 7 config files
- typescript.ts: extractFullType collapsed from 12 branches to 3 lines
- generic.ts: added extractNames? optional hook + buildField refactor
LOW:
- ruby.ts: extractVisibility(node) → extractVisibility(_node)
- python.ts: fixed misleading isStatic comment
TypeScript compiles cleanly.
* test: add 24 field extraction tests for generic factory + 5 languages
Generic factory (4 tests):
- createFieldExtractor with TypeScript config validates factory itself
- Body discovery for interfaces, static/readonly modifiers
- Non-type node rejection
Python (4 tests):
- Annotated class field extraction
- Underscore-based visibility: _name=protected, __name=private
Go (5 tests):
- isTypeDeclaration on type_declaration nodes
- Config functions: uppercase=public, lowercase=package visibility
- extractType, isStatic, isReadonly
C++ (5 tests):
- public/private/protected access specifier backward-sibling walk
- Default visibility: class=private, struct=public
- static/const modifier detection
Ruby (6 tests):
- attr_accessor multi-symbol: :name, :email, :age → 3 fields
- attr_reader=readonly, attr_writer=non-readonly
- Multiple attr_* calls in one class
Total: 46 tests passing
* chore: remove plan doc from PR
---------
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
|