From a451d124883f6e256f4a109475321e8a067417da Mon Sep 17 00:00:00 2001 From: Benjamin Borbe Date: Wed, 21 Oct 2026 21:53:11 +0200 Subject: [PATCH 1/5] move prompt to completed --- ...helm-cluster-assigner-exclude-assignees.md | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 prompts/completed/245-helm-cluster-assigner-exclude-assignees.md diff --git a/prompts/completed/245-helm-cluster-assigner-exclude-assignees.md b/prompts/completed/245-helm-cluster-assigner-exclude-assignees.md new file mode 100644 index 0000000..acde046 --- /dev/null +++ b/prompts/completed/245-helm-cluster-assigner-exclude-assignees.md @@ -0,0 +1,98 @@ +--- +status: completed +execution_id: agent-cluster-assigner-exclude-env-exec-245-helm-cluster-assigner-exclude-assignees +dark-factory-version: v0.198.7 +created: "2026-10-21T18:54:17Z" +queued: "2026-10-21T18:54:17Z" +started: "2026-10-21T18:54:27Z" +completed: "2026-10-21T19:53:10Z" +--- +# feat(helm): cluster-assigner passes EXCLUDE_ASSIGNEES + + +- An operator can name agents whose tasks the cluster assigner must never send to the sun cluster, straight from the chart values +- The list reaches the assigner as its `EXCLUDE_ASSIGNEES` setting (agent-cluster-assigner v0.1.3 and later) +- Leaving the list empty changes nothing: existing installs render exactly what they render today +- The values reference, chart version and changelog are updated + + + +Add an optional `clusterAssigner.excludeAssignees` value (a YAML list of strings, default `[]`) to the Helm chart in `helm/`. When it is non-empty, the cluster-assigner CronJob container gets env `EXCLUDE_ASSIGNEES` set to the entries joined with `,`. When it is empty, no env entry is rendered, so every existing render stays byte-identical apart from the `helm.sh/chart` version label. + + + +- `helm/templates/cluster-assigner-cronjob.yaml` — the CronJob. Its container `env:` list already renders `SUN_ROUTING` and `DRY_RUN` from `$ca` (`{{- $ca := .Values.clusterAssigner }}`). Add the new entry directly after `DRY_RUN`. +- `helm/values.yaml` — the `clusterAssigner:` block (`sunRouting`, `dryRun`, `sun:` …). Add `excludeAssignees: []` after `dryRun`, with a comment in the file's style. +- `helm/README.md` — the `### clusterAssigner (optional)` table, which has a row for `clusterAssigner.sunRouting` / `dryRun`. Add one row after it. +- `helm/Chart.yaml` — currently `version: 0.13.0`. +- `CHANGELOG.md` — no `## Unreleased` section exists yet if the top heading is a version; add one above it. +- `prompts/completed/244-helm-cluster-assigner-sun-secret.md` — previous prompt on this component; its `` shows the helm install precondition and the `/tmp/ca-base.yaml` render base reused below. +- The binary (bborbe/agent-cluster-assigner) reads `EXCLUDE_ASSIGNEES` as a comma-separated list; tasks of a listed assignee are never newly tagged sun. + + + +1. **Snapshot first.** Install helm per the `` precondition, then `rm -rf /tmp/helm-orig && cp -r helm /tmp/helm-orig` before editing. Create `/tmp/ca-base.yaml`: + ```yaml + namespace: test + executor: + kafkaBrokers: kafka:9092 + sentry: + dsn: dummy + ``` +2. **`helm/values.yaml`.** In the `clusterAssigner:` block, after `dryRun`, add: + ```yaml + # Assignees whose tasks are never newly tagged sun; they get their stage + # default (→ EXCLUDE_ASSIGNEES, comma-joined). Empty renders no env entry. + # Needs image.tag v0.1.3 or later; older images ignore the variable. + excludeAssignees: [] + ``` +3. **`helm/templates/cluster-assigner-cronjob.yaml`.** Directly after the `DRY_RUN` env entry, add an entry rendered only when the list is non-empty, matching the surrounding indentation exactly: + ```yaml + {{- with $ca.excludeAssignees }} + - name: EXCLUDE_ASSIGNEES + value: {{ join "," . | quote }} + {{- end }} + ``` +4. **`helm/README.md`.** In the `### clusterAssigner (optional)` table, add this exact row directly after the `sunRouting` / `dryRun` row: + ```markdown + | `clusterAssigner.excludeAssignees` | `[]` | Assignees never newly tagged sun (→ `EXCLUDE_ASSIGNEES`, comma-joined; needs `image.tag` v0.1.3 or later). Empty renders nothing. | + ``` +5. **`helm/Chart.yaml`.** `version: 0.13.0` → `version: 0.14.0`. Leave `appVersion` unchanged. +6. **`CHANGELOG.md`.** Add a `## Unreleased` section above the top version heading (or reuse an existing `## Unreleased`) with this bullet: + ```markdown + - feat(helm): `clusterAssigner.excludeAssignees` (list, default `[]`) renders `EXCLUDE_ASSIGNEES` on the cluster-assigner CronJob, comma-joined; tasks of a listed assignee are never newly tagged sun; an empty list renders nothing; chart 0.13.0→0.14.0 + ``` + + + +- Do NOT commit. +- Touch only `helm/values.yaml`, `helm/templates/cluster-assigner-cronjob.yaml`, `helm/README.md`, `helm/Chart.yaml`, `CHANGELOG.md`. No Go code, no mocks, no other template. +- No other new values keys. +- An empty list must render no `EXCLUDE_ASSIGNEES` entry at all, not an empty string. + + + +Precondition: the container does not ship helm; install it from the Go proxy. If this fails, report the prompt as FAILED and never skip the render checks: +`command -v helm >/dev/null 2>&1 || { go install helm.sh/helm/v3/cmd/helm@v3.16.4; export PATH="$PATH:$(go env GOPATH)/bin"; }` +`helm version --short | grep -q '^v3\.' || { echo "PRECONDITION FAILED: helm v3 unavailable in container"; exit 1; }` + +Run every render check below inside ONE `bash <<'EOS' … EOS` block, because the tool shell is zsh and `$B` relies on word-splitting. Every command must exit 0 and every `diff` must print nothing: +```bash +B="-f /tmp/ca-base.yaml --set clusterAssigner.enabled=true --set clusterAssigner.kafkaBrokers=b:1 --set clusterAssigner.gitRestURL=http://g" +f() { grep -v 'helm.sh/chart:'; } +# disabled: unchanged +diff <(helm template x /tmp/helm-orig -f /tmp/ca-base.yaml | f) <(helm template x helm/ -f /tmp/ca-base.yaml | f) +# enabled, list empty: unchanged, no env entry +diff <(helm template x /tmp/helm-orig $B | f) <(helm template x helm/ $B | f) +! helm template x helm/ $B | grep -q EXCLUDE_ASSIGNEES +# one entry +helm template x helm/ $B --set-json 'clusterAssigner.excludeAssignees=["releaser"]' --show-only templates/cluster-assigner-cronjob.yaml | grep -A1 'name: EXCLUDE_ASSIGNEES$' | grep -qx ' value: "releaser"' +# two entries, comma-joined +helm template x helm/ $B --set-json 'clusterAssigner.excludeAssignees=["releaser","claude"]' --show-only templates/cluster-assigner-cronjob.yaml | grep -A1 'name: EXCLUDE_ASSIGNEES$' | grep -qx ' value: "releaser,claude"' +helm lint helm/ -f /tmp/ca-base.yaml +helm lint helm/ $B --set-json 'clusterAssigner.excludeAssignees=["releaser"]' +grep -qx 'version: 0.14.0' helm/Chart.yaml +awk '/^## /{sec=$0} /excludeAssignees/{print sec; exit}' CHANGELOG.md | grep -qx '## Unreleased' +``` +Then run `ROOTDIR=/workspace make precommit`. If its ONLY failure is the govulncheck finding GO-2026-6809 (Go standard library crypto/x509 in the container's Go 1.27.2, fixed in 1.27.3), it is an environment issue unrelated to this chart-only change: record it in the summary and report success. Any other precommit failure means FAILED. + From 4b6fa1532a89cabc58b43bac8a9aca27eca95389 Mon Sep 17 00:00:00 2001 From: Benjamin Borbe Date: Wed, 21 Oct 2026 21:53:12 +0200 Subject: [PATCH 2/5] feat(helm): cluster-assigner passes EXCLUDE_ASSIGNEES --- CHANGELOG.md | 4 ++++ helm/Chart.yaml | 2 +- helm/README.md | 1 + helm/templates/cluster-assigner-cronjob.yaml | 4 ++++ helm/values.yaml | 4 ++++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ef10c5..9f93cef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Please choose versions by [Semantic Versioning](http://semver.org/). * MINOR version when you add functionality in a backwards-compatible manner, and * PATCH version when you make backwards-compatible bug fixes. +## Unreleased + +- feat(helm): `clusterAssigner.excludeAssignees` (list, default `[]`) renders `EXCLUDE_ASSIGNEES` on the cluster-assigner CronJob, comma-joined; tasks of a listed assignee are never newly tagged sun; an empty list renders nothing; chart 0.13.0→0.14.0 + ## v0.114.0 - feat(helm): `clusterAssigner` can render its own sun Secret `cluster-assigner-sun` (keys `sun-token` / `sun-ca-data`) from new values `clusterAssigner.sun.token` / `sun.caData`, supplied at apply time via `--set-json` from TeamVault; rendered only when `sunRouting` is on and `sun.secretName` is empty, and `SUN_TOKEN` / `SUN_CA_DATA` then reference it; installs setting `sun.secretName` render unchanged; sun routing with neither fails the render naming the missing value; chart 0.12.0→0.13.0. diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 201872d..6969d4e 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -3,7 +3,7 @@ name: agent description: bborbe agent framework — event-driven, Kafka-based, K8s-native task platform (controller, executor, and example agents) type: application # Chart version — bumped on chart changes, independent of appVersion. -version: 0.13.0 +version: 0.14.0 # Default image tag for the framework components when values don't override. appVersion: "0.3.1" home: https://github.com/bborbe/agent diff --git a/helm/README.md b/helm/README.md index a1ab8fe..2da8b96 100644 --- a/helm/README.md +++ b/helm/README.md @@ -280,6 +280,7 @@ the OpenClaw vault by setting `cluster:` frontmatter — it publishes the task c | `clusterAssigner.vault` / `taskGlob` | `openclaw` / `tasks/*.md` | Vault name + task file glob. | | `clusterAssigner.defaultClusterProd` / `defaultClusterDev` | `nukeprod` / `nukedev` | Cluster assigned to unstarted tasks per environment. | | `clusterAssigner.sunRouting` / `dryRun` | `false` / `false` | Route via the sun API / log instead of publishing. | +| `clusterAssigner.excludeAssignees` | `[]` | Assignees never newly tagged sun (→ `EXCLUDE_ASSIGNEES`, comma-joined; needs `image.tag` v0.2.0 or later). Empty renders nothing. | | `clusterAssigner.sun.apiURL` / `sun.namespace` / `sun.secretName` | `""` / `a2a` / `""` | Sun API base URL, namespace, and an existing Secret with keys `sun-token` / `sun-ca-data`. Empty `secretName` = the chart renders Secret `cluster-assigner-sun` from `sun.token` / `sun.caData`. | | `clusterAssigner.sun.token` / `sun.caData` | `""` / `""` | Sun API token and CA data. With `sunRouting` on and no `sun.secretName`, both are required and rendered into Secret `cluster-assigner-sun`; pass them at apply time via `--set-json` from TeamVault, never commit them. | | `clusterAssigner.kafkaUser.*` | as `recurringTaskCreator.kafkaUser` | Strimzi `KafkaUser` + mTLS cert mounts; derived user name = `-cluster-assigner`. | diff --git a/helm/templates/cluster-assigner-cronjob.yaml b/helm/templates/cluster-assigner-cronjob.yaml index b15fdf1..3d5582f 100644 --- a/helm/templates/cluster-assigner-cronjob.yaml +++ b/helm/templates/cluster-assigner-cronjob.yaml @@ -79,6 +79,10 @@ spec: value: {{ $ca.sunRouting | quote }} - name: DRY_RUN value: {{ $ca.dryRun | quote }} + {{- with $ca.excludeAssignees }} + - name: EXCLUDE_ASSIGNEES + value: {{ join "," . | quote }} + {{- end }} - name: SUN_API_URL value: {{ $ca.sun.apiURL | quote }} - name: SUN_NAMESPACE diff --git a/helm/values.yaml b/helm/values.yaml index 7c5d662..2afee7f 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -558,6 +558,10 @@ clusterAssigner: sunRouting: false # Log the commands instead of publishing them. dryRun: false + # Assignees whose tasks are never newly tagged sun; they get their stage + # default (→ EXCLUDE_ASSIGNEES, comma-joined). Empty renders no env entry. + # Needs image.tag v0.2.0 or later; older images ignore the variable. + excludeAssignees: [] sun: # Sun routing API base URL (SUN_API_URL). apiURL: "" From b1f8e728374428270767e9819b1164faa7039f5c Mon Sep 17 00:00:00 2001 From: Benjamin Borbe Date: Wed, 21 Oct 2026 23:05:43 +0200 Subject: [PATCH 3/5] fix: bump chart to 0.15.0 (0.14.0 already released) --- CHANGELOG.md | 2 +- helm/Chart.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f624e3..fd6a2be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Please choose versions by [Semantic Versioning](http://semver.org/). ## Unreleased -- feat(helm): `clusterAssigner.excludeAssignees` (list, default `[]`) renders `EXCLUDE_ASSIGNEES` on the cluster-assigner CronJob, comma-joined; tasks of a listed assignee are never newly tagged sun; an empty list renders nothing; chart 0.13.0→0.14.0 +- feat(helm): `clusterAssigner.excludeAssignees` (list, default `[]`) renders `EXCLUDE_ASSIGNEES` on the cluster-assigner CronJob, comma-joined; tasks of a listed assignee are never newly tagged sun; an empty list renders nothing; chart 0.14.0→0.15.0 - feat(helm): optional `executor.taskVault` / `executors[].taskVault` renders a `TASK_VAULT` env on the executor (the vault whose task events it owns, matched against the controller-stamped `Task.Vault`, executor >= v0.24.0); omitted when unset, so existing installs render unchanged; chart 0.13.0→0.14.0 ## v0.114.0 diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 6969d4e..3d5578b 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -3,7 +3,7 @@ name: agent description: bborbe agent framework — event-driven, Kafka-based, K8s-native task platform (controller, executor, and example agents) type: application # Chart version — bumped on chart changes, independent of appVersion. -version: 0.14.0 +version: 0.15.0 # Default image tag for the framework components when values don't override. appVersion: "0.3.1" home: https://github.com/bborbe/agent From 9f1b3699c574d8f1c0a9c42b90c98152cc29e38d Mon Sep 17 00:00:00 2001 From: Benjamin Borbe Date: Thu, 22 Oct 2026 01:57:55 +0200 Subject: [PATCH 4/5] feat(helm): default clusterAssigner image to v0.2.0, cover EXCLUDE_ASSIGNEES renders clusterAssigner.image.tag default v0.1.0 -> v0.2.0 so excludeAssignees is not a silent no-op on chart defaults; v0.2.0 is the first tag that honours EXCLUDE_ASSIGNEES. Extend hack/test-helm-render.sh with the empty-list (no env entry) and two-element (comma-joined) cases, and split the CHANGELOG entry back under ## Unreleased after the v0.116.0 fold. --- CHANGELOG.md | 5 ++- hack/test-helm-render.sh | 69 +++++++++++++++++++++++++++++++++++++++- helm/README.md | 4 +-- helm/values.yaml | 7 ++-- 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a5bf34..453aea1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,12 @@ Please choose versions by [Semantic Versioning](http://semver.org/). * MINOR version when you add functionality in a backwards-compatible manner, and * PATCH version when you make backwards-compatible bug fixes. +## Unreleased + +- feat(helm): `clusterAssigner.excludeAssignees` (list, default `[]`) renders `EXCLUDE_ASSIGNEES` on the cluster-assigner CronJob, comma-joined; tasks of a listed assignee are never newly tagged sun; an empty list renders nothing; `clusterAssigner.image.tag` default raised v0.1.0→v0.2.0 so the value works without an override; `hack/test-helm-render.sh` covers the empty and two-element renders; chart 0.14.1→0.15.0 + ## v0.116.0 -- feat(helm): `clusterAssigner.excludeAssignees` (list, default `[]`) renders `EXCLUDE_ASSIGNEES` on the cluster-assigner CronJob, comma-joined; tasks of a listed assignee are never newly tagged sun; an empty list renders nothing; chart 0.14.1→0.15.0 - feat(helm): render `rerunOnLostClaim` on the agent Config whenever `agents[].rerunOnLostClaim` is set, `false` included (presence test via `hasKey`, not truthiness), as a bare bool; nothing renders when unset, so no existing agent's Config changes; new `hack/test-helm-render.sh`, run by `make test`, asserts the false/true/unset renders; chart 0.14.0→0.14.1. Takes effect only with a Config CRD that declares `spec.rerunOnLostClaim` (older CRDs prune the field) and an agent-task-executor that reads it; older executors ignore the field. ## v0.115.0 diff --git a/hack/test-helm-render.sh b/hack/test-helm-render.sh index f91261c..c74d322 100755 --- a/hack/test-helm-render.sh +++ b/hack/test-helm-render.sh @@ -5,7 +5,12 @@ # # Renders helm/templates/agents.yaml and asserts that the optional per-agent # rerunOnLostClaim field renders whenever it is present — including `false` — -# and not at all when unset. Run from repo root. +# and not at all when unset. +# +# Also renders helm/templates/cluster-assigner-cronjob.yaml and asserts that +# clusterAssigner.excludeAssignees renders EXCLUDE_ASSIGNEES comma-joined for a +# non-empty list and no env entry at all for the empty default. +# Run from repo root. set -euo pipefail @@ -106,6 +111,68 @@ run_case false "$tmpdir/false.yaml" false run_case true "$tmpdir/true.yaml" true run_case unset "$tmpdir/unset.yaml" unset +# clusterAssigner.excludeAssignees → EXCLUDE_ASSIGNEES + +# write_ca_values +write_ca_values() { + local file="$1" + local exclude="$2" + { + cat <<'EOF' +namespace: test +executor: + kafkaBrokers: kafka:9092 + sentry: + dsn: dummy +clusterAssigner: + enabled: true + kafkaBrokers: kafka:9092 + gitRestURL: http://git-rest +EOF + printf ' excludeAssignees: %s\n' "$exclude" + } >"$file" +} + +# run_ca_case +run_ca_case() { + local name="$1" + local values="$2" + local expect="$3" + local out="$tmpdir/$name.out" + + "$helm_bin" template x "$CHART_DIR" -f "$values" \ + --show-only templates/cluster-assigner-cronjob.yaml >"$out" + + if [ "$expect" = "empty" ]; then + if grep -q 'EXCLUDE_ASSIGNEES' "$out"; then + echo "FAIL: $name: unexpected EXCLUDE_ASSIGNEES entry for an empty list" + fail=1 + return + fi + echo "OK: $name" + return + fi + + if ! grep -qx ' - name: EXCLUDE_ASSIGNEES' "$out"; then + echo "FAIL: $name: missing 'EXCLUDE_ASSIGNEES' env entry" + fail=1 + return + fi + if ! grep -qxF " value: \"$expect\"" "$out"; then + echo "FAIL: $name: EXCLUDE_ASSIGNEES value is not \"$expect\"" + fail=1 + return + fi + + echo "OK: $name" +} + +write_ca_values "$tmpdir/ca-empty.yaml" "[]" +write_ca_values "$tmpdir/ca-two.yaml" "[releaser, claude]" + +run_ca_case ca-empty "$tmpdir/ca-empty.yaml" empty +run_ca_case ca-two "$tmpdir/ca-two.yaml" "releaser,claude" + if [ "$fail" -ne 0 ]; then exit 1 fi diff --git a/helm/README.md b/helm/README.md index b38113d..1d8afc5 100644 --- a/helm/README.md +++ b/helm/README.md @@ -278,7 +278,7 @@ the OpenClaw vault by setting `cluster:` frontmatter — it publishes the task c | Key | Default | Description | |---|---|---| | `clusterAssigner.enabled` | `false` | Deploy the cluster-assigner CronJob (nothing renders when false). | -| `clusterAssigner.image.repository` / `image.tag` | `bborbe/agent-cluster-assigner` / `v0.1.0` | Image; versioned independently of the chart (no `appVersion` fallback). | +| `clusterAssigner.image.repository` / `image.tag` | `bborbe/agent-cluster-assigner` / `v0.2.0` | Image; versioned independently of the chart (no `appVersion` fallback). | | `clusterAssigner.schedule` / `successfulJobsHistoryLimit` / `failedJobsHistoryLimit` | `*/5 * * * *` / `1` / `3` | CronJob schedule and history. Overlapping runs are forbidden. | | `clusterAssigner.kafkaBrokers` | `""` | Kafka bootstrap brokers. Required when enabled. | | `clusterAssigner.topicPrefix` | `""` | Prefix for `-agent-task-v1-request` (nuke prod: `master`). | @@ -287,7 +287,7 @@ the OpenClaw vault by setting `cluster:` frontmatter — it publishes the task c | `clusterAssigner.vault` / `taskGlob` | `openclaw` / `tasks/*.md` | Vault name + task file glob. | | `clusterAssigner.defaultClusterProd` / `defaultClusterDev` | `nukeprod` / `nukedev` | Cluster assigned to unstarted tasks per environment. | | `clusterAssigner.sunRouting` / `dryRun` | `false` / `false` | Route via the sun API / log instead of publishing. | -| `clusterAssigner.excludeAssignees` | `[]` | Assignees never newly tagged sun (→ `EXCLUDE_ASSIGNEES`, comma-joined; needs `image.tag` v0.2.0 or later). Empty renders nothing. | +| `clusterAssigner.excludeAssignees` | `[]` | Assignees never newly tagged sun (→ `EXCLUDE_ASSIGNEES`, comma-joined; needs `image.tag` v0.2.0 or later, which is the default). Empty renders nothing. | | `clusterAssigner.sun.apiURL` / `sun.namespace` / `sun.secretName` | `""` / `a2a` / `""` | Sun API base URL, namespace, and an existing Secret with keys `sun-token` / `sun-ca-data`. Empty `secretName` = the chart renders Secret `cluster-assigner-sun` from `sun.token` / `sun.caData`. | | `clusterAssigner.sun.token` / `sun.caData` | `""` / `""` | Sun API token and CA data. With `sunRouting` on and no `sun.secretName`, both are required and rendered into Secret `cluster-assigner-sun`; pass them at apply time via `--set-json` from TeamVault, never commit them. | | `clusterAssigner.kafkaUser.*` | as `recurringTaskCreator.kafkaUser` | Strimzi `KafkaUser` + mTLS cert mounts; derived user name = `-cluster-assigner`. | diff --git a/helm/values.yaml b/helm/values.yaml index a6310f0..cfaab3d 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -537,8 +537,9 @@ clusterAssigner: image: repository: bborbe/agent-cluster-assigner # Versioned independently of the chart (like a2aWorkerController) — no - # appVersion fallback. - tag: v0.1.0 + # appVersion fallback. v0.2.0 is the first tag that honours + # EXCLUDE_ASSIGNEES; anything older silently ignores it. + tag: v0.2.0 logLevel: "2" schedule: "*/5 * * * *" successfulJobsHistoryLimit: 1 @@ -571,7 +572,7 @@ clusterAssigner: dryRun: false # Assignees whose tasks are never newly tagged sun; they get their stage # default (→ EXCLUDE_ASSIGNEES, comma-joined). Empty renders no env entry. - # Needs image.tag v0.2.0 or later; older images ignore the variable. + # Needs image.tag v0.2.0 or later (the default); older images ignore it. excludeAssignees: [] sun: # Sun routing API base URL (SUN_API_URL). From 21d6928d73dd51af4e0067683ed23f4d3e316020 Mon Sep 17 00:00:00 2001 From: Benjamin Borbe Date: Thu, 22 Oct 2026 03:03:39 +0200 Subject: [PATCH 5/5] test(helm): cover absent, one-element and default-tag renders for excludeAssignees Add the cases the review asked for: an omitted excludeAssignees key (older values files) renders no env entry, a single-element list joins to itself, and every cluster-assigner render pins the v0.2.0 default image tag so the feature cannot silently regress to a tag that ignores EXCLUDE_ASSIGNEES. Document that entries must be comma-free, since the value is comma-joined and re-split. --- CHANGELOG.md | 2 +- hack/test-helm-render.sh | 19 +++++++++++++++++-- helm/README.md | 2 +- helm/values.yaml | 1 + 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 453aea1..70c317d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Please choose versions by [Semantic Versioning](http://semver.org/). ## Unreleased -- feat(helm): `clusterAssigner.excludeAssignees` (list, default `[]`) renders `EXCLUDE_ASSIGNEES` on the cluster-assigner CronJob, comma-joined; tasks of a listed assignee are never newly tagged sun; an empty list renders nothing; `clusterAssigner.image.tag` default raised v0.1.0→v0.2.0 so the value works without an override; `hack/test-helm-render.sh` covers the empty and two-element renders; chart 0.14.1→0.15.0 +- feat(helm): `clusterAssigner.excludeAssignees` (list, default `[]`) renders `EXCLUDE_ASSIGNEES` on the cluster-assigner CronJob, comma-joined; tasks of a listed assignee are never newly tagged sun; an empty or absent list renders nothing; `clusterAssigner.image.tag` default raised v0.1.0→v0.2.0 so the value works without an override; `hack/test-helm-render.sh` covers the absent, empty, one-element and two-element renders and pins the v0.2.0 default tag; chart 0.14.1→0.15.0 ## v0.116.0 diff --git a/hack/test-helm-render.sh b/hack/test-helm-render.sh index c74d322..6b41242 100755 --- a/hack/test-helm-render.sh +++ b/hack/test-helm-render.sh @@ -113,7 +113,7 @@ run_case unset "$tmpdir/unset.yaml" unset # clusterAssigner.excludeAssignees → EXCLUDE_ASSIGNEES -# write_ca_values +# write_ca_values write_ca_values() { local file="$1" local exclude="$2" @@ -129,7 +129,9 @@ clusterAssigner: kafkaBrokers: kafka:9092 gitRestURL: http://git-rest EOF - printf ' excludeAssignees: %s\n' "$exclude" + if [ -n "$exclude" ]; then + printf ' excludeAssignees: %s\n' "$exclude" + fi } >"$file" } @@ -143,6 +145,15 @@ run_ca_case() { "$helm_bin" template x "$CHART_DIR" -f "$values" \ --show-only templates/cluster-assigner-cronjob.yaml >"$out" + # Pin the chart default introduced alongside this feature: on defaults the + # assigner must run a tag that honours EXCLUDE_ASSIGNEES, otherwise the + # value is a silent no-op. + if ! grep -q 'agent-cluster-assigner:v0\.2\.0' "$out"; then + echo "FAIL: $name: default image tag is not v0.2.0" + fail=1 + return + fi + if [ "$expect" = "empty" ]; then if grep -q 'EXCLUDE_ASSIGNEES' "$out"; then echo "FAIL: $name: unexpected EXCLUDE_ASSIGNEES entry for an empty list" @@ -168,9 +179,13 @@ run_ca_case() { } write_ca_values "$tmpdir/ca-empty.yaml" "[]" +write_ca_values "$tmpdir/ca-one.yaml" "[releaser]" write_ca_values "$tmpdir/ca-two.yaml" "[releaser, claude]" +write_ca_values "$tmpdir/ca-absent.yaml" "" run_ca_case ca-empty "$tmpdir/ca-empty.yaml" empty +run_ca_case ca-absent "$tmpdir/ca-absent.yaml" empty +run_ca_case ca-one "$tmpdir/ca-one.yaml" "releaser" run_ca_case ca-two "$tmpdir/ca-two.yaml" "releaser,claude" if [ "$fail" -ne 0 ]; then diff --git a/helm/README.md b/helm/README.md index 1d8afc5..becc823 100644 --- a/helm/README.md +++ b/helm/README.md @@ -287,7 +287,7 @@ the OpenClaw vault by setting `cluster:` frontmatter — it publishes the task c | `clusterAssigner.vault` / `taskGlob` | `openclaw` / `tasks/*.md` | Vault name + task file glob. | | `clusterAssigner.defaultClusterProd` / `defaultClusterDev` | `nukeprod` / `nukedev` | Cluster assigned to unstarted tasks per environment. | | `clusterAssigner.sunRouting` / `dryRun` | `false` / `false` | Route via the sun API / log instead of publishing. | -| `clusterAssigner.excludeAssignees` | `[]` | Assignees never newly tagged sun (→ `EXCLUDE_ASSIGNEES`, comma-joined; needs `image.tag` v0.2.0 or later, which is the default). Empty renders nothing. | +| `clusterAssigner.excludeAssignees` | `[]` | Assignees never newly tagged sun (→ `EXCLUDE_ASSIGNEES`, comma-joined; needs `image.tag` v0.2.0 or later, which is the default). Empty renders nothing. Entries must not contain a comma. | | `clusterAssigner.sun.apiURL` / `sun.namespace` / `sun.secretName` | `""` / `a2a` / `""` | Sun API base URL, namespace, and an existing Secret with keys `sun-token` / `sun-ca-data`. Empty `secretName` = the chart renders Secret `cluster-assigner-sun` from `sun.token` / `sun.caData`. | | `clusterAssigner.sun.token` / `sun.caData` | `""` / `""` | Sun API token and CA data. With `sunRouting` on and no `sun.secretName`, both are required and rendered into Secret `cluster-assigner-sun`; pass them at apply time via `--set-json` from TeamVault, never commit them. | | `clusterAssigner.kafkaUser.*` | as `recurringTaskCreator.kafkaUser` | Strimzi `KafkaUser` + mTLS cert mounts; derived user name = `-cluster-assigner`. | diff --git a/helm/values.yaml b/helm/values.yaml index cfaab3d..91a82c3 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -573,6 +573,7 @@ clusterAssigner: # Assignees whose tasks are never newly tagged sun; they get their stage # default (→ EXCLUDE_ASSIGNEES, comma-joined). Empty renders no env entry. # Needs image.tag v0.2.0 or later (the default); older images ignore it. + # Entries must not contain a comma — the value is comma-joined and re-split. excludeAssignees: [] sun: # Sun routing API base URL (SUN_API_URL).