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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +162 -0
- data/README.md +60 -117
- data/docs/usage.md +247 -421
- data/handbook/workflow-instructions/hitl.wf.md +55 -6
- data/lib/ace/hitl/adapters/registry.rb +65 -0
- data/lib/ace/hitl/adapters/terminal.rb +75 -0
- data/lib/ace/hitl/adapters.rb +80 -0
- data/lib/ace/hitl/atoms/hitl_address.rb +89 -0
- data/lib/ace/hitl/atoms/hitl_event_text.rb +54 -0
- data/lib/ace/hitl/cli/commands/create.rb +81 -12
- data/lib/ace/hitl/cli/commands/lifecycle_command.rb +1 -7
- data/lib/ace/hitl/cli/commands/list.rb +22 -1
- data/lib/ace/hitl/cli/commands/send.rb +85 -0
- data/lib/ace/hitl/cli/commands/serve.rb +25 -44
- data/lib/ace/hitl/cli/commands/show.rb +23 -3
- data/lib/ace/hitl/cli/commands/update.rb +4 -0
- data/lib/ace/hitl/cli/commands/wait.rb +5 -0
- data/lib/ace/hitl/cli.rb +8 -18
- data/lib/ace/hitl/lifecycle/frame_serving.rb +0 -15
- data/lib/ace/hitl/lifecycle/kinds.rb +1 -1
- data/lib/ace/hitl/lifecycle/lifecycle_requests.rb +0 -36
- data/lib/ace/hitl/lifecycle/policy.rb +0 -44
- data/lib/ace/hitl/lifecycle/protocol.rb +1 -2
- data/lib/ace/hitl/lifecycle/service.rb +2 -3
- data/lib/ace/hitl/lifecycle/store.rb +11 -75
- data/lib/ace/hitl/lifecycle/tls_access_policy.rb +0 -14
- data/lib/ace/hitl/lifecycle/tls_project_scope.rb +0 -15
- data/lib/ace/hitl/lifecycle/tls_service.rb +2 -3
- data/lib/ace/hitl/lifecycle.rb +2 -4
- data/lib/ace/hitl/live_client.rb +11 -87
- data/lib/ace/hitl/models/hitl_event.rb +35 -0
- data/lib/ace/hitl/molecules/hitl_address_resolver.rb +80 -0
- data/lib/ace/hitl/molecules/hitl_config_loader.rb +22 -1
- data/lib/ace/hitl/molecules/hitl_creator.rb +126 -27
- data/lib/ace/hitl/molecules/hitl_delivery_state.rb +58 -0
- data/lib/ace/hitl/molecules/hitl_display_formatter.rb +3 -2
- data/lib/ace/hitl/molecules/hitl_loader.rb +19 -2
- data/lib/ace/hitl/molecules/hitl_route_loader.rb +108 -0
- data/lib/ace/hitl/organisms/hitl_manager.rb +73 -6
- data/lib/ace/hitl/organisms/hitl_sender.rb +148 -0
- data/lib/ace/hitl/providers/lab.rb +15 -129
- data/lib/ace/hitl/version.rb +1 -1
- data/lib/ace/hitl.rb +8 -1
- metadata +12 -43
- data/lib/ace/hitl/cli/commands/ask.rb +0 -136
- data/lib/ace/hitl/cli/commands/overseer_ack.rb +0 -30
- data/lib/ace/hitl/cli/commands/overseer_pending.rb +0 -28
- data/lib/ace/hitl/cli/commands/overseer_send.rb +0 -35
- data/lib/ace/hitl/cli/commands/proposal.rb +0 -75
- data/lib/ace/hitl/lifecycle/overseer.rb +0 -111
- data/lib/ace/hitl/lifecycle/proposals.rb +0 -297
- data/lib/ace/hitl/lifecycle/service_publication_binding.rb +0 -97
- data/lib/ace/hitl/proposals/evaluator.rb +0 -29
- data/lib/ace/hitl/proposals/policy.rb +0 -127
- data/lib/ace/hitl/providers/lab/assignment_binding.rb +0 -126
- data/lib/ace/hitl/providers/lab/protected_assignment_binding.rb +0 -75
- data/lib/ace/hitl/providers/providers.rb +0 -33
data/docs/usage.md
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
---
|
|
2
2
|
doc-type: user
|
|
3
3
|
title: ace-hitl Usage Guide
|
|
4
|
-
purpose: Practical CLI
|
|
5
|
-
flows.
|
|
4
|
+
purpose: Practical CLI reference for local HITL event creation, triage, and answer flows.
|
|
6
5
|
ace-docs:
|
|
7
|
-
last-updated:
|
|
8
|
-
last-checked:
|
|
6
|
+
last-updated: 2026-10-17
|
|
7
|
+
last-checked: 2026-10-17
|
|
9
8
|
---
|
|
10
9
|
|
|
11
10
|
# ace-hitl Usage
|
|
12
11
|
|
|
12
|
+
> **State owner:** `.ace-local/hitl/` -- HITL event markdown files owned by
|
|
13
|
+
> `ace-hitl`. See [docs/lab-flow.md](../../docs/lab-flow.md).
|
|
14
|
+
|
|
13
15
|
Canonical handbook resources:
|
|
14
16
|
|
|
15
17
|
- Workflow: `wfi://hitl`
|
|
16
18
|
- Skill: `as-hitl`
|
|
17
19
|
|
|
18
|
-
Runtime store default: `.ace-local/hitl
|
|
19
|
-
|
|
20
|
-
## Testing
|
|
20
|
+
Runtime store default: `.ace-local/hitl/`.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
## Event files
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
Each event is a markdown file with frontmatter (`id`, `title`, `kind`,
|
|
25
|
+
`status`, `tags`, `assignment`, `step`, `resume`) and the question/answer
|
|
26
|
+
sections. Folders:
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
- `.ace-local/hitl/` -- active events (`next`)
|
|
29
|
+
- `.ace-local/hitl/archive/` -- archived events
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
```
|
|
31
|
+
`--scope current` reads only the current worktree's events; `--scope all` reads
|
|
32
|
+
the main checkout's operator view. Without `--scope`, lookup is local-first and
|
|
33
|
+
falls back across worktrees when smart scope is active.
|
|
33
34
|
|
|
34
35
|
## Create
|
|
35
36
|
|
|
@@ -38,254 +39,261 @@ ace-hitl create "Which auth strategy?" \
|
|
|
38
39
|
--kind decision \
|
|
39
40
|
--question "JWT or sessions?" \
|
|
40
41
|
--question "Refresh token storage?" \
|
|
41
|
-
--assignment
|
|
42
|
+
--assignment abc123 \
|
|
42
43
|
--step 020 \
|
|
43
44
|
--step-name implement-auth \
|
|
44
|
-
--resume "/as-assign-drive
|
|
45
|
+
--resume "/as-assign-drive abc123"
|
|
45
46
|
```
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
Kinds: `clarification`, `decision`, `approval`. Completion handoff uses
|
|
49
|
+
`--step completion`:
|
|
48
50
|
|
|
49
51
|
```bash
|
|
50
52
|
ace-hitl create "Review completed assignment results" \
|
|
51
53
|
--kind approval \
|
|
52
|
-
--question "Please confirm next action for
|
|
53
|
-
--assignment
|
|
54
|
-
--step completion \
|
|
55
|
-
--step-name assignment-complete \
|
|
56
|
-
--resume "/as-assign-drive 8qr5kx"
|
|
54
|
+
--question "Please confirm next action for abc123." \
|
|
55
|
+
--assignment abc123 --step completion
|
|
57
56
|
```
|
|
58
57
|
|
|
59
|
-
##
|
|
60
|
-
|
|
61
|
-
`ace-hitl list` is local-first by default:
|
|
58
|
+
## Recipient addressing and Reply
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
Every event can carry an explicit recipient address read from the `hitl`
|
|
61
|
+
configuration cascade (`.ace/hitl/config.yml` project override; HITL reads
|
|
62
|
+
the address config — consumers pass a configured name, no client-side
|
|
63
|
+
routing):
|
|
66
64
|
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
```yaml
|
|
66
|
+
hitl:
|
|
67
|
+
machine: lab01 # this machine's identity for addresses
|
|
68
|
+
default_recipient: overseer # optional name applied when --to is absent
|
|
69
|
+
recipients:
|
|
70
|
+
overseer: # panel form: machine + panel (route-deliverable)
|
|
71
|
+
machine: lab01
|
|
72
|
+
panel: overseer # the pane/channel the recipient reads
|
|
73
|
+
captain: # panel form on another machine
|
|
74
|
+
machine: lab02
|
|
75
|
+
panel: telegram
|
|
76
|
+
archive: # folder form: machine + events folder (+ case ID)
|
|
77
|
+
machine: lab01
|
|
78
|
+
folder: .ace-local/hitl
|
|
76
79
|
```
|
|
77
80
|
|
|
78
|
-
## Show
|
|
79
|
-
|
|
80
|
-
`ace-hitl show` also accepts `--scope current|all`.
|
|
81
|
-
Without `--scope`, lookup is local-first; if not found and smart scope is active, it retries across all worktrees.
|
|
82
|
-
When lookup resolves outside the current worktree, output includes an explicit `Resolved Location:` line.
|
|
83
|
-
When using `--scope all`, ambiguous matches return an error with candidate paths so the operator can select the intended event explicitly.
|
|
84
|
-
|
|
85
81
|
```bash
|
|
86
|
-
ace-hitl
|
|
87
|
-
ace-hitl show abc123 --scope current
|
|
88
|
-
ace-hitl show abc123 --scope all
|
|
89
|
-
ace-hitl show abc123 --path
|
|
90
|
-
ace-hitl show abc123 --content
|
|
82
|
+
ace-hitl create "Blocked on auth" --kind decision --to overseer --task 8xg.t.sbz.1.1
|
|
91
83
|
```
|
|
92
84
|
|
|
93
|
-
|
|
85
|
+
- The folder form stamps the new event's ID as the case ID; the panel form
|
|
86
|
+
carries no case ID. Questions additionally record a `reply_to` address
|
|
87
|
+
(sender machine + events folder + own case ID), so an answer binds to the
|
|
88
|
+
original case and the right agent — task/assignment/step are context,
|
|
89
|
+
never an ID substitute.
|
|
90
|
+
- Route delivery (`send`) requires a panel-form recipient. A folder-form
|
|
91
|
+
recipient has no shipped delivery adapter: selected adapters refuse it per-route
|
|
92
|
+
with a clear `failed` reason (the event file already sitting in a folder
|
|
93
|
+
is a local write, not a delivery confirmation). Refusal semantics are
|
|
94
|
+
strict on both axes of the address: the terminal adapter delivers only
|
|
95
|
+
when the recipient's `machine` matches the route's machine (default
|
|
96
|
+
`hitl.machine`) and its `panel` names the target pane; the hermes
|
|
97
|
+
adapter delivers only when its channel folder is the recipient's target
|
|
98
|
+
(panel equal to the channel name on the route's machine). A route never
|
|
99
|
+
silently sends to its own configured destination for a recipient it
|
|
100
|
+
cannot serve.
|
|
101
|
+
- The `reply_to` folder is the resolved absolute sender events folder (not
|
|
102
|
+
the configured `root_dir` setting, which stays relative), so two
|
|
103
|
+
worktrees on one machine never share a reply address.
|
|
104
|
+
- Local Reply is unchanged: `update <ref> --answer` binds by exact ID with
|
|
105
|
+
strict ambiguity handling and an explicit not-found error; a transport
|
|
106
|
+
later reads `reply_to` from the answered event instead of searching for the
|
|
107
|
+
"last question".
|
|
108
|
+
- Unknown recipient names, malformed configured addresses, and a missing
|
|
109
|
+
`hitl.machine` (needed for `reply_to`) fail closed before anything is
|
|
110
|
+
written. Ambiguous shortcuts list candidates instead of guessing.
|
|
111
|
+
- `--to` absent with no `default_recipient` creates a local-only event with
|
|
112
|
+
no address keys (legacy flow unchanged). `show` prints `Recipient:` and
|
|
113
|
+
`Reply-To:` when present; local write is not delivery (adapters and
|
|
114
|
+
redelivery land in the remaining L2 leaves).
|
|
115
|
+
|
|
116
|
+
## Reports
|
|
117
|
+
|
|
118
|
+
The same interface records reports — completion or error notifications that do
|
|
119
|
+
not await an answer:
|
|
94
120
|
|
|
95
121
|
```bash
|
|
96
|
-
ace-hitl
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
ace-hitl
|
|
122
|
+
ace-hitl create "Work done" \
|
|
123
|
+
--type report --result success \
|
|
124
|
+
--body "Implemented JWT login" \
|
|
125
|
+
--body "Tests green" \
|
|
126
|
+
--task 8xg.t.sbz.1.0 --assignment abc123 --step 010
|
|
127
|
+
|
|
128
|
+
ace-hitl create "Step failed" \
|
|
129
|
+
--type report --result error \
|
|
130
|
+
--body "Verifier crashed after merge" \
|
|
131
|
+
--task 8xg.t.sbz.1.0 --step 020
|
|
103
132
|
```
|
|
104
133
|
|
|
105
|
-
|
|
134
|
+
Rules:
|
|
106
135
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
136
|
+
- `--type report` requires `--result success|error`; `--question`, `--kind`,
|
|
137
|
+
and `--resume` are rejected on reports.
|
|
138
|
+
- `--result` and `--body` are only valid with `--type report`.
|
|
139
|
+
- Reports are created with status `recorded` and never appear as pending
|
|
140
|
+
questions: `wait <report>`, `update <report> --answer`, and
|
|
141
|
+
`update <report> --resume` exit 1 with an explicit error. Generic updates
|
|
142
|
+
(`--set`, `--add`, `--remove`, `--move-to archive`) still work.
|
|
143
|
+
- `--task` is accepted for both questions and reports.
|
|
144
|
+
- Local write is not delivery: `recorded` means the event is persisted under
|
|
145
|
+
`.ace-local/hitl/`, not that it reached the recipient. Remote addressing and
|
|
146
|
+
configurable adapters are planned follow-up leaves (8xg.t.sbz.1.1–.1.3).
|
|
112
147
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
and Runtime's existing process ancestry/birth authority. Missing peer PID or
|
|
116
|
-
native evidence refuses an exact-owner claim. Darwin uses LOCAL_PEERPID; Linux
|
|
117
|
-
uses SO_PEERCRED. UID equality alone never selects another attempt. There is no
|
|
118
|
-
Work binding, Lab daemon socket or environment-derived reverse target.
|
|
148
|
+
Questions keep the existing pending → answered cycle; files without a `type`
|
|
149
|
+
frontmatter key load as questions.
|
|
119
150
|
|
|
120
|
-
|
|
151
|
+
## Send — delivery routes
|
|
121
152
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
watcher.value
|
|
126
|
-
client.status(request: request_id)
|
|
127
|
-
```
|
|
153
|
+
`create` persists locally; `send` is the explicit delivery step through the
|
|
154
|
+
configured adapter routes. Switching transports is pure configuration — no
|
|
155
|
+
assignment or coworker logic changes.
|
|
128
156
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
The shared versioned envelope is semantically owned by HITL and packaged in
|
|
148
|
-
`ace-hitl-contract` to preserve the acyclic HITL → Assign → Herdr → contract
|
|
149
|
-
graph. Its nested Hermes message and reverse reference are distinct schemas.
|
|
150
|
-
OTP answers and their hashes are excluded from the envelope and native delivery.
|
|
151
|
-
|
|
152
|
-
## Scoped Store Boundary (spec 8wq.t.34i)
|
|
153
|
-
|
|
154
|
-
The relay request store is the PRIVATE state of one authenticated
|
|
155
|
-
boundary service. `ace-hitl serve` owns the store and speaks a bounded
|
|
156
|
-
JSON protocol over a UNIX socket; every client operation
|
|
157
|
-
(`ask`/`deliver`/`consume`/`cancel`/`pending`/`states`/`duty`/`read`)
|
|
158
|
-
runs through the authenticated boundary client — the CLI never touches
|
|
159
|
-
shared store files directly.
|
|
160
|
-
|
|
161
|
-
Identity is a kernel fact: the service resolves each connection's peer
|
|
162
|
-
uid from the OS (`getpeereid`), and payload fields or environment
|
|
163
|
-
variables can never name a requester. Clients authenticate the
|
|
164
|
-
endpoint back: the socket must be owned by the trusted service uid,
|
|
165
|
-
must not be world-writable, and the connected peer must BE the service
|
|
166
|
-
uid. Authorization facts (service uid, transport uids, per-project
|
|
167
|
-
Captain visibility) come only from the trusted deployment grants
|
|
168
|
-
document — the same file and traversal trust rules as ace-lab
|
|
169
|
-
(default `ACE_HITL_GRANTS_PATH`, `/etc/lab/ace-lab/authorization.yml`).
|
|
157
|
+
```yaml
|
|
158
|
+
hitl:
|
|
159
|
+
adapters:
|
|
160
|
+
routes:
|
|
161
|
+
- route: overseer-terminal # display name; defaults to the adapter name
|
|
162
|
+
recipients: [overseer] # configured names resolved to the stored destination
|
|
163
|
+
adapter: terminal # built-in: any ace-runtime terminal
|
|
164
|
+
runtime: herdr # tmux | herdr
|
|
165
|
+
pane: p5 # opaque pane handle (runtime-owned)
|
|
166
|
+
machine: lab01 # defaults to hitl.machine; recipient machine must match
|
|
167
|
+
- route: captain-telegram
|
|
168
|
+
recipients: [captain]
|
|
169
|
+
adapter: hermes # ships in ace-hitl-hermes
|
|
170
|
+
channel: project
|
|
171
|
+
machine: lab01 # defaults to hitl.machine
|
|
172
|
+
folder: /run/hermes/project
|
|
173
|
+
sender: lab01 # defaults to machine
|
|
174
|
+
```
|
|
170
175
|
|
|
171
176
|
```bash
|
|
172
|
-
|
|
173
|
-
ace-hitl serve --socket /run/ace-hitl-project/lifecycle.sock \
|
|
174
|
-
--store-root /var/lib/ace-hitl-project --group ace-hitl-project
|
|
175
|
-
# ACE_HITL_SOCKET, ACE_HITL_STORE_ROOT and ACE_HITL_GRANTS_PATH may supply paths.
|
|
177
|
+
ace-hitl send 8xhkep
|
|
176
178
|
```
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
`
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
`
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
180
|
+
```
|
|
181
|
+
HITL event 8xhkep sent through 1 route(s):
|
|
182
|
+
overseer-terminal (terminal): delivered - text pushed to herdr pane p5
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Rules:
|
|
186
|
+
|
|
187
|
+
- For an addressed event, only routes whose `recipients` resolve to its exact
|
|
188
|
+
stored machine and panel/folder are selected. Changing a configured alias does
|
|
189
|
+
not redirect an existing case. Missing selectors do not match addressed events;
|
|
190
|
+
no match fails before adapter invocation or delivery-state writes. Recipient-less
|
|
191
|
+
events use the explicitly configured route set.
|
|
192
|
+
- Several matching routes deliver to the same intended recipient independently.
|
|
193
|
+
Unmatched routes are not invoked or recorded as failures. One result line per
|
|
194
|
+
selected route; a route's success never hides another selected route's
|
|
195
|
+
failure. Exit 0 only when every route is `delivered`; exit 1 on any
|
|
196
|
+
`failed`/`unavailable` route, no configured routes (concrete error naming
|
|
197
|
+
`hitl.adapters.routes`), or unknown/ambiguous ref. An unknown adapter on
|
|
198
|
+
one route no longer aborts the send: that route is retained as
|
|
199
|
+
`unavailable` with the resolution error as its reason, the remaining
|
|
200
|
+
routes still deliver, and the send still exits 1.
|
|
201
|
+
- Route names must be a single token matching `[a-z0-9][a-z0-9_-]*`
|
|
202
|
+
(no dots): delivery retention keys route names literally, and `send`
|
|
203
|
+
rejects a dotted name (e.g. `captain.telegram`) before any delivery
|
|
204
|
+
attempt.
|
|
205
|
+
- Every route receives the identical shared rendering: `[HITL question]`/
|
|
206
|
+
`[HITL report <result>]` prefix, one `Q:` line per question, `Reply-To:`
|
|
207
|
+
on questions only, task/assignment/step context.
|
|
208
|
+
- `delivered` means accepted by the route's transport. An unavailable
|
|
209
|
+
adapter never reroutes to another recipient.
|
|
210
|
+
- Native terminal handles such as `w7:p1` are accepted unchanged as `panel`.
|
|
211
|
+
This does not relax the machine-token or folder-traversal rules.
|
|
212
|
+
- Adding an adapter requires no ace-hitl change: a gem ships
|
|
213
|
+
`lib/ace/hitl/adapters/<name>.rb` calling `Ace::Hitl::Adapters.register`
|
|
214
|
+
(duck contract: `factory.call(route)` → adapter with
|
|
215
|
+
`deliver(event:, text:, recipient:, reply_to:)` returning a
|
|
216
|
+
`RouteResult`; status `:delivered`/`:failed`/`:unavailable`). Nervus is
|
|
217
|
+
the out-of-scope future example.
|
|
218
|
+
|
|
219
|
+
### Retention and redelivery
|
|
220
|
+
|
|
221
|
+
Per-route outcomes are persisted on the event's own file
|
|
222
|
+
(`delivery.<route>` frontmatter) — undelivered messages and their failure
|
|
223
|
+
reasons stay in the local HITL files; there is no journal or queue.
|
|
216
224
|
|
|
217
225
|
```bash
|
|
218
|
-
ace-hitl
|
|
219
|
-
|
|
226
|
+
ace-hitl send 8xhkep # retries undelivered/unknown routes only
|
|
227
|
+
ace-hitl send 8xhkep --force # redeliver every route (same case ID)
|
|
228
|
+
ace-hitl send 8xhkep --dry-run # show retry/skip decisions, no delivery
|
|
229
|
+
ace-hitl list --delivery undelivered
|
|
230
|
+
ace-hitl show 8xhkep # Delivery: <route>: <status> - <reason>
|
|
220
231
|
```
|
|
221
232
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
233
|
+
- Confirmed `delivered` routes are skipped by default; a redelivery of a
|
|
234
|
+
fully delivered event is a no-op success (`use --force to redeliver`).
|
|
235
|
+
- A crashed `send` leaves an `in_flight` marker = unknown result; the next
|
|
236
|
+
`send` retries it. This is at-least-once: a retry after an unknown result
|
|
237
|
+
may duplicate, and every rendering carries the same `(case <ID>)` and task
|
|
238
|
+
binding so the recipient dedups by ID. Exactly-once is not promised.
|
|
239
|
+
- Redelivery retries the *delivery of the same case* — same event ID, same
|
|
240
|
+
content, same task/assignment binding — never the assignment or its next
|
|
241
|
+
step.
|
|
242
|
+
- The delivery filters are record-relative: an event that was never sent
|
|
243
|
+
appears in neither `--delivery` filter, and a route added to the
|
|
244
|
+
configuration after a full delivery does not flip a `delivered` event
|
|
245
|
+
back to `undelivered` (the next `send` still attempts the new route).
|
|
229
246
|
|
|
230
|
-
|
|
247
|
+
## List
|
|
231
248
|
|
|
232
249
|
```bash
|
|
233
|
-
ace-hitl
|
|
234
|
-
ace-hitl
|
|
235
|
-
ace-hitl
|
|
236
|
-
ace-hitl
|
|
250
|
+
ace-hitl list
|
|
251
|
+
ace-hitl list --scope current
|
|
252
|
+
ace-hitl list --scope all
|
|
253
|
+
ace-hitl list --status pending
|
|
254
|
+
ace-hitl list --kind decision
|
|
255
|
+
ace-hitl list --type question
|
|
256
|
+
ace-hitl list --type report
|
|
257
|
+
ace-hitl list --delivery undelivered
|
|
258
|
+
ace-hitl list --delivery delivered
|
|
259
|
+
ace-hitl list --tags auth,security
|
|
260
|
+
ace-hitl list --in archive
|
|
237
261
|
```
|
|
238
262
|
|
|
239
|
-
|
|
240
|
-
bytes go to the service's memory vault; plain answers to the
|
|
241
|
-
service-owned `0600` answers file. Liveness is re-verified under the
|
|
242
|
-
lock, and the declared effect callback (non-OTP kinds only) executes:
|
|
243
|
-
exec-style argv (never a shell), `{answer}` substituted once per
|
|
244
|
-
element, optional fullmatch regex gate, bounded timeout, attempts
|
|
245
|
-
logged redacted in the effects log, and one deduped escalation with
|
|
246
|
-
`effect_state: callback-escalated` in the public projection on failure
|
|
247
|
-
(`callback-ok` on success). Duplicate deliveries are idempotent: the
|
|
248
|
-
committed answer is reported without re-running any effect.
|
|
263
|
+
With no `--status`, all statuses in the selected folder scope are listed.
|
|
249
264
|
|
|
250
|
-
|
|
265
|
+
## Show
|
|
251
266
|
|
|
252
267
|
```bash
|
|
253
|
-
ace-hitl
|
|
254
|
-
ace-hitl
|
|
255
|
-
ace-hitl
|
|
268
|
+
ace-hitl show abc123
|
|
269
|
+
ace-hitl show abc123 --scope all
|
|
270
|
+
ace-hitl show abc123 --path
|
|
271
|
+
ace-hitl show abc123 --content
|
|
256
272
|
```
|
|
257
273
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
discards the pending secret. Memory retention ends at the earlier of the
|
|
265
|
-
challenge deadline and the vault TTL. Delivery, duplicate delivery, waits,
|
|
266
|
-
and service restarts cannot extend the authorized window. Request a fresh
|
|
267
|
-
authorized challenge after expiry. Cancel is the ONLY way to abandon a request, requester-only;
|
|
268
|
-
it records `cancelled_by` and the `reason` in the public projection,
|
|
269
|
-
and late answers/consumes fail closed against the committed receipt.
|
|
270
|
-
|
|
271
|
-
Overseer reverse address (bounded, type-tagged responses):
|
|
274
|
+
When lookup resolves outside the current worktree, output includes an explicit
|
|
275
|
+
`Resolved Location:` line. Ambiguous matches under `--scope all` list candidate
|
|
276
|
+
paths instead of guessing. Report events print `Type: report`, `Result:`, and
|
|
277
|
+
the report body instead of `Kind:`/`Questions:`/`Answer:`.
|
|
278
|
+
|
|
279
|
+
## Update
|
|
272
280
|
|
|
273
281
|
```bash
|
|
274
|
-
ace-hitl
|
|
275
|
-
ace-hitl
|
|
276
|
-
ace-hitl
|
|
282
|
+
ace-hitl update abc123 --set status=in-progress
|
|
283
|
+
ace-hitl update abc123 --add tags=reviewed
|
|
284
|
+
ace-hitl update abc123 --remove tags=stale
|
|
285
|
+
ace-hitl update abc123 --answer "Use JWT with server-side refresh tokens."
|
|
286
|
+
ace-hitl update abc123 --answer "close the assignment" --resume
|
|
287
|
+
ace-hitl update abc123 --move-to archive
|
|
288
|
+
ace-hitl update abc123 --move-to next
|
|
277
289
|
```
|
|
278
290
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
IDs, or the word "SHA" are rejected. `overseer-send` is the overseer
|
|
282
|
-
user's operation; `overseer-pending`/`overseer-ack` are host-broker
|
|
283
|
-
(root) operations used by the transport to drain and acknowledge
|
|
284
|
-
relayed responses.
|
|
291
|
+
`--answer` writes the `## Answer` body and marks the event answered. `--resume`
|
|
292
|
+
optionally dispatches the event's resume instructions after the answer.
|
|
285
293
|
|
|
286
|
-
## Wait
|
|
294
|
+
## Wait
|
|
287
295
|
|
|
288
|
-
|
|
296
|
+
Poll one specific event until it is answered:
|
|
289
297
|
|
|
290
298
|
```bash
|
|
291
299
|
ace-hitl wait abc123
|
|
@@ -293,28 +301,12 @@ ace-hitl wait abc123 --poll-every 600 --timeout 14400
|
|
|
293
301
|
ace-hitl wait abc123 --scope current
|
|
294
302
|
```
|
|
295
303
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
local event. Explicit pane-less scripts can wait without any local event:
|
|
299
|
-
|
|
300
|
-
```bash
|
|
301
|
-
ace-hitl wait --request hitl001 --timeout 30
|
|
302
|
-
ace-hitl wait --request otp001 --operation gem-push
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
The default managed wait is indefinite; a positive timeout bounds only this
|
|
306
|
-
call. It never expires the request. OTP consumption requires the authorized
|
|
307
|
-
operation and returns its bytes only over protected IPC. Local event polling
|
|
308
|
-
remains available for ordinary unbound local events. Managed events cannot use
|
|
309
|
-
`update --resume` to launch unscoped session/shell delivery.
|
|
310
|
-
|
|
311
|
-
Installed acceptance uses actual registered Telegram ingress, the selected
|
|
312
|
-
live Herdr/tmux terminal and the real scoped users in Lab. Controlled local
|
|
313
|
-
transport tests do not replace launching and observing the actual task.
|
|
304
|
+
The default wait is indefinite; a positive `--timeout` bounds only this call
|
|
305
|
+
and never expires the request. `wait` never invents an answer.
|
|
314
306
|
|
|
315
|
-
## Lifecycle
|
|
307
|
+
## Lifecycle event names
|
|
316
308
|
|
|
317
|
-
|
|
309
|
+
HITL lifecycle signals use this namespace:
|
|
318
310
|
|
|
319
311
|
- `hitl.event.created`
|
|
320
312
|
- `hitl.event.answered`
|
|
@@ -325,195 +317,29 @@ Canonical namespace for HITL lifecycle signaling:
|
|
|
325
317
|
- `hitl.event.resume_failed`
|
|
326
318
|
- `hitl.event.archived`
|
|
327
319
|
|
|
328
|
-
##
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
## Immutable second-commander proposals
|
|
348
|
-
|
|
349
|
-
`ace-hitl proposal create proposal-0123456789abcdef01234567 --assignment ID --attempt ID --project ID --file proposal.json`
|
|
350
|
-
returns persisted immutable proposal/revision/request IDs and current awaiting-delivery state immediately, even with unavailable transport. delivered_at and deadline are absent until acknowledged submission; show exposes them afterward. The sole Hermes
|
|
351
|
-
polling actor publishes the full precise proposal and records confirmed submission before
|
|
352
|
-
HITL persists delivered_at and a deadline exactly sixteen hours later. Failed or uncertain
|
|
353
|
-
submission cannot arm the window. A Telegram Reply `approve [rationale]`, `veto [rationale]`
|
|
354
|
-
or `clarify [rationale]` applies only to the correlated immutable revision; other replies
|
|
355
|
-
stop automatic approval and require revision. Missing rationale remains absent.
|
|
356
|
-
|
|
357
|
-
The JSON proposal requires operation, target (`resource` and optional `artifact_digest`),
|
|
358
|
-
candidate_head, input_digest, context, options, recommendation and prerequisites; rationale
|
|
359
|
-
is optional. Contents are bounded and non-secret. Input digest uses the canonical service
|
|
360
|
-
input digest. Candidate head is separate from base head and the evidence journal commit.
|
|
361
|
-
Only operation classes explicitly delegated in trusted project policy are eligible
|
|
362
|
-
for sixteen-hour silence. Delegation must exist when the revision is created and
|
|
363
|
-
remain current at the deadline; a later grant cannot approve an older revision.
|
|
364
|
-
Without delegation the proposal still accepts an explicit correlated decision.
|
|
365
|
-
Fixed service scope, current head,
|
|
366
|
-
independently executed review/tests, bound running attempt and operation-specific OTP gates
|
|
367
|
-
still apply at execution. Proposal authorization never supplies credentials or runs a callback.
|
|
368
|
-
|
|
369
|
-
All proposal reads, revisions and transport acknowledgements/replies carry an explicit project.
|
|
370
|
-
The authority selects that project's canonical Assign journal; a proposal ID does not select a journal.
|
|
371
|
-
The managed request's existing project supplies this selector for ordinary `read`; Hermes uses the
|
|
372
|
-
accepted envelope's project for acknowledgement, reply and reconciliation. Missing selectors are
|
|
373
|
-
rejected, and an ID from another project's journal cannot be read or changed through the selected project.
|
|
374
|
-
|
|
375
|
-
`ace-hitl proposal show ID --project PROJECT --format json` shows decision, deadline, actual Assign claim/outcome
|
|
376
|
-
and a bounded history page; continue with `--history-after HISTORY_NEXT`.
|
|
377
|
-
`ace-hitl proposal history --project ID --query TEXT` retrieves relevant prior decisions;
|
|
378
|
-
continue with `--after NEXT`. Current project grants and exact requester identity gate history and show; losing project access refuses reads and excludes history, including lifecycle proposal request reads.
|
|
379
|
-
`ace-hitl proposal revise ID --project PROJECT --expected-revision N --operation-id revision-abcdef0123456789abcdef01 --file changed.json` supersedes the prior decision and creates a
|
|
380
|
-
new request with a fresh full window after acknowledgement. An unresolved claimed effect
|
|
381
|
-
must be reconciled before revision; known successful or proven no-effect settlement can be
|
|
382
|
-
followed by a new revision. Persist the proposal ID, expected source revision, stable revision operation ID and exact file before invocation. The operation ID is `revision-` followed by 24 lowercase hex digits and is unique across proposals. Exact retry returns that committed revision, including after acknowledged delivery, approval or a later revision, without creating another request or resetting the window. Changed proposal/source/content/caller bindings and stale-source new operations are refused. Supersession and new prepared revision commit atomically against effect claims. Generic lifecycle `create` rejects proposal kind; use this canonical proposal interface.
|
|
383
|
-
|
|
384
|
-
Set ACE_HITL_SOCKET and ACE_HITL_PROJECT for the living overseer. It calls
|
|
385
|
-
`ace-hitl proposal resolve-due --project PROJECT` on start/status/watch ticks.
|
|
386
|
-
The authenticated proposer queues an idempotent reconciliation wake in the
|
|
387
|
-
canonical Assign proposal; the command returns `queued-for-transport`, never an
|
|
388
|
-
approval claim. The existing installed Hermes `serve` loop polls Telegram under
|
|
389
|
-
its own configured transport UID and reconciles queued deadlines after polling.
|
|
390
|
-
No proposer subprocess opens transport configuration or impersonates transport.
|
|
391
|
-
Hermes holds its ingress lock across checkpoint and HITL decision transition;
|
|
392
|
-
unknown health/backlog defers resolution. Failed ticks remain visible while
|
|
393
|
-
watch/status continues. Production `--now` is rejected.
|
|
394
|
-
|
|
395
|
-
Creation requires an explicit stable ID (`proposal-` followed by 24 lowercase
|
|
396
|
-
hex digits). Persist that ID before invocation and retry the exact same ID,
|
|
397
|
-
assignment, attempt, caller and document after failure or a lost reply. Changed
|
|
398
|
-
binding/content is refused. Assign commits the immutable prepared lifecycle
|
|
399
|
-
request first; exact retry or the transport pending scan materializes it after
|
|
400
|
-
restart. No pending lifecycle orphan exists before canonical commit. Concurrent
|
|
401
|
-
materialization creates once and preserves current delivered/answered state.
|
|
402
|
-
|
|
403
|
-
Unresolved earlier same-request ingress blocks later approval delivery. Exact
|
|
404
|
-
reply sequence/content/time deduplication uses canonical decision history;
|
|
405
|
-
unseen lower sequence is applied, and changed duplicate content is refused.
|
|
406
|
-
History grows in the existing canonical event chain, while public responses use
|
|
407
|
-
bounded pages. Hermes metadata never stores message bodies.
|
|
408
|
-
|
|
409
|
-
The immutable authorization reference is the revision ID, passed to `ace-lab service request`.
|
|
410
|
-
Assign atomically checks the canonical proposal under its sole journal claim lock/CAS;
|
|
411
|
-
a forged prefix/YAML string or changed operation/target/head/input/caller cannot authorize.
|
|
412
|
-
A late veto before claim denies execution. After claim it records stop_requested without
|
|
413
|
-
rewriting performed effects; the service rechecks it before safely stoppable invocation.
|
|
414
|
-
An uncertain claim remains uncertain on restart and is never automatically dispatched again.
|
|
415
|
-
Canonical sanitized decision history shares the qjl evidence ref; no parallel executor or
|
|
416
|
-
execution ledger exists in HITL. This source workflow is not installed/native/Telegram or
|
|
417
|
-
multi-UID acceptance proof.
|
|
418
|
-
|
|
419
|
-
### Proposal admission configuration
|
|
420
|
-
|
|
421
|
-
The protected HITL service reads proposer admission from the same trusted grants
|
|
422
|
-
policy as transport and project authorization. For example:
|
|
320
|
+
## Transport boundary
|
|
321
|
+
|
|
322
|
+
The installed Lab provider and the attempt-bound managed HITL path (`ask`, the
|
|
323
|
+
provider assignment binding, native Herdr delivery, managed/TLS `serve`) were
|
|
324
|
+
removed with the old authority model. The local file-based event commands above
|
|
325
|
+
remain the active path. The generic lifecycle/relay transport (`Lifecycle::*`)
|
|
326
|
+
and its commands (read/pending/states/duty/deliver/consume/cancel) remain for
|
|
327
|
+
adapters such as `ace-hitl-hermes`; `serve` supports `--operator-only` only.
|
|
328
|
+
The immutable second-commander proposal flow is removed, and so is the unused
|
|
329
|
+
old-lab Root Overseer reverse-address channel (`overseer-*` commands); generic
|
|
330
|
+
transport has no replacement role-specific queue.
|
|
331
|
+
|
|
332
|
+
> **L2 status:** the report/question distinction, recipient addressing
|
|
333
|
+
> (`--to`, `reply_to`), adapter-route delivery (`send` with the built-in
|
|
334
|
+
> terminal adapter and `ace-hitl-hermes` as the hermes adapter), and
|
|
335
|
+
> retention/redelivery of undelivered messages (`send` retry/`--force`,
|
|
336
|
+
> `list --delivery`, per-route `Delivery:` records) are available. Adapters
|
|
337
|
+
> (`ace-hitl-hermes`) and transports (tmux, Herdr) are not additional state
|
|
338
|
+
> owners.
|
|
423
339
|
|
|
424
|
-
|
|
425
|
-
hitl:
|
|
426
|
-
service_uid: 1200
|
|
427
|
-
transport_uids: [1201]
|
|
428
|
-
proposal_uids: [1202]
|
|
429
|
-
silence_operations:
|
|
430
|
-
ace: [merge]
|
|
431
|
-
authorization:
|
|
432
|
-
principals:
|
|
433
|
-
"1201":
|
|
434
|
-
projects: [ace]
|
|
435
|
-
"1202":
|
|
436
|
-
projects: [ace]
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
`proposal_uids` admits authenticated kernel peers to create and revise proposals
|
|
440
|
-
only for their configured projects. Transport admission alone cannot create a
|
|
441
|
-
proposal; proposer admission cannot acknowledge delivery, fabricate ingress, or
|
|
442
|
-
resolve silence. Missing role or project admission refuses the operation,
|
|
443
|
-
including direct library calls. This is a source configuration contract;
|
|
444
|
-
installed deployment adoption remains part of gad.8 acceptance.
|
|
445
|
-
|
|
446
|
-
`silence_operations` is an exact project-to-operation list, with no wildcards or
|
|
447
|
-
implicit publish/deploy/access grants. It never expands command or credential
|
|
448
|
-
authority. TLS authorities read the same trusted grants document selected by
|
|
449
|
-
`--grants-path`; certificate authentication alone grants no silence decision.
|
|
450
|
-
|
|
451
|
-
### Controlled installed proposal verification
|
|
452
|
-
|
|
453
|
-
From an ACE checkout with Ruby, tmux and the complete dependency archives cached locally:
|
|
340
|
+
## Testing
|
|
454
341
|
|
|
455
342
|
```bash
|
|
456
|
-
bin/ace-test ace-hitl
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
The explicit fixture configuration activates the installed scenario without changing
|
|
460
|
-
test deadlines. Default package runs skip it. The run builds the current runtime
|
|
461
|
-
gem closure and installs it into an empty GEM_HOME, using local archives only.
|
|
462
|
-
It prints `Installed SC3 artifacts: /tmp/ace-installed-sc3/run-...`; read the
|
|
463
|
-
reported test receipt and that directory's `result.json`, `artifacts.json`,
|
|
464
|
-
`build-provenance.json`, `processes.jsonl` and `canonical-evidence.json`. A manifest/result pointer is
|
|
465
|
-
retained under checkout `.ace-local/installed-sc3/`.
|
|
466
|
-
|
|
467
|
-
The expected terminal result is one passing test with no failures/errors. Its
|
|
468
|
-
consumer asserts confirmed submission + sixteen hours, actual service/actor
|
|
469
|
-
process restarts, one effective authorization and one executor invocation with
|
|
470
|
-
a canonical verified receipt. Failed submission does not arm a deadline; lost
|
|
471
|
-
poll coverage stays blocked, including after fresh polling. Changed technical
|
|
472
|
-
scope remains refused after approval. No inbox record is manually inserted.
|
|
473
|
-
|
|
474
|
-
UTC and Telegram are test-only injected adapters. Assignment binding, kernel peer
|
|
475
|
-
authentication, policy, claim, execution and receipt verification remain real.
|
|
476
|
-
The fixture uses one host and the current UID, with an isolated tmux process and
|
|
477
|
-
the existing local service mode. It does not prove live Telegram, protected Herdr
|
|
478
|
-
launch, installed root-owned grants, or multiUID privilege separation. There is
|
|
479
|
-
no production `--now` or transport-override option.
|
|
480
|
-
|
|
481
|
-
Missing local dependency archives fail visibly without network fallback. A failed
|
|
482
|
-
consumer retains diagnostic artifacts; inspect `result.json` and `service.log`
|
|
483
|
-
before retrying. Each invocation retains a new run directory and requires a new
|
|
484
|
-
empty GEM_HOME.
|
|
485
|
-
|
|
486
|
-
## The host/Incus mTLS crossing (nh0-A)
|
|
487
|
-
|
|
488
|
-
`ace-hitl serve --tls-authority FILE` runs one lifecycle authority of
|
|
489
|
-
the shared-Hermes topology on a loopback TCP endpoint where the
|
|
490
|
-
project's Incus TCP proxy device connects. The authority config
|
|
491
|
-
(`ace.hitl.tls.authority/v1`, closed fields, root-owned runtime file
|
|
492
|
-
`root:<service-group> 0640`) owns the loopback listen address, the
|
|
493
|
-
project ID, the service account names and the server credential
|
|
494
|
-
reference — never secret bytes:
|
|
495
|
-
|
|
496
|
-
```json
|
|
497
|
-
{"schema": "ace.hitl.tls.authority/v1",
|
|
498
|
-
"project_id": "proj-a", "listen": "127.0.0.1", "port": 8443,
|
|
499
|
-
"service_user": "hitl-proj-a", "service_group": "hitl-proj-a",
|
|
500
|
-
"tls_server_credential": {
|
|
501
|
-
"certificate_file": "/etc/ace-hitl/proj-a/server.pem",
|
|
502
|
-
"private_key_file": "/etc/ace-hitl/proj-a/server-key.pem",
|
|
503
|
-
"ca_file": "/etc/ace-hitl/proj-a/ca.pem",
|
|
504
|
-
"server_uri_san": "urn:ace:hitl:proj-a:lifecycle-server",
|
|
505
|
-
"allowed_client_uri_san": "urn:ace:hitl:proj-a:hermes-client",
|
|
506
|
-
"allowed_client_certificate_sha256": "<64 lowercase hex>"}}
|
|
343
|
+
bin/ace-test ace-hitl
|
|
344
|
+
bin/ace-test ace-hitl all
|
|
507
345
|
```
|
|
508
|
-
|
|
509
|
-
Startup enforces the exact provisioning layout (ancestors `root:root`
|
|
510
|
-
0755, project directory `root:<service-group>` 0750, private key
|
|
511
|
-
`<service-user>:<service-group>` 0400 and never hard-linked,
|
|
512
|
-
certificate/CA `root:root` 0444) BEFORE any socket opens, validates its
|
|
513
|
-
own certificate identity (single URI SAN, serverAuth EKU, key match),
|
|
514
|
-
speaks TLS 1.3 only, and admits exactly the configured client
|
|
515
|
-
SAN/fingerprint pair — bound to the authority's one project, with
|
|
516
|
-
pending flows and creates refused for any other project. Unix
|
|
517
|
-
peer-credential serving (`serve` without `--tls-authority`) remains the
|
|
518
|
-
same-runtime boundary. lab-config (gad.8/gad.b) owns accounts, file
|
|
519
|
-
provisioning, Incus proxy devices and the tailnet listeners.
|