@mannyc1/ts-release 0.0.2 → 0.0.5

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 (181) hide show
  1. package/ARCHITECTURE.md +55 -15
  2. package/README.md +229 -75
  3. package/SPEC.md +53 -9
  4. package/dist/config/errors.d.ts +2 -1
  5. package/dist/config/errors.d.ts.map +1 -1
  6. package/dist/config/errors.js +4 -3
  7. package/dist/config/errors.js.map +1 -1
  8. package/dist/config/load.d.ts.map +1 -1
  9. package/dist/config/load.js +5 -3
  10. package/dist/config/load.js.map +1 -1
  11. package/dist/config/schema.d.ts +4 -0
  12. package/dist/config/schema.d.ts.map +1 -1
  13. package/dist/config/schema.js +13 -0
  14. package/dist/config/schema.js.map +1 -1
  15. package/dist/domain/artifact.d.ts +3 -3
  16. package/dist/domain/artifact.d.ts.map +1 -1
  17. package/dist/domain/artifact.js +1 -1
  18. package/dist/domain/artifact.js.map +1 -1
  19. package/dist/domain/evidence.d.ts +18 -14
  20. package/dist/domain/evidence.d.ts.map +1 -1
  21. package/dist/domain/evidence.js +8 -3
  22. package/dist/domain/evidence.js.map +1 -1
  23. package/dist/domain/operation.d.ts +15 -14
  24. package/dist/domain/operation.d.ts.map +1 -1
  25. package/dist/domain/operation.js +117 -1
  26. package/dist/domain/operation.js.map +1 -1
  27. package/dist/domain/release.d.ts +84 -9
  28. package/dist/domain/release.d.ts.map +1 -1
  29. package/dist/domain/release.js +76 -5
  30. package/dist/domain/release.js.map +1 -1
  31. package/dist/domain/remote-state.d.ts +21 -16
  32. package/dist/domain/remote-state.d.ts.map +1 -1
  33. package/dist/domain/remote-state.js +14 -8
  34. package/dist/domain/remote-state.js.map +1 -1
  35. package/dist/domain/status.d.ts +4 -4
  36. package/dist/domain/status.d.ts.map +1 -1
  37. package/dist/domain/status.js +3 -2
  38. package/dist/domain/status.js.map +1 -1
  39. package/dist/domain/target.d.ts +7 -7
  40. package/dist/domain/target.d.ts.map +1 -1
  41. package/dist/domain/target.js +1 -1
  42. package/dist/domain/target.js.map +1 -1
  43. package/dist/host/host.d.ts +1 -0
  44. package/dist/host/host.d.ts.map +1 -1
  45. package/dist/host/host.js +2 -1
  46. package/dist/host/host.js.map +1 -1
  47. package/dist/host/http-live.d.ts.map +1 -1
  48. package/dist/host/http-live.js +7 -3
  49. package/dist/host/http-live.js.map +1 -1
  50. package/dist/host/http.d.ts +4 -1
  51. package/dist/host/http.d.ts.map +1 -1
  52. package/dist/host/http.js +5 -2
  53. package/dist/host/http.js.map +1 -1
  54. package/dist/host/platform.d.ts.map +1 -1
  55. package/dist/host/platform.js +2 -2
  56. package/dist/host/platform.js.map +1 -1
  57. package/dist/host/test.d.ts +1 -0
  58. package/dist/host/test.d.ts.map +1 -1
  59. package/dist/host/test.js +67 -7
  60. package/dist/host/test.js.map +1 -1
  61. package/dist/internal/workspace-path.d.ts +16 -0
  62. package/dist/internal/workspace-path.d.ts.map +1 -0
  63. package/dist/internal/workspace-path.js +33 -0
  64. package/dist/internal/workspace-path.js.map +1 -0
  65. package/dist/planner/artifact-inventory.d.ts.map +1 -1
  66. package/dist/planner/artifact-inventory.js +28 -9
  67. package/dist/planner/artifact-inventory.js.map +1 -1
  68. package/dist/planner/create-release-plan.d.ts +1 -1
  69. package/dist/planner/errors.d.ts +12 -6
  70. package/dist/planner/errors.d.ts.map +1 -1
  71. package/dist/planner/errors.js +20 -12
  72. package/dist/planner/errors.js.map +1 -1
  73. package/dist/planner/evidence-recorder.d.ts.map +1 -1
  74. package/dist/planner/evidence-recorder.js +32 -10
  75. package/dist/planner/evidence-recorder.js.map +1 -1
  76. package/dist/planner/executor.d.ts +4 -4
  77. package/dist/planner/executor.d.ts.map +1 -1
  78. package/dist/planner/executor.js +36 -4
  79. package/dist/planner/executor.js.map +1 -1
  80. package/dist/planner/normalize-release.d.ts +11 -2
  81. package/dist/planner/normalize-release.d.ts.map +1 -1
  82. package/dist/planner/normalize-release.js +98 -19
  83. package/dist/planner/normalize-release.js.map +1 -1
  84. package/dist/planner/reconcile.d.ts +1 -1
  85. package/dist/planner/reconcile.d.ts.map +1 -1
  86. package/dist/planner/reconcile.js +75 -92
  87. package/dist/planner/reconcile.js.map +1 -1
  88. package/dist/planner/release-eligibility.d.ts +20 -14
  89. package/dist/planner/release-eligibility.d.ts.map +1 -1
  90. package/dist/planner/release-eligibility.js +431 -25
  91. package/dist/planner/release-eligibility.js.map +1 -1
  92. package/dist/planner/render-plan.d.ts +10 -0
  93. package/dist/planner/render-plan.d.ts.map +1 -1
  94. package/dist/planner/render-plan.js +159 -0
  95. package/dist/planner/render-plan.js.map +1 -1
  96. package/dist/planner/status.d.ts +1 -1
  97. package/dist/planner/status.d.ts.map +1 -1
  98. package/dist/planner/status.js +8 -2
  99. package/dist/planner/status.js.map +1 -1
  100. package/dist/targets/adapter-helpers.d.ts +20 -2
  101. package/dist/targets/adapter-helpers.d.ts.map +1 -1
  102. package/dist/targets/adapter-helpers.js +32 -1
  103. package/dist/targets/adapter-helpers.js.map +1 -1
  104. package/dist/targets/github-release.d.ts +25 -0
  105. package/dist/targets/github-release.d.ts.map +1 -0
  106. package/dist/targets/github-release.js +82 -0
  107. package/dist/targets/github-release.js.map +1 -0
  108. package/dist/targets/github.d.ts +2 -2
  109. package/dist/targets/github.d.ts.map +1 -1
  110. package/dist/targets/github.js +28 -66
  111. package/dist/targets/github.js.map +1 -1
  112. package/dist/targets/homebrew.d.ts +2 -2
  113. package/dist/targets/homebrew.d.ts.map +1 -1
  114. package/dist/targets/homebrew.js +15 -31
  115. package/dist/targets/homebrew.js.map +1 -1
  116. package/dist/targets/scoop.d.ts +2 -2
  117. package/dist/targets/scoop.d.ts.map +1 -1
  118. package/dist/targets/scoop.js +13 -28
  119. package/dist/targets/scoop.js.map +1 -1
  120. package/dist/workflows/config.d.ts +191 -18
  121. package/dist/workflows/config.d.ts.map +1 -1
  122. package/dist/workflows/config.js +339 -61
  123. package/dist/workflows/config.js.map +1 -1
  124. package/dist/workflows/diagnostics.d.ts +62 -0
  125. package/dist/workflows/diagnostics.d.ts.map +1 -0
  126. package/dist/workflows/diagnostics.js +536 -0
  127. package/dist/workflows/diagnostics.js.map +1 -0
  128. package/dist/workflows/evidence.d.ts +8 -3
  129. package/dist/workflows/evidence.d.ts.map +1 -1
  130. package/dist/workflows/evidence.js +9 -0
  131. package/dist/workflows/evidence.js.map +1 -1
  132. package/dist/workflows/index.d.ts +7 -0
  133. package/dist/workflows/index.d.ts.map +1 -0
  134. package/dist/workflows/index.js +6 -0
  135. package/dist/workflows/index.js.map +1 -0
  136. package/dist/workflows/init.d.ts +84 -0
  137. package/dist/workflows/init.d.ts.map +1 -0
  138. package/dist/workflows/init.js +451 -0
  139. package/dist/workflows/init.js.map +1 -0
  140. package/dist/workflows/live.d.ts +2 -0
  141. package/dist/workflows/live.d.ts.map +1 -1
  142. package/dist/workflows/live.js +2 -0
  143. package/dist/workflows/live.js.map +1 -1
  144. package/dist/workflows/options.d.ts +30 -0
  145. package/dist/workflows/options.d.ts.map +1 -0
  146. package/dist/workflows/options.js +16 -0
  147. package/dist/workflows/options.js.map +1 -0
  148. package/examples/README.md +16 -12
  149. package/examples/github-release/release.config.json +1 -0
  150. package/examples/homebrew-tap/release.config.json +1 -0
  151. package/examples/multi-target/release.config.json +5 -1
  152. package/examples/non-strict-skips/release.config.json +1 -0
  153. package/examples/npm-first-publish/release.config.json +1 -0
  154. package/examples/npm-only/release.config.json +5 -1
  155. package/examples/pypi-registry/release.config.json +1 -0
  156. package/examples/scoop-bucket/release.config.json +1 -0
  157. package/package.json +37 -26
  158. package/templates/README.md +59 -0
  159. package/templates/github-actions/plan-and-approved-execute.yml +58 -0
  160. package/templates/github-actions/plan-only.yml +33 -0
  161. package/templates/github-actions/trusted-publishing.yml +61 -0
  162. package/templates/multi-target-homebrew/release.config.json +70 -0
  163. package/templates/multi-target-scoop/release.config.json +72 -0
  164. package/templates/npm-github/release.config.json +50 -0
  165. package/templates/npm-only/release.config.json +39 -0
  166. package/dist/cli/command.d.ts +0 -4
  167. package/dist/cli/command.d.ts.map +0 -1
  168. package/dist/cli/command.js +0 -173
  169. package/dist/cli/command.js.map +0 -1
  170. package/dist/cli/main.d.ts +0 -3
  171. package/dist/cli/main.d.ts.map +0 -1
  172. package/dist/cli/main.js +0 -9
  173. package/dist/cli/main.js.map +0 -1
  174. package/dist/runtime/bun.d.ts +0 -9
  175. package/dist/runtime/bun.d.ts.map +0 -1
  176. package/dist/runtime/bun.js +0 -10
  177. package/dist/runtime/bun.js.map +0 -1
  178. package/dist/version.d.ts +0 -2
  179. package/dist/version.d.ts.map +0 -1
  180. package/dist/version.js +0 -2
  181. package/dist/version.js.map +0 -1
