Skip to content

feat: async TUI syncing and 16x vitest speed optimization#65

Merged
flyingrobots merged 17 commits into
mainfrom
cycle/restoration-and-alignment
Jun 21, 2026
Merged

feat: async TUI syncing and 16x vitest speed optimization#65
flyingrobots merged 17 commits into
mainfrom
cycle/restoration-and-alignment

Conversation

@flyingrobots

Copy link
Copy Markdown
Owner

Summary

This PR implements asynchronous remote syncing for the Bijou TUI and optimizes test suite performance.

Key Changes

  1. Asynchronous TUI Remote Syncing
    • Introduced { syncOnQuery: false } to prevent reading adapters from blocking the main render thread.
    • Decoupled syncCoverage runs in DashboardApp from the event loop, running syncs asynchronously in a background Cmd boundary.
    • Added user-facing sync lifecycle toasts (Syncing..., Synced successfully) to highlight network updates without spamming the TUI.
  2. Vitest Speed Optimization
    • Configured WarpGraphAdapter to optionally use @git-stunts/git-warp's InMemoryGraphAdapter when process.env['XYPH_TEST_IN_MEMORY'] is enabled.
    • Implemented a static registry mapped by cwd (repository path) inside the adapter to preserve shared state semantics across derived worldlines and isolated read graphs during tests.
    • Enabled in-memory persistence globally in test/setup.ts, boosting vitest runtime from ~129s to ~8.13s (16x overall speedup, and 350x speedup for the main integration test).

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@flyingrobots, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 44 minutes and 47 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 52b4ec4c-0ce3-4442-af7d-8faafea047d5

📥 Commits

Reviewing files that changed from the base of the PR and between af064f2 and 560167e.

⛔ Files ignored due to path filters (15)
  • docs/diagrams/cycle-loop.svg is excluded by !**/*.svg
  • docs/diagrams/milestone-lifecycle.svg is excluded by !**/*.svg
  • docs/diagrams/orchestration-fsm.svg is excluded by !**/*.svg
  • docs/diagrams/planning-pipeline.svg is excluded by !**/*.svg
  • docs/diagrams/quest-status-lifecycle.svg is excluded by !**/*.svg
  • docs/diagrams/roadmap.svg is excluded by !**/*.svg
  • docs/diagrams/task-lifecycle.svg is excluded by !**/*.svg
  • docs/diagrams/teardown-cli-context.svg is excluded by !**/*.svg
  • docs/diagrams/teardown-domain-entities.svg is excluded by !**/*.svg
  • docs/diagrams/teardown-hexagonal-arch.svg is excluded by !**/*.svg
  • docs/diagrams/teardown-jsonl-types.svg is excluded by !**/*.svg
  • docs/diagrams/teardown-schema.svg is excluded by !**/*.svg
  • docs/diagrams/teardown-submission-lifecycle.svg is excluded by !**/*.svg
  • docs/diagrams/vision-tenets.svg is excluded by !**/*.svg
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (70)
  • GUIDE.md
  • backlog.md
  • docs/audit/2024-07-29_code_quality.md
  • docs/audit/2024-07-29_documentation_quality.md
  • docs/audit/2024-07-29_ship_readiness.md
  • docs/diagrams/roadmap.mmd
  • docs/diagrams/roadmap.mmd.sha256
  • docs/feedback/2026-06-11-agent-reply.md
  • docs/roadmap.md
  • package.json
  • patches/@git-stunts+git-warp+18.0.0.patch
  • scripts/_blocker-analysis.ts
  • scripts/_check-campaign.ts
  • scripts/_check-status.ts
  • scripts/assign-orphan-campaigns.ts
  • scripts/audit-orphans.ts
  • scripts/backlog-update.mts
  • scripts/execute-merge-rethink.ts
  • scripts/execute-triage.ts
  • scripts/export-dag.ts
  • scripts/generate-work-dag.ts
  • scripts/graveyard-ghosts.mts
  • scripts/migrate-voc-001.ts
  • scripts/reconcile-backlog.ts
  • scripts/repair-orphans.ts
  • scripts/repair-warp-graph.ts
  • scripts/seed-bearing.ts
  • scripts/seed-invariants.ts
  • scripts/seed-legends.ts
  • scripts/seed-milestone-deps.ts
  • scripts/setup-milestone-2.js
  • scripts/setup-milestones-4-7.ts
  • scripts/wire-campaign-links-wave2.ts
  • scripts/wire-deps-fixup.ts
  • scripts/wire-deps-wave2.ts
  • scripts/wire-deps-wave3.ts
  • scripts/wire-deps.ts
  • src/cli/commands/dashboard.ts
  • src/domain/services/AiSuggestionReadService.ts
  • src/domain/services/ControlPlaneService.ts
  • src/domain/services/GovernanceArtifacts.ts
  • src/domain/services/MutationKernelService.ts
  • src/domain/services/RecordService.ts
  • src/domain/services/ReviewLaneReadService.ts
  • src/infrastructure/ObservedGraphProjection.ts
  • src/infrastructure/adapters/WarpDashboardReadAdapter.ts
  • src/infrastructure/adapters/WarpGraphAdapter.ts
  • src/infrastructure/adapters/WarpObservationAdapter.ts
  • src/infrastructure/adapters/WarpRoadmapAdapter.ts
  • src/infrastructure/logging/AdapterLogging.ts
  • src/inspect-graph.ts
  • src/ports/ObservationPort.ts
  • src/tui/bijou/DashboardApp.ts
  • src/tui/bijou/__tests__/DashboardApp.test.ts
  • src/tui/bijou/__tests__/integration.test.ts
  • src/tui/bijou/__tests__/views.test.ts
  • src/tui/render-status.ts
  • src/tui/theme/xyph-presets.ts
  • test/helpers/ansi.ts
  • test/integration/ConcurrentClaim.test.ts
  • test/integration/ControlPlaneWorldlineParity.test.ts
  • test/integration/GraphContextEntityDetail.test.ts
  • test/integration/GraphContextReadHonesty.test.ts
  • test/setup.ts
  • test/unit/ControlPlaneService.test.ts
  • test/unit/DashboardTraceCommand.test.ts
  • test/unit/MutationKernelService.test.ts
  • test/unit/ReviewLaneReadService.test.ts
  • test/unit/WarpDashboardReadAdapter.test.ts
  • xyph-theme-lab.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cycle/restoration-and-alignment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@flyingrobots

