[PR #4045] [MERGED] fix(kuna): share and bound single-function decompile runs #6015

Closed
opened 2026-10-31 01:11:11 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

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

Base: main ← Head: fix/kuna-bound-single-function-runs


📝 Commits (1)

  • 8ae38fd fix(kuna): share and bound single-function decompile runs

📊 Changes

8 files changed (+420 additions, -42 deletions)

View changed files

📝 docs/kuna-provider.md (+1 -0)
📝 src/kuna/KunaProvider.ts (+16 -0)
➕ src/kuna/KunaRunSlots.ts (+60 -0)
📝 src/kuna/KunaSessionClient.ts (+186 -40)
📝 tests/boundary/providers/kuna/fakeKuna.ts (+25 -2)
📝 tests/boundary/providers/kuna/kunaInstallation.test.ts (+1 -0)
📝 tests/boundary/providers/kuna/kunaSessionFixture.ts (+2 -0)
➕ tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts (+129 -0)

📄 Description

Summary

While a Kuna project export builds, each pseudocode request started its own kuna decompile --addr process, and every one of them analyzes the whole image again. A cold batch_decompile therefore started one process per address, duplicates included, with no bound. Refs #1957.

Problem and expected behavior

Smallest trigger: cold cache, open_binary a thin Mach-O with provider_id: "kuna", then call batch_decompile with the same address 12 times. On main this starts 12 concurrent kuna decompile trees (decomp_dbg children at about 360 MiB each). They compete with the background export that would answer all of them. The invariant that breaks: an analysis request should not start an unbounded number of whole-image analyses on the host.

Expected: requests for one function share a single run. Concurrent runs are bounded by host capacity. A request still waiting for a slot is answered by the export if the export finishes first.

Change and scope

  • KunaSessionClient keeps one shared single-function run per entry address. Each caller's cancellation only ends that caller's wait. The run stops when its last waiter is cancelled, and that waiter gets the run's real outcome, including unproven cleanup (this keeps close_binary's retained-cleanup reporting intact).
  • New KunaRunSlots: one FIFO admission pool per KunaProvider (so per REA process), sized max(1, availableParallelism() - REA_KUNA_JOBS). Single-function runs only happen while the export's --jobs workers are running, so the cores those workers leave free are the real resource limit. Other providers already serialize requests through one engine session; Kuna is the only adapter that starts a process per request, so the bound lives in the Kuna adapter.
  • A queued request races its slot against this session's export. If the export finishes first, the request reads it and no Kuna process is started.
  • docs/kuna-provider.md documents the sharing, the bound and the hand-off to the export.

Not included: no new setting (the bound follows the host and REA_KUNA_JOBS); single-function results are not memoized after they finish; batch_decompile itself (all providers) still accepts duplicate addresses and returns each one.

Scope: an ordinary fix within the existing Kuna provider boundary; no maintainer scope decision applies.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Kuna provider adapter (src/kuna/KunaSessionClient.ts, new src/kuna/KunaRunSlots.ts, KunaProvider composition).
  • Contribution scope fit and linked maintainer decision: not applicable (fix inside the merged provider).
  • CLI and MCP/tool-catalog contract: unchanged. Answers keep the same raw_result.source shapes.
  • Provider, bridge, target-format, or platform compatibility: Kuna only. Same targets and platforms.
  • Evidence, artifact, provenance, or reconstruction contract: unchanged. A queued request answered by the export reports project-export with its file span, as any export answer does.
  • Target selection, authority, upstream behavior, and cleanup details: fewer Kuna processes. Cleanup ownership of the old per-request runs is kept: retained runs are still recorded and retried by close_binary.
  • 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.

(Process launches are the existing kuna runs; this change starts fewer of them.)

