opencode-ship 0.10.0-rc.19 → 0.10.0-rc.28
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.
- package/CHANGELOG.md +63 -17
- package/README.md +3 -3
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/cli.js +8 -6
- package/dist/core.js +1 -1
- package/dist/plugin.js +2 -2
- package/docs/release/1.0.0-execution-plan.md +972 -0
- package/package.json +2 -1
- package/tests/plugin/expected-tools.mjs +56 -0
- package/tests/plugin/plugin-load.test.mjs +7 -27
package/CHANGELOG.md
CHANGED
|
@@ -4,22 +4,68 @@ All notable changes to `opencode-ship` are recorded here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
-
- The `release/
|
|
8
|
-
production runtime for the planned `0.10.0`
|
|
9
|
-
`
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
7
|
+
- The `release/0.10.0` branch is the live release branch carrying
|
|
8
|
+
the complete production runtime for the planned `0.10.0` and
|
|
9
|
+
`1.0.0` releases. Release candidates are published under the
|
|
10
|
+
`next` npm dist-tag. No `0.10.0` or `1.0.0` has been published;
|
|
11
|
+
consumers should keep using `opencode-ship@0.9.0`. The local
|
|
12
|
+
`v0.10.0` and `v1.0.0` tags are placeholders only and must not
|
|
13
|
+
be pushed.
|
|
14
|
+
- See `docs/release/1.0.0-execution-plan.md` for the authoritative
|
|
15
|
+
execution plan and issue #37 for the bounded evidence ledger.
|
|
16
|
+
|
|
17
|
+
### Release-qualification gaps closed (S1)
|
|
18
|
+
|
|
19
|
+
These changes close the S1 release-qualification gaps on the
|
|
20
|
+
`release/0.10.0` branch:
|
|
21
|
+
|
|
22
|
+
- **Real Node compatibility lanes.** The `node-compat` job's
|
|
23
|
+
`setup-node` step now drives from `${{ matrix.node }}` for each
|
|
24
|
+
matrix row (`22.6.0`, current `22`, `24`); the trusted-publishing
|
|
25
|
+
`publish` job remains the only place that pins `22.14.0`. The
|
|
26
|
+
per-row observed `node --version` output is uploaded and
|
|
27
|
+
aggregated into the qualification report by
|
|
28
|
+
`scripts/compose-node-versions.mjs`.
|
|
29
|
+
- **Real OpenCode startup and discovery smoke.** A new
|
|
30
|
+
`tests/release/opencode-discovery.test.mjs` boots a real
|
|
31
|
+
`opencode serve` instance against a packed-tarball fixture
|
|
32
|
+
with both `core` and `engineering` profiles, polls
|
|
33
|
+
`/global/health`, reads `/experimental/tool/ids`, and asserts
|
|
34
|
+
the canonical 24-tool set exported from
|
|
35
|
+
`tests/plugin/expected-tools.mjs`. The canonical set is the
|
|
36
|
+
single source of truth shared by the in-process plugin-load
|
|
37
|
+
test and the live-server smoke.
|
|
38
|
+
- **Correct prerelease metadata.** The publish job now resolves a
|
|
39
|
+
`prerelease` flag from a dedicated step that delegates to
|
|
40
|
+
`scripts/is-prerelease.mjs`. SemVer prereleases (`-rc.N`,
|
|
41
|
+
`-alpha.N`, `-beta.N`) become `prerelease: true`; stable
|
|
42
|
+
versions become `prerelease: false`.
|
|
43
|
+
- **Version-independent runtimeSourceSha256.** A new
|
|
44
|
+
`scripts/runtime-source-sha.mjs` computes a deterministic
|
|
45
|
+
digest over `src/**`, `assets/**`, `schema/**`, `vendor/**`,
|
|
46
|
+
`scripts/build.mjs`, `scripts/prepack.mjs`, and a normalised
|
|
47
|
+
`package.json` with the top-level `version` field removed.
|
|
48
|
+
The qualification report carries `runtimeSourceSha256`, and the
|
|
49
|
+
release-policy job refuses any `1.0.x` tag whose digest does
|
|
50
|
+
not match the accepted `0.10.0` qualification artifact
|
|
51
|
+
(via `scripts/promote-1.0-policy.mjs`).
|
|
52
|
+
- **Truthful documentation.** README, CHANGELOG, and this file
|
|
53
|
+
no longer claim `release/1.0-completion` is the live branch,
|
|
54
|
+
no longer assert a stale test-count baseline, and no longer
|
|
55
|
+
promise a promotion rule that depends on equal source SHAs
|
|
56
|
+
between `0.10.0` and `1.0.0`.
|
|
57
|
+
- **Stronger neutral-consumer assertions.**
|
|
58
|
+
`tests/package/neutral-consumer.test.mjs` now requires explicit
|
|
59
|
+
model IDs before any engineering install writes, asserts the
|
|
60
|
+
engineering install's exit code is `0` before doctor runs, and
|
|
61
|
+
covers the engineering-init-without-models failure path.
|
|
62
|
+
|
|
63
|
+
### Shipped source on `release/0.10.0` (not yet released)
|
|
64
|
+
|
|
65
|
+
These changes are present in source on the `release/0.10.0`
|
|
66
|
+
branch but are not production-ready until the formal registry
|
|
67
|
+
dogfood on the npm-published `0.10.0` succeeds. Items will move
|
|
68
|
+
to a dated release header only after that step completes.
|
|
23
69
|
|
|
24
70
|
- Crash-safe Git-common storage: link()-based atomic publication
|
|
25
71
|
with rename() fallback, SHA-256 hashed resource locks, explicit
|
|
@@ -76,7 +122,7 @@ that step completes.
|
|
|
76
122
|
|
|
77
123
|
### Planned (not yet shipped)
|
|
78
124
|
|
|
79
|
-
These changes are present in source on the `release/
|
|
125
|
+
These changes are present in source on the `release/0.10.0` branch but are not production-ready and have not been published. Items will move to a dated release header only after the relevant plan task is verified end-to-end and the formal registry dogfood passes.
|
|
80
126
|
|
|
81
127
|
- Lock schema v3 with a `scope` field per root pointer record (core | engineering) and byte-stable hash identity.
|
|
82
128
|
- `src/state/git-common-dir.js` and `src/state/durable-store.js` for the shared crash-safe storage under the resolved Git common directory.
|
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> npm-distributed OpenCode installer and delivery plugin: a single command materialises the lifecycle plugin, reviewer/verifier agents, and skills into any consumer repository, with a recoverable lock and never silently overwrites managed files.
|
|
4
4
|
>
|
|
5
|
-
> **Status:** v0.9.0
|
|
5
|
+
> **Status:** v0.9.0 is the current `npm latest`. The `release/0.10.0` branch is the live release branch carrying the complete production runtime for the upcoming `0.10.0` and `1.0.0` releases: 24 typed tools (9 existing delivery tools + 7 control-plane tools + 8 workflow tools), the deterministic controller, the contract-version-2 envelope, the GitHub operation store, the immutable plan + run + commit ledgers, the same-HEAD gate, the bounded compaction block, the per-run resume lock, the fail-closed engineering profile, the version-independent `runtimeSourceSha256` digest, the ten-job release qualification pipeline with a real OpenCode discovery smoke, and the prerelease-aware GitHub release metadata. Release candidates are published under `npm dist-tag next`; stable `0.10.0` and `1.0.0` remain unpublished until their gates pass. The previously-mentioned `release/1.0-completion` branch is not the live release branch and is not referenced by this README or `RELEASING.md`. See `docs/release/1.0.0-execution-plan.md` for the authoritative execution plan and issue #37 for the bounded evidence ledger.
|
|
6
6
|
>
|
|
7
|
-
>
|
|
7
|
+
> Consumers should keep using `opencode-ship@0.9.0` until `1.0.0` is promoted to `npm dist-tag latest`.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -113,7 +113,7 @@ Existing consumers of `opencode-delivery@0.1.x` (commit-pinned shim) can run `pn
|
|
|
113
113
|
|
|
114
114
|
## Development
|
|
115
115
|
|
|
116
|
-
`npm run verify` runs `format:check`, `lint`, `typecheck`, `build`, and the auto-discovered test suite. The tests cover the installer CLI, the lock and root-config planners, the catalog validator, the schema validator, agents, the packed-artifact smoke check, the transaction-recovery contract, the profile-resolution precedence chain, the catalog profile filter, the order-preserving root-config merge, the engineering↔core transition, the durable plan artifact, the Plan Mode permission integration, the M3 task loop contract (run store + task brief + Spec/Quality verdicts + 3-round breaker + commit binding + compaction context), the Ready gate (parallel GPT Standards/Spec + verifier + CI on one HEAD; Build cannot self-record),
|
|
116
|
+
`npm run verify` runs `format:check`, `lint`, `typecheck`, `build`, and the auto-discovered test suite. The tests cover the installer CLI, the lock and root-config planners, the catalog validator, the schema validator, agents, the packed-artifact smoke check, the transaction-recovery contract, the profile-resolution precedence chain, the catalog profile filter, the order-preserving root-config merge, the engineering↔core transition, the durable plan artifact, the Plan Mode permission integration, the M3 task loop contract (run store + task brief + Spec/Quality verdicts + 3-round breaker + commit binding + compaction context), the Ready gate (parallel GPT Standards/Spec + verifier + CI on one HEAD; Build cannot self-record), the transition matrix smoke (core omits engineering, engineering adds engineering, lock tracks the active profile), the static release-policy tests (Node matrix wiring, prerelease metadata, runtime-source digest wiring, 1.0 promotion policy), the runtime-source digest algorithm tests (deterministic across clean checkouts; ignores version and release-metadata changes; tracks every runtime-source modification), and the OpenCode live-server discovery smoke (24-tool canonical set exposed via `/experimental/tool/ids` on a real `opencode serve` instance for both `core` and `engineering` profiles). The suite count and per-test count are derived from the test runner at qualification time; this README intentionally does not lock in a specific test-count baseline.
|
|
117
117
|
|
|
118
118
|
```
|
|
119
119
|
npm ci
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Third-Party Notices
|
|
2
2
|
|
|
3
|
-
`opencode-ship@0.10.0-rc.
|
|
3
|
+
`opencode-ship@0.10.0-rc.28` ships the complete Matt Pocock and Superpowers
|
|
4
4
|
methodology under the MIT license, plus the Ship-owned installer,
|
|
5
5
|
plugin, agents, and skills. The complete immutable pin list is in
|
|
6
6
|
`vendor/sources.json`; the upstream snapshots are under
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// opencode-ship CLI v0.10.0-rc.
|
|
2
|
+
// opencode-ship CLI v0.10.0-rc.28
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __esm = (fn, res) => function __init() {
|
|
@@ -689,7 +689,7 @@ function resolvePackageRoot(startUrl) {
|
|
|
689
689
|
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
690
690
|
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
691
691
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
692
|
-
var PACKAGE_VERSION = "0.10.0-rc.
|
|
692
|
+
var PACKAGE_VERSION = "0.10.0-rc.28";
|
|
693
693
|
var TEMPLATE_SET = `v${PACKAGE_VERSION}`;
|
|
694
694
|
|
|
695
695
|
// src/installer/catalog.js
|
|
@@ -2960,7 +2960,7 @@ async function assembleLock({ repoRoot, plan, lock, configPlan, rootPlan, profil
|
|
|
2960
2960
|
manager: {
|
|
2961
2961
|
schemaVersion: CURRENT_LOCK_SCHEMA,
|
|
2962
2962
|
name: "opencode-ship",
|
|
2963
|
-
version: "0.10.0-rc.
|
|
2963
|
+
version: "0.10.0-rc.28",
|
|
2964
2964
|
templateSet: TEMPLATE_SET_ID,
|
|
2965
2965
|
// Newly written locks always carry the resolved profile so
|
|
2966
2966
|
// future CLI invocations without --profile still resolve to
|
|
@@ -3535,7 +3535,8 @@ async function runDiff(options) {
|
|
|
3535
3535
|
profile: options.profile ?? null,
|
|
3536
3536
|
replaceManaged: false,
|
|
3537
3537
|
forceConfig: false,
|
|
3538
|
-
forceRootConfig: false
|
|
3538
|
+
forceRootConfig: false,
|
|
3539
|
+
models: options.models ?? null
|
|
3539
3540
|
});
|
|
3540
3541
|
if (!preview.ok) {
|
|
3541
3542
|
const exitCode = preview.error?.kind === "unsupported-lock-schema" ? 5 : preview.error?.kind === "lock-invalid" ? 3 : 2;
|
|
@@ -3610,7 +3611,8 @@ async function runUpdate(options) {
|
|
|
3610
3611
|
profile: options.profile ?? null,
|
|
3611
3612
|
replaceManaged: options.replaceManaged,
|
|
3612
3613
|
forceConfig: options.forceConfig,
|
|
3613
|
-
forceRootConfig: options.forceRootConfig
|
|
3614
|
+
forceRootConfig: options.forceRootConfig,
|
|
3615
|
+
models: options.models ?? null
|
|
3614
3616
|
});
|
|
3615
3617
|
if (!preview.ok) {
|
|
3616
3618
|
if (preview.error?.kind === "unsupported-lock-schema") {
|
|
@@ -3791,7 +3793,7 @@ ${helpText()}`);
|
|
|
3791
3793
|
});
|
|
3792
3794
|
return;
|
|
3793
3795
|
case "diff":
|
|
3794
|
-
await runDiff({ json: !!opts.json, rootPath: opts.rootPath, profile });
|
|
3796
|
+
await runDiff({ json: !!opts.json, rootPath: opts.rootPath, profile, models: hasModels ? models : null });
|
|
3795
3797
|
return;
|
|
3796
3798
|
case "update":
|
|
3797
3799
|
await runUpdate({
|
package/dist/core.js
CHANGED
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// opencode-ship v0.10.0-rc.
|
|
1
|
+
// opencode-ship v0.10.0-rc.28
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
4
|
var __esm = (fn, res) => function __init() {
|
|
@@ -16793,7 +16793,7 @@ function flattenShipConfig(ship) {
|
|
|
16793
16793
|
import { readFileSync as readFileSync2, existsSync as existsSync12 } from "node:fs";
|
|
16794
16794
|
import { dirname as dirname7, resolve as resolve13 } from "node:path";
|
|
16795
16795
|
import { fileURLToPath } from "node:url";
|
|
16796
|
-
var PACKAGE_VERSION = "0.10.0-rc.
|
|
16796
|
+
var PACKAGE_VERSION = "0.10.0-rc.28";
|
|
16797
16797
|
var TEMPLATE_SET = `v${PACKAGE_VERSION}`;
|
|
16798
16798
|
|
|
16799
16799
|
// src/plugin.js
|
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
# opencode-ship 1.0.0 execution plan
|
|
2
|
+
|
|
3
|
+
> Status: Approved for execution
|
|
4
|
+
> Owner: Maintainer
|
|
5
|
+
> Last reviewed: 2026-08-07
|
|
6
|
+
> Tracker: https://github.com/Viktorxyz/opencode-ship/issues/37
|
|
7
|
+
> Related: [RELEASING.md](../../RELEASING.md),
|
|
8
|
+
> [README.md](../../README.md), [CHANGELOG.md](../../CHANGELOG.md)
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
Finish `opencode-ship` as a registry-installed OpenCode engineering workflow,
|
|
13
|
+
prove it in a neutral repository with real OpenCode, GitHub, and model
|
|
14
|
+
dispatch, publish a byte-equivalent `1.0.0`, move npm `latest` to `1.0.0`, and
|
|
15
|
+
then install it in a real consumer project.
|
|
16
|
+
|
|
17
|
+
This plan is deliberately explicit so a low-cost Build model can execute
|
|
18
|
+
reversible steps without making architecture or release-policy decisions.
|
|
19
|
+
|
|
20
|
+
## Fixed decisions
|
|
21
|
+
|
|
22
|
+
| Decision | Value |
|
|
23
|
+
|---|---|
|
|
24
|
+
| Final npm target | `opencode-ship@1.0.0` on `latest` |
|
|
25
|
+
| Consumer profile | `engineering` |
|
|
26
|
+
| Planner model | `openai/gpt-5.6-sol` |
|
|
27
|
+
| Builder model | `minimax/MiniMax-M3` |
|
|
28
|
+
| Final reviewer model | `openai/gpt-5.6-sol` |
|
|
29
|
+
| First real qualification consumer | `Viktorxyz/opencode-ship-dogfood` |
|
|
30
|
+
| Package source | npm registry only for external dogfood |
|
|
31
|
+
| Merge policy | Explicit user request only |
|
|
32
|
+
| Release policy | Immutable versions; a failed published RC is replaced by a new RC |
|
|
33
|
+
|
|
34
|
+
Do not revisit these decisions during execution unless the user explicitly
|
|
35
|
+
changes them.
|
|
36
|
+
|
|
37
|
+
## Current factual baseline
|
|
38
|
+
|
|
39
|
+
Re-check every value before starting because registry and branch state can
|
|
40
|
+
change between sessions.
|
|
41
|
+
|
|
42
|
+
| Item | Value at plan approval |
|
|
43
|
+
|---|---|
|
|
44
|
+
| Upstream repository | `/home/viktorcordas/repos/opencode-ship` |
|
|
45
|
+
| Active release branch | `release/0.10.0` |
|
|
46
|
+
| Active release HEAD | `9bc178c692cd197982c64c109440ece9205c460b` |
|
|
47
|
+
| Published candidate | `0.10.0-rc.19` |
|
|
48
|
+
| npm `next` | `0.10.0-rc.19` |
|
|
49
|
+
| npm `latest` | `0.9.0` |
|
|
50
|
+
| RC19 npm integrity | `sha512-rpD5sgCokj2GNBnXmnbRhnQh5FpHel2GtzspApLTMcQaZCuLEkt1iJcRqhwtzqY8ikC+nFHLWPkuvksNQFeXlw==` |
|
|
51
|
+
| RC19 tarball SHA-256 | `410ef29ed3189fb041c44c51006b685b46b6d1d2b8da45128d1bc4ea7b3ca8d7` |
|
|
52
|
+
| Neutral dogfood repository | Does not exist yet |
|
|
53
|
+
| Obsolete Leo migration | `Viktorxyz/leo#221` |
|
|
54
|
+
|
|
55
|
+
`Viktorxyz/leo#221` is not a migration base. It targets the abandoned
|
|
56
|
+
`practices`/v0.4 proposal, is conflicting, and was produced by copying an
|
|
57
|
+
unpublished artifact. It must eventually be closed as superseded by a fresh
|
|
58
|
+
registry-generated migration.
|
|
59
|
+
|
|
60
|
+
## Scope
|
|
61
|
+
|
|
62
|
+
The critical path includes:
|
|
63
|
+
|
|
64
|
+
1. Persisting the release tracker and evidence ledger.
|
|
65
|
+
2. Closing known release-qualification gaps.
|
|
66
|
+
3. Publishing and qualifying one final RC.
|
|
67
|
+
4. Running registry-sourced neutral install acceptance.
|
|
68
|
+
5. Publishing stable `0.10.0`.
|
|
69
|
+
6. Running the real 14-step engineering dogfood.
|
|
70
|
+
7. Promoting equivalent runtime source to `1.0.0` and npm `latest`.
|
|
71
|
+
8. Installing `1.0.0` into a real project.
|
|
72
|
+
9. Replacing Leo PR #221 with a clean migration.
|
|
73
|
+
|
|
74
|
+
## Non-goals
|
|
75
|
+
|
|
76
|
+
- Do not change Leo product, design-lab, API, dashboard, web, or database code.
|
|
77
|
+
- Do not touch, clean, reset, stash, stage, or commit the dirty Leo main
|
|
78
|
+
worktree.
|
|
79
|
+
- Do not add new workflow features after the final RC is selected.
|
|
80
|
+
- Do not manually copy plugin, agent, command, or skill files into a consumer.
|
|
81
|
+
- Do not treat `scripts/dogfood-local.mjs` or a fake harness as formal
|
|
82
|
+
external dogfood.
|
|
83
|
+
- Do not publish `1.0.0` merely because unit and package tests pass.
|
|
84
|
+
|
|
85
|
+
## Definition of done
|
|
86
|
+
|
|
87
|
+
The release is complete only when all rows are proven with linked evidence.
|
|
88
|
+
|
|
89
|
+
- [ ] `npm view opencode-ship dist-tags --json` returns `latest: 1.0.0`.
|
|
90
|
+
- [ ] `pnpm dlx opencode-ship@latest --version` prints `1.0.0`.
|
|
91
|
+
- [ ] The accepted final RC has a green qualification workflow and public
|
|
92
|
+
qualification artifact.
|
|
93
|
+
- [ ] npm and pnpm install both `core` and `engineering` from the registry.
|
|
94
|
+
- [ ] A real OpenCode CLI starts in the neutral consumer and discovers the
|
|
95
|
+
exact expected tool IDs.
|
|
96
|
+
- [ ] `diff` is read-only and same-version init/update are idempotent.
|
|
97
|
+
- [ ] A changed managed file causes a conflict without being overwritten.
|
|
98
|
+
- [ ] `0.9.0` upgrades to the final candidate or stable package.
|
|
99
|
+
- [ ] `engineering -> core -> engineering` transitions converge safely.
|
|
100
|
+
- [ ] Uninstall restores pre-install root config bytes.
|
|
101
|
+
- [ ] The real 14-step dogfood completes with real GitHub and real models.
|
|
102
|
+
- [ ] Final Standards review, Spec review, verifier, CI, PR, and Ready records
|
|
103
|
+
bind to one HEAD.
|
|
104
|
+
- [ ] Merge happens only after a separate explicit user request.
|
|
105
|
+
- [ ] Cleanup succeeds and leaves no unpublished worktree commits.
|
|
106
|
+
- [ ] `1.0.0` has the same version-independent runtime-source digest as the
|
|
107
|
+
accepted `0.10.0`.
|
|
108
|
+
- [ ] A user-selected real project installs the `engineering` profile and
|
|
109
|
+
`doctor --json` exits 0.
|
|
110
|
+
- [ ] Leo PR #221 is superseded by a fresh registry-generated migration.
|
|
111
|
+
- [ ] README, CHANGELOG, RELEASING, GitHub release metadata, and npm dist-tags
|
|
112
|
+
all describe the same shipped state.
|
|
113
|
+
|
|
114
|
+
## Safety and authorization gates
|
|
115
|
+
|
|
116
|
+
The executing agent may autonomously inspect, edit, test, commit, push a normal
|
|
117
|
+
feature branch, and open a draft PR when the user starts a Build session with
|
|
118
|
+
this plan.
|
|
119
|
+
|
|
120
|
+
The agent must stop and request explicit user authorization before each of the
|
|
121
|
+
following operations:
|
|
122
|
+
|
|
123
|
+
| Gate | Irreversible or externally visible operation |
|
|
124
|
+
|---|---|
|
|
125
|
+
| A | Create `Viktorxyz/opencode-ship-dogfood`; the user chooses visibility, default private |
|
|
126
|
+
| B | Merge a qualification or release PR |
|
|
127
|
+
| C | Push any release tag |
|
|
128
|
+
| D | Trigger an npm stable/candidate publication if tag push is not itself the trigger |
|
|
129
|
+
| E | Merge the real dogfood PR |
|
|
130
|
+
| F | Run `npm dist-tag add ... latest` |
|
|
131
|
+
| G | Merge the Leo migration PR |
|
|
132
|
+
|
|
133
|
+
Never use force-push, hard reset, stash, tag mutation, version overwrite,
|
|
134
|
+
manual npm publish, raw Git ref plumbing, or a bypass around a failed gate.
|
|
135
|
+
|
|
136
|
+
## Evidence ledger
|
|
137
|
+
|
|
138
|
+
The parent issue is the authoritative progress tracker. Add one bounded comment
|
|
139
|
+
after every slice using this schema:
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
Status: pass | fail | blocked | partial
|
|
143
|
+
Phase: S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | S8
|
|
144
|
+
Source SHA: <40 hex>
|
|
145
|
+
Package version: <semver or n/a>
|
|
146
|
+
Summary:
|
|
147
|
+
- <at most three bullets>
|
|
148
|
+
Evidence:
|
|
149
|
+
- issue/PR/workflow/release URLs
|
|
150
|
+
- npm integrity and tarball SHA-256 when applicable
|
|
151
|
+
- artifact URL when applicable
|
|
152
|
+
Verification:
|
|
153
|
+
- <command> -> exit <code>
|
|
154
|
+
Risks:
|
|
155
|
+
- <unresolved concern or none>
|
|
156
|
+
Next authorization: <exact gate or none>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Never place tokens, credentials, raw model prompts containing secrets, or
|
|
160
|
+
unsanitized environment dumps in the issue or repository.
|
|
161
|
+
|
|
162
|
+
## Work breakdown
|
|
163
|
+
|
|
164
|
+
| Slice | Deliverable | Type | Blocked by |
|
|
165
|
+
|---|---|---|---|
|
|
166
|
+
| S0 | Durable plan and parent tracker | AFK | None |
|
|
167
|
+
| S1 | Release qualification gaps closed | AFK | S0 |
|
|
168
|
+
| S2 | Final RC published and qualified | HITL release | S1 |
|
|
169
|
+
| S3 | Neutral registry acceptance green | HITL repo creation | S2 |
|
|
170
|
+
| S4 | Stable `0.10.0` published | HITL release | S3 |
|
|
171
|
+
| S5 | Real 14-step dogfood green | HITL merge | S4 |
|
|
172
|
+
| S6 | Equivalent `1.0.0` promoted to `latest` | HITL release | S5 |
|
|
173
|
+
| S7 | Real-project and Leo consumer migrations | HITL merge | S6 |
|
|
174
|
+
| S8 | Evidence closure and truthful docs | AFK | S7 |
|
|
175
|
+
|
|
176
|
+
## S0: Persist the plan and tracker
|
|
177
|
+
|
|
178
|
+
### Goal
|
|
179
|
+
|
|
180
|
+
Create durable coordination surfaces before changing release code.
|
|
181
|
+
|
|
182
|
+
### Steps
|
|
183
|
+
|
|
184
|
+
1. Re-read this plan, `README.md`, `CHANGELOG.md`, `RELEASING.md`,
|
|
185
|
+
`schema/ship-config.schema.json`, `.github/workflows/release.yml`, and
|
|
186
|
+
`tests/package/neutral-consumer.test.mjs`.
|
|
187
|
+
2. Confirm the upstream worktree is clean and record branch, HEAD, remote main,
|
|
188
|
+
npm dist-tags, and the highest published RC.
|
|
189
|
+
3. Create or reuse the parent GitHub issue named at the top of this file.
|
|
190
|
+
4. Add the issue URL to this document's `Tracker` header.
|
|
191
|
+
5. Add the S0 evidence comment to the issue.
|
|
192
|
+
6. Commit this plan before any release or runtime changes.
|
|
193
|
+
|
|
194
|
+
### Verification
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
git diff --check
|
|
198
|
+
git status --short
|
|
199
|
+
npm view opencode-ship dist-tags --json
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Acceptance
|
|
203
|
+
|
|
204
|
+
- [ ] The parent issue exists.
|
|
205
|
+
- [ ] The issue and this file contain identical fixed decisions.
|
|
206
|
+
- [ ] Current branch, SHA, and npm tags are recorded.
|
|
207
|
+
- [ ] No runtime or release workflow code changed in the plan commit.
|
|
208
|
+
|
|
209
|
+
## S1: Close release-qualification gaps
|
|
210
|
+
|
|
211
|
+
### Goal
|
|
212
|
+
|
|
213
|
+
Make the release pipeline prove the claims it records before publishing another
|
|
214
|
+
immutable candidate.
|
|
215
|
+
|
|
216
|
+
### Branching
|
|
217
|
+
|
|
218
|
+
Create a normal feature branch from the current `release/0.10.0` head. Open a
|
|
219
|
+
PR back to `release/0.10.0`. Do not work directly on the release branch.
|
|
220
|
+
|
|
221
|
+
### Required changes
|
|
222
|
+
|
|
223
|
+
#### 1. Real Node compatibility lanes
|
|
224
|
+
|
|
225
|
+
The release workflow currently declares Node `22.6.0`, current 22, and 24 but
|
|
226
|
+
sets up Node `22.14.0` in every lane. Change the compatibility job so each
|
|
227
|
+
matrix row runs the declared runtime. Keep Node 22.14/npm 11.5.2 only in the
|
|
228
|
+
trusted-publishing job where npm OIDC requires it.
|
|
229
|
+
|
|
230
|
+
Acceptance:
|
|
231
|
+
|
|
232
|
+
- [ ] Workflow source proves `matrix.node` drives setup-node.
|
|
233
|
+
- [ ] Tests or static policy checks fail if the matrix is ignored.
|
|
234
|
+
- [ ] Qualification reports the actual Node versions observed.
|
|
235
|
+
|
|
236
|
+
#### 2. Real OpenCode startup and discovery smoke
|
|
237
|
+
|
|
238
|
+
Package-level plugin import tests are insufficient. Add a deterministic test or
|
|
239
|
+
release job that:
|
|
240
|
+
|
|
241
|
+
1. Creates a neutral temporary Git repository.
|
|
242
|
+
2. Installs the packed package into that repository.
|
|
243
|
+
3. Initializes `core` and `engineering` in separate fixtures.
|
|
244
|
+
4. Installs a pinned real OpenCode CLI.
|
|
245
|
+
5. Starts `opencode serve` with an isolated HOME and the fixture as cwd.
|
|
246
|
+
6. Polls `/global/health` until healthy or a bounded timeout expires.
|
|
247
|
+
7. Reads `/experimental/tool/ids`.
|
|
248
|
+
8. Compares the exact set with the canonical expected set from
|
|
249
|
+
`tests/plugin/plugin-load.test.mjs`.
|
|
250
|
+
9. Terminates the server and proves cleanup.
|
|
251
|
+
|
|
252
|
+
Exercise OpenCode `1.15.5` and `1.18.10`. Do not make a model call.
|
|
253
|
+
|
|
254
|
+
Acceptance:
|
|
255
|
+
|
|
256
|
+
- [ ] Both supported OpenCode versions boot successfully.
|
|
257
|
+
- [ ] The engineering fixture exposes exactly the expected 24 custom tools.
|
|
258
|
+
- [ ] Missing or extra tools fail the release.
|
|
259
|
+
- [ ] Timeout and process cleanup are deterministic.
|
|
260
|
+
|
|
261
|
+
#### 3. Correct prerelease metadata
|
|
262
|
+
|
|
263
|
+
GitHub release `0.10.0-rc.19` was marked as a normal release. Configure the
|
|
264
|
+
release step so SemVer prereleases are marked prerelease and stable versions
|
|
265
|
+
are not.
|
|
266
|
+
|
|
267
|
+
Acceptance:
|
|
268
|
+
|
|
269
|
+
- [ ] RC metadata test expects `prerelease=true`.
|
|
270
|
+
- [ ] Stable metadata test expects `prerelease=false`.
|
|
271
|
+
|
|
272
|
+
#### 4. Version-independent runtime-source digest
|
|
273
|
+
|
|
274
|
+
The existing runbook asks `1.0.0` to match the `0.10.0` source SHA, which is
|
|
275
|
+
impossible after a version/docs commit. Replace that rule with a reproducible
|
|
276
|
+
`runtimeSourceSha256`.
|
|
277
|
+
|
|
278
|
+
The canonical digest must include the version-independent inputs that can
|
|
279
|
+
change behavior:
|
|
280
|
+
|
|
281
|
+
```text
|
|
282
|
+
src/**
|
|
283
|
+
assets/**
|
|
284
|
+
schema/**
|
|
285
|
+
vendor/**
|
|
286
|
+
scripts/build.mjs
|
|
287
|
+
scripts/prepack.mjs
|
|
288
|
+
package.json with the top-level `version` field removed before canonicalization
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Sort normalized relative paths, include path separators and bytes in the hash,
|
|
292
|
+
and exclude generated output, lockfile version, README, CHANGELOG, and release
|
|
293
|
+
metadata. The normalized package manifest remains in scope because `bin`,
|
|
294
|
+
`exports`, `files`, `engines`, and peer ranges affect consumer behavior even
|
|
295
|
+
when runtime source bytes do not change. Document the exact algorithm and test
|
|
296
|
+
it with an independent implementation or golden fixture.
|
|
297
|
+
|
|
298
|
+
Add `runtimeSourceSha256` to the qualification report. For `1.0.x`, the release
|
|
299
|
+
policy must fetch the accepted `0.10.0` qualification artifact and refuse the
|
|
300
|
+
release unless the digest matches.
|
|
301
|
+
|
|
302
|
+
Acceptance:
|
|
303
|
+
|
|
304
|
+
- [ ] Digest is deterministic across clean checkouts.
|
|
305
|
+
- [ ] Runtime source modification changes the digest.
|
|
306
|
+
- [ ] Version/docs-only changes do not change the digest.
|
|
307
|
+
- [ ] `1.0.0` promotion fails when the accepted `0.10.0` digest is absent or
|
|
308
|
+
different.
|
|
309
|
+
|
|
310
|
+
#### 5. Truthful documentation and branch names
|
|
311
|
+
|
|
312
|
+
Update README, CHANGELOG, and RELEASING to state the actual current status:
|
|
313
|
+
|
|
314
|
+
- RCs are published under npm `next`.
|
|
315
|
+
- `release/0.10.0` is the live release branch.
|
|
316
|
+
- `release/1.0-completion` does not exist and must not be referenced as live.
|
|
317
|
+
- Stable `0.10.0` and `1.0.0` remain unpublished until their gates pass.
|
|
318
|
+
- Promotion compares `runtimeSourceSha256`, not impossible source-SHA equality.
|
|
319
|
+
- The release job count and matrix must match the actual workflow.
|
|
320
|
+
|
|
321
|
+
#### 6. Stronger neutral-consumer assertions
|
|
322
|
+
|
|
323
|
+
`tests/package/neutral-consumer.test.mjs` must not run `doctor` after an
|
|
324
|
+
unchecked engineering init. Require explicit model IDs, assert init exit 0,
|
|
325
|
+
then assert doctor exit 0 and profile footprint.
|
|
326
|
+
|
|
327
|
+
Add explicit coverage that engineering init without model IDs fails before any
|
|
328
|
+
write.
|
|
329
|
+
|
|
330
|
+
### Required verification
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
npm ci
|
|
334
|
+
npm run verify
|
|
335
|
+
npm pack --json
|
|
336
|
+
git diff --check
|
|
337
|
+
git status --short
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Run an independent code review on the final PR HEAD. Resolve every Critical or
|
|
341
|
+
Important finding before merge.
|
|
342
|
+
|
|
343
|
+
### Acceptance
|
|
344
|
+
|
|
345
|
+
- [ ] Every required change above has an automated regression test.
|
|
346
|
+
- [ ] `npm run verify` exits 0.
|
|
347
|
+
- [ ] Packed artifact smoke exits 0.
|
|
348
|
+
- [ ] Independent review passes on the final HEAD.
|
|
349
|
+
- [ ] The PR is merged into `release/0.10.0` only after Gate B authorization.
|
|
350
|
+
|
|
351
|
+
## S2: Publish and qualify the final RC
|
|
352
|
+
|
|
353
|
+
### Goal
|
|
354
|
+
|
|
355
|
+
Publish one immutable candidate containing the corrected qualification policy.
|
|
356
|
+
|
|
357
|
+
### Version selection
|
|
358
|
+
|
|
359
|
+
Resolve the next unused RC at execution time. The expected version is
|
|
360
|
+
`0.10.0-rc.20`, but if that version exists, choose the next unused integer.
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
npm view opencode-ship versions --json
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Set and record:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
export RC=<next-unused-0.10.0-rc.N>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Steps
|
|
373
|
+
|
|
374
|
+
1. Start from the reviewed and merged `release/0.10.0` head.
|
|
375
|
+
2. Bump `package.json` and `package-lock.json` to `$RC` without creating a tag.
|
|
376
|
+
3. Update release-status docs only where the candidate number must appear.
|
|
377
|
+
4. Run the full local verification.
|
|
378
|
+
5. Commit and push the release version commit.
|
|
379
|
+
6. Confirm the release branch HEAD, package version, lock version, and proposed
|
|
380
|
+
tag all match.
|
|
381
|
+
7. Stop at Gate C and request permission to push the signed release tag.
|
|
382
|
+
8. After authorization, create and push the canonical no-leading-v signed tag.
|
|
383
|
+
9. Monitor all release qualification jobs.
|
|
384
|
+
10. If any job fails, do not reuse the version. Fix source, choose the next RC,
|
|
385
|
+
and repeat S1/S2 verification.
|
|
386
|
+
11. On success, verify npm registry metadata, provenance, integrity,
|
|
387
|
+
post-publish install smoke, GitHub prerelease metadata, SBOM, tarball hash,
|
|
388
|
+
and qualification artifact.
|
|
389
|
+
12. Record all evidence in the parent issue.
|
|
390
|
+
|
|
391
|
+
### Acceptance
|
|
392
|
+
|
|
393
|
+
- [ ] Every qualification job is green on one source tag.
|
|
394
|
+
- [ ] npm `next` points to `$RC`.
|
|
395
|
+
- [ ] Registry integrity equals the qualification report.
|
|
396
|
+
- [ ] GitHub asset digest equals the tarball SHA-256.
|
|
397
|
+
- [ ] GitHub marks the release prerelease.
|
|
398
|
+
- [ ] `runtimeSourceSha256` exists in the qualification artifact.
|
|
399
|
+
- [ ] Registry-sourced `--version` prints `$RC`.
|
|
400
|
+
|
|
401
|
+
## S3: Neutral registry acceptance
|
|
402
|
+
|
|
403
|
+
### Goal
|
|
404
|
+
|
|
405
|
+
Prove that a non-Leo project can safely install and operate the published RC.
|
|
406
|
+
|
|
407
|
+
### Repository fixture
|
|
408
|
+
|
|
409
|
+
The neutral repository is `Viktorxyz/opencode-ship-dogfood`. Re-check whether
|
|
410
|
+
it exists. If absent, stop at Gate A before creating it. Ask the user to choose
|
|
411
|
+
visibility; use private unless the user explicitly requests public evidence.
|
|
412
|
+
|
|
413
|
+
The repository must contain only:
|
|
414
|
+
|
|
415
|
+
- a Git repository on `main` with a GitHub origin;
|
|
416
|
+
- a private, single-dependency Node package;
|
|
417
|
+
- `node --test` as its verification command;
|
|
418
|
+
- a required GitHub status check named `verify`;
|
|
419
|
+
- two ordered implementation tasks used later in S5;
|
|
420
|
+
- an `opencode.json` with `$schema`, `share: "disabled"`, and unrelated sentinel
|
|
421
|
+
fields that must survive install/uninstall;
|
|
422
|
+
- no Leo code, no local opencode-ship link, and no manually copied assets.
|
|
423
|
+
|
|
424
|
+
Use these deterministic dogfood tasks:
|
|
425
|
+
|
|
426
|
+
| Task | User-visible behavior | Test seam |
|
|
427
|
+
|---|---|---|
|
|
428
|
+
| A | Add `formatReleaseLabel(version)` that returns a canonical `v<semver>` label and rejects malformed input | `node:test` unit cases |
|
|
429
|
+
| B | Add a small CLI that prints the canonical label and exits nonzero for malformed input | spawned CLI integration cases |
|
|
430
|
+
|
|
431
|
+
For Task B round 1, the dogfood fixture deliberately omits the nonzero invalid
|
|
432
|
+
input path so the reviewer has one real blocking finding. Round 2 fixes it.
|
|
433
|
+
|
|
434
|
+
### Preflight
|
|
435
|
+
|
|
436
|
+
Record, without secrets:
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
node --version
|
|
440
|
+
git --version
|
|
441
|
+
gh --version
|
|
442
|
+
gh auth status
|
|
443
|
+
opencode --version
|
|
444
|
+
pnpm --version
|
|
445
|
+
npm --version
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Requirements:
|
|
449
|
+
|
|
450
|
+
- Node `>=22.6.0`;
|
|
451
|
+
- OpenCode `>=1.15.5`;
|
|
452
|
+
- authenticated GitHub CLI with repository access;
|
|
453
|
+
- npm and pnpm able to reach the public registry.
|
|
454
|
+
|
|
455
|
+
### Command wrapper
|
|
456
|
+
|
|
457
|
+
Use the exact accepted RC version, never `latest` or an unpinned local path:
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
ship() {
|
|
461
|
+
pnpm dlx --package="opencode-ship@$RC" opencode-ship "$@"
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Acceptance matrix
|
|
466
|
+
|
|
467
|
+
#### A1: Root conflict refusal
|
|
468
|
+
|
|
469
|
+
Create a disposable conflicting Build permission in a fixture clone. Run init.
|
|
470
|
+
|
|
471
|
+
Expected: exit 3, conflict reported, existing bytes unchanged, no partial lock
|
|
472
|
+
or managed files.
|
|
473
|
+
|
|
474
|
+
#### A2: Read-only preview
|
|
475
|
+
|
|
476
|
+
Digest the fixture tree, run:
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
ship diff \
|
|
480
|
+
--profile engineering \
|
|
481
|
+
--planner-model openai/gpt-5.6-sol \
|
|
482
|
+
--builder-model minimax/MiniMax-M3 \
|
|
483
|
+
--final-reviewer-model openai/gpt-5.6-sol \
|
|
484
|
+
--json
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Expected: exit 1 with `status: changed`; tree digest is unchanged.
|
|
488
|
+
|
|
489
|
+
#### A3: Clean engineering initialization
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
ship init \
|
|
493
|
+
--profile engineering \
|
|
494
|
+
--planner-model openai/gpt-5.6-sol \
|
|
495
|
+
--builder-model minimax/MiniMax-M3 \
|
|
496
|
+
--final-reviewer-model openai/gpt-5.6-sol \
|
|
497
|
+
--force-config \
|
|
498
|
+
--strict-doctor \
|
|
499
|
+
--json
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
Use `--force-root-config` only in a fixture that has no root OpenCode config.
|
|
503
|
+
|
|
504
|
+
Expected: exit 0; engineering lock/profile, model configuration, managed files,
|
|
505
|
+
root pointers, hashes, and sentinel fields are correct.
|
|
506
|
+
|
|
507
|
+
#### A4: Doctor
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
ship doctor --profile engineering --json
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
Expected: exit 0 and every required row `noop`.
|
|
514
|
+
|
|
515
|
+
#### A5: Real OpenCode discovery
|
|
516
|
+
|
|
517
|
+
Quit any existing OpenCode session because project config is not hot-reloaded.
|
|
518
|
+
Start a fresh real OpenCode server with the neutral repo as cwd and an isolated
|
|
519
|
+
HOME. Poll `/global/health`; read `/experimental/tool/ids`; assert the exact
|
|
520
|
+
expected custom tool set from the accepted package.
|
|
521
|
+
|
|
522
|
+
Expected: health 200, no config error, exact tool IDs, clean server shutdown.
|
|
523
|
+
|
|
524
|
+
#### A6: Idempotency
|
|
525
|
+
|
|
526
|
+
Run `diff`, `init`, and `update` again at the same version.
|
|
527
|
+
|
|
528
|
+
Expected: diff exit 0/noop; init/update exit 0; no creates, updates, deletes, or
|
|
529
|
+
conflicts; managed bytes and sentinel root config are unchanged.
|
|
530
|
+
|
|
531
|
+
#### A7: Managed-file conflict
|
|
532
|
+
|
|
533
|
+
Modify one managed agent in a disposable fixture and run update.
|
|
534
|
+
|
|
535
|
+
Expected: exit 3; edited bytes and prior lock remain unchanged. Restore the
|
|
536
|
+
file; update then exits 0.
|
|
537
|
+
|
|
538
|
+
#### A8: Upgrade from stable 0.9.0
|
|
539
|
+
|
|
540
|
+
Initialize a fresh fixture using exactly `0.9.0`, then run accepted RC diff,
|
|
541
|
+
update, doctor, and OpenCode discovery.
|
|
542
|
+
|
|
543
|
+
Expected: update exit 0, lock manager version changes to `$RC`, final diff is
|
|
544
|
+
noop, doctor and discovery pass.
|
|
545
|
+
|
|
546
|
+
#### A9: Profile transitions
|
|
547
|
+
|
|
548
|
+
Run `core -> engineering -> core -> engineering`, checking managed files,
|
|
549
|
+
Plan Mode root pointers, lock scope, and root sentinel bytes after every step.
|
|
550
|
+
|
|
551
|
+
Expected: profile-specific files/pointers are added and removed exactly once,
|
|
552
|
+
with no residue or user-field loss.
|
|
553
|
+
|
|
554
|
+
#### A10: Uninstall and restoration
|
|
555
|
+
|
|
556
|
+
```bash
|
|
557
|
+
ship uninstall --purge-config --json
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
Expected: exit 0; managed files, lock, and generated ship config are absent;
|
|
561
|
+
user files remain; original root OpenCode config bytes are restored.
|
|
562
|
+
|
|
563
|
+
#### A11: npm invoker
|
|
564
|
+
|
|
565
|
+
Repeat clean core and engineering installs with:
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
npm exec --yes --package="opencode-ship@$RC" -- opencode-ship <command>
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Expected: results match pnpm invocation.
|
|
572
|
+
|
|
573
|
+
### Acceptance
|
|
574
|
+
|
|
575
|
+
- [ ] A1-A11 pass from registry bytes.
|
|
576
|
+
- [ ] No test uses source checkout, local link, or local tarball.
|
|
577
|
+
- [ ] Sanitized evidence is linked in the parent issue.
|
|
578
|
+
- [ ] No consumer-owned root field changed unexpectedly.
|
|
579
|
+
|
|
580
|
+
## S4: Publish stable 0.10.0
|
|
581
|
+
|
|
582
|
+
### Goal
|
|
583
|
+
|
|
584
|
+
Publish the accepted candidate runtime as stable `0.10.0` under npm `next`.
|
|
585
|
+
|
|
586
|
+
### Invariants
|
|
587
|
+
|
|
588
|
+
- No runtime-source file may change after S3 acceptance.
|
|
589
|
+
- The candidate and stable `runtimeSourceSha256` must match.
|
|
590
|
+
- Only package/lock version, release docs, and release metadata may differ.
|
|
591
|
+
|
|
592
|
+
### Steps
|
|
593
|
+
|
|
594
|
+
1. Start from the accepted RC source.
|
|
595
|
+
2. Bump `package.json` and `package-lock.json` to `0.10.0` without tagging.
|
|
596
|
+
3. Update README, CHANGELOG, and RELEASING status truthfully.
|
|
597
|
+
4. Run full local verification and compute the runtime-source digest.
|
|
598
|
+
5. Open the stable release PR into `main` so the eventual tag is reachable
|
|
599
|
+
from `origin/main`.
|
|
600
|
+
6. Require independent review and all branch CI.
|
|
601
|
+
7. Stop at Gate B before merging.
|
|
602
|
+
8. After explicit authorization, merge and fetch the final main SHA.
|
|
603
|
+
9. Verify final main has version `0.10.0`, expected digest, and a clean tree.
|
|
604
|
+
10. Stop at Gate C before creating/pushing signed tag `0.10.0`.
|
|
605
|
+
11. After authorization, push the tag and monitor the full release workflow.
|
|
606
|
+
12. Verify npm `next`, registry integrity, provenance, GitHub assets, SBOM,
|
|
607
|
+
qualification report, and post-publish registry smoke.
|
|
608
|
+
13. Record the accepted `0.10.0` runtime-source digest and artifact URLs.
|
|
609
|
+
|
|
610
|
+
### Acceptance
|
|
611
|
+
|
|
612
|
+
- [ ] `opencode-ship@0.10.0` is publicly installable.
|
|
613
|
+
- [ ] npm `next` points to `0.10.0`.
|
|
614
|
+
- [ ] Stable release metadata is not marked prerelease.
|
|
615
|
+
- [ ] Stable digest equals accepted RC digest.
|
|
616
|
+
- [ ] Full qualification report is public and green.
|
|
617
|
+
|
|
618
|
+
## S5: Formal real 14-step dogfood
|
|
619
|
+
|
|
620
|
+
### Goal
|
|
621
|
+
|
|
622
|
+
Prove the complete engineering lifecycle using the npm-published stable
|
|
623
|
+
`0.10.0`, real OpenCode, real GitHub, and the approved real model roles.
|
|
624
|
+
|
|
625
|
+
### Hard rule
|
|
626
|
+
|
|
627
|
+
Do not substitute `scripts/dogfood-local.mjs`, fake GitHub, fake models, direct
|
|
628
|
+
source imports, or a packed local tarball. Those remain useful pre-release
|
|
629
|
+
tests but do not satisfy S5.
|
|
630
|
+
|
|
631
|
+
### Environment
|
|
632
|
+
|
|
633
|
+
```bash
|
|
634
|
+
export DOGFOOD_REPO=/path/to/opencode-ship-dogfood
|
|
635
|
+
export DOGFOOD_PLANNER=openai/gpt-5.6-sol
|
|
636
|
+
export DOGFOOD_BUILDER=minimax/MiniMax-M3
|
|
637
|
+
export DOGFOOD_REVIEWER=openai/gpt-5.6-sol
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Required sequence
|
|
641
|
+
|
|
642
|
+
1. Install `opencode-ship@0.10.0` core from npm.
|
|
643
|
+
2. Upgrade the same repo to engineering with explicit model IDs.
|
|
644
|
+
3. Restart OpenCode and have the real strong planner generate PlanV2 for dogfood
|
|
645
|
+
Tasks A and B.
|
|
646
|
+
4. Obtain explicit user approval and persist the immutable plan seal.
|
|
647
|
+
5. Use typed tools to create the issue, worktree, branch, and draft PR linked
|
|
648
|
+
with `Closes #N`.
|
|
649
|
+
6. Dispatch Task A to the MiniMax builder; run Spec and Quality task reviews;
|
|
650
|
+
require pass; commit only the reviewed range.
|
|
651
|
+
7. Trigger native OpenCode compaction after Task A and capture the bounded
|
|
652
|
+
opencode-ship resume context.
|
|
653
|
+
8. Dispatch Task B round 1 with the controlled missing invalid-input path;
|
|
654
|
+
require the reviewer to return one blocking finding.
|
|
655
|
+
9. Trigger native compaction during Task B fix work.
|
|
656
|
+
10. Invoke `ship_resume`; verify the durable ledger resumes the same task,
|
|
657
|
+
revision, review round, and immutable plan hash.
|
|
658
|
+
11. Test issue-mirror restoration. Before removing local plan storage, stop for
|
|
659
|
+
explicit user confirmation and record the exact disposable path. Remove
|
|
660
|
+
only the dogfood plan copy, then invoke `ship_resume` and prove restoration
|
|
661
|
+
from the verified issue mirror.
|
|
662
|
+
12. Finish Task B round 2. On one final HEAD, run parallel strong Standards and
|
|
663
|
+
Spec reviews, independent verification, and required `verify` CI. Every
|
|
664
|
+
record must reference the same HEAD and package integrity.
|
|
665
|
+
13. Invoke `delivery_ready`. Capture evidence that the PR is Ready and unmerged.
|
|
666
|
+
14. Stop at Gate E. Only after the user says `merge it`, invoke the typed merge,
|
|
667
|
+
re-check fresh same-HEAD gates, squash merge, clean the worktree, downgrade
|
|
668
|
+
to core, then uninstall and prove root-config restoration.
|
|
669
|
+
|
|
670
|
+
### Required evidence
|
|
671
|
+
|
|
672
|
+
- Issue and PR URLs.
|
|
673
|
+
- npm package version and integrity.
|
|
674
|
+
- Plan revision, plan hash, approval record, and mirror URL.
|
|
675
|
+
- Sanitized model-routing records for planner, builder, and final reviewers.
|
|
676
|
+
- Task A and Task B review records.
|
|
677
|
+
- Compaction snapshots and resume counters.
|
|
678
|
+
- Mirror restoration evidence.
|
|
679
|
+
- Final Standards, Spec, verifier, CI, PR, and Ready common HEAD.
|
|
680
|
+
- Ready-before-merge snapshot.
|
|
681
|
+
- Explicit merge authorization and merge SHA.
|
|
682
|
+
- Cleanup result.
|
|
683
|
+
- Core downgrade and uninstall byte-restoration hashes.
|
|
684
|
+
|
|
685
|
+
### Acceptance
|
|
686
|
+
|
|
687
|
+
- [ ] All 14 steps complete in order.
|
|
688
|
+
- [ ] No step is simulated.
|
|
689
|
+
- [ ] No context loss changes task identity, plan hash, or review round.
|
|
690
|
+
- [ ] Ready does not merge.
|
|
691
|
+
- [ ] Merge requires a separate explicit user request.
|
|
692
|
+
- [ ] Cleanup and uninstall restoration pass.
|
|
693
|
+
|
|
694
|
+
If any runtime defect appears, stop. Publish a new immutable 0.10 patch/RC as
|
|
695
|
+
appropriate and repeat the complete dogfood. Never promote known-defective
|
|
696
|
+
runtime bytes.
|
|
697
|
+
|
|
698
|
+
## S6: Promote equivalent 1.0.0 to npm latest
|
|
699
|
+
|
|
700
|
+
### Goal
|
|
701
|
+
|
|
702
|
+
Publish `1.0.0` with the exact dogfooded runtime-source inputs and make it the
|
|
703
|
+
default npm install.
|
|
704
|
+
|
|
705
|
+
### Allowed changes from accepted 0.10.0
|
|
706
|
+
|
|
707
|
+
- `package.json` version;
|
|
708
|
+
- `package-lock.json` version;
|
|
709
|
+
- README release status;
|
|
710
|
+
- CHANGELOG release status;
|
|
711
|
+
- RELEASING release status;
|
|
712
|
+
- release metadata that does not affect runtime behavior.
|
|
713
|
+
|
|
714
|
+
No other change is allowed. Any runtime-source change invalidates S5 and
|
|
715
|
+
requires another 0.10 dogfood.
|
|
716
|
+
|
|
717
|
+
### Steps
|
|
718
|
+
|
|
719
|
+
1. Create a release branch from the accepted `0.10.0` tag/main state.
|
|
720
|
+
2. Bump package and lock to `1.0.0` without tagging.
|
|
721
|
+
3. Update release documentation.
|
|
722
|
+
4. Run full verification and compute `runtimeSourceSha256`.
|
|
723
|
+
5. Compare it to the accepted `0.10.0` qualification artifact.
|
|
724
|
+
6. Open the 1.0 promotion PR into `main`.
|
|
725
|
+
7. Require independent review and CI.
|
|
726
|
+
8. Stop at Gate B before merge.
|
|
727
|
+
9. After authorization, merge and verify final main state.
|
|
728
|
+
10. Stop at Gate C before creating/pushing signed tag `1.0.0`.
|
|
729
|
+
11. After authorization, push the tag and monitor all qualification jobs.
|
|
730
|
+
12. Verify publication under npm `candidate`, provenance, integrity, assets,
|
|
731
|
+
qualification artifact, and registry smoke.
|
|
732
|
+
13. Re-run clean neutral `init`, `doctor`, real OpenCode discovery, and
|
|
733
|
+
uninstall using exactly `opencode-ship@1.0.0`.
|
|
734
|
+
14. Stop at Gate F before changing npm `latest`.
|
|
735
|
+
15. After authorization, run:
|
|
736
|
+
|
|
737
|
+
```bash
|
|
738
|
+
npm dist-tag add opencode-ship@1.0.0 latest
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
16. Verify registry propagation from a clean npm cache and fresh directory.
|
|
742
|
+
|
|
743
|
+
### Acceptance
|
|
744
|
+
|
|
745
|
+
- [ ] `1.0.0` qualification is green.
|
|
746
|
+
- [ ] `1.0.0` runtime-source digest equals accepted `0.10.0`.
|
|
747
|
+
- [ ] npm provenance and GitHub asset digest match.
|
|
748
|
+
- [ ] Clean registry install and real OpenCode discovery pass.
|
|
749
|
+
- [ ] `npm view opencode-ship dist-tags --json` reports `latest: 1.0.0`.
|
|
750
|
+
- [ ] `pnpm dlx opencode-ship@latest --version` prints `1.0.0`.
|
|
751
|
+
|
|
752
|
+
## S7: Install real consumers
|
|
753
|
+
|
|
754
|
+
### Part A: User-selected project
|
|
755
|
+
|
|
756
|
+
Ask the user for the target repository path. Inspect its package manager,
|
|
757
|
+
OpenCode config, Git remote, default branch, verification command, required CI,
|
|
758
|
+
and existing agents/skills before writing.
|
|
759
|
+
|
|
760
|
+
Run read-only preview first:
|
|
761
|
+
|
|
762
|
+
```bash
|
|
763
|
+
pnpm dlx opencode-ship@latest diff \
|
|
764
|
+
--profile engineering \
|
|
765
|
+
--planner-model openai/gpt-5.6-sol \
|
|
766
|
+
--builder-model minimax/MiniMax-M3 \
|
|
767
|
+
--final-reviewer-model openai/gpt-5.6-sol \
|
|
768
|
+
--json
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
Resolve every ownership conflict explicitly. Then initialize:
|
|
772
|
+
|
|
773
|
+
```bash
|
|
774
|
+
pnpm dlx opencode-ship@latest init \
|
|
775
|
+
--profile engineering \
|
|
776
|
+
--planner-model openai/gpt-5.6-sol \
|
|
777
|
+
--builder-model minimax/MiniMax-M3 \
|
|
778
|
+
--final-reviewer-model openai/gpt-5.6-sol \
|
|
779
|
+
--force-config \
|
|
780
|
+
--strict-doctor \
|
|
781
|
+
--json
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
Use `--force-root-config` only when the target has no root OpenCode config and
|
|
785
|
+
the user approves creating one.
|
|
786
|
+
|
|
787
|
+
Quit and restart OpenCode after installation. Verify doctor, startup, exact
|
|
788
|
+
tool/agent/skill discovery, unchanged user-owned root fields, and one small
|
|
789
|
+
real issue through worktree, PR, review, verification, CI, and Ready. Stop
|
|
790
|
+
before merge unless the user explicitly authorizes it.
|
|
791
|
+
|
|
792
|
+
### Part B: Leo replacement migration
|
|
793
|
+
|
|
794
|
+
The Leo main worktree contains unrelated design-lab changes. Never touch it.
|
|
795
|
+
|
|
796
|
+
1. Use a fresh isolated worktree from current remote `main`.
|
|
797
|
+
2. Create a new Leo GitHub issue for the `opencode-ship@1.0.0` registry
|
|
798
|
+
migration.
|
|
799
|
+
3. Run registry `diff` and `init --profile engineering` with the approved model
|
|
800
|
+
IDs.
|
|
801
|
+
4. Never copy managed artifacts manually.
|
|
802
|
+
5. Reconcile `opencode.json`, `.opencode/ship.config.json`, generated lock,
|
|
803
|
+
package lock, agent task permissions, AGENTS skill names, workflow docs,
|
|
804
|
+
plugin contract tests, and `docs/delivery/STATUS.md`.
|
|
805
|
+
6. Ensure every installed companion file is present and every managed hash is
|
|
806
|
+
final, not `pending`.
|
|
807
|
+
7. Remove legacy `opencode-delivery` paths only through supported migration
|
|
808
|
+
behavior or explicit reviewed cleanup.
|
|
809
|
+
8. Run frozen install from a clean checkout/worktree.
|
|
810
|
+
9. Run `pnpm verify:workspace`.
|
|
811
|
+
10. Run canonical delivery reviewer, verifier, and required CI on one final
|
|
812
|
+
HEAD.
|
|
813
|
+
11. Open the replacement PR and mark Ready only when gates are fresh.
|
|
814
|
+
12. Close Leo PR #221 as superseded only after the replacement URL exists.
|
|
815
|
+
13. Stop at Gate G before merging the replacement.
|
|
816
|
+
|
|
817
|
+
### Acceptance
|
|
818
|
+
|
|
819
|
+
- [ ] User-selected project works with `opencode-ship@latest` engineering.
|
|
820
|
+
- [ ] Leo replacement uses registry-generated `1.0.0` artifacts.
|
|
821
|
+
- [ ] Leo frozen install and workspace verification pass.
|
|
822
|
+
- [ ] PR #221 links to the replacement before closure.
|
|
823
|
+
- [ ] Unrelated design-lab files remain untouched.
|
|
824
|
+
|
|
825
|
+
## S8: Close release evidence
|
|
826
|
+
|
|
827
|
+
### Steps
|
|
828
|
+
|
|
829
|
+
1. Update README install commands to use `opencode-ship@latest` and document
|
|
830
|
+
the engineering model flags/config requirement.
|
|
831
|
+
2. Move final CHANGELOG items from Unreleased to `1.0.0` with the release date.
|
|
832
|
+
3. Update RELEASING current-state tables and preserve the historical process.
|
|
833
|
+
4. Verify GitHub releases, npm tags, registry metadata, and docs agree.
|
|
834
|
+
5. Add a final bounded evidence comment to the parent issue.
|
|
835
|
+
6. Close the parent issue only after every Definition of Done checkbox is
|
|
836
|
+
linked to evidence.
|
|
837
|
+
7. Record any non-blocking follow-up as a new issue, not an unchecked note in
|
|
838
|
+
this plan.
|
|
839
|
+
|
|
840
|
+
## Failure and recovery rules
|
|
841
|
+
|
|
842
|
+
| Failure | Required response |
|
|
843
|
+
|---|---|
|
|
844
|
+
| Local verification fails | Diagnose and fix before any tag |
|
|
845
|
+
| Release qualification fails before publish | Fix and rerun same unconsumed tag only if no immutable publication occurred; otherwise choose a new RC |
|
|
846
|
+
| npm version exists with different integrity | Stop immediately; never overwrite |
|
|
847
|
+
| Registry propagation times out | Preserve workflow evidence, retry read-only checks, do not republish |
|
|
848
|
+
| Neutral install conflict | Preserve consumer bytes and resolve ownership explicitly |
|
|
849
|
+
| Managed hash conflict | Do not use `--replace-managed` without user review and approval |
|
|
850
|
+
| Real dogfood runtime defect | Publish a corrected 0.10 patch/candidate and repeat S3-S5 |
|
|
851
|
+
| Plan/mirror restoration fails | Stop promotion; preserve issue and local evidence |
|
|
852
|
+
| Same-HEAD gate mismatch | Re-run every invalidated final gate on the new HEAD |
|
|
853
|
+
| Cleanup refuses | Preserve worktree and manifest; diagnose without deleting it manually |
|
|
854
|
+
| 1.0 digest mismatch | Refuse promotion and identify the unauthorized runtime-source change |
|
|
855
|
+
|
|
856
|
+
## Per-session operating contract
|
|
857
|
+
|
|
858
|
+
At the start of every continuation session:
|
|
859
|
+
|
|
860
|
+
1. Read this file and the parent issue.
|
|
861
|
+
2. Inspect current git branch, HEAD, worktree status, npm dist-tags, open PRs,
|
|
862
|
+
release workflows, and the last evidence comment.
|
|
863
|
+
3. Identify exactly one active slice and mark it in progress in the issue.
|
|
864
|
+
4. Do not repeat completed irreversible operations.
|
|
865
|
+
5. Use current registry/remote facts over stale values in this document.
|
|
866
|
+
6. Execute until the next approval gate, a real blocker after one retry, or the
|
|
867
|
+
slice acceptance criteria are complete.
|
|
868
|
+
7. Post the evidence envelope before ending the session.
|
|
869
|
+
|
|
870
|
+
## Suggested skills
|
|
871
|
+
|
|
872
|
+
Load strong matches before acting:
|
|
873
|
+
|
|
874
|
+
- `customize-opencode` for config, plugin, agent, permission, and skill work;
|
|
875
|
+
- `verification-before-completion` before every success claim;
|
|
876
|
+
- `requesting-code-review` before release/release-policy merges;
|
|
877
|
+
- `systematic-debugging` for qualification or registry failures;
|
|
878
|
+
- `test-driven-development` for release-policy and installer regressions;
|
|
879
|
+
- `delivery-workflow` where the consumer has the typed delivery lifecycle;
|
|
880
|
+
- `git-commit` only when the user asks the session to commit;
|
|
881
|
+
- `handoff` when stopping at an approval gate.
|
|
882
|
+
|
|
883
|
+
## Final report format
|
|
884
|
+
|
|
885
|
+
The executor's final response must include:
|
|
886
|
+
|
|
887
|
+
```text
|
|
888
|
+
Status: pass | fail | blocked | partial
|
|
889
|
+
Completed through: S<N>
|
|
890
|
+
Source SHA: <40 hex>
|
|
891
|
+
Published versions: <list>
|
|
892
|
+
npm dist-tags: <json>
|
|
893
|
+
Evidence URLs: <issue, PRs, workflows, releases, qualification artifacts>
|
|
894
|
+
Verification: <commands and exit codes>
|
|
895
|
+
Definition of Done: <passed>/<total>
|
|
896
|
+
Remaining risks: <list or none>
|
|
897
|
+
Next authorization: <exact gate or none>
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
Do not report `complete` until every Definition of Done checkbox is proven.
|
|
901
|
+
|
|
902
|
+
## Copyable handoff prompt
|
|
903
|
+
|
|
904
|
+
Use this prompt to start or resume execution with a lower-cost Build model:
|
|
905
|
+
|
|
906
|
+
```text
|
|
907
|
+
Work in /home/viktorcordas/repos/opencode-ship.
|
|
908
|
+
|
|
909
|
+
Goal: Execute the approved opencode-ship 1.0 release plan through the next
|
|
910
|
+
authorization gate, preserving evidence so another session can resume without
|
|
911
|
+
reconstructing context.
|
|
912
|
+
|
|
913
|
+
Mandatory source of truth:
|
|
914
|
+
docs/release/1.0.0-execution-plan.md
|
|
915
|
+
|
|
916
|
+
Before acting:
|
|
917
|
+
1. Load every strong matching skill, especially customize-opencode,
|
|
918
|
+
verification-before-completion, requesting-code-review, and systematic
|
|
919
|
+
debugging or test-driven-development when applicable.
|
|
920
|
+
2. Read the entire plan, README.md, CHANGELOG.md, RELEASING.md,
|
|
921
|
+
schema/ship-config.schema.json, .github/workflows/release.yml,
|
|
922
|
+
src/installer/cli-args.js, and tests/package/neutral-consumer.test.mjs.
|
|
923
|
+
3. Inspect git status/branch/HEAD, npm dist-tags, published versions, open
|
|
924
|
+
issues/PRs, release workflows, and the parent issue's latest evidence.
|
|
925
|
+
4. If the parent issue does not exist, create or reuse the exact issue required
|
|
926
|
+
by S0 and add its URL to the plan header before release-code changes.
|
|
927
|
+
5. Identify exactly one active slice. Do not skip ahead or repeat an already
|
|
928
|
+
completed irreversible operation.
|
|
929
|
+
|
|
930
|
+
Fixed decisions:
|
|
931
|
+
- Final target: opencode-ship@1.0.0 on npm latest.
|
|
932
|
+
- Profile: engineering.
|
|
933
|
+
- Planner: openai/gpt-5.6-sol.
|
|
934
|
+
- Builder: minimax/MiniMax-M3.
|
|
935
|
+
- Final reviewer: openai/gpt-5.6-sol.
|
|
936
|
+
- Registry-sourced real dogfood is mandatory.
|
|
937
|
+
|
|
938
|
+
Constraints:
|
|
939
|
+
- Never edit, reset, stash, clean, stage, or commit the dirty Leo main
|
|
940
|
+
worktree. Leo is out of scope until S7 and must use a fresh worktree.
|
|
941
|
+
- Never copy managed plugin/agent/skill files manually into a consumer.
|
|
942
|
+
- Never overwrite, retag, unpublish, or mutate a published npm version.
|
|
943
|
+
- Never use force-push, hard reset, raw Git ref plumbing, or bypass a failed
|
|
944
|
+
review/verification/CI gate.
|
|
945
|
+
- Never expose tokens or credentials in logs, issues, commits, or artifacts.
|
|
946
|
+
- Stop for every authorization gate in the plan. In particular, do not create
|
|
947
|
+
the dogfood repository, merge a PR, push a release tag, publish/move an npm
|
|
948
|
+
dist-tag, or merge dogfood/Leo without explicit user authorization.
|
|
949
|
+
- Restart OpenCode after project config, plugin, agent, command, or skill files
|
|
950
|
+
change.
|
|
951
|
+
- Do not claim completion from unit tests alone.
|
|
952
|
+
|
|
953
|
+
Execution behavior:
|
|
954
|
+
- Work autonomously through reversible steps in the active slice.
|
|
955
|
+
- Verify every change using the commands required by that slice.
|
|
956
|
+
- Run independent review before release-policy or release merges.
|
|
957
|
+
- Update the parent issue with the bounded evidence envelope before stopping.
|
|
958
|
+
- Stop only at an authorization gate, a real blocker after one retry, or when
|
|
959
|
+
the active slice acceptance criteria are complete.
|
|
960
|
+
|
|
961
|
+
Return exactly:
|
|
962
|
+
Status: pass | fail | blocked | partial
|
|
963
|
+
Completed through: S<N>
|
|
964
|
+
Source SHA: <40 hex>
|
|
965
|
+
Published versions: <list>
|
|
966
|
+
npm dist-tags: <json>
|
|
967
|
+
Evidence URLs: <issue, PRs, workflows, releases, qualification artifacts>
|
|
968
|
+
Verification: <commands and exit codes>
|
|
969
|
+
Definition of Done: <passed>/<total>
|
|
970
|
+
Remaining risks: <list or none>
|
|
971
|
+
Next authorization: <exact gate or none>
|
|
972
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-ship",
|
|
3
|
-
"version": "0.10.0-rc.
|
|
3
|
+
"version": "0.10.0-rc.28",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "npm-distributed OpenCode installer that materializes the delivery plugin, reviewer/verifier agents, skills, ship config and lock into any consumer repository.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"docs",
|
|
50
50
|
"vendor",
|
|
51
51
|
"tests/plugin/plugin-load.test.mjs",
|
|
52
|
+
"tests/plugin/expected-tools.mjs",
|
|
52
53
|
"THIRD_PARTY_NOTICES.md",
|
|
53
54
|
"README.md",
|
|
54
55
|
"CHANGELOG.md",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Canonical opencode-ship tool set.
|
|
3
|
+
*
|
|
4
|
+
* The bundled plugin registers exactly 24 typed tools (9
|
|
5
|
+
* existing delivery + 7 control-plane + 8 workflow). This module
|
|
6
|
+
* is the single source of truth for the expected set so:
|
|
7
|
+
*
|
|
8
|
+
* - the in-process plugin-load test asserts the contract at the
|
|
9
|
+
* plugin boundary;
|
|
10
|
+
* - the opencode-discovery smoke test asserts the contract at
|
|
11
|
+
* the runtime boundary by polling a real opencode server;
|
|
12
|
+
* - the release workflow can fail fast if a future change adds
|
|
13
|
+
* or removes a tool without updating both consumers.
|
|
14
|
+
*
|
|
15
|
+
* Changing this set is a contract change. Both the in-process
|
|
16
|
+
* test and the live-server test must continue to agree.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const EXPECTED_OPENCODE_SHIP_TOOLS = Object.freeze([
|
|
20
|
+
// 9 existing delivery tools
|
|
21
|
+
"delivery_cleanup",
|
|
22
|
+
"delivery_github_read",
|
|
23
|
+
"delivery_inspect",
|
|
24
|
+
"delivery_issue",
|
|
25
|
+
"delivery_issue_close",
|
|
26
|
+
"delivery_issue_comment",
|
|
27
|
+
"delivery_issue_labels",
|
|
28
|
+
"delivery_issue_link",
|
|
29
|
+
"delivery_merge",
|
|
30
|
+
"delivery_pr",
|
|
31
|
+
"delivery_publish",
|
|
32
|
+
"delivery_ready",
|
|
33
|
+
"delivery_review",
|
|
34
|
+
"delivery_sync",
|
|
35
|
+
"delivery_verify",
|
|
36
|
+
"delivery_worktree",
|
|
37
|
+
// 7 control-plane + 8 workflow tools
|
|
38
|
+
"ship_plan_approve",
|
|
39
|
+
"ship_plan_start",
|
|
40
|
+
"ship_plan_submit",
|
|
41
|
+
"ship_resume",
|
|
42
|
+
"ship_run_start",
|
|
43
|
+
"ship_status",
|
|
44
|
+
"ship_task_report",
|
|
45
|
+
"ship_task_review",
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Frozen set, sorted lexicographically. The plugin-load and
|
|
50
|
+
* opencode-discovery tests both compare against this exact set.
|
|
51
|
+
*/
|
|
52
|
+
export const EXPECTED_OPENCODE_SHIP_TOOL_IDS = Object.freeze(
|
|
53
|
+
[...EXPECTED_OPENCODE_SHIP_TOOLS].sort(),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
export const OPENCODE_SHIP_TOOL_COUNT = EXPECTED_OPENCODE_SHIP_TOOLS.length;
|
|
@@ -7,41 +7,21 @@
|
|
|
7
7
|
* - calling it returns an object with a `tool` key
|
|
8
8
|
* - the `tool` object exposes exactly the 24 named tool
|
|
9
9
|
* definitions (9 delivery + 7 control-plane + 8 workflow)
|
|
10
|
+
*
|
|
11
|
+
* The canonical 24-tool set is imported from
|
|
12
|
+
* `tests/plugin/expected-tools.mjs`, the single source of truth
|
|
13
|
+
* shared with the opencode-discovery smoke test.
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
16
|
import test from "node:test";
|
|
13
17
|
import assert from "node:assert/strict";
|
|
14
18
|
import { pathToFileURL } from "node:url";
|
|
15
19
|
import { resolve } from "node:path";
|
|
20
|
+
import { EXPECTED_OPENCODE_SHIP_TOOL_IDS, OPENCODE_SHIP_TOOL_COUNT } from "./expected-tools.mjs";
|
|
16
21
|
|
|
17
22
|
const pluginPath = pathToFileURL(resolve("dist/plugin.js")).href;
|
|
18
23
|
|
|
19
|
-
const EXPECTED_TOOLS =
|
|
20
|
-
"delivery_cleanup",
|
|
21
|
-
"delivery_github_read",
|
|
22
|
-
"delivery_inspect",
|
|
23
|
-
"delivery_issue",
|
|
24
|
-
"delivery_issue_close",
|
|
25
|
-
"delivery_issue_comment",
|
|
26
|
-
"delivery_issue_labels",
|
|
27
|
-
"delivery_issue_link",
|
|
28
|
-
"delivery_merge",
|
|
29
|
-
"delivery_pr",
|
|
30
|
-
"delivery_publish",
|
|
31
|
-
"delivery_ready",
|
|
32
|
-
"delivery_review",
|
|
33
|
-
"delivery_sync",
|
|
34
|
-
"delivery_verify",
|
|
35
|
-
"delivery_worktree",
|
|
36
|
-
"ship_plan_approve",
|
|
37
|
-
"ship_plan_start",
|
|
38
|
-
"ship_plan_submit",
|
|
39
|
-
"ship_resume",
|
|
40
|
-
"ship_run_start",
|
|
41
|
-
"ship_status",
|
|
42
|
-
"ship_task_report",
|
|
43
|
-
"ship_task_review",
|
|
44
|
-
];
|
|
24
|
+
const EXPECTED_TOOLS = EXPECTED_OPENCODE_SHIP_TOOL_IDS;
|
|
45
25
|
|
|
46
26
|
test("plugin: default export is a function", async () => {
|
|
47
27
|
const mod = await import(pluginPath);
|
|
@@ -60,7 +40,7 @@ test("plugin: registers exactly 24 tools", async () => {
|
|
|
60
40
|
const result = await mod.default(fakeCtx);
|
|
61
41
|
assert.ok(result.tool, "result.tool should exist");
|
|
62
42
|
const ids = Object.keys(result.tool).sort();
|
|
63
|
-
assert.deepEqual(ids, EXPECTED_TOOLS, `expected
|
|
43
|
+
assert.deepEqual(ids, EXPECTED_TOOLS, `expected ${OPENCODE_SHIP_TOOL_COUNT} tools, got ${ids.length}: ${ids.join(", ")}`);
|
|
64
44
|
for (const id of ids) {
|
|
65
45
|
assert.equal(typeof result.tool[id].execute, "function", `${id} should expose an execute function`);
|
|
66
46
|
assert.equal(typeof result.tool[id].description, "string", `${id} should expose a description`);
|