Skip to content

[codex] harden v6 vault service decomposition#36

Merged
flyingrobots merged 49 commits intomainfrom
cycle/vault-service-decomposition
May 9, 2026
Merged

[codex] harden v6 vault service decomposition#36
flyingrobots merged 49 commits intomainfrom
cycle/vault-service-decomposition

Conversation

@flyingrobots
Copy link
Copy Markdown
Member

@flyingrobots flyingrobots commented May 9, 2026

Summary

This PR prepares the v6.0.0 vault/service hardening branch for review. It decomposes the vault implementation into cohesive collaborators, centralizes domain error codes, tightens diagnostic behavior, and updates the public and maintainer documentation for the v6 security posture.

Key changes include:

  • Slimmed VaultService into an orchestrator backed by VaultPersistence, VaultStateCache, VaultMetadataCodec, VaultTreeCodec, VaultPrivacyIndex, VaultKeyVerifier, and VaultMutationRetryPolicy.
  • Preserved vault tree compatibility by keeping slug/tree path encoding behind the Slug value object and codecs.
  • Hardened vault diagnostics so corrupt/unreadable vault heads fail as VAULT_HEAD_INVALID instead of being treated as missing vaults.
  • Made privacy vault reads fail closed when .privacy-index does not cover every HMAC tree entry.
  • Added doctor command support for privacy vault credentials across human CLI, agent CLI, and TUI flows.
  • Centralized error codes and re-exported public error handling surfaces.
  • Updated README, API docs, upgrading guidance, security notes, changelog, and vault internals docs.

Why

The v6 release branch had accumulated security and architecture blockers around vault responsibilities, error consistency, and diagnostics. The root cause was that VaultService owned too many concerns at once: Git persistence details, state caching, metadata decoding, privacy index resolution, key verification, and retry timing. That made correctness-sensitive behavior harder to isolate and test.

This PR moves those concerns into explicit collaborators and adds behavior-level regression coverage for the release blockers.

Impact

  • Public vault APIs remain compatible.
  • Existing vault on-disk format is preserved.
  • Privacy-mode diagnostics now require the same key material as privacy-mode listing and state reads.
  • Corrupt vault heads and privacy index mismatches are reported as explicit failures instead of silent partial success.
  • Consumers can import CasError from the package root for typed error handling.

Validation

  • npx eslint .
  • npm test — 183 test files, 1598 passed, 2 skipped
  • Pre-push hook reran lint and unit tests successfully before pushing cycle/vault-service-decomposition.

Summary by CodeRabbit

  • New Features

    • Enforced restore-path safety: restoreFile requires baseDirectory and prevents escapes.
    • Doctor/diagnostics: doctor accepts vault key/passphrase sources for encrypted inspections.
    • Storage controls: per-operation merkleThreshold and default maxBlobSize (10 MiB).
    • CasError exported from package root with optional documentationUrl.
  • Bug Fixes

    • Docker CLI version fallback; stabilized passphrase rotation/tests; improved restore output authority and byte-level dedupe metrics.
  • Breaking Changes

    • restoreFile signature now requires baseDirectory; KDF algorithms limited to pbkdf2/scrypt; some byte APIs moved to Uint8Array.
  • Documentation

    • Added Vault internals guide and updated API, upgrade, README, and changelog.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

📝 Walkthrough

Walkthrough

Large refactor: vault internals decomposed into collaborators with retry/backoff; unified ErrorCodes and CasError (options/toJSON); added CLI/agent/UI “doctor” with encryption-key threading; enforced restore baseDirectory; added per-operation merkleThreshold; adapters gained blob limits; extensive docs and tests updated.

Vault, Errors, CLI/UI, and Docs

