@mannyc1/ts-release 0.2.2 → 0.3.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 (107) hide show
  1. package/ARCHITECTURE.md +60 -0
  2. package/CHANGELOG.md +82 -0
  3. package/README.md +129 -6
  4. package/SPEC.md +51 -0
  5. package/apps/release-ts/README.md +7 -0
  6. package/apps/ts-release-agents/package.json +1 -1
  7. package/apps/ts-release-agents/src/check.ts +7 -2
  8. package/apps/ts-release-agents/test/contract.test.ts +2 -2
  9. package/dist/bin/ts-release.js +154 -47
  10. package/dist/drivers/archive.d.ts +2 -1
  11. package/dist/drivers/archive.d.ts.map +1 -1
  12. package/dist/drivers/archive.js +12 -8
  13. package/dist/drivers/archive.js.map +1 -1
  14. package/dist/drivers/process.d.ts.map +1 -1
  15. package/dist/drivers/process.js +4 -4
  16. package/dist/drivers/process.js.map +1 -1
  17. package/dist/drivers/python-wheel.d.ts +21 -0
  18. package/dist/drivers/python-wheel.d.ts.map +1 -0
  19. package/dist/drivers/python-wheel.js +109 -0
  20. package/dist/drivers/python-wheel.js.map +1 -0
  21. package/dist/drivers/workspace.d.ts +3 -0
  22. package/dist/drivers/workspace.d.ts.map +1 -1
  23. package/dist/drivers/workspace.js +5 -1
  24. package/dist/drivers/workspace.js.map +1 -1
  25. package/dist/operation-journal/authority.d.ts +3 -0
  26. package/dist/operation-journal/authority.d.ts.map +1 -0
  27. package/dist/operation-journal/authority.js +147 -0
  28. package/dist/operation-journal/authority.js.map +1 -0
  29. package/dist/operation-journal/aws/deadline.d.ts +12 -0
  30. package/dist/operation-journal/aws/deadline.d.ts.map +1 -0
  31. package/dist/operation-journal/aws/deadline.js +38 -0
  32. package/dist/operation-journal/aws/deadline.js.map +1 -0
  33. package/dist/operation-journal/aws/oidc.d.ts +27 -0
  34. package/dist/operation-journal/aws/oidc.d.ts.map +1 -0
  35. package/dist/operation-journal/aws/oidc.js +361 -0
  36. package/dist/operation-journal/aws/oidc.js.map +1 -0
  37. package/dist/operation-journal/aws/policy.d.ts +42 -0
  38. package/dist/operation-journal/aws/policy.d.ts.map +1 -0
  39. package/dist/operation-journal/aws/policy.js +404 -0
  40. package/dist/operation-journal/aws/policy.js.map +1 -0
  41. package/dist/operation-journal/aws/s3-boundary.d.ts +17 -0
  42. package/dist/operation-journal/aws/s3-boundary.d.ts.map +1 -0
  43. package/dist/operation-journal/aws/s3-boundary.js +417 -0
  44. package/dist/operation-journal/aws/s3-boundary.js.map +1 -0
  45. package/dist/operation-journal/aws.d.ts +11 -0
  46. package/dist/operation-journal/aws.d.ts.map +1 -0
  47. package/dist/operation-journal/aws.js +50 -0
  48. package/dist/operation-journal/aws.js.map +1 -0
  49. package/dist/operation-journal/canonical.d.ts +52 -0
  50. package/dist/operation-journal/canonical.d.ts.map +1 -0
  51. package/dist/operation-journal/canonical.js +282 -0
  52. package/dist/operation-journal/canonical.js.map +1 -0
  53. package/dist/operation-journal/model.d.ts +251 -0
  54. package/dist/operation-journal/model.d.ts.map +1 -0
  55. package/dist/operation-journal/model.js +56 -0
  56. package/dist/operation-journal/model.js.map +1 -0
  57. package/dist/operation-journal/reducer.d.ts +28 -0
  58. package/dist/operation-journal/reducer.d.ts.map +1 -0
  59. package/dist/operation-journal/reducer.js +73 -0
  60. package/dist/operation-journal/reducer.js.map +1 -0
  61. package/dist/operation-journal/s3.d.ts +7 -0
  62. package/dist/operation-journal/s3.d.ts.map +1 -0
  63. package/dist/operation-journal/s3.js +667 -0
  64. package/dist/operation-journal/s3.js.map +1 -0
  65. package/dist/operation-journal.d.ts +14 -0
  66. package/dist/operation-journal.d.ts.map +1 -0
  67. package/dist/operation-journal.js +12 -0
  68. package/dist/operation-journal.js.map +1 -0
  69. package/dist/platform/credentials.d.ts.map +1 -1
  70. package/dist/platform/credentials.js +65 -3
  71. package/dist/platform/credentials.js.map +1 -1
  72. package/dist/publication/github.d.ts.map +1 -1
  73. package/dist/publication/github.js +66 -15
  74. package/dist/publication/github.js.map +1 -1
  75. package/dist/release/capabilities.d.ts.map +1 -1
  76. package/dist/release/capabilities.js +2 -2
  77. package/dist/release/capabilities.js.map +1 -1
  78. package/dist/release/prepare.d.ts.map +1 -1
  79. package/dist/release/prepare.js +4 -5
  80. package/dist/release/prepare.js.map +1 -1
  81. package/dist/release/prepared-store.d.ts.map +1 -1
  82. package/dist/release/prepared-store.js +11 -9
  83. package/dist/release/prepared-store.js.map +1 -1
  84. package/package.json +19 -4
  85. package/src/drivers/archive.ts +17 -8
  86. package/src/drivers/process.ts +3 -4
  87. package/src/drivers/python-wheel.ts +145 -0
  88. package/src/drivers/workspace.ts +7 -1
  89. package/src/operation-journal/authority.ts +162 -0
  90. package/src/operation-journal/aws/deadline.ts +41 -0
  91. package/src/operation-journal/aws/oidc.ts +439 -0
  92. package/src/operation-journal/aws/policy.ts +491 -0
  93. package/src/operation-journal/aws/s3-boundary.ts +562 -0
  94. package/src/operation-journal/aws.ts +60 -0
  95. package/src/operation-journal/canonical.ts +373 -0
  96. package/src/operation-journal/model.ts +301 -0
  97. package/src/operation-journal/reducer.ts +112 -0
  98. package/src/operation-journal/s3.ts +884 -0
  99. package/src/operation-journal.ts +51 -0
  100. package/src/platform/credentials.ts +71 -3
  101. package/src/publication/github.ts +83 -15
  102. package/src/release/capabilities.ts +3 -2
  103. package/src/release/prepare.ts +3 -5
  104. package/src/release/prepared-store.ts +8 -7
  105. package/templates/README.md +2 -2
  106. package/templates/github-actions/release.yml +24 -7
  107. package/templates/github-actions/reviewed-release.yml +49 -14
