Skip to content

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Feb 4, 2026

Summary

  • GK/FRONTIER/1: New graph.hasFrontierChanged() — O(writers) cheap change detection without materialization
  • GK/IDX/1: Bitmap index stores frontier.cbor + frontier.json at build time for staleness tracking
  • GK/IDX/2: New IndexStalenessChecker — on index load, compare frontier metadata against current refs; warn when stale, opt-in autoRebuild
  • GK/GC/1: materialize() checks GC thresholds post-run; warn by default, execute only when gcPolicy: { enabled: true }

Test plan

  • npm run test:local — 1605 tests pass (71 files, including no-coordination regression suite)
  • npm run lint — clean
  • node scripts/roadmap.js status — GROUNDSKEEPER at 100% (4/4)
  • New test files: WarpGraph.frontierChanged.test.js (7), BitmapIndexBuilder.frontier.test.js (8), IndexStalenessChecker.test.js (13), WarpGraph.autoGC.test.js (6)
  • No existing tests broken — all 1578 pre-existing tests still pass

Summary by CodeRabbit

  • New Features

    • Detect writer-tip changes without full materialization.
    • Index staleness detection with optional automatic rebuild (off by default).
    • Frontier metadata written as authoritative CBOR with JSON fallback for debugging.
    • Opt-in automatic GC with configurable policy, post-materialization checks, and logger hooks.
    • New options for auto-materialization and checkpointing.
  • Tests

    • Comprehensive unit tests covering frontier tracking, staleness logic, and GC behavior.
  • Documentation

    • Roadmap revised with updated milestones, formats, and acceptance criteria.

- Fix version reality: v7.1.0 complete on main (unreleased), GROUNDSKEEPER in progress
- Reorder GROUNDSKEEPER: FRONTIER first (multiplier), IDX second, GC last
- Make GC and index auto-rebuild opt-in with warn-by-default semantics
- WEIGHTED mixed-version sync: fail fast with E_SCHEMA_UNSUPPORTED, never silently drop
- Elevate auto-materialize coalescing from edge-case test to core invariant
- HANDSHAKE deletion guards: two-layer validation (best-effort at build, authoritative at commit)
- LIGHTHOUSE receipts: reinforce zero-cost invariant when disabled
- Frontier storage: CBOR authoritative, JSON as debug artifact
O(writers) method comparing stored writer tip SHAs against current refs.
Returns true if any writer tip has changed since the last materialize(),
enabling cheap "has anything changed?" polling without full materialization.

Closes GK/FRONTIER/1.
Adds frontier.cbor (authoritative) and frontier.json (debug) to the
bitmap index tree when a frontier Map is provided to serialize/finalize.
Threads frontier option through IndexRebuildService.rebuild().

Closes GK/IDX/1.
New IndexStalenessChecker with loadIndexFrontier() and checkStaleness().
On index load, reads frontier metadata, compares against current refs,
and warns when stale. Opt-in autoRebuild triggers automatic rebuild.

Closes GK/IDX/2.
After materialize(), check GC metrics and warn when thresholds are
exceeded. Execute GC only when gcPolicy.enabled === true. GC failure
never breaks materialize. Adds logger parameter to WarpGraph.open().

Closes GK/GC/1.
@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Adds frontier metadata serialization (CBOR + canonical JSON), frontier-aware index staleness detection with optional auto-rebuild, WarpGraph frontier-change checking via hasFrontierChanged(), and opt-in post-materialization GC wiring and logging; tests exercise frontier serialization, staleness logic, hasFrontierChanged, and GC behavior.

Changes

Cohort / File(s) Summary
Docs & Types
ROADMAP.md, index.d.ts
ROADMAP updated with milestone/status and artifact format changes. index.d.ts expands gcPolicy shape (adds enabled and thresholds), adds checkpointPolicy and autoMaterialize, and declares hasFrontierChanged(): Promise<boolean> and updated BitmapIndexBuilder.serialize signature.
Core Graph
src/domain/WarpGraph.js
Adds _lastFrontier tracking, optional logger parameter, new hasFrontierChanged() method, records frontier after materialize, and invokes _maybeRunGC(state) post-materialize with opt-in GC behavior and logging.
Index Builders
src/domain/services/BitmapIndexBuilder.js, src/domain/services/StreamingBitmapIndexBuilder.js
Serialize now accepts { frontier } and emits frontier.cbor and frontier.json envelopes (CBOR preferred, JSON canonical for debug). Streaming builder finalization accepts frontier and persists both blobs. Added wrap/envelope helpers and CBOR usage.
Index Rebuild & Staleness
src/domain/services/IndexRebuildService.js, src/domain/services/IndexStalenessChecker.js, src/domain/services/GCPolicy.js
New IndexStalenessChecker exports loadIndexFrontier and checkStaleness (CBOR primary, JSON fallback). IndexRebuildService propagates frontier through rebuild/persist paths and performs staleness checks on load with optional autoRebuild behavior. GCPolicy adds enabled (default false).
Tests
test/unit/domain/WarpGraph.*.test.js, test/unit/domain/services/*frontier*.test.js, test/unit/domain/services/IndexStalenessChecker.test.js
Adds comprehensive unit tests for hasFrontierChanged, frontier serialization (CBOR/JSON), index staleness detection, load-time staleness handling and optional auto-rebuild, and post-materialization GC behavior including logger assertions.

Sequence Diagrams

sequenceDiagram
    participant Client as Client
    participant WarpGraph as WarpGraph
    participant Storage as Storage

    Client->>WarpGraph: hasFrontierChanged()
    activate WarpGraph
    WarpGraph->>Storage: read writer tips / frontier
    Storage-->>WarpGraph: current frontier (Map)
    WarpGraph->>WarpGraph: compare current vs _lastFrontier
    WarpGraph-->>Client: Promise<boolean>
    deactivate WarpGraph

    Note over WarpGraph,Storage: materialize() updates _lastFrontier and may trigger GC
    Client->>WarpGraph: materialize()
    activate WarpGraph
    WarpGraph->>WarpGraph: perform materialization
    WarpGraph->>WarpGraph: _lastFrontier = getFrontier()
    WarpGraph->>WarpGraph: _maybeRunGC(state)
    alt gcPolicy.enabled && thresholds met
        WarpGraph->>WarpGraph: run GC
        WarpGraph->>WarpGraph: log info (logger)
    else thresholds met && !enabled
        WarpGraph->>WarpGraph: log warning (logger)
    end
    WarpGraph-->>Client: materialize result
    deactivate WarpGraph
Loading
sequenceDiagram
    participant Client as Client
    participant IndexRebuild as IndexRebuildService
    participant Staleness as IndexStalenessChecker
    participant Storage as Storage
    participant Builder as Bitmap/StreamingBuilder

    Client->>IndexRebuild: load(treeOid, { currentFrontier, autoRebuild })
    activate IndexRebuild
    IndexRebuild->>Storage: read shard OIDs
    Storage-->>IndexRebuild: shard OIDs
    IndexRebuild->>Staleness: loadIndexFrontier(shardOids)
    activate Staleness
    Staleness->>Storage: read frontier.cbor (or frontier.json)
    Storage-->>Staleness: frontier blob
    Staleness-->>IndexRebuild: indexFrontier (Map)
    deactivate Staleness
    IndexRebuild->>Staleness: checkStaleness(indexFrontier, currentFrontier)
    Staleness-->>IndexRebuild: { stale, reason, details }
    alt stale && autoRebuild
        IndexRebuild->>IndexRebuild: rebuild({ frontier: currentFrontier })
        IndexRebuild->>Builder: serialize/finalize({ frontier })
        Builder->>Storage: write shards + frontier.cbor + frontier.json
        IndexRebuild-->>Client: rebuilt index
    else stale && !autoRebuild
        IndexRebuild-->>Client: stale index + warning
    else not stale
        IndexRebuild-->>Client: current index
    end
    deactivate IndexRebuild
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • feat: complete AUTOPILOT milestone (v7.1.0) — Kill the Materialize Tax #7: Overlaps WarpGraph open/options and materialization flow changes that this PR extends with frontier tracking, GC hooks, and new options.
  • Package #2: Related index-building foundations (Bitmap/Streaming builders and rebuild service) that this PR extends with frontier metadata and staleness handling.
  • Package #1: Prior indexing/rebuild work that shares code paths and is extended by the frontier-aware serialization and staleness checks here.

Poem

🐰 I stored the frontier in CBOR light,

JSON for humans when debugging at night.
I sniffed for changes with a tiny twitch,
GC waits politely unless you switch,
Indexes sing and rebuild takes flight. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly matches the main feature: GROUNDSKEEPER v7.2.0 infrastructure milestone encompassing frontier tracking, index staleness detection, and GC management.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

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

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

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.

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

Caution

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

⚠️ Outside diff range comments (1)
index.d.ts (1)

506-508: ⚠️ Potential issue | 🟠 Major

Update BitmapIndexBuilder.serialize typings to accept frontier.

The implementation now supports serialize({ frontier }); the type definition still exposes a zero-arg signature, which blocks TS users from passing the new option.

🧩 Proposed fix
-  /** Serializes the index to a tree structure of buffers */
-  serialize(): Record<string, Buffer>;
+  /** Serializes the index to a tree structure of buffers */
+  serialize(options?: { frontier?: Map<string, string> }): Record<string, Buffer>;
🤖 Fix all issues with AI agents
In `@ROADMAP.md`:
- Line 227: Add a language identifier to the fenced code block to satisfy
markdownlint MD040 by changing the opening triple-backtick fence to include a
language (e.g., use "```text" instead of "```"); locate the lone fenced block in
ROADMAP.md (the opening "```" shown in the diff) and update it accordingly so
the block is "```text" and the closing fence remains unchanged.

In `@src/domain/services/IndexStalenessChecker.js`:
- Around line 15-28: The loadIndexFrontier function currently assumes the
decoded envelope contains a valid frontier object and will throw an opaque
TypeError if malformed; update loadIndexFrontier to validate the parsed envelope
(after cborDecode or JSON.parse) by checking that envelope is an object,
envelope.frontier exists and is an object (or map-like), and that
Object.entries(envelope.frontier) is safe to call; if validation fails, throw a
clear, descriptive error mentioning the blob type and shardOids key (e.g.,
'invalid frontier envelope for frontier.cbor' or 'frontier.json') so callers can
diagnose corrupted or future-version blobs; keep validation logic adjacent to
the cborDecode and JSON.parse usage and ensure you use the same variable names
(envelope, buffer, cborOid, jsonOid) shown in loadIndexFrontier.

In `@src/domain/services/StreamingBitmapIndexBuilder.js`:
- Around line 355-365: The frontier.json must be written using canonical
(sorted-key) JSON; change the JSON serialization for envelope (the code that
creates the jsonOid via
this.storage.writeBlob(Buffer.from(JSON.stringify(envelope)))) to use a
canonical JSON encoder instead (either a small helper that recursively
serializes objects with Object.keys(...).sort() or a
stable-json-stringify/canonicalize utility) so keys are lexicographically
ordered across the entire envelope; ensure you use that helper in the
frontier.json write path (where jsonOid is created) and keep the existing sorted
frontier object and envelope structure.
🧹 Nitpick comments (2)
src/domain/services/IndexRebuildService.js (2)

5-109: Document the new frontier option for rebuild().
The frontier propagation looks good, but the JSDoc doesn’t mention the new option, which makes it harder to discover.

📚 Suggested JSDoc update
-   * `@param` {AbortSignal} [options.signal] - Optional AbortSignal for cancellation support.
+   * `@param` {AbortSignal} [options.signal] - Optional AbortSignal for cancellation support.
+   * `@param` {Map<string, string>} [options.frontier] - Frontier to persist alongside the rebuilt index.

282-315: Document staleness options on load().
The staleness check and auto‑rebuild flow are clear, but the new currentFrontier, autoRebuild, and rebuildRef options aren’t described in the JSDoc.

📚 Suggested JSDoc update
-   * `@param` {boolean} [options.strict=true] - Enable strict integrity verification (fail-closed).
+   * `@param` {boolean} [options.strict=true] - Enable strict integrity verification (fail-closed).
+   * `@param` {Map<string, string>} [options.currentFrontier] - Frontier to compare for staleness.
+   * `@param` {boolean} [options.autoRebuild=false] - Auto-rebuild when a stale index is detected.
+   * `@param` {string} [options.rebuildRef] - Ref to rebuild from when autoRebuild is true.

- Update BitmapIndexBuilder.serialize typings to accept frontier option
- Add language identifier to ROADMAP.md fenced code block (MD040)
- Validate frontier envelope in loadIndexFrontier before accessing fields
- Use canonical sorted-key JSON for frontier.json serialization
- Document frontier param in IndexRebuildService.rebuild() JSDoc
- Document currentFrontier/autoRebuild/rebuildRef in IndexRebuildService.load() JSDoc
@flyingrobots flyingrobots merged commit 75b332b into main Feb 4, 2026
3 checks passed
@flyingrobots flyingrobots deleted the phase-2-groundskeeper branch February 4, 2026 13:59
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