Layer / File(s) Summary
Contracts and Types
index.d.ts, src/domain/errors/*, src/ports/*, index.js, docs/API.md
Adds ErrorCodes enum, expands CasError (options, documentationUrl, toJSON), updates GitRefPort (expectedOldOid) and persistence maxBlobSize, re-exports CasError, documents new options and error codes.
Core Vault and CAS
src/domain/services/Vault*.js, CasService.js, ManifestRepository.js, helpers/*, value-objects/*
Decomposes VaultService to Persistence/StateCache/Tree+Metadata codecs/PrivacyIndex/KeyVerifier/RetryPolicy; standardizes error codes; adds merkleThreshold propagation; rotates passphrase via verified metadata.
Infrastructure Adapters
src/infrastructure/adapters/*
GitPersistenceAdapter limit and setMaxBlobSize; GitRefAdapter CAS semantics; Crypto adapters switch to ErrorCodes; FileIOHelper enforces restore baseDirectory containment.
CLI/Agent/UI
bin/*
Introduces doctor command module, normalizes error payload (documentationUrl), resolves diagnostic encryption key, threads encryptionKey through UI and stats; restore output targeting helper.
Docs
ARCHITECTURE.md, README.md, SECURITY.md, UPGRADING.md, docs/*
Documents vault internals, restore boundary, merkleThreshold overrides, security policies, and release notes.
Tests
test/**/*
Adds broad coverage: vault collaborators, privacy index coverage, retry policy, adapters’ limits, CLI doctor, restore boundary, docs truth, and facade error surface.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI
  participant AgentDoctor
  participant VaultService
  participant GitAdapter
  User->>CLI: git cas doctor [--key-file|--vault-passphrase]
  CLI->>AgentDoctor: invoke(args, stdin, session)
  AgentDoctor->>VaultService: inspectVaultHealth({ encryptionKey? })
  VaultService->>GitAdapter: readState / readTree / readBlob (maxBytes)
  GitAdapter-->>VaultService: tree/meta/blob bytes
  VaultService-->>AgentDoctor: report { status, stats, issues }
  AgentDoctor-->>CLI: exitCode, { report }
Loading

Estimated code review effort:
🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs:

  • git-stunts/git-cas#6 — Adds per-operation merkleThreshold across CAS APIs and ManifestRepository, overlapping updated paths here.
  • git-stunts/git-cas#24 — Modifies dashboard-cmds and vault-report; related to doctor flow and stats changes.
  • git-stunts/git-cas#13 — Touches rotateKey/rotateVaultPassphrase logic also revised in this refactor.

A rabbit mends the vault with care,
Keys verified in salted air;
Doctor hums through chunks and trees,
Bytes dedupe, ratios please.
Errors speak one tongue at last—
Safe paths chosen, dangers passed. 🐇✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cycle/vault-service-decomposition

@flyingrobots
Copy link
Copy Markdown
Member Author

Feedback/CI resolution summary:

Issue Resolution SHA Outcome
Docker runtime checks emitted 6.0.0+unknown from git-cas --version when the image had neither .git metadata nor a stamped package SHA. 3274738 resolveVersionString() now treats the unknown sentinel as absent build metadata and falls back to plain semver. Added a regression test in test/unit/cli/build-version.test.js.
Changelog entry for the Docker version fallback. 317d28c Documented the Docker version fallback under CHANGELOG.md Unreleased Fixed.
Docker full-unit runs exposed order/load-sensitive Git subprocess failures in rotateVaultPassphrase unit coverage. 22c7cfa Added MemoryRefAdapter regression coverage and moved passphrase-rotation domain tests onto in-memory persistence/ref ports so the unit suite validates domain behavior without Git scheduling noise.
Changelog entry for vault rotation test isolation. 0846532 Documented the Docker unit-test stability fix under CHANGELOG.md Unreleased Fixed.

Verification completed before push:

Command Result
npx eslint . Pass
npm test Pass: 184 files, 1,601 passing, 2 skipped
npm run test:node Pass: 184 files, 1,600 passing, 3 skipped
npm run test:bun Pass: 184 files, 1,600 passing, 3 skipped
npm run test:deno Pass: 184 files, 1,591 passing, 12 skipped
pre-push lint/unit gate Pass

No unresolved inline review threads were present when processed; the visible PR feedback was the draft-skip CodeRabbit notice plus failing CI. CI is rerunning on the pushed branch.

@flyingrobots
Copy link
Copy Markdown
Member Author

Feedback Resolution Summary

All unresolved inline review threads have been replied to and marked resolved. Final pushed head is 7f959de.