Evidence and regression coverage

  • Tests added or updated: tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts:

    • concurrent name and address requests share one run;
    • one caller cancelling leaves the shared run going for the others;
    • with one slot, the second run starts only after the first process exits (checked from the fake's recorded start and exit times);
    • a queued request is answered from the export while the only slot stays held.

    The fake Kuna now records start times, records exits for decompile, and supports decompileDelayMs.

  • Base reproduction: all 4 new tests fail on the base revision (git stash of the src/kuna changes): 3 assertion failures and one 30 s timeout for the export hand-off.

  • Real Kuna 1.833, macOS arm64 (10 cores), MCP batch_decompile on a cold cache, target zip thinned to arm64e (203 KiB, 339 functions). Same script before and after. Kuna family RSS was sampled every 250 ms, so short peaks can be missed:

    Workflow Build Wall CPU (user) Peak concurrent decomp_dbg Peak Kuna RSS
    12 × 0x100001278 main 18.8 s 155.9 s 12 4389 MiB
    12 × 0x100001278 this PR 8.0 s 16.6 s 1 1090 MiB
    12 distinct addresses main 9.0 s 35.8 s 12 2156 MiB
    12 distinct addresses this PR 9.2 s 35.9 s 9 2181 MiB

    Each row is one cold observation on a shared host, not a benchmark. For distinct small functions on this 10-core host the bound (9) barely changes anything. The bound matters on hosts with fewer cores and for larger batches. Response bytes are unchanged (2185399 and 568864); model tokens were not measured.

  • Real MCP cancellation: three of four concurrent requests for one heavy function were cancelled mid-run. The fourth still got its single-function answer. close_binary returned null, no kuna processes were left, and staging/ and trash/ were empty.

  • Remaining proof gaps: no Linux or x86-64 host run of this change; the queued-request hand-off to the export was exercised only with the fake Kuna (one real slot cannot be forced without a setting); multi-session sharing of the pool inside one process is source-derived only.

  • 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:focused -- tests/boundary/providers/kuna/*.test.ts: 93 passed (11 files), three consecutive runs.
  • Same 4 new tests on the base revision: 4 failed.
  • npm run check:fast: typecheck and lint passed.
  • npm run docs:check: passed.
  • REA_KUNA_COMMAND=… npm run verify:kuna (real Kuna 1.833, darwin/arm64): passed, process lineage verified, cleanup: "complete".
  • Real MCP measurements and the cancellation run above.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: real Kuna on macOS arm64 only.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: fewer owned Kuna process trees. Cancellation, timeouts and retained-cleanup ownership still go through runKunaCommand and close_binary.

Author readiness

Human owner:

  • 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/4045 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/15/2026 **Status:** ✅ Merged **Merged:** 10/15/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `fix/kuna-bound-single-function-runs` --- ### 📝 Commits (1) - [`8ae38fd`](https://github.com/morluto/rea/commit/8ae38fdebac258dbd648cb44b4c7b8ba9ac46022) fix(kuna): share and bound single-function decompile runs ### 📊 Changes **8 files changed** (+420 additions, -42 deletions) <details> <summary>View changed files</summary> 📝 `docs/kuna-provider.md` (+1 -0) 📝 `src/kuna/KunaProvider.ts` (+16 -0) ➕ `src/kuna/KunaRunSlots.ts` (+60 -0) 📝 `src/kuna/KunaSessionClient.ts` (+186 -40) 📝 `tests/boundary/providers/kuna/fakeKuna.ts` (+25 -2) 📝 `tests/boundary/providers/kuna/kunaInstallation.test.ts` (+1 -0) 📝 `tests/boundary/providers/kuna/kunaSessionFixture.ts` (+2 -0) ➕ `tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts` (+129 -0) </details> ### 📄 Description ## Summary While a Kuna project export builds, each pseudocode request started its own `kuna decompile --addr` process, and every one of them analyzes the whole image again. A cold `batch_decompile` therefore started one process per address, duplicates included, with no bound. Refs #1957. ## Problem and expected behavior Smallest trigger: cold cache, `open_binary` a thin Mach-O with `provider_id: "kuna"`, then call `batch_decompile` with the same address 12 times. On main this starts 12 concurrent `kuna decompile` trees (`decomp_dbg` children at about 360 MiB each). They compete with the background export that would answer all of them. The invariant that breaks: an analysis request should not start an unbounded number of whole-image analyses on the host. Expected: requests for one function share a single run. Concurrent runs are bounded by host capacity. A request still waiting for a slot is answered by the export if the export finishes first. ## Change and scope - `KunaSessionClient` keeps one shared single-function run per entry address. Each caller's cancellation only ends that caller's wait. The run stops when its last waiter is cancelled, and that waiter gets the run's real outcome, including unproven cleanup (this keeps `close_binary`'s retained-cleanup reporting intact). - New `KunaRunSlots`: one FIFO admission pool per `KunaProvider` (so per REA process), sized `max(1, availableParallelism() - REA_KUNA_JOBS)`. Single-function runs only happen while the export's `--jobs` workers are running, so the cores those workers leave free are the real resource limit. Other providers already serialize requests through one engine session; Kuna is the only adapter that starts a process per request, so the bound lives in the Kuna adapter. - A queued request races its slot against this session's export. If the export finishes first, the request reads it and no Kuna process is started. - `docs/kuna-provider.md` documents the sharing, the bound and the hand-off to the export. Not included: no new setting (the bound follows the host and `REA_KUNA_JOBS`); single-function results are not memoized after they finish; `batch_decompile` itself (all providers) still accepts duplicate addresses and returns each one. Scope: an ordinary fix within the existing Kuna provider boundary; no maintainer scope decision applies. ## Contract and boundary impact - Semantic owner and earliest changed stage: Kuna provider adapter (`src/kuna/KunaSessionClient.ts`, new `src/kuna/KunaRunSlots.ts`, `KunaProvider` composition). - Contribution scope fit and linked maintainer decision: not applicable (fix inside the merged provider). - CLI and MCP/tool-catalog contract: unchanged. Answers keep the same `raw_result.source` shapes. - Provider, bridge, target-format, or platform compatibility: Kuna only. Same targets and platforms. - Evidence, artifact, provenance, or reconstruction contract: unchanged. A queued request answered by the export reports `project-export` with its file span, as any export answer does. - Target selection, authority, upstream behavior, and cleanup details: fewer Kuna processes. Cleanup ownership of the old per-request runs is kept: retained runs are still recorded and retried by `close_binary`. - Generated metadata, package, or installation impact: none. Expected effects: - [x] Read existing files or captured data. - [x] 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. (Process launches are the existing `kuna` runs; this change starts fewer of them.) ## Evidence and regression coverage - Tests added or updated: `tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts`: - concurrent name and address requests share one run; - one caller cancelling leaves the shared run going for the others; - with one slot, the second run starts only after the first process exits (checked from the fake's recorded start and exit times); - a queued request is answered from the export while the only slot stays held. The fake Kuna now records start times, records exits for `decompile`, and supports `decompileDelayMs`. - Base reproduction: all 4 new tests fail on the base revision (`git stash` of the `src/kuna` changes): 3 assertion failures and one 30 s timeout for the export hand-off. - Real Kuna 1.833, macOS arm64 (10 cores), MCP `batch_decompile` on a cold cache, target `zip` thinned to arm64e (203 KiB, 339 functions). Same script before and after. Kuna family RSS was sampled every 250 ms, so short peaks can be missed: | Workflow | Build | Wall | CPU (user) | Peak concurrent `decomp_dbg` | Peak Kuna RSS | | --- | --- | --- | --- | --- | --- | | 12 × `0x100001278` | main | 18.8 s | 155.9 s | 12 | 4389 MiB | | 12 × `0x100001278` | this PR | 8.0 s | 16.6 s | 1 | 1090 MiB | | 12 distinct addresses | main | 9.0 s | 35.8 s | 12 | 2156 MiB | | 12 distinct addresses | this PR | 9.2 s | 35.9 s | 9 | 2181 MiB | Each row is one cold observation on a shared host, not a benchmark. For distinct small functions on this 10-core host the bound (9) barely changes anything. The bound matters on hosts with fewer cores and for larger batches. Response bytes are unchanged (2185399 and 568864); model tokens were not measured. - Real MCP cancellation: three of four concurrent requests for one heavy function were cancelled mid-run. The fourth still got its `single-function` answer. `close_binary` returned `null`, no `kuna` processes were left, and `staging/` and `trash/` were empty. - Remaining proof gaps: no Linux or x86-64 host run of this change; the queued-request hand-off to the export was exercised only with the fake Kuna (one real slot cannot be forced without a setting); multi-session sharing of the pool inside one process is source-derived only. - [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:focused -- tests/boundary/providers/kuna/*.test.ts`: 93 passed (11 files), three consecutive runs. - Same 4 new tests on the base revision: 4 failed. - `npm run check:fast`: typecheck and lint passed. - `npm run docs:check`: passed. - `REA_KUNA_COMMAND=… npm run verify:kuna` (real Kuna 1.833, darwin/arm64): passed, process lineage verified, `cleanup: "complete"`. - Real MCP measurements and the cancellation run above. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: real Kuna on macOS arm64 only. - Package or release metadata impact: none. - Security, privacy, process, or containment review: fewer owned Kuna process trees. Cancellation, timeouts and retained-cleanup ownership still go through `runKunaCommand` and `close_binary`. ## Author readiness Human owner: <!-- @GitHub-handle --> - [ ] 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](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-10-31 01:11:11 +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#6015
No description provided.