Stabilize TUI API runtime foundation#218
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@coderabbitai review |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughIntroduces ChangesUnified Safe Repair, TuiApiRuntime, and Dashboard Auto-Repair
Sequence Diagram(s)sequenceDiagram
rect rgba(100, 149, 237, 0.5)
Note over CLI,repairNotifier: aimux restart repair flow
end
participant CLI
participant restartAimuxControlPlane
participant repairRuntimeContract
participant tmux
participant emitRepairDiagnostics
participant repairNotifier
CLI->>restartAimuxControlPlane: RestartAimuxControlPlaneOptions
restartAimuxControlPlane->>repairRuntimeContract: project, tmux, runtimeRebuildRequired
repairRuntimeContract->>tmux: configureManagedSession (project + client sessions)
repairRuntimeContract->>tmux: setSessionOption `@aimux-runtime-rebuild-required` "0"
repairRuntimeContract-->>restartAimuxControlPlane: repaired | skipped | failed
restartAimuxControlPlane->>emitRepairDiagnostics: result, repairNotifier
emitRepairDiagnostics->>repairNotifier: record(control-plane-restart event)
emitRepairDiagnostics->>repairNotifier: record(tmux-runtime-repair event)
emitRepairDiagnostics->>repairNotifier: notify("Aimux repaired", summary)
restartAimuxControlPlane-->>CLI: RuntimeRestartResult
sequenceDiagram
rect rgba(144, 238, 144, 0.5)
Note over refreshRuntimeGuard,spawn: Dashboard auto-repair flow
end
participant refreshRuntimeGuard
participant shouldAutoRepairRuntimeGuard
participant startRuntimeGuardRepair
participant spawn
participant host
refreshRuntimeGuard->>shouldAutoRepairRuntimeGuard: stabilized state
shouldAutoRepairRuntimeGuard-->>refreshRuntimeGuard: true (stale/rebuild-required)
refreshRuntimeGuard->>startRuntimeGuardRepair: host, state
startRuntimeGuardRepair->>host: runtimeGuardRepairing=true, dashboardBusyState="Repairing Aimux"
startRuntimeGuardRepair->>spawn: aimux restart --project <root> (detached)
spawn-->>startRuntimeGuardRepair: exit 0
startRuntimeGuardRepair->>host: runtimeGuardState={kind:"ok"}, clear busy/repair flags
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/multiplexer/tui-api-runtime.ts (1)
145-154: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueConsider adding a typed interface for the host parameter.
Using
anyforhostloses type safety and makes the contract implicit. A minimal interface would document the expected shape and catch misuse at compile time.interface TuiApiRuntimeHost { tuiApiRuntime?: TuiApiRuntime; tuiApiConnectionState?: TuiApiConnectionState; getFromProjectService: (path: string, opts?: { timeoutMs?: number }) => Promise<unknown>; }🤖 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/tui-api-runtime.ts` around lines 145 - 154, The `host` parameter in the `getOrCreateTuiApiRuntime` function uses the `any` type, which eliminates type safety and makes the expected structure implicit. Replace the `any` type annotation with a typed interface that documents the required properties of the host object including tuiApiRuntime, tuiApiConnectionState, and the getFromProjectService method. This will provide compile-time type checking and make the function's contract explicit.
🤖 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/repair-events.test.ts`:
- Around line 14-28: The test uses a fixed `/tmp/aimux-repair-project` path for
projectRoot, which causes data from previous test runs to persist and
contaminate subsequent test runs. Replace the hardcoded projectRoot string with
a unique temporary directory generated per test execution. Additionally, update
the assertion logic to read and validate the last line of the JSONL file instead
of the first line, since prior runs may have written earlier lines that would
interfere with the test expectations.
In `@src/runtime-restart.ts`:
- Around line 539-542: The restoreActiveWindows() call is only executed on the
success path, meaning if resolveDashboard() or relinkDashboardToClientSessions()
throws an error, active window restoration is skipped. Wrap the
resolveDashboard() and relinkDashboardToClientSessions() calls in a try-finally
block so that restoreActiveWindows() is always called with the captured
activeWindows, even when an error occurs during the dashboard reload or relink
operations.
- Around line 688-694: The "Runtime repaired:" section is filtering only by
runtimeRebuildRequired status, but this can include projects with failed
repairs. Modify the filter condition in the for loop that iterates over
result.projects to check both that entry.runtimeRebuildRequired is true AND that
entry.runtime.status equals "repaired", ensuring only successfully repaired
projects are displayed under the "Runtime repaired:" heading.
---
Nitpick comments:
In `@src/multiplexer/tui-api-runtime.ts`:
- Around line 145-154: The `host` parameter in the `getOrCreateTuiApiRuntime`
function uses the `any` type, which eliminates type safety and makes the
expected structure implicit. Replace the `any` type annotation with a typed
interface that documents the required properties of the host object including
tuiApiRuntime, tuiApiConnectionState, and the getFromProjectService method. This
will provide compile-time type checking and make the function's contract
explicit.
🪄 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: 5348c154-5a58-4af4-80d9-c0a5ed5d5add
📒 Files selected for processing (31)
AGENTS.mdREADME.mddocs/dev-runtime.mddocs/runtime-lifecycle.mdsrc/context/context-bridge.test.tssrc/context/context-bridge.tssrc/main.tssrc/metadata-server.test.tssrc/metadata-server.tssrc/multiplexer/dashboard-actions-methods.tssrc/multiplexer/dashboard-control.test.tssrc/multiplexer/dashboard-control.tssrc/multiplexer/dashboard-model-service.test.tssrc/multiplexer/dashboard-model.tssrc/multiplexer/index.tssrc/multiplexer/runtime-guard.test.tssrc/multiplexer/runtime-guard.tssrc/multiplexer/runtime-lifecycle-methods.tssrc/multiplexer/session-launch.test.tssrc/multiplexer/session-launch.tssrc/multiplexer/tui-api-runtime.test.tssrc/multiplexer/tui-api-runtime.tssrc/paths.tssrc/project-api-contract.tssrc/repair-events.test.tssrc/repair-events.tssrc/runtime-coherence.test.tssrc/runtime-coherence.tssrc/runtime-restart.test.tssrc/runtime-restart.tssrc/tui/screens/overlay-renderers.ts
💤 Files with no reviewable changes (1)
- src/multiplexer/dashboard-actions-methods.ts
| const projectRoot = "/tmp/aimux-repair-project"; | ||
|
|
||
| recordRepairEvent({ | ||
| ts: "2026-06-22T00:00:00.000Z", | ||
| projectRoot, | ||
| action: "tmux-runtime-repair", | ||
| reason: "runtime contract drift", | ||
| status: "repaired", | ||
| details: { currentDaemonPid: 123 }, | ||
| }); | ||
|
|
||
| const path = getProjectRepairLogPathFor(projectRoot); | ||
| expect(existsSync(path)).toBe(true); | ||
| const [line] = readFileSync(path, "utf8").trim().split("\n"); | ||
| expect(JSON.parse(line!)).toMatchObject({ |
There was a problem hiding this comment.
Use a unique temp project root per test run.
This test writes to a fixed path and reads the first JSONL line, so prior runs can leak data and make assertions non-hermetic. Use a per-test temp directory and assert the last line written.
Proposed fix
-import { existsSync, readFileSync } from "node:fs";
+import { existsSync, mkdtempSync, readFileSync } from "node:fs";
+import { tmpdir } from "node:os";
+import { join } from "node:path";
@@
it("records repair events in the project repair log", () => {
- const projectRoot = "/tmp/aimux-repair-project";
+ const projectRoot = mkdtempSync(join(tmpdir(), "aimux-repair-project-"));
@@
- const [line] = readFileSync(path, "utf8").trim().split("\n");
- expect(JSON.parse(line!)).toMatchObject({
+ const lines = readFileSync(path, "utf8").trim().split("\n");
+ expect(JSON.parse(lines.at(-1)!)).toMatchObject({📝 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.
| const projectRoot = "/tmp/aimux-repair-project"; | |
| recordRepairEvent({ | |
| ts: "2026-06-22T00:00:00.000Z", | |
| projectRoot, | |
| action: "tmux-runtime-repair", | |
| reason: "runtime contract drift", | |
| status: "repaired", | |
| details: { currentDaemonPid: 123 }, | |
| }); | |
| const path = getProjectRepairLogPathFor(projectRoot); | |
| expect(existsSync(path)).toBe(true); | |
| const [line] = readFileSync(path, "utf8").trim().split("\n"); | |
| expect(JSON.parse(line!)).toMatchObject({ | |
| import { existsSync, mkdtempSync, readFileSync } from "node:fs"; | |
| import { tmpdir } from "node:os"; | |
| import { join } from "node:path"; | |
| const projectRoot = mkdtempSync(join(tmpdir(), "aimux-repair-project-")); | |
| recordRepairEvent({ | |
| ts: "2026-06-22T00:00:00.000Z", | |
| projectRoot, | |
| action: "tmux-runtime-repair", | |
| reason: "runtime contract drift", | |
| status: "repaired", | |
| details: { currentDaemonPid: 123 }, | |
| }); | |
| const path = getProjectRepairLogPathFor(projectRoot); | |
| expect(existsSync(path)).toBe(true); | |
| const lines = readFileSync(path, "utf8").trim().split("\n"); | |
| expect(JSON.parse(lines.at(-1)!)).toMatchObject({ |
🤖 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/repair-events.test.ts` around lines 14 - 28, The test uses a fixed
`/tmp/aimux-repair-project` path for projectRoot, which causes data from
previous test runs to persist and contaminate subsequent test runs. Replace the
hardcoded projectRoot string with a unique temporary directory generated per
test execution. Additionally, update the assertion logic to read and validate
the last line of the JSONL file instead of the first line, since prior runs may
have written earlier lines that would interfere with the test expectations.
| const activeWindows = captureActiveNonDashboardWindows(projectRoot, tmux); | ||
| const resolved = resolveDashboard(projectRoot, tmux, { forceReload: true, openInHostSession: true }); | ||
| relinkDashboardToClientSessions(projectRoot, tmux, resolved.dashboardTarget); | ||
| restoreActiveWindows(tmux, activeWindows); |
There was a problem hiding this comment.
Always restore active non-dashboard windows, even on dashboard reload errors.
restoreActiveWindows(...) currently runs only on the success path. If reload/relink throws, focus restoration is skipped and clients can be left on an unintended window.
Proposed fix
- try {
- const activeWindows = captureActiveNonDashboardWindows(projectRoot, tmux);
+ const activeWindows = captureActiveNonDashboardWindows(projectRoot, tmux);
+ try {
const resolved = resolveDashboard(projectRoot, tmux, { forceReload: true, openInHostSession: true });
relinkDashboardToClientSessions(projectRoot, tmux, resolved.dashboardTarget);
- restoreActiveWindows(tmux, activeWindows);
result.dashboard.status = "reloaded";
result.dashboard.sessionName = resolved.dashboardSession.sessionName;
result.dashboard.target = resolved.dashboardTarget;
} catch (error) {
result.dashboard.status = "failed";
result.dashboard.error = errorMessage(error);
+ } finally {
+ restoreActiveWindows(tmux, activeWindows);
}Also applies to: 546-549
🤖 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/runtime-restart.ts` around lines 539 - 542, The restoreActiveWindows()
call is only executed on the success path, meaning if resolveDashboard() or
relinkDashboardToClientSessions() throws an error, active window restoration is
skipped. Wrap the resolveDashboard() and relinkDashboardToClientSessions() calls
in a try-finally block so that restoreActiveWindows() is always called with the
captured activeWindows, even when an error occurs during the dashboard reload or
relink operations.
| if (result.summary.runtimeRebuildRequired > 0) { | ||
| lines.push(""); | ||
| lines.push("Runtime rebuild required:"); | ||
| lines.push("Runtime repaired:"); | ||
| for (const project of result.projects.filter((entry) => entry.runtimeRebuildRequired)) { | ||
| lines.push(` ${project.projectRoot}`); | ||
| } | ||
| lines.push("Control plane repair completed without killing agents."); | ||
| lines.push("The dashboard will show a rebuild warning; manual command:"); | ||
| lines.push(" aimux restart-runtime --project-root <project> --open"); | ||
| } |
There was a problem hiding this comment.
The “Runtime repaired” section can report failed repairs as repaired.
This block is gated by runtimeRebuildRequired, so projects with runtime.status === "failed" still appear under “Runtime repaired:”. Consider filtering by runtime.status === "repaired" or renaming the heading to avoid incorrect operator output.
Proposed fix (status-accurate sections)
- if (result.summary.runtimeRebuildRequired > 0) {
+ const repairedRuntime = result.projects.filter(
+ (entry) => entry.runtimeRebuildRequired && entry.runtime.status === "repaired",
+ );
+ const failedRuntime = result.projects.filter(
+ (entry) => entry.runtimeRebuildRequired && entry.runtime.status === "failed",
+ );
+
+ if (repairedRuntime.length > 0) {
lines.push("");
lines.push("Runtime repaired:");
- for (const project of result.projects.filter((entry) => entry.runtimeRebuildRequired)) {
+ for (const project of repairedRuntime) {
+ lines.push(` ${project.projectRoot}`);
+ }
+ }
+
+ if (failedRuntime.length > 0) {
+ lines.push("");
+ lines.push("Runtime repair failed:");
+ for (const project of failedRuntime) {
lines.push(` ${project.projectRoot}`);
}
}📝 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.
| if (result.summary.runtimeRebuildRequired > 0) { | |
| lines.push(""); | |
| lines.push("Runtime rebuild required:"); | |
| lines.push("Runtime repaired:"); | |
| for (const project of result.projects.filter((entry) => entry.runtimeRebuildRequired)) { | |
| lines.push(` ${project.projectRoot}`); | |
| } | |
| lines.push("Control plane repair completed without killing agents."); | |
| lines.push("The dashboard will show a rebuild warning; manual command:"); | |
| lines.push(" aimux restart-runtime --project-root <project> --open"); | |
| } | |
| const repairedRuntime = result.projects.filter( | |
| (entry) => entry.runtimeRebuildRequired && entry.runtime.status === "repaired", | |
| ); | |
| const failedRuntime = result.projects.filter( | |
| (entry) => entry.runtimeRebuildRequired && entry.runtime.status === "failed", | |
| ); | |
| if (repairedRuntime.length > 0) { | |
| lines.push(""); | |
| lines.push("Runtime repaired:"); | |
| for (const project of repairedRuntime) { | |
| lines.push(` ${project.projectRoot}`); | |
| } | |
| } | |
| if (failedRuntime.length > 0) { | |
| lines.push(""); | |
| lines.push("Runtime repair failed:"); | |
| for (const project of failedRuntime) { | |
| lines.push(` ${project.projectRoot}`); | |
| } | |
| } |
🤖 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/runtime-restart.ts` around lines 688 - 694, The "Runtime repaired:"
section is filtering only by runtimeRebuildRequired status, but this can include
projects with failed repairs. Modify the filter condition in the for loop that
iterates over result.projects to check both that entry.runtimeRebuildRequired is
true AND that entry.runtime.status equals "repaired", ensuring only successfully
repaired projects are displayed under the "Runtime repaired:" heading.
Summary
Verification
Summary by CodeRabbit
Release Notes
Documentation
aimux restart→aimux doctor versions, with advanced/debug commands moved to an optional path.New Features
repairs.jsonlin project logs and trigger quiet desktop notifications.Bug Fixes