package/ARCHITECTURE.md CHANGED
@@ -33,6 +33,12 @@ the authority for publication progress.
33
33
  coordinator and provider subjects.
34
34
  - `src/correction` binds provider-specific correction requests to exact
35
35
  prepared subjects; catalog Git installs the sole conditional correction writer.
36
+ - `src/operation-journal` owns the provider-neutral opaque event envelope,
37
+ finite transition reducer, and the one S3 conditional-write protocol. It
38
+ does not decode provider payloads or acquire cloud credentials.
39
+ - `src/operation-journal/aws` owns the sole production backend: one sealed
40
+ GitHub-OIDC/STS session and exact IAM/S3 policy and object boundary. The core
41
+ subpath does not load the AWS SDK.
36
42
  - `src/api` exposes the public lifecycle used by CLI, Action, and library users.
37
43
  - `src/platform` supplies Node or Bun filesystem, process, HTTP, durable-store,
38
44
  and opaque credential sinks at the host boundary.
@@ -112,6 +118,60 @@ the engine's private service tags. Credential values remain host-owned; the
112
118
  public seam carries only prepared requests, opaque grants, safe references,
113
119
  typed acquisition failures, and authorized HTTP results.
114
120
 
121
+ The separate `operation-journal` subpath is not part of the six-operation
122
+ release API. Its credential-agnostic core receives an exact structural S3
123
+ boundary so the protocol can be qualified without live credentials. The
124
+ separate `operation-journal/aws` subpath is the only admitted backend and
125
+ closes over a directly acquired short-lived purpose-scoped session; it exposes
126
+ no profile, ambient credential-chain, endpoint, bucket-selection, or fallback
127
+ input. Operation identities, payloads, and retained objects have fixed
128
+ pre-hash/pre-allocation byte bounds. The
129
+ journal records an immutable event attempt before conditionally advancing one versioned `head.bin`; the
130
+ head version is the only commit point. Every returned acknowledgment follows
131
+ an exact event/head version re-read and full reachable-chain validation.
132
+ Unacknowledged event attempts may be adopted or rebased by a fresh process only
133
+ when one transaction and one legal reducer transition are unambiguous. Reachable
134
+ and orphan attempts sharing a transaction ID must have the same canonical
135
+ logical record and workflow.
136
+
137
+ The authority snapshot distinguishes standard GitHub OIDC claims for the
138
+ caller (`repository`, `ref`, `sha`, `workflow_ref`, and
139
+ `workflow_sha`) from `job_workflow_ref` and `job_workflow_sha` for the called
140
+ reusable workflow. The package validates canonical relationships and exact
141
+ expected-versus-observed equality without embedding a consumer repository,
142
+ ID, environment, subject, or caller filename. The consumer-owned activation
143
+ contract must pin its protected caller workflow and branch; a broader release
144
+ workflow must not possess that authority. The
145
+ snapshot also binds the exact OIDC trust-policy digest. Ref and source SHA are
146
+ never collapsed into one invented claim. Append records
147
+ derive repository/run/attempt from this snapshot. The structural boundary is a
148
+ test and host seam, not policy evidence by itself. The package-owned AWS adapter
149
+ decodes the JWT in memory, re-observes STS caller identity, parses live bucket,
150
+ Object Lock, ownership, and public-access configuration, canonicalizes the
151
+ bucket, role, and OIDC trust policies, and derives every boolean and digest it
152
+ returns. Until that adapter is released and the exact infrastructure,
153
+ opaque-byte workflow topology, and retained-object protocol are qualified, the
154
+ reusable workflow is a permissionless, always-failing interface only.
155
+
156
+ The parsed AWS trust projection is exact `aud`, the frozen environment `sub`,
157
+ repository and immutable repository/owner IDs, caller `workflow`, `ref`,
158
+ `environment`, and called `job_workflow_ref`. Current AWS IAM can condition on
159
+ those GitHub claims directly. `workflow_sha` and `job_workflow_sha` remain
160
+ locally re-observed source evidence; the trust-policy projection does not
161
+ pretend AWS conditions on `job_workflow_sha`.
162
+ The frozen activation selects either the exact name-bound environment subject
163
+ or the exact immutable owner/repository-ID-bound form; validation reconstructs
164
+ the selected form and never falls back to the other. The same STS-admitted
165
+ token is locally pinned to `workflow_dispatch`, a branch ref, public repository
166
+ visibility, and a GitHub-hosted runner. OIDC fetch, every SDK send, and every
167
+ response/body stream have a fixed 10,000 ms wall-clock deadline.
168
+ The reusable `job_workflow_ref` itself is conditioned at an exact lowercase
169
+ 40-hex ts-release commit, and the local claim validator requires that suffix to
170
+ equal `job_workflow_sha`. This makes the AWS boundary immutable even though AWS
171
+ does not expose `job_workflow_sha` as a separate condition key. An activated
172
+ job runs the package on Node 22.22.2 through an `env -i` allowlist; it does not
173
+ inherit a caller's Node 24.14.1 process environment.
174
+
115
175
  ## Extension translation
