Skip to content

feat(helm): cluster-assigner passes EXCLUDE_ASSIGNEES - #127

Merged
bborbe merged 8 commits into
masterfrom
feat/cluster-assigner-exclude-assignees
Oct 22, 2026
Merged

bborbe merged 8 commits into
masterfrom
feat/cluster-assigner-exclude-assignees

Conversation

@bborbe

@bborbe bborbe commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Adds clusterAssigner.excludeAssignees (list, default []). When non-empty, the cluster-assigner CronJob gets EXCLUDE_ASSIGNEES set to the entries comma-joined. An empty list renders no env entry at all, so existing installs render byte-identically apart from the helm.sh/chart label. Chart 0.13.0 → 0.14.0.

Why: releaser tasks must stay off sun until the releaser's PR-fallback fix ships, and the assigner's only switch for that is EXCLUDE_ASSIGNEES (agent-cluster-assigner v0.2.0).

Verified on host: 9/9 checks pass — disabled unchanged, empty list unchanged and no env entry, one entry, two entries joined, helm lint both ways, chart version, CHANGELOG placement. make precommit green.

The dark-factory run reported failed only on the container's Go 1.27.2 govulncheck GO-2026-6809 (stdlib crypto/x509, fixed in 1.27.3); host precommit on Go 1.27.3 passes. Its stray mocks/mocks.go edit was reverted.

@bborbe
bborbe marked this pull request as ready for review October 21, 2026 20:04
…er-exclude-assignees

# Conflicts:
#	CHANGELOG.md

@ben-s-pull-request-reviewer ben-s-pull-request-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
  "verdict": "request-changes",
  "summary": "The Helm change itself is correct: `{{- with $ca.excludeAssignees }}` renders `EXCLUDE_ASSIGNEES` comma-joined only when the list is non-empty, values/README docs match the upstream binary (v0.2.0 introduced the variable), and the mechanical funnel plus selector adjudication found no rule violations. The one blocking defect is release versioning: the PR changes chart templates/values/README but does not bump `helm/Chart.yaml` (already 0.14.0 on master), while its CHANGELOG bullet claims `chart 0.13.0→0.14.0`, duplicating the preceding executor.taskVault PR's claim. (1 file, prompts/completed/245-helm-cluster-assigner-exclude-assignees.md, was excluded by .reviewignore and not reviewed.)",
  "comments": [
    {
      "file": "CHANGELOG.md",
      "line": 13,
      "severity": "major",
      "blocking": true,
      "blocking_reason": "The chart change ships without a chart version bump and the CHANGELOG claims a bump that already happened: helm/Chart.yaml on master is already version 0.14.0 (bumped in 2da38ef) and is not in this PR's diff, so two different chart changes now share one chart version and the new bullet's `chart 0.13.0→0.14.0` statement is false.",
      "message": "The bullet claims `chart 0.13.0→0.14.0`, but `helm/Chart.yaml` is already `version: 0.14.0` on master (bumped by 2da38ef, the `executor.taskVault` PR) and this PR does not touch `Chart.yaml` — the new bullet sits directly above that PR's bullet making the same claim. Per the repo's chart convention (\"Chart version — bumped on chart changes\"; feature → minor), bump `helm/Chart.yaml` to `0.15.0` and correct this bullet to `chart 0.14.0→0.15.0`."
    }
  ],
  "concerns_addressed": [
    {
      "concern": "correctness: Chart.yaml version bump missing while PR body/CHANGELOG claim 0.13.0→0.14.0",
      "disposition": "addressed",
      "detail": "Confirmed: origin/master helm/Chart.yaml is 0.14.0 (commit 2da38ef bumped 0.13.0→0.14.0) and Chart.yaml is absent from git diff origin/master...HEAD. Reported as a blocking comment on CHANGELOG.md:13 requiring a 0.15.0 bump."
    },
    {
      "concern": "correctness: two consecutive Unreleased bullets both state chart 0.13.0→0.14.0",
      "disposition": "addressed",
      "detail": "Confirmed at CHANGELOG.md:13 and CHANGELOG.md:14; the same blocking comment covers correcting this bullet so the two claims no longer collide."
    },
    {
      "concern": "correctness: no chart-side validation of excludeAssignees entry format (comma re-split, scalar via --set)",
      "disposition": "not-an-issue",
      "detail": "The chart has no values.schema.json at all (helm/ contains only Chart.yaml, README.md, crds, templates, values.yaml), so no schema/type guard convention exists to violate. Assignee values are GitHub handles, which cannot contain commas, and the README explicitly documents the comma-joined contract the binary parses. No finding."
    },
    {
      "concern": "correctness: README/values assert EXCLUDE_ASSIGNEES needs image.tag v0.2.0 or later (prompt said v0.1.3); verify against the actual release and the image.tag default",
      "disposition": "not-an-issue",
      "detail": "Verified upstream: bborbe/agent-cluster-assigner CHANGELOG shows `EXCLUDE_ASSIGNEES` introduced in v0.2.0, so the changed docs are correct and the excluded prompt's v0.1.3 was wrong. values.yaml:534-536 confirms image.tag has no appVersion fallback (default v0.1.0), and the docs correctly say v0.2.0+ is required."
    },
    {
      "concern": "tests: no automated helm render test for the new optional value / empty-list-renders-nothing guarantee",
      "disposition": "not-an-issue",
      "detail": "The repo has no Helm test or CI render harness to extend: .github/workflows/ci.yml has no helm step, the Makefile has no helm target, and helm/ has no tests directory. Verification is manual helm lint/template (recorded in the excluded prompt), matching every prior chart PR; not a new gap introduced here."
    }
  ]
}

