Problem
On gazelle the kagent-default WorkerPool (4 workers, nodeSelector karpenter.k8s.aws/instance-cpu-manufacturer: amd) currently spans two AMD generations: two workers on an m7a.2xlarge (generation 7, AVX-512) and two on c5ad.xlarge (generation 5). The golden snapshots of sre-agent (Swarmgeist's default agent) and test-agent were taken on the m7a workers at 16:24Z on 2026-09-14. A new AgentInstance placed on a c5ad worker cannot restore them:
starting container: starting sub-container [/app]: failed to load kernel: incompatible FeatureSet: missing features: map[monitor:{} pcid:{} x2apic:{} invpcid:{} avx512f:{} avx512dq:{} avx512cd:{} avx512bw:{} avx512vl:{} …
Substrate retries the restore once on the same worker (30 s each), the controller answers SendStreamingMessage with Internal after 60 s, klaus-gateway logs actor "ai-…" request timed out and Slack shows "the turn failed; please try again". Observed live on 2026-09-14 19:29Z (first cold turn of the evening); the retry on an m7a worker answered in 9 s. Half of the pool cannot serve the default agent.
This is the caveat recorded when #429 pinned the manufacturer: a newer generation's gVisor checkpoint does not restore on an older one of the same vendor. Karpenter picks any AMD instance type for the pool, so the mix changes with every spot replacement.
Proposed solution
Pick one, in order of preference:
- Pin the pool to one CPU generation/family through a chart value (
karpenter.k8s.aws/instance-generation or karpenter.k8s.aws/instance-family in substrateWorkerPool.template.nodeSelector), chosen per installation from the generation the golden snapshots were taken on; document the capacity trade-off (a generation pin narrows the spot pool).
- Make the golden snapshot the lowest common denominator: take goldens on the oldest generation admitted by the pool so they restore everywhere.
- Longer term (Substrate proposal, fork-first): workers advertise their CPU feature set, snapshots record it, the scheduler only places a restore where the host's set is a superset.
Also: a restore that fails with incompatible FeatureSet should be retried on a different worker, not the same one twice.
Acceptance criteria
Problem
On gazelle the
kagent-defaultWorkerPool (4 workers, nodeSelectorkarpenter.k8s.aws/instance-cpu-manufacturer: amd) currently spans two AMD generations: two workers on anm7a.2xlarge(generation 7, AVX-512) and two onc5ad.xlarge(generation 5). The golden snapshots ofsre-agent(Swarmgeist's default agent) andtest-agentwere taken on the m7a workers at 16:24Z on 2026-09-14. A new AgentInstance placed on a c5ad worker cannot restore them:Substrate retries the restore once on the same worker (30 s each), the controller answers
SendStreamingMessagewithInternalafter 60 s, klaus-gateway logsactor "ai-…" request timed outand Slack shows "the turn failed; please try again". Observed live on 2026-09-14 19:29Z (first cold turn of the evening); the retry on an m7a worker answered in 9 s. Half of the pool cannot serve the default agent.This is the caveat recorded when #429 pinned the manufacturer: a newer generation's gVisor checkpoint does not restore on an older one of the same vendor. Karpenter picks any AMD instance type for the pool, so the mix changes with every spot replacement.
Proposed solution
Pick one, in order of preference:
karpenter.k8s.aws/instance-generationorkarpenter.k8s.aws/instance-familyinsubstrateWorkerPool.template.nodeSelector), chosen per installation from the generation the golden snapshots were taken on; document the capacity trade-off (a generation pin narrows the spot pool).Also: a restore that fails with
incompatible FeatureSetshould be retried on a different worker, not the same one twice.Acceptance criteria
kubectl get nodes -L karpenter.k8s.aws/instance-generationfor the worker nodes).sre-agenton gazelle never fails withincompatible FeatureSet; 20 consecutive new threads succeed.