135 lines
6.4 KiB
Markdown
135 lines
6.4 KiB
Markdown
# Tasks: add-installer-upgrade
|
|
|
|
## 1. Error codes
|
|
|
|
- [ ] 1.1 Append `UPGRADE_NOT_INSTALLED`, `UPGRADE_VERSION_NOT_FOUND`,
|
|
`UPGRADE_SAME_VERSION`, `UPGRADE_DIRTY_TREE`, `UPGRADE_STATE_INVALID`,
|
|
`UPGRADE_MIGRATION_FAILED`, `UPGRADE_LOCKFILE_MISSING` to the
|
|
`defineCodes("INSTALLER", [...])` tuple in
|
|
`packages/tooling/installer/src/error-codes.ts`.
|
|
- [ ] 1.2 Extend `src/error-codes.spec-conformance.test.ts` with an
|
|
`INSTALLER-UPGRADE-09` block asserting the codes are present, frozen,
|
|
and prefixed `INSTALLER.`.
|
|
|
|
## 2. State file (`src/state/`)
|
|
|
|
- [ ] 2.1 `src/state/state.types.ts` — `InstallerState`, `InstalledPlugin`,
|
|
`FileHash` types.
|
|
- [ ] 2.2 `src/state/state.schemas.ts` — Zod schemas for boundary validation.
|
|
- [ ] 2.3 `src/state/state-file.ts` — `loadState(fs, cwd)`,
|
|
`writeState(fs, cwd, state)` (atomic write → fsync → rename),
|
|
`updatePlugin(state, entry)`. Failure → `INSTALLER.UPGRADE_STATE_INVALID`.
|
|
- [ ] 2.4 `src/state/hash-tree.ts` — `hashTree(fs, dir)` → `Record<path, hex>`
|
|
using BLAKE3 via `@suaveplan/crypto-hash`.
|
|
- [ ] 2.5 `src/state/state-file.spec-conformance.test.ts` —
|
|
`INSTALLER-UPGRADE-08`.
|
|
- [ ] 2.6 `.md` siblings for every implementation file.
|
|
|
|
## 3. Pre-flight checks (`src/host/upgrade/preflight.ts`)
|
|
|
|
- [ ] 3.1 `detectLockfile(fs, cwd)` returning `"bun" | "npm" | "pnpm" | "yarn"`
|
|
or throwing `INSTALLER.UPGRADE_LOCKFILE_MISSING`.
|
|
- [ ] 3.2 `detectDirtyTree(state, currentHashes)` returning the list of
|
|
user-modified paths. Throws `INSTALLER.UPGRADE_DIRTY_TREE` unless
|
|
`--allow-dirty` or `--force` was supplied.
|
|
- [ ] 3.3 `src/host/upgrade/preflight.spec-conformance.test.ts` —
|
|
`INSTALLER-UPGRADE-04`.
|
|
- [ ] 3.4 `src/host/upgrade/dirty-tree.spec-conformance.test.ts` —
|
|
`INSTALLER-UPGRADE-03`.
|
|
|
|
## 4. Diff engine (`src/host/upgrade/diff.ts`)
|
|
|
|
- [ ] 4.1 `computeDiff(fromOps, toOps)` returning
|
|
`{ added: FileOp[], removed: FileOp[], changed: FileOp[], unchanged: FileOp[] }`.
|
|
- [ ] 4.2 `materialiseDiff(diff, options)` translating the diff into the
|
|
existing `FileOp` union: removals → `{ kind: "delete" }`, additions →
|
|
`{ kind: "write" }`, changes → `{ kind: "write", conflict: "overwrite" }`
|
|
(or `merge` for `patch-json` ops).
|
|
- [ ] 4.3 Sibling `diff.md` covering algorithm and tie-breaking rules.
|
|
|
|
## 5. Migration runner (`src/host/upgrade/migrations.ts`)
|
|
|
|
- [ ] 5.1 `MigrationScript`, `MigrationContext`, `MigrationTable` types in
|
|
`src/host/upgrade/migrations.types.ts`.
|
|
- [ ] 5.2 `loadMigrationTable(plugin)` dynamic-imports the plugin's
|
|
`./upgrade.js` if declared in the manifest; absent table → no-op.
|
|
- [ ] 5.3 `selectMigrations(table, from, to)` returns the ordered semver path.
|
|
- [ ] 5.4 `runMigrations(selected, ctx)` runs each script inside an
|
|
`AbortSignal`-propagating sandbox. Throw → `INSTALLER.UPGRADE_MIGRATION_FAILED`.
|
|
- [ ] 5.5 `src/host/upgrade/migrations.spec-conformance.test.ts` —
|
|
`INSTALLER-UPGRADE-06`, `INSTALLER-UPGRADE-07`.
|
|
|
|
## 6. Upgrade orchestration (`src/host/upgrade/upgrade.ts`)
|
|
|
|
- [ ] 6.1 `Host.upgrade(plugin, options)` method:
|
|
1. Load state-file entry; throw `UPGRADE_NOT_INSTALLED` if absent.
|
|
2. Resolve target version; throw `UPGRADE_VERSION_NOT_FOUND` if
|
|
`node_modules` resolution fails.
|
|
3. Short-circuit with `UPGRADE_SAME_VERSION` when installed === target
|
|
and not `--force`.
|
|
4. Run preflight (lockfile + dirty-tree).
|
|
5. Hash current tree; compute snapshot for rollback.
|
|
6. Run `plugin.plan` for both versions (or load cached bundle manifests).
|
|
7. `computeDiff` → `materialiseDiff` → `Host.applyOps` (existing
|
|
transactional path).
|
|
8. Run migrations between the same staged-tmp transaction window.
|
|
9. On success, `writeState` atomically.
|
|
- [ ] 6.2 OTel span `installer.upgrade` wrapping the whole flow with
|
|
`plugin.id`, `plugin.version.from`, `plugin.version.to`, `dry_run`.
|
|
- [ ] 6.3 `src/host/upgrade/upgrade.spec-conformance.test.ts` —
|
|
`INSTALLER-UPGRADE-01`, `INSTALLER-UPGRADE-02`, `INSTALLER-UPGRADE-10`,
|
|
`INSTALLER-UPGRADE-11`.
|
|
- [ ] 6.4 `src/host/upgrade/apply-diff.spec-conformance.test.ts` —
|
|
`INSTALLER-UPGRADE-05`.
|
|
|
|
## 7. CLI wiring (`src/bin/suaveplan-install.ts`)
|
|
|
|
- [ ] 7.1 Add `app.command("upgrade <plugin>")` with flags `--dry-run`,
|
|
`--force`, `--no-interactive`, `--answers <file>`, `--from <version>`,
|
|
`--to <version>`, `--allow-dirty`.
|
|
- [ ] 7.2 `runUpgrade(pluginId, flags)` mirrors `runAdd` but calls
|
|
`host.upgrade(...)`.
|
|
- [ ] 7.3 `src/bin/suaveplan-install.spec-conformance.test.ts` —
|
|
`INSTALLER-UPGRADE-12`.
|
|
|
|
## 8. Testing helpers (`src/testing/`)
|
|
|
|
- [ ] 8.1 `src/testing/upgrade-plugin.ts` — `upgradePlugin(plugin, { fromState, toBundle, answers? })`
|
|
mirroring the existing `runPlugin` harness for upgrade tests.
|
|
- [ ] 8.2 `src/testing/fixtures/upgrade-fixture-plugin.ts` — two-version
|
|
fixture plugin (`1.0.0` + `2.0.0`) with one migration script, used by
|
|
every upgrade conformance test for end-to-end coverage at 100%.
|
|
|
|
## 9. Docs
|
|
|
|
- [ ] 9.1 Sibling `.md` for every new implementation `.ts` per
|
|
`openspec/AGENTS.md` §4 (Purpose, Features, Basic, Advanced, API, Notes).
|
|
- [ ] 9.2 Update root `packages/tooling/installer/README.md` Quickstart to
|
|
include the `upgrade` verb and link the new module docs.
|
|
|
|
## 10. Quality gates
|
|
|
|
- [ ] 10.1 `bunx turbo lint typecheck test --filter=@suaveplan/installer` green.
|
|
- [ ] 10.2 `bunx turbo test:coverage --filter=@suaveplan/installer` 100%
|
|
istanbul on every new module.
|
|
- [ ] 10.3 No banned patterns (`openspec/AGENTS.md` §2) in new files.
|
|
- [ ] 10.4 No `biome-ignore` in new files (`openspec/AGENTS.md` §2a).
|
|
- [ ] 10.5 File-size check: every `src/host/upgrade/*.ts` ≤ 300 lines,
|
|
every `*.test.ts` ≤ 500 lines.
|
|
- [ ] 10.6 Sibling `.md` audit returns zero gaps for new files.
|
|
|
|
## 11. Dependent sweep
|
|
|
|
- [ ] 11.1 `bunx turbo test --filter=...@suaveplan/installer` green
|
|
(validates `@suaveplan/install-agent-skills` against the new surface).
|
|
- [ ] 11.2 Add empty `upgrade.ts` migration table export to
|
|
`@suaveplan/install-agent-skills` and wire it through its manifest so
|
|
the migration-loader path is exercised even with zero migrations.
|
|
|
|
## 12. Archive
|
|
|
|
- [ ] 12.1 Fold `specs/installer/spec.md` delta into
|
|
`openspec/specs/installer/spec.md`.
|
|
- [ ] 12.2 Move `openspec/changes/add-installer-upgrade/` to
|
|
`openspec/archive/<yyyy-mm-dd>-add-installer-upgrade/`.
|