Skip to content

usm/upgrade [built]

usm upgrade — detect stale USM projects and guide users through adopting new optional capabilities (like the feedback policy) via a self-describing capability registry. Compares the installed USM version against system.usm.version, reports missing/recommended capabilities, offers interactive or default setup, and bumps the project version on completion.

Status: built

Intent

There is no upgrade path today. Existing users who npm update USM get new code, but their system.usm is untouched — new optional blocks (feedback, and future ones) are silently absent, generate omits the corresponding output, and the user gets no signal. The only version check is a passive $version warning in validate. This feature adds a capability registry where each optional block declares how to detect and set itself up, plus an usm upgrade command that orchestrates detection, guided setup, and version bumping — so adoption of new features is discovered and guided, not silent.

Decisions

capability-registry-pattern [accepted]

Decision: Capabilities self-describe (detect + setup + introducedIn); upgrade orchestrates without hardcoding.

Rationale: Adding a future capability means one registry entry, zero changes to upgrade. Same extensibility principle as the generators target list.

usm-version-field-for-alignment [accepted]

Decision: Use system.usm.usm_version for USM-tool alignment, NOT the project's own version field.

Rationale: The version field tracks the consuming project's own release (e.g. 5.2.0). Repurposing it would miscompare against the USM tool version. A dedicated usm_version field keeps project version and tool alignment separate and unambiguous.

Alternatives considered:

  • Reuse the existing version field for alignment — rejected: Overwrites the consuming project's own release version; a project at 5.2.0 would be misread as 'ahead of' USM 0.1.0 and never offered upgrades.
  • Use $version (schema format) for alignment — rejected: Conflates schema-format changes with feature additions; additive schema changes (like feedback) intentionally do not bump $version.

schema-version-independence [accepted]

Decision: Schema $version moves independently of the package version: additive schema changes do NOT bump $version (still v1); breaking changes bump $version + CURRENT_SCHEMA_VERSION together and ship a migration in usm upgrade.

Rationale: Decouples the file format from the tool release cadence. A 0.2.0 release may still be schema v1. Additive changes (new optional fields) are backward compatible and must not force every consumer to migrate.

setup-owned-by-capability [accepted]

Decision: Each registry entry owns its own setup function (interactive + default); upgrade just calls it.

Rationale: Avoids subprocess fragility (no need for the binary on PATH) and keeps each capability's setup logic co-located with its detection.

interactive-with-flag-fallback [accepted]

Decision: TTY prompts per capability; --apply uses defaults for CI/scripts; --check is report-only.

Rationale: Composes with existing per-feature setup commands (e.g. usm feedback) while supporting non-interactive and CI use cases.

non-destructive [accepted]

Decision: Upgrade only adds missing blocks and bumps usm_version; existing config is never overwritten.

Rationale: detect() returns true for configured capabilities → they are skipped. Safe to re-run idempotently.

Flows

Detect project version and missing capabilities (upgrade-detect)

Read the installed USM version and the project's system.usm.version, walk the capability registry, and produce a status report.

  1. read → installed USM version from package.json (resolved relative to the compiled module)
  2. read → project version from system.usm.version (absent = 0.0.0)
  3. compare → semver compare — stale if project < installed
  4. walk → capability registry — call detect(system) for each entry
  5. report → list missing (with recommended flag + setup hint), up-to-date, and version status

Set up missing capabilities (upgrade-setup)

For each missing capability, either prompt interactively (TTY) or apply defaults (--apply), calling the capability's own setup function.

  1. branch → interactive (TTY) vs --apply (defaults) vs --check (report only)
  2. prompt → for each missing recommended capability, ask whether to set it up
  3. setup → call capability.setup(systemPath, { interactive }) — e.g. feedback writes its policy block
  4. skip → capabilities where detect() returns true are never re-set-up

Bump project version after applying (upgrade-bump)

After capabilities are applied, set system.usm.version to the installed version, validate, and suggest regenerating outputs.

  1. write → system.usm.version = installed version
  2. validate → system.usm still passes schema validation
  3. suggest → run 'usm generate' to refresh rules files and docs with the new capabilities

Contracts

version-compared

Upgrade reads the installed package.json version and system.usm.version and reports stale or up-to-date.

Acceptance criteria:

  • [ ] Installed version resolved from the USM package.json (not the project's)
  • [ ] Absent system.usm.version treated as 0.0.0 (stale)
  • [ ] Stale vs up-to-date clearly reported

capabilities-detected

Every registry capability is checked; missing and recommended ones are reported with a setup hint.

Acceptance criteria:

  • [ ] All registry entries run through detect()
  • [ ] Missing recommended capabilities surfaced prominently
  • [ ] Each missing capability shows its setup hint

setup-non-destructive

Setup never overwrites an existing block.

Acceptance criteria:

  • [ ] detect() returns true → capability skipped entirely
  • [ ] No existing config field is overwritten

version-bumped-on-completion

After a successful apply, system.usm.version is set to the installed version.

Acceptance criteria:

  • [ ] version field written only after setup succeeds
  • [ ] Resulting system.usm validates against the schema

non-interactive-safe

Non-interactive modes work without prompts and are CI-safe.

Acceptance criteria:

  • [ ] --apply runs all recommended missing capabilities with defaults, no prompts
  • [ ] --check reports only and exits non-zero if stale
  • [ ] Non-TTY defaults to report-only (no hanging on stdin)

Tests

stale-project-detected

Given:

  • system_version_absent: true

Then:

  • assertion: upgrade reports project as stale
  • assertion: exit code non-zero under --check

up-to-date-noop

Given:

  • system_version_equals_installed: true
  • all_capabilities_configured: true

Then:

  • assertion: upgrade reports nothing to do
  • assertion: no files modified

missing-capability-reported

Given:

  • system_has_no_feedback_block: true

Then:

  • assertion: feedback listed as missing
  • assertion: setup hint shown

existing-capability-skipped

Given:

  • system_has_feedback_block: true

Then:

  • assertion: feedback not offered for setup

apply-uses-defaults

Given:

  • system_has_no_feedback_block: true
  • flag_apply: true

Then:

  • assertion: feedback block written with default human-gate policy
  • assertion: no prompt blocks on stdin

version-bumped-after-apply

Given:

  • apply_succeeds: true

Then:

  • assertion: system.usm.version equals installed version
  • assertion: system.usm validates

Implementation

  • Primary: src/scan/upgrade.ts; src/scan/capabilities.ts; src/cli/index.ts
  • Test code status: none

See Also

  • usm/agent-feedback
  • usm/cli-init
  • usm/cli-check
  • usm/cli-validate