[GH-ISSUE #7630] [Bug] CLI keyed-archive selection errors identify the bundle argument instead of archive #4555

Closed
opened 2026-10-31 00:50:45 +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/7630

Before submitting

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

Area

CLI

Observed behavior

With a valid static app bundle, node scripts/rea.mjs inspect-keyed-archive BUNDLE absent.plist --json correctly refuses the nonexistent second argument but identifies the failed input as ["path"]. Command help names the first argument path and the second archive, so the structured issue identifies the valid bundle argument.

Selecting ../sample.plist produces the same field mismatch for a non-canonical relative path. Selecting sample.plist against the unchanged bundle succeeds. Both refusals return exit 1 and invalid_request / invalid_input; this report concerns their field attribution.

Reproduction

  1. Use Node.js v24.22.1 and the built source revision below. Run this block from the checkout root. It creates an inert app directory and XML plists, isolates REA's HOME/XDG/temp directories, invokes the public CLI, prints reduced results and deletes its fixture automatically.
python3 -I - <<'PYTHON'
import json, os, pathlib, plistlib, subprocess, tempfile
with tempfile.TemporaryDirectory() as directory:
    root = pathlib.Path(directory)
    app = root / 'StaticArchive.app'
    app.mkdir()
    (app / 'Info.plist').write_bytes(plistlib.dumps({
        'CFBundleIdentifier': 'org.example.staticarchive',
        'CFBundleName': 'StaticArchive', 'CFBundleExecutable': 'StaticArchive'}))
    (app / 'sample.plist').write_bytes(plistlib.dumps({
        '$archiver': 'NSKeyedArchiver', '$version': 100000,
        '$objects': ['$null', {'NS.string': 'sample'}],
        '$top': {'root': {'CF$UID': 1}}}))
    env = {k: v for k, v in os.environ.items()
           if not k.startswith(('REA_', 'HOPPER_', 'GHIDRA_', 'JAVA_'))}
    for key in ['HOME', 'XDG_CONFIG_HOME', 'XDG_CACHE_HOME', 'XDG_DATA_HOME',
                'XDG_STATE_HOME', 'XDG_RUNTIME_DIR', 'TMPDIR']:
        location = root / key.lower()
        location.mkdir()
        env[key] = str(location)
    for repeat in (1, 2):
        for selection in ['sample.plist', 'absent.plist', '../sample.plist']:
            call = subprocess.run(['node', 'scripts/rea.mjs', 'inspect-keyed-archive',
                str(app), selection, '--json'], env=env, text=True,
                capture_output=True, timeout=60)
            result = json.loads(call.stdout)
            if selection == 'sample.plist':
                assert call.returncode == 0 and result['evidence_id']
                print(json.dumps({'repeat': repeat, 'selection': selection,
                    'exit': call.returncode,
                    'archive_path': result['normalized_result']['archive_path'],
                    'total_objects': result['normalized_result']['total_objects']}))
            else:
                assert call.returncode == 1 and result['code'] == 'invalid_request'
                print(json.dumps({'repeat': repeat, 'selection': selection,
                    'exit': call.returncode, 'code': result['code'],
                    'issues': result['details']['issues']}))
PYTHON
  1. Compare the successful sample.plist selection with each refusal. Both invalid selections return issues[0].path: ["path"]; the declared name of the changed argument is archive.

The exact reduced block above was executed: each selection ran twice. A preceding independent driver also ran each selection twice with the same result. All four controls succeeded, and each invalid input reproduced in all four calls.

Expected behavior

Attribute invalid bundle-relative selections to ["archive"], preserving the existing refusal, reason, useful message and input-contract recovery. The command's executed help and schema distinguish the bundle path from the relative archive argument. The native investigation guide documents the second argument, and the input-error design contract requires caller field/index paths. Exact source references are listed in the evidence section.

Impact and scope

P3: incorrect diagnostic metadata for correctly rejected requests. The message still identifies the missing or non-canonical selection, and changing the second argument to the valid archive succeeds with the same bundle. A caller can use that message and command help to identify the correct input. No failed agent recovery, data loss, invalid successful decode or target mutation was observed.

Executed scope is the public CLI on macOS for missing and non-canonical second arguments. Directory selections, Unicode-equivalent ambiguity, other platforms and live MCP behavior were not reproduced. A direct reader or MCP caller uses the field name path; that interface does not have the CLI's argument-name mismatch.

REA version

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

6.8.0

Source commit: 65f1c447344a423fa2785847dbcea1e9a9e2454a.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1, build 26A434; arm64. No external analysis provider.

Target and artifact context

A synthetic root-layout StaticArchive.app with XML Info.plist and a two-object XML NSKeyedArchiver sample.plist. Its executable name is declared in metadata, but no executable file exists. No application, GUI or Mach-O target was opened.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

The executed reduced reproduction printed these records on its first pass; the second pass differed only in repeat:

{"repeat":1,"selection":"sample.plist","exit":0,"archive_path":"sample.plist","total_objects":2}
{"repeat":1,"selection":"absent.plist","exit":1,"code":"invalid_request","issues":[{"path":["path"],"reason":"invalid_value","message":"No regular file exists at absent.plist in the active app bundle."}]}
{"repeat":1,"selection":"../sample.plist","exit":1,"code":"invalid_request","issues":[{"path":["path"],"reason":"invalid_format","message":"Archive path must be a canonical relative bundle path without empty, '.', or '..' segments: ../sample.plist"}]}

The original driver also captured category: "invalid_input", retryable: false and recovery: {"kind":"inspect_input_contract","operation":"inspect_keyed_archive"}. Its stderr was empty. Both control calls returned Evidence identifying sample.plist and two objects.

Executed help:

Usage: rea inspect-keyed-archive <path> [archive] [options]

Arguments:
  path     Foundation plist archive or Apple app bundle
  archive  Relative archive path when the target is a bundle

At the stated baseline, docs/tool-design.md:348-351 requires caller field/index paths for expected input failures. docs/native-investigation.md:611-614 documents the bundle-relative archive as the second CLI argument. docs/cli.md:943-945 points callers to command help for the declared argument shape; it does not itself contain this command's two-argument synopsis or an explicit issue-path definition.

Production references: src/cli/artifactCommands.ts:29-64 declares the two arguments and forwards args.archive as provider parameter path. src/artifacts/apple/KeyedArchiveReader.ts:340-390 rejects these selections; archivePathError at lines 477-483 always identifies ["path"]. Existing tests/boundary/filesystem/keyedArchiveSelection.test.ts:37-78 exercises direct reader input {path}, where that field name is appropriate; it does not verify CLI argument attribution.

Typed outcome or recovery expected

Keep invalid_request, category invalid_input, the current invalid-value/invalid-format reasons and inspect_input_contract recovery. For these CLI second-argument refusals, set details.issues[0].path to ["archive"] so it names the field exposed by the CLI schema.

Analysis or suspected cause

Source-derived explanation: the CLI renames a provider input while forwarding its diagnostic unchanged. The provider's path means the selected archive, whereas CLI path means the bundle and CLI archive means the selection. The reader's fixed field name is correct at its own boundary; it needs the caller's argument name when projected through this adapter. The executed output is consistent with that mapping loss.

Additional context

Read-only searches covered open/closed issues, open/closed/merged PRs and issue comments. Closed #6119 concerns malformed environment capture payloads blaming a valid operation. Open #7523 concerns a different MCP tool's source-file field mapping. Neither closes this CLI archive-argument mismatch.

Merged PR #1070 changed selection mistakes to invalid_request issues using provider path; it did not change the CLI adapter. The present output retains that correct classification and exposes a remaining argument-name mismatch. Merged PR #1226 addresses intact files of the wrong archive kind; closed PR #1314 addresses Unicode inventory selection. Their closure signals differ from this report.

Open #7377, closed #6064 and closed #4923 were also checked and concern empty target admission, read-denial classification and selected-plist/default classification respectively. No duplicate of this CLI attribution defect was found. The npm registry returned 6.8.0 as the latest published rea-agents version. No known-good version or regression claim is established.

Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7630 ### 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 CLI ### Observed behavior With a valid static app bundle, `node scripts/rea.mjs inspect-keyed-archive BUNDLE absent.plist --json` correctly refuses the nonexistent second argument but identifies the failed input as `["path"]`. Command help names the first argument `path` and the second `archive`, so the structured issue identifies the valid bundle argument. Selecting `../sample.plist` produces the same field mismatch for a non-canonical relative path. Selecting `sample.plist` against the unchanged bundle succeeds. Both refusals return exit 1 and `invalid_request` / `invalid_input`; this report concerns their field attribution. ### Reproduction 1. Use Node.js v24.22.1 and the built source revision below. Run this block from the checkout root. It creates an inert app directory and XML plists, isolates REA's HOME/XDG/temp directories, invokes the public CLI, prints reduced results and deletes its fixture automatically. ```sh python3 -I - <<'PYTHON' import json, os, pathlib, plistlib, subprocess, tempfile with tempfile.TemporaryDirectory() as directory: root = pathlib.Path(directory) app = root / 'StaticArchive.app' app.mkdir() (app / 'Info.plist').write_bytes(plistlib.dumps({ 'CFBundleIdentifier': 'org.example.staticarchive', 'CFBundleName': 'StaticArchive', 'CFBundleExecutable': 'StaticArchive'})) (app / 'sample.plist').write_bytes(plistlib.dumps({ '$archiver': 'NSKeyedArchiver', '$version': 100000, '$objects': ['$null', {'NS.string': 'sample'}], '$top': {'root': {'CF$UID': 1}}})) env = {k: v for k, v in os.environ.items() if not k.startswith(('REA_', 'HOPPER_', 'GHIDRA_', 'JAVA_'))} for key in ['HOME', 'XDG_CONFIG_HOME', 'XDG_CACHE_HOME', 'XDG_DATA_HOME', 'XDG_STATE_HOME', 'XDG_RUNTIME_DIR', 'TMPDIR']: location = root / key.lower() location.mkdir() env[key] = str(location) for repeat in (1, 2): for selection in ['sample.plist', 'absent.plist', '../sample.plist']: call = subprocess.run(['node', 'scripts/rea.mjs', 'inspect-keyed-archive', str(app), selection, '--json'], env=env, text=True, capture_output=True, timeout=60) result = json.loads(call.stdout) if selection == 'sample.plist': assert call.returncode == 0 and result['evidence_id'] print(json.dumps({'repeat': repeat, 'selection': selection, 'exit': call.returncode, 'archive_path': result['normalized_result']['archive_path'], 'total_objects': result['normalized_result']['total_objects']})) else: assert call.returncode == 1 and result['code'] == 'invalid_request' print(json.dumps({'repeat': repeat, 'selection': selection, 'exit': call.returncode, 'code': result['code'], 'issues': result['details']['issues']})) PYTHON ``` 2. Compare the successful `sample.plist` selection with each refusal. Both invalid selections return `issues[0].path: ["path"]`; the declared name of the changed argument is `archive`. The exact reduced block above was executed: each selection ran twice. A preceding independent driver also ran each selection twice with the same result. All four controls succeeded, and each invalid input reproduced in all four calls. ### Expected behavior Attribute invalid bundle-relative selections to `["archive"]`, preserving the existing refusal, reason, useful message and input-contract recovery. The command's executed help and schema distinguish the bundle `path` from the relative `archive` argument. The native investigation guide documents the second argument, and the input-error design contract requires caller field/index paths. Exact source references are listed in the evidence section. ### Impact and scope P3: incorrect diagnostic metadata for correctly rejected requests. The message still identifies the missing or non-canonical selection, and changing the second argument to the valid archive succeeds with the same bundle. A caller can use that message and command help to identify the correct input. No failed agent recovery, data loss, invalid successful decode or target mutation was observed. Executed scope is the public CLI on macOS for missing and non-canonical second arguments. Directory selections, Unicode-equivalent ambiguity, other platforms and live MCP behavior were not reproduced. A direct reader or MCP caller uses the field name `path`; that interface does not have the CLI's argument-name mismatch. ### REA version Exact `node scripts/rea.mjs --version` stdout: ```text 6.8.0 ``` Source commit: `65f1c447344a423fa2785847dbcea1e9a9e2454a`. ### Runtime and platform Node.js v24.22.1; macOS 27.0.1, build 26A434; arm64. No external analysis provider. ### Target and artifact context A synthetic root-layout `StaticArchive.app` with XML `Info.plist` and a two-object XML NSKeyedArchiver `sample.plist`. Its executable name is declared in metadata, but no executable file exists. No application, GUI or Mach-O target was opened. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence The executed reduced reproduction printed these records on its first pass; the second pass differed only in `repeat`: ```json {"repeat":1,"selection":"sample.plist","exit":0,"archive_path":"sample.plist","total_objects":2} {"repeat":1,"selection":"absent.plist","exit":1,"code":"invalid_request","issues":[{"path":["path"],"reason":"invalid_value","message":"No regular file exists at absent.plist in the active app bundle."}]} {"repeat":1,"selection":"../sample.plist","exit":1,"code":"invalid_request","issues":[{"path":["path"],"reason":"invalid_format","message":"Archive path must be a canonical relative bundle path without empty, '.', or '..' segments: ../sample.plist"}]} ``` The original driver also captured `category: "invalid_input"`, `retryable: false` and `recovery: {"kind":"inspect_input_contract","operation":"inspect_keyed_archive"}`. Its stderr was empty. Both control calls returned Evidence identifying `sample.plist` and two objects. Executed help: ```text Usage: rea inspect-keyed-archive <path> [archive] [options] Arguments: path Foundation plist archive or Apple app bundle archive Relative archive path when the target is a bundle ``` At the stated baseline, `docs/tool-design.md:348-351` requires caller field/index paths for expected input failures. `docs/native-investigation.md:611-614` documents the bundle-relative archive as the second CLI argument. `docs/cli.md:943-945` points callers to command help for the declared argument shape; it does not itself contain this command's two-argument synopsis or an explicit issue-path definition. Production references: `src/cli/artifactCommands.ts:29-64` declares the two arguments and forwards `args.archive` as provider parameter `path`. `src/artifacts/apple/KeyedArchiveReader.ts:340-390` rejects these selections; `archivePathError` at lines 477-483 always identifies `["path"]`. Existing `tests/boundary/filesystem/keyedArchiveSelection.test.ts:37-78` exercises direct reader input `{path}`, where that field name is appropriate; it does not verify CLI argument attribution. ### Typed outcome or recovery expected Keep `invalid_request`, category `invalid_input`, the current invalid-value/invalid-format reasons and `inspect_input_contract` recovery. For these CLI second-argument refusals, set `details.issues[0].path` to `["archive"]` so it names the field exposed by the CLI schema. ### Analysis or suspected cause Source-derived explanation: the CLI renames a provider input while forwarding its diagnostic unchanged. The provider's `path` means the selected archive, whereas CLI `path` means the bundle and CLI `archive` means the selection. The reader's fixed field name is correct at its own boundary; it needs the caller's argument name when projected through this adapter. The executed output is consistent with that mapping loss. ### Additional context Read-only searches covered open/closed issues, open/closed/merged PRs and issue comments. Closed [#6119](https://github.com/morluto/rea/issues/6119) concerns malformed environment capture payloads blaming a valid operation. Open [#7523](https://github.com/morluto/rea/issues/7523) concerns a different MCP tool's source-file field mapping. Neither closes this CLI archive-argument mismatch. Merged [PR #1070](https://github.com/morluto/rea/pull/1070) changed selection mistakes to `invalid_request` issues using provider `path`; it did not change the CLI adapter. The present output retains that correct classification and exposes a remaining argument-name mismatch. Merged [PR #1226](https://github.com/morluto/rea/pull/1226) addresses intact files of the wrong archive kind; closed [PR #1314](https://github.com/morluto/rea/pull/1314) addresses Unicode inventory selection. Their closure signals differ from this report. Open [#7377](https://github.com/morluto/rea/issues/7377), closed [#6064](https://github.com/morluto/rea/issues/6064) and closed [#4923](https://github.com/morluto/rea/issues/4923) were also checked and concern empty target admission, read-denial classification and selected-plist/default classification respectively. No duplicate of this CLI attribution defect was found. The npm registry returned 6.8.0 as the latest published rea-agents version. No known-good version or regression claim is 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#4555
No description provided.