Skip to content

feat(spring): build bean candidate inventory#2494

Open
PieceOfFall wants to merge 19 commits into
abhigyanpatwari:mainfrom
PieceOfFall:main
Open

feat(spring): build bean candidate inventory#2494
PieceOfFall wants to merge 19 commits into
abhigyanpatwari:mainfrom
PieceOfFall:main

Conversation

@PieceOfFall

@PieceOfFall PieceOfFall commented Jul 15, 2026

Copy link
Copy Markdown

Summary

Adds a Java-only Spring bean candidate inventory for the six common stereotype annotations. The inventory is persisted as canonical framework annotation evidence on existing Class nodes and exposed as derived Bean metadata from context and impact.

Motivation / context

Closes #2411.

Later Spring dependency-resolution work needs a reliable inventory of candidate classes without introducing framework-specific graph nodes or relationships. This change keeps Spring recognition in Java scope resolution while giving storage and MCP consumers a small, framework-neutral evidence field.

Areas touched

  • gitnexus/ (CLI / core / MCP server)
  • gitnexus-web/ (Vite / React UI)
  • .github/ (workflows, actions)
  • eval/ or other tooling
  • Docs / agent config only (AGENTS.md, CLAUDE.md, .cursor/, llms.txt, etc.)

Scope & constraints

In scope

  • Recognize Component, Service, Repository, Controller, RestController, and Configuration on Java class declarations.
  • Resolve fully-qualified annotations, unique explicit imports, and a single unambiguous Spring wildcard import after Java scope/import resolution.
  • Fail closed for lexical or same-package shadowing, multiple wildcard imports, composed annotations, conflicting stereotypes, and unresolved ambiguity.
  • Persist canonical evidence in Class.frameworkAnnotations and derive a stable { framework, role, annotation } Bean shape for MCP output.
  • Preserve existing Spring route discovery and HANDLES_ROUTE behavior.

Explicitly out of scope / not done here

  • Kotlin support, @Bean methods, Bean names, qualifiers, @Primary, conditions, profiles, or composed annotations.
  • Full external classpath resolution. When multiple on-demand imports could expose the same simple annotation name, inventory generation remains conservative.
  • New Bean nodes, relationships, pipeline phases, or impact traversal behavior.
  • Runtime guarantees that a candidate is actually instantiated by Spring.

Implementation notes

  • The existing Java scope query captures class annotation syntax during its normal AST traversal; no second parse or tree traversal is added.
  • Annotation facts are serialized through the Java worker capture side channel.
  • Spring stereotype resolution runs from the Java scope resolver's post-resolution hook, after imports, lexical declarations, and same-package symbols are available.
  • Class extraction remains framework-neutral; Bean evidence is attached directly to matching Class graph nodes only after resolution.
  • LadybugDB stores only canonical framework annotation evidence. The Spring FQN-to-role mapping remains in a data-only Java module shared by resolution and MCP enrichment.
  • Parse-cache schema version 13 and incremental schema version 7 force older indexes to rebuild instead of producing a partial inventory.
  • Older or partially upgraded indexes degrade gracefully by omitting Bean enrichment.

Testing & verification

  • cd gitnexus && npm test
  • cd gitnexus && npm run test:integration
  • cd gitnexus && npx tsc --noEmit
  • cd gitnexus-web && npm test (not applicable)
  • cd gitnexus-web && npx tsc -b --noEmit (not applicable)
  • Manual / Playwright E2E (not applicable)
  • cd gitnexus && npm run test:unit
  • cd gitnexus && node --import tsx bench/emit-persistence/measure.mjs --check

Targeted tests cover all six roles, explicit imports, FQNs, annotation arguments, nested classes, a single valid wildcard import, multiple-wildcard fail-closed behavior, lexical and cross-file same-package shadowing, route regression, persistence write paths, schema-version gates, and MCP context / impact output.

Risk & rollout

The MCP change is additive and does not alter impact traversal or risk calculation. Existing indexes require a full re-analysis because the Class table gains a nullable list column; schema-version gates already enforce that rebuild.

