ace-hitl 0.17.0 → 0.23.2

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +162 -0
  3. data/README.md +60 -117
  4. data/docs/usage.md +247 -421
  5. data/handbook/workflow-instructions/hitl.wf.md +55 -6
  6. data/lib/ace/hitl/adapters/registry.rb +65 -0
  7. data/lib/ace/hitl/adapters/terminal.rb +75 -0
  8. data/lib/ace/hitl/adapters.rb +80 -0
  9. data/lib/ace/hitl/atoms/hitl_address.rb +89 -0
  10. data/lib/ace/hitl/atoms/hitl_event_text.rb +54 -0
  11. data/lib/ace/hitl/cli/commands/create.rb +81 -12
  12. data/lib/ace/hitl/cli/commands/lifecycle_command.rb +1 -7
  13. data/lib/ace/hitl/cli/commands/list.rb +22 -1
  14. data/lib/ace/hitl/cli/commands/send.rb +85 -0
  15. data/lib/ace/hitl/cli/commands/serve.rb +25 -44
  16. data/lib/ace/hitl/cli/commands/show.rb +23 -3
  17. data/lib/ace/hitl/cli/commands/update.rb +4 -0
  18. data/lib/ace/hitl/cli/commands/wait.rb +5 -0
  19. data/lib/ace/hitl/cli.rb +8 -18
  20. data/lib/ace/hitl/lifecycle/frame_serving.rb +0 -15
  21. data/lib/ace/hitl/lifecycle/kinds.rb +1 -1
  22. data/lib/ace/hitl/lifecycle/lifecycle_requests.rb +0 -36
  23. data/lib/ace/hitl/lifecycle/policy.rb +0 -44
  24. data/lib/ace/hitl/lifecycle/protocol.rb +1 -2
  25. data/lib/ace/hitl/lifecycle/service.rb +2 -3
  26. data/lib/ace/hitl/lifecycle/store.rb +11 -75
  27. data/lib/ace/hitl/lifecycle/tls_access_policy.rb +0 -14
  28. data/lib/ace/hitl/lifecycle/tls_project_scope.rb +0 -15
  29. data/lib/ace/hitl/lifecycle/tls_service.rb +2 -3
  30. data/lib/ace/hitl/lifecycle.rb +2 -4
  31. data/lib/ace/hitl/live_client.rb +11 -87
  32. data/lib/ace/hitl/models/hitl_event.rb +35 -0
  33. data/lib/ace/hitl/molecules/hitl_address_resolver.rb +80 -0
  34. data/lib/ace/hitl/molecules/hitl_config_loader.rb +22 -1
  35. data/lib/ace/hitl/molecules/hitl_creator.rb +126 -27
  36. data/lib/ace/hitl/molecules/hitl_delivery_state.rb +58 -0
  37. data/lib/ace/hitl/molecules/hitl_display_formatter.rb +3 -2
  38. data/lib/ace/hitl/molecules/hitl_loader.rb +19 -2
  39. data/lib/ace/hitl/molecules/hitl_route_loader.rb +108 -0
  40. data/lib/ace/hitl/organisms/hitl_manager.rb +73 -6
  41. data/lib/ace/hitl/organisms/hitl_sender.rb +148 -0
  42. data/lib/ace/hitl/providers/lab.rb +15 -129
  43. data/lib/ace/hitl/version.rb +1 -1
  44. data/lib/ace/hitl.rb +8 -1
  45. metadata +12 -43
  46. data/lib/ace/hitl/cli/commands/ask.rb +0 -136
  47. data/lib/ace/hitl/cli/commands/overseer_ack.rb +0 -30
  48. data/lib/ace/hitl/cli/commands/overseer_pending.rb +0 -28
  49. data/lib/ace/hitl/cli/commands/overseer_send.rb +0 -35
  50. data/lib/ace/hitl/cli/commands/proposal.rb +0 -75
  51. data/lib/ace/hitl/lifecycle/overseer.rb +0 -111
  52. data/lib/ace/hitl/lifecycle/proposals.rb +0 -297
  53. data/lib/ace/hitl/lifecycle/service_publication_binding.rb +0 -97
  54. data/lib/ace/hitl/proposals/evaluator.rb +0 -29
  55. data/lib/ace/hitl/proposals/policy.rb +0 -127
  56. data/lib/ace/hitl/providers/lab/assignment_binding.rb +0 -126
  57. data/lib/ace/hitl/providers/lab/protected_assignment_binding.rb +0 -75
  58. data/lib/ace/hitl/providers/providers.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ab176a502d7a751a22e89f461245046caa44d71943897b7f27b6d5ffdd10c84
