ace-handbook 0.36.2 → 0.37.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/CHANGELOG.md +23 -1
- data/handbook/workflow-instructions/release/cycle-record.wf.md +61 -0
- data/handbook/workflow-instructions/release/publisher.wf.md +90 -0
- data/lib/ace/handbook/atoms/provider_registry.rb +67 -0
- data/lib/ace/handbook/organisms/provider_syncer.rb +1 -1
- data/lib/ace/handbook/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9985cf345994f51d958a0d848c532a7e7cb4f98baa3dac71d9de06e2cff578f7
|
|
4
|
+
data.tar.gz: aba7c5dc35f70aa7edf202bdf2ebe76ac65936c2ed44fbaa596675e79399afbe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba495d1f76e1997a523c998c41471ba5750f0e852e8ac2fdd84b3b7d27b96da6b8c084a07854e32be0349caf10a19d48ff69aff05862d6331982a18ec1e08418
|
|
7
|
+
data.tar.gz: cd38950c60eeb374135da2e02395211f0d023e3f8f568ae59b3a0b4aacd343adbb76b5a39219850fa555b8c8f3e68b8838ddf2f912c1e84dd647ed00bbfdc4cf
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.37.0] - 2026-10-30
|
|
9
|
+
|
|
10
|
+
### Fixed (review rounds 3/3b)
|
|
11
|
+
|
|
12
|
+
- `resolve_named_package` raises a typed `UnresolvableExtensionsPackageError`
|
|
13
|
+
in auto mode when a configured `extensions_package` resolves nowhere;
|
|
14
|
+
installed mode keeps its fallback rule.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `wfi://release/publisher` (task 8xs.t.pu0.9): the publisher coworker role —
|
|
19
|
+
existing release workflows, ace-lab auth access, OTP as a secret HITL ask,
|
|
20
|
+
resume that recognizes already-published artifacts by version+digest, and
|
|
21
|
+
the hand-off to install/verify/record.
|
|
22
|
+
- `wfi://release/cycle-record`: the delivery-cycle evidence record contract
|
|
23
|
+
(task/cycle, source HEAD, bound review/test evidence, publish
|
|
24
|
+
versions+digests, runtime paths and outcome) through a normal record-only
|
|
25
|
+
PR that never triggers a release by itself.
|
|
26
|
+
- Provider manifests can declare `extensions_package`: extension assets project
|
|
27
|
+
from the named asset package (local sibling directory first, installed gem
|
|
28
|
+
second), enabling gem-backed extension shipping such as `ace-wake` for the pi
|
|
29
|
+
provider (task 8xs.t.pu0.0); in `ACE_TOOLCHAIN=installed` mode the packaged
|
|
30
|
+
extension source is always the installed gem, never the checkout directory.
|
|
9
31
|
|
|
10
32
|
## [0.36.2] - 2026-10-27
|
|
11
33
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-cycle-record
|
|
3
|
+
description: Write the integrated delivery-cycle evidence record into .ace-tasks/_records through a normal reviewed record-only PR
|
|
4
|
+
allowed-tools: Bash, Read
|
|
5
|
+
doc-type: workflow
|
|
6
|
+
title: Delivery Cycle Record Workflow
|
|
7
|
+
purpose: Bind the record contract — one reviewed document per delivery cycle naming task/cycle, source HEAD, review/test evidence, publish versions and digests, runtime paths and the runtime outcome — without starting a new release or a recursive record.
|
|
8
|
+
ace-docs:
|
|
9
|
+
last-updated: '2026-10-30'
|
|
10
|
+
last-checked: '2026-10-30'
|
|
11
|
+
---
|
|
12
|
+
# Delivery Cycle Record Workflow
|
|
13
|
+
|
|
14
|
+
## Goal
|
|
15
|
+
|
|
16
|
+
Close a delivery cycle with a durable, reviewed Git record of the WHOLE path:
|
|
17
|
+
sources, integration, publication, installation, runtime outcome (task
|
|
18
|
+
8xs.t.pu0.9). The record is evidence, not a control queue: the live cycle
|
|
19
|
+
state stays in its task/assignment until the record is integrated.
|
|
20
|
+
|
|
21
|
+
## Instructions
|
|
22
|
+
|
|
23
|
+
### 1) Gather facts from the owners — never from memory
|
|
24
|
+
|
|
25
|
+
Every value comes from evidence produced by the owners:
|
|
26
|
+
|
|
27
|
+
- task/cycle reference and the managing assignment;
|
|
28
|
+
- source HEAD (the merged candidate) and the merge proof;
|
|
29
|
+
- review/test evidence: the independent reviewer verdict, the executed test
|
|
30
|
+
commands with their results, and the HEAD each was bound to
|
|
31
|
+
(`ace-coworker gate` inputs);
|
|
32
|
+
- publication: package versions and artifact digests from the artifact
|
|
33
|
+
repository (checked, not assumed — an interrupted publish must have been
|
|
34
|
+
reconciled first);
|
|
35
|
+
- runtime: the installed gem/CLI/extension paths and versions the target
|
|
36
|
+
account actually ran, and the acceptance result.
|
|
37
|
+
|
|
38
|
+
Do not fabricate identifiers, SHAs, or version endings. If a fact is
|
|
39
|
+
missing, the cycle is NOT complete — say so in the task and stop.
|
|
40
|
+
|
|
41
|
+
### 2) Write the record
|
|
42
|
+
|
|
43
|
+
Place one document under `.ace-tasks/_records/<dated-cycle-slug>/` with the
|
|
44
|
+
sections: Goal, Source (HEAD, PR), Review & Tests (bound heads, verdicts,
|
|
45
|
+
commands), Publication (versions + digests), Installation & Runtime
|
|
46
|
+
(paths, versions, acceptance under the target account), Outcome (closed or
|
|
47
|
+
explicitly open with the blocking reason and resume point).
|
|
48
|
+
|
|
49
|
+
### 3) Integrate through a normal review
|
|
50
|
+
|
|
51
|
+
The record goes through a record-only PR with the ordinary review/integration
|
|
52
|
+
gate. It does NOT trigger another release (no product change) and requires no
|
|
53
|
+
recursive record of its own merge. After the record's integration, mark the
|
|
54
|
+
task done (`ace-task update <ref> --set status=done` — archive per the task
|
|
55
|
+
family's convention).
|
|
56
|
+
|
|
57
|
+
## Boundaries
|
|
58
|
+
|
|
59
|
+
- No secrets, OTP values, or tokens in the record.
|
|
60
|
+
- A failed or partially deployed cycle keeps the task open with a resumable
|
|
61
|
+
blocking state; the record documents the stage truthfully instead.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release-publisher
|
|
3
|
+
description: Publisher coworker role — deliver a merged change through release inventory, bumps, build, publication (OTP via a secret HITL ask), and hand off to install/verify
|
|
4
|
+
allowed-tools: Bash, Read
|
|
5
|
+
doc-type: workflow
|
|
6
|
+
title: Release Publisher Workflow
|
|
7
|
+
purpose: Bind the publisher role's delivery pass: existing release workflows, short-lived access via ace-lab auth, OTP as a secret HITL case whose wake never carries the value, resume that recognizes already-published artifacts, and the cycle record contract.
|
|
8
|
+
ace-docs:
|
|
9
|
+
last-updated: '2026-10-30'
|
|
10
|
+
last-checked: '2026-10-30'
|
|
11
|
+
---
|
|
12
|
+
# Release Publisher Workflow
|
|
13
|
+
|
|
14
|
+
## Goal
|
|
15
|
+
|
|
16
|
+
Deliver a MERGED change to the installed runtime and hand the cycle to
|
|
17
|
+
install/verify and the record (task 8xs.t.pu0.9). You are the publisher
|
|
18
|
+
coworker: you run the project's existing release workflows — never a private
|
|
19
|
+
variant.
|
|
20
|
+
|
|
21
|
+
## Instructions
|
|
22
|
+
|
|
23
|
+
Run the loaded workflow as the source of truth and execute it end-to-end
|
|
24
|
+
instead of only summarizing it.
|
|
25
|
+
|
|
26
|
+
### 1) Access
|
|
27
|
+
|
|
28
|
+
Request the publisher profile through `ace-lab auth` (task 8xs.t.pu0.7):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
ace-lab auth request --profile <publisher-profile> --role publisher \
|
|
32
|
+
--task <task> --assignment <assignment> --repo <repo>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Verify principal, repo, scope and use-until before each operation. Static env
|
|
36
|
+
profiles expose process-scoped values only and are never proof of remote
|
|
37
|
+
expiry. If a wait needs a paused access window, do NOT extend it — request a
|
|
38
|
+
fresh lease after the answer.
|
|
39
|
+
|
|
40
|
+
### 2) Release preparation
|
|
41
|
+
|
|
42
|
+
Use the existing release workflows exactly: inventory the modified packages,
|
|
43
|
+
version bumps, changelogs, and the coordinated release commit
|
|
44
|
+
(`ace-bundle wfi://release/local` in ACE-style repos). Do not close the task
|
|
45
|
+
at merge when runtime delivery is still required.
|
|
46
|
+
|
|
47
|
+
### 3) OTP through HITL — a SECRET ask
|
|
48
|
+
|
|
49
|
+
When publication needs an OTP, ask with the explicit secret flag:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ace-hitl ask "OTP for gem push" --to captain --kind approval --secret \
|
|
53
|
+
--task <task> --question "One-time code for <publisher step>"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`--secret` marks the case AT CREATION (no title heuristics): the armed wake
|
|
57
|
+
carries the CASE REFERENCE ONLY — the value never passes through the model.
|
|
58
|
+
End the turn after asking; there is no deadline on the Captain's answer and
|
|
59
|
+
waiting is never a model loop. Read the value from the case file through the
|
|
60
|
+
existing wrapper (the publishing tool consumes it via env/stdin, never argv).
|
|
61
|
+
A rejected or expired OTP needs a NEW case — never replay an old value.
|
|
62
|
+
|
|
63
|
+
### 4) Publication and resume
|
|
64
|
+
|
|
65
|
+
Publish through the project's existing publication queue. If publication was
|
|
66
|
+
interrupted, first check the artifact repository: a matching version AND
|
|
67
|
+
digest means ALREADY PUBLISHED — continue with the missing steps only; a
|
|
68
|
+
version/digest conflict BLOCKS (never overwrite a version, never bump around
|
|
69
|
+
a conflict as a workaround). A failed publication leaves an explicit,
|
|
70
|
+
resumable state — never pretend a partially deployed cycle is complete.
|
|
71
|
+
|
|
72
|
+
### 5) Install, verify, record
|
|
73
|
+
|
|
74
|
+
After publication, the lab side runs `ace-lab install` convergence (manifest,
|
|
75
|
+
gem versions, dependency closure, extension projections) and the runtime
|
|
76
|
+
acceptance under the target account — separately evidenced from source and
|
|
77
|
+
integration. The cycle's evidence record then goes to
|
|
78
|
+
`.ace-tasks/_records/` through a normal reviewed record-only PR
|
|
79
|
+
(`ace-bundle wfi://release/cycle-record`). The record-only PR does not
|
|
80
|
+
trigger another release without a product change and needs no recursive
|
|
81
|
+
record of its own merge. The task closes only after the successful runtime
|
|
82
|
+
acceptance AND the integrated record.
|
|
83
|
+
|
|
84
|
+
## Boundaries
|
|
85
|
+
|
|
86
|
+
- Source work happens on the host; the lab installs published artifacts.
|
|
87
|
+
- Secrets (API keys, OTP) live only in the allowed wrapper/env and the secret
|
|
88
|
+
HITL case — never in prompts, reports, argv, or the wake.
|
|
89
|
+
- The live cycle state stays in the existing task/assignment; `_records` is
|
|
90
|
+
the final reviewed evidence write-up, not a control queue.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "date"
|
|
4
4
|
require "yaml"
|
|
5
|
+
require "ace/support/cli/toolchain"
|
|
5
6
|
|
|
6
7
|
module Ace
|
|
7
8
|
module Handbook
|
|
@@ -42,6 +43,23 @@ module Ace
|
|
|
42
43
|
manifest(provider)["extensions_dir"]
|
|
43
44
|
end
|
|
44
45
|
|
|
46
|
+
# Source root for a provider's extension assets: the package the
|
|
47
|
+
# manifest names through `extensions_package` (an asset package
|
|
48
|
+
# such as ace-wake that is NOT an integration package). The named
|
|
49
|
+
# package resolves like every other package: the sibling package
|
|
50
|
+
# directory in the local project first, the installed gem second
|
|
51
|
+
# — so a monorepo checkout projects the working tree and an
|
|
52
|
+
# installed lab projects the exact installed gem (task
|
|
53
|
+
# 8xs.t.pu0.0). In auto mode an unresolvable package raises; in
|
|
54
|
+
# installed mode the manifest's own package root is the fallback
|
|
55
|
+
# (the installed-mode rule).
|
|
56
|
+
def extensions_source_root(provider)
|
|
57
|
+
package = manifest(provider)["extensions_package"]
|
|
58
|
+
return package_root(provider) if package.nil? || package.to_s.strip.empty?
|
|
59
|
+
|
|
60
|
+
resolve_named_package(package.to_s.strip) || package_root(provider)
|
|
61
|
+
end
|
|
62
|
+
|
|
45
63
|
def package_root(provider)
|
|
46
64
|
manifest_dir = File.dirname(manifest(provider).fetch("_manifest_path"))
|
|
47
65
|
File.expand_path("../../..", manifest_dir)
|
|
@@ -63,6 +81,55 @@ module Ace
|
|
|
63
81
|
|
|
64
82
|
private
|
|
65
83
|
|
|
84
|
+
# A package named through `extensions_package` resolves like the
|
|
85
|
+
# registry's own packages: the sibling directory inside the
|
|
86
|
+
# local project first (monorepo checkout), the installed gem
|
|
87
|
+
# second. In installed mode (ACE_TOOLCHAIN=installed) the
|
|
88
|
+
# checkout directory is project DATA, never the runtime source:
|
|
89
|
+
# the package must come from an installed gem, and only an
|
|
90
|
+
# installed spec counts. Only a package that actually carries
|
|
91
|
+
# handbook/extensions counts. In auto mode a configured package
|
|
92
|
+
# that resolves nowhere is a hard error: an unresolvable
|
|
93
|
+
# extensions_package must never be treated as "no extensions",
|
|
94
|
+
# which would prune every receipt-owned file from the target.
|
|
95
|
+
def resolve_named_package(name)
|
|
96
|
+
return installed_package(name) if Ace::Support::Cli::Toolchain.installed?
|
|
97
|
+
|
|
98
|
+
local = File.join(project_root, name)
|
|
99
|
+
return local if File.directory?(File.join(local, "handbook", "extensions"))
|
|
100
|
+
|
|
101
|
+
spec = Gem::Specification.find_by_name(name)
|
|
102
|
+
return spec.full_gem_path if File.directory?(File.join(spec.full_gem_path, "handbook", "extensions"))
|
|
103
|
+
|
|
104
|
+
raise_unresolvable_extensions_package(name,
|
|
105
|
+
"the installed gem carries no handbook/extensions tree")
|
|
106
|
+
rescue Gem::LoadError, ArgumentError => e
|
|
107
|
+
raise_unresolvable_extensions_package(name, e.message)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# A typed contract for an unresolvable extensions_package: callers
|
|
111
|
+
# (syncers, tests) rescue this precisely, never bare StandardError.
|
|
112
|
+
class UnresolvableExtensionsPackageError < StandardError; end
|
|
113
|
+
|
|
114
|
+
def raise_unresolvable_extensions_package(name, detail)
|
|
115
|
+
raise UnresolvableExtensionsPackageError,
|
|
116
|
+
"the configured extensions_package '#{name}' cannot be resolved (#{detail}): " \
|
|
117
|
+
"neither a sibling package directory under #{project_root} nor an installed gem " \
|
|
118
|
+
"carries the extension assets. Install the package or fix the provider manifest — " \
|
|
119
|
+
"an unresolvable extensions_package must never be treated as 'no extensions'."
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Installed mode: the packaged extension source must be the
|
|
123
|
+
# installed gem (max version), never the checkout directory.
|
|
124
|
+
def installed_package(name)
|
|
125
|
+
spec = Gem::Specification.find_all
|
|
126
|
+
.select { |candidate| candidate.name == name && Ace::Support::Cli::Toolchain.installed_spec?(candidate) }
|
|
127
|
+
.max_by(&:version)
|
|
128
|
+
return nil if spec.nil? || !File.directory?(File.join(spec.full_gem_path, "handbook", "extensions"))
|
|
129
|
+
|
|
130
|
+
spec.full_gem_path
|
|
131
|
+
end
|
|
132
|
+
|
|
66
133
|
def local_manifest_paths
|
|
67
134
|
LOCAL_MANIFEST_GLOBS.flat_map do |glob|
|
|
68
135
|
Dir.glob(File.join(project_root, glob))
|
|
@@ -184,7 +184,7 @@ module Ace
|
|
|
184
184
|
extensions_dir = registry.extensions_dir(provider)
|
|
185
185
|
return nil if extensions_dir.nil? || extensions_dir.to_s.empty?
|
|
186
186
|
|
|
187
|
-
source_dir = File.join(registry.
|
|
187
|
+
source_dir = File.join(registry.extensions_source_root(provider), "handbook", "extensions")
|
|
188
188
|
expected = extension_source_files(source_dir)
|
|
189
189
|
output_dir = File.join(project_root, extensions_dir)
|
|
190
190
|
FileUtils.mkdir_p(output_dir)
|
data/lib/ace/handbook/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-handbook
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.37.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10-
|
|
10
|
+
date: 2026-10-30 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-support-config
|
|
@@ -201,8 +201,10 @@ files:
|
|
|
201
201
|
- handbook/workflow-instructions/handbook/synthesize-research.wf.md
|
|
202
202
|
- handbook/workflow-instructions/handbook/update-docs.wf.md
|
|
203
203
|
- handbook/workflow-instructions/release/bump-version.wf.md
|
|
204
|
+
- handbook/workflow-instructions/release/cycle-record.wf.md
|
|
204
205
|
- handbook/workflow-instructions/release/local.wf.md
|
|
205
206
|
- handbook/workflow-instructions/release/publish.wf.md
|
|
207
|
+
- handbook/workflow-instructions/release/publisher.wf.md
|
|
206
208
|
- handbook/workflow-instructions/release/rubygems-publish.wf.md
|
|
207
209
|
- handbook/workflow-instructions/release/update-changelog.wf.md
|
|
208
210
|
- lib/ace/handbook.rb
|