Skip to content

feat: M8 Spit Shine + M9 Cockpit — code review polish & CLI improvements#11

Merged
flyingrobots merged 6 commits intomainfrom
feat/m8-spit-shine
Feb 28, 2026
Merged

feat: M8 Spit Shine + M9 Cockpit — code review polish & CLI improvements#11
flyingrobots merged 6 commits intomainfrom
feat/m8-spit-shine

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Feb 28, 2026

Summary

  • M8 Spit Shine: Crypto port refactor (extract shared helpers to CryptoPort base class), key validation hardening, roadmap cleanup (purge completed tasks, add STATUS.md, ADR-001)
  • M9 Cockpit: runAction error handler, --json output mode for all commands, verify command, vault list --filter + table formatting
  • Code review fixes (12 issues): process.exitCode over process.exit, vault info JSON encryption metadata, Uint8Array acceptance in NodeCryptoAdapter, matchGlob ReDoS guard, non-Error throw handling, redundant _validateKey removal, ADR method name corrections

Test plan

  • npm test — 616 tests pass
  • npx eslint . — 0 errors
  • node bin/git-cas.js --help — spot-check CLI output
  • Pre-push hooks (lint + full test suite) pass

Summary by CodeRabbit

Release Notes

  • New Features

    • Added --json flag across commands for structured output
    • Introduced verify command for asset integrity checks
    • Enhanced vault list with --filter pattern support
    • vault info now displays encryption metadata with --encryption flag
  • Documentation

    • Added documentation section to README with API and architecture links
    • Created COMPLETED_TASKS.md, STATUS.md, and ADR-001
    • Updated CHANGELOG.md, ROADMAP.md, and added GRAVEYARD.md
  • Tests

    • Added CLI action, vault list utilities, and CryptoPort test suites

…leanup

- Refactor CryptoPort and all adapters (Node, Bun, Web) for key validation
- Move completed task cards to COMPLETED_TASKS.md, superseded to GRAVEYARD.md
- Add STATUS.md, ADR-001, and documentation links to README
- Update ROADMAP.md to reflect current milestone state
… formatting

- Add bin/actions.js with runAction() error wrapper and actionable HINTS
- Replace all 10 identical catch blocks with runAction()
- Add global --json flag with structured JSON output for 8 commands
- Add verify command for integrity checking (exit 0/1)
- Add vault list --filter with glob matching and TTY-aware table formatting
- Convert validateRestoreFlags and vault history validation to throw
Replace process.exit(1) with exitCode, include encryption in vault
info JSON, accept Uint8Array in NodeCryptoAdapter, guard matchGlob
pattern length, harden writeError for non-Error throws, remove
redundant _validateKey call, fix ADR-001 method names, update test
count and CHANGELOG.
@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

Warning

Rate limit exceeded

@flyingrobots has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 44 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 1b16f6e and 1c1e3d0.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • STATUS.md
  • bin/actions.js
  • bin/git-cas.js
  • bin/ui/vault-list.js
  • src/infrastructure/adapters/NodeCryptoAdapter.js
  • src/infrastructure/adapters/WebCryptoAdapter.js
  • test/unit/cli/actions.test.js
  • test/unit/cli/vault-list.test.js
📝 Walkthrough

Walkthrough

This PR introduces comprehensive fixes and refactoring across documentation, CLI infrastructure, and cryptographic services. Key changes include: centralizing CLI error handling with JSON output support, delegating key validation from CasService to CryptoPort, refactoring crypto adapter KDF flows via _doDeriveKey hooks, implementing vault list utilities with glob pattern matching, and updating documentation to reflect completed milestones and architectural patterns.

Changes

