Skip to content

feat(tui): source project and topology from service - #180

Merged
TraderSamwise merged 4 commits into
masterfrom
feat/tui-project-topology-api
Jun 20, 2026
Merged

feat(tui): source project and topology from service#180
TraderSamwise merged 4 commits into
masterfrom
feat/tui-project-topology-api

Conversation

@TraderSamwise

@TraderSamwise TraderSamwise commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add /project-observability and /topology project-service endpoints.
  • Move the TUI Project and Topology screens to service-sourced refreshes with payload validation.
  • Add typed app client wrappers for the new project-service contracts.

Verification

  • PATH="$HOME/.nvm/versions/node/v24.16.0/bin:$PATH" yarn typecheck
  • PATH="$HOME/.nvm/versions/node/v24.16.0/bin:$PATH" yarn lint
  • PATH="$HOME/.nvm/versions/node/v24.16.0/bin:$PATH" yarn vitest run src/metadata-server.test.ts src/multiplexer/project.test.ts src/multiplexer/topology.test.ts src/project-observability.test.ts src/project-topology.test.ts
  • PATH="$HOME/.nvm/versions/node/v24.16.0/bin:$PATH" yarn vitest run
  • PATH="$HOME/.nvm/versions/node/v24.16.0/bin:$PATH" yarn build

Summary by CodeRabbit

  • New Features

    • Added project observability API to retrieve diagnostic metrics including running agents, services, worktrees, and unread notifications.
    • Added project topology API to display project structure and resource allocation across worktrees.
  • Tests

    • Added comprehensive test coverage for new observability and topology endpoints and refresh functionality.

@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
app Ready Ready Preview, Comment Jun 20, 2026 12:52am

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

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

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 225951b8-e68d-4bfa-8ace-a2fdfdce4404

📥 Commits

Reviewing files that changed from the base of the PR and between 5df202a and 164b948.

📒 Files selected for processing (6)
  • src/metadata-server.test.ts
  • src/metadata-server.ts
  • src/multiplexer/project.test.ts
  • src/multiplexer/project.ts
  • src/multiplexer/topology.test.ts
  • src/multiplexer/topology.ts
📝 Walkthrough

Walkthrough

Two new project-level diagnostic endpoints (/project-observability and /topology) are added end-to-end: typed API client interfaces and fetch functions, metadata server GET handlers backed by desktop state, and refactored multiplexer screen refresh logic replacing synchronous snapshot rebuilding with async service-fetched, validated flows with fallback and conditional re-render.

Changes

Async project observability & topology endpoints

Layer / File(s) Summary
API client response types and fetch functions
app/lib/api.ts
Adds ProjectObservabilityResponse and ProjectTopologyResponse interfaces, and getProjectObservability/getProjectTopology functions calling the new endpoints via callProjectJson.
Metadata server handlers and desktop state mapping
src/metadata-server.ts, src/metadata-server.test.ts
Adds buildTopologyWorktreesFromDesktopState helper and two new GET handlers (/project-observability, /topology), plus integration tests asserting summary counts, project name, and topology row contents.
Async project observability refresh in multiplexer
src/multiplexer/project.ts, src/multiplexer/project.test.ts
Replaces synchronous internal refresh with an exported async refreshProjectObservability that fetches /project-observability, validates and applies the payload with projectIndex clamping, falls back to empty observability on error, and conditionally re-renders only when still on the project screen.
Async topology refresh in multiplexer
src/multiplexer/topology.ts, src/multiplexer/topology.test.ts
Replaces synchronous refreshTopology with an exported async version fetching /topology, validates via isProjectTopology, applies or falls back to emptyTopology, and conditionally re-renders only when still on the topology screen. Updates showTopology, renderTopology, and the r key handler.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(70, 130, 180, 0.5)
    Note over MultiplexerProject,MetadataServer: Project Observability Refresh
    MultiplexerProject->>MetadataServer: getFromProjectService("/project-observability")
    MetadataServer->>DesktopState: getState()
    DesktopState-->>MetadataServer: sessions/services/worktrees/tasks/notifications
    MetadataServer-->>MultiplexerProject: ProjectObservabilityResponse (or error)
    alt payload valid
      MultiplexerProject->>MultiplexerProject: applyProjectObservability(host, project)
      MultiplexerProject-->>Screen: re-render if still on "project" screen
    else invalid/error
      MultiplexerProject->>MultiplexerProject: emptyProjectObservability → host.projectObservability
    end
  end
  rect rgba(80, 160, 100, 0.5)
    Note over MultiplexerTopology,MetadataServer: Topology Refresh
    MultiplexerTopology->>MetadataServer: getFromProjectService("/topology")
    MetadataServer->>DesktopState: getState()
    DesktopState-->>MetadataServer: worktrees/sessions/teammates
    MetadataServer-->>MultiplexerTopology: ProjectTopologyResponse (or error)
    alt isProjectTopology valid
      MultiplexerTopology->>MultiplexerTopology: applyTopology(host, topology)
      MultiplexerTopology-->>Screen: re-render if still on "topology" screen
    else invalid/error
      MultiplexerTopology->>MultiplexerTopology: emptyTopology() → host.topology
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • TraderSamwise/aimux#158: Introduces the initial project screen behavior in src/multiplexer/project.ts, which this PR refactors from synchronous to async observability refresh.
  • TraderSamwise/aimux#160: Introduces the topology screen and handleTopologyKey/refreshTopology in src/multiplexer/topology.ts, which this PR migrates from synchronous to async service-fetched refresh.

