feat: wire the core workflows — daemon status, orchestrator attach, git rail, lifecycle#178
feat: wire the core workflows — daemon status, orchestrator attach, git rail, lifecycle#178ashish921998 wants to merge 6 commits into
Conversation
…it rail, lifecycle
Four core-workflow gaps closed end to end:
Daemon status (Electron main):
- Discover an externally-started daemon by polling running.json and
confirming /healthz identity, so the status pill reflects the daemon
actually serving the renderer instead of reporting "stopped" forever.
- Auto-start supervision at launch when AO_DAEMON_COMMAND is set; dedupe
status broadcasts.
Orchestrator (frontend):
- Split orchestrator sessions out of the worker list; the orchestrator
view attaches the selected project's orchestrator terminal and offers
a Start-orchestrator empty state wired to POST /orchestrators.
Git review rail (backend + frontend):
- New WorkspaceGit port + git-CLI adapter (status/stage/discard/commit+push)
with integration tests against real repos.
- Session service + routes: GET /sessions/{id}/git, POST git/stage,
git/discard, git/commit, with typed GIT_NOTHING_TO_COMMIT / GIT_NO_REMOTE
errors; OpenAPI + TS types regenerated.
- Session read model now carries the worktree branch.
- GitRail shows live changed files, stage-all, armed discard-all,
Commit & Push; Create PR is explicitly disabled until the PR lane exists.
Lifecycle (frontend):
- Right-click context menus: kill/restore workers, new-worker/cleanup/
remove-project with two-step destructive confirm and inline errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Greptile SummaryThis PR wires four core workflows onto the existing backend: external-daemon discovery via
Confidence Score: 5/5Safe to merge; no regressions in the new backend surface or Electron main changes, and both previously flagged correctness issues are resolved in this diff. The git-rail backend is complete end-to-end with integration tests covering the commit+push happy path, discard cleanup, and the push-only failure case (SHA preserved in a 200 response). The controller fix for the previously identified commit-succeeds-but-caller-sees-error issue is in place and covered by a dedicated test. The external-daemon observer in Electron main is guarded against reentrancy, supervised-daemon interference, and foreign-process squatting. The context-menu armed-confirm now decays after 3 s. The only observation is a documentation mismatch on the forward-declared orchestrator type field, which is intentional follow-up scaffolding. No files require special attention. The frontend/src/renderer/types/workspace.ts orchestrator field is a forward declaration for follow-up work and is intentionally unpopulated. Important Files Changed
Sequence DiagramsequenceDiagram
participant Main as Electron main.ts
participant RunFile as running.json
participant Daemon as ao daemon (/healthz)
participant Renderer as Renderer process
participant GitAPI as GET/POST /sessions/{id}/git[/*]
participant GitOps as GitOps adapter
participant GitCLI as git CLI
Note over Main: App launch
alt AO_DAEMON_COMMAND set
Main->>Daemon: spawn (startDaemon)
end
loop every 2.5s (no daemonProcess)
Main->>RunFile: readFile running.json
RunFile-->>Main: port info
Main->>Daemon: GET /healthz (timeout 1.5s)
Daemon-->>Main: "{service, status}"
Main->>Main: isDaemonHealthz check
Main->>Renderer: daemon:status (deduped via daemonStatusEquals)
end
Note over Renderer,GitCLI: Git Rail workflow
Renderer->>GitAPI: "GET /sessions/{id}/git"
GitAPI->>GitOps: Status(ctx, workspacePath)
GitOps->>GitCLI: git rev-parse --abbrev-ref HEAD
GitOps->>GitCLI: git status --porcelain -z
GitOps->>GitCLI: git diff --numstat HEAD
GitCLI-->>GitOps: branch + file list + line counts
GitOps-->>GitAPI: "GitStatus{branch, files}"
GitAPI-->>Renderer: SessionGitStatusResponse
Renderer->>GitAPI: "POST /sessions/{id}/git/commit {message, push:true}"
GitAPI->>GitOps: CommitAll(ctx, path, message, push)
GitOps->>GitCLI: git add -A
GitOps->>GitCLI: git commit -m message
GitOps->>GitCLI: git push --set-upstream origin HEAD
alt push succeeds
GitOps-->>GitAPI: "GitCommitResult{SHA, branch, pushed:true}"
GitAPI-->>Renderer: "200 GitCommitResponse{sha, pushed:true}"
else push fails (no remote / rejected)
GitOps-->>GitAPI: "(result{SHA}, ErrGitNoRemote)"
GitAPI-->>Renderer: "200 GitCommitResponse{sha, pushed:false, pushError}"
end
Reviews (4): Last reviewed commit: "fix(git): keep the commit SHA when the p..." | Re-trigger Greptile |
| const commitAndPush = useCallback( | ||
| (message: string, description: string) => | ||
| runAction(async () => { | ||
| const fullMessage = description.trim() ? `${message.trim()}\n\n${description.trim()}` : message.trim(); | ||
| const { error } = await apiClient.POST("/api/v1/sessions/{sessionId}/git/commit", { | ||
| params: { path: { sessionId: sessionId ?? "" } }, | ||
| body: { message: fullMessage, push: true }, | ||
| }); | ||
| if (error) throw new Error(apiErrorMessage(error, "Could not commit")); | ||
| }), | ||
| [runAction, sessionId], |
There was a problem hiding this comment.
push is unconditionally true; no commit-only path exists in the UI
commitAndPush always sends body: { message: fullMessage, push: true }. For a session whose worktree has no remote (local-only repo), the operation will always commit successfully but return GIT_NO_REMOTE, which — per the controller issue above — surfaces as an error even though the commit landed. Even once that controller issue is fixed, there is currently no way for a user to commit without pushing. The API supports push: false, so wiring an option here (or at least exposing a separate "Commit only" path) would let the rail handle remoteless repos cleanly.
# Conflicts: # backend/internal/httpd/apispec/openapi.yaml # frontend/src/api/schema.ts # frontend/src/renderer/App.test.tsx # frontend/src/renderer/App.tsx # frontend/src/renderer/components/CenterPane.tsx # frontend/src/renderer/components/SideRail.tsx # frontend/src/renderer/components/Sidebar.tsx # frontend/src/renderer/hooks/useWorkspaceQuery.ts
…cardAll's -fd choice
Greptile flagged that an armed destructive item ("Confirm remove") stayed
armed until the menu closed, so a much-later mistaken second click could
fire it. The armed state now decays back to the resting label after 3s.
DiscardAll keeping gitignored files is intentional (discard means "drop my
changes", not "wipe node_modules and the AO session files") — now said in
the doc comment instead of being implicit in the -fd flag.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sizing it The Session read model exposes `branch` (from session metadata), but useWorkspaceQuery clobbered it with a synthesized `session/<id>`, so workers on custom branches were mislabeled everywhere the branch is shown (Topbar, SessionInspector, board cards, PR rows, terminal prompt). Prefer the real branch and keep the synthesized name only as a fallback for sessions without branch metadata yet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CommitAll resolves the SHA before it ever attempts to push, so a push-only
failure (GIT_NO_REMOTE or a rejected push) returns a non-empty SHA alongside
the error — the commit has already landed. The commit controller discarded the
result and forwarded the error, so the caller got a 409 with no SHA and the
committed work was invisible until the user checked the branch by hand.
Now a SHA-bearing error returns 200 GitCommitResponse{pushed:false, pushError}
so the commit is never lost; genuine pre-commit failures (nothing-to-commit,
no-workspace) still error as before. useSessionGit surfaces pushError as a
non-fatal warning instead of dropping it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Closes the four core-workflow gaps found by driving the running app end to end:
1. Daemon status pill tells the truth
The Electron supervisor only knew about daemons it spawned; in dev (external
ao start) the pill read "daemon stopped" forever while sessions were visibly served. The main process now discovers an external daemon by polling therunning.jsonhandshake and confirming/healthzidentity (guarded against foreign processes squatting on a stale port), dedupes status broadcasts, and auto-starts supervision at launch whenAO_DAEMON_COMMANDis configured.2. Orchestrator view attaches a real terminal
The orchestrator pane was a placeholder ("No session selected"). Orchestrator sessions are now split out of the worker list, the view attaches the selected project's orchestrator PTY through the existing mux machinery, and a Start-orchestrator empty state spawns one via
POST /orchestrators.3. Git review rail works end to end
New backend surface (port → git-CLI adapter → session service → routes, spec + TS types regenerated):
GET /sessions/{id}/git— branch + changed files (adds/dels, staged)POST /sessions/{id}/git/stage|discard|commit— stage all, discard all, commit-and-push with typedGIT_NOTHING_TO_COMMIT/GIT_NO_REMOTEerrorsbranchfield)The rail shows live files, wires Stage all / armed Discard all / Commit & Push, and explicitly disables Create PR (the PR action lane is still a stub — out of scope here).
4. Worker/project lifecycle from the sidebar
Right-click context menus: kill (armed confirm) / restore on workers; new worker, clean up finished workers, remove project (armed confirm) on projects — all wired to the existing daemon routes with inline error reporting.
Verified
npm run lint(backend tests + golangci) green; new gitops integration tests run real git reposNotes
nothing to resume from) — pre-existing; should become a typed conflict (follow-up)~/.rc/wt/…) doesn't match the real managed root — follow-up🤖 Generated with Claude Code
Update 2026-06-11 — merged main's renderer redesign
mainlanded the agent-orchestrator-clone shell (e493de6, per the explicitdesign decision recorded in DESIGN.md on 2026-06-10), which deleted/rewrote the
old emdash-style renderer this PR had wired. The merge keeps main's renderer
wholesale and this PR's backend + Electron-main work:
WorkspaceGitport + gitworktree adapter,GET/POST /sessions/{id}/git[...]routes,branchon the Session read model,orchestrator spawn route, external-daemon discovery in Electron main
(running.json + /healthz), regenerated OpenAPI/schema.ts combining the git +
review routes.
status pill in the sidebar footer, orchestrator attach in CenterPane, the git
review rail (SideRail), and the sidebar right-click lifecycle menus. The new
shell has no equivalents yet; re-implementing those four surfaces on the new
Sidebar/SessionView is follow-up work.