[GH-ISSUE #7399] [Bug] binary_session advertises default address context queries for unsupported artifact targets #4405

Open
opened 2026-10-31 00:49:33 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 27, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7399

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Provider or capability discovery

Observed behavior

After opening a generated XML plist, I observed binary_session {} advertise inspect_address_context as available: true, reason: "available", with null remediation. Calling it immediately with {"address":"0x1000"} failed with capability_unavailable, category unsupported_provider, and selection_reason: "target_unsupported" for current_document. The same session correctly marked address_name, read_bytes, and xrefs unavailable for the target. Both summary and complete discovery showed this mismatch.

Reproduction

  1. In an empty working directory outside the source checkout, make the built baseline checkout accessible as the relative directory rea (a directory symlink is sufficient). Use Node 24.22.1 on PATH. Save the following standard-library client; it creates its own plist and isolated HOME/XDG/TMPDIR directories inline and removes them after closing the server.
cat > repro.py <<'PYTHON'
import json, os, pathlib, subprocess, sys, tempfile, threading
repo = pathlib.Path(sys.argv[1]).resolve()
with tempfile.TemporaryDirectory(dir=".", prefix="context-probe-") as root:
    work = pathlib.Path(root).resolve()
    (work / "sample.plist").write_text('<?xml version="1.0" encoding="UTF-8"?><plist version="1.0"><dict><key>CFBundleName</key><string>Example</string></dict></plist>')
    env = {"PATH": os.environ["PATH"], "REA_LOG_LEVEL": "silent"}
    for key in ("HOME", "XDG_CONFIG_HOME", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_STATE_HOME", "TMPDIR"):
        env[key] = str(work / key.lower())
        pathlib.Path(env[key]).mkdir()
    p = subprocess.Popen(["node", str(repo / "scripts/rea.mjs"), "--mcp"], cwd=work, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)
    deadline = threading.Timer(45, p.kill)
    deadline.start()
    def request(i, method, params):
        p.stdin.write(json.dumps({"jsonrpc": "2.0", "id": i, "method": method, "params": params}) + "\n")
        p.stdin.flush()
        for line in p.stdout:
            message = json.loads(line)
            if message.get("id") == i:
                if "error" in message:
                    raise RuntimeError(message["error"])
                return message["result"]
        raise RuntimeError("server exited before reply")
    def call(i, name, args):
        reply = request(i, "tools/call", {"name": name, "arguments": args})
        body = reply.get("structuredContent") or json.loads(reply["content"][0]["text"])
        return reply, body
    try:
        request(1, "initialize", {"protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": {"name": "address-context-probe", "version": "1"}})
        p.stdin.write(json.dumps({"jsonrpc": "2.0", "method": "notifications/initialized"}) + "\n")
        p.stdin.flush()
        reply, opened = call(2, "open_binary", {"path": str(work / "sample.plist")})
        print("open_result", json.dumps({k: opened["result"][k] for k in ("kind", "format")}))
        for i, detail in ((3, "summary"), (4, "complete")):
            _, body = call(i, "binary_session", {} if detail == "summary" else {"detail": detail})
            status = body["result"]
            print("status", json.dumps({"detail": detail, "open": status["open"]}))
            for name in ("inspect_address_context", "address_name", "read_bytes", "xrefs", "get_navigation_context"):
                group = next(g for g in status["tool_availability"]["groups"] if name in g["names"])
                print("availability", json.dumps({"detail": detail, "name": name, **{k: group[k] for k in ("available", "reason", "remediation")}}))
        reply, body = call(5, "inspect_address_context", {"address": "0x1000"})
        error = body["error"]
        print("address_call", json.dumps({"isError": reply.get("isError", False), "code": error["code"], "category": error["category"], "operation": error["details"]["operation"], "selection_reason": error["details"]["selection_reason"]}))
        reply, body = call(6, "inspect_address_context", {"address": "0x1000", "document": "sample.plist"})
        print("explicit_document_call", json.dumps({"isError": reply.get("isError", False), "body": body}))
    finally:
        p.stdin.close()
        try:
            p.wait(timeout=10)
        except subprocess.TimeoutExpired:
            p.terminate()
            p.wait(timeout=10)
        deadline.cancel()
        print("server_exit", p.returncode)
PYTHON
python3 repro.py rea
  1. Observe these exact relevant output lines; unrelated tools, the complete-projection repetition, and the explicit-document control are omitted here.
open_result {"kind": "artifact", "format": "plist"}
status {"detail": "summary", "open": true}
availability {"detail": "summary", "name": "inspect_address_context", "available": true, "reason": "available", "remediation": null}
availability {"detail": "summary", "name": "address_name", "available": false, "reason": "target_unsupported", "remediation": "Inventory or extract a native executable, then call open_binary on that executable."}
address_call {"isError": true, "code": "capability_unavailable", "category": "unsupported_provider", "operation": "current_document", "selection_reason": "target_unsupported"}
server_exit 0
  1. Repeat python3 repro.py rea for a fresh process and fixture. The default-request mismatch reproduced in all three independent attempts; both discovery projections were checked on every attempt and all servers exited 0. The explicit-document control in the displayed client ran in the latter two attempts and returned a successful result with every facet unavailable in both.

Expected behavior

The “Identity and discovery” section of docs/mcp-contracts.md says to use result.tool_availability “to choose a callable operation for the current target, provider, host, and negotiated client capabilities.” It also requires clients to preserve each group's availability, reason and remediation. The default address query should therefore expose its unsupported-target prerequisite before the call, with target_unsupported and target-appropriate recovery, rather than unconditional readiness. If explicit-document calls remain callable because they return typed unavailable facets, discovery should distinguish that mode from the default document-resolution path.

Impact and scope

The observed readiness signal led to a predictable failed default request on the generated plist, in three of three fresh sessions. Recovery arrives only in the invocation error; discovery gives no remediation for this tool. A caller can use the observed open_binary result's artifact kind to avoid assuming default native-document support, or handle the typed refusal instead of relying solely on the ready entry. Other target kinds, other hosts, downstream client frequency, and successful native-provider analysis were not tested. The explicit-document control returned all five facets unavailable; it did not establish useful native analysis of the plist.

REA version

6.8.0 (source commit a905a71e31)

Runtime and platform

Node 24.22.1; macOS 27.0.1 arm64; production stdio MCP with isolated HOME/XDG/TMPDIR.

Target and artifact context

Generated XML plist containing one CFBundleName string, admitted by open_binary as kind: "artifact", format: "plist"; no deep provider was bound.

Interface

MCP

Earliest failure stage

Transport or result projection

Relevant output or evidence

The output under Reproduction was observed through the actual node scripts/rea.mjs --mcp process. The subsequent explicit-document request {"address":"0x1000","document":"sample.plist"} returned isError: false; its name, procedure, comment, inline_comment, and bookmarks facets each had state: "unavailable" and a provider-selection reason containing target_unsupported.

The expected discovery contract is at docs/mcp-contracts.md:614-623. The request is valid under src/contracts/sessionToolSchemas.ts:24-27. Source-only (not executed as a separate source test): src/server/registerSessionTools.ts:256-276 registers this public tool through admitted analysis; src/application/AnalysisContextQueries.ts:81-91 resolves the document before querying facets, and src/application/AnalysisDocument.ts:29-40 executes current_document when document is omitted. src/application/binary/BinarySessionExecution.ts:69-79 returns the unbound-route selection error before provider execution. src/application/binary/AnalysisProviderRegistry.ts:548-573 derives target_unsupported from the candidate target-support facts.

Typed outcome or recovery expected

For the tested artifact and default request, discovery should identify unavailable default document resolution and target-opening or extraction recovery before invocation. Preserve typed unavailable-facet results for any intentionally supported explicit-document mode. Proposed restoration checks: repeat the default mismatch probe, retain the explicit-document control, and verify an appropriate supported executable/provider case separately. No patch or successful restoration was tested.

Analysis or suspected cause

Source-only (not executed as a source-level regression test): src/application/CapabilityInventory.ts:405-410 accepts a workflow decision before considering target availability, and its session-kind workflow at line 644 returns available unconditionally. Even when reached, the non-executable target-kind guard at lines 770-779 covers only official-proxy and enhanced. The address contract at src/contracts/sessionToolContracts.ts:179-183 is a session tool. Its missing active-target flag explains the separate no-target issue but does not explain this already-open artifact by itself. The default call fails before facet conversion, while an explicit document bypasses that resolution and permits unavailable facets.

Additional context

Open #7310 concerns the same tool with no target open and a target_unavailable response. Its acceptance criterion is missing-target recovery; this report concerns an open artifact and a target-kind refusal. Open PRs #7337 and #7340 consult the existing target-availability rule and add an active-target requirement, but their inspected patches do not extend session tools' unsupported-kind handling. Neither was merged when checked.

Related (closed):
#1166, #5729, and #5731 corrected other inspection operations or missing-target navigation; their different acceptance cases and fixes leave this default address-context failure directly reproducible on the baseline.

npm view rea-agents version returned 6.8.0, matching the baseline package version. The supplied compiled runtime was used without rebuilding. NOT_ENOUGH_INFO: the first affected revision and a last known good version were not established.

Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7399 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Provider or capability discovery ### Observed behavior After opening a generated XML plist, I observed `binary_session {}` advertise `inspect_address_context` as `available: true`, `reason: "available"`, with null remediation. Calling it immediately with `{"address":"0x1000"}` failed with `capability_unavailable`, category `unsupported_provider`, and `selection_reason: "target_unsupported"` for `current_document`. The same session correctly marked `address_name`, `read_bytes`, and `xrefs` unavailable for the target. Both summary and complete discovery showed this mismatch. ### Reproduction 1. In an empty working directory outside the source checkout, make the built baseline checkout accessible as the relative directory `rea` (a directory symlink is sufficient). Use Node 24.22.1 on PATH. Save the following standard-library client; it creates its own plist and isolated HOME/XDG/TMPDIR directories inline and removes them after closing the server. ```sh cat > repro.py <<'PYTHON' import json, os, pathlib, subprocess, sys, tempfile, threading repo = pathlib.Path(sys.argv[1]).resolve() with tempfile.TemporaryDirectory(dir=".", prefix="context-probe-") as root: work = pathlib.Path(root).resolve() (work / "sample.plist").write_text('<?xml version="1.0" encoding="UTF-8"?><plist version="1.0"><dict><key>CFBundleName</key><string>Example</string></dict></plist>') env = {"PATH": os.environ["PATH"], "REA_LOG_LEVEL": "silent"} for key in ("HOME", "XDG_CONFIG_HOME", "XDG_DATA_HOME", "XDG_CACHE_HOME", "XDG_STATE_HOME", "TMPDIR"): env[key] = str(work / key.lower()) pathlib.Path(env[key]).mkdir() p = subprocess.Popen(["node", str(repo / "scripts/rea.mjs"), "--mcp"], cwd=work, env=env, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True) deadline = threading.Timer(45, p.kill) deadline.start() def request(i, method, params): p.stdin.write(json.dumps({"jsonrpc": "2.0", "id": i, "method": method, "params": params}) + "\n") p.stdin.flush() for line in p.stdout: message = json.loads(line) if message.get("id") == i: if "error" in message: raise RuntimeError(message["error"]) return message["result"] raise RuntimeError("server exited before reply") def call(i, name, args): reply = request(i, "tools/call", {"name": name, "arguments": args}) body = reply.get("structuredContent") or json.loads(reply["content"][0]["text"]) return reply, body try: request(1, "initialize", {"protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": {"name": "address-context-probe", "version": "1"}}) p.stdin.write(json.dumps({"jsonrpc": "2.0", "method": "notifications/initialized"}) + "\n") p.stdin.flush() reply, opened = call(2, "open_binary", {"path": str(work / "sample.plist")}) print("open_result", json.dumps({k: opened["result"][k] for k in ("kind", "format")})) for i, detail in ((3, "summary"), (4, "complete")): _, body = call(i, "binary_session", {} if detail == "summary" else {"detail": detail}) status = body["result"] print("status", json.dumps({"detail": detail, "open": status["open"]})) for name in ("inspect_address_context", "address_name", "read_bytes", "xrefs", "get_navigation_context"): group = next(g for g in status["tool_availability"]["groups"] if name in g["names"]) print("availability", json.dumps({"detail": detail, "name": name, **{k: group[k] for k in ("available", "reason", "remediation")}})) reply, body = call(5, "inspect_address_context", {"address": "0x1000"}) error = body["error"] print("address_call", json.dumps({"isError": reply.get("isError", False), "code": error["code"], "category": error["category"], "operation": error["details"]["operation"], "selection_reason": error["details"]["selection_reason"]})) reply, body = call(6, "inspect_address_context", {"address": "0x1000", "document": "sample.plist"}) print("explicit_document_call", json.dumps({"isError": reply.get("isError", False), "body": body})) finally: p.stdin.close() try: p.wait(timeout=10) except subprocess.TimeoutExpired: p.terminate() p.wait(timeout=10) deadline.cancel() print("server_exit", p.returncode) PYTHON python3 repro.py rea ``` 2. Observe these exact relevant output lines; unrelated tools, the complete-projection repetition, and the explicit-document control are omitted here. ```text open_result {"kind": "artifact", "format": "plist"} status {"detail": "summary", "open": true} availability {"detail": "summary", "name": "inspect_address_context", "available": true, "reason": "available", "remediation": null} availability {"detail": "summary", "name": "address_name", "available": false, "reason": "target_unsupported", "remediation": "Inventory or extract a native executable, then call open_binary on that executable."} address_call {"isError": true, "code": "capability_unavailable", "category": "unsupported_provider", "operation": "current_document", "selection_reason": "target_unsupported"} server_exit 0 ``` 3. Repeat `python3 repro.py rea` for a fresh process and fixture. The default-request mismatch reproduced in all three independent attempts; both discovery projections were checked on every attempt and all servers exited 0. The explicit-document control in the displayed client ran in the latter two attempts and returned a successful result with every facet unavailable in both. ### Expected behavior The “Identity and discovery” section of `docs/mcp-contracts.md` says to use `result.tool_availability` “to choose a callable operation for the current target, provider, host, and negotiated client capabilities.” It also requires clients to preserve each group's availability, reason and remediation. The default address query should therefore expose its unsupported-target prerequisite before the call, with `target_unsupported` and target-appropriate recovery, rather than unconditional readiness. If explicit-document calls remain callable because they return typed unavailable facets, discovery should distinguish that mode from the default document-resolution path. ### Impact and scope The observed readiness signal led to a predictable failed default request on the generated plist, in three of three fresh sessions. Recovery arrives only in the invocation error; discovery gives no remediation for this tool. A caller can use the observed `open_binary` result's artifact kind to avoid assuming default native-document support, or handle the typed refusal instead of relying solely on the ready entry. Other target kinds, other hosts, downstream client frequency, and successful native-provider analysis were not tested. The explicit-document control returned all five facets unavailable; it did not establish useful native analysis of the plist. ### REA version 6.8.0 (source commit a905a71e31f71457cb21fd056b234f872126974e) ### Runtime and platform Node 24.22.1; macOS 27.0.1 arm64; production stdio MCP with isolated HOME/XDG/TMPDIR. ### Target and artifact context Generated XML plist containing one CFBundleName string, admitted by `open_binary` as `kind: "artifact"`, `format: "plist"`; no deep provider was bound. ### Interface MCP ### Earliest failure stage Transport or result projection ### Relevant output or evidence The output under Reproduction was observed through the actual `node scripts/rea.mjs --mcp` process. The subsequent explicit-document request `{"address":"0x1000","document":"sample.plist"}` returned `isError: false`; its `name`, `procedure`, `comment`, `inline_comment`, and `bookmarks` facets each had `state: "unavailable"` and a provider-selection reason containing `target_unsupported`. The expected discovery contract is at `docs/mcp-contracts.md:614-623`. The request is valid under `src/contracts/sessionToolSchemas.ts:24-27`. Source-only (not executed as a separate source test): `src/server/registerSessionTools.ts:256-276` registers this public tool through admitted analysis; `src/application/AnalysisContextQueries.ts:81-91` resolves the document before querying facets, and `src/application/AnalysisDocument.ts:29-40` executes `current_document` when `document` is omitted. `src/application/binary/BinarySessionExecution.ts:69-79` returns the unbound-route selection error before provider execution. `src/application/binary/AnalysisProviderRegistry.ts:548-573` derives `target_unsupported` from the candidate target-support facts. ### Typed outcome or recovery expected For the tested artifact and default request, discovery should identify unavailable default document resolution and target-opening or extraction recovery before invocation. Preserve typed unavailable-facet results for any intentionally supported explicit-document mode. Proposed restoration checks: repeat the default mismatch probe, retain the explicit-document control, and verify an appropriate supported executable/provider case separately. No patch or successful restoration was tested. ### Analysis or suspected cause Source-only (not executed as a source-level regression test): `src/application/CapabilityInventory.ts:405-410` accepts a workflow decision before considering target availability, and its session-kind workflow at line 644 returns available unconditionally. Even when reached, the non-executable target-kind guard at lines 770-779 covers only `official-proxy` and `enhanced`. The address contract at `src/contracts/sessionToolContracts.ts:179-183` is a session tool. Its missing active-target flag explains the separate no-target issue but does not explain this already-open artifact by itself. The default call fails before facet conversion, while an explicit document bypasses that resolution and permits unavailable facets. ### Additional context Open [#7310](https://github.com/morluto/rea/issues/7310) concerns the same tool with no target open and a `target_unavailable` response. Its acceptance criterion is missing-target recovery; this report concerns an open artifact and a target-kind refusal. Open PRs #7337 and #7340 consult the existing target-availability rule and add an active-target requirement, but their inspected patches do not extend session tools' unsupported-kind handling. Neither was merged when checked. Related (closed): [#1166](https://github.com/morluto/rea/issues/1166), [#5729](https://github.com/morluto/rea/issues/5729), and [#5731](https://github.com/morluto/rea/issues/5731) corrected other inspection operations or missing-target navigation; their different acceptance cases and fixes leave this default address-context failure directly reproducible on the baseline. `npm view rea-agents version` returned 6.8.0, matching the baseline package version. The supplied compiled runtime was used without rebuilding. NOT_ENOUGH_INFO: the first affected revision and a last known good version were not established.
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#4405
No description provided.