feat(operator): API & CRD foundations for D + E parity (PR-49 of #48) - #49
Merged
Conversation
Lock acquisition per coordination protocol — single-file commit pushed immediately so the other Claude instance (10.199.12.8) sees the claim on next git pull. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The RoleDefinition type was missing its closing brace before AgentRoleSpec began on line 93. zz_generated.deepcopy.go was generated from a previously correct version, so the file had been broken by a hand edit without a follow-up `make generate`. After this change, source and generated deepcopy match shapes (verified by inspection of zz_generated.deepcopy.go:333-353). Refs openspec/changes/20260508-operator-feature-parity-d-e/tasks.md PR-49 task 1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The closed Enum= validation marker on AgentRole rejected every role
added since ACCv3 — including the 5 coding-split-skills personas (D3),
the 6 autoresearcher personas (E4), and the umbrella coding_agent.
Replace with a regex Pattern (DNS-label-style: ^[a-z][a-z0-9_]{1,62}$)
plus length bounds. Drop the redundant field-level Enum= markers on
AgentRoleSpec.Role and RoleScalingSpec.Role.
Semantic validation against the operator's compiled-in catalogue moves
to the AgentCollective validating webhook (subsequent commit on this
branch).
Append 12 exported role consts so samples and tests can refer to the
new personas by name:
RoleCodingAgent + 5 split-skill variants
6 research_* personas
Refs openspec/changes/20260508-operator-feature-parity-d-e/tasks.md
PR-49 tasks 2 and 3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the schema-side surface for two later reconcilers (no behaviour
change in this commit):
spec.mcpServers — slice of MCPServerSpec entries describing the
shared MCP servers visible across all collectives in this corpus.
Mirrors the per-MCP shape used in
container/production/podman-compose.yml: name, image, replicas,
port, env, secretEnv, shmSizeMi (for browser-harness Chromium
/dev/shm headroom), resources.
spec.manifestDelivery — Enum{all|none}, default "all". Controls
whether the operator mounts the operator-baked roles/, skills/,
mcps/ ConfigMaps into agent pods. "none" lets users bake the trees
into a custom agent image instead.
Also wires the matching status fields:
status.mcpServerStatuses[name] — Ready/Replicas/ServiceURL
status.manifestDeliveryReady — boolean
Reconcilers that consume these fields ship in PR-50 (manifest
delivery) and PR-51 (MCP server). Existing CRs without these fields
continue to validate cleanly because every new field is optional.
Refs openspec/changes/20260508-operator-feature-parity-d-e/tasks.md
PR-49 tasks 4 and 5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds operator/internal/rolecatalogue/, the compile-time set of known
agent role names. Sourced from known_roles.txt — generated from the
live roles/ tree by hack/gen-catalogue.go (skips _base / TEMPLATE,
requires role.yaml to be present).
Public API:
IsKnown(role) — membership check
All() — sorted slice for help text
Suggest(role, n) — Levenshtein-ranked closest matches, capped
at distance 8 so unrelated typos don't
produce nonsense suggestions
Two invocation paths produce byte-identical output:
go run ./hack/gen-catalogue.go (from operator/)
go generate ./internal/rolecatalogue/... (any cwd)
The generator walks up from cwd to find operator/, so the //go:generate
directive works regardless of where it's invoked from.
Spot-checked behaviour:
knows analyst, coding_agent_tester, research_planner: true
knows fnord: false
suggest "research_plan" → research_planner, research_critic, ...
suggest "coding_agnt_arch" → coding_agent, coding_agent_architect, ...
Used by the AgentCollective validating webhook in the next commit on
this branch.
Refs openspec/changes/20260508-operator-feature-parity-d-e/tasks.md
PR-49 task 6.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous pseudo-version v0.0.0-20240813175640-2e4dc7204e66 of k8s.io/kube-openapi is no longer resolvable on proxy.golang.org — the underlying git revision was retracted. This blocked every go tooling invocation on the operator module (vet, build, test, controller-gen). go.sum had also never been committed. Bump to v0.0.0-20240228011516-70dd3763d340 (a real pseudo-version contemporaneous with controller-runtime v0.19.0 / apimachinery v0.31.0) and run `go mod tidy` to regenerate go.sum. go mod tidy also bumps the language version directive to 1.23.0 (toolchain go1.24.6) — the older go 1.22 directive was inconsistent with the toolchain image the Containerfile uses. After this commit: go vet ./... — clean go build ./... — clean Surfaced while preparing PR-49 (API & CRD foundations); separating into its own commit so the build-hygiene fix is reviewable on its own. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… updates
Adds a new validating + defaulting webhook for AgentCollective parallel
to the existing one for AgentCorpus. The webhook plugs the gap left by
loosening the AgentRole enum to a regex: schema-level validation now
accepts any DNS-label-style string, but the webhook rejects role names
not present in the operator's compiled-in catalogue.
Catalogue lookup uses internal/rolecatalogue.IsKnown(); rejection
messages include up to 3 closest matches by Levenshtein distance via
.Suggest(), so a typo like "research_plan" gets
role "research_plan" is not in the operator's known-roles
catalogue; did you mean "research_planner", "research_critic",
"research_economist"?
instead of an opaque schema rejection.
Webhook also enforces:
- roles in spec.scaling.roleScaling[*].role must be declared in
spec.agents[*] (otherwise the override is dead config)
- minReplicas <= maxReplicas
- llm sub-struct present matches the chosen llm.backend
agentcorpus_webhook.go updates:
- Default ManifestDelivery to "all" when unset (PR-50 reconciler
relies on this).
- Default each MCPServerSpec.Replicas to 1 and .Port to 8080.
- Reject duplicate MCP server names — PR-51 reconciler derives
Service names from MCPServerSpec.Name and collisions are fatal.
Verified via `go vet ./...` and `go build ./...` (both clean).
Refs openspec/changes/20260508-operator-feature-parity-d-e/tasks.md
PR-49 task 7.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Output of `make manifests generate` after the API changes earlier in
this branch. Reviewable as a deterministic delta of the previous five
commits.
CRD changes:
acc.redhat.io_agentcollectives.yaml
- spec.agents[].role: closed enum dropped, replaced by
pattern ^[a-z][a-z0-9_]{1,62}$ + length bounds (semantic
check moves to the AgentCollective webhook).
- spec.scaling.roleScaling[].role: same loosening.
acc.redhat.io_agentcorpora.yaml
- spec.mcpServers[]: new MCPServerSpec slice schema.
- spec.manifestDelivery: new enum field {all|none}, default all.
- status.mcpServerStatuses: new map keyed by MCP server name.
- status.manifestDeliveryReady: new bool.
zz_generated.deepcopy.go regenerated with DeepCopy methods for the
two new types (MCPServerSpec, MCPServerStatus) and the slice/map
wiring in AgentCorpusSpec and AgentCorpusStatus.
config/webhook/manifests.yaml gains the AgentCollective mutating +
validating webhook entries (paths /mutate- and /validate-...
agentcollective).
Also adds operator/bin/ to .gitignore — the Makefile's controller-gen,
kustomize, and envtest targets land binaries there per developer.
Refs openspec/changes/20260508-operator-feature-parity-d-e/tasks.md
PR-49 task 8.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds operator/test/unit/role_catalogue_test.go covering the public API
of internal/rolecatalogue:
IsKnown
- legacy ACCv3 5-role set still admitted (regression guard)
- D3 coding-split-skills personas + umbrella admitted
- E4 autoresearcher personas admitted
- empty / fnord / case-shifted / reserved (_base, TEMPLATE)
reliably rejected
All
- sorted, deduped, fresh slice (mutation isolation contract)
Suggest
- returns the intended target for realistic typos:
research_plan → research_planner
research_economis → research_economist
coding_agnt_architect → coding_agent_architect
coding_agent_implmenter → coding_agent_implementer
analyzt → analyst
observator → observer
- n cap honoured
- n <= 0 returns nil (documented contract)
- distance > 8 cutoff suppresses nonsense matches
("qqq..." input yields zero suggestions)
go test ./test/unit/... runs clean (existing tests unaffected).
Refs openspec/changes/20260508-operator-feature-parity-d-e/tasks.md
PR-49 task 9.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
flg77
marked this pull request as ready for review
May 9, 2026 06:02
flg77
added a commit
that referenced
this pull request
Jun 11, 2026
…49) Net-new MLFlow layer complementary to the OTel trace export already shipped (20260527-mlflow-otel-telemetry Phases 1-4): turns a golden-prompt / eval / reasoning-bench execution into an MLFlow run so "benchmark after a role or model change" lands in MLFlow's experiment-comparison UI. acc/backends/mlflow_runs.py (new, opt-in + lazy + best-effort): - enabled() — true only when ACC_MLFLOW_TRACKING_URI set AND mlflow importable; import acc.backends.mlflow_runs is always safe. - mlflow_run(...) ctxmanager — yields None (no-op) when disabled. - log_golden_results(results, *, run_meta) — one run per suite; params from run_meta (model/host/git_sha), metrics golden.count / passed / pass_rate + per-prompt pass.<name> + latency_ms.<name>. Reuses the exact GoldenResult shape — no new data model. - log_eval_outcome(...), log_reasoning_depth(...) for the evals + acc-bench consumers. Key/param sanitisation + length clipping for MLFlow limits; a tracking-server outage logs + returns False, never raises. Wired beside acc/golden_prompts.py::persist_results (same run_meta keeps the JSONL history + MLFlow in sync); best-effort try/except mirrors the JSONL writer. Logs from the runner/CLI/scheduled side, never the agent hot path. pyproject.toml — mlflow-skinny added to the existing [mlflow] extra (client-only; keeps the extra light). tests/test_mlflow_runs.py — 10 tests: no-op/disabled paths, recorded-call via an injected fake mlflow, tracking-outage resilience, key sanitisation, persist_results integration (JSONL + run both fire). Sweep: 3071 passed, 69 skipped (delta +10). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.
Summary
Implements PR-49 of the openspec change merged in #48: API & CRD foundations that unblock the manifest delivery (#50) + MCP server (#51) reconcilers and the demo samples (#52).
What changed
Schema (api/v1alpha1)
^[a-z][a-z0-9_]{1,62}$+ length bounds) onAgentRole. Field-levelEnum=markers dropped onAgentRoleSpec.RoleandRoleScalingSpec.Role. Schema now accepts every persona underroles/; semantic check moves to a webhook (see below).coding_agentused bycontainer/production/podman-compose.yml.MCPServerSpec+MCPServerStatustypes and their slice/map wiring onAgentCorpusSpec/AgentCorpusStatus. Fields:Name,Image,Replicas,Port,Env,SecretEnv,ShmSizeMi,Resources. Reconciler ships in feat(operator): manifest delivery reconciler (PR-51 of #48) #51.ManifestDeliveryenum (all|none, defaultall) onAgentCorpusSpec. Reconciler ships in docs(index): cross-link to acc-podman-desktop extension #50.RoleDefinitionstruct was missing its closing}beforeAgentRoleSpec.Webhook (api/v1alpha1)
New
agentcollective_webhook.goparallel to the existing corpus webhook. Validates roles against the operator's compiled-in catalogue with closest-match Levenshtein suggestions:Plus bonus checks:
scaling.roleScaling[*].rolemust be declared inspec.agents;minReplicas <= maxReplicas;llmsub-struct must match the chosenllm.backend.agentcorpus_webhook.godefaultsmanifestDelivery=allandMCPServer.Replicas=1,MCPServer.Port=8080. Rejects duplicate MCP server names (the reconciler in feat(operator): manifest delivery reconciler (PR-51 of #48) #51 derives Service names fromMCPServerSpec.Nameand collisions would be fatal).Catalogue infrastructure
internal/rolecatalogue/—IsKnown/All/Suggest(role, n)public API. Catalogue is//go:embed-baked fromknown_roles.txt, generated from the liveroles/tree (47 roles).hack/gen-catalogue.go— regenerator triggered by//go:generate. Walks up tooperator/automatically so it works whether invoked from the package directory or the operator root.Generated artefacts
zz_generated.deepcopy.goregenerated (+632/-238 lines).config/crd/bases/acc.redhat.io_agent{collectives,corpora}.yamlregenerated. CRD schemas now reflect the loosened role validation, the newmcpServersslice, the newmanifestDeliveryfield, and the new status fields.config/webhook/manifests.yamlregenerated to register the new AgentCollective mutating + validating webhooks.Build hygiene (chore commit
24e5414)go.modhad a stalekube-openapipseudo-version that no longer resolved onproxy.golang.org, blocking everygoinvocation. Bumped tov0.0.0-20240228011516-70dd3763d340.go.sumhad also never been committed — generated and added.Verification
go vet ./...go build ./...go test ./test/unit/...make manifests generatesol-corpus/rhoai-corpussample roles match new regexkubectl apply --dry-run=serveragainst a live cluster was deliberately skipped — the cluster is shared and the original task list specified dry-run only; the regex verification above is equivalent for the intended question (does the new schema accept everything the old one did, plus the new personas).Coordination
Lock claimed via commit
22ecaa6and held through this entire PR. Updates totasks.mdmark all PR-49 boxes complete.Commit graph (8 commits)
22ecaa6lock claim466273b}fix9052391enum loosening + 12 role constsd9c2784MCPServerSpec + ManifestDelivery schema24e5414go.mod kube-openapi repair + go.sum (build hygiene)d2043d0AgentCollective webhook + corpus defaulter updates1d61a17regenerated deepcopy + CRDs + webhook manifestsb325344unit tests497fbeelock board: PR-49 tasks complete🤖 Generated with Claude Code