package/ARCHITECTURE.md CHANGED
@@ -2,19 +2,50 @@
2
2
 
3
3
  `@mannyc1/ts-release` is library-first and CLI-second.
4
4
 
5
- The package turns release intent into typed data, plans, evidence, and approved operations. The CLI is only an argv, console, and terminal-file adapter over those workflows.
5
+ The package turns release intent into typed data, plans, evidence, and approved operations. The CLI and GitHub Action are adapters over those workflows.
6
6
 
7
- ## Module Taxonomy
7
+ ## Target Boundary
8
8
 
9
- - `domain/` contains durable schema-backed data models and errors.
9
+ The intended repository shape separates reusable library code from the
10
+ first-party release application:
11
+
12
+ ```text
13
+ src/ reusable TypeScript release library
14
+ apps/release-ts/ official CLI app, Bun runtime shell, and self-release dogfood
15
+ apps/ts-release-action/
16
+ official JavaScript action app and Node runtime shell
17
+ scripts/ repo-wide maintenance gates only
18
+ examples/ reusable release config examples
19
+ ```
20
+
21
+ `src/` contains generic library code only. It may require platform services
22
+ such as `FileSystem`, `Path`, `ReleaseCommandRunner`, `ReleaseHttp`, or
23
+ `HttpClient`, but it must not provide the concrete Bun runtime for the official
24
+ CLI.
25
+
26
+ `apps/release-ts/` owns argv parsing, terminal output, Bun runtime assembly,
27
+ standalone CLI compilation, and self-release policy/config. A module consumed
28
+ only by the official CLI or self-release dogfood belongs in `apps/release-ts/`
29
+ unless it is made generic and documented as public library API.
30
+
31
+ `apps/ts-release-action/` owns GitHub Action input parsing, GitHub step-summary
32
+ and output adapters, evidence artifact upload, and the Node runtime assembly
33
+ used by the bundled action. Action code should call public workflow APIs rather
34
+ than reaching into CLI modules.
35
+
36
+ ## Current Module Taxonomy
37
+
38
+ - `domain/` contains durable schema-backed data models, typed errors, and scalar schemas owned by their semantic domain modules, such as release names in `domain/release`, target IDs in `domain/target`, artifact IDs in `domain/artifact`, operation IDs in `domain/operation`, and evidence IDs in `domain/evidence`.
10
39
  - `config/` parses and validates release config into domain values.
