ace-assign 0.64.0 → 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.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.ace-defaults/assign/catalog/steps/auto-merge.step.yml +11 -0
  3. data/CHANGELOG.md +19 -0
  4. data/docs/protected-authority.md +23 -7
  5. data/docs/usage.md +117 -4
  6. data/exe/ace-scope-ready +55 -0
  7. data/handbook/workflow-instructions/assign/drive.wf.md +26 -1
  8. data/lib/ace/assign/atoms/attempt_state_machine.rb +8 -0
  9. data/lib/ace/assign/atoms/delivery_parameters.rb +4 -39
  10. data/lib/ace/assign/authority/assignment_inventory.rb +290 -0
  11. data/lib/ace/assign/authority/campaign_consumer_policy.rb +100 -0
  12. data/lib/ace/assign/authority/campaign_execution.rb +119 -0
  13. data/lib/ace/assign/authority/campaign_round_transfer.rb +82 -0
  14. data/lib/ace/assign/authority/candidate_transfer.rb +169 -2
  15. data/lib/ace/assign/authority/cleanup_result_protection.rb +53 -0
  16. data/lib/ace/assign/authority/client.rb +156 -6
  17. data/lib/ace/assign/authority/deployment.rb +201 -39
  18. data/lib/ace/assign/authority/deployment_history.rb +146 -0
  19. data/lib/ace/assign/authority/endcap.rb +131 -18
  20. data/lib/ace/assign/authority/endcap_attempts.rb +274 -0
  21. data/lib/ace/assign/authority/endcap_campaign_results.rb +232 -0
  22. data/lib/ace/assign/authority/endcap_campaign_rounds.rb +266 -0
  23. data/lib/ace/assign/authority/endcap_contexts.rb +23 -0
  24. data/lib/ace/assign/authority/endcap_inboxes.rb +3 -280
  25. data/lib/ace/assign/authority/endcap_prepared_work.rb +80 -0
  26. data/lib/ace/assign/authority/endcap_publication.rb +207 -0
  27. data/lib/ace/assign/authority/endcap_results.rb +36 -18
  28. data/lib/ace/assign/authority/endcap_review_requests.rb +183 -0
  29. data/lib/ace/assign/authority/endcap_reviews.rb +130 -0
  30. data/lib/ace/assign/authority/endcap_service_settlement.rb +152 -0
  31. data/lib/ace/assign/authority/endcap_services.rb +221 -18
  32. data/lib/ace/assign/authority/execution_scope_observation.rb +468 -41
  33. data/lib/ace/assign/authority/inbox_context_original.rb +113 -0
  34. data/lib/ace/assign/authority/launch_attempt_consumers.rb +51 -0
  35. data/lib/ace/assign/authority/launch_campaign_registration.rb +214 -0
  36. data/lib/ace/assign/authority/launch_control_channel.rb +313 -0
  37. data/lib/ace/assign/authority/launch_control_exclusions.rb +141 -0
  38. data/lib/ace/assign/authority/launch_driver.rb +298 -19
  39. data/lib/ace/assign/authority/launch_input_inhibition.rb +132 -0
  40. data/lib/ace/assign/authority/launch_lifecycle.rb +275 -100
  41. data/lib/ace/assign/authority/launch_prepared_work.rb +122 -0
  42. data/lib/ace/assign/authority/launch_review.rb +91 -0
  43. data/lib/ace/assign/authority/launch_scope_abort.rb +0 -7
  44. data/lib/ace/assign/authority/launch_scope_admission.rb +136 -5
  45. data/lib/ace/assign/authority/launch_scope_close.rb +68 -37
  46. data/lib/ace/assign/authority/launch_scope_release.rb +696 -17
  47. data/lib/ace/assign/authority/launch_steering.rb +353 -0
  48. data/lib/ace/assign/authority/launch_stop.rb +254 -0
  49. data/lib/ace/assign/authority/maintenance_workspace_retirement.rb +229 -0
  50. data/lib/ace/assign/authority/original_inbox_selection.rb +41 -0
  51. data/lib/ace/assign/authority/posix_acl.rb +5 -2
  52. data/lib/ace/assign/authority/prepared_input.rb +130 -0
  53. data/lib/ace/assign/authority/prepared_queue.rb +185 -0
  54. data/lib/ace/assign/authority/prepared_task_context.rb +116 -0
  55. data/lib/ace/assign/authority/prepared_work.rb +304 -0
  56. data/lib/ace/assign/authority/prepared_worker.rb +69 -0
  57. data/lib/ace/assign/authority/protected_assignment_context.rb +226 -0
  58. data/lib/ace/assign/authority/readiness_hook.rb +120 -0
  59. data/lib/ace/assign/authority/router.rb +24 -2
  60. data/lib/ace/assign/authority/server.rb +103 -33
  61. data/lib/ace/assign/authority/service_cleanup_dispatch.rb +88 -0
  62. data/lib/ace/assign/authority/service_cleanup_evidence.rb +180 -0
  63. data/lib/ace/assign/authority/service_delivery_evidence.rb +101 -0
  64. data/lib/ace/assign/authority/service_evidence.rb +243 -12
  65. data/lib/ace/assign/authority/service_publication_evidence.rb +110 -0
  66. data/lib/ace/assign/authority/task_context_entry.rb +32 -0
  67. data/lib/ace/assign/authority/transfer_codec.rb +44 -12
  68. data/lib/ace/assign/cli/commands/assignment_target.rb +15 -2
  69. data/lib/ace/assign/cli/commands/attempt/base.rb +49 -0
  70. data/lib/ace/assign/cli/commands/attempt/finish.rb +20 -4
  71. data/lib/ace/assign/cli/commands/attempt/reconcile.rb +11 -0
  72. data/lib/ace/assign/cli/commands/authority/inbox_context_principal.rb +25 -0
  73. data/lib/ace/assign/cli/commands/authority/inbox_context_selection.rb +28 -0
  74. data/lib/ace/assign/cli/commands/authority/launch.rb +58 -6
  75. data/lib/ace/assign/cli/commands/authority/serve.rb +2 -1
  76. data/lib/ace/assign/cli/commands/authority/status.rb +1 -1
  77. data/lib/ace/assign/cli/commands/authority/task_context.rb +31 -0
  78. data/lib/ace/assign/cli/commands/authority/task_context_principal.rb +25 -0
  79. data/lib/ace/assign/cli/commands/authority/task_context_selection.rb +30 -0
  80. data/lib/ace/assign/cli/commands/authority/worker.rb +23 -0
  81. data/lib/ace/assign/cli/commands/campaign_export_result.rb +55 -0
  82. data/lib/ace/assign/cli/commands/campaign_record_round.rb +57 -0
  83. data/lib/ace/assign/cli/commands/create.rb +2 -0
  84. data/lib/ace/assign/cli/commands/delivery.rb +54 -4
  85. data/lib/ace/assign/cli/commands/fail.rb +6 -0
  86. data/lib/ace/assign/cli/commands/finish.rb +23 -2
  87. data/lib/ace/assign/cli/commands/fork_session.rb +6 -2
  88. data/lib/ace/assign/cli/commands/inbox_bind.rb +34 -0
  89. data/lib/ace/assign/cli/commands/protected_submission.rb +61 -0
  90. data/lib/ace/assign/cli/commands/resume.rb +12 -0
  91. data/lib/ace/assign/cli/commands/select.rb +2 -0
  92. data/lib/ace/assign/cli/commands/start.rb +2 -0
  93. data/lib/ace/assign/cli/commands/status.rb +4 -2
  94. data/lib/ace/assign/cli/commands/step.rb +3 -1
  95. data/lib/ace/assign/cli/commands/submit_candidate.rb +34 -0
  96. data/lib/ace/assign/cli/commands/submit_result.rb +61 -0
  97. data/lib/ace/assign/cli.rb +36 -5
  98. data/lib/ace/assign/errors.rb +139 -0
  99. data/lib/ace/assign/models/assignment.rb +26 -4
  100. data/lib/ace/assign/models/evidence_event.rb +3 -3
  101. data/lib/ace/assign/molecules/assignment_manager.rb +12 -2
  102. data/lib/ace/assign/molecules/attempt_reconciler.rb +20 -0
  103. data/lib/ace/assign/molecules/canonical_attempt_state.rb +151 -0
  104. data/lib/ace/assign/molecules/canonical_evidence.rb +5 -2
  105. data/lib/ace/assign/molecules/canonical_read_snapshot.rb +374 -0
  106. data/lib/ace/assign/molecules/evidence_journal.rb +720 -44
  107. data/lib/ace/assign/molecules/execution_scope_lineage.rb +27 -9
  108. data/lib/ace/assign/molecules/fork_session_launcher.rb +16 -6
  109. data/lib/ace/assign/molecules/journal_input_inhibition.rb +78 -0
  110. data/lib/ace/assign/molecules/journal_mutation.rb +82 -13
  111. data/lib/ace/assign/molecules/journal_prompt_mutation.rb +241 -0
  112. data/lib/ace/assign/molecules/lifecycle_exclusion.rb +178 -26
  113. data/lib/ace/assign/molecules/original_launch_binding.rb +37 -0
  114. data/lib/ace/assign/molecules/protected_control_exclusion.rb +303 -0
  115. data/lib/ace/assign/molecules/protected_workspace_exclusion.rb +330 -0
  116. data/lib/ace/assign/molecules/protected_workspace_provisioner.rb +164 -0
  117. data/lib/ace/assign/molecules/queue_scanner.rb +26 -11
  118. data/lib/ace/assign/molecules/receipt_verifier.rb +21 -15
  119. data/lib/ace/assign/molecules/step_writer.rb +27 -5
  120. data/lib/ace/assign/molecules/terminal_scope_receipt.rb +88 -0
  121. data/lib/ace/assign/organisms/assignment_executor.rb +4 -4
  122. data/lib/ace/assign/organisms/attempt_coordinator.rb +66 -76
  123. data/lib/ace/assign/organisms/prepared_work_builder.rb +194 -0
  124. data/lib/ace/assign/organisms/protected_delivery_coordinator.rb +100 -0
  125. data/lib/ace/assign/organisms/task_assignment_creator.rb +4 -2
  126. data/lib/ace/assign/version.rb +1 -1
  127. data/lib/ace/assign.rb +1 -112
  128. metadata +88 -9
  129. data/lib/ace/assign/cli/commands/inbox_reconcile.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8f85e3c4fd7760b26493ade861cb5a7b1fda59227909762005d75ee2dc14db1