4
- data.tar.gz: f789375f072b07f087462b78fd2d2fb7a9ae45e167eedbd413a11fe09e9ead0e
3
+ metadata.gz: 973228979e65843573a7dc67bc91566a22478ca762f3751babdbd269e0960ca9
4
+ data.tar.gz: c4b09f84c60c2bfb225294d56c4bfa4abded7ced7ecc75fe830ecc8987e239ee
5
5
  SHA512:
6
- metadata.gz: b6f1c00953d56deac3fe8e7099a1aac527681b1314aeda336c7691de04bf7ab40af33c8815ddcac76777e02151609c555cb0f4f0be2276e8a7b8ab0359b3a9ae
7
- data.tar.gz: 19e978992b0041ae887a26d743555fe69b8292fd01c763c488bef96e6feecb00c3437f58909d015eeaefa21884b552b203af47f94e583b2feef4e8bc3f4e06fc
6
+ metadata.gz: f08442f7582db2b088cbaa5f0af24d9d81bc7caccdf5e2bdfc9de5feb6a9ee9f5125f541f02e9585f4921933fa08a712fe47e80e8c668dd89330084654e13984
7
+ data.tar.gz: c3ad787393cc3c045efda84016b9e2783fa8fb5f3696e4a9da9e072840dfb99e12f706c79bafc6b655f90f4ccd35f661d0f3b05932a96d00d17ed563a63b27c1
data/CHANGELOG.md CHANGED
@@ -7,6 +7,168 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.23.2] - 2026-10-20
11
+
12
+ ### Fixed
13
+
14
+ - Select addressed-event routes by configured recipient names resolved to the
15
+ exact stored address. Unrelated terminal/Telegram routes no longer send or
16
+ create false delivery failures. Zero matches fail before adapter IO.
17
+ **Configuration migration:** add `recipients: [name]` to each addressed route;
18
+ names must exist in `hitl.recipients`. Recipient-less local events retain their
19
+ explicitly configured route set.
20
+ - Accept native Herdr panel handles such as `w7:p1` without changing machine or
21
+ folder validation.
22
+ - Provide first-answer-wins `HitlManager#accept_answer` for native transport
23
+ imports; exact replays preserve bytes and conflicting answers cannot replace
24
+ an existing answer under the case write lock.
25
+
26
+ ## [0.23.1] - 2026-10-18
27
+
28
+ ### Fixed
29
+
30
+ - Terminal routes are machine-scoped (round-9 review H1): the adapter now
31
+ reads the route's `machine` (defaults to `hitl.machine`) and refuses a
32
+ panel recipient on a different machine instead of delivering to a
33
+ local pane that merely shares the panel name and reporting a false
34
+ `delivered`. The refusal mirrors the hermes adapter, so both shipped
35
+ adapters apply the same address check.
36
+
37
+ ### Changed
38
+
39
+ - Docs only: `docs/usage.md` now documents that route delivery requires
40
+ a panel-form recipient (folder-form recipients have no shipped delivery
41
+ route and are refused per-route), the per-route refusal semantics of
42
+ both shipped adapters, per-route `unavailable` retention for unknown
43
+ adapters, dotted route-name rejection, and the absolute `reply_to`
44
+ folder. `HitlConfigLoader.normalize` notes that mixing flat/shaped
45
+ config documents across tiers can invert cascade precedence.
46
+
47
+ ## [0.23.0] - 2026-10-18
48
+
49
+ ### Fixed
50
+
51
+ - Adapters deliver to the event's recipient, not just the route's own
52
+ destination (PR #383 audit F1): the terminal adapter sends to the
53
+ recipient's `panel` (the route `pane` remains the fallback only for
54
+ legacy recipient-less events) and explicitly refuses a folder-form
55
+ recipient it cannot serve — never a silent send to the route pane.
56
+ Flat hitl config documents are no longer silently dropped (F11):
57
+ `.ace/hitl/config.yml` without the `hitl:` key now merges over the
58
+ shipped defaults instead of being shadowed by them. The default reply
59
+ address is built from the resolved sender folder (F6): an absolute
60
+ path that identifies the originating worktree, so two worktrees on
61
+ one machine never share a reply address (the relative `root_dir`
62
+ setting stays relative in config). Route names containing a dot
63
+ (e.g. `captain.telegram`) are rejected before any delivery attempt
64
+ (F10): delivery retention keys route names literally and a dot would
65
+ nest frontmatter keys, breaking retry/skip — `send` now fails closed
66
+ with `InvalidRouteError` naming the route.
67
+
68
+ ### Changed
69
+
70
+ - An unknown adapter on one route no longer aborts the whole send
71
+ (PR #383 audit F5): the route is retained as `unavailable` with the
72
+ resolution error as its reason (never a bare `in_flight` marker), the
73
+ remaining routes still deliver, and the send still reports overall
74
+ delivery failure.
75
+
76
+ ## [0.22.0] - 2026-10-18
77
+
78
+ ### Added
79
+
80
+ - Retention and redelivery of undelivered messages (8xg.t.sbz.1.3 / L2.4):
81
+ `send` now persists each route's last outcome, failure reason, and attempt
82
+ timestamp on the event's own file (`delivery.<route>` frontmatter — the
83
+ single state owner per ADR-034; no journal or queue). Default `send`
84
+ retries only undelivered or unknown-result routes and skips confirmed
85
+ delivered ones; `--force` redelivers every route and `--dry-run` reports
86
+ the retry/skip decisions without delivering. A `in_flight` marker written
87
+ before each attempt means a crashed send leaves an unknown result that the
88
+ next `send` retries — at-least-once semantics: a retry may duplicate, and
89
+ every rendering carries the same `(case <ID>)` + task binding so
90
+ recipients dedup by ID (exactly-once is not promised). Redelivery retries
91
+ the delivery, never the assignment. `list --delivery undelivered|delivered`
92
+ discovers retained events; `show` prints a per-route `Delivery:` block
93
+ with the retained failure reason. Redelivering an all-delivered event is a
94
+ no-op success (`nothing to retry (use --force to redeliver)`).
95
+
96
+ ## [0.21.0] - 2026-10-18
97
+
98
+ ### Added
99
+
100
+ - Configurable adapter delivery routes (8xg.t.sbz.1.2 / L2.3): `ace-hitl
101
+ send <ref>` delivers a persisted event through the routes configured under
102
+ `hitl.adapters.routes` — the sender interface is adapter-independent, so
103
+ switching transports is pure configuration (no assignment or coworker
104
+ change). Every route reports its own result line; one route's success never
105
+ hides another's failure and an unavailable adapter never silently reroutes
106
+ (per-route isolation, fail-closed unknown-adapter/no-route errors).
107
+ Built-in `terminal` adapter pushes the shared event text to a pane via any
108
+ `ace-runtime` terminal (tmux/herdr); `ace-hitl-hermes` 0.8.0 ships the
109
+ `hermes` adapter entrypoint (publishes the shared rendering to the channel
110
+ folder; identical resend is idempotent, different-bytes collision fails
111
+ without overwrite, missing folder is `unavailable`). New adapters register
112
+ through the duck-typed `Ace::Hitl::Adapters` registry
113
+ (`lib/ace/hitl/adapters/<name>.rb` entrypoint — a future Nervus adapter
114
+ needs no ace-hitl change). Shared rendering (`Atoms::HitlEventText`):
115
+ `[HITL question]`/`[HITL report <result>]` prefix, `Reply-To:` on questions
116
+ only, task/assignment/step context. Delivery results are reported, not
117
+ persisted — retention and redelivery of undelivered messages is
118
+ 8xg.t.sbz.1.3.
119
+
120
+ ## [0.20.0] - 2026-10-18
121
+
122
+ ### Added
123
+
124
+ - Recipient addressing and Reply binding (8xg.t.sbz.1.1 / L2.2): `ace-hitl
125
+ create ... --to <name>` resolves a configured recipient into an explicit
126
+ address — machine + events folder + case ID (folder form, ID stamped from
127
+ the new event) or machine + panel — read from the `hitl` cascade
128
+ (`machine`, `default_recipient`, `recipients:<name>:{machine,folder|panel}`).
129
+ Questions record a `reply_to` address (sender machine + events folder + own
130
+ case ID) so answers bind to the original case and the right agent;
131
+ task/assignment/step stay context, never an ID substitute. Local Reply is
132
+ unchanged: `update <ref> --answer` bound by exact ID with strict ambiguity
133
+ and explicit not-found — no write to a foreign event. Unknown recipient
134
+ names, malformed configured addresses, and a missing `hitl.machine` fail
135
+ closed before anything is written. `show`/create output print
136
+ `Recipient:`/`Reply-To:`; `HitlEvent#recipient`/`#reply_to` expose the
137
+ address hashes for adapters (delivery itself lands in 8xg.t.sbz.1.2/.1.3;
138
+ local write is not delivery).
139
+
140
+ ## [0.19.0] - 2026-10-18
141
+
142
+ ### Fixed (review PR #383, round 3)
143
+
144
+ - Report events carry no `kind` (loader returns nil), so question-oriented
145
+ `list --kind` filters never mix reports into a clarification list.
146
+
147
+ ### Added
148
+
149
+ - Report events: the same `ace-hitl` file interface now records work-done/error notifications alongside questions. `ace-hitl create "..." --type report --result success|error [--body line]... [--task REF]` writes an event with status `recorded`, a `## Report` body, task/assignment/step references, and its own ID. Reports never await an answer: `wait`, `update --answer`, and `update --resume` on a report fail fast with an explicit error (exit 1); generic `--set/--add/--remove/--move-to` still work so an overseer can archive processed reports.
150
+ - `--type question` is now written explicitly on new question events (loader defaults keep existing files without `type` valid questions); `--task` is accepted for both types; `list --type question|report` filters by type; `show` prints `Type:`/`Result:` and list gains the `recorded` status symbol.
151
+ - Library guards: `HitlManager#update(answer:)` raises `NotAnswerableError` on reports before any file mutation; `wait_for_answer` and `dispatch_resume` return `{status: :report}` without sleeping.
152
+
153
+ ### Notes
154
+
155
+ - Local write is not delivery: `recorded` means the report is persisted in `.ace-local/hitl/`, not that it reached the overseer. Remote addressing, reply routing, and configurable adapters land in 8xg.t.sbz.1.1–.1.3.
156
+
157
+ ## [0.18.0] - 2026-10-18
158
+
159
+ ### Removed
160
+
161
+ - Remove the installed Lab provider and its attempt-bound managed HITL path: `Providers::Lab::AssignmentBinding`, `ProtectedAssignmentBinding`, the provider `ask`/native-delivery methods, the `ask` CLI command, and the Herdr-native delivery client in `LiveClient` (the old ace-assign `AttemptCoordinator`/journal identity).
162
+ - Remove the second-commander proposal surface: the `proposal` CLI command, `Lifecycle::Proposals`, the proposal policy/evaluator, proposal frames, and the publication HITL binding tied to the removed publication authority.
163
+ - Remove managed and TLS `serve` modes: `serve` requires `--operator-only` and fails clearly otherwise.
164
+ - Remove the unused old-lab Root Overseer reverse-address channel: `Lifecycle::Overseer`, the `overseer-send`/`overseer-pending`/`overseer-ack` CLI commands, the default `mo` responder, the `ACE_HITL_OVERSEER_CHANNEL_ROOT`/`/lab/state/overseer-channel` default, and the `[decyzja]`/`[pytanie]`/`[info]` response policy. Generic HITL transport and the ready lifecycle surface are unchanged; there is no replacement queue or relay for this role policy.
165
+ - Remove the now-dead `LifecycleCommand::STDIN_READER` constant (`deliver` reads stdin inline) and add a Duty projection regression test covering the answer-delivered pending state and mixed effect visibility.
166
+
167
+ ### Changed
168
+
169
+ - `LiveClient` keeps the requester-side authenticated boundary operations (`wait`, `status`, `pending`); the lifecycle/relay transport (`Lifecycle::*`) remains for `ace-hitl-hermes`.
170
+ - Drop the now-unused `ace-assign` and `ace-herdr` runtime dependencies (the managed attempt binding required them); re-lock the workspace Gemfile.lock locally.
171
+
10
172
  ## [0.17.0] - 2026-10-16
