Repository navigation
build: install the toolchain from a locked mise.toml - #40
Merged
Merged
Conversation
CI ran Python 3.11 from astral-sh/setup-uv while laptops ran whatever uv found, so the two never ran the same interpreter. mise.toml now pins Python 3.14.9 (the deployed Hermes interpreter), uv and just, and mise.lock locks them for linux-x64, linux-arm64 and macos-arm64. Every workflow installs them with jdx/mise-action, which installs --locked when a lock is present. The justfile recipes stay the entry points. UV_PYTHON_PREFERENCE=only-system keeps uv on mise's Python: without it uv picks its own managed 3.14 even with mise's Python first on PATH. Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
hermes-plugin-kit, hermes-agent-runtime-image and infra now pin the same uv (0.13.5) and just (1.59.0) through mise, so one toolchain update moves all three. Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
The drift lane fed the classifier only the last 25 lines of the suite log. When both contract modules failed to import the host (No module named 'ruamel'), the ERROR headers were cut, no failure block parsed, and the classifier printed "No contract failures against upstream." and exited 0. These tests use the real captured shape of that log, tail-only and full, plus Ran 0, a missing Ran line, and an unexplained non-zero exit. Five fail on today's classify_drift.py; the full-log case already passes because its ERROR headers survive. The two baseline tests gain a realistic Ran/EXIT trailer, since a log without one is no longer evidence that anything ran. Refs: #40 Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
4 tasks done
classify_drift.py only knew about parsed FAIL/ERROR blocks, so a log whose blocks were missing read as clean. It now also files as new drift: - any "ImportError: Failed to import test module: X", naming X; - no "Ran N tests" line, or Ran 0; - no EXIT= line, or a non-zero EXIT with no failure block to explain it. A run whose only failure is baselined still exits non-zero and still classifies as known. _run-contract-suite now prints the whole log instead of tail -25, since the drift workflow classifies that output. The workflow's step summary keeps its own tail for people. Refs: #41 Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
DEPLOYED_HERMES_REVISION moves from f80f453 to 939e45c9 (v2026.9.11), the revision minilab deploys today. test.yml's hermes-deployed-contract job carries two literals for it (env and checkout ref), because actions/checkout reads the ref before any Python exists. Both the constant's docstring and test.yml said a release-contract test kept those literals equal to the constant. No such test existed: moving the constant alone left `just test` green. A harness test now asserts it, and fails on a constant that moves without the workflow. release.yml reads the constant at run time and compares it with the pinned checkout's HEAD, so it has no literal to move. Refs: #41 Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
The lanes ran the contract modules in the kit's venv, which lacks the host's dependencies, so both modules died importing hermes_cli (No module named 'ruamel'). The host's pyproject.toml alone does not resolve on v0.22.5 either, so its uv.lock is the faithful source (KTD4). provision-hermes-host now runs `uv sync --frozen` from the host checkout into a venv kept in this repository (the checkout is only read), with the first system interpreter satisfying the host's own requires-python, then installs the kit into it with --no-deps. Both lanes call it after the checkout is resolved, so CI's HERMES_AGENT_PATH path is covered too, and _run-contract-suite runs on that venv's python. The deployed pin 939e45c9 requires <3.14, so mise.toml adds Python 3.13.14 (the deployed image's 3.13 line) beside 3.14.9, and mise.lock is regenerated for linux-x64, linux-arm64 and macos-arm64. A scoped UV_PYTHON_DOWNLOADS=automatic was the alternative; it would bypass the locked toolchain the [env] block exists to enforce. Drop 3.13 when the pin moves to v0.22.5 (KTD7). Verified locally: pinned lane at 939e45c9 ran 17 tests, all passing, on 3.13.14. The upstream lane at v0.22.5 now imports the host and fails only on SCHEMA_VERSION (U5), which the classifier reports as new drift. Refs: #41 Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
test-context-engine-contract still ran in the kit venv, so pointed at a real host it hit the same import failure the full lanes just shed. It now provisions the named host through provision-hermes-host first. Against 939e45c9 it ran 3 tests, OK. Refs: #41 Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
Hermes v0.22.5 removed the PLUGIN-COMPAT pointer layer (upstream #126164), so hermes_state no longer re-exports SCHEMA_VERSION and the contract module failed to import against bf0203a7. Import it from hermes_state_common first and fall back to hermes_state for older hosts. SessionDB still comes from hermes_state. Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
The lanes installed the kit into the host env with --no-deps, on the theory that the env should stay exactly the host's lock. The runtime image does not install the kit that way: its Dockerfile and plugin-refresh install it with its dependencies. The pinned host (939e45c9) happened to lock pyyaml, so the gap was invisible there. v0.22.5 dropped PyYAML for ruamel, so plugin_skill raised "No module named 'yaml'" and the upstream lane reported one error. Install the kit with its deps. Packages the host lock already satisfies are left alone; on v0.22.5 the only addition is pyyaml 6.0.3. Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
…comments provision-hermes-host read requires-python with whatever python3 was first on PATH; it now goes through the same uv indirection as every other recipe. Comments that narrated the change, restated code, or carried a plan label from another repository are gone; the ones recording an external constraint stay. Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
A lane whose unittest summary reads OK (skipped=N) exits 0 inside the suite; the lane recipe then fails on its own skip guard, but the drift workflow never appends the outer EXIT line, so the classifier saw a clean run. Cross-model code review (adversarial lens) found the path; it is not reachable today because the contract modules skip only at class level under HERMES_CONTRACT_REQUIRED. Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
Without a version input the action installs the newest mise at least a day old, so two runs of one commit, release jobs holding PRIVATE_ARTIFACTS_TOKEN included, could use different installers. The action keeps verifying the download against mise's signed checksums; a sha256 input would replace that signature check with a plain hash, so only the version is pinned. min_version follows the pin. Claude-Session: https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb
fix(contract): make the contract lanes pass on Hermes v0.22.5
fix(contract): make the contract lanes report what they ran
offendingcommit
marked this pull request as ready for review
October 24, 2026 22:29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Unit U1 of the Hermes v0.22.5 upgrade plan: the kit's toolchain comes from one locked file, so CI and laptops run the same Python.
What changed
mise.tomlpins Python 3.14.9 (the interpreter the deployed Hermes image uses, from upstreampm/lock.json), uv 0.13.1 (same as infra) and just 1.58.0 (the version CI already used).mise.locklocks all three forlinux-x64,linux-arm64andmacos-arm64(mise lock --platform linux-x64,linux-arm64,macos-arm64).test.yml,hermes-drift.ymlandrelease.yml: everyastral-sh/setup-uv(Python 3.11) andextractions/setup-juststep is replaced by onejdx/mise-actionstep, pinned by SHA to v5.2.3. The action adds--lockeditself whenmise.lockis present. In the releasematerializejob the install moves ahead ofjust release-attach-trigger, which needs just.mise.tomlsetsUV_PYTHON_PREFERENCE=only-systemandUV_PYTHON_DOWNLOADS=never. Without them, uv picked its own managed Python 3.14 even with mise's 3.14.9 first onPATH(checked withuv python findboth ways).justfilerecipes stay the entry points; nothing moved to mise tasks.AGENTS.mdand the README Development section now say to install withmise trust && mise install --locked.Verification (macOS arm64)
mise trust && mise install --locked: exit 0.just testunder mise (uv reportsUsing CPython 3.14.9from the mise install):Ran 258 tests,OK (skipped=17), exit 0.origin/mainwith uv's default Python (3.14.7 locally):Ran 258 tests,OK (skipped=17), exit 0. No errors before or after.Not in this PR
DEPLOYED_HERMES_REVISION, the contract lanes and the drift classifier are unchanged (U4).[task_config]or mise tasks, because the justfile remains the task runner.pyproject.tomlrequires-python = ">=3.11"is unchanged.https://claude.ai/code/session_01VDtQ1pDTJksqAcSdrGePVb