Skip to content

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Feb 6, 2026

Summary

  • Add readManifest({ treeOid }) — reads a Git tree, locates and decodes the manifest, returns a validated Manifest value object
  • Add deleteAsset({ treeOid }) — returns logical deletion metadata ({ slug, chunksOrphaned }) without destructive Git ops
  • Add findOrphanedChunks({ treeOids }) — aggregates referenced chunk blob OIDs across assets, returns { referenced, total }
  • Facade pass-throughs on ContentAddressableStore for all three methods
  • New error codes: MANIFEST_NOT_FOUND, GIT_ERROR
  • 42 new unit tests across three test suites (355 total)
  • Updated CHANGELOG and ROADMAP for M4

Test plan

  • All 355 unit tests passing
  • ESLint clean
  • Pre-push hooks passing (lint + unit tests)
  • CI pipeline green

Summary by CodeRabbit

  • New Features

    • Added readManifest API to read and validate manifest data from Git trees
    • Added deleteAsset API providing asset deletion metadata without destructive operations
    • Added findOrphanedChunks API to aggregate and identify unused chunk references across multiple assets
    • New error codes: MANIFEST_NOT_FOUND and GIT_ERROR
  • Documentation

    • Updated CHANGELOG documenting new API functionality for M4 Compass
    • Marked milestones M1–M4 as completed on ROADMAP

Add readManifest(), deleteAsset(), and findOrphanedChunks() to
CasService with facade pass-throughs. Includes 42 new unit tests
and MANIFEST_NOT_FOUND / GIT_ERROR codes.
Break large describe callbacks into smaller blocks to satisfy
max-lines-per-function (50) and max-nested-callbacks (3) rules.
Remove unused variable in deleteAsset tests.
@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

This PR introduces three new manifest-based methods to CasService: readManifest() reads and validates Git tree manifests, deleteAsset() extracts deletion metadata without destructive operations, and findOrphanedChunks() aggregates chunk references across multiple assets. Corresponding facade pass-throughs are added to ContentAddressableStore. Comprehensive unit tests and documentation updates (marking M4 complete) accompany the changes.

Changes

Cohort / File(s) Summary
Documentation & Milestones
CHANGELOG.md, ROADMAP.md
CHANGELOG documents three new public API methods with error codes and test counts; ROADMAP marks M1–M4 milestones as completed with checkmarks and badges.
Public API Facade
index.js
Adds three new pass-through methods to ContentAddressableStore: readManifest(), deleteAsset(), findOrphanedChunks(); each delegates to underlying service.
Core Service Implementation
src/domain/services/CasService.js
Implements readManifest() (reads/decodes/validates Git tree manifest), deleteAsset() (returns slug and orphaned chunk count), findOrphanedChunks() (aggregates referenced chunks across multiple trees); includes consistent CasError-based error handling with MANIFEST_NOT_FOUND and GIT_ERROR codes.
Unit Test Suites
test/unit/domain/services/CasService.readManifest.test.js, test/unit/domain/services/CasService.deleteAsset.test.js, test/unit/domain/services/CasService.findOrphanedChunks.test.js
Comprehensive test coverage for all three methods: golden paths (single/multi-chunk, empty), error scenarios (missing manifest, Git failures, malformed data), edge cases (empty trees, long slugs, deduplication), and error metadata validation.

Sequence Diagram

sequenceDiagram
    actor Caller
    participant Store as ContentAddressableStore
    participant Service as CasService
    participant Git as Git<br/>Persistence
    participant Codec as JsonCodec

    Caller->>Store: readManifest({ treeOid })
    Store->>Service: readManifest({ treeOid })
    Service->>Git: readTree(treeOid)
    Git-->>Service: tree entries
    alt manifest found
        Service->>Git: readBlob(manifestBlobOid)
        Git-->>Service: blob data
        Service->>Codec: decode(blob)
        Codec-->>Service: JSON manifest
        Service->>Service: validate(manifest)
        Service-->>Store: Manifest instance
        Store-->>Caller: Manifest instance
    else manifest not found
        Service-->>Store: CasError(MANIFEST_NOT_FOUND)
        Store-->>Caller: error
    else Git error
        Service-->>Store: CasError(GIT_ERROR)
        Store-->>Caller: error
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