Checklist

  • PR body meets repo minimum length (workflow may label short descriptions)
  • If AGENTS.md / overlays changed: not applicable; none are included in this PR
  • No secrets, tokens, or machine-specific paths committed

@vercel

vercel Bot commented Jul 15, 2026

Copy link
Copy Markdown

@PieceOfFall is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@PieceOfFall
PieceOfFall marked this pull request as ready for review July 15, 2026 06:20
Comment thread gitnexus/src/core/ingestion/languages/java/spring-bean-metadata.ts Outdated
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

✨ PR Autofix

Found fixable formatting / unused-import issues across 13 changed lines. Comment /autofix on this PR to apply them, or run npm run lint:fix && npm run format locally.

{"schema":"gitnexus.pr-autofix/v2","state":"fixes-available","pr_number":2494,"changed_lines":13,"head_sha":"fdede426d13deac622b5c61d4431152cf6b5832e","run_id":"29653557366","apply_command":"/autofix"}

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

CI Report

Some checks failed

Pipeline Status

Stage Status Details
❌ Typecheck failure tsc --noEmit
✅ Tests success unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
14874 14812 0 62 20s

✅ All 14812 tests passed

62 test(s) skipped — expand for details

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 81.25% 56652/69720 81.02% 📈 +0.2 🟢 ████████████████░░░░
Branches 68.75% 35150/51127 68.15% 📈 +0.6 🟢 █████████████░░░░░░░
Functions 87.5% 6650/7600 87.18% 📈 +0.3 🟢 █████████████████░░░
Lines 84.74% 50495/59586 84.54% 📈 +0.2 🟢 ████████████████░░░░

📋 View full run · Generated by CI

@PieceOfFall

Copy link
Copy Markdown
Author

@magyargergo I’ve pushed the fix for the lint failure reported by CI.

The requested implementation changes have already been addressed, and the PR is currently only blocked by the required checks. Could you please take another look or re-run/approve the workflows when convenient?

The CI fix is in commit 1df1ed3.

@PieceOfFall
PieceOfFall requested a review from magyargergo July 16, 2026 07:19

@magyargergo magyargergo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Spring bean candidate inventory