Copy link
Copy Markdown
Owner Author

Self-Code Review Findings

An automated self-review has been conducted on the changes in this branch using the Code Rabbit persona.

Severity Breakdown

Critical High Medium Low Total
0 0 0 1 1

Evidence Bundle & Detailed Findings

File Line Type Severity Description Recommended Mitigation / Prompt
src/tui/bijou/DashboardApp.ts 2897 UX Feedback Low Auto-dismissing the Syncing... toast on a fixed 3-second timer. If remote syncing takes longer than 3 seconds due to network latency, the toast is dismissed early, causing the subsequent completion toast (sync-complete) to be suppressed. Modify the syncing toast so it is only dismissed or replaced when the async sync-complete message arrives, rather than scheduling an early timer-based dismissal on a static 3s window.

Hey @codex, please confirm if this matches our review guidelines and if there are any other edge cases to consider!

@flyingrobots

Copy link
Copy Markdown
Owner Author

Update: All Issues Resolved

The code review finding regarding the premature sync toast expiration has been successfully resolved:

  • The syncing toast in DashboardApp.ts now remains active with an extended fallback duration (30 seconds) rather than dismissing after 3 seconds.
  • The async sync-complete event transitions the active toast to the final success/failure message and schedules its 3-second fadeout cleanly.
  • Verified all 1006 tests and the linter pass successfully.

CC @codex

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 129b0c327b

ℹ️ 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".

