Skip to content

Add structured bundle and page storage#63

Merged
flyingrobots merged 4 commits into
mainfrom
feat/structured-bundle-pages
Jul 13, 2026
Merged

Add structured bundle and page storage#63
flyingrobots merged 4 commits into
mainfrom
feat/structured-bundle-pages

Conversation

@flyingrobots

@flyingrobots flyingrobots commented Jul 13, 2026

Copy link
Copy Markdown
Member

Summary

  • add bounded immutable page storage with opaque PageHandle values
  • add deterministic structured bundles with streaming ordered construction and targeted member reads
  • extend retention and publication across assets, pages, and bundles
  • harden imported-graph admission, corruption evidence, bounded validation, and Git metadata reads

Linked Issue

Design / Proof

  • Design doc: docs/design/0047-application-storage-cache-boundary/application-storage-cache-boundary.md
  • Witness: docs/design/0047-application-storage-cache-boundary/witness/bundle-pages.md
  • The witness contains 51 validated immutable source citations across the implementation and review-hardening commits.

Self-review

Reviewed the complete delta for object ownership, bounded residency, deterministic identity, imported-handle admission, transitive Git reachability, failure evidence, runtime portability, and additive semver posture. Graft reports 40 changed files, no breaking structural changes, and no removed or changed exports.

Code Lawyer

The adversarial review found and corrected:

  • imported pages bypassing ingress size policy
  • persisted bundle policy requiring independent read-side enforcement
  • unbounded validation memoization
  • selected-member OID/type/size integrity gaps
  • exact-boundary staging-evidence ambiguity
  • invalid clocks being checked after writes
  • non-injective or unsafe Unicode paths, including a trailing high-surrogate boundary
  • a 1 MiB implementation default conflicting with the documented 16 MiB page default
  • impossible metadata accepted by exported StagedBundle
  • an undersized initial streaming proof
  • root descriptors using a broader materialization cap
  • persistence type declarations diverging from runtime requirements
  • non-native missing-object errors escaping normalization
  • duplicated Git object-query error mapping
  • review-critical admission branches without direct regression coverage

The complete CL-001 through CL-017 record is in the witness.

Validation

  • pnpm lint
  • pnpm test (204 files, 1,789 passed, 2 skipped)
  • pnpm test:integration:node (7 files, 165 passed)
  • pnpm test:integration:bun (7 files, 165 passed)
  • pnpm test:integration:deno (7 files, 165 passed)
  • focused review suite (7 files, 68 passed)
  • documentation proof suite (2 files, 8 passed)
  • Graft structural review (40 files, no breaking changes)
  • Graft export review (additive minor surface, no removals or changed exports)
  • 51 immutable source citations validated

Release Impact

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added immutable, size-bounded page storage with deduplication and streaming access.
    • Added deterministic structured bundles with ordered construction, bounded fanout, nesting controls, and targeted member lookup.
    • Expanded retention and publication support to include asset, page, and bundle handles.
    • Added structured handle types, validation, and detailed error reporting.
  • Documentation

    • Updated the README, API reference, guide, changelog, and design evidence with page and bundle capabilities, limits, and usage examples.
  • Bug Fixes

    • Improved validation of imported content and bundle graphs while avoiding unnecessary content loading.

Walkthrough

This change adds immutable page blobs and deterministic structured bundles to the CAS, with typed handles, bounded fanout construction, targeted member reads, full graph validation, retention/publication support, Git object-size queries, public API updates, and comprehensive tests and documentation.

Changes

Structured storage