Feedback item Commit(s) Outcome
Guard agent diagnostic passphrase resolver 33861e5 Added RED credential-resolution coverage; missing resolver now fails through the injected error factory instead of raw TypeError.
Byte dedupe ratio semantics f77b752 Added RED vault-report coverage; byte dedupe now uses totalChunkBytes / uniqueChunkBytes.
maxBlobSize API wording 7c00006 Added docs truth coverage; facade docs now say metadata blob reads.
ManifestDiff JSDoc typedef c8b4247 Added declaration accuracy coverage and local Manifest typedef.
Malformed vault encryption metadata 0a163a7 Added codec RED coverage; present-but-falsy encryption now fails closed with VAULT_METADATA_INVALID.
Vault ref update failure classification cbc2465 Added persistence RED coverage; generic update failures now use VAULT_REF_UPDATE_FAILED, not retryable VAULT_CONFLICT.
Git missing-ref structure/fallback 78880f5, 7f959de Structured missing-ref errors at adapter boundary, plus source/doc coverage for the C/English-locale best-effort fallback.
Privacy index metadata guard 040c40f Added privacy RED coverage; missing privacy.indexMeta now fails as VAULT_PRIVACY_INDEX_INVALID before decrypt.
Restore symlink boundary 01949cc Added FileIO RED coverage; restore paths are canonicalized with realpath before boundary checks.
VaultMetadata privacy docs 2ce0971 Added docs truth coverage for the optional privacy metadata shape and privacy errors.
Git blob per-call limit validation e543886 Added adapter RED coverage; invalid readBlob(maxBytes) values now fail with INVALID_OPTIONS.
Empty CLI restore output path bd0b9c2 Added CLI RED coverage; empty --out now fails with INVALID_OPTIONS.
Metadata snapshot fallback docs 464148d Added source-doc guard documenting why iterator metadata reads do not seed VaultStateCache.
VaultService constructor injection guard 8f4fb89 Added constructor RED coverage; mixed vaultPersistence and legacy persistence/ref injection is rejected.
Verifier memoization 7e60f23 Added verifier RED coverage; cached list/resolve/mutation paths reuse verified-key proofs.
Privacy cache concurrent dedupe 2ffe288 Added concurrency RED coverage; concurrent same-key privacy reads share one in-flight index resolution.
Bounded VaultStateCache 012baef Added LRU/capacity RED coverage; cache now has a validated default capacity and LRU eviction.

Verification on final head 7f959de:

Command Result
npx eslint . PASS
npm test PASS: 184 files, 1627 passed, 2 skipped
npm run release:verify -- --skip-jsr PASS: 12/12 steps, 5335 observed tests; JSR dry-run skipped
PR review threads 0 unresolved, non-outdated threads after replies/resolution

@coderabbitai review please

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

@flyingrobots That's a thorough resolution pass — 17 items addressed, all with dedicated RED coverage commits, and the verification table looks solid across the board (1627 passing, release:verify 12/12, clean lint). Kicking off the review on 7f959de now. 🥕

✅ 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
Copy Markdown

@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

🧹 Nitpick comments (2)
test/unit/vault/VaultService.privacy.test.js (1)

474-499: ⚡ Quick win

Prefer ErrorCodes constants in new assertions instead of raw strings.

New tests assert 'VAULT_PRIVACY_INDEX_INVALID' as a literal even though ErrorCodes is already imported. Using constants here reduces drift/typo risk as error surfaces evolve.