const expiresAt = Date.now() + 3000;
const syncMessage = msg.error ? `Sync failed: ${msg.error}` : 'Graph sync complete.';
const syncVariant = msg.error ? 'error' : 'success';
const showToast = !!(model.toast && model.toast.message.includes('Syncing'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Keep sync toasts until background sync finishes

When remote sync takes longer than the fixed 3-second toast lifetime scheduled for remote-change/manual refresh, the dismiss-toast handler clears the "Syncing" toast before this check runs. In that case showToast is false, so both the success toast and a possible Sync failed: ... toast are suppressed even though the background sync just completed; keep the syncing toast alive until sync-complete replaces it.

Useful? React with 👍 / 👎.

}

if (msg.type === 'sync-complete') {
if (msg.requestId !== model.requestId) return [model, []];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve sync completion for queued refreshes

When a watcher remote-change arrives while a dashboard load is already in progress, the early return only sets refreshPending; snapshot-loaded then bumps requestId for the queued refresh but does not launch a backgroundSync for that new id. The original sync-complete is discarded here as stale, so syncing can stay true and the queued remote update is refreshed without ever pulling coverage; schedule a sync for the pending refresh or don't invalidate the in-flight completion.

Useful? React with 👍 / 👎.

deps.logger?.debug('dashboard background sync started', { requestId });
const graph = await deps.graphPort.getGraph();
if (typeof graph.syncCoverage === 'function') {
await graph.syncCoverage();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Materialize after background sync before refetching

When syncCoverage() imports remote patches, the graph's materialized basis can be stale until materialize() runs; this command emits sync-complete immediately and the completion handler refetches through dashboard observer sessions with syncCoverage: false, so the "complete" refresh can still read the old basis or hit stale-basis errors instead of showing the remote changes. Mirror the other post-sync paths by materializing before emitting completion.

Useful? React with 👍 / 👎.

Comment thread package.json
"@flyingrobots/bijou-node": "^7.1.0",
"@flyingrobots/bijou-tui": "^7.1.0",
"@git-stunts/git-warp": "^18.0.0",
"@git-stunts/plumbing": "^3.0.3",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Migrate remaining async plumbing call sites

With this bump to @git-stunts/plumbing 3.x, createDefault is async (the same change adds await in WarpGraphAdapter and inspect-graph), but rg still finds many direct script call sites such as scripts/generate-work-dag.ts:105 and scripts/repair-orphans.ts:62 passing the returned Promise into GitGraphAdapter. Those maintenance commands will fail at runtime when they touch the plumbing object; either migrate the remaining call sites or don't bump the dependency yet.

Useful? React with 👍 / 👎.

Comment thread scripts/generate-work-dag.ts Outdated
Comment on lines +114 to +115
// await graph.syncCoverage();
// await graph.materialize();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep graph DAG generation synced with current refs

For npm run graph:work, removing both the coverage sync and materialization means the DAG is built from whatever graph state was already local/materialized when the process opened. In repositories where another writer has pushed graph refs, this now emits a stale work DAG even though the script previously synchronized first; keep the sync/materialize step for this offline report path.

Useful? React with 👍 / 👎.

Comment on lines +79 to +82
let memPersistence = WarpGraphAdapter.memoryBackends.get(this.cwd);
if (!memPersistence) {
memPersistence = new InMemoryGraphAdapter();
WarpGraphAdapter.memoryBackends.set(this.cwd, memPersistence);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Isolate in-memory test backends across recreated repos

Under the global XYPH_TEST_IN_MEMORY=true test setup, this static map is keyed only by the cwd string and is never cleared, so a temp repo that is deleted and recreated at the same path (several tests use os.tmpdir() plus Date.now()) reuses the old in-memory graph instead of starting from an empty Git repo. That can leak nodes between tests and make the optimized suite flaky; include a per-repo incarnation key or clear the backend when the repo is recreated.

Useful? React with 👍 / 👎.

const aiSuggestions: AiSuggestionNode[] = [];
for (const node of suggestionNodes) {
if (node.props['type'] !== 'ai_suggestion') continue;
if (node.props['type'] !== 'ai_suggestion' && node.props['type'] !== 'ai-suggestion') continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep dashed AI suggestions resolvable

By broadening the read side to include raw type: 'ai-suggestion' nodes, those suggestions can now appear in the dashboard lanes, but the resolution path still rejects anything whose graph property is not exactly ai_suggestion in RecordService.adoptAiSuggestion/dismissAiSuggestion/supersedeAiSuggestion. In graphs containing dashed-type legacy or imported suggestions, the UI can offer adopt/dismiss/supersede actions that always fail as [NOT_FOUND]; normalize these nodes before display or accept the same type spelling in the write path.

Useful? React with 👍 / 👎.

@flyingrobots flyingrobots merged commit da070d8 into main Jun 21, 2026
7 of 9 checks passed
@flyingrobots flyingrobots deleted the cycle/restoration-and-alignment branch June 21, 2026 04:58
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