[GH-ISSUE #7647] [Bug] IDA discovery reports available for an unusable headless workspace parent #4570

Closed
opened 2026-10-31 00:50:53 +00:00 by bsakaguchi · 0 comments
Owner

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

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

With a headless IDA registration selecting either a missing directory or a regular file as workspaceRoot, both node scripts/rea.mjs providers --json and node scripts/rea.mjs capabilities --json exit 0 and report the IDA candidate as available, with code: null and reason: null. The candidate also advertises its inventory, pseudocode, and search operation groups as available; the separate procedure_callers limitation remains unavailable.

In the same environment, node scripts/rea.mjs doctor --provider ida --json exits 1, reports healthy: false and IDA available: false, and identifies the unusable workspaceRoot in the private_workspace check with code: "invalid_configuration".

An existing writable directory and an omitted workspaceRoot both pass scoped doctor and remain available in discovery. No target was opened. The configured IDA command and package runners were logging fixtures; none was invoked.

Reproduction

  1. From the built baseline checkout with Node.js v24.22.1 first on PATH, run this inline fixture. It isolates host state, prevents real package-runner execution, compares both invalid parents and two controls, and removes its temporary files:
python3 - <<'PYREPRO'
import json, os, pathlib, shutil, subprocess, tempfile
node = shutil.which("node")
assert node and subprocess.check_output([node, "--version"], text=True).strip() == "v24.22.1"
root = pathlib.Path(tempfile.mkdtemp(prefix="rea-ida-verify-")).resolve()
try:
    for name in ("bin", "home", "config", "cache", "data", "state", "temp", "writable"):
        (root / name).mkdir()
    fixture = '#!/bin/sh\necho fixture >> "$FIXTURE_LOG"\nexit 0\n'
    for name in ("npx", "npm", "bun", "fixture-ida"):
        path = root / "bin" / name
        path.write_text(fixture)
        path.chmod(0o700)
    env = {"PATH": str(root / "bin") + ":" + str(pathlib.Path(node).parent) + ":/usr/bin:/bin:/usr/sbin:/sbin",
           "HOME": str(root / "home"), "TMPDIR": str(root / "temp"),
           "FIXTURE_LOG": str(root / "calls.log"), "npm_config_prefix": str(root / "npm-global")}
    for key, directory in (("CONFIG", "config"), ("CACHE", "cache"), ("DATA", "data"), ("STATE", "state")):
        env["XDG_" + key + "_HOME"] = str(root / directory)
    (root / "file").write_text("fixture")
    for case in ("missing", "file", "writable", "omitted"):
        config = {"mode": "headless", "command": str(root / "bin" / "fixture-ida"), "args": []}
        if case != "omitted":
            config["workspaceRoot"] = str(root / case)
        registration = root / "ida.json"
        registration.write_text(json.dumps(config))
        env["REA_IDA_MCP_CONFIG"] = str(registration)
        for args in (["providers", "--json"], ["capabilities", "--json"], ["doctor", "--provider", "ida", "--json"]):
            run = subprocess.run([node, "scripts/rea.mjs", *args], env=env,
                                 capture_output=True, text=True, timeout=45)
            assert run.stderr == "", run.stderr
            result = json.loads(run.stdout)
            if args[0] == "doctor":
                ida = next(x for x in result["providerInspections"] if x["id"] == "ida")
                check = next(x for x in ida["checks"] if x["name"] == "private_workspace")
                print(case, "doctor", run.returncode, "healthy", result["healthy"],
                      "available", ida["available"], "workspace", check["ok"], check["code"])
                if not check["ok"]:
                    print(check["detail"].replace(str(root), "<fixture>"))
            else:
                ida = next(x for x in result["analysis_provider_candidates"] if x["provider"]["id"] == "ida")
                availability = ida["availability"]
                print(case, args[0], run.returncode, availability["status"], availability["code"], availability["reason"])
    print("fixture commands invoked:", (root / "calls.log").exists())
finally:
    shutil.rmtree(root)
PYREPRO
  1. Compare the discovery lines with the doctor lines for each parent. Both invalid inputs reproduced in two independent passes, including the inline fixture above. Both control inputs passed in each pass.

Expected behavior

Both discovery commands should mark IDA unavailable when its configured local workspace parent is missing or is a regular file, and retain an actionable reason naming workspaceRoot, consistent with scoped doctor. The CLI guide presents these commands as the way to list available providers and supported operations. The IDA guide requires an existing absolute workspace parent and describes checking that local path during readiness. This check needs neither a target nor a live IDA connection; target support can remain unknown.

Impact and scope

P2: consumers of the machine-readable discovery result receive a false local-readiness result and available operation groups for these unusable configurations. Doctor supplies the missing diagnosis, so callers must perform that additional check to detect the problem reliably.

The workaround is available: run scoped doctor and choose an existing writable directory or omit workspaceRoot. Both alternatives restored passing local readiness in the fixture controls. This verifies readiness metadata only; actual upstream compatibility, target opening, analysis, MCP discovery, Windows behavior, and production frequency were not tested. No regression range was established.

REA version

Exact node scripts/rea.mjs --version stdout:

6.8.0

Source baseline: 65f1c447344a423fa2785847dbcea1e9a9e2454a.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1, arm64. No IDA engine or upstream MCP server was used.

Target and artifact context

Headless stdio registration JSON containing a scratch logging command, empty args, and an absolute workspaceRoot pointing to a missing directory, a regular file, or a writable directory. The fourth control omits that setting. No binary, IDB, or target data is required.

Interface

CLI

Earliest failure stage

Setup or provider admission

Relevant output or evidence

Reduced stdout from the inline reproduction; None represents JSON null in this Python summary:

missing providers 0 available None None
missing capabilities 0 available None None
missing doctor 1 healthy False available False workspace False invalid_configuration
IDA workspaceRoot <fixture>/missing is not a usable directory: ENOENT: no such file or directory, stat '<fixture>/missing'
file providers 0 available None None
file capabilities 0 available None None
file doctor 1 healthy False available False workspace False invalid_configuration
IDA workspaceRoot <fixture>/file is not a directory.
writable providers 0 available None None
writable capabilities 0 available None None
writable doctor 0 healthy True available True workspace True None
omitted providers 0 available None None
omitted capabilities 0 available None None
omitted doctor 0 healthy True available True workspace True None
fixture commands invoked: False

All command stderr streams were empty. Separate inspection of the discovery JSON confirmed the available operation groups described above. Doctor's failing check recommends selecting an existing directory REA can create entries in, or omitting workspaceRoot.

Contract references at the baseline: docs/cli.md:412-423 describes discovery; docs/cli.md:446-450 distinguishes local readiness from target support; docs/ida-provider.md:176-183 specifies the existing absolute parent and native path readiness checks; docs/ida-provider.md:249-250 describes the pre-worker configuration refusal.

Source-derived explanation: src/ida/IdaProvider.ts:178-246 parses the registration but only checks platform private-workspace support for headless readiness before returning available. src/ida/IdaConfiguration.ts:40-44 checks absolute syntax, not directory usability. In contrast, src/ida/IdaDoctor.ts:33-43,69-114 checks the configured parent's existence, directory type, and write/search access. These source observations explain the public CLI disagreement; they do not establish a real-engine run.

Typed outcome or recovery expected

An unavailable IDA candidate with a configuration reason naming workspaceRoot and its existing usable-directory requirement. Discovery should stop advertising its operation groups as locally ready until that configuration passes, while preserving their capability descriptions and independent limitations.

Analysis or suspected cause

Hypothesis: the selected-parent validation used by doctor has not been included in the provider readiness snapshot consumed by discovery. The snapshot checks host support for private workspaces but does not inspect the caller's configured parent.

Additional context

Read-only duplicate searches covered open and closed issues, open/closed/merged PRs, and issue comments using workspace, readiness, providers, and capabilities terms. No matching report or fix for this discovery projection was found.

Issue #5559 concerns doctor's former false readiness result; its related closed PR #5591 changes doctor checks. Issue #5830 concerns analysis-time classification and recovery; its closure describes preserving configuration errors during workspace allocation/session startup. The current reproduction confirms doctor has the useful local check, while discovery still disagrees. Neither earlier report claims that this discovery projection was fixed, so this report does not assert a regression of those fixes.

The merged registration-recovery PR #5773 concerns already-failed registration parsing and retained recovery facts. Here registration syntax succeeds, and the configured parent is the unusable input.

Originally created by @coygeek on GitHub (Oct 28, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7647 ### 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 With a headless IDA registration selecting either a missing directory or a regular file as `workspaceRoot`, both `node scripts/rea.mjs providers --json` and `node scripts/rea.mjs capabilities --json` exit 0 and report the IDA candidate as `available`, with `code: null` and `reason: null`. The candidate also advertises its inventory, pseudocode, and search operation groups as available; the separate `procedure_callers` limitation remains unavailable. In the same environment, `node scripts/rea.mjs doctor --provider ida --json` exits 1, reports `healthy: false` and IDA `available: false`, and identifies the unusable `workspaceRoot` in the `private_workspace` check with `code: "invalid_configuration"`. An existing writable directory and an omitted `workspaceRoot` both pass scoped doctor and remain available in discovery. No target was opened. The configured IDA command and package runners were logging fixtures; none was invoked. ### Reproduction 1. From the built baseline checkout with Node.js v24.22.1 first on PATH, run this inline fixture. It isolates host state, prevents real package-runner execution, compares both invalid parents and two controls, and removes its temporary files: ```sh python3 - <<'PYREPRO' import json, os, pathlib, shutil, subprocess, tempfile node = shutil.which("node") assert node and subprocess.check_output([node, "--version"], text=True).strip() == "v24.22.1" root = pathlib.Path(tempfile.mkdtemp(prefix="rea-ida-verify-")).resolve() try: for name in ("bin", "home", "config", "cache", "data", "state", "temp", "writable"): (root / name).mkdir() fixture = '#!/bin/sh\necho fixture >> "$FIXTURE_LOG"\nexit 0\n' for name in ("npx", "npm", "bun", "fixture-ida"): path = root / "bin" / name path.write_text(fixture) path.chmod(0o700) env = {"PATH": str(root / "bin") + ":" + str(pathlib.Path(node).parent) + ":/usr/bin:/bin:/usr/sbin:/sbin", "HOME": str(root / "home"), "TMPDIR": str(root / "temp"), "FIXTURE_LOG": str(root / "calls.log"), "npm_config_prefix": str(root / "npm-global")} for key, directory in (("CONFIG", "config"), ("CACHE", "cache"), ("DATA", "data"), ("STATE", "state")): env["XDG_" + key + "_HOME"] = str(root / directory) (root / "file").write_text("fixture") for case in ("missing", "file", "writable", "omitted"): config = {"mode": "headless", "command": str(root / "bin" / "fixture-ida"), "args": []} if case != "omitted": config["workspaceRoot"] = str(root / case) registration = root / "ida.json" registration.write_text(json.dumps(config)) env["REA_IDA_MCP_CONFIG"] = str(registration) for args in (["providers", "--json"], ["capabilities", "--json"], ["doctor", "--provider", "ida", "--json"]): run = subprocess.run([node, "scripts/rea.mjs", *args], env=env, capture_output=True, text=True, timeout=45) assert run.stderr == "", run.stderr result = json.loads(run.stdout) if args[0] == "doctor": ida = next(x for x in result["providerInspections"] if x["id"] == "ida") check = next(x for x in ida["checks"] if x["name"] == "private_workspace") print(case, "doctor", run.returncode, "healthy", result["healthy"], "available", ida["available"], "workspace", check["ok"], check["code"]) if not check["ok"]: print(check["detail"].replace(str(root), "<fixture>")) else: ida = next(x for x in result["analysis_provider_candidates"] if x["provider"]["id"] == "ida") availability = ida["availability"] print(case, args[0], run.returncode, availability["status"], availability["code"], availability["reason"]) print("fixture commands invoked:", (root / "calls.log").exists()) finally: shutil.rmtree(root) PYREPRO ``` 2. Compare the discovery lines with the doctor lines for each parent. Both invalid inputs reproduced in two independent passes, including the inline fixture above. Both control inputs passed in each pass. ### Expected behavior Both discovery commands should mark IDA unavailable when its configured local workspace parent is missing or is a regular file, and retain an actionable reason naming `workspaceRoot`, consistent with scoped doctor. The CLI guide presents these commands as the way to list available providers and supported operations. The IDA guide requires an existing absolute workspace parent and describes checking that local path during readiness. This check needs neither a target nor a live IDA connection; target support can remain unknown. ### Impact and scope P2: consumers of the machine-readable discovery result receive a false local-readiness result and available operation groups for these unusable configurations. Doctor supplies the missing diagnosis, so callers must perform that additional check to detect the problem reliably. The workaround is available: run scoped doctor and choose an existing writable directory or omit `workspaceRoot`. Both alternatives restored passing local readiness in the fixture controls. This verifies readiness metadata only; actual upstream compatibility, target opening, analysis, MCP discovery, Windows behavior, and production frequency were not tested. No regression range was established. ### REA version Exact `node scripts/rea.mjs --version` stdout: ```text 6.8.0 ``` Source baseline: `65f1c447344a423fa2785847dbcea1e9a9e2454a`. ### Runtime and platform Node.js v24.22.1; macOS 27.0.1, arm64. No IDA engine or upstream MCP server was used. ### Target and artifact context Headless stdio registration JSON containing a scratch logging command, empty `args`, and an absolute `workspaceRoot` pointing to a missing directory, a regular file, or a writable directory. The fourth control omits that setting. No binary, IDB, or target data is required. ### Interface CLI ### Earliest failure stage Setup or provider admission ### Relevant output or evidence Reduced stdout from the inline reproduction; `None` represents JSON null in this Python summary: ```text missing providers 0 available None None missing capabilities 0 available None None missing doctor 1 healthy False available False workspace False invalid_configuration IDA workspaceRoot <fixture>/missing is not a usable directory: ENOENT: no such file or directory, stat '<fixture>/missing' file providers 0 available None None file capabilities 0 available None None file doctor 1 healthy False available False workspace False invalid_configuration IDA workspaceRoot <fixture>/file is not a directory. writable providers 0 available None None writable capabilities 0 available None None writable doctor 0 healthy True available True workspace True None omitted providers 0 available None None omitted capabilities 0 available None None omitted doctor 0 healthy True available True workspace True None fixture commands invoked: False ``` All command stderr streams were empty. Separate inspection of the discovery JSON confirmed the available operation groups described above. Doctor's failing check recommends selecting an existing directory REA can create entries in, or omitting `workspaceRoot`. Contract references at the baseline: `docs/cli.md:412-423` describes discovery; `docs/cli.md:446-450` distinguishes local readiness from target support; `docs/ida-provider.md:176-183` specifies the existing absolute parent and native path readiness checks; `docs/ida-provider.md:249-250` describes the pre-worker configuration refusal. Source-derived explanation: `src/ida/IdaProvider.ts:178-246` parses the registration but only checks platform private-workspace support for headless readiness before returning available. `src/ida/IdaConfiguration.ts:40-44` checks absolute syntax, not directory usability. In contrast, `src/ida/IdaDoctor.ts:33-43,69-114` checks the configured parent's existence, directory type, and write/search access. These source observations explain the public CLI disagreement; they do not establish a real-engine run. ### Typed outcome or recovery expected An unavailable IDA candidate with a configuration reason naming `workspaceRoot` and its existing usable-directory requirement. Discovery should stop advertising its operation groups as locally ready until that configuration passes, while preserving their capability descriptions and independent limitations. ### Analysis or suspected cause Hypothesis: the selected-parent validation used by doctor has not been included in the provider readiness snapshot consumed by discovery. The snapshot checks host support for private workspaces but does not inspect the caller's configured parent. ### Additional context Read-only duplicate searches covered open and closed issues, open/closed/merged PRs, and issue comments using workspace, readiness, providers, and capabilities terms. No matching report or fix for this discovery projection was found. [Issue #5559](https://github.com/morluto/rea/issues/5559) concerns doctor's former false readiness result; its related [closed PR #5591](https://github.com/morluto/rea/pull/5591) changes doctor checks. [Issue #5830](https://github.com/morluto/rea/issues/5830) concerns analysis-time classification and recovery; its closure describes preserving configuration errors during workspace allocation/session startup. The current reproduction confirms doctor has the useful local check, while discovery still disagrees. Neither earlier report claims that this discovery projection was fixed, so this report does not assert a regression of those fixes. The [merged registration-recovery PR #5773](https://github.com/morluto/rea/pull/5773) concerns already-failed registration parsing and retained recovery facts. Here registration syntax succeeds, and the configured parent is the unusable input.
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#4570
No description provided.