Skip to content

feat: Work DAG analysis suite + bijou v1.2.0 adoption#33

Merged
flyingrobots merged 11 commits intomainfrom
feat/work-dag-analysis
Mar 4, 2026
Merged

feat: Work DAG analysis suite + bijou v1.2.0 adoption#33
flyingrobots merged 11 commits intomainfrom
feat/work-dag-analysis

Conversation

@flyingrobots
Copy link
Owner

@flyingrobots flyingrobots commented Mar 4, 2026

Summary

  • Work DAG Analysis Suite — pure-function library (DagAnalysis.ts) for DAG structure analysis: level assignment, width, greedy worker scheduling, transitive reduction/closure, anti-chain decomposition, reverse reachability, provenance tracing. Generator script (scripts/generate-work-dag.ts) produces SVG visualizations + work.md analysis doc.
  • bijou v1.2.0 upgrade@flyingrobots/bijou, @flyingrobots/bijou-node, @flyingrobots/bijou-tui from v0.10.0 to v1.2.0.
  • Adopt dagPane() — replaced ~50 lines of manual dagLayout() + viewport() + scroll-centering in roadmap view with bijou's dagPane() building block. Auto-scroll-to-selection, keyboard-synced DAG highlight.
  • Adopt focusArea() — dashboard columns now show a colored gutter indicating which panel has focus (bright = active, muted = inactive).
  • PR feat(analysis): add Work DAG analysis suite #32 review fixes — DONE task weight 0 in scheduling, CI traceability fix, Kahn's topo sort delegated to git-warp, non-null assertion cleanup, SVG removed from index.
  • Workflow infrastructure — git hooks (pre-commit lint, pre-push test), graph:pull/graph:push scripts, CI traceability job.
  • 43 new tests — unit tests for all DagAnalysis functions.

Test plan

  • npm run build — 0 TypeScript errors
  • npm run lint — 0 errors, 0 warnings
  • npm run test:local — 788/788 tests pass
  • CI passes (build + lint + test + traceability)
  • Manual: npx tsx xyph-dashboard.tsx — roadmap DAG renders with auto-scroll, dashboard columns show focus gutter

Summary by CodeRabbit

  • New Features

    • Roadmap now uses a DAG pane with auto-scroll-to-selection and keyboard navigation; dashboard columns use a focus-area system for clearer focused/unfocused presentation
    • Added an adapter to support pane-based DAG rendering
  • Bug Fixes

    • Safer handling for missing DAG data and snapshot edge cases to prevent runtime errors
    • Restored fallback scrolling when no DAG edges exist
  • Chores

    • Bumped package version and upgraded bijou dependencies
    • Updated ignore rules for generated SVG assets

