-
Notifications
You must be signed in to change notification settings - Fork 22
OAPE-882: Add OpenSpec harness evals and constitution #164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # Harness evals (operator-owned) | ||
|
|
||
| Operator-specific inputs for the [OpenSpec agile workflow](https://github.com/sujkini/openspec/blob/v2-restructured/README.md). | ||
|
|
||
| ``` | ||
| harness-evals/ | ||
| ├── harness-docs/ # Operator docs (source for /opsx-constitute) | ||
| ├── constitution.md # Guardrails (required before plan.md) | ||
| └── evals/ # Stage eval YAMLs (quality gates; optional until /eval-loop) | ||
| ``` | ||
|
Comment on lines
+5
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Add a language to the fenced block.
🧰 Tools🪛 markdownlint-cli2 (0.23.2)[warning] 5-5: Fenced code blocks should have a language specified (MD040, fenced-code-language) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| | Path | Command | Notes | | ||
| |------|---------|-------| | ||
| | `harness-docs/` | `/opsx-constitute` | Already populated with ESO guidelines | | ||
| | `constitution.md` | `/opsx-continue` (before plan) | Present — regenerate with `/opsx-constitute` if needed | | ||
| | `evals/*_eval.yaml` | `/opsx-continue`, `/opsx-apply` | Stub files seeded; cases accumulate via `/eval-loop` | | ||
|
|
||
| ## Populate real eval cases | ||
|
|
||
| 1. Fill `eval-generation/input/feature-bundle.yaml` from a **completed** feature (EP, epic, stories, PRs, bugs). | ||
| 2. Run `/eval-loop`. | ||
| 3. Review `eval-generation/eval-generation-workflow/template-gaps/` and `eval-generation/output-refined-templates/`. | ||
| 4. Generated cases sync automatically into `harness-evals/evals/`. | ||
|
|
||
| Until cases exist, forward workflow skips eval scoring and relies on verification + user approval. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| <!-- Companion artifact: repo-assessment.md (target files, reusable assets, risks) --> | ||
| # External Secrets Operator Constitution | ||
|
|
||
| **AgentRoutingMode:** PROVIDED | ||
| <!-- PROVIDED — AGENTS.md exists at repo root --> | ||
|
|
||
| **Version**: 1.0.0 | **Ratified**: 2026-07-01 | **Last Amended**: 2026-07-01 | ||
|
|
||
| ## Core Principles | ||
|
|
||
| ### I. Upstream Operand Separation — Do Not Fork Upstream Logic | ||
|
|
||
| The operator deploys and manages upstream **external-secrets** and the optional **bitwarden-sdk-server** plugin via **embedded manifests in `bindata/external-secrets/`**. The operator **never** reimplements upstream secret-sync logic (provider authentication, ExternalSecret reconciliation, generator behavior, Bitwarden SDK protocol). Operator packages reconcile operator CRs and deploy/configure operand workloads only. | ||
|
|
||
| **Evidence:** `bindata/external-secrets/resources/` — operand YAML from upstream helm; `pkg/controller/external_secrets/` installs deployments/RBAC/webhooks but contains zero provider-specific secret-fetch logic. `README.md` states the operator uses upstream helm charts. | ||
|
|
||
| ### II. Two Operand Workloads — Core vs Plugin | ||
|
|
||
| | Workload | Always? | Controlled by | Image env | | ||
| |----------|---------|---------------|-----------| | ||
| | **external-secrets** (core controller + webhook + cert-controller) | Yes | `ExternalSecretsConfig` | `RELATED_IMAGE_EXTERNAL_SECRETS` | | ||
| | **bitwarden-sdk-server** (provider plugin) | No — when `plugins.bitwardenSecretManagerProvider.mode == Enabled` | `ExternalSecretsConfig.spec.plugins` | `RELATED_IMAGE_BITWARDEN_SDK_SERVER` | | ||
|
|
||
| New plugin workloads MUST follow the bitwarden pattern: API under `spec.plugins`, bindata deployment asset, conditional entry in `createOrApplyDeployments`, dedicated network policy, TLS via `certProvider` or `secretRef`. | ||
|
|
||
| **Evidence:** `deployments.go` — conditional deployment table; `constants.go` — `bitwardenDeploymentAssetName`, image env var names; `api/v1alpha1/external_secrets_config_types.go` — `BitwardenSecretManagerProvider`, CEL rules for TLS prerequisites. | ||
|
|
||
| ### III. Controller-Runtime Only — Single Manager, Three Reconcilers | ||
|
|
||
| All controllers use **`sigs.k8s.io/controller-runtime`** on **one** shared manager. Register reconcilers in `pkg/operator/setup_manager.go` only. Do not introduce library-go, informer factories, or separate managers. | ||
|
|
||
| **Evidence:** `pkg/operator/setup_manager.go` — wires `external_secrets_manager`, `external_secrets`, and optional `crd_annotator`; `cmd/external-secrets-operator/main.go` — single `ctrl.Manager`; `go.mod` — `sigs.k8s.io/controller-runtime v0.23.3`, no `openshift/library-go`. | ||
|
|
||
| ### IV. Create-or-Update Reconciliation — Not SSA-First | ||
|
|
||
| Operand resources are reconciled via **create-or-update with deep equality** (`createWithFallback`, `common.HasObjectChanged`, `UpdateWithRetry`). Limited SSA (`client.Apply` with field owner) is allowed only for CR annotation patches. Do not convert operand reconcilers to SSA-first patterns. | ||
|
|
||
| **Evidence:** `pkg/controller/external_secrets/install_external_secrets.go`, `pkg/controller/common/utils.go` — `HasObjectChanged()`; `pkg/controller/common/constants.go` — `ExternalSecretsOperatorCommonName` as field owner for annotation patches only. | ||
|
Comment on lines
+34
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win Remove the Server-Side Apply exception.
Use the existing metadata-only patch path instead. Do not permit SSA in this constitution. 🤖 Prompt for AI Agents |
||
|
|
||
| ### V. Singleton CR Convention — Name `cluster`, One Per Kind | ||
|
|
||
| Operator CRs `ExternalSecretsConfig` and `ExternalSecretsManager` are **cluster-scoped singletons named `cluster`**. The operator auto-creates default `ExternalSecretsManager` named `cluster`. CEL validation enforces singleton naming. | ||
|
|
||
| **Evidence:** `pkg/controller/common/constants.go` — `ExternalSecretsConfigObjectName` and `ExternalSecretsManagerObjectName` = `"cluster"`; `pkg/operator/setup_manager.go` — `CreateDefaultESMResource()`; `README.md` — auto-creates `externalsecretsmanagers.operator.openshift.io` named `cluster`. | ||
|
|
||
| ### VI. Feature Flags on ExternalSecretsManager — Not OpenShift FeatureGate API | ||
|
|
||
| Runtime feature toggles are defined on `ExternalSecretsManager.Spec.Features[]` with typed `FeatureName` values. Check via `common.IsFeatureEnabled()`. Do not add OpenShift cluster FeatureGate discovery or `pkg/features/` patterns from other operators. | ||
|
|
||
| **Evidence:** `api/v1alpha1/external_secrets_manager_types.go` — `Feature` slice; `pkg/controller/common/utils.go` — `IsFeatureEnabled()`; `pkg/controller/external_secrets/constants.go` — `featureContainerArgs` map. | ||
|
|
||
| ### VII. Webhook TLS — cert-manager or In-Tree cert-controller (Mutually Exclusive) | ||
|
|
||
| Webhook TLS uses either cert-manager `Certificate` CRs (`certProvider.certManager.mode == Enabled`) **or** the in-tree `external-secrets-cert-controller` deployment — never both. cert-controller deployment is skipped when cert-manager path is active. | ||
|
|
||
| **Evidence:** `deployments.go` — `certControllerDeploymentAssetName` condition `!isCertManagerConfigEnabled(esc)`; `certificate.go`, `certificate_external-secrets-webhook.yml` vs `secret_external-secrets-webhook.yml`. | ||
|
|
||
| ### VIII. Bindata / Manifest Regeneration — Never Hand-Edit, Always `make update` | ||
|
|
||
| Operand manifests under `bindata/` and generated code (`zz_generated.deepcopy.go`, `pkg/operator/assets/bindata.go`, CRD YAML under `config/crd/bases/`) are **generated artifacts**. Changes require `make update-operand-manifests` (helm pipeline) and/or `make generate && make manifests && make update-bindata`. CI verification (`make verify`) fails if outputs are stale. | ||
|
|
||
| **Evidence:** `hack/update-external-secrets-manifests.sh`; `Makefile` — `EXTERNAL_SECRETS_VERSION`, `update`, `verify`, `verify-bindata`, `verify-generated`; `pkg/operator/assets/bindata.go` — generated. | ||
|
|
||
| ### IX. Verification-First Development — `make verify && make lint && make test` | ||
|
|
||
| All changes MUST pass: `make test` (manifests, generate, fmt, vet, test-apis, test-unit), `make verify` (bindata, generated, govulncheck, git diff), and `make lint` (golangci-lint + kube-api-linter). E2E (`make test-e2e`, build tag `e2e`) requires a live cluster. | ||
|
|
||
| **Evidence:** `Makefile` — `test`, `test-unit`, `test-apis`, `test-e2e`, `verify`, `lint` targets; `.golangci.yml` — linter configuration with kube-api-linter plugin. | ||
|
Comment on lines
+64
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win Correct the verification target descriptions. This text assigns manifest generation, formatting, and vet checks to Based on learnings, 🤖 Prompt for AI AgentsSource: Learnings |
||
|
|
||
| ### X. RBAC Least Privilege — Explicit Operator and Operand Manifests | ||
|
|
||
| Operator RBAC is in `config/rbac/`. Operand RBAC is embedded in `bindata/external-secrets/resources/` and applied by `pkg/controller/external_secrets/rbacs.go`. New permissions MUST be explicit ClusterRole rules in bindata or operator RBAC — not broad cluster-admin grants. | ||
|
|
||
| **Evidence:** `config/rbac/role.yaml`; `bindata/external-secrets/resources/` — per-component RBAC YAML; `pkg/controller/external_secrets/rbacs.go`. | ||
|
|
||
| ### XI. OLM Bundle and Related Images | ||
|
|
||
| The operator ships via OLM (`bundle/`, `config/manifests/`). Operand version is pinned in `Makefile` (`EXTERNAL_SECRETS_VERSION`). Images: | ||
| - `RELATED_IMAGE_EXTERNAL_SECRETS` / `OPERAND_EXTERNAL_SECRETS_IMAGE_VERSION` — core operand | ||
| - `RELATED_IMAGE_BITWARDEN_SDK_SERVER` / `BITWARDEN_SDK_SERVER_IMAGE_VERSION` — Bitwarden plugin | ||
|
|
||
| Missing image env vars cause irrecoverable errors. | ||
|
|
||
| **Evidence:** `Makefile` — `IMG_VERSION`, `EXTERNAL_SECRETS_VERSION`, `bundle` target; `bundle/manifests/openshift-external-secrets-operator.clusterserviceversion.yaml`; `pkg/controller/external_secrets/constants.go`. | ||
|
|
||
| ### XII. Namespace Conventions | ||
|
|
||
| Operator runs in `external-secrets-operator` namespace. Operand runs in `external-secrets` namespace (`OperandDefaultNamespace`). Both are established conventions in controller constants and README. | ||
|
|
||
| **Evidence:** `README.md` — "operator runs in `external-secrets-operator` namespace"; `pkg/controller/external_secrets/constants.go` — `OperandDefaultNamespace`. | ||
|
|
||
| ## Additional Constraints | ||
|
|
||
| - **Go version**: Match `go.mod` — currently `go 1.26.0`. — **Evidence:** `go.mod` | ||
| - **Workspace**: Multi-module `go.work` (root, `cmd/external-secrets-operator`, `test`, `tools`). Vendor via `make update-vendor`. — **Evidence:** `go.work`, `vendor/` | ||
| - **Import ordering**: Local prefix `github.com/openshift/external-secrets-operator`. — **Evidence:** `.golangci.yml` `local-prefixes` | ||
| - **FIPS**: Production builds use `hack/go-fips.sh` (`GOEXPERIMENT=strictfipsruntime`, `CGO_ENABLED=1`). — **Evidence:** `Makefile` `build-operator`, `hack/go-fips.sh` | ||
| - **Container image**: Operator image from `Dockerfile` / `images/ci/`; operand images from `RELATED_IMAGE_*` env vars. — **Evidence:** `Dockerfile`, CSV relatedImages | ||
| - **Test frameworks**: Standard `testing` + counterfeiter fakes in `pkg/`; Ginkgo v2 + envtest in `test/apis/`; Ginkgo + live cluster in `test/e2e/` (tag `e2e`). — **Evidence:** `pkg/controller/client/fakes/`, `test/apis/`, `test/e2e/` | ||
| - **CI system**: Prow via `openshift/release`; in-repo verify via `make verify`. — **Evidence:** `README.md` contributing section | ||
| - **Optional cert-manager**: Webhook TLS may use cert-manager `Certificate` CRs when cert-manager is installed; `crd_annotator` is conditional. Never assume cert-manager is present. — **Evidence:** `pkg/operator/setup_manager.go`, `pkg/controller/crd_annotator/` | ||
|
|
||
| ## Development Workflow | ||
|
|
||
| | Activity | Requirement | Evidence | | ||
| |----------|-------------|----------| | ||
| | Local unit tests | `make test-unit` or full `make test` | `Makefile` | | ||
| | API validation tests | `make test-apis` after CRD/testsuite changes | `hack/test-apis.sh`, `test/apis/` | | ||
| | Full verify | `make verify` | `Makefile` `verify` target | | ||
| | Lint | `make lint` | `Makefile`, `.golangci.yml` | | ||
| | Codegen refresh | `make generate && make manifests` after API edits | `Makefile` | | ||
| | Operand bump | `make update-operand-manifests && make update-bindata` | `hack/update-external-secrets-manifests.sh` | | ||
| | E2E tests | `make test-e2e` (cluster required); filter via `E2E_GINKGO_LABEL_FILTER` | `Makefile` `test-e2e` | | ||
| | Bundle generation | `make bundle` after CSV/CRD changes | `Makefile` `bundle` | | ||
| | PR pre-merge | `make test && make verify && make lint`; commit generated outputs | `AGENTS.md` verification matrix | | ||
|
|
||
| ## Agent Routing | ||
|
|
||
| | Agent ID | Scope | When to route | | ||
| |----------|-------|---------------| | ||
| | **API_Agent** | `api/v1alpha1/`, testsuite YAML | CRD types, validation, markers | | ||
| | **OperatorController_Agent** | `pkg/controller/external_secrets/`, `external_secrets_manager/`, `crd_annotator/`, `setup_manager.go` | Core operand reconciliation, wiring | | ||
| | **ManifestsBindata_Agent** | `bindata/`, `hack/update-external-secrets-manifests.sh`, operand CRDs | Operand manifest refresh, version pins | | ||
| | **BitwardenPlugin_Agent** | `Plugins.BitwardenSecretManagerProvider`, bitwarden bindata assets | Bitwarden SDK plugin workload | | ||
| | **WebhookTLS_Agent** | `certificates.go`, webhook deployments, trusted CA | Webhook TLS paths | | ||
| | **RBACSecurity_Agent** | `config/rbac/`, `rbacs.go`, `networkpolicy.go` | RBAC and network policy | | ||
| | **OLMRelease_Agent** | `bundle/`, `config/manifests/` | CSV, relatedImages | | ||
| | **Testing_Agent** | `test/e2e/`, `test/apis/` | Test authoring | | ||
| | **Docs_Agent** | `README.md`, `docs/` | User-facing docs | | ||
|
|
||
| Full routing detail: `AGENTS.md` (repo root). | ||
|
|
||
| ## Governance | ||
|
|
||
| - This constitution supersedes ad-hoc conventions for downstream Planning, Task Creation, and Code Generation agents. | ||
| - **Amendments:** require documented evidence of repo change; bump Version and Last Amended date. | ||
| - **Conflicts:** if spec contradicts constitution, escalate in plan.md §8 — do not silently override. Forking upstream external-secrets logic into the operator is a constitution violation. | ||
| - **Companion docs:** | ||
| - **AGENTS.md** takes precedence for agent routing, controller map, Make targets, and test patterns. | ||
| - **README.md** takes precedence for human-facing install and contributing procedures. | ||
| - **This constitution** takes precedence for architectural principles and non-negotiable guardrails. | ||
| - **Complexity:** new patterns must justify deviation from existing repo conventions. Adding a second controller framework or SSA-first operand reconciliation requires constitution amendment. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # Stage evals (quality gates) | ||
|
|
||
| Operator-owned eval rubrics used by `/opsx-continue` and `/opsx-apply`. | ||
|
|
||
| | File | Used by | | ||
| |------|---------| | ||
| | `repo-assessment_eval.yaml` | `/opsx-continue` (repo-assessment) | | ||
| | `plan_eval.yaml` | `/opsx-continue` (plan) | | ||
| | `tasks_eval.yaml` | `/opsx-continue` (tasks) | | ||
| | `code-generation_eval.yaml` | `/opsx-apply` (per-task, ai-helpers mode) | | ||
|
|
||
| - Empty `evals: []` means the stage gate is present but has no cases yet — scoring is a no-op until `/eval-loop` populates cases. | ||
| - `/eval-loop` auto-syncs generated cases from `eval-generation/output-evals/` into this directory. | ||
| - Do not edit schema package evals for forward workflow; this directory is the source of truth. | ||
|
|
||
| See: https://github.com/sujkini/openspec/blob/v2-restructured/README.md |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Update the no-case guidance.
The evaluation suite now contains cases in
harness-evals/evals/plan_eval.yaml,harness-evals/evals/repo-assessment_eval.yaml, andharness-evals/evals/tasks_eval.yaml. These statements incorrectly describe scoring as optional or a no-op.AGENTS.md#L89-L89: state that stage evaluations are populated and active when the OpenSpec workflow loads these files.harness-evals/README.md#L16-L16: replace the “Stub files seeded” statement with the current populated-case status.harness-evals/README.md#L25-L25: remove the statement that forward workflow skips evaluation scoring until cases exist.📍 Affects 2 files
AGENTS.md#L89-L89(this comment)harness-evals/README.md#L16-L16harness-evals/README.md#L25-L25🤖 Prompt for AI Agents