[PR #6256] [MERGED] fix(call-path): count only expanded edges as explored #7230

Closed
opened 2026-11-01 21:46:21 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/6256
Author: @kaoru0822-kitauji
Created: 10/22/2026
Status: ✅ Merged
Merged: 10/22/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: fix/5906-call-path-explored-edges


📝 Commits (1)

  • fb36e1b fix(call-path): count only expanded edges as explored

📊 Changes

3 files changed (+241 additions, -25 deletions)

View changed files

📝 src/domain/callPath.test.ts (+118 -2)
📝 src/domain/callPath.ts (+19 -23)
➕ tests/boundary/mcp/callPathSearchExtentMcp.test.ts (+104 -0)

📄 Description

What broke: build_call_path reported a terminal goal's outgoing callees in explored.edges, even though the forward search never expands the goal. Zero-hop searches reported the start's callees too.
Fix: The search now counts explored edges and depth as it expands each function. The post-hoc summarizeSearch out-degree sum and the unused CallGraph.outDegree helper are removed.
Risk: Low. Only explored.edges changes, and only when the reached goal has supplied callees. Paths, shortest_hops, status, exhaustiveness, limitations and citations are unchanged, and so is the schema.

Summary

Fixes #5906.

explored is supposed to describe the forward search's extent. The search treats the goal as terminal, but summarizeSearch added up the out-degree of every reached function, the goal included. Supplying a goal dossier with callees therefore made explored.edges larger without the search doing any more work.

Problem and expected behavior

  • Smallest trigger: dossiers 0x1000 → 0x4000 and 0x4000 → 0x5000, with goal 0x4000. Before: explored: {nodes: 2, edges: 2, depth_reached: 1}. Expected: edges: 1.
  • Zero hops: start = goal = 0x1000, which has one callee. Before: edges: 1 at depth 0. Expected: edges: 0.
  • Violated invariant: explored.edges is the number of direct-callee edges examined while expanding non-goal functions. A terminal goal's outgoing relationships do not count.

Change and scope

  • inspectSearch in src/domain/callPath.ts is the one place that decides which functions get expanded. It now keeps explored as part of its state: the discovered function count, the edges examined at the expansion step, and the greatest discovery depth.
  • buildCallPath publishes that value directly. The second interpretation in summarizeSearch is gone, along with CallGraph.outDegree, whose only caller was summarizeSearch. The state reuses the schema-owned CallPathResult["explored"] type, so there is no parallel representation.
  • nodes and depth_reached mean the same as before. Discovered terminals, such as the goal and functions without Evidence, are still counted as nodes. They are now computed in the same pass instead of two array spreads over the reached map.
  • Not included: the inline path-enumeration bound in open PR #4984. That PR changes path enumeration and result fields, not search extent. The two PRs touch nearby lines in buildCallPath and around the removed summarizeSearch, so one of them will need a small textual rebase. The behaviors are independent.
  • Scope decision: not applicable. This is an ordinary fix within existing boundaries.

Contract and boundary impact

  • Semantic owner and earliest changed stage: the domain forward search (inspectSearch in src/domain/callPath.ts).
  • Contribution scope fit and linked maintainer decision: an ordinary bug fix. No decision is required.
  • CLI and MCP/tool-catalog contract: no schema or catalog change. Only the explored.edges value is corrected. buildCallPath is the only producer, and the shared build_call_path tool registration uses it.
  • Provider, bridge, target-format, or platform compatibility: none. This is offline reconstruction from supplied Evidence.
  • Evidence, artifact, provenance, or reconstruction contract: derived Evidence now carries an accurate search-extent count. Citations and limitations are unchanged.
  • Target selection, authority, upstream behavior, and cleanup details: not applicable.
  • Generated metadata, package, or installation impact: none.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated:
    • tests/boundary/mcp/callPathSearchExtentMcp.test.ts: a public MCP regression through the in-memory session harness, adapted from the issue repro. It checks the published explored for three cases: no goal dossier, a goal dossier with two callees (one points back to the start), and zero hops.
    • src/domain/callPath.test.ts: a new "call path search extent" table. Cases:
      • goal at depth 1 with a callee
      • zero hops from a start with callees
      • zero hops from a self-recursive start
      • self-recursive goal
      • depth-2 goal that calls back into and beyond the explored set
      • goal with several expanded callers
      • controls: an expanded cycle without the goal, an unreachable goal with its own dossier, and a reachable function without Evidence (unknown)
    • A separate test checks that explored is identical with and without the goal's dossier, across three graphs.
    • Two existing goal-terminal tests now assert the full explored object.
  • Base reproduction: the issue's exact test, run on origin/main at 63fe0eb5d, failed with goal has outgoing edge: expected 2 to be 1 and zero hops: expected 1 to be +0. With this change it passes (edges 1 and 0). On the unfixed source, the new tests fail in 9 cases (all goal-affected cases, plus the two updated existing tests), and the three control cases pass. The MCP regression fails with edges: 3 against an expected 1.
  • User-visible MCP output (goal dossier with outgoing edge, after the fix): {"status":"found","shortest_hops":1,"search_scope":{"exhaustive":true},"explored":{"nodes":2,"edges":1,"depth_reached":1},"limitations":[]}
  • Remaining proof gaps: the CLI adapter was not run separately. It reaches the same buildCallPath producer, so its correctness here is source-derived. No real provider was used because the bug is in offline reconstruction.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • node node_modules/vitest/vitest.mjs run tests/boundary/mcp/callPathGoalEdges.test.ts --maxWorkers 1 (the issue repro, not committed): failed on base as reported, passed with the fix.
  • node node_modules/vitest/vitest.mjs run src/domain/callPath.test.ts tests/boundary/mcp/{callPathSearchExtentMcp,functionCallDirectionsMcp,investigationAnalysisMcp,workflowUnknownIdentityMcp}.test.ts --maxWorkers 2: passed.
  • npm run test:focused -- tests/boundary/mcp/toolSchemaValidity.test.ts src/domain/callPath.test.ts tests/boundary/mcp/callPathSearchExtentMcp.test.ts: 3 files, 55 tests passed.
  • npm run check:fast: passed.
  • npm run format:check: passed.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. The schema is unchanged. explored.edges is lower only in cases where it was previously overcounted.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable (offline reconstruction from synthetic Evidence).
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no new I/O, processes or network access.

Author readiness

Human owner: @kaoru0822-kitauji

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/6256 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/22/2026 **Status:** ✅ Merged **Merged:** 10/22/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `fix/5906-call-path-explored-edges` --- ### 📝 Commits (1) - [`fb36e1b`](https://github.com/morluto/rea/commit/fb36e1b33abb01364012069b8950850f504c74ee) fix(call-path): count only expanded edges as explored ### 📊 Changes **3 files changed** (+241 additions, -25 deletions) <details> <summary>View changed files</summary> 📝 `src/domain/callPath.test.ts` (+118 -2) 📝 `src/domain/callPath.ts` (+19 -23) ➕ `tests/boundary/mcp/callPathSearchExtentMcp.test.ts` (+104 -0) </details> ### 📄 Description **What broke:** `build_call_path` reported a terminal goal's outgoing callees in `explored.edges`, even though the forward search never expands the goal. Zero-hop searches reported the start's callees too. **Fix:** The search now counts explored edges and depth as it expands each function. The post-hoc `summarizeSearch` out-degree sum and the unused `CallGraph.outDegree` helper are removed. **Risk:** Low. Only `explored.edges` changes, and only when the reached goal has supplied callees. Paths, `shortest_hops`, `status`, exhaustiveness, limitations and citations are unchanged, and so is the schema. ## Summary Fixes #5906. `explored` is supposed to describe the forward search's extent. The search treats the goal as terminal, but `summarizeSearch` added up the out-degree of every reached function, the goal included. Supplying a goal dossier with callees therefore made `explored.edges` larger without the search doing any more work. ## Problem and expected behavior - Smallest trigger: dossiers `0x1000 → 0x4000` and `0x4000 → 0x5000`, with goal `0x4000`. Before: `explored: {nodes: 2, edges: 2, depth_reached: 1}`. Expected: `edges: 1`. - Zero hops: start = goal = `0x1000`, which has one callee. Before: `edges: 1` at depth 0. Expected: `edges: 0`. - Violated invariant: `explored.edges` is the number of direct-callee edges examined while expanding non-goal functions. A terminal goal's outgoing relationships do not count. ## Change and scope - `inspectSearch` in `src/domain/callPath.ts` is the one place that decides which functions get expanded. It now keeps `explored` as part of its state: the discovered function count, the edges examined at the expansion step, and the greatest discovery depth. - `buildCallPath` publishes that value directly. The second interpretation in `summarizeSearch` is gone, along with `CallGraph.outDegree`, whose only caller was `summarizeSearch`. The state reuses the schema-owned `CallPathResult["explored"]` type, so there is no parallel representation. - `nodes` and `depth_reached` mean the same as before. Discovered terminals, such as the goal and functions without Evidence, are still counted as nodes. They are now computed in the same pass instead of two array spreads over the reached map. - Not included: the inline path-enumeration bound in open PR #4984. That PR changes path enumeration and result fields, not search extent. The two PRs touch nearby lines in `buildCallPath` and around the removed `summarizeSearch`, so one of them will need a small textual rebase. The behaviors are independent. - Scope decision: not applicable. This is an ordinary fix within existing boundaries. ## Contract and boundary impact - Semantic owner and earliest changed stage: the domain forward search (`inspectSearch` in `src/domain/callPath.ts`). - Contribution scope fit and linked maintainer decision: an ordinary bug fix. No decision is required. - CLI and MCP/tool-catalog contract: no schema or catalog change. Only the `explored.edges` value is corrected. `buildCallPath` is the only producer, and the shared `build_call_path` tool registration uses it. - Provider, bridge, target-format, or platform compatibility: none. This is offline reconstruction from supplied Evidence. - Evidence, artifact, provenance, or reconstruction contract: derived Evidence now carries an accurate search-extent count. Citations and limitations are unchanged. - Target selection, authority, upstream behavior, and cleanup details: not applicable. - Generated metadata, package, or installation impact: none. Expected effects: - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: - `tests/boundary/mcp/callPathSearchExtentMcp.test.ts`: a public MCP regression through the in-memory session harness, adapted from the issue repro. It checks the published `explored` for three cases: no goal dossier, a goal dossier with two callees (one points back to the start), and zero hops. - `src/domain/callPath.test.ts`: a new "call path search extent" table. Cases: - goal at depth 1 with a callee - zero hops from a start with callees - zero hops from a self-recursive start - self-recursive goal - depth-2 goal that calls back into and beyond the explored set - goal with several expanded callers - controls: an expanded cycle without the goal, an unreachable goal with its own dossier, and a reachable function without Evidence (`unknown`) - A separate test checks that `explored` is identical with and without the goal's dossier, across three graphs. - Two existing goal-terminal tests now assert the full `explored` object. - Base reproduction: the issue's exact test, run on `origin/main` at 63fe0eb5d, failed with `goal has outgoing edge: expected 2 to be 1` and `zero hops: expected 1 to be +0`. With this change it passes (`edges` 1 and 0). On the unfixed source, the new tests fail in 9 cases (all goal-affected cases, plus the two updated existing tests), and the three control cases pass. The MCP regression fails with `edges: 3` against an expected `1`. - User-visible MCP output (goal dossier with outgoing edge, after the fix): `{"status":"found","shortest_hops":1,"search_scope":{"exhaustive":true},"explored":{"nodes":2,"edges":1,"depth_reached":1},"limitations":[]}` - Remaining proof gaps: the CLI adapter was not run separately. It reaches the same `buildCallPath` producer, so its correctness here is source-derived. No real provider was used because the bug is in offline reconstruction. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `node node_modules/vitest/vitest.mjs run tests/boundary/mcp/callPathGoalEdges.test.ts --maxWorkers 1` (the issue repro, not committed): failed on base as reported, passed with the fix. - `node node_modules/vitest/vitest.mjs run src/domain/callPath.test.ts tests/boundary/mcp/{callPathSearchExtentMcp,functionCallDirectionsMcp,investigationAnalysisMcp,workflowUnknownIdentityMcp}.test.ts --maxWorkers 2`: passed. - `npm run test:focused -- tests/boundary/mcp/toolSchemaValidity.test.ts src/domain/callPath.test.ts tests/boundary/mcp/callPathSearchExtentMcp.test.ts`: 3 files, 55 tests passed. - `npm run check:fast`: passed. - `npm run format:check`: passed. ## Compatibility, safety, and release - Breaking changes or migration steps: none. The schema is unchanged. `explored.edges` is lower only in cases where it was previously overcounted. - Real Hopper/Ghidra, browser, or OS coverage: not applicable (offline reconstruction from synthetic Evidence). - Package or release metadata impact: none. - Security, privacy, process, or containment review: no new I/O, processes or network access. ## Author readiness Human owner: @kaoru0822-kitauji - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-11-01 21:46:21 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#7230
No description provided.