Suggested patch
-    await expect(vault.readState({ encryptionKey: TEST_KEY })).rejects.toMatchObject({
-      code: 'VAULT_PRIVACY_INDEX_INVALID',
+    await expect(vault.readState({ encryptionKey: TEST_KEY })).rejects.toMatchObject({
+      code: ErrorCodes.VAULT_PRIVACY_INDEX_INVALID,
       meta: { field: 'privacy.indexMeta' },
     });
@@
     await expect(vault.resolveVaultEntry({ slug: 'alpha', encryptionKey: TEST_KEY }))
       .rejects.toMatchObject({
-        code: 'VAULT_PRIVACY_INDEX_INVALID',
+        code: ErrorCodes.VAULT_PRIVACY_INDEX_INVALID,
         meta: { field: 'privacy.indexMeta' },
       });
@@
-    await expect(vault.readState({ encryptionKey: TEST_KEY })).rejects.toMatchObject({
-      code: 'VAULT_PRIVACY_INDEX_INVALID',
+    await expect(vault.readState({ encryptionKey: TEST_KEY })).rejects.toMatchObject({
+      code: ErrorCodes.VAULT_PRIVACY_INDEX_INVALID,
       meta: {
         unmatchedCount: 1,
         treeEntryCount: 2,
         resolvedCount: 1,
       },
     });
@@
-    await expect(vault.listVault({ encryptionKey: TEST_KEY })).rejects.toMatchObject({
-      code: 'VAULT_PRIVACY_INDEX_INVALID',
+    await expect(vault.listVault({ encryptionKey: TEST_KEY })).rejects.toMatchObject({
+      code: ErrorCodes.VAULT_PRIVACY_INDEX_INVALID,
       meta: {
         unmatchedCount: 1,
         treeEntryCount: 2,
         resolvedCount: 1,
       },
     });

Also applies to: 515-535

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/vault/VaultService.privacy.test.js` around lines 474 - 499, Replace
the hard-coded error code string 'VAULT_PRIVACY_INDEX_INVALID' in the test
assertions with the ErrorCodes constant to avoid drift; update the
expect(...).rejects.toMatchObject calls that reference the literal (e.g., the
assertions against vault.readState and vault.resolveVaultEntry) to use
ErrorCodes.VAULT_PRIVACY_INDEX_INVALID for the code field, and similarly change
the same literal occurrences noted around lines 515-535 to use the ErrorCodes
constant.
test/unit/types/declaration-accuracy.test.js (1)

52-60: ⚡ Quick win

Consider using regex patterns for more robust JSDoc matching.

The exact string matching with toContain will fail on benign formatting changes (e.g., double quotes in import paths, extra whitespace). The existing tests in this file use regex patterns with toMatch for flexibility (see lines 27–30 and 42). Adopting the same approach here would make the test more maintainable and consistent.

🔧 Suggested refactor using regex patterns
  it('keeps ManifestDiff parameter typedefs resolvable', () => {
    const source = read('src/domain/services/ManifestDiff.js');

-   expect(source).toContain(
-     '@typedef {import(\'../value-objects/Manifest.js\').default} Manifest',
-   );
-   expect(source).toContain('@param {Manifest} oldManifest');
-   expect(source).toContain('@param {Manifest} newManifest');
+   expect(source).toMatch(
+     /@typedef\s+\{import\(['"]\.\.\/value-objects\/Manifest\.js['"]\)\.default\}\s+Manifest/
+   );
+   expect(source).toMatch(/@param\s+\{Manifest\}\s+oldManifest/);
+   expect(source).toMatch(/@param\s+\{Manifest\}\s+newManifest/);
  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/unit/types/declaration-accuracy.test.js` around lines 52 - 60, The test
that verifies ManifestDiff JSDoc currently uses exact string checks with
toContain; change these assertions to use regex-based matching with toMatch so
they tolerate benign formatting differences (e.g., different quotes or
whitespace). Update the three assertions that inspect the content
read('src/domain/services/ManifestDiff.js') to use appropriate RegExp patterns
that match the typedef import (Manifest) and the `@param` {Manifest} oldManifest
and `@param` {Manifest} newManifest patterns, following the style used earlier in
this file (replace toContain with toMatch and supply flexible regexes).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/unit/vault/VaultService.verifier.test.js`:
- Around line 190-209: The test claims to verify the mutation occurs after
readState has already validated the key but never calls readState; update the
test to call the vault.readState method (e.g., await vault.readState({ slug:
'asset', treeOid: 'asset-tree', encryptionKey: TEST_KEY })) before invoking
vault.addToVault so the verifier cache is populated, then assert
crypto.decryptBuffer was called only once; keep existing mocks (persistence,
ref, crypto) and call sequence (readState then addToVault) to exercise the
cross-operation cache scenario for the verifier.

---

Nitpick comments:
In `@test/unit/types/declaration-accuracy.test.js`:
- Around line 52-60: The test that verifies ManifestDiff JSDoc currently uses
exact string checks with toContain; change these assertions to use regex-based
matching with toMatch so they tolerate benign formatting differences (e.g.,
different quotes or whitespace). Update the three assertions that inspect the
content read('src/domain/services/ManifestDiff.js') to use appropriate RegExp
patterns that match the typedef import (Manifest) and the `@param` {Manifest}
oldManifest and `@param` {Manifest} newManifest patterns, following the style used
earlier in this file (replace toContain with toMatch and supply flexible
regexes).

In `@test/unit/vault/VaultService.privacy.test.js`:
- Around line 474-499: Replace the hard-coded error code string
'VAULT_PRIVACY_INDEX_INVALID' in the test assertions with the ErrorCodes
constant to avoid drift; update the expect(...).rejects.toMatchObject calls that
reference the literal (e.g., the assertions against vault.readState and
vault.resolveVaultEntry) to use ErrorCodes.VAULT_PRIVACY_INDEX_INVALID for the
code field, and similarly change the same literal occurrences noted around lines
515-535 to use the ErrorCodes constant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ba5f4ab8-bf1f-4a08-a63c-4fe6c7801af8

📥 Commits

Reviewing files that changed from the base of the PR and between 0846532 and 7f959de.

📒 Files selected for processing (37)
  • CHANGELOG.md
  • GUIDE.md
  • SECURITY.md
  • bin/credentials.js
  • bin/restore-output-target.js
  • bin/ui/vault-report.js
  • docs/API.md
  • docs/EXTENDING.md
  • docs/VAULT_INTERNALS.md
  • docs/design/manifest-diffing.md
  • src/domain/errors/Codes.js
  • src/domain/services/ManifestDiff.js
  • src/domain/services/VaultMetadataCodec.js
  • src/domain/services/VaultPersistence.js
  • src/domain/services/VaultService.js
  • src/domain/services/VaultStateCache.js
  • src/infrastructure/adapters/FileIOHelper.js
  • src/infrastructure/adapters/GitPersistenceAdapter.js
  • src/infrastructure/adapters/GitRefAdapter.js
  • test/helpers/MemoryRefAdapter.js
  • test/unit/cli/credential-resolution.test.js
  • test/unit/cli/restore-output-target.test.js
  • test/unit/cli/vault-report.test.js
  • test/unit/docs/release-truth.test.js
  • test/unit/docs/test-style.test.js
  • test/unit/domain/errors/domain-errors.test.js
  • test/unit/domain/services/VaultMetadataCodec.test.js
  • test/unit/domain/services/VaultPersistence.test.js
  • test/unit/domain/services/VaultStateCache.test.js
  • test/unit/helpers/MemoryRefAdapter.test.js
  • test/unit/infrastructure/adapters/FileIOHelper.test.js
  • test/unit/infrastructure/adapters/GitPersistenceAdapter.readBlob.test.js
  • test/unit/infrastructure/adapters/GitRefAdapter.test.js
  • test/unit/types/declaration-accuracy.test.js
  • test/unit/vault/VaultService.privacy.test.js
  • test/unit/vault/VaultService.test.js
  • test/unit/vault/VaultService.verifier.test.js
✅ Files skipped from review due to trivial changes (7)
  • docs/design/manifest-diffing.md
  • src/domain/services/ManifestDiff.js
  • test/unit/infrastructure/adapters/GitPersistenceAdapter.readBlob.test.js
  • SECURITY.md
  • test/unit/helpers/MemoryRefAdapter.test.js
  • GUIDE.md
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (22)
  • bin/restore-output-target.js
  • docs/EXTENDING.md
  • test/unit/cli/credential-resolution.test.js
  • src/domain/errors/Codes.js
  • test/unit/infrastructure/adapters/GitRefAdapter.test.js
  • test/unit/docs/release-truth.test.js
  • test/unit/cli/restore-output-target.test.js
  • test/unit/domain/errors/domain-errors.test.js
  • bin/credentials.js
  • test/unit/docs/test-style.test.js
  • test/unit/vault/VaultService.test.js
  • src/infrastructure/adapters/FileIOHelper.js
  • test/helpers/MemoryRefAdapter.js
  • test/unit/domain/services/VaultPersistence.test.js
  • bin/ui/vault-report.js
  • src/infrastructure/adapters/GitPersistenceAdapter.js
  • test/unit/domain/services/VaultMetadataCodec.test.js
  • src/domain/services/VaultMetadataCodec.js
  • test/unit/cli/vault-report.test.js
  • src/domain/services/VaultStateCache.js
  • src/domain/services/VaultPersistence.js
  • src/domain/services/VaultService.js

Comment thread test/unit/vault/VaultService.verifier.test.js
@flyingrobots
Copy link
Copy Markdown
Member Author

Final feedback round summary:

Issue Resolution SHA / Evidence
CodeRabbit flagged test/unit/vault/VaultService.verifier.test.js because the verifier-cache mutation test claimed to cover readState()-then-mutation reuse but did not call readState() first. Strengthened the regression test to call readState({ encryptionKey }) before addToVault(...); updated docs/VAULT_INTERNALS.md testing posture and CHANGELOG.md. c6e78299a84eb1389e315bd5f686a2edca4c7081
Latest CI failed on test-docker (bun) in agent CLI protocol — vault remove (success) with Vault head ref could not be resolved. Retrieved the Actions logs, reproduced the focused test locally under Bun, reproduced the full Bun integration suite locally in Docker, and reran the failed GitHub Actions job. The rerun passed without code changes, confirming the failure was transient rather than tied to the latest diff. Rerun job 75127479753; local docker compose run --rm test-bun bunx vitest run test/integration passed: 5 files, 155 tests.

Current verification:

Check Result
Unresolved non-outdated review threads 0
Local targeted verifier/docs tests Passed
Local pre-push eslint . and npm test Passed: 184 files, 1627 passing tests, 2 skipped
GitHub CI Passed: lint, test-unit, test-docker node/bun/deno
CodeRabbit status Passed / review skipped on latest doc-test-only commit

@flyingrobots
Copy link
Copy Markdown
Member Author

Additional non-inline CodeRabbit nitpicks resolved:

Issue Resolution SHA / Evidence
test/unit/vault/VaultService.privacy.test.js used raw 'VAULT_PRIVACY_INDEX_INVALID' strings in new assertions. Replaced those assertions with ErrorCodes.VAULT_PRIVACY_INDEX_INVALID; added a style guard so this specific drift is caught later. 1338f8f47cde33d62d8957c4dcc2813677d65b5c
test/unit/types/declaration-accuracy.test.js used exact toContain() checks for ManifestDiff JSDoc source text. Switched to regex toMatch() checks that tolerate benign quote and whitespace formatting; added a style guard for that test block. 1338f8f47cde33d62d8957c4dcc2813677d65b5c

Verification after this commit:

Check Result
Red guard before fix Failed as expected in test/unit/docs/test-style.test.js
Focused local tests Passed: test/unit/docs/test-style.test.js, test/unit/types/declaration-accuracy.test.js, test/unit/vault/VaultService.privacy.test.js
Pre-push local gates Passed: eslint . and npm test with 184 files, 1629 passing tests, 2 skipped
GitHub CI Passed: lint, test-unit, test-docker node/bun/deno
Unresolved non-outdated review threads 0

@flyingrobots
Copy link
Copy Markdown
Member Author

@coderabbitai review please

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

@flyingrobots Nice cleanup — swapping the raw string literals for ErrorCodes.VAULT_PRIVACY_INDEX_INVALID and tightening the declaration-accuracy assertions to regex matchers are exactly the right moves, and the style guards mean neither regression can quietly reappear. Kicking off the incremental review on the latest head now. 🥕

✅ 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
Copy link
Copy Markdown
Member Author

Additional release-gate fix resolved:

Issue Resolution SHA / Evidence
release:verify -- --skip-jsr exposed an intermittent Node integration failure where git rev-parse refs/cas/vault could exit 128 with only the unresolved ref echoed on stdout, causing empty-vault startup to surface as VAULT_HEAD_INVALID instead of an absent ref. Added a shared Git missing-ref classifier, covered it at the GitRefAdapter boundary and in VaultPersistence.resolveHead(), and documented the stdout-only shape in vault internals and the changelog. 7deb215 (fix(git): classify stdout-only ref misses). RED tests failed before the fix in test/unit/infrastructure/adapters/GitRefAdapter.test.js and test/unit/domain/services/VaultPersistence.test.js; focused lint/tests passed; npm run release:verify -- --skip-jsr passed 12/12 with 5,347 observed tests; pre-push lint/unit passed.

Current review-thread scan: 0 unresolved non-outdated threads, 0 unresolved outdated threads.

@flyingrobots
Copy link
Copy Markdown
Member Author

@coderabbitai review please

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

@flyingrobots Solid fix — classifying the stdout-only git rev-parse exit-128 shape at the GitRefAdapter boundary and propagating it through VaultPersistence.resolveHead() is the right place to handle it, and having RED coverage at both layers means that particular failure mode won't quietly regress. Kicking off the incremental review on 7deb215 now. 🥕

✅ 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 marked this pull request as ready for review May 9, 2026 05:57
Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
src/infrastructure/adapters/GitRefAdapter.js (1)

15-16: ⚡ Quick win

Adapt null-OID length to match the repository's active object format.

Git's update-ref CAS validation requires the null-OID length to match the repository's OID format (40 chars for SHA-1, 64 chars for SHA-256). Hardcoding 40 assumes SHA-1 and will fail on repositories configured with a different object format.

Replace the hardcoded GIT_NULL_OID with a dynamic calculation using newOid.length at call sites:

♻️ Proposed change
-const GIT_NULL_OID = '0'.repeat(40);
@@
   async updateRef({ ref, newOid, expectedOldOid }) {
     const args = ['update-ref', ref, newOid];
     if (expectedOldOid !== undefined) {
-      args.push(expectedOldOid ?? GIT_NULL_OID);
+      args.push(expectedOldOid ?? '0'.repeat(newOid.length));
     }
     await this.policy.execute(() =>
       this.plumbing.execute({ args }),
     );
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/infrastructure/adapters/GitRefAdapter.js` around lines 15 - 16,
GIT_NULL_OID is hardcoded to 40 bytes which breaks repos using non-SHA1 OIDs;
remove or stop using the constant GIT_NULL_OID and instead generate the null OID
dynamically at each call site by using '0'.repeat(newOid.length) (or equivalent)
where newOid is the computed OID passed into functions like
updateRef/updateRefWithCAS in GitRefAdapter.js so the null-OID length matches
the repository's active object format.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/infrastructure/adapters/GitRefAdapter.js`:
- Around line 15-16: GIT_NULL_OID is hardcoded to 40 bytes which breaks repos
using non-SHA1 OIDs; remove or stop using the constant GIT_NULL_OID and instead
generate the null OID dynamically at each call site by using
'0'.repeat(newOid.length) (or equivalent) where newOid is the computed OID
passed into functions like updateRef/updateRefWithCAS in GitRefAdapter.js so the
null-OID length matches the repository's active object format.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c0cbb26-687c-41f9-86df-80469e677f15

📥 Commits

Reviewing files that changed from the base of the PR and between 7f959de and 7deb215.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • docs/VAULT_INTERNALS.md
  • src/domain/helpers/gitRefErrors.js
  • src/domain/services/VaultPersistence.js
  • src/infrastructure/adapters/GitRefAdapter.js
  • test/unit/docs/test-style.test.js
  • test/unit/domain/services/VaultPersistence.test.js
  • test/unit/infrastructure/adapters/GitRefAdapter.test.js
  • test/unit/types/declaration-accuracy.test.js
  • test/unit/vault/VaultService.privacy.test.js
  • test/unit/vault/VaultService.verifier.test.js
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (7)
  • test/unit/types/declaration-accuracy.test.js
  • test/unit/infrastructure/adapters/GitRefAdapter.test.js
  • test/unit/vault/VaultService.verifier.test.js
  • test/unit/docs/test-style.test.js
  • test/unit/domain/services/VaultPersistence.test.js
  • test/unit/vault/VaultService.privacy.test.js
  • src/domain/services/VaultPersistence.js

@flyingrobots flyingrobots merged commit 3a3cd3e into main May 9, 2026
6 checks passed
@flyingrobots flyingrobots deleted the cycle/vault-service-decomposition branch May 9, 2026 06:06
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