116
176
 
117
177
  The kernel admits extensions only through an owner with a narrow invariant:
package/CHANGELOG.md CHANGED
@@ -1,5 +1,87 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.3.0 - 2026-09-01
6
+
7
+ ### Self-release hardening
8
+
9
+ - Split credential-free preparation, no-upload npm OIDC certification,
10
+ lightweight tag creation, npm publication, and later GitHub publication
11
+ into five exact-SHA dispatch modes. GitHub and npm consume distinct
12
+ content-addressed prepared bundles
13
+ under separately named environments and least-privilege job permissions;
14
+ no default path receives publication authority.
15
+ - Pin GitHub-hosted runner labels, third-party Actions, Node 22.22.2, Bun
16
+ 1.3.14, and the self-release npm CLI at 11.11.0. Reauthenticate the exact
17
+ checkout against the current public `main` tip immediately before every
18
+ preparation or publication boundary, and reject ambient npm credentials.
19
+ - After npm publication, bind the adopted tarball bytes to registry integrity,
20
+ the exact GitHub SLSA v1 provenance statement and run identity, and npm's
21
+ cryptographic signature audit. Select same-run proof for a fresh mutation
22
+ and authenticate the provenance-named prior run for an exact
23
+ `AlreadyEquivalent` response-loss recovery. Pin Sigstore 4.1.0 from the
24
+ audited npm archive and require the exact Fulcio workflow SAN, OIDC issuer,
25
+ environment-bound ID-qualified repository subject, source/workflow SHA, and
26
+ run-invocation certificate extensions. Preserve
27
+ GitHub tag, release, and asset equivalence checks inside the provider-native
28
+ publisher.
29
+ - Keep every credentialed job free of nonlocal Actions by natively checking
30
+ out the exact current-main candidate and executing its local Action. Stage
31
+ GitHub assets only in a private draft; a fresh later full asset reread is the
32
+ sole authority for PATCH-only public promotion. Bind trusted npm children to
33
+ an empty private home and explicit empty mode-0600 user/global configs.
34
+ - Move final npm certification/publication evidence out of the `id-token:
35
+ write` producer jobs. A dependency-free Node bootstrap validates then drops
36
+ the runner-injected OIDC request authority, binds the exact private report,
37
+ and commits one explicitly non-final handoff. A separate no-environment,
38
+ no-id-token job verifies that artifact and alone retains the final v2
39
+ receipt; missing outputs, changed attempts, and unknown writes fail closed.
40
+
41
+ ### Canonical operation journal
42
+
43
+ - Add a provider-neutral `operation-journal` subpath with one opaque canonical
44
+ event envelope, one finite transition reducer, and one versioned S3
45
+ conditional-write protocol. Durable acknowledgments require exact retained
46
+ event/head re-read and full-chain validation; response loss, CAS conflict,
47
+ and fresh-process orphan recovery are bounded and fail closed. Every retained
48
+ attempt that shares a transaction ID must carry the same opaque logical
49
+ record and workflow, including attempts already reachable from the head.
50
+ - Keep provider codecs and provider calls outside the journal package boundary.
51
+ Add the sole package-owned `operation-journal/aws` adapter: it rejects
52
+ ambient AWS configuration, exchanges a GitHub OIDC token directly for one
53
+ short-lived role session, re-observes STS/IAM/S3 authority, parses the exact
54
+ role/trust/bucket policies, and emits only single-part conditional S3 writes.
55
+ Serialized SDK requests and fakes qualify this code without claiming live
56
+ AWS policy or retained-object qualification. No SQLite, Git ref, Actions
57
+ artifact, alternate endpoint, credential chain, or fallback store is added.
58
+ - Bind caller and called reusable-workflow OIDC refs and source SHAs as
59
+ distinct claims, derive event run coordinates from the observed session, and
60
+ include the exact OIDC trust-policy digest in authority drift checks. Pin the
61
+ called workflow at one immutable commit, admit one frozen name- or
62
+ immutable-ID-bound environment subject, and locally require hosted public
63
+ workflow-dispatch branch claims from the same STS-admitted token. Bound every
64
+ OIDC fetch, AWS SDK send, and object stream by a fixed wall-clock deadline.
65
+ Operation identities, payloads, retained objects, request/response
66
+ coordinates, and JWT bytes are independently bounded, and Actions request
67
+ coordinates are consumed from the environment before use. The
68
+ checked-in reusable workflow remains permissionless and always fails until a
69
+ released adapter, exact infrastructure, opaque-byte call topology, and live
70
+ retained-object protocol are separately qualified.
71
+
72
+ ### PyPI embedded-binary distributions
73
+
74
+ - Reopen the repository-specific PyPI wrapper-wheel decision with four
75
+ deterministic `ts-release` wheels: manylinux 2.17 and macOS 13, each for
76
+ x64 and arm64. Every wheel embeds exactly one Bun-compiled executable and
77
+ exposes the `ts-release` Python console script.
78
+ - Add an isolated manual PyPI workflow. Its build job has read-only repository
79
+ authority; its separate `pypi` environment job grants only `id-token: write`
80
+ and invokes `pypa/gh-action-pypi-publish@release/v1` over the transferred
81
+ four-wheel artifact set.
82
+ - Make native self-release builds use the version-bearing Bun entrypoint and
83
+ require the Linux candidate executable to report the exact manifest version.
84
+
3
85
  ## 0.2.2 - 2026-08-14
