Commit Graph

249 Commits

Author SHA1 Message Date
Harshit Singh Bhandari 36ea5835e7
chore: add daemon build script (#365)
* chore: add daemon build script

* chore: make daemon build script windows-aware
2026-06-21 19:45:27 +05:30
Laxman 0e1c5fe54b
Feat/posthog error tracking redaction (#329)
* feat(telemetry): send renderer exceptions to PostHog error tracking

* fix(telemetry): redact local paths and urls before capture

* feat(telemetry): enrich backend error metadata

* fix(telemetry): document telemetry helper exports

* fix(telemetry): satisfy gocritic on helper signature

* chore: format with prettier [skip ci]

* fix(telemetry): redact embedded local urls

* chore: format with prettier [skip ci]

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-21 18:50:47 +05:30
swyam sharma 34a9fdb736
fix: surface session branch in frontend (#358)
Co-authored-by: Swyam Sharma <cruzer@Swyams-MacBook-Pro.local>
2026-06-21 14:57:37 +05:30
i-trytoohard 78cdc6d5ca
fix(session-manager): terminate sessions even when handle is missing (#360)
* fix(session-manager): terminate sessions even when handle is missing

Kill hard-failed with ErrIncompleteHandle before recording terminal
intent, so a session that lost its runtime/workspace handle (crash,
partial spawn) was un-killable and stuck forever on the dashboard.

MarkTerminated now runs unconditionally; each destroy step is gated
individually on whether that handle exists. If nothing is present to
tear down, the session still terminates — freed=false just signals
nothing was freed. ErrIncompleteHandle is still returned by Restore
and runtimeMessenger.Send, where a missing handle is genuinely fatal.

Updates TestKill_RefusesIncompleteHandle → TestKill_TerminatesIncompleteHandle.

* style: gofmt blank line between test functions

---------

Co-authored-by: AO Bot <ao-bot@composio.dev>
2026-06-21 14:30:00 +05:30
Vaibhaav Tiwari 708ec5db56
fix: recover terminal reattach after daemon idle (#354)
* fix: recover terminal reattach after daemon idle

* chore: format with prettier [skip ci]

* fix: harden daemon start recovery

* fix: cancel stale daemon start attempts

* fix: quarantine untrusted daemon base url

* chore: format with prettier [skip ci]

* fix: close daemon status race windows

* chore: format with prettier [skip ci]

* fix: bootstrap daemon trust before shell load

* test: trust mocked API base in PR hydration

---------

Co-authored-by: Vaibhaav <user@example.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-21 13:59:15 +05:30
Adil Shaikh 72b757b203
feat: require explicit project agents (#355)
* feat: require explicit project agents

* chore: format with prettier [skip ci]

* test: configure integration project agents

* fix: center project agent selection dialog

* fix: keep agent selects neutral before validation

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-21 06:07:53 +05:30
Adil Shaikh 5e8c8defbd
fix: keep terminal mux persistent across navigation (#325)
* fix: keep terminal mux persistent across navigation

* chore: format with prettier [skip ci]

* fix: require configured agent defaults

* chore: format with prettier [skip ci]

* fix: recreate stale orchestrator worktrees

* fix: stop terminal reattach on socket close

* fix: stop terminal attach loop on close

* fix: keep agent default selects controlled

* revert: undo persistent mux branch changes

* fix: harden terminal mux attachment flow

* chore: format with prettier [skip ci]

* chore: remove terminal flow diagnostics

* chore: format with prettier [skip ci]

* fix: satisfy zellij command lint

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-21 06:07:32 +05:30
Harshit Singh Bhandari cdfec7f9d8
fix(desktop): green the tag desktop build on Linux/Windows and add app icon (#348)
The `Desktop testing build` workflow (tag 0.0.0-testing-*) failed on two of
its three runners, and the unsigned macOS artifact has no real app icon.

Linux (rpm): maker-rpm reported "cannot run on linux" because
electron-installer-redhat is only a deeply-nested optional dependency and npm
non-deterministically skipped it on the runner (debian installed, redhat did
not). Promote both electron-installer-debian and electron-installer-redhat to
top-level optionalDependencies so npm reliably installs them on linux/darwin
and still skips them cleanly on win32. Also give the rpm maker an explicit
License (rpmbuild rejects an empty License field) and a maintainer/homepage.

Windows (squirrel): NuGet pack exits 1 when <authors> is empty. package.json
had no author, so add author/license/homepage and set authors + setupIcon on
the squirrel maker.

App icon: generate icon.icns/.ico/.png from src/landing/public/og-image.png
(1024x1024) and wire packagerConfig.icon, the deb/rpm/squirrel makers, and the
runtime BrowserWindow icon (Linux/Windows; macOS uses the bundle .icns).

Verified on macOS: npm run make builds the zip, the packaged app's icns
matches the generated icon, typecheck + 160 vitest tests pass.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:53:11 +05:30
Harshit Singh Bhandari d9dde9280f
ci: build unsigned desktop artifacts on 0.0.0-testing-* tags (#347)
Add a tag-triggered workflow that builds the Electron desktop app (with the
bundled Go daemon) on macOS, Windows, and Linux runners and attaches the
unsigned artifacts to a GitHub prerelease for end-to-end pipeline validation.

Signing/notarization is intentionally off until the certs and secrets exist,
so these builds are for validating packaging, not distribution.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:19:24 +05:30
i-trytoohard 83091ceb4c
fix(sidebar): always show project row action icons (#346)
* fix(sidebar): always show project row action icons

The hover-reveal mechanism (opacity-0 → group-hover/menu-item:opacity-100)
was not reliably triggering in the Electron app — icons never appeared on
hover. Instead of debugging CSS :hover group propagation in Chromium, make
the action icons always visible.

Changes:
- Remove opacity-0/opacity-100 hover gating from the action cluster div
- Change button padding from hover-gated pr-[84px] to always pr-[84px]
- Hide the session count badge (it was only shown when icons were hidden)
- Keep z-10 on the action cluster so session rows don't paint over it

Reported by phylolver(vaibhaav).

* fix(sidebar): correct inverted collapsed-count class

The count badge was set to hidden-by-default but shown-as-grid in
collapsed/icon mode — the opposite of correct. In icon mode there's no
room for a count badge and the action cluster is hidden anyway. Since the
action icons are now always visible and permanently replace the count,
the badge should be hidden in all states.

Reported by phylolver(vaibhaav).

---------

Co-authored-by: AO Bot <ao-bot@composio.dev>
2026-06-20 21:17:39 +05:30
i-trytoohard e175da0691
fix(sidebar): make project row hover icons clickable and non-overlapping (#345)
* fix(sidebar): make project row hover icons clickable and non-overlapping

Three fixes for project row hover actions (dashboard, orchestrator, kebab):

1. Count badge pointer-events: The session count span fades to opacity-0 on
   hover but kept pointer-events active, intercepting clicks meant for the
   action icons. Added pointer-events-none on hover/focus/menu-open states.

2. Action icons z-index: The absolutely-positioned action cluster had no
   z-index, so positioned session rows (SidebarMenuSubItem with position:
   relative) in expanded projects painted on top and blocked hover/click on
   the first project's icons. Added z-10 to lift the action cluster.

3. Padding clearance: Bumped pr-[78px] to pr-[84px] to give more room for
   the three 20px action buttons + right offset.

Reported by aditi and phylolver(vaibhaav).

* fix(test): update Sidebar padding assertion from 78px to 84px

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: AO Bot <ao-bot@composio.dev>
Co-authored-by: Vaibhaav <user@example.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 19:58:50 +05:30
i-trytoohard f6ca7c2662
fix(session_manager): append confidentiality guard to agent system prompts (#341)
The orchestrator/worker system prompts (role, coordination, branch
conventions) contained no instruction telling the agent to treat them as
private, so a plain "give me your system prompt" made Claude Code dump
the role block verbatim.

Add a systemPromptGuard appended to every non-empty system prompt via
buildSystemPrompt, covering both spawn and restore paths. The guard
covers direct, indirect, and embedded reveal requests while leaving
general project/workflow questions answerable.

Adds TestSystemPrompt_AppendsConfidentialityGuard across orchestrator
and both worker variants.

Co-authored-by: i-trytoohard <i-trytoohard@users.noreply.github.com>
2026-06-20 17:44:59 +05:30
Pritom Mazumdar b4a8fad215
feat(frontend): surface multiple PRs per session + reviewer in inspector (#335)
* feat(frontend): surface multiple PRs per session

Replace the single optional pullRequest on a session with a prs[] list and
render it across every PR surface: the inspector stacks one card per PR, the
PR board lists one row per attributed PR, and the board card shows a PR count
summary. useWorkspaceQuery now maps the live /api/v1/sessions prs[] into the
query (the frontend surfaced no PRs before this). Ordering is actionable-first
(open, draft, merged, closed).

Adds unit tests (SessionInspector, PullRequestsPage), a Playwright e2e spec,
and a multi-PR mock-data fixture.

* chore: format with prettier [skip ci]

* feat(frontend): swap inspector Changes tab for empty Reviews placeholder

The inspector rail's Summary tab already renders the multi-PR stack from
PR #237 work, so the Changes (Git rail) tab is the next inspector surface
to evolve. Reviews will land on its own backed by PR-scoped review data
(separate workstream); reserve the slot now with an empty placeholder so
the navigation lands before the data does.

- Tabs are now Summary, Reviews, Browser. The InspectorView union and
  the VIEWS array are updated; Reviews gets a message-bubble icon to
  distinguish it from Summary's list icon.
- ChangesView and its lucide imports (GitBranch, GitCommitHorizontal,
  Plus, Square, Trash2) and the unused Button import are removed; a
  small ReviewsView mirrors BrowserView's empty-state shape.
- styles.css drops the orphaned .inspector-changes__* block.
- SessionInspector.test.tsx asserts the new tab labels and that Reviews
  shows the empty placeholder.

* feat(frontend): wire the reviewer feature into the inspector Reviews tab

The multi-PR transplant left the Reviews tab an empty placeholder, which would
have regressed the existing session review feature. Move the reviewer panel
(trigger/re-run a review, open the reviewer terminal, surface verdict + status)
into the Reviews tab, gated on the multi-PR model: it reads the session's prs[]
to decide between the reviewer card and the "no PR yet" empty state, and pulls
review runs + project reviewer config straight from the daemon.

Reconciles the prs[]-from-session-list model across the suite: ShellTopbar and
pr-hydration fixtures carry prs[], useWorkspaceQuery tests drop the obsolete
per-session /pr fetch, and SessionsBoard restores the dropped board title.

Adds a Playwright reviews-tab e2e spec (reviewer card for a session with PRs;
empty state for one without).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(frontend): adapt multi-PR e2e specs to main's expanded fixture

The rebase pulled in main's larger mock-data fixture (4 workspaces, 13 PRs)
and its renamed refactor-mux title. Rewrite the PR-board assertion to verify
the actionable-first ordering invariant instead of a brittle full-list match,
and update the empty-state selector to the session's current title.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: format with prettier [skip ci]

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-20 16:27:53 +05:30
Khushi Diwan a8a3056a6b
feat(frontend): polish board chrome and task actions
Polish the board and task action chrome.

- move board actions into the board header and remove empty board topbar space
- refine session topbar actions with notification, Kill, and Orchestrator controls
- add pointer cursors for clickable controls and clean sidebar child-session styling

Verified with frontend typecheck and targeted renderer tests.
2026-06-20 14:22:05 +05:30
Khushi Diwan 2cb20c23e4
feat(frontend): improve orchestrator board workflows
Improve the frontend board and session workflow presentation.

- refine project sidebar hierarchy and child session display
- clean up kanban task cards and status labels
- add direct task creation from project boards

Verified with frontend typecheck and targeted renderer tests.
2026-06-20 14:19:11 +05:30
neversettle c53c4af8bd
test(storage): guard against duplicate goose migration version prefixes (#336)
* test(storage): guard against duplicate goose migration version prefixes

Statically scans embedded migration filenames for repeated numeric
version prefixes and fails with a clear message, catching the class
of bug from #333 (two PRs adding 0014_*.sql independently) before
goose.Up() would panic at runtime.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(storage): dedupe migration versions by goose's parsed int64, not raw string

versionPrefix compared raw filename prefixes, so 014_x.sql and 0014_y.sql
were treated as distinct even though goose.NumericComponent parses both
to version 14 and panics on the collision. Use goose.NumericComponent
directly so the test enforces goose's actual collision rule.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(storage): trim PR-specific framing from migration version test comment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 12:49:59 +05:30
Khushi Diwan f98c5e56cc
fix(storage): renumber telemetry migration to 0015 to resolve 0014 version collision (#334)
* fix(storage): renumber telemetry migration to 0015 to resolve goose version collision

* chore: format with prettier [skip ci]

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-20 11:23:13 +05:30
swyam sharma 891fb79e79
fix(lifecycle): sanitize CI logs and reviewer comments before PTY paste (#323) 2026-06-20 10:27:52 +05:30
Vaibhaav Tiwari 7037f4aa4e
fix: surface missing reviewer harness (#318)
* fix: surface missing reviewer harness

* fix: satisfy review lint

---------

Co-authored-by: Vaibhaav <user@example.com>
2026-06-20 10:19:50 +05:30
Khushi Diwan 7d0ca02440
fix(project): resolve default branch from origin/HEAD, not checked-out branch (#289)
* fix(project): resolve default branch from origin/HEAD, not checked-out branch

Detecting the project default via `symbolic-ref --short HEAD` captured
whatever branch the repo happened to be on at add time. Adding a project
while on a feature branch (e.g. fix/pr-attachment) persisted that branch
as the default, so every session worktree based off it instead of main.

Prefer the remote default (origin/HEAD), falling back to the checked-out
branch only when no remote default is set. This still records a non-main
default like master correctly, while ignoring the active feature branch.

* test: cover branch-not-fetched API error
2026-06-19 19:50:19 +05:30
Laxman 43ee6c9b02
Feat/backend telemetry v0 (#307)
* feat(backend): add telemetry event plumbing

* feat(backend): emit session telemetry events

* feat(backend): add http and cli telemetry export

* feat(backend): add onboarding and dwell telemetry

* feat(frontend): add renderer posthog telemetry

* feat(frontend): bundle posthog project defaults

* feat(telemetry): add canonical active-user event

* fix(telemetry): repair cli test expectations

* fix(telemetry): sanitize remote payloads and respect event toggles
2026-06-18 22:00:25 +05:30
Madhav Kumar 6885af26b2
feat(windows): ConPTY terminal, zellij discovery, agent launcher trampoline, codex shim resolution (#310)
* fix(daemon): self-heal a stale run-file instead of refusing to start

On Windows the desktop supervisor can only TerminateProcess the daemon
(no POSIX signal reaches a detached child), so the daemon's graceful
shutdown never runs and ~/.ao/running.json is never removed. The leaked
file survives into the next launch, and because Windows reuses PIDs
aggressively the recorded PID usually belongs to an unrelated process.
The startup pre-flight trusted PID liveness alone (runfile.CheckStale ->
processalive.Alive), so it concluded a daemon was "already running" and
exited with "refusing to start" on every restart. A dead daemon then
makes the renderer's loopback REST calls (e.g. Spawn Orchestrator) fail
silently.

Verify the recorded port is actually served by an AO daemon with the
recorded PID (a /healthz probe matching service + pid, the same ground
truth inspectDaemon already uses) before refusing. A run-file left by a
crashed, hard-killed, or reused-PID predecessor is treated as stale and
overwritten, so startup is robust to a leaked run-file from any cause.

Fixes #256

* fix(release): build the desktop daemon natively on each target OS

build-daemon.mjs compiles the bundled `ao` daemon with the build host's
GOOS and names it off the host platform (ao.exe only when the builder is
Windows). The release workflow ran only on macos-latest, so a Windows
package would ship a macOS binary named `ao` with no `ao.exe`, and the
app could not launch a valid Windows daemon ("This program cannot be run
in DOS mode" / binary not found).

Run the release as a per-OS matrix (macOS + Windows) so host == target
and each installer bundles a daemon compiled for its own platform, and
pin the Go toolchain with setup-go since build-daemon needs it on every
runner.

Fixes #235

* feat(terminal): Windows ConPTY support for /mux attach

Replaces the Windows stub in internal/terminal/pty_windows.go with a real ConPTY implementation backed by github.com/aymanbagabas/go-pty, so the daemon's /mux attach can stream a live terminal to the renderer on Windows.

PTYSource.AttachCommand now returns (argv, env, err). On Windows the zellij attach is spawned directly (no powershell.exe wrapper) — wrapping ConPTY startup around a shell surfaces as modal application-error dialogs — and the per-session ZELLIJ_SOCKET_DIR is delivered via the spawn's CreateProcess env block instead of an 'env -u NO_COLOR' shim. Unix continues to use the env-shim wrapper and returns nil env.

Adds go-pty v0.2.3 (+ bumps golang.org/x/sys to v0.44.0 transitively). Updates the in-process test fakes (terminal/fakes_test.go, httpd/terminal_mux_test.go) for the new signature.

* feat(zellij): discover zellij binary on Windows and raise command timeout

Defaults the zellij binary to whatever exec.LookPath finds first (preferring zellij.exe on Windows), falling back to LOCALAPPDATA\Programs\zellij\zellij.exe and ProgramFiles{,(x86)}\{zellij,Zellij}\zellij.exe so a fresh-installed Windows user gets a working runtime without setting Options.Binary.

Raises the per-command timeout from 5s to 30s on Windows: the first zellij invocation after install routinely takes longer than 5s on Windows due to filesystem/AV warmup, which was causing benign DeadlineExceeded failures during session create.

* feat(zellij,cli): Windows agent launcher trampoline for codex argv

On Windows, zellij's KDL `args` quoting cannot round-trip codex's --config key=value flags (or any argv with embedded quotes), and shell-wrapping the agent in powershell/cmd quoting is equally unsound. This adds a small launch trampoline so zellij runs a known-fixed argv and the real argv is delivered out-of-band.

How it works on Windows:

1. zellij.Runtime.writeLayout persists cfg.Argv to a temp JSON spec via the new agentlaunch package (AO_LAUNCH_SPEC env var points at the file).

2. The KDL layout runs the trampoline as `<ao.exe> launch` (windowsLaunchArgv); PATH is augmented so the trampoline resolves.

3. The new hidden `ao launch` subcommand reads the spec, deletes the temp file, and execs the real agent with cfg.Argv inside cfg.WorkspacePath.

Also adds:

- runner.Start fire-and-forget path (process_windows.go uses powershell.exe -EncodedCommand + Start-Process -WindowStyle Hidden with CREATE_NEW_CONSOLE so the daemon is not blocked on zellij's --create-background settling).

- powerShellEncodedCommand helper and switch from -Command to -EncodedCommand for the existing powershell shellLaunchSpec (avoids brittle KDL→PowerShell quoting round-trips).

Unix is unchanged: writeLayout passes cfg.Env straight through, createSession stays synchronous via runner.Run, and process_other.go is a stub that returns an error if anyone calls into the background path.

* feat(codex): Windows binary resolution, terminal compat flags, TOML literal strings

Three Windows-targeted refinements to the codex agent plugin so a default Windows install lands in a working state:

1. ResolveCodexBinary now follows .cmd/.ps1 shims to the underlying codex.exe (resolveNativeWindowsCodex + windowsNativeCodexCandidatesForShim). The npm-distributed codex shim cannot be exec'd directly under ConPTY without a shell wrapper; jumping straight to the .exe avoids that wrapper.

2. appendTerminalCompatibilityFlags adds Windows-specific args (e.g. --no-alt-screen) so codex's TUI renders correctly inside zellij's pane without the alternate-screen buffer churn that breaks ConPTY redraws.

3. hooks.go gains codexTOMLLiteralString / codexTOMLConfigString / containsTOMLControl so paths and other values with backslashes and quotes round-trip through codex's --config TOML parser using literal strings ('...') when basic strings would require unsafe escaping.

* fix(lint): paramTypeCombine in pty_unix.go, revive doc comments in agentlaunch, codex test quotes

* fix: stabilize windows zellij sessions

---------

Co-authored-by: harshitsinghbhandari <24b4506@iitb.ac.in>
Co-authored-by: Madhav <madhavkumar@microsoft.com>
Co-authored-by: Vaibhaav <user@example.com>
2026-06-18 21:51:05 +05:30
neversettle 9ae9f08887
fix(session): make worker branch namespace child-safe (#309)
* fix(session): make worker branch namespace child-safe

* test(cli): wait for daemon exit after shutdown
2026-06-18 20:00:15 +05:30
Harshit Singh Bhandari 5fff98087a
feat(import): rewrite-side legacy → rewrite first-boot import (#314)
* feat(import): rewrite-side legacy → rewrite first-boot import

Port the legacy-side TS reader (AgentWrapper #2144/#2129) to Go and run the
migration inside the rewrite as an opt-in import, per the FINAL v2 plan. Reads
the legacy flat-file store (~/.agent-orchestrator) read-only and writes the
rewrite's own SQLite DB via the native storage layer; legacy files are never
touched, and a re-run skips existing rows, so a declined or failed import loses
nothing.

What's included:
- internal/legacyimport: Go reader + field mappers (issue #247). Lifecycle
  double-decode (lifecycle key or statePayload+stateVersion:"2"),
  role/orchestrator detection, sessionPrefix fallback (first 12 chars of id),
  8→4 activity-state map, per-harness resume-id selection, permission/harness
  remap, and the claude transcript slug + relocation to the rewrite's
  orchestrator worktree path ({DataDir}/worktrees/{id}/orchestrator/{prefix}-orchestrator).
- store.ImportSession: verbatim session insert (explicit id/num, ON CONFLICT
  DO NOTHING) so the orchestrator lands at id "{prefix}-orchestrator", num 0.
- `ao import`: explicit, idempotent import with --from/--dry-run/--yes/--json.
  Refuses while a live daemon owns the run-file (the daemon is sole writer; the
  import runs offline, matching the #2129 reference).
- First-boot opt-in: `ao start` offers the import before launching the daemon
  when legacy data is present and the rewrite DB has no projects yet. Declining
  or any failure is non-fatal; a non-interactive boot prints a hint instead of
  auto-importing.

Scope (gist §6): all projects + per-project settings, and the single
non-terminated orchestrator session per project (claude-code/codex/opencode;
aider skipped with a note). Workers are not imported (they respawn fresh).

Resume-id mapping (#247 §2.2): agent_session_id carries claudeSessionUuid /
codexThreadId / opencodeSessionId by harness. codexModel and
restoreFallbackReason have no rewrite column, so they are dropped and surfaced
as import notes — codex resumes from the thread id alone, the rest is forensic.

Gate: `go build ./... && go test -race ./...` green (1423 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(import): resolve golangci-lint errcheck/gocritic/nilerr findings

- start.go: check fmt.Fprint* returns in the first-boot import path
- project.go: combine same-typed return params (gocritic paramTypeCombine)
- claude.go: use a pathExists helper so a missing transcript source is a normal
  skip, not an err-then-return-nil (nilerr)
- importer.go: fold best-effort transcript relocation into a switch so the
  non-fatal path no longer returns nil from an error branch (nilerr)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(import): resolve transcript dest path like the daemon; harden lifecycle parse

Code-review follow-ups on the legacy importer:

- claude.go: compute the Claude transcript DESTINATION slug from the
  symlink-resolved orchestrator worktree path (new resolvePhysical, mirroring
  gitworktree.physicalAbs), not the literal path. The daemon resolves that cwd
  through physicalAbs before `claude --resume` runs, so a literal-path slug
  missed the resume bucket whenever any component of AO_DATA_DIR was a symlink
  (custom data dir, macOS /tmp→/private/tmp, symlinked $HOME) — the orchestrator
  would have resumed without its prior context. Source slug now uses the same
  resolver for symmetry.
- orchestrator.go: accept a numeric stateVersion (JSON 2 → float64) as well as
  the string "2" when falling back to statePayload, so a V2 record carried only
  in statePayload is not misparsed as stateless.
- orchestrator.go: build the dropped-resume-metadata note as a joined list
  instead of string concatenation.

Tests: added a symlinked-data-dir dest-slug test and a numeric-stateVersion
fallback test. Gate green: `go build ./... && go test -race ./...` (1425).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 19:54:51 +05:30
Harshit Singh Bhandari b37b6acefe
feat(frontend): refresh dashboard, orchestrator, and AO logos (#317)
* feat(frontend): refresh dashboard, orchestrator, and AO logos

Replaces the dashboard and orchestrator glyphs and the AO brand logo
per issue #315.

- Dashboard buttons (sidebar action + Open Kanban) now use the
  asymmetric LayoutDashboard icon instead of the equal-cell LayoutGrid.
- Orchestrator buttons (sidebar action + every topbar badge/button) use
  a new org-chart OrchestratorIcon (parent fanning to three children),
  authored in lucide's stroke style since lucide has no matching glyph.
- The AO brand logo (sidebar header mark, landing nav, docs header) is
  now the pixel-mascot image; the old ao-logo.svg monogram is removed.

Closes #315

* chore: format with prettier [skip ci]

* fix(landing): use next/image and next/link for the AO logo and docs link

Addresses react-doctor review on #317: the Next.js landing app should use
next/image for the brand logo (optimized formats, responsive srcset, lazy
loading) and next/link for the internal /docs navigation (client-side
routing + prefetch) instead of plain <img>/<a>.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-18 16:23:19 +05:30
Harshit Singh Bhandari 93123edd34
fix(frontend): stop fit loop committing transient cell-box mis-measurements (#313) (#316)
The onRender convergence loop added in #312 recovered the under-counted
rows from #280, but on a HiDPI display it could lock the Codex
orchestrator terminal at half size with a ghosted composer (#313).

FitAddon derives the grid by dividing the pane box by the renderer's
measured css cell box. During the WebGL atlas warm-up that cell box can
emit a one-frame transient (a doubled box on a 2x display), which halves
the proposed cols/rows. The loop committed that single frame's proposal,
resized the grid to half, then detached after a few "stable" frames — so
nothing re-fired the PTY resize that makes zellij repaint, leaving the
grid stuck at half width and the stale composer un-cleared.

Require a differing proposal to repeat identically across two consecutive
renders before applying it, so a one-frame transient only updates the
pending value and is never committed. Add 600ms/1200ms settle fits as a
session-bounded backstop: by then the atlas and font metrics are warm, so
even if the loop detached at a briefly-stable wrong measurement, a late
re-measure corrects the grid and fires the resize zellij needs to repaint
cleanly. fit() is idempotent, so a correct terminal never reflows.

Fixes #313

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 16:09:51 +05:30
Harshit Singh Bhandari 63d488ba29
fix(frontend): recover clipped Codex terminal via onRender convergence re-fit (#312)
The Codex terminal rendered only in the top half of the pane: FitAddon
divided the pane height by a too-tall cell box (measured before the
post-open WebGL renderer and the monospace font's real metrics resolved),
under-counted rows, and sent that short grid to zellij. It never recovered
because every remaining fit trigger after the settle window was the host
ResizeObserver, and the host's height:100% box never changes when only the
row count is wrong.

Add an onRender convergence loop: each renderer repaint re-proposes
dimensions from the current measured cell box and re-fits when they differ,
converging the grid to the true row count once metrics settle, then detaches
once stable (bounded by a re-fit cap). proposeDimensions returns undefined
until the cell box is non-zero, so a fit is never accepted from an unmeasured
cell. Also listen on window resize for OS-window / DPR changes that move the
true cell box without touching the host box.

Fixes #280

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 15:48:51 +05:30
Harshit Singh Bhandari dd3faa7357
docs: refocus README on the product, move progress to docs/STATUS.md (#311)
* docs: refocus README on the product, move progress to docs/STATUS.md

Rework the README around what ReverbCode is and does, drawing the
agent/runtime/tracker framing and the "how it works" flow from the legacy
agent-orchestrator README but stating only what the rewrite's code actually
implements (zellij runtime, GitHub SCM/tracker, 23 verified agent adapters,
port/adapter extensibility surface).

Move progress tracking out of the README: rename docs/status.md to
docs/STATUS.md, reconcile the README's "Status and roadmap" content into it
(SCM observer issue refs, milestone link), correct the adapter count to 23,
and drop the stray trailing markup. Update the README, AGENTS.md, docs/README.md,
and docs/stack.md references accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: format with prettier [skip ci]

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-18 15:43:41 +05:30
neversettle b06ea39103
docs: remove unwanted documents to reduce clutter (#200)
* remove unwanted docs
2026-06-18 15:25:54 +05:30
Vaibhaav Tiwari 73b166a015
fix(frontend): make review inspector panels responsive (#300)
* fix(frontend): make review inspector panels responsive

* chore: format with prettier [skip ci]

---------

Co-authored-by: Vaibhaav <user@example.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-17 23:44:43 +05:30
Harshit Singh Bhandari 4cff3f76eb
feat(frontend): show dashboard and orchestrator buttons on project hover (#293)
* feat(frontend): show dashboard and orchestrator buttons on project hover

Hovering a project row in the sidebar now reveals a dashboard button
(opens the project board), an orchestrator button (opens the running
orchestrator or spawns one), and a vertical three-dot kebab menu,
replacing the lone horizontal kebab. Closes #292.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: format with prettier [skip ci]

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-17 23:21:34 +05:30
Harshit Singh Bhandari b8fbbec4ac
feat(frontend): move kill session control to topbar beside Open orchestrator (#298)
* feat(frontend): move kill session control to topbar beside Open orchestrator

Relocate the worker "Kill session" control out of the inspector's Summary
"Danger zone" section and into the app topbar actions row, as a small
danger-tinted icon button next to "Open orchestrator". It only renders for
active worker sessions and keeps the same arm-then-confirm flow and
POST /sessions/{id}/kill behavior.

Move the kill-button tests to ShellTopbar.test.tsx accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: format with prettier [skip ci]

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-17 22:29:22 +05:30
prateek edf00de61b
fix(skills): adapt bug-triage skill to ReverbCode stack (#281)
* fix(skills): adapt bug-triage skill to ReverbCode stack

The bug-triage skill was copied verbatim from the upstream TypeScript
agent-orchestrator and never adapted to ReverbCode (Go + Electron).

- Target repo: ComposioHQ/agent-orchestrator -> aoagents/ReverbCode
- Stack translation: pm2/tmux/Node -> Go daemon on 127.0.0.1:3001 + Zellij
  runtime adapter; packages/*.ts paths -> verified backend/ Go paths;
  ao --version -> ao version; lsof :3000 -> :3001; SQLite at ~/.ao/data,
  handshake at ~/.ao/running.json
- Add prominent CLI-footgun warning: bare 'ao' may resolve to a different AO
  install (old npm build on :3000); build /tmp/ao or use the bundled daemon
  and confirm 'ao status' shows port 3001
- Remove broken push_fix_to_github.py reference; replace 5f with a Go flow
  (branch + go build/test + gh pr create for trivial fixes; ao spawn worker
  for non-trivial)
- Label guidance: check 'gh label list' first, only apply existing labels,
  state priority/confidence in the body otherwise
- Add pitfall: verify the bug reproduces against ReverbCode (:3001) first

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: format with prettier [skip ci]

---------

Co-authored-by: itrytoohard <ayetrytoohard@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-17 22:27:25 +05:30
Vaibhaav Tiwari ee044e4edb
feat(frontend): show reviewer worker controls (#255)
* feat(frontend): show reviewer worker controls

* chore: format with prettier [skip ci]

* fix(frontend): surface reviewer config and reused reviews

* chore: format with prettier [skip ci]

* chore: format with prettier [skip ci]

---------

Co-authored-by: Vaibhaav <user@example.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-17 21:46:09 +05:30
Harshit Singh Bhandari 4bbcf9e925
feat(frontend): add kill button to worker session page (#288)
The worker session inspector had no way to stop a running session from
the UI. Add a "Kill session" action in the Summary view's Danger zone
that arms a one-step confirmation, then POSTs /sessions/{id}/kill and
invalidates the workspace query so the session moves to the terminated
group. The action is hidden for already-terminated/merged sessions.

Closes #287

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 20:50:10 +05:30
neversettle 6ac65d2cd3
fix(frontend): populate session.pullRequest from /pr endpoint (#251) (#279)
* fix(frontend): populate session.pullRequest from /pr endpoint (#251)

WorkspaceSession.pullRequest was declared but never populated, so every
Boolean(session.pullRequest) check was dead: the Summary tab gated its PR
fetch on it (never ran), and the Board card and /prs page read it directly
(always empty).

Hydrate the field centrally in fetchWorkspaces — the single place that
builds session objects for the workspace, board, PR page, and sidebar — by
fetching GET /sessions/{sessionId}/pr per non-terminated session in parallel
and attaching {number, state}. A per-session fetch error degrades to "no PR"
rather than failing the whole workspace query; terminated sessions are
skipped. GET /sessions/{sessionId}/pr stays the single source of truth, so
no new backend endpoint and no changes to the consuming components.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: format with prettier [skip ci]

* test(frontend): verify PR hydration end-to-end for a normal project (#251)

Drives the real useWorkspaceQuery + real SessionsBoard / PullRequestsPage
for an ordinary project (from /api/v1/projects) whose session has an open PR,
mocking only the HTTP client and router. Confirms PR facts fetched from
/sessions/{id}/pr flow through the shared workspace cache into both the Board
card ("PR #278 · open") and the PR page row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(frontend): move PR hydration integration test into __tests__/integration

Cross-component integration test belongs in a dedicated folder, separate
from the co-located unit tests. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-17 17:48:03 +05:30
Pritom Mazumdar 3986d488c8
feat(session): support multiple PRs per session (#230)
* feat(session): support multiple PRs per session

A session can now own several pull requests (a root plus stacked
children) instead of being capped at one. The SQLite schema was already
1-session->many-PR (pr.url PK, session_id a plain FK), so this is a
behavioural change across the observe -> persist -> derive -> react
pipeline, not a migration.

- observe: the SCM observer discovers every open PR whose source branch
  matches a session branch or descends from it ("branch/..." stacking),
  attributing each to the owning session; the longest matching branch
  wins so a child session claims its own stacked PRs.
- derive: session status is a worst-wins aggregate over all owned PRs,
  with a stack model (B is a child of A iff B.target == A.source and A is
  open) exposed via prs[] on every session read DTO.
- react: per-PR reactions; a stacked child blocked by an open parent is
  exempt from the rebase/merge-conflict nudge (only the bottom of the
  stack is eligible), and the session completes only when no PR is open
  and at least one merged.
- tests: unit coverage across stack/status/observer/lifecycle, a
  real-SQLite ListPRFactsForSession test for the stacked-PR read path,
  and a functional end-to-end integration test driving the real store +
  lifecycle + observer through attribution, completion, and stacked-child
  nudge suppression.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(scm): ignore fork heads in PR attribution and persist discovered siblings before completion

Branch-prefix attribution now requires a discovered PR's head branch to live
in the project repo. A fork PR can reuse a session's branch name while its
commits live in the fork, so the previous code could auto-claim foreign work.
Carry head repo full_name from the REST list response and skip any PR whose
head repo is not the base repo.

discoverNewPRs also writes each newly discovered PR as an open baseline row
before the refresh/lifecycle pass runs. A session can own several PRs, and a
terminal observation triggers a completion check that reads all of the
session's PRs from the store. Without the early write, an open sibling found
in the same poll was not yet durable and the session could terminate while
that PR was still open.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(session): surface actionable signals from blocked stacked children; clarify worker prompt

Status aggregation previously dropped any open PR blocked by an open parent,
hiding actionable child signals (failing CI, draft, requested changes,
unresolved comments) behind the parent's status. A blocked child still cannot
merge, so its readiness signals (mergeable/approved/review-pending/open) stay
suppressed, but its problem signals now contribute to the worst-wins aggregate.
The all-blocked fallback is preserved so a session never goes dark.

The worker multi-PR prompt said independent PRs could branch off the base
branch as usual, which conflicts with branch-prefix attribution. Clarify that
a PR may target the base branch, but its source branch must stay under the
session branch namespace for AO to track it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 17:31:28 +05:30
neversettle eeaddd221a
fix(review): reviewer posts to GitHub and records its verdict autonomously (#259)
* fix(review): make the reviewer post to GitHub and record its verdict autonomously

The claude-code reviewer never completed a review on its own. Three defects
in the reviewer launch + flow:

- It launched with no permission mode, so a headless pane stalled on the
  first tool-permission prompt and never ran gh/ao. Launch with
  bypassPermissions (read-only is enforced by the prompt, not a sandbox).
- The reviewer pane got no pinned PATH, so `ao review submit` resolved to a
  foreign `ao` on the inherited PATH and failed. Pin PATH to the daemon's
  own dir the same way worker sessions do — export HookPATH and reuse it in
  the launcher.
- The prompt did not enforce ordering. Make it post the review on the PR
  via gh first, then run `ao review submit`.

Fixes #258

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(review): fall back to a comment review when self-approval is rejected

GitHub does not let an author approve their own PR, so a reviewer running
under the same account can't post an `approve`. Tell the reviewer to post
the approval as a regular comment review (COMMENT event stating it is an
approval) when the provider rejects the self-approval, instead of failing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 16:34:15 +05:30
Khushi Diwan 96d1649e46
fix(review): serialize concurrent triggers per worker to stop reviewer double-spawn (#246)
* fix(review): serialize concurrent triggers per worker to stop double-spawn

Engine.Trigger was a read-then-write (idempotency check -> reviewer spawn ->
InsertReviewRun) with no serialization and no backing constraint. Two near-
simultaneous triggers for the same worker at the same head SHA both passed the
GetReviewRunBySessionAndSHA check, both spawned a reviewer against the same
deterministic review-<id> handle, and both inserted a running run for one commit.

Add a per-worker keyed mutex (lockWorker) held across the whole Trigger body, so
the loser re-reads the freshly-recorded run and short-circuits to Created:false
instead of spawning. Back it with a partial unique index on
review_run(session_id, target_sha) (migration 0013) as a cross-restart safety
net; rows with an empty target_sha (head not yet observed) are excluded so they
are not blocked.

Adds a concurrency test asserting N simultaneous triggers spawn once and record
one run.

Closes #242

* fix(review): make migration 0013 dedup-safe and handle the unique conflict in Trigger

Pre-#242 daemons can already hold duplicate (session_id, target_sha)
review_run rows, on which CREATE UNIQUE INDEX fails and wedges startup.
Migration 0013 now collapses each duplicate group to a single survivor
(a completed pass over a still-running one, then newest by created_at)
before building the index.

Trigger now treats a unique-constraint hit as a fallback rather than an
error: InsertReviewRun maps it to the new domain.ErrDuplicateReviewRun
sentinel, and Trigger re-reads GetReviewRunBySessionAndSHA and returns
that run with Created:false instead of surfacing a raw error after the
reviewer may already have launched.
2026-06-17 00:43:10 +05:30
Harshit Singh Bhandari 43ae7ebbc4
fix(frontend): worker session opens orchestrator, not Kanban (#248)
A worker agent session's primary topbar action read "Kanban" and
navigated to the board. Workers now get an "Open orchestrator" button
(Waypoints icon) that navigates to their project's orchestrator, while
orchestrator sessions keep the "Open Kanban" board action.

Closes #234

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:49:16 +05:30
Khushi Diwan 198b79757c
fix(session): stamp session timestamps in UTC (#215)
Two clocks defaulted to local time.Now while the rest of the codebase writes
UTC, so ao session get showed created and updated in different timezones:

- session manager clock → spawn-stamped CreatedAt/UpdatedAt
- lifecycle manager clock → activity-driven LastActivityAt/UpdatedAt

A real spawn made this visible: createdAt came back UTC but updatedAt/
lastActivityAt were local once the agent reported activity. Default both clocks
to UTC.

Closes #214
2026-06-15 14:51:12 +05:30
Khushi Diwan d81d76280d
fix(project): detect the repo's default branch on add (#209)
Register the repo's actual checked-out branch as the project default so
session worktrees base off a ref that exists. Previously Config.DefaultBranch
was left empty and defaulted to "main", so a repo on master/develop/trunk
failed every spawn with BRANCH_NOT_FETCHED and had no CLI workaround.

Detection is best-effort (symbolic-ref --short HEAD); a detached HEAD or git
error falls back to the existing main default. Only persist when the branch
diverges from main, so the common main repo keeps a NULL config.

Closes #208
2026-06-15 14:51:07 +05:30
Khushi Diwan d747b57ca6
fix(frontend): exclude nested node_modules from the vitest run (#217)
A bare "node_modules/**" replaces vitest's default "**/node_modules/**"
and only matches the repo root, so the tracked src/landing preview app's
nested node_modules had its vendored third-party test suites (zod, next, ...)
collected and run once those deps were installed — 20+ failures from code
that isn't ours.

Anchor it at any depth with "**/node_modules/**".

Closes #216
2026-06-15 14:51:03 +05:30
Khushi Diwan 9e84a3b5ae
fix(spawn): map invalid branch name to 400 instead of opaque 500 (#213)
validateBranch returned an untyped error for a name rejected by
git check-ref-format, so toAPIError fell through to INTERNAL_ERROR 500.

Add a ports.ErrWorkspaceBranchInvalid sentinel (mirroring the not-fetched /
checked-out-elsewhere ones), wrap it in validateBranch, and map it to
INVALID_BRANCH (400). Completes the residual of #152 Bug 3, which typed the
not-fetched and checked-out-elsewhere cases but left the invalid-format case
collapsing to 500.

Closes #212
2026-06-15 14:50:58 +05:30
Khushi Diwan cbf3f0a2db
fix(spawn): reject unknown harness with 400 instead of opaque 500 (#211)
An unknown --harness was only caught at the agent-registry lookup deep in
Spawn, after the seed row and worktree were already created: the untyped
error collapsed to INTERNAL_ERROR 500 and left a terminated orphan row.

Validate the harness against the registry before any durable state is
created and return a typed ErrUnknownHarness mapped to UNKNOWN_HARNESS (400).
Sibling to #152 Bug 6 (unknown binary on PATH), which did not cover a harness
with no registered adapter.

Closes #210
2026-06-15 14:50:53 +05:30
neversettle da30da5a45
feat(review): configurable AO code review backend (V1) (#192) (#197)
* feat(review): configurable AO code review backend (V1)

Add per-project configurable code review of a worker's PR. A reviewer
agent runs one-shot over the worker's own worktree and posts its result
to the PR; the worker picks the feedback up through the existing SCM
observer review-nudge path.

- domain: ProjectConfig.reviewers (+ default reviewer harness), Review /
  ReviewRun types and verdict/status vocab.
- storage: review + review_run tables (0011), sqlc queries, store methods.
- service/review: rewrite the in-memory stub as a persisted ReviewService
  (Trigger/Submit/List) with a reviewer Runner over agent resolver +
  runtime; ports.PRReviewPoster implemented on the GitHub adapter.
- http: session-scoped routes POST /sessions/{id}/reviews/trigger,
  POST .../submit, GET .../reviews; regenerated OpenAPI + TS types.
- cli: ao review trigger|submit|list.
- frontend: adapt ReviewDashboard to the per-worker reviews API.

Closes #192

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(review): address review — drop submit/poster/CLI, default reviewer to worker harness

Per PR #197 review feedback:
- Reviewer agent posts its review to the PR itself, so remove the
  ports.PRReviewPoster port, the GitHub review poster, the submit HTTP
  route + DTO, and the service Submit method (#1, #4, #7).
- Trigger spawns the reviewer agent over the worker's worktree with its
  own review prompt, mirroring the session launch flow (resolve agent by
  harness -> argv -> runtime.Create) (#8, #9).
- Default reviewer harness reuses the worker's harness when supported,
  falling back to claude-code; reviewer config stays independent of the
  worker override (#5, #6).
- Drop the `ao review` CLI for this PR's scope (#2, #3).

Regenerated OpenAPI + TS types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(review): restore ao review submit (records verdict+body in AO)

Per maintainer request, bring back `ao review submit`. AO records the
reviewer's verdict and body on the review_run and marks the pass complete;
it does not post to GitHub — the reviewer agent posts its review to the PR
itself.

- storage: add review_run.body (0011), persist via Insert/UpdateReviewRunResult.
- service: restore Submit (no SCM poster) storing verdict + body.
- http: restore POST /sessions/{id}/reviews/submit + SubmitReviewInput.
- cli: ao review submit [worker] --verdict --body (worker from arg/--session/$AO_REVIEW_WORKER).
- runner: reviewer prompt instructs posting to GitHub and recording via ao review submit.

Regenerated OpenAPI + TS types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(review): move reviewer runner to its own package; sharpen prompt

Per PR #197 review:
- Move the concrete reviewer runner out of the service layer into a new
  internal/review_runner package (package reviewrunner), beside other
  orchestration packages like session_manager. The service keeps only the
  Runner interface + RunSpec it depends on; the agent-resolver + runtime
  launch flow lives in review_runner.
- Sharpen the reviewer prompt: tell the agent to diff against the PR base,
  focus on high-confidence findings, post via `gh pr review`, and record
  the result with `ao review submit`; review-only (no commits/edits).
- Add unit tests for the runner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(review): simplify review_run schema; provider-agnostic reviewer prompt

Per PR #197 review:
- review_run: status default 'running' (drop 'pending'), drop CHECK
  constraints on status/verdict, drop the updated_at column and the
  session/iteration index. Propagated through queries, domain, store,
  service, and tests.
- Reviewer prompt no longer hardcodes GitHub/gh commands — it instructs the
  agent to use whatever review tooling the provider offers, keeping the
  flow extensible across SCM providers.

Regenerated sqlc + OpenAPI/TS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(review): launch reviewer before persisting the run

Trigger now spawns the reviewer agent first and then writes the review_run
with a status derived from the launch outcome (running on success, failed
if it never started), instead of inserting a running row and correcting it
to failed afterwards.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(review): pluggable reviewer registry distinct from worker harnesses

Reviewers are now their own pluggable adapter set, separate from the worker
agent registry — adding a reviewer (claude-code today, greptile tomorrow) is
a one-line registration that does not widen the worker harness vocabulary,
and a worker harness does not automatically become a valid reviewer.

- domain.ReviewerHarness: a distinct vocabulary (AllReviewerHarnesses) with
  its own IsKnown; ReviewerConfig/Review/ReviewRun use it. ResolveReviewerHarness
  reuses the worker harness only when it is itself a supported reviewer, else
  falls back to claude-code.
- ports.Reviewer: a reviewer-specific contract (ReviewCommand → argv + env)
  that models one-shot / non-prompt CLIs natively instead of forcing every
  reviewer through the worker's interactive GetLaunchCommand(Prompt:...).
- internal/adapters/reviewer: a separate registry + resolver (mirrors the
  worker agent registry) with the claude-code reviewer adapter, which owns the
  review prompt and reuses the worker claude-code launch construction.
- review_runner resolves via the reviewer registry (not the worker
  AgentResolver) and merges AO_REVIEW_WORKER into the adapter's env.
- daemon wires the reviewer resolver. Registry/domain parity is test-enforced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(review): cover run-scoped reviewer submit

* fix(api): update generated review submit schema

* refactor(review): split core engine (internal/review) from API service

Move the review orchestration (Trigger/Submit/List, run-id generation,
deps, RunSpec/Runner, sentinels) into a transport-independent core package
internal/review (Engine). internal/service/review is now a thin API-flow
boundary: the controller-facing Manager interface + a Service that delegates
to the engine + error re-exports.

This keeps the service layer to API concerns and lets the same engine back a
future in-process CLI trigger without going through HTTP. review_runner now
depends on the core package; daemon builds the engine and wraps it in the
service. No API/schema changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(review): commit-aware trigger, reviewer handle for UI, no env vars

Reworks the review trigger lifecycle and drops env-based coupling:

- review_run gains target_sha (the reviewed commit) and drops iteration.
  A repeat trigger for the same PR head short-circuits to the existing run.
- review gains reviewer_handle_id: the live reviewer pane's runtime handle,
  reused across passes and exposed in the reviews API so the UI can attach
  its terminal over /mux.
- Trigger flow: if a live reviewer pane exists and a new commit arrived,
  message it to re-review; otherwise spawn a fresh reviewer. The run is
  recorded only after the reviewer is launched.
- No environment variables: the reviewer adapter embeds the explicit
  `ao review submit --session <w> --run <id>` command in the spawn prompt
  and the re-review message. CLI submit requires --run/--session (no env
  fallbacks).
- Merge review_runner into internal/review as a Launcher (spawn/notify/alive).
- Trigger returns 201 for a new pass, 200 when reusing an existing run.

Regenerated sqlc + OpenAPI/TS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(review): author the reviewer prompt centrally, not in the adapter

Mirror the worker model (session_manager builds the prompt; adapters just
place it via LaunchConfig.Prompt). The reviewer prompt now lives in
internal/review/prompt.go and is passed through ports.ReviewInvocation.Prompt;
the claude-code reviewer adapter just feeds inv.Prompt to its launch command
and returns it as the re-review message. One-shot CLI reviewers may ignore it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(review): split reviewer prompt into system+task, mirroring buildSpawnTexts

Mirror session_manager.buildSpawnTexts for the reviewer: a standing role goes
in the system prompt, the per-pass task (PR/commit + exact `ao review submit`
command) goes in the user prompt. internal/review/prompt.go now returns
(prompt, systemPrompt); both flow through ports.ReviewInvocation and the
claude-code adapter places them via LaunchConfig{Prompt, SystemPrompt}. The
re-review message reuses the per-pass prompt (role already established in the
running pane).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Vaibhaav <user@example.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-15 01:17:17 +05:30
Khushi Diwan a197ff6d88
fix(spawn): persist the resolved default agent on the session (#221)
A spawn with no explicit harness ran the daemon default (claude-code) but
stored an empty harness: effectiveHarness returned "", seedRecord persisted it,
and the empty->default resolution lived only inside agentRegistry.Agent. The API
then omitted harness and the UI defaulted to "codex" — mislabelling a Claude
Code session.

Inject the daemon's default agent (AO_AGENT / config.DefaultAgent) into the
session manager and resolve an unspecified harness to it before the seed row is
written, so the stored/returned harness matches the agent that actually runs.

Closes #220
2026-06-15 00:24:20 +05:30
Harshit Singh Bhandari 724b9a2d81
Use ~/.ao as canonical state home (#233)
* Use ~/.ao as canonical state home

* chore: format with prettier [skip ci]

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-14 22:52:25 +05:30
Adil Shaikh 17df922652
feat: add notifications v1 (#181)
* feat: add notifications v1

* fix: address notification review feedback

* fix: require passing CI for merge-ready notifications

* fix: simplify notification listing

* fix: ignore missing sessions for scm notifications

* fix: project notifications from cdc

* fix: stream notifications without cdc
2026-06-14 20:02:32 +05:30
Harshit Singh Bhandari 7da911711f
docs: sync documentation with current state of main (#228)
* docs: sync documentation with current state of main

The rewrite is further along than several docs claimed. Bring the docs
in line with the actual code on main:

- status.md: rewrite the stale "session HTTP routes not wired yet" /
  "next integration work" framing into a shipped vs in-flight breakdown.
- cli/README.md: document the full product command surface (project,
  session, spawn, send, orchestrator) instead of "not present yet".
- architecture.md: correct the package layout (service/{pr,review},
  observe/scm, observe/reaper, daemon, config) and add the no_signal
  status to the derivation precedence.
- backend-code-structure.md: add service/review and observe/scm.
- README.md: expand the agent-adapter list (20+), add project set-config,
  and describe the frontend as the real wired supervisor it is.
- AGENTS.md / docs/README.md: drop "placeholder frontend" wording and
  add the agent-adapter doc to the index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: format with prettier [skip ci]
2026-06-14 18:42:18 +05:30