ace-hitl 0.23.2 → 0.24.1
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 +12 -0
- data/README.md +12 -0
- data/handbook/workflow-instructions/hitl.wf.md +15 -7
- data/lib/ace/hitl/cli/commands/observe.rb +24 -0
- data/lib/ace/hitl/cli.rb +3 -0
- data/lib/ace/hitl/molecules/event_observation.rb +62 -0
- data/lib/ace/hitl/organisms/hitl_manager.rb +3 -1
- data/lib/ace/hitl/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 80aaacaa6ef70bbe80b87e00b4edee2f4cf935b679a115fd5e42f001ddb0f60b
|
|
4
|
+
data.tar.gz: 85b36915a24996df17f1a550f3c5ca017227482472183b0d27ff9b6bc09a65ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 387437e8824ee6c94bc89c6c683434c129821527a6cf80dd3983b7bf875551d3e782c59e344294569722cc5531a8862ab6633f4616e46bb46e55c6224bb6d808
|
|
7
|
+
data.tar.gz: 8035330d8eea2aed94243161bff6e93426befa2beda2e474d1bacd80341363a7777728851d068509b1e69a5472f0f31be9b93fcf9d13c6ae64c68538aa4602d5
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.24.1] - 2026-10-25
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Replace wait-by-polling with the semantic wake contract in the HITL workflow (PR74): one scoped watch per pending answer, end the turn, reconcile on wake.
|
|
15
|
+
|
|
16
|
+
## [0.24.0] - 2026-10-23
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Add read-only semantic event observations with configured-owner filtering; exclude delivery metadata and unrelated recipients.
|
|
21
|
+
|
|
10
22
|
## [0.23.2] - 2026-10-20
|
|
11
23
|
|
|
12
24
|
### Fixed
|
data/README.md
CHANGED
|
@@ -78,3 +78,15 @@ bin/ace-test ace-hitl all
|
|
|
78
78
|
`ace-hitl` owns HITL event semantics and the markdown contract.
|
|
79
79
|
`ace-support-items` remains generic support infrastructure and does not absorb
|
|
80
80
|
HITL-specific behaviour.
|
|
81
|
+
|
|
82
|
+
### Read semantic event revisions
|
|
83
|
+
|
|
84
|
+
`ace-hitl observe --scope current --limit 500` prints one JSON object with schema
|
|
85
|
+
`ace.hitl.observation/v1` and a bounded `events` list. `--scope all` includes linked
|
|
86
|
+
worktrees of this repository. Each row contains a hashed canonical file identity,
|
|
87
|
+
event ID, semantic revision hash, type/status and task/assignment references.
|
|
88
|
+
It emits no private message or answer text. It reads active (non-special-folder) canonical events and
|
|
89
|
+
does not import transports, acknowledge, archive, resume or mutate events.
|
|
90
|
+
Delivery/poll timestamps and filesystem mtime are excluded from revisions;
|
|
91
|
+
content, answers and report/status changes are included. Exceeding the configured
|
|
92
|
+
limit refuses the entire projection rather than silently truncating it.
|
|
@@ -3,8 +3,8 @@ doc-type: workflow
|
|
|
3
3
|
title: HITL Workflow
|
|
4
4
|
purpose: Standardize human-attention handling for blocked and completed work using ace-hitl events.
|
|
5
5
|
ace-docs:
|
|
6
|
-
last-updated: '2026-10-
|
|
7
|
-
last-checked: '2026-10-
|
|
6
|
+
last-updated: '2026-10-23'
|
|
7
|
+
last-checked: '2026-10-23'
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# HITL Workflow
|
|
@@ -98,11 +98,19 @@ ace-hitl show <hitl-id>
|
|
|
98
98
|
ace-hitl update <hitl-id> --answer "<human decision>"
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
ace-hitl
|
|
105
|
-
|
|
101
|
+
Waiting on a pending answer is semantic, never a blocking poll. While an
|
|
102
|
+
answer is pending (for example a RubyGems OTP window of ~30–45 s), the
|
|
103
|
+
waiting session registers exactly ONE in-process `/watch` scoped to the
|
|
104
|
+
project ace-hitl transport folder (the folder holding that case's
|
|
105
|
+
request/reply files, e.g. `.ace-local/hitl`), then ends its turn. On wake:
|
|
106
|
+
run the project `ace-hitl-hermes sync` ONCE, reconcile the exact case state
|
|
107
|
+
(`ace-hitl show <hitl-id>` — answered, not merely replies>0), and act within
|
|
108
|
+
the answer window or report the precise blocker. Never block a model turn
|
|
109
|
+
with sleep/poll loops (`ace-hitl wait` blocks the turn — it is a sleep/poll
|
|
110
|
+
command, not a waiting strategy), and never introduce cron, systemd, or any
|
|
111
|
+
external heartbeat — none exists and none may be added. Observed defect
|
|
112
|
+
(eyes-20261023 OTP wait): the supervising session blocked its turn with a
|
|
113
|
+
36x sleep-8 poll loop.
|
|
106
114
|
|
|
107
115
|
### 4b) Remote delivery through configured adapter routes
|
|
108
116
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "ace/support/cli"
|
|
3
|
+
require_relative "../../molecules/event_observation"
|
|
4
|
+
module Ace
|
|
5
|
+
module Hitl
|
|
6
|
+
module CLI
|
|
7
|
+
module Commands
|
|
8
|
+
class Observe < Ace::Support::Cli::Command
|
|
9
|
+
desc "Read bounded semantic event revisions without private message text"
|
|
10
|
+
option :scope, type: :string, default: "current", desc: "current or all linked worktrees"
|
|
11
|
+
option :owner, type: :string, desc: "Configured recipient owning incoming events"
|
|
12
|
+
option :limit, type: :integer, default: 500, desc: "Maximum event count (1..2000)"
|
|
13
|
+
def call(**options)
|
|
14
|
+
scope = options[:scope] || "current"
|
|
15
|
+
raise Ace::Support::Cli::Error, "invalid scope" unless %w[current all].include?(scope)
|
|
16
|
+
puts JSON.generate(Molecules::EventObservation.new.call(scope: scope, limit: options[:limit] || 500, owner: options[:owner]))
|
|
17
|
+
rescue ArgumentError => e
|
|
18
|
+
raise Ace::Support::Cli::Error, e.message
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/ace/hitl/cli.rb
CHANGED
|
@@ -8,6 +8,7 @@ require_relative "cli/commands/operator_question"
|
|
|
8
8
|
require_relative "cli/commands/read"
|
|
9
9
|
require_relative "cli/commands/show"
|
|
10
10
|
require_relative "cli/commands/list"
|
|
11
|
+
require_relative "cli/commands/observe"
|
|
11
12
|
require_relative "cli/commands/update"
|
|
12
13
|
require_relative "cli/commands/wait"
|
|
13
14
|
require_relative "cli/commands/deliver"
|
|
@@ -31,6 +32,7 @@ module Ace
|
|
|
31
32
|
["read", "Read authenticated lifecycle request facts"],
|
|
32
33
|
["show", "Show HITL event details"],
|
|
33
34
|
["list", "List HITL events"],
|
|
35
|
+
["observe", "Read semantic event revisions"],
|
|
34
36
|
["update", "Update HITL event metadata or answer"],
|
|
35
37
|
["send", "Send a HITL event through the configured adapter routes"],
|
|
36
38
|
["wait", "Wait for an answer on a specific HITL event"],
|
|
@@ -58,6 +60,7 @@ module Ace
|
|
|
58
60
|
register "read", CLI::Commands::Read
|
|
59
61
|
register "show", CLI::Commands::Show
|
|
60
62
|
register "list", CLI::Commands::List
|
|
63
|
+
register "observe", CLI::Commands::Observe
|
|
61
64
|
register "update", CLI::Commands::Update
|
|
62
65
|
register "send", CLI::Commands::Send
|
|
63
66
|
register "wait", CLI::Commands::Wait
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "digest"
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Ace
|
|
6
|
+
module Hitl
|
|
7
|
+
module Molecules
|
|
8
|
+
# Read-only semantic projection. No private text is emitted; delivery
|
|
9
|
+
# timestamps, filesystem metadata and observer acknowledgements are not
|
|
10
|
+
# decision changes. Canonical HITL remains the only event owner.
|
|
11
|
+
class EventObservation
|
|
12
|
+
def initialize(manager: Organisms::HitlManager.new)
|
|
13
|
+
@manager = manager
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(scope: "current", limit: 500, owner: nil)
|
|
17
|
+
raise ArgumentError, "invalid observation limit" unless (1..2000).cover?(limit)
|
|
18
|
+
events = @manager.list(in_folder: "next", scope: scope, limit: limit, strict: true)
|
|
19
|
+
raise ArgumentError, "observation exceeds configured event budget" if events.size > limit
|
|
20
|
+
address = owner && Molecules::HitlAddressResolver.new(config: @manager.config).resolve(owner)
|
|
21
|
+
events = events.select { |event| incoming?(event, address) } if address
|
|
22
|
+
rows = events.map do |event|
|
|
23
|
+
semantic = [event.id, event.type, event.status, event.result, event.kind,
|
|
24
|
+
event.title, event.content, event.answer, event.questions,
|
|
25
|
+
Array(event.tags).sort, canonical(event.recipient), canonical(event.reply_to),
|
|
26
|
+
event.metadata.values_at("task", "assignment", "step", "step_name", "resume_instructions")]
|
|
27
|
+
{"key" => Digest::SHA256.hexdigest(event.file_path.to_s),
|
|
28
|
+
"id" => event.id, "revision" => Digest::SHA256.hexdigest(JSON.generate(canonical(semantic))),
|
|
29
|
+
"type" => event.type, "status" => event.status, "result" => event.result,
|
|
30
|
+
"task" => event.metadata["task"], "assignment" => event.metadata["assignment"]}
|
|
31
|
+
end
|
|
32
|
+
{"schema" => "ace.hitl.observation/v1", "events" => rows.sort_by { |row| row["key"] }}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Questions created locally are outbound until actually answered. A
|
|
38
|
+
# worker's reply belongs to that worker, not every linked-worktree actor.
|
|
39
|
+
# Native instructions have no recipient; their explicit import root owns them.
|
|
40
|
+
def incoming?(event, address)
|
|
41
|
+
local = File.expand_path(event.file_path).start_with?(File.expand_path(@manager.root_dir) + File::SEPARATOR)
|
|
42
|
+
return local if Array(event.tags).include?("hermes-instruction")
|
|
43
|
+
if event.question? && event.answered?
|
|
44
|
+
return local && event.reply_to &&
|
|
45
|
+
event.reply_to["machine"] == @manager.config.dig("hitl", "machine") &&
|
|
46
|
+
File.expand_path(event.reply_to["folder"].to_s) == File.expand_path(@manager.root_dir)
|
|
47
|
+
end
|
|
48
|
+
recipient = event.recipient
|
|
49
|
+
recipient && address.all? { |key, value| recipient[key] == value }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def canonical(value)
|
|
53
|
+
case value
|
|
54
|
+
when Hash then value.sort.to_h.transform_values { |item| canonical(item) }
|
|
55
|
+
when Array then value.map { |item| canonical(item) }
|
|
56
|
+
else value
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -94,10 +94,12 @@ module Ace
|
|
|
94
94
|
}
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
def list(status: nil, kind: nil, type: nil, delivery: nil, in_folder: "next", tags: [], scope: nil)
|
|
97
|
+
def list(status: nil, kind: nil, type: nil, delivery: nil, in_folder: "next", tags: [], scope: nil, limit: nil, strict: false)
|
|
98
98
|
effective_scope = @scope_resolver.effective_scope(scope)
|
|
99
99
|
scan_results = scan_results_for_scope(effective_scope, in_folder: in_folder)
|
|
100
|
+
raise ArgumentError, "observation exceeds configured event budget" if limit && scan_results.size > limit
|
|
100
101
|
events = load_events(scan_results)
|
|
102
|
+
raise ArgumentError, "incomplete HITL observation" if strict && events.size != scan_results.size
|
|
101
103
|
|
|
102
104
|
events = events.select { |event| event.status == status } if status
|
|
103
105
|
events = events.select { |event| event.kind == kind } if kind
|
data/lib/ace/hitl/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-hitl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.24.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10-
|
|
10
|
+
date: 2026-10-25 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-hitl-contract
|
|
@@ -160,6 +160,7 @@ files:
|
|
|
160
160
|
- lib/ace/hitl/cli/commands/duty.rb
|
|
161
161
|
- lib/ace/hitl/cli/commands/lifecycle_command.rb
|
|
162
162
|
- lib/ace/hitl/cli/commands/list.rb
|
|
163
|
+
- lib/ace/hitl/cli/commands/observe.rb
|
|
163
164
|
- lib/ace/hitl/cli/commands/operator_question.rb
|
|
164
165
|
- lib/ace/hitl/cli/commands/pending.rb
|
|
165
166
|
- lib/ace/hitl/cli/commands/read.rb
|
|
@@ -198,6 +199,7 @@ files:
|
|
|
198
199
|
- lib/ace/hitl/lifecycle/tls_service.rb
|
|
199
200
|
- lib/ace/hitl/live_client.rb
|
|
200
201
|
- lib/ace/hitl/models/hitl_event.rb
|
|
202
|
+
- lib/ace/hitl/molecules/event_observation.rb
|
|
201
203
|
- lib/ace/hitl/molecules/hitl_address_resolver.rb
|
|
202
204
|
- lib/ace/hitl/molecules/hitl_answer_editor.rb
|
|
203
205
|
- lib/ace/hitl/molecules/hitl_config_loader.rb
|