4
86
 
5
87
  This release delivers the intended `0.2` product relative to `0.0.7` and
package/README.md CHANGED
@@ -94,6 +94,25 @@ publishing is represented as an external, host-owned
94
94
  `pypa/gh-action-pypi-publish@release/v1` path; the stock coordinator neither
95
95
  exchanges its OIDC token nor claims to recover that external upload.
96
96
 
97
+ This repository's selected self-release shape is declared in
98
+ `apps/release-ts/pypi-release.config.json`. It deterministically embeds one
99
+ native executable in each of these `ts-release` wheels:
100
+
101
+ - `py3-none-manylinux_2_17_x86_64`
102
+ - `py3-none-manylinux_2_17_aarch64`
103
+ - `py3-none-macosx_13_0_x86_64`
104
+ - `py3-none-macosx_13_0_arm64`
105
+
106
+ `.github/workflows/pypi-release.yml` prepares the exact four-file set in a
107
+ read-only build job, transfers it with GitHub Actions artifact retention, and
108
+ gives only the separate `pypi` environment job `id-token: write`. That job has
109
+ no checkout or arbitrary command step; it downloads the prepared wheels and
110
+ invokes the official PyPA trusted-publishing Action. The PyPI publisher must
111
+ be configured for owner `mannyc2`, repository `ts-release`, workflow
112
+ `pypi-release.yml`, branch `main`, and environment `pypi` before dispatch.
113
+ The macOS wheel tags certify the cross-compiled artifact targets; they do not
114
+ expand the product's Linux-only execution-host claim.
115
+
97
116
  With the canonical GitHub origin or package repository configured, `init` can