Poem

🐰 Through trees and blobs we hop with care,
To read the manifests hiding there,
Chunks catalogued, orphans found,
M4 complete—sweet victory's sound! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: M4 Compass — lifecycle management' clearly summarizes the main change: introducing lifecycle management features as part of the M4 Compass milestone, which aligns with the changeset's readManifest, deleteAsset, and findOrphanedChunks additions.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch compass

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.

@flyingrobots
Copy link
Member Author

@coderabbitai review please

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

@flyingrobots: I'll review the changes in this PR, focusing on the new lifecycle management features including readManifest, deleteAsset, and findOrphanedChunks methods, along with the facade updates and error handling.

✅ 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

🤖 Fix all issues with AI agents
In `@ROADMAP.md`:
- Around line 131-134: The table rows (e.g., the lines starting with "v1.1.0",
"v1.2.0", "v1.3.0", "v1.4.0") have an extra cell for the checkmark, causing a
5th column while the header is "Version | Milestone | Codename | Theme"; fix
each row by removing the extra pipe and placing the checkmark inside the
existing Theme cell (so Theme contains "Foundation hardening ✅", "File retrieval
round trip + CLI ✅", etc.), ensuring each row has exactly four cells to match
the header.
🧹 Nitpick comments (6)
src/domain/services/CasService.js (1)

320-333: findOrphanedChunks processes trees sequentially — consider naming clarity.

The method name says "orphaned" but it actually returns referenced chunks and a total count. The caller must diff against the full set of blobs in the ODB to determine truly orphaned chunks. The ROADMAP contract matches this behavior, but the name may mislead consumers into thinking it returns the orphaned set itself.

This is a minor naming concern — the JSDoc and return type are accurate, so consumers reading docs won't be confused.

test/unit/domain/services/CasService.findOrphanedChunks.test.js (2)

436-451: Double invocation of the SUT in error assertions — consider consolidating.

The pattern of rejects.toThrow(CasError) followed by a separate try/catch block calls findOrphanedChunks twice against the mocks. This works here because the mocks aren't using Once variants, but it's fragile and wasteful. You can combine both checks in a single invocation:

Suggested consolidation
-    await expect(
-      service.findOrphanedChunks({ treeOids: ['tree-missing'] }),
-    ).rejects.toThrow(CasError);
-
-    try {
-      await service.findOrphanedChunks({ treeOids: ['tree-missing'] });
-    } catch (err) {
-      expect(err.code).toBe('MANIFEST_NOT_FOUND');
-      expect(err.message).toContain('No manifest entry');
-      expect(err.meta.treeOid).toBe('tree-missing');
-    }
+    const err = await service.findOrphanedChunks({ treeOids: ['tree-missing'] })
+      .catch((e) => e);
+
+    expect(err).toBeInstanceOf(CasError);
+    expect(err.code).toBe('MANIFEST_NOT_FOUND');
+    expect(err.message).toContain('No manifest entry');
+    expect(err.meta.treeOid).toBe('tree-missing');

This same pattern applies to the other error tests in this file (lines 465–497, 511–527, 529–548).


1-6: Test helpers: digestOf imported here vs locally defined sha256Digest in deleteAsset tests.

This file imports digestOf from ../../../helpers/crypto.js, while CasService.deleteAsset.test.js defines its own local sha256Digest function that does the same thing. Consider using the shared helper consistently across all three test files.

test/unit/domain/services/CasService.deleteAsset.test.js (2)

8-13: Local sha256Digest duplicates the shared digestOf helper.

This file defines its own sha256Digest function (identical to digestOf from test/helpers/crypto.js). The findOrphanedChunks test file already imports the shared helper. Consider using the same import here for consistency.

Suggested change
-import { createHash } from 'node:crypto';
+import { digestOf } from '../../../helpers/crypto.js';

Then replace sha256Digest(...) calls with digestOf(...).


228-255: Same double-invocation pattern in error assertions as noted in the findOrphanedChunks tests.

The rejects.toThrow + try/catch pattern calls the SUT twice. Additionally, the try/catch blocks here lack expect.unreachable() (unlike the readManifest tests), so if the method unexpectedly resolves on the second call, the property assertions silently don't run. The first rejects.toThrow assertion provides a safety net, but a single-invocation pattern would be cleaner and more robust.

test/unit/domain/services/CasService.readManifest.test.js (1)

9-11: Local digestOf duplicates the shared helper.

Same function exists in test/helpers/crypto.js and is already imported in findOrphanedChunks tests. Consider using the shared import here too.

Comment on lines +131 to +134
| v1.1.0 | M1 | Bedrock | Foundation hardening | ✅ |
| v1.2.0 | M2 | Boomerang| File retrieval round trip + CLI | ✅ |
| v1.3.0 | M3 | Launchpad| CI/CD pipeline | ✅ |
| v1.4.0 | M4 | Compass | Lifecycle management | ✅ |
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Table column count mismatch — the ✅ creates a 5th cell outside the header.

The header defines 4 columns (Version | Milestone | Codename | Theme) but lines 131–134 each have 5 cells. Strict Markdown renderers will drop the extra cell or display it incorrectly. Move the checkmark into the existing Theme column instead.

Proposed fix
-| v1.1.0  | M1        | Bedrock  | Foundation hardening | ✅ |
-| v1.2.0  | M2        | Boomerang| File retrieval round trip + CLI | ✅ |
-| v1.3.0  | M3        | Launchpad| CI/CD pipeline | ✅ |
-| v1.4.0  | M4        | Compass  | Lifecycle management | ✅ |
+| v1.1.0  | M1        | Bedrock  | Foundation hardening ✅ |
+| v1.2.0  | M2        | Boomerang| File retrieval round trip + CLI ✅ |
+| v1.3.0  | M3        | Launchpad| CI/CD pipeline ✅ |
+| v1.4.0  | M4        | Compass  | Lifecycle management ✅ |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| v1.1.0 | M1 | Bedrock | Foundation hardening | |
| v1.2.0 | M2 | Boomerang| File retrieval round trip + CLI | |
| v1.3.0 | M3 | Launchpad| CI/CD pipeline | |
| v1.4.0 | M4 | Compass | Lifecycle management | |
| v1.1.0 | M1 | Bedrock | Foundation hardening ✅ |
| v1.2.0 | M2 | Boomerang| File retrieval round trip + CLI ✅ |
| v1.3.0 | M3 | Launchpad| CI/CD pipeline ✅ |
| v1.4.0 | M4 | Compass | Lifecycle management ✅ |
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)