- Replace userland Kahn's topo sort with graph.traverse.topologicalSort() (P1-01)
- Remove unused reverseReachability import (P2-01)
- Remove dead allNodes set in computeProvenance (P2-02)
- Replace non-null assertions with guard patterns (P3-01/02/04)
- Extract DEFAULT_COLORS constant to eliminate redundant assertion (P3-03)
- Remove 237KB SVG from tracking, add docs/assets/*.svg to .gitignore (P4-01)
Upgrade @flyingrobots/bijou, bijou-node, bijou-tui from v0.10.0 to v1.2.0.
New features: dagPane(), focusArea(), granular port types, detectColorScheme().
No breaking changes — 788/788 tests pass.
Replace ~50 lines of manual dagLayout() + viewport() + scroll-centering
math in roadmap-view.ts with bijou v1.2.0's dagPane() building block.

- RoadmapState: dagScrollY/dagScrollX → dagPane: DagPaneState | null
- Extract buildDagSource() for DagSource adapter construction
- Snapshot-loaded handler builds dagPane with critical path highlighting
- Selection sync: j/k navigation updates dagPane selection
- Scroll handlers delegate to dagPanePageDown/PageUp/ScrollByX
- Resize handler recreates dagPane with new dimensions
- No-deps fallback uses fixed scrollY: 0
Replace bare viewport() calls in dashboard-view.ts with bijou v1.2.0's
focusArea() building block. The focused column gets a bright gutter
indicator, the unfocused column gets a muted gutter. Tab switches focus
and the gutter color swaps — instant visual indicator of which panel
accepts PgDn/PgUp input.

No model changes — FocusAreaState is created on the fly from existing
leftScrollY/rightScrollY values.
@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a0c9b180-636c-4422-8529-f3edaad09c6a

📥 Commits

Reviewing files that changed from the base of the PR and between 3dcb914 and ae885b8.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • src/tui/bijou/__tests__/DashboardApp.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/tui/bijou/tests/DashboardApp.test.ts

📝 Walkthrough

Walkthrough

Replaces raw dagScrollX/dagScrollY state and ad-hoc topo sorting with bijou v1.2.0 pane primitives: loadGraph now returns a topologically sorted ID list; DashboardApp and roadmap view use DagPane/DagPaneState and focusArea; scripts/tests updated for guarded access and DEFAULT_COLORS usage.

Changes

Cohort / File(s) Summary
Dependencies & Ignore
package.json, .gitignore
Bumped package to 1.0.0-alpha.13 and upgraded bijou packages to ^1.2.0; narrowed SVG ignore to docs/assets/work-dag*.svg.
Work-dag script & colors
scripts/generate-work-dag.ts
loadGraph now returns sorted: string[]; removed internal topo-sort; added DEFAULT_COLORS; added guards for optional values and adjusted bottleneck logic.
Graph analysis
src/domain/services/DagAnalysis.ts
Removed unused allNodes set from provenance/root-finding; simplified computeProvenance.
Dashboard state & pane wiring
src/tui/bijou/DashboardApp.ts, src/tui/bijou/__tests__/DashboardApp.test.ts
Replaced dagScrollX/dagScrollY with `dagPane: DagPaneState
Roadmap view & DAG source
src/tui/bijou/views/roadmap-view.ts
Added buildDagSource(snap, critSet): SlicedDagSource; render via dagPane(model.roadmap.dagPane, ...); use fallbackScrollY for non-pane fallbacks; removed DagLayout usage.
Dashboard layout focusArea
src/tui/bijou/views/dashboard-view.ts
Replaced column viewport rendering with focusArea primitives and restored prior scroll offsets via focusAreaScrollTo.
Model/type/test updates
src/tui/bijou/__tests__/views.test.ts, test/unit/DagAnalysis.test.ts
Updated DashboardModel.roadmap shape to include dagPane and fallbackScrollY; tests adjusted to expect dagPane null initial state and use guards instead of non-null assertions.
Changelog
CHANGELOG.md
Documented dependency upgrades, DAG/pane refactor, delegation of topological sort, and safety/guard changes.

Sequence Diagram(s)

sequenceDiagram
    participant DA as DashboardApp
    participant Graph as Graph Engine
    participant Pane as DagPane
    participant Table as Roadmap Table

    DA->>Graph: loadGraph(snapshot) → tasks, campaigns, sorted
    Graph-->>DA: return tasks, campaigns, sorted
    DA->>Graph: computeCriticalPath(sorted, tasks)
    Graph-->>DA: critical path set
    DA->>Pane: createDagPaneState(buildDagSource(...), size, options)
    Pane-->>DA: DagPaneState
    alt user focuses table row
        Table->>DA: focusRow → questId
        DA->>Pane: dagPaneSelectNode(questId)
        Pane-->>DA: selection updated
        DA->>Table: sync focus state
    end
    alt user pages/scrolls DAG
        DA->>Pane: dagPanePageDown/Up / dagPaneScrollByX
        Pane-->>DA: updated DagPaneState
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through nodes and tidy lines,
Through panes that hold my sorted vines.
Guards now stand where bangs once leapt,
Focus finds the rows once kept.
A tiny trail of SVG delight—hop, hop, hooray! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main objectives of the PR: introducing a Work DAG analysis suite and upgrading to bijou v1.2.0 with associated API adoptions.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/work-dag-analysis

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

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

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

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

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 (2)
.gitignore (1)

15-15: Consider narrowing the SVG ignore pattern to generated artifacts only.

docs/assets/*.svg will ignore all SVGs in that folder, including future hand-maintained diagrams/icons. If only generated DAG outputs should be ignored, a narrower pattern reduces accidental omissions.

Example narrow pattern
-docs/assets/*.svg
+docs/assets/work-dag*.svg
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore at line 15, The .gitignore currently ignores all SVGs via the
pattern docs/assets/*.svg which may hide hand-edited icons; replace or
supplement that entry by narrowing it to only generated artifacts (e.g., change
docs/assets/*.svg to a more specific pattern matching generated DAG outputs such
as docs/assets/*-dag.svg or docs/assets/generated/*.svg) so that hand-maintained
SVGs remain tracked; update the docs/assets/*.svg line accordingly and add a
comment explaining the pattern and what is considered generated.
scripts/generate-work-dag.ts (1)

343-346: Drop the unused sorted parameter from buildAnalysisInputs().

Line 345 is currently unused, and passing it on Line 639 makes the API look semantically required when it is not.

♻️ Proposed cleanup
-function buildAnalysisInputs(
-  tasks: Map<string, TaskNode>,
-  sorted: string[],
-): {
+function buildAnalysisInputs(
+  tasks: Map<string, TaskNode>,
+): {
   summaries: TaskSummary[];
   edges: DepEdge[];
 } {
@@
-  const { summaries, edges } = buildAnalysisInputs(tasks, sorted);
+  const { summaries, edges } = buildAnalysisInputs(tasks);

Also applies to: 639-639

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

In `@scripts/generate-work-dag.ts` around lines 343 - 346, The function
buildAnalysisInputs currently accepts an unused parameter named sorted; remove
the sorted parameter from buildAnalysisInputs' signature and update all call
sites that pass sorted (e.g., the call that currently passes sorted) to stop
providing that argument so the API no longer suggests the value is required;
ensure you update any related TypeScript types or function usages (function
name: buildAnalysisInputs) so compilation succeeds after the parameter is
removed.
🤖 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/tui/bijou/DashboardApp.ts`:
- Around line 402-404: The computed pane sizes can become zero or negative on
small terminals; update the width/height calculations in DashboardApp.ts
(symbols: leftWidth, dagWidth, dagHeight and the other analogous calculations
later) to clamp to safe minimums using Math.max so they never go <= 0 (e.g.,
keep leftWidth = Math.max(28, Math.floor(msg.columns * 0.3)), dagWidth =
Math.max(1, msg.columns - leftWidth - 1), dagHeight = Math.max(1, msg.rows -
3)); apply the same defensive Math.max clamping to the alternate calculations
around the second block that uses these same symbols.
- Around line 447-473: The DAG pane is only created when dagEdges.length > 0
which leaves roadmap.dagPane null for snapshots that have tasks but no
dependency edges; change the logic to create a DagPane whenever there are
dagTasks (or roadmapQuestIds) present: compute the criticalPath only if
dagEdges.length > 0 (calling computeCriticalPath when edges exist) but always
call buildDagSource and createDagPaneState when there are tasks, set dagOptions
as before, and still apply dagPaneSelectNode using
selectedId/roadmapQuestIds/newRoadmapTable.focusRow; in short, remove the strict
dagEdges gating around createDagPaneState and instead gate only the
critical-path computation.

---

Nitpick comments:
In @.gitignore:
- Line 15: The .gitignore currently ignores all SVGs via the pattern
docs/assets/*.svg which may hide hand-edited icons; replace or supplement that
entry by narrowing it to only generated artifacts (e.g., change
docs/assets/*.svg to a more specific pattern matching generated DAG outputs such
as docs/assets/*-dag.svg or docs/assets/generated/*.svg) so that hand-maintained
SVGs remain tracked; update the docs/assets/*.svg line accordingly and add a
comment explaining the pattern and what is considered generated.

In `@scripts/generate-work-dag.ts`:
- Around line 343-346: The function buildAnalysisInputs currently accepts an
unused parameter named sorted; remove the sorted parameter from
buildAnalysisInputs' signature and update all call sites that pass sorted (e.g.,
the call that currently passes sorted) to stop providing that argument so the
API no longer suggests the value is required; ensure you update any related
TypeScript types or function usages (function name: buildAnalysisInputs) so
compilation succeeds after the parameter is removed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e035f5dd-91eb-4abe-8b22-f9aef713d60a

📥 Commits

Reviewing files that changed from the base of the PR and between fa85965 and d103c8f.

⛔ Files ignored due to path filters (2)
  • docs/assets/work-dag.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • .gitignore
  • CHANGELOG.md
  • package.json
  • scripts/generate-work-dag.ts
  • src/domain/services/DagAnalysis.ts
  • src/tui/bijou/DashboardApp.ts
  • src/tui/bijou/__tests__/DashboardApp.test.ts
  • src/tui/bijou/__tests__/views.test.ts
  • src/tui/bijou/views/dashboard-view.ts
  • src/tui/bijou/views/roadmap-view.ts
  • test/unit/DagAnalysis.test.ts
💤 Files with no reviewable changes (1)
  • src/domain/services/DagAnalysis.ts

@flyingrobots
Copy link
Owner Author

Addressed remaining nit feedback from the review summary as well:

  • Removed the unused sorted parameter from buildAnalysisInputs() and updated its call site.
  • Narrowed .gitignore SVG scope to generated DAG artifacts only and documented the intent inline.

✅ Addressed in commits be2b31f and bee71c9.

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

🧹 Nitpick comments (2)
src/tui/bijou/DashboardApp.ts (1)

454-462: Consider centralizing snapshot→DepEdge[] projection.

Lines 454–462 duplicate dependency-edge extraction logic also present in src/tui/bijou/views/roadmap-view.ts. A shared helper would reduce drift risk between model-update and rendering paths.

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

In `@src/tui/bijou/DashboardApp.ts` around lines 454 - 462, Duplicate logic builds
dependency edges from a snapshot (creating dagEdges from snap.quests using
q.dependsOn) in DashboardApp.ts (dagTasks/dagEdges with snap.quests) and the
roadmap-view; extract that projection into a shared helper function (e.g.,
buildDepEdgesFromSnapshot or projectSnapshotDepEdges) and replace the inline
loop in DashboardApp (and the corresponding code in roadmap-view) to call the
helper; ensure the helper accepts the same snapshot shape (or an array of
quests) and returns DepEdge[] so both rendering and model-update paths use the
single implementation.
src/tui/bijou/views/roadmap-view.ts (1)

29-31: Prefer sortedTaskIds for SlicedDagSource.ids() ordering.

Line 29 uses raw quest insertion order. Using snap.sortedTaskIds (filtered to known quests) will keep DAG layout/order deterministic across refreshes.

💡 Suggested change
-  const questIds = snap.quests.map(q => q.id);
+  const questIds = snap.sortedTaskIds.length > 0
+    ? snap.sortedTaskIds.filter((id) => questMap.has(id))
+    : snap.quests.map((q) => q.id);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tui/bijou/views/roadmap-view.ts` around lines 29 - 31, The current
SlicedDagSource.ids() implementation builds questIds from snap.quests (questIds
variable) which preserves insertion order; change it to derive ids from
snap.sortedTaskIds (filtered to only include IDs that exist in snap.quests) so
DAG layout/order is deterministic across refreshes—use
snap.sortedTaskIds.filter(id => snap.quests.some(q => q.id === id)) (or build a
Set of snap.quests ids for faster lookup) and return that list from ids()
instead of questIds.
🤖 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`:
- Around line 26-27: The release notes conflict about the .gitignore SVG
pattern—one line claims it was narrowed to `docs/assets/work-dag*.svg` while
another claims `docs/assets/*.svg` was added; reconcile them by deciding the
true change and updating both occurrences in CHANGELOG.md (references: the
`docs/assets/work-dag*.svg` and `docs/assets/*.svg` phrases) so the same pattern
and rationale appear consistently (e.g., change the Line 26 entry to match Line
37 or vice versa) and ensure the note explains why that specific pattern exists.
- Line 32: Update the CHANGELOG entry to remove the contradictory phrase by
clearly stating the direction of the change: replace the “reimplemented in
userland” wording with an unambiguous description such as “Replaced hand-rolled
Kahn’s algorithm with graph.traverse.topologicalSort()” and mention the affected
module (generate-work-dag.ts) so it reads: generate-work-dag.ts now delegates to
graph.traverse.topologicalSort() instead of using a hand-rolled Kahn
implementation.

In `@src/tui/bijou/__tests__/DashboardApp.test.ts`:
- Around line 304-334: The test currently asserts rendered output equality to
verify paging; instead assert the roadmap fallback scroll state: after calling
app.update(makeKey('pagedown'), loaded) verify that the returned model
(afterPgDn) has roadmap.fallbackScrollY increased relative to the initial
loaded. Then after calling app.update(makeKey('pageup'), afterPgDn) verify the
returned model (afterPgUp) has roadmap.fallbackScrollY equal to the original
loaded value (or decreased appropriately). Locate and update assertions around
makeKey('pagedown')/makeKey('pageup'), app.update(), app.view(), and the
DashboardModel to check roadmap.fallbackScrollY delta/reset rather than
comparing full rendered strings.

---

Nitpick comments:
In `@src/tui/bijou/DashboardApp.ts`:
- Around line 454-462: Duplicate logic builds dependency edges from a snapshot
(creating dagEdges from snap.quests using q.dependsOn) in DashboardApp.ts
(dagTasks/dagEdges with snap.quests) and the roadmap-view; extract that
projection into a shared helper function (e.g., buildDepEdgesFromSnapshot or
projectSnapshotDepEdges) and replace the inline loop in DashboardApp (and the
corresponding code in roadmap-view) to call the helper; ensure the helper
accepts the same snapshot shape (or an array of quests) and returns DepEdge[] so
both rendering and model-update paths use the single implementation.

In `@src/tui/bijou/views/roadmap-view.ts`:
- Around line 29-31: The current SlicedDagSource.ids() implementation builds
questIds from snap.quests (questIds variable) which preserves insertion order;
change it to derive ids from snap.sortedTaskIds (filtered to only include IDs
that exist in snap.quests) so DAG layout/order is deterministic across
refreshes—use snap.sortedTaskIds.filter(id => snap.quests.some(q => q.id ===
id)) (or build a Set of snap.quests ids for faster lookup) and return that list
from ids() instead of questIds.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7c8bc1ce-5667-4123-b6ae-94087685702f

📥 Commits

Reviewing files that changed from the base of the PR and between d103c8f and 3dcb914.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • .gitignore
  • CHANGELOG.md
  • package.json
  • scripts/generate-work-dag.ts
  • src/tui/bijou/DashboardApp.ts
  • src/tui/bijou/__tests__/DashboardApp.test.ts
  • src/tui/bijou/__tests__/views.test.ts
  • src/tui/bijou/views/roadmap-view.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • .gitignore
  • src/tui/bijou/tests/views.test.ts

@flyingrobots
Copy link
Owner Author

Addressed the remaining open review items in commit ae885b8:

  • Updated CHANGELOG.md to remove contradictory SVG ignore wording and unify on docs/assets/work-dag*.svg rationale.
  • Reworded the topological-sort changelog bullet to clearly state delegation to graph.traverse.topologicalSort().
  • Updated DashboardApp.test.ts fallback paging test to assert model state (roadmap.fallbackScrollY) rather than rendered text equality.

Validation:

  • npm run test:local -- src/tui/bijou/__tests__/DashboardApp.test.ts
  • pre-push hook full suite: vitest run passed (58 files, 792 tests).

@flyingrobots flyingrobots merged commit 39b4bba into main Mar 4, 2026
8 checks passed
@flyingrobots flyingrobots deleted the feat/work-dag-analysis branch March 4, 2026 13:34
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