[PR #4169] [MERGED] fix(kuna): answer a repeated single-function request without re-running Kuna #6106

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

📋 Pull Request Information

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

Base: main ← Head: fix/kuna-reuse-single-function-answers


📝 Commits (2)

  • 3024108 fix(kuna): answer a repeated single-function request without re-running Kuna
  • 814525c test(kuna): pin that a kept text's hit refreshes its recency

📊 Changes

4 files changed (+155 additions, -5 deletions)

View changed files

📝 docs/kuna-provider.md (+1 -0)
📝 src/kuna/KunaSessionClient.ts (+54 -4)
📝 tests/boundary/providers/kuna/fakeKuna.ts (+4 -1)
📝 tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts (+96 -0)

📄 Description

Summary

While a Kuna session has no export reader, each procedure_pseudo_code request for a procedure starts a kuna decompile --addr run, which analyzes the whole image again. Concurrent requests share one run (#4045), but the session dropped the run as soon as it settled. A repeat request for the same procedure therefore paid for a full new analysis, and got back the same text with the same evidence ID. After a failed export this applies to every pseudocode request for the rest of the session. Refs #1957.

Problem and expected behavior

Observed on main, real Kuna 1.833, macOS arm64, /usr/bin/zip (arm64e slice). REA_KUNA_PROJECT_TIMEOUT_MS=2000 makes the export fail, and main is then requested three times over MCP:

procedure_assembly      2280 ms  capability_unavailable (export failed, retryable: false)
procedure_pseudo_code   7674 ms  193297 bytes  single-function
procedure_pseudo_code   7702 ms  193297 bytes  single-function   ← same evidence_id
procedure_pseudo_code   7732 ms  193297 bytes  single-function   ← same evidence_id
total 25.4 s, user CPU 25.3 s

The same happens while an export is still building. On Debian trixie linux/arm64, /usr/bin/bash's export takes about 40 s, and each repeat of main re-ran Kuna for about 1.5 s.

Expected: a completed single-function answer depends only on the session's target, its committed Kuna executable and the entry address. Within the session it is reusable work, and it remains so until the export can answer.

Change and scope

KunaSessionClient keeps each successful single-function text by entry address:

  • Bound: 16 MiB of UTF-8 text in all, plus a fixed per-entry charge, dropping the least recently used. This mirrors the session's exact-address xref memo.
  • Lookup: a request by name or by address resolves to the same entry, so both reuse one text.
  • What isn't kept: failed or cancelled runs, a run whose waiters all left, a run finishing after the export reader was adopted, or one finishing during close.
  • Lifetime: the kept texts are dropped when the export reader is adopted, so the export's text supersedes them exactly as before. They are also dropped on close.
  • Limitations unchanged: each answer still carries the single-function limitation, and the export-failure note when it applies. The project_export status is computed per request.

The fake Kuna gains decompilePadding so the budget can be tested. docs/kuna-provider.md describes the retention.

Not included: sharing these texts across sessions or servers. The export cache already does that once it finishes.

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

Contract and boundary impact

  • Semantic owner and earliest changed stage: KunaSessionClient, which owns single-function runs.
  • Contribution scope fit and linked maintainer decision: not applicable.
  • CLI and MCP/tool-catalog contract: unchanged. Answers are byte-identical; only their latency changes.
  • Provider, bridge, target-format, or platform compatibility: unchanged.
  • Evidence, artifact, provenance, or reconstruction contract: unchanged. A kept answer still reports raw_result.source.kind: "single-function" and its limitation.
  • Target selection, authority, upstream behavior, and cleanup details: no new processes or files. Kept texts are released on close.
  • 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.

(These are the existing effects of a Kuna decompile; the change removes repeated launches.)

Evidence and regression coverage

  • Tests added in kunaSingleFunctionRuns.test.ts:
    • a repeat by name or by address while the export builds runs Kuna once;
    • repeats after a failed export run once and keep the export-failure limitation;
    • a failed run is not kept;
    • past the 16 MiB budget the least recently used text is dropped, and a hit refreshes recency. With three 6 MiB texts (two fit), main, parse, main, broken, main, parse runs 0x1000, 0x1040, 0x1080, 0x1040. The test fails when the refresh on a hit is removed.
  • Base reproduction: with main's KunaSessionClient.ts, the three retention tests fail. The "failed run is not kept" test passes on both; it guards against keeping errors.
  • Real provider, after the fix:
Scenario Before After
zip, macOS, export failed: 3 × main 7674 / 7702 / 7732 ms 8630 / 3 / 5 ms
zip, macOS: whole run 25.4 s, 25.3 s user CPU 11.1 s, 11.0 s user CPU
zip, macOS: peak RSS 650 MB 648 MB (largest process: Kuna)
bash, Linux, export building: 3 × main ~1.5 s each 2500 / 2 / 2 ms
  • Answers match: the three zip answers are identical (193297 bytes each, same result SHA-256 prefix 74234e98afe7, source single-function).

  • Handoff to the export on bash: the repeats while building kept the same evidence ID and project_export.state: "building". After procedure_assembly waited 39.8 s for the export, main came from project-export, with its own text and no single-function limitation.

  • Tokens: response sizes are unchanged. The saving is time, CPU and Kuna processes (each run held about 360 MiB on a 200 KiB image in earlier measurements).

  • Remaining proof gaps:

    • Eviction is covered by the fake only; no real target produced texts near 16 MiB.
    • The Linux run used a copy of the change applied to an earlier main (40f73f62b), built in the container.
  • 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 (20 files) — 194 passed.
  • npm run test:focused -- tests/boundary/filesystem/directAnalysisPermissions.test.ts — 2 passed.
  • npm run check:fast — passed.
  • npm run docs:check — passed.
  • Real Kuna 1.833 over MCP, macOS arm64 (zip): table above.
  • Real Kuna 1.833 over MCP, Debian trixie linux/arm64 in Colima (bash): table above.
  • kunaProjectConcurrency.test.ts > preserves concurrent results and reclaims capacity (pin: true) failed under CPU stress (9 × yes), with main's KunaSessionClient.ts as well (3 of 3). It passes on an idle host. This is pre-existing flakiness, not caused by this change.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: real Kuna on macOS arm64 and linux/arm64.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: memory retained per session is bounded at 16 MiB of text plus entry charges and released on close. Fewer Kuna processes are launched.

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/4169 **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-reuse-single-function-answers` --- ### 📝 Commits (2) - [`3024108`](https://github.com/morluto/rea/commit/30241085c361836fba28fe17a26f96627a0eac79) fix(kuna): answer a repeated single-function request without re-running Kuna - [`814525c`](https://github.com/morluto/rea/commit/814525c5081ab7c1f03c16ef45753c0e848d9327) test(kuna): pin that a kept text's hit refreshes its recency ### 📊 Changes **4 files changed** (+155 additions, -5 deletions) <details> <summary>View changed files</summary> 📝 `docs/kuna-provider.md` (+1 -0) 📝 `src/kuna/KunaSessionClient.ts` (+54 -4) 📝 `tests/boundary/providers/kuna/fakeKuna.ts` (+4 -1) 📝 `tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts` (+96 -0) </details> ### 📄 Description ## Summary While a Kuna session has no export reader, each `procedure_pseudo_code` request for a procedure starts a `kuna decompile --addr` run, which analyzes the whole image again. Concurrent requests share one run (#4045), but the session dropped the run as soon as it settled. A repeat request for the same procedure therefore paid for a full new analysis, and got back the same text with the same evidence ID. After a failed export this applies to every pseudocode request for the rest of the session. Refs #1957. ## Problem and expected behavior Observed on main, real Kuna 1.833, macOS arm64, `/usr/bin/zip` (arm64e slice). `REA_KUNA_PROJECT_TIMEOUT_MS=2000` makes the export fail, and `main` is then requested three times over MCP: ``` procedure_assembly 2280 ms capability_unavailable (export failed, retryable: false) procedure_pseudo_code 7674 ms 193297 bytes single-function procedure_pseudo_code 7702 ms 193297 bytes single-function ← same evidence_id procedure_pseudo_code 7732 ms 193297 bytes single-function ← same evidence_id total 25.4 s, user CPU 25.3 s ``` The same happens while an export is still building. On Debian trixie linux/arm64, `/usr/bin/bash`'s export takes about 40 s, and each repeat of `main` re-ran Kuna for about 1.5 s. Expected: a completed single-function answer depends only on the session's target, its committed Kuna executable and the entry address. Within the session it is reusable work, and it remains so until the export can answer. ## Change and scope `KunaSessionClient` keeps each successful single-function text by entry address: - **Bound:** 16 MiB of UTF-8 text in all, plus a fixed per-entry charge, dropping the least recently used. This mirrors the session's exact-address xref memo. - **Lookup:** a request by name or by address resolves to the same entry, so both reuse one text. - **What isn't kept:** failed or cancelled runs, a run whose waiters all left, a run finishing after the export reader was adopted, or one finishing during close. - **Lifetime:** the kept texts are dropped when the export reader is adopted, so the export's text supersedes them exactly as before. They are also dropped on close. - **Limitations unchanged:** each answer still carries the single-function limitation, and the export-failure note when it applies. The `project_export` status is computed per request. The fake Kuna gains `decompilePadding` so the budget can be tested. `docs/kuna-provider.md` describes the retention. Not included: sharing these texts across sessions or servers. The export cache already does that once it finishes. Scope: performance fix within the existing Kuna provider boundary; no maintainer scope decision applies. ## Contract and boundary impact - Semantic owner and earliest changed stage: `KunaSessionClient`, which owns single-function runs. - Contribution scope fit and linked maintainer decision: not applicable. - CLI and MCP/tool-catalog contract: unchanged. Answers are byte-identical; only their latency changes. - Provider, bridge, target-format, or platform compatibility: unchanged. - Evidence, artifact, provenance, or reconstruction contract: unchanged. A kept answer still reports `raw_result.source.kind: "single-function"` and its limitation. - Target selection, authority, upstream behavior, and cleanup details: no new processes or files. Kept texts are released on close. - 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. (These are the existing effects of a Kuna decompile; the change removes repeated launches.) ## Evidence and regression coverage - Tests added in `kunaSingleFunctionRuns.test.ts`: - a repeat by name or by address while the export builds runs Kuna once; - repeats after a failed export run once and keep the export-failure limitation; - a failed run is not kept; - past the 16 MiB budget the least recently used text is dropped, and a hit refreshes recency. With three 6 MiB texts (two fit), `main`, `parse`, `main`, `broken`, `main`, `parse` runs `0x1000`, `0x1040`, `0x1080`, `0x1040`. The test fails when the refresh on a hit is removed. - Base reproduction: with main's `KunaSessionClient.ts`, the three retention tests fail. The "failed run is not kept" test passes on both; it guards against keeping errors. - Real provider, after the fix: | Scenario | Before | After | |---|---|---| | zip, macOS, export failed: 3 × `main` | 7674 / 7702 / 7732 ms | 8630 / 3 / 5 ms | | zip, macOS: whole run | 25.4 s, 25.3 s user CPU | 11.1 s, 11.0 s user CPU | | zip, macOS: peak RSS | 650 MB | 648 MB (largest process: Kuna) | | bash, Linux, export building: 3 × `main` | ~1.5 s each | 2500 / 2 / 2 ms | - **Answers match:** the three zip answers are identical (193297 bytes each, same `result` SHA-256 prefix `74234e98afe7`, source `single-function`). - **Handoff to the export on bash:** the repeats while building kept the same evidence ID and `project_export.state: "building"`. After `procedure_assembly` waited 39.8 s for the export, `main` came from `project-export`, with its own text and no single-function limitation. - Tokens: response sizes are unchanged. The saving is time, CPU and Kuna processes (each run held about 360 MiB on a 200 KiB image in earlier measurements). - Remaining proof gaps: - Eviction is covered by the fake only; no real target produced texts near 16 MiB. - The Linux run used a copy of the change applied to an earlier main (40f73f62b), built in the container. - [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` (20 files) — 194 passed. - `npm run test:focused -- tests/boundary/filesystem/directAnalysisPermissions.test.ts` — 2 passed. - `npm run check:fast` — passed. - `npm run docs:check` — passed. - Real Kuna 1.833 over MCP, macOS arm64 (zip): table above. - Real Kuna 1.833 over MCP, Debian trixie linux/arm64 in Colima (bash): table above. - `kunaProjectConcurrency.test.ts > preserves concurrent results and reclaims capacity (pin: true)` failed under CPU stress (9 × `yes`), with main's `KunaSessionClient.ts` as well (3 of 3). It passes on an idle host. This is pre-existing flakiness, not caused by this change. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: real Kuna on macOS arm64 and linux/arm64. - Package or release metadata impact: none. - Security, privacy, process, or containment review: memory retained per session is bounded at 16 MiB of text plus entry charges and released on close. Fewer Kuna processes are launched. ## 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:55 +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#6106
No description provided.