98
117
  discover the exact owner/repository coordinate and write this explicit shape:
99
118
 
@@ -104,9 +123,70 @@ ts-release init --preset bun-npm-github
104
123
  It refuses to guess when no repository coordinate is observable and strictly
105
124
  inspects the exact generated configuration before writing it.
106
125
 
107
- ## Automatic GitHub Actions release
108
-
109
- The default workflow is one job and one Action invocation. Copy the exact
126
+ ## Repository 0.3 self-release
127
+
128
+ The checked-in `.github/workflows/release.yml` is a manual, fail-closed
129
+ five-mode workflow. `prepare-exact-sha` has read-only repository authority
130
+ and independently commits one GitHub-assets bundle and one npm-tarball bundle
131
+ to the content-addressed Actions store. `certify-npm-oidc` adopts the exact npm
132
+ bundle, proves one trusted-publisher exchange through pinned npm's dry-run, and
133
+ proves unchanged anonymous registry state without upload, provenance, or
134
+ publication. `create-tag` has only the separate
135
+ `github-tag` environment and creates or reobserves the lightweight `v0.3.0`
136
+ tag at the exact candidate; it creates no Release. `publish-npm` accepts only
137
+ the npm bundle under the `npm` environment with `id-token: write`, then
138
+ rereads the public registry to prove exact tarball bytes and the exact GitHub
139
+ SLSA provenance identity. The pinned npm archive's Sigstore 4.1.0 verifier
140
+ requires the exact Fulcio workflow SAN and GitHub OIDC issuer, then binds the
141
+ certificate's workflow/source SHA and run-invocation extensions to the same
142
+ candidate and provenance statement. Its repository-subject extension must be
143
+ the ID-qualified `repo:mannyc2@126291407/ts-release@1271545637:environment:npm`
144
+ identity issued for the protected npm environment; the historical ref-bound
145
+ subject is rejected. Generic signature validity is insufficient.
146
+ Only afterward, `publish-github` accepts the GitHub
147
+ bundle plus the already-published npm bundle under the `github-release`
148
+ environment with `contents: write`. Every boundary
149
+ reauthenticates the checkout against the current public `main` tip immediately
150
+ before it can adopt or mutate anything.
151
+
152
+ The npm client itself is fetched from the canonical npm 11.11.0 tarball,
153
+ checked against its fixed SHA-512 and SHA-1 registry digests, and freshly
154
+ rematerialized before every boundary. Any nonempty `npm_config_*`, `PREFIX`,
155
+ `DESTDIR`, token variable, project/user npmrc, or default global npmrc stops
156
+ before repository or provider work.
157
+
158
+ The exact order is preparation, no-upload npm OIDC certification, lightweight
159
+ tag creation, npm publication and report-bound public/provenance verification,
160
+ then GitHub publication. A fresh
161
+ npm mutation must carry provenance for that exact workflow run attempt. If a
162
+ prior run applied the bytes but lost its response, an exact `AlreadyEquivalent`
163
+ retry instead authenticates the earlier canonical publishing run named by the
164
+ public provenance; it never demands impossible same-run provenance from a
165
+ no-op retry. Any uncertain, blocked, malformed, or differently bound Action
166
+ report stops verification. The first
167
+ GitHub publication invocation creates or resumes a private draft and uploads
168
+ only missing exact assets. For a desired public Release that staging result is
169
+ deliberately `uncertain`, not success, and the Release remains nonpublic. A
170
+ fresh invocation with the same candidate and both exact `prepared:gha:`
171
+ references must fully reread draft metadata and every paginated asset, download
172
+ and hash any asset whose API digest is absent, and only then perform a
173
+ PATCH-only public transition. Extra, duplicate, missing, or different assets
174
+ stop that transition. On 2026-09-01, repository environments `github-tag`
175
+ (ID `20986778371`), `npm` (ID `20985327992`), and `github-release`
176
+ (ID `20985328229`) were observed with reviewer `mannyc2`, self-review allowed,
177
+ admin bypass disabled, a custom deployment policy selecting exactly `main`,
178
+ and no environment secrets or variables. Re-query those mutable facts before
179
+ every dispatch. Repository Release immutability was also observed enabled on
180
+ 2026-09-01; re-query it before GitHub staging or promotion. The repository OIDC
181
+ policy was observed on that date with the immutable ID-qualified subject
182
+ `repo:mannyc2@126291407/ts-release@1271545637:environment:npm`; re-query it
183
+ before certification or publication. That GitHub-side policy does not prove an
184
+ npm-side trusted-publisher record. The exact npm trusted-publisher subject
185
+ remains a separate prerequisite and is not inferred from an environment.
186
+
187
+ ## Automatic GitHub Actions template
188
+
189
+ The reusable automatic template is one job and one Action invocation. Copy the exact
110
190
  [automatic workflow template](templates/github-actions/release.yml). It is