Layer / File(s) Summary
Handle and storage contracts
src/domain/value-objects/*, index.d.ts
Adds immutable page/bundle handles, application-handle unions, bundle limits and paths, staged results, and structured error codes.
Bounded page persistence
src/domain/services/PageService.js, src/infrastructure/adapters/GitPersistenceAdapter.js, src/ports/*
Adds bounded page writes and reads, imported-size validation, streaming APIs, and Git object-size lookup.
Bundle encoding and construction
src/domain/services/BundleDescriptorCodec.js, src/domain/services/BundleFanoutBuilder.js, src/domain/services/BundleService.js
Builds deterministic bounded fanout trees, validates descriptors and persisted limits, supports nested members, and records staging evidence.
Facade and persistence integration
index.js, src/domain/value-objects/RetentionWitness.js
Exposes pages and bundles, resolves generic application roots, detects cycles, bounds validation caching, and broadens retention/publication roots.
Validation and public documentation
test/**, docs/**, README.md, GUIDE.md, CHANGELOG.md
Adds unit/integration coverage and documents structured materializations, APIs, limits, errors, and implementation evidence.

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

Possibly related issues

  • Issue 51 — Directly covers structured bundle and Merkle page storage implemented by this change.
  • git-stunts/git-warp issue 739 — Plans to consume the new pages and bundles primitives for checkpoints, indexes, tries, and read caches.

Possibly related PRs

  • git-stunts/git-cas#36 — Shares the persistence-boundary changes for bounded blob reads and centralized CAS errors.

Sequence Diagram(s)

sequenceDiagram
  participant Application
  participant ContentAddressableStore
  participant PageService
  participant BundleService
  participant GitPersistenceAdapter
  Application->>ContentAddressableStore: pages.put(source)
  ContentAddressableStore->>PageService: store bounded page
  PageService->>GitPersistenceAdapter: writeBlob(bytes)
  PageService-->>ContentAddressableStore: PageHandle
  Application->>ContentAddressableStore: bundles.putOrdered(members)
  ContentAddressableStore->>BundleService: build fanout tree
  BundleService->>GitPersistenceAdapter: write descriptors and tree nodes
  BundleService-->>ContentAddressableStore: BundleHandle
  Application->>ContentAddressableStore: bundles.openMember(bundle, path)
  ContentAddressableStore->>BundleService: resolve selected path
  BundleService->>GitPersistenceAdapter: read selected descriptor and payload
  BundleService-->>Application: member byte stream
Loading

Poem

A rabbit hops through pages bright,
Bundles branch in fanout light.
Handles freeze, paths stay true,
One chosen stream comes hopping through.
Git keeps each root in place—
Structured storage wins the race!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding structured bundle and page storage.
Description check ✅ Passed The description is directly related to the changeset and matches the implemented storage, validation, and docs updates.
Linked Issues check ✅ Passed The PR appears to satisfy #51 by adding page storage, structured bundles, deterministic fanout, targeted reads, deduplication, and corruption evidence.
Out of Scope Changes check ✅ Passed The changes shown are scoped to the new storage feature, its docs, and tests; no unrelated additions stand out.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/infrastructure/adapters/GitPersistenceAdapter.js (1)

189-233: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate missing-object error-mapping logic.

readObjectSize repeats the exact try/catch → isMissingGitObjectErrorCasError(GIT_OBJECT_NOT_FOUND) pattern already used in readObjectType. Extracting a shared private helper would keep both methods in sync if the mapping ever changes.

♻️ Proposed refactor
+  async `#executeMappingMissingObject`(args, oid) {
+    try {
+      return await this.policy.execute(() => this.plumbing.execute({ args }));
+    } catch (err) {
+      if (isMissingGitObjectError(err)) {
+        throw new CasError(`Git object not found: ${oid}`, ErrorCodes.GIT_OBJECT_NOT_FOUND, {
+          oid,
+          originalError: err,
+        });
+      }
+      throw err;
+    }
+  }
+
   async readObjectType(oid) {
-    try {
-      return await this.policy.execute(() =>
-        this.plumbing.execute({ args: ['cat-file', '-t', oid] }),
-      );
-    } catch (err) {
-      if (isMissingGitObjectError(err)) {
-        throw new CasError(`Git object not found: ${oid}`, ErrorCodes.GIT_OBJECT_NOT_FOUND, {
-          oid,
-          originalError: err,
-        });
-      }
-      throw err;
-    }
+    return this.#executeMappingMissingObject(['cat-file', '-t', oid], oid);
   }

   async readObjectSize(oid) {
-    let output;
-    try {
-      output = await this.policy.execute(() =>
-        this.plumbing.execute({ args: ['cat-file', '-s', oid] }),
-      );
-    } catch (err) {
-      if (isMissingGitObjectError(err)) {
-        throw new CasError(`Git object not found: ${oid}`, ErrorCodes.GIT_OBJECT_NOT_FOUND, {
-          oid,
-          originalError: err,
-        });
-      }
-      throw err;
-    }
+    const output = await this.#executeMappingMissingObject(['cat-file', '-s', oid], oid);
     const size = Number(output);
🤖 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/GitPersistenceAdapter.js` around lines 189 - 233,
Extract the repeated missing-object error mapping from readObjectType and
readObjectSize into a shared private helper on GitPersistenceAdapter. Have both
methods use the helper while preserving propagation of non-missing-object errors
and the existing readObjectSize validation behavior.
🤖 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 `@src/domain/helpers/handleTarget.js`:
- Around line 32-47: Update mapHandleTargetError’s missing-error message
extraction to read a string `.message` from any thrown value, not only Error
instances, before falling back to String(error). Preserve the existing
object/blob/tree “not found” matching and HANDLE_TARGET_MISSING mapping
behavior.

In `@src/domain/services/BundleDescriptorCodec.js`:
- Around line 231-246: Update assertCanonicalPath so normalizeBundlePath
failures and non-canonical round-trip results are handled separately: wrap only
the normalizeBundlePath call in the corrupt error handling with its thrown error
as originalError, then perform the value comparison afterward and report a
distinct corruption error for a differing normalized path without attributing
the manually detected mismatch to normalizeBundlePath.

In `@src/domain/services/BundleService.js`:
- Around line 261-266: Update `#readRoot`’s call to `#readDescriptorBlob` for the
root descriptor to pass this.#limits.maxDescriptorBytes as the read limit,
matching the bounded read behavior used by `#readNode` while preserving the
existing handle and kind options.

In `@src/domain/services/CasService.d.ts`:
- Around line 82-83: Make readObjectSize mandatory in the GitPersistencePort
declaration by removing its optional marker in
src/domain/services/CasService.d.ts lines 82-83, matching PageService’s runtime
requirement. No direct change is needed in src/domain/services/PageService.js
lines 166-174 because the corrected type aligns with its existing validation.

In `@src/domain/value-objects/BundlePath.js`:
- Around line 57-71: Update hasUnpairedSurrogate so a high surrogate at the end
of value is treated as unpaired; explicitly handle the missing next code unit
before validating the low-surrogate range, while preserving existing handling
for valid pairs and isolated low surrogates.

In `@test/unit/domain/services/PageService.test.js`:
- Around line 57-71: Add a test in the existing page-limit suite that calls
pages.put with a non-byte, non-iterable source and asserts rejection with code
INVALID_OPTIONS and the message "Page source must be bytes or an iterable of
byte chunks", covering the dedicated PageService.#collect validation branch.

In `@test/unit/domain/value-objects/ApplicationStorageResults.test.js`:
- Around line 129-150: The “accepts every application handle kind” test only
covers PageHandle; parameterize it to exercise AssetHandle, PageHandle, and
BundleHandle instances, reusing the same RetentionWitness assertions for each
case.

In `@test/unit/infrastructure/adapters/GitPersistenceAdapter.readTree.test.js`:
- Around line 124-133: Strengthen the readObjectSize() test to verify the size
lookup does not materialize the Git object: after the existing execute
assertion, assert plumbing.executeStream has not been called, and optionally
verify the expected plumbing call count.

---

Outside diff comments:
In `@src/infrastructure/adapters/GitPersistenceAdapter.js`:
- Around line 189-233: Extract the repeated missing-object error mapping from
readObjectType and readObjectSize into a shared private helper on
GitPersistenceAdapter. Have both methods use the helper while preserving
propagation of non-missing-object errors and the existing readObjectSize
validation behavior.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c6a642e0-1164-46c7-b870-67ac9f52de0c

📥 Commits

Reviewing files that changed from the base of the PR and between 863c692 and aa5565f.

📒 Files selected for processing (39)
  • CHANGELOG.md
  • GUIDE.md
  • README.md
  • docs/API.md
  • docs/design/0047-application-storage-cache-boundary/application-storage-cache-boundary.md
  • docs/design/0047-application-storage-cache-boundary/witness/bundle-pages.md
  • index.d.ts
  • index.js
  • src/domain/errors/Codes.js
  • src/domain/helpers/handleTarget.js
  • src/domain/services/BundleDescriptorCodec.js
  • src/domain/services/BundleFanoutBuilder.js
  • src/domain/services/BundleService.js
  • src/domain/services/CasService.d.ts
  • src/domain/services/PageService.js
  • src/domain/services/StagingEvidence.js
  • src/domain/value-objects/ApplicationHandle.js
  • src/domain/value-objects/BundleHandle.js
  • src/domain/value-objects/BundleLimits.js
  • src/domain/value-objects/BundlePath.js
  • src/domain/value-objects/OpaqueHandle.js
  • src/domain/value-objects/PageHandle.js
  • src/domain/value-objects/RetentionWitness.js
  • src/domain/value-objects/StagedBundle.js
  • src/domain/value-objects/StagedPage.js
  • src/infrastructure/adapters/GitPersistenceAdapter.js
  • src/ports/GitPersistencePort.js
  • test/helpers/MemoryPersistenceAdapter.js
  • test/integration/application-storage.test.js
  • test/unit/domain/services/BundleAdmission.test.js
  • test/unit/domain/services/BundleService.test.js
  • test/unit/domain/services/PageService.test.js
  • test/unit/domain/services/StagingEvidence.test.js
  • test/unit/domain/value-objects/ApplicationStorageResults.test.js
  • test/unit/domain/value-objects/StructuredHandle.test.js
  • test/unit/facade/ContentAddressableStore.application-storage.test.js
  • test/unit/infrastructure/adapters/GitPersistenceAdapter.readTree.test.js
  • test/unit/ports/GitPersistencePort.test.js
  • test/unit/types/declaration-accuracy.test.js
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: test-docker (bun)
  • GitHub Check: test-docker (deno)
  • GitHub Check: test-docker (node)
🧰 Additional context used
📓 Path-based instructions (4)
docs/design/**

📄 CodeRabbit inference engine (AGENTS.md)

Use docs/design/ directory for durable design contracts and proof plans

Files:

  • docs/design/0047-application-storage-cache-boundary/application-storage-cache-boundary.md
  • docs/design/0047-application-storage-cache-boundary/witness/bundle-pages.md
CHANGELOG.md

📄 CodeRabbit inference engine (AGENTS.md)

Use CHANGELOG.md to record the historical truth of merged behavior

Files:

  • CHANGELOG.md
README.md

📄 CodeRabbit inference engine (AGENTS.md)

Use README.md as the public front door, core value prop, and quick start documentation

Files:

  • README.md
GUIDE.md

📄 CodeRabbit inference engine (AGENTS.md)

Use GUIDE.md for orientation and productive-fast path documentation

Files:

  • GUIDE.md
🧠 Learnings (2)
📚 Learning: 2026-02-28T19:21:13.982Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 15
File: test/unit/domain/services/CasService.envelope.test.js:326-330
Timestamp: 2026-02-28T19:21:13.982Z
Learning: In fuzz tests for cryptographic operations, use a seeded PRNG (e.g., xorshift32) for control-flow variables such as plaintext size selection and recipient index selection to ensure reproducibility. Continue to use randomBytes() for cryptographic keys and nonces to preserve realistic randomness and avoid security anti-patterns. This guideline applies to test files that perform fuzz testing of crypto logic; implement a consistent seed setup (e.g., fixed seed in test initialization) and document the rationale to enable deterministic replays across runs.

Applied to files:

  • test/unit/domain/value-objects/StructuredHandle.test.js
  • test/unit/domain/services/StagingEvidence.test.js
  • test/unit/ports/GitPersistencePort.test.js
  • test/unit/infrastructure/adapters/GitPersistenceAdapter.readTree.test.js
  • test/unit/domain/services/BundleAdmission.test.js
  • test/unit/domain/services/PageService.test.js
  • test/integration/application-storage.test.js
  • test/unit/types/declaration-accuracy.test.js
  • test/unit/domain/services/BundleService.test.js
  • test/unit/facade/ContentAddressableStore.application-storage.test.js
  • test/unit/domain/value-objects/ApplicationStorageResults.test.js
📚 Learning: 2026-03-30T19:53:48.000Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 29
File: docs/design/TR-010-planning-index-consistency-review.md:121-131
Timestamp: 2026-03-30T19:53:48.000Z
Learning: In this repo, CHANGELOG.md entries should be user-facing and descriptive (bullet points) rather than cycle-ID headings (e.g., use a phrase like “Planning-index consistency review” instead of a “TR-010 — …” heading). When searching the changelog, don’t rely on grepping for cycle IDs (e.g., “TR-010”) because it may cause false negatives; search for the descriptive keywords/phrases from the bullet entries instead.

Applied to files:

  • CHANGELOG.md
🔇 Additional comments (39)
test/unit/domain/value-objects/StructuredHandle.test.js (1)

1-26: LGTM!

test/unit/domain/services/StagingEvidence.test.js (1)

1-28: LGTM!

src/domain/errors/Codes.js (1)

2-13: LGTM!

Also applies to: 43-43

test/unit/infrastructure/adapters/GitPersistenceAdapter.readTree.test.js (1)

135-140: LGTM!

docs/design/0047-application-storage-cache-boundary/application-storage-cache-boundary.md (1)

800-802: LGTM!

src/domain/services/StagingEvidence.js (1)

1-39: LGTM!

docs/design/0047-application-storage-cache-boundary/witness/bundle-pages.md (1)

1-82: LGTM!

src/domain/value-objects/RetentionWitness.js (1)

4-4: LGTM!

Also applies to: 17-17, 41-41

test/unit/ports/GitPersistencePort.test.js (1)

43-45: LGTM!

src/ports/GitPersistencePort.js (1)

27-32: LGTM!

Also applies to: 81-89

test/unit/domain/services/BundleAdmission.test.js (1)

7-55: LGTM!

test/integration/application-storage.test.js (1)

174-296: LGTM!

test/unit/domain/services/BundleService.test.js (1)

14-300: LGTM!

src/domain/value-objects/ApplicationHandle.js (1)

1-40: LGTM!

test/helpers/MemoryPersistenceAdapter.js (1)

65-134: LGTM!

src/domain/services/BundleService.js (1)

70-260: LGTM!

Also applies to: 278-663

src/domain/value-objects/StagedBundle.js (1)

10-66: LGTM!

src/domain/value-objects/BundleLimits.js (1)

23-80: LGTM!

test/unit/facade/ContentAddressableStore.application-storage.test.js (1)

4-9: LGTM!

Also applies to: 27-36, 38-44

test/unit/domain/value-objects/ApplicationStorageResults.test.js (1)

3-8: LGTM!

Also applies to: 65-91

index.d.ts (1)

80-152: LGTM!

Also applies to: 188-266, 291-297, 402-402, 415-415, 530-535, 825-877, 887-893, 918-919

index.js (2)

14-38: LGTM!

Also applies to: 82-86, 118-164, 170-214, 275-304, 332-372, 420-446, 908-918


374-418: 🎯 Functional Correctness

No issue: sequential traversal prevents sibling false-positives. Bundle validation resolves children and leaf targets with plain for...of + await, not concurrent fan-out, so the shared validation.active set only tracks the current path. Shared members can still be reused via the cache once the first resolution completes.

			> Likely an incorrect or invalid review comment.
docs/API.md (1)

78-80: LGTM!

Also applies to: 1001-1104, 1136-1137, 1176-1177, 1193-1193, 1207-1207, 2653-2670

src/domain/services/CasService.d.ts (1)

69-69: LGTM!

src/domain/value-objects/PageHandle.js (1)

1-34: LGTM!

src/domain/helpers/handleTarget.js (1)

11-25: LGTM!

src/domain/value-objects/StagedPage.js (1)

1-47: LGTM!

test/unit/domain/services/PageService.test.js (1)

1-114: LGTM!

test/unit/types/declaration-accuracy.test.js (1)

82-90: LGTM!

src/domain/services/PageService.js (1)

1-164: LGTM!

src/domain/services/BundleDescriptorCodec.js (2)

1-165: LGTM!

Also applies to: 180-284


167-178: 🗄️ Data Integrity & Integration

Confirm the leaf kind→type mapping covers every blob-backed handle kind. expectedType = handle.kind === 'page' ? 'blob' : 'tree' assumes only page is blob-backed; if any other leaf handle kind can also carry blobs, this will reject valid entries as corrupt.

CHANGELOG.md (1)

23-46: LGTM!

README.md (1)

26-117: LGTM!

src/domain/value-objects/BundleHandle.js (1)

1-32: LGTM!

GUIDE.md (1)

54-165: LGTM!

src/domain/services/BundleFanoutBuilder.js (1)

38-227: LGTM!

src/domain/value-objects/OpaqueHandle.js (1)

16-171: LGTM!

Comment thread src/domain/helpers/handleTarget.js
Comment thread src/domain/services/BundleDescriptorCodec.js
Comment thread src/domain/services/BundleService.js
Comment thread src/domain/services/CasService.d.ts Outdated
Comment thread src/domain/value-objects/BundlePath.js
Comment thread test/unit/domain/services/PageService.test.js
Comment thread test/unit/domain/value-objects/ApplicationStorageResults.test.js
@flyingrobots

Copy link
Copy Markdown
Member Author

Review follow-up: the outside-diff duplicate missing-object mapping is fixed in c76d26da; readObjectType() and readObjectSize() now share #executeObjectQuery() while preserving malformed-size validation. [cite: src/infrastructure/adapters/GitPersistenceAdapter.js#184-229@c76d26da2965dbcea41f7e3b50628016efdebe04] The informational bundle leaf-type question is also closed by the current supported handle union: asset and bundle targets are trees, page targets are blobs, and unknown kinds are rejected during application-handle parsing. [cite: src/domain/value-objects/ApplicationHandle.js#1-40@c76d26da2965dbcea41f7e3b50628016efdebe04] [cite: src/domain/services/BundleDescriptorCodec.js#160-177@c76d26da2965dbcea41f7e3b50628016efdebe04] All eight review threads have focused fixes or tests; CL-011 through CL-017 and 51 validated immutable citations are recorded in the witness.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/domain/services/BundleService.js (1)

98-109: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate summary metadata along targeted traversal.

getMember() follows root.index and branch child ranges without checking each loaded node against its declared OID/depth/count/path summary. A corrupted descriptor can therefore return a member on this path, while resolveRoot() correctly rejects the same graph via assertSummary()/assertChildSummary().

Carry the expected root/child summary through the loop and validate it immediately after each #readNode(); add corruption tests for mismatched root-index and branch-child summaries.

🤖 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/domain/services/BundleService.js` around lines 98 - 109, Update
getMember()’s targeted traversal to carry the expected root and branch-child
summary metadata, validating each loaded node immediately after `#readNode`() with
the same assertSummary()/assertChildSummary() checks used by resolveRoot().
Ensure root.index and each selected child range are validated for declared OID,
depth, count, and path before continuing, and add corruption tests covering
mismatched root-index and branch-child summaries.
🤖 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.

Outside diff comments:
In `@src/domain/services/BundleService.js`:
- Around line 98-109: Update getMember()’s targeted traversal to carry the
expected root and branch-child summary metadata, validating each loaded node
immediately after `#readNode`() with the same assertSummary()/assertChildSummary()
checks used by resolveRoot(). Ensure root.index and each selected child range
are validated for declared OID, depth, count, and path before continuing, and
add corruption tests covering mismatched root-index and branch-child summaries.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 27c23417-fbfb-41d9-aea9-ce3d86bcdfd2

📥 Commits

Reviewing files that changed from the base of the PR and between aa5565f and 0dfe211.

📒 Files selected for processing (13)
  • docs/design/0047-application-storage-cache-boundary/witness/bundle-pages.md
  • src/domain/helpers/handleTarget.js
  • src/domain/services/BundleDescriptorCodec.js
  • src/domain/services/BundleService.js
  • src/domain/services/CasService.d.ts
  • src/domain/value-objects/BundlePath.js
  • src/infrastructure/adapters/GitPersistenceAdapter.js
  • test/unit/domain/helpers/handleTarget.test.js
  • test/unit/domain/services/BundleAdmission.test.js
  • test/unit/domain/services/BundleService.test.js
  • test/unit/domain/services/PageService.test.js
  • test/unit/domain/value-objects/ApplicationStorageResults.test.js
  • test/unit/infrastructure/adapters/GitPersistenceAdapter.readTree.test.js
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: test-unit
🧰 Additional context used
📓 Path-based instructions (1)
docs/design/**

📄 CodeRabbit inference engine (AGENTS.md)

Use docs/design/ directory for durable design contracts and proof plans

Files:

  • docs/design/0047-application-storage-cache-boundary/witness/bundle-pages.md
🧠 Learnings (1)
📚 Learning: 2026-02-28T19:21:13.982Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 15
File: test/unit/domain/services/CasService.envelope.test.js:326-330
Timestamp: 2026-02-28T19:21:13.982Z
Learning: In fuzz tests for cryptographic operations, use a seeded PRNG (e.g., xorshift32) for control-flow variables such as plaintext size selection and recipient index selection to ensure reproducibility. Continue to use randomBytes() for cryptographic keys and nonces to preserve realistic randomness and avoid security anti-patterns. This guideline applies to test files that perform fuzz testing of crypto logic; implement a consistent seed setup (e.g., fixed seed in test initialization) and document the rationale to enable deterministic replays across runs.

Applied to files:

  • test/unit/domain/helpers/handleTarget.test.js
  • test/unit/infrastructure/adapters/GitPersistenceAdapter.readTree.test.js
  • test/unit/domain/services/BundleAdmission.test.js
  • test/unit/domain/services/PageService.test.js
  • test/unit/domain/value-objects/ApplicationStorageResults.test.js
  • test/unit/domain/services/BundleService.test.js
🔇 Additional comments (12)
test/unit/domain/helpers/handleTarget.test.js (1)

1-19: LGTM!

test/unit/infrastructure/adapters/GitPersistenceAdapter.readTree.test.js (1)

133-133: LGTM!

src/domain/services/CasService.d.ts (1)

69-82: LGTM!

src/domain/value-objects/BundlePath.js (1)

57-74: LGTM!

test/unit/domain/services/BundleAdmission.test.js (1)

27-27: LGTM!

docs/design/0047-application-storage-cache-boundary/witness/bundle-pages.md (1)

7-8: LGTM!

Also applies to: 168-216, 235-238

src/domain/helpers/handleTarget.js (1)

32-48: LGTM!

test/unit/domain/services/PageService.test.js (1)

74-82: LGTM!

test/unit/domain/value-objects/ApplicationStorageResults.test.js (1)

130-150: LGTM!

src/domain/services/BundleDescriptorCodec.js (1)

238-247: LGTM!

test/unit/domain/services/BundleService.test.js (1)

267-294: LGTM!

src/infrastructure/adapters/GitPersistenceAdapter.js (1)

199-207: 🗄️ Data Integrity & Integration

Reject empty or non-decimal size output before coercion. Number(output) treats blank strings as 0, so malformed cat-file -s output can be accepted as a valid zero-byte size.

@flyingrobots flyingrobots merged commit cf7685b into main Jul 13, 2026
10 of 11 checks passed
@flyingrobots flyingrobots deleted the feat/structured-bundle-pages branch July 13, 2026 13:34
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.

Add structured bundle and Merkle page storage

1 participant