[PR #2630] feat(cli): expose caller-selected reference-source path exclusions #5293

Open
opened 2026-10-31 01:05:30 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2630
Author: @YunzhouLi-hub
Created: 10/13/2026
Status: 🔄 Open

Base: main ← Head: feat/import-path-exclusions


📝 Commits (7)

  • b4befcb feat(cli): expose caller-selected reference-source path exclusions
  • d0e87ba docs(cli): use an effective reference exclusion negation
  • 18e007c Merge commit 'ebaaa79ca0' into HEAD
  • 20b2bc8 Merge branch 'main' into feat/import-path-exclusions
  • dd184f0 Merge branch 'main' into feat/import-path-exclusions
  • 42c8cb1 test(cli): expect entry_kind on reference-source exclusions
  • 7bee2d8 test(cli): record caller *.tmp over project ignore

📊 Changes

3 files changed (+364 additions, -1 deletions)

View changed files

📝 docs/cli.md (+1 -0)
📝 src/cli/utilityCommands.ts (+77 -1)
📝 tests/boundary/cli/referenceSourceExclusions.test.ts (+286 -0)

📄 Description

Let import-reference-source accept a JSON array of Git-ignore rules and forward them to the existing importer before the source tree is opened.

Fixes #2443

Summary

The reference-source importer already accepted excludePaths, recorded caller-excluded provenance, and pruned traversal — but the CLI adapter never exposed it. A caller who needed to skip a vendor/ directory had to import it anyway, or hand-edit the saved graph. import-reference-source now takes --exclude-paths and passes the parsed rules to the importer it already used.

Two commits, both on this branch. b4befcb10 is the feature; d0e87ba7b was pushed by the maintainer to correct the documentation example and its negation explanation, addressing the two review threads below. The second commit is the maintainer's, not the PR author's.

Problem and expected behavior

Reported in #2443.

Smallest trigger: rea import-reference-source /path/to/source --exclude-paths '["vendor/"]' — the option does not exist, so there is no way to reach the importer's caller-selected exclusion policy from the CLI.

Violated invariant: the importer owns a caller-selected exclusion stage that emits caller-excluded provenance, but the CLI adapter could not reach it. The capability was unreachable rather than merely undocumented, and the only way to exercise it was to hand-edit the saved graph.

Expected behavior, from #2443's acceptance list:

  • an optional JSON-array option selecting the importer's Git-ignore-style rules;
  • JSON shape and nonempty-string elements validated before any filesystem access;
  • the existing default-rule, project-.gitignore and declared-secret policies retained, with secret patterns still applied first;
  • matches still recorded as caller-excluded with the winning pattern;
  • no execution of anything in the imported tree.

Change and scope

src/cli/utilityCommands.ts (+82/−2) — all of it in the CLI adapter layer:

  • referenceSourceExcludePathsSchema: z.array(z.string().min(1)), the narrowest shape that satisfies "JSON array of nonempty rules".
  • parseReferenceSourceExcludePaths: decodes with the existing safeParseJson, then validates, then returns { ok: true, rules } or a projected failure. It runs before parseConfig(environment) and before runReferenceSourceImport, so a bad rule set never reaches the filesystem.
  • invalidReferenceSourceExcludePaths: routes the failure through the existing AnalysisInputError → projectAnalysisError → projectInputIssues chain rather than inventing a new error shape, so the JSON keeps the same error/code/category/details.issues envelope as every other input refusal in this command.
  • The parsed rules are spread into the import request as ...(excludePaths === undefined ? {} : { excludePaths }), so omitting the option produces a byte-identical request to before. An empty array is forwarded as an empty array and is asserted equivalent to omission.

docs/cli.md (+18) — documents the option, the Git-ignore grammar (wildcards, vendor/ suffixes, ! negations, and that these are not literal path matches), the validation ordering, caller-excluded provenance, and the precedence rules.

tests/boundary/cli/referenceSourceExclusions.test.ts (+280) — one executable behavioral test plus eight input-validation cases (breakdown under Evidence).

Why this fits the architecture: the CLI adapter owns argument parsing and error projection; the importer keeps owning exclusion semantics and provenance. Nothing in src/domain/ or src/application/ reinterprets the rules, so the dependency direction is unchanged. The only new abstraction is the width-of-one-function schema/parse pair, which exists to make the "validate before the tree opens" ordering testable.

Intentionally not included:

  • no new importer capability — excludePaths already existed; this only reaches it;
  • no MCP surface — import-reference-source is a CLI-only command and does not appear in src/contracts/;
  • no change to secret-pattern precedence, default generated-path rules, or project .gitignore handling;
  • no execution, no network, no process, no memory writes.

Follow the contribution scope.
Unresolved scope, authority, side effects, or required verification block
integration. Link the required maintainer scope decision for new providers or
capabilities involving target execution, injection, mutation, or protection
recovery; ordinary fixes within existing boundaries may state "not applicable".
Draft PRs may leave the scope decision pending and the final review checklist
unchecked. Maintainers can help resolve open questions.

Contract and boundary impact

  • Semantic owner and earliest changed stage: the reference-source importer owns exclusion semantics and caller-excluded provenance, and is unchanged. The earliest changed stage is the CLI argument adapter in src/cli/utilityCommands.ts.
  • Contribution scope fit and linked maintainer decision: not applicable. This is an ordinary CLI surface over an existing importer boundary, with no target execution, injection, mutation, or protection recovery. No maintainer scope decision required.
  • CLI and MCP/tool-catalog contract: import-reference-source gains one optional string option, --exclude-paths. Additive: omitting it yields the previous request shape, and an empty array is asserted equivalent to omission. No MCP tool is affected — import-reference-source is absent from src/contracts/, and the generated docs/public/product-catalog.json command list is unchanged (exclude-paths appears 0 times in it).
  • Provider, bridge, target-format, or platform compatibility: none. No provider, bridge, or target-format code is touched. The import itself remains unavailable on Windows because safe no-follow opens are unsupported there; that is pre-existing host behavior, not introduced here, and it is why the behavioral test is skipIf(win32).
  • Evidence, artifact, provenance, or reconstruction contract: unchanged. The importer continues to record a caller match as caller-excluded with the winning pattern. Declared secret patterns are applied first, so a caller rule cannot reclassify a declared secret — asserted directly (see Evidence).
  • Target selection, authority, upstream behavior, and cleanup details (if applicable): no target interaction. The import stays read-only over the caller-selected tree. Nothing is executed from the imported content.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none. No authored instruction changed, so skills/ and the catalog are untouched and CI validation passes unchanged.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • 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.

Evidence and regression coverage

Executed, not proposed. All runs on Node 24.15.0 / Windows x64.

  • Tests added or updated: tests/boundary/cli/referenceSourceExclusions.test.ts gains two blocks.

    Behavioral — describe.skipIf(process.platform === "win32")("compiled reference-source caller exclusions"), one test, prunes caller-excluded directories and applies Git-ignore rules. It imports the same tree three times (option omitted, empty array, and ["skip-me/","*.tmp","!keep.log","private.txt"]) and asserts:

    • omitted and empty produce identical exclusions and identical entry paths, so the empty array is provably a no-op rather than a second code path;
    • with rules selected, exclusions is exactly five entries — logs/application.log default-ignored *.log, notes.tmp caller-excluded *.tmp, private.txt configured-secret private.txt, project.tmp project-ignored project.tmp, skip-me caller-excluded skip-me/;
    • private.txt matching a caller rule is still classified configured-secret, which is the secret-precedence assertion;
    • project.tmp stays project-ignored and logs/application.log stays default-ignored, so caller rules neither replace project nor default policy;
    • keep.log survives as an entry — the !keep.log negation restores a file excluded by the default *.log, proving negation works when the parent is still traversed;
    • no entry exists at skip-me, skip-me/nested or skip-me/nested/hidden.ts, and no exclusion path starts with skip-me/, so the matched directory is pruned rather than its files listed individually.

    Input validation — describe("compiled reference-source caller exclusion input"), eight cases that run on every platform: malformed JSON ("[" → invalid_format/JSON), a non-array JSON value ("{}" → invalid_type/array), and six invalid elements — numeric, null, object, boolean, empty-string (out_of_range/minimum: 1) — plus starts an import after an empty exclusion array. Each asserts exitCode 1, the invalid_input envelope, the projected issue path, and that the output contains neither "could not be resolved" nor the source root, which is how "failed before the tree was opened" is pinned down rather than assumed.

  • Base reproduction or other evidence: #2443's report is the base evidence that the importer capability was unreachable from the CLI. On the affected base revision the option is simply absent, so the new behavioral test cannot pass at all — it is not a re-worded existing assertion.

  • User-visible CLI/MCP output (if applicable): real node scripts/rea.mjs runs, captured verbatim.

    Invalid input, rejected before the source tree is opened:

    {"error":"Import failed","code":"invalid_request","category":"invalid_input","message":"--exclude-paths must be a JSON array of nonempty Git-ignore rules. Each rule uses Git-ignore syntax, including wildcards, directory suffixes, and negations, rather than a literal path match.","retryable":true,"remediation":{"action":"Correct the listed arguments and retry."},"details":{"operation":"import-reference-source","issues":[{"path":["excludePaths"],"reason":"invalid_type","expected":"array"}]}}
    
    {"error":"Import failed","code":"invalid_request","category":"invalid_input","message":"--exclude-paths must be a JSON array of nonempty Git-ignore rules. …","details":{"operation":"import-reference-source","issues":[{"path":["excludePaths",0],"reason":"invalid_type","expected":"string"},{"path":["excludePaths",1],"reason":"invalid_type","expected":"string"}]}}
    

    Ordering proof — a valid or empty array clears validation and reaches the import, which then fails on this host for a different, pre-existing reason:

    {"error":"Import failed","category":"unsupported_host","message":"Safe no-follow file opens are unavailable … on this host. Import the source tree with REA on Linux (including WSL) or macOS."}
    

    So invalid_input precedes unsupported_host, which is observable confirmation that validation happens before the tree opens rather than merely being claimed in the docs.

  • Remaining proof gaps: the import itself cannot run on Windows (safe no-follow opens unsupported), so the behavioral test's two skipIf(win32) cases are skipped here and the end-to-end import was not exercised on this host. The eight input-validation cases and the ordering proof above were exercised on Windows. No Linux or macOS run was performed. No MCP workflow is affected, so none was exercised.

For evidence-bearing changes:

  • 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

Listed commands actually ran, in this order, on this branch at d0e87ba7b:

  • npm run test:focused -- tests/boundary/cli/referenceSourceExclusions.test.ts — 8 passed, 2 skipped (win32), 10 total
  • npm run check:fast — 2/2 tasks successful (typecheck + lint)
  • npm run verify:test-discovery — "Discovered all 1022 test files exactly once."
  • npx oxfmt --check docs/cli.md src/cli/utilityCommands.ts tests/boundary/cli/referenceSourceExclusions.test.ts — "All matched files use the correct format."
  • node scripts/check-doc-facts.mjs — exit 0
  • npm run docs:check — fails only on artifacts:managed-evidence with EPERM: operation not permitted, mkdtemp '…rea-managed-conformance-XXXXXX'; reproduced identically on a clean upstream/main with none of this change applied, so it is environmental and unrelated. Note this failure aborts turbo early, which is why check-doc-facts.mjs was run separately above.
  • Four real node scripts/rea.mjs import-reference-source invocations — outputs quoted under Evidence.

Not run: the full Vitest suite, npm run build:cached (the compiled CLI in dist/ was already current), any provider workflow, and any Linux or macOS host.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. The option is optional and additive. Omitting it produces the same import request as before, asserted by the omitted-versus-empty equivalence in the behavioral test. No previously valid call changes behavior.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable — no provider is involved. The Linux/macOS import path was not run; Windows cannot run the import at all (pre-existing host limitation, stated above rather than hidden).
  • Package or release metadata impact: none. No dependency, no generated catalog, no skills/ change. Docs and CLI surface only, so patch-level at most if released separately.
  • Security, privacy, process, or containment review: the option can only narrow what is imported, never widen it. Declared secret patterns are applied first and a caller rule cannot reclassify a declared secret — asserted directly by the private.txt → configured-secret case — so the option cannot be used to launder a secret path into ordinary content or the reverse. docs/cli.md states explicitly that selecting a path for exclusion is not a secrecy heuristic. No network, no process, no memory writes, nothing from the imported tree is executed. No new ambient environment is persisted.

Author readiness

Human owner: @YunzhouLi-hub

  • 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.

Two review threads remain open on the branch and are addressed by d0e87ba7b:

  • CodeRabbit thread — the !vendor/keep.ts example had no effect, because a negation cannot re-include a file whose parent directory was excluded. Replaced with !keep.generated after *.generated, whose parent stays traversable, and the skipped-parent limitation is now documented.
  • Greptile thread — same defect, same fix.

Both were fixed at the producing boundary (the documented example) rather than by changing Git-ignore semantics. The final diff is three files, 378 insertions, 2 deletions, with no unrelated cleanup.


🔄 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/2630 **Author:** [@YunzhouLi-hub](https://github.com/YunzhouLi-hub) **Created:** 10/13/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/import-path-exclusions` --- ### 📝 Commits (7) - [`b4befcb`](https://github.com/morluto/rea/commit/b4befcb1013bfea52af42e180a2d9514e86fd567) feat(cli): expose caller-selected reference-source path exclusions - [`d0e87ba`](https://github.com/morluto/rea/commit/d0e87ba7bc1d5609c8e866432c8fef22e49b1b14) docs(cli): use an effective reference exclusion negation - [`18e007c`](https://github.com/morluto/rea/commit/18e007c6c124161706d85f7d9c8590ef169dfb72) Merge commit 'ebaaa79ca02bde4a73013a2a9a781d0b07a57ee4' into HEAD - [`20b2bc8`](https://github.com/morluto/rea/commit/20b2bc800242e2fcd44aaf8f11abe5a8d80c5b39) Merge branch 'main' into feat/import-path-exclusions - [`dd184f0`](https://github.com/morluto/rea/commit/dd184f0211f79fa659a3610f1ca19ae3caa58b75) Merge branch 'main' into feat/import-path-exclusions - [`42c8cb1`](https://github.com/morluto/rea/commit/42c8cb1cfcc8da036d9ee099a769ac9a1640557d) test(cli): expect entry_kind on reference-source exclusions - [`7bee2d8`](https://github.com/morluto/rea/commit/7bee2d8972092f87f567c8249053456499dd63d9) test(cli): record caller *.tmp over project ignore ### 📊 Changes **3 files changed** (+364 additions, -1 deletions) <details> <summary>View changed files</summary> 📝 `docs/cli.md` (+1 -0) 📝 `src/cli/utilityCommands.ts` (+77 -1) 📝 `tests/boundary/cli/referenceSourceExclusions.test.ts` (+286 -0) </details> ### 📄 Description Let `import-reference-source` accept a JSON array of Git-ignore rules and forward them to the existing importer before the source tree is opened. Fixes #2443 <!-- PR title: type(optional-scope): imperative outcome The title becomes the squash-merge commit subject and generated changelog entry. --> ## Summary The reference-source importer already accepted `excludePaths`, recorded `caller-excluded` provenance, and pruned traversal — but the CLI adapter never exposed it. A caller who needed to skip a `vendor/` directory had to import it anyway, or hand-edit the saved graph. `import-reference-source` now takes `--exclude-paths` and passes the parsed rules to the importer it already used. Two commits, both on this branch. `b4befcb10` is the feature; `d0e87ba7b` was pushed by the maintainer to correct the documentation example and its negation explanation, addressing the two review threads below. The second commit is the maintainer's, not the PR author's. ## Problem and expected behavior Reported in #2443. Smallest trigger: `rea import-reference-source /path/to/source --exclude-paths '["vendor/"]'` — the option does not exist, so there is no way to reach the importer's caller-selected exclusion policy from the CLI. Violated invariant: the importer owns a caller-selected exclusion stage that emits `caller-excluded` provenance, but the CLI adapter could not reach it. The capability was unreachable rather than merely undocumented, and the only way to exercise it was to hand-edit the saved graph. Expected behavior, from #2443's acceptance list: - an optional JSON-array option selecting the importer's Git-ignore-style rules; - JSON shape and nonempty-string elements validated before any filesystem access; - the existing default-rule, project-`.gitignore` and declared-secret policies retained, with secret patterns still applied first; - matches still recorded as `caller-excluded` with the winning pattern; - no execution of anything in the imported tree. ## Change and scope `src/cli/utilityCommands.ts` (+82/−2) — all of it in the CLI adapter layer: - `referenceSourceExcludePathsSchema`: `z.array(z.string().min(1))`, the narrowest shape that satisfies "JSON array of nonempty rules". - `parseReferenceSourceExcludePaths`: decodes with the existing `safeParseJson`, then validates, then returns `{ ok: true, rules }` or a projected failure. It runs **before** `parseConfig(environment)` and before `runReferenceSourceImport`, so a bad rule set never reaches the filesystem. - `invalidReferenceSourceExcludePaths`: routes the failure through the existing `AnalysisInputError` → `projectAnalysisError` → `projectInputIssues` chain rather than inventing a new error shape, so the JSON keeps the same `error`/`code`/`category`/`details.issues` envelope as every other input refusal in this command. - The parsed rules are spread into the import request as `...(excludePaths === undefined ? {} : { excludePaths })`, so omitting the option produces a byte-identical request to before. An empty array is forwarded as an empty array and is asserted equivalent to omission. `docs/cli.md` (+18) — documents the option, the Git-ignore grammar (wildcards, `vendor/` suffixes, `!` negations, and that these are not literal path matches), the validation ordering, `caller-excluded` provenance, and the precedence rules. `tests/boundary/cli/referenceSourceExclusions.test.ts` (+280) — one executable behavioral test plus eight input-validation cases (breakdown under Evidence). Why this fits the architecture: the CLI adapter owns argument parsing and error projection; the importer keeps owning exclusion semantics and provenance. Nothing in `src/domain/` or `src/application/` reinterprets the rules, so the dependency direction is unchanged. The only new abstraction is the width-of-one-function schema/parse pair, which exists to make the "validate before the tree opens" ordering testable. Intentionally not included: - no new importer capability — `excludePaths` already existed; this only reaches it; - no MCP surface — `import-reference-source` is a CLI-only command and does not appear in `src/contracts/`; - no change to secret-pattern precedence, default generated-path rules, or project `.gitignore` handling; - no execution, no network, no process, no memory writes. Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope). Unresolved scope, authority, side effects, or required verification block integration. Link the required maintainer scope decision for new providers or capabilities involving target execution, injection, mutation, or protection recovery; ordinary fixes within existing boundaries may state "not applicable". Draft PRs may leave the scope decision pending and the final review checklist unchecked. Maintainers can help resolve open questions. ## Contract and boundary impact - **Semantic owner and earliest changed stage:** the reference-source importer owns exclusion semantics and `caller-excluded` provenance, and is unchanged. The earliest changed stage is the CLI argument adapter in `src/cli/utilityCommands.ts`. - **Contribution scope fit and linked maintainer decision:** not applicable. This is an ordinary CLI surface over an existing importer boundary, with no target execution, injection, mutation, or protection recovery. No maintainer scope decision required. - **CLI and MCP/tool-catalog contract:** `import-reference-source` gains one optional string option, `--exclude-paths`. Additive: omitting it yields the previous request shape, and an empty array is asserted equivalent to omission. No MCP tool is affected — `import-reference-source` is absent from `src/contracts/`, and the generated `docs/public/product-catalog.json` command list is unchanged (`exclude-paths` appears 0 times in it). - **Provider, bridge, target-format, or platform compatibility:** none. No provider, bridge, or target-format code is touched. The import itself remains unavailable on Windows because safe no-follow opens are unsupported there; that is pre-existing host behavior, not introduced here, and it is why the behavioral test is `skipIf(win32)`. - **Evidence, artifact, provenance, or reconstruction contract:** unchanged. The importer continues to record a caller match as `caller-excluded` with the winning pattern. Declared secret patterns are applied first, so a caller rule cannot reclassify a declared secret — asserted directly (see Evidence). - **Target selection, authority, upstream behavior, and cleanup details (if applicable):** no target interaction. The import stays read-only over the caller-selected tree. Nothing is executed from the imported content. - **Generated metadata (`docs/public/product-catalog.json`), package, or installation impact:** none. No authored instruction changed, so `skills/` and the catalog are untouched and CI validation passes unchanged. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [x] 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. ## Evidence and regression coverage Executed, not proposed. All runs on Node 24.15.0 / Windows x64. - **Tests added or updated:** `tests/boundary/cli/referenceSourceExclusions.test.ts` gains two blocks. *Behavioral* — `describe.skipIf(process.platform === "win32")("compiled reference-source caller exclusions")`, one test, `prunes caller-excluded directories and applies Git-ignore rules`. It imports the same tree three times (option omitted, empty array, and `["skip-me/","*.tmp","!keep.log","private.txt"]`) and asserts: - omitted and empty produce identical `exclusions` and identical entry paths, so the empty array is provably a no-op rather than a second code path; - with rules selected, `exclusions` is exactly five entries — `logs/application.log` `default-ignored` `*.log`, `notes.tmp` `caller-excluded` `*.tmp`, `private.txt` **`configured-secret`** `private.txt`, `project.tmp` `project-ignored` `project.tmp`, `skip-me` `caller-excluded` `skip-me/`; - `private.txt` matching a caller rule is still classified `configured-secret`, which is the secret-precedence assertion; - `project.tmp` stays `project-ignored` and `logs/application.log` stays `default-ignored`, so caller rules neither replace project nor default policy; - `keep.log` survives as an entry — the `!keep.log` negation restores a file excluded by the default `*.log`, proving negation works when the parent is still traversed; - no entry exists at `skip-me`, `skip-me/nested` or `skip-me/nested/hidden.ts`, and no exclusion path starts with `skip-me/`, so the matched directory is pruned rather than its files listed individually. *Input validation* — `describe("compiled reference-source caller exclusion input")`, eight cases that run on every platform: malformed JSON (`"["` → `invalid_format`/`JSON`), a non-array JSON value (`"{}"` → `invalid_type`/`array`), and six invalid elements — numeric, `null`, object, boolean, empty-string (`out_of_range`/`minimum: 1`) — plus `starts an import after an empty exclusion array`. Each asserts `exitCode` 1, the `invalid_input` envelope, the projected issue path, and that the output contains neither `"could not be resolved"` nor the source root, which is how "failed before the tree was opened" is pinned down rather than assumed. - **Base reproduction or other evidence:** #2443's report is the base evidence that the importer capability was unreachable from the CLI. On the affected base revision the option is simply absent, so the new behavioral test cannot pass at all — it is not a re-worded existing assertion. - **User-visible CLI/MCP output (if applicable):** real `node scripts/rea.mjs` runs, captured verbatim. Invalid input, rejected before the source tree is opened: ```json {"error":"Import failed","code":"invalid_request","category":"invalid_input","message":"--exclude-paths must be a JSON array of nonempty Git-ignore rules. Each rule uses Git-ignore syntax, including wildcards, directory suffixes, and negations, rather than a literal path match.","retryable":true,"remediation":{"action":"Correct the listed arguments and retry."},"details":{"operation":"import-reference-source","issues":[{"path":["excludePaths"],"reason":"invalid_type","expected":"array"}]}} ``` ```json {"error":"Import failed","code":"invalid_request","category":"invalid_input","message":"--exclude-paths must be a JSON array of nonempty Git-ignore rules. …","details":{"operation":"import-reference-source","issues":[{"path":["excludePaths",0],"reason":"invalid_type","expected":"string"},{"path":["excludePaths",1],"reason":"invalid_type","expected":"string"}]}} ``` Ordering proof — a valid or empty array clears validation and reaches the import, which then fails on this host for a different, pre-existing reason: ```json {"error":"Import failed","category":"unsupported_host","message":"Safe no-follow file opens are unavailable … on this host. Import the source tree with REA on Linux (including WSL) or macOS."} ``` So `invalid_input` precedes `unsupported_host`, which is observable confirmation that validation happens before the tree opens rather than merely being claimed in the docs. - **Remaining proof gaps:** the import itself cannot run on Windows (safe no-follow opens unsupported), so the behavioral test's two `skipIf(win32)` cases are skipped here and the end-to-end import was not exercised on this host. The eight input-validation cases and the ordering proof above *were* exercised on Windows. No Linux or macOS run was performed. No MCP workflow is affected, so none was exercised. For evidence-bearing changes: - [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 Listed commands actually ran, in this order, on this branch at `d0e87ba7b`: - `npm run test:focused -- tests/boundary/cli/referenceSourceExclusions.test.ts` — 8 passed, 2 skipped (win32), 10 total - `npm run check:fast` — 2/2 tasks successful (typecheck + lint) - `npm run verify:test-discovery` — "Discovered all 1022 test files exactly once." - `npx oxfmt --check docs/cli.md src/cli/utilityCommands.ts tests/boundary/cli/referenceSourceExclusions.test.ts` — "All matched files use the correct format." - `node scripts/check-doc-facts.mjs` — exit 0 - `npm run docs:check` — fails only on `artifacts:managed-evidence` with `EPERM: operation not permitted, mkdtemp '…rea-managed-conformance-XXXXXX'`; reproduced identically on a clean `upstream/main` with none of this change applied, so it is environmental and unrelated. Note this failure aborts turbo early, which is why `check-doc-facts.mjs` was run separately above. - Four real `node scripts/rea.mjs import-reference-source` invocations — outputs quoted under Evidence. Not run: the full Vitest suite, `npm run build:cached` (the compiled CLI in `dist/` was already current), any provider workflow, and any Linux or macOS host. ## Compatibility, safety, and release - **Breaking changes or migration steps:** none. The option is optional and additive. Omitting it produces the same import request as before, asserted by the omitted-versus-empty equivalence in the behavioral test. No previously valid call changes behavior. - **Real Hopper/Ghidra, browser, or OS coverage:** not applicable — no provider is involved. The Linux/macOS import path was not run; Windows cannot run the import at all (pre-existing host limitation, stated above rather than hidden). - **Package or release metadata impact:** none. No dependency, no generated catalog, no `skills/` change. Docs and CLI surface only, so patch-level at most if released separately. - **Security, privacy, process, or containment review:** the option can only *narrow* what is imported, never widen it. Declared secret patterns are applied first and a caller rule cannot reclassify a declared secret — asserted directly by the `private.txt` → `configured-secret` case — so the option cannot be used to launder a secret path into ordinary content or the reverse. `docs/cli.md` states explicitly that selecting a path for exclusion is not a secrecy heuristic. No network, no process, no memory writes, nothing from the imported tree is executed. No new ambient environment is persisted. ## Author readiness <!-- The human owner completes these declarations before requesting review. The PR author is the owner by default. A different owner must explicitly accept ownership on this PR. AI agents must leave these declarations to a human to dictate to them or there could be catastrophic consequences for many users of REA. Draft PRs may leave them unchecked. These declarations record initial readiness when opening the PR for review; later pushes do not require a new human sign-off. Keep the description and validation evidence current as the work changes. See CONTRIBUTING.md#pull-request-ownership-and-readiness. --> Human owner: @YunzhouLi-hub - [x] 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. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. Two review threads remain open on the branch and are addressed by `d0e87ba7b`: - [CodeRabbit thread](https://github.com/morluto/rea/pull/2630#discussion_r4255277931) — the `!vendor/keep.ts` example had no effect, because a negation cannot re-include a file whose parent directory was excluded. Replaced with `!keep.generated` after `*.generated`, whose parent stays traversable, and the skipped-parent limitation is now documented. - [Greptile thread](https://github.com/morluto/rea/pull/2630#discussion_r4255475395) — same defect, same fix. Both were fixed at the producing boundary (the documented example) rather than by changing Git-ignore semantics. The final diff is three files, 378 insertions, 2 deletions, with no unrelated cleanup. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
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#5293
No description provided.