@ben-s-pull-request-reviewer
ben-s-pull-request-reviewer Bot dismissed their stale review October 21, 2026 21:44

superseded by new automated review

@ben-s-pull-request-reviewer ben-s-pull-request-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
  "verdict": "approve",
  "summary": "Helm-only change (5 reviewable files; 1 file excluded by .reviewignore). Selector mode: 7 candidate rules, 4 applicable, 0 mechanical findings; no rule violations survived adjudication. The new `clusterAssigner.excludeAssignees` -> `EXCLUDE_ASSIGNEES` block renders correctly (empty list renders no entry, the following SUN_API_URL entry stays intact, values are quoted and YAML-safe, backward compatible with values files lacking the key), and Chart.yaml/CHANGELOG agree on chart 0.15.0. One substantive non-blocking gap: the chart still defaults `clusterAssigner.image.tag` to v0.1.0 while EXCLUDE_ASSIGNEES only exists from agent-cluster-assigner v0.2.0 (verified upstream), so the value is a documented-but-silent no-op unless the operator also bumps the tag; the PR body's '0.13.0 -> 0.14.0' is stale (code says 0.15.0). precommit skipped (selector mode) - CI covers lint+test.",
  "comments": [
    {
      "file": "helm/values.yaml",
      "line": 570,
      "severity": "major",
      "blocking": false,
      "blocking_reason": "",
      "message": "Should Fix: the new `excludeAssignees` value needs agent-cluster-assigner image v0.2.0 or later (verified against the upstream repo: v0.2.0 is the first tag containing the EXCLUDE_ASSIGNEES feature; there is no v0.1.3), but the chart still defaults `clusterAssigner.image.tag` to v0.1.0 (helm/values.yaml:536). An operator who sets `excludeAssignees` without also overriding `image.tag` gets a rendered env var that the running image silently ignores. The requirement is documented here and in helm/README.md, so this does not block, but consider bumping the default tag to the released v0.2.0, or adding a render-time `fail` when `excludeAssignees` is non-empty and the configured tag predates v0.2.0."
    },
    {
      "file": "helm/templates/cluster-assigner-cronjob.yaml",
      "line": 82,
      "severity": "nit",
      "blocking": false,
      "blocking_reason": "",
      "message": "Nice to Have: no automated render coverage exists for this whitespace-sensitive conditional block - the chart has no helm-unittest suite and CI runs neither `helm lint` nor `helm template`, so verification rests on the manual render checks recorded in the excluded prompt artifact. A small helm-unittest (or CI `helm template` smoke) asserting both the empty-list and non-empty cases would lock in the behaviour. The rendering itself is sound: `{{- with }}` / `{{- end }}` leave the following SUN_API_URL entry intact, and the pattern matches the existing `sentry.proxy` block."
    }
  ],
  "concerns_addressed": [
    {
      "concern": "correctness: helm/templates/cluster-assigner-cronjob.yaml - new env block uses `with` + `join \",\" . | quote`; verify empty list renders no entry, whitespace trimming leaves SUN_API_URL intact, and a single string via --set renders sanely",
      "disposition": "not-an-issue",
      "detail": "Verified against the template: an empty/missing list makes `with` falsy so no entry renders; `{{- with }}`/`{{- end }}` trim the surrounding newlines only, so `value: \"false\"` is followed directly by `- name: SUN_API_URL` and the entry survives; the block mirrors the already-shipped `{{- with .Values.sentry.proxy }}` pattern. A bare string passed via --set makes Helm's `join` fail loudly (fail-fast render error), not silently."
    },
    {
      "concern": "correctness: helm/values.yaml - docs say v0.2.0+, prompt artifact says v0.1.3; confirm which tag reads EXCLUDE_ASSIGNEES and check the chart default image tag",
      "disposition": "addressed",
      "detail": "Upstream check of bborbe/agent-cluster-assigner: v0.2.0 is the first tag containing 'feat: EXCLUDE_ASSIGNEES keeps listed assignees off sun'; no v0.1.3 tag exists (v0.1.2 predates the feature). The shipped docs' v0.2.0 claim is correct. The chart default is v0.1.0 (older), so enabling the value with the default tag is a silent no-op - reported as a non-blocking Should Fix comment on helm/values.yaml:570."
    },
    {
      "concern": "correctness: helm/Chart.yaml - PR description says 0.13.0 to 0.14.0 but diff bumps 0.14.0 to 0.15.0; confirm the release note is stale",
      "disposition": "not-an-issue",
      "detail": "helm/Chart.yaml:6 is version 0.15.0, the CHANGELOG bullet says chart 0.14.0->0.15.0, and commit b1f8e72 ('bump chart to 0.15.0, 0.14.0 already released') explains the extra bump. Only the PR body text is stale; the code is internally consistent."
    },
    {
      "concern": "correctness: prompts/completed/245-helm-cluster-assigner-exclude-assignees.md - artifact records 0.13.0->0.14.0 and image v0.1.3, contradicting the shipped chart and README",
      "disposition": "not-an-issue",
      "detail": "The file is excluded by .reviewignore (prompts/), so it is out of the reviewable diff. It is a historical dark-factory artifact capturing the original prompt; the shipped chart and docs were subsequently corrected to v0.2.0 and chart 0.15.0, and the requirement text is not consumed at runtime."
    },
    {
      "concern": "correctness: helm/templates/cluster-assigner-cronjob.yaml - comma-joined encoding is lossy if an assignee name contains a comma; no values.schema.json to constrain the list to strings",
      "disposition": "not-an-issue",
      "detail": "Assignee identifiers in this system are usernames/handles (e.g. 'releaser', 'claude'); a comma is not a plausible value. The chart ships no values.schema.json for any key, so this is consistent with the existing chart. Non-string elements via --set-json make Helm's `join` fail at render time rather than producing a corrupt value."
    },
    {
      "concern": "tests: helm/templates/cluster-assigner-cronjob.yaml - no automated render test (helm-unittest or CI snapshot); coverage rests on manual host checks",
      "disposition": "not-an-issue",
      "detail": "The repo has no helm test harness and no CI step running helm lint/template for any chart change, so this PR does not regress an existing gate. Raised as a non-blocking nit on the template line rather than a defect."
    },
    {
      "concern": "security: helm/templates/cluster-assigner-cronjob.yaml - operator-supplied values flow into an env var; confirm `quote` neutralizes YAML injection for every entry point",
      "disposition": "not-an-issue",
      "detail": "`join \",\" .` produces a single string and `quote` wraps the whole joined value, so embedded quotes/newlines are escaped and cannot break out of the YAML scalar. Both entry points (values.yaml and --set/--set-json) flow through the same `$ca.excludeAssignees` value and the same `quote` call; no unquoted interpolation of list items exists."
    }
  ]
}

