feat: Claude Code Monitor — lanes, pipelines and a merged workspace

Internal SmartGift build of a Claude Code monitoring dashboard.

Lanes: a durable unit of parallel agent work, one per working directory,
tracked across session restarts. Managed lanes are git worktrees the
dashboard provisions and can reset or remove behind a three-check destroy
guard and a counted preflight; adopted lanes are directories you already
own and are never destroyable.

Pipelines: a lane moves through pipeline stages. A stage the agent declares
with evidence renders green; a stage inferred from the tool-event stream
renders dashed amber and never counts as done. Detection is forward-only
within a 30-minute window, and never writes the declared stage.

Workspace: one page at /run with a lane grid, the selected lane's pipeline,
and a full Claude console behind a disclosure.
This commit is contained in:
2026-07-29 17:07:45 +07:00
commit d2fc4a4701
783 changed files with 221217 additions and 0 deletions
@@ -0,0 +1,172 @@
/**
* @file AddLaneModal.test.tsx
* @description Pins the "+ Add lane" flow after it was rebuilt around a source
* repo instead of an existing folder: picking or typing a repo path triggers a
* branch lookup, the base-branch picker only appears once that lookup resolves,
* confirm submits through the provisioning endpoint (not the adopt/ensure one),
* an unresolvable path degrades to a quiet hint instead of blocking the form,
* and a server error surfaces instead of closing the modal.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { AddLaneModal } from "../AddLaneModal";
import { api } from "../../../lib/api";
import type { CwdSuggestion } from "../../../lib/api";
import type { Lane } from "../../../lib/types";
vi.mock("../../../lib/api", () => ({
api: { lanes: { branches: vi.fn(), worktree: vi.fn() } },
}));
function laneFixture(over: Partial<Lane> = {}): Lane {
return {
id: 9,
title: "",
cwd: "/lanes/repo__feature",
branch: "feat/feature",
kind: "managed",
pipeline: "default",
session_id: null,
run_id: null,
stage: "idle",
stage_since: null,
status: "provisioning",
gate_decision: null,
ci_status: null,
needs_action: null,
links: {},
stages: {},
notes: null,
pipeline_name: "Default",
pipeline_nodes: [],
progress: 0,
stage_seconds: null,
last_event_seconds: null,
liveness: "idle",
detected_stage: null,
detected_signal: null,
...over,
};
}
const SUGGESTIONS: CwdSuggestion[] = [
{ kind: "home", path: "/Users/tester", label: "Home" },
{ kind: "recent", path: "/Users/tester/projects/repo", label: "repo" },
];
function renderModal(over: Partial<React.ComponentProps<typeof AddLaneModal>> = {}) {
return render(
<AddLaneModal
open
onClose={vi.fn()}
onAdded={vi.fn()}
cwdSuggestions={SUGGESTIONS}
{...over}
/>
);
}
/** ConfirmModal focuses its Cancel button on a 0ms timer after mount, which
* races userEvent.type() and can eat the first keystroke. Let that timer fire,
* then click the field to reclaim focus before typing. */
async function focusField(user: ReturnType<typeof userEvent.setup>, el: HTMLElement) {
await new Promise((r) => setTimeout(r, 0));
await user.click(el);
}
beforeEach(() => {
vi.mocked(api.lanes.branches).mockReset();
vi.mocked(api.lanes.worktree).mockReset();
});
describe("AddLaneModal", () => {
it("disables confirm until a repo, a title, and a resolved branch list are all present", () => {
renderModal();
expect(screen.getByRole("button", { name: "Add lane" })).toBeDisabled();
});
it("looks up branches once the repo path settles, and shows them as a picker", async () => {
vi.mocked(api.lanes.branches).mockResolvedValue({
branches: ["main", "feat/other"],
current: "main",
});
renderModal();
const user = userEvent.setup();
const repoField = screen.getByLabelText("Source repository");
await focusField(user, repoField);
await user.type(repoField, "/Users/tester/projects/repo");
await waitFor(() => expect(api.lanes.branches).toHaveBeenCalledWith("/Users/tester/projects/repo"));
const base = await screen.findByLabelText("Branch to fork from");
expect(base).toHaveValue("main"); // the repo's current branch is preselected
expect(screen.getByRole("option", { name: "feat/other" })).toBeInTheDocument();
});
it("stays disabled and shows a quiet hint when the path is not a resolvable repo", async () => {
vi.mocked(api.lanes.branches).mockRejectedValue(new Error("EBADSOURCEREPO"));
renderModal();
const user = userEvent.setup();
const repoField = screen.getByLabelText("Source repository");
await focusField(user, repoField);
await user.type(repoField, "/not/a/repo");
await waitFor(() => expect(api.lanes.branches).toHaveBeenCalled());
expect(await screen.findByText(/Not a git repository/)).toBeInTheDocument();
expect(screen.queryByLabelText("Branch to fork from")).toBeNull();
expect(screen.getByRole("button", { name: "Add lane" })).toBeDisabled();
});
it("submits through the worktree provisioning endpoint, not ensure", async () => {
vi.mocked(api.lanes.branches).mockResolvedValue({ branches: ["main"], current: "main" });
vi.mocked(api.lanes.worktree).mockResolvedValue({ lane: laneFixture({ id: 9 }) });
const onAdded = vi.fn();
const onClose = vi.fn();
renderModal({ onClose, onAdded });
const user = userEvent.setup();
const repoField = screen.getByLabelText("Source repository");
await focusField(user, repoField);
await user.type(repoField, "/Users/tester/projects/repo");
await screen.findByLabelText("Branch to fork from");
await user.type(screen.getByLabelText("Title"), "New feature");
await user.click(screen.getByRole("button", { name: "Add lane" }));
await waitFor(() => {
expect(api.lanes.worktree).toHaveBeenCalledWith({
sourceRepo: "/Users/tester/projects/repo",
title: "New feature",
base: "main",
});
});
expect(onAdded).toHaveBeenCalledWith(expect.objectContaining({ id: 9, status: "provisioning" }));
expect(onClose).toHaveBeenCalled();
});
it("shows a server error and leaves the modal open instead of closing silently", async () => {
vi.mocked(api.lanes.branches).mockResolvedValue({ branches: ["main"], current: "main" });
vi.mocked(api.lanes.worktree).mockRejectedValue(new Error("EWORKTREEDIRCOLLISION"));
const onClose = vi.fn();
renderModal({ onClose });
const user = userEvent.setup();
const repoField = screen.getByLabelText("Source repository");
await focusField(user, repoField);
await user.type(repoField, "/Users/tester/projects/repo");
await screen.findByLabelText("Branch to fork from");
await user.type(screen.getByLabelText("Title"), "New feature");
await user.click(screen.getByRole("button", { name: "Add lane" }));
expect(await screen.findByText("EWORKTREEDIRCOLLISION")).toBeInTheDocument();
expect(onClose).not.toHaveBeenCalled();
});
it("renders nothing when closed", () => {
renderModal({ open: false });
expect(screen.queryByRole("dialog")).toBeNull();
});
});