Skip to content

M14 HYGIENE: test hardening, DRY extraction, SOLID quick-wins#61

Merged
flyingrobots merged 18 commits intomainfrom
m14-hygiene
Mar 3, 2026
Merged

M14 HYGIENE: test hardening, DRY extraction, SOLID quick-wins#61
flyingrobots merged 18 commits intomainfrom
m14-hygiene

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Mar 2, 2026

Summary

Executes M14 HYGIENE milestone — the top-priority work from the HEX_AUDIT covering test quality, DRY extraction, and SOLID quick-wins (T1–T6, B130–B141).

  • T1 Test Hardening (B130, B131) — replaced private field access (_idToShaCache, _snapshotState, _cachedState) with behavioral assertions; moved vi.useFakeTimers() from beforeAll to beforeEach/afterEach
  • T2 Test Determinism (B132, B133) — seeded Math.random() in benchmarks with Mulberry32 RNG, added seed: 42 to all fast-check property tests, replaced random delays with deterministic values, documented intentional global mutations
  • T3 Codec Trailer Template (B134, B138) — created TrailerValidation.js with requireTrailer(), parsePositiveIntTrailer(), validateKindDiscriminator(); all 4 codec decoders refactored
  • T4 HTTP Adapter Extraction (B135) — created httpAdapterUtils.js with MAX_BODY_BYTES, readStreamBody(), noopLogger; eliminates duplication across Node/Bun/Deno adapters
  • T5 Bitmap Checksum (B136) — extracted computeChecksum() to shared checksumUtils.js
  • T6 SOLID Quick Wins (B140, B141) — deleted deprecated PerformanceClockAdapter/GlobalClockAdapter shims (BREAKING); moved BitmapNeighborProvider validation to method level for lazy init

Deferred

  • B137 (frontier serialization) — I/O model divergence (sync vs async)
  • B139 (Lamport increment) — only 2 sites, semantically distinct
  • B142 (296 error string assertions) — requires per-assertion human judgment

New files

  • src/domain/services/TrailerValidation.js
  • src/infrastructure/adapters/httpAdapterUtils.js
  • src/domain/utils/checksumUtils.js

Deleted files

  • src/infrastructure/adapters/PerformanceClockAdapter.js
  • src/infrastructure/adapters/GlobalClockAdapter.js

Test plan

  • npm run lint — clean (changed files)
  • npx tsc --noEmit — clean
  • npx tsc --noEmit -p test/type-check/tsconfig.json — clean
  • npm run test:local — 13,686 tests pass
  • node scripts/check-dts-surface.js — pass (clock shims removed from manifest)
  • CI matrix (node22, bun, deno)

Summary by CodeRabbit

  • Breaking Changes

    • Removed deprecated PerformanceClockAdapter and GlobalClockAdapter exports — use ClockAdapter directly.
  • Bug Fixes / Validation

    • Message codecs now share centralized trailer validation, adding stricter/consistent error checks for invalid messages.
    • HTTP adapters now enforce a 10MB body limit and standardized "Payload Too Large" handling.
  • Tests

    • Improved determinism with seeded RNGs and stricter timer lifecycle; tests assert observable behavior rather than internal state.
  • Chores

    • Centralized shared utilities (HTTP, checksum, trailer validation) and moved neighbor-provider validation to runtime (lazy).

@coderabbitai
Copy link

coderabbitai bot commented Mar 2, 2026

Warning

Rate limit exceeded

@flyingrobots has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 16 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 5d2ebdb and 68a535c.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • src/domain/services/CheckpointMessageCodec.js
  • src/domain/services/PatchMessageCodec.js
  • test/benchmark/ReducerV5.benchmark.js
  • test/benchmark/benchmarkUtils.js
  • test/unit/domain/properties/Join.property.test.js
  • test/unit/domain/services/GitGraphAdapter.stress.test.js
  • test/unit/domain/services/WarpMessageCodec.test.js
📝 Walkthrough

Walkthrough

Consolidates duplicated utilities (trailer validation, checksum, HTTP stream helpers), removes deprecated clock re-exports, makes BitmapNeighborProvider lazy-validate on use, and hardens tests for determinism and behavioral assertions. No public API signatures were added.

Changes

Cohort / File(s) Summary
Public API Deprecation Removal
index.js, index.d.ts, contracts/type-surface.m8.json
Removed deprecated re-exports PerformanceClockAdapter and GlobalClockAdapter from the public entry points and type manifest.
Clock Adapter Shim Deletion
src/infrastructure/adapters/GlobalClockAdapter.js, src/infrastructure/adapters/PerformanceClockAdapter.js
Deleted backward-compatibility shim files that re-exported ClockAdapter.js (deprecation aliases removed).
Trailer Validation Consolidation
src/domain/services/TrailerValidation.js (new), src/domain/services/AnchorMessageCodec.js, src/domain/services/AuditMessageCodec.js, src/domain/services/CheckpointMessageCodec.js, src/domain/services/PatchMessageCodec.js
Added shared helpers (requireTrailer, parsePositiveIntTrailer, validateKindDiscriminator) and refactored four codecs to use them instead of inline trailer parsing.
Checksum Utility Extraction
src/domain/utils/checksumUtils.js (new), src/domain/services/BitmapIndexBuilder.js, src/domain/services/StreamingBitmapIndexBuilder.js
Moved computeChecksum to a shared module and imported it from bitmap builders.
HTTP Adapter Utilities
src/infrastructure/adapters/httpAdapterUtils.js (new), src/infrastructure/adapters/NodeHttpAdapter.js, src/infrastructure/adapters/BunHttpAdapter.js, src/infrastructure/adapters/DenoHttpAdapter.js
Introduced MAX_BODY_BYTES, readStreamBody, and noopLogger into a shared httpAdapterUtils and replaced duplicated local implementations.
Lazy Validation in BitmapNeighborProvider
src/domain/services/BitmapNeighborProvider.js
Constructor now accepts an optional param object; validation deferred to _assertReady() invoked from getNeighbors and hasNode rather than throwing in constructor.
Test Hardening: Behavioral Assertions
test/unit/domain/services/BitmapIndexReader.test.js, test/unit/domain/services/PatchBuilderV2.snapshot.test.js, test/unit/domain/services/BitmapNeighborProvider.test.js
Replaced private-field assertions with behavior-based checks and added spy/assert patterns for caching behavior.
Test Hardening: Determinism
test/benchmark/ReducerV5.benchmark.js, test/benchmark/benchmarkUtils.js, test/unit/domain/properties/Join.property.test.js, test/unit/domain/services/GitGraphAdapter.stress.test.js
Replaced Math.random with seeded RNGs (fixed seeds), added deterministic shuffles and fixed micro-delays to reduce nondeterminism.
Test Setup Refinement
test/unit/domain/WarpGraph.watch.test.js, test/unit/domain/WarpGraph.timing.test.js
Moved fake timer lifecycle to per-test setup/teardown and adjusted clock-related test descriptions to reflect removal of deprecated shims.
Test Cleanup & Documentation
test/unit/domain/index.exports.test.js, test/unit/domain/services/SyncAuthService.test.js, test/unit/domain/services/noBufferGlobal.test.js
Removed tests for deprecated clock exports; added explanatory comments for intentional global mutations and crypto usage in tests.
Project Roadmap & Changelog
ROADMAP.md, CHANGELOG.md
Marked several M14 tasks done and documented the utility extractions, test hardening, and lazy validation work.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Shims tucked away at dawn’s first light,
Trailers checked, and checksums tight,
Streams read safe within the bound,
Tests seeded so they stay spellbound,
Lazy neighbors wake on call—hooray!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: test hardening, DRY extraction, and SOLID quick-wins from the M14 HYGIENE milestone. It is concise and clearly identifies the primary focus of the changeset.
Description check ✅ Passed The PR description provides comprehensive details of all major changes (T1–T6, B130–B141), lists deferred items, new/deleted files, and a complete test plan with verification status. It exceeds the template requirements and fully documents the changeset.
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 m14-hygiene

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.

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Release Preflight

  • package version: 12.4.1
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v12.4.1, release workflow will publish.

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: 2

🧹 Nitpick comments (6)
test/unit/domain/properties/Join.property.test.js (2)

308-314: Use RNG helper’s built-in shuffle to remove duplicate Fisher-Yates code.

Line 308–314 manually re-implements shuffling even though createRng(...).shuffle() already exists in test/helpers/seededRng.js.

Suggested refactor
-            const shuffleRng = createRng(42);
-            const shuffled = [...patches];
-            for (let i = shuffled.length - 1; i > 0; i--) {
-              const j = Math.floor(shuffleRng.next() * (i + 1));
-              [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
-            }
+            const shuffled = createRng(42).shuffle(patches);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/domain/properties/Join.property.test.js` around lines 308 - 314,
The test duplicates a Fisher–Yates shuffle instead of using the RNG helper's
built-in shuffle: replace the manual loop that creates shuffleRng via
createRng(42) and computes shuffled = [...patches] then swaps, with a call to
createRng(42).shuffle(patches) (or use shuffleRng.shuffle(patches)) to produce
the shuffled array; update references to shuffled to use the result of
shuffle(...) and remove the manual loop and temporary variables
(shuffleRng/shuffled).

199-199: Extract shared property-test seed constant for DRY and easier override.

The repeated seed: 42 values are consistent, but centralizing them makes future updates (or env-based overrides) trivial.

Suggested refactor
+const PROPERTY_TEST_SEED = 42;

-        { numRuns: 100, seed: 42 }
+        { numRuns: 100, seed: PROPERTY_TEST_SEED }

(Apply to all fc.assert options in this file.)

Also applies to: 210-210, 220-220, 231-231, 244-244, 257-257, 323-323, 345-345, 394-394

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

In `@test/unit/domain/properties/Join.property.test.js` at line 199, Extract a
single shared seed constant (e.g., const PROPERTY_TEST_SEED =
Number(process.env.PROPERTY_TEST_SEED) || 42) at the top of the test file and
replace all inline `seed: 42` occurrences used in `fc.assert` options with
`seed: PROPERTY_TEST_SEED`; update every `fc.assert(..., { numRuns: X, seed: 42
})` instance in this test (and the other listed occurrences) so the seed is
centralized and can be overridden via env.
test/benchmark/ReducerV5.benchmark.js (1)

39-39: Scope RNG per generation to keep fixtures reproducible per test case.

Right now, Line 39 creates shared mutable RNG state for all calls. generateV5Patches and the shuffle test then consume a single sequence, so outcomes vary with prior benchmark execution order.

Suggested refactor
-const rng = createRng(0xDEADBEEF);
+const BENCHMARK_SEED = 0xDEADBEEF;

 function generateV5Patches(patchCount, options = {}) {
   const {
     writerCount = 5,
     opsPerPatch = 3,
     includeRemoves = true,
+    seed = BENCHMARK_SEED,
   } = options;
+  const rng = createRng(seed);
   ...
 }

 ...
-const shuffled = rng.shuffle(patches);
+const shuffled = createRng(BENCHMARK_SEED).shuffle(patches);

Also applies to: 68-173, 322-323

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

In `@test/benchmark/ReducerV5.benchmark.js` at line 39, The shared RNG created by
createRng(0xDEADBEEF) at top-level causes non-deterministic fixtures because
generateV5Patches and the shuffle benchmark consume the same mutable RNG across
tests; fix by scoping RNG creation to each fixture generation and test (e.g.,
call createRng with the fixed seed inside each generateV5Patches invocation and
inside the shuffle test setup or pass a fresh rng instance into those functions)
so each call gets its own independent RNG instance; update calls to
generateV5Patches and the shuffle test to use the locally created rng instead of
the shared rng.
test/benchmark/benchmarkUtils.js (1)

12-12: Avoid cross-benchmark coupling from module-global RNG state.

Line 12 + Line 88 make randomHex depend on prior calls in the same process. Keep deterministic seeding, but allow RNG injection so each benchmark can isolate its sequence.

Suggested refactor
-const rng = createRng(0xDEADBEEF);
+const BENCHMARK_RNG_SEED = 0xDEADBEEF;
+const rng = createRng(BENCHMARK_RNG_SEED);

-export function randomHex(length = 8) {
+export function randomHex(length = 8, random = rng) {
   let result = '';
   const chars = '0123456789abcdef';
   for (let i = 0; i < length; i++) {
-    result += chars[Math.floor(rng.next() * 16)];
+    result += chars[Math.floor(random.next() * 16)];
   }
   return result;
 }

Also applies to: 84-89

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

In `@test/benchmark/benchmarkUtils.js` at line 12, The module currently creates a
module-global RNG via const rng = createRng(0xDEADBEEF) and uses it inside
randomHex, causing cross-benchmark coupling; change randomHex to accept an
optional RNG parameter (e.g., function randomHex(len, rngArg) { const r = rngArg
?? createRng(0xDEADBEEF); ... }) or provide a factory like createRandomHex(rng)
that returns a randomHex closure so each benchmark can construct its own seeded
RNG via createRng(seed) and pass it in; update callers in benchmarks to create
and pass a per-benchmark RNG instead of relying on the module-global rng to
preserve determinism while isolating sequences.
src/domain/services/CheckpointMessageCodec.js (1)

98-103: Same validation gap as PatchMessageCodec: decoder lacks semantic validation.

The encoder validates graph with validateGraphName, stateHash with validateSha256, and frontierOid/indexOid with validateOid. The decoder only checks presence via requireTrailer.

🛡️ Proposed fix to add semantic validation
   validateKindDiscriminator(trailers, 'checkpoint');
   const graph = requireTrailer(trailers, 'graph', 'checkpoint');
+  validateGraphName(graph);
   const stateHash = requireTrailer(trailers, 'stateHash', 'checkpoint');
+  validateSha256(stateHash, 'stateHash');
   const frontierOid = requireTrailer(trailers, 'frontierOid', 'checkpoint');
+  validateOid(frontierOid, 'frontierOid');
   const indexOid = requireTrailer(trailers, 'indexOid', 'checkpoint');
+  validateOid(indexOid, 'indexOid');
   const schema = parsePositiveIntTrailer(trailers, 'schema', 'checkpoint');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/domain/services/CheckpointMessageCodec.js` around lines 98 - 103, Decoder
currently only checks presence of trailers (using
requireTrailer/parsePositiveIntTrailer) but misses semantic validation present
in the encoder; after extracting values in CheckpointMessageCodec (the variables
graph, stateHash, frontierOid, indexOid), call the corresponding
validators—validateGraphName(graph), validateSha256(stateHash),
validateOid(frontierOid) and validateOid(indexOid)—and ensure any validation
errors propagate (throw or return) the same way other codec validations do; keep
validateKindDiscriminator(trailers, 'checkpoint') in place.
src/domain/services/PatchMessageCodec.js (1)

86-91: Decoder lacks semantic validation applied by encoder.

The encoder validates graph with validateGraphName, writer with validateWriterId, and patchOid with validateOid, but the decoder only uses requireTrailer which checks for presence, not validity. In contrast, AuditMessageCodec.js (lines 84-94) applies secondary validation (validateGraphName, validateWriterId, validateOid, validateSha256) after extracting trailers.

Consider adding the same semantic validations for consistency and defense-in-depth:

🛡️ Proposed fix to add semantic validation
   validateKindDiscriminator(trailers, 'patch');
   const graph = requireTrailer(trailers, 'graph', 'patch');
+  validateGraphName(graph);
   const writer = requireTrailer(trailers, 'writer', 'patch');
+  validateWriterId(writer);
   const lamport = parsePositiveIntTrailer(trailers, 'lamport', 'patch');
   const patchOid = requireTrailer(trailers, 'patchOid', 'patch');
+  validateOid(patchOid, 'patchOid');
   const schema = parsePositiveIntTrailer(trailers, 'schema', 'patch');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/domain/services/PatchMessageCodec.js` around lines 86 - 91, The decoder
currently extracts trailers using requireTrailer/parsePositiveIntTrailer in
PatchMessageCodec.js but does not perform the semantic checks the encoder
applies; after extracting graph, writer, and patchOid (and before using them)
call validateGraphName(graph), validateWriterId(writer), and
validateOid(patchOid) respectively (similar to AuditMessageCodec.js) to enforce
the same validation rules and keep defense-in-depth; keep existing
validateKindDiscriminator(trailers, 'patch') and existing lamport/schema parsing
as-is.
🤖 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/domain/services/TrailerValidation.js`:
- Around line 42-44: The validation uses parseInt(str, 10) which accepts
partial-numeric inputs like "2abc"; update the trailer numeric validation in
src/domain/services/TrailerValidation.js to first assert the string is a strict
unsigned integer (e.g. test str against a /^\d+$/ or equivalent) before
converting, then parse (or Number) and keep the existing check (Number.isInteger
and >0) and error throw; look for the parseInt(str, 10) call and the surrounding
variables kind, key and TRAILER_KEYS to modify.

In `@test/unit/domain/services/GitGraphAdapter.stress.test.js`:
- Around line 15-16: The comment above the simulated latency is stale: it says
"0-10ms" but the implementation uses await new Promise(r => setTimeout(r, (id %
3) * 2)); which yields deterministic delays of 0, 2, or 4 ms; update the comment
to match the actual behavior (e.g., "Simulate deterministic latency: 0, 2, or 4
ms based on id") next to the setTimeout line (referencing the id variable and
the setTimeout call), or alternatively change the expression if you intended a
0–10ms range.

---

Nitpick comments:
In `@src/domain/services/CheckpointMessageCodec.js`:
- Around line 98-103: Decoder currently only checks presence of trailers (using
requireTrailer/parsePositiveIntTrailer) but misses semantic validation present
in the encoder; after extracting values in CheckpointMessageCodec (the variables
graph, stateHash, frontierOid, indexOid), call the corresponding
validators—validateGraphName(graph), validateSha256(stateHash),
validateOid(frontierOid) and validateOid(indexOid)—and ensure any validation
errors propagate (throw or return) the same way other codec validations do; keep
validateKindDiscriminator(trailers, 'checkpoint') in place.

In `@src/domain/services/PatchMessageCodec.js`:
- Around line 86-91: The decoder currently extracts trailers using
requireTrailer/parsePositiveIntTrailer in PatchMessageCodec.js but does not
perform the semantic checks the encoder applies; after extracting graph, writer,
and patchOid (and before using them) call validateGraphName(graph),
validateWriterId(writer), and validateOid(patchOid) respectively (similar to
AuditMessageCodec.js) to enforce the same validation rules and keep
defense-in-depth; keep existing validateKindDiscriminator(trailers, 'patch') and
existing lamport/schema parsing as-is.

In `@test/benchmark/benchmarkUtils.js`:
- Line 12: The module currently creates a module-global RNG via const rng =
createRng(0xDEADBEEF) and uses it inside randomHex, causing cross-benchmark
coupling; change randomHex to accept an optional RNG parameter (e.g., function
randomHex(len, rngArg) { const r = rngArg ?? createRng(0xDEADBEEF); ... }) or
provide a factory like createRandomHex(rng) that returns a randomHex closure so
each benchmark can construct its own seeded RNG via createRng(seed) and pass it
in; update callers in benchmarks to create and pass a per-benchmark RNG instead
of relying on the module-global rng to preserve determinism while isolating
sequences.

In `@test/benchmark/ReducerV5.benchmark.js`:
- Line 39: The shared RNG created by createRng(0xDEADBEEF) at top-level causes
non-deterministic fixtures because generateV5Patches and the shuffle benchmark
consume the same mutable RNG across tests; fix by scoping RNG creation to each
fixture generation and test (e.g., call createRng with the fixed seed inside
each generateV5Patches invocation and inside the shuffle test setup or pass a
fresh rng instance into those functions) so each call gets its own independent
RNG instance; update calls to generateV5Patches and the shuffle test to use the
locally created rng instead of the shared rng.

In `@test/unit/domain/properties/Join.property.test.js`:
- Around line 308-314: The test duplicates a Fisher–Yates shuffle instead of
using the RNG helper's built-in shuffle: replace the manual loop that creates
shuffleRng via createRng(42) and computes shuffled = [...patches] then swaps,
with a call to createRng(42).shuffle(patches) (or use
shuffleRng.shuffle(patches)) to produce the shuffled array; update references to
shuffled to use the result of shuffle(...) and remove the manual loop and
temporary variables (shuffleRng/shuffled).
- Line 199: Extract a single shared seed constant (e.g., const
PROPERTY_TEST_SEED = Number(process.env.PROPERTY_TEST_SEED) || 42) at the top of
the test file and replace all inline `seed: 42` occurrences used in `fc.assert`
options with `seed: PROPERTY_TEST_SEED`; update every `fc.assert(..., { numRuns:
X, seed: 42 })` instance in this test (and the other listed occurrences) so the
seed is centralized and can be overridden via env.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59c5a9c and ac9264b.

📒 Files selected for processing (32)
  • CHANGELOG.md
  • ROADMAP.md
  • contracts/type-surface.m8.json
  • index.d.ts
  • index.js
  • src/domain/services/AnchorMessageCodec.js
  • src/domain/services/AuditMessageCodec.js
  • src/domain/services/BitmapIndexBuilder.js
  • src/domain/services/BitmapNeighborProvider.js
  • src/domain/services/CheckpointMessageCodec.js
  • src/domain/services/PatchMessageCodec.js
  • src/domain/services/StreamingBitmapIndexBuilder.js
  • src/domain/services/TrailerValidation.js
  • src/domain/utils/checksumUtils.js
  • src/infrastructure/adapters/BunHttpAdapter.js
  • src/infrastructure/adapters/DenoHttpAdapter.js
  • src/infrastructure/adapters/GlobalClockAdapter.js
  • src/infrastructure/adapters/NodeHttpAdapter.js
  • src/infrastructure/adapters/PerformanceClockAdapter.js
  • src/infrastructure/adapters/httpAdapterUtils.js
  • test/benchmark/ReducerV5.benchmark.js
  • test/benchmark/benchmarkUtils.js
  • test/unit/domain/WarpGraph.timing.test.js
  • test/unit/domain/WarpGraph.watch.test.js
  • test/unit/domain/index.exports.test.js
  • test/unit/domain/properties/Join.property.test.js
  • test/unit/domain/services/BitmapIndexReader.test.js
  • test/unit/domain/services/BitmapNeighborProvider.test.js
  • test/unit/domain/services/GitGraphAdapter.stress.test.js
  • test/unit/domain/services/PatchBuilderV2.snapshot.test.js
  • test/unit/domain/services/SyncAuthService.test.js
  • test/unit/domain/services/noBufferGlobal.test.js
💤 Files with no reviewable changes (6)
  • contracts/type-surface.m8.json
  • index.js
  • src/infrastructure/adapters/GlobalClockAdapter.js
  • test/unit/domain/index.exports.test.js
  • src/infrastructure/adapters/PerformanceClockAdapter.js
  • index.d.ts

…ordering and whitespace nits

- Replace inline regex+Number schema parsing in AuditMessageCodec with
  shared parsePositiveIntTrailer() from TrailerValidation.js
- Harden parsePositiveIntTrailer() with digits-only regex guard so
  non-integer strings like "1.5" are rejected before parseInt
- Reorder CHANGELOG [Unreleased] sections: Changed → Removed → Fixed
  (Keep a Changelog canonical order)
- Remove double blank lines in index.d.ts and index.exports.test.js
@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Release Preflight

  • package version: 12.4.1
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v12.4.1, release workflow will publish.

Defense-in-depth: PatchMessageCodec and CheckpointMessageCodec decoders
now validate field semantics (validateGraphName, validateWriterId,
validateOid, validateSha256) after extracting trailers, matching the
pattern already established in AuditMessageCodec.

Added 7 tests covering invalid graph names, writer IDs, OIDs, and
SHA-256 hashes in both decoders.
Comment said "0-10ms" but the deterministic expression (id % 3) * 2
produces 0, 2, or 4ms. Updated to match actual behavior.
…le helper

- Replaced 9 inline `seed: 42` values with `PROPERTY_TEST_SEED` constant
- Replaced manual Fisher-Yates shuffle with `createRng().shuffle()`
- benchmarkUtils.js: randomHex() accepts optional RNG parameter
- ReducerV5.benchmark.js: generateV5Patches() creates its own seeded
  RNG instead of consuming shared module-level state; shuffle test
  uses a fresh RNG instance
@flyingrobots
Copy link
Member Author

Review Feedback — All Items Addressed

Thanks @coderabbitai for the thorough review! All items have been addressed:

Actionable (2)

  • TrailerValidation.js parseInt weakness — ✅ Already fixed in 5d2ebdb (added ^\d+$ regex guard)
  • GitGraphAdapter.stress.test.js stale comment — ✅ Fixed in 5ed676b

Nitpicks (6)

  • PatchMessageCodec decoder semantic validation — ✅ Fixed in 654408a (added validateGraphName, validateWriterId, validateOid)
  • CheckpointMessageCodec decoder semantic validation — ✅ Fixed in 654408a (added validateGraphName, validateSha256, validateOid × 2)
  • Join.property.test.js manual Fisher-Yates — ✅ Replaced with createRng().shuffle() in bed003f
  • Join.property.test.js seed constant — ✅ Extracted PROPERTY_TEST_SEED constant in bed003f
  • ReducerV5.benchmark.js shared RNG — ✅ Scoped to generateV5Patches + shuffle test in 4d72a69
  • benchmarkUtils.js module-global RNG — ✅ randomHex() now accepts optional RNG param in 4d72a69

All 4569 tests passing (7 new tests added for decoder validation). Ready for re-review.

@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

@flyingrobots: Thank you for addressing all the feedback! I can see you've resolved:

✅ The parseInt weakness with regex guard
✅ Stale comments in stress tests
✅ Semantic validation in PatchMessageCodec and CheckpointMessageCodec decoders
✅ Manual Fisher-Yates replaced with createRng().shuffle()
✅ Extracted PROPERTY_TEST_SEED constant
✅ Scoped RNG usage in benchmarks
✅ Made randomHex() accept optional RNG parameter

I'll conduct a fresh review of the PR 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.

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

@flyingrobots, thank you for the detailed follow-up and for addressing all the review feedback! I can see you've systematically tackled each item with specific commits:

Actionable items fixed:

  • Hardened parseInt with regex guard (5d2ebdb)
  • Updated stale comment in stress test (5ed676b)

Nitpicks addressed:

  • Added semantic validation to PatchMessageCodec & CheckpointMessageCodec decoders (654408a)
  • Improved RNG determinism in property tests with constant extraction (bed003f)
  • Scoped RNG usage in benchmarks and made utilities accept optional RNG params (4d72a69)

The addition of 7 new validation tests is great for coverage. I'll trigger a fresh review now to verify all the changes!

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

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Release Preflight

  • package version: 12.4.1
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v12.4.1, release workflow will publish.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Line 13: Update the CHANGELOG entry to remove the "no behavioral change" claim
and state that adding TrailerValidation.js (requireTrailer,
parsePositiveIntTrailer, validateKindDiscriminator) introduces stricter semantic
validation: Patch and Checkpoint decoders now perform the same graph name,
writer ID, OID, and SHA-256 checks as the Audit decoder and will reject certain
previously-accepted malformed messages; edit the sentence describing the change
to reflect this behavioral impact.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d2ebdb and 73a8a6e.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • src/domain/services/CheckpointMessageCodec.js
  • src/domain/services/PatchMessageCodec.js
  • test/benchmark/ReducerV5.benchmark.js
  • test/benchmark/benchmarkUtils.js
  • test/unit/domain/properties/Join.property.test.js
  • test/unit/domain/services/GitGraphAdapter.stress.test.js
  • test/unit/domain/services/WarpMessageCodec.test.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • test/benchmark/benchmarkUtils.js
  • test/benchmark/ReducerV5.benchmark.js
  • test/unit/domain/properties/Join.property.test.js
  • test/unit/domain/services/GitGraphAdapter.stress.test.js

The codec B134/B138 entry claimed "no behavioral change" but Patch and
Checkpoint decoders now reject previously-accepted malformed inputs.
Updated to "stricter rejection of malformed messages".
@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Mar 3, 2026

@flyingrobots: I'll review the changes 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.

@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Release Preflight

  • package version: 12.4.1
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v12.4.1, release workflow will publish.

@flyingrobots flyingrobots merged commit 000b447 into main Mar 3, 2026
8 checks passed
@flyingrobots flyingrobots deleted the m14-hygiene branch March 3, 2026 17:37
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