fix: the ops platform declared an AgentFleet the API server refuses - #200
Merged
Conversation
`addons/ai-platform/agent-platform/base/platform.yaml` shipped an AgentFleet
whose single agent carried name, systemPrompt and modelRoute. The AgentFleet
CRD requires `spec.agents[].image`, and it has no default, so every cluster
syncing this addon got
AgentFleet.agents.nanohype.dev "ops-fleet" is invalid:
spec.agents[0].image: Required value
and the Application never reached Healthy. This manifest is in the base — it is
applied to every cluster in the fleet.
`image` is the container the agent actually runs, and it is the tenant's own
build. This catalog has nothing truthful to put there: nothing in the org
publishes a generic agent, and agentctl's own scaffold writes the literal
REPLACE_WITH_YOUR_AGENT_IMAGE for a human to fill in. So the fleet is gone
rather than patched.
The EvalSuite went with it, deliberately rather than incidentally: its
`agentFleetRef` had nothing left to point at, and an EvalSuite over an absent
fleet is the same shape of problem one layer up. The eval alerts and the
eval-quality dashboard therefore start empty, which is the honest state for a
cluster running no agents. AgentEvalNeverRan carries noDataState: OK, so an
empty catalog is silent rather than alarming.
─── why nothing caught it ───
Platform, Tenant, ModelGateway, BudgetPolicy, AgentFleet and EvalSuite are on
`kubeconform-scan.sh`'s skip list. That skip is honest about why — their schemas
are published to no public catalog — and says these kinds are validated
"out-of-band" with `kubectl apply --dry-run=server`. For this manifest that
never happened. A skip that records a gap beats a green tick that pretends there
is none, but a gap nobody closes is still a gap.
`scripts/check-platform-crs.py` closes it without a cluster. It resolves the
CRDs from the operator chart at the version this catalog PINS — read out of
addons-agent-operator's targetRevision, not `latest`, because the question is
whether the manifest is valid against the CRDs this catalog installs — and walks
every CR of those kinds: required present at every level, nothing excess, arrays
transparent.
Run against the manifest as it shipped, it fails naming
`spec.agents[0].image`.
─── one thing the gate had to be taught ───
Its first pass also reported the ops Tenant as refused over
`spec.primaryPersona`. That was wrong, and the correction is the interesting
part: structural-schema defaulting runs BEFORE validation, so a required
property carrying a `default` is filled in by the API server and admitted.
`primaryPersona` defaults to `generic`; `image` has no default. Reading
`required` alone cannot tell those apart, and a gate that cries wolf about a
manifest running on every cluster today would not survive its first week.
CI Results
All checks passed. |
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.
addons/ai-platform/agent-platform/base/platform.yamlships an AgentFleet whose single agent carriesname,systemPromptandmodelRoute. The AgentFleet CRD requiresspec.agents[].image, and it has no default, so every cluster syncing this addon getsand that Application never reaches Healthy. This manifest is in the base — it is applied to every cluster in the fleet.
imageis the container the agent actually runs, and it is the tenant's own build. This catalog has nothing truthful to put there: nothing in the org publishes a generic agent, and agentctl's own scaffold writes the literalREPLACE_WITH_YOUR_AGENT_IMAGEfor a human to fill in. So the fleet is gone rather than patched.The EvalSuite went with it, deliberately rather than incidentally: its
agentFleetRefhad nothing left to point at, and an EvalSuite over an absent fleet is the same shape of problem one layer up. The eval alerts and the eval-quality dashboard therefore start empty, which is the honest state for a cluster running no agents —AgentEvalNeverRancarriesnoDataState: OK, so an empty catalog is silent rather than alarming.Why nothing caught it
Platform, Tenant, ModelGateway, BudgetPolicy, AgentFleet and EvalSuite are on
kubeconform-scan.sh's skip list. That skip is honest about why — their schemas are published to no public catalog — and says these kinds are validated "out-of-band" withkubectl apply --dry-run=server. For this manifest that never happened. A skip that records a gap beats a green tick that pretends there is none, but a gap nobody closes is still a gap.scripts/check-platform-crs.pycloses it without a cluster. It resolves the CRDs from the operator chart at the version this catalog pins — read out ofaddons-agent-operator'stargetRevision, notlatest, because the question is whether the manifest is valid against the CRDs this catalog installs — then walks every CR of those kinds: required present at every level, nothing excess, arrays transparent.Against the manifest as it shipped:
One thing the gate had to be taught
Its first pass also reported the ops Tenant as refused over
spec.primaryPersona. That was wrong, and the correction is the interesting part: structural-schema defaulting runs before validation, so a required property carrying adefaultis filled in by the API server and admitted.primaryPersonadefaults togeneric;imagehas no default. Readingrequiredalone cannot tell those apart, and a gate that cried wolf about a manifest running on every cluster today would not survive its first week. There is a self-test case pinning it.Found while tracing every rackctl phase that has never executed live. rackctl's GitOps phase waits for
--for=jsonpath={.status.health.status}=Healthy applications --all, which is stricter than the e2e harness's catalog gate — so this would have failed the first real install.