opencode-ship 1.1.0 → 1.1.1-rc.9

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.
@@ -0,0 +1,655 @@
1
+ # opencode-ship 1.1.1 stabilization + self-hosting plan
2
+
3
+ > **Purpose:** Repair `opencode-ship` so it is fully self-hosting, then install it into its own repository as the last-known-good control plane.
4
+ > **Status:** Approved for execution
5
+ > **Owner:** Maintainer
6
+ > **Last reviewed:** 2026-08-15
7
+ > **Tracker:** https://github.com/Viktorxyz/opencode-ship/issues/40
8
+ > **Related:** [README.md](../../README.md), [CHANGELOG.md](../../CHANGELOG.md), [RELEASING.md](../../RELEASING.md), [THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md), [docs/adr/0001-opencode-ship-redesign.md](../adr/0001-opencode-ship-redesign.md)
9
+
10
+ ---
11
+
12
+ ## Fixed decisions
13
+
14
+ | Decision | Value |
15
+ |---|---|
16
+ | Target release | `opencode-ship@1.1.1` (first fully self-hosting release) |
17
+ | Rationale | Repairs behavior already promised by `1.1.0`; not a new product contract |
18
+ | Self-host control plane | Exact pinned `opencode-ship@1.1.1`; never `@latest`, local `dist`, local tarball, or dev-source plugin |
19
+ | Skill discovery | `trusted-auto` with immutable provenance; max 5 per workflow; always into active issue worktree |
20
+ | Planner | `openai/gpt-5.6-sol` |
21
+ | Builder / controller / task reviewer | `minimax/MiniMax-M3` |
22
+ | Final Standards + Spec reviewer | `openai/gpt-5.6-sol` |
23
+ | Tracker | GitHub (others deferred) |
24
+ | Built-in `plan` agent | Consumer-owned; `opencode-ship` provides dedicated `ship-planner` |
25
+ | `subagent_depth` | `2` |
26
+ | Merge policy | Squash, only after explicit `merge it`, fresh gates required |
27
+ | OpenCode restart | User-owned only |
28
+ | Leo local dirty main / worktrees | Not touched |
29
+ | `1.1.0` post-publication | Deprecated, not retagged |
30
+
31
+ These decisions are immutable during execution unless the user explicitly changes them.
32
+
33
+ ## Current factual baseline (re-check before every slice)
34
+
35
+ | Item | Value |
36
+ |---|---|
37
+ | Upstream repository | `/home/viktorcordas/repos/opencode-ship` |
38
+ | Release branch base | `release/1.1.0-engineering-only` |
39
+ | Release branch HEAD | `35fc676011e0592ef25756f6f7b48ab5fa6b9ffb` |
40
+ | Published stable | `1.1.0` (npm `latest`; sha512 verified; provenance signed) |
41
+ | Published previous stable | `1.0.0` (npm `next`) |
42
+ | Immutable tag | `1.1.0@5ea0794354d309e23a394dbf29f1fab0321a7428` (do not move) |
43
+ | `origin/main` HEAD (at S0) | `be1dad497e894c00f492932c601f9db8a9624245` |
44
+ | Divergence at S0 | `release/1.1.0-engineering-only` ahead of `origin/main` by 9 commits; `origin/main` ahead of `release/1.1.0-engineering-only` by 2 commits (both are docs-only closures) |
45
+ | Stabilization branch | `fix/1.1.1-stabilization` (created in S0) |
46
+ | Release qualification artifact | https://github.com/Viktorxyz/opencode-ship/releases/download/1.1.0/opencode-ship-1.1.0.qualification.json |
47
+ | GitHub release | https://github.com/Viktorxyz/opencode-ship/releases/tag/1.1.0 |
48
+ | Node minimum | `>=22.6.0` |
49
+ | OpenCode peer | `>=1.15.5 <2` |
50
+
51
+ The stabilization branch re-verifies this baseline in S0 and re-reports after each merge.
52
+
53
+ ## Scope
54
+
55
+ The critical path is S0 → S18. Each slice has explicit acceptance criteria and posts an evidence comment to issue #40. A failed slice is fixed by an additive commit and the affected qualification is repeated end-to-end.
56
+
57
+ ## Non-goals
58
+
59
+ - Do not change Leo product, design-lab, API, dashboard, web, or database code.
60
+ - Do not touch, clean, reset, stash, stage, or commit the dirty Leo `main` worktree.
61
+ - Do not touch the existing Leo worktrees.
62
+ - Do not rebase or rewrite pushed history.
63
+ - Do not retag `1.1.0` or `1.0.0`.
64
+ - Do not publish new major/minor versions until `1.1.1` is on `latest` and proven self-hosting.
65
+ - Do not bypass a failed CI check or dogfood step.
66
+ - Do not kill, signal, or restart any OpenCode process; the user owns restarts.
67
+
68
+ ## Definition of done
69
+
70
+ - [ ] `main` contains both the 1.0 promotion and 1.1 stabilization history.
71
+ - [ ] Zero tracked filename-disabled tests.
72
+ - [ ] `npm run format:check`, `npm run lint`, `npm run typecheck`, `npm run build`, `npm test` all pass.
73
+ - [ ] Configured models appear in installed agent frontmatter and are enforced by tool authorization.
74
+ - [ ] Default-deny consumer runs the full controller flow without manual permission patching.
75
+ - [ ] Public plugin tool state machine drives plan → Ready → explicit merge → cleanup end-to-end.
76
+ - [ ] Two final review axes (Standards + Spec) are required and bound to one HEAD.
77
+ - [ ] Trusted skills install only in the active issue worktree with immutable provenance and drift protection.
78
+ - [ ] `setup-ship-workflow` is chat-only and GitHub-only.
79
+ - [ ] Packed E2E runs only through public plugin tools.
80
+ - [ ] Release workflow qualifies the same SHA and tarball.
81
+ - [ ] Real registry dogfood passes on `1.1.1-rc.N` with real OpenCode, GitHub, and models.
82
+ - [ ] `1.1.1` is on npm `latest`; `1.1.0` is deprecated.
83
+ - [ ] `opencode-ship` uses exact `1.1.1` for its own workflow.
84
+ - [ ] Self-hosted docs issue passes plan → cleanup end-to-end.
85
+ - [ ] Leo uses exact `1.1.1`; migration and dogfood PRs both pass.
86
+ - [ ] README, CHANGELOG, RELEASING, GitHub release, npm dist-tags, and qualification evidence all describe the same shipped state.
87
+
88
+ ## Safety and authorization gates
89
+
90
+ The Build agent autonomously edits, tests, commits, pushes, opens and updates PRs, and pushes release tags after the underlying PR merges. It stops and requests explicit user authorization before each of the following:
91
+
92
+ | Gate | Irreversible or externally visible operation |
93
+ |---|---|
94
+ | A | Any real plan approval — controller surfaces, user approves, controller calls `ship_plan_approve` |
95
+ | B | Merge of the stabilization PR into `main` |
96
+ | C | Merge of the RC/stable promotion PR |
97
+ | D | Merge of the self-host bootstrap PR |
98
+ | E | Merge of any Leo migration PR |
99
+ | F | Merge of the Leo dogfood PR |
100
+ | G | `npm dist-tag add ... latest` and any other 2FA/OTP step |
101
+ | H | User restarts OpenCode — Build never touches OpenCode processes |
102
+
103
+ No gate authorizes force-push, hard reset, stash, rebasing pushed work, amending pushed commits, bypassing failed checks, retagging existing versions, overwriting published versions, or weakening a verification or dogfood gate.
104
+
105
+ ## Evidence ledger
106
+
107
+ The parent issue is the authoritative progress tracker. Add one bounded comment after every slice using this schema:
108
+
109
+ ```text
110
+ Status: pass | fail | blocked | partial
111
+ Phase: S0 | S1 | ... | S18
112
+ Source SHA: <40 hex>
113
+ Package version: <semver or n/a>
114
+ Summary:
115
+ - <at most three bullets>
116
+ Evidence:
117
+ - issue / PR / workflow / release URLs
118
+ - npm integrity and tarball SHA-256 when applicable
119
+ - artifact URL when applicable
120
+ Verification:
121
+ - <command> -> exit <code>
122
+ Risks:
123
+ - <unresolved concern or none>
124
+ Next authorization: <exact gate or none>
125
+ ```
126
+
127
+ Never place tokens, credentials, raw model prompts containing secrets, or unsanitized environment dumps in the issue or repository.
128
+
129
+ ---
130
+
131
+ ## S0: Persist plan and tracker
132
+
133
+ ### Goal
134
+
135
+ Create durable coordination surfaces before any runtime change.
136
+
137
+ ### Steps
138
+
139
+ 1. Re-read this plan, `README.md`, `CHANGELOG.md`, `RELEASING.md`, `schema/ship-config.schema.json`, `.github/workflows/release.yml`, and `tests/package/neutral-consumer.test.mjs`.
140
+ 2. Confirm the upstream worktree is clean.
141
+ 3. Create parent issue #40 with body that mirrors the fixed decisions and A–H gates.
142
+ 4. Anchor this plan's `Tracker` header to issue URL.
143
+ 5. Add the S0 evidence comment to the issue.
144
+ 6. Create `fix/1.1.1-stabilization` from `release/1.1.0-engineering-only`.
145
+ 7. Merge `origin/main` into the stabilization branch with `--no-ff`. Resolve `package.json`, `package-lock.json`, and `THIRD_PARTY_NOTICES.md` in favor of 1.1 behavior.
146
+ 8. Commit this plan to the stabilization branch.
147
+
148
+ ### Verification
149
+
150
+ ```bash
151
+ git status --short
152
+ git diff --check
153
+ git log --oneline HEAD~1..HEAD
154
+ npm view opencode-ship dist-tags --json
155
+ ```
156
+
157
+ ### Acceptance
158
+
159
+ - [ ] Parent issue #40 exists.
160
+ - [ ] The issue and this file contain identical fixed decisions.
161
+ - [ ] Current branch, SHA, and npm tags are recorded.
162
+ - [ ] No runtime or release workflow code changed in the plan commit.
163
+ - [ ] `release/1.1.0-engineering-only` is an ancestor of `HEAD`.
164
+ - [ ] `origin/main` is an ancestor of `HEAD`.
165
+
166
+ ## S1: Restore the test contract
167
+
168
+ ### Goal
169
+
170
+ Make the test suite executable specification of the actual contract; no filename-disabled tests.
171
+
172
+ ### Branching
173
+
174
+ Continue on `fix/1.1.1-stabilization`. No new branches per slice.
175
+
176
+ ### Required changes
177
+
178
+ - Remove `*.skip.test.mjs` exclusions and the `.core-removed` filter from `scripts/run-all-tests.mjs`.
179
+ - Rename all 15 `*.skip.test.mjs` files back to `*.test.mjs`.
180
+ - Convert core-profile assertions into legacy-input migration assertions.
181
+ - Add `tests/release/no-disabled-tests.test.mjs` that fails if any tracked test is disabled by filename convention.
182
+ - Fix the `root-config` teardown race (move per-test cleanup to file-level or use `t.before/after`).
183
+ - Restore `tests/release/opencode-discovery.test.mjs`, `tests/release/runtime-source-sha.test.mjs`, `tests/release/release-metadata.test.mjs`.
184
+ - Restore `tests/package/packed-artifact.test.mjs`, `tests/package/two-task-workflow.test.mjs`, `tests/package/neutral-consumer.test.mjs`, `tests/package/transition-matrix.test.mjs`.
185
+ - Restore `tests/installer/root-config.test.mjs`, `tests/installer/root-restoration.test.mjs`, `tests/installer/profile.test.mjs`, `tests/installer/profile-transition.test.mjs`, `tests/installer/lock-profile.test.mjs`, `tests/installer/executor-profile.test.mjs`, `tests/installer/engineering-routing.test.mjs`, `tests/installer/catalog-profile.test.mjs`.
186
+
187
+ ### Acceptance
188
+
189
+ - [ ] `zero filename-disabled tests`.
190
+ - [ ] All restored tests run in `npm test`.
191
+ - [ ] Restored tests may fail at this slice; S2–S7 make them green.
192
+
193
+ ## S2: Config + legacy migration
194
+
195
+ ### Goal
196
+
197
+ `0.4`, `0.9`, `1.0`, and `1.1.0` consumers safely converge on `1.1.1` engineering profile without losing user-owned config.
198
+
199
+ ### Required changes
200
+
201
+ - `schema/ship-config.schema.json`: keep `additionalProperties: false`, but add `skillDiscovery` enum object; preserve legacy `profile: "core"` only inside an `x-legacy-profiles` annotation used by readers, never accepted in new writes.
202
+ - `schema/ship-lock.schema.json`: bump to `v4`; add `manager.setupComplete`; remove `cleanupPending` from the install lock.
203
+ - `src/profile.js`: keep `PROFILES = ["engineering"]`; keep legacy-core promotion only at read path for persisted locks/configs.
204
+ - `src/installer/config.js`: `loadConfig` accepts legacy core configs and flags them as `legacyPromotion: true`. `hasCompletedModels` reads `cfg.workflow.models` (full config object).
205
+ - `src/installer/engineering-config.js`: unify on `cfg.workflow.models`; remove runtime defaults.
206
+ - `src/installer/lock.js`: read v1/v2/v3 core locks; promote to engineering on read; refuse to write v4 over user-modified v3 bytes.
207
+ - `src/installer/planner.js`: model flags patch only `workflow.models`; never trigger `forceConfig`.
208
+ - `src/installer/executor.js`: thread correct `cfg` to `hasCompletedModels`; derive setup state from current config + lock.
209
+ - `src/installer/setup-pending.js`: move all writes/deletes into the installer's transaction layer.
210
+ - `src/installer/commands/init.js`: write setup marker when `workflow.models` is incomplete; remove marker when complete.
211
+ - `src/installer/commands/update.js`: model flags are not `forceConfig`; preserve unrelated config fields.
212
+ - `src/installer/commands/doctor.js`: report incomplete models, stale/missing marker, lock/config disagreement, and ownership drift.
213
+ - Move `cleanupPending` from `ship.lock.json` to `<git-common-dir>/opencode-ship/cleanup-pending.json`.
214
+
215
+ ### Acceptance
216
+
217
+ - [ ] Fresh init without model flags creates marker.
218
+ - [ ] Init with model flags omits marker and writes `setupComplete: true`.
219
+ - [ ] Update with model flags preserves unrelated config bytes.
220
+ - [ ] v1/v2/v3 core locks load and converge to engineering.
221
+ - [ ] Unknown profile in legacy config still fails closed.
222
+ - [ ] Cleanup retry state lives under Git common dir, not in the install lock.
223
+
224
+ ## S3: Dynamic model routing
225
+
226
+ ### Goal
227
+
228
+ Configured planner / builder / finalReviewer models appear in installed agent frontmatter and are enforced by tool authorization.
229
+
230
+ ### Required changes
231
+
232
+ - New `src/installer/agent-renderer.js`:
233
+ - Reads `cfg.workflow.models` plus the agent map.
234
+ - Returns rendered agent bytes.
235
+ - `src/installer/catalog.js`: mark each workflow agent as `dynamic: true` with renderer binding.
236
+ - `assets/agents/ship-controller.md`, `ship-planner.md`, `ship-task-builder.md`, `ship-task-reviewer.md`, `ship-final-standards-reviewer.md`, `ship-final-spec-reviewer.md`: remove concrete `model:` values; add `<model-from-config>` placeholder or rely on renderer-only frontmatter.
237
+ - `src/installer/planner.js`: compute managed hashes from rendered bytes.
238
+ - `src/installer/executor.js`: after render, hash the rendered agent bytes for the lock entry.
239
+ - `src/tools/ship-plan-start.js`: dispatch the planner agent through the plugin's agent invocation; record `dispatchedPlannerModel`.
240
+ - `src/tools/ship-plan-submit.js`: authorize `submittedBy` against `dispatchedPlannerModel`.
241
+ - `src/tools/ship-task-report.js`: authorize `submittedBy` against builder model.
242
+ - `src/tools/ship-task-review.js`: authorize `submittedBy` against builder model.
243
+ - `src/tools/ship-final-review.js` (new): authorize against finalReviewer model; persist axis and verdict.
244
+
245
+ ### Acceptance
246
+
247
+ - [ ] Canonical templates contain no provider/model IDs.
248
+ - [ ] Rendered consumer agents match configured model roles exactly.
249
+ - [ ] Changing only the builder model updates exactly three agent files.
250
+ - [ ] Wrong-role model cannot submit task report/review/final review.
251
+ - [ ] Plan/run snapshots capture model snapshot used for that run.
252
+
253
+ ## S4: Root permissions
254
+
255
+ ### Goal
256
+
257
+ Default-deny consumers can run the full controller flow without manual permission patching; JSONC preserves user formatting; Plan permissions revert to user-owned.
258
+
259
+ ### Required changes
260
+
261
+ - New `src/installer/root-permissions.js`: declarative matrix of Build + controller + subagent + tool permissions.
262
+ - `src/installer/root-config.js`: replace ad-hoc pointer list with the matrix; capture previous values for each pointer.
263
+ - `src/installer/root-reconciliation.js`: structural-equality comparison via `stableStringify`; restore exact previous bytes when no unrelated edits occurred.
264
+ - `src/installer/plan-mode-permissions.js`: delete the consumer-owned Plan pointer from installer ownership; record previous values for cleanup.
265
+ - `src/installer/commands/init.js`: prompt before creating a missing root config; use `update --force-root-config` instead.
266
+ - Add `jsonc-parser` to dependencies; use it to preserve comments, trailing commas, and key order on rewrite.
267
+ - Pre-install snapshot stored under `<git-common-dir>/opencode-ship/install-backup/<sha>.json` so uninstall restores exact bytes when untouched.
268
+
269
+ ### Acceptance
270
+
271
+ - [ ] Fresh root creation prompts before writing.
272
+ - [ ] Existing JSON and JSONC retain comments, ordering, and unrelated keys.
273
+ - [ ] Install/update/uninstall restore exact bytes when untouched.
274
+ - [ ] Unrelated post-install edits survive uninstall.
275
+ - [ ] Pointer drift fails closed.
276
+ - [ ] Build cannot impersonate planner/builder/reviewers.
277
+ - [ ] Controller cannot approve or merge.
278
+
279
+ ## S5: Workflow state machine
280
+
281
+ ### Goal
282
+
283
+ Public plugin tools drive the workflow end-to-end without tests importing internal reducer helpers.
284
+
285
+ ### Required changes
286
+
287
+ - `src/workflow/run-controller.js`: add cases for `TASK_REPORT`, `TASK_COMPLETE`, `FINAL_REVIEW`, `READY`, `READY_PENDING`, `ALL_TASKS_DONE`, `MERGE`, `DONE`. Reject out-of-order events. Reject second active task. Preserve task ID across fix rounds.
288
+ - New `src/tools/ship-task-start.js`: dispatch a task; record child session ID, brief hash, reviewed paths.
289
+ - New `src/tools/ship-task-commit.js`: validate HEAD, reviewed path set, evidence, trailers; append immutable commit record.
290
+ - New `src/tools/ship-task-complete.js`: advance to next dependency-satisfied task or to final review.
291
+ - `src/tools/ship-run-start.js`: require `revision` and `sha256`; verify approval models and live base SHA; use `createInitialState`.
292
+ - `src/tools/ship-plan-start.js`: open workflow; record dispatched planner model.
293
+ - `src/tools/ship-plan-submit.js`: validate configured planner model and workflow/revision equality.
294
+ - `src/tools/ship-plan-approve.js`: persist immutable approval with model snapshot, base SHA, decision.
295
+ - `src/tools/ship-task-report.js`: include `changes` and `tests`.
296
+ - `src/tools/ship-task-review.js`: validate spec + quality.
297
+ - `src/tools/ship-resume.js`: restore from disk; reconcile live Git state; never duplicate work.
298
+ - `src/tools/ship-status.js`: return the documented compact shape.
299
+ - `src/plugin.js`: register the four new tool definitions and ensure the schema is correct.
300
+
301
+ ### Acceptance
302
+
303
+ - [ ] All reducer transitions have tests.
304
+ - [ ] Public plugin schema includes every required argument.
305
+ - [ ] Two-task flow with one failed review round passes.
306
+ - [ ] Base SHA drift rejects `ship_run_start`.
307
+ - [ ] Resume after each state and crash boundary.
308
+
309
+ ## S6: Dual final review + same-HEAD Ready gate + mirror restore
310
+
311
+ ### Goal
312
+
313
+ Ready requires Standards and Spec reviews, verifier, and CI all bound to one HEAD, package hash, and merge base.
314
+
315
+ ### Required changes
316
+
317
+ - `src/workflow/final-review.js`: persist axis-specific records (`standards`, `spec`) with HEAD, merge-base SHA, and package hash.
318
+ - `src/workflow/same-head-gate.js`: bind Standards + Spec + verifier + CI + PR head to the same commit.
319
+ - `src/workflow/plan-store.js`: mirror restoration hook used by `ship_resume`.
320
+ - `src/tools/delivery-review.js`: support axis mode for engineering runs; preserve legacy single-review mode for legacy manifests.
321
+ - `src/tools/delivery-ready.js`: enforce the engineering same-HEAD gate when the run is engineering.
322
+ - `src/tools/delivery-merge.js`: fresh gate recheck on every merge.
323
+ - `src/tools/delivery-cleanup.js`: advance merged runs to done.
324
+ - New `src/state/issue-mirror.js`: idempotent mirror publish; restore from issue when local plan copy is missing.
325
+
326
+ ### Acceptance
327
+
328
+ - [ ] Missing Standards or Spec review blocks Ready.
329
+ - [ ] Different HEAD blocks Ready.
330
+ - [ ] Different package hash blocks Ready.
331
+ - [ ] Failed review blocks Ready.
332
+ - [ ] Ready does not merge.
333
+ - [ ] Merge requires explicit `merge it` subject.
334
+ - [ ] Cleanup succeeds only on a clean, published-PR worktree.
335
+
336
+ ## S7: Trusted skill lifecycle
337
+
338
+ ### Goal
339
+
340
+ Trusted auto-install works with immutable provenance, audit, and uninstall.
341
+
342
+ ### Required changes
343
+
344
+ - New `src/skills/policy.js`: read/write trusted-owner list, install threshold, auto-install cap.
345
+ - New `src/skills/registry.js`: pinned version of the `skills` CLI; `shell: false`; immutable ref extraction.
346
+ - New `src/skills/inventory.js`: write/read `.opencode/ship.skills.lock.json` under the consumer repo (not Git common dir).
347
+ - New `src/tools/ship-skill-discover.js`: parse `npx skills find`; partition candidates.
348
+ - New `src/tools/ship-skill-install.js`: enforce policy; install only into active issue worktree.
349
+ - New `src/tools/ship-skill-audit.js`: report missing, drifted, unpinned, colliding skills.
350
+ - New `src/tools/ship-skill-uninstall.js`: remove only unchanged recorded files.
351
+ - `schema/ship-config.schema.json`: add `skillDiscovery` schema.
352
+ - `schema/ship-lock.schema.json`: keep install lock clean; skill inventory is a sibling lock.
353
+ - `assets/agents/ship-controller.md`: remove raw `npx skills add` permission.
354
+ - Remove direct network fetches from `ship-deliver`; only the typed tools fetch.
355
+ - Reject path escape, symlink, managed-skill collision, malformed frontmatter, oversize tree.
356
+
357
+ ### Acceptance
358
+
359
+ - [ ] Trusted pinned skill auto-installs.
360
+ - [ ] Trusted unpinned skill requires approval.
361
+ - [ ] Untrusted skill requires approval.
362
+ - [ ] Sixth trusted skill is not auto-installed.
363
+ - [ ] Install into main worktree is rejected.
364
+ - [ ] Linked worktree uses the resolved Git common dir.
365
+ - [ ] Audit detects missing, drifted, untracked skills.
366
+ - [ ] Uninstall removes only files matching lock hash.
367
+ - [ ] Modified skill files are preserved as conflicts.
368
+
369
+ ## S8: Setup workflow
370
+
371
+ ### Goal
372
+
373
+ After `init`, the user only chats; setup is GitHub-only.
374
+
375
+ ### Required changes
376
+
377
+ - Rename `assets/skills/setup-engineering-workflow` to `assets/skills/setup-ship-workflow`.
378
+ - Remove duplicate entry from `MATT_SKILLS` in `src/installer/catalog.js`.
379
+ - Install every companion file for every bundled skill.
380
+ - `assets/commands/setup-ship-workflow.md`: thin command wrapper around the canonical skill.
381
+ - Skill reads root `opencode.json` or `opencode.jsonc`; uses `opencode providers list` and `opencode models <provider>` for validation.
382
+ - Apply model selections through `opencode-ship update --planner-model ... --builder-model ... --final-reviewer-model ... --force-config` so config + lock + rendered agents + setup marker all update in one transaction.
383
+ - Do not manually delete the setup marker.
384
+ - Limit supported tracker to GitHub in 1.1.1; surface others as unsupported.
385
+ - Add `## Ship workflow` block to `AGENTS.md` if absent.
386
+ - Add `docs/agents/issue-tracker.md`, `domain.md`, and `triage-labels.md` if absent.
387
+ - No `TODO.md`, `FOLLOW_UP.md`, or `BACKLOG.md`.
388
+
389
+ ### Acceptance
390
+
391
+ - [ ] Exactly one installed skill has `name: setup-ship-workflow`.
392
+ - [ ] Every relative link in each installed skill resolves in the consumer.
393
+ - [ ] Setup rerun is no-op for completed sections.
394
+ - [ ] Custom delivery config survives model setup.
395
+ - [ ] Unsupported tracker stops before writing.
396
+ - [ ] Setup completes with no global `opencode-ship` install.
397
+
398
+ ## S9: Packed self-hosting E2E
399
+
400
+ ### Goal
401
+
402
+ Prove the packed artifact works end-to-end through the public plugin tools.
403
+
404
+ ### Required changes
405
+
406
+ - Rewrite `tests/package/two-task-workflow.test.mjs` to operate through the packed plugin's public tool surface. No direct imports of `src/workflow/*` or `src/state/*`.
407
+ - `tests/package/packed-artifact.test.mjs`: install into a neutral Git repo; run setup, model render, root permissions, skill install/uninstall, package uninstall.
408
+ - `tests/package/neutral-consumer.test.mjs`: real OpenCode discovery against supported versions.
409
+ - `tests/release/opencode-discovery.test.mjs`: same coverage as S10.
410
+ - Replace the no-op `scripts/dogfood-local.mjs` with a real disposable-repository driver.
411
+
412
+ ### Acceptance
413
+
414
+ - [ ] No E2E step imports internal helpers to manufacture state.
415
+ - [ ] Every lifecycle change is produced through a registered tool.
416
+ - [ ] Packed artifact has no source-tree or `node_modules` dependency.
417
+ - [ ] Discovery exposes the canonical tool set on both supported OpenCode versions.
418
+
419
+ ## S10: CI + release policy
420
+
421
+ ### Goal
422
+
423
+ Every qualification job runs against the same tag SHA and tarball.
424
+
425
+ ### Required changes
426
+
427
+ - `.github/workflows/release.yml`:
428
+ - Resolve the release ref once from tag push or `workflow_dispatch` input.
429
+ - All `actions/checkout` use that exact ref.
430
+ - `workflow-e2e` runs the packed self-hosting test from S9.
431
+ - Release policy rejects tags not reachable from `origin/main`.
432
+ - `node-compat` matrix uploads per-row Node versions under unique artifact names.
433
+ - `opencode-discovery` matrix is mandatory; skip is a release failure.
434
+ - Publish job is the only place pinned to Node 22.14.0 / npm 11.5.2.
435
+ - `.github/workflows/verify.yml`: trigger on PRs to `main`, `merge_group`, and pushes to `main` and stabilization branches.
436
+ - `tests/release/release-metadata.test.mjs`: assert single resolved ref per job.
437
+ - `tests/release/node-compat-policy.test.mjs`: assert each matrix lane uploads its own observed Node version.
438
+ - `tests/release/no-disabled-tests.test.mjs`: gate.
439
+ - Generate tool/agent/skill counts from canonical inventories at build time.
440
+
441
+ ### Acceptance
442
+
443
+ - [ ] Workflow tests pass on the qualification ref.
444
+ - [ ] Dispatch and tag paths use the same checkout SHA.
445
+ - [ ] Qualification report SHA matches the tag SHA.
446
+ - [ ] `workflow-e2e` runs the real packed flow.
447
+
448
+ ## S11: Docs hygiene
449
+
450
+ ### Goal
451
+
452
+ Docs reflect the actual shipped state.
453
+
454
+ ### Required changes
455
+
456
+ - Remove tracked `tsconfig.dts.json`; rely on the `.tmp` generation.
457
+ - New `tests/release/no-tracked-temp-configs.test.mjs`: fail if any tracked file is a generated config.
458
+ - Rewrite README around the actual 1.1.1 flow.
459
+ - Rewrite RELEASING around the `1.1.x` trunk-based RC cycle.
460
+ - Add `## 1.1.1` changelog entry that explicitly identifies `1.1.0` as an incomplete workflow release.
461
+ - Update THIRD_PARTY_NOTICES to reflect the actual shipped skill count.
462
+ - Remove false claims about green E2E, core downgrade, unsupported trackers, automatic skill auditing, and stale matrix lanes.
463
+ - Restore provenance for unchanged Matt companion files under the canonical setup skill; do not classify copied bytes as first-party.
464
+
465
+ ### Acceptance
466
+
467
+ - [ ] No tracked file contains developer-specific absolute paths.
468
+ - [ ] README, CHANGELOG, RELEASING, package metadata, GitHub release, and npm dist-tags all agree.
469
+ - [ ] Skill count and tool count are generated, not hand-written.
470
+
471
+ ## S12: `1.1.1-rc.1`
472
+
473
+ ### Goal
474
+
475
+ Publish the first self-hosting RC.
476
+
477
+ ### Steps
478
+
479
+ 1. Merge stabilization PR into `main` after Gate B.
480
+ 2. Push the `1.1.1-rc.1` tag from the merged commit.
481
+ 3. Watch the release workflow to `publish` step.
482
+ 4. Verify npm integrity, provenance, GitHub release digest, and qualification artifact.
483
+ 5. Post the S12 evidence comment to issue #40.
484
+
485
+ ### Acceptance
486
+
487
+ - [ ] `npm view opencode-ship dist-tags` shows `next: 1.1.1-rc.1`.
488
+ - [ ] `npm install --prefix /tmp/fresh opencode-ship@next` exits 0; CLI prints `opencode-ship 1.1.1-rc.1`.
489
+
490
+ ## S13: Real registry dogfood
491
+
492
+ ### Goal
493
+
494
+ End-to-end proof of the engineering workflow using the npm-published RC.
495
+
496
+ ### Steps
497
+
498
+ 1. Provision the existing disposable `Viktorxyz/opencode-ship-dogfood` or create a fresh one.
499
+ 2. Install `opencode-ship@1.1.1-rc.N` with the exact `--planner-model`, `--builder-model`, `--final-reviewer-model` flags.
500
+ 3. Restart OpenCode.
501
+ 4. Run the full 14-step dogfood from `docs/release/1.0.0-execution-plan.md` (S5), adapted to `ship-planner`, dual final review, mirror restore, `ship_resume`, and cleanup.
502
+ 5. Capture evidence in the issue comment.
503
+
504
+ ### Acceptance
505
+
506
+ - [ ] Real planner, real builder, real final reviewers.
507
+ - [ ] One failed review round + fix + retry succeeds.
508
+ - [ ] User-controlled restart + `ship_resume` restores from ledger.
509
+ - [ ] Local plan deletion + mirror restore succeeds.
510
+ - [ ] Standards + Spec + verifier + CI all bind to one HEAD.
511
+ - [ ] Ready-before-merge snapshot proves no premature merge.
512
+ - [ ] Explicit `merge it` merges, cleans up, and leaves no unpublished commits.
513
+
514
+ ## S14: `1.1.1` stable
515
+
516
+ ### Goal
517
+
518
+ Promote the validated RC to stable.
519
+
520
+ ### Steps
521
+
522
+ 1. Bump `package.json`, `package-lock.json` from `1.1.1-rc.1` to `1.1.1`.
523
+ 2. Update README status and CHANGELOG `## 1.1.1` header.
524
+ 3. Open the stable promotion PR; merge after Gate C.
525
+ 4. Push the `1.1.1` tag.
526
+ 5. Watch the release workflow to `publish`.
527
+ 6. User resolves npm 2FA + `npm dist-tag add opencode-ship@1.1.1 latest` (Gate G).
528
+ 7. `npm dist-tag deprecate opencode-ship@1.1.0` with an upgrade note.
529
+
530
+ ### Acceptance
531
+
532
+ - [ ] `npm view opencode-ship dist-tags` shows `latest: 1.1.1`, `next: 1.0.0`.
533
+ - [ ] `npm view opencode-ship@1.1.0` deprecation warning points to `1.1.1`.
534
+ - [ ] Runtime-source digest matches the accepted RC.
535
+
536
+ ## S15: Self-hosting bootstrap
537
+
538
+ ### Goal
539
+
540
+ Install `opencode-ship@1.1.1` into its own repository as the LKG control plane.
541
+
542
+ ### Steps
543
+
544
+ 1. Bootstrap from a fresh worktree based on `origin/main` after `1.1.1` is on `latest`.
545
+ 2. `pnpm dlx opencode-ship@1.1.1 init --force-root-config` (only if root config is missing).
546
+ 3. In chat: `/setup-ship-workflow`. Provide planner, builder, final reviewer, GitHub repo, triage defaults.
547
+ 4. Restart OpenCode.
548
+ 5. Commit the complete managed snapshot: `opencode.json`, `.opencode/plugins/opencode-ship.js`, `.opencode/agents/*.md`, `.opencode/commands/*.md`, `.opencode/skills/**`, `.opencode/ship.config.json`, `.opencode/ship.lock.json`, `.opencode/ship.skills.lock.json`, `AGENTS.md` block, `docs/agents/**`.
549
+ 6. Open bootstrap PR; merge after Gate D.
550
+ 7. Restart OpenCode again so the merged snapshot is loaded.
551
+
552
+ ### Acceptance
553
+
554
+ - [ ] Exactly one plugin instance.
555
+ - [ ] Expected ship and delivery agents discoverable.
556
+ - [ ] Expected typed tools discoverable (24 + 4 task/final + 4 skill = 32).
557
+ - [ ] `doctor --json` exits 0.
558
+ - [ ] `diff --json` exits 0 with status `noop`.
559
+ - [ ] Setup marker absent.
560
+ - [ ] No use of `@latest`, local `dist`, local tarball, or dev-source plugin.
561
+
562
+ ## S16: Self-host acceptance
563
+
564
+ ### Goal
565
+
566
+ Prove the installed `opencode-ship@1.1.1` drives one real issue through the full workflow inside this repo.
567
+
568
+ ### Steps
569
+
570
+ 1. Open a small docs-only issue (suggested: "docs: add a short self-hosting recovery example to RELEASING.md").
571
+ 2. Run `Ship issue N` through plan, approval, trusted skill discovery, worktree, builder, task review, dual final reviews, verifier, CI, Ready.
572
+ 3. Stop at Ready.
573
+ 4. After Gate F (`merge it`), merge, cleanup.
574
+ 5. Post S16 evidence.
575
+
576
+ ### Acceptance
577
+
578
+ - [ ] `1.1.1` is now a validated self-hosting LKG.
579
+
580
+ ## S17: Leo migration
581
+
582
+ ### Goal
583
+
584
+ Migrate `Viktorxyz/leo` to `opencode-ship@1.1.1` engineering profile without losing user-owned config.
585
+
586
+ ### Steps
587
+
588
+ 1. Create a fresh worktree from `origin/main`. Do not touch the dirty local `main` or any existing worktree.
589
+ 2. Open a new Leo migration issue that supersedes PR #221.
590
+ 3. `pnpm dlx opencode-ship@1.1.1 diff --root . --json` (exit 1 expected).
591
+ 4. Reconcile: preserve `pnpm verify:workspace`, `pnpm accept:dev`, `dev-acceptance` CI gate, `agent.plan.permission`, all force-push/reset/cleanup restrictions.
592
+ 5. Remove legacy `.opencode/delivery.json`, `.opencode/delivery.lock.json`, legacy `opencode-delivery` skills path, explicit plugin entry, stale `pnpm-lock.yaml` importer.
593
+ 6. `pnpm dlx opencode-ship@1.1.1 diff --root . --json` (no conflicts expected).
594
+ 7. `pnpm dlx opencode-ship@1.1.1 update --root . --json`.
595
+ 8. `pnpm dlx opencode-ship@1.1.1 doctor --root . --json` (exit 0).
596
+ 9. `pnpm dlx opencode-ship@1.1.1 diff --root . --json` (exit 0, status `noop`).
597
+ 10. Commit and open the migration PR. Do not use `--replace-managed`, `--force-config`, or `--force-root-config` to mask conflicts.
598
+ 11. After Gate E, merge.
599
+ 12. User restarts OpenCode.
600
+ 13. Close PR #221 as superseded.
601
+
602
+ ### Acceptance
603
+
604
+ - [ ] `opencode-ship doctor --json` exits 0.
605
+ - [ ] No `opencode-delivery` references remain in active config.
606
+ - [ ] `pnpm verify:workspace` exits 0.
607
+ - [ ] `pnpm accept:dev` exits 0.
608
+ - [ ] Lock: exact `1.1.1`, engineering profile, `setupComplete: true`, no cleanup pending.
609
+
610
+ ## S18: Leo dogfood
611
+
612
+ ### Goal
613
+
614
+ Prove Leo's installed `opencode-ship@1.1.1` drives one real Leo issue through the full workflow.
615
+
616
+ ### Steps
617
+
618
+ 1. Open a small docs-only Leo issue (suggested: small docs drift fix).
619
+ 2. Run `Ship issue N` through the full workflow.
620
+ 3. Stop at Ready. Merge only after Gate F.
621
+ 4. Post S18 evidence.
622
+
623
+ ### Acceptance
624
+
625
+ - [ ] Standards + Spec + verifier + CI all bind to one HEAD.
626
+ - [ ] Ready-before-merge snapshot proves no premature merge.
627
+ - [ ] Explicit `merge it` merges and cleans up with no unpublished commits.
628
+
629
+ ---
630
+
631
+ ## Rescue procedures
632
+
633
+ If startup is broken after self-host bootstrap:
634
+
635
+ ```sh
636
+ OPENCODE_DISABLE_PROJECT_CONFIG=1 opencode
637
+ ```
638
+
639
+ If the plugin loader is broken:
640
+
641
+ ```sh
642
+ OPENCODE_PURE=1 opencode
643
+ ```
644
+
645
+ If both fail, open a rescue worktree at the known-good commit and use the exact registry CLI to repair; do not load the candidate plugin.
646
+
647
+ ## Rollback
648
+
649
+ Before merge: stop on conflict; add a corrective commit or close the PR. Preserve the worktree.
650
+
651
+ After self-host bootstrap: open a normal revert PR; user restarts OpenCode.
652
+
653
+ After Leo migration: open a normal revert PR; re-run `pnpm verify:workspace` and `pnpm accept:dev`; user restarts OpenCode.
654
+
655
+ Do not use `opencode-ship uninstall --purge-config` as a primary rollback; a Git revert restores the exact prior state.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-ship",
3
- "version": "1.1.0",
3
+ "version": "1.1.1-rc.9",
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",