fix(serviceoffer-controller): render Hermes config from the Agent CR + hash-skip to stop the resync-wipe#728
Open
bussyjd wants to merge 2 commits into
Open
fix(serviceoffer-controller): render Hermes config from the Agent CR + hash-skip to stop the resync-wipe#728bussyjd wants to merge 2 commits into
bussyjd wants to merge 2 commits into
Conversation
…s from Agent CR Additive AgentSpec fields (modelProvider, mcpServers, maxTurns, disabledToolsets) threaded through renderHermesConfig so the CR is the source of truth for the agent's inference provider and paid MCP wiring. Unset fields render byte-identical to the prior fixed template (no CRD version bump, no behavior change for existing agents). Claude-Session: https://claude.ai/code/session_01VquWN9UMaSHH7MHGcG8bw1
…rift condition Gate the per-agent hermes-config ConfigMap write on a content-hash annotation (obol.org/hermes-config-hash) stored on the live ConfigMap. Skip the apply when the freshly-rendered desired hash matches the stored annotation — so an unchanged config is not rewritten on every reconcile or controller restart. This ends the every-'obol stack up' re-provision that silently wiped operator config (now CR-driven after b7b1bf4). The skip decision is purely desiredHash == storedAnnotation, never desired-vs-live-content, so operator edits are not treated as drift to revert. Out-of-band ConfigMap edits are surfaced via a new ConfigDrift status condition instead of being clobbered. Annotation lives on the persistent ConfigMap so the skip survives controller-pod restarts. Claude-Session: https://claude.ai/code/session_01VquWN9UMaSHH7MHGcG8bw1
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.
Closes #727.
Makes the Agent CR the source of truth for per-agent Hermes config and stops the controller rewriting it on every reconcile/restart — the recurring wipe that reverts an agent's LLM provider, MCP wiring, and turn cap (and recently took a paying endpoint down with a 502 after an upgrade).
Changes
A — Additive CR fields + render passthrough (
b7b1bf4)AgentSpecgains optionalmodelProvider,mcpServers,maxTurns,disabledToolsets(newMCPServertype).model stringis untouched — no CRD version bump, no conversion.renderHermesConfignow honors them: non-customproviders omit the litellmbase_url/api_key; anmcp_servers:block is emitted fromspec.mcpServers; turns/toolsets fall back to today's literals.TestRenderHermesConfig_UnsetFieldsByteIdentical) — zero change for existing agents.zz_generated.deepcopy.goregenerated viacontroller-gen.B — Hash-skip (
9a347d4)buildAgentConfigMapstampsobol.org/hermes-config-hash(sha256 of the renderedconfig.yaml) on the ConfigMap.provisionAgentgets the live ConfigMap and, when the freshly-rendered desired hash equals the stored annotation, skips the apply. The decision is purelydesiredHash == storedAnnotation(pure functionhermesConfigDecision), never desired-vs-live-content — so operator edits are not treated as drift to revert. Because the annotation lives on the persistent ConfigMap, the skip survives controller restarts, which is what ends the every-obol stack upre-provision.C —
ConfigDriftstatus condition (9a347d4)ConfigDriftcondition is set instead of silently clobbering. This is the safety net for B (which deliberately stops self-healing drift).Scope is limited to the
hermes-configConfigMap; every other manifest applies exactly as before.Tests
New
agent_confighash_test.go: table test ofhermesConfigDecision(nil / match+no-drift / match+drift / mismatch / absent-annotation), plus fake-client integration tests proving skip preservesresourceVersion, stale/absent annotation applies + stamps, and drift sets the condition without clobbering.agent_render_test.goadds the byte-identical golden + override cases.go build ./..., fullserviceoffercontroller+monetizeapisuites, andgo vetare green.Validated live on silvernuc3
Built + deployed this controller, applied the CRD, moved the paying
hyperliquid-analystagent's grok+xai-oauth+MCP config into its CR, then:mcp_servers+max_turns: 300from the CR and stamps the hash.resourceVersionunchanged (skip) — config preserved.Ready=True,ConfigDrift=False(InSync), offer Ready, 402 gate healthy.Migration / compatibility
Fully additive.
modelProviderdefaults tocustom, so existing agents render identically until an operator opts in by setting the new CR fields. The regenerated CRD is additive (existing CRs remain valid).https://claude.ai/code/session_01VquWN9UMaSHH7MHGcG8bw1