[warning] 131-131: Table column count
Expected: 4; Actual: 5; Too many cells, extra data will be missing

(MD056, table-column-count)


[warning] 132-132: Table column count
Expected: 4; Actual: 5; Too many cells, extra data will be missing

(MD056, table-column-count)


[warning] 133-133: Table column count
Expected: 4; Actual: 5; Too many cells, extra data will be missing

(MD056, table-column-count)


[warning] 134-134: Table column count
Expected: 4; Actual: 5; Too many cells, extra data will be missing

(MD056, table-column-count)

🤖 Prompt for AI Agents
In `@ROADMAP.md` around lines 131 - 134, The table rows (e.g., the lines starting
with "v1.1.0", "v1.2.0", "v1.3.0", "v1.4.0") have an extra cell for the
checkmark, causing a 5th column while the header is "Version | Milestone |
Codename | Theme"; fix each row by removing the extra pipe and placing the
checkmark inside the existing Theme cell (so Theme contains "Foundation
hardening ✅", "File retrieval round trip + CLI ✅", etc.), ensuring each row has
exactly four cells to match the header.

@flyingrobots flyingrobots merged commit bbc9a21 into main Feb 6, 2026
6 checks passed
@flyingrobots flyingrobots deleted the compass branch February 6, 2026 23:30
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