Poem

🐇 Hoppity-hop through the endpoint maze,
No more sync blocking in the project haze!
Fetch from the server, validate the shape,
Empty fallback if data can't escape.
Re-render only if the screen's still there —
Async rabbits handle state with care! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely captures the main change: moving TUI project and topology screens to use service-sourced data.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tui-project-topology-api

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

@TraderSamwise

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@TraderSamwise

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@TraderSamwise

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/metadata-server.test.ts (1)

623-656: ⚡ Quick win

Add teammate coverage to the observability summary test.

This case only populates sessions, so it won’t catch regressions where teammate agents are excluded from summary counts.

🤖 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/metadata-server.test.ts` around lines 623 - 656, The test "serves project
observability from desktop state, tasks, and notifications" only populates
sessions, services, and worktrees in the mock desktop state returned by the
getState() method, which means it won't detect regressions if teammate agents
are excluded from summary counts. Add a teammates array to the desktop state
object in the getState() method to include sample teammate data, and update the
expected unreadNotifications count in the toMatchObject assertion if needed to
ensure the test validates that teammate agents are properly included in the
observability summary.
src/multiplexer/project.test.ts (1)

39-49: ⚡ Quick win

Add one test for malformed nested payloads that still satisfy top-level shape.

Current failure coverage mostly exercises missing story. Add a case like { summary: {}, progress: {}, story: [] } and assert refreshProjectObservability returns false plus empty fallback. That protects contract validation from regressing.

🤖 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/multiplexer/project.test.ts` around lines 39 - 49, Add a new test case
alongside the existing test in the test file that validates contract validation
for malformed nested payloads. Create a test where the mocked
getFromProjectService returns a response with all top-level shape properties
present (summary, progress, story) but with malformed or invalid nested content,
then assert that refreshProjectObservability still returns false and the
projectObservability fallback remains empty. This ensures the contract
validation properly handles cases where the top-level structure is correct but
inner data is corrupted or invalid.
src/multiplexer/topology.test.ts (1)

32-42: ⚡ Quick win

Add a validation-focused negative test for missing health / non-numeric counts.

Given the ProjectTopology contract, include one explicit malformed payload test (with top-level keys present) and assert refreshTopology returns false with empty fallback. This guards the runtime validator against silent weakening.

🤖 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/multiplexer/topology.test.ts` around lines 32 - 42, Add a new test case
in the topology.test.ts file that validates malformed ProjectTopology payloads
are properly rejected. Create a test where the getFromProjectService returns a
response with a malformed topology object that has missing or invalid fields
(such as missing health field or non-numeric counts values) while keeping other
top-level keys present. Assert that refreshTopology returns false and that the
topology falls back to an empty state with zero counts (worktrees: 0, agents: 0,
services: 0). This ensures the runtime validator properly enforces the
ProjectTopology contract and prevents silently accepting incomplete or invalid
payloads.
🤖 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/metadata-server.ts`:
- Around line 1365-1373: The buildProjectObservability function is called with
only sessions, services, and worktrees from the state object, but it should also
include the teammates data to ensure agent summary counts (agentsRunning,
agentsWaiting, agentsOffline) are accurate. Add the teammates property to the
object passed to buildProjectObservability by including teammates:
state.teammates ?? [] alongside the existing state.sessions, state.services, and
state.worktrees properties.

