ace-herdr 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cff3b8e4bd486d4b0e4e9a914fbdfe7010ffd1bc2a2be82babd93acf273e875d
4
- data.tar.gz: 1b2e18f9c0faef285ff1e4003ff65b49565227a95ce4d80321cf43108486ff8c
3
+ metadata.gz: 834ebff9d2c1a854b060e5a5cba406efd05975f6f9029f8dcc09a98219e06f2a
4
+ data.tar.gz: 2982b7314cf2afda59ed79485f5141dbd7b83a0860f9fd24f93705da5c1cfa8d
5
5
  SHA512:
6
- metadata.gz: ccc1d8b7fc65614ff262c9a84cf431e0a964057426a49e3cd981a38909c85f22752d31bb3b0924f26ccee69eb74487fa41a46a4fef8ab50fcd607cda64f921bd
7
- data.tar.gz: 0c0f22e240507331336df50b2ebc8966a4b390ae6728b6ccb86b1628eba228d6a82ffa81db19439d2974d124a015bf06a02770558dec1911b40b22a51d2131e6
6
+ metadata.gz: b0a88945a44f495fd8c2da2ccee8ec6bf6dd7f6e39c4c8626983c13ad557ad1316e27bb9d60822d9c124d106c3ad8d57b7beb66decbb1368d7368c6432b67a71
7
+ data.tar.gz: eb3baa9a982cba3250359c380af8112b79ba373d82c5204b6e93269f7efd1f12531381a5559322f3c46e9cb3c0c9d35081f13959867cd53e4a77e512290213ab
data/CHANGELOG.md CHANGED
@@ -7,108 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [0.6.1] - 2026-10-16
11
10
 
12
- ### Fixed
13
-
14
- - Share native immutable session normalization between Inbox and process admission so real Pi path selectors bind their session UUID without weakening ancestry, foreground owner or live-session checks.
15
-
16
- ### Changed
17
-
18
- - Follow the ace-hitl-contract 0.3 request-envelope line (dependency floor).
19
-
20
- ## [0.6.0] - 2026-10-16
21
-
22
- ### Added
23
-
24
- - Expose bounded native observations and hold ordinary Inbox delivery settlement through canonical Assign acceptance. Preserve exact targets and unresolved outcomes without repeat sends or alternate execution records.
25
-
26
- ## [0.5.1] - 2026-10-10
27
-
28
- ### Fixed
29
-
30
- - Require the published producer and consumer patch versions containing the protected context and managed-attempt APIs; exclude incompatible prior dependency graphs.
31
-
32
- ## [0.5.0] - 2026-10-08
33
-
34
- ### Changed
35
-
36
- - Submit Codex answers through the existing Herdr prompt command to the original live terminal. A successful send means submitted, not read; retain unknown sends without automatic resend.
37
- - Remove dedicated Codex app-server selection, message-read observation, signing keys and consumed/superseded reconciliation APIs.
38
- - Keep authenticated original-context admission and actual scoped effects without a second current-state projection.
39
-
40
- ### Fixed
41
-
42
- - Load bounded process/error primitives directly and preserve bounded cleanup of the original process after execution uncertainty.
43
-
44
- ## [0.4.0] - 2026-10-05
45
-
46
- ### Fixed
47
- - Use the shared typed reverse-reference pair at CLI, Inbox and delivery boundaries, refusing malformed persisted targets and noncanonical serialized files before claim or native observation.
48
- - Pin managed delivery to the accepted original native target under the event lock and reject secret-bearing payloads even without a nested message.
49
-
50
- ### Changed
51
-
52
- - Declare the required direct dependencies and minimum producer versions for this coordinated release: `ace-hitl-contract ~> 0.2`, `ace-runtime ~> 0.2`.
53
- - Bind protected control to the canonical per-attempt server/socket identity while retaining the fixed native executable view selected by installation verification.
54
- - Validate and preserve the shared managed delivery envelope at Inbox enqueue, rejecting mismatched scope/digest/correlation and changed replay metadata.
11
+ ## [0.8.0] - 2026-10-20
55
12
 
56
13
  ### Added
57
-
58
- - Add protected Herdr control pinned to the installed server/socket and fixed workspace, creating one fresh bootstrap pane with exact child observation and no default shell.
59
-
60
- - Expose verified native process/session ownership and allow identical signed inbox proof re-verification after settlement without duplicate transitions.
61
-
62
- ## [0.3.2] - 2026-10-04
14
+ - Documented the L7.3 report-delivery surface to a pi overseer (task
15
+ 8xg.t.sbz.6.2) in `docs/usage.md`: pane push via `ace-herdr deliver`
16
+ (idempotent per event id, concurrent deliveries prompt once) and the
17
+ provider-native pi queue client (`ACE_HERDR_PI_QUEUE_CLIENT`), with the
18
+ single-execution guarantee — a delivery during an active overseer turn
19
+ joins the one session run and never starts a second overseer.
63
20
 
64
21
  ### Changed
65
22
 
66
- - Depend on the leaf `ace-hitl-contract` provider vocabulary instead of full HITL orchestration, removing the assignment adapter dependency cycle.
67
-
68
- ## [0.3.1] - 2026-10-04
69
-
70
- ### Fixed
71
- - Replacing a dead prepared pane updates only the recorded pane pointer: a foreign tab's pointer-only identity record stays pointer-only instead of gaining invented `root`/`preset` ownership keys, so the live replacement pane is reused across adapter and process restarts and `ensure_window` at the verified native root still adopts the tab. Records with genuine provenance keep their exact root and preset, and wrong ids, conflicting provenance, corrupt records, and stale pointers still cannot bypass the ownership guards.
72
- - `ace-herdr tab` reports a failed preset materialization (native tab created, later step failed) through the standard CLI error boundary — non-zero exit, actionable message, no success payload, and no stack trace in ordinary mode. The runtime adapter keeps its documented runtime classification and exact-id rollback; this entrypoint never closes tabs.
23
+ - `ace-herdr status` refined to the reviewed `l7-statuses` implementation
24
+ (task 8xg.t.sbz.6.0; extracted from the superseded PR #382): composition
25
+ moved into a `StatusComposer` molecule, new `--workspace <id>` scoping,
26
+ counts recomputed from the nesting with panes referencing vanished
27
+ tabs/workspaces kept visible in a synthetic `(unlinked)` group, and a
28
+ live-but-empty layout printing `No live herdr workspaces.` (exit 0) while
29
+ an unreachable runtime stays an actionable non-zero error.
73
30
 
74
- ## [0.3.0] - 2026-10-02
75
-
76
- ### Added
77
- - Durable `inbox enqueue`, `status`, and `deliver` commands for attempt-linked messages. Enqueue binds the live pane, terminal, agent, and native session; per-event locked claims and saved submission intent prevent duplicate dispatch after a crash. Codex and Pi use exact-session native queues, and idle agents receive a bounded, payload-free Herdr wake after accepted submission.
78
- - Inbox status reports claim generation, target binding, accepted submission receipt, and uncertainty. `reconcile` accepts an operator or supervisor native-outcome receipt file only when its trusted detached signature and event, attempt, generation, digest, and full bound identity match; missing or mismatched proof returns a machine-readable refusal and keeps the event uncertain.
79
- - Installed acceptance covers Codex delivery, signed uncertainty reconciliation, and idle Pi delivery; bounded wake tests cover subprocess stalls and structured errors.
80
-
81
- ### Fixed
82
- - Post-launch subprocess I/O failures terminate the child process group before surfacing, so a failed run can never leave an orphaned native client behind past the deadline.
83
- - Post-launch subprocess I/O failures are classified separately from spawn failures, so a native client that already ran keeps its event uncertain instead of being re-queued for a duplicate submission; and a pane that changed agent is classified as target drift before agent-specific validation can mask it.
84
- - Unreadable or malformed reconciliation receipt files surface as the documented machine-readable refusal while persistence failures still exit as command errors.
85
- - Orphaned claims recover by proof: a claim saved before any submission intent requeues as retryable, while only intent-carrying claims stay uncertain; every spawn-time failure is classified at the process boundary as proven pre-launch (retryable) across both executors; and the inbox CLI reports persistence failures as command failures instead of receipt refusals.
86
- - Target identity drift is classified before agent-specific event id rules, so a bound event whose pane changed agent lands in reconcilable uncertainty instead of looping on a pre-send validation error.
87
- - Native queue bindings require an immutable session id for both Codex and Pi, so an agent restarting under the same name in a reused pane can never inherit an old event; signed replacement targets are validated against the replacement agent's own event-id and payload rules before a supersession re-binds the event.
88
- - Pane probe responses are shape-checked and exit-checked before use, so malformed or failed `herdr pane get` output becomes a classified retryable error instead of stranding a claimed event or delivering against a failed observation; the Pi identity probe rejects non-object JSON the same way.
89
- - The pane identity probe runs under a bounded child deadline (process-group kill), so a stalled herdr pane get can no longer hold the per-event inbox lock; a probe timeout classifies as a retryable pre-submission failure. The idle-wake decision rides the first delivered transition (a busy target can never be prompted by a crash-recovery retry), and enqueue rejects payloads that are not valid UTF-8 text with an actionable validation error.
90
- - Pane observation launch failures are normalized to executor unavailability so a delivery probe can never strand an event in `claimed`; idle-wake retry outcomes persist across restarts; reverse-address files that are not JSON objects fail enqueue with an actionable validation error instead of crashing.
91
- - Enqueue applies the target agent's native payload limit (Codex argv bound, Pi body bound) before persisting an event, validates recognized agent statuses before submission (undetermined statuses stay retryable pre-send), and shares one Pi event id validator between enqueue and delivery, so events that could never be delivered are rejected up front instead of queuing forever.
92
- - Idle-agent wakes are persisted as pending before they are attempted and retried by later deliver calls (re-verifying the live target; identity drift demotes the event to uncertain) without ever resubmitting the native payload, so a crash or transient wake failure can no longer leave an accepted message undelivered to an idle agent. Pi targets now enforce the `inb-`/`wnk-` event id constraint at enqueue instead of queuing an event that can never be delivered.
93
- - Native queue submission and the Pi identity probe enforce their deadline at the child boundary through a shared bounded-process molecule (process-group kill, bounded pipe draining, nonblocking partial stdin writes). A stalled or non-reading native client can no longer hold the per-event inbox lock past the configured timeout the way a cleanup-blocking `Timeout.timeout` around `Open3.capture3` allowed. Codex payloads are size-bounded before launch and a spawn-time `E2BIG` is classified as a proven pre-submission rejection, keeping the event retryable instead of stranding it uncertain.
94
-
95
- ## [0.2.0] - 2026-10-02
96
-
97
- ### Added
98
- - `Ace::Herdr::Organisms::RuntimeAdapter` and the lazy `ace/runtime/adapters/herdr` entrypoint implement the shared ace-runtime intent contract. It preserves native agent prompt and wait semantics, polls all four lifecycle observations, and maps Herdr failures to contract errors. Native tab and pane probes support context, focus, and retained pane preparation. The packaged shared adapter contract suite runs against a Herdr-shaped fake executor.
99
-
100
- ## [0.1.0] - 2026-09-27
101
-
102
- ### Added
103
- - `ace-herdr tidy` (spec 8wq.t.1w0): dry-run-by-default cleanup of finished agent panes and delivery records. Pane closure requires positive completion evidence only (observed `done` agent state, or a pane whose native `pane process-info` shows no live foreground process — herdr omits `foreground_processes` when empty) re-confirmed by a fresh probe immediately before the rename-to-`done`-then-close mutation; revived or uncertain candidates are excluded and never mutated. Delivered records strictly older than the new `tidy.delivered_retention_days` config key (default 7) are atomically archived to `deliveries_dir/archive/` after a lock-guarded reload re-proves eligibility; `pending`/`retryable`/`failed` and unreadable records are never touched, an unreadable record encountered during apply is preserved and reported instead of aborting. Archival never breaks delivery idempotency (`deliver`/`--resume` consult the archive copy, so identical re-delivery still short-circuits and conflicts still fail closed). Deterministic one-line JSON report with explicit empty states; an unreachable herdr runtime fails with an explicit error and no partial report.
104
- - Terminal-control surface matching the ace-tmux intent vocabulary (spec 8wq.t.k84), keeping one-line JSON output: `ace-herdr list` (panes/tabs/workspaces, `--workspace` scoping, explicit empty arrays), `ace-herdr send` (declaration-ordered `--cmd`/`--msg`/`--key`; agent-aware routing sends one self-submitting `agent prompt` on agent panes with `agent_blocked`/`agent_prompt_stalled` gating and drop-and-report of a single trailing `--key Enter`; invalid shapes fail closed before any transport call), `ace-herdr capture` (raw `pane read` text, `--source visible|recent`), `ace-herdr wait --for output --pattern` (literal match, immediate check then poll, seconds→ms conversion, timeout CLI error) alongside the unchanged agent wait, and `--list-presets`.
105
- - Preset-driven creation: `ace-herdr workspace <preset>` / `ace-herdr tab <preset>` compose YAML layouts through the ADR-022 cascade (`.ace/herdr/` > `~/.ace/herdr/` > gem `.ace-defaults/herdr/`, ships `workspaces/development.yml` + `tabs/agent.yml`), with recursive `preset:` inheritance (deep merge, array concat, cycle detection, fail-closed missing references), deterministic creation order (workspace → tabs → root pane + splits → renames → pane commands → readiness-gated agent starts; herdr's seeded initial tab is closed once the declared tabs exist), CLI `--cwd` overrides (CLI > tab > root > pane), and pre-creation layout validation (unplaced panes, duplicate placements, unknown split targets/directions fail closed and create nothing). Includes a tmux-intent ↔ herdr-command parity table in `docs/usage.md`.
106
- - `Ace::Herdr::Organisms::ControlSurface`: owner of the terminal-control behavior — native JSON normalization into public payloads, send validation/routing, and preset instantiation.
107
- - `Ace::Herdr::Molecules::PresetLoader` (ADR-022 cascade discovery) and `Ace::Herdr::Molecules::PresetResolver` (`preset:` reference resolution).
108
- - `Ace::Herdr::Molecules::HerdrExecutor`: native operations for workspace/tab/pane listing, pane read/send-text/send-keys, pane wait-output, pane split, workspace create, agent send-keys, and focus flags on tab/workspace creation; typed `TabNotFoundError`/`WorkspaceNotFoundError` classification; herdr machine codes are now carried in executor error messages (for example `pane_not_found: ...`); output-wait timeouts raise `WaitTimeoutError`.
109
- - Gem bootstrap (spec 8wm.t.vs0): push delivery + agent bootstrap for the Herdr runtime, implementing the ace-hitl provider delivery contract (`deliver(ref, answer)` -> `DeliverResult` with `:delivered`/`:retryable`/`:failed`).
110
- - `Ace::Herdr::Organisms::Deliverer`: idempotent per event id with write-ahead delivery records under `.ace-local/herdr/deliveries/` (0600, atomic writes; the full answer is persisted so a crash never loses it, recoverable via `ace-herdr deliver --resume`), per-event lock serializing concurrent deliveries, duplicate-identical short-circuit, fail-closed content/destination conflicts, bootstrap of missing agents (`herdr agent start` with shell-escaped `HERDR_SESSION`/`HERDR_PANE` exported into the pane shell), a readiness gate before prompting, retry limits with fixed deterministic backoff (covering prompt and probe failures), and terminal-failure reporting — including the ambiguous crash window after a submitted prompt, reported instead of silently resent.
111
- - `Ace::Herdr::Organisms::Dispatcher`: one-command subagent dispatch — tab + `herdr agent start` + prompt with deterministic defaults (caller's workspace, label = agent/pane name, prompt from file/stdin/`--no-prompt`); workspace and pane identifiers are token-validated and shell-escaped before they reach a pane shell.
112
- - `ace-herdr` CLI: `deliver`, `dispatch`, `wait`, `close` (JSON output, exception-based exit codes per ADR-023).
113
- - `Ace::Herdr::Molecules::HerdrExecutor`: the single seam to the herdr CLI (argv arrays per ADR-031) with typed error classification from herdr's machine-readable codes (`agent_blocked`, `agent_prompt_stalled`, `pane_not_found`, `timeout`).
114
- - Config cascade (ADR-022) defaults: `default_agent_kind`, delivery retry limits/backoff, timeouts, deliveries dir (ADR-029).
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <div align="center">
2
2
  <h1> ACE - HERDR </h1>
3
3
 
4
- Zero-token push delivery and agent bootstrap for the Herdr runtime, behind the ace-hitl provider delivery contract.
4
+ Zero-token terminal transport and agent bootstrap for the Herdr runtime.
5
5
 
6
6
  <img src="../docs/brand/AgenticCodingEnvironment.Logo.XS.jpg" alt="ACE Logo" width="480">
7
7
  <br><br>
@@ -14,44 +14,62 @@
14
14
 
15
15
  > Works with: Claude Code, Codex CLI, OpenCode, Gemini CLI, pi-agent, and more.
16
16
 
17
+ > **State owner:** none for task, assignment, or HITL state. `ace-herdr` owns
18
+ > only terminal transport and its delivery retry records under
19
+ > `.ace-local/herdr/deliveries/`. tmux/Herdr are transports, not owners of the
20
+ > file-based flow ([docs/lab-flow.md](../docs/lab-flow.md)).
21
+
17
22
  [Usage Guide](docs/usage.md)
18
23
 
19
- `ace-herdr` is to herdr what `ace-tmux` is to tmux: a deterministic, zero-token wrapper over the `herdr` CLI. It implements the ace-hitl push-delivery contract — `deliver(ref, answer)` maps to `herdr agent prompt <pane>`, bootstrapping the agent first (`herdr agent start`) when the pane has none, so an answer is never lost — and adds one-command agent dispatch, noiseless monitoring, and closure on top.
24
+ `ace-herdr` is to herdr what `ace-tmux` is to tmux: a deterministic, zero-token
25
+ wrapper over the `herdr` CLI. It sends text and keys to panes, starts agents,
26
+ captures output, waits for agent state or output, and closes finished panes.
27
+ It also implements the HITL push-delivery transport used by `ace-hitl` adapters.
20
28
 
21
29
  ## How It Works
22
30
 
23
- 1. Every ace-hitl ask captures a reverse address (`HERDR_SESSION` / `HERDR_PANE`, schema `ace.hitl.ref/v1`). `ace-herdr deliver` pushes an answer back to that address.
24
- 2. Delivery is idempotent per event id: write-ahead delivery records under `.ace-local/herdr/deliveries/` make retries safe — identical content is never delivered twice.
25
- 3. If the target pane has no agent, `ace-herdr` bootstraps one (`herdr agent start --kind <kind> --pane <pane>`), waits for readiness, and exports `HERDR_SESSION` / `HERDR_PANE` into the agent's environment.
26
- 4. Retryable failures back off on a fixed deterministic schedule; terminal failures are reported and persisted in the delivery record.
27
- 5. `ace-herdr dispatch`, `wait`, and `close` give agents one-command subagent lifecycle with sensible defaults (same workspace as the caller, label = task id, prompt from file or stdin).
28
-
29
- The `inbox` command records one attempt-linked agent message before native submission. It binds the live terminal, agent session, and configured receipt verification key at enqueue, serializes claims by event ID, and exposes uncertain outcomes through `inbox status`. An uncertain event stays uncertain until `inbox reconcile --receipt FILE` receives a matching, signed operator or supervisor observation of consumption or supersession; missing or mismatched proof returns a JSON refusal without resending. See the [usage guide](docs/usage.md#durable-agent-inbox) for states, key configuration, and receipt shape.
31
+ 1. Terminal operations address panes through opaque handles; `ace-runtime`
32
+ selects tmux or Herdr and hides the difference.
33
+ 2. `ace-herdr deliver` pushes an answer to the pane that asked, bootstrapping an
34
+ agent (`herdr agent start`) when the pane has none.
35
+ 3. Delivery is idempotent per event id: write-ahead delivery records under
36
+ `.ace-local/herdr/deliveries/` make retries safe.
37
+ 4. `ace-herdr dispatch`, `wait`, and `close` give agents one-command subagent
38
+ lifecycle with deterministic defaults.
30
39
 
31
40
  ## Use Cases
32
41
 
33
- - Push an HITL answer to the pane that asked, even if its agent was restarted since.
34
- - Dispatch a subagent in one command with deterministic defaults and no LLM decisions.
42
+ - Push an answer to the pane that asked, even if its agent restarted.
43
+ - Dispatch a subagent in one command with deterministic defaults.
35
44
  - Wait for agent readiness or completion without scraping pane noise.
36
- - Close out finished agent panes (rename/close) and return their result.
37
- - Use Herdr through the shared `ace-runtime` Ruby intent API, with workspace/session and tab/window mapping handled by the adapter.
45
+ - Close finished agent panes and return their result.
46
+ - See the real layout of spaces, tabs, and panes in one command.
47
+ - Use Herdr through the shared [ace-runtime](../ace-runtime) API.
38
48
 
39
49
  ## Installation
40
50
 
41
- Install as part of the ACE toolkit (see the repo README), or add to your Gemfile:
42
-
43
51
  ```ruby
44
52
  gem "ace-herdr"
45
53
  ```
46
54
 
55
+ ## Planned waves
56
+
57
+ > **Planned (L2):** the unified report/question/reply interface and configurable
58
+ > adapter set are not available yet. `ace-herdr` remains a transport/adapter
59
+ > building block; it does not own HITL state.
60
+ >
61
+ > The old durable agent inbox (`inbox`) and guarded native-source requirements
62
+ > belong to the replaced protected model and are not part of the file-based
63
+ > flow.
64
+
47
65
  ## Testing
48
66
 
49
67
  Fast tests only (no live herdr needed):
50
68
 
51
69
  ```bash
52
- ace-test ace-herdr
70
+ bin/ace-test ace-herdr
53
71
  ```
54
72
 
55
73
  ## License
56
74
 
57
- MIT — see [LICENSE](LICENSE).
75
+ MIT -- see [LICENSE](LICENSE).
data/docs/usage.md CHANGED
@@ -1,399 +1,167 @@
1
1
  ---
2
2
  doc-type: user
3
3
  title: ace-herdr Usage
4
- purpose: Full CLI and configuration reference for ace-herdr: push delivery, agent bootstrap, the terminal-control surface (list, send, capture, wait, presets), and tidy cleanup.
4
+ purpose: CLI and configuration reference for ace-herdr terminal transport, delivery, and agent bootstrap.
5
5
  ace-docs:
6
- last-updated: 2026-10-04
7
- last-checked: 2026-10-04
6
+ last-updated: 2026-10-17
7
+ last-checked: 2026-10-17
8
8
  ---
9
9
 
10
- # Usage
10
+ # ace-herdr Usage
11
11
 
12
- `ace-herdr` is a zero-token wrapper over the `herdr` CLI. It implements the ace-hitl push-delivery contract -- `deliver(ref, answer)` -> `herdr agent prompt <pane>` -- one-command agent dispatch, noiseless waiting and pane closure, the terminal-control intents known from `ace-tmux` (`list`, `send`, `capture`, output waits, preset-driven workspace/tab creation), and dry-run-first cleanup of finished panes and delivery records (`tidy`). No LLM is consulted anywhere in the gem.
12
+ > **State owner:** none for task, assignment, or HITL state. `ace-herdr` owns
13
+ > terminal transport and its delivery retry records only. See
14
+ > [docs/lab-flow.md](../../docs/lab-flow.md).
13
15
 
14
- ## Command Surface
16
+ `ace-herdr` is a zero-token wrapper over the `herdr` CLI. It is a transport:
17
+ it does not own task, assignment, queue, or HITL event state.
15
18
 
16
- - `ace-herdr deliver [OPTIONS]`
17
- - `ace-herdr inbox enqueue|status|deliver [OPTIONS]`
18
- - `ace-herdr dispatch [OPTIONS]`
19
- - `ace-herdr list [--panes|--tabs|--workspaces] [--workspace ID] [--quiet]`
20
- - `ace-herdr send [--cmd TEXT] [--msg TEXT...] [--key NAME...] --pane ID [--quiet]`
21
- - `ace-herdr capture --pane ID [--lines N] [--source visible|recent]`
22
- - `ace-herdr wait --pane ID (--for output --pattern PATTERN | --for agent [--until ...]) [--timeout S] [--quiet]`
23
- - `ace-herdr close [OPTIONS]`
24
- - `ace-herdr tidy [--apply] [--quiet]`
25
- - `ace-herdr workspace <preset> [--cwd PATH] [--quiet]`
26
- - `ace-herdr tab <preset> [--workspace ID] [--cwd PATH] [--quiet]`
27
- - `ace-herdr --list-presets [workspaces|tabs]`
19
+ ## Command surface
28
20
 
29
- ## Output policy
21
+ | Command | Purpose |
22
+ |---------|---------|
23
+ | `deliver` | Push an answer to an agent pane (HITL delivery transport) |
24
+ | `dispatch` | Start an agent in one command: tab + agent + prompt |
25
+ | `send` | Send a command, raw text, or named keys to a pane |
26
+ | `capture` | Print recent pane output as raw text |
27
+ | `wait` | Wait for an agent state or matching pane output |
28
+ | `close` | Rename and/or close a finished agent pane |
29
+ | `list` | List live panes, tabs, or workspaces as one JSON line |
30
+ | `status` | Show the live layout: workspaces, tabs, and panes in one view |
31
+ | `tidy` | Report cleanable panes/delivery records; close/archive with `--apply` |
32
+ | `tab` / `workspace` | Create a tab/workspace from a preset |
30
33
 
31
- Control commands print exactly one deterministic JSON line on stdout; `--quiet` suppresses it. Failures surface herdr's machine error codes in the CLI error message (for example `pane_not_found: pane w9:p1 not found`) and exit non-zero. The one exception is `capture`, which prints raw pane text without any JSON wrapping.
34
+ ## Delivery
32
35
 
33
- herdr *sessions* (server persistence) are intentionally not exposed; the tmux session analogue is the herdr **workspace**.
34
-
35
- ## ace-runtime adapter
36
-
37
- Install `ace-herdr` alongside `ace-runtime`, then resolve the adapter with `Ace::Runtime.resolve("herdr")`. The contract's `session` is a Herdr **workspace**, `window` is a **tab**, and `pane` is an opaque Herdr pane ID. The adapter does not construct pane IDs. It locates the caller through `HERDR_SESSION` and `HERDR_PANE` and reads the explicit pane with `pane get`; the pane's native workspace is authoritative and a conflicting `HERDR_WORKSPACE_ID` hint is rejected instead of honored. Outside Herdr, `context` reports `in_runtime: false`; operations requiring a caller workspace raise `Ace::Runtime::RuntimeUnavailableError`.
38
-
39
- The adapter implements `context`, `ensure_window`, `prepare_pane`, `focus`, ordered `send` and its convenience methods, `capture`, `wait_output`, `wait_agent`, `wait_lifecycle`, `close_window`, `list_windows`, and `list_panes`. `ensure_window` scopes the sanitized tab label to the caller workspace. It records the tab's root, preset, and prepared pane under `~/.ace/local/herdr/runtime-tabs/` — keyed by workspace and label, so adapter instances from any working directory or requested root share one record, create through the same lock, and serialize prepared-pane creation through it. Later instances verify an idempotent request and reject a conflicting one; a tab whose ownership cannot be proven is never closed or replaced (a failed create rolls back only tabs the same attempt created). `prepare_pane` splits a retained shell target and returns the native pane ID after verifying it exists and has a shell process.
40
-
41
- Replacing a dead prepared pane updates only the recorded pane pointer: a foreign tab's pointer-only record stays pointer-only (no root/preset keys are invented, so it never becomes ownership evidence), and an owned record keeps its exact root and preset. A prepared-pane pointer to a dead pane is replaced by the next split; a pointer-only record never satisfies `ensure_window` by itself -- adoption still requires the tab's verified native root.
42
-
43
- Sends probe for a live agent. Plain panes receive raw text and keys in order; agent panes receive one self-submitting `agent prompt`, with one trailing Enter dropped and reported. Agent waits use native `agent wait` states (`idle`, `working`, `blocked`, `done`). Contract timeout values are seconds and are converted to Herdr milliseconds. Lifecycle waits observe `tab get`, focused tab state, `pane get`, and `pane process-info`; a missing pane keeps `pane-exists` waiting but immediately satisfies `pane-exited`. A pane with only its retained shell also satisfies `pane-exited` because no submitted foreground command remains. This observation does not prove assignment success or authorize cleanup.
44
-
45
- Native `agent_blocked` becomes `SendRejectedError`; `agent_prompt_stalled` becomes `SendStalledError` and must not be automatically resent. Native wait timeouts become `WaitTimeoutError`, missing targets become `TargetNotFoundError`, and an unavailable binary or socket becomes `RuntimeUnavailableError`.
46
-
47
- ## Durable agent inbox
48
-
49
- Use a stable event ID for one message and the assignment attempt that owns it. The reverse-address JSON contains `session` and `pane`, as with `deliver`.
50
-
51
- Reverse references use the shared `ace.hitl.ref/v1` pair contract. Herdr token names
52
- remain valid; the codec also preserves paired native tmux IDs (`$0`/`%0`) when
53
- carried by managed metadata. Mixed native/token or cross-field IDs refuse.
54
- Wire and persisted references must be canonical. Syntax acceptance does not add
55
- tmux execution to this Herdr controller or replace its exact native ownership,
56
- target and submission checks.
57
-
58
- ```bash
59
- ace-herdr inbox enqueue --event inb-12345678 --attempt ATTEMPT --ref ref.json --file prompt.txt
60
- ace-herdr inbox status --event inb-12345678 --format json
61
- ace-herdr inbox deliver --event inb-12345678
62
- ```
63
-
64
- `enqueue` checks the live pane, terminal and original agent-session identity.
65
- Managed callers supply `expected_target` from the accepted runtime owner. Reusing
66
- an event with identical attempt, target and payload returns its original record;
67
- changed input refuses. `deliver` records one submission intent before sending.
68
-
69
- Codex delivery uses the existing Herdr terminal prompt; Pi uses its maintained
70
- queue boundary. `delivered` means the submission was accepted, not that the
71
- agent read it or completed the task. A known pre-submission refusal stays queued.
72
- A lost response, process interruption after intent, or changed target is uncertain;
73
- a later `deliver` does not automatically resend or select a replacement terminal.
74
-
75
- Inspect current progress directly through `ace-herdr capture` or the native pane.
76
- Inbox status is transport history, not a cached projection of current agent state.
77
- No agent-read proof, signing key, reconciliation command or dedicated Codex
78
- app-server is required. Actual task results and scoped service effects retain
79
- their independent authorization and verification.
80
-
81
- ## tmux-intent ↔ herdr-command parity
82
-
83
- Every common terminal-control intent available through `ace-tmux` is available through `ace-herdr` (herdr 0.9.1). Parity is of INTENT and FLAG VOCABULARY, not byte-format: ace-herdr keeps one-line JSON where ace-tmux renders human tables.
84
-
85
- | ace-tmux intent | ace-herdr command | Native herdr call |
86
- |---|---|---|
87
- | `list` (panes) | `list` / `list --panes` | `pane list` |
88
- | `list --windows` | `list --tabs` | `tab list` |
89
- | `list --sessions` | `list --workspaces` | `workspace list` |
90
- | `send --cmd` | `send --cmd` | `pane run` (plain) / `agent prompt` (agent pane) |
91
- | `send --msg` / `--key` | `send --msg` / `--key` | `pane send-text` / `pane send-keys`; `agent prompt` / `agent send-keys` |
92
- | `capture` | `capture` | `pane read` |
93
- | `wait --for output` | `wait --for output --pattern` | `pane wait-output` |
94
- | `wait --for agent` | `wait --until` (agent state) | `agent wait` |
95
- | `start <preset>` | `workspace <preset>` | `workspace create` + tabs/panes |
96
- | `window <preset>` | `tab <preset>` | `tab create` + splits |
97
- | `--list-presets` | `--list-presets` | -- (config cascade) |
98
- | `attach` / `detach` | ❌ out of scope | human-facing; no automation equivalent |
99
- | layout strings / `select-layout` | ❌ out of scope | herdr has split/resize only |
100
-
101
- ## `ace-herdr list`
102
-
103
- Inspect live state. Panes are the default scope; `--workspace <id>` scopes panes and tabs.
104
-
105
- ```bash
106
- ace-herdr list # panes across all workspaces
107
- ace-herdr list --workspace w1 # panes in one workspace
108
- ace-herdr list --tabs --workspace w1 # tabs in one workspace
109
- ace-herdr list --workspaces # workspaces
110
- ```
111
-
112
- Output: `{"panes":[{"id":"w1:p1","tab":"w1:t1","workspace":"w1","title":"~","cwd":"/tmp","focused":true,"agent_status":"idle"},...]}` -- `{"tabs":[{id, workspace, title, number, pane_count, focused}...]}` for `--tabs`, `{"workspaces":[{id, title, number, tab_count, pane_count, focused}...]}` for `--workspaces`. Empty results are a success with an empty array, never an error.
113
-
114
- ## `ace-herdr send`
115
-
116
- Send a command, raw text, or named keys. Input reaches the pane in **declaration order** (an intentional divergence from ace-tmux's msgs-then-keys; order is expressive here, so it is preserved, not normalized). At least one input token is required -- none is a usage error before any transport call.
36
+ `deliver` pushes an answer to the pane that asked, bootstrapping an agent when
37
+ the pane has none. Deliveries are idempotent per event id and never lose the
38
+ answer: a write-ahead record survives crashes, transient failures retry with
39
+ fixed backoff, and `--resume` re-delivers from the stored record.
117
40
 
118
41
  ```bash
119
- ace-herdr send --pane p5 --cmd 'bundle exec rake test'
120
- ace-herdr send --pane p5 --msg 'continue with option 2' --key Enter
121
- ace-herdr send --pane p5 --key Esc --cmd 'reset' # rejected -- see rules
122
- ace-herdr send --pane p5 --key enter
42
+ ace-herdr deliver --session ws-1 --pane p5 --event-id evt-1 --answer-file answer.md
43
+ echo 'the answer' | ace-herdr deliver --pane p5
123
44
  ```
124
45
 
125
- ### Plain pane (raw input, declaration order)
126
-
127
- - `--cmd TEXT` is exactly one `pane run` submission (text + Enter). It must be declared before every `--key`; trailing keys are sent after the submission (post-submission keystrokes such as `y`/`n` confirmations). Leading keys (`--key Esc --cmd run`) are rejected before any transport call.
128
- - `--msg a --msg b` types raw text without submitting, concatenated in order.
129
- - `--key K...` sends named keys in order; each `enter` submits pending text.
130
- - `--cmd` combined with `--msg` is a usage error before any transport call (fail closed, nothing sent).
131
- - Multi-Enter sequences submit per Enter.
132
- - Output: `{"pane":"p5","sent":"cmd"}` for command-led sends, `"text"` for message-led sends, `"keys"` for key-only sends.
46
+ These are transport records, not a journal or a state owner.
133
47
 
134
- ### Agent pane (prompt semantics)
48
+ ## Delivering reports to a pi overseer (L7.3)
135
49
 
136
- When the target pane hosts a live agent (`herdr agent get`), the same flags route to agent transport -- replacing ace-tmux's INTERACTIVE_CLI_COMMANDS/busy-pattern heuristics with native agent state:
50
+ An ace-hitl report reaches a pi overseer session through the transports Herdr
51
+ already owns — there is no second queue on the ACE side:
137
52
 
138
- - `--cmd T`, or concatenated `--msg` texts (joined with newlines), becomes **one** agent prompt that submits itself -- message-only input submits once on an agent pane (intended divergence from plain panes).
139
- - At most one trailing `--key Enter` is dropped and reported: `{"pane":"p5","sent":"prompt","dropped_keys":["Enter"]}` -- this guarantees exactly-one submission instead of erroring.
140
- - `--key`-only sequences go to `agent send-keys` (`esc`, `ctrl+c`, ...); at most one `enter` total.
141
- - Submission is gated natively: a blocked agent rejects pre-send with a CLI error carrying `agent_blocked` (never silently dropped); a stalled prompt surfaces `agent_prompt_stalled`.
142
- - Rejected before transport: keys interleaved between or after messages other than the single trailing `enter`; multiple `enter` keys; `--cmd` with non-Enter keys; `--cmd` combined with `--msg`.
53
+ - **Pane push (`ace-herdr deliver`):** the report text is prompted into the
54
+ overseer pane. Delivery is idempotent per event id: a repeated notification
55
+ of the same event short-circuits against the delivery record instead of
56
+ prompting again, and concurrent deliveries of one event prompt once. A pane
57
+ without an agent is bootstrapped and gated on readiness first.
58
+ - **Provider-native queue (`ACE_HERDR_PI_QUEUE_CLIENT`):** the configured pi
59
+ queue client receives the payload (`--delivery <event-id> --session-id ...`
60
+ with a digest-bound receipt) and hands it to the session, which accepts it
61
+ while waiting (immediate turn) and while working (queued follow-up for the
62
+ active run).
143
63
 
144
- ## `ace-herdr capture`
64
+ Either way a delivery during an ACTIVE overseer turn never starts a second
65
+ overseer execution: the message joins the one session run, and the woken
66
+ session resumes the commissioned process with the `/continue-work` prompt
67
+ (see ace-handbook-integration-pi). Repetition of a delivery is transport
68
+ noise — work repetition is decided by event state on the receiving side, not
69
+ by the number of deliveries.
145
70
 
146
- Print pane content as raw text (no JSON wrapping). Read-only -- agent routing rules do not apply.
71
+ ## Dispatch, send, capture, wait, close
147
72
 
148
73
  ```bash
149
- ace-herdr capture --pane p5 # last 40 lines of history
150
- ace-herdr capture --pane p5 --lines 40 --source recent
151
- ace-herdr capture --pane p5 --source visible # the agent screen
152
- ```
74
+ ace-herdr dispatch --label t.123 --kind pi --prompt-file prompt.md
75
+ ace-herdr dispatch --label review --pane p7 --cwd /Users/me/project
153
76
 
154
- ## `ace-herdr wait`
155
-
156
- Wait for an agent state or matching pane output.
157
-
158
- ```bash
159
- ace-herdr wait --pane p5 # agent: idle, done, or blocked
160
- ace-herdr wait --pane p5 --until done --timeout 120 # agent: one state
161
- ace-herdr wait --pane p5 --for output --pattern 'tests? OK' --timeout 30
77
+ ace-herdr send --pane p5 --cmd 'bundle exec rake test'
78
+ ace-herdr send --pane p5 --msg 'continue with option 2' --key Enter
79
+ ace-herdr capture --pane p5 --lines 40
80
+ ace-herdr wait --pane p5 --until done --timeout 120
81
+ ace-herdr wait --pane p5 --for output --pattern 'build succeeded'
82
+ ace-herdr close --pane p5 --rename done
162
83
  ```
163
84
 
164
- - Agent form (default; unchanged): `--until idle,working,blocked,done,unknown`, output `{"pane":"p5","state":"ready"}`.
165
- - Output form: `--for output --pattern PATTERN` (literal substring, matching ace-tmux's observable contract). herdr checks existing pane content immediately, then polls; `--timeout` is seconds (gem converts to milliseconds). Output `{"pane":"p5","matched":true}`; a timeout is a CLI error (non-zero exit).
166
- - The modes are mutually exclusive: `--pattern` requires `--for output`; `--until` requires the agent form.
85
+ `send` delivers input in declaration order. Panes hosting a live agent get
86
+ prompt semantics: text becomes one self-submitting agent prompt, and a single
87
+ trailing Enter is dropped and reported. A command must be declared before any
88
+ key.
167
89
 
168
- ## `ace-herdr workspace` / `ace-herdr tab` (presets)
90
+ ## Workspaces, tabs, and presets
169
91
 
170
- Declare a workspace or tab layout in YAML and create it in one command -- `workspace <preset>` mirrors `ace-tmux start`, `tab <preset>` mirrors `ace-tmux window`.
92
+ Presets resolve through the ADR-022 cascade (`.ace/herdr/` > `~/.ace/herdr/` >
93
+ gem defaults) and may compose via `preset:` refs.
171
94
 
172
95
  ```bash
173
- ace-herdr workspace development
174
96
  ace-herdr workspace development --cwd /path/to/project
175
97
  ace-herdr tab agent --workspace w1
176
- ace-herdr --list-presets # merged inventory, both types
177
- ace-herdr --list-presets workspaces
178
- ```
179
-
180
- ### Preset cascade (ADR-022)
181
-
182
- Presets load nearest-wins and the `--list-presets` inventory reflects the merged set:
183
-
184
- 1. project `.ace/herdr/{workspaces,tabs}/*.yml` (highest)
185
- 2. user `~/.ace/herdr/{workspaces,tabs}/*.yml`
186
- 3. gem `.ace-defaults/herdr/{workspaces,tabs}/*.yml` -- ships `workspaces/development.yml` and `tabs/agent.yml`
187
-
188
- A project file with the same name overrides a gem/user file; names unique to a level still appear in the merged listing.
189
-
190
- ### Schema
191
-
192
- ```yaml
193
- # .ace/herdr/workspaces/development.yml
194
- label: development # workspace label (defaults to the preset name)
195
- cwd: /workspace/project # root cwd (tab/panes inherit; ~ expands)
196
- focus: true # pass --focus to workspace create
197
- tabs:
198
- - preset: agent # a tab may inherit a tab preset (recursive; overlay wins)
199
- label: work # ...and override any field
200
- - label: editor
201
- cwd: /workspace/project # tab cwd overrides the root cwd
202
- focus: false
203
- panes:
204
- - label: shell # the FIRST pane is the tab's root pane
205
- - label: agent
206
- cwd: /workspace/project
207
- agent: # declares an agent pane
208
- kind: pi # default: config default_agent_kind
209
- name: development-agent
210
- prompt: Review the current task.
211
- splits: # every pane after the first must be placed by a split
212
- - direction: right # herdr-native right|down; horizontal|vertical accepted
213
- target: shell # pane label to split from (default: the root pane)
214
- pane: agent # the declared pane placed by this split
215
- ratio: 0.5
216
- ```
217
-
218
- ```yaml
219
- # .ace/herdr/tabs/agent.yml -- a tab preset uses the tab-level fields directly
220
- label: agent
221
- panes:
222
- - label: agent
223
- agent:
224
- kind: pi
225
- name: task-agent
226
- ```
227
-
228
- Creation is deterministic and ordered: the workspace is created first, then tabs/panes in declared order (root pane from `tab create`, further panes from `pane split`, panes renamed to their labels), then pane `command`s run, then declared agents start readiness-gated (`agent start` blocks until the pane is interactive; vs0 bootstrap order: reverse address exported into the pane shell, agent start, optional prompt). herdr seeds every new workspace with an initial tab; once the preset's declared tabs exist that seeded tab is closed, so only declared tabs remain (a preset with no `tabs:` keeps the seeded one). All layouts are validated before any herdr call -- an unplaced pane, a duplicate split placement, an unknown split target, or an unknown direction fails closed and creates nothing.
229
-
230
- Output: `{"workspace":"w2","tabs":[{"tab":"w2:t1","panes":["w2:p1","w2:p2"],"commands":1,"agents":["development-agent"]}]}`; the tab command reports the single tab object. `--cwd` overrides the resolved root/tab cwd (CLI > tab > root > pane inheritance).
231
-
232
- Unknown preset: CLI error listing the available names (ace-tmux `--list-presets` parity), for example `Error: Unknown workspace preset 'nope' (available: development)`.
233
-
234
- Failed tab materialization: when a preset's native tab is created but a later materialization step fails (split, command, or agent start), both `ace-herdr tab` and `ace-herdr workspace` report a standard CLI error -- the process exits non-zero with the underlying failure on stderr, prints no success payload on stdout, and shows no stack trace in ordinary mode. These entrypoints never close tabs (rollback of the failed tab is the runtime adapter's exact-id policy).
235
-
236
- ## `ace-herdr deliver`
237
-
238
- Push an answer to an agent pane.
239
-
240
- ```bash
241
- # Answer from a file, explicit ref
242
- ace-herdr deliver --session ws-1 --pane p5 --event-id evt-1 --answer-file answer.md
243
-
244
- # Answer from stdin, ref from the environment (inside the asking pane)
245
- echo 'the answer' | ace-herdr deliver
246
-
247
- # Bootstrap a specific agent kind if the pane has none
248
- ace-herdr deliver --session ws-1 --pane p5 --kind codex --label 8wm.t.vs0 --answer-file a.md
98
+ ace-herdr --list-presets
249
99
  ```
250
100
 
251
- Options: `--session`, `--pane` (default: `HERDR_SESSION` / `HERDR_PANE`), `--event-id` (default: derived from the ref and content digest), `--kind`, `--label`, `--answer-file` (default: stdin), `--resume <event-id>` (re-deliver the stored answer from the record; no ref or answer input needed).
252
-
253
- Output: one JSON line `{"ref":{...},"state":"delivered|retryable|failed"}`. Exit code is non-zero unless the state is `delivered`.
254
-
255
- ### The delivery contract
256
-
257
- `ace-hitl ask` captures the asker's reverse address fail-closed from the environment (`HERDR_SESSION` / `HERDR_PANE`, schema `ace.hitl.ref/v1`). `ace-herdr deliver` pushes an answer back to that address:
258
-
259
- 1. A write-ahead delivery record carrying the full answer is persisted under `.ace-local/herdr/deliveries/<event-id>.json` (mode 0600, atomic rename) before any herdr contact, so a crash can never lose the answer. A crashed run is recovered with `--resume <event-id>`.
260
- 2. Delivery is idempotent per event id and serialized by a per-event lock: concurrent deliveries prompt once. Re-delivering identical content after a `delivered` record short-circuits without contacting herdr; different content or a different destination for the same event id fails closed.
261
- 3. If the target pane has no agent, one is bootstrapped (`herdr agent start`), the reverse address is exported into the pane shell (`export HERDR_SESSION=... HERDR_PANE=...`; values are token-validated and shell-escaped), and delivery waits for the agent to become idle before prompting.
262
- 4. Transient failures (readiness timeout, `agent_prompt_stalled`, socket/binary unavailability -- at the probe as well as the prompt) persist their history and report `retryable` within `delivery.max_attempts`.
263
- 5. Terminal failures (`agent_blocked` pre-send rejection, missing pane, agent start failure) report `failed` immediately with the error persisted in the record history.
264
- 6. An interrupted run whose last recorded event is a prompt submission without an outcome is ambiguous: the answer may already have been delivered. Re-running reports `failed` ("previous run crashed after submitting") instead of silently resending.
265
-
266
- Result states follow the ace-hitl contract (spec 8wm.t.vrz §1.2): `delivered`, `retryable` (safe to re-push identical content), `failed` (terminal).
267
-
268
- ## `ace-herdr dispatch`
269
-
270
- Start an agent in one command: tab + `herdr agent start` + prompt, all with deterministic defaults.
101
+ ## List and tidy
271
102
 
272
103
  ```bash
273
- ace-herdr dispatch --label 8wm.t.vs0 --kind pi --prompt-file prompt.md
274
- ace-herdr dispatch --label review --pane p7 --cwd /path/to/project
275
- ace-herdr dispatch --label 8wm.t.vs0 --no-prompt
104
+ ace-herdr list
105
+ ace-herdr list --tabs --workspace w1
106
+ ace-herdr list --workspaces
107
+ ace-herdr tidy # dry run: report candidates only
108
+ ace-herdr tidy --apply # close eligible panes, archive old delivered records
276
109
  ```
277
110
 
278
- Defaults: the caller's herdr workspace (flag `--workspace` > `HERDR_WORKSPACE_ID` > `herdr pane current`), the label as agent and pane name, the prompt from `--prompt-file` or stdin (`--no-prompt` skips submission). The new agent's environment receives `HERDR_SESSION` (workspace id) and `HERDR_PANE` (pane id) so its own `ace-hitl ask` calls carry a working reverse address.
111
+ `tidy` closes panes only on positive completion evidence re-confirmed right
112
+ before closing; unknown, pending, retryable, and failed resources are never
113
+ touched.
279
114
 
280
- Output: one JSON line `{"workspace":...,"pane":...,"agent":...,"kind":...,"tab_created":...,"prompted":...}`.
115
+ ## Status
281
116
 
282
- ## `ace-herdr close`
283
-
284
- Close out a finished agent pane; optionally rename first.
285
-
286
- ```bash
287
- ace-herdr close --pane p5 --rename done # rename, then close
288
- ace-herdr close --pane p5 --keep --rename wip # rename only
289
- ```
290
-
291
- ## `ace-herdr tidy`
292
-
293
- Report cleanable agent panes and delivery records as one deterministic JSON line; **dry run by default** -- nothing is closed, moved, or removed without `--apply`.
117
+ `status` composes the three live lists (`workspace_list`, `tab_list`,
118
+ `pane_list`) into one readable tree: workspaces with their tabs and each tab
119
+ with its panes, focused items marked with `*`, agent panes suffixed with
120
+ `agent: <state>`, and home directories shortened to `~`.
294
121
 
295
122
  ```bash
296
- ace-herdr tidy # dry run: report candidates, zero side effects
297
- ace-herdr tidy --apply # close eligible panes, archive old delivered records
298
- ace-herdr tidy --quiet # suppress the report
123
+ ace-herdr status # whole layout as a table
124
+ ace-herdr status --workspace w1 # one workspace
125
+ ace-herdr status --format json # the composed tree as JSON
299
126
  ```
300
127
 
301
- Output: `{"apply":false,"retention_days":7,"panes":{"candidates":[...],"preserved":[...],"closed":[...],"excluded":[...]},"deliveries":{"candidates":[...],"protected":[...],"archived":[...],"preserved":[...]}}`. Entries are ordered by pane id / event id; every bucket is always present (an explicit empty array means "nothing to do", never an error).
302
-
303
- ### Pane closure: positive evidence only
304
-
305
- A pane qualifies as a `candidate` solely on positive completion evidence:
306
-
307
- - the agent in the pane was **observed** in state `done` (`agent get`), or
308
- - the pane has **no foreground process** (`pane process-info` reports an empty process list).
309
-
310
- `unknown` or unreadable evidence is preserve-only: `unknown` status, a missing field, a malformed response, or a probe failure puts the pane in `preserved` with the reason (`active` / `unknown` / `unreadable`) -- no proof is never treated as dead. Panes whose process could not be read are reported, not closed.
311
-
312
- With `--apply`, every candidate is **re-probed immediately before mutation**: only a second positive reading closes it (rename to `done`, then close -- the `close` semantics). A candidate that revived in between (`idle`/`working`) or whose fresh probe turned uncertain or failed is moved to `excluded` with the reason and never mutated. A pane that vanished in the meantime is `excluded` as `gone` -- there is nothing left to close.
313
-
314
- ### Delivery record retention
315
-
316
- Delivered records under `deliveries_dir` whose `updated_at` is **strictly older** than `tidy.delivered_retention_days` (default 7; an exact-threshold record stays) become `candidates`; with `--apply` they are atomically moved to `deliveries_dir/archive/<event-id>.json` (mode 0600 preserved) and reported in `archived` with the archive path. Age parsing is RFC 3339 only; a delivered record with a missing or invalid `updated_at` is `preserved` (`invalid_updated_at`), never guessed.
317
-
318
- `pending`, `retryable`, and `failed` records are **never touched** (they are audit/recovery state) and are listed in `protected`. Malformed or unreadable record files are `preserved` as `unreadable`, never removed. Lock files, temp files, and the archive directory itself are ignored. Before each archival the record is re-loaded under its per-event lock: a record that changed after discovery (no longer delivered or no longer old enough) is left in place and reported as `preserved` (`changed`); one that turned unreadable is `preserved` (`unreadable`) without aborting the run. Archival never breaks delivery idempotency: `deliver`/`--resume` consult the archive copy, so re-delivering an archived event still short-circuits (identical content) or fails closed (conflicting content) instead of re-prompting.
319
-
320
- Errors: if the herdr runtime is unreachable, `tidy` fails with an explicit CLI error before reporting anything (no partial report, no mutation). No LLM is consulted anywhere; the report costs zero tokens.
128
+ Semantics:
129
+
130
+ - A live but empty result prints `No live herdr workspaces.` and exits 0 —
131
+ an empty valid set, not an error.
132
+ - An unreachable runtime (`ExecutorUnavailableError` and other executor
133
+ failures) is reported as an actionable non-zero error, never as an empty
134
+ layout.
135
+ - Counts are recomputed from the composed nesting, and panes whose tab or
136
+ workspace vanished between the list calls are kept in a synthetic
137
+ `(unlinked)` group instead of being dropped.
138
+ - JSON shape: `{"workspaces":[{"id","title","number","focused","tab_count",
139
+ "pane_count","tabs":[{"id","title","number","focused","pane_count","panes":
140
+ [{"id","tab","workspace","title","cwd","focused","agent_status"}]}]}]}`
141
+ with synthetic groups carrying `synthetic: true`.
142
+
143
+ ## Planned waves
144
+
145
+ > **Planned (L2):** the unified HITL report/question/reply interface and
146
+ > configurable adapter set are not available yet.
147
+ >
148
+ > The old durable agent inbox (`inbox`) and guarded native-source requirements
149
+ > belong to the replaced protected model and are not part of the file-based
150
+ > flow.
321
151
 
322
152
  ## Configuration
323
153
 
324
- Defaults (`.ace-defaults/herdr/config.yml`), overridable in `~/.ace/herdr/config.yml` or `.ace/herdr/config.yml`:
325
-
326
154
  ```yaml
327
- default_agent_kind: pi # herdr agent kind used for bootstrap/dispatch/preset agents
328
- delivery:
329
- max_attempts: 3 # prompt attempts per delivery sequence
330
- backoff_seconds: [1, 2, 4] # fixed deterministic backoff, no jitter
155
+ # .ace/herdr/config.yml
156
+ default_agent_kind: pi
331
157
  timeouts:
332
- agent_start: 60 # seconds to interactive readiness (also preset agents)
333
- prompt: 30
334
- wait: 30 # readiness gate / ace-herdr wait (both wait modes)
335
- deliveries_dir: .ace-local/herdr/deliveries
158
+ wait: 300
336
159
  tidy:
337
- delivered_retention_days: 7 # archive delivered records strictly older than this (tidy)
160
+ delivered_retention_days: 7
338
161
  ```
339
162
 
340
- ## Delivery records
341
-
342
- Records are JSON, one file per event id under `deliveries_dir` (relative to the working directory, mode 0600): event id, reverse address, SHA-256 answer digest, the full answer (so a crash never loses content), state (`pending` / `delivered` / `retryable` / `failed`), attempt count, and an append-only history of bootstrap, readiness, and prompt events with errors. Records are written atomically and guarded by a per-event lock. Re-running `deliver` with the same event id and content resumes or short-circuits; with different content or a different destination it fails closed.
343
-
344
- ## Error semantics
345
-
346
- Commands raise a CLI error (non-zero exit) carrying herdr's machine code where one exists:
347
-
348
- - Unknown pane/tab/workspace: `pane_not_found: ...`, `tab_not_found: ...`, `workspace_not_found: ...`
349
- - herdr binary or socket unavailable: explicit CLI error, no partial output
350
- - Blocked agent: `agent_blocked: ...` (terminal -- never silently dropped); stalled prompt: `agent_prompt_stalled: ...` (transient)
351
- - Output wait timeout: `timeout: ...`
352
- - Failed tab materialization: CLI error carrying the underlying failure (`ace-herdr tab`, `ace-herdr workspace`); never a false success or a stack trace
353
- - Unknown preset: usage error listing the available preset names
354
- - Invalid send shapes: usage error **before any transport call** (nothing is sent)
355
-
356
- Exit `0` means success (including an explicit empty `list` result or a satisfied wait).
357
-
358
- Runtime recovery exposes the read-only `process_binding(pane:, caller_pid:)`
359
- adapter operation, which checks OS ancestry, foreground ownership and the
360
- original agent-session identity. Message submission is never task completion.
361
-
362
- ## Packaged guarded native source
363
-
364
- `ace-herdr native-source selection` describes the source selection and patch shipped by this gem. These assets live under `lib/ace/herdr/native_source`; installed consumers do not need task directories. This source retains native version 0.9.3 and protocol 22; its exact source commit and patch digest identify the guarded producer. No upstream release or publication is implied.
163
+ ## Testing
365
164
 
366
- An explicit nonroot build owner can run:
367
-
368
- ```sh
369
- ace-herdr native-source build --source /absolute/clean/local/herdr-checkout --output /absolute/new/artifact-directory --target x86_64-unknown-linux-musl
370
- ace-herdr native-source verify --artifact /absolute/artifact-directory
165
+ ```bash
166
+ bin/ace-test ace-herdr
371
167
  ```
372
-
373
- The build requires a matching native Linux host (x86_64 or aarch64), the selected Rust and Zig versions, and a native `musl-gcc` toolchain already provisioned by the build owner. Cross builds are unsupported. The output must be new. The builder reproduces the exact baseline plus shipped patch in an isolated checkout, checks the commit/tree/lock/toolchain bytes, pins resolved Rust/Zig/linker paths, invokes the locked release build, and records observed executable/tool digests. It never runs the resulting Herdr binary, changes global tool defaults, contacts a running Herdr server, or publishes an artifact. Locked dependency fetching by Cargo may require network access. Failed builds retain their output claim; a partial directory does not verify.
374
-
375
- Verification checks bounded strict provenance against this gem's source selection and the observed ELF bytes/architecture. It provides byte provenance, not deployment authorization. The trusted Lab deployment owner separately selects the gem, source selection, artifact and receipt digests before installing the fixed Lab binary. Protected runtime replacement must also use the canonical maintenance admission/release owner. That operational installer join remains required source work; these commands alone do not activate the producer or complete installed acceptance.
376
-
377
- The selected source also provides `terminal.inhibit_input` for an exact original terminal/origin. Its successful `terminal_input_drained` reply proves permanent input inhibition and zero queued input, including terminal replies; it does not prove child exit or canonical scope cleanup. Lost acknowledgement may be retried only against the same original actor. Source fixtures validate this contract without running a native binary; actual Lab installation and N2 consumption remain required.
378
-
379
- The inbox context source owner supplies durable operation admission and exclusive key rotation through a fixed authenticated exchange. A lost begin reply recovers the same original process's grant; unknown effects, owner restart and incomplete installation retain blocking state. Replacement and rollback each require an attestation from the actual configured signer plus protected public/config readback. This core does not yet activate a context CLI/server, migrate Assign/private-store consumers, or authorize orphan reclamation. Existing protected mutations must all be migrated before this becomes an accepted end-to-end rotation path; installation and private-key pair handling remain the trusted provisioner's responsibility.
380
-
381
- ### Trusted bounded descriptor handoff
382
-
383
- Internal source installers using `BoundedProcess.call` may supply
384
- `descriptor_mapping: {4 => authenticated_readonly_file}` together with bounded
385
- `stdin_data` carrying exact selected source bytes. The mapping has at most16
386
- entries; child descriptor keys are strict Integers3..63 and handles must be live
387
- read-only regular files. Stdio overwrite, writable/closed/nonregular handles and
388
- ambient descriptor/path fallback refuse before spawn. Standard Open3 spawn maps
389
- only explicit descriptors; `close_others: true` excludes all other descriptors.
390
- The supplying verification owner keeps the handle scoped and authenticates the
391
- selected inode; the runner does not confer authority or authenticate arbitrary
392
- files. Actual installed privilege-transition/PAM preservation is a separate
393
- installed acceptance check, not established by controlled file/child tests.
394
-
395
- ### Bounded subprocess cleanup
396
-
397
- `BoundedProcess.call` retains capped stdout/stderr and uses an execution deadline plus at most one additional second of cleanup. `stderr_limit:` defaults to `output_limit:`. Unconfirmed cleanup raises `PostLaunchError` and transfers only the still-owned original child to an eventual reaper; no success or absence proof follows. Generic successful calls do not newly signal process groups.
398
-
399
- The fixed protected handler selects `cleanup_group: true`: stream EOF alone never triggers termination. Nonreaping observation must establish the original leader exited before its group is signalled and the leader reaped. Loss of original child ownership refuses further signalling. Controlled source checks cover ownership/IO boundaries; installed native/descendant effectiveness belongs to gad.2.
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "ace/support/cli"
5
+ require "ace/core"
6
+ require_relative "support"
7
+
8
+ module Ace
9
+ module Herdr
10
+ module CLI
11
+ module Commands
12
+ class Status < Ace::Support::Cli::Command
13
+ include Ace::Support::Cli::Base
14
+ include Runtime
15
+
16
+ desc <<~DESC.strip
17
+ Show the live herdr layout: workspaces, tabs, and panes in one view
18
+
19
+ Composes workspace/tab/pane lists into a readable tree. A live but
20
+ empty result is a valid state; an unreachable runtime is reported
21
+ as an error, never as an empty layout.
22
+ DESC
23
+
24
+ example [
25
+ "--workspace w1 # One workspace only",
26
+ "--format json # Composed tree as JSON"
27
+ ]
28
+
29
+ option :workspace, type: :string, desc: "Scope tabs and panes to a workspace id"
30
+ option :format, type: :string, default: "table", desc: "Output format: table or json"
31
+ option :quiet, type: :boolean, aliases: %w[-q], desc: "Suppress output"
32
+
33
+ def initialize(executor: nil)
34
+ @executor = executor
35
+ end
36
+
37
+ def call(**options)
38
+ translate_errors do
39
+ format = options[:format] || "table"
40
+ cli_error("Invalid format: #{format} (use table or json)") unless %w[table json].include?(format)
41
+
42
+ control = Organisms::ControlSurface.new(executor: executor)
43
+ scope = options[:workspace]
44
+ tree = Molecules::StatusComposer.call(
45
+ workspaces: control.list_workspaces,
46
+ tabs: control.list_tabs(workspace_id: scope),
47
+ panes: control.list_panes(workspace_id: scope)
48
+ )
49
+ tree = scope_tree(tree, scope) if scope
50
+
51
+ return if options[:quiet]
52
+
53
+ if format == "json"
54
+ puts JSON.generate(tree)
55
+ else
56
+ puts render_table(tree)
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ # Scoping must never conflate "scoped away" with "empty": the
64
+ # rendered tree contains only the requested workspace (header
65
+ # counts included), and an id no live workspace carries is a
66
+ # concrete error, not a silently full or empty layout.
67
+ def scope_tree(tree, scope)
68
+ live_ids = tree[:workspaces].reject { |workspace| workspace[:synthetic] }.map { |workspace| workspace[:id] }
69
+ cli_error("Unknown workspace: #{scope} (no live workspace with that id)") unless live_ids.include?(scope)
70
+
71
+ tree[:workspaces] = tree[:workspaces].select { |workspace| workspace[:id] == scope }
72
+ tree
73
+ end
74
+
75
+ def render_table(tree)
76
+ workspaces = tree[:workspaces]
77
+ return "No live herdr workspaces." if workspaces.empty?
78
+
79
+ tab_total = workspaces.sum { |workspace| workspace[:tab_count] }
80
+ pane_total = workspaces.sum { |workspace| workspace[:pane_count] }
81
+ lines = ["Herdr: #{workspaces.length} workspace(s), #{tab_total} tab(s), #{pane_total} pane(s)"]
82
+ workspaces.each do |workspace|
83
+ lines << format(
84
+ "%s %-14s %-16s %d tab(s), %d pane(s)",
85
+ workspace[:focused] ? "*" : " ", workspace[:id], workspace[:title].to_s,
86
+ workspace[:tab_count], workspace[:pane_count]
87
+ )
88
+ workspace[:tabs].each do |tab|
89
+ lines << format(
90
+ " %s %-18s %-14s %d pane(s)",
91
+ tab[:focused] ? "*" : " ", tab[:id], tab[:title].to_s, tab[:pane_count]
92
+ )
93
+ tab[:panes].each do |pane|
94
+ lines << pane_line(pane)
95
+ end
96
+ end
97
+ end
98
+ lines.join("\n")
99
+ end
100
+
101
+ def pane_line(pane)
102
+ title = pane[:title].to_s.empty? ? "—" : pane[:title]
103
+ line = format(
104
+ " %s %-8s %-14s %s",
105
+ pane[:focused] ? "*" : " ", pane[:id], title, shorten_home(pane[:cwd].to_s)
106
+ )
107
+ line += " agent: #{pane[:agent_status]}" if pane[:agent_status]
108
+ line
109
+ end
110
+
111
+ def shorten_home(path)
112
+ home = File.join(Dir.home, "")
113
+ path.start_with?(home) ? path.sub(home, "~/") : path
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
data/lib/ace/herdr/cli.rb CHANGED
@@ -10,6 +10,7 @@ require_relative "cli/commands/wait"
10
10
  require_relative "cli/commands/close"
11
11
  require_relative "cli/commands/tidy"
12
12
  require_relative "cli/commands/list"
13
+ require_relative "cli/commands/status"
13
14
  require_relative "cli/commands/send"
14
15
  require_relative "cli/commands/capture"
15
16
  require_relative "cli/commands/workspace"
@@ -32,6 +33,7 @@ module Ace
32
33
  ["dispatch", "Start an agent in one command: tab + agent + prompt"],
33
34
  ["native-source", "Describe, build, or verify the packaged guarded native source"],
34
35
  ["list", "List live panes, tabs, or workspaces as one JSON line"],
36
+ ["status", "Show live herdr layout: workspaces, tabs, and panes in one view"],
35
37
  ["send", "Send a command, raw text, or named keys to a pane"],
36
38
  ["capture", "Print recent pane output as raw text"],
37
39
  ["wait", "Wait for an agent state or matching pane output"],
@@ -48,6 +50,7 @@ module Ace
48
50
  "echo 'the answer' | ace-herdr deliver --pane p5",
49
51
  "ace-herdr dispatch --label 8wm.t.vs0 --kind pi --prompt-file prompt.md",
50
52
  "ace-herdr list --workspace w1",
53
+ "ace-herdr status --workspace w1",
51
54
  "ace-herdr send --pane p5 --cmd 'bundle exec rake test'",
52
55
  "ace-herdr send --pane p5 --msg 'continue' --key Enter",
53
56
  "ace-herdr capture --pane p5 --lines 40",
@@ -73,6 +76,7 @@ module Ace
73
76
  register "dispatch", CLI::Commands::Dispatch.new
74
77
  register "native-source", CLI::Commands::NativeSource.new
75
78
  register "list", CLI::Commands::List.new
79
+ register "status", CLI::Commands::Status.new
76
80
  register "send", CLI::Commands::Send.new
77
81
  register "capture", CLI::Commands::Capture.new
78
82
  register "wait", CLI::Commands::Wait.new
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ace
4
+ module Herdr
5
+ module Molecules
6
+ # Pure join of the three normalized live lists (workspaces, tabs,
7
+ # panes) into one nested status tree. Counts are recomputed from the
8
+ # nesting so the tree is internally consistent even if the runtime's
9
+ # own counters raced; panes referencing a tab or workspace missing
10
+ # from the lists are kept in a deterministic synthetic "(unlinked)"
11
+ # group instead of being dropped (task 8xg.t.sbz.6.0: a source gap
12
+ # must stay visible, never become a "clean" empty layout).
13
+ class StatusComposer
14
+ UNLINKED = "(unlinked)"
15
+
16
+ # @param workspaces [Array<Hash>] normalized workspace hashes
17
+ # @param tabs [Array<Hash>] normalized tab hashes
18
+ # @param panes [Array<Hash>] normalized pane hashes
19
+ # @return [Hash] {workspaces: [...]} with nested tabs/panes and
20
+ # recomputed counts
21
+ def self.call(workspaces:, tabs:, panes:)
22
+ new.compose(workspaces: workspaces, tabs: tabs, panes: panes)
23
+ end
24
+
25
+ def compose(workspaces:, tabs:, panes:)
26
+ panes_by_tab = group_panes(panes)
27
+ tabs_by_workspace = group_tabs(tabs, panes_by_tab)
28
+
29
+ roots = build_workspaces(workspaces, tabs_by_workspace, panes_by_tab)
30
+
31
+ synthetic_root = build_synthetic_root(tabs_by_workspace, panes_by_tab)
32
+ roots << synthetic_root if synthetic_root
33
+
34
+ {workspaces: roots}
35
+ end
36
+
37
+ private
38
+
39
+ def group_panes(panes)
40
+ panes.group_by { |pane| pane[:tab] }
41
+ end
42
+
43
+ def group_tabs(tabs, panes_by_tab)
44
+ # Tabs carry the panes nested under them; orphan panes whose tab
45
+ # is absent from the tab list get a synthetic tab inside their
46
+ # own workspace so the pane stays visible.
47
+ tabs_by_workspace = Hash.new { |hash, key| hash[key] = [] }
48
+ tabs.each do |tab|
49
+ tabs_by_workspace[tab[:workspace]] << compose_tab(tab, panes_by_tab.delete(tab[:id]) || [])
50
+ end
51
+
52
+ panes_by_tab.each do |tab_id, orphan_panes|
53
+ # Panes sharing a vanished tab id may still disagree on their
54
+ # workspace (snapshot race): each workspace keeps its own
55
+ # synthetic tab so neither side silently undercounts.
56
+ orphan_panes.group_by { |pane| pane[:workspace] }.each do |workspace_id, workspace_panes|
57
+ tabs_by_workspace[workspace_id] << synthetic_tab(tab_id, workspace_id, workspace_panes)
58
+ end
59
+ end
60
+ tabs_by_workspace
61
+ end
62
+
63
+ def build_workspaces(workspaces, tabs_by_workspace, panes_by_tab)
64
+ ordered(workspaces).map do |workspace|
65
+ nested_tabs = sort_tabs(tabs_by_workspace.delete(workspace[:id]) || [])
66
+ compose_workspace(workspace, nested_tabs)
67
+ end
68
+ end
69
+
70
+ def build_synthetic_root(tabs_by_workspace, panes_by_tab)
71
+ return nil if tabs_by_workspace.empty?
72
+
73
+ nested_tabs = sort_tabs(tabs_by_workspace.values.flatten)
74
+ compose_workspace(
75
+ {id: UNLINKED, title: UNLINKED, number: nil, focused: nil, synthetic: true},
76
+ nested_tabs
77
+ )
78
+ end
79
+
80
+ def compose_workspace(workspace, nested_tabs)
81
+ pane_count = nested_tabs.sum { |tab| tab[:pane_count] }
82
+ {
83
+ id: workspace[:id],
84
+ title: workspace[:title],
85
+ number: workspace[:number],
86
+ focused: workspace[:focused],
87
+ synthetic: workspace[:synthetic] || false,
88
+ tab_count: nested_tabs.length,
89
+ pane_count: pane_count,
90
+ tabs: nested_tabs
91
+ }
92
+ end
93
+
94
+ def compose_tab(tab, nested_panes)
95
+ nested_panes = ordered(nested_panes)
96
+ {
97
+ id: tab[:id],
98
+ title: tab[:title],
99
+ number: tab[:number],
100
+ focused: tab[:focused],
101
+ synthetic: tab[:synthetic] || false,
102
+ pane_count: nested_panes.length,
103
+ panes: nested_panes
104
+ }
105
+ end
106
+
107
+ def synthetic_tab(tab_id, workspace_id, orphan_panes)
108
+ {
109
+ id: tab_id,
110
+ workspace: workspace_id,
111
+ title: UNLINKED,
112
+ number: nil,
113
+ focused: nil,
114
+ synthetic: true
115
+ }.then { |tab| compose_tab(tab, orphan_panes) }
116
+ end
117
+
118
+ def sort_tabs(tabs)
119
+ # Synthetic tabs sort last within their workspace
120
+ synthetic, natural = tabs.partition { |tab| tab[:synthetic] }
121
+ ordered(natural) + ordered(synthetic)
122
+ end
123
+
124
+ # Deterministic ordering: number, then id; nil numbers last
125
+ def ordered(items)
126
+ items.sort_by { |item| [item[:number] ? 0 : 1, item[:number] || 0, item[:id].to_s] }
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ace
4
4
  module Herdr
5
- VERSION = "0.6.1"
5
+ VERSION = "0.8.0"
6
6
  end
7
7
  end
data/lib/ace/herdr.rb CHANGED
@@ -25,6 +25,7 @@ require_relative "herdr/organisms/inbox_context_owner"
25
25
  require_relative "herdr/organisms/inbox_context_server"
26
26
  require_relative "herdr/molecules/inbox_context_client"
27
27
  require_relative "herdr/organisms/dispatcher"
28
+ require_relative "herdr/molecules/status_composer"
28
29
  require_relative "herdr/organisms/control_surface"
29
30
  require_relative "herdr/organisms/runtime_adapter"
30
31
  require_relative "herdr/organisms/tidy"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ace-herdr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-10-16 00:00:00.000000000 Z
10
+ date: 2026-10-20 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: websocket-driver
@@ -230,6 +230,7 @@ files:
230
230
  - lib/ace/herdr/cli/commands/list_presets.rb
231
231
  - lib/ace/herdr/cli/commands/native_source.rb
232
232
  - lib/ace/herdr/cli/commands/send.rb
233
+ - lib/ace/herdr/cli/commands/status.rb
233
234
  - lib/ace/herdr/cli/commands/support.rb
234
235
  - lib/ace/herdr/cli/commands/tab.rb
235
236
  - lib/ace/herdr/cli/commands/tidy.rb
@@ -261,6 +262,7 @@ files:
261
262
  - lib/ace/herdr/molecules/preset_resolver.rb
262
263
  - lib/ace/herdr/molecules/protected_inbox_selection.rb
263
264
  - lib/ace/herdr/molecules/protected_native_control.rb
265
+ - lib/ace/herdr/molecules/status_composer.rb
264
266
  - lib/ace/herdr/native_source/HERDR-LICENSE
265
267
  - lib/ace/herdr/native_source/guarded-prompt.patch
266
268
  - lib/ace/herdr/native_source/selection.json