Skip to content

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 09 Oct 16:40
· 109 commits to main since this release
v1.3.0
b057102

Added

  • README "Upgrading from 1.2.x" section: branch-mode migration, re-running xpo init, xpo board network binding and access token, JSON output changes, principal-first assignees. The Board section now documents the --host access token (xpo-e60c26)
  • xpo board --host <non-loopback> requires a per-run access token. It is printed on startup as …/?token=… and exchanged for an HttpOnly, SameSite=Lax session cookie named exponential-<sha256(token)[:8]>. Without the cookie, /api/* returns 401 and pages redirect to a new /auth form; the web UI redirects there on any 401. /healthz stays public, and loopback binds are unchanged (xpo-897f35)
  • Web UI bundles Inter Variable and JetBrains Mono Variable (latin + latin-ext woff2, embedded in the binary) instead of relying on locally installed fonts; @font-face pins ascent-override / descent-override / line-gap-override so Linux and macOS lay out text identically. SIL OFL 1.1 license texts ship at /licenses/ (xpo-bade50)
  • SearchInput in the My Issues TopBar filters the active tab by title, ID or label; Backlog and My Issues share a new matchesSearch predicate, and Backlog search now ignores surrounding whitespace (xpo-5ed512)
  • SearchInput in the Labels TopBar filters labels by name (case-insensitive substring), with a "No labels match" empty state (xpo-c6043f)
  • unlink MCP tool and xpo unlink <source> <target> -t <type> CLI command (with --json): remove one relationship between two issues, whichever side stores it, including links to deleted issues (xpo-9d5d00)
  • "Worked by" filter (Agent / Human) in the Backlog, My Issues and Inbox filter menus, backed by a new assignee_via field on issues in the CLI JSON, MCP and HTTP API (xpo-35fc16)
  • "Open" button in the issue detail top bar that opens the issue's local worktree in VS Code via a vscode://file/… link; the path comes from a new local-only GET /api/local/issues/{id}/worktree endpoint, which xpo board answers itself even in remote/proxy mode (xpo-c4fbed)
  • Unified JSON I/O layer: internal/jsonio/ package as single source of truth for all JSON-serializable types shared between CLI and MCP (xpo-1a5504)
  • xpo list --json outputs jsonio.ListOutput envelope (xpo-2e3060)
  • xpo show --json outputs jsonio.ShowOutput envelope with children and archived status (xpo-b5d8f0)
  • xpo comments --json outputs jsonio.CommentsOutput envelope (xpo-e43fff)
  • xpo blocked without arguments lists all blocked issues; --json flag for JSON output (xpo-c3f7a0)
  • xpo inbox --json outputs jsonio.InboxOutput envelope (xpo-d31d61)
  • SearchInput component added to Timeline TopBar (xpo-23529e)
  • MenuFilter component for filterable menus; bare prop on Menu for chrome-free mode inside existing containers (xpo-9a7907)
  • Digit keys (0-9) registered in Menu keyboard handler for number-key shortcuts (xpo-9a7907)
  • Composable Menu, MenuItem, SubMenu, MenuDivider, MenuLabel component system with ARIA roles, keyboard navigation via the shortcut registry, safe-triangle diagonal movement, submenu close delay, and sub-menu flyout positioning; ContextMenu and FilterMenu refactored to compose from these primitives (xpo-de8d06)
  • PopoverPanel component for visual chrome in raw-content Popovers (xpo-de8d06)
  • pointInTriangle utility for safe-triangle hover calculations (xpo-de8d06)
  • popover-utils extended with right-start placement for sub-menu flyouts with left-flip and viewport clamping (xpo-de8d06)
  • menu-utils pure functions for index navigation with wrapping/skip and shortcut matching (xpo-de8d06)
  • Text component for consistent typography primitives (xpo-e0334f)
  • Shared Tabs<T> component with [/] keyboard cycling via the shortcut registry; Backlog and My Issues migrated, removing duplicated tab markup (xpo-d47781)
  • Control-priority shortcuts fold into the active view's group in the keyboard help overlay instead of creating separate sections (xpo-d47781)
  • Keyboard system architecture design document (design-docs/keyboard-system.md) (xpo-d47781)
  • Shared SearchInput component with / focus and Esc clear via the shortcut registry; Backlog and Dependencies migrated (xpo-ef6602)
  • Shared IconButton component for toolbar actions with built-in tooltip and active-indicator dot; 6 instances across Backlog, Board, Inbox, MyIssues, and Timeline migrated (xpo-4235c6)
  • Popover refactored to anchor-ref positioning with placement options, 4-edge viewport clamping, and built-in click-outside/Escape dismissal; 17 consumer instances migrated, InlineDropdown rewritten on Popover (xpo-5d1c38)
  • Button component supports forwardRef for ref-based popover anchoring (xpo-5d1c38)
  • Shared FilterButton component bundling IconButton, FilterMenu, f shortcut, and active dot; Backlog, MyIssues, and Inbox migrated (xpo-e16962)
  • xpo spec CLI command for viewing and managing issue specs (xpo-d9d6f2)
  • xpo walkthrough CLI command for viewing and managing issue walkthroughs (xpo-09a820)
  • --with-spec and --with-walkthrough flags on xpo show command and MCP show tool to inline artifact content (xpo-68eb93)
  • xpo artifact --json input/output with jsonio envelope (xpo-43d8c5)
  • xpo start --json input/output with jsonio envelope (xpo-cdd060)
  • xpo merge --json input/output with jsonio envelope (xpo-e19cfd)
  • xpo link --json input/output with jsonio envelope (xpo-885f42)
  • Standardized JSON error output across all CLI --json commands — consistent {"error": ...} envelope on validation and runtime failures (xpo-d1a06b)
  • Heading component for consistent title and heading styling (xpo-250ddf)
  • CountBadge component for issue count badges (xpo-f27a3e)
  • xpo doctor --fix: auto-fixes local state silently, shows change plan for committed files, offers replace/keep/diff for edited managed blocks (xpo-76e2a1)
  • xpo doctor --strict: warnings exit non-zero for CI (xpo-76e2a1)
  • xpo init flags: --yes, --prefix, --agents, --force for non-interactive/CI use (xpo-76e2a1)
  • Version stamping in managed block markers — integration version derived from blocks, not stored in config (xpo-76e2a1)
  • Re-init intelligence: up-to-date one-liner, stale version diffs, downgrade protection, edited-block detection (xpo-76e2a1)
  • charmbracelet/huh dependency for multi-select and select prompts (xpo-76e2a1)
  • 53 new tests: managed blocks, health checks, doctor scenarios, version comparison (xpo-76e2a1)
  • xpo doctor reports issues.db lines whose created_at does not strictly increase in file order (report-only) (xpo-e9a2eb)

Changed

  • make release is replaced by make release-prep VERSION=x.y.z, which bumps CLIVersion and dates the CHANGELOG section in the release issue's worktree without committing, and make release-tag VERSION=x.y.z, which tags on main after the merge after checking the branch, a clean tree and the version, and does not push. It uses perl -pi instead of GNU-only sed -i, so it works on macOS (xpo-e60c26)
  • xpo start (CLI and MCP) is idempotent when run from inside the issue's existing worktree. It returns success with the same branch and worktree path, moves the issue to DOING if needed, and doesn't recreate the worktree or re-run worktree_setup. This lets an orchestrator start an issue and then let the agent inside the worktree call start again. --force still tears down and recreates the worktree (xpo-7325f7)
  • Web UI writes now go through TanStack Query mutations (api/mutations.ts) instead of components calling the API and then onRefresh(). Status, estimate, priority, label, assignee and cycle edits, plus Board and Backlog drags, show up immediately and roll back with a "Couldn't save: " toast if the server rejects them. A status cascade to sub-issues refetches issues once instead of once per child. Creating an issue, starting work, deleting, commenting and label edits now toast on failure instead of failing silently. The onRefresh, patchIssue and onConfigLabelsChange props are gone (xpo-6e757d)
  • Web UI per-view data (cycles, cycle progress, dashboard metrics and activity, timeline, instances, and issue history, artifacts, worktree, commits, diffs and mergeability) now loads through shared TanStack Query hooks: requests are deduplicated across views, and cycles, dashboard and timeline refresh on server events instead of going stale until remounted. MergeView spins its Refresh icon while refreshing (at least one full turn on a click), then briefly shows a check, or an alert plus a "Couldn't refresh: " toast; a failed uncommitted-diff poll shows "Couldn't refresh, showing changes as of HH:MM" beside it instead of silently showing outdated changes (xpo-ec0480)
  • Web UI restructured from a single App component into Layout → Sidebar + ViewRouter → views. Views no longer receive props: they read issues, config, inbox and user from TanStack Query hooks, whose cache is refreshed by server events, and own their own tab, filter and sort state. Saved filters, sort and tabs keep their existing localStorage keys. Filter chips are rendered into the status bar by each view, the sidebar, routes, titles and g shortcuts all come from one VIEWS registry, and the Inbox list now uses ViewContainer (xpo-de3808)
  • Issue detail prev/next (j/k) now walks the order of the view the issue was opened from (Issues, Board, My Issues or a cycle), instead of always the Issues list; deep links and other views use the default sorted order (xpo-de3808)
  • Backlog rows and status headers no longer draw separator lines and have rounded backgrounds (xpo-c26f7b)
  • All TopBar views except Inbox render through a shared ViewContainer shell (TopBar, optional header bars, and a full-width content area with the scrollbar at the view's right edge; width-limited views center a max-w-7xl or issue-detail column inside it). Scroll areas that change while visible (views, sidebars, MergeView panes and diffs, pickers, the command palette, scrolling menus) use a new scroll-stable utility: a forced, transparent track with a symmetric both-edges gutter, so content no longer shifts when a scrollbar appears in Chrome or Safari. The global .overflow-y-auto { overflow-y: scroll } override is gone, so overflow-y-auto behaves normally again (xpo-c26f7b)
  • Links are bidirectional: A blocks B also means B blocked_by A, whichever issue stores it. show (MCP, CLI, API, web) lists derived inverse links with derived: true. start and xpo drive honour blockers stored on the other issue. link reports a relationship already stored from the other side as existing. update { links } treats the list as the issue's full relationship set, so a link left out is removed from whichever side stores it (xpo-9d6609)
  • Assignees are principal-first: an issue is always assigned to a person, never an agent. Old assignments to agent identities (Claude Code <agent@host.local>) resolve to the person the agent worked for, at read time, with no event-log rewrite. Avatars and assignee names always show the person; agent-written comments read "Claude Code on behalf of " (xpo-35fc16)
  • start assigns the issue to the person the starter works for (the MCP/drive principal, or the CLI user); an issue held by someone else keeps its assignee unless --force (xpo-35fc16)
  • xpo drive records its events on behalf of the git-config user (xpo-35fc16)
  • CLI timeline and inbox lead with the person: "Nicolas (via Claude Code)" (xpo-35fc16)
  • xpo add --json now uses jsonio.AddInput directly (dropping inputs shim), calls ValidateCreatePayload for full validation parity with MCP, and emits jsonio.AddOutput JSON instead of plain text (xpo-3847a5)
  • xpo update --json now uses jsonio.UpdateInput directly, calls ValidateUpdatePayload for full validation parity with MCP, and emits jsonio.UpdateOutput JSON instead of plain text; done, planned, and blocked <id> gain --json output (xpo-199282)
  • xpo rationale --json now outputs jsonio.RationaleOutput envelope instead of raw search result (xpo-11e559)
  • Breaking: xpo history --json now outputs a single JSON envelope with rich timeline entries instead of JSONL; MCP history tool output upgraded to include payload, on_behalf_of, and source fields (xpo-f56ce4)
  • internal/inputs/ reduced to a backward-compat re-export shim over internal/jsonio/; will be removed once CLI commands are migrated (xpo-1a5504)
  • StatusPicker, PriorityPicker, EstimatePicker, LabelPicker rewritten as Menu + MenuItem compositions, eliminating duplicated keyboard handling, focus management, and CheckIcon definitions (xpo-9a7907)
  • Popover is now positioning-only; visual chrome moved to PopoverPanel or Menu (xpo-de8d06)
  • ContextMenu decomposed from monolithic 470-line component into Menu/MenuItem/SubMenu composition (xpo-de8d06)
  • FilterMenu rebuilt with Menu/SubMenu; positioning fixed with useLayoutEffect + queueMicrotask (xpo-de8d06, xpo-b5a70c)
  • Backlog and Board view option menus refactored from inline JSX to Menu compositions (xpo-de8d06)
  • Menu items use compact sizing (py-1.5) with three-tier visual hierarchy: secondary text, hover highlight, active highlight (xpo-de8d06)
  • xpo init is now a unified wizard: prefix prompt → agent multi-select → terraform-style change plan → confirm → apply. Replaces the separate init mcp and init skill subcommands (xpo-76e2a1)
  • Agent instruction files use managed blocks (<!-- xpo:begin VERSION sha256:HASH -->) for safe refresh — only the xpo-owned section is replaced, user content is never touched (xpo-76e2a1)
  • Prefix stored without trailing dash (pay not pay-); separator added at point of use. Existing configs normalized on load (xpo-76e2a1)
  • xpo doctor reorganized into four sections: xpo, Project, Integrations, This machine (xpo-76e2a1)
  • Branded welcome message when running xpo or any command outside a project directory (xpo-76e2a1)
  • Tagline defined once (ui.Tagline) and used consistently across all CLI output (xpo-76e2a1)
  • comments command refactored from raw storage.ReadEvents() to Client.GetIssue() (xpo-e43fff)
  • xpo comment --json refactored to use shared MCP CommentToolInput schema via jsonio, with full validation parity (xpo-aa4529)
  • All CLI --json commands now resolve input IDs (short IDs, bare hashes) to canonical issue IDs before processing (xpo-db6d52)
  • Pulse types moved from internal/pulse to jsonio package; velocity calculation no longer double-counts issues (xpo-b18d99)
  • Cosmetic consistency pass for --json flags across all CLI commands — uniform flag descriptions and help text (xpo-66b8af)

Security

  • xpo board no longer listens on all network interfaces. It binds 127.0.0.1 by default (--host overrides it and logs a warning for non-loopback addresses) and rejects requests whose Host header isn't a loopback name or the --host value, blocking DNS rebinding. Previously anyone who could reach the port had unauthenticated read/write access, and in remote mode could use the user's bearer token through the proxy. The README documents ssh -L as the supported way to view a board on another machine. The Vite dev proxy targets 127.0.0.1 (xpo-809ad0)

Fixed

  • Event timestamps in issues.db strictly increase in file order again. merge stamped the DONE update about 100µs before its MERGE event, and cascading or link updates gave all their events one shared time, so xpo doctor reported out-of-order and tied timestamps after every merge. Storage now stamps each appended event, and each event in a rewritten web-UI tail, at least 1ns after the line before it, so this holds for every writer. merge and update also build their events in time order. This repo's 28 affected lines were re-timed once (xpo-e669d4)
  • Agent-written comments in the merge view's Conversation tab show the principal's avatar and "Claude Code on behalf of " instead of the raw agent identity; xpo comments and the TUI details pane show " (via )". Comments now carry on_behalf_of in the HTTP API, remote transport, CLI --json and MCP show output (xpo-a103c3)
  • Dropping an issue onto a Backlog row with no status group no longer sends status: null (xpo-6e757d)
  • MergeView no longer re-renders the whole diff on every 3s uncommitted-diff poll or Refresh. Unchanged polls and refreshes no longer block the page (before: ~190–200ms per poll on a 1,000-line diff), and a change to one file re-renders only that file's card (xpo-5839aa)
  • MergeView's All changes / Uncommitted toggle no longer shifts sideways by a couple of pixels when switching, because each label reserves its bold width (xpo-e64b2b)
  • Switching Backlog tabs now loads that tab's saved filters instead of carrying over the previous tab's filters and overwriting the new tab's saved ones (xpo-0c0556)
  • Clicking a label on the Labels page no longer briefly sets an invalid #/backlog URL, and the applied label filter is saved like a manually chosen one (xpo-787e7b)
  • Issue activity timeline no longer logs a React missing-key warning for updates that change several fields (xpo-1657f1)
  • MergeView diff panes no longer jump sideways when expanding or collapsing diffs; scrolling menus (Assignee, Cycle, filter submenus) now actually get the stable gutter their undefined scrollbar-gutter-both class intended (xpo-c26f7b)
  • Backlog right-click context menu no longer stops working after the list was empty (at load, or after a search with no matches was cleared) (xpo-164038)
  • merge with strategy: ff no longer adds a separate xpo: merge <id> commit: the issue database and artifacts are amended into the fast-forwarded tip, and an explicit commit_message rewords it. The merge tool, the mcp-tools reference and skill step 9 now tell agents to omit strategy unless the user asks for one. Re-run xpo init in other projects to refresh installed skill copies (xpo-c16e28)
  • Issue detail sidebar Cycle popover now uses the shared CyclePicker (also used by the context menu), gaining a filter input, Cycle N (status) labels and keyboard navigation (xpo-f16b80)
  • Context Menu Cycle submenu now has a filter input and uses the shared Menu/MenuItem styling and keyboard navigation; cycles are labelled Cycle N (status) so the checkmark only marks the selected cycle (xpo-70e66a)
  • Context Menu Assignee submenu now autofocuses its filter input and uses the shared Menu/MenuItem styling and keyboard navigation, matching Status, Priority and Labels (xpo-690930)
  • Every xpo command stalled ~5 s (and printed a stray ESC ]11;?) under a pseudo-terminal that doesn't answer terminal queries: bubbletea v1's package init queried the background colour. Migrated the interactive prompts to charm.land/huh/v2, which removes bubbletea v1 (xpo-d053b5)
  • xpo init and xpo doctor now detect CLAUDE.md/AGENTS.md instruction blocks that differ from the current template even when the version is unchanged; init refreshes them and doctor reports "Instructions out of date" (fixed by --fix) (xpo-757732)
  • Backlog: ghost parents (faded parent rows shown above sub-issues in another status group) can be collapsed and expanded by click, ←/→ and Expand/Collapse all; the collapsed state is shared with the real parent row. While a search or filter is active, saved collapsed state is ignored so matches are never hidden, and collapses made during the search are temporary (xpo-0cbecc)
  • Issue detail Activity avatars show the actor's gravatar or initials instead of the dashed placeholder; avatar and name share an "on behalf of …" tooltip when an agent acted for someone (xpo-53ed0e)
  • Backlog: parent issues whose sub-issues are all hidden from the current view no longer show a non-clickable collapse chevron; the slot is left blank, like issues without sub-issues (xpo-e7c400)
  • Agents no longer call merge on an uncommitted worktree. Step 9 of the generated xpo skill now requires a commit (it is a gate and an explicit step, with an optional changelog entry first), and the self-contradicting "do not commit" sentence is gone. The merge tool description says a committed worktree is required. Generated skill files end with an xpo:skill version/hash marker: xpo doctor reports "Skill out of date" when a skill differs from the current template, even at the same CLI version, and reports local edits separately. xpo init / doctor --fix refresh out-of-date skills and offer replace/keep/diff for edited ones instead of silently overwriting them (xpo-e88f1b)
  • The instance registry no longer treats every process as dead on Windows, which emptied the web UI instance list and pruned live peers on Register. Liveness is now checked per platform: Signal(0) on Unix (with EPERM counted as alive) and OpenProcess + GetExitCodeProcess on Windows. make lint also runs GOOS=windows go vet ./... (xpo-60dc3c)
  • update { links } now stores dependencies with source_id set to the issue's ID and rejects self-links and duplicate (target, kind) pairs; links: [] clears all links instead of failing with "no fields set". Existing rows with an empty source_id are attributed to their owning issue on read, so link behaves correctly for them. The link tool, the add/update links schemas and the CLI link help now state that only blocked_by gates start (xpo-080fc4)
  • Concurrent writers (several MCP servers, the CLI, the web server) no longer lose or corrupt events in issues.db: append, collapse and archive all hold an exclusive .xpo/events.lock, and each event line is written in a single call. xpo archive re-reads the log under the lock, keeps events written since the preview, and skips issues reopened since then, reporting them. Readers ignore a torn final line from an in-progress append (xpo-d6288f)
  • xpo merge no longer fails with a phantom "resolve the conflict" error when xpo runs from inside the issue's worktree: every merge git call now targets the hub checkout. MERGE/DONE events are recorded only after the code commit succeeds, so a failed merge (hook, conflict, nothing to commit) leaves the issue in its old status with nothing staged and never rewrites issues.db. Errors carry git's real output, and the conflict advice appears only for real conflicts (xpo-9fef67)
  • Web UI edit collapsing no longer merges edits across actors or past a newer event on the same issue, moves the merged event to the end so timestamps follow file order, keeps edited comments at their original time, and prunes no-op edits against running state; this repo's issues.db timestamps were repaired once so time-ordered and file-ordered replay agree (xpo-e9a2eb)
  • Popover visibility in production builds: anchor ref not attached when useLayoutEffect fires; deferred via queueMicrotask (xpo-b5a70c)
  • FilterMenu arrow-left key not registered for submenu entry (xpo-e16962)
  • FilterMenu submenu options don't scroll into view on keyboard navigation (xpo-e16962)
  • TopBar center slot now stays truly centered regardless of left/right content width changes (xpo-c078b7)
  • MergeView "Files changed" badge now reflects the active scope — shows uncommitted file count when viewing uncommitted changes instead of always showing 0 (xpo-54a423)
  • "All Changes" view falls back to uncommitted diffs when there are no commits yet, instead of showing an empty pane (xpo-54a423)
  • Scope selector ("All changes" / "Uncommitted") promoted from file-tree sidebar to the tab bar for better visibility (xpo-54a423)
  • Issues.db validation in xpo doctor with line-number reporting for invalid entries (xpo-76e2a1)
  • comment --json now accepts MCP CommentToolInput payload format (xpo-eb0d61)
  • worktree_setup hook output captured instead of leaking to stdout (xpo-7abb38)

Removed

  • BREAKING: branch mode is removed; worktrees are the only way to work on an issue (xpo-863802). xpo start always creates (or reuses) a worktree under .xpo/worktrees/, and xpo merge always merges from the hub checkout and never runs git checkout. Removed:

    • xpo start --mode and xpo merge --no-wt, which now fail with "unknown flag".
    • The mode field of the MCP start tool and xpo start --json. Passing it is now rejected as an unknown field.
    • The worktrees config key. worktrees: false prints a warning and is ignored; worktrees: true is a silent no-op, and xpo init no longer writes it.
    • The squash/ff issues.db union-merge that only branch mode needed.

    Migrating: before upgrading, merge or park any issue you started in branch mode, and check out the default branch in the primary checkout. xpo merge refuses to run while the hub is on another branch. xpo start refuses an issue whose branch is checked out in the hub, and tells you to switch the hub back first. Branches without a worktree still merge normally from the hub.

  • Unused PendingEventsPanel component (xpo-c26f7b)

  • "Agent Identity" instructions telling agents to set themselves as assignee, from CLAUDE.md/AGENTS.md/GEMINI.md, the xpo skill and the generated templates (xpo-35fc16)

  • xpo init mcp and xpo init skill subcommands — absorbed into unified xpo init (xpo-76e2a1)

  • internal/inputs backward-compat re-export shim — all CLI commands now use jsonio directly (xpo-aebfc5)