src/web/CLAUDE.md gets the load-bearing rules (hand-maintained file, strictly-true availability, one surface per notice per load, seen only on a user action, no CLI-id branching, theme tokens only), and docs/releasing.md a section on adding a notice, its fields, and letting `expires` retire it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
8.6 KiB
Releasing Codeman (the COM procedure)
Moved from the root
CLAUDE.md, which keeps a pointer here. What SemVer covers is defined indocs/versioning-policy.md. The root file's Session Safety rules (branch check, explicit paths, nevergit add -A) apply to every step.
When user says "COM":
-
Determine bump type:
COM= patch (default),COM minor= minor,COM major= major -
Create a changeset file (no interactive prompts). Write a
.mdfile in.changeset/with a random filename:cat > .changeset/$(openssl rand -hex 4).md << 'CHANGESET' --- "aicodeman": patch --- Detailed description of ALL changes since last release (not just the most recent commit — review full git log since last version tag) CHANGESETReplace
patchwithminorormajoras needed. Include"xterm-zerolag-input": patchon a separate line if that package changed too. -
Consume the changeset:
npm run version-packages(auto-bumpspackage.jsonfiles, updatesCHANGELOG.md, runsnpm install --package-lock-only, and verifies lockfile sync viascripts/check-lockfile-sync.mjs— all in one command; never hand-editCHANGELOG.mdorpackage-lock.jsonversions. It also writessrc/web/public/whats-new.json(scripts/generate-whats-new.mjs): the bold lead-in of each change in the newCHANGELOG.mdsection, which the browser announces once per version in a toast. Commit it with the release;test/whats-new.test.tsfails when its version differs frompackage.json, so a hand bump needsnode scripts/generate-whats-new.mjs && npx prettier --write src/web/public/whats-new.jsontoo. A release whose notes have no bold lead-ins ships an empty list and shows no toast) -
Sync CLAUDE.md version: Update the
**Version**line in the rootCLAUDE.md(under COM Shorthand) to match the new version frompackage.json -
Commit and deploy: verify the branch first (
git branch --show-current), then stage EXPLICIT paths — nevergit add -A, which has swept another session's WIP into a release.git status --shortand account for every line before committing:git add <paths> && git commit -m "chore: version packages" && git push && npm run build && systemctl --user restart codeman-web -
Refresh the getcodeman.com version badge: the landing page's status bar carries the release version (
v<x.y.z> · getcodeman.com · MIT), so it goes stale on every release if nobody bumps it. The site source and its deploy script are maintained outside this repository, on the maintainer's machine only; follow the local site handbook there, which also covers the numbers strip andsitemap.xmlrefresh that belong in the same pass. Poll production (curl -s https://getcodeman.com/ | grep v<x.y.z>) before calling it done, since the edge lags a deploy by up to a minute. Not applicable to contributor clones — skip it and say so. -
Wait for CI: after
git push, TWO workflows fire per master push —CIandRelease(the npm publish + GitHub release). List both runs for the pushed commit withgh run list --commit $(git rev-parse HEAD) --json databaseId,workflowNameand watch EACH withgh run watch <id> --exit-status. Confirm both pass before considering the release done (gh run list -L 1returns only one of the two). -
Publish the new version to the MCP registry:
codeman mcpis listed in the official MCP registry asio.github.Ark0N/codeman, and the registry does not follow npm, so every release is published there by hand or the listing stays on the old version.npm run version-packageshas already written the new version intoconfig/mcp-registry/server.json(scripts/sync-plugin.mjs). Wait until npm serves it (npm view aicodeman version; it can lag the greenReleaserun by several minutes), because the registry checks that the npm package exists and carriesmcpName: io.github.Ark0N/codeman. Then runcd config/mcp-registry && mcp-publisher publish, preceded bymcp-publisher login githubwhen the saved login has expired, signed in as the account that owns theio.github.Ark0N/namespace. Use mcp-publisher 1.8.1 or newer (mcp-publisher --version), the release binary from https://github.com/modelcontextprotocol/registry/releases: the Snap Store package is stuck at 1.1.0 and rejects the current$schemawith a false "deprecated schema detected", so never editserver.jsonfor that error. Older publishers also save their login as.mcpregistry_github_tokenand.mcpregistry_registry_token(live tokens) in the current directory; move those out of the repo, never commit them. Confirm withcurl -s 'https://registry.modelcontextprotocol.io/v0/servers?search=io.github.Ark0N/codeman' | jq -r '.servers[] | "\(.server.version) latest=\(._meta["io.modelcontextprotocol.registry/official"].isLatest)"': the new version must be the one withlatest=true. Maintainer only (it needs that GitHub account); contributor clones skip it and say so. -
Announce it in Discussions: every release gets a post in the Announcements category, shaped like #418 and #302: the short version first (one bold lead-in per theme, features before fixes, what it does for the user rather than how it works), contributor @-mentions inline where their work is described (a mention notifies them, and a contributor reposting is the cheapest reach this project has), a link to the release, and the Thanks names at the end. Casual first-person voice, no em-dashes, humanizer pass when the skill is available. A same-day follow-on patch (1.28.1 after 1.28.0) is folded into the previous post as an edit, never a second thread. Post it with
gh api graphql -F body=@<file> -f title='Codeman <x.y.z>: <hook>' -f repo=R_kgDOQ-SMDg -f cat=DIC_kwDOQ-SMDs4DCHZE -f query='mutation($repo:ID!,$cat:ID!,$title:String!,$body:String!){createDiscussion(input:{repositoryId:$repo,categoryId:$cat,title:$title,body:$body}){discussion{number url}}}'(the repo's node id and its Announcements category id;pinDiscussiondoes not exist in the API, so pinning stays a click in the UI). This step exists because announcements stopped at 1.18 (#302) while ten releases shipped with nobody notified; #418 is the backfill covering 1.19.0 to 1.28.1, and release notes only count as content once they reach a surface people are subscribed to.
CI runs npm run check:lockfile on every push/PR, so lockfile drift fails the build even if the version-packages script is bypassed.
Security notices
A security notice is a sticky warning card about something outside Codeman that its users should act on (the first: OpenCode's Console breach, October 2026). On a browser that gets the release's update toast it appears inside that toast instead, so nobody sees it twice. Notices live in src/web/public/security-notices.json, which is maintained BY HAND; never put one in whats-new.json, which npm run version-packages rewrites. A notice reaches users only with a release, so add it before the COM that should carry it.
Adding one: append an entry to notices:
{
"id": "opencode-console-breach-2026-10",
"severity": "warning",
"cli": "opencode",
"published": "2026-10-18",
"expires": "2026-12-31",
"title": "OpenCode Console breach: check your API keys",
"message": "One or two plain sentences: what happened and what to do.",
"link": { "label": "Read OpenCode's notice", "url": "https://..." },
"i18n": { "zh-CN": { "title": "...", "message": "...", "label": "..." } }
}
idis stable and unique, and never reused: a browser that closed a notice remembers its id (codeman-security-notices-seenin localStorage), so a reworded notice under the same id is NOT shown again to anyone who closed it. Give a materially new warning a new id.cli(optional) is a registry CLI id. The notice is then shown only on a server where that CLI is installed and enabled; withoutcliit is shown to everyone.publishedandexpiresare inclusiveYYYY-MM-DDdays in the viewer's local time.expiresis what keeps an old notice from alarming a browser that first opens Codeman months later, so always set one.title(optional) is the card's bold line; keep it short.link.urlmust be https.i18nis optional; a missing string falls back to the English one.- Run
npx prettier --write src/web/public/security-notices.jsonandnpm test -- test/security-notices.test.ts, which validates the file (unique ids, real dates, https links, everyclia stock registry id, a zh-CN title wherever there is an English one).
Retiring one: let it expire, or delete the entry once it has. Removing it early stops it on the next release only; browsers already showing it keep the card until they close it or reload.