4
- data.tar.gz: 3a491eefefece8e3e7995f68e4fd84536a3e55e436c503119555ea55a17da464
3
+ metadata.gz: 72c3ae204cb6b8b4ee165ba56ca4a9820a28e417b94d74e423048cae1cecc73e
4
+ data.tar.gz: b9b4e043e80071b3d9335876e9d0a4981ac2a532d2db70216050b8d279665cc8
5
5
  SHA512:
6
- metadata.gz: 0a47581f03920859a3712c10026b0bd034127dfd640c33d64636d88543108ebc2815b475414eccef721ac5d68db1a625a2538c8ffcc381c02cf851041206bb1e
7
- data.tar.gz: 1bf276d020c5ff7c2740d9d9a3ccaf9837af09197156ded76af8f7d6ca27cb99bf4b24cfb7f1176b3134cc9ecf732822302a3ddd987355e6ff0dbecfd9ada77c
6
+ metadata.gz: 51cabda59e2bf66a9b3fb4e9f78670db06e239d290dd7d97ccc98eb43160dc24fd83c6a58b26fb400f7ff4b35e274ea81e625d1469c575c863ee1db50f0b23da
7
+ data.tar.gz: d95a4198c73b1769e21329230b1df943c87826d08905fa1b5f783334b89cc24e6bd3d3dd011f787560f8f6b8859058892cfed2ee254966b73e9d71aad5aba2ba
@@ -0,0 +1,11 @@
1
+ name: auto-merge
2
+ description: Consume an authorized exact-candidate merge through the delivery owner
3
+ workflow: wfi://assign/drive
4
+ instructions: |
5
+ Merge only when separately authorized and the existing merge checks pass.
6
+ For an original protected worker, follow the protected delivery rule in
7
+ wfi://assign/drive using the admitted assignment, attempt and scope. Retain
8
+ exact approved candidate and request selectors. Request the installed service,
9
+ inspect canonical status, and consume its succeeded result with ace-assign
10
+ delivery. Never execute another provider effect during result consumption.
11
+ Refused, unavailable or uncertain delivery leaves this step unfinished.
data/CHANGELOG.md CHANGED
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.66.0] - 2026-10-08
11
+
12
+ ### Added
13
+
14
+ - Connect protected PR delivery and parent/child campaign results to the canonical assignment owner, with current candidate checks, independent review and original operation authorization.
15
+ - Launch authenticated prepared workers through the existing ace-llm provider CLI in the original terminal.
16
+
17
+ ### Changed
18
+
19
+ - Remove message-read proofs, observation/signing APIs and Inbox settlement requirements from finish, recovery and stop. Retain actual service/result/review evidence and whole-scope writer checks.
20
+ - Reuse verified immutable journal inventories only within a single operation while retaining fresh admission and compare-and-swap checks.
21
+ - Preserve original workspace leases when provider ownership or shutdown is uncertain.
22
+
23
+ ## [0.65.0] - 2026-10-06
24
+
25
+ ### Added
26
+
27
+ - Source-only protected original/candidate deployment inventory and shared execution-slot exclusions with immutable journal snapshots; full maintenance eligibility and retirement remain unavailable until historical authentication and current inventory verification are connected.
28
+
10
29
  ## [0.64.0] - 2026-10-05
