Conversation
Adds an engineering harness built on okena's projects, worktrees and multiplexing: five views (Projects, Tasks, Agents, Specs, Knowledge) reachable from a HARNESS section in the sidebar and rendered full-width in the main area. Tasks. `okena-tasks` is a GPUI-free provider abstraction with a Linear implementation over GraphQL; the daemon owns the credential and every network call, so a thin client never holds a task-manager token. Task lists are deliberately not persisted — a stale issue list is worse than a slow one because its staleness is invisible. Tasks render in swimlanes with an epic > feature > story > task hierarchy, collapsible, and "Start work" opens a dialog to pick projects, branch name and agent. A task spanning several projects gets a worktree in each plus an agent session rooted above them. Agents. Running coding agents are detected across managed terminals and linked back to their task. okena's own MCP server (`okena mcp`) is injected into each agent's config automatically, so an agent can ask what task it is on and report status and produced assets back without the user configuring anything. Identity comes from $OKENA_TERMINAL_ID rather than a forgeable parameter. Specs. A convention-based OpenSpec integration (https://github.com/Fission-AI/OpenSpec): okena reads the openspec/ tree straight off disk from a configured spec repository, so browsing works whether or not the CLI is installed. "New change" scaffolds a change directory and starts an agent briefed on the conventions. Document reads are resolved and checked against the repository root — the action is reachable by any client and by agents over MCP. Also: a terminal-kind picker when splitting or adding a tab (plain shell, or a coding agent), harness settings in the settings UI, and spec/task sessions grouped apart from repos in the sidebar. Fixes two pre-existing bugs found on the way: custom shells with a program and args were not forwarded to the session backend, so an agent never spawned; and an okena-files test raced on a shared temp path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Engineering harness: Linear tasks, agent sessions, okena MCP, OpenSpec
Brings in 56 commits from contember/okena on top of the engineering harness. Conflicts resolved: - keybindings/descriptions: union of the action imports — upstream's DetachTerminal and ExportTerminalBuffer alongside the harness's ShowHarness. - window/handlers: took upstream. It moved terminal split/close out of OverlayManagerEvent and removed both variants, so the harness's only edit there (a shell_type field on the split arm) belonged to an arm that no longer exists. The field is re-applied at upstream's new call site in overlay_manager. - sidebar: took upstream's redesigned header wholesale — it replaced the inline attention and sort-order toggles with the Create/View context menus, and consolidated render_header and render_projects_header into render_overview_row. The harness's PROJECTS/AGENTS tabs are re-applied as their own row (render_list_tabs) rather than wedged back into a header that no longer has a place for them. - tabs: took upstream's more_button refactor and rustfmt shape, then re-applied the terminal-kind picker on top: right-click on split and new-tab to choose a shell or agent, and shell_type on both split dispatches. Also adapted to two upstream signature changes: ProjectData dropped the retired is_remote flag, and hook_input_row dropped a parameter. 2086 tests pass, clippy clean, okena-daemon still gpui-free. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Consolidates everything an agent session is and how it is presented, and gives the harness views a single consistent header. One agent-session panel. The sidebar beside a session's terminal and the cards in the Agents overview were separately written and had already started to diverge; they are now one `AgentSessionPanel` rendered at two densities. Session kinds are open — task work, spec writing, and now free-form sessions the user configures — and the panel asks the kind for its own headings and empty states rather than special-casing them. A third way to start one. "New agent" in the sidebar header opens a dialog for the goal, projects, working directory and agent. One selected project runs the agent inside it; several run it above them, the same reasoning a multi-project task already used. Proper teardown. Deleting a workspace opened with a task lookup, so a spec session — which has no task — failed outright and stayed in the list. The selection is now kind-aware and extracted into `teardown_plan` with tests: a task session takes its worktrees, a spec or free-form session takes nothing on disk (they run in repositories that must survive), and a worktree is refused so deleting one checkout cannot delete its siblings. Adds "Stop agent", which ends the process without destroying the checkouts it was working in. One header per view. The harness views share a toolbar; the sidebar's stacked tabs-plus-overview rows became a single row with a list selector, an overview button, a "..." menu for ordering and layout, and one "+" on both tabs. Projects gains fuzzy search and an in-flight filter; Tasks gains a settings page for provider credentials, which used to exist only in the view. The session-info panel now sits beside the terminal when a column is wide enough and takes the column when it is not, rather than always replacing it. Fixes found on the way: - The agents-overview switch replaced *every* project's terminal with an empty panel: the body swap had no agent-session guard. The guard now lives inside the reconcile rule, where a caller cannot drop it. - Ten sidebar paths focused a project without leaving the harness view, so the click looked like it did nothing. They all route through one helper now. - The header menu was positioned with window-absolute bounds inside a positioned ancestor, and clipped by the sidebar; it is deferred and anchored to the window. Removes the Agents harness view, whose job the agents overview does better, and the resizable sidebar that duplicated the column's own toggle. 2136 tests pass, clippy clean, okena-daemon still gpui-free. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TgthGtVf8d979EukNwormt
Unify agent sessions, and one header per harness view
OpenSpec now plans across repositories with stores (https://openspec.dev/docs/stores): a machine registry of store checkouts, `store:` pointers and `references:` in openspec/config.yaml, and a machine defaultStore. The Specs view knew only one configured repository and missed OpenSpec's real layout, so capabilities under specs/<id>/spec.md and changes scaffolded by `openspec new change` never showed. - okena-openspec: a GPUI-free crate that reads and writes the store registry, store identity, project config and global config the way OpenSpec 1.13 does, including its registry lock and diagnostic codes - the Specs view lists every discovered root (stores, projects, folders) with its health, references and pointers, and drafts a change into the chosen root with the CLI's .openspec.yaml - Settings → Specs registers, creates and unregisters stores, sets the machine default store, and configures discovery and OpenSpec's directories - the daemon runs store changes off the workspace lock: setup commits with git and registry writes may wait on a lock the CLI holds - the legacy harness.spec_repo keeps working as a folder root Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MLwRxb2n448sSTvoUq8Yck
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MLwRxb2n448sSTvoUq8Yck
feat(specs): support OpenSpec stores in the harness
Tasks: list and detail, not two lanes. The board's Todo and In progress columns forced every row into half the width — which a task's key, kind, state and title do not fit into — and left one column idle whenever the work was all in one state. There is now a single list on the left, with In progress above Todo and each foldable, and the selected task's detail on the right. The existing divider still resizes the split. Start work and Open session moved from every row into the detail pane. A row's worth of buttons repeated down a list is noise, and a task you are deciding about is one you have selected. A task with a live session shows what okena has running for it: the agent, its status, the checkouts it was handed and what it produced — read through the shared `AgentSessionInfo`, so it says exactly what the session's own panel says rather than a second derivation of it. `TaskLinks` now tracks the session separately from `open_target`, which falls back to a worktree; only a session has session facts. One worktree card. A worktree turned up in a project's lane, a task's detail and an agent session's panel, and each had grown its own idea of what to say about one. `WorktreeSummary` + `render_worktree_card` is the single answer: the repo it belongs to, its branch, what changed, whether that has been pushed, and where the review stands. A Diff button appears only when something changed — a button that opens an empty diff is worse than no button. Push state distinguishes three cases, not two. Git reports "never pushed" and "pushed, nothing outstanding" as `None` and `Some(0)`, which collapse into the same chip if you are not careful; the difference is whether the work exists anywhere but this machine. Fixes a wire gap found while testing the card: `ApiWorktreeMetadata` carried only the parent id and colour, so a thin client rebuilt every worktree with an empty `branch_name` and could never show a branch before the first git poll. The branch now crosses the wire, mapped in both client paths, with a test. The paths deliberately stay daemon-side: a client has no filesystem to resolve them against. 2141 tests pass, clippy clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TgthGtVf8d979EukNwormt
Tasks: list and detail pane, and one worktree card everywhere
# Conflicts: # Cargo.lock # Cargo.toml # crates/okena-app-core/src/workspace/actions/execute/mod.rs # crates/okena-daemon-core/src/command_loop.rs
Sync upstream contember/okena into the fork
Adds task creation to the harness, and a way to hand a breakdown to an
agent rather than typing children one at a time.
Provider writes. `TaskProvider` gains `create_task`, `list_children` and
`list_containers`, each defaulting to `Unsupported` so a backend that
cannot do something says so rather than failing oddly — the seam Azure
DevOps slots into. `TaskDraft` is its own type: a draft has no id, key,
url or state, and a half-filled `Task` would put five meaningless fields
in front of every caller.
Linear implements all three. okena's breakdown — epic, feature, story,
task, defect — is a shape no provider has natively, so it travels as a
label, the same signal `TaskKind::from_labels` already reads back.
Creating an epic resolves the team's existing label first and only
creates one when no alias matches, so a team that already says "Bug"
does not end up with "Defect" beside it. A sub-task inherits its
parent's team, since Linear has no cross-team parenting.
Breaking down with an agent. Two MCP tools — `okena_list_subtasks` and
`okena_create_subtask` — both defaulting to the session's own task, so
an agent need not discover an id first. "Break down with agent" starts a
free-form session briefed to read the existing children and write new
ones back. It gets no worktrees: it is not doing the work, it is
deciding what the work is.
Such a session carries both markers — `custom_session` and `task_ref` —
and `TaskLinks` keeps helpers apart from the work session. Counting a
helper as work would move the task into "in progress" the moment you
asked for a breakdown.
The task detail now shows the hierarchy. PARENT and SUB-TASKS are
navigable rows, so an epic → feature → story chain can be walked without
leaving the pane. Children are fetched rather than read off the board:
the board is the user's own queue, and a task's children are often
assigned to somebody else and would simply be missing. Selection
resolves through everything okena knows, or clicking such a child would
land on an empty pane.
The two sections describing agents became one AGENTS list — the same
question ("what is running for this task?") took two places to answer,
and only the work session had any status.
Fixes, from watching it used: clicking "Break down" gave no feedback for
the several seconds a session takes to start and then switched views, so
repeated clicks made four agents on one task. It is one control with
four states now — break down, refine, starting, open — guarded against a
second start, and it no longer steals the view.
622 unit tests pass, clippy clean. The one failure is upstream's
`a_parked_worktree_removal_still_serves_terminal_input`, which fails the
same way on pristine upstream/main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgthGtVf8d979EukNwormt
Author and break down tasks from the harness
A segment can now be disabled and carry a tooltip. The grid's view bar offers a canvas layout before it exists, so the bar keeps its shape when it lands; a disabled segment with no explanation would read as broken, hence the tooltip alongside it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Txh2o42B4jNfM78zAJ5brw
…the grid Projects and agents are now shown the same way. An agent session already kept its context in its own column, behind an info toggle, with a resizable panel beside the terminal. Projects get exactly that: every repo and worktree column has an info panel with its git state (branch, diff, divergence, PR, pipeline), the worktrees open against it, and the agent sessions working its tasks. Which panel a column shows is decided by what the project is, never by the overview it sits in. That makes the harness Projects view redundant — it showed the same facts in lanes of their own, away from the terminals they were about — so it is removed, along with `HarnessSection::Projects`. A view bar above the grid holds the two choices both overviews share: Columns / Stacked (with Canvas shown, disabled, for later) and Info / Terminals. Each overview keeps its own info switch, as it already kept its own orientation; `grid_show_info` reads whichever is on screen, the same way `grid_layout_mode` does, so the bar and the columns cannot disagree. The sidebar's "..." menu gains "Show project info" to match agents. Shared on the way: focusing a project and opening its diff were copied in three panels and now live in `project_nav`; PR/CI chip styling and a session's running/waiting/stopped label are defined once. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Txh2o42B4jNfM78zAJ5brw
Project info beside the terminal, and a view bar over the grid
The task detail printed the provider permalink in full — a long opaque string nobody reads and cannot click. It is now two icon buttons at the top of the pane, beside the key and chips: copy the link, or open it in the browser. The list rows get the open button too, since that is where you usually decide you want the provider's own view. Both stop propagation: they sit on rows that select or open a task, and a button that also does the row's job is the bug this shape invites. Opening goes through `okena_core::process::open_url`, which reaps the opener process, rather than a fifth copy of the platform match that already exists in four crates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TgthGtVf8d979EukNwormt
Sync upstream contember/okena (PDF previews, diff composition fixes)
Task links as buttons, not raw text
ADR-0003 fixes the store layout (kind folders, okena-owned registry, project .okena/knowledge.yaml). The sprint plans the work; backlog 04 and 05 hold the follow-ups this format is built for: handing knowledge to agents at launch, and template-driven prompts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015VGQtZttNnYFXVZnE8tUQP
Adds okena-knowledge: store identity, project config, the per-profile store registry, discovery with diagnostics, the entry tree (docs, skills, agents, templates), and clone/fetch/fast-forward sync plus store setup. okena-git gains current_upstream, fast_forward_to_upstream, origin_url and init/commit helpers for it. okena-core gains the knowledge wire types, a neutral diagnostic type (specs re-exports it unchanged) and shared fs helpers, which okena-openspec now uses instead of private copies. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015VGQtZttNnYFXVZnE8tUQP
Knowledge actions list, read, clone, register, create, fetch and pull stores. They run off the workspace lock, since even a listing runs git per store. KnowledgeDraft opens an agent session in a root, briefed on the store layout. Adds harness.knowledge settings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015VGQtZttNnYFXVZnE8tUQP
Replaces the Knowledge stub with a real view: roots with sync badges, entries grouped by kind with a filter, rendered markdown, fetch/pull, and a "New with agent" form. Settings -> Knowledge clones, adds and creates stores. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015VGQtZttNnYFXVZnE8tUQP
Knowledge stores in the harness
Specs showed documents line by line as plain text and task descriptions as one grey text block, while Knowledge already rendered formatted markdown. The Knowledge renderer moves into a shared harness module so all three views read the same way. Task descriptions are parsed once per task and theme rather than every frame. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GxaQAo1cNPWadFQ7mgr9ok
feat(harness): render spec documents and task descriptions as markdown
The palette refocused the last-focused terminal pane before dispatching. Behind a harness view that pane is alive but not rendered, so GPUI dispatched from the window's outermost element and every WindowView handler (ShowSettings, theme selector, keybindings help) was skipped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMZTGRvZtaKHfpgoisF4iU
Project focus narrows the projects grid; while a harness view replaces the grid the status bar named a project the user wasn't looking at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMZTGRvZtaKHfpgoisF4iU
fix(harness): settings and palette commands behind harness views
Switching the sidebar to PROJECTS cleared the agents-overview flag, and with nothing focused that made `compute_visible_projects` fall back to every project — so the projects grid appeared unbidden. Switching to AGENTS set nothing and left the main area alone. The same gesture did two different things depending on which way you went. The tabs now choose which list you are browsing and nothing else; only the Overview button changes what the grid shows, and `open_overview` already enforces that the two overviews cannot contradict each other. Removing that clear exposed a second bug it had been hiding: with the agents overview on, the projects Overview button lit up as though its own view were on screen. The decision is now a pure `overview_is_showing` covering both overviews, the folder filter and focus. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TgthGtVf8d979EukNwormt
`settings.svg` was a circle with eight straight rays radiating out of it, which is how a sun is drawn. It now has actual teeth: a six-tooth cog outline with a hub, generated geometrically so the teeth are evenly spaced and land on the axes, which keeps it crisp on the pixel grid. Rendered at the 13px the toolbar actually uses rather than trusting it to scale. At the house `stroke-width="1.5"` the hub filled in at 1x; 1.35 keeps it open, and is the closer match to its neighbours anyway — `external-link`, `refresh` and `plus` are 24-viewBox at stroke 2, an effective weight of 0.083 against this icon's 0.084. Shared by the Tasks, Specs and Knowledge toolbars. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TgthGtVf8d979EukNwormt
The bar above the grid held two rows of segmented chips — Columns / Stacked / Canvas and Info / Terminals — and cost a full row of vertical space above every overview to do it. It is gone, along with the wrapper that existed only to stack it above the grid. The same choices are now two buttons on the right of the status bar, each wearing its current setting as its label and opening a small menu on hover. Two buttons rather than one, because the questions are unrelated: rearranging the columns has nothing to do with whether they open on a terminal or on a session's facts. Hover is read as state, not as events. `on_hover` is edge-triggered and the button and its menu fire independently in no fixed order, so acting on each event in turn let a late "left the button" schedule a close that nothing was left to cancel — the menu vanished as you reached it. `sync_grid_menu` reads both flags together instead, with a short settle either side so sweeping past the footer does not flash the menus open. The old bar's visibility rule came free from living inside the non-harness branch of `render.rs`; `shows_grid_controls` now states it, including the harness case the bar never had to handle. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TgthGtVf8d979EukNwormt
Every backend has its own words for the same few shapes: Linear calls a body of work a Project and a time box a Cycle, Azure DevOps calls them an Area Path and an Iteration Path. `Task` now carries a `groups` list normalized to the shape rather than the word, so the harness can offer "filter by iteration" without knowing whose iteration it is. `GroupAxis::Other` carries its name rather than discarding it, which is what makes the next backend additive: an axis this build has never heard of still groups and filters, under the provider's own word for it. Each group keeps the provider's id alongside its name, so filtering survives a rename — a sprint renamed mid-week must not empty the board for anyone who had it selected. Linear asks for team, project and cycle in all three issue queries, so a task's groups do not depend on which call fetched it. Unnamed cycles are named by number, since "Cycle 42" is what they are called on the board; groups with no usable name are dropped rather than rendered blank. Verified against the live API, including that an issue with no project and no cycle returns both keys as null. The field is `serde(default)`, so an older daemon still decodes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TgthGtVf8d979EukNwormt
The list was split Todo / In progress by the provider's workflow state. It is now Active tasks / Tasks, split by whether okena has an agent running on it — a question the provider cannot answer, and the one that matters when deciding what to look at. Helper sessions still do not count, so asking an agent for a breakdown does not make a task look like work in flight. It is a partition: a task appears in one section, never both. Status is no longer implied by which heading a task sits under, so it has to be legible on its own. One `status_name` shared by the chip and the filter (three places rendered a blank state as "—"), a Status facet, and a sort toggle — furthest along first, stable so the provider's recency order survives within each status. A `Waiting` chip carries the signal the old "Needs attention" heading did. The facet bar filters on those groups and labels too: OR within an axis, AND across axes, which is what people expect of a faceted filter. Facets are derived from the loaded tasks rather than fetched, so it cannot offer a choice that empties the list, and an axis every task agrees on is left out — filtering by the one team you work in changes nothing. Selections are pruned on refresh, so a closed sprint does not leave a board reading "you have no work". The New task form now opens in the detail panel instead of replacing the whole view, which took away the list you were adding to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TgthGtVf8d979EukNwormt
…rols Task facets, agent-based sections, and footer grid controls
One control for starting an agent session: what would start, a round button per agent, a configure-first button, a busy state, and the sessions already running with their status and a way in. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvgfoWAvqk36aDpq8VxVFK
Task detail gets two launchers: start work and break down. Each starts an agent in one click, configures first, or shows the sessions already running. They replace the Start work / Open session button, the break down button and the separate AGENTS list. The Start work dialog, New agent dialog, new spec change and knowledge draft forms end in the same launcher row instead of agent chips and a Start button. The offered agents come from one catalog rather than three copies of the list. A one-click start works in the last projects used, else the focused repo, else the only repo; otherwise it opens the dialog. Configuring a breakdown opens the New agent dialog prefilled with its brief, and the goal input there is multiline and scrolls. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CvgfoWAvqk36aDpq8VxVFK
Author
|
Opened against the wrong repository by mistake — closing. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Starting an agent took a different shape everywhere it could happen. This adds one component,
AgentLauncher(okena-ui), and uses it in every place okena starts an agent.The launcher shows:
Where it's used
Details
AGENT_CHOICESare now one catalog (agent_session/launch.rs) holding each agent's command, icon and accent colour. A test keeps it in step with agent detection.OverlayRequest::NewAgentDialog(NewAgentPrefill). Its goal input is now multiline and scrolls, so a long brief no longer draws over the form.Testing
cargo clippyis clean on the touched crates.okena-workspacepersistence tests (legacy worktree registry recovery) fail on macOS. They fail the same way on untouchedmain.🤖 Generated with Claude Code
https://claude.ai/code/session_01CvgfoWAvqk36aDpq8VxVFK