Skip to content

v8.5.0 - A cloned Hermes profile drifts forever

Choose a tag to compare

@ShugokiFable ShugokiFable released this 25 Aug 22:50
· 3 commits to main since this release

hermes profile create --clone-from default copies default once. The migration managed mcp_servers and nothing else, so nothing ever re-converged the copies. Measured on a live machine at v8.4.0:

default   fallback_providers: laguna-s / inkling / inkling-small / laguna-xs
roblox    fallback_providers: dots-3-note-preview:free, openrouter/free
skyrim    fallback_providers: dots-3-note-preview:free, openrouter/free

roblox and skyrim were still running the chain from the day they were cloned.

A fallback chain is only consulted when the primary is already failing. That is what makes this drift nasty: nothing looks wrong, no command reports an error, and the stale value surfaces at the one moment it is supposed to save you. MCP topology was correct in all three profiles the whole time, which is why every run reported "Hermes profiles already match the target architecture".

What changed

The migration now converges fallback_providers and model.aliases across every managed profile, planned before the no-op check — otherwise correct MCP topology keeps masking drifted preferences.

Two fields, two rules, because the risk differs:

Field Rule
fallback_providers Replaced only when absent, empty, or byte-equal to a chain this pack itself shipped. A chain you chose is reported under KEPT and left alone.
model.aliases Additive via setdefault. An alias you already defined keeps your value.

Both are read back and verified after writing.

The starter shipped no fallback chain at all

Only a commented-out example — so a BYOK user had no failover until they wrote one by hand, and every cloned profile inherited the gap. It now ships a four-deep free chain:

poolside/laguna-s-2.1:free -> thinkingmachines/inkling:free
  -> thinkingmachines/inkling-small:free -> poolside/laguna-xs-2.1:free

Ordering is not arbitrary. laguna-s is text-only, so a vision task failing over to it loses its images; inkling takes text/image/audio and sits directly behind rather than at the bottom. A contract asserts that position, and another refuses to point the vision auxiliary at a text-only model.

Verified, not recalled

Ten new model aliases and the whole README cost ladder were checked against the live openrouter.ai/api/v1/models list — ids, prices, context windows and input modalities. Contracts keep README and starter honest in both directions: the README cannot advertise an alias the starter does not ship, and the starter cannot ship one the README never explains.

auxiliary.compression deliberately stays on a paid model: it ingests up to the 160000-token threshold and the free first choice tops out at 262K context. ~$0.006 per compression beats one truncated summary. The existing cost contract caught this when it was briefly switched to free.

A new contract also refuses to publish machine state in the starter: absolute user paths, localhost: endpoints, a local LM Studio provider, non-empty mcp_servers, or any non-ASCII byte.

Also

One bug fixed before shipping: ConvertTo-UabsPlain returns ,$items so an array survives being returned intact, and re-wrapping it in @() nested the whole chain one level deep — every fallback entry then read as Object[] and the ledger reported "user choice" for chains it should have migrated. Caught by dry-running against real profiles rather than trusting the plan.

83 release contracts (4 new, 11 mutations falsified), pack gate PASS, 5157 files manifested, 18/18 CI checks green on c445242.