Cohort / File(s) Summary
Documentation & Status
CHANGELOG.md, COMPLETED_TASKS.md, GRAVEYARD.md, README.md, ROADMAP.md, STATUS.md, docs/ADR-001-vault-in-facade.md
Comprehensive documentation updates reflecting M8 code review fixes, completed milestones M13–M14, superseded tasks, roadmap progression, and new architectural decision record for vault service composition.
CLI Error Handling
bin/actions.js
New module introducing structured error output with writeError function (text/JSON formatting), error hints mapping, and runAction wrapper for standardized command execution and error handling.
CLI Commands & JSON Output
bin/git-cas.js
Refactored CLI commands to support global --json flag with JSON output for vault list/init/dashboard/history, store/tree/inspect operations, restore progress, and new verify command; replaced inline try-catch with centralized error handling via runAction.
Vault List Utilities
bin/ui/vault-list.js
New module exporting matchGlob (glob-to-regex conversion with ReDoS protection), filterEntries, formatTable, and formatTabSeparated for vault list display and filtering.
CAS Service
src/domain/services/CasService.js
Removed private _validateKey method; key validation now delegated to CryptoPort via crypto._validateKey at call sites in store() and _resolveEncryptionKey().
Crypto Port
src/ports/CryptoPort.js
Added _validateKey helper enforcing 32-byte Buffer/Uint8Array keys; added _buildMeta for metadata construction; refactored deriveKey to normalize parameters, generate/validate salt, and invoke adapter-specific _doDeriveKey.
Crypto Adapters
src/infrastructure/adapters/BunCryptoAdapter.js, NodeCryptoAdapter.js, WebCryptoAdapter.js
Replaced public deriveKey with internal _doDeriveKey(passphrase, saltBuf, params) hook; updated key validation and metadata construction to use underscore-prefixed methods (_validateKey, _buildMeta); adjusted encryption streams and KDF flows to consume normalized parameter objects.
CLI & Port Tests
test/unit/cli/actions.test.js, test/unit/cli/vault-list.test.js, test/unit/ports/CryptoPort.test.js
Added comprehensive unit tests for error handling, vault list utilities, and CryptoPort methods including key validation, metadata building, and parameter normalization in key derivation flows.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through crypto caves,
Validating keys in cleaner ways,
CLI blooms with JSON light,
Vault lists sorted, patterns bright,
Refactored flows, no more delays! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main changes: M8 Spit Shine (crypto refactoring and polish), M9 Cockpit (CLI improvements), and code review polish.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/m8-spit-shine

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

@flyingrobots, I'll review the changes right away! 🐰

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
test/unit/cli/vault-list.test.js (1)

4-34: Consider adding test for pattern length limit.

The CHANGELOG mentions that matchGlob rejects patterns longer than 200 characters to prevent ReDoS. Consider adding a test case to verify this behavior:

it('rejects patterns longer than 200 characters', () => {
  const longPattern = 'a'.repeat(201);
  expect(() => matchGlob(longPattern, 'test')).toThrow();
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/cli/vault-list.test.js` around lines 4 - 34, Add a unit test that
verifies matchGlob enforces the 200-character pattern limit by asserting that
passing a pattern longer than 200 characters throws; create a test case (e.g.,
"rejects patterns longer than 200 characters") that builds a string of length
201 (e.g., 'a'.repeat(201)) and calls matchGlob(longPattern, 'test') inside an
expect(...).toThrow() assertion so the behavior is covered in the matchGlob
tests.
STATUS.md (1)

26-39: Task checkboxes may need updating post-merge.

The M8 and M9 task items are shown as unchecked ([ ]), but this PR implements these features. Consider updating the checkboxes to [x] after merging, or clarify if STATUS.md is meant to reflect pre-merge state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@STATUS.md` around lines 26 - 39, The task checklist in STATUS.md still shows
M8 and M9 items as unchecked although this PR implements them; update the three
affected checkboxes to checked (change "- [ ] **8.2** Extract shared crypto
helpers to CryptoPort base class", "- [ ] **8.3** README polish and
architectural decision record (ADR-001)", "- [ ] **9.2** CLI `verify` command
(`git cas verify --slug <slug>`)","- [ ] **9.3** CLI `--json` output mode
(structured JSON for all commands)","- [ ] **9.4** CLI error handler DRY cleanup
+ actionable error messages","- [ ] **9.5** Vault list filtering (`--filter`)
and table formatting" to "- [x] ..." as appropriate, or add a short note
clarifying that STATUS.md represents pre-merge state if you prefer not to mark
them; commit the change to STATUS.md in this PR so the merged README reflects
the implemented work.
src/infrastructure/adapters/NodeCryptoAdapter.js (2)

68-80: Orphaned comment on line 68.

Line 68 has an incomplete JSDoc-style comment that appears orphaned—it describes delegation to the base class but doesn't attach to any method. The actual _doDeriveKey method has its own @override tag on line 70.

🧹 Proposed fix to remove the orphaned comment
-  /** `@override` – delegate to base class which accepts both Buffer and Uint8Array */
-
   /** `@override` */
   async _doDeriveKey(passphrase, saltBuf, { algorithm, iterations, cost, blockSize, parallelization, keyLength }) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/infrastructure/adapters/NodeCryptoAdapter.js` around lines 68 - 80, The
file has an orphaned JSDoc comment above the _doDeriveKey method; remove the
stray comment (or merge its text into the existing `@override` JSDoc for the async
_doDeriveKey function) so the documentation properly attaches to the
method—update NodeCryptoAdapter.js by deleting the incomplete comment preceding
async _doDeriveKey or consolidating it into the method's JSDoc block.

70-80: Verify _doDeriveKey should be async despite synchronous promisify returns.

The method is not marked async, yet it returns promises from promisify(pbkdf2)(...) and promisify(scrypt)(...). This works correctly since the base class likely awaits the result, but adding async would make the intent clearer and ensure consistent error wrapping in a rejected promise if synchronous code before the return throws.

✨ Proposed improvement for clarity
   /** `@override` */
-  _doDeriveKey(passphrase, saltBuf, { algorithm, iterations, cost, blockSize, parallelization, keyLength }) {
+  async _doDeriveKey(passphrase, saltBuf, { algorithm, iterations, cost, blockSize, parallelization, keyLength }) {
     if (algorithm === 'pbkdf2') {
       return promisify(pbkdf2)(passphrase, saltBuf, iterations, keyLength, 'sha512');
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/infrastructure/adapters/NodeCryptoAdapter.js` around lines 70 - 80, _mark
_doDeriveKey_ should be declared async and should await the promisified
operations to ensure synchronous thrown errors are wrapped as rejected promises;
update the _doDeriveKey_ implementation to use the async keyword (if not
already) and change the returns to await promisify(pbkdf2)(...) and await
promisify(scrypt)(...) so any exceptions from promisify/pbkdf2/scrypt are
consistently propagated as promise rejections.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/actions.js`:
- Around line 47-50: The catch block in runAction currently calls
process.exit(1); change it to set process.exitCode = 1 instead to match the
verify command pattern — locate the catch in the runAction handler where
writeError(err, getJson()) is called and replace the explicit process.exit call
with process.exitCode = 1 so the handler signals failure without forcing an
immediate exit.

In `@bin/git-cas.js`:
- Around line 108-133: The CLI currently ignores opts.force unless opts.tree is
set, which can mislead users; update the command handler in the runAction async
callback to validate flags early: if opts.force is true and opts.tree is
false/undefined, throw an error or call process.exit(1) with a clear message
(e.g., "The --force flag requires --tree") before calling cas.storeFile;
reference the options used in the handler (opts.force, opts.tree) and perform
the check near the start of the action callback so users fail fast rather than
silently having --force ignored.
- Around line 374-390: The vault history command currently always prints
human-readable output; update the action handler around GitPlumbing.execute (in
the runAction callback that uses ShellRunnerFactory.create and
ContentAddressableStore.VAULT_REF) to respect the global/command --json flag:
detect JSON mode (check opts.json and/or program.opts().json) and when true emit
structured JSON to stdout instead of calling renderHistoryTimeline — either by
parsing the git log output into an array of commit objects (commit id, message,
date) and writing JSON.stringify(...) or by wrapping the raw output in a JSON
field like { history: [...] } if a parser isn’t available; otherwise preserve
the current pretty/plain branches for non-JSON mode.
- Around line 176-188: The inspect command currently chooses a pretty TTY view
whenever stdout.isTTY is true, which ignores a global JSON flag; update the
output selection in the async handler passed to runAction so that opts.json (or
the global JSON flag on opts) takes precedence: keep the heatmap branch
(renderHeatmap), then if opts.json produce the JSON string of manifest.toJSON(),
otherwise if process.stdout.isTTY use renderManifestView, else fall back to
JSON; adjust the branches around renderManifestView/JSON in the anonymous async
function (the block that calls createCas, cas.readManifest and writes to
process.stdout) so JSON mode is honored even in TTY.

In `@bin/ui/vault-list.js`:
- Around line 17-22: The current glob-to-regex replacement uses .replace(/\?/g,
'.') which allows '?' to match '/', letting a single-segment wildcard cross path
separators; change the replacement so '?' becomes a single non-separator token
(e.g., use '[^/]' instead of '.'). Update the code that constructs the RegExp
(the chain of .replace calls that includes .replace(/\?/g, '.')) to use
.replace(/\?/g, '[^/]'). Keep the other replacements (escaping, ** -> .*, * ->
[^/]* via the existing logic) unchanged so '**' still spans segments while '*'
and '?' do not cross '/'.

---

Nitpick comments:
In `@src/infrastructure/adapters/NodeCryptoAdapter.js`:
- Around line 68-80: The file has an orphaned JSDoc comment above the
_doDeriveKey method; remove the stray comment (or merge its text into the
existing `@override` JSDoc for the async _doDeriveKey function) so the
documentation properly attaches to the method—update NodeCryptoAdapter.js by
deleting the incomplete comment preceding async _doDeriveKey or consolidating it
into the method's JSDoc block.
- Around line 70-80: _mark _doDeriveKey_ should be declared async and should
await the promisified operations to ensure synchronous thrown errors are wrapped
as rejected promises; update the _doDeriveKey_ implementation to use the async
keyword (if not already) and change the returns to await promisify(pbkdf2)(...)
and await promisify(scrypt)(...) so any exceptions from promisify/pbkdf2/scrypt
are consistently propagated as promise rejections.

In `@STATUS.md`:
- Around line 26-39: The task checklist in STATUS.md still shows M8 and M9 items
as unchecked although this PR implements them; update the three affected
checkboxes to checked (change "- [ ] **8.2** Extract shared crypto helpers to
CryptoPort base class", "- [ ] **8.3** README polish and architectural decision
record (ADR-001)", "- [ ] **9.2** CLI `verify` command (`git cas verify --slug
<slug>`)","- [ ] **9.3** CLI `--json` output mode (structured JSON for all
commands)","- [ ] **9.4** CLI error handler DRY cleanup + actionable error
messages","- [ ] **9.5** Vault list filtering (`--filter`) and table formatting"
to "- [x] ..." as appropriate, or add a short note clarifying that STATUS.md
represents pre-merge state if you prefer not to mark them; commit the change to
STATUS.md in this PR so the merged README reflects the implemented work.

In `@test/unit/cli/vault-list.test.js`:
- Around line 4-34: Add a unit test that verifies matchGlob enforces the
200-character pattern limit by asserting that passing a pattern longer than 200
characters throws; create a test case (e.g., "rejects patterns longer than 200
characters") that builds a string of length 201 (e.g., 'a'.repeat(201)) and
calls matchGlob(longPattern, 'test') inside an expect(...).toThrow() assertion
so the behavior is covered in the matchGlob tests.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3654c6a and 1b16f6e.

📒 Files selected for processing (18)
  • CHANGELOG.md
  • COMPLETED_TASKS.md
  • GRAVEYARD.md
  • README.md
  • ROADMAP.md
  • STATUS.md
  • bin/actions.js
  • bin/git-cas.js
  • bin/ui/vault-list.js
  • docs/ADR-001-vault-in-facade.md
  • src/domain/services/CasService.js
  • src/infrastructure/adapters/BunCryptoAdapter.js
  • src/infrastructure/adapters/NodeCryptoAdapter.js
  • src/infrastructure/adapters/WebCryptoAdapter.js
  • src/ports/CryptoPort.js
  • test/unit/cli/actions.test.js
  • test/unit/cli/vault-list.test.js
  • test/unit/ports/CryptoPort.test.js

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

📝 Walkthrough

Walkthrough

This pull request introduces CLI enhancements with centralized error handling, adds structured documentation for project milestones and completions, refactors cryptographic key validation from CasService to CryptoPort, and implements new utility modules for vault listing, filtering, and formatting.

Changes

Cohort / File(s) Summary
Documentation & Metadata
CHANGELOG.md, COMPLETED_TASKS.md, GRAVEYARD.md, README.md, ROADMAP.md, STATUS.md
Added project status tracking, milestone completion records, task graveyard entries, and documentation links. ROADMAP significantly restructured with task status consolidation.
Architecture Documentation
docs/ADR-001-vault-in-facade.md
New ADR documenting Vault as separate domain service composed by facade, with VaultService handling multi-asset lifecycle and CasService retained for content-addressed storage.
CLI Error Handling
bin/actions.js
New module introducing writeError() for structured error output with hints, HINTS map for common error codes, and runAction() wrapper for centralized async action execution and error handling.
CLI Core & Commands
bin/git-cas.js
Added JSON-first output mode via --json flag across commands, new verify command for asset integrity checking, enhanced vault info with optional encryption metadata, vault list with filtering (--filter) and table/tab-separated formatting, and unified error handling via runAction wrappers.
CLI UI Utilities
bin/ui/vault-list.js
New module with matchGlob() for glob pattern matching (max 200 chars), filterEntries() for entry filtering, formatTable() for aligned table output, and formatTabSeparated() for pipe-friendly output.
Domain Services
src/domain/services/CasService.js
Removed private _validateKey() method; key validation now delegated to CryptoPort._validateKey() across all encryption and key resolution paths.
Crypto Port
src/ports/CryptoPort.js
Added _validateKey() for 32-byte key enforcement, _buildMeta() for metadata construction with base64-encoded values, new _doDeriveKey() adapter hook, and refactored deriveKey() with support for pbkdf2 and scrypt with normalized parameters.
Crypto Adapters
src/infrastructure/adapters/NodeCryptoAdapter.js, BunCryptoAdapter.js, WebCryptoAdapter.js
Aligned key validation and metadata construction with CryptoPort base methods (_validateKey, _buildMeta); replaced public deriveKey() with private _doDeriveKey() override; updated encryption metadata to use base64-encoded nonce and tag.
Tests
test/unit/cli/actions.test.js, test/unit/cli/vault-list.test.js, test/unit/ports/CryptoPort.test.js
Added comprehensive test coverage for error handling (writeError, runAction), vault list utilities (glob matching, filtering, formatting), and CryptoPort key validation and key derivation flows.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through code so clean,
New errors handled, never seen.
With vault lists filtered, formatted bright,
And crypto keys validated right,
The CLI blooms with JSON light!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the two main milestone features (M8 Spit Shine and M9 Cockpit) and their primary focus areas (code review polish and CLI improvements), which align with the substantial changes across crypto port refactoring, CLI enhancements, and documentation updates.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/m8-spit-shine

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- runAction: process.exitCode = 1 instead of process.exit(1)
- store: --force without --tree now fails fast
- inspect: --json honored in TTY mode
- vault history: --json emits structured array
- matchGlob: ? no longer matches / separator
- NodeCryptoAdapter: remove orphaned comment, await in _doDeriveKey
- Tests: pattern length guard, ? separator, exitCode assertions
@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

@flyingrobots, I'll review the changes for you! 🥕🐰

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
bin/ui/vault-list.js (1)

16-22: ⚠️ Potential issue | 🟡 Minor

? wildcard should not match / in slug globs.

Line 21 maps ? to ., so single-character matches can cross path separators. Use a non-separator token instead.

🔧 Proposed fix
-    .replace(/\?/g, '.');
+    .replace(/\?/g, '[^/]');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/ui/vault-list.js` around lines 16 - 22, The current glob-to-regex
transformation in vault-list.js builds `escaped` and maps `?` to `.` which
allows matching a `/`; change the `?` mapping so it does not match path
separators by replacing the `.replace(/\?/g, '.')` step with a mapping that
forbids `/` (e.g. `.replace(/\?/g, '[^/]')`) so the RegExp built from `escaped`
will treat `?` as “any single non-separator character” when testing `str`.
bin/actions.js (1)

47-50: ⚠️ Potential issue | 🟡 Minor

Use process.exitCode instead of process.exit(1) in runAction.

Line 49 still performs an immediate exit. Please set process.exitCode = 1 so command cleanup/output flushing remains consistent with the rest of the CLI error path.

🔧 Proposed fix
     } catch (err) {
       writeError(err, getJson());
-      process.exit(1);
+      process.exitCode = 1;
     }
#!/bin/bash
# Verify exit signaling style in the action wrapper and its tests.
rg -n "process\.exitCode|process\.exit\(" bin/actions.js test/unit/cli/actions.test.js
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/actions.js` around lines 47 - 50, In the catch block of the action
wrapper (the runAction error handler) replace the immediate termination call
process.exit(1) with setting process.exitCode = 1 so that writeError(err,
getJson()) can finish flushing and normal cleanup runs; locate the catch that
calls writeError and swap process.exit(1) for process.exitCode = 1 (leave
writeError and getJson() unchanged).
🧹 Nitpick comments (2)
test/unit/cli/vault-list.test.js (1)

4-34: Consider adding a test for the pattern length guard.

The matchGlob implementation rejects patterns longer than 200 characters to prevent ReDoS (per CHANGELOG entry M2), but there's no test verifying this behavior.

📝 Suggested test case
   it('handles exact match', () => {
     expect(matchGlob('exact', 'exact')).toBe(true);
     expect(matchGlob('exact', 'other')).toBe(false);
   });
+
+  it('throws for patterns exceeding 200 characters', () => {
+    const longPattern = 'a'.repeat(201);
+    expect(() => matchGlob(longPattern, 'test')).toThrow(/too long/);
+  });
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/cli/vault-list.test.js` around lines 4 - 34, Add a unit test in the
vault-list.test.js suite that verifies matchGlob enforces the pattern-length
guard: construct a pattern string longer than 200 characters (e.g., repeat 'a'
201 times or build a long glob) and assert matchGlob(longPattern, 'anything')
returns false (and optionally add a complementary assertion that a 200-character
pattern is accepted if desired); reference the matchGlob function in the new
test so the rejection behavior is covered.
src/infrastructure/adapters/NodeCryptoAdapter.js (1)

68-69: Remove orphan comment.

Line 68 contains an orphan JSDoc comment that doesn't precede any method. It appears to be a leftover from removed code (likely the old deriveKey method that now delegates to the base class).

♻️ Proposed fix
-  /** `@override` – delegate to base class which accepts both Buffer and Uint8Array */
-
   /** `@override` */
   async _doDeriveKey(passphrase, saltBuf, { algorithm, iterations, cost, blockSize, parallelization, keyLength }) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/infrastructure/adapters/NodeCryptoAdapter.js` around lines 68 - 69,
Remove the orphan JSDoc comment that sits by itself (the "override – delegate to
base class which accepts both Buffer and Uint8Array") in the NodeCryptoAdapter
class; it no longer precedes any method (the old deriveKey was
removed/delegated), so delete that standalone comment near the deriveKey-related
area to avoid confusion and keep comments aligned with their methods (search for
NodeCryptoAdapter and deriveKey to locate the orphan).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/infrastructure/adapters/WebCryptoAdapter.js`:
- Around line 87-89: The encrypt async generator uses a shared variable finalTag
and the finalize() path can encode a null finalTag if the encryption stream
hasn't been fully consumed; update the encrypt function (and any finalize()
helper it uses) to track completion (e.g., an internal boolean like isConsumed
or finalTagSet) and refuse to run finalize() until the generator has emitted its
end (throw a consistent STREAM_NOT_CONSUMED error when finalize() is called
early), ensuring you only call the encoder on a non-null finalTag and set/clear
the guard when the stream finishes in the encrypt generator.

---

Duplicate comments:
In `@bin/actions.js`:
- Around line 47-50: In the catch block of the action wrapper (the runAction
error handler) replace the immediate termination call process.exit(1) with
setting process.exitCode = 1 so that writeError(err, getJson()) can finish
flushing and normal cleanup runs; locate the catch that calls writeError and
swap process.exit(1) for process.exitCode = 1 (leave writeError and getJson()
unchanged).

In `@bin/ui/vault-list.js`:
- Around line 16-22: The current glob-to-regex transformation in vault-list.js
builds `escaped` and maps `?` to `.` which allows matching a `/`; change the `?`
mapping so it does not match path separators by replacing the `.replace(/\?/g,
'.')` step with a mapping that forbids `/` (e.g. `.replace(/\?/g, '[^/]')`) so
the RegExp built from `escaped` will treat `?` as “any single non-separator
character” when testing `str`.

---

Nitpick comments:
In `@src/infrastructure/adapters/NodeCryptoAdapter.js`:
- Around line 68-69: Remove the orphan JSDoc comment that sits by itself (the
"override – delegate to base class which accepts both Buffer and Uint8Array") in
the NodeCryptoAdapter class; it no longer precedes any method (the old deriveKey
was removed/delegated), so delete that standalone comment near the
deriveKey-related area to avoid confusion and keep comments aligned with their
methods (search for NodeCryptoAdapter and deriveKey to locate the orphan).

In `@test/unit/cli/vault-list.test.js`:
- Around line 4-34: Add a unit test in the vault-list.test.js suite that
verifies matchGlob enforces the pattern-length guard: construct a pattern string
longer than 200 characters (e.g., repeat 'a' 201 times or build a long glob) and
assert matchGlob(longPattern, 'anything') returns false (and optionally add a
complementary assertion that a 200-character pattern is accepted if desired);
reference the matchGlob function in the new test so the rejection behavior is
covered.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3654c6a and 1b16f6e.

📒 Files selected for processing (18)
  • CHANGELOG.md
  • COMPLETED_TASKS.md
  • GRAVEYARD.md
  • README.md
  • ROADMAP.md
  • STATUS.md
  • bin/actions.js
  • bin/git-cas.js
  • bin/ui/vault-list.js
  • docs/ADR-001-vault-in-facade.md
  • src/domain/services/CasService.js
  • src/infrastructure/adapters/BunCryptoAdapter.js
  • src/infrastructure/adapters/NodeCryptoAdapter.js
  • src/infrastructure/adapters/WebCryptoAdapter.js
  • src/ports/CryptoPort.js
  • test/unit/cli/actions.test.js
  • test/unit/cli/vault-list.test.js
  • test/unit/ports/CryptoPort.test.js

Throw STREAM_NOT_CONSUMED error if finalize() is called before the
encrypt stream is fully consumed, preventing null finalTag encoding.
@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

@flyingrobots, I'll review the changes for you! 🥕🐰

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@flyingrobots flyingrobots merged commit 9b7f02f into main Feb 28, 2026
6 checks passed
@flyingrobots flyingrobots deleted the feat/m8-spit-shine branch February 28, 2026 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant