refactor: shared GraphPort architecture + GraphContext + graph.patch() (1.0.0-alpha.9)#18
Conversation
…text Delete WarpDashboardAdapter, DashboardService, WeaverService, WeaverPort, and WarpWeaverAdapter — replaced by GraphContext (query-based snapshot) and DepAnalysis (pure frontier/critical-path functions). Convert all WARP adapters and xyph-actuator from manual syncCoverage+materialize+createPatchSession+commit to atomic graph.patch(). Drop redundant materialize() calls (autoMaterialize handles reads). Submission lifecycle test drops from 15s timeout to default 5s (~1.2s actual). 327 tests, all passing.
…1.0.0-alpha.9) Replace per-adapter WarpGraphHolder with a shared GraphPort/WarpGraphAdapter singleton injected via DI. All adapters, actuator, dashboard, and coordinator daemon share one graph instance, eliminating CAS conflicts and redundant syncCoverage()/materialize() calls. Fix GraphContext cache invalidation: frontier key comparison replaces hasFrontierChanged() to detect both in-process and external mutations. Delete dead WarpGraphHolder. Net -259 lines.
📝 WalkthroughWalkthroughIntroduces a GraphPort + WarpGraphAdapter DI pattern and a GraphContext gateway, refactors adapters to use graph.patch(...) for atomic writes, removes DashboardService/WarpDashboardAdapter and WeaverService/WarpWeaverAdapter (replacing Weaver logic with DepAnalysis utilities), updates TUI to use GraphContext, and bumps version to 1.0.0-alpha.9. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant GraphContext as GraphContext\n(createGraphContext)
participant Adapter as WarpIntakeAdapter\n(or WarpSubmissionAdapter)
participant GraphPort as WarpGraphAdapter\n(GraphPort)
participant WarpGraph as WarpGraph\n(internal)
Client->>Adapter: perform write (promote/submit/patch)
Adapter->>GraphPort: getGraph()
GraphPort->>WarpGraph: return shared graph
Adapter->>WarpGraph: patch(fn) -- apply atomic mutation
WarpGraph-->>GraphPort: commit & autoMaterialize
GraphPort-->>Adapter: patch result (sha)
Client->>GraphContext: fetchSnapshot()
GraphContext->>GraphPort: getGraph()
GraphPort->>WarpGraph: return up-to-date graph
GraphContext->>WarpGraph: materialize/query nodes & neighbors
WarpGraph-->>GraphContext: node data
GraphContext-->>Client: GraphSnapshot (cached)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~65 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ebe4b344ed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
CHANGELOG.md (1)
552-559:⚠️ Potential issue | 🟡 MinorAdd comparison links for alpha.8 and alpha.9.
The link references at the bottom are missing entries for
1.0.0-alpha.8and1.0.0-alpha.9. The[Unreleased]link also points toalpha.7instead of the latest release.📝 Proposed fix
-[Unreleased]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.7...HEAD +[Unreleased]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.9...HEAD +[1.0.0-alpha.9]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.8...v1.0.0-alpha.9 +[1.0.0-alpha.8]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.7...v1.0.0-alpha.8 [1.0.0-alpha.7]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.6...v1.0.0-alpha.7🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@CHANGELOG.md` around lines 552 - 559, Update the bottom link references to include the missing versions and point Unreleased to the latest release: change the [Unreleased] link to compare v1.0.0-alpha.9...HEAD and add entries for [1.0.0-alpha.9] (compare v1.0.0-alpha.8...v1.0.0-alpha.9) and [1.0.0-alpha.8] (compare v1.0.0-alpha.7...v1.0.0-alpha.8) so the symbols [Unreleased], [1.0.0-alpha.9], and [1.0.0-alpha.8] all exist and link to the correct GitHub compare URLs.
🧹 Nitpick comments (3)
src/infrastructure/GraphContext.ts (1)
105-116: Don’t silently drop neighbor-query failures.Ignoring rejected neighbor fetches can produce partial snapshots without surfacing data integrity problems. Prefer fail-fast or at least structured warning + degraded-mode signaling.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/infrastructure/GraphContext.ts` around lines 105 - 116, The current Promise.allSettled loop silently drops rejected neighbor fetches (ids.map -> graph.neighbors, toNeighborEntries) which can hide data integrity issues; update the handling so that after Promise.allSettled(results) you iterate results and for any result.status === 'rejected' capture the associated id (from the original ids array at the same index), log/emit a structured warning or error including that id and result.reason, and then either fail-fast by throwing a descriptive error or set a clearly named degraded flag (e.g., neighborsDegraded or neighborFetchFailed) so callers can detect partial snapshots; still add fulfilled entries to map via map.set(id, neighbors) for successful ones.test/integration/WarpIntakeAdapter.test.ts (1)
26-80: Consider consolidating seed patches for efficiency.The seven sequential
graph.patch()calls could be merged into a single atomic patch. This would reduce overhead and better reflect real-world batch seeding. However, the current approach is functionally correct and may improve test readability.♻️ Optional: Consolidated seed patch
- await graph.patch((p) => { - p.addNode('intent:sovereign-test') - .setProperty('intent:sovereign-test', 'title', 'Sovereign Test Intent') - .setProperty('intent:sovereign-test', 'requested_by', 'human.tester') - .setProperty('intent:sovereign-test', 'created_at', 1700000000000) - .setProperty('intent:sovereign-test', 'type', 'intent'); - }); - - await graph.patch((p) => { - p.addNode('task:INTAKE-001') - ... - }); - // ... remaining patches + await graph.patch((p) => { + // Intent + p.addNode('intent:sovereign-test') + .setProperty('intent:sovereign-test', 'title', 'Sovereign Test Intent') + .setProperty('intent:sovereign-test', 'requested_by', 'human.tester') + .setProperty('intent:sovereign-test', 'created_at', 1700000000000) + .setProperty('intent:sovereign-test', 'type', 'intent'); + + // Test tasks + p.addNode('task:INTAKE-001') + .setProperty('task:INTAKE-001', 'title', 'Intake promote target task') + .setProperty('task:INTAKE-001', 'status', 'INBOX') + .setProperty('task:INTAKE-001', 'hours', 2) + .setProperty('task:INTAKE-001', 'type', 'task'); + + // ... add remaining nodes in same patch + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/integration/WarpIntakeAdapter.test.ts` around lines 26 - 80, Multiple sequential graph.patch(...) calls should be consolidated into a single atomic patch to reduce overhead and mimic batch seeding; replace the seven separate graph.patch invocations with one graph.patch(p => { ... }) that calls p.addNode('intent:sovereign-test') and all p.addNode('task:...') entries and their corresponding p.setProperty(...) calls inside the same callback (preserving node ids like 'intent:sovereign-test', 'task:INTAKE-001', 'task:INTAKE-002', 'task:INTAKE-003', 'task:INTAKE-004', 'task:INTAKE-FORBIDDEN', 'task:INTAKE-ALREADY-PROMOTED') so the changes are applied atomically.xyph-actuator.ts (1)
140-151: Potential inconsistency between graphPort writerId and command agentId.The
graphPortis initialized at module load time withagentId(line 15), but theclaimcommand re-readsXYPH_AGENT_IDat line 142. If the environment variable changes between process start and command execution (unlikely in normal use but possible in tests), theassigned_toproperty will differ from the graph'swriterId.This doesn't break functionality since the adapter accepts any
agentIdfor data, but could cause confusion if debugging writer attribution.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@xyph-actuator.ts` around lines 140 - 151, The claim command reads process.env['XYPH_AGENT_ID'] again, which can diverge from the module-initialized graphPort writerId; to fix, stop re-reading the env var inside the .action handler and instead use the same agent identifier established at module load (e.g., the module-level DEFAULT_AGENT_ID/AGENT_ID or graphPort.writerId) so that the assigned_to property matches the graphPort writer attribution; update the .action(async (id: string) => { ... }) block to reference that shared agentId symbol rather than process.env.
🤖 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/infrastructure/GraphContext.ts`:
- Around line 138-143: invalidateCache() is clearing local
cachedSnapshot/cachedFrontierKey/_graph but calling this.graphPort.reset()
breaks the “single WarpGraph instance per process” invariant by allowing a new
WarpGraph to be created while old references may still exist; remove the
this.graphPort.reset() call from invalidateCache() and only clear
this.cachedSnapshot, this.cachedFrontierKey, and this._graph in
invalidateCache(), and move any graphPort.reset() use to a controlled
shutdown/close path (e.g. a dedicated close/destroy method) that ensures no
active WarpGraph references remain before calling graphPort.reset().
---
Outside diff comments:
In `@CHANGELOG.md`:
- Around line 552-559: Update the bottom link references to include the missing
versions and point Unreleased to the latest release: change the [Unreleased]
link to compare v1.0.0-alpha.9...HEAD and add entries for [1.0.0-alpha.9]
(compare v1.0.0-alpha.8...v1.0.0-alpha.9) and [1.0.0-alpha.8] (compare
v1.0.0-alpha.7...v1.0.0-alpha.8) so the symbols [Unreleased], [1.0.0-alpha.9],
and [1.0.0-alpha.8] all exist and link to the correct GitHub compare URLs.
---
Nitpick comments:
In `@src/infrastructure/GraphContext.ts`:
- Around line 105-116: The current Promise.allSettled loop silently drops
rejected neighbor fetches (ids.map -> graph.neighbors, toNeighborEntries) which
can hide data integrity issues; update the handling so that after
Promise.allSettled(results) you iterate results and for any result.status ===
'rejected' capture the associated id (from the original ids array at the same
index), log/emit a structured warning or error including that id and
result.reason, and then either fail-fast by throwing a descriptive error or set
a clearly named degraded flag (e.g., neighborsDegraded or neighborFetchFailed)
so callers can detect partial snapshots; still add fulfilled entries to map via
map.set(id, neighbors) for successful ones.
In `@test/integration/WarpIntakeAdapter.test.ts`:
- Around line 26-80: Multiple sequential graph.patch(...) calls should be
consolidated into a single atomic patch to reduce overhead and mimic batch
seeding; replace the seven separate graph.patch invocations with one
graph.patch(p => { ... }) that calls p.addNode('intent:sovereign-test') and all
p.addNode('task:...') entries and their corresponding p.setProperty(...) calls
inside the same callback (preserving node ids like 'intent:sovereign-test',
'task:INTAKE-001', 'task:INTAKE-002', 'task:INTAKE-003', 'task:INTAKE-004',
'task:INTAKE-FORBIDDEN', 'task:INTAKE-ALREADY-PROMOTED') so the changes are
applied atomically.
In `@xyph-actuator.ts`:
- Around line 140-151: The claim command reads process.env['XYPH_AGENT_ID']
again, which can diverge from the module-initialized graphPort writerId; to fix,
stop re-reading the env var inside the .action handler and instead use the same
agent identifier established at module load (e.g., the module-level
DEFAULT_AGENT_ID/AGENT_ID or graphPort.writerId) so that the assigned_to
property matches the graphPort writer attribution; update the .action(async (id:
string) => { ... }) block to reference that shared agentId symbol rather than
process.env.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (30)
CHANGELOG.mdpackage.jsonsrc/coordinator-daemon.tssrc/domain/models/dashboard.tssrc/domain/services/DashboardService.tssrc/domain/services/DepAnalysis.tssrc/domain/services/WeaverService.tssrc/infrastructure/GraphContext.tssrc/infrastructure/adapters/WarpDashboardAdapter.tssrc/infrastructure/adapters/WarpGraphAdapter.tssrc/infrastructure/adapters/WarpIntakeAdapter.tssrc/infrastructure/adapters/WarpRoadmapAdapter.tssrc/infrastructure/adapters/WarpSubmissionAdapter.tssrc/infrastructure/adapters/WarpWeaverAdapter.tssrc/ports/DashboardPort.tssrc/ports/GraphPort.tssrc/ports/WeaverPort.tssrc/tui/Dashboard.tsxsrc/tui/GraphProvider.tsxtest/integration/CrossAdapterVisibility.test.tstest/integration/WarpDashboardAdapter.test.tstest/integration/WarpIntakeAdapter.test.tstest/integration/WarpRoadmapAdapter.test.tstest/integration/WarpSubmissionAdapter.test.tstest/integration/WarpWeaverAdapter.test.tstest/unit/DashboardService.test.tstest/unit/DepAnalysis.test.tstest/unit/WeaverService.test.tsxyph-actuator.tsxyph-dashboard.tsx
💤 Files with no reviewable changes (11)
- src/domain/models/dashboard.ts
- src/infrastructure/adapters/WarpDashboardAdapter.ts
- src/infrastructure/adapters/WarpWeaverAdapter.ts
- src/ports/DashboardPort.ts
- src/domain/services/WeaverService.ts
- src/ports/WeaverPort.ts
- test/integration/WarpWeaverAdapter.test.ts
- test/unit/DashboardService.test.ts
- test/integration/WarpDashboardAdapter.test.ts
- test/unit/WeaverService.test.ts
- src/domain/services/DashboardService.ts
Critical: invalidateCache() no longer orphans shared graph via reset(). High: remove stale syncCoverage() from depend, inline dead getGraph(). Medium: reduce getStateSnapshot() calls, switch to Promise.all. Low: batch test seeds, fix JSDoc reference. Update ARCHITECTURE.md to reflect current hexagonal architecture.
…its) - Fix topologicalSort traversal direction (dir: 'in') so deps view produces prerequisite-first execution order instead of reversed - Add node type validation in depend command (prefix + type property) - Use module-level agentId in claim command instead of re-reading env - Add missing CHANGELOG comparison links for alpha.8 and alpha.9
Addressing review feedback — round 3 (cf570d1)Fixes applied
False positives (2)1. GraphContext 2. Consolidate WarpIntakeAdapter test seed patches (nitpick on Build ✅ | Lint ✅ | 327/327 tests passing ✅ |
|
@coderabbitai review please 🥕 |
|
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 561: The CHANGELOG.md contains an unused link reference "[Unreleased]:
https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.9...HEAD" with no
corresponding section header; fix by either adding a top-level section header
"## [Unreleased]" (and place any pending notes under it) or remove the link
reference line entirely if you don't intend to publish an Unreleased section
yet—update the file so the "[Unreleased]" link reference and the "##
[Unreleased]" header are consistent.
In `@docs/canonical/ARCHITECTURE.md`:
- Around line 5-17: The fenced ASCII diagram blocks (for example the block
containing "Driving Adapters / xyph-actuator / xyph-dashboard / coordinator" and
the corresponding "Domain Core / domain/entities / domain/services" and "Driven
Adapters / WarpGraphAdapter / WarpIntakeAdapter" diagram) lack a language
specifier; update each such fenced code block to start with ```text (or
```plaintext) instead of ``` so the ASCII diagrams are treated as plain text
(apply the same change to the other ASCII diagram blocks in the file).
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
CHANGELOG.mddocs/canonical/ARCHITECTURE.mdsrc/infrastructure/GraphContext.tssrc/ports/GraphPort.tstest/integration/WarpIntakeAdapter.test.tstest/integration/WarpSubmissionAdapter.test.tsxyph-actuator.ts
| - Refined Actuator `syncWith` logic to use `syncCoverage()` for reliable multi-writer convergence. | ||
|
|
||
| [Unreleased]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.7...HEAD | ||
| [Unreleased]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.9...HEAD |
There was a problem hiding this comment.
Unused [Unreleased] link reference.
The [Unreleased] link definition exists but there's no corresponding ## [Unreleased] section header in the document. Either add an ## [Unreleased] section header for future changes, or remove the unused link reference if you plan to add it only when there are unreleased changes.
🔧 Option 1: Add section header
# CHANGELOG
All notable changes to XYPH will be documented in this file.
+## [Unreleased]
+
## [1.0.0-alpha.9] - 2026-02-25🔧 Option 2: Remove unused link
-[Unreleased]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.9...HEAD
[1.0.0-alpha.9]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.8...v1.0.0-alpha.9📝 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.
| [Unreleased]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.9...HEAD | |
| [1.0.0-alpha.9]: https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.8...v1.0.0-alpha.9 |
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 561-561: Link and image reference definitions should be needed
Unused link or image reference definition: "unreleased"
(MD053, link-image-reference-definitions)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CHANGELOG.md` at line 561, The CHANGELOG.md contains an unused link reference
"[Unreleased]:
https://github.com/flyingrobots/xyph/compare/v1.0.0-alpha.9...HEAD" with no
corresponding section header; fix by either adding a top-level section header
"## [Unreleased]" (and place any pending notes under it) or remove the link
reference line entirely if you don't intend to publish an Unreleased section
yet—update the file so the "[Unreleased]" link reference and the "##
[Unreleased]" header are consistent.
| ``` | ||
| Driving Adapters Domain Core Driven Adapters | ||
| ┌──────────────┐ ┌───────────────────┐ ┌──────────────────┐ | ||
| │ xyph-actuator│───────▶│ domain/entities │ │ WarpGraphAdapter │ | ||
| │ (CLI) │ │ domain/services │◀──────▶│ WarpIntakeAdapter│ | ||
| ├──────────────┤ │ domain/models │ ports │ WarpSubmission..│ | ||
| │xyph-dashboard│───────▶│ │◀──────▶│ WarpRoadmap... │ | ||
| │ (TUI/Ink) │ └───────────────────┘ │ GitWorkspace... │ | ||
| ├──────────────┤ └──────────────────┘ | ||
| │ coordinator │ | ||
| │ (daemon) │ | ||
| └──────────────┘ | ||
| ``` |
There was a problem hiding this comment.
Add language specifier to fenced code blocks.
The ASCII diagrams lack language specifiers, triggering MD040 warnings. Use text or plaintext for ASCII art diagrams.
🔧 Proposed fix
-```
+```text
Driving Adapters Domain Core Driven AdaptersApply the same change to the other diagram blocks at lines 34, 53, 58, and 66.
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 5-5: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/canonical/ARCHITECTURE.md` around lines 5 - 17, The fenced ASCII diagram
blocks (for example the block containing "Driving Adapters / xyph-actuator /
xyph-dashboard / coordinator" and the corresponding "Domain Core /
domain/entities / domain/services" and "Driven Adapters / WarpGraphAdapter /
WarpIntakeAdapter" diagram) lack a language specifier; update each such fenced
code block to start with ```text (or ```plaintext) instead of ``` so the ASCII
diagrams are treated as plain text (apply the same change to the other ASCII
diagram blocks in the file).
There was a problem hiding this comment.
🧹 Nitpick comments (1)
xyph-actuator.ts (1)
517-518: Consider clarifying the comment about patch visibility.The comment references
_onPatchCommittedwhich is an internal detail of WarpGraph. Consider rephrasing to focus on the user-visible behavior:🔧 Suggested clarification
- // Same graph instance — decide() patch is already visible via _onPatchCommitted + // Same graph instance — decide() patch is immediately visible due to autoMaterialize: true const sealGraph = await graphPort.getGraph();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@xyph-actuator.ts` around lines 517 - 518, Update the inline comment above the call to graphPort.getGraph() (near sealGraph declaration) to remove the internal method reference _onPatchCommitted and instead describe the observable behavior: that the graph instance is the same and any decide() patch is already applied/visible on that instance (so no additional patch handling is required). Keep the comment concise and reference sealGraph and graphPort.getGraph() so future readers understand the reason for retrieving the same graph instance without exposing internal WarpGraph internals.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@xyph-actuator.ts`:
- Around line 517-518: Update the inline comment above the call to
graphPort.getGraph() (near sealGraph declaration) to remove the internal method
reference _onPatchCommitted and instead describe the observable behavior: that
the graph instance is the same and any decide() patch is already applied/visible
on that instance (so no additional patch handling is required). Keep the comment
concise and reference sealGraph and graphPort.getGraph() so future readers
understand the reason for retrieving the same graph instance without exposing
internal WarpGraph internals.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
CHANGELOG.mddocs/canonical/ARCHITECTURE.mdsrc/infrastructure/GraphContext.tssrc/ports/GraphPort.tstest/integration/WarpIntakeAdapter.test.tstest/integration/WarpSubmissionAdapter.test.tsxyph-actuator.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/ports/GraphPort.ts
Replace filesystem-specific terms (file, path, rename) with storage-agnostic phrasing in the port interface documentation.
Summary
GraphPort/WarpGraphAdapter— oneWarpGraphinstance per process, injected via DI. EliminatesWRITER_CAS_CONFLICTerrors from multipleWarpGraphHolderinstances sharing the samewriterId.graph.query()for typed node fetching andgraph.traversefor graph algorithms. ExtractedDepAnalysis.tsfor frontier/critical-path computation.graph.patch()for all writes: All adapters, actuator, and coordinator daemon converted from manualsyncCoverage → materialize → createPatchSession → committograph.patch(p => { … }). RedundantsyncCoverage()/materialize()calls eliminated.GraphContextnow uses frontier key comparison (serialized writer:tick pairs) instead ofhasFrontierChanged(), which missed in-process writes.WarpGraphHolder,WarpDashboardAdapter,DashboardService,DashboardPort,WeaverService,WeaverPort,WarpWeaverAdapterand their tests.Test plan
npm run build— cleannpm run test:local— 327/327 tests passing (32 files)npx tsx xyph-actuator.ts status --view roadmaprenders correctlynpx tsx xyph-actuator.ts status --view depsrenders correctlySummary by CodeRabbit
New Features
Bug Fixes
Architecture
Removed
Documentation