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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7e0dbdef987c766d7465a9d825e42109f9e0cca131cbd72cbe86fd722ab3788
4
- data.tar.gz: ebb309bf73b17f5fe41cdcf0ffeb7ecab58fee5f8b1034b4e8465a1921eb567d
3
+ metadata.gz: 9985cf345994f51d958a0d848c532a7e7cb4f98baa3dac71d9de06e2cff578f7
4
+ data.tar.gz: aba7c5dc35f70aa7edf202bdf2ebe76ac65936c2ed44fbaa596675e79399afbe
5
5
  SHA512:
6
- metadata.gz: 62494535cfe334c728174675f69529e16a768875ca05876693a20a8e48e502f1a0c4ae8f575c48e7fa718f4435dbd6816b4a0dd5067eb132e6134d576053b152
7
- data.tar.gz: 56b1fe6f7c586f89b294c267ab0125a3e528fcba74135d75b314027f54f611f6a0d173be3c77bdcdb7e957e9c86a6ea48d9e472b6af0c9707612bc2d7ddfabf9
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
- ## [Unreleased]
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.package_root(provider), "handbook", "extensions")
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)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ace
4
4
  module Handbook
5
- VERSION = "0.36.2"
5
+ VERSION = "0.37.0"
6
6
  end
7
7
  end
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.36.2
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-28 00:00:00.000000000 Z
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