ace-assign 0.63.1 → 0.66.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 +4 -4
- data/.ace-defaults/assign/catalog/steps/auto-merge.step.yml +11 -0
- data/.ace-defaults/assign/catalog/steps/create-pr.step.yml +2 -2
- data/.ace-defaults/assign/catalog/steps/mark-pr-ready.step.yml +1 -1
- data/.ace-defaults/assign/catalog/steps/release-minor.step.yml +4 -2
- data/.ace-defaults/assign/catalog/steps/release.step.yml +4 -2
- data/.ace-defaults/assign/catalog/steps/update-pr-desc.step.yml +1 -1
- data/.ace-defaults/assign/presets/fix-bug.yml +15 -3
- data/.ace-defaults/assign/presets/work-on-task-ace-development.yml +16 -4
- data/.ace-defaults/assign/presets/work-on-task-auto-merge.yml +15 -3
- data/.ace-defaults/assign/presets/work-on-task.yml +16 -4
- data/CHANGELOG.md +62 -0
- data/docs/demo/canonical-skill-source.tape.yml +4 -4
- data/docs/protected-authority.md +282 -0
- data/docs/usage.md +205 -3
- data/exe/ace-scope-ready +55 -0
- data/handbook/workflow-instructions/assign/drive.wf.md +28 -3
- data/handbook/workflow-instructions/assign/prepare.wf.md +2 -2
- data/lib/ace/assign/atoms/attempt_state_machine.rb +8 -0
- data/lib/ace/assign/atoms/delivery_parameters.rb +19 -0
- data/lib/ace/assign/authority/assignment_inventory.rb +290 -0
- data/lib/ace/assign/authority/campaign_consumer_policy.rb +100 -0
- data/lib/ace/assign/authority/campaign_execution.rb +119 -0
- data/lib/ace/assign/authority/campaign_round_transfer.rb +82 -0
- data/lib/ace/assign/authority/candidate_transfer.rb +327 -0
- data/lib/ace/assign/authority/cleanup_result_protection.rb +53 -0
- data/lib/ace/assign/authority/client.rb +257 -0
- data/lib/ace/assign/authority/deployment.rb +627 -0
- data/lib/ace/assign/authority/deployment_history.rb +146 -0
- data/lib/ace/assign/authority/endcap.rb +455 -0
- data/lib/ace/assign/authority/endcap_attempts.rb +274 -0
- data/lib/ace/assign/authority/endcap_campaign_results.rb +232 -0
- data/lib/ace/assign/authority/endcap_campaign_rounds.rb +266 -0
- data/lib/ace/assign/authority/endcap_contexts.rb +23 -0
- data/lib/ace/assign/authority/endcap_inboxes.rb +37 -0
- data/lib/ace/assign/authority/endcap_prepared_work.rb +80 -0
- data/lib/ace/assign/authority/endcap_publication.rb +207 -0
- data/lib/ace/assign/authority/endcap_results.rb +398 -0
- data/lib/ace/assign/authority/endcap_review_requests.rb +183 -0
- data/lib/ace/assign/authority/endcap_reviews.rb +130 -0
- data/lib/ace/assign/authority/endcap_service_settlement.rb +152 -0
- data/lib/ace/assign/authority/endcap_services.rb +598 -0
- data/lib/ace/assign/authority/evidence_transfer.rb +67 -0
- data/lib/ace/assign/authority/execution_scope_observation.rb +903 -0
- data/lib/ace/assign/authority/inbox_context_original.rb +113 -0
- data/lib/ace/assign/authority/launch_attempt_consumers.rb +51 -0
- data/lib/ace/assign/authority/launch_campaign_registration.rb +214 -0
- data/lib/ace/assign/authority/launch_control_channel.rb +313 -0
- data/lib/ace/assign/authority/launch_control_exclusions.rb +141 -0
- data/lib/ace/assign/authority/launch_driver.rb +393 -0
- data/lib/ace/assign/authority/launch_input_inhibition.rb +132 -0
- data/lib/ace/assign/authority/launch_lifecycle.rb +997 -0
- data/lib/ace/assign/authority/launch_prepared_work.rb +122 -0
- data/lib/ace/assign/authority/launch_review.rb +91 -0
- data/lib/ace/assign/authority/launch_scope_abort.rb +81 -0
- data/lib/ace/assign/authority/launch_scope_admission.rb +215 -0
- data/lib/ace/assign/authority/launch_scope_close.rb +146 -0
- data/lib/ace/assign/authority/launch_scope_parent.rb +44 -0
- data/lib/ace/assign/authority/launch_scope_release.rb +859 -0
- data/lib/ace/assign/authority/launch_steering.rb +353 -0
- data/lib/ace/assign/authority/launch_stop.rb +254 -0
- data/lib/ace/assign/authority/maintenance_workspace_retirement.rb +229 -0
- data/lib/ace/assign/authority/original_inbox_selection.rb +41 -0
- data/lib/ace/assign/authority/posix_acl.rb +59 -0
- data/lib/ace/assign/authority/prepared_input.rb +130 -0
- data/lib/ace/assign/authority/prepared_queue.rb +185 -0
- data/lib/ace/assign/authority/prepared_task_context.rb +116 -0
- data/lib/ace/assign/authority/prepared_work.rb +304 -0
- data/lib/ace/assign/authority/prepared_worker.rb +69 -0
- data/lib/ace/assign/authority/private_directory.rb +33 -0
- data/lib/ace/assign/authority/protected_assignment_context.rb +226 -0
- data/lib/ace/assign/authority/readiness_hook.rb +120 -0
- data/lib/ace/assign/authority/receipt_transfer.rb +48 -0
- data/lib/ace/assign/authority/router.rb +76 -0
- data/lib/ace/assign/authority/server.rb +300 -0
- data/lib/ace/assign/authority/service_cleanup_dispatch.rb +88 -0
- data/lib/ace/assign/authority/service_cleanup_evidence.rb +180 -0
- data/lib/ace/assign/authority/service_delivery_evidence.rb +101 -0
- data/lib/ace/assign/authority/service_evidence.rb +286 -0
- data/lib/ace/assign/authority/service_publication_evidence.rb +110 -0
- data/lib/ace/assign/authority/task_context_entry.rb +32 -0
- data/lib/ace/assign/authority/transfer_codec.rb +187 -0
- data/lib/ace/assign/cli/commands/assignment_target.rb +15 -2
- data/lib/ace/assign/cli/commands/attempt/base.rb +49 -0
- data/lib/ace/assign/cli/commands/attempt/finish.rb +20 -4
- data/lib/ace/assign/cli/commands/attempt/reconcile.rb +11 -0
- data/lib/ace/assign/cli/commands/authority/inbox_context_principal.rb +25 -0
- data/lib/ace/assign/cli/commands/authority/inbox_context_selection.rb +28 -0
- data/lib/ace/assign/cli/commands/authority/launch.rb +97 -0
- data/lib/ace/assign/cli/commands/authority/serve.rb +31 -0
- data/lib/ace/assign/cli/commands/authority/status.rb +26 -0
- data/lib/ace/assign/cli/commands/authority/task_context.rb +31 -0
- data/lib/ace/assign/cli/commands/authority/task_context_principal.rb +25 -0
- data/lib/ace/assign/cli/commands/authority/task_context_selection.rb +30 -0
- data/lib/ace/assign/cli/commands/authority/terminate.rb +29 -0
- data/lib/ace/assign/cli/commands/authority/worker.rb +23 -0
- data/lib/ace/assign/cli/commands/campaign_export_result.rb +55 -0
- data/lib/ace/assign/cli/commands/campaign_record_round.rb +57 -0
- data/lib/ace/assign/cli/commands/create.rb +11 -1
- data/lib/ace/assign/cli/commands/delivery.rb +107 -0
- data/lib/ace/assign/cli/commands/fail.rb +6 -0
- data/lib/ace/assign/cli/commands/finish.rb +23 -2
- data/lib/ace/assign/cli/commands/fork_session.rb +6 -2
- data/lib/ace/assign/cli/commands/inbox_bind.rb +34 -0
- data/lib/ace/assign/cli/commands/protected_submission.rb +61 -0
- data/lib/ace/assign/cli/commands/resume.rb +38 -0
- data/lib/ace/assign/cli/commands/select.rb +2 -0
- data/lib/ace/assign/cli/commands/start.rb +2 -0
- data/lib/ace/assign/cli/commands/status.rb +4 -2
- data/lib/ace/assign/cli/commands/step.rb +3 -1
- data/lib/ace/assign/cli/commands/submit_candidate.rb +34 -0
- data/lib/ace/assign/cli/commands/submit_result.rb +61 -0
- data/lib/ace/assign/cli.rb +56 -4
- data/lib/ace/assign/errors.rb +139 -0
- data/lib/ace/assign/models/assignment.rb +26 -4
- data/lib/ace/assign/models/evidence_event.rb +4 -2
- data/lib/ace/assign/molecules/assignment_manager.rb +12 -2
- data/lib/ace/assign/molecules/attempt_reconciler.rb +51 -12
- data/lib/ace/assign/molecules/canonical_attempt_state.rb +151 -0
- data/lib/ace/assign/molecules/canonical_evidence.rb +153 -0
- data/lib/ace/assign/molecules/canonical_read_snapshot.rb +374 -0
- data/lib/ace/assign/molecules/evidence_journal.rb +922 -91
- data/lib/ace/assign/molecules/execution_identity_resolver.rb +49 -9
- data/lib/ace/assign/molecules/execution_scope_lineage.rb +345 -0
- data/lib/ace/assign/molecules/fork_session_launcher.rb +16 -6
- data/lib/ace/assign/molecules/journal_input_inhibition.rb +78 -0
- data/lib/ace/assign/molecules/journal_mutation.rb +271 -0
- data/lib/ace/assign/molecules/journal_prompt_mutation.rb +241 -0
- data/lib/ace/assign/molecules/lifecycle_exclusion.rb +182 -26
- data/lib/ace/assign/molecules/original_launch_binding.rb +37 -0
- data/lib/ace/assign/molecules/proposal_journal.rb +151 -0
- data/lib/ace/assign/molecules/protected_control_exclusion.rb +303 -0
- data/lib/ace/assign/molecules/protected_workspace_exclusion.rb +330 -0
- data/lib/ace/assign/molecules/protected_workspace_provisioner.rb +164 -0
- data/lib/ace/assign/molecules/queue_scanner.rb +26 -11
- data/lib/ace/assign/molecules/receipt_verifier.rb +68 -32
- data/lib/ace/assign/molecules/step_writer.rb +27 -5
- data/lib/ace/assign/molecules/terminal_scope_receipt.rb +88 -0
- data/lib/ace/assign/organisms/assignment_executor.rb +10 -4
- data/lib/ace/assign/organisms/attempt_coordinator.rb +302 -13
- data/lib/ace/assign/organisms/delivery_coordinator.rb +342 -0
- data/lib/ace/assign/organisms/prepared_work_builder.rb +194 -0
- data/lib/ace/assign/organisms/protected_delivery_coordinator.rb +100 -0
- data/lib/ace/assign/organisms/task_assignment_creator.rb +11 -4
- data/lib/ace/assign/version.rb +1 -1
- data/lib/ace/assign.rb +4 -102
- metadata +186 -8
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: user
|
|
3
|
+
purpose: Fixed protected authority deployment and canonical launch lifecycle
|
|
4
|
+
ace-docs:
|
|
5
|
+
last-updated: 2026-10-05
|
|
6
|
+
last-checked: 2026-10-05
|
|
7
|
+
subject:
|
|
8
|
+
- code:
|
|
9
|
+
diff:
|
|
10
|
+
paths:
|
|
11
|
+
- ace-assign/lib/ace/assign/authority/**/*.rb
|
|
12
|
+
- ace-assign/lib/ace/assign/molecules/execution_scope_lineage.rb
|
|
13
|
+
- ace-runtime/lib/ace/runtime/molecules/systemd_scope_manager.rb
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Protected native launch
|
|
17
|
+
|
|
18
|
+
`ace-assign authority serve --authority AUTHORITY` runs the standalone launch
|
|
19
|
+
composition. Lab owns the services composition using the same Assign server,
|
|
20
|
+
router, lifecycle, journal and endpoint. Its full-service startup guard remains
|
|
21
|
+
closed while required finish, recovery, inbox and no-effect handlers are incomplete.
|
|
22
|
+
The installed `composition` must match the entrypoint. A project has exactly one
|
|
23
|
+
configured authority owner; duplicate project owners or endpoints fail before
|
|
24
|
+
listener creation.
|
|
25
|
+
|
|
26
|
+
An administrator provisions `/etc/ace/assignment-authorities.json`. There is no
|
|
27
|
+
caller-selected configuration path or local fallback. The outer object contains
|
|
28
|
+
`schema: "ace.assign.authorities/v2"`, `authorities`, `projects`, and
|
|
29
|
+
`launch_mappings`; unknown fields are refused. The fixed schema is implemented
|
|
30
|
+
by `Ace::Assign::Authority::Deployment`:
|
|
31
|
+
|
|
32
|
+
| Installed object | Required fields |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `authorities.ID` | `uid`, `gid`, sorted unique `groups`, `socket_path`, `state_root`, `composition` (`launch` or `services`) |
|
|
35
|
+
| `projects.ID` | `journal_repository`, `evidence_git_ref` (`refs/ace/execution`), `evidence_checkout_root`, `assignment_root`, `candidate_root`, `campaign_repository`, `campaign_store_root`, `launcher_uids`, `reviewer_uids`, `worker_uids`, `service_executor_uids`, `supervisor_uids`, `peer_credentials`, optional `service_receivers` |
|
|
36
|
+
| `projects.ID.peer_credentials.UID` | `gid`, sorted unique `groups`, `scratch_root`; every configured role UID has one fixed entry |
|
|
37
|
+
| `launch_mappings.ID` | `project_id`, `authority_id`, `launcher_uid`, `launcher_gid`, `launcher_groups`, `worker_uid`, `worker_gid`, `worker_groups`, `worker_actor`, `worker_cwd`, `worker_entry`, `worker_env`, `bootstrap`, `bootstrap_sha256`, `native`, `execution_scope`, `task_context_entry` |
|
|
38
|
+
| `launch_mappings.ID.native` | `socket_path`, `executable`, `executable_sha256`, `version` (`0.9.3`), `protocol` (`22`), canonical `workspace_id` (`wN`) |
|
|
39
|
+
| `launch_mappings.ID.execution_scope` | `backend` (`linux_systemd_cgroup_v2`), `slot_id`, `slice_unit`, `service_unit`, `unit_manifest_sha256`, `boundary_manifest_sha256`, `root_directory`, `runtime_directory`, `network_namespace_path` |
|
|
40
|
+
|
|
41
|
+
A services composition requires a nonempty installed `service_receivers` map; launch composition may omit it. Each fixed service ID maps to exactly `{executor_uid, socket_path, staging_root}`. The positive nonroot executor UID belongs to the project's executor allowlist and cannot also be any configured authority, launcher, reviewer, worker or supervisor. This keeps kernel role selection unambiguous and private authority journals inaccessible to executors.
|
|
42
|
+
|
|
43
|
+
Receiver socket and staging paths are canonical absolute paths with root-or-executor owned, nonwritable ancestry. Staging is executor-owned mode0700 and disjoint from other receiver staging and authority state roots. The endpoint resides outside private staging, where the actual authority can traverse its ancestry and inspect it. A pre-existing endpoint must be an executor-owned socket with its installed GID and no permissions for others. Duplicate receiver/authority endpoints, substituted paths and unreadable placement refuse before the authority opens its listener or creates journal state. These mappings select identity and placement; operation argv and grants remain the existing Lab ServicePolicy.
|
|
44
|
+
|
|
45
|
+
All paths are canonical and absolute. The native socket identity is the observed
|
|
46
|
+
`[device, inode, worker_uid]`. The server identity is the exact observed object
|
|
47
|
+
`{pid, uid, gid, groups, parent_pid, started_at, host}`, where `started_at` is
|
|
48
|
+
`linux:BOOT_ID:START_TICKS`. These observations must be made after the root-owned
|
|
49
|
+
native service starts; they cannot be invented, copied from a previous boot, or
|
|
50
|
+
supplied by a worker. The immutable per-attempt native stage owns these facts;
|
|
51
|
+
the deployment record contains no persisted live server/socket tuple. A replacement
|
|
52
|
+
cannot be repinned into the original generation.
|
|
53
|
+
The root-installed native service, its executable, environment and configuration
|
|
54
|
+
are trusted. Merely running arbitrary worker-supplied server code under the worker
|
|
55
|
+
account does not meet this deployment contract.
|
|
56
|
+
|
|
57
|
+
The authority, launcher and worker are distinct non-root accounts. Authority
|
|
58
|
+
journal/cache/import/candidate/assignment/state roots are authority-owned 0700
|
|
59
|
+
with protected ancestry. Peer scratch roots are private to their mapped account.
|
|
60
|
+
The authority socket's directory prevents worker/launcher replacement; its group
|
|
61
|
+
and narrow installer ACL must permit the configured peers to connect. Native
|
|
62
|
+
socket ACL permits the trusted launcher to use the pinned Herdr API. The
|
|
63
|
+
bootstrap, payload executable, native executable, mapping and their ancestors are
|
|
64
|
+
root-owned and non-writable by the worker. Payload argv/cwd/environment are fixed
|
|
65
|
+
installed data; loader and interpreter injection environment fields are refused.
|
|
66
|
+
|
|
67
|
+
Linux Yama `ptrace_scope=2`, empty Inh/Prm/Eff/Bnd/Amb capabilities,
|
|
68
|
+
`NoNewPrivs=1`, fixed real/effective/saved/filesystem UID/GID and supplementary
|
|
69
|
+
groups are mandatory for every participant, including the pinned native server.
|
|
70
|
+
The shipped [native build/install instructions](../../ace-runtime/native/README.md)
|
|
71
|
+
describe architecture, distro dependencies, immutable artifact digest and service
|
|
72
|
+
unit requirements. ACE verifies and refuses missing policy; it does not modify
|
|
73
|
+
sysctls, create accounts, change another user's Herdr, or install service policy.
|
|
74
|
+
|
|
75
|
+
After installation, the trusted launcher preflights without any mutation or
|
|
76
|
+
native creation:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
ace-assign authority launch --mapping MAPPING --dry-run
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Launch one managed definition using a stable invocation ID:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
ace-assign authority launch --mapping MAPPING --assignment ASSIGNMENT \
|
|
86
|
+
--definition /absolute/prepared-definition.json \
|
|
87
|
+
--prepared-bundle /absolute/prepared-input.bundle --step 010 \
|
|
88
|
+
--base-head EXACT_40_CHARACTER_COMMIT --mutation INVOCATION_ID
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The final definition (maximum 32 KiB) includes the exact `prepared_work` Git
|
|
92
|
+
head/tree, manifest and selected subtree reference, with matching `session_id`,
|
|
93
|
+
`task_id` and installed `project_id`. `--prepared-bundle` is mandatory: it supplies
|
|
94
|
+
the original complete prepared-input Git bundle, bounded to 64 MiB. Both inputs
|
|
95
|
+
are read from held regular files with bounded reads and no final symlink following
|
|
96
|
+
before launch effects. The authority derives the final definition from the verified
|
|
97
|
+
prepared tree and commits it with the exact received bundle in one journal mutation.
|
|
98
|
+
Reusing the same definition with different bundle bytes refuses. The owner-private
|
|
99
|
+
definition cache remains separate from the worker queue. An active attempt blocks
|
|
100
|
+
changed definitions. A fresh reservation creates canonical intent/provisioning,
|
|
101
|
+
then the owner activates and binds its independent parent under the slot exclusion.
|
|
102
|
+
It pins existing resource objects, the authority mount namespace and the installed
|
|
103
|
+
nsfs network namespace. Parent binding selects protected network evidence references
|
|
104
|
+
without asserting policy readiness or requiring an available report.
|
|
105
|
+
|
|
106
|
+
Native service admission is a separate private canonical CAS after all required
|
|
107
|
+
readiness evidence succeeds. Only its fresh winner may start the fixed service;
|
|
108
|
+
replay and lost replies never permit another start. Native and original-child
|
|
109
|
+
bindings remain separate immutable stages before payload release.
|
|
110
|
+
|
|
111
|
+
The original authenticated native layout response fixes workspace/tab/pane and
|
|
112
|
+
exact child lineage. Record captures that original child; bind creates the running
|
|
113
|
+
attempt without payload permission. The immutable native bootstrap connects once,
|
|
114
|
+
sets non-dumpable before admission, and waits for the separate release. Release
|
|
115
|
+
commits issuance before one framed permission. Replay returns canonical issuance
|
|
116
|
+
without sending another frame. Reply loss after issuance is potentially executed.
|
|
117
|
+
A worker-controlled native input/close path can deny or interrupt launch, but cannot
|
|
118
|
+
replace the authenticated origin or forge authority release.
|
|
119
|
+
|
|
120
|
+
Inspect or request exact native termination through the same installed mapping.
|
|
121
|
+
Once the original launcher process has exited, run these commands as a mapped
|
|
122
|
+
recovery supervisor with the installed native close ACL; a new process under the
|
|
123
|
+
launcher UID does not inherit the original launcher incarnation:
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
ace-assign authority status --mapping MAPPING --assignment ASSIGNMENT --attempt ATTEMPT
|
|
127
|
+
ace-assign authority terminate --mapping MAPPING --assignment ASSIGNMENT --attempt ATTEMPT
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
A child pidfd proving exit cannot release the whole parent reservation. The
|
|
131
|
+
connected never-native-admitted path seals the original parent before fixed service
|
|
132
|
+
stop, records fresh exact recursive-empty/no-pending/resource closure, selects that
|
|
133
|
+
canonical proof in the guarded pre-release abort, then privately commits reservation
|
|
134
|
+
release after service/inbox settlement at the same immutable journal commit. A lost
|
|
135
|
+
reply keeps the original accepted response. Restart can resume a missing private
|
|
136
|
+
release without issuing native or payload permission. A new reservation first retires
|
|
137
|
+
its exactly released predecessor through the same owner and slot exclusion.
|
|
138
|
+
|
|
139
|
+
`observe_execution_scope` is read-only. `close_execution_scope` is an ordinary
|
|
140
|
+
canonical mutation: the first fresh call seals, a later fresh call records closure
|
|
141
|
+
when verifiable. A fresh sealed close can resume a lost fixed service stop; replay
|
|
142
|
+
performs no manager I/O. The private `scope_service_admission` and
|
|
143
|
+
`scope_reservation_release` operations are absent from the public allowlist.
|
|
144
|
+
Reserve/close RPCs have a fixed 90-second ceiling for bounded manager waits;
|
|
145
|
+
other control calls retain their existing ceiling.
|
|
146
|
+
|
|
147
|
+
This source checkpoint proves the connected never-native-admitted failure/recovery
|
|
148
|
+
path with controlled native-owner observations and real canonical journals. Full
|
|
149
|
+
native hook/readiness and post-native writer observation, admitted-but-unbound
|
|
150
|
+
cleanup, non-abort terminal release, and complete original/candidate installation
|
|
151
|
+
maintenance remain separate required implementation work. The current maintenance
|
|
152
|
+
helpers do not yet implement the accepted candidate-deployment union contract.
|
|
153
|
+
No source fixture establishes installed namespace, network policy or containment
|
|
154
|
+
acceptance; every installed SC remains open.
|
|
155
|
+
|
|
156
|
+
Control replies are at most 16 KiB. Source-selected service transfer operations use
|
|
157
|
+
a 16 KiB authenticated header followed by bounded raw bytes, a shared 30-second
|
|
158
|
+
deadline, exact hashes, upload write-EOF and at most two active transfers. Exact
|
|
159
|
+
origin/role admission happens before private spool allocation and is revalidated
|
|
160
|
+
before canonical mutation. The standalone launch composition exposes no transfer
|
|
161
|
+
operations.
|
|
162
|
+
|
|
163
|
+
Source tests and Linux primitive fixtures are separate from installed deployment
|
|
164
|
+
acceptance. A Docker kernel with Yama=0 is expected to refuse the product gate;
|
|
165
|
+
it does not establish the required positive protected launch proof.
|
|
166
|
+
|
|
167
|
+
Protected launch uses only `layout.apply` in the installed workspace. `workspace.get` checks that exact workspace ID before creation; its reply must echo the ID even when another workspace is active. Missing or replaced containers refuse without fallback. Container contents supply no worker evidence. The original fresh layout reply remains the source of the new tab/pane identity.
|
|
168
|
+
|
|
169
|
+
Receiver admission reads Linux `system.posix_acl_access` on every directory from
|
|
170
|
+
the endpoint parent and private staging root to `/`, and evaluates search access
|
|
171
|
+
for the installed executor UID, primary GID and supplemental groups. Named user
|
|
172
|
+
entries override group/other entries; the ACL mask constrains named user and
|
|
173
|
+
group entries. Absence of an access ACL uses ordinary mode permissions;
|
|
174
|
+
unsupported, unreadable or malformed ACL data refuses startup. The authority
|
|
175
|
+
must also actually traverse the endpoint and staging parents. This inspection
|
|
176
|
+
does not claim an executor readiness handshake or bypass additional LSM policy;
|
|
177
|
+
the installed receiver still performs its own bind and private staging checks.
|
|
178
|
+
|
|
179
|
+
## Canonical result Client API
|
|
180
|
+
|
|
181
|
+
The source-owned services handlers accept a live mapped worker's bounded result
|
|
182
|
+
without terminalizing the attempt or releasing scope. The public seam is
|
|
183
|
+
`Ace::Assign::Authority::Client`; no result CLI is introduced. Launch-only
|
|
184
|
+
`attempt_status` retains its existing schema without a result selector. These
|
|
185
|
+
source handlers and their deterministic tests do not establish installed
|
|
186
|
+
full-service deployment acceptance.
|
|
187
|
+
|
|
188
|
+
From an admitted services attempt, discover its current candidate and authority
|
|
189
|
+
generation, then upload receipt JSON bytes followed by its declared artifact
|
|
190
|
+
bytes in order:
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
require "ace/assign"
|
|
194
|
+
require "json"
|
|
195
|
+
require "digest"
|
|
196
|
+
|
|
197
|
+
client = Ace::Assign::Authority::Client.new(mapping_id: mapping_id)
|
|
198
|
+
binding = {"assignment_id" => assignment_id, "attempt_id" => attempt_id}
|
|
199
|
+
status = client.call("attempt_status", binding.merge(
|
|
200
|
+
"result_candidate_generation" => nil
|
|
201
|
+
)).data
|
|
202
|
+
receipt_bytes = File.binread(local_receipt_path)
|
|
203
|
+
receipt = JSON.parse(receipt_bytes)
|
|
204
|
+
artifacts = receipt.fetch("artifacts").map { |ref| File.binread(ref.fetch("path")) }
|
|
205
|
+
params = binding.merge(
|
|
206
|
+
"expected_generation" => status.fetch("authority_generation"),
|
|
207
|
+
"candidate_generation" => status.fetch("result_candidate_generation"),
|
|
208
|
+
"head" => receipt.fetch("head"),
|
|
209
|
+
"receipt_sha256" => Digest::SHA256.hexdigest(receipt_bytes)
|
|
210
|
+
)
|
|
211
|
+
reply = client.call("submit_result", params, mutation_id: persisted_mutation_id,
|
|
212
|
+
upload_parts: [receipt_bytes, *artifacts], purpose: :receipt_artifacts, timeout: 30)
|
|
213
|
+
result = reply.data
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Only local byte strings cross the boundary; receipt paths are declarations, not
|
|
217
|
+
authority filesystem access. The authority checks exact canonical worker
|
|
218
|
+
attribution, original receipt digest and SHA256, candidate, native lineage and
|
|
219
|
+
current visibility. It normalizes artifact references and writes private
|
|
220
|
+
`result_submitted` metadata, imported blobs and a sanitized mutation reply in one
|
|
221
|
+
Git CAS. Failed results may have `artifacts: []`; no synthetic artifact is added.
|
|
222
|
+
Campaign receipts refuse until the trusted campaign owner is integrated.
|
|
223
|
+
|
|
224
|
+
Persist the exact params, mutation ID and ordered byte strings before submission.
|
|
225
|
+
An exact retry while the worker remains live and the attempt active returns the
|
|
226
|
+
same result without another import. A changed input or fresh mutation ID for the
|
|
227
|
+
same candidate conflicts. Correcting a result requires a new candidate generation
|
|
228
|
+
even at unchanged HEAD; both records remain immutable. Submission creates no
|
|
229
|
+
`receipt_accepted` event or terminal transition.
|
|
230
|
+
|
|
231
|
+
The reply contains result ID, head, candidate generation, verdict, ordered
|
|
232
|
+
canonical `{path, sha256}` references, generation and journal commit. Its
|
|
233
|
+
`uploaded_receipt_sha256`, `original_receipt_digest` and normalized
|
|
234
|
+
`receipt_digest` are distinct values. Private receipt, producer and process
|
|
235
|
+
binding are excluded from fresh and replay result replies.
|
|
236
|
+
|
|
237
|
+
After reply loss or worker exit, the exact owning launcher or mapped supervisor
|
|
238
|
+
can discover the retained result and fetch one referenced artifact:
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
status = client.call("attempt_status", binding.merge(
|
|
242
|
+
"result_candidate_generation" => retained_generation # nil selects latest
|
|
243
|
+
)).data
|
|
244
|
+
result = status.fetch("submitted_result")
|
|
245
|
+
if result && (reference = result.fetch("artifacts").first)
|
|
246
|
+
fetched = client.call("evidence_fetch", binding.merge(
|
|
247
|
+
"kind" => "result", "purpose_id" => result.fetch("result_id"),
|
|
248
|
+
"artifact_id" => reference.fetch("path").delete_prefix("evidence/imports/")
|
|
249
|
+
), download: true, purpose: :artifacts, timeout: 30)
|
|
250
|
+
artifact_bytes = fetched.parts.fetch(0)
|
|
251
|
+
end
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Client closes its request write side for services status and evidence fetch;
|
|
255
|
+
Server refuses extra body bytes before exposing either read projection. Status
|
|
256
|
+
reads launch and result projections from one immutable canonical commit.
|
|
257
|
+
`authority_generation` is the current CAS generation; a known candidate without
|
|
258
|
+
a result has `submitted_result: null`. Unknown selected generation is `missing`;
|
|
259
|
+
corrupt or missing referenced provenance is `evidence_unavailable`. Disposable
|
|
260
|
+
caches and requester files never supply evidence.
|
|
261
|
+
|
|
262
|
+
Fetch returns exactly `descriptor`, `generation`, `journal_commit` and the
|
|
263
|
+
Server-generated `transfer` descriptor, with one binary part. Client validates
|
|
264
|
+
the closed canonical descriptor, exact selected purpose, one-part framing,
|
|
265
|
+
aggregate length and SHA256 before exposing bytes. Owning workers read only
|
|
266
|
+
their own results; assigned reviewers read their exact retained review and
|
|
267
|
+
same-candidate result under their live process binding; recorded executors read
|
|
268
|
+
only their own service evidence. Exact owning launchers and mapped supervisors
|
|
269
|
+
read permitted attempt evidence, including retained terminal history. Current
|
|
270
|
+
visibility and peer authorization apply to every read. Reassignment revokes the
|
|
271
|
+
old reviewer purpose. Inbox and observation evidence refuse until their existing
|
|
272
|
+
signer and observer owners provide complete canonical provenance.
|
|
273
|
+
|
|
274
|
+
The fixed worker entry is the closed `{interpreter: REF, wrapper: REF}` pair, with
|
|
275
|
+
exact path/bytes/SHA256 references (interpreter<=32MiB, wrapper<=1MiB). The
|
|
276
|
+
existing installed unit manifest associates the wrapper with `worker_executable`
|
|
277
|
+
and the interpreter uniquely with `runtime_dependency`. The wrapper is source,
|
|
278
|
+
so it need not carry executable permission; the selected interpreter must. The
|
|
279
|
+
original retained mapping pair is part of the authenticated compact release.
|
|
280
|
+
The native gate hashes held files, seals a readonly code snapshot, and uses the
|
|
281
|
+
fixed isolated Python FD4/FD5 command while preserving operational stdio. No
|
|
282
|
+
worker argv/shebang/PATH fallback remains. Native installed acceptance is separate.
|
data/docs/usage.md
CHANGED
|
@@ -89,7 +89,9 @@ ace-assign add --yaml .ace-local/assign/jobs/add-task.yml --after 010 --child
|
|
|
89
89
|
|
|
90
90
|
Execution evidence lives in assignment attempts, not in reports. An attempt binds an immutable ID to an assignment, step/subtree scope, project, boundary-derived actor/role/runtime, source task, and `base_head`; the deliverable `candidate_head` is pinned only when accepted evidence is submitted, and the evidence journal commit is tracked separately from both.
|
|
91
91
|
|
|
92
|
-
###
|
|
92
|
+
### Ordinary local attempt lifecycle
|
|
93
|
+
|
|
94
|
+
Lifecycle exclusion uses the selected repository's actual Git common directory, shared by linked worktrees. `CACHE_BASE` changes assignment cache storage but cannot split creator and prune locks into a separate namespace. A missing Git repository or malformed retained removal fence refuses admission; it does not create a fallback lock root.
|
|
93
95
|
|
|
94
96
|
```bash
|
|
95
97
|
ace-assign attempt start --assignment ASSIGNMENT --step STEP --project PROJECT
|
|
@@ -101,7 +103,21 @@ ace-assign attempt reconcile --attempt ATTEMPT --receipt receipt.json
|
|
|
101
103
|
- `attempt start` returns a JSON projection of the attempt (ID, state, binding facts, evidence references, digests). A repeated identical start returns the same attempt; a conflicting start exits 5 without launching a second writer.
|
|
102
104
|
- `attempt status` exposes only state, binding, references, and digests -- never credentials or receipt bytes.
|
|
103
105
|
- `attempt finish` accepts a structured receipt: operation, attributed producer, exact tested/reviewed head, verdict, artifact paths with SHA-256 digests, executed checks, and -- for review operations -- an executed independent reviewer verdict for that head.
|
|
104
|
-
- `attempt reconcile` classifies interrupted attempts (`
|
|
106
|
+
- `attempt reconcile` classifies interrupted attempts (`uncertain` when launch or effect completion cannot be proven either way, still `running` only for verifiably live processes) and resolves uncertainty only against a verified receipt attributed to the recorded execution boundary. Merge, publish, and deploy effects are never replayed automatically.
|
|
107
|
+
|
|
108
|
+
### Protected completion, recovery and Inbox registration
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
ace-assign attempt finish --mapping MAP --assignment ASSIGNMENT --attempt ATTEMPT --result RESULT --head SHA --candidate-generation N --mutation ID --expected-generation N
|
|
112
|
+
ace-assign attempt reconcile --mapping MAP --assignment ASSIGNMENT --attempt ATTEMPT --mutation ID --expected-generation N
|
|
113
|
+
ace-assign inbox-bind --mapping MAP --assignment ASSIGNMENT --attempt ATTEMPT --event EVENT --inbox-context CONTEXT --mutation ID --expected-generation N
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The installed authority authenticates the current caller. Finish and recovery require the original launcher or an authorized mapped supervisor; Inbox binding requires the original worker or its authenticated descendant and an open execution scope. Installed current or retained authority, service and Inbox accounts also stay on the protected path: unsupported roles refuse rather than opening local authority.
|
|
117
|
+
|
|
118
|
+
Protected finish selects an already submitted canonical result. A succeeded result requires accepted independent review for that exact candidate, prior no-writers proof and complete service settlement. It does not close the scope or accept `--receipt`. Protected recovery observes one original attempt; unresolved service effects remain unresolved. Terminal/released recovery returns authenticated retained selectors without creating another mutation or restarting execution. Inbox binding retains the original event/attempt attribution; fresh binding after scope seal refuses.
|
|
119
|
+
|
|
120
|
+
Keep the original `--mutation` and `--expected-generation` for exact retries. Authority generation is distinct from `--candidate-generation`; rereading a newer generation changes the request. Assignment, attempt and mapping hints must agree with explicit selection. Commands print canonical JSON metadata and never resend effects during recovery.
|
|
105
121
|
|
|
106
122
|
### State machine
|
|
107
123
|
|
|
@@ -138,7 +154,14 @@ External service operations (driven through `ace-lab service request`) claim the
|
|
|
138
154
|
|
|
139
155
|
- Task-attached (managed) attempts journal accepted evidence to the configured evidence Git ref (default `refs/ace/execution`) through an isolated, disposable audit checkout, outside the deliverable candidate branch. Journal commits never advance or exempt the reviewed candidate.
|
|
140
156
|
- Taskless assignments persist attempts under `.ace-local/assign` with `recovery_mode: local_only`. This path never claims Git-backed recovery and cannot record external effects (`merge`, `publish`, `deploy`, `release`); attach the assignment to a source task before managed delivery.
|
|
141
|
-
- Actor identity always comes from the execution boundary (
|
|
157
|
+
- Actor identity always comes from the execution boundary (the kernel process account for local use, or the trusted service executor's identity). Local execution requires equal real and effective UIDs and resolves the effective UID's OS account; terminal login, `USER`, and `LOGNAME` do not select the actor. Unknown accounts, changing credentials, and unsupported privilege transitions fail closed; flags never grant identity or authority. Historical attempt ownership is never rewritten.
|
|
158
|
+
|
|
159
|
+
For developer callers, `Ace::Assign::Molecules::ExecutionIdentityResolver.new(adapter: "local").resolve.actor`
|
|
160
|
+
returns `Etc.getpwuid(Process.euid).name` for an ordinary process, including one without a terminal login.
|
|
161
|
+
If the OS account cannot be established or real/effective UIDs differ, it raises
|
|
162
|
+
`Ace::Assign::AttemptErrors::UnauthorizedIdentity` (CLI exit code 5). Correct the process launch/account;
|
|
163
|
+
setting `USER` or `LOGNAME` cannot authorize it. A previously recorded foreign owner still requires the
|
|
164
|
+
existing recovery process; this change does not transfer ownership.
|
|
142
165
|
|
|
143
166
|
### status integration
|
|
144
167
|
|
|
@@ -409,3 +432,182 @@ ace-assign select --clear
|
|
|
409
432
|
| 130 | Interrupted (SIGINT) |
|
|
410
433
|
|
|
411
434
|
See [exit-codes.md](exit-codes.md) for complete descriptions.
|
|
435
|
+
|
|
436
|
+
## Resume accepted assignment state
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
ace-assign resume --assignment ID --dry-run
|
|
440
|
+
ace-assign resume --assignment ID
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Resume returns JSON with task goal, checkpoint, attempts, pending HITL references and unresolved effect/inbox references. Each attempt reports `adopt`, `restart-required` or `reconcile-required`, liveness, recovery reason and its last verified observation. Dry-run writes nothing. Actual resume journals a bounded observation; unproven running attempts become uncertain and keep their writer slot. Intent-only reservations remain `reserved` with `reconcile-required`: absent process-start history never proves that a child did not launch. `attempt start` refuses to reuse an unbound reservation, and `attempt reconcile` reports that protected launch reconciliation requires positive no-execution/no-surviving-writer proof before abort. The protected launch authority owns that proof and abort path; these source primitives do not implement it. Finish also rejects an unbound reservation before accepting any receipt, even if a stale local cache says running. Imported canonical evidence stores the exact submitted bytes without Git clean filters or line-ending conversions; immutable reuse compares the accepted Git blob. It never launches a new process or resends a payload. Restart goes through a new `attempt start` only after the previous attempt ends with accepted evidence.
|
|
444
|
+
|
|
445
|
+
Managed agents invoking attempt start inside tmux/Herdr obtain a runtime-owned process binding. Recovery compares PID, UID, OS start time, host, exact pane/native session and Herdr terminal/native thread. A surviving shell is not the agent. Missing, unreadable or reused identity remains unknown. A plain local CLI establishes its actor through its effective UID's OS account, requiring equal real/effective UIDs, but supplies no durable agent identity; trusted service executors may supply `process_pid` in their configured identity JSON. Process observation never grants service privilege or effect authority.
|
|
446
|
+
|
|
447
|
+
Pending HITL references retain their exact scope/attempt and are not rebound by
|
|
448
|
+
resume. Uncertain external effects require their actual owner/result evidence;
|
|
449
|
+
message submission cannot establish business success. `AttemptCoordinator#bind_inbox`
|
|
450
|
+
retains original event attribution only. There is no agent-read reconciliation
|
|
451
|
+
or signing-key requirement for ending or recovering an attempt. Inspect current
|
|
452
|
+
agent progress at its original Herdr/tmux panel; uncertain sends are not repeated
|
|
453
|
+
automatically.
|
|
454
|
+
|
|
455
|
+
Installed Herdr/Pi compaction and writing-child stop/close acceptance must be recorded separately from deterministic fixture coverage before Lab cutover.
|
|
456
|
+
|
|
457
|
+
### Attempt-bound forge delivery
|
|
458
|
+
|
|
459
|
+
`ace-assign create --task TASK --preset work-on-task --delivery-parameters delivery.json`
|
|
460
|
+
accepts an explicit delivery mapping. A local-only preset rejects that mapping.
|
|
461
|
+
The same mapping is retained as `delivery` in the assignment job:
|
|
462
|
+
|
|
463
|
+
```json
|
|
464
|
+
{"forge_server":"lab-forge","pr_provenance":{"mode":"fork","head_repository_url":"https://forge.example/worker/project","head_ref":"task-branch","base_repository_url":"https://forge.example/team/project","base_ref":"main"}}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Use either `forge_server` or boolean `forge_default: true`. With neither,
|
|
468
|
+
the first remote delivery resolves the repository remote and journals the selected
|
|
469
|
+
forge. Subsequent mutations retain that identity. Canonical mode requires equal
|
|
470
|
+
head and base repository URLs; fork mode names both repositories explicitly.
|
|
471
|
+
The source ref must already contain the exact local candidate commit.
|
|
472
|
+
|
|
473
|
+
`ace-assign delivery --assignment ID --attempt ATTEMPT --operation create --title TITLE`
|
|
474
|
+
creates a draft. Operations `update`, `review` and `status` reuse that attempt;
|
|
475
|
+
status and review do not append evidence. `ready --tests TEST-REF.json --review
|
|
476
|
+
REVIEW-REF.json` requires references containing `attempt_id` and `receipt_digest`
|
|
477
|
+
to coordinator-accepted, executed tests and independent approval at the current
|
|
478
|
+
candidate. Remote CI remains visible and advisory. An unresolved mutation is
|
|
479
|
+
reconciled by exact provider identity; absence or conflicting matches remain
|
|
480
|
+
unresolved and never trigger an automatic second write. Retrying readiness uses
|
|
481
|
+
the original intent's evidence references, revalidated against the current head.
|
|
482
|
+
|
|
483
|
+
Merge runs in the authorized qjx executor, which calls the neutral provider merge
|
|
484
|
+
primitive under its existing scoped policy. The worker only consumes completion:
|
|
485
|
+
`delivery --operation merge --service-request REQUEST --tests TEST-REF.json
|
|
486
|
+
--review REVIEW-REF.json`. A completed, verified receipt must bind this assignment,
|
|
487
|
+
attempt, candidate and exact PR resource; credentials do not grant authority.
|
|
488
|
+
Version and changelog preparation do not authorize publication.
|
|
489
|
+
|
|
490
|
+
The neutral `git/pr/create` and `git/pr/update` workflow and skill assets require
|
|
491
|
+
qkb.1 companion adoption across installed consumers. Their source tests do not
|
|
492
|
+
claim fresh installed workflow acceptance or authorized live merge acceptance.
|
|
493
|
+
|
|
494
|
+
Delivery context belongs to the assignment's existing evidence journal, while each
|
|
495
|
+
operation belongs to its own scoped attempt. Closing create step `030` and starting
|
|
496
|
+
update `147` or readiness `148` retains the exact PR and forge. An unresolved intent
|
|
497
|
+
from any earlier scoped attempt prevents a second mutation: reconciliation records
|
|
498
|
+
a result under the current attempt and links the original intent digest and attempt.
|
|
499
|
+
Status history includes each event's owning `attempt_id` and `event_digest`. No
|
|
500
|
+
single delivery attempt needs to remain active throughout the recipe.
|
|
501
|
+
If reconciliation recovers a different operation than the requested step (for
|
|
502
|
+
example, create while invoking readiness), the command reports that the requested
|
|
503
|
+
step still needs execution. It does not return success for an unperformed step.
|
|
504
|
+
Retry that requested operation with its current evidence after recovery settles.
|
|
505
|
+
|
|
506
|
+
Recovery compares the exact requested effect, not only its operation name. An
|
|
507
|
+
uncertain update of one title/body cannot complete a later update with different
|
|
508
|
+
content. Creation also checks the requested title/body against the observed draft,
|
|
509
|
+
including adoption of an already known draft. A mismatched request receives
|
|
510
|
+
`AttemptErrors::CurrentEffectRequired` after the old intent is settled; the new
|
|
511
|
+
effect remains unperformed until explicitly retried. Explicit readiness references
|
|
512
|
+
must match the recovered intent; omitting them during retry retains and revalidates
|
|
513
|
+
its original references. Merge always consumes the exact named qjx service request,
|
|
514
|
+
its candidate/target binding and verified receipt, never an older PR result.
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
## Protected cross-user native launch
|
|
518
|
+
|
|
519
|
+
The fixed installed authority exposes `ace-assign authority serve`, `launch`,
|
|
520
|
+
`status` and `terminate`. See [protected authority](protected-authority.md) for
|
|
521
|
+
OS policy, native artifact installation, canonical launch stages and safe replay.
|
|
522
|
+
|
|
523
|
+
## Source-owned installation maintenance inventory
|
|
524
|
+
|
|
525
|
+
The trusted installation owner loads exact protected content references for the
|
|
526
|
+
original and staged candidate descriptor using `Authority::Deployment.load_artifact`.
|
|
527
|
+
This Ruby-only source interface validates immutable authenticated descriptor bytes;
|
|
528
|
+
it does not establish installed network or native readiness and is not exposed
|
|
529
|
+
through worker transport or a CLI selector.
|
|
530
|
+
|
|
531
|
+
`LaunchLifecycle#with_execution_slots(mapping_ids:, candidate_deployment:)` holds
|
|
532
|
+
the same slot exclusions as normal admission while yielding frozen fixed
|
|
533
|
+
mapping/journal/commit contexts. Changed, added and removed mappings cannot be
|
|
534
|
+
omitted. Removed mappings retain their original journal selection; new mappings
|
|
535
|
+
use the staged selection. Existing mappings and physical slots cannot be recycled
|
|
536
|
+
to hide history. Ref changes, unreadable roots and conflicting aliases refuse.
|
|
537
|
+
|
|
538
|
+
Full `slot_reusable!` maintenance eligibility and parent retirement remain
|
|
539
|
+
unavailable until complete historical authentication and actual current inventory
|
|
540
|
+
verification are connected. The inventory block grants neither policy publication
|
|
541
|
+
nor positive maintenance readiness. Finish/stop wiring and installed acceptance
|
|
542
|
+
remain separate unfinished 9c2 requirements. Existing normal admission and guarded
|
|
543
|
+
abort/release behavior retain their current checks.
|
|
544
|
+
|
|
545
|
+
## Authenticated readiness and terminal scope closure
|
|
546
|
+
|
|
547
|
+
The source-owned fixed ExecStartPost readiness hook uploads one bounded report
|
|
548
|
+
through the authenticated authority connection, closes its write side and waits
|
|
549
|
+
for acknowledgement. The authority joins the exact manager-selected hook/server
|
|
550
|
+
actors, immutable runtime/configuration dependencies, original boot/network
|
|
551
|
+
proofs and complete live mount/IPC/resource observations before binding native
|
|
552
|
+
admission. The hook’s observed server network namespace must match both the original
|
|
553
|
+
parent selection and the canonical network installation admission. A missing,
|
|
554
|
+
malformed or different identity refuses native binding before child release.
|
|
555
|
+
Unknown writable projections, writable API descendants, changed
|
|
556
|
+
socket endpoints and incomplete evidence refuse. A read-only socket mount does
|
|
557
|
+
not authorize messages.
|
|
558
|
+
|
|
559
|
+
Original service issuance remains tracked while the manager command runs outside
|
|
560
|
+
the callback's exclusions. Sealing cannot publish an empty-scope proof while an
|
|
561
|
+
original issuer can still start the service. Admitted-but-unbound starts use the
|
|
562
|
+
same positive cleanup and worker baseline as bound starts; an inactive snapshot
|
|
563
|
+
does not assert that execution never happened.
|
|
564
|
+
|
|
565
|
+
After the genuine terminal owner accepts authenticated result evidence, the
|
|
566
|
+
existing release producer joins that receipt to the exact sealed empty scope.
|
|
567
|
+
Current activation, jobs, recursive population, original resource identities,
|
|
568
|
+
native directory absence and outside worker UID writers are checked again before
|
|
569
|
+
publishing or consuming the proof. Empty scope alone supplies no terminal result.
|
|
570
|
+
|
|
571
|
+
The required per-boot host proof is selected before parent activation and retained
|
|
572
|
+
in canonical `scope_bound.boot_baseline_selection`; later reads authenticate that
|
|
573
|
+
original immutable artifact rather than a current pointer. Domain gad.8/gad.b
|
|
574
|
+
still must deliver trusted cold-start/boot refresh and activation inhibition.
|
|
575
|
+
These source interfaces and controlled tests do not establish installed native,
|
|
576
|
+
network or boot acceptance, and task 9c2 remains in progress through integration
|
|
577
|
+
and independent review.
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
### Protected sealed-service no-effect settlement
|
|
581
|
+
|
|
582
|
+
For the fixed original executor, `service_status` includes a closed immutable `settlement_context` containing the authenticated original request, execution binding and accepted current challenge (or null). Full original mapping/project/receiver credentials are checked before this private context is exposed. Ordinary status roles retain their existing projection. Terminal status still authenticates the imported original receipt through the canonical reader.
|
|
583
|
+
|
|
584
|
+
The fixed original executor uses `claim_service_settlement` against the sealed original request. The authority records an acyclic challenge selecting the latest canonical uncertain/failed outcome. `complete_no_effect` uploads the exact original receipt and bounded inspection artifact through the existing private transfer. It authenticates the accepted challenge, original dispatch binding and imported artifact before atomically reaching `failed-settled`. Exact retries retain their first reply and do not invoke a new effect. A later genuine failure needs a fresh challenge; missing/orphan/stale challenges and changed input refuse.
|
|
585
|
+
|
|
586
|
+
This generic authority path attributes the closed inspection report to the recorded executor. Actual operation-specific target absence, handler termination and surviving-writer inspection are an open gad.b producer obligation; a closed scope or timeout does not prove no effect. The current protected ingress atomically accepts a request and executor claim, so an absent canonical request cannot be converted into a requested-but-unclaimed settlement record.
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
### Protected original worker submission
|
|
590
|
+
|
|
591
|
+
Run under the actual original prepared worker while its lineage is live. Read
|
|
592
|
+
`ace-assign authority status --mapping M --assignment A --attempt T` and save
|
|
593
|
+
`authority_generation` for the next new mutation. Candidate submission uses
|
|
594
|
+
current `result_candidate_generation` (zero if null), then returns the next
|
|
595
|
+
positive candidate generation. Review/result use that returned generation.
|
|
596
|
+
|
|
597
|
+
```sh
|
|
598
|
+
ace-assign submit-candidate --mapping M --assignment A --attempt T --head H \
|
|
599
|
+
--candidate-generation CURRENT --expected-generation G --mutation CANDIDATE --bundle candidate.bundle
|
|
600
|
+
ace-assign submit-result --mapping M --assignment A --attempt T --head H \
|
|
601
|
+
--candidate-generation ACCEPTED --expected-generation G --mutation RESULT --receipt result.json \
|
|
602
|
+
--artifact first-evidence --artifact second-evidence
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
The actual worker builds `Ace::Assign::Models::ExecutionReceipt` from its original
|
|
606
|
+
assignment/project/scope, installed worker actor, tested head and actual executed
|
|
607
|
+
checks/artifact digests. Failed receipts may have zero artifacts; succeeded
|
|
608
|
+
receipts require evidence. Captured queue completion does not create a receipt or
|
|
609
|
+
canonical acceptance. Independent installed reviewers use the existing
|
|
610
|
+
`ace-overseer review` consumer for that exact candidate. Result submission obtains
|
|
611
|
+
a new generation after review; exact retries retain the original generation,
|
|
612
|
+
mutation and identical ordered bytes. Supervisor finish still revalidates result,
|
|
613
|
+
review, writer exclusion and settlement. A lost reply never causes automatic resend.
|
data/exe/ace-scope-ready
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
# Private fixed interpreter action; the unit supplies --disable=gems,rubyopt.
|
|
4
|
+
abort "fixed readiness requires one slot" unless ARGV.length == 1 && ARGV.first.match?(/\A[A-Za-z0-9][A-Za-z0-9._-]{0,127}\z/)
|
|
5
|
+
abort "readiness startup features differ" if defined?(Gem) || ENV.keys.any? { |key| key.match?(/\A(?:RUBY|GEM|BUNDLE)/) }
|
|
6
|
+
protected_path = lambda do |path, directory|
|
|
7
|
+
abort "readiness path substituted" unless path.start_with?("/") && File.expand_path(path) == path && File.realpath(path) == path
|
|
8
|
+
current = path
|
|
9
|
+
loop do
|
|
10
|
+
stat = File.lstat(current)
|
|
11
|
+
abort "readiness path is writable" unless stat.uid.zero? && !stat.symlink? && (stat.mode & 0o022).zero?
|
|
12
|
+
abort "readiness directory differs" if current == path && directory && !stat.directory?
|
|
13
|
+
break if current == "/"
|
|
14
|
+
current = File.dirname(current)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
$LOAD_PATH.each { |path| protected_path.call(path, true) }
|
|
18
|
+
require "json"
|
|
19
|
+
require "digest"
|
|
20
|
+
path = "/etc/ace/execution-slots/#{ARGV.first}/readiness.json"
|
|
21
|
+
protected_path.call(path, false)
|
|
22
|
+
bytes = File.open(path, File::RDONLY | File::NOFOLLOW) { |file| file.read(65_537) }
|
|
23
|
+
abort "readiness configuration bound differs" unless bytes.bytesize.between?(1, 65_536)
|
|
24
|
+
config = JSON.parse(bytes.force_encoding(Encoding::UTF_8), create_additions: false, max_nesting: 8,
|
|
25
|
+
allow_duplicate_key: false, allow_comments: false)
|
|
26
|
+
runtime = config.fetch("runtime")
|
|
27
|
+
references = runtime.fetch("dependencies")
|
|
28
|
+
abort "readiness runtime closure oversized" unless references.is_a?(Array) && references.size.between?(1, 256)
|
|
29
|
+
total = 0
|
|
30
|
+
references.each do |ref|
|
|
31
|
+
abort "readiness dependency reference differs" unless ref.is_a?(Hash) && ref.keys.sort == %w[bytes path sha256] &&
|
|
32
|
+
ref["path"].is_a?(String) && ref["path"].bytesize.between?(1, 4096) && !ref["path"].include?("\0") &&
|
|
33
|
+
ref["bytes"].is_a?(Integer) && ref["bytes"].between?(1, 1_048_576) &&
|
|
34
|
+
ref["sha256"].is_a?(String) && ref["sha256"].match?(/\A[0-9a-f]{64}\z/)
|
|
35
|
+
total += ref.fetch("bytes")
|
|
36
|
+
abort "readiness runtime closure byte bound differs" if total > 16_777_216
|
|
37
|
+
protected_path.call(ref.fetch("path"), false)
|
|
38
|
+
content = File.open(ref.fetch("path"), File::RDONLY | File::NOFOLLOW) { |file| file.read(ref.fetch("bytes") + 1) }
|
|
39
|
+
abort "readiness dependency changed" unless content.bytesize == ref.fetch("bytes") && Digest::SHA256.hexdigest(content) == ref.fetch("sha256")
|
|
40
|
+
end
|
|
41
|
+
builtins = %w[enumerator.so thread.rb fiber.so rational.so complex.so ruby2_keywords.rb]
|
|
42
|
+
$LOADED_FEATURES.each do |feature|
|
|
43
|
+
abort "readiness startup loaded undeclared code" unless builtins.include?(feature) || references.any? { |ref| ref.fetch("path") == feature }
|
|
44
|
+
end
|
|
45
|
+
paths = runtime.fetch("load_paths")
|
|
46
|
+
abort "readiness load paths oversized" unless paths.is_a?(Array) && paths.size.between?(1, 16)
|
|
47
|
+
paths.each { |selected| protected_path.call(selected, true) }
|
|
48
|
+
$LOAD_PATH.replace(paths)
|
|
49
|
+
require "ace/runtime/molecules/readiness_configuration"
|
|
50
|
+
require "ace/runtime/molecules/readiness_runtime"
|
|
51
|
+
configuration = Ace::Runtime::Molecules::ReadinessConfiguration.decode(bytes, slot: ARGV.first)
|
|
52
|
+
Ace::Runtime::Molecules::ReadinessRuntime.new(configuration: configuration.data).activate!
|
|
53
|
+
require "ace/assign/errors"
|
|
54
|
+
require "ace/assign/authority/readiness_hook"
|
|
55
|
+
Ace::Assign::Authority::ReadinessHook.new(slot: ARGV.first, configuration: configuration).run
|
|
@@ -40,7 +40,8 @@ If this workflow is invoked with an argument (for example `/as-assign-drive abc1
|
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
# Set once from workflow argument or internal scoped default (empty when neither is provided)
|
|
43
|
-
|
|
43
|
+
DEFAULT_ASSIGNMENT_TARGET="${ACE_ASSIGN_DEFAULT_TARGET:-}"
|
|
44
|
+
ASSIGNMENT_TARGET="${1:-$DEFAULT_ASSIGNMENT_TARGET}"
|
|
44
45
|
|
|
45
46
|
# Resolve and pin assignment identity for the full drive loop
|
|
46
47
|
if [ -n "$ASSIGNMENT_TARGET" ]; then
|
|
@@ -659,6 +660,30 @@ Follow the instructions directly, performing the described work:
|
|
|
659
660
|
|
|
660
661
|
Every external-facing step (implement, verify, review, merge, publish, deploy, release) runs inside a scoped attempt. The attempt -- not the report and not exit status -- is the delivery record.
|
|
661
662
|
|
|
663
|
+
For an original protected PreparedWorker, use its already admitted assignment,
|
|
664
|
+
attempt and selected scope. Do not run the local `attempt start`/receipt-file
|
|
665
|
+
sequence below, acquire another attempt, or execute an outer preset step. The
|
|
666
|
+
delivery step must already belong to the immutable captured subtree. Submit the
|
|
667
|
+
actual candidate and obtain the installed independent reviewer acceptance through
|
|
668
|
+
the protected commands supplied by the original drive prompt.
|
|
669
|
+
|
|
670
|
+
For an authorized protected merge, retain the exact approved head/candidate
|
|
671
|
+
generation, structured input file, installed receiver, authorization and stable
|
|
672
|
+
request ID. Read current authority generation for the first new request. Invoke
|
|
673
|
+
`ace-lab service request` with those original mapping/assignment/attempt/scope
|
|
674
|
+
selectors. Inspect `ace-lab service status` with the same request, input digest and
|
|
675
|
+
target, then consume succeeded canonical evidence with `ace-assign delivery
|
|
676
|
+
--operation merge --service-request REQUEST` and the same selectors. Include
|
|
677
|
+
`--artifact-digest` on status and consumption when the original target has one.
|
|
678
|
+
Exact retries retain the original generation, mutation/request ID and input bytes;
|
|
679
|
+
uncertainty never permits refreshing or resending an effect. The protected delivery
|
|
680
|
+
consumer verifies the imported receipt and canonical delivery event without another
|
|
681
|
+
provider mutation. Only that succeeded result permits finishing the active queue
|
|
682
|
+
step. Refusal, unavailable evidence or uncertainty leaves it unfinished and reports
|
|
683
|
+
the exact blocker. Credentials and queue completion do not replace authorization
|
|
684
|
+
or canonical evidence. The following local attempt sequence applies only to
|
|
685
|
+
ordinary standalone assignments.
|
|
686
|
+
|
|
662
687
|
**Step 1: Start the attempt before executing the step.** Actor identity is derived from the execution boundary; never pass actor flags.
|
|
663
688
|
|
|
664
689
|
```bash
|
|
@@ -924,10 +949,10 @@ When executing a step with a `skill:` field:
|
|
|
924
949
|
|-------|-----------|---------|
|
|
925
950
|
| `onboard` | `/as-onboard` | Load project context |
|
|
926
951
|
| `ace:task-work` | `/as-task-work <taskref>` | Implement task changes |
|
|
927
|
-
| `ace:
|
|
952
|
+
| `ace:git-pr-create` | `/as-git-pr-create` | Create pull request |
|
|
928
953
|
| `ace:review-pr` | `/as-review-pr [pr#]` | Review code changes |
|
|
929
954
|
| `ace:git-commit` | `/as-git-commit` | Generate commit message |
|
|
930
|
-
| `ace:
|
|
955
|
+
| `ace:git-pr-update` | `/as-git-pr-update` | Update PR description |
|
|
931
956
|
|
|
932
957
|
## Error Handling
|
|
933
958
|
|