11
30
 
12
31
  ### Added
@@ -32,9 +32,9 @@ by `Ace::Assign::Authority::Deployment`:
32
32
  | Installed object | Required fields |
33
33
  | --- | --- |
34
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`, `launcher_uids`, `reviewer_uids`, `worker_uids`, `service_executor_uids`, `supervisor_uids`, `peer_credentials`, optional `service_receivers` |
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
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_argv`, `worker_env`, `bootstrap`, `bootstrap_sha256`, `native`, `execution_scope` |
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
38
  | `launch_mappings.ID.native` | `socket_path`, `executable`, `executable_sha256`, `version` (`0.9.3`), `protocol` (`22`), canonical `workspace_id` (`wN`) |
39
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
40
 
@@ -83,14 +83,20 @@ Launch one managed definition using a stable invocation ID:
83
83
 
84
84
  ```sh
85
85
  ace-assign authority launch --mapping MAPPING --assignment ASSIGNMENT \
86
- --definition /absolute/managed-definition.json --step 010 \
86
+ --definition /absolute/prepared-definition.json \
87
+ --prepared-bundle /absolute/prepared-input.bundle --step 010 \
87
88
  --base-head EXACT_40_CHARACTER_COMMIT --mutation INVOCATION_ID
88
89
  ```
89
90
 