@ben-s-pull-request-reviewer
ben-s-pull-request-reviewer Bot dismissed their stale review October 21, 2026 23:54

superseded by new automated review

@ben-s-pull-request-reviewer ben-s-pull-request-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
  "verdict": "approve",
  "summary": "The chart change is correct and safe: `EXCLUDE_ASSIGNEES` renders only for a non-empty `excludeAssignees` list, is YAML-quoted before interpolation, and leaves the sun token/CA secret rendering untouched. Two non-blocking gaps remain (chart default image tag is below the documented v0.2.0 requirement, and no render-regression test guards the empty-list invariant); selector mode found 7 candidates, 4 applicable, 3 skipped, mechanical funnel 0 findings, no rule violations.",
  "comments": [
    {
      "file": "helm/values.yaml",
      "line": 575,
      "severity": "major",
      "blocking": false,
      "blocking_reason": "",
      "message": "`clusterAssigner.image.tag` still defaults to `v0.1.0`, while the new value is documented (here and in helm/README.md:290) as needing `v0.2.0` or later. With chart defaults, setting `excludeAssignees` renders `EXCLUDE_ASSIGNEES` but the v0.1.0 binary ignores it, so listed assignees are still newly tagged sun — the feature silently no-ops for anyone who does not also override `image.tag`. Either bump the default tag to v0.2.0 (if it is published) or fail closed (e.g. a `fail` guard when `excludeAssignees` is non-empty and the tag is older than the required version) instead of relying on the docs alone."
    },
    {
      "file": "helm/templates/cluster-assigner-cronjob.yaml",
      "line": 82,
      "severity": "major",
      "blocking": false,
      "blocking_reason": "",
      "message": "No automated render test covers the new invariant. `hack/test-helm-render.sh` (run by `make test` via the `test-helm` target, Makefile:33) already guards the analogous `rerunOnLostClaim` chart feature, but this PR relies only on manual `helm template` checks. The key regression risk — `excludeAssignees: []` must render no `EXCLUDE_ASSIGNEES` entry at all, not an empty value — has no guard. Add a case to that script: `clusterAssigner.enabled: true` with required kafkaBrokers/gitRestURL, asserting no `EXCLUDE_ASSIGNEES` line for `[]` and `value: \"releaser,claude\"` for a two-element list."
    },
    {
      "file": "CHANGELOG.md",
      "line": 13,
      "severity": "nit",
      "blocking": false,
      "blocking_reason": "",
      "message": "This branch is behind origin/master, which already turned the merge-base `## Unreleased` heading into `## v0.116.0` (commit 00a16d0). The new bullet sits immediately under `## Unreleased` at the merge base, so merging master may conflict or place this feature entry under the already-released `## v0.116.0` heading, where the release tooling will not see it for the next version bump. Merge/rebase master and keep the bullet under a fresh `## Unreleased`."
    }
  ],
  "concerns_addressed": [
    {
      "concern": "correctness: `{{- with $ca.excludeAssignees }}` + `join \",\"` edge cases (empty list, null/missing key, empty/whitespace strings, non-string elements)",
      "disposition": "not-an-issue",
      "detail": "Examined helm/templates/cluster-assigner-cronjob.yaml:82-85. Go template truthiness treats a zero-length slice and a missing/nil key as false, so `[]` and an absent key render no env entry. Sprig `join` strvals each element, so ints are accepted; `| quote` YAML-escapes the result. A list containing an empty string (`[\"a\", \"\"]`) does yield `\"a,\"`, but that is operator-supplied input and the chart faithfully joins it; no doubled comma is produced. Whitespace is not trimmed (same operator-input caveat)."
    },
    {
      "concern": "correctness: Chart.yaml version / CHANGELOG drift versus PR body and checked-in prompt (0.13.0→0.14.0, v0.1.3 vs 0.14.1→0.15.0, v0.2.0)",
      "disposition": "not-an-issue",
      "detail": "The shipped artifacts are internally consistent: helm/Chart.yaml, CHANGELOG.md:13, helm/README.md:290 and helm/values.yaml:573-575 all say chart 0.14.1→0.15.0 and image v0.2.0. origin/master's Chart.yaml is 0.14.1, so 0.15.0 is an unused minor bump. The stale numbers live only in the PR description and the completed prompt (prompts/ is excluded by .reviewignore), not in reviewed code."
    },
    {
      "concern": "correctness: chart default clusterAssigner image tag versus the documented v0.2.0 requirement",
      "disposition": "addressed",
      "detail": "Verified helm/values.yaml:540 sets `tag: v0.1.0`, below the v0.2.0 the new value requires, so enabling excludeAssignees on defaults silently no-ops. Raised as a major comment on helm/values.yaml:575."
    },
    {
      "concern": "tests: no automated render test for the new env; check hack/test-helm-render.sh and values.schema.json",
      "disposition": "addressed",
      "detail": "hack/test-helm-render.sh exists and is wired into `make test` (Makefile:33) but only exercises templates/agents.yaml (rerunOnLostClaim); no case covers the new cluster-assigner env, and no helm/values.schema.json exists so there is no schema to extend. Raised as a major comment on the new template block."
    },
    {
      "concern": "correctness: README claim about when the env is rendered",
      "disposition": "not-an-issue",
      "detail": "helm/README.md:290 says the value is comma-joined into EXCLUDE_ASSIGNEES and that an empty value renders nothing, which matches the template: the entry is emitted only inside `{{- with $ca.excludeAssignees }}`, i.e. only for a non-empty list."
    },
    {
      "concern": "security: YAML injection via the comma-joined value; sun token/caData rendering untouched",
      "disposition": "not-an-issue",
      "detail": "The value is passed through `| quote` before interpolation, so it is emitted as a single YAML scalar and reaches the container as plain env data with no shell interpretation. The diff inserts the block between DRY_RUN and SUN_API_URL and does not modify the SUN_TOKEN/SUN_CA_DATA secretKeyRef block or the sun Secret rendering."
    }
  ]
}