111
191
  manual-only and requires `candidate_sha` to equal the current commit on
112
192
  `refs/heads/main` before the job can reach checkout. Its
@@ -118,7 +198,7 @@ recovery artifact.
118
198
 
119
199
  ```yaml
120
200
  - id: release
121
- uses: mannyc2/ts-release/apps/ts-release-action@v0.2.2
201
+ uses: mannyc2/ts-release/apps/ts-release-action@v0.3.0
122
202
  env:
123
203
  GITHUB_TOKEN: ${{ github.token }}
124
204
  with:
@@ -132,7 +212,7 @@ preparation, dispatch the same candidate with the exact emitted
132
212
  `prepared:gha:` reference; the job selects `publish`, loads and verifies the
133
213
  original bundle, and does not rebuild.
134
214
 
135
- `v0.2.2` is the immutable monorepo-subpath coordinate intended for this
215
+ `v0.3.0` is the immutable monorepo-subpath coordinate intended for this
136
216
  candidate. Packaging and release certification must stop unless that tag is
137
217
  created from the exact certified result commit before consumers can see a
138
218
  README that names it. A floating Action branch is never an alternative.
@@ -262,7 +342,9 @@ The kernel translates extension requests to the owner that can enforce them:
262
342
 
263
343
  Homebrew and Scoop rendering/delivery use typed renderers and an exact paired
264
344
  Git Data subject; arbitrary whole-file catalog templating remains excluded.
265
- Wrapper wheels remain excluded from the prebuilt PyPI slice unless an explicit product decision reopens them. PyPI
345
+ Generic wrapper-wheel generation remains excluded from the prebuilt PyPI
346
+ capability. The repository-specific four-wheel `ts-release` self-release is an
347
+ explicit product decision and is prepared by its dedicated workflow. PyPI
266
348
  support is contract-tested but has not been live-write-dogfooded in this wave.
267
349
  Custom library applications may compose full provider subjects through the
268
350
  [`provider-sdk`](https://github.com/mannyc2/ts-release/blob/main/docs/native-extensions.md)
@@ -318,6 +400,47 @@ The public operations are `inspect`, `prepare`, `observe`, `publish`,
318
400
  prepared paths. The derived graph is ephemeral; the verified prepared
319
401
  manifest and blobs are the durable cross-process boundary.
320
402
 
403
+ The separate `@mannyc1/ts-release/operation-journal` subpath exposes the
404
+ provider-neutral canonical envelope and one S3 CAS protocol for durable
405
+ external-operation continuation. It stores consumer-owned opaque bytes and
406
+ never decodes provider fields. Workflow repository/run coordinates are derived
407
+ from the re-observed GitHub OIDC session rather than accepted from an append
408
+ request. A host can supply one already-authenticated, exact-policy S3 boundary
409
+ as the credential-agnostic qualification seam. The separate
410
+ `@mannyc1/ts-release/operation-journal/aws` subpath is the only operational
411
+ backend implementation: it accepts one sealed activation contract, rejects
412
+ ambient AWS variables and shared files, requests GitHub OIDC directly, uses
413
+ only the returned short-lived role session, and parses live STS, IAM, bucket,
414
+ Object Lock, ownership, public-access, and policy responses. It accepts no
415
+ profile, credential, endpoint, alternate bucket, or fallback-store input.
416
+ Operation identities are bounded to 65,536 bytes, payloads to 1,048,576 bytes,
417
+ and retained objects to 1,500,000 bytes before hashing or allocation. The AWS
418
+ trust and activation contract require the
419
+ reusable workflow to be called as `uses: .../operational-journal.yml@<40-hex>`;
420
+ a branch or tag ref is not authority, and the observed `job_workflow_ref` SHA
421
+ must equal `job_workflow_sha`. The frozen subject selects exactly one GitHub
422
+ environment-subject form: name-bound `repo:owner/repository:environment:name`
423
+ or immutable-ID-bound
424
+ `repo:owner@ownerId/repository@repositoryId:environment:name`; observed IDs
425
+ must reconstruct the latter exactly, and there is no runtime fallback between
426
+ forms. The same STS-admitted token must also report `workflow_dispatch`, a
427
+ branch ref, a public repository, and a GitHub-hosted runner. Every OIDC fetch,
428
+ AWS SDK send, and retained-object stream has a fixed 10,000 ms wall-clock
429
+ deadline in addition to single-attempt retry policy.
430
+
431
+ The checked-in `operational-journal.yml` is intentionally inert: it has no
432
+ OIDC permission, AWS coordinate, credential input, checkout, or executable
433
+ adapter and always stops. Activating it requires the separately provisioned
434
+ bucket/role, a released adapter version, a reviewed opaque-byte caller/callee
435
+ transport, and qualification of the exact retained object protocol. The
436
+ serialized SDK requests, structural boundary, and fake tests are not live AWS
437
+ or workflow qualification.
438
+ An activated reusable job must install the package-supported Node 22.22.2
439
+ runtime independently of the caller and launch the adapter through an
440
+ `env -i` allowlist containing only the exact Actions OIDC request coordinates
441
+ and non-secret activation inputs. The caller's Node 24.14.1 runtime is below a
442
+ transitive dependency's admitted Node 24 floor and must not run this package.
443
+
321
444
  ## Agent bundles and development
322
445
 
323
446
  The single tracked agent source owner is `apps/ts-release-agents`. Generated
package/SPEC.md CHANGED
@@ -127,6 +127,57 @@ Read-convergence timing defaults are conservative assumed bounds until live
127
127
  post-write evidence measures them. Announcements and unsupported providers
128
128
  remain outside the retained product.
129
129
 
130
+ The provider-neutral `operation-journal` subpath admits only
131
+ `IntentRecorded`, `ReceiptRecorded`, zero or more `ObservationRecorded`, and
132
+ `TerminalRecorded`, with `OutcomeUnknown` as the terminal alternative from an
133
+ intent. Payload bytes and their codec identifier are opaque to ts-release.
134
+ There is one storage protocol: a versioned, Object-Locked S3 namespace with
135
+ immutable conditional event creates and conditional `head.bin` replacement.
136
+ An acknowledgment exists only after exact retained event/head versions and the
137
+ complete chain are re-read. Conditional conflicts, response loss, and process
138
+ death are reconciled by bounded exact-version reads; ambiguity stops and never
139
+ authorizes a provider resubmission. Credential acquisition, provider codecs,
140
+ and provider calls remain host/consumer responsibilities.
141
+
142
+ The release point must equal the re-observed caller `sha`. Standard OIDC claims
143
+ identify the caller while `job_workflow_ref` and
144
+ `job_workflow_sha` separately identify the called ts-release workflow and its
145
+ exact source commit. The OIDC trust-policy digest is independent of the bucket
146
+ and role-policy digests. Repository ID, run ID, and run attempt in journal
147
+ events come only from that observed session; they are not append inputs. The
148
+ checked-in reusable workflow is inert and always fails without OIDC permission
149
+ until the package-owned `operation-journal/aws` adapter has been released and
150
+ the exact infrastructure, opaque-byte workflow topology, and retained-object
151
+ protocol have been separately qualified. Local fake and serialized-request
152
+ tests establish implementation behavior, not live AWS certification.
153
+ AWS trust must compare `job_workflow_ref` to the reusable workflow path at one
154
+ lowercase 40-hex ts-release commit. The called workflow ref must end in the
155
+ same value as `job_workflow_sha`; branch and tag refs are rejected before STS.
156
+
157
+ The library embeds no consumer repository, immutable ID, environment, subject,
158
+ or caller-workflow name. An activation contract supplies those exact expected
159
+ values and the adapter re-observes them. Its parsed trust projection must bind
160
+ `aud`, the frozen environment `sub`, repository/IDs, caller `workflow`, `ref`,
161
+ `environment`, and `job_workflow_ref`. Workflow source SHAs remain separately
162
+ re-observed evidence and are not misrepresented as AWS trust-condition keys.
163
+ The activation contract freezes either the exact name-bound environment
164
+ subject or its immutable owner/repository-ID-bound form; the adapter admits no
165
+ fallback between those forms. After STS admits the token, local equality also
166
+ requires `event_name=workflow_dispatch`, `ref_type=branch`,
167
+ `repository_visibility=public`, and `runner_environment=github-hosted`.
168
+ The adapter accepts no credential, profile, endpoint, or alternate-store input:
169
+ it rejects ambient AWS variables and shared files, directly exchanges GitHub
170
+ OIDC for one short-lived role session, and derives governance facts from signed
171
+ STS/IAM/S3 responses and exact parsed policies rather than caller booleans.
172
+ Operation identities are limited to 65,536 bytes, consumer payloads to
173
+ 1,048,576 bytes, and every retained object to 1,500,000 bytes; declared and
174
+ streamed lengths are both enforced before hashing or full object allocation.
175
+ Every OIDC fetch, AWS SDK send, and response/body stream is
176
+ bounded by one fixed 10,000 ms wall-clock deadline. The activated reusable job
177
+ uses pinned Node 22.22.2 and an
178
+ `env -i` allowlist. Node 24.14.1 is not in this package's engine because the
179
+ authoritative transitive floor begins at Node 24.15.0.
180
+
130
181
  ## 10. Product evidence
131
182
 
132
183
  In the source distribution, the generated capability inventory is joined
@@ -30,3 +30,10 @@ The preset binds trusted publication to `.github/workflows/release.yml` at
30
30
  `refs/heads/main`. Change both `workflow` and `workflowRef` when another exact
31
31
  workflow/ref is the intended host; a mismatch is rejected before OIDC request
32
32
  material is read.
33
+
34
+ The repository-specific PyPI self-release is separate from that npm/GitHub
35
+ path. `pypi-release.config.json` builds the four Linux/macOS x64/arm64 native
36
+ wheel artifacts, and `scripts/prepare-pypi-distributions.ts` materializes the
37
+ verified set for `.github/workflows/pypi-release.yml`. Its external publisher
38
+ identity is the official PyPA Action bound to the `pypi` GitHub environment;
39
+ the script prepares bytes only and never exchanges OIDC or uploads them.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mannyc1/ts-release-agents",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -25,6 +25,11 @@ const generatedFiles = (directory: string): string[] => readdirSync(directory, {
25
25
  const containsFiles = (directory: string): boolean => readdirSync(directory, { withFileTypes: true }).some((entry) => {
26
26
  return entry.isDirectory() ? containsFiles(join(directory, entry.name)) : true
27
27
  })
28
+ const commandFailure = (result: ReturnType<typeof spawnSync>): string => {
29
+ const stderr = typeof result.stderr === "string" ? result.stderr.trim() : ""
30
+ const stdout = typeof result.stdout === "string" ? result.stdout.trim() : ""
31
+ return stderr || stdout || result.error?.message || "unknown command failure"
32
+ }
28
33
 
29
34
  const firstPaths = buildAgents()
30
35
  const first = firstPaths.map((path) => bytes(path))
@@ -54,7 +59,7 @@ for (const path of ["SKILL.md", join("skills", "release", "SKILL.md")]) {
54
59
  if (existsSync(join(root, path))) fail(`Root canonical agent owner remains: ${path}`)
55
60
  }
56
61
  const tracked = spawnSync("git", ["ls-files", "-z"], { cwd: root, encoding: "utf8", stdio: "pipe" })
57
- if (tracked.status !== 0) fail(`Could not inspect tracked agent owners: ${tracked.stderr.trim() || tracked.stdout.trim()}`)
62
+ if (tracked.status !== 0) fail(`Could not inspect tracked agent owners: ${commandFailure(tracked)}`)
58
63
  const trackedFiles = tracked.stdout.split("\0").filter((path) => path.length > 0)
59
64
  const canonicalRootOwners = trackedFiles.filter((path) =>
60
65
  path === ".codex-plugin/plugin.json" ||
@@ -95,7 +100,7 @@ try {
95
100
  stdio: "pipe"
96
101
  })
97
102
  if (validated.status !== 0) {
98
- fail(`Installed Claude plugin validator failed: ${validated.stderr.trim() || validated.stdout.trim()}`)
103
+ fail(`Installed Claude plugin validator failed: ${commandFailure(validated)}`)
99
104
  }
100
105
  }
101
106
  providerLayouts.push(relative(disposableRoot, installation.packageRoot).replaceAll("\\", "/"))
@@ -21,7 +21,7 @@ describe("agent distribution contract", () => {
21
21
  const native = provider === "codex" ? ".codex-plugin" : ".claude-plugin"
22
22
  const manifest = join(root, ".release", "agents", provider, "ts-release", native, "plugin.json")
23
23
  expect(existsSync(manifest)).toBe(true)
24
- expect(JSON.parse(readFileSync(manifest, "utf8")).version).toBe("0.2.2")
24
+ expect(JSON.parse(readFileSync(manifest, "utf8")).version).toBe("0.3.0")
25
25
  }
26
26
  })
27
27
 
@@ -43,7 +43,7 @@ describe("agent distribution contract", () => {
43
43
  "local-archive",
44
44
  "ts-release"
45
45
  ))
46
- expect(installed.version).toBe("0.2.2")
46
+ expect(installed.version).toBe("0.3.0")
47
47
  expect(installed.packageRoot).toBe(join(installed.providerRoot, installed.version))
48
48
  expect(existsSync(join(installed.packageRoot, provider === "codex" ? ".codex-plugin" : ".claude-plugin", "plugin.json"))).toBe(true)
49
49
  expect(existsSync(join(installed.packageRoot, "skills", "release", "SKILL.md"))).toBe(true)