90
- The definition is a normal managed assignment JSON document (maximum 32 KiB),
91
- with matching `session_id`, `task_id` and installed `project_id`. The authority
92
- stores its exact immutable bytes in the canonical journal and materializes the
93
- owner-private assignment cache from that accepted blob. An active attempt blocks
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
94
100
  changed definitions. A fresh reservation creates canonical intent/provisioning,
95
101
  then the owner activates and binds its independent parent under the slot exclusion.
96
102
  It pins existing resource objects, the authority mount namespace and the installed
@@ -264,3 +270,13 @@ read permitted attempt evidence, including retained terminal history. Current
264
270
  visibility and peer authorization apply to every read. Reassignment revokes the
265
271
  old reviewer purpose. Inbox and observation evidence refuse until their existing
266
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
- ### Attempt lifecycle
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
@@ -103,6 +105,20 @@ ace-assign attempt reconcile --attempt ATTEMPT --receipt receipt.json
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
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.
105
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.
121
+
106
122
  ### State machine
107
123
 
108
124
  `reserved -> running -> succeeded | failed | stopped | uncertain`; reconciliation may resolve `uncertain -> succeeded | failed`. Terminal attempts accept no new effects, and accepted history is append-only.
@@ -422,14 +438,19 @@ See [exit-codes.md](exit-codes.md) for complete descriptions.
422
438
  ```bash
423
439
  ace-assign resume --assignment ID --dry-run
424
440
  ace-assign resume --assignment ID
425
- ace-assign inbox-reconcile --attempt ID --event ID --receipt proof.json
426
441
  ```
427
442
 
428
- Both commands return 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.
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.
429
444
 
430
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.
431
446
 
432
- Pending HITL references retain their exact scope/attempt and are not rebound by resume. Uncertain external claims require existing effect receipt reconciliation. Delivery consumption is separate from business success. Herdr owns signed inbox verification (`ace-herdr inbox reconcile --event ID --receipt FILE`); the assignment `inbox-reconcile` command uses that same configured verifier and additionally records the accepted observation references. `AttemptCoordinator#bind_inbox` records only the event reference, and `#reconcile_inbox` delegates that same verifier and journals only the verified proof digest/path and native observation reference. A consumed receipt settles transport once. A superseded receipt requeues the same event for an explicit retry, without succeeding the attempt. Exact signed replay recovers a crash between Herdr settlement and the assignment journal write; changed key, target, payload or generation fails closed. Retain the matching protected key pair for unresolved events or postpone rotation.
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.
433
454
 
434
455
  Installed Herdr/Pi compaction and writing-child stop/close acceptance must be recorded separately from deterministic fixture coverage before Lab cutover.
435
456
 
@@ -498,3 +519,95 @@ its candidate/target binding and verified receipt, never an older PR result.
498
519
  The fixed installed authority exposes `ace-assign authority serve`, `launch`,