…SIGNEES 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.
@ben-s-pull-request-reviewer
ben-s-pull-request-reviewer Bot dismissed their stale review October 22, 2026 01:01

superseded by new automated review

@ben-s-pull-request-reviewer ben-s-pull-request-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've completed the review. Verifying the last mechanical details confirmed: no tag on the PR head (so git-commit/feature-branch-no-tag passes), the render test is wired into make test, and the CHANGELOG entry sits correctly under ## Unreleased.

Review summary (selector mode):

Candidates: 6 rules matched by the Step 4b-i glob filter
Applicable: 3 rules selected
Skipped:

  • go-security/resource-ownership → no Go handler/service method in diff
  • go-security/tenant-isolation → no Go handler/service method in diff
  • markdown-todo/lowercase-x-for-complete → no markdown checkbox bullets added

Must Fix (Critical): None. Mechanical funnel reported 0 findings; no Go source changed.

Should Fix (Important):

  • hack/test-helm-render.sh:170 — only the empty-list and two-element cases are covered; no single-element case, no absent-key case, no assertion that the empty render is otherwise identical, and nothing pins the new v0.2.0 default.

Nice to Have (Optional):

  • helm/templates/cluster-assigner-cronjob.yaml:84 — comma-joined values are ambiguous (an assignee containing a comma re-splits; a list entry that is the empty string renders EXCLUDE_ASSIGNEES as "").

