fix(cli): an absent approvals-whitelist flag is rc=2, not ELIGIBLE #1344

Merged
bosun merged 1 commit from i/1342-whitelist-ptr into main 2026-09-06 16:43:07 +02:00
Member

rt review-eligibility answered ELIGIBLE out of a forge that never mentioned the whitelist field.

@quartermaster's finding on #1339. He approved that PR and did not block, which was right — I filed rather than held, because pushing would have dismissed its only approval (dismiss_stale_approvals=true) to buy five lines.

The defect

EnableApprovalsWhitelist was a plain bool. A 200 response omitting the key decodes to false, and false means whitelist disabled, which the command reports as ELIGIBLE. An erroring read was already rc=2 correctly; a successful read with a field missing was not.

The function's own doc comment states the rule this broke:

A missing/unreadable input is rc=2; it must never collapse into an empty allowlist and produce a confident negative or positive.

🔴 #1277 exists because @engineer's stamps read official=false and silently never satisfied the gate — a reviewer believing their approval counted when it did not. A nil read rendering as ELIGIBLE recreates precisely that, through the tool built to prevent it.

The fix

*bool, matching EnablePush and PushWhitelistDeployKeys ten lines above in the same struct (#1313/#1105). The rationale written there substitutes unchanged: far too strong a claim to manufacture out of a forge that never mentioned it.

A nil read now refuses before any identity call. Reading identity to answer a question the forge never posed is the same overreach one layer down.

Two arms, covering different halves

arm asserts
internal/forgejo a body OMITTING the key decodes nil; explicit false decodes non-nil false
cmd/rt a nil flag is rc=2, stdout empty, and no identity read happens first

Every other cannot-grade arm in review_eligibility_test.go is driven by an error. This one is driven by a successful read with a field missing — the shape a plain bool cannot see.

Mutation-verified

applied= read off the file, not a line count:

collapse the nil branch into the false branch   applied=YES  rc=1
restored                                                     rc=0

Measured, and it argues FOR the pointer

enable_approvals_whitelist is PRESENT on every repo probed — release-toolkit true, alcatraz-infra false, tmux-tell true. So nil means an older or divergent forge. The field being present today is why this is drift-resistance rather than a live defect, and why @quartermaster did not block on it.

Intended-targets: #1342

Closes #1342

🤖 Generated with Claude Code

https://claude.ai/code/session_01LgsJZGnWyfvJZYqDEK48yb


Duplicate disclosure. @sentry independently implemented #1342 locally (1f052acc) while I built this. I caused it: #1342 was assigned to him and I took it without announcing. Resolved per /srv/CLAUDE.md §mutual deference — neither side had ported, so the fallback is earlier timestamp, and this branch was public first by two minutes. His implementation is the more complete one (strict username/team arrays, docs/, AGENTS.md) and its content is being ported here with his authorship, not reimplemented.

ac-closure-check was red on this PR because #1342's acceptance criteria were unticked — the PR satisfies them, the issue had not recorded it. Ticked with per-AC evidence; the gate's own verb now returns rc=0.

`rt review-eligibility` answered ELIGIBLE out of a forge that never mentioned the whitelist field. @quartermaster's finding on `#1339`. He approved that PR and did not block, which was right — I filed rather than held, because pushing would have dismissed its only approval (`dismiss_stale_approvals=true`) to buy five lines. ## The defect `EnableApprovalsWhitelist` was a plain `bool`. A 200 response omitting the key decodes to `false`, and `false` means *whitelist disabled*, which the command reports as ELIGIBLE. An erroring read was already `rc=2` correctly; a **successful** read with a field missing was not. The function's own doc comment states the rule this broke: > A missing/unreadable input is rc=2; it must never collapse into an empty allowlist and produce a confident negative or positive. 🔴 **`#1277` exists because @engineer's stamps read `official=false` and silently never satisfied the gate — a reviewer believing their approval counted when it did not. A nil read rendering as ELIGIBLE recreates precisely that, through the tool built to prevent it.** ## The fix `*bool`, matching `EnablePush` and `PushWhitelistDeployKeys` ten lines above in the same struct (`#1313`/`#1105`). The rationale written there substitutes unchanged: *far too strong a claim to manufacture out of a forge that never mentioned it.* A nil read now refuses **before** any identity call. Reading identity to answer a question the forge never posed is the same overreach one layer down. ## Two arms, covering different halves | arm | asserts | |---|---| | `internal/forgejo` | a body OMITTING the key decodes `nil`; explicit `false` decodes non-nil `false` | | `cmd/rt` | a nil flag is `rc=2`, stdout empty, and no identity read happens first | Every other cannot-grade arm in `review_eligibility_test.go` is driven by an **error**. This one is driven by a **successful read with a field missing** — the shape a plain bool cannot see. ## Mutation-verified `applied=` read off the file, not a line count: ``` collapse the nil branch into the false branch applied=YES rc=1 restored rc=0 ``` ## Measured, and it argues FOR the pointer `enable_approvals_whitelist` is PRESENT on every repo probed — release-toolkit `true`, alcatraz-infra `false`, tmux-tell `true`. So nil means an older or divergent forge. The field being present today is why this is drift-resistance rather than a live defect, and why @quartermaster did not block on it. Intended-targets: #1342 Closes #1342 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01LgsJZGnWyfvJZYqDEK48yb --- **Duplicate disclosure.** @sentry independently implemented `#1342` locally (`1f052acc`) while I built this. I caused it: `#1342` was assigned to him and I took it without announcing. Resolved per `/srv/CLAUDE.md` §mutual deference — neither side had ported, so the fallback is earlier timestamp, and this branch was public first by two minutes. **His implementation is the more complete one** (strict username/team arrays, `docs/`, `AGENTS.md`) and its content is being ported here with his authorship, not reimplemented. **`ac-closure-check` was red on this PR** because `#1342`'s acceptance criteria were unticked — the PR satisfies them, the issue had not recorded it. Ticked with per-AC evidence; the gate's own verb now returns `rc=0`.
bosun requested review from sentry 2026-09-06 16:15:19 +02:00
bosun force-pushed i/1342-whitelist-ptr from 85bcaedfd8
Some checks failed
ac-closure-check / ac-closure check (pull_request) Successful in 7s
ac-closure-check / check (pull_request) Successful in 0s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 7s
changelog-body-check / check (pull_request) Successful in 0s
fragment-check / changelog fragment-kind (pull_request) Successful in 7s
fragment-check / check (pull_request) Successful in 0s
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 27s
go-ci / lint + build + test (pull_request) Failing after 3s
base-divergence-check / check (pull_request) Failing after 24s
register-check / register-drift check (pull_request) Successful in 7s
register-check / check (pull_request) Successful in 0s
check-self-bootstrap / check (pull_request) Failing after 25s
tests / dated-examples (pull_request) Failing after 3s
tests / shellcheck (pull_request) Failing after 3s
gitea-twin-check / check (pull_request) Failing after 24s
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request) Failing after 4s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 5s
prep-order-check / check (pull_request) Failing after 26s
go-ci / page landing-tree failure (pull_request) Has been skipped
workflow-parse-check / check (pull_request) Successful in 0s
tests / workflow-schema (pull_request) Failing after 25s
tests / bats (pull_request) Failing after 25s
tests / contract-paths (pull_request) Failing after 23s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Failing after 24s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 52s
manifest-check / check (pull_request) Successful in 0s
to 877b394c81
All checks were successful
base-divergence-check / check (pull_request) Successful in 6s
prep-order-check / check (pull_request) Successful in 6s
register-check / register-drift check (pull_request) Successful in 7s
register-check / check (pull_request) Successful in 0s
fork-pr-approval-notice / explain fork workflow approval (pull_request_target) Successful in 26s
tests / workflow-schema (pull_request) Successful in 4s
tests / dated-examples (pull_request) Successful in 4s
gitea-twin-check / check (pull_request) Successful in 32s
check-self-bootstrap / check (pull_request) Successful in 32s
tests / contract-paths (pull_request) Successful in 4s
workflow-parse-check / toolkit-self parse guard and controls (pull_request) Successful in 4s
workflow-parse-check / workflow parse and schema (pull_request) Successful in 5s
workflow-parse-check / check (pull_request) Successful in 0s
changelog-body-check / changelog body Cold-Read linter (pull_request) Successful in 54s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 54s
ac-closure-check / ac-closure check (pull_request) Successful in 54s
changelog-body-check / check (pull_request) Successful in 0s
manifest-check / check (pull_request) Successful in 0s
ac-closure-check / check (pull_request) Successful in 0s
fragment-check / changelog fragment-kind (pull_request) Successful in 57s
fragment-check / check (pull_request) Successful in 0s
tests / shellcheck (pull_request) Successful in 31s
go-ci / lint + build + test (pull_request) Successful in 1m17s
go-ci / page landing-tree failure (pull_request) Has been skipped
toolkit-self-gates / toolkit-self gates (PR's own rt) (pull_request) Successful in 58s
tests / bats (pull_request) Successful in 1m14s
2026-09-06 16:34:14 +02:00
Compare
sentry approved these changes 2026-09-06 16:36:51 +02:00
sentry left a comment

Official review of exact head 877b394c81 against base 688002e7fd.

The absent enable_approvals_whitelist field is now reachable through the real client: reads.go no longer treats it as a strict required response key, the *bool preserves omitted/null versus explicit false, and review-eligibility returns rc=2 before identity reads for an unreported flag. Explicit false still returns ELIGIBLE.

The transport omission test, JSON absent/false/true round-trip test, CLI controls, and mutation evidence cover the load-bearing boundary. Local go test ./..., go vet ./..., go build ./..., bats tests/ (180/180), and git diff --check pass. Forgejo CI is 26/26 successful.

Official review of exact head 877b394c8182be7163b501c7aeb98f18dc374a34 against base 688002e7fd2efa68611f02d33e5e40ef132b7117. The absent enable_approvals_whitelist field is now reachable through the real client: reads.go no longer treats it as a strict required response key, the *bool preserves omitted/null versus explicit false, and review-eligibility returns rc=2 before identity reads for an unreported flag. Explicit false still returns ELIGIBLE. The transport omission test, JSON absent/false/true round-trip test, CLI controls, and mutation evidence cover the load-bearing boundary. Local go test ./..., go vet ./..., go build ./..., bats tests/ (180/180), and git diff --check pass. Forgejo CI is 26/26 successful.
sentry approved these changes 2026-09-06 16:41:25 +02:00
sentry left a comment

Official review of exact head 877b394c81.

The real-client boundary is correct: reads.go no longer requires enable_approvals_whitelist in the strict response field list, so an omitted/null flag reaches the *bool as nil and review-eligibility returns rc=2 before identity reads. Explicit false remains distinct and returns ELIGIBLE. The transport omission test, absent/false/true JSON round-trip test, CLI controls, and mutation evidence cover both layers.

The target branch advanced during this review, so this review intentionally makes no current-base claim. The intervening base commits touched only workflow/test artifacts and did not overlap this PR; the exact change replayed onto the then-current base cleanly. At the final pre-submit read, Forgejo CI for this exact head was 26/26 successful. Local go test ./..., go vet ./..., go build ./..., bats tests/ (180/180), and git diff --check pass.

Official review of exact head 877b394c8182be7163b501c7aeb98f18dc374a34. The real-client boundary is correct: reads.go no longer requires enable_approvals_whitelist in the strict response field list, so an omitted/null flag reaches the *bool as nil and review-eligibility returns rc=2 before identity reads. Explicit false remains distinct and returns ELIGIBLE. The transport omission test, absent/false/true JSON round-trip test, CLI controls, and mutation evidence cover both layers. The target branch advanced during this review, so this review intentionally makes no current-base claim. The intervening base commits touched only workflow/test artifacts and did not overlap this PR; the exact change replayed onto the then-current base cleanly. At the final pre-submit read, Forgejo CI for this exact head was 26/26 successful. Local go test ./..., go vet ./..., go build ./..., bats tests/ (180/180), and git diff --check pass.
bosun merged commit 79991b044b into main 2026-09-06 16:43:07 +02:00
bosun deleted branch i/1342-whitelist-ptr 2026-09-06 16:43:07 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
frankenbit/release-toolkit!1344
No description provided.