# Release Process This project ships a conservative Hermes skill layer. A release is ready only when install behavior, generated workflow docs, runtime evidence validation, and public claims are all checked. ## Channels | Channel | Purpose | Install target | | --- | --- | --- | | `stable` | Pinned user installs and support reproduction | Hermes skill tap plus published Git tag archive such as `v` | | `preview` | Latest `main` for early testing | Hermes skill tap plus `main` branch archive | | `local` | Maintainer smoke tests from local fixtures | Explicit local source or package URL | ### Beta releases A beta ships under the same immutable `vX.Y.Z` tag contract; beta-ness rides in the repo at the tag, not in the tag name. Commit `.release-channel` containing `beta` before tagging and the workflow publishes the GitHub release as a **prerelease** (so `/releases/latest` — and with it the stable installer default — keeps resolving the previous stable), publishes npm under the **`beta` dist-tag** (so `latest` does not move), and **skips the Homebrew tap** entirely (brew has no beta lane). Set the file back to `stable` in the commit that cuts the next stable release. Beta users opt in explicitly: `npm install -g oh-my-hermes@beta`, or `OMH_VERSION= sh install.sh`. ## Package-manager distribution The tag-driven npm/Bun, GitHub wheel, and Homebrew tap release is defined by `.github/workflows/release.yml`. Its release order, one-time external setup, resume rules, rollback matrix, immutable artifact checks, and pending-first- release status are the single contract in [Distribution](DISTRIBUTION.md). Releases are cut only when a maintainer asks for one; the *Cut Release* workflow performs the mechanics on demand, and its guards live in the same Distribution contract under "Cutting a release on demand". Package-manager channels therefore advance per release, not per merge — people who want `main` itself use the preview channel below, which needs no version. Those package-manager artifacts extend the stable channel; they do not replace the installer, Hermes skill tap, generated-document, or evidence checks below. Until the external npm namespace and Homebrew tap are created, keep every public package-manager command marked pending and do not dispatch the workflow. Hermes-native skill install: ```sh hermes skills tap add rlaope/oh-my-hermes hermes skills install rlaope/oh-my-hermes/skills/omh-routing --yes ``` Default install, which resolves the newest release through the `releases/latest` redirect *in the installer script* and fetches its published `oh_my_hermes--py3-none-any.whl` asset. The lookup lives in `install.sh`/`install.ps1` rather than in `omh` because core `omh` makes no network calls: ```sh curl -fsSL https://raw.githubusercontent.com/rlaope/oh-my-hermes/main/install.sh | sh ``` Pinned stable install: ```sh curl -fsSL https://raw.githubusercontent.com/rlaope/oh-my-hermes/main/install.sh | OMH_VERSION= sh ``` Because the stable channel names that asset by convention, **a release that publishes no wheel breaks the default install path for everyone**, not just for people who pinned that version. The "Required Checks" wheel steps below are what keep the asset present; do not tag a release that skips them. The same applies to the `latest` pointer: whatever GitHub marks as the latest release must carry a wheel. Preview install, now explicit opt-in, which downloads the full `main` repository archive (measured 46,012,605 bytes on 2026-08-15) because GitHub publishes release assets per tag only: ```sh curl -fsSL https://raw.githubusercontent.com/rlaope/oh-my-hermes/main/install.sh | OMH_CHANNEL=preview sh ``` Preview update with an auditable source ref: ```sh curl -fsSL https://raw.githubusercontent.com/rlaope/oh-my-hermes/main/install.sh | OMH_CHANNEL=preview OMH_SOURCE_REF=main@ sh ``` Custom archive, and the documented fallback for a tag with no published wheel asset: ```sh curl -fsSL https://raw.githubusercontent.com/rlaope/oh-my-hermes/main/install.sh | OMH_PACKAGE_URL=https://github.com/rlaope/oh-my-hermes/archive/refs/tags/v.zip sh ``` Advanced one-shot setup compatibility smoke: ```sh curl -fsSL https://raw.githubusercontent.com/rlaope/oh-my-hermes/main/install.sh | OMH_RUN_SETUP=1 OMH_PROFILE_PACKS=cto-loop OMH_RUN_DOCTOR=0 sh ``` ## Version Bump Surfaces The release bump commit must move every current-version surface together. `src/omh/version.py` (`__version__`) is canonical; the others must match it: | Surface | Why it must move | | --- | --- | | `pyproject.toml` `version` | wheel and package metadata | | `src/plugin_bundle/omh/plugin.yaml` `version` | the installed Hermes plugin manifest — `hermes plugins doctor` and the plugin listing report this value, so leaving it behind under-reports every install (issue #1079: it sat at 1.0.5 through the 1.0.6 and 1.0.7 releases) | | `site/index.html` and `site/i18n.js` `hero.badge` | the landing page's first visible version — the static markup plus all four locale strings (it sat at v1.0.6 through the 2.0.0 release) | `VersionSurfaceParityTests` in `tests/test_release_smoke.py` gates all three: a bump commit that misses one fails the suite instead of shipping the drift. Version strings quoted in README/docs prose and CLI help examples are illustrative and deliberately not parity-gated — update them in the bump commit, but a stale example is a docs nit, not a diagnostics lie. ### Bundled plugin runtime compatibility (maintainer contract) Treat `src/plugin_bundle/omh/plugin.yaml` `requires_hermes` as a separate release-contract surface, not an OMH package-version bump. Keep the floor at the earliest observed supported Hermes version and the ceiling at the next minor after the latest verified host minor. Before widening either bound, record real-host admission evidence in `tests.test_plugin_distribution.PluginHermesAdmissionTests` and update `HERMES_COMPAT_MATRIX` in `src/install/plugin_compat.py` in the same change. Run the named tests and `omh release product-readiness --json`; do not count a skipped real-host fixture or local declaration conformance as runtime evidence. Keep the OMH-owned `requires_hermes` key; do not move it into the host-reserved `hermes:` namespace. For existing installs, run `omh update` to regenerate managed bundle hashes rather than copying plugin files by hand. Check the required versus running Hermes versions in a loader rejection before attempting a manifest repair; use a supported host. For a damaged managed declaration, use `omh setup --force` followed by `omh doctor`. ## Curated release notes (maintainer and automation reference) Author release copy once, under the exact level-two `## Unreleased` heading in `CHANGELOG.md`. Do not maintain a separate GitHub body or generate release copy from commits. A version heading is `## X.Y.Z - YYYY-MM-DD`; fenced examples are content, not headings. Missing, empty or duplicate Unreleased sections, duplicate target versions, invalid dates/UTF-8 and oversized input are refused before publication. Changelogs are bounded to 2 MiB and extracted notes to 256 KiB. Explicit preparation is local-only: no build, tag, GitHub call or publication. Use a staging directory outside the checkout for derived notes: ```sh stage="$(mktemp -d)" uv run python -m omh.cli release prepare --version "$VERSION" \ --repo-root "$PWD" --notes-file "$stage/release-notes.md" --json ``` Preparation renames the nonempty Unreleased section to the target version and current **UTC date**, inserting exactly one fresh empty Unreleased above it. Commit `CHANGELOG.md` with the version surfaces before tagging. Cut Release performs this preparation and stages the changelog before its gates/commit/tag. Historical entries are not rewritten. Notes exclude the version heading and separator blank lines; authored interior Markdown, trailing spaces, fences, quotes, dollar signs and Unicode are preserved, with CRLF normalized to LF and one final LF in the artifact. Copy is passed through files, never shell text. Re-running preparation for the same version preserves the original stamp/date and produces identical notes, provided the target is nonempty and Unreleased is empty. A stamped target plus newly authored Unreleased is ambiguous and is refused without edits: review which release the new copy belongs to first. Each file replacement is atomic, not a two-file transaction. If a run stops after stamping but before writing notes, rerun the same preparation to recover the artifact; do not duplicate or move the stamped text manually. For an immutable tagged checkout, extract without stamping: ```sh uv run python -m omh.cli release notes --version "$VERSION" \ --repo-root "$PWD" --notes-file "$stage/release-notes.md" --json uv run python -m omh.cli release evidence-bundle --version "$VERSION" --write \ --repo-root "$PWD" --notes-file "$stage/release-notes.md" --json ``` New publication evidence requires notes matching that exact source section. The optional closed `release_notes` object in v2 evidence records `schema_version: omh_release_notes/v1`, `path`, `version`, `sha256` and `byte_length`, and is included in the input-manifest digest. `path` is only the resolved notes basename, relative to its parent staging directory; absolute paths and traversal are not publication identity. Verify a saved bundle with its same `--notes-file` (and `--artifact` when a wheel was bound). Legacy v2 evidence still verifies its old source/artifact bindings, but reports `notes_binding: not_recorded` and `publication_ready: false`; local gate status `ready` alone never authorizes publication. Regenerate notes and evidence from the **same immutable tag** before attempting resume. If that historical tag has no authored version section, it cannot meet the new contract: do not backfill its GitHub body or move the tag as part of recovery. Review a new release instead. Rolling back this feature means reverting the tooling and workflow together; retain stamped entries and original evidence, and do not reinterpret notes-bound bundles with an older verifier as notes-verified. Distribution Release uses `gh release create --notes-file`, checks an existing release's decoded body **before any upload**, and checks again after creation or resume. Authentication/network errors cannot select create; absence needs repository access plus a structured release-endpoint 404. Body drift stops the run without `gh release edit`, uploads or downstream publication. Investigate the source/tag/artifact and remote body; resume never silently repairs either. For an explicitly fetched `gh release view --json body` file, local verification is exact (no trimming): ```sh uv run python -m omh.cli release notes-verify --notes-file "$stage/release-notes.md" \ --body-json "$stage/body.json" --json ``` Exit 0 means matching, 1 means body mismatch, and 2 means invalid input. Preparation/extraction also return 2 for invalid input. Local QA exercises real CLI commands and the workflow shell with a process-level `gh` fixture via `tools/qa/seven_issues_release.py --scenario local --output-dir `; it is not GitHub publication evidence. Remove the caller-owned staging directory when done; the runner removes only its own scratch homes and reaps its processes. ## Required Checks Run before tagging: ```sh python3 -m unittest discover -s tests python3 -m compileall src python3 -m omh.cli docs workflows --check python3 -m omh.cli harness validate python3 -m omh.cli release checklist --json python3 -m omh.cli release skill-content-smoke --json python3 -m omh.cli release product-readiness --version 1.0.6 --json python3 -m omh.cli release evidence-bundle --version 1.0.6 --write --repo-root "$PWD" --json python3 -m omh.cli release evidence-bundle --version 1.0.6 --verify --repo-root "$PWD" --json python3 -m omh.cli cases demo --all --json python3 -m omh.cli cases artifact --all --json python3 -m omh.cli cases replay --json python3 -m omh.cli cases readiness --json python3 -m omh.cli demo routing-precision --json python3 -m omh.cli demo router-fast-path --json python3 -m omh.cli --omh-home /tmp/omh-smoke --hermes-home /tmp/hermes-smoke learning review --all python3 -m omh.cli --omh-home /tmp/omh-smoke --hermes-home /tmp/hermes-smoke install --dry-run --channel stable --version 1.0.5 python3 -m omh.cli --omh-home /tmp/omh-smoke --hermes-home /tmp/hermes-smoke setup --dry-run --channel stable --version 1.0.5 python3 -m omh.cli --omh-home /tmp/omh-smoke --hermes-home /tmp/hermes-smoke probe python3 -m omh.cli release hermes-smoke python3 -m omh.cli release install-smoke omh release install-smoke --live --repo-root "$PWD" --install-script "$PWD/install.sh" omh --help omh --omh-home /tmp/omh-smoke --hermes-home /tmp/hermes-smoke release hermes-smoke --install-path setup --omh-command omh --include-command-smoke uv build python3 -m venv /tmp/omh-wheel-smoke /tmp/omh-wheel-smoke/bin/python -m pip install --upgrade dist/oh_my_hermes-1.0.5-py3-none-any.whl /tmp/omh-wheel-smoke/bin/omh --help /tmp/omh-wheel-smoke/bin/omh release skill-content-smoke --json /tmp/omh-wheel-smoke/bin/omh --omh-home /tmp/omh-wheel-home --hermes-home /tmp/hermes-wheel-home release hermes-smoke --install-path setup --omh-command /tmp/omh-wheel-smoke/bin/omh --include-command-smoke /tmp/omh-wheel-smoke/bin/omh --omh-home /tmp/omh-wheel-home --hermes-home /tmp/hermes-wheel-home setup --dry-run --channel stable --version 1.0.5 OMH_PYTHON=/tmp/omh-wheel-smoke/bin/python OMH_PACKAGE_URL=file://$PWD/dist/oh_my_hermes-1.0.5-py3-none-any.whl OMH_VENV_DIR=/tmp/omh-installer-venv OMH_BIN_DIR=/tmp/omh-installer-bin sh install.sh /tmp/omh-installer-bin/omh --omh-home /tmp/omh-installer-home --hermes-home /tmp/omh-installer-hermes setup --dry-run ``` The checklist command renders the same release gates as a deterministic `release_readiness_checklist/v1` contract: ```sh omh release checklist --version 1.0.5 omh release checklist --version 1.0.5 --json ``` It is plan-only: it does not run checks, mutate Hermes, create tags, or publish GitHub releases. Treat it as the operator-facing index of the evidence that must be attached before a stable tag. The checklist also gates the G1-G10 use-case demo cards through `omh cases demo --all --json`. That proves OMH can render wrapper-safe use-case projections with route, action, status-card, and evidence-boundary metadata. It is not evidence that cron, connectors, files, memory updates, executors, reviews, CI, merges, or delivery actually ran. It also gates the G1-G10 use-case artifact bundle through `omh cases artifact --all --json`. That proves OMH can render local prepared runbooks with operator steps and proof surfaces for each use case. It is not evidence that those runbooks were accepted, executed, delivered, reviewed, verified, merged, or billed by any runtime. The same gate replays G1-G10 natural-language use-case fixtures through `omh cases replay --json`. That proves deterministic routing for the checked-in synthetic English/Korean operator corpus. It is not evidence that a live Hermes profile selected the route in chat or that any connector, executor, review, CI, merge, delivery, or billing event happened. The readiness rollup, `omh cases readiness --json`, combines the catalog, demo-card, artifact-bundle, replay, and optional local artifact-store states into one operator-readable card. It should be ready before a release, but it still proves only local deterministic contracts, not live Hermes selection or runtime execution. The grounded routing score checks representative operator requests end to end: ```sh omh demo grounded-score --json ``` It should report every scenario at `10/10`. This catches regressions where a realistic operator message still routes somewhere, but the selected skill, response kind, next action, playbook recommendation, coding boundary, or prepared-vs-observed wording no longer matches the product contract. It remains local deterministic contract-compliance evidence only: it does not prove live Hermes chat rendering, executor execution, review, CI, merge, delivery, or plugin loading. The wrapper chat-card gate checks the user-facing card corpus: ```sh omh demo chat-card-coverage --json ``` It should report every representative workflow route as a dedicated chat card with generic ack count `0`. This catches regressions where Hermes would answer a real operator request with a vague acknowledgement instead of an actionable workflow card. It is still local deterministic wrapper-contract evidence only: it does not prove live Hermes rendering, platform delivery, executor execution, review, CI, merge, or plugin loading. The route-hint alignment gate checks the same public story from the Hermes-awareness side: ```sh omh demo route-hint-alignment --json ``` It should report every grounded-score and chat-card scenario with a primary plugin awareness hint that matches the router-selected workflow. This catches regressions where the chat router chooses the right skill, but the plugin hint returns `no_hint` or points the wrapper at a different workflow. It remains local deterministic router/hint agreement evidence only: it does not prove live Hermes chat rendering, platform delivery, executor execution, review, CI, merge, or plugin loading. The context-brief coverage gate checks the first-turn Hermes mental model that is shown before generic image, file, search, chat, or coding tools: ```sh omh demo context-brief-coverage --json ``` It should report representative visual-summary, catalog-picker, feedback, GitHub issue, paper-learning, source-finder, safe-feature, and research prompts with a metadata-only `omh_context_brief/v1`, matching route hint or picker hint, generic-tool checkpoint, bounded prompt context, and evidence boundary. It is local context-contract evidence only: it does not prove live Hermes chat rendering, plugin load, generic tool invocation, source retrieval, image generation, executor dispatch, review, CI, merge, or delivery. The routing precision gate checks both sides of the router boundary: negative-control prompts where OMH should not open a workflow, and expected intervention prompts where OMH should route to a workflow, picker, or bounded context brief: ```sh omh demo routing-precision --json ``` It should report ordinary file lookup and general-help prompts as direct file lookup or direct chat answers with overroute count `0`, catalog picker count `0`, and generic acknowledgement count `0`. It should also report OMH-shaped requests with missed intervention count `0`, including safe feature planning, source acquisition, image-card preparation, feedback triage, workflow catalog opening, and OMH usage context. This catches regressions where OMH feels pushy for ordinary questions or too passive for requests that should use OMH. It is local routing-boundary evidence only: it does not prove live Hermes chat rendering, source retrieval, file inspection, executor dispatch, review, CI, merge, or plugin loading. The native competition gate checks the generated picker surface against representative native browser, file, shell, and live-information descriptions: ```sh omh demo native-competition --json ``` It should report all ordinary-action cases preferring the representative native skill and all confirmation, safety, freshness, or evidence-boundary cases preferring the OMH policy overlay. The comparison uses only generated frontmatter `name` + `description`, matching the measured picker-visible surface. It is a deterministic lexical regression heuristic, not proof of a live Hermes picker decision, installed native-skill inventory, or market competitiveness. The localized chat-copy gate checks common non-English operator prompts: ```sh omh demo localized-chat-copy --json ``` It should report every localized card case passing across the supported local copy fixtures. This catches regressions where a Japanese, Chinese, Spanish, French, German, or Korean user prompt still routes correctly but falls back to English framing, loses the expected card kind, or changes the next action. It is local copy-contract evidence only: it does not prove live Hermes chat rendering, translation quality, platform delivery, source retrieval, executor dispatch, review, CI, merge, or plugin loading. The router fast-path gate checks common chat turns where perceived latency is most visible: ```sh omh demo router-fast-path --json ``` It should report picker, status, direct-answer, file-lookup, setup health, product issue, coding progress, image-card, scheduled-ops, paper-learning, and source-finder prompts as staying on explicit fast-path route markers. This is a deterministic route-contract guard, not a wall-clock benchmark. It catches regressions where frequent chat turns accidentally fall back to slower full workflow scoring or drift into the wrong next action. The common request coverage gate checks ordinary Hermes-agent request breadth against a curated 95% target corpus: ```sh omh demo common-request-coverage --json ``` It should report every representative common request passing, including planning, coding handoff, research, materials, frontend/design, observability, service-quality, memory, skill, tool, catalog, direct-answer, and file-lookup turns. It also reports a 100-point popular-plugin-family rollup covering search/source, browser/live info, files/shell/workspace, GitHub/coding, documents/slides/PDF/visuals, communications/connectors, ops metrics, data analysis, knowledge/memory, and subagent safety families. This is deterministic local coverage evidence only; the plugin-family rollup is a local heuristic, not live Hermes chat rendering, external plugin telemetry, connector work, executor work, review, CI, merge, delivery, or market-share evidence. The Hermes UX quality rollup checks the chat-first user experience across the routing, card, hint, context, precision, native-competition, localized-copy, fast-path, and common-request coverage rails: ```sh omh demo hermes-ux-quality --json ``` It should report all UX gates passing: grounded natural-language routing, dedicated wrapper cards with generic acknowledgements at zero, route hints aligned with the router, first-turn context briefs with catalog picker coverage, negative-control prompts that stay out of OMH workflows, expected intervention prompts that still enter the right workflow surface, and common non-English operator prompts that keep local card framing, and frequent requests that stay on deterministic fast paths, plus a broad common-request corpus above the 95% target. It is local UX-contract evidence only: it does not prove live Hermes chat rendering, plugin load, platform delivery, generic tool invocation, executor dispatch, review, CI, merge, or delivery. The product readiness rollup sits one level above use cases: ```sh omh release product-readiness --version 1.0.5 --json ``` It checks the generated skill content, G1-G10 readiness, grounded routing score, wrapper chat-card coverage, route-hint alignment, context-brief coverage, routing precision, native competition, router fast-path quality, common request coverage, Hermes UX quality, parity matrix, and release checklist shape in one operator-readable card. It is useful for release notes and maintainer handoff, but it is still local deterministic evidence only: it does not run the checklist, mutate Hermes, dispatch executors, review code, pass CI, merge, deliver messages, or spend provider budget. When the local release story is ready, write an attachable evidence bundle from the exact checkout the release will be cut from. Omitting `--repo-root` binds the current working directory, so the literal maintainer command is: ```sh omh release evidence-bundle --version 1.0.5 --write --json ``` Pass `--repo-root "$PWD"` when the checkout must be explicit. The bundle writes `omh_release_evidence_bundle/v2` to `.omh/runtime/release-evidence/.json` with the checklist, product readiness, skill-content smoke, use-case readiness, grounded score, chat-card coverage, route-hint alignment, context-brief coverage, routing precision, native competition, Hermes UX quality, and parity snapshots, plus a `omh_release_source_identity/v1` block that binds the bundle to the full commit and tree hashes of `--repo-root`, its dirty state, and a deterministic input manifest (repo-relative POSIX paths and `sha256:` digests only). A bundle whose source identity is unavailable — no git checkout and no explicit `--archive-digest` or `--artifact-digest` — is still written for local use but reports `publication_ready: false` and exits non-zero under `--write`. A recorded bundle is re-checked, never regenerated, with either the canonical version path or one exact explicit path. Under the normal default `~/.omh` home, a missing relative `.omh/...` explicit path is interpreted below that configured home, so the literal command below works from a checkout. An existing literal path always wins; other missing relative paths remain missing: ```sh omh release evidence-bundle --verify --repo-root "$PWD" --json omh release evidence-bundle --verify .omh/runtime/release-evidence/1.0.5.json --repo-root "$PWD" --json ``` The verifier is pure: it never writes. Its verdict vocabulary is `matching`, `dirty` (uncommitted changes in the worktree), `mismatched_revision` (the recorded commit differs from HEAD), `stale` (same revision, but the tree, a declared input digest, or the artifact digest changed), `unverifiable` (identity unavailable on either side), `legacy_schema` (a pre-v2 bundle, which can never verify as `matching`), and `missing` (no bundle recorded for the version). Exit code is 0 only for `matching`. Passing `--artifact ` also binds or checks the built artifact's digest. The bundle is useful for release PRs and notes, but it is still local deterministic evidence only; live Hermes smoke, CI, review, merge, delivery, and GitHub release publication must be observed separately. It proves evidence provenance for the recorded revision, not deployment, adoption, or runtime behavior outside the executed gates. The distribution workflow regenerates this bundle from the exact tagged checkout and refuses publication when the binding does not hold; see [Distribution](DISTRIBUTION.md). ## Hermes CLI Install Smoke The release gate includes a deterministic smoke plan for the real Hermes CLI path. Plan mode is safe for CI because it does not touch the current Hermes profile: ```sh python3 -m omh.cli release hermes-smoke ``` The installer gate separately checks the user-facing `curl ... | sh` entry point without depending on curl or GitHub. Plan mode reports the isolated target and remains unobserved: ```sh python3 -m omh.cli release install-smoke ``` Live mode executes the local `install.sh` in a temporary HOME with an isolated OMH virtual environment and bin directory. It installs from the local checkout, does not run setup through the installer, and then runs installed-command smoke. It does not mutate the operator's real Hermes profile or prove a later Hermes chat selected OMH: ```sh omh release install-smoke --live --repo-root "$PWD" --install-script "$PWD/install.sh" ``` The plan includes two release-contract subchecks: - `installed_command_smoke`: first resolves the installed `omh` command path, then proves the console script can run `omh --help` and render the setup-path smoke plan. - `first_use_status_smoke`: documents the first Hermes chat/status path and locks that pre-handoff status cards do not expose executor open/result actions. Run the installed command smoke in CI or a release shell after installing OMH: ```sh command -v omh omh --help omh release skill-content-smoke --json omh release product-readiness --version 1.0.5 --json omh release evidence-bundle --version 1.0.5 --write --repo-root "$PWD" --json omh release evidence-bundle --version 1.0.5 --verify --repo-root "$PWD" --json omh --omh-home /tmp/omh-smoke --hermes-home /tmp/hermes-smoke release hermes-smoke --install-path setup --omh-command omh --include-command-smoke ``` `release skill-content-smoke` is non-mutating package-content evidence. It checks that the command package can render the router awareness primer and the generated workflow context rails that keep direct skill invocation inside the broader OMH model. It also checks all-skill awareness lane coverage, full capability manifest context, bundled role context, standalone plugin capability fallback coverage, playbook capability context, fallback routing/context/boundary fields, bounded prompt context budgets, and bounded capability payload budgets so the shared OMH mental model stays present without becoming prompt bloat or manifest bloat. It does not prove Hermes loaded those skills or selected them in chat. In short, this gate preserves bounded context budgets while still giving Hermes enough OMH workflow context to route well. For release candidates, run exactly one live smoke against the target Hermes profile and paste the JSON result into the release note. Use the native tap path when Hermes skill taps are available: ```sh omh release hermes-smoke --live --install-path tap --target-confirmed ``` Use the bootstrap path when validating the installer-managed `skills.external_dirs` route instead: ```sh omh release hermes-smoke --live --install-path setup --target-confirmed ``` For an isolated smoke profile, bind the target home explicitly instead of confirming the ambient default profile: ```sh omh --omh-home /tmp/omh-smoke --hermes-home /tmp/hermes-smoke release hermes-smoke --live --install-path setup ``` The live smoke runs the selected Hermes install path plus: ```sh hermes skills tap list hermes skills list --enabled-only hermes skills check oh-my-hermes hermes skills inspect rlaope/oh-my-hermes/skills/omh-routing ``` Passing the tap smoke means Hermes CLI install/list/check/inspect commands succeeded for the target profile. Passing the setup smoke means OMH managed skill setup, Hermes list/check visibility, and `omh doctor` succeeded for the target profile. It still does not prove a later Hermes chat session selected OMH unless that chat response is observed separately. Runtime evidence smoke: ```sh run_json="$(python3 -m omh.cli --omh-home /tmp/omh-smoke runtime record --skill oh-my-hermes --harness coding-handling --status started)" run_id="$(printf '%s' "$run_json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["run"]["run_id"])')" python3 -m omh.cli --omh-home /tmp/omh-smoke runtime delegate --run "$run_id" --requested --not-observed --result not_observed python3 -m omh.cli --omh-home /tmp/omh-smoke runtime wrapper --run "$run_id" --prompt-dispatched --response-observed --completion-status completed python3 -m omh.cli --omh-home /tmp/omh-smoke runtime validate --run "$run_id" python3 -m omh.cli --omh-home /tmp/omh-smoke runtime export --redacted ``` Source-checkout console smoke: ```sh uv run --no-editable omh recommend "risky refactor" --limit 1 --json ``` Use `uv run python -m omh.cli ...` for fast module-level development checks. Use `uv run --no-editable omh ...` when the release question is whether the packaged `omh` console script imports and runs from a source checkout. This check is local command importability only; it is not Hermes chat visibility, plugin load, executor dispatch, review, CI, merge, or delivery evidence. ## After the Cut Tagging and publishing move the tag, the GitHub release, and the package registries. **They change no installed machine.** A machine keeps running the version it installed until it updates itself, so the release is not done when the tag exists; it is done when the machines that should run it report it. Every version an operator can see is the *installed* version, never the newest published tag: | Surface | What it reports | | --- | --- | | `omh --version` | the `omh` package on PATH | | `~/.hermes/plugins/omh/plugin.yaml` | the plugin bundle the last install/update wrote | | The Hermes TUI HUD footer (` v2.0.1`) | the version recorded by the last install/update, read by the plugin-bundle reader the widget spawns | The 2.0.1 cut is the worked example: the tag was pushed and npm `latest` moved, and the maintainer's own HUD kept showing `v2.0.0` for as long as the machine kept the 2.0.0 install. Nothing was broken; nothing had been updated. Sync each machine that should run the release: ```sh omh update omh --version # must print the released version omh doctor ``` Then restart the Hermes TUI and confirm the HUD footer shows the new version. The widget reads the installed plugin bundle, so a terminal that was already open keeps rendering the version it started with. Never hand-copy files into `~/.hermes/plugins/` or `~/.hermes/tui-widgets/` to close that gap. Hand-copied artifacts drift from the install manifests and make the next `omh update` refuse or demand `--force`. `omh release checklist` carries this as `machine_sync_after_cut`, a post-release follow-up rather than a pre-tag gate. Machines that opted into `omh update-check set --mode auto` sync themselves on the next bare `omh` launch and print an `OMH Auto Update` line while they do; every other machine syncs only when someone runs `omh update`. See [Installation](INSTALLATION.md), "Startup update check". ## Release Notes Must Include - Release version and channel. - Hermes skill tap/install wording and bootstrap install target used for smoke testing. - Update path tested. - Workflow docs generation status. - Harness catalog validation status. - Source-checkout console script smoke status. - Runtime validation status. - Workflow learning review queue status when workflow-learning contracts changed. - Capability probe status. - Install script smoke status, including whether it was plan-only or live. - Hermes CLI install smoke status, including whether it was plan-only or live. - Plugin bundle status when `omh setup` changed. - GitHub Pages workflow status when public site copy changed. - Known manual Hermes checks that could not be automated. - Machine sync status after the cut: which machines ran `omh update` and what version they now report, or that none has synced yet. - Any public claim that depends on wrapper evidence rather than Hermes-native capability evidence. ## Known Gap Language Use explicit proof-boundary language: - "Prompt-level routing guidance" when only installed skills are involved. - "Wrapper-observed" when evidence comes from a bot or shell wrapper. - "Not observed" when specialist delegation metadata is unavailable. Do not claim native Hermes runtime use from plugin installation alone. `plugin_distribution_ready` means the local bundle exists and passed local import/register smoke; `native_integration_claim_ready` still requires observed Hermes active runtime-load, hook/tool-use, or status-query evidence. Session-end and plugin-unload observations are historical evidence only.