Skip to content

Recognise the Hermes config this installer wrote - #684

Merged
MongLong0214 merged 2 commits into
mainfrom
fix-682-hermes-upgrade
Aug 15, 2026
Merged

Recognise the Hermes config this installer wrote#684
MongLong0214 merged 2 commits into
mainfrom
fix-682-hermes-upgrade

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Closes #682.

install.sh v1.0.0 exited 1 on a machine that had v0.8.2:

could not configure: mcp_servers.commitlore already exists but does not
point at this CommitLore install

The entry pointed at exactly the right wrapper. It was matched as five exact
lines, and the config on disk wrote args: [mcp] rather than a block list — so
a formatting difference read as a foreign installation.

Two defects from one comparison

Every upgrade was refused. The version lives in skills.external_dirs, which
goes stale each release, so hermes install would fail on every future version
too. Five other hosts updated fine; Hermes alone could not recognise its own
work. Worth noting what did survive: .mcp.json for ACP pins nothing and came
through the upgrade untouched.

The refusal named the wrong key. command was correct. Anyone following that
message inspected the one value that was right and had nowhere to go — a true
observation carrying a false name.

What changed

The entry is read field by field:

  • flow and block args are the same args
  • a user who deleted enabled: true has not stopped using this install
  • the refusal names the key that actually differs, with both values
was   mcp_servers.commitlore already exists but does not point at this CommitLore install
now   mcp_servers.commitlore.command is "/opt/other/commitlore", not this install ("/…/bin/commitlore")
now   mcp_servers.commitlore.args is ["mcp","--verbose"], not ["mcp"] — left unchanged rather than overwritten

Not a YAML parser. It reads the fields this installer writes and treats
anything structurally unfamiliar as unreadable rather than foreign — the same
distinction between "I could not tell" and "this is not mine" the rest of the
product makes. A blocked entry is still never overwritten.

Verified

test/hermes-upgrade.test.ts, six cases built from the config that actually
failed. Negative control: restoring the exact-line match makes the flow-style
cases fail. Existing Hermes tests unchanged and passing — 13 total.

Canonical artifact b29745c3.

Installing v1.0.0 over v0.8.2 failed with "mcp_servers.commitlore already exists
but does not point at this CommitLore install" while the entry pointed at
exactly the right wrapper. The entry was matched as five exact lines, and the
config wrote its args in flow style -- args: [mcp] rather than a block list --
so a formatting difference read as a foreign installation.

Two defects from one comparison. Every upgrade was refused, because the version
lives in skills.external_dirs and goes stale each release. And the refusal named
command, which was correct, sending whoever read it to inspect the one value
that was right.

The entry is now read field by field. Flow and block args are the same args, a
user who deleted enabled: true has not stopped using this install, and the
refusal names the key that actually differs with both values in it.

Not a YAML parser. It reads the fields this installer writes and treats anything
structurally unfamiliar as unreadable rather than foreign -- the same
distinction between "I could not tell" and "this is not mine" that the rest of
the product makes.

Verified by restoring the exact-line match: the flow-style cases fail.

Limit: recognition is by field, never by formatting
Blast: module
Undo: easy
Certainty: firm
Provenance: authored
Record-Id: r-682herm
@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 2 commits in origin/main..e6fc9b2ae6f2e9cad53980b52cee1cc37798204c
Active constraints: not read — commitlore: git log --follow accepts exactly one pathspec, so renames are not followed for 6 paths; query one path at a time to follow its rename chain (6 changed paths)

Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR.

@MongLong0214
MongLong0214 merged commit 2e64424 into main Aug 15, 2026
12 checks passed
MongLong0214 added a commit that referenced this pull request Aug 15, 2026
installedPath resolves against the bundle that is executing. That is right when
the bundle is the installed one, and wrong the moment it is not: running a
checkout's dist/ while --data-root pointed at the real installation wrote the
checkout's path into a permanent Hermes config.

The damage is not a missing file today. It is a config bound to a tree that will
be deleted or switched, after which the skills vanish while mcp_servers stays
valid -- the half-configured state #684 was about, reached through a different
door. The same wrong-name refusal would follow.

So when the data root holds this version's skills, that is the answer. The
bundle's own location stays as the fallback for the case it was always correct
for, an installation invoking itself, and an explicit --skills-dir still wins.

Found while verifying #684 against a real Hermes profile, which is the only place
it could have been found: every test until now supplied skillsDir explicitly and
so never exercised the derivation.

Verified by restoring the old resolution -- the data-root case fails.

Limit: a permanent config never records a path that belongs to one invocation
Blast: module
Undo: easy
Certainty: firm
Provenance: authored
Record-Id: r-686skil
@MongLong0214 MongLong0214 mentioned this pull request Aug 15, 2026
MongLong0214 added a commit that referenced this pull request Aug 15, 2026
Five fixes have been sitting on main since v1.0.0 and none of them has reached
anyone. They are all defects at the install and distribution boundary, and that
boundary has exactly one delivery mechanism: a release.

    #683  the release workflow failing on a release that already exists
    #684  hermes install refusing the config it wrote
    #687  the skills root taken from the running bundle instead of --data-root
    #688  an installed plugin treated as a reason to stop rather than upgrade
    #690  claude-code wired by nothing, in neither hosts nor notDetected

Measured rather than assumed: re-installing with main's install.sh still left the
plugin cache at 0.8.0 and claude-code out of both lists, because the enumeration
that #690 fixed is compiled into the binary being installed -- and that binary is
v1.0.0.

dist is unchanged and the canonical digest is identical to before the bump. The
version is read from package.json at runtime rather than compiled in. Rebuilt
through the canonical builder and verified rather than assumed, the same as
v1.0.0.

Thirty-eight pins across nine files. The installers and READMEs carry the tag in
URLs that resolve only once the tag exists, so this is one commit and the tag
goes on it.

This is the first release cut under release-gate 6c, which asks for an upgrade
over the previous version rather than a fresh install. The five fixes above are
why that section exists.

Limit: a distribution-boundary fix reaches nobody until it is released
Blast: system
Undo: costly
Certainty: firm
Provenance: authored
Record-Id: r-rel101
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hermes install refuses to upgrade the config it wrote, and blames the wrong key

1 participant