hermes: harden worker isolation and blocked-task semantics #13
Loading…
x
Reference in New Issue
Block a user
No description provided.
Delete Branch "wt/t_cca008de"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Three narrowly scoped Hermes reliability fixes, each backed by live evidence from the Cassandra/titan-iac proof run. No merge, no Flux reconcile, no deploy, no agent restart.
1. Worker concurrency starved the dashboard and auth sidecars
Three simultaneous direct CLI workers on the 4-core
hermes-agentnode drove load to ~45, made thehermesandoauth2-proxycontainers fail their probes and restart, and temporarily left the pod 8/10 Ready. Two simultaneous workers stayed at 10/10.HERMES_CLI_LANE_CONCURRENCY:4→2cli-lane-runnerCPU limit:3→2Confirmed from inside the running
cli-lane-runnercontainer that this is the right cgroup to cap — it reportsHERMES_CLI_LANE_CONCURRENCY=4andcpu.max = 300000 100000(3 cores), so the container limit is what actually bounds worker CPU.Requests remain sensible and are unchanged. The pod requests 745m CPU in total (
cli-lane-runner100m,hermes350m). Placement does not move. Worst case the lane runner now takes 2 of 4 cores, leaving the dashboard (350m guaranteed) andoauth2-proxyreal headroom — which is the property the incident showed was missing.2. Service-link variables contaminate worker and test environments
The deployment already addresses every service by DNS name, but Kubernetes still injects a service-link variable pair per namespace service.
hermes-claude-brokeryields:which
claude_oauth_broker.pyparses as an int. 93 such variables are present in the live worker environment right now.Set
enableServiceLinks: falseon thehermes-agentpod spec, with a manifest regression asserting it.This is not theoretical — it currently breaks the test suite:
2 failed, 105 passed—ValueError: invalid literal for int() with base 10: 'tcp://10.43.31.76:9006'107 passed3. Scheduler defect: explicitly blocked tasks auto-promote
create_task(initial_status="blocked")records acreatedevent whose payload carriesstatus=blocked, but never ablockedevent._has_sticky_block()only inspectsblocked/unblockedevents, andrecompute_ready()considers blocked tasks — so an explicitly parked task with no incomplete parent silently promoted itself toreadyon the next dispatcher cycle.The fix teaches
_has_sticky_block()to also recognize acreatedevent whose payload status isblocked. Becausecreatedis always the first event, it is only the most recent of the three kinds when noblocked/unblockedevent has fired, sounblock_task()still releases either kind of block. This also covers tasks created before the image patch without adding a persisted field.Dependency-driven promotion and the circuit-breaker failure-limit guard are untouched.
RED / GREEN evidence
Proven directly against the real upstream
kanban_dbAPI — not against a rendered manifest — by applying the Dockerfile's verbatim patch script to an isolated copy of/opt/hermes/hermes_cliand running both variants against throwaway SQLite databases. No live Kanban data was read or written.initial_block_without_parents_is_stickyrecompute_ready() == 1initial_block_with_complete_parents_is_stickyrecompute_ready() == 1initial_block_is_released_by_an_explicit_unblockdependency_block_promotes_after_parent_completesexplicit_block_and_unblock_remain_sticky_and_reversiblecircuit_breaker_block_is_not_auto_promotedFAILED (failures=2)OK(6/6)Exactly the two defect tests flip. The four guard tests pass in both directions, so the fix does not weaken dependency-driven promotion,
block_task/unblock_task, or circuit-breaker stickiness.hermes-kanban-blocked-regression.pyruns during the image build, so a regression fails the build. Verified this gate is real: the exact build-step invocation exits 1 against the current pre-fix image.Verification
test_hermes_cli_lanes.py+test_hermes_chat_quality.pytesting/tests/suiteruff check(0.8.4, pinned)kustomize build services/hermeskubectl apply --dry-run=clientgit diff --checkThe 2 full-suite failures (
test_provider_status_separates_observed_activity_from_plan_quota,test_cassandra_sync_repairs_origin_without_token) are pre-existing and unrelated — reproduced identically on a pristinegit archiveof main HEAD0dd6ea0f.Blocker: image cannot be built or published
This change patches
Dockerfile.hermes-agent, so it needs a new image to take effect. I could not produce one:/var/run/docker.sockabsent).Dockerfile.hermes-agent.Jenkinsfile/ci/Jenkinsfile.titan-iacare quality-gate pipelines only; the sole Kaniko reference isservices/logging/Jenkinsfile.data-prepper, and there is noImageRepositoryforhermes-agent.Per the shepherd's safety correction I did not stand up a privileged Docker-in-Docker builder or expose an unauthenticated Docker/BuildKit API. The image digest in this PR is therefore unchanged and the manifest still references the current
sha256:81970563…. A maintainer must build and publish via an unprivileged bounded builder on a healthy node, then bump the digest.Residual risks
hermesFlux Kustomization is not reconciling —dependency 'flux-system/cert-manager' is not ready. This is why live runssha256:37ebf720…while main HEAD pinssha256:81970563….cert-manageris now healthy, so this looks transient, but merging this PR will not take effect until that Kustomization reconciles. Not touched, per instructions.createdevent payload. A task whosecreatedevent predates the payloadstatusfield would fall back to non-sticky, i.e. current behavior — no new failure mode.🤖 Generated with Claude Code
hermes-automation referenced this pull request2026-08-17 10:14:46 +00:00