Skip to content

fix: address the retroactive review findings on the v1.6.1 port - #291

Merged
sunerpy merged 9 commits into
mainfrom
fix/retro-review-findings
Oct 1, 2026
Merged

sunerpy merged 9 commits into
mainfrom
fix/retro-review-findings

Conversation

@sunerpy

@sunerpy sunerpy commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

The owner asked for a retroactive kirocodex review of the large v1.6.1 port PRs that merged before the review rule existed: #271, #275, #277, #278, #279, #281 and #282. Each was reviewed by the goal gate against its own parent. #271, #275 and #279 passed. #277, #278, #281 and #282 failed with ten findings in total.

Each finding was checked against current main (c00aa2b) and upstream v1.6.1 (f4ddf50). This PR fixes the four that are real defects in the port and bumps the extraction version. The other six are left as they are, with reasons below.

Fixed

  • Vue same-line functions (fix: v1.6.1 node identity, traversal depth, method values and handler recall #278, #1349). The Vue extractor mints its own script-block function nodes and still called generate_node_id directly. Two same-name declarations on one line shared an id, so the store kept one row. Upstream reaches its allocator by delegating Vue scripts to the tree-sitter extractor. The fix routes them through NodeIdAllocator with the block-relative line and UTF-16 column that the delegated extractor would use. Non-colliding ids are unchanged.
  • Store initializer calls (fix(resolve): v1.6.1 receiver evidence, store and object-literal bindings, and extraction recall #277, KEEP-RUST). After fix(resolve): v1.6.1 receiver evidence, store and object-literal bindings, and extraction recall #277 made an exported store's inline actions into function nodes, the walker skipped the rest of the initializer. create(persist(..., { storage: createJSONStorage(...) })) then recorded no call to create, persist or createJSONStorage. The port recorded those calls before fix(resolve): v1.6.1 receiver evidence, store and object-literal bindings, and extraction recall #277; upstream skips them too.
    • The initializer is now walked as the store's own code. Only the extracted action values are skipped, so no action's calls are counted twice.
    • The factory closure is walked as the store's body. A curried create<S>()(...) factory therefore no longer becomes a function named after the store.
  • Trim note honesty (fix(explore): v1.6.1 exact targets, named-file budget, honest completion notes and trim names #281, KEEP-RUST). A gap marker names the indexed symbols it hides only from the budget its file has spare. The tail marker after a dropped or windowed cluster names nothing, and a dropped file appears only in the pointer list. Yet the closing note on both tiers still said the gap markers named what was elided. Upstream's wording is the same. The notes now always say the markers name what room allowed. Round 1 of the kirocodex review rejected a first attempt that hedged only when an inner gap withheld a name, because the tail marker was missed.
  • Default-import binding (fix: v1.6.1 node identity, traversal depth, method values and handler recall #278, KEEP-RUST). A default import resolved to the module's first exported component before the binding its export default NAME statement names. The React resolver mints component nodes inside ordinary .js/.ts modules, so import Api from './screen' bound to Screen in a module that exports function Screen() { return <div /> } beside const Api = { upload }; export default Api. Api.upload() then became a call to Screen, and the upload edge was lost. Upstream orders the lookup the same way. The stated binding now comes first, then a component (a Svelte or Vue file has no such statement and is its own default export), then the first exported function or class.
  • Extraction version 18. The first two fixes change extraction output, so a version-17 index is reported outdated and re-extracted.

Declined, with reasons

Finding Why it stays
#277: .getState() chains bind for a store built by any factory, not only Zustand Same as upstream: matchStoreAccessorChain checks provenance only for selectors. The member must sit in that holder's own object literal, and the call site must call .getState() on that holder. A provenance gate would also drop zustand/vanilla, wrapped (createSelectors(create(...))) and locally re-exported factories.
#278: an unknown receiver falls back to a project-unique method name Upstream's deliberate design ("Unknown receivers retain the old unique-or-drop discipline"), and it predates #278.
#278: no exact-head evidence artifact A process finding. The PR merged after CI run 36798356332 passed on a tree-identical head.
#282: compound #if conditions are unknown Upstream's evaluator reads the same forms: a literal, defined(), or a bare name. An unknown condition keeps the call, which was the behavior before #2069.
#282: #ifndef X / empty #define X is read as a guard Upstream's documented idiom in guardsItself. A default value (#define X 0) stays unknown.
#282: any initializer_list constructor declines the overload set Upstream's deliberate decline: the reference carries arity, not the brace or paren form. The decline is fail-closed.

Verification

  • Red-first evidence:
    • red-vue-identity.log and red-vue-store.log: the store kept 1 row where there should be 2;
    • red-store-initializer.log;
    • red-unnamed-gap-note.log: both tiers;
    • red-default-binding.log: consume called src/screen.js::Screen.
    • r2-red-trailing-gap.log: the tail marker case on both tiers, against round 1's code.
  • Mutation checks: mutation-factory-guard.log and mutation-action-skip.log show that each guard in the store walk is load-bearing.
  • Goldens: the drift check re-indexed all 19 corpora at 60d441e with a release build; every corpus is identical (golden-drift.log).
  • make pre-ci at 60d441e, clean tree: 3969 tests passed, 0 failed, and all checks passed (pre-ci.log). That target is what the pre-push hook runs. It was rerun on the final head (pre-ci-r2.log).
  • kirocodex review (goal gate, on the PR's scope):
    • round 1 raised one blocking item, the tail marker, which 60366e4 fixes;
    • round 2 passed;
    • after CI's pinned Rust 1.96 clippy rejected !opt.is_some_and(p) (nonminimal_bool), 59a2520 rewrote it as opt.is_none_or(!p). A fresh review of the cumulative diff at 59a2520 passed with no blocking items.
  • make pre-ci on Rust 1.96.0 at 59a2520, clean tree: 3971 passed, 0 failed (pre-ci-r3.log). The earlier runs used 1.98, because this shell exports RUSTUP_TOOLCHAIN.

All logs are under /tmp/evidence-retro/.

BEGIN_COMMIT_OVERRIDE
fix(extract): give same-line Vue script functions distinct ids (#1349)
fix(extract): keep a store initializer's own calls on the store
fix(resolve): prefer an explicit default binding over an exported component
fix(mcp): never claim that gap markers name every elided symbol
END_COMMIT_OVERRIDE

🤖 Generated with Claude Code

CodeGraph Test added 6 commits October 1, 2026 21:25
The Vue extractor mints its script-block functions itself and still
called generate_node_id directly, so two same-name function
declarations on one line shared an id and the store kept one row.
Upstream reaches the allocator by delegating Vue scripts to its
tree-sitter extractor. Route the script functions through
NodeIdAllocator, with the block-relative line and UTF-16 column the
delegated extractor would use; a non-colliding id is unchanged.
Since exported stores own their inline actions, the walker skipped the
rest of the initializer, so `create(persist(...), { storage:
createJSONStorage(...) })` recorded no call to `create`, `persist` or
`createJSONStorage`, which the port recorded before. Upstream skips the
initializer the same way. Walk the initializer attributed to the store
and skip only the extracted action values, so no action's calls are
counted twice, and walk the factory closure as the store's body instead
of binding a curried `create<S>()(...)` factory as a function named
after the store.
Vue same-line ids and store initializer calls change what extraction emits, so an index built at version 17 is reported outdated and re-extracted.
A gap marker names the indexed symbols it hides only from the budget the
file has spare; a name that costs more leaves a bare `... (gap) ...`.
The note closing the response still said the gap markers named what was
elided, on both tiers. Report a withheld name from the render, and say
the markers name only what room allowed when one was withheld. The
epilogue reserve holds the longer of the two wordings.
…ponent

A default import resolved to the module's first exported component
before the binding its `export default NAME` statement names. The React
resolver mints component nodes inside ordinary JS/TS modules, so a
module exporting `function Screen() { return <div /> }` beside `const
Api = { upload }; export default Api` bound `import Api` to `Screen`:
`Api.upload()` became a call to `Screen` and lost its `upload` edge.
Upstream orders the lookup the same way. Try the stated binding first,
then a component (a Svelte or Vue file has no such statement and is its
own default export), then the first exported function or class.
Seven merged port PRs were reviewed by kirocodex after the fact. The entry lists each verdict, the four defects #291 fixes (three of them KEEP-RUST divergences), and the findings kept at upstream behavior with upstream's reasons.
@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.38710% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/codegraph-extract/src/walker.rs 97.56% 1 Missing ⚠️

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #291      +/-   ##
==========================================
+ Coverage   95.29%   95.31%   +0.01%     
==========================================
  Files         161      161              
  Lines       99084    99130      +46     
==========================================
+ Hits        94422    94485      +63     
+ Misses       4662     4645      -17     
Files with missing lines Coverage Δ
crates/codegraph-extract/src/embedded/vue.rs 99.31% <100.00%> (+<0.01%) ⬆️
crates/codegraph-mcp/src/engine.rs 97.68% <100.00%> (+<0.01%) ⬆️
crates/codegraph-resolve/src/import_resolver.rs 96.73% <100.00%> (+<0.01%) ⬆️
crates/codegraph-store/src/index_state.rs 83.33% <ø> (ø)
crates/codegraph-extract/src/walker.rs 93.41% <97.56%> (+0.25%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The previous commit switched the trim note to a hedged wording only when
an inner gap marker withheld a name. The tail marker after a dropped or
windowed cluster names nothing either, and a dropped file appears only
in the pointer list, so the unhedged wording was still false on most
trimmed responses. Use the hedged wording on both tiers always, and drop
the withheld-name plumbing that chose between the two. Cover the tail
marker on both tiers.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60d441e772

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

self.file_path,
NodeKind::Function,
&name,
start_pos.row as u32 + 1,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use absolute Vue lines when allocating node IDs

For a valid SFC containing both <script> and <script setup>, functions with the same name at the same block-relative row and column receive the same ID even though their actual source lines differ; the allocator treats the second declaration as a revisit, and the store upsert drops one node. Pass the line including block.line_offset, matching the Node.start_line, so IDs follow the required file-relative formula.

AGENTS.md reference: AGENTS.md:L46-L52

Useful? React with 👍 / 👎.

Comment on lines +3139 to +3142
let previous = self.store_walk.replace(StoreWalk {
factory: nearest_function_ancestor(actions).map(|factory| factory.id()),
actions: actions_ids,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Skip every callable action during the initializer walk

When the returned action object contains a function under an unsupported key such as [dynamic]: () => wrongCall() or a numeric key, extract_js_object_literal_members does not mint a node or include its syntax ID in actions_ids. The subsequent initializer walk therefore descends into that closure and attributes wrongCall to the store, even though it runs only when the action is invoked; track and skip all callable member values, not only successfully extracted actions.

Useful? React with 👍 / 👎.

Comment thread crates/codegraph-mcp/src/engine.rs Outdated
Comment on lines 2474 to 2475
let (mut file_section, names_withheld) =
join_parts_with_named_gaps(file_path, &parts, &file_index_nodes, spare);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Propagate withholding from the trailing bare gap

When a file is clipped because a single cluster is windowed or another cluster is omitted, join_parts_with_named_gaps can return names_withheld = false because it only examines gaps between rendered parts, after which the renderer appends a separate bare ... (gap) .... The epilogue then uses the full wording claiming elided symbols are named inside gap markers even though this trailing marker names nothing; include this clipped marker in the flag or name its omitted symbols, and cover this branch as part of the MCP text contract.

AGENTS.md reference: AGENTS.md:L62-L64

Useful? React with 👍 / 👎.

CodeGraph Test added 2 commits October 1, 2026 21:48
The trim note now hedges on both tiers always, rather than only when an inner gap withheld a name.
Rust 1.96's clippy, which CI pins, rejects !opt.is_some_and(p); write it as opt.is_none_or(!p).
@sunerpy
sunerpy merged commit 436791b into main Oct 1, 2026
10 checks passed
@sunerpy
sunerpy deleted the fix/retro-review-findings branch October 1, 2026 14:15
sunerpy added a commit that referenced this pull request Oct 1, 2026
The release record for #291 and #292: the workflow run and its re-run past a transient cache outage, the published digest, and the black-box acceptance against v0.52.0.
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