feat(dashboard): reduce poll/render cost with leaner usage queries and UI refinements - #77
Conversation
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
test/dashboard/data.test.ts (1)
748-798: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider adding a project-only scope case (
{ projectId: 'p1', loopName: null }).That's the repo-list view the dashboard actually requests when no loop is open, and no test pins its payload shape (findings/usage present, plan/sections/amendments absent).
🤖 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 `@test/dashboard/data.test.ts` around lines 748 - 798, Extend the dashboard data test around collectDashboardData to cover project-only scope with { projectId: 'p1', loopName: null }. Assert that all loops in project p1 include findings and usage, while plan, sections, and amendments remain absent; also verify out-of-scope project data retains the expected empty findings, null usage, and populated bugCount 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/dashboard/app/app.ts`:
- Around line 88-95: Bound the fetch started in the load polling function with
an AbortSignal.timeout or equivalent manual AbortController so hung /api/data
requests are aborted after a finite duration. Pass the signal to fetch while
preserving the existing inFlight cleanup and coalescing behavior, allowing
finally to clear the request and the next interval to retry.
In `@src/storage/database.ts`:
- Line 94: Normalize dataDir at the configuration boundary before the mkdirSync
call, then reuse the normalized value for both directory creation and
resolveForgeDbPath. Update the surrounding database initialization flow so
whitespace-padded paths cannot produce different creation and connection
locations.
In `@test/utils/tui-client-stored-plan.test.ts`:
- Line 19: Update the resolveForgeDbPath mock to match production’s handling of
dataDir: trim whitespace and use the default '/tmp/forge-test-data-dir' path
when the value is empty or whitespace-only, while preserving the resolved
directory for non-empty input.
---
Nitpick comments:
In `@test/dashboard/data.test.ts`:
- Around line 748-798: Extend the dashboard data test around
collectDashboardData to cover project-only scope with { projectId: 'p1',
loopName: null }. Assert that all loops in project p1 include findings and
usage, while plan, sections, and amendments remain absent; also verify
out-of-scope project data retains the expected empty findings, null usage, and
populated bugCount 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: CHILL
Plan: Pro Plus
Run ID: 6db2d7bd-eba4-4a5e-9ca7-dca59614cf14
📒 Files selected for processing (61)
AGENTS.mdREADME.mddocs/api/README.mddocs/api/_media/loop-system.mddocs/api/functions/createForgePlugin.mddocs/api/functions/createParentSessionLookup.mddocs/api/functions/createSessionDirectoryLookup.mddocs/api/interfaces/CompactionConfig.mddocs/api/interfaces/CreateParentSessionLookupOptions.mddocs/api/interfaces/CreateSessionDirectoryLookupOptions.mddocs/api/interfaces/PluginConfig.mddocs/api/variables/VERSION.mddocs/api/variables/default.mddocs/loop-system.mdscripts/dashboard.tssrc/dashboard/app-bundle.tssrc/dashboard/app/app.tssrc/dashboard/app/components.tssrc/dashboard/app/helpers.tssrc/dashboard/data.tssrc/dashboard/launch.tssrc/dashboard/render.tssrc/dashboard/server.tssrc/hooks/plan-approval.tssrc/index.tssrc/loop/service.tssrc/loop/token-usage.tssrc/services/deterministic-decomposer.tssrc/services/execution.tssrc/storage/database.tssrc/storage/index.tssrc/storage/repos/loop-session-usage-repo.tssrc/storage/repos/loop-transitions-repo.tssrc/storage/repos/plans-repo.tssrc/storage/repos/review-findings-repo.tssrc/storage/repos/section-plans-repo.tssrc/tools/loop.tssrc/tools/plan-authoring.tssrc/tui.tsxsrc/utils/format.tssrc/utils/logger.tssrc/utils/loop-format.tssrc/utils/opencode-paths.tssrc/utils/tui-client.tssrc/utils/tui-loop-store.tstest/dashboard/app-dom.test.tstest/dashboard/app-helpers.test.tstest/dashboard/data.test.tstest/dashboard/server.test.tstest/hooks/plan-approval-dedupe.test.tstest/hooks/plan-approval-worktree-timing.test.tstest/loop-format.test.tstest/loop-session-usage-repo.test.tstest/loop-status-tool.test.tstest/loop-transitions-repo.test.tstest/loop/token-usage.test.tstest/plan-approval.test.tstest/review-findings-repo.test.tstest/tools/plan-authoring.test.tstest/utils/tui-client-stored-plan.test.tstest/worktree-log.test.ts
| const load = async () => { | ||
| const url = scopedDataUrl() | ||
| if (inFlight.url === url) return // coalesce overlapping same-scope polls | ||
| const gen = ++loadGen | ||
| inFlight.url = url | ||
| inFlight.gen = gen | ||
| try { | ||
| const res = await fetch('/api/data', { cache: 'no-store' }) | ||
| const res = await fetch(url, { cache: 'no-store' }) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
A hung /api/data request permanently wedges polling for that scope.
inFlight.url is only cleared in the finally, and the coalescing guard on Line 90 drops every subsequent same-scope poll while it is set. If a fetch never settles (dropped connection, sleeping laptop resumed mid-request), the 5s interval becomes a no-op indefinitely — the UI keeps rendering stale data with no error. A scope change is the only escape.
An AbortSignal.timeout (or a manual controller) bounds this; the abort rejects, finally clears inFlight, and the next tick recovers.
🛡️ Proposed fix: bound the request
const load = async () => {
const url = scopedDataUrl()
if (inFlight.url === url) return // coalesce overlapping same-scope polls
const gen = ++loadGen
inFlight.url = url
inFlight.gen = gen
try {
- const res = await fetch(url, { cache: 'no-store' })+ const res = await fetch(url, { cache: 'no-store', signal: AbortSignal.timeout(20000) })
if (gen !== loadGen) return📝 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.
| constload=async()=>{ | |
| consturl=scopedDataUrl() | |
| if(inFlight.url===url)return// coalesce overlapping same-scope polls | |
| constgen=++loadGen | |
| inFlight.url=url | |
| inFlight.gen=gen | |
| try{ | |
| constres=awaitfetch('/api/data',{cache: 'no-store'}) | |
| constres=awaitfetch(url,{cache: 'no-store'}) | |
| constload=async()=>{ | |
| consturl=scopedDataUrl() | |
| if(inFlight.url===url)return// coalesce overlapping same-scope polls | |
| constgen=++loadGen | |
| inFlight.url=url | |
| inFlight.gen=gen | |
| try{ | |
| constres=awaitfetch(url,{cache: 'no-store',signal: AbortSignal.timeout(20000)}) |
🤖 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/dashboard/app/app.ts` around lines 88 - 95, Bound the fetch started in
the load polling function with an AbortSignal.timeout or equivalent manual
AbortController so hung /api/data requests are aborted after a finite duration.
Pass the signal to fetch while preserving the existing inFlight cleanup and
coalescing behavior, allowing finally to clear the request and the next interval
to retry.
| } | ||
| const dbPath = `${dataDir}/forge.db` | ||
| const dbPath = resolveForgeDbPath(dataDir) |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
Normalize dataDir before creating it.
resolveForgeDbPath trims the directory, but mkdirSync(dataDir) above still uses the raw value. A whitespace-padded configured path can therefore create one directory while opening the database under another path. Normalize the directory once at the configuration boundary and use that value for both operations.
🤖 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/storage/database.ts` at line 94, Normalize dataDir at the configuration
boundary before the mkdirSync call, then reuse the normalized value for both
directory creation and resolveForgeDbPath. Update the surrounding database
initialization flow so whitespace-padded paths cannot produce different creation
and connection locations.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
31ad952
into
feat/dashboard-repo-shellUh oh!
There was an error while loading. Please reload this page.
…gation (#75) * feat(dashboard): add multi-level repo shell with searchable loop navigation Replace the flat dashboard loop list with a three-level shell: a repository index, a per-repo view with Loops/Groups/Findings/Plans sections, and a per-loop detail with tabs. Add a searchable loop picker in the breadcrumb that lists every repo loop sorted most recent first with a timestamp, supports type-to-filter and arrow/Enter/Escape keyboard navigation, and preserves the active tab on jump. The recency-ordered options drive both the picker and the prev/next pager so they agree on order. Hide the repo-level section-nav inside a loop detail, leaving only the loop tabs. * feat(dashboard): make markdown sections collapsible and non-scrolling Replace the internal scroll box with a full-height body that toggles collapse via an accessible caret, and promote the state-machine graph on the timeline tab out of a <details> block to always-visible. * feat(dashboard): add markdown TOC, feature-stage badges, and faster usage queries * feat(dashboard): stack running cards and make title a home link * feat: add plan-authoring tools, capture, and structure utilities (#76) * feat: add plan-authoring tools, capture, and structure utilities Introduces a plan-authoring tool surface with capture and parsing helpers, section-bootstrap and decomposer updates, plus tests across plan-authoring, capture, structure, fences, and TUI stored-plan state. * refactor: centralize plan-authoring tool names and plan-of-record resolution --------- Co-authored-by: Forge <forge@example.com> * feat(dashboard): reduce poll/render cost with leaner usage queries and UI refinements (#77) * feat(dashboard): reduce poll/render cost with leaner usage queries and UI refinements * refactor: unify forge-db path and active-loop guards, slim dashboard payload * Update test/utils/tui-client-stored-plan.test.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Forge <forge@example.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: apply CodeRabbit auto-fixes Fixed 7 file(s) based on 6 unresolved review comments. Co-authored-by: CodeRabbit <noreply@coderabbit.ai> --------- Co-authored-by: Forge <forge@example.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
Summary
Reduces dashboard poll/render cost with leaner server payload assembly and more efficient loop-session usage queries, plus UI refinements in the dashboard app. Refactors loop token-usage handling and loop status formatting, and adds aggregated helpers in the plans/section-plans repos.
Key changes
Files changed
22 files changed, +2142/-204
Summary by CodeRabbit
New Features
Bug Fixes
Documentation