11
40
  - `planner/` normalizes release intent, builds plans, renders plans, executes operation data through injected services, records evidence, reports status, resumes safe work, and reconciles remote state.
12
41
  - `targets/` models ecosystem-specific target semantics and produces operation data. Target modules may describe commands and HTTP checks, but they do not execute them.
13
42
  - `host/` defines injectable command and HTTP services plus live or test implementations.
14
- - `workflows/` contains reusable application workflows over config files, evidence files, and live target/HTTP composition. This is the high-level programmatic surface.
15
- - `runtime/` contains runtime-specific layer assembly. `runtime/bun` is the Bun composition used by the published executable and maintenance scripts.
16
- - `cli/` parses command-line flags, calls workflows, prints terminal output, and writes user-requested CLI output files.
17
- - `scripts/` contains repository maintenance checks. Scripts may use runtime layers, but they are not package library code.
43
+ - `workflows/` contains reusable application workflows over config files, init/scaffolding plans, diagnostics, evidence files, and live target/HTTP composition. This is the high-level programmatic surface.
44
+ - `apps/release-ts/src/runtime/` contains the Bun runtime shell for the official CLI app.
45
+ - `apps/release-ts/src/cli/` parses command-line flags, calls workflows, prints terminal output, and writes user-requested CLI output files.
46
+ - `apps/ts-release-action/src/runtime/` contains the Node runtime shell for the bundled GitHub Action.
47
+ - `apps/ts-release-action/src/` adapts GitHub Action inputs, outputs, step summaries, and artifact uploads to workflow calls.
48
+ - `scripts/` contains repository maintenance checks. Scripts may use app runtime layers, but they are not package library code.
18
49
 
19
50
  ## Dependency Direction
20
51
 
@@ -27,27 +58,36 @@ domain <- config
27
58
  domain <- planner <- targets
28
59
  domain <- host
29
60
  workflows -> config/planner/host/targets
30
- runtime -> host/workflows/platform layers
31
- cli -> workflows/runtime boundary
61
+ apps/release-ts runtime -> host/workflows/platform layers
62
+ apps/release-ts cli -> workflows/runtime boundary
63
+ apps/ts-release-action runtime -> host/workflows/platform layers
64
+ apps/ts-release-action action -> workflows/runtime boundary
32
65
  ```
33
66
 
34
- `src/index.ts` intentionally stays empty. Public API is the explicit subpath list in `package.json`, checked by `scripts/check-package-exports.ts` and `scripts/check-tree-shaking.ts`.
67
+ `src/index.ts` intentionally stays empty. Public API is the explicit subpath list in `package.json`, checked by `scripts/check-package-exports.ts` and `scripts/check-tree-shaking.ts`. The only workflow aggregate is the opt-in `./workflows` facade.
35
68
 
36
69
  ## Public Workflow Surface
37
70
 
38
- There is no public `./api` facade. The public workflow modules are named after the work they own:
71
+ There is no public `./api` facade, and the root package export stays empty. The public workflow modules are named after the work they own:
39
72
 
73
+ - `./workflows` for the curated opt-in `Config`, `Init`, `Diagnostics`, `Evidence`, and `Live` namespaces.
40
74
  - `./workflows/config` for config-file release workflows.
75
+ - `./workflows/init` for data-first scaffolding previews and approved writes.
76
+ - `./workflows/diagnostics` for static config, auth, and CI readiness reports.
41
77
  - `./workflows/evidence` for reusable evidence persistence.
42
78
  - `./workflows/live` for runtime-neutral live target and HTTP services.
43
- - `./runtime/bun` for the Bun runtime composition.
44
79
 
45
- Use lower-level `domain/`, `config/`, `planner/`, `host/`, and `targets/` subpaths when a caller needs more control than the workflow modules provide.
80
+ The `./workflows` facade lives at `src/workflows/index.ts` and should only re-export workflow namespaces. Use exact workflow leaf imports for maximum tree-shaking or direct option-class access, and lower-level `domain/`, `config/`, `planner/`, `host/`, and `targets/` subpaths when a caller needs more control than the workflow modules provide.
46
81
 
47
82
  ## Boundary Rules
48
83
 
49
84
  - Publish operations are data until execution is explicitly approved.
50
85
  - `Effect.run*` belongs at true runtime boundaries.
51
- - Layers are provided at CLI, runtime, script, and test boundaries.
86
+ - Reusable effectful operations use `Effect.fn`; inline orchestration bodies use `Effect.gen`.
87
+ - Durable models, options, target variants, and typed errors use `Schema.Class`, `Schema.TaggedClass`, and `Schema.TaggedErrorClass`.
88
+ - Layers are provided at CLI, action, runtime, script, application, and test boundaries.
52
89
  - Config parsing, evidence persistence, status, resume, reconciliation, and release execution are library workflows, not CLI behavior.
53
- - Terminal formatting, argv parsing, and `--out` file writing belong in `cli/`.
90
+ - Terminal formatting, argv parsing, and `--out` file writing belong in
91
+ `apps/release-ts/src/cli/`.
92
+ - GitHub Action input parsing, output names, step summaries, and evidence artifact
93
+ uploads belong in `apps/ts-release-action/src/`.
package/README.md CHANGED
@@ -2,20 +2,56 @@
2
2
 
3
3
  `@mannyc1/ts-release` turns release intent into explicit, inspectable, repeatable publishing operations.
4
4
 
5
+ The root package is the reusable TypeScript release library. The official Bun
6
+ CLI app lives in `apps/release-ts`, and the root `cli` script delegates to that
7
+ private first-party app.
8
+
5
9
  The default workflow is plan-first:
6
10
 
7
11
  ```sh
12
+ bun run cli validate-config --config release.config.json
8
13
  bun run cli plan --config release.config.json --format text
9
14
  bun run cli run --config release.config.json --execute --approve-irreversible
10
15
  ```
11
16
 
17
+ On GitHub Actions, the primary CI integration is the JavaScript action:
18
+
19
+ ```yaml
20
+ - uses: mannyc2/ts-release-action@v1
21
+ with:
22
+ command: plan
23
+ config: release.config.json
24
+ format: markdown
25
+ ```
26
+
27
+ The action calls the TypeScript workflow APIs directly and keeps target-native
28
+ operations visible in the plan. It defaults to `runtime: bundled`; workspace
29
+ runtime mode is deferred until a same-module-graph Node platform setup can be
30
+ required without surprising users. The source currently lives in
31
+ `apps/ts-release-action`.
32
+
33
+ The action supports the review commands `plan`, `validate-config`, `status`,
34
+ `eligibility`, `doctor`, `check-auth`, and `check-ci`, plus the approved workflow commands
35
+ `validate`, `run`, `resume`, and `reconcile`. Use `upload-evidence: true` when
36
+ an action job should publish collected `.release/evidence` JSON files even after
37
+ a command fails.
38
+
12
39
  The `run` command is the recommended release path: it renders generated files,
13
40
  validates every preflight, executes approved publish operations, and verifies
14
41
  remote state in order. The primitive commands remain available for review and
15
42
  debug flows:
16
43
 
17
44
  ```sh
45
+ bun run cli schema --out release-config.schema.json
46
+ bun run cli init --template npm-github --package @scope/pkg --repo owner/repo
47
+ bun run cli validate-config --config release.config.json --format text
18
48
  bun run cli plan --config release.config.json --format text
49
+ bun run cli plan --config release.config.json --format summary
50
+ bun run cli explain npm:npm-publish --config release.config.json
51
+ bun run cli doctor --config release.config.json --format text
52
+ bun run cli check-auth --config release.config.json --target npm --format text
53
+ bun run cli check-ci --config release.config.json --workflow .github/workflows/release.yml --format markdown
54
+ bun run cli check-intent --config release.config.json --format text
19
55
  bun run cli render --config release.config.json --execute
20
56
  bun run cli validate --config release.config.json
21
57
  bun run cli print --config release.config.json
@@ -35,136 +71,117 @@ bun run cli resume --config release.config.json --execute --approve-irreversible
35
71
  ```
36
72
 
37
73
  Resume skips operations with successful matching evidence, reruns safe read-only failures, and blocks failed publish operations until remote state is reconciled manually.
38
- `eligibility` checks npm and GitHub remote state before the self-release workflow decides whether to run.
74
+ `eligibility` resolves the configured release decision strategy and checks npm and GitHub remote state when a release is intended.
75
+ `check-intent` is a read-only CI gate for the explicit intent-file strategy.
39
76
  `reconcile` is separate from resume: it inspects GitHub release state through the API and can publish a matching draft release with explicit `--execute` without republishing immutable npm versions.
40
77
 
41
78
  The executable is an argv and console adapter over TypeScript workflows. Release workflows are modeled as typed functions first, then exposed through the CLI for terminal and CI usage.
42
79
 
43
80
  ## Imports
44
81
 
45
- The package intentionally avoids aggregate library barrels. The root `release` export is intentionally empty; import the exact module you need from an explicit subpath.
82
+ The package root export is intentionally empty. For onboarding and application workflow code, import the opt-in workflow facade from `@mannyc1/ts-release/workflows`; for maximum tree-shaking or target-author control, import the exact module you need from an explicit subpath.
46
83
 
47
84
  ```ts
48
85
  import * as Effect from "effect/Effect"
49
86
  import { type ReleaseIntent } from "@mannyc1/ts-release/domain/release"
50
87
  import { createReleasePlan } from "@mannyc1/ts-release/planner/create-release-plan"
51
88
  import { validatePlan } from "@mannyc1/ts-release/planner/executor"
52
- import { makeBunReleaseWorkflowRuntimeLayer } from "@mannyc1/ts-release/runtime/bun"
53
89
 
54
- const planAndValidate = (intent: ReleaseIntent) =>
55
- Effect.gen(function*() {
56
- const plan = yield* createReleasePlan(intent)
57
- const evidence = yield* validatePlan(plan)
58
- return { plan, evidence }
59
- }).pipe(
60
- Effect.provide(makeBunReleaseWorkflowRuntimeLayer())
61
- )
90
+ export const planAndValidate = Effect.fn("docs.planAndValidate")(function*(intent: ReleaseIntent) {
91
+ const plan = yield* createReleasePlan(intent)
92
+ const evidence = yield* validatePlan(plan)
93
+ return { plan, evidence }
94
+ })
62
95
  ```
63
96
 
64
- `createReleasePlan` needs a `TargetRegistry` layer. Command execution needs a `ReleaseCommandRunner` layer, while artifact checks and checksum generation use Effect Platform `FileSystem`, `Path`, and `Crypto` services directly. High-level config-file workflows, render writes, and evidence writes also need Effect Platform `FileSystem` and `Path` services. Workflows that verify HTTP evidence, such as `verifyPlan`, `runApprovedReleaseWorkflow`, or direct `VerifyHttpOperation` execution, also need a `ReleaseHttp` layer. `makeBunReleaseWorkflowRuntimeLayer` is the Bun runtime composition used by the published CLI; applications on other runtimes can compose `@mannyc1/ts-release/workflows/live`, `@mannyc1/ts-release/host/platform`, and their platform services at the edge. Tests can import `makeTestReleaseHttpLayer` from `@mannyc1/ts-release/host/http`. Internal Effect imports use deep module paths such as `effect/Effect` and `effect/Layer` to keep bundlers from depending on broad root-package analysis. See `ARCHITECTURE.md` for the module taxonomy.
97
+ `createReleasePlan` needs a `TargetRegistry` layer. Command execution needs a `ReleaseCommandRunner` layer, while artifact checks and checksum generation use Effect Platform `FileSystem`, `Path`, and `Crypto` services directly. High-level config-file workflows, render writes, and evidence writes also need Effect Platform `FileSystem` and `Path` services. Workflows that verify HTTP evidence, such as `verifyPlan`, `runApprovedReleaseWorkflow`, or direct `VerifyHttpOperation` execution, also need a `ReleaseHttp` layer. Applications can compose `@mannyc1/ts-release/workflows` and their platform services at the edge, or use exact lower-level imports from `@mannyc1/ts-release/workflows/live`, `@mannyc1/ts-release/host/platform`, and `@mannyc1/ts-release/host/http`. Tests can import `makeTestReleaseHttpLayer` from `@mannyc1/ts-release/host/http`. Internal Effect imports use deep module paths such as `effect/Effect` and `effect/Layer` to keep bundlers from depending on broad root-package analysis. See `ARCHITECTURE.md` for the module taxonomy.
98
+
99
+ Reusable operations in docs and examples should use `Effect.fn`; workflow bodies use `Effect.gen`. Durable data, options, tagged target variants, and typed errors use `Schema.Class`, `Schema.TaggedClass`, and `Schema.TaggedErrorClass`, with `.make(...)` for construction. Runtime layers are provided once at CLI, action, script, application, or test boundaries.
65
100
 
66
101
  ## TypeScript Workflows
67
102
 
68
103
  Applications can call high-level release workflows without argv arrays or CLI command names:
69
104
 
70
105
  ```ts
106
+ import * as BunHttpClient from "@effect/platform-bun/BunHttpClient"
107
+ import * as BunServices from "@effect/platform-bun/BunServices"
71
108
  import * as Effect from "effect/Effect"
72
- import { makeBunReleaseWorkflowRuntimeLayer } from "@mannyc1/ts-release/runtime/bun"
73
- import {
74
- PlanReleaseConfigOptions,
75
- ReleaseExecutionOptions,
76
- ReleaseEligibilityConfigOptions,
77
- ReleaseReconcileConfigOptions,
78
- ReleaseResumeConfigOptions,
79
- ReleaseStatusOptions,
80
- checkReleaseConfigEligibility,
81
- reconcileReleaseConfig,
82
- planReleaseConfig,
83
- renderReleaseConfigPlan,
84
- resumeReleaseConfig,
85
- runReleaseConfig,
86
- statusReleaseConfig
87
- } from "@mannyc1/ts-release/workflows/config"
109
+ import * as Layer from "effect/Layer"
110
+ import { Config, Live } from "@mannyc1/ts-release/workflows"
88
111
 
89
112
  const root = "/path/to/release-workspace"
90
- const RuntimeLayer = makeBunReleaseWorkflowRuntimeLayer({ root })
113
+ const RuntimeLayer = Live.makeLayer({ root }).pipe(
114
+ Layer.provideMerge(BunServices.layer),
115
+ Layer.provideMerge(BunHttpClient.layer)
116
+ )
91
117
 
92
118
  const textPlan = await Effect.runPromise(
93
- renderReleaseConfigPlan(
94
- PlanReleaseConfigOptions.make({ root, configPath: "release.config.json", format: "text" })
95
- ).pipe(Effect.provide(RuntimeLayer))
119
+ Config.renderPlan({ root, configPath: "release.config.json", format: "text" }).pipe(
120
+ Effect.provide(RuntimeLayer)
121
+ )
96
122
  )
97
123
 