11
173
 
12
174
  ### Added
data/README.md CHANGED
@@ -1,137 +1,80 @@
1
1
  # ace-hitl
2
2
 
3
- `ace-hitl` manages ACE human-in-the-loop (HITL) events in `.ace-local/hitl/`.
3
+ Human-in-the-loop (HITL) event files for ACE agents and operators.
4
+
5
+ > **State owner:** `.ace-local/hitl/` -- HITL event markdown files owned by
6
+ > `ace-hitl`. Other tools must not write there directly. The agent event
7
+ > history is local and untracked. The canonical flow is
8
+ > [docs/lab-flow.md](../docs/lab-flow.md).
4
9
 
5
10
  Canonical workflow and skill for agents:
6
11
 
7
12
  - Workflow: `wfi://hitl`
8
13
  - Skill: `as-hitl`
9
14
 
10
- ## Commands
11
-
12
- - `ace-hitl create` creates a HITL event
13
- - `ace-hitl ask` asks a human via HITL and forwards the request through a provider adapter (`--provider`, default `lab`); ONE operation: local event + relay request through the authenticated boundary (`--assignment/--attempt/--project` managed binding, effect callback flags)
14
- - `ace-hitl list` lists HITL events with filters (`--scope current|all`, all statuses by default)
15
- - `ace-hitl show` renders event details, path, or raw content (`--scope current|all`)
16
- - `ace-hitl update` updates frontmatter, answer content, and folder location
17
- - `ace-hitl wait` polls a specific HITL event until answered (`--poll-every`, `--timeout`)
18
- - `ace-hitl deliver` answers a pending relay request from stdin (configured transport operation through the boundary); executes the declared effect callback (non-OTP kinds)
19
- - `ace-hitl consume` consumes one own relay request's answer (indefinite by default; `--timeout` bounds only the local wait)
20
- - `ace-hitl cancel` cancels with an audited reason (the only way to abandon a request)
21
- - `ace-hitl pending` / `ace-hitl states` / `ace-hitl duty` transport projections (pending, public lifecycle records, pending + escalated)
22
- - `ace-hitl serve` runs the authenticated store boundary service (peer-credential UNIX socket; trusted grants authorize the transport)
23
-
24
- Multi-user HITL state (spec 8wq.t.34i) is the PRIVATE state of the
25
- `ace-hitl serve` boundary: identity comes from kernel peer
26
- credentials, transport authority from the trusted grants document,
27
- and OTP secrets are held in service memory only — never in files,
28
- logs, or projections.
29
- - `ace-hitl overseer-send` / `ace-hitl overseer-pending` / `ace-hitl overseer-ack` the Overseer reverse-address response channel
30
-
31
- `ace-hitl` is a blocker-resolution tool, not a global dashboard:
32
-
33
- - linked worktree default: local (`--scope current`)
34
- - main checkout default: operator view (`--scope all`)
35
-
36
- Use `ace-overseer status` for a global worktree dashboard.
37
-
38
- ## Provider adapters
39
-
40
- `ace-hitl ask --question ... --assignment ... --attempt ...` creates a scoped
41
- request through authenticated IPC. The managed coordinator verifies the exact
42
- active native owner and reverse address; caller environment variables cannot
43
- supply authority. No Lab daemon or Work binding is used.
44
-
45
- `Ace::Hitl::LiveClient` provides explicit in-process `deliver`, `watch`, `wait`,
46
- `status`, `pending`, and signed `reconcile`. `watch` returns a thread owned by
47
- its calling agent. Native delivery commits an incarnation-bound event through
48
- Herdr Inbox and registers it through Assign. Queue acceptance and wake do not
49
- prove consumption. Only an event-bound signed native observation verified by
50
- Herdr and journaled by Assign completes delivery. Business callbacks have
51
- separate authorization and receipts and are never rerun by native retries.
52
-
53
- Pane-less `wait --request ID` consumes an existing authorized request over IPC;
54
- it neither creates a native target nor proves a business effect. OTP answers
55
- use that protected path with their authorized operation; no OTP bytes or OTP
56
- hash enter the shared envelope, folder or native inbox.
15
+ ## What exists today
16
+
17
+ `ace-hitl` manages HITL events stored as markdown files under `.ace-local/hitl/`.
18
+ An event records a question or decision, its context (assignment/step), its
19
+ status, and the answer — or a report (work done / error) that never awaits an
20
+ answer. This is the local, file-based path; it needs no
21
+ listener, daemon, or database.
22
+
23
+ | Command | Purpose |
24
+ |---------|---------|
25
+ | `ace-hitl create` | Create a question event (kind, questions, assignment/step context, resume instructions) or a report event (`--type report --result success\|error --body ... --task ...`) |
26
+ | `ace-hitl list` | List events with status/kind/type/tag/folder/scope filters |
27
+ | `ace-hitl show` | Render event details, path, or raw markdown |
28
+ | `ace-hitl update` | Update frontmatter, write an answer, or move the event |
29
+ | `ace-hitl wait` | Poll one question event until it is answered (reports are rejected) |
30
+ | `ace-hitl send` | Deliver an event through the configured adapter routes; per-route results retained on the event, redelivery retries undelivered/unknown routes |
31
+
32
+ `ace-hitl` is a blocker-resolution tool, not a global dashboard: in a linked
33
+ worktree it defaults to local (`--scope current`); in the main checkout it uses
34
+ the operator view (`--scope all`). Use `ace-overseer status` for a worktree
35
+ dashboard.
57
36
 