Notes: 1 file (prompts/completed/245-...md) excluded by .reviewignore. precommit skipped (selector mode) — CI covers lint+test. The PR body's "0.13.0→0.14.0 / byte-identical" description is stale versus the shipped 0.14.1→0.15.0 plus default image-tag bump; the CHANGELOG itself is accurate. The HEAD commit subject is 84 chars (SHOULD-tier git-commit/subject-under-50-chars), but merge/long scoped subjects are the repo norm and the funnel did not flag it.

{
  "verdict": "approve",
  "summary": "The change is a small, self-consistent Helm feature: `clusterAssigner.excludeAssignees` renders `EXCLUDE_ASSIGNEES` comma-joined only when non-empty, the chart bumps 0.14.1→0.15.0, the default cluster-assigner tag moves to v0.2.0 (documented in CHANGELOG, values.yaml and README), and the new render assertions are wired into `make test`. No blocking defect found; the remaining items are test-coverage gaps and a minor comma-encoding edge case. precommit skipped (selector mode) — CI covers lint+test. 1 file (prompts/completed/245-...md) excluded by .reviewignore. Selector traceability: 6 candidates, 3 applicable, 3 skipped (go-security/resource-ownership, go-security/tenant-isolation — no Go code in diff; markdown-todo/lowercase-x-for-complete — no checkbox bullets added).",
  "comments": [
    {
      "file": "hack/test-helm-render.sh",
      "line": 170,
      "severity": "major",
      "blocking": false,
      "blocking_reason": "",
      "message": "Should Fix (test gap): the new cases cover only an explicit empty list (`[]`) and a two-element list. There is no single-element case (the join path most likely to regress), no case where `excludeAssignees` is absent entirely (older values files / pure default), no assertion that the empty render is otherwise unchanged from the base chart, and nothing pins the new `v0.2.0` image-tag default introduced in this same PR. Add a one-element case and a values file omitting the key."
    },
    {
      "file": "helm/templates/cluster-assigner-cronjob.yaml",
      "line": 84,
      "severity": "nit",
      "blocking": false,
      "blocking_reason": "",
      "message": "Nice to Have: `join \",\"` makes the encoding ambiguous — an assignee containing a comma is re-split by the binary, and a list entry that is the empty string (`excludeAssignees: [\"\"]`) renders `EXCLUDE_ASSIGNEES` as an empty value rather than being skipped. This is acceptable if assignee identifiers are guaranteed comma-free; otherwise consider documenting the constraint or filtering empty entries. No `values.schema.json` exists to reject the wrong type, though sprig's `join` coerces a scalar string to a single-element list rather than erroring."
    }
  ],
  "concerns_addressed": [
    {
      "concern": "correctness: default clusterAssigner.image.tag bumped v0.1.0 -> v0.2.0, contradicting the PR body's byte-identical claim",
      "disposition": "not-an-issue",
      "detail": "The bump is deliberate and documented in three places: the CHANGELOG bullet (v0.116.0 → Unreleased) explicitly says the tag was raised so the value works without an override, values.yaml:539-542 explains v0.2.0 is the first tag honouring EXCLUDE_ASSIGNEES, and helm/README.md updates the default row. The chart bump 0.14.1→0.15.0 correctly signals a default-behavior change. Only the PR description is stale, not the code. The image's publication is verified at deploy time, not in this diff."
    },
    {
      "concern": "correctness: `with` + sprig `join` assumes a list; a scalar value could fail with a cryptic template error",
      "disposition": "not-an-issue",
      "detail": "Sprig's `join` delegates to `strslice`, whose default branch returns `[]string{strval(v)}` for a non-slice, non-nil value — a scalar string renders as itself rather than raising a template error. No values.schema.json exists, but the failure mode the concern predicts does not occur."
    },
    {
      "concern": "tests: committed cases cover only empty and two-element lists; no single-element, absent-key, identity or image-tag assertions",
      "disposition": "addressed",
      "detail": "Reported as a Should Fix comment on hack/test-helm-render.sh:170."
    },
    {
      "concern": "docs: CHANGELOG / PR body disagree on chart version and byte-identical renders",
      "disposition": "not-an-issue",
      "detail": "The CHANGELOG bullet matches the diff exactly (chart 0.14.1→0.15.0, tag v0.1.0→v0.2.0, empty list renders nothing). Only the PR description is stale; that is a process/description issue, not a code defect in the diff."
    },
    {
      "concern": "correctness: comma-joined values are ambiguous; empty-string entry renders an empty EXCLUDE_ASSIGNEES",
      "disposition": "addressed",
      "detail": "Reported as a Nice to Have comment on helm/templates/cluster-assigner-cronjob.yaml:84."
    },
    {
      "concern": "docs: prompts/completed/245-... still specifies chart 0.13.0->0.14.0 and image tag v0.1.3",
      "disposition": "not-an-issue",
      "detail": "That file is excluded by the repo's own .reviewignore (`prompts/`) and is a historical completed-prompt record, not shipped documentation; it does not affect the chart's behaviour or user-facing docs."
    }
  ]
}

