Skip to content

chore(shared): apply Ring 2 SHARED review follow-ups in one diff#964

Merged
magyargergo merged 2 commits into
mainfrom
rfc/scope-resolution/ring2-shared-review-followups
Apr 18, 2026
Merged

chore(shared): apply Ring 2 SHARED review follow-ups in one diff#964
magyargergo merged 2 commits into
mainfrom
rfc/scope-resolution/ring2-shared-review-followups

Conversation

@magyargergo

Copy link
Copy Markdown
Collaborator

Aggregates actionable follow-ups from the 9 Ring 2 SHARED PRs (#949#963) before proceeding to Ring 2 PKG. No behavior changes; docstring edits, test refinements, and one structural cleanup (ScopeLookup migration to types.ts).

What's in this diff

#913 — DefIndex / ModuleScopeIndex / QualifiedNameIndex

Change Why
Rename freezeIndexwrapIndex in all three builders Old name implied Object.freeze on the wrapper, which we never applied. wrapIndex describes the lightweight readonly-interface wrap accurately.
Document that buildModuleScopeIndex callers must pre-normalize filePath Prevents cross-platform (C:\ vs C:/) or trailing-slash misses.
Add hit-path freeze assertion in qualified-name-index test Existing test covered only the miss-path EMPTY array.

#914 — MethodDispatchIndex

Change Why
Differentiate C3 vs BFS test cases They used identical MRO orderings; now show that the materializer preserves whatever ordering the per-language callback produces.
Add implementsOfCalls counter + document call-count contract implementsOf fires per occurrence in input.owners, not per unique owner. computeMro is memoized. Callers with expensive implementsOf need this.

#916 — resolveTypeRef

Change Why
Document 'Type' NodeLabel exclusion from TYPE_KINDS Verified no extractor in gitnexus/src/core/ingestion/ emits it for annotation-relevant symbols.
Rename namespace-origin test 'resolves ...''returns null for a namespace-origin binding whose def is not a type kind' matches intent.

#918 — shadow diff + aggregate

Change Why
Remove partial export type { ShadowAgreement, ShadowDiff } from aggregate.ts Omitted ShadowCallsite, diverged from the top-level barrel.
Fix invalid 'wildcard' evidence kind in diff.test.ts fixture Not a valid ResolutionEvidence.kind; test uses 'global-name' now (a real kind the test treats identically).

#912 — ScopeTree / PositionIndex

Change Why
Document touching-boundary semantics on atPosition Siblings sharing a boundary point: right (later-start) sibling wins. Was an undocumented invariant.
Move ScopeLookup interface from resolve-type-ref.ts to types.ts Layer inversion flagged by review — scope-tree.ts shouldn't import from a §4 module. Home is the data-model layer (types.ts). No convenience re-export — per feedback_no_reexport preference, the one call site (scope-tree.ts) updated directly, and the top-level barrel moves alongside.

#917 — Class / Method / Field registries

Change Why
Replace dangling comment in lookupReceiverType Prose now accurately says callers must pre-resolve via resolveTypeRef on ambiguous qnames; no phantom fallback.
Fix tieBreakKey.origin default for pure Step-2 candidates Type-binding-only hits previously inherited 'local' from the neutral default. Now demoted to 'import' on first type-binding hit; a later Step-1 lexical hit can still upgrade to 'local'. Keeps the Appendix B cascade faithful to the true origin.
Document 'global-name' origin as Ring 3 reserved Currently never emitted; weight stays live so composeEvidence remains exhaustive over the origin union.
Rename Step-7 test + add real multi-candidate test The old 'confidence DESC is the primary key' test actually exercised hard-shadow baseline. Renamed + added a genuine multi-candidate local-vs-wildcard ordering test.

Verification

  • tsc --noEmit clean (both packages)
  • gitnexus-shared build clean
  • Combined scope-resolution / model / shadow suite: 260/260 pass (+1 from the new multi-candidate ordering test)

Not addressed (non-actionable, documented in commit)

Closes the Ring 2 SHARED review loop

With this diff merged, all actionable inline review feedback across the 9 Ring 2 SHARED PRs is addressed. Ready to proceed to Ring 2 PKG (#919#925).

Aggregates all actionable follow-ups from the 9 Ring 2 SHARED PRs
(#949#963) before proceeding to Ring 2 PKG. No behavior changes;
docstring edits, test refinements, and one structural cleanup.

## #913 (DefIndex / ModuleScopeIndex / QualifiedNameIndex)
  - Rename `freezeIndex` → `wrapIndex` across all three index builders.
    The old name implied `Object.freeze` on the wrapper, which we never
    applied; `wrapIndex` more accurately describes the lightweight
    readonly-interface wrap. Safety surface (frozen bucket arrays,
    frozen miss-empty array, readonly Maps) is unchanged.
  - Document in `buildModuleScopeIndex` JSDoc that callers must
    pre-normalize `filePath` keys (no path-separator canonicalization
    happens here). Prevents silent cross-platform misses.
  - Add an explicit hit-path freeze assertion in
    `qualified-name-index.test.ts` (the existing test covered only the
    miss-path `EMPTY` array).

## #914 (MethodDispatchIndex)
  - Differentiate the C3 and BFS test cases: both tests now use
    distinct MRO orderings so they prove the materializer stores
    whatever order the `computeMro` callback produces (not that C3 and
    BFS yield identical output).
  - Add `implementsOfCalls` counter in the first-write-wins test, and
    document the call-count contract in `MethodDispatchInput.implementsOf`
    JSDoc: `implementsOf` fires **per occurrence** in `input.owners`
    (not per unique owner); `computeMro` fires at most once per unique
    owner. Callers with expensive `implementsOf` implementations should
    pre-dedupe `owners`.

## #916 (resolveTypeRef)
  - Document the deliberate exclusion of `'Type'` from `TYPE_KINDS`
    (verified no extractor in `gitnexus/src/core/ingestion/` emits
    `type: 'Type'` for annotation-relevant symbols).
  - Rename the namespace-origin test from `'resolves ...'` to
    `'returns null for a namespace-origin binding whose def is not a
    type kind'`, matching the failure-case intent.

## #918 (shadow diff + aggregate)
  - Remove the partial re-export `export type { ShadowAgreement, ShadowDiff };`
    from `aggregate.ts` — it omitted `ShadowCallsite` and diverged
    from the top-level barrel. Consumers import all three from the
    `gitnexus-shared` entry point.
  - Fix the invalid `'wildcard'` evidence kind in `diff.test.ts` fixture
    (that kind is not a valid `ResolutionEvidence.kind`). Replaced with
    `'global-name'`, a real kind the test treats identically.

## #912 (ScopeTree / PositionIndex / makeScopeId)
  - Document the touching-boundary semantics on `PositionIndex.atPosition`:
    when siblings share a boundary point, the right (later-start) sibling
    wins per the existing innermost-wins sort contract.
  - Resolve the layer-inversion flagged by review: move `ScopeLookup`
    from `resolve-type-ref.ts` to `types.ts` (its natural home in the
    data-model layer). `scope-tree.ts` now imports `ScopeLookup` from
    `types.js` directly; the old re-export from `resolve-type-ref.ts`
    is removed per repo convention (`feedback_no_reexport`). Barrel
    export moved alongside.

## #917 (ClassRegistry / MethodRegistry / FieldRegistry)
  - Replace the dangling "try a name-match among class-like defs"
    comment in `lookupReceiverType` with explicit prose that callers
    must pre-resolve via `resolveTypeRef` if they want richer semantics.
    No behavior change — the function already returned `undefined` on
    ambiguous/missing qnames.
  - Fix `tieBreakKey.origin` default for pure Step-2 candidates.
    Type-binding-only hits no longer falsely inherit `'local'` from
    `ensureCandidate`'s neutral default; they now demote to `'import'`
    on their first type-binding hit, and only a later Step-1 lexical
    hit can upgrade them back to `'local'`. Keeps the Appendix B
    cascade faithful to the true origin.
  - Document `'global-name'` in `evidence.ts`: currently reserved for
    Ring 3's byName global index; `lookupCore` never emits it today.
    The weight stays live so `composeEvidence` remains exhaustive over
    the origin union.
  - Rename the mislabeled Step-7 test from `'confidence DESC is the
    primary key'` (which actually tested hard-shadow baseline) to
    `'inner scope shadows outer, yielding single result'`, and add a
    separate test that actually exercises multi-candidate confidence
    ordering (local vs wildcard at the same scope).

## Verification
  - `tsc --noEmit` clean (both `gitnexus-shared` and `gitnexus`)
  - `gitnexus-shared` build clean
  - Combined scope-resolution / model / shadow suite: **260/260 pass**
    (+1 from the new multi-candidate ordering test in #917)

## Not addressed (non-actionable)
  - #949 CI "failure with zero failing tests": pre-existing Swift Node 22
    grammar flake unrelated to #910 scope.
  - #950: the two non-blocking findings were already addressed in
    follow-up commit `cbac32ba` (ParsedImport discriminated union +
    `ScopeId | null` on the two hooks).
  - #915: the five in-scope findings were already addressed in
    follow-up commit `54515a7e` (dead code, unused params, multi-hop
    docs, cap-hit test, stats granularity).
  - #915 LanguageProvider.resolveImportTarget signature divergence +
    `findDefById` O(F×D) perf: tracked separately as follow-up issues
    for the Ring 3 migration window.
@vercel

vercel Bot commented Apr 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gitnexus Ready Ready Preview, Comment Apr 18, 2026 5:28pm

Request Review

@github-actions

github-actions Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

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

Test Results

Tests Passed Failed Skipped Duration
6797 6700 0 97 263s

✅ All 6700 tests passed

97 test(s) skipped — expand for details
  • Swift MethodExtractor > isTypeDeclaration > recognizes class_declaration
  • Swift MethodExtractor > isTypeDeclaration > recognizes protocol_declaration
  • Swift MethodExtractor > isTypeDeclaration > rejects import_declaration
  • Swift MethodExtractor > visibility > extracts public method
  • Swift MethodExtractor > visibility > extracts private method
  • Swift MethodExtractor > visibility > defaults to internal when no modifier
  • Swift MethodExtractor > protocol methods > marks protocol method as abstract
  • Swift MethodExtractor > static and class methods > detects static func as isStatic
  • Swift MethodExtractor > static and class methods > detects class func as isStatic
  • Swift MethodExtractor > parameters > extracts parameters with types and default values
  • Swift MethodExtractor > return type > extracts return type from -> annotation
  • Swift MethodExtractor > annotations > extracts @objc attribute
  • Swift MethodExtractor > isFinal > detects final func
  • Swift MethodExtractor > isFinal > is false when not final
  • Swift MethodExtractor > isAsync > detects async func
  • Swift MethodExtractor > isOverride > detects override method
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift explicit init inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference does not bind plain functions
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature
  • Swift constructor-inferred type resolution > detects User and Repo classes, both with save methods
  • Swift constructor-inferred type resolution > resolves user.save() to Models/User.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > resolves repo.save() to Models/Repo.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > emits exactly 2 save() CALLS edges (one per receiver type)
  • Swift self resolution > detects User and Repo classes, each with a save function
  • Swift self resolution > resolves self.save() inside User.process to User.save, not Repo.save
  • Swift parent resolution > detects BaseModel and User classes plus Serializable protocol
  • Swift parent resolution > emits EXTENDS edge: User → BaseModel
  • Swift parent resolution > emits IMPLEMENTS edge: User → Serializable (protocol conformance)
  • Swift cross-file User.init() inference > resolves user.save() via User.init(name:) inference
  • Swift cross-file User.init() inference > resolves user.greet() via User.init(name:) inference
  • Swift return type inference > detects User class and getUser function
  • Swift return type inference > detects save function on User (Swift class methods are Function nodes)
  • Swift return type inference > resolves user.save() to User#save via return type of getUser() -> User
  • Swift return-type inference via function return type > resolves user.save() to User#save via return type of getUser()
  • Swift return-type inference via function return type > user.save() does NOT resolve to Repo#save
  • Swift return-type inference via function return type > resolves repo.save() to Repo#save via return type of getRepo()
  • Swift implicit imports (cross-file visibility) > detects UserService class in Models.swift
  • Swift implicit imports (cross-file visibility) > resolves UserService() constructor call across files (no explicit import)
  • Swift implicit imports (cross-file visibility) > resolves service.fetchUser() member call across files
  • Swift implicit imports (cross-file visibility) > creates IMPORTS edges between files in the same module
  • Swift extension deduplication > detects Product class
  • Swift extension deduplication > resolves Product() constructor despite extension creating duplicate class node
  • Swift extension deduplication > resolves product.save() to Product.swift (primary definition)
  • Swift constructor call fallback (no new keyword) > resolves OCRService() as constructor call across files
  • Swift constructor call fallback (no new keyword) > resolves ocr.recognize() member call via constructor-inferred type
  • Swift export visibility (internal vs private) > resolves PublicService() constructor across files
  • Swift export visibility (internal vs private) > resolves internalHelper() across files (internal = module-scoped)
  • Swift if let / guard let binding resolution > detects User and Repo classes
  • Swift if let / guard let binding resolution > resolves user.save() inside if-let to User#save
  • Swift if let / guard let binding resolution > resolves repo.save() inside guard-let to Repo#save
  • Swift if let / guard let binding resolution > user.save() in if-let does NOT resolve to Repo#save
  • Swift await / try expression unwrapping > resolves user.save() via await fetchUser() return type
  • Swift await / try expression unwrapping > resolves repo.save() via try parseRepo() return type
  • Swift await / try expression unwrapping > detects fetchUser and parseRepo as functions
  • Swift for-in loop element type inference > detects User and Repo classes
  • Swift for-in loop element type inference > creates implicit import edges between files
  • Swift field-type resolution > detects classes and their properties
  • Swift field-type resolution > emits HAS_PROPERTY edges from class to field
  • Swift field-type resolution > resolves field-chain call user.address.save() → Address#save
  • Swift field-type resolution > emits ACCESSES edges for field reads in chains
  • Swift field-type resolution > populates field metadata (visibility, declaredType) on Property nodes
  • Swift call-result binding > resolves call-result-bound method call user.save() → User#save
  • Swift call-result binding > getUser() is present as a defined function
  • Swift call-result binding > emits processUser -> getUser CALLS edge for let-assigned free function call
  • Swift method enrichment > detects Animal protocol and Dog class
  • Swift method enrichment > emits IMPLEMENTS edge Dog -> Animal
  • Swift method enrichment > emits HAS_METHOD edges for Dog methods
  • Swift method enrichment > marks protocol Animal.speak as isAbstract
  • Swift method enrichment > marks Dog.speak as NOT isAbstract
  • Swift method enrichment > marks breathe as isFinal
  • Swift method enrichment > marks classify as isStatic
  • Swift method enrichment > captures @objc annotation on breathe
  • Swift method enrichment > populates parameterTypes for classify(_ name: String)
  • Swift method enrichment > records parameterCount for classify
  • Swift method enrichment > records returnType for speak
  • Swift method enrichment > resolves dog.speak() CALLS edge
  • Swift method enrichment > resolves Dog.classify("dog") CALLS edge
  • Swift abstract dispatch > detects Repository protocol and SqlRepository class
  • Swift abstract dispatch > emits IMPLEMENTS edge SqlRepository -> Repository
  • Swift abstract dispatch > emits HAS_METHOD edges for Repository.find and Repository.save
  • Swift abstract dispatch > emits HAS_METHOD edges for SqlRepository.find and SqlRepository.save
  • Swift abstract dispatch > marks base Repository.find as isAbstract
  • Swift abstract dispatch > marks base Repository.save as isAbstract
  • Swift abstract dispatch > marks concrete SqlRepository.find as NOT isAbstract
  • Swift abstract dispatch > resolves repo.find(id: 42) CALLS edge
  • Swift abstract dispatch > resolves repo.save(entity: user) CALLS edge
  • Swift abstract dispatch > populates parameterTypes for Repository.find
  • Swift abstract dispatch > populates parameterTypes for Repository.save
  • Swift abstract dispatch > records returnType for SqlRepository.find
  • Swift abstract dispatch > emits METHOD_IMPLEMENTS edges from SqlRepository methods → Repository protocol methods
  • Swift overloaded method disambiguation > detects 2 distinct find Method nodes on SqlRepository
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edges for both find overloads
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edge for save
  • Swift overloaded method disambiguation > emits exactly 3 METHOD_IMPLEMENTS edges total
  • Swift Child extends Parent — inherited method resolution (SM-9) > detects Parent and Child classes
  • Swift Child extends Parent — inherited method resolution (SM-9) > resolves c.parentMethod() to Parent.parentMethod via first-wins MRO walk

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 73.32% 17940/24467 73.32% = 0.0 🟢 ██████████████░░░░░░
Branches 62.32% 11396/18285 62.31% 📈 +0.0 🟢 ████████████░░░░░░░░
Functions 77.97% 1703/2184 77.97% = 0.0 🟢 ███████████████░░░░░
Lines 75.87% 16261/21430 75.87% = 0.0 🟢 ███████████████░░░░░

📋 View full run · Generated by CI

ce:review (interactive) on PR #964 surfaced two P2s and several P3s. This
commit applies all `safe_auto` fixes + both manual tests in-line so the
PR ships with a cleaner review trail.

## P2 fixes

- **Complete `freezeIndex` → `wrapIndex` rename.** The prior commit renamed
  3 of 5 sibling index files; `method-dispatch-index.ts` and
  `position-index.ts` still carried the old name. Now all 5 helpers use
  the consistent `wrapIndex` naming.
  (maintainability + project-standards reviewers both flagged this.)

- **Add regression tests for the `recordTypeBindingHit` origin demotion.**
  The prior commit introduced the `tieBreakKey.origin = 'import'`
  demotion for Step-2-only candidates without a direct test. Added:
    - `registries.test.ts`: two Step-2-only siblings under the same
      interface, asserting deterministic DefId.localeCompare tie-break
      AND the stronger invariant that composeEvidence never emits a
      where-found signal for Step-2-only candidates (no `signals.origin`).
    - `position-index.test.ts`: touching-boundary test proving the
      right-sibling-wins rule documented in the new JSDoc.
  (testing + kieran-typescript + api-contract reviewers all flagged these gaps.)

## P3 fixes

- Fix wrong comment in `recordTypeBindingHit` that claimed Step 1 could
  later upgrade a demoted origin. Step 1 runs BEFORE Step 2 — the actual
  upgrade path is Step 3 (`seedFromOwnerScopedContributor`). Comment now
  describes execution order correctly.

- Fix inaccurate "re-exported there" comment in `index.ts`. `types.ts`
  *defines* ScopeLookup natively; it's not a re-export. Phrasing now
  says "defined in types.ts and exported from the type-export block
  above — not from this module."

- Update stale `scope-tree.ts` file-header prose that still referenced
  `ScopeLookup` as living in #916/resolve-type-ref.ts. Now points to
  `./types.js` with a cross-ref to both #916 and #917 consumers.

- Expand `atPosition` touching-boundary JSDoc to name the mechanism
  (backward scan through start-sorted array) so readers can trace the
  binary-search code to the claim.

- Add breadcrumb to `aggregate.ts` module header pointing future readers
  to `./diff.ts` / the top-level barrel for `ShadowAgreement`,
  `ShadowCallsite`, and `ShadowDiff`.

- Remove unnecessary non-null assertion in `recordTypeBindingHit`. Local
  `const existingMroDepth = ...` lets TS narrow to `number` in the
  else-branch, eliminating the `!` without behavior change.

## Verification

- `tsc --noEmit` clean (both `gitnexus-shared` and `gitnexus`)
- `gitnexus-shared` build clean
- Combined scope-resolution / model / shadow suite: **262/262 pass** (+2
  from the new origin-demotion + touching-boundary regression tests)
@magyargergo

Copy link
Copy Markdown
Collaborator Author

@claude Act as a senior reviewer for GitNexus. Your job is to determine whether this PR is production-ready for this repo, not to give a generic code review.

You are reviewing a PR in the GitNexus monorepo:

  • gitnexus/ → CLI + MCP
  • gitnexus-web/
  • gitnexus-shared/

Your task has 2 phases, in this exact order:

PHASE 1 — DEFINE THE BAR
Before reviewing the diff, establish a concise repo-specific definition of “production-ready” for GitNexus, based only on the repo docs and the affected area.
Keep this definition practical and reviewable. Do not invent standards that are not grounded in the repo.

PHASE 2 — REVIEW THE PR AGAINST THAT BAR
Review the actual diff only after defining the bar.
Stay tightly scoped to the changed code and its direct consequences.


CONTEXT TO LOAD FIRST
Read these before reviewing:

  • AGENTS.md
  • GUARDRAILS.md
  • CONTRIBUTING.md
  • TESTING.md
  • ARCHITECTURE.md

Additional context:


PRIMARY OBJECTIVE
Decide whether this PR is safe, correct, maintainable, and operationally acceptable to merge into production for GitNexus.

Do not optimize for completeness at the expense of signal.
Do not pad the review.
Do not propose unrelated refactors.
Do not restate the PR description unless needed for verification.


REVIEW RULES

  • Every finding must be grounded in specific evidence from the diff or directly relevant surrounding code.
  • Every finding must include path:line.
  • If you make a behavioral claim, cite the code that proves it.
  • If you make a performance claim, explain the mechanism.
  • If something cannot be verified from the diff alone, explicitly say so.
  • Distinguish clearly between:
    • verified issue
    • plausible risk
    • unverified concern
  • Avoid vague wording like “might be better” or “could be improved” unless you explain exactly why.
  • Keep the review focused on this PR’s scope only.

For each finding, assign one severity:

  • BLOCKING → must be fixed before merge
  • NON-BLOCKING → valid issue, but merge may still be acceptable
  • NIT → stylistic/minor, not merge-relevant

REPO-SPECIFIC REVIEW CHECKLIST
Use these exact headings.

1. Correctness & functional completeness

Check:

  • Does the implementation actually satisfy the PR claim?
    • ManifestExtractor is truly invoked
    • config.links produces non-zero cross-links where expected
  • Resolver contracts are preserved:
    • resolveSymbol remains exact-match
    • label-scoped Cypher remains correct per contract type
    • flag any regression toward fuzzy or unscoped matching
  • Graph schema integrity is preserved:
    • no silent changes to node labels
    • no silent changes to edge types
    • no silent changes to ID generation (generateId)
  • Call out any missing wiring, partial integration, dead branch, or mismatch between tests and runtime behavior

2. Code clarity & clean code

Check:

  • naming quality
  • local cohesion
  • dead code
  • unnecessary abstraction
  • hidden control flow
  • confusing indirection
  • adherence to repo conventions:
    • direct imports from gitnexus-shared
    • no barrel re-export regressions
    • no // removed comments
    • no unused re-exports
  • no drive-by refactors outside stated scope per CONTRIBUTING.md and GUARDRAILS.md § Scope

3. Test coverage & change safety

Evaluate against TESTING.md:

  • Are there unit tests under gitnexus/test/unit/ covering the newly wired path?
  • Is there a regression guard for 0-link → N-link behavior?
  • Are assertions meaningful rather than tautological?
  • Are fixtures realistic for manifest inputs?
  • If memoization/cache was introduced, is there a test proving hit/miss behavior and correctness?
  • Is there evidence the expected validation path would pass for staged gitnexus/ files?
    • tsc --noEmit
    • vitest run --project default
      If not verifiable, say exactly what is missing.

4. Performance

Inspect for:

  • hot-path overhead in ingestion/group sync
  • excess allocations per manifest entry
  • redundant Cypher round-trips
  • missed batching or missed parallelism (Promise.all) where it materially matters
  • O(n²) or repeated lookup patterns on large repos
  • memoization tradeoffs:
    • correctness
    • invalidation
    • bounded vs unbounded memory growth
      Do not speculate casually; explain the mechanism and likely impact.

5. Operational risk

Check:

  • Windows/cross-platform safety:
    • stream lifecycle
    • FD/file handle lifecycle
    • path separator assumptions
    • anything resembling prior ENOTEMPTY-style lifecycle regressions
  • LadybugDB single-writer invariant is preserved
  • Embeddings preservation:
    • no silent breakage of --embeddings
    • .gitnexus/meta.json.stats.embeddings not silently zeroed by changed paths
  • MCP contracts remain compatible:
    • group_*
    • query
    • context
    • impact
    • detect_changes
    • rename
    • cypher
      Flag any schema or contract break without migration note
  • staleness behavior still triggers correctly (gitnexus/src/mcp/staleness.ts)
  • rollback safety:
    • can this PR be reverted safely without re-analyze?
    • if not, explain why

6. Maintainability

Check:

  • Does the change respect Pipeline Phase DAG boundaries?
  • Is cross-phase coupling being introduced?
  • Are manifest-extractor entry points discoverable from group/sync.ts?
  • Would the next contributor know where to extend this?
  • Is shared logic placed appropriately:
    • in gitnexus-shared/
    • or gitnexus/src/core/**/utils.ts
    • and not duplicated between CLI and web bridge (server/api.ts)?

NON-NEGOTIABLES TO VERIFY
From GUARDRAILS.md / CONTRIBUTING.md:

  • no secrets, tokens, or machine-specific paths committed
  • no blind find-and-replace renames
  • if a symbol rename occurred, rename MCP tool or equivalent careful review was used
  • upstream impact was considered for modified shared symbols
  • no HIGH/CRITICAL impact left unaddressed
  • PR title follows conventional commit style + [area] Short description
  • PolyForm Noncommercial license headers/metadata untouched

OUTPUT FORMAT

Production-ready bar for this PR

Give a concise repo-specific definition in 5–10 bullets.

Findings

For each finding, use this structure exactly:

[BLOCKING|NON-BLOCKING|NIT] Short title

  • Evidence: path:line, path:line
  • Why it matters: ...
  • Recommendation: ...
  • Confidence: High / Medium / Low

Only include findings that are real and supported.
If there are no findings in a section, say:

  • No issues found in this area based on the diff and reviewed context.

What I could not fully verify

List any behavior you could not prove from the diff, tests, or available context.

Final verdict

Choose exactly one:

  • ✅ Ready to merge — no blocking issues.
  • ⚠️ Merge after addressing blocking items.
  • ❌ Not ready — fundamental concerns.

If verdict is not ready, list the blocking items in priority order.


IMPORTANT REVIEW BEHAVIOR

  • Be strict on correctness and contracts.
  • Be conservative about operational risk.
  • Be explicit about uncertainty.
  • Do not suggest unrelated refactors.
  • Do not turn nits into blockers.
  • Do not reward intent; review actual implementation.

You must also ensure we are on track with the RFC plan! Check the issue tickets involved!

@github-actions

github-actions Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Claude finished @magyargergo's task in 4m 13s —— View job


PR Review: chore(shared): apply Ring 2 SHARED review follow-ups in one diff

Tasks:

  • Initialize review
  • Read repo docs (AGENTS.md, GUARDRAILS.md, CONTRIBUTING.md, TESTING.md, ARCHITECTURE.md)
  • Read changed files in the diff
  • Check RFC plan tracking via commit history and PR context
  • Phase 1: Define production-ready bar
  • Phase 2: Review against the bar
  • Post final verdict

Production-ready bar for this PR

Based on AGENTS.md, GUARDRAILS.md, CONTRIBUTING.md, TESTING.md, ARCHITECTURE.md, and the affected area (gitnexus-shared/scope-resolution):


Findings

1. Correctness & functional completeness

freezeIndex → wrapIndex rename — verified complete (2 commits)

The first commit missed method-dispatch-index.ts and position-index.ts; the second commit (a9fef0b) completed the rename. All 5 internal helper functions now use wrapIndex. Safety surface unchanged (frozen bucket arrays, frozen EMPTY array, readonly Map wrappers).

ScopeLookup migration to types.ts — verified correct

  • Definition removed from resolve-type-ref.ts, added to types.ts (identical signature: getScope(id: ScopeId): Scope | undefined).
  • scope-tree.ts import updated from resolve-type-ref.jstypes.js — layer inversion resolved.
  • Barrel (index.ts:51) adds ScopeLookup to the types.js type-export block and removes it from the resolve-type-ref.js line. Net exports unchanged for barrel consumers.
  • Verified: no consumer importing ScopeLookup from gitnexus-shared top-level would observe any difference (structural interface, same shape).

recordTypeBindingHit origin demotion — verified correct with Step 3 interaction checked

Evidence: lookup-core.ts:383-385

if (firstHit && state.signals.origin === undefined) {
  state.tieBreakKey.origin = 'import';
}

The guard firstHit && signals.origin === undefined correctly skips the demotion for Step-1 lexical hits (which set signals.origin before Step 2 runs). Step 3 (seedFromOwnerScopedContributor, lookup-core.ts:398-404) unconditionally writes state.tieBreakKey.origin = 'local' for every def it processes — verified in source, confirming the comment's claim. Any same-def Step-1+Step-2 overlap is correctly handled. ensureCandidate default at lookup-core.ts:180 confirmed as origin: 'local' — the bug being fixed.

aggregate.ts partial re-export removal — verified safe

The removed export type { ShadowAgreement, ShadowDiff } from aggregate.ts was a partial re-export (missing ShadowCallsite), inconsistent with the barrel. The barrel at index.ts:140-144 already exports all three types from diff.js directly. No consumer of the public API (gitnexus-shared) is broken. Consumers doing direct subpath imports from aggregate.ts (discouraged per AGENTS.md conventions) would need to update.

diff.test.ts invalid 'wildcard' kind fix — verified

'wildcard' is not a valid ResolutionEvidence.kind; replaced with 'global-name', a real kind. The test's behavioral assertion (agreement: 'both-agree') is unaffected by the kind value.

lookupReceiverType comment fix — verified

Old comment at the return undefined path described a "name-match among class-like defs" fallback that doesn't exist in the code. New comment accurately states the intentional absence of a fallback. No code change.

  • No issues found in this area based on the diff and reviewed context.

2. Code clarity & clean code

BFS vs C3 test differentiation — correct and meaningful

The old BFS test used ['def:B', 'def:C', 'def:A'] — identical to the C3 test input. Now uses ['def:B', 'def:A', 'def:C'], producing a distinct expected MRO ['def:B', 'def:A', 'def:C']. The two tests now independently prove the materializer stores whatever order the callback provides.

[NIT] Overlong JSDoc line in position-index.ts

  • Evidence: gitnexus-shared/src/scope-resolution/position-index.ts (diff line corresponding to the atPosition JSDoc)
  • Why it matters: The sentence "the right sibling is scanned first. Queries at non-boundary positions between them naturally\n * fall to the unique containing scope." is a single run-on line well past 100 chars, breaking the surrounding comment's line-wrap style.
  • Recommendation: Split after "scanned first." into a new * continuation line.
  • Confidence: High (easily observed in diff)

Otherwise: no dead code, no // removed comments, no unused re-exports, no unrelated drive-by refactors. Barrel comment added at index.ts:75-76 explaining ScopeLookup's new export location — useful for navigators.

3. Test coverage & change safety

Five new/modified assertions:

Test File:block Covers
Touching-boundary right-sibling-wins position-index.test.ts:436-452 New JSDoc invariant; exercises the backward scan tie-break
implementsOfCalls counter method-dispatch-index.test.ts:138-425 Pins the implementsOf-per-occurrence contract
Hit-path freeze assertion qualified-name-index.test.ts:464-466 Proves frozen bucket on hit path, not just miss path
Multi-candidate local>wildcard ordering registries.test.ts:496-515 First genuine confidence-DESC ordering test in Step 7
Step-2 demotion + evidence kind invariant registries.test.ts:524-575 Pins tieBreakKey.origin='import' demotion AND asserts no signals.origin in evidence output

All assertions are non-tautological. The Step-2 demotion test in particular is well-constructed: it asserts the stronger observable invariant (no origin signal in evidence) rather than the weaker internal state, making the test robust to implementation changes.

CI confirms: 260/260 scope-resolution tests pass (6698/6698 total). TypeCheck clean.

[NON-BLOCKING] PR description opening line vs. actual change

  • Evidence: PR body: "No behavior changes" vs. the recordTypeBindingHit demotion fix described in the #917 table row
  • Why it matters: The opening sentence says "no behavior changes" but the Step-2 origin demotion IS a ranking behavior change (though it's a correctness fix for a pre-existing bug, not a new feature). The detailed table in the description is accurate. Reviewers scanning only the lede could be misled about the scope.
  • Recommendation: Change "No behavior changes" to "No new features; one latent ranking bug fixed (tieBreakKey.origin demotion for Step-2-only candidates) — see #917 row."
  • Confidence: High

4. Performance

No hot-path changes. The demotion fix adds two field assignments and one boolean check per recordTypeBindingHit invocation — negligible. The collectOwnedMembers O(D) scan (lookup-core.ts:341) is pre-existing and acknowledged in the comment ("tracked as a follow-up optimization before Ring 3"). No new allocations, no new Cypher paths.

  • No issues found in this area based on the diff and reviewed context.

5. Operational risk

  • No changes to ingestion phases, LadybugDB schema, MCP tool contracts, embeddings, or staleness logic.

  • The buildModuleScopeIndex JSDoc caller contract (path normalization warning) is documentation-only; no runtime behavior altered.

  • Rollback is safe: all changes are in gitnexus-shared/ types and tests; no runtime state migration required.

  • No cross-platform path handling changes introduced (despite the warning about C:\ vs C:/ in the new JSDoc — that warning is additive documentation for callers, not a behavior change).

  • No issues found in this area based on the diff and reviewed context.

6. Maintainability

  • ScopeLookup is now correctly anchored in the data-model layer (types.ts), resolving the layer inversion flagged in Ring 2 review. Future §4 modules can depend on it without coupling to each other.

  • aggregate.ts module-level comment points consumers to the canonical barrel, so the next contributor won't re-introduce the partial re-export.

  • wrapIndex naming is consistent across all 5 index builders — no stale freezeIndex reference remains.

  • Pipeline Phase DAG boundaries: not touched — PR is entirely in gitnexus-shared/, which sits above the DAG layer.

  • No issues found in this area based on the diff and reviewed context.


Non-negotiables (GUARDRAILS.md / CONTRIBUTING.md)

Check Status
No secrets or machine-specific paths
No blind find-and-replace — wrapIndex rename was deliberate, applied file-by-file
Upstream impact considered for shared symbol move (ScopeLookup) ✅ — barrel route unchanged for consumers
No HIGH/CRITICAL impact left unaddressed
PR title: chore(shared): ... — valid conventional commit
PolyForm license untouched

RFC Plan Tracking (RFC #909)

Based on recent commit history and PR references:

The sequencing is on track. No Ring 2 SHARED item appears to have been dropped silently.


What I could not fully verify

  1. Whether any external consumer uses a direct subpath import from gitnexus-shared/dist/scope-resolution/aggregate.js for ShadowAgreement/ShadowDiff — would break silently. Internal usage within gitnexus/ would be caught by tsc --noEmit (confirmed clean by CI), but a downstream consumer in a separate package would not. Low risk given the AGENTS.md convention of barrel-only imports.
  2. Whether gitnexus/src/core/ingestion/ currently imports ScopeLookup from the old path — the tsc clean result confirms no broken imports exist, but I did not independently trace all import sites.
  3. Ring 2 PKG issues RING2-PKG-1: ScopeExtractor driver (tree-sitter + provider hooks → shared ScopeTree) #919RING2-PKG-7: ReferenceIndex emission to LadybugDB #925 contents — cannot access GitHub issue bodies directly to confirm no SHARED prerequisite was missed.

Final verdict

Ready to merge — no blocking issues.

The one NON-BLOCKING finding (PR description lede vs. actual change) is a documentation precision issue only and doesn't affect correctness or safety. The NIT is trivial. All code logic is verified correct, all new tests are substantive, CI is fully green (6698/6698, tsc clean), and the RFC Ring 2 SHARED loop is properly closed. Ready to proceed to Ring 2 PKG.

@magyargergo magyargergo merged commit e944f90 into main Apr 18, 2026
14 checks passed
@magyargergo magyargergo deleted the rfc/scope-resolution/ring2-shared-review-followups branch April 18, 2026 17:40
github714801013 pushed a commit to github714801013/GitNexus that referenced this pull request Apr 28, 2026
…igyanpatwari#964)

* chore(shared): apply Ring 2 SHARED review follow-ups in one diff

Aggregates all actionable follow-ups from the 9 Ring 2 SHARED PRs
(abhigyanpatwari#949abhigyanpatwari#963) before proceeding to Ring 2 PKG. No behavior changes;
docstring edits, test refinements, and one structural cleanup.

## abhigyanpatwari#913 (DefIndex / ModuleScopeIndex / QualifiedNameIndex)
  - Rename `freezeIndex` → `wrapIndex` across all three index builders.
    The old name implied `Object.freeze` on the wrapper, which we never
    applied; `wrapIndex` more accurately describes the lightweight
    readonly-interface wrap. Safety surface (frozen bucket arrays,
    frozen miss-empty array, readonly Maps) is unchanged.
  - Document in `buildModuleScopeIndex` JSDoc that callers must
    pre-normalize `filePath` keys (no path-separator canonicalization
    happens here). Prevents silent cross-platform misses.
  - Add an explicit hit-path freeze assertion in
    `qualified-name-index.test.ts` (the existing test covered only the
    miss-path `EMPTY` array).

## abhigyanpatwari#914 (MethodDispatchIndex)
  - Differentiate the C3 and BFS test cases: both tests now use
    distinct MRO orderings so they prove the materializer stores
    whatever order the `computeMro` callback produces (not that C3 and
    BFS yield identical output).
  - Add `implementsOfCalls` counter in the first-write-wins test, and
    document the call-count contract in `MethodDispatchInput.implementsOf`
    JSDoc: `implementsOf` fires **per occurrence** in `input.owners`
    (not per unique owner); `computeMro` fires at most once per unique
    owner. Callers with expensive `implementsOf` implementations should
    pre-dedupe `owners`.

## abhigyanpatwari#916 (resolveTypeRef)
  - Document the deliberate exclusion of `'Type'` from `TYPE_KINDS`
    (verified no extractor in `gitnexus/src/core/ingestion/` emits
    `type: 'Type'` for annotation-relevant symbols).
  - Rename the namespace-origin test from `'resolves ...'` to
    `'returns null for a namespace-origin binding whose def is not a
    type kind'`, matching the failure-case intent.

## abhigyanpatwari#918 (shadow diff + aggregate)
  - Remove the partial re-export `export type { ShadowAgreement, ShadowDiff };`
    from `aggregate.ts` — it omitted `ShadowCallsite` and diverged
    from the top-level barrel. Consumers import all three from the
    `gitnexus-shared` entry point.
  - Fix the invalid `'wildcard'` evidence kind in `diff.test.ts` fixture
    (that kind is not a valid `ResolutionEvidence.kind`). Replaced with
    `'global-name'`, a real kind the test treats identically.

## abhigyanpatwari#912 (ScopeTree / PositionIndex / makeScopeId)
  - Document the touching-boundary semantics on `PositionIndex.atPosition`:
    when siblings share a boundary point, the right (later-start) sibling
    wins per the existing innermost-wins sort contract.
  - Resolve the layer-inversion flagged by review: move `ScopeLookup`
    from `resolve-type-ref.ts` to `types.ts` (its natural home in the
    data-model layer). `scope-tree.ts` now imports `ScopeLookup` from
    `types.js` directly; the old re-export from `resolve-type-ref.ts`
    is removed per repo convention (`feedback_no_reexport`). Barrel
    export moved alongside.

## abhigyanpatwari#917 (ClassRegistry / MethodRegistry / FieldRegistry)
  - Replace the dangling "try a name-match among class-like defs"
    comment in `lookupReceiverType` with explicit prose that callers
    must pre-resolve via `resolveTypeRef` if they want richer semantics.
    No behavior change — the function already returned `undefined` on
    ambiguous/missing qnames.
  - Fix `tieBreakKey.origin` default for pure Step-2 candidates.
    Type-binding-only hits no longer falsely inherit `'local'` from
    `ensureCandidate`'s neutral default; they now demote to `'import'`
    on their first type-binding hit, and only a later Step-1 lexical
    hit can upgrade them back to `'local'`. Keeps the Appendix B
    cascade faithful to the true origin.
  - Document `'global-name'` in `evidence.ts`: currently reserved for
    Ring 3's byName global index; `lookupCore` never emits it today.
    The weight stays live so `composeEvidence` remains exhaustive over
    the origin union.
  - Rename the mislabeled Step-7 test from `'confidence DESC is the
    primary key'` (which actually tested hard-shadow baseline) to
    `'inner scope shadows outer, yielding single result'`, and add a
    separate test that actually exercises multi-candidate confidence
    ordering (local vs wildcard at the same scope).

## Verification
  - `tsc --noEmit` clean (both `gitnexus-shared` and `gitnexus`)
  - `gitnexus-shared` build clean
  - Combined scope-resolution / model / shadow suite: **260/260 pass**
    (+1 from the new multi-candidate ordering test in abhigyanpatwari#917)

## Not addressed (non-actionable)
  - abhigyanpatwari#949 CI "failure with zero failing tests": pre-existing Swift Node 22
    grammar flake unrelated to abhigyanpatwari#910 scope.
  - abhigyanpatwari#950: the two non-blocking findings were already addressed in
    follow-up commit `cbac32ba` (ParsedImport discriminated union +
    `ScopeId | null` on the two hooks).
  - abhigyanpatwari#915: the five in-scope findings were already addressed in
    follow-up commit `54515a7e` (dead code, unused params, multi-hop
    docs, cap-hit test, stats granularity).
  - abhigyanpatwari#915 LanguageProvider.resolveImportTarget signature divergence +
    `findDefById` O(F×D) perf: tracked separately as follow-up issues
    for the Ring 3 migration window.

* chore(shared): address ce:review findings on the follow-up diff

ce:review (interactive) on PR abhigyanpatwari#964 surfaced two P2s and several P3s. This
commit applies all `safe_auto` fixes + both manual tests in-line so the
PR ships with a cleaner review trail.

## P2 fixes

- **Complete `freezeIndex` → `wrapIndex` rename.** The prior commit renamed
  3 of 5 sibling index files; `method-dispatch-index.ts` and
  `position-index.ts` still carried the old name. Now all 5 helpers use
  the consistent `wrapIndex` naming.
  (maintainability + project-standards reviewers both flagged this.)

- **Add regression tests for the `recordTypeBindingHit` origin demotion.**
  The prior commit introduced the `tieBreakKey.origin = 'import'`
  demotion for Step-2-only candidates without a direct test. Added:
    - `registries.test.ts`: two Step-2-only siblings under the same
      interface, asserting deterministic DefId.localeCompare tie-break
      AND the stronger invariant that composeEvidence never emits a
      where-found signal for Step-2-only candidates (no `signals.origin`).
    - `position-index.test.ts`: touching-boundary test proving the
      right-sibling-wins rule documented in the new JSDoc.
  (testing + kieran-typescript + api-contract reviewers all flagged these gaps.)

## P3 fixes

- Fix wrong comment in `recordTypeBindingHit` that claimed Step 1 could
  later upgrade a demoted origin. Step 1 runs BEFORE Step 2 — the actual
  upgrade path is Step 3 (`seedFromOwnerScopedContributor`). Comment now
  describes execution order correctly.

- Fix inaccurate "re-exported there" comment in `index.ts`. `types.ts`
  *defines* ScopeLookup natively; it's not a re-export. Phrasing now
  says "defined in types.ts and exported from the type-export block
  above — not from this module."

- Update stale `scope-tree.ts` file-header prose that still referenced
  `ScopeLookup` as living in abhigyanpatwari#916/resolve-type-ref.ts. Now points to
  `./types.js` with a cross-ref to both abhigyanpatwari#916 and abhigyanpatwari#917 consumers.

- Expand `atPosition` touching-boundary JSDoc to name the mechanism
  (backward scan through start-sorted array) so readers can trace the
  binary-search code to the claim.

- Add breadcrumb to `aggregate.ts` module header pointing future readers
  to `./diff.ts` / the top-level barrel for `ShadowAgreement`,
  `ShadowCallsite`, and `ShadowDiff`.

- Remove unnecessary non-null assertion in `recordTypeBindingHit`. Local
  `const existingMroDepth = ...` lets TS narrow to `number` in the
  else-branch, eliminating the `!` without behavior change.

## Verification

- `tsc --noEmit` clean (both `gitnexus-shared` and `gitnexus`)
- `gitnexus-shared` build clean
- Combined scope-resolution / model / shadow suite: **262/262 pass** (+2
  from the new origin-demotion + touching-boundary regression tests)
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.

1 participant