Reviewed at head d1b2d82d against merge-base c2dc9c11, in a dedicated worktree with a fresh --pdg GitNexus index. Evidence: full diff read; detect_changes (maps exactly the diff's 48 symbols); upstream impact on the persistence/MCP hubs; taint pass (clean — the new list writers are escaped or fed from the closed stereotype set, and the MCP lookup is parameterized); tsc --noEmit clean; all 6 PR test files pass locally; CI green. Four domain/cross-cutting expert passes followed, and the headline finding below was reproduced empirically with this PR's own built CLI.

The feature is well-architected: it reuses the established collectCaptureSideChannel/applyCaptureSideChannel/emitPostResolutionEdges seams exactly as C/C++/Kotlin do, keeps all Spring knowledge in languages/java/, keeps the three Class write paths (CREATE TABLE / COPY / MERGE) consistent, and the v7 INCREMENTAL_SCHEMA_VERSION bump correctly forces a full re-analyze on upgrade. The fixture matrix for the fail-closed cases is strong. The verdict is driven by two correctness gaps in the fail-closed guarantee itself:

Empirical repro for the HIGH finding (incremental staleness)

Step WildcardService.frameworkAnnotations
Full analyze (import org.springframework.stereotype.*; + @Service) ["org.springframework.stereotype.Service"]
Add same-package public @interface Service {}incremental analyze ("1 unchanged file rows preserved") ["org.springframework.stereotype.Service"] ✗ stale false positive
Same tree, analyze --force full rebuild []

Incremental and full rebuild diverge, and MCP context/impact serve a false-positive bean indefinitely — breaking #2411's criterion 4 (fail closed, no misleading results) and the incremental ≡ full-rebuild invariant. Details and remediation options inline.

Findings (inline)

  • HIGH — incremental writeback persists stale bean attributions (spring-bean-metadata.ts)
  • MEDIUM — wildcard gate fails open on three legal Java shadowing channels (spring-bean-metadata.ts)
  • MEDIUM — new side-channel map lacks the per-pass clear its C/Kotlin precedents have (capture-side-channel.ts)
  • MEDIUM — mid-test return silently passes the batch-insert assertions on Windows (spring-bean-metadata-roundtrip.test.ts)
  • LOW — bean lookup runs as dead work on the deliberately-slimmed impact paths (local-backend.ts)
  • LOW — CSV list items with commas silently split, diverging from the Cypher path (csv-generator.ts)

Coverage and residual risk

Tested well: all six stereotypes incl. RestController's distinct package, eight negative shadow fixtures, real-DB roundtrip incl. two-item lists, MCP shapes for context+impact, version-gate assertions, and a route-discovery regression assertion in the same fixture. Missing: an incremental-analyze test that mutates annotation-relevant sibling files (HIGH finding), inherited-member / static-import / capped-package shadow cases (MEDIUM finding), and Windows coverage of the batch write path. Records/interfaces/enums are deliberately out of scope and pinned by a unit test — noted as a product boundary, not a defect (@Component on records is legal Spring 6, worth a follow-up).

Evidence is valid for head d1b2d82d; findings were verified in source (and the HIGH one end-to-end) rather than inferred from graph output alone.

Comment thread gitnexus/src/core/ingestion/languages/java/spring-bean-metadata.ts Outdated
Comment thread gitnexus/src/core/ingestion/languages/java/spring-bean-metadata.ts Outdated
Comment thread gitnexus/src/core/ingestion/languages/java/capture-side-channel.ts Outdated
Comment thread gitnexus/test/integration/spring-bean-metadata-roundtrip.test.ts Outdated
Comment thread gitnexus/src/mcp/local/local-backend.ts Outdated
Comment thread gitnexus/src/core/lbug/csv-generator.ts
@magyargergo

Copy link
Copy Markdown
Collaborator

@PieceOfFall Can you please resolve the conflicts? 🙏

@magyargergo

magyargergo commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

@PieceOfFall could we please include kotlin in this implementation? 🙏 We need to share common functionalities with java.

@PieceOfFall

Copy link
Copy Markdown
Author

OK. I’ll add Kotlin support and share the common parts with Java.

magyargergo added a commit to magyargergo/GitNexus that referenced this pull request Jul 17, 2026
…eview/work/plan

Eight incident-backed hardenings from running the full skill cycle
(review -> plan -> work, 28-finding fix series) on PR abhigyanpatwari#2522:

gitnexus-review:
- Expert lenses execute the code under review on candidate failing shapes
  (empirical probe outranks source reading — every HIGH the language
  lenses found came from a probe, not a read).
- Step 7 re-runs the exact CI check for refreshed baselines/fingerprints
  (a stale committed artifact is invisible in the diff; caught a red
  benchmarks arm).
- Step 8 treats version/invalidation constants as review surface
  (INCREMENTAL_SCHEMA_VERSION class recurred verbatim from abhigyanpatwari#2494).

gitnexus-work:
- Step 4 proves regression tests discriminate against the pre-fix tree.
- Step 5 rebuilds executed build output before every verification run
  (parse workers load dist/; a correct fix 'failed' until rebuilt).
- Step 6 makes stage -> detect_changes -> commit one unbroken sequence.

gitnexus-plan:
- Phase 0 seeded-evidence mode: plan FROM a completed review's verified
  findings instead of re-running the graph ladder.
- Template §7: fingerprint/golden-guarded output rebaselines once, at the
  series tip.

All distribution copies resynced; shipped-skills-sync + skills-steering
24/24 locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@azizur100389

Copy link
Copy Markdown
Collaborator

Thanks for the follow-up fixes. I rechecked the current head against current main.

The earlier technical review findings look addressed in the Java path: the bean attribution now runs after scope resolution, stale incremental frameworkAnnotations are handled by comparing persisted vs fresh values, the side-channel state is cleared per pass, CSV list encoding now fails explicitly on unsafe items, and the skip-epistemic / summary-only lookup path avoids extra bean queries.

I would still keep this as changes-requested for the remaining maintainer scope: the implementation is still Java-only. The current diff adds the Spring bean metadata extraction under languages/java/*, Java fixtures, and Java pipeline/MCP tests, but there is no Kotlin equivalent and no shared Java/Kotlin abstraction yet. Since Gergo asked to include Kotlin and share common functionality with Java, this is not ready until the Kotlin provider participates in the same Spring bean inventory behavior or the PR is explicitly narrowed.

One useful next step would be to extract the stereotype/metadata derivation and post-resolution attachment shape into a JVM-shared helper, then wire both Java and Kotlin provider hooks with Kotlin fixtures covering @Service / @Component / @RestController and shadowing behavior.

@PieceOfFall

Copy link
Copy Markdown
Author

I’m happy to add the Kotlin support and shared JVM logic. Since main is moving quickly, I’d prefer to finish the implementation first and sync with the latest main once before final review, rather than repeatedly resolving conflicts along the way. Hope that works.

@PieceOfFall

Copy link
Copy Markdown
Author

Would you be okay with bumping INCREMENTAL_SCHEMA_VERSION from 7 to 8?

This PR does not change the physical database schema, but it changes the index completeness guarantee: v7 contains Java Spring Bean evidence, while this PR adds Kotlin evidence.

A same-commit v7 index can hit alreadyUpToDate before parse-cache v14 invalidation runs. Kotlin frameworkAnnotations would then remain missing until a forced rebuild or relevant source change.v8 would trigger a one-time full re-analysis and mark the index complete for both Java and Kotlin.

Alternatively, I can add a dedicated Spring Bean feature epoch, but that introduces additional metadata and migration logic. I recommend the v8 bump. Would that approach work for you?

@PieceOfFall

Copy link
Copy Markdown
Author

One additional thought before you decide:
I also remembered that #2411 is the first of the eight planned Spring follow-up issues under #1225. Several later tasks will likely add more persisted Spring semantics, including injection, @bean methods, configuration binding, conditions, AOP, and non-HTTP handlers.
This does not change the correctness issue described above: some durable version gate must run before alreadyUpToDate. The v8 bump remains the smallest fix for this PR.
However, repeatedly using the global schema version for the later Spring phases could force unrelated repositories to re-index. If you prefer to reserve INCREMENTAL_SCHEMA_VERSION for global index invariants, I can instead introduce a dedicated Spring analysis feature stamp, starting with spring.beanInventory: 1.
I am happy to follow either approach. I just wanted to include this longer-term implication before finalizing the implementation.

@azizur100389 azizur100389 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for current-head readiness.

gitnexus/test/unit/spring-bean-schema.test.ts still expects PARSE_CACHE_VERSION to match ^13+, but current head has SCHEMA_BUMP = 17 in gitnexus/src/storage/parse-cache.ts, so the unit suite should fail after the latest merge-from-main.

The feature is also still Java-only. Since the current maintainer scope asks for Kotlin/shared JVM support, this is not ready unless the PR is explicitly narrowed to Java-only. If Kotlin support lands here, please also settle the durable version gate, either with INCREMENTAL_SCHEMA_VERSION = 8 or a dedicated Spring feature stamp checked before the up-to-date fast path.

Please rerun the required GitHub Actions checks after the fix; current head only shows the Vercel authorization failure.

@magyargergo

Copy link
Copy Markdown
Collaborator

@PieceOfFall Thanks, the latest fixes address all six findings and @azizur100389 confirmed the Java path independently, so the technical review is settled. Answers to your questions:

  1. Version gate: go with the bump to 8. It follows what this PR already did for the Java evidence and it is the smallest fix that closes the gap for Kotlin. Let's hold off on the dedicated Spring feature stamp until a later phase of Improve analysis for Java projects built with Spring and Spring Boot #1225 actually needs it, no point building that machinery now.

  2. Merge strategy: agreed, finish the Kotlin and shared JVM work first and sync with main once at the end. No need to chase conflicts along the way.

  3. One thing for when you sync: please change the pinned versions in the schema test to minimums instead of exact values. The exact asserts are what broke after the last merge from main and they will keep breaking on unrelated bumps.

Once the Kotlin support is pushed I will approve the workflow runs and do the final review pass.

@PieceOfFall

Copy link
Copy Markdown
Author

Thanks, that is clear. I’ll proceed with v8, defer the dedicated Spring feature stamp, finish the Kotlin/shared JVM work, and update the schema assertions to minimum-version checks when syncing with main.

PieceOfFall and others added 5 commits July 18, 2026 22:21
# Conflicts:
#	gitnexus/src/core/ingestion/languages/java/captures.ts
#	gitnexus/src/core/ingestion/languages/kotlin/captures.ts
#	gitnexus/src/storage/parse-cache.ts
#	gitnexus/src/storage/repo-manager.ts
#	gitnexus/test/unit/call-summary-schema-version.test.ts
@PieceOfFall

PieceOfFall commented Jul 18, 2026

Copy link
Copy Markdown
Author

Thanks for the guidance. I followed the suggested direction and extracted the Spring Bean logic into shared framework modules.

Shared Spring Bean Resolution

The stereotype catalog, metadata derivation, annotation resolution, and post-resolution attachment are now shared by Java and Kotlin through thin language adapters.

Resolution supports:

  • Fully qualified annotation names
  • Explicit and aliased imports
  • A single wildcard import
  • Conservative, fail-closed behavior for lexical, inherited, same-package, multiple-wildcard, and incomplete-visibility conflicts

Kotlin Support

Kotlin now participates in the same Bean inventory pipeline without requiring a second AST traversal. Annotation and package facts are captured during the existing scope traversal, persisted through the Kotlin ParsedFile side-channel, and restored from cache.

The fixtures cover:

  • @Service, @Component, and @RestController
  • Fully qualified annotations and import aliases
  • Nested classes
  • Abstract, data, sealed, and value classes
  • Lexical and same-package shadowing

Interfaces, objects, enums, annotation classes, and composed annotations remain excluded.

Java and Kotlin now produce the same canonical frameworkAnnotations values and MCP Bean output.

Shared JVM Package Visibility

I also extracted Java’s package-sibling handling into a shared JVM helper used by both languages.

It distinguishes the default package from package parsing failures and marks visibility as incomplete when source content is missing, parsing fails, or the package-size limit is reached. Wildcard attribution therefore remains conservative.

Incremental annotation drift detection now covers .java, .kt, and .kts, including same-package shadow additions and removals.

Cache and Index Compatibility

After syncing with the current main, incremental schema v8 was already assigned to [#2550](#2550). Rather than immediately advancing to v9, I kept v8 and added independently versioned analysis capability stamps:

  • graph.class-framework-annotations@1
  • spring.bean-inventory@1, applied only to JVM repositories

Missing, malformed, mismatched, or unknown capability stamps trigger a one-time full rebuild before the fast path can be used.

The parse-cache schema is now v20. Upstream v19 is reserved for the Java enum-constant and anonymous-class identity changes, while v20 invalidates caches created before the new Java/Kotlin Spring side-channel data was available.

Warm-Cache Behavior

Kotlin also opts into a generic cached-source policy. Warm durable-ParsedFile runs now load source content only for cache misses instead of loading every Kotlin source file.

Other resolvers retain their existing behavior, and the shared ingestion pipeline contains no language-specific checks.

Validation

The full test suite passes:

  • 699 test files passed
  • 14,466 tests passed
  • 49 tests skipped

Existing Java Bean behavior, MCP context and impact queries, route and HANDLES_ROUTE behavior, incremental analysis, parse caching, and storage regressions remain covered.

# Conflicts:
#	gitnexus/src/storage/parse-cache.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(spring): build bean candidate inventory

3 participants