499
520
  `status` and `terminate`. See [protected authority](protected-authority.md) for
500
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.
@@ -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
- ASSIGNMENT_TARGET="${1:-${ACE_ASSIGN_DEFAULT_TARGET:-}}"
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
@@ -59,6 +59,14 @@ module Ace
59
59
  TRANSITIONS.fetch(from.to_s, [])
60
60
  end
61
61
 
62
+ # Source-owned cessation, invoked only after original no-writers and
63
+ # exhaustive independent settlement proof. This does not broaden the
64
+ # ordinary uncertain receipt-reconciliation transition table.
65
+ def self.proof_stopped_transition!(state)
66
+ return "stopped" if %w[running uncertain].include?(state.to_s)
67
+ raise AttemptErrors::InvalidTransition, "Proof-linked stop requires an active bound attempt"
68
+ end
69
+
62
70
  # Validate a transition.
63
71
  #
64
72
  # @param from [String] Current state
@@ -1,52 +1,17 @@
1
1
  # frozen_string_literal: true
2
- require "ace/git/atoms/server_url"
2
+ require "ace/git/atoms/delivery_parameters"
3
3
 
4
4
  module Ace
5
5
  module Assign
6
6
  module Atoms
7
- # Explicit non-secret remote delivery inputs; local recipes need not call this validator.
7
+ # Assignment configuration consumes the shared forge-neutral owner.
8
8
  module DeliveryParameters
9
- PROVENANCE_FIELDS = %w[mode head_repository_url head_ref base_repository_url base_ref].freeze
10
9
  def self.validate(input)
11
- raise ArgumentError, "Delivery parameters must be a mapping" unless input.is_a?(Hash)
12
- unless (input.keys - %w[forge_server forge_default pr_provenance]).empty?
13
- raise ArgumentError, "Unexpected delivery parameter fields"
14
- end
15
- default = input.fetch("forge_default", false)
16
- raise ArgumentError, "forge_default must be boolean" unless [true, false].include?(default)
17
- server = input["forge_server"]
18
- if server && (!server.is_a?(String) || !server.match?(/\A[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}\z/))
19
- raise ArgumentError, "Invalid forge_server"
20
- end
21
- raise ArgumentError, "forge_server and forge_default are mutually exclusive" if server && default
22
- provenance = input["pr_provenance"]
23
- unless provenance.is_a?(Hash) && provenance.keys.sort == PROVENANCE_FIELDS.sort &&
24
- %w[fork canonical].include?(provenance["mode"])
25
- raise ArgumentError, "Explicit complete fork or canonical PR provenance is required"
26
- end
27
- %w[head_repository_url base_repository_url].each { |key| validate_url!(provenance[key]) }
28
- %w[head_ref base_ref].each do |key|
29
- ref = provenance[key]
30
- unless ref.is_a?(String) && ref.match?(/\A[a-zA-Z0-9][a-zA-Z0-9_\/.\-]{0,255}\z/) &&
31
- !ref.include?("..") && !ref.include?("//") && !ref.end_with?("/", ".", ".lock")
32
- raise ArgumentError, "Invalid PR ref"
33
- end
34
- end
35
- if provenance["mode"] == "canonical" && !Ace::Git::Atoms::ServerUrl.match?(
36
- provenance["head_repository_url"], provenance["base_repository_url"])
37
- raise ArgumentError, "Canonical provenance requires matching head and base repositories"
38
- end
39
- {"forge_server" => server, "forge_default" => default,
40
- "pr_provenance" => provenance.transform_values(&:dup)}
10
+ Ace::Git::Atoms::DeliveryParameters.validate(input)
41
11
  end
42
12
 
43
13
  def self.validate_url!(url)
44
- unless url.is_a?(String) && url.length <= 512 &&
45
- (url.match?(%r{\Ahttps?://[a-zA-Z0-9.:-]+/[a-zA-Z0-9_./-]+\z}) ||
46
- url.match?(%r{\Agit@[a-zA-Z0-9.-]+:[a-zA-Z0-9_./-]+\z}) ||
47
- url.match?(%r{\Assh://git@[a-zA-Z0-9.:-]+/[a-zA-Z0-9_./-]+\z}))
48
- raise ArgumentError, "PR repository URL must contain a repository and no credentials or query"
49
- end
14
+ Ace::Git::Atoms::DeliveryParameters.validate_url!(url)
50
15
  end
51
16
  end
52
17
  end