Commit Graph
3 Commits
Author SHA1 Message Date
Miguel Palhas 003e3839bc feat(maestro): carry a blocked-work indicator on Session (#83)
Session.blocked_work is Option<InTheWay>, refreshed by the daemon's
watcher; None for worktree-less sessions and until first probed.
Defaults on the wire so older payloads stay forward-compatible.
2026-08-24 12:58:16 +01:00
Miguel Palhas e9368bb242 fix(maestro): fourth-review findings across daemon, tui and web
The existing-branch picker could never succeed in either client: both
treated the main checkout's HEAD as available, and since that is usually
`main` it became the default pick — git then refuses the second checkout.
A TUI test had baked the bug in by asserting the selection lands on the
`is_head` entry. Both pickers now treat "checked out anywhere" as held.

Worktree paths were still not injective. Flattening `/` and `.` to `-`
made `feat/x`, `feat.x` and `feat-x` share one directory, and the
override root keyed on the repo basename alone, so `~/a/maestro` and
`~/b/maestro` collided. A branch name is already a valid relative path —
git stores it as refs/heads/feat/example — so it is used verbatim and
nests under `worktrees/feat/`; the override root gains a digest of the
full repo path. Git forbids holding both `feat` and `feat/x`, so the
nesting cannot collide for valid refs.

Also:

- Recovered sessions dropped their worktree association, so after a
  restart a kill cleaned up nothing and the leftover directory blocked
  that branch forever. It is persisted on Session now.
- The nesting guard compared git's resolved paths against an unresolved
  one, so it silently passed on any symlinked repo path — and that guard
  is what stands between a kill and an inner tree's uncommitted work.
- Draft loads ran on the TUI event loop: /api/projects on a cold cache
  walks all of $HOME, freezing keys, frames and attached panes for up to
  the request timeout. They are detached tasks posting to a channel now.
- Fast-forward in the web client created the session and never mounted
  it, so `N` looked like a dead key while the TUI attached.
- The fast-forward fallback seed forced the worktree toggle off, against
  both a normal draft and the TUI.
- The projects background refresh honoured the TTL, so two of every three
  ticks were no-ops and requests paid for the rescan instead.
2026-08-24 00:10:35 +01:00
Miguel PalhasandClaude Opus 5 30bd9d9042 test(maestro): one mock daemon instead of three answers
ci / web (pull_request) Successful in 29s
ci / rust (pull_request) Successful in 3m17s
ci / web (push) Successful in 30s
ci / rust (push) Successful in 2m39s
There were three answers to "how do I test against a daemon", and the CLI
— the thinnest client — had none, so its formatting and error handling
were exercised by compiling and forking the real binary plus tmux.

Adds crates/maestro-testing with a MockDaemon that serves the real /api/*
surface from maestro-protocol types, backed by a Vec<Session>. It replaces
two ad-hoc stubs: maestro-web's, which returned [{"id":"session-1"}] —
not a valid Session — and maestro-tui's scripted socket with a hardcoded
timeline.

maestro-cli gets its first tests against a daemon: six cases covering the
catalog, that DaemonInfo carries the version and presets its old private
copy dropped, that a structured failure surfaces the daemon's own message,
and that connection-refused stays distinguishable from an HTTP rejection.

maestro-web's proxy suite gains an end-to-end check that an upstream
rejection reaches the browser as a 4502 close carrying the daemon's
message — upstream_close_reason had only unit tests.

e2e keeps the tmux-shaped tests. cli_list_reads_from_daemon still runs the
shipped binary as a subprocess but now against MockDaemon, and
tui_integration.rs is deleted: the real daemon's /api/events contract is
covered by maestro-daemon/tests/local_api.rs and the client reducer by
maestro-tui/tests/catalog_ws.rs. The e2e suite drops from ~32s to ~20s.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 14:51:22 +01:00