[PR #6260] [MERGED] feat(managed): project call and field-access edges into the application graph #7236

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

📋 Pull Request Information

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

Base: main ← Head: feat/4056-managed-graph-calls


📝 Commits (1)

  • a6473eb feat(managed): project call and field-access edges into the application graph

📊 Changes

9 files changed (+492 additions, -19 deletions)

View changed files

📝 docs/javascript-application-graph.md (+2 -2)
📝 docs/managed-code-analysis.md (+26 -0)
📝 src/contracts/managed/managedWorkflowToolContracts.ts (+1 -1)
📝 src/domain/javascript/javascriptApplicationGraphSchemas.ts (+1 -0)
📝 src/domain/managed/managedApplicationGraph.ts (+39 -9)
📝 src/domain/managed/managedApplicationGraphCoverage.ts (+41 -2)
📝 src/domain/managed/managedApplicationGraphNodes.ts (+1 -1)
➕ src/domain/managed/managedApplicationGraphRelations.ts (+200 -0)
📝 tests/composition/analysis-sessions/managedApplicationGraph.test.ts (+181 -4)

📄 Description

What broke: project_managed_application_graph dropped the call_edges and field_accesses that member Evidence already decodes. The graph was a containment tree that could not answer "who calls this P/Invoke wrapper", yet it reported complete-within-inputs.
Fix: in-module calls and field accesses become calls / accesses edges, aggregated per source and target with site counts. Every other site is counted by reason in limitations, and coverage becomes partial.
Risk: low to medium. Graphs get larger (+23% on a real assembly). Most real graphs now report partial, which is honest. The graph vocabulary gains one relation, accesses.

Summary

Fixes #4056. The plan was posted on the issue before implementation. On LibreHardwareMonitor 0.9.6, the graph now answers the issue's questions directly. NvApi.GetDelegate → NvAPI32_QueryInterface and NvAPI64_QueryInterface. The callers of the NtQuerySystemInformation P/Invoke declaration are CpuLoad.GetWindowsTimes and a wrapper overload.

Problem and expected behavior

  • A call_edges row with a method-def target, a call/callvirt/newobj/jmp opcode, and both endpoints present becomes a calls edge between managed-method nodes.
  • A field_accesses row whose field and method nodes exist becomes an accesses edge to the managed-field node.
  • Each edge represents every site between one source and one target. Properties are {kind, site_count, opcodes}, plus access (read/write/address) for field accesses. Edge evidence links the member Evidence.
  • Everything else is counted and named in limitations rather than dropped silently:
    • MemberRef targets and MethodSpec (generic instantiation) targets
    • unknown targets
    • ldftn/ldvirtftn function-pointer loads (references, not calls)
    • recursive self-calls (the graph schema forbids self-loops; LibreHardwareMonitor has 3)
    • MemberRef fields
    • sites whose node is absent, e.g. under type selection
  • Graph coverage becomes partial with omitted_count equal to the omitted sites when nothing else is incomplete. When other omissions exist (partial input, selection), omitted_count stays null. summary gains calls and field_accesses.

Change and scope

  • New src/domain/managed/managedApplicationGraphRelations.ts, which aggregates edges and counts omissions. The omission types and their total live in managedApplicationGraphCoverage.ts, so the modules have no import cycle.
  • accesses is added to the provider-neutral relation vocabulary. No existing relation fits data reads and writes, and traces need no allowlist change.
  • Historical graph Evidence (no counters) still parses, with calls: null and field_accesses: null. Null means "not projected", not zero. This follows the existing nullable().default(null) pattern for older Evidence. A reconstruction-candidates test caught that older Evidence would otherwise stop parsing.
  • Not included: nodes for external MemberRef members, which would make BCL/API callers queryable but need a new node kind. Also not included: resolving MethodSpec to its generic MethodDef. Both are named on the issue as possible follow-ups.

Contract and boundary impact

  • Semantic owner and earliest changed stage: src/domain/managed projection, using member Evidence that already exists. No new parsing.

  • Contribution scope fit and linked maintainer decision: static, read-only. The direction was agreed in the issue thread, and the proposal was posted there.

  • CLI and MCP/tool-catalog contract: same inputs. Additive output: the summary.calls and summary.field_accesses counters, the new edge relations, and coverage that is now partial with named omissions. The tool description mentions the new edges.

  • Provider/platform compatibility: none.

  • Evidence/provenance: edges carry exact member-Evidence links and managed-static-analysis observed/exact evidence, the same as the existing member-derived nodes.

  • Generated metadata: no committed outputs; metadata:check passes.

  • Read existing files or captured data.

Evidence and regression coverage

  • Tests added or updated (tests/composition/analysis-sessions/managedApplicationGraph.test.ts), using a real CIL fixture with two methods that share a body: call/callvirt/self call/ldftn/MemberRef call/ldfld/stfld/ldflda.
    • Exact calls edges: one direction aggregates {call:1, callvirt:1}, the reverse has {call:1}. Also exact accesses edges, summary, evidence IDs, omitted_count: 7, and the limitation text.
    • Endpoints removed as by selection produce missing-node counts.
    • Graph Evidence from before this change parses with null counters.
    • The legacy-selection test now uses an in-module-only body, because the default fixture's MemberRef call is now (correctly) a counted omission.
  • Base reproduction (origin/main c537c52c9): contains: 12240, imports: 97, no calls, complete-within-inputs, the same as the issue reports.

Real-data before/after: LibreHardwareMonitor 0.9.6 LibreHardwareMonitorLib.dll

The DLL's SHA-256 is 6ebc1943…f66bf0 and the zip's is 086d9f1b…c001, both matching the issue. Inputs were artifact, members and native-boundary Evidence. Runs used rea project-managed-application-graph g-in.json --format json, 3 warm runs each, on macOS arm64.

origin/main this branch
edges 12,337 (contains 12,240, imports 97) 19,451 (+ calls 3,076, accesses 4,038)
coverage complete-within-inputs partial, omitted_count 12,668
limitations (new) none …call sites are omitted: 12013 member-reference, 395 generic method-instantiation, 163 function-pointer (ldftn/ldvirtftn), 3 recursive self-call. and …field-access sites are omitted: 94 member-reference.
wall s 5.31 / 5.47 / 5.35 6.21 / 6.09 / 6.27
user CPU s 6.78 / 7.09 / 6.76 7.98 / 7.92 / 7.95
peak RSS MB 1022 / 991 / 950 969 / 1050 / 1071
output bytes 35,804,598 44,148,378 (+23%)

Aggregation keeps the graph proportional to distinct relationships rather than sites: 10,651 in-module call sites become 3,076 edges, and 7,424 field-access sites become 4,038 edges.

  • Remaining proof gaps:

    • Output token count of the full envelope was not measured. Byte size is reported instead.
    • The extra cost (~0.8 s wall, ~1.2 s CPU) is per-edge graph identity hashing and validation, the same per-edge cost the existing contains edges pay.
  • 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

  • npm run test:local -- tests/composition/analysis-sessions/managedApplicationGraph.test.ts src/contracts/workflowInputContracts.test.ts src/application/ReconstructionObligationCandidates.test.ts src/contracts/toolEffects.test.ts src/domain/javascript/javascriptApplicationGraph.test.ts: passed. Before the historical-schema fix, 2 reconstruction-candidate tests failed (they pass on main); after the fix the graph and reconstruction files pass, 21 + 6 tests.
  • npm run test:focused -- tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts tests/boundary/mcp/toolSchemaValidity.test.ts: 32 passed.
  • npm run check:fast: passed.
  • npm run format:check: passed.
  • npm run docs:check: passed.
  • npm run metadata:check: passed.
  • Real-data runs and spot checks: see above.

Compatibility, safety, and release

  • Breaking changes or migration steps: none intended. Consumers that treated complete-within-inputs as the normal state will now see partial for graphs with external calls, with the omitted classes named in limitations.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable (managed static parsing only).
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no execution, loading, or network access. The DLL is a public MPL-2.0 release, used only locally and not committed.

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/6260 **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:** `feat/4056-managed-graph-calls` --- ### 📝 Commits (1) - [`a6473eb`](https://github.com/morluto/rea/commit/a6473eb431945c3499b5ca8a2012b79e8c2297be) feat(managed): project call and field-access edges into the application graph ### 📊 Changes **9 files changed** (+492 additions, -19 deletions) <details> <summary>View changed files</summary> 📝 `docs/javascript-application-graph.md` (+2 -2) 📝 `docs/managed-code-analysis.md` (+26 -0) 📝 `src/contracts/managed/managedWorkflowToolContracts.ts` (+1 -1) 📝 `src/domain/javascript/javascriptApplicationGraphSchemas.ts` (+1 -0) 📝 `src/domain/managed/managedApplicationGraph.ts` (+39 -9) 📝 `src/domain/managed/managedApplicationGraphCoverage.ts` (+41 -2) 📝 `src/domain/managed/managedApplicationGraphNodes.ts` (+1 -1) ➕ `src/domain/managed/managedApplicationGraphRelations.ts` (+200 -0) 📝 `tests/composition/analysis-sessions/managedApplicationGraph.test.ts` (+181 -4) </details> ### 📄 Description > **What broke:** `project_managed_application_graph` dropped the `call_edges` and `field_accesses` that member Evidence already decodes. The graph was a containment tree that could not answer "who calls this P/Invoke wrapper", yet it reported `complete-within-inputs`. > **Fix:** in-module calls and field accesses become `calls` / `accesses` edges, aggregated per source and target with site counts. Every other site is counted by reason in `limitations`, and coverage becomes `partial`. > **Risk:** low to medium. Graphs get larger (+23% on a real assembly). Most real graphs now report `partial`, which is honest. The graph vocabulary gains one relation, `accesses`. ## Summary Fixes #4056. The plan was posted on the issue before implementation. On LibreHardwareMonitor 0.9.6, the graph now answers the issue's questions directly. `NvApi.GetDelegate` → `NvAPI32_QueryInterface` and `NvAPI64_QueryInterface`. The callers of the `NtQuerySystemInformation` P/Invoke declaration are `CpuLoad.GetWindowsTimes` and a wrapper overload. ## Problem and expected behavior - A `call_edges` row with a `method-def` target, a `call`/`callvirt`/`newobj`/`jmp` opcode, and both endpoints present becomes a `calls` edge between `managed-method` nodes. - A `field_accesses` row whose field and method nodes exist becomes an `accesses` edge to the `managed-field` node. - Each edge represents every site between one source and one target. Properties are `{kind, site_count, opcodes}`, plus `access` (`read`/`write`/`address`) for field accesses. Edge evidence links the member Evidence. - Everything else is counted and named in `limitations` rather than dropped silently: - MemberRef targets and MethodSpec (generic instantiation) targets - unknown targets - `ldftn`/`ldvirtftn` function-pointer loads (references, not calls) - recursive self-calls (the graph schema forbids self-loops; LibreHardwareMonitor has 3) - MemberRef fields - sites whose node is absent, e.g. under type selection - Graph coverage becomes `partial` with `omitted_count` equal to the omitted sites when nothing else is incomplete. When other omissions exist (partial input, selection), `omitted_count` stays `null`. `summary` gains `calls` and `field_accesses`. ## Change and scope - New `src/domain/managed/managedApplicationGraphRelations.ts`, which aggregates edges and counts omissions. The omission types and their total live in `managedApplicationGraphCoverage.ts`, so the modules have no import cycle. - `accesses` is added to the provider-neutral relation vocabulary. No existing relation fits data reads and writes, and traces need no allowlist change. - Historical graph Evidence (no counters) still parses, with `calls: null` and `field_accesses: null`. Null means "not projected", not zero. This follows the existing `nullable().default(null)` pattern for older Evidence. A reconstruction-candidates test caught that older Evidence would otherwise stop parsing. - Not included: nodes for external MemberRef members, which would make BCL/API callers queryable but need a new node kind. Also not included: resolving MethodSpec to its generic MethodDef. Both are named on the issue as possible follow-ups. ## Contract and boundary impact - Semantic owner and earliest changed stage: `src/domain/managed` projection, using member Evidence that already exists. No new parsing. - Contribution scope fit and linked maintainer decision: static, read-only. The direction was agreed in the issue thread, and the proposal was posted there. - CLI and MCP/tool-catalog contract: same inputs. Additive output: the `summary.calls` and `summary.field_accesses` counters, the new edge relations, and coverage that is now `partial` with named omissions. The tool description mentions the new edges. - Provider/platform compatibility: none. - Evidence/provenance: edges carry exact member-Evidence links and `managed-static-analysis` observed/exact evidence, the same as the existing member-derived nodes. - Generated metadata: no committed outputs; `metadata:check` passes. - [x] Read existing files or captured data. ## Evidence and regression coverage - Tests added or updated (`tests/composition/analysis-sessions/managedApplicationGraph.test.ts`), using a real CIL fixture with two methods that share a body: `call`/`callvirt`/self `call`/`ldftn`/MemberRef `call`/`ldfld`/`stfld`/`ldflda`. - Exact `calls` edges: one direction aggregates `{call:1, callvirt:1}`, the reverse has `{call:1}`. Also exact `accesses` edges, `summary`, evidence IDs, `omitted_count: 7`, and the limitation text. - Endpoints removed as by selection produce `missing-node` counts. - Graph Evidence from before this change parses with null counters. - The legacy-selection test now uses an in-module-only body, because the default fixture's MemberRef call is now (correctly) a counted omission. - Base reproduction (origin/main `c537c52c9`): `contains: 12240, imports: 97`, no `calls`, `complete-within-inputs`, the same as the issue reports. ### Real-data before/after: LibreHardwareMonitor 0.9.6 `LibreHardwareMonitorLib.dll` The DLL's SHA-256 is `6ebc1943…f66bf0` and the zip's is `086d9f1b…c001`, both matching the issue. Inputs were artifact, members and native-boundary Evidence. Runs used `rea project-managed-application-graph g-in.json --format json`, 3 warm runs each, on macOS arm64. | | origin/main | this branch | |---|---|---| | edges | 12,337 (`contains` 12,240, `imports` 97) | 19,451 (+ `calls` 3,076, `accesses` 4,038) | | coverage | `complete-within-inputs` | `partial`, `omitted_count` 12,668 | | limitations (new) | none | `…call sites are omitted: 12013 member-reference, 395 generic method-instantiation, 163 function-pointer (ldftn/ldvirtftn), 3 recursive self-call.` and `…field-access sites are omitted: 94 member-reference.` | | wall s | 5.31 / 5.47 / 5.35 | 6.21 / 6.09 / 6.27 | | user CPU s | 6.78 / 7.09 / 6.76 | 7.98 / 7.92 / 7.95 | | peak RSS MB | 1022 / 991 / 950 | 969 / 1050 / 1071 | | output bytes | 35,804,598 | 44,148,378 (+23%) | Aggregation keeps the graph proportional to distinct relationships rather than sites: 10,651 in-module call sites become 3,076 edges, and 7,424 field-access sites become 4,038 edges. - Remaining proof gaps: - Output token count of the full envelope was not measured. Byte size is reported instead. - The extra cost (~0.8 s wall, ~1.2 s CPU) is per-edge graph identity hashing and validation, the same per-edge cost the existing `contains` edges pay. - [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 - `npm run test:local -- tests/composition/analysis-sessions/managedApplicationGraph.test.ts src/contracts/workflowInputContracts.test.ts src/application/ReconstructionObligationCandidates.test.ts src/contracts/toolEffects.test.ts src/domain/javascript/javascriptApplicationGraph.test.ts`: passed. Before the historical-schema fix, 2 reconstruction-candidate tests failed (they pass on main); after the fix the graph and reconstruction files pass, 21 + 6 tests. - `npm run test:focused -- tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts tests/boundary/mcp/toolSchemaValidity.test.ts`: 32 passed. - `npm run check:fast`: passed. - `npm run format:check`: passed. - `npm run docs:check`: passed. - `npm run metadata:check`: passed. - Real-data runs and spot checks: see above. ## Compatibility, safety, and release - Breaking changes or migration steps: none intended. Consumers that treated `complete-within-inputs` as the normal state will now see `partial` for graphs with external calls, with the omitted classes named in limitations. - Real Hopper/Ghidra, browser, or OS coverage: not applicable (managed static parsing only). - Package or release metadata impact: none. - Security, privacy, process, or containment review: no execution, loading, or network access. The DLL is a public MPL-2.0 release, used only locally and not committed. ## 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:23 +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#7236
No description provided.