58
37
  ## Examples
59
38
 
60
39
  ```bash
61
- ace-hitl list
62
- ace-hitl list --scope all
63
- ace-hitl create "Which auth strategy?" --kind decision --question "JWT or sessions?"
64
- ace-hitl ask --question "Proceed with deploy?" --assignment assign685 --attempt attempt685 --effect-arg /bin/false --effect-cwd /tmp
65
- ace-hitl ask --question "Proceed with deploy?" --provider lab --assignment assign685 --attempt attempt685
40
+ ace-hitl create "Which auth strategy?" --kind decision \
41
+ --question "JWT or sessions?" --assignment abc123 --step 020
42
+ ace-hitl create "Work done" --type report --result success \
43
+ --body "Implemented JWT login" --task 8xg.t.sbz.1.0
44
+ ace-hitl list --status pending
45
+ ace-hitl list --type report
66
46
  ace-hitl show abc123 --content
67
- ace-hitl show abc123 --scope current
68
47
  ace-hitl update abc123 --answer "Use JWT with server-side refresh tokens."
69
- ace-hitl wait abc123
70
- ace-hitl update abc123 --answer "Use JWT with server-side refresh tokens." --resume
48
+ ace-hitl update abc123 --move-to archive
49
+ ace-hitl wait abc123 --poll-every 600 --timeout 14400
71
50
  ```