…ludeAssignees

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.
@ben-s-pull-request-reviewer
ben-s-pull-request-reviewer Bot dismissed their stale review October 22, 2026 01:20

superseded by new automated review

@ben-s-pull-request-reviewer ben-s-pull-request-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
  "verdict": "approve",
  "summary": "The change is small and coherent: a `{{- with $ca.excludeAssignees }}` env block, a v0.1.0→v0.2.0 default image tag, matching Chart/README/CHANGELOG updates, and render tests wired into `make test` via `test-helm`. No blocking defect found; only three non-blocking nits. Selector traceability: 7 candidates, 5 applicable, 2 skipped (no rule violations); 1 file excluded by `.reviewignore`; precommit skipped (selector mode) — CI covers lint+test.",
  "comments": [
    {
      "file": "helm/templates/cluster-assigner-cronjob.yaml",
      "line": 82,
      "severity": "nit",
      "blocking": false,
      "blocking_reason": "",
      "message": "Nice to Have (Optional): a non-list value (`--set clusterAssigner.excludeAssignees=releaser` yields a string, which is truthy) reaches `join \",\" .` and aborts the render with a cryptic `wrong type for value; expected []interface {}` error. This is loud rather than silent, so it is acceptable, but the chart has no `values.schema.json` (confirmed: `helm/` contains only Chart.yaml, README.md, crds, templates, values.yaml) to type-check it earlier — worth a schema or a one-line note in values.yaml."
    },
    {
      "file": "helm/README.md",
      "line": 290,
      "severity": "nit",
      "blocking": false,
      "blocking_reason": "",
      "message": "Nice to Have (Optional): the row says entries 'must not contain a comma' but not what happens when they do — the entry is silently split into two assignees on the binary side, with no error. Stating the failure mode would make the restriction actionable."
    },
    {
      "file": "hack/test-helm-render.sh",
      "line": 167,
      "severity": "nit",
      "blocking": false,
      "blocking_reason": "",
      "message": "Nice to Have (Optional): the assertions pin exact indentation (16 spaces for the env name, 18 for the value line), so a purely cosmetic template reindent would fail the suite even though the rendered env entry is unchanged. Matching `- name: EXCLUDE_ASSIGNEES` and the value line without leading-whitespace coupling would make the test resilient."
    }
  ],
  "concerns_addressed": [
    {
      "concern": "correctness: helm/values.yaml — Default clusterAssigner.image.tag is raised v0.1.0 -> v0.2.0. Every existing install that enables the assigner and does not override the tag will silently deploy a different binary on upgrade. The PR body also claims existing installs render byte-identically apart from the helm.sh/chart label, which is false once the default tag changes. Verify the tag bump is intended, that v0.2.0 is published, and that the backward-compatibility wording is corrected.",
      "disposition": "not-an-issue",
      "detail": "Bump is intentional and consistently documented in helm/values.yaml:540-542, helm/README.md:281 and CHANGELOG.md:13. The 'byte-identical apart from helm.sh/chart' claim survives only in the .reviewignore-excluded archived prompt (prompts/completed/245-*.md); no shipped doc repeats it, and with the default clusterAssigner.enabled=false the CronJob renders nothing at all. Registry publication of v0.2.0 is external to this repo and could not be checked (no network in sandbox)."
    },
    {
      "concern": "correctness: helm/templates/cluster-assigner-cronjob.yaml — The new block relies on `with $ca.excludeAssignees` being falsy for an empty list and truthy for a populated one. Check the non-list path: a scalar supplied via --set (for example excludeAssignees=releaser) is truthy and then `join` fails at render time. There is no values.schema.json type guard mentioned; confirm whether the chart has one and whether it should be extended.",
      "disposition": "not-an-issue",
      "detail": "Confirmed: an empty slice is falsy in Go templates (`truth` uses Len() > 0), so `[]` and an absent key both render no entry, and a populated list renders it. A scalar is truthy and `join` fails loudly with a type error — no silent misrender. `ls helm/` confirms there is no values.schema.json to extend; noted as a nit rather than a defect."
    },
    {
      "concern": "correctness: helm/templates/cluster-assigner-cronjob.yaml — Entries are comma-joined, so an assignee containing a comma is silently split into two on the binary side. README and values.yaml document the restriction but nothing enforces it; consider whether `fail` on comma-containing entries is warranted.",
      "disposition": "not-an-issue",
      "detail": "The restriction is documented in both helm/values.yaml:576 and helm/README.md:290. Adding a `fail` would be a defensible hardening, but it is not required for correctness and no such guard exists elsewhere in the chart; left as a documentation nit."
    },
    {
      "concern": "tests: hack/test-helm-render.sh — New run_ca_case assertions match exact indentation (16 spaces for the env name, 18 for the value) and the exact `value:` line, so any template reindent breaks the suite. Every case also hard-pins agent-cluster-assigner:v0.2.0, coupling unrelated cases to the default tag. Confirm the tests are wired into `make test` / precommit and that the disabled (clusterAssigner.enabled=false) render is still covered.",
      "disposition": "not-an-issue",
      "detail": "Wiring confirmed: Makefile:28 makes `test` depend on `test-helm`, and Makefile:31-33 runs `bash hack/test-helm-render.sh`; `precommit` depends on `test`. The v0.2.0 pin is intentional (it asserts the feature is not a silent no-op). The enabled=false guard is unchanged by this PR and is not asserted, but that guard predates the change. Indentation coupling is raised as a non-blocking nit."
    },
    {
      "concern": "tests: hack/test-helm-render.sh — Coverage is absent/empty/one/two entries, but there is no case for a scalar (non-list) value, a comma-containing entry, or an operator tag override. Check whether the empty case genuinely distinguishes `excludeAssignees: []` from the key being absent (both currently expect the same output).",
      "disposition": "not-an-issue",
      "detail": "The two cases are distinct inputs (key present with `[]` vs key omitted) that must both render nothing, so identical expectations are correct. Missing scalar/comma/override cases are acceptable: the scalar path fails loudly, and comma handling is documented as unsupported."
    },
    {
      "concern": "docs: CHANGELOG.md — Verify the Unreleased bullet matches the actual chart bump (0.14.1 -> 0.15.0) and image tag (v0.2.0). The archived prompt at prompts/completed/245-*.md still states v0.1.3 and chart 0.13.0 -> 0.14.0; it is .reviewignore-excluded, but check that no shipped doc copied those stale numbers.",
      "disposition": "addressed",
      "detail": "CHANGELOG.md:13 matches helm/Chart.yaml:6 (0.14.1→0.15.0) and helm/values.yaml:542 (v0.1.0→v0.2.0). The stale v0.1.3 / 0.13.0→0.14.0 numbers appear only in the excluded archived prompt; grep over shipped files found no copy of them."
    },
    {
      "concern": "docs: helm/README.md — The new row says entries must not contain a comma but does not state the failure mode (silent split rather than error), and the values.yaml comment about the stage default is ambiguous. Confirm the description matches the assigner v0.2.0 behaviour.",
      "disposition": "addressed",
      "detail": "The description matches the rendered behaviour (comma-joined env value, empty renders nothing, v0.2.0 default). The missing failure-mode detail is raised as a non-blocking nit comment on helm/README.md:290."
    },
    {
      "concern": "security: helm/templates/cluster-assigner-cronjob.yaml — The env value is operator-controlled and rendered through `quote`, so YAML/env injection looks contained. Confirm the rendered value cannot break out of the env entry and that no secret-adjacent data is placed in a plain env var.",
      "disposition": "not-an-issue",
      "detail": "The value is rendered with `join \",\" . | quote` (cluster-assigner-cronjob.yaml:84), so embedded quotes/newlines are escaped and cannot break out of the YAML scalar. The env var carries only assignee names — no secret or secret-adjacent data; token/CA material continues to flow through secretKeyRef."
    }
  ]
}

@bborbe
bborbe merged commit 923e984 into master Oct 22, 2026
3 checks passed
@bborbe
bborbe deleted the feat/cluster-assigner-exclude-assignees branch October 22, 2026 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant