Skip to content

Latest commit

 

History

History
245 lines (191 loc) · 16.5 KB

versioning.md

File metadata and controls

245 lines (191 loc) · 16.5 KB

CodeMem Versioning Policy

CodeMem uses one shared semantic version stream across its npm packages.

Canonical packages

  • npm: codemem (CLI)
  • npm: @codemem/opencode-plugin (OpenCode plugin)
  • npm: @codemem/pi-extension (pi coding-agent extension)

Policy

  • Release tags vX.Y.Z represent the product version.
  • npm packages publish the same X.Y.Z.
  • GitHub Release notes are shared per version.

Keep notable changes in the root CHANGELOG.md, following Keep a Changelog: collect pending entries under Unreleased, group them by Added, Changed, Deprecated, Removed, Fixed, or Security, and move them under a dated version heading in the release PR. Omit empty categories, link versions to tag comparisons, and include compatibility or migration details when needed. Do not create per-version release-note documents or copy release announcements into the changelog.

GitHub release announcements remain separate. The release workflow uses GitHub's generated PR list, contributor credits, and comparison link, configured by .github/release.yml; a human summary can be added to the GitHub release after publication. The comparison link shows the changes between tags, not a curated changelog. Retrying an older tag does not require changelog tooling in that tagged checkout.

Release workflow

Version bumps are prepared on main. A maintenance version bump is prepared through a PR into the matching release/X.Y branch and touches these files:

  • packages/core/package.json (version)
  • packages/embeddings/package.json (version)
  • packages/cli/package.json (version)
  • packages/opencode-plugin/package.json (version)
  • packages/pi-extension/package.json (version)
  • packages/mcp-server/package.json (version)
  • packages/viewer-server/package.json (version)
  • packages/core/src/index.ts (VERSION export)
  • packages/core/src/index.test.ts (version assertion)
  • packages/cli/.opencode/plugins/codemem.js (PINNED_BACKEND_VERSION)
  • packages/opencode-plugin/.opencode/lib/runtime.js (PINNED_BACKEND_VERSION)
  • .claude-plugin/marketplace.json (marketplace metadata version and codemem plugin entry version)
  • plugins/claude/.claude-plugin/plugin.json (Claude plugin metadata version and exact MCP --package pins for codemem and @codemem/embeddings)
  • plugins/codex/.codex-plugin/plugin.json (Codex plugin metadata version; also pins the npx -y codemem@<version> fallback used by Codex hook scripts)
  • plugins/codex/.mcp.json (exact MCP --package pins for codemem and @codemem/embeddings)

Both checked-in MCP launchers use the shared release version for both packages, including prereleases. Plugin metadata alone does not select an npm version: unversioned package specs resolve the registry's latest tag, which may still point to the previous stable release. The release helper checks these four pins and repairs unversioned specs even when the manifest already has the target version. It changes only the package specs in launcher arguments; the plain codemem mcp executable, timeouts, and unrelated arguments stay intact. Malformed launchers fail validation before any managed files are written. These pins apply only to the shipped Claude and Codex launchers, not user CLI setup or global configuration.

Use the release version helper to verify or apply the bump:

  • pnpm run release:version -- check
  • pnpm run release:version -- set X.Y.Z

Regenerate release artifacts before opening the release PR:

  • pnpm install (lockfile and generated artifacts when applicable)
  • pnpm build (viewer UI bundle/assets)

Keep .opencode/.npmrc pinned to the public npm registry:

  • registry=https://registry.npmjs.org/

First publication of a new package

npm sets latest on a package's first-ever version regardless of --tag. A new package whose debut is a prerelease therefore exposes that prerelease to untagged installs (npm install <pkg>).

The release workflow cannot fix this automatically: OIDC trusted publishing grants publish only, not dist-tag mutation. Instead, a post-publish step runs scripts/release-latest-guard.mjs in verify mode and reports any package whose latest points at a prerelease. Attempt the cleanup from a machine with an npm login:

node scripts/release-latest-guard.mjs --apply <package>

The guard only removes; it never adds or moves latest, so an established package's stable latest is untouched. npm may reject removal when the prerelease is the package's only latest version; in that case, keep prerelease installs on explicit channel tags until the first stable release moves latest. The verify step is continue-on-error while any package is still dirty; flip it to a hard gate once the guard reports clean.

Release tag preflight

Before creating or pushing a release tag, run:

pnpm run release:preflight-tag

This verifies release tagging safety in two contexts:

  • local preflight accepts a clean main checkout only when HEAD equals the freshly fetched origin/main HEAD. It also accepts a clean release/X.Y checkout only when HEAD equals the freshly fetched origin/release/X.Y HEAD and the package and tag versions share that X.Y series.
  • CI tag workflow accepts a tagged commit that is reachable from remote main, or from the exact remote branch release/X.Y matching the RELEASE_TAG major/minor series. The branch may have advanced since the tag was pushed. When RELEASE_TAG is supplied, its version must equal the core package version at the tagged commit.

Cached remote-tracking refs do not authorize a missing remote branch. Feature branches, unmerged commits, stale branch heads, and release branches for another version series fail preflight. The maintenance branch name is exact: use release/0.47, not release/0.47.0.

For a stable release, merge the release PR into main, verify that HEAD is the merged release commit, and then tag it. For a maintenance release, prepare aligned versions in a PR targeting the matching release/X.Y branch. After the PR passes CI and merges, update that branch locally, run preflight, and tag the merged commit.

After a successful stable vX.Y.0 publication, the workflow creates release/X.Y at the tagged commit when that branch is absent. It never moves an existing branch and does not create branches for patch or prerelease publications. This automation starts with the updated workflow. It does not apply retroactively to existing branches, including the separately created release/0.47 branch.

When a higher stable version is already tagged, an older maintenance release publishes under the npm tag maintenance-X.Y and is not marked Latest on GitHub. For example, after v0.48.0 is tagged, v0.47.4 publishes under maintenance-0.47 so it does not move npm's latest backward. Install that maintenance version explicitly or use its series tag.

Run stable releases one at a time across version series. Finish the newer stable release before tagging an older maintenance release so its workflow sees the newer tag when choosing an npm channel.

Stable publication requires the tagged checkout to contain the history-aware dist-tag helper. A retry of an older stable tag without that helper stops before publishing packages. Prepare a new maintenance release with the updated tooling rather than moving an existing tag.

Release discovery

codemem update check derives the release channel from the installed version and queries the fixed public npm registry endpoint for the matching dist-tag: alpha uses alpha, beta uses beta, release candidates use rc, and a stable version uses latest. Registry responses, status output, guidance, and cache records must match that channel. Results are cached locally for six hours; use --refresh to bypass a fresh cache and --json for the additive automation contract. Existing stable-only cache records remain valid for stable installations, but no cache or in-process result is reused across channels. If a refresh fails, a previously validated same-channel cache may be returned as stale guidance. A running process backs off failed registry checks for 15 minutes, while --refresh bypasses that backoff. codemem update check remains informational. It detects a semver-qualified mise/installs/npm-codemem/<version>/ layout from the resolved CLI entry path, or the equivalent layout beneath a normalized MISE_DATA_DIR, and reports mise use -g npm:codemem@<exact-version> for that installation kind. It also recognizes canonical pnpm global virtual-store entries from resolved paths and reports the exact paired pnpm add -g codemem@<exact-version> @codemem/embeddings@<exact-version> command. Those path markers do not prove pnpm package ownership. codemem update install separately requires fresh validated status. Proven npm-global installations retain the 24-hour first-seen delay and install exact matching codemem and @codemem/embeddings versions with an argv-only npm command. Before mutation, proven mise installations compare bounded machine-readable active and global source records and require current install-path ownership. A recognized user-level ~/.config/mise.toml source may migrate when the bounded global query succeeds with an empty result. The matching source must resolve within the user's home directory; system, unresolved, and ambiguous custom paths fail closed. Matching evidence authorizes codemem to execute the exact reported mise use -g command without a shell, inherited environment, and pinned public default and @codemem registries; Linux also sets ONNXRUNTIME_NODE_INSTALL=skip. This writes the exact release into the primary global config, so a declaration may move from user conf.d or ~/.config/mise.toml to config.toml. Mise verification re-reads bounded global state, requires an exact target version or requested_version value, and runs mise exec -- codemem version outside the invoking project; npm-global verification uses the active CLI. For pnpm-global installations, the explicit installer uses bounded, no-shell pnpm root -g, pnpm bin -g, and pnpm list -g --depth 0 --json calls from a neutral directory immediately before mutation. It requires pnpm root -g to equal the list root (pnpm 12) or its node_modules directory (pnpm 9–11), the canonical registered Codemem package to remain under that list root, and that package to own the resolved JavaScript entry. It then installs exact matching paired packages using public default and scoped registry flags and verifies exact registered package versions plus the specific pnpm-bin launcher. The updater never runs a build-approval command; pnpm 9 still runs install scripts by default, while newer releases apply their configured build-script policy. Bare codemem update remains non-mutating. Installing an alpha, beta, or release candidate is explicit opt-in, so the existing auto-update policy may install a delayed eligible update within that installed channel. Installation refuses pinned, cross-channel, unsupported-prerelease, downgrade, development, stale, Docker, and unknown states. The npm-global updater always installs exact matching codemem and @codemem/embeddings versions. The npm operation runs the packages' installation scripts for native CPU dependencies, just as a manual global install does. Mise remains ineligible for background auto-update because its command changes declarative global tool configuration; only a direct user install command may authorize that change, and a local source that overrides the global source is refused with manual global-update guidance. pnpm-global is also never eligible for plugin background auto-install; it supports notifications and the direct codemem update install path only.

Release discovery compares the running product version with the latest release on its channel. It is separate from the compatibility-floor check below: discovering a newer release does not change whether the current CLI satisfies the plugin's minimum supported version.

Compatibility-floor check

The OpenCode 1 plugin performs a runtime CLI version check and warns if the local CLI is below CODEMEM_MIN_VERSION (default 0.9.20).

The compatibility reaction is controlled by CODEMEM_BACKEND_UPDATE_POLICY:

  • notify (default): warn with an upgrade hint
  • auto: attempt a best-effort same-channel update for eligible npm runners and delayed releases, then re-check
  • off: suppress compatibility toasts

This check enforces a minimum supported CLI version. It does not query the npm registry or report the latest available release, and its existing policy and update behavior are unchanged by release discovery.

OpenCode host compatibility

Codemem 0.45 raises the minimum supported OpenCode 1 host to 1.18.29. The @codemem/opencode-plugin package records this floor through engines.opencode, which OpenCode checks when loading npm plugins, and CI runs the V1 plugin suite against that exact SDK release. The packed-artifact smoke also installs OpenCode 1.18.30 and requires the dual entrypoint to complete the host's non-pure startup path. The package default export changes from a callable V1 plugin function to the documented dual-host object; named V1 exports remain available for integrations that invoke the function directly. This is a breaking host-compatibility change from Codemem 0.44; upgrade OpenCode before installing the 0.45 plugin.

Dependabot continues to propose SDK updates, but accepting one requires updating all checked-in SDK pins together. The published dependency and both nested runtime manifests use the same exact SDK version; that version can be newer than the supported host floor. CI explicitly installs the minimum SDK with --no-save and keeps the minimum host gate and engines.opencode fixed at 1.18.29 until the documented compatibility floor changes. The ignored .opencode/package.json is only a local contributor runtime and is not a release pin. Refresh it when testing the minimum host locally:

npm install --prefix .opencode --save-exact @opencode-ai/plugin@1.18.29

The same 0.45 package also runs on OpenCode 2 through its setup() entrypoint. Codemem validates that path against the exact stable @opencode/cli@2.0.24 and @opencode/plugin@2.0.24 releases and labels its OpenCode 2 integration beta. codemem setup --opencode-only keeps writing the singular plugin key because OpenCode 1 requires it and OpenCode 2 translates it. Required CI gates run only against the 1.18.29 floor and the exact 2.0.24 pin; any compatibility run against a newer or moving OpenCode release is advisory and never blocks a Codemem change until the pin moves in its own reviewed PR. Rollback is a host-edge change: disable the V2 path with CODEMEM_PLUGIN_IGNORE=1 or return to OpenCode 1 without any database migration.

The OpenCode 2 contract separately pins matching @opencode/cli@2.0.24 and @opencode/plugin@2.0.24 packages. The workspace allows the CLI package's postinstall because it installs the matching platform binary used by the packed-host smoke test. The 2.0.24 postinstall is byte-identical to the independently reviewed 2.0.2 script. It selects an exact optional platform package, detects AVX2 support with sysctl on macOS or PowerShell on Windows, and hard-links or copies its binary. If the optional package is absent, it installs that exact package in a temporary directory before copying and verifying the binary. Re-review the postinstall whenever the exact pin changes. Update both revisions together and rerun the executable checks documented in the OpenCode 2 contract; do not replace these pins with a moving tag or semver range.

The approved minimum-release-age exception covers only the exact OpenCode 2.0.24 packages listed in pnpm-workspace.yaml. Remove those exceptions once the versions clear the configured release-age window.

Override for testing:

export CODEMEM_MIN_VERSION=0.9.20

Pi host compatibility

Codemem requires Pi 1.1.0 or newer, below 2.0. The @codemem/pi-extension package records this floor as its declared host peer (@earendil-works/pi-coding-agent >=1.1.0 <2). This is a breaking host-compatibility change from the previous >=0.84.1 <1 peer: Pi 0.x hosts must upgrade before installing. pi update replaces the Pi host; pi update --extensions only reconciles installed packages. A Nix installation cannot update itself with pi update; use the official installer flow or nix profile upgrade pi. Native tools stay the setup default with 15 memory_* tools, and the explicit native and mcp-adapter modes are unchanged.

Transition notes

  • codemem is the CLI package on npm.
  • @codemem/opencode-plugin is the OpenCode plugin identifier.