In `@src/multiplexer/project.ts`:
- Around line 14-16: The projectIndex clamping logic in the conditional block
starting at line 14 only guards against the upper bound (when projectIndex >=
storyLength) but does not protect against negative values. Add an additional
check to clamp the lower bound by ensuring projectIndex cannot be negative, such
as using Math.max(0, host.projectIndex) or adding a separate condition to
validate that projectIndex is greater than or equal to zero after the existing
bounds checks.
- Around line 18-25: The isProjectObservability type guard function is
performing only shallow type checks that allow invalid objects to pass through.
Harden the validation by checking that the nested objects (summary and progress)
contain the required fields expected by the ProjectObservability contract, and
that the story array contains properly structured elements. Replace the
surface-level type checks with deeper validation that ensures each property
matches the actual ProjectObservability shape, so invalid payloads are rejected
and the refresh can fall back safely.

In `@src/multiplexer/topology.ts`:
- Around line 14-16: The bounds checking for host.topologyIndex in the topology
application logic only validates the upper bound but ignores negative values,
which can result in invalid selection. After the existing check that bounds
topologyIndex to be less than len, add an additional condition to clamp negative
topologyIndex values to 0, ensuring the index is always within the valid range
of [0, len-1]. This should be done before the closing brace on line 16.
- Around line 18-26: The isProjectTopology function is validating the
ProjectTopology type too loosely. It needs to be stricter in its type checking
to prevent malformed payloads from passing through. Currently it only checks
that projectName is a string, counts is an object, and worktrees and rows are
arrays, but it should also validate that the counts object contains the expected
numeric properties and that the required health property exists. Update the
isProjectTopology function to perform deeper validation of the counts object
structure to ensure all required fields have the correct types (for example,
validating that numeric count fields are actually numbers), and add a check for
the health property to match the full ProjectTopology contract.

---

Nitpick comments:
In `@src/metadata-server.test.ts`:
- Around line 623-656: The test "serves project observability from desktop
state, tasks, and notifications" only populates sessions, services, and
worktrees in the mock desktop state returned by the getState() method, which
means it won't detect regressions if teammate agents are excluded from summary
counts. Add a teammates array to the desktop state object in the getState()
method to include sample teammate data, and update the expected
unreadNotifications count in the toMatchObject assertion if needed to ensure the
test validates that teammate agents are properly included in the observability
summary.

In `@src/multiplexer/project.test.ts`:
- Around line 39-49: Add a new test case alongside the existing test in the test
file that validates contract validation for malformed nested payloads. Create a
test where the mocked getFromProjectService returns a response with all
top-level shape properties present (summary, progress, story) but with malformed
or invalid nested content, then assert that refreshProjectObservability still
returns false and the projectObservability fallback remains empty. This ensures
the contract validation properly handles cases where the top-level structure is
correct but inner data is corrupted or invalid.

In `@src/multiplexer/topology.test.ts`:
- Around line 32-42: Add a new test case in the topology.test.ts file that
validates malformed ProjectTopology payloads are properly rejected. Create a
test where the getFromProjectService returns a response with a malformed
topology object that has missing or invalid fields (such as missing health field
or non-numeric counts values) while keeping other top-level keys present. Assert
that refreshTopology returns false and that the topology falls back to an empty
state with zero counts (worktrees: 0, agents: 0, services: 0). This ensures the
runtime validator properly enforces the ProjectTopology contract and prevents
silently accepting incomplete or invalid payloads.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: f2fad601-1b05-4f5b-8960-53858e44dbd5

📥 Commits

Reviewing files that changed from the base of the PR and between 5f18139 and 5df202a.

📒 Files selected for processing (7)
  • app/lib/api.ts
  • src/metadata-server.test.ts
  • src/metadata-server.ts
  • src/multiplexer/project.test.ts
  • src/multiplexer/project.ts
  • src/multiplexer/topology.test.ts
  • src/multiplexer/topology.ts

Comment thread src/metadata-server.ts
Comment thread src/multiplexer/project.ts
Comment thread src/multiplexer/project.ts Outdated
Comment thread src/multiplexer/topology.ts
Comment thread src/multiplexer/topology.ts
@TraderSamwise

Copy link
Copy Markdown
Owner Author

Resolved the CodeRabbit project/topology review items in 164b948: teammate counts, negative index clamps, stricter payload validators, and validation-focused tests. Latest CodeRabbit check is green and the independent review returned no findings.

@TraderSamwise
TraderSamwise merged commit 65a3de9 into master Jun 20, 2026
3 checks passed
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