72
51
 
73
- ## Testing
74
-
75
- This package has deterministic fast and scoped integration tests.
76
-
77
- - Deterministic test coverage lives under `test/fast/`.
78
- - This migration does not introduce `test/feat/` or `test/e2e/` for this package.
79
-
80
- Verification commands:
81
-
82
- - `ace-test ace-hitl`
83
- - `ace-test ace-hitl all`
52
+ ## Transport boundary
53
+
54
+ > The installed Lab provider and the attempt-bound managed HITL path (`ask`,
55
+ > provider assignment binding, native Herdr delivery, managed/TLS `serve`) were
56
+ > removed with the old authority model. The local file-based HITL events above
57
+ > remain the active path. The lifecycle/relay transport (`Lifecycle::*`) and
58
+ > its commands remain for `ace-hitl-hermes` and other adapters; `serve` supports
59
+ > `--operator-only` only. Second-commander proposals are removed as well.
60
+ >
61
+ > **L2 status:** report/question distinction, recipient addressing
62
+ > (`--to`, `reply_to`), adapter-route delivery (`send` with the built-in
63
+ > terminal adapter and `ace-hitl-hermes` as the hermes adapter), and
64
+ > retention/redelivery of undelivered messages (per-route records on the
65
+ > event; `send` retries undelivered/unknown routes, `--force` redelivers
66
+ > with the same case ID) are available. `ace-hitl-hermes` remains an
67
+ > adapter package; tmux/Herdr remain transports.
84
68
 
