[GH-ISSUE #6291] [Bug] CLI path-array schemas omit enforced 16-item limits #3805

Closed
opened 2026-10-31 00:44:15 +00:00 by bsakaguchi · 3 comments
Owner

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

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

recover-javascript-sources --schema --json omits the enforced array cap on options.properties.additionalPath. inspect-wasm-artifact --schema --json similarly omits it on glue_paths and candidate_paths. All three advertise string arrays without maxItems; 17 entries pass CLI parsing and are rejected by the shared workflow with maximum 16. All three were exercised independently.

Reproduction

From the source checkout with locked dependencies installed and the runtime built (npm run build:cached), save this as reproduce.py at the repository root and run python3 reproduce.py. It uses isolated configuration directories, does not create input artifacts, and requires no Wakaru or WABT installation. The script was run once; each of its six boundary calls matched an earlier independently executed call, for two observations per field/count.

import json, os, subprocess, tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as directory:
    root = Path(directory)
    env = {"PATH": os.environ["PATH"]}
    for key in ("HOME", "DSH_HOME", "XDG_CONFIG_HOME", "XDG_CACHE_HOME"):
        location = root / key
        location.mkdir()
        env[key] = str(location)
    for command, option, field in (
        ("recover-javascript-sources", "--additional-path", "additionalPath"),
        ("inspect-wasm-artifact", "--glue_paths", "glue_paths"),
        ("inspect-wasm-artifact", "--candidate_paths", "candidate_paths"),
    ):
        def run(args):
            return subprocess.run(["node", "scripts/rea.mjs", *args, "--json"], env=env, text=True, capture_output=True, timeout=45)
        schema = run([command, "--schema"])
        print(field, "schema:", json.loads(schema.stdout)["options"]["properties"][field])
        for count in (16, 17):
            args = [command, str(root / "input.js")]
            if command == "recover-javascript-sources":
                args.append(str(root / "output"))
            for index in range(count):
                args += [option, str(root / f"selected-{index}.js")]
            result = run(args)
            body = json.loads(result.stdout)
            print(field, count, "exit:", result.returncode, "code:", body["code"], "issues:", body.get("details", {}).get("issues"))
python3 reproduce.py

Expected behavior

The command schema used for validation and tooling should expose the existing 16-item limit for additionalPath, glue_paths, and candidate_paths. Sixteen entries should remain admissible, and seventeen should be rejected before provider work. No particular CLI error-code spelling is required by this report. The CLI framework documents --schema as command input discovery for validation/tooling; REA uses shared workflows across CLI and MCP. The JavaScript recovery guide explicitly states the 16-additional-file limit. The JavaScript request field and WASM request fields enforce it. MCP-specific compact-schema exceptions are not the basis of this CLI report.

Impact and scope

P2: Schema-driven callers cannot discover the enforced cardinality limit and can generate a schema-valid call that the workflow refuses. The runtime error correctly reports the maximum, so the failure is recoverable by using at most 16 entries. That control reached provider admission in these runs. No provider analysis or data loss was observed; incident frequency outside this local sample is unknown.

REA version

Source commit 4a4bbab3c3 (main)

Runtime and platform

Node.js v26.12.1; macOS 27.0.1; arm64. The repository pins Node.js 24.18.0; that exact version was not run. Locked dependencies and a source runtime build were used.

Target and artifact context

Inert distinct absolute path strings; no target files, engines, or external services were needed or executed.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

All three schema queries exit 0 and return an array with string items but no maxItems. For each field, 16 entries exit 1 with capability_unavailable for unconfigured Wakaru or WABT. Seventeen entries instead exit 1 with invalid_request, category invalid_input, and these issues:

{"path":["additional_paths"],"reason":"out_of_range","maximum":16}
{"path":["glue_paths"],"reason":"out_of_range","maximum":16}
{"path":["candidate_paths"],"reason":"out_of_range","maximum":16}

Compiling each complete emitted options schema with the installed Ajv2020 also accepted both the 16- and 17-entry objects. The controls establish passage through input validation to provider configuration checks; they do not establish successful analysis.

Typed outcome or recovery expected

Discover the already enforced maximum before constructing an oversized call; retain accurate rejection and continued admission of supported 16-entry inputs. This report does not prescribe a new error code.

Analysis or suspected cause

Source-derived: the JavaScript CLI option and WASM CLI options each reconstruct plain string arrays. The shared workflows validate the bounded request before provider dispatch. No claim of a single shared implementation cause is needed: closure checks every field independently.

Additional context

No duplicate surfaced in the checked open/closed issue and PR searches. #4876 concerns APK locale bounds; #5493 and tracker #4330 concern MCP host-absolute path constraints. Current-main source inspection at 002e1b68c6 still has the same declarations and owning array limits. The sole relevant intervening change, 7590b50801, changes result numeric constructors rather than these input bounds. Current main was not executed. Restoration should check all three fields with both 16 and 17 values; a fix to one field does not close the remaining instances.

Originally created by @coygeek on GitHub (Oct 22, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6291 ### 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 `recover-javascript-sources --schema --json` omits the enforced array cap on `options.properties.additionalPath`. `inspect-wasm-artifact --schema --json` similarly omits it on `glue_paths` and `candidate_paths`. All three advertise string arrays without `maxItems`; 17 entries pass CLI parsing and are rejected by the shared workflow with maximum 16. All three were exercised independently. ### Reproduction From the source checkout with locked dependencies installed and the runtime built (`npm run build:cached`), save this as `reproduce.py` at the repository root and run `python3 reproduce.py`. It uses isolated configuration directories, does not create input artifacts, and requires no Wakaru or WABT installation. The script was run once; each of its six boundary calls matched an earlier independently executed call, for two observations per field/count. ```python import json, os, subprocess, tempfile from pathlib import Path with tempfile.TemporaryDirectory() as directory: root = Path(directory) env = {"PATH": os.environ["PATH"]} for key in ("HOME", "DSH_HOME", "XDG_CONFIG_HOME", "XDG_CACHE_HOME"): location = root / key location.mkdir() env[key] = str(location) for command, option, field in ( ("recover-javascript-sources", "--additional-path", "additionalPath"), ("inspect-wasm-artifact", "--glue_paths", "glue_paths"), ("inspect-wasm-artifact", "--candidate_paths", "candidate_paths"), ): def run(args): return subprocess.run(["node", "scripts/rea.mjs", *args, "--json"], env=env, text=True, capture_output=True, timeout=45) schema = run([command, "--schema"]) print(field, "schema:", json.loads(schema.stdout)["options"]["properties"][field]) for count in (16, 17): args = [command, str(root / "input.js")] if command == "recover-javascript-sources": args.append(str(root / "output")) for index in range(count): args += [option, str(root / f"selected-{index}.js")] result = run(args) body = json.loads(result.stdout) print(field, count, "exit:", result.returncode, "code:", body["code"], "issues:", body.get("details", {}).get("issues")) ``` ```sh python3 reproduce.py ``` ### Expected behavior The command schema used for validation and tooling should expose the existing 16-item limit for `additionalPath`, `glue_paths`, and `candidate_paths`. Sixteen entries should remain admissible, and seventeen should be rejected before provider work. No particular CLI error-code spelling is required by this report. The CLI framework documents `--schema` as command input discovery for validation/tooling; REA uses shared workflows across CLI and MCP. The [JavaScript recovery guide](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/docs/javascript-recovery.md#L81) explicitly states the 16-additional-file limit. The [JavaScript request field](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/src/domain/javascript/javascriptRecovery.ts#L11) and [WASM request fields](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/src/domain/wasm/wasmArtifact.ts#L22) enforce it. MCP-specific compact-schema exceptions are not the basis of this CLI report. ### Impact and scope P2: Schema-driven callers cannot discover the enforced cardinality limit and can generate a schema-valid call that the workflow refuses. The runtime error correctly reports the maximum, so the failure is recoverable by using at most 16 entries. That control reached provider admission in these runs. No provider analysis or data loss was observed; incident frequency outside this local sample is unknown. ### REA version Source commit 4a4bbab3c337701febeb6b532ace49041c4876e0 (main) ### Runtime and platform Node.js v26.12.1; macOS 27.0.1; arm64. The repository pins Node.js 24.18.0; that exact version was not run. Locked dependencies and a source runtime build were used. ### Target and artifact context Inert distinct absolute path strings; no target files, engines, or external services were needed or executed. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence All three schema queries exit 0 and return an array with string items but no `maxItems`. For each field, 16 entries exit 1 with `capability_unavailable` for unconfigured Wakaru or WABT. Seventeen entries instead exit 1 with `invalid_request`, category `invalid_input`, and these issues: ```json {"path":["additional_paths"],"reason":"out_of_range","maximum":16} {"path":["glue_paths"],"reason":"out_of_range","maximum":16} {"path":["candidate_paths"],"reason":"out_of_range","maximum":16} ``` Compiling each complete emitted options schema with the installed Ajv2020 also accepted both the 16- and 17-entry objects. The controls establish passage through input validation to provider configuration checks; they do not establish successful analysis. ### Typed outcome or recovery expected Discover the already enforced maximum before constructing an oversized call; retain accurate rejection and continued admission of supported 16-entry inputs. This report does not prescribe a new error code. ### Analysis or suspected cause Source-derived: the [JavaScript CLI option](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/src/cli/javascriptRecoveryCommands.ts#L30) and [WASM CLI options](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/src/cli/wasmCommands.ts#L52) each reconstruct plain string arrays. The shared workflows validate the bounded request before provider dispatch. No claim of a single shared implementation cause is needed: closure checks every field independently. ### Additional context No duplicate surfaced in the checked open/closed issue and PR searches. #4876 concerns APK locale bounds; #5493 and tracker #4330 concern MCP host-absolute path constraints. Current-main source inspection at 002e1b68c66a243948fdca57ac416cfaec617315 still has the same declarations and owning array limits. The sole relevant intervening change, 7590b50801553ca443922b1fb48e168334f8cc5d, changes result numeric constructors rather than these input bounds. Current main was not executed. Restoration should check all three fields with both 16 and 17 values; a fix to one field does not close the remaining instances.
Author
Owner

@coygeek commented on GitHub (Oct 23, 2026):

Another affected field is start-adb-activity --extra (CLI/schema extra, shared request extras), which is a typed-extra array rather than a path array. It is absent from this issue's three-field checklist, and the linked companion-path fix does not touch its declaration.

Reproduced on current main at 7f9a03d6ab37a63ac1be768e3eae8545187f7d21 with locked dependencies and npm run build:cached, and on an isolated installation of published rea-agents@6.7.0. Runtime: Node.js v26.12.1, macOS 27.0.1, arm64; the repository-pinned Node.js version was not run.

Both builds emit this options.properties.extra schema, without maxItems; compiling each complete emitted options schema with Ajv2020 accepts both 16 and 17 well-formed entries:

{"description":"Typed extra as type:key:value (string, boolean, int, long, float), repeatable","default":[],"type":"array","items":{"type":"string","minLength":1}}

To reproduce from a built checkout, save this as reproduce.py and run python3 reproduce.py .; for an installed package, pass its package directory instead:

import json, os, subprocess, sys, tempfile
from pathlib import Path

entry = Path(sys.argv[1]).resolve() / 'scripts' / 'rea.mjs'
with tempfile.TemporaryDirectory() as directory:
    root = Path(directory)
    env = {'PATH': os.environ['PATH'], 'REA_ADB_PATH': str(root / 'missing-adb')}
    for name in ('HOME', 'DSH_HOME', 'XDG_CONFIG_HOME', 'XDG_CACHE_HOME'):
        location = root / name
        location.mkdir()
        env[name] = str(location)
    def run(args):
        return subprocess.run(['node', str(entry), 'start-adb-activity', *args, '--json'], env=env, text=True, capture_output=True, timeout=45)
    result = run(['--schema'])
    schema = json.loads(result.stdout)
    print('schema', result.returncode, schema['options']['properties']['extra'])
    for count in (16, 17):
        args = ['SERIAL', 'android.intent.action.VIEW']
        for index in range(count):
            args += ['--extra', f'string:key{index}:value{index}']
        result = run(args)
        body = json.loads(result.stdout)
        print(count, result.returncode, body['code'], body.get('details', {}).get('issues'))

On both builds, 16 extras exit 1 with capability_unavailable for the deliberately nonexistent ADB executable; 17 exit 1 with invalid_request. The main build additionally reports:

{"path":["extras"],"reason":"out_of_range","maximum":16}

The published build does not include issue details in that error. The script above was run once against each build. No device was contacted: the 16-entry control establishes passage through validation to provider admission, not a successful activity launch.

Source-derived: src/cli/adbCommands.ts reconstructs an unbounded string array for extra, while src/domain/adb/adbDeviceAnalysis.ts caps the parsed extras array at 16. This supplies another affected-field closure case for the same schema/admission mismatch: advertise the existing 16-item bound, retain admission of 16 extras, and reject 17 before provider work. It does not request support for more extras.

<!-- gh-comment-id:6357885121 --> @coygeek commented on GitHub (Oct 23, 2026): Another affected field is `start-adb-activity --extra` (CLI/schema `extra`, shared request `extras`), which is a typed-extra array rather than a path array. It is absent from this issue's three-field checklist, and the linked companion-path fix does not touch its declaration. Reproduced on current `main` at `7f9a03d6ab37a63ac1be768e3eae8545187f7d21` with locked dependencies and `npm run build:cached`, and on an isolated installation of published `rea-agents@6.7.0`. Runtime: Node.js v26.12.1, macOS 27.0.1, arm64; the repository-pinned Node.js version was not run. Both builds emit this `options.properties.extra` schema, without `maxItems`; compiling each complete emitted options schema with Ajv2020 accepts both 16 and 17 well-formed entries: ```json {"description":"Typed extra as type:key:value (string, boolean, int, long, float), repeatable","default":[],"type":"array","items":{"type":"string","minLength":1}} ``` To reproduce from a built checkout, save this as `reproduce.py` and run `python3 reproduce.py .`; for an installed package, pass its package directory instead: ```python import json, os, subprocess, sys, tempfile from pathlib import Path entry = Path(sys.argv[1]).resolve() / 'scripts' / 'rea.mjs' with tempfile.TemporaryDirectory() as directory: root = Path(directory) env = {'PATH': os.environ['PATH'], 'REA_ADB_PATH': str(root / 'missing-adb')} for name in ('HOME', 'DSH_HOME', 'XDG_CONFIG_HOME', 'XDG_CACHE_HOME'): location = root / name location.mkdir() env[name] = str(location) def run(args): return subprocess.run(['node', str(entry), 'start-adb-activity', *args, '--json'], env=env, text=True, capture_output=True, timeout=45) result = run(['--schema']) schema = json.loads(result.stdout) print('schema', result.returncode, schema['options']['properties']['extra']) for count in (16, 17): args = ['SERIAL', 'android.intent.action.VIEW'] for index in range(count): args += ['--extra', f'string:key{index}:value{index}'] result = run(args) body = json.loads(result.stdout) print(count, result.returncode, body['code'], body.get('details', {}).get('issues')) ``` On both builds, 16 extras exit 1 with `capability_unavailable` for the deliberately nonexistent ADB executable; 17 exit 1 with `invalid_request`. The `main` build additionally reports: ```json {"path":["extras"],"reason":"out_of_range","maximum":16} ``` The published build does not include issue details in that error. The script above was run once against each build. No device was contacted: the 16-entry control establishes passage through validation to provider admission, not a successful activity launch. Source-derived: `src/cli/adbCommands.ts` reconstructs an unbounded string array for `extra`, while `src/domain/adb/adbDeviceAnalysis.ts` caps the parsed `extras` array at 16. This supplies another affected-field closure case for the same schema/admission mismatch: advertise the existing 16-item bound, retain admission of 16 extras, and reject 17 before provider work. It does not request support for more extras.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

The original three-field checklist has progressed at d87e73f: the CLI now shares the owning bounded schemas, and the regression assertions check both the advertised complete options schema and CLI parser with 16 and 17 entries for additionalPath, glue_paths, and candidate_paths.

The added start-adb-activity --extra case remains outside that correction. Its CLI declaration still builds an unbounded string array. The handler converts those strings to extras, whose shared input schema retains .max(16). The shared execution boundary validates before provider execution. This is current-source evidence for the remaining mismatch; the earlier CLI/Ajv results in the added comment are author-reported, and I have not repeated them on this revision.

Closure should include that fourth field: advertise the existing 16-item maximum, verify that the emitted complete schema and CLI parser admit 16 well-formed extras and reject 17, and retain the three original controls. For the workflow check, use a deliberately nonexistent ADB executable in an isolated environment: 16 should reach provider-unavailable admission, while 17 must be refused before provider work. No device contact or successful activity launch is needed. The existing tests for malformed or individually invalid extras do not cover this cardinality boundary; this asks for matching discovery metadata, not a higher limit.

<!-- gh-comment-id:6380889534 --> @coygeek commented on GitHub (Oct 24, 2026): The original three-field checklist has progressed at `d87e73f`: the CLI now shares the owning bounded schemas, and the [regression assertions](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/boundary/cli/cliInputSchemaRequired.test.ts#L143-L158) check both the advertised complete options schema and CLI parser with 16 and 17 entries for `additionalPath`, `glue_paths`, and `candidate_paths`. The added `start-adb-activity --extra` case remains outside that correction. Its [CLI declaration](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/cli/adbCommands.ts#L337-L349) still builds an unbounded string array. The handler converts those strings to `extras`, whose [shared input schema](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/adb/adbDeviceAnalysis.ts#L336-L340) retains `.max(16)`. The [shared execution boundary](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/ProviderOperationExecution.ts#L60-L70) validates before provider execution. This is current-source evidence for the remaining mismatch; the earlier CLI/Ajv results in the added comment are author-reported, and I have not repeated them on this revision. Closure should include that fourth field: advertise the existing 16-item maximum, verify that the emitted complete schema and CLI parser admit 16 well-formed extras and reject 17, and retain the three original controls. For the workflow check, use a deliberately nonexistent ADB executable in an isolated environment: 16 should reach provider-unavailable admission, while 17 must be refused before provider work. No device contact or successful activity launch is needed. The existing tests for malformed or individually invalid extras do not cover this cardinality boundary; this asks for matching discovery metadata, not a higher limit.
Author
Owner

@morluto commented on GitHub (Oct 25, 2026):

Fixed on main: the JavaScript recovery and WASM companion arrays advertise and enforce their canonical 16-item bounds, and cb26e8ca63 adds the same ownership for ADB --extra. Main has schema/parser 16/17 boundary checks plus actual CLI checks that 16 ADB extras reach host admission and 17 fail before it. The related ADB/catalog validation passed 59 tests. Current main also includes the portable-pattern fix in 4c680d099c. Superseded PR #6297 is closed.

<!-- gh-comment-id:6392313497 --> @morluto commented on GitHub (Oct 25, 2026): Fixed on main: the JavaScript recovery and WASM companion arrays advertise and enforce their canonical 16-item bounds, and cb26e8ca638ab5b64f4c475d727274d91ba1595f adds the same ownership for ADB --extra. Main has schema/parser 16/17 boundary checks plus actual CLI checks that 16 ADB extras reach host admission and 17 fail before it. The related ADB/catalog validation passed 59 tests. Current main also includes the portable-pattern fix in 4c680d099c286d267fc1bb76aad81c1ee23f130c. Superseded PR #6297 is closed.
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#3805
No description provided.