98
124
  const plan = await Effect.runPromise(
99
- planReleaseConfig(PlanReleaseConfigOptions.make({ root, configPath: "release.config.json" })).pipe(
125
+ Config.plan({ root, configPath: "release.config.json" }).pipe(
100
126
  Effect.provide(RuntimeLayer)
101
127
  )
102
128
  )
103
129
 
104
130
  const evidence = await Effect.runPromise(
105
- runReleaseConfig(
106
- ReleaseExecutionOptions.make({
107
- root,
108
- configPath: "release.config.json",
109
- execute: true,
110
- approveIrreversible: true
111
- })
112
- ).pipe(
131
+ Config.run({
132
+ root,
133
+ configPath: "release.config.json",
134
+ execute: true,
135
+ approveIrreversible: true
136
+ }).pipe(
113
137
  Effect.provide(RuntimeLayer)
114
138
  )
115
139
  )
116
140
 
117
141
  const status = await Effect.runPromise(
118
- statusReleaseConfig(
119
- ReleaseStatusOptions.make({ root, configPath: "release.config.json", format: "json" })
120
- ).pipe(
142
+ Config.status({ root, configPath: "release.config.json", format: "json" }).pipe(
121
143
  Effect.provide(RuntimeLayer)
122
144
  )
123
145
  )
124
146
 
125
147
  const resumedEvidence = await Effect.runPromise(
126
- resumeReleaseConfig(
127
- ReleaseResumeConfigOptions.make({
128
- root,
129
- configPath: "release.config.json",
130
- execute: true,
131
- approveIrreversible: true
132
- })
133
- ).pipe(
148
+ Config.resume({
149
+ root,
150
+ configPath: "release.config.json",
151
+ execute: true,
152
+ approveIrreversible: true
153
+ }).pipe(
134
154
  Effect.provide(RuntimeLayer)
135
155
  )
136
156
  )
137
157
 
138
158
  const eligibility = await Effect.runPromise(
139
- checkReleaseConfigEligibility(
140
- ReleaseEligibilityConfigOptions.make({
141
- root,
142
- configPath: "release.config.json"
143
- })
144
- ).pipe(
159
+ Config.checkEligibility({
160
+ root,
161
+ configPath: "release.config.json"
162
+ }).pipe(
145
163
  Effect.provide(RuntimeLayer)
146
164
  )
147
165
  )
148
166
 
149
167
  const reconciliationEvidence = await Effect.runPromise(
150
- reconcileReleaseConfig(
151
- ReleaseReconcileConfigOptions.make({
152
- root,
153
- configPath: "release.config.json",
154
- execute: true
155
- })
156
- ).pipe(
168
+ Config.reconcile({
169
+ root,
170
+ configPath: "release.config.json",
171
+ execute: true
172
+ }).pipe(
157
173
  Effect.provide(RuntimeLayer)
158
174
  )
159
175
  )
160
176
  ```
161
177
 
162
- Use `@mannyc1/ts-release/workflows/config` for high-level config-file workflows, `@mannyc1/ts-release/workflows/evidence` for reusable evidence persistence, and `@mannyc1/ts-release/workflows/live` for the live target/HTTP layer. Bun callers can use `@mannyc1/ts-release/runtime/bun` for the same runtime layer used by the published binary. Node callers can provide Node platform services and a Node HTTP client instead. Use explicit lower-level planner, config, target, host, and domain subpaths when an application needs finer control over planning, execution, or test layers.
178
+ Use `@mannyc1/ts-release/workflows` for the curated `Config`, `Init`, `Diagnostics`, `Evidence`, and `Live` namespaces. Exact leaf imports such as `@mannyc1/ts-release/workflows/config`, `@mannyc1/ts-release/workflows/init`, `@mannyc1/ts-release/workflows/diagnostics`, `@mannyc1/ts-release/workflows/evidence`, and `@mannyc1/ts-release/workflows/live` remain stable and are preferred when an application needs maximum tree-shaking or direct access to option classes. Applications provide platform services at the edge, such as `FileSystem`, `Path`, `HttpClient`, and command execution. Use explicit lower-level planner, config, target, host, and domain subpaths when an application needs finer control over planning, execution, or test layers.
163
179
 
164
180
  ## Example Config
165
181
 
166
182
  ```json
167
183
  {
184
+ "$schema": "https://mannyc2.github.io/ts-release/schema/release-config.schema.json",
168
185
  "identity": {
169
186
  "name": "@mannyc1/ts-release",
170
187
  "version": "0.1.0",
@@ -200,6 +217,7 @@ Use `@mannyc1/ts-release/workflows/config` for high-level config-file workflows,
200
217
  "verifyPackageExists": true
201
218
  },
202
219
  "access": "public",
220
+ "provenance": true,
203
221
  "dryRunSupport": "native",
204
222
  "mutability": "immutable",
205
223
  "recovery": "publish-new-version"
@@ -220,7 +238,62 @@ Use `@mannyc1/ts-release/workflows/config` for high-level config-file workflows,
220
238
  }
221
239
  ```
222
240
 
241
+ The optional `$schema` key powers editor completion and does not change release behavior. Print the derived schema with `bun run cli schema`, and use `bun run cli validate-config` to check JSON syntax and release config shape without running target validators.
242
+
223
243
  Paths are release-workspace relative and may not be absolute or contain parent traversal. `evidenceDirectory` may include the literal `{version}` placeholder, which is resolved during planning so each release version can use its own evidence directory.
244
+ Artifact paths may use `{version}`, `{name}`, and `{normalizedName}`. `normalizedName` removes a leading npm scope marker and replaces `/` with `-`, matching generated self-release artifact names such as `mannyc1-ts-release-0.1.0.tgz`.
245
+
246
+ ## Release Strategies
247
+
248
+ Release identity and release decisions are strategy-backed data. Target adapters still receive a concrete release identity and still produce reviewable, approval-gated operations.
249
+
250
+ | Strategy | Good for | Source of truth |
251
+ |---|---|---|
252
+ | Static config | audited/manual release identity | release config |
253
+ | Package manifest | npm/package releases with one version source | `package.json` |
254
+ | Git tag | tag-triggered release workflows | current Git tag |
255
+ | Conventional commits | automated SemVer from commit messages | commits since the latest matching tag |
256
+ | Intent files | reviewed release intent in PRs | `.release/intents/*.json` |
257
+
258
+ Static identity remains supported:
259
+
260
+ ```json
261
+ {
262
+ "identity": {
263
+ "name": "@scope/pkg",
264
+ "version": "0.1.0",
265
+ "commit": "abc123",
266
+ "tag": "v0.1.0"
267
+ }
268
+ }
269
+ ```
270
+
271
+ For npm-style packages, prefer manifest-derived identity to avoid repeating versions in release config:
272
+
273
+ ```json
274
+ {
275
+ "identity": {
276
+ "_tag": "PackageManifestReleaseIdentitySource",
277
+ "packagePath": "package.json",
278
+ "commit": "HEAD",
279
+ "tagTemplate": "v{version}"
280
+ }
281
+ }
282
+ ```
283
+
284
+ Decision strategies are opt-in through `releaseDecision`. The default `RemoteStateReleaseDecision` uses the resolved identity, then checks npm and GitHub state. Git tag and conventional commit strategies can return `skipped` without error when no release input is present. Intent files are small JSON documents in `.release/intents`:
285
+
286
+ ```json
287
+ {
288
+ "$schema": "https://mannyc2.github.io/ts-release/schema/release-intent.schema.json",
289
+ "package": "@scope/pkg",
290
+ "release": "patch",
291
+ "summary": "Explain the user-visible change.",
292
+ "empty": false
293
+ }
294
+ ```
295
+
296
+ This is a first-party intent-file format, not full Changesets compatibility. Publish operations remain plan data until explicit execution approval, regardless of which strategy chose the intended version.
224
297
 
225
298
  Homebrew tap targets model catalog updates as generated files plus an approval-gated push:
226
299
 
@@ -296,6 +369,24 @@ targets:
296
369
  - npm [NpmRegistryTarget] auth=trusted-publishing runs-in=ci provider=github-actions workflow=release.yml required-permission=id-token:write package-prerequisite=exists dry-run=native strategy=native-command mutability=immutable recovery=publish-new-version
297
370
  ```
298
371
 
372
+ Summary plans provide a compact human first pass over risk, execution gates, target auth setup, and gated operation IDs:
373
+
374
+ ```sh
375
+ bun run cli plan --config release.config.json --format summary
376
+ ```
377
+
378
+ Markdown plans are intended for CI artifacts and PR review:
379
+
380
+ ```sh
381
+ bun run cli plan --config release.config.json --format markdown > release-plan.md
382
+ ```
383
+
384
+ Use `explain` when one operation needs a focused review without executing anything:
385
+
386
+ ```sh
387
+ bun run cli explain npm:npm-publish --config release.config.json
388
+ ```
389
+
299
390
  JSON plans include the same data in a stable, CI-artifact-friendly shape, including `targetCapabilities`.
300
391
 
301
392
  GitHub release verification uses the GitHub REST API to check the release tag, title, draft flag, prerelease flag, and each uploaded artifact name.
@@ -310,10 +401,46 @@ GitHub release verification uses the GitHub REST API to check the release tag, t
310
401
 
311
402
  ## Public API
312
403
 
313
- The intentional public API is the explicit subpath list in `package.json`. The npm package exposes the `release` executable through `bin`; programmatic callers should import high-level workflows from `@mannyc1/ts-release/workflows/config` or use the explicit lower-level planner/config/target/status subpaths for finer control. `release/cli` and `release/cli/command` remain public for applications that need to embed the CLI command adapter.
404
+ The intentional public API is the explicit subpath list in `package.json`. The root package export remains empty. Programmatic callers can use the opt-in `@mannyc1/ts-release/workflows` facade for happy-path workflow APIs, or exact leaf subpaths such as `@mannyc1/ts-release/workflows/config`, `@mannyc1/ts-release/workflows/init`, and `@mannyc1/ts-release/workflows/diagnostics` for maximum tree-shaking and direct option-class access. Lower-level planner/config/target/status subpaths remain available for finer control. The official CLI command adapter lives in the private `apps/release-ts` app rather than the reusable root package API.
314
405
 
315
406
  The package export checker fails if a new export is added without being added to the intentional API list.
316
407
 
408
+ ## Templates
409
+
410
+ Copyable starter configs live in `templates/`. They are authoring starting
411
+ points with placeholder package, repository, tap, and bucket names. Runnable
412
+ fixtures live in `examples/`.
413
+
414
+ Config templates are intentionally narrow:
415
+
416
+ - `npm-only` for an existing npm package using GitHub Actions trusted publishing.
417
+ - `npm-github` for npm plus GitHub Releases.
418
+ - `multi-target-homebrew` for npm, GitHub Releases, and a Homebrew tap.
419
+ - `multi-target-scoop` for npm, GitHub Releases, and a Scoop bucket.
420
+
421
+ ```sh
422
+ bun run cli init --template npm-github --package @scope/pkg --repo owner/repo
423
+ bun run cli init --template npm-github --package @scope/pkg --repo owner/repo --write
424
+ bun run cli init --template npm-github --package @scope/pkg --repo owner/repo --github-actions --package-manager npm --write
425
+ bun run cli plan --config release.config.json --format text
426
+ ```
427
+
428
+ The npm templates enable provenance and set `verifyPackageExists: true`, which
429
+ adds a read-only `npm view <package>` validation before trusted publishing.
430
+ Add `--github-actions` to include the action-first trusted-publishing workflow
431
+ template in the preview or write set. Workflow scaffolding supports
432
+ `--package-manager bun|npm|pnpm|yarn`, plus single-line `--install-command` and
433
+ `--build-command` overrides. Those commands are CI setup steps, not release
434
+ target policy; publish operations still come from the `ts-release` plan.
435
+ Existing files are not overwritten unless `--overwrite` is also passed.
436
+
437
+ Action-first GitHub templates live under `templates/github-actions/`. The
438
+ checked-in templates use npm setup by default, while this repository's own
439
+ self-release workflow uses the Bun preset.
440
+
441
+ Use `doctor`, `check-auth`, and `check-ci` after writing a template to inspect
442
+ static readiness before any publish operation is approved.
443
+
317
444
  ## Examples
318
445
 
319
446
  Runnable example configs live in `examples/`:
@@ -327,14 +454,18 @@ Runnable example configs live in `examples/`:
327
454
  - `examples/scoop-bucket`
328
455
  - `examples/non-strict-skips`
329
456
 
330
- `examples/multi-target` demonstrates one release coordinated across a release host, a package registry, and an installer catalog. Build the package first, then plan an example from its directory:
457
+ `examples/multi-target` demonstrates one release coordinated across GitHub Releases, npm, and a Homebrew tap. The focused fixtures cover PyPI, Scoop, npm-only trusted publishing, token-based first npm publish, GitHub-only releases, and non-strict skipped validators. Build the package first, then plan an example from its directory:
331
458
 
332
459
  ```sh
333
460
  bun run build
334
461
  cd examples/multi-target
335
- bun ../../dist/cli/main.js plan --config release.config.json --format text
462
+ bun ../../apps/release-ts/src/cli/main.ts plan --config release.config.json --format text
336
463
  ```
337
464
 
465
+ Trusted-publishing npm examples use provenance and `verifyPackageExists`. The
466
+ `npm-first-publish` example intentionally stays token-based because npm trusted
467
+ publishing can only be configured after the package already exists.
468
+
338
469
  ## Evidence
339
470
 
340
471
  Render, validation, execution, and verification evidence is written as JSON bundles. Failed commands still preserve partial evidence before the command failure is returned. Use `evidenceDirectory` such as `.release/evidence/{version}` when older local evidence should not collide with the current release version.
@@ -348,6 +479,7 @@ Render, validation, execution, and verification evidence is written as JSON bund
348
479
  {
349
480
  "id": "npm:npm-pack-dry-run:command",
350
481
  "operationId": "npm:npm-pack-dry-run",
482
+ "operationFingerprint": "{\"_tag\":\"ValidateCommandOperation\",\"id\":\"npm:npm-pack-dry-run\",\"targetId\":\"npm\",\"approval\":{\"requiresExecute\":false,\"requiresIrreversibleApproval\":false},\"command\":{\"executable\":\"npm\",\"args\":[\"pack\",\"--dry-run\",\"--json\"],\"requiredEnv\":[],\"redactedEnv\":[]}}",
351
483
  "status": "passed",
352
484
  "severity": "info",
353
485
  "exitCode": 0
@@ -366,7 +498,9 @@ Normal verification stays deterministic and does not require live external servi
366
498
  bun run check:release
367
499
  ```
368
500
 
369
- CI runs the portable package checks on Linux, macOS, and Windows. The Ubuntu release-readiness lane also runs the self-release config guard.
501
+ CI runs the portable package checks on Linux, macOS, and Windows. The release
502
+ gate runs the self-release config guard, the static self-release CI diagnostic,
503
+ and then the portable checks.
370
504
 
371
505
  Real-tool integration checks are opt-in:
372
506
 
@@ -377,16 +511,17 @@ RELEASE_INTEGRATION_GITHUB=1 bun run test:integration:tools
377
511
 
378
512
  The first command validates npm adapter operations against the real `npm` CLI. The second also validates GitHub adapter readiness checks against the real `gh` CLI and requires `gh auth status` to succeed. GitHub release creation itself has no native dry-run; release validation is simulated from the deterministic plan before publish and verified against GitHub only after publish.
379
513
 
380
- Example configs are checked through the TypeScript workflow path:
514
+ Example configs and templates are checked through the TypeScript workflow path:
381
515
 
382
516
  ```sh
383
517
  bun run check:examples
384
518
  ```
385
519
 
386
- This repository also includes a first release config at `release.config.json` that targets both npm and GitHub for the scoped `@mannyc1/ts-release` package. The self-release config must pass `bun run check:self-release-config` before release checks proceed. Its `identity.commit` may be the explicit current short commit, or `HEAD` as a stored-config convenience. Generated plans resolve `HEAD` to the current short commit, and the self-release guard requires a committed Git checkout with clean tracked files.
520
+ This repository also includes a self-release config at `apps/release-ts/release.config.json` that targets both npm and GitHub for the scoped `@mannyc1/ts-release` package. The app-owned self-release scripts live under `apps/release-ts/scripts`, with root package scripts delegating to them. The self-release config must pass `bun run check:self-release-config`, and the workflow must pass `bun run check:self-release-ci`, before portable release checks proceed. It derives name and version from the root `package.json`, uses `{version}` artifact templates, and keeps `identity.commit` as `HEAD` for stored-config convenience. Generated plans resolve `HEAD` to the current short commit, and the self-release guard requires a committed Git checkout with clean tracked files.
387
521
 
388
522
  ```sh
389
523
  bun run check:self-release-config
524
+ bun run check:self-release-ci
390
525
  ```
391
526
 
392
527
  ### Self Release
@@ -396,10 +531,10 @@ The local non-publish gates for this package are:
396
531
  ```sh
397
532
  bun run check:release
398
533
  bun run release:artifacts
399
- bun dist/cli/main.js plan --config release.config.json --format text
534
+ bun run cli plan --config apps/release-ts/release.config.json --format text
400
535
  ```
401
536
 
402
- `release:artifacts` writes ignored files under `.release/artifacts`: the npm package tarball and standalone CLI executables for Linux, macOS, and Windows. GitHub Actions runs the approved release workflow on protected `main` when the package version has not already been published. The workflow uses npm trusted publishing with GitHub Actions OIDC instead of an npm token, and uploads `.release/evidence/**` for audit.
537
+ `release:artifacts` delegates to `apps/release-ts/scripts/build-release-artifacts.ts` and writes ignored files under `.release/artifacts`: the npm package tarball and standalone CLI executables for Linux, macOS, and Windows. GitHub Actions runs on protected `main` and checks release eligibility before the full release gate. When `should_release` is true, the plan job runs `check:release`, builds artifacts, records a Markdown release plan, uploads evidence, and does not execute release operations. The protected `execute` job uses the reviewed `.release/artifacts` download, grants `contents: write` and `id-token: write`, and runs approved execution with npm trusted publishing OIDC instead of an npm token.
403
538
 
404
539
  ### Local Release Auth
405
540
 
@@ -407,6 +542,7 @@ Use `.env.example` as the local credential contract. Export `GH_TOKEN`, or copy
407
542
  `.env.example` to `.env` and fill in token values locally. `.env` and `.npmrc`
408
543
  are ignored intentionally; keep token values out of commits. `.npmrc.example`
409
544
  shows npm's `${NPM_TOKEN}` interpolation form for token-based npm targets.
545
+ `TWINE_USERNAME` and `TWINE_PASSWORD` are only needed for PyPI/Twine examples.
410
546
 
411
547
  For npmjs releases from GitHub Actions, prefer structured `trustedPublishing` on
412
548
  the npm target:
@@ -419,7 +555,9 @@ the npm target:
419
555
  "workflow": "release.yml",
420
556
  "packageExists": true,
421
557
  "verifyPackageExists": true
422
- }
558
+ },
559
+ "access": "public",
560
+ "provenance": true
423
561
  }
424
562
  ```
425
563
 
@@ -433,6 +571,22 @@ publishing for an existing package and use a GitHub-hosted runner with
433
571
  check. Trusted publishing does not use `NPM_TOKEN` for `npm publish`; token-based
434
572
  npm targets may still use `.npmrc.example` and `NPM_TOKEN`.
435
573
 
574
+ Diagnostics stay static by default and report confidence instead of pretending
575
+ local checks prove provider setup:
576
+
577
+ ```sh
578
+ bun run cli check-auth --config release.config.json --format text
579
+ bun run cli check-ci --config release.config.json --workflow .github/workflows/release.yml --format markdown
580
+ bun run cli doctor --config release.config.json --format json
581
+ ```
582
+
583
+ The reusable GitHub Actions trusted-publishing workflow template lives at
584
+ `templates/github-actions/trusted-publishing.yml`. It uses
585
+ `mannyc2/ts-release-action@v1` to record a Markdown plan, uploads review
586
+ artifacts, and requires a protected `release` environment before running
587
+ approved execution. npm trusted publishing uses OIDC, not `NPM_TOKEN`;
588
+ `GH_TOKEN` is for GitHub Releases and API verification.
589
+
436
590
  ### First npm Publish Bootstrap
437
591
 
438
592
  Trusted publishing is the preferred steady state for GitHub Actions, but npm