85
- ## Ownership Boundary
86
-
87
- `ace-hitl` owns HITL-specific event semantics and markdown contract.
88
-
89
- `ace-support-items` remains generic support infrastructure and should not absorb HITL-specific domain behavior.
90
-
91
- ## Unassigned operator questions
92
-
93
- `ace-hitl operator-question ID --project lab-config --question "Which option?"`
94
- creates a nonsecret question under an explicit installed operator grant. An
95
- optional `--plan` adds question context; `--options A,B` supplies bounded choices.
96
- The owner binds the exact Captain-visible body including this context and choices. It does not create an Assign execution,
97
- request a command, grant a decision, or permit OTP/effects. Stable IDs support
98
- exact retries after lost responses, restart and consumption; changed content,
99
- principal, project or context is refused. `ace-hitl read ID` returns authenticated
100
- nonsecret facts; existing `consume ID` retrieves the requester's answer and
101
- replays its terminal receipt.
102
-
103
- The public Ruby client exposes `operator_question(id:, project:, question:,
104
- plan: "", options: [])`, `read`, and the existing pending/deliver/consume/cancel
105
- methods. The request records the actual kernel peer UID/name. No caller can supply
106
- requester attribution, assignment, attempt, OTP, effect or publication fields.
107
- Secret-shaped questions, context, choices and answers are refused.
108
-
109
- An operator-only service uses the existing owner and Unix protocol:
69
+ ## Testing
110
70
 
111
- ```sh
112
- ace-hitl serve --operator-only --group ace-hitl-project \
113
- --socket /run/ace-hitl-project/lifecycle.sock --store-root /var/lib/ace-hitl-project \
114
- --grants-path /etc/ace-hitl-project/authorization.yml
71
+ ```bash
72
+ bin/ace-test ace-hitl
73
+ bin/ace-test ace-hitl all
115
74
  ```
116
75
 
117
- The root-controlled grants document must explicitly name `hitl.service_uid`,
118
- `hitl.operator_uids`, `hitl.transport_uids` and each UID's exact project list in
119
- `authorization.principals`. Operator grants never interpret an empty project list
120
- as all projects. Existing trusted-file ownership/traversal checks apply. The
121
- operator-only binding refuses managed requests and requires no Assign journal.
122
- Admission and requester grants are checked again on reads, answer transitions,
123
- retries and terminal replay; transport delivery retains its independent project
124
- grant. Revocation preserves request and terminal history. Operator questions
125
- cannot be consumed as native execution or authorized-operation claims.
126
-
127
- `ping` advertises the supported `request_contexts` (`managed` and/or
128
- `operator-question`), not a grant to create or execute work. Hermes project
129
- routes may require `request_contexts: ["operator-question"]` at startup. The
130
- shared contract uses a closed tagged union of managed execution requests and
131
- operator questions; neither missing IDs nor an unknown schema select a fallback.
132
-
133
- One Unix UID with both roles is one OS trust domain. Use a distinct transport
134
- principal where Captain reply isolation is required. Source tests using one UID
135
- for both roles do not prove that isolation. Domain adapters must separately
136
- preserve legacy unresolved host questions and prove the installed group handoff;
137
- this API never migrates or deletes that legacy history.
76
+ ## Ownership boundary
77
+
78
+ `ace-hitl` owns HITL event semantics and the markdown contract.
79
+ `ace-support-items` remains generic support infrastructure and does not absorb
80
+ HITL-specific behaviour.