[codex] make the dashboard Work-first - #82
Merged
Merged
Conversation
The dashboard could steer work it had not started: prompt a ticket's panes, abort them, annotate them. It could not create the team. Meanwhile `muxa work up` could create the team but had no presence in the UI. This closes both halves. `POST /api/work-control/up` runs the pipeline, delegating to the `muxa` binary rather than reimplementing it. The launcher and the managed-tmux registry live in the CLI crate, which depends on this one, so calling them in-process would mean inverting that dependency or keeping a second implementation alive — and this library deliberately holds a boundary the move would have breached: no clap, no anyhow, typed errors only. One implementation behind a subprocess beats two behind none. It is gated on `[dashboard] allow_work_start` *in addition to* the control token. Every other write route steers a process the operator already started; this one starts new ones with permissions bypassed. That is a different kind of authority, so it gets its own grant, the way `[ask]` and `[collaboration]` do. Off by default. `work-control/prompt` now also accepts skill/body/context and runs them through `muxa::request::compose` — the same composer behind `muxa_call_peer` and `muxa work up`. A `/name` template registered once now expands from all three front doors. Literal `text` stays as the escape hatch, and a request carrying none of the four is refused rather than sending an empty line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The dashboard asked an operator to type a title the ticket resolver had just fetched — two sources of truth for the same window, drifting from the moment either was edited. A work item created through `/api/work-control/up` now takes its title from the resolved ticket, but only when the store holds no record for that window. A title someone typed is theirs and a later run must not overwrite it, which is the same rule that keeps the pipeline from touching a pane it did not create. The response reports whether anything was written, so the UI never has to guess. It seeds here rather than in the CLI because `WorkStore` rewrites the whole file on every save: a second writer outside the daemon's mutex would drop whatever the dashboard held in memory. The consequence is that a CLI-initiated `muxa work up` still leaves the title empty — closing that needs an IPC route, not a second writer. The keying and the overwrite rule are split into `work_key_for_window` and `already_annotated` so both are tested without a live pane scan: a window id alone is not a key (the same `@3` exists on every tmux server), and an existing record blocks seeding for its own window only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The dashboard has a stage axis the CLI never had — queued, in_progress, review, blocked, done — so the same work item read as "two agents in a window" on one surface and "waiting on review" on the other. `muxa work list` and `muxa work show` now print `stage=` when someone has set one. Reading it needs an identity the CLI did not carry: the store keys on (host, socket, session_id, window_id), while `WorkInfo` had only the mutable session *name*. `session_id` was already parsed and thrown away, so it is now kept, and the socket basename is asked of tmux rather than read from `$TMUX` — that way it is right outside a pane and under MUXA_TMUX_SOCKET scoping too. Read-only on purpose. `WorkStore` rewrites the whole file on save, so a second writer outside muxad's mutex would drop records the dashboard holds in memory. `stage=auto` prints nothing: auto means nobody has said anything, and a column that is always there says less than one that appears when it has something to report. Verified end to end against an isolated tmux server and an isolated XDG_DATA_HOME: a matching record shows `stage=review`, and changing the socket in that record makes it stop matching — the same `@2` on another server is not this work item. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The endpoint landed without a way to reach it from a browser. The work board header now carries a work-id field, an optional instruction, and a "start work" button; the button reports what happened (`2 started`, `2 prompted`, or `already staffed`) and refreshes the board. Wiring the UI turned up a real defect in the gate. `controlFetch` reads 401/403 as "your token is bad" and flips the whole dashboard to read-only, so refusing a disabled capability with 403 would have told the operator their PAT was broken. The refusal is now 501 — the same status the backend capability checks already use for "this deployment cannot do that" — and the test says why so it does not drift back. It also says the button is slow on purpose: resolving a ticket spends a headless agent turn, so the button disables itself and reads "starting…" rather than looking dead. Docs: the `allow_work_start` switch in config.example.toml, the endpoint and its reasoning in DASHBOARD.md, and a bidirectional section in PIPELINE.md/.ko.md — the board can start a pipeline, and the pipeline's ticket title and the board's workflow stage now cross to the other surface. Verified end to end against an isolated daemon, tmux server, and XDG_DATA_HOME: the served HTML carries the form; a first call resolves the ticket, launches two panes, and reports `seeded_metadata: true`; a second call launches nothing, prompts both, and seeds nothing; setting `stage=review` from the dashboard makes `muxa work list` print it for that window and nothing for the window with no record. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
What changed
Workdomain separate from external issues, runs, agents, and tmux topology.muxa dashboardinto a Work-first CLI board while retaining exact run/pane controls.Why
The previous dashboard treated tmux windows and agents as tickets, which blurred durable work, provider issues, and transient execution. This change makes Work the operator-facing unit while preserving session/window/pane topology as an implementation detail that can still be expanded and controlled.
Impact
Self-review fixes
Validation
cargo fmt --all -- --checkcargo test --workspace(718 library passed, 1 ignored; 705 CLI passed; 12 E2E passed; 70 daemon passed)cargo clippy --workspace --all-targets --all-features -- -D warningsnode --test crates/muxa/tests-js/*.test.mjs(6 passed)