6.4 KiB
6.4 KiB
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_MISSINGto thedefineCodes("INSTALLER", [...])tuple inpackages/tooling/installer/src/error-codes.ts. - 1.2 Extend
src/error-codes.spec-conformance.test.tswith anINSTALLER-UPGRADE-09block asserting the codes are present, frozen, and prefixedINSTALLER..
2. State file (src/state/)
- 2.1
src/state/state.types.ts—InstallerState,InstalledPlugin,FileHashtypes. - 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
.mdsiblings for every implementation file.
3. Pre-flight checks (src/host/upgrade/preflight.ts)
- 3.1
detectLockfile(fs, cwd)returning"bun" | "npm" | "pnpm" | "yarn"or throwingINSTALLER.UPGRADE_LOCKFILE_MISSING. - 3.2
detectDirtyTree(state, currentHashes)returning the list of user-modified paths. ThrowsINSTALLER.UPGRADE_DIRTY_TREEunless--allow-dirtyor--forcewas 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 existingFileOpunion: removals →{ kind: "delete" }, additions →{ kind: "write" }, changes →{ kind: "write", conflict: "overwrite" }(ormergeforpatch-jsonops). - 4.3 Sibling
diff.mdcovering algorithm and tie-breaking rules.
5. Migration runner (src/host/upgrade/migrations.ts)
- 5.1
MigrationScript,MigrationContext,MigrationTabletypes insrc/host/upgrade/migrations.types.ts. - 5.2
loadMigrationTable(plugin)dynamic-imports the plugin's./upgrade.jsif 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 anAbortSignal-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; throwUPGRADE_NOT_INSTALLEDif absent. 2. Resolve target version; throwUPGRADE_VERSION_NOT_FOUNDifnode_modulesresolution fails. 3. Short-circuit withUPGRADE_SAME_VERSIONwhen installed === target and not--force. 4. Run preflight (lockfile + dirty-tree). 5. Hash current tree; compute snapshot for rollback. 6. Runplugin.planfor 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,writeStateatomically. - 6.2 OTel span
installer.upgradewrapping the whole flow withplugin.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)mirrorsrunAddbut callshost.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 existingrunPluginharness 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
.mdfor every new implementation.tsperopenspec/AGENTS.md§4 (Purpose, Features, Basic, Advanced, API, Notes). - 9.2 Update root
packages/tooling/installer/README.mdQuickstart to include theupgradeverb and link the new module docs.
10. Quality gates
- 10.1
bunx turbo lint typecheck test --filter=@suaveplan/installergreen. - 10.2
bunx turbo test:coverage --filter=@suaveplan/installer100% istanbul on every new module. - 10.3 No banned patterns (
openspec/AGENTS.md§2) in new files. - 10.4 No
biome-ignorein 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
.mdaudit returns zero gaps for new files.
11. Dependent sweep
- 11.1
bunx turbo test --filter=...@suaveplan/installergreen (validates@suaveplan/install-agent-skillsagainst the new surface). - 11.2 Add empty
upgrade.tsmigration table export to@suaveplan/install-agent-skillsand wire it through its manifest so the migration-loader path is exercised even with zero migrations.
12. Archive
- 12.1 Fold
specs/installer/spec.mddelta intoopenspec/specs/installer/spec.md. - 12.2 Move
openspec/changes/add-installer-upgrade/toopenspec/archive/<yyyy-mm-dd>-add-installer-upgrade/.