You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Require the CLA gate status check on world-at-ruin #107
Evidence: world-at-ruin#35 delivered a first-party CLA gate (CLA gate check from .github/workflows/cla.yaml) that blocks external PRs until their author signs CLA.md (world-at-ruin#2). The check currently fails red on unsigned external PRs but is not required by the repo's ruleset, so a merge is technically possible while it is red. Audience/problem: the maintainer and agents relying on the CLA legal gate being unbypassable; today it depends on reviewer discipline (agents never merge external PRs per the trust gate, but the ruleset should encode it). Proposed: add CLA gate to world-at-ruin's required status checks in the declarative org config (note: required_status_checks rulesets have historically been UI-created + Observe-imported — follow that established path). Acceptance: an external (non-allowlisted) PR with a red CLA gate check cannot be merged; trusted-author PRs are unaffected (the check passes instantly via the allowlist). Size: small.
#74 - feat(rulesets): require the CodeRabbit review status check org-wide [merged] #75 - revert(rulesets): drop the provider-uncreatable CodeRabbit required-check ruleset [merged] #92 - docs: complete agent and deploy contributor guidance [merged]
📝 Issue Planner
Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.
Create Plan
🧪 Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your .coderabbit.yaml:
issue_enrichment:
auto_enrich:
enabled: false
💬 Have feedback or questions? Drop into our discord!
Verified ready to apply — and safe today (measured 2026-07-28)
This issue proposed the change but left the risky part unstated: a required status check that does not
report on every PR wedges the repo permanently. I verified each precondition against live state rather
than reasoning about it.
1. The exact context string is CLA gate. Not the workflow name (CLA) — the job name. Confirmed
on two live PRs (world-at-ruin#468, #460): name=CLA gate, app github-actions, conclusion=success.
⚠️The same workflow's other job, Record CLA signature, reports skipped on every pull_request
run (it is issue_comment-gated). Requiring that context would block every merge. Require only CLA gate.
2. It reports on every PR.cla.yaml is on: pull_request with no path filter and no branch
filter, and cla-check is gated on github.event_name == 'pull_request'. There is no configuration
under which a PR silently lacks the check.
3. No PR author is currently blocked. Every author who has ever opened a PR on world-at-ruin — devantler (185) and dependabot[bot] (15) across the last 200 PRs — is already in the workflow's ALLOWLIST, so the gate passes instantly for them. Making it required blocks nothing that exists today.
4. Residual risk, and it is a maintainer decision — not mine to close. The allowlist is devantler dependabot[bot] renovate[bot] github-actions[bot] coderabbitai[bot]. It does not include cursor[bot], which is a trusted PR author under the current contract. That lane has never opened a PR
on this repo, so nothing is blocked now — but if it ever does, a required CLA gate would hold it until
the allowlist is widened. I am deliberately not widening it: this is a legal gate (copyright
assignment), so who bypasses it is your call, not an agent's.
Applying it
Rulesets here are not declaratively managed (.github/deploy/ covers labels, teams and team-repository
grants — there is no deploy/rulesets/), so this is a repository-settings change and I am leaving it to
you rather than mutating settings from an unattended run.
The target is ruleset 14974342 ("Require status checks to pass"), which today requires only CI - Required Checks. This reads the current ruleset and adds one context, preserving everything else —
it is derived rather than hand-written, so it cannot drop existing state, and unique_by makes it
idempotent (verified by applying the transform twice — identical output):
gh api repos/devantler-tech/world-at-ruin/rulesets/14974342 \
--jq '{name,target,enforcement,conditions,rules}' \
| jq '(.rules[] | select(.type=="required_status_checks") | .parameters.required_status_checks) |= (. + [{"context":"CLA gate"}] | unique_by(.context))' \
| gh api --method PUT repos/devantler-tech/world-at-ruin/rulesets/14974342 --input -
Dry-run output of the transform: [{"context":"CI - Required Checks"},{"context":"CLA gate"}]
Reverting is the same command with unique_by(.context) replaced by map(select(.context != "CLA gate")).
Acceptance check after applying: open any PR and confirm CLA gate appears under required checks and
that a trusted-author PR still merges (it should pass in seconds via the allowlist).
Investigated — the proposed approach needs one correction, and the enforcing step is yours, not an agent's.
Correction: this must be repo-scoped, not the org ruleset. The issue proposes adding CLA gate to "the declarative org config". Measured today:
world-at-ruin has no repo-level status-check ruleset — it inherits org ruleset 14974342 ("Require status checks to pass", source_type: Organization), whose required contexts are currently exactly ["CI - Required Checks"].
.github/workflows/cla.yaml exists on world-at-ruin only (checked ksail, platform, monorepo, actions — all absent).
A required status check that never reports leaves a PR permanently un-mergeable, so adding CLA gate to the org ruleset would wedge every other repo in the org. The right shape is a repo-scoped RepositoryRuleset for world-at-ruin, matching the existing deploy/repository-rulesets/require-merge-queue-on-platform.yaml pattern.
Why no PR is attached. Every ruleset resource in deploy/ is managementPolicies: ["Observe"], and organization-rulesets/README.md explicitly warns against promoting past Observe without backfilling from status.atProvider. So the declarative layer observes rulesets; it cannot create one. The enforcing change is a repository-settings mutation, which is the maintainer's to make — I am not going to change a security setting out from under you, and a PR adding an inert Observe file for a ruleset that does not exist yet would misrepresent the state as delivered.
Exact change to apply (world-at-ruin → Settings → Rules → New ruleset):
Target: Branch, default branch
Rule: Require status checks to pass
Required check: CLA gate
Enforcement: Active
Once it exists, the follow-up is mechanical and I can take it: add deploy/repository-rulesets/require-cla-gate-on-world-at-ruin.yaml in Observe mode with crossplane.io/external-name set to the bare numeric ruleset id (per that directory's README — the Terraform <repo>:<id> form fails the provider's strconv.ParseInt), and add the row to its table.
On the acceptance criterion — "trusted-author PRs are unaffected (the check passes instantly via the allowlist)" is worth verifying against a real trusted PR after the ruleset goes active, rather than assumed: if the allowlist path skips the job rather than reporting success, a required check would block trusted PRs too. That is the failure mode to watch for on the first PR after enabling.
The proposed approach would break every repo in the org — corrected design below
Investigated this end to end. The acceptance criteria are sound, but the ruleset this issue points
at is org-scoped, not repo-scoped, so implementing it as written would have caused a portfolio-wide
outage.
What the investigation found
world-at-ruin's "Require status checks to pass" ruleset (14974342) reports source_type: Organization — it is inherited from the org and currently requires exactly one check, CI - Required Checks. Adding CLA gate to it would demand that check on every repository in devantler-tech, and only world-at-ruin has a CLA workflow. Every other repo's PRs would sit
permanently on "Expected — waiting for status to be reported".
The correct shape is a new repo-scoped RepositoryRuleset on world-at-ruin alone.
The gate itself is safe to require — verified behaviourally, not assumed
It runs on every PR.cla-check is on: pull_request with no path or branch filter, and recent pull_request runs complete success.
All actual authors are allowlisted. The only PR authors on this repo across the last 100 PRs are devantler (86) and dependabot[bot] (14); both are in the workflow's ALLOWLIST and exit 0
immediately.
The issue_comment trigger does not poison the gate. Those runs are skipped (the job's if
is false), which I expected might overwrite a PR's green — it does not: they attach to the base-branch SHA, never to a PR head.
The allowlist omits cursor[bot] and botantler-1, which are trusted PR authors elsewhere in the
portfolio. Neither has ever opened a PR here, so this is theoretical today — and it fails safe
(a blocked PR, never a bypassed gate). Deliberately not widening a legal gate's allowlist as a
side effect of this change; that belongs in its own issue if either lane starts publishing here.
Blocked on a permission I do not hold
Creating the ruleset is a repository-settings mutation, and the agent runtime's permission layer
denied it. I do not widen my own guards, so this needs the maintainer. The exact, ready-to-apply
payload:
No bypass actors, by design — the point of the issue is that the legal gate is unbypassable.
Once it exists, the declarative record follows the established Observe-first convention: a new deploy/repository-rulesets/require-cla-gate-on-world-at-ruin.yaml whose crossplane.io/external-name is the bare numeric ruleset id returned by that call, plus its kustomization.yaml entry and README table row. That step is a normal PR and I can ship it as soon
as the id exists.
Unassigning myself so this is not read as a live claim while it waits on the maintainer.
Verified and prepared — blocked only by a runtime permission, not by anything about the change
The premise is confirmed live.world-at-ruin ruleset 14974342 ("Require status checks to pass", active, scoped to ~DEFAULT_BRANCH) currently requires exactly one context:
CI - Required Checks
CLA gate is not among them, so the gate can indeed be red while a merge is technically permitted — exactly as this issue states.
The over-tightening check, measured before proposing the change
Requiring a check that does not always run would block every PR in the repo, so I measured CLA gate across the 12 most recent world-at-ruin PRs (#681–#695) by reading each head's check-runs:
result
count
CLA gate present and success
12 / 12
CLA gate absent
0
The companion Record CLA signature check is skipped on all 12, which is the allowlist path behaving as designed. So requiring CLA gatecannot block trusted-author or bot PRs — it passes instantly for them — and bites only on an unsigned external contributor, which is the stated intent.
The exact change, ready to apply
PATCH /repos/devantler-tech/world-at-ruin/rulesets/14974342 with the existing parameters preserved and one context appended:
strict_required_status_checks_policy: false, do_not_enforce_on_create: true, the ~DEFAULT_BRANCH condition and the OrganizationAdmin bypass actor are all carried over unchanged — this appends one context and alters nothing else. It is fully reversible by removing that one entry.
Why I did not apply it
The agent runtime's permission layer denied the ruleset mutation. Widening my own guard to get around that is precisely what the engineering contract forbids — a permission expansion is an authorisation change and the maintainer's call, never something the agent grants itself. So this is recorded rather than forced.
This needs one of: the maintainer applying the PATCH above (or the equivalent two clicks in the ruleset UI — the issue notes required-status-check rulesets have historically been UI-created), or a runtime grant for ruleset mutation on devantler-tech repos, after which a later tick can apply it directly.
Everything else the acceptance criteria ask for is already established: the change is a pure tightening, trusted PRs are provably unaffected, and the exact payload is above.
Investigated — here is exactly what is needed, and the one step I cannot take unattended
Verified.
Check name is exactly CLA gate.world-at-ruin/.github/workflows/cla.yaml job cla-check sets name: CLA gate (the workflow itself is named CLA, so the workflow name is not the context to require — an easy thing to get wrong).
Repo-scoped rulesets are managed in deploy/repository-rulesets/, one file per rule, named <verb>-on-<repo>.yaml, so the target file is require-cla-gate-on-world-at-ruin.yaml.
required_status_checks is NOT in the provider's unsupported list in deploy/organization-rulesets/README.md, and the org already runs an imported "Require status checks to pass" ruleset — so the rule type is expressible in principle.
A net-new managed ruleset has precedent — protect-release-tags.yaml is Observe + Create + Update + LateInitialize with no external-name, so Crossplane creates it on first reconcile. That is the shape a net-new repo ruleset would take.
Blocker, named precisely. Every existing RepositoryRuleset in this repo is an Observe-only import whose external-name is the numeric id of a ruleset that already exists. So the Observe path cannot be used until the ruleset exists, and the Create path needs the provider's exact requiredStatusChecks schema (requiredCheck → context/integrationId, and the strict-policy field name). I could not authoritatively verify that schema this run:
no cluster access to read repositoryrulesets.repo.github.m.upbound.io — the CRD is the authority, and kubectl returned nothing;
the CRD is not vendored anywhere in platform/ or this repo;
the Terraform registry page is JS-rendered and returned no schema.
I am not guessing a schema for a security control. A malformed ruleset either silently fails to reconcile — leaving the gate un-enforced while the config looks done, which is worse than the status quo — or applies partially.
Two ways to finish, whichever you prefer:
UI + import (the established path this issue already names). Add CLA gate to a world-at-ruin branch ruleset in the UI; I then adopt it Observe-first exactly like require-merge-queue-on-platform.yaml, using gh api repos/devantler-tech/world-at-ruin/rulesets for the id. That is a small follow-up PR.
Managed Create. If a run gets cluster read access to that CRD, the schema can be read directly and shipped as a net-new managed file on the protect-release-tags.yaml pattern — no UI step at all.
Not doing the API mutation myself unattended: creating a governance control that decides what can merge is a change I would rather you saw first. Everything else is ready.
Investigated — and the proposed direction needs correcting before anyone implements it
Two live findings change what this issue should ask for.
1. There is no repo-scoped ruleset on world-at-ruin to extend — every rule it has is
organization-sourced. Read live today:
GET repos/devantler-tech/world-at-ruin/rulesets/{16885082,6455804,4134020}
→ source=devantler-tech source_type=Organization (all of them)
2. The org ruleset that carries required checks requires exactly one context, for every repo:
GET repos/devantler-tech/world-at-ruin/rulesets/14974342
name: "Require status checks to pass" source_type: Organization
required_status_checks: [ { context: "CI - Required Checks" } ]
do_not_enforce_on_create: true strict: false
So the portfolio's established pattern is one aggregate required context per repo, not a list of
individual checks — and CLA gate is a job in world-at-ruin's separate CLA workflow, not part of CI - Required Checks.
⚠️This means "add CLA gate to the declarative org config" would be actively harmful as written.
That org ruleset applies to every repository in the organisation. Every repo except world-at-ruin
has no CLA workflow, so the context would never post and every PR org-wide would block forever
waiting for a check that cannot arrive.
What the fix actually is
A new repository-scoped ruleset on world-at-ruin requiring the CLA gate context — the same
shape as platform's repo-scoped merge-queue rule, which is the only precedent in deploy/repository-rulesets/.
Why this is not agent-work — missing authority, verified rather than assumed
Creating or editing a ruleset is a repository/organisation settings mutation, which is the
maintainer's. Confirmed live rather than inferred:
GET orgs/devantler-tech/rulesets
→ 403 "Resource not accessible by personal access token"
And the declarative layer cannot substitute: every manifest in deploy/organization-rulesets/ and deploy/repository-rulesets/ is pinned managementPolicies: ["Observe"], with the README warning
against promoting past Observe (lossy round-trip from status.atProvider). The declarative path records a ruleset; it cannot create one.
Maintainer action needed (one-off, UI): on devantler-tech/world-at-ruin, add a repository
ruleset targeting the default branch requiring the CLA gate status check. Once it exists, adopting
it is ordinary agent work — a RepositoryRuleset manifest in deploy/repository-rulesets/
(require-cla-on-world-at-ruin.yaml), Observe-first, crossplane.io/external-name set to the bare
numeric ruleset id, added to that directory's kustomization.yaml.
Note for whoever writes that manifest: use the bare numeric id, not <repo>:<id> — see the
correction already recorded in require-merge-queue-on-platform.yaml.
Unassigning myself: the blocking half is not mine to perform, and a bare assignment would otherwise
read as work in progress.
Delivered — #135 merged 23:41:08Z. Left open deliberately for the live read-back.
deploy/repository-rulesets/require-cla-gate-on-world-at-ruin.yaml is on main: a repo-scoped RepositoryRuleset requiring the CLA gate context on the default branch, managed (Observe + Create + Update + LateInitialize, never Delete) so Crossplane creates it.
Correcting the direction this issue proposed: it suggested adding the context to the existing required-checks ruleset. That does not work — every ruleset on world-at-ruin is organization-sourced, and the org's "Require status checks to pass" carries one context for all repos, so CLA gate there would block every repo without a CLA workflow. Repo-scoped is the only fit.
What is already proven: the context string is the cla-checkjob name (the workflow is named CLA — requiring that would block every PR on a check that never reports); the CR schema came from the live CRD; and requiring it over-tightens nothing — 14/14 recent world-at-ruin PRs carry exactly one CLA gate, all success.
What is NOT yet proven, and why this stays open. At 23:4xZ the resource had not reconciled:
kubectl --context admin@prod get repositoryruleset.repo.github.m.upbound.io -A
→ only github-config/platform-require-merge-queue (mine absent)
gh api 'repos/devantler-tech/world-at-ruin/rulesets?includes_parents=false' --jq length
→ 0
Closing on the merge would repeat platform#2922 exactly — a config that validated green, was read by its consumer, and never reached the running system. To close this, re-run both commands above (expect the CR SYNCED=True READY=True with a numeric external-name, and the repo-scoped count to become 1), then confirm the negative control: a PR with a red CLA gate is genuinely unmergeable. ⚠️ Query with -A — these CRs live in the github-config namespace, so an unqualified get returns nothing and reads as a failed rollout.
Verified live — the gate is in force. Closure confirmed on evidence.
The previous run left a carry-forward to verify this after reconciliation, and noted the issue was
being "left open deliberately for the live read-back". It was in fact already closed at 2026-08-05T23:41:09Z — one second after #135
merged, and one minute before that comment was written. A closing keyword survived in the merged
commit despite the intent to use Part of. Recording the missing evidence here so the closure rests
on a verified outcome rather than an accident of timing.
1. The Crossplane resource reconciled.
$ kubectl get repositoryruleset -A
NAMESPACE NAME SYNCED READY EXTERNAL-NAME AGE
github-config world-at-ruin-require-cla-gate True True 20481768 17m
2. The ruleset exists on GitHub and is active (repos/devantler-tech/world-at-ruin/rulesets/20481768):
name Require CLA gate, target: branch, enforcement: active, conditions include: [~DEFAULT_BRANCH],
required check CLA gate.
3. It is actually IN FORCE on the branch, which is the part that matters. A defined-and-synced
ruleset is not the same fact as an enforced one, so this reads the effective rules GitHub applies
to the default branch rather than the ruleset's own definition:
CLA gate is present, attributed to ruleset 20481768 — the one this issue delivered. The
pre-existing org-sourced CI - Required Checks is still there beside it, so this added a
requirement without displacing the existing one.
Acceptance criteria met. An external PR with a red CLA gate can no longer be merged; the check
passes instantly for allowlisted authors, so trusted-author PRs are unaffected (14/14 recent PRs on
the repo carry exactly one CLA gate, all success — measured before merging #135, which is what
ruled out over-tightening).
created 2026-08-05T23:42:32Z. The external-name is a real GitHub-assigned ruleset id, which is what proves the provider actually created the object rather than merely accepting the CR.
GitHub side (gh api repos/devantler-tech/world-at-ruin/rulesets/20481768):
and world-at-ruin's repo-scoped ruleset count went 0 → 1 (rulesets?includes_parents=false).
Against the acceptance criteria:CLA gate is now an active required status check on the default branch, so an external PR whose gate is red cannot be merged — the gate is enforced by the ruleset rather than by reviewer discipline. Trusted-author PRs are unaffected: the check passes instantly via the workflow allowlist (measured beforehand — 14/14 recent PRs carried exactly one CLA gate, all success), and strict is deliberately false so no PR gained a rebase round-trip.
Activity
coderabbitai commentedon Jul 17, 2026
🔗 Related PRs
#74 - feat(rulesets): require the CodeRabbit review status check org-wide [merged]
#75 - revert(rulesets): drop the provider-uncreatable CodeRabbit required-check ruleset [merged]
#92 - docs: complete agent and deploy contributor guidance [merged]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
devantler commentedon Jul 28, 2026
Verified ready to apply — and safe today (measured 2026-07-28)
This issue proposed the change but left the risky part unstated: a required status check that does not
report on every PR wedges the repo permanently. I verified each precondition against live state rather
than reasoning about it.
1. The exact context string is
CLA gate. Not the workflow name (CLA) — the job name. Confirmedon two live PRs (world-at-ruin#468, #460):
name=CLA gate, appgithub-actions,conclusion=success.Record CLA signature, reportsskippedon everypull_requestrun (it is
issue_comment-gated). Requiring that context would block every merge. Require onlyCLA gate.2. It reports on every PR.
cla.yamlison: pull_requestwith no path filter and no branchfilter, and
cla-checkis gated ongithub.event_name == 'pull_request'. There is no configurationunder which a PR silently lacks the check.
3. No PR author is currently blocked. Every author who has ever opened a PR on world-at-ruin —
devantler(185) anddependabot[bot](15) across the last 200 PRs — is already in the workflow'sALLOWLIST, so the gate passes instantly for them. Making it required blocks nothing that exists today.4. Residual risk, and it is a maintainer decision — not mine to close. The allowlist is
devantler dependabot[bot] renovate[bot] github-actions[bot] coderabbitai[bot]. It does not includecursor[bot], which is a trusted PR author under the current contract. That lane has never opened a PRon this repo, so nothing is blocked now — but if it ever does, a required
CLA gatewould hold it untilthe allowlist is widened. I am deliberately not widening it: this is a legal gate (copyright
assignment), so who bypasses it is your call, not an agent's.
Applying it
Rulesets here are not declaratively managed (
.github/deploy/covers labels, teams and team-repositorygrants — there is no
deploy/rulesets/), so this is a repository-settings change and I am leaving it toyou rather than mutating settings from an unattended run.
The target is ruleset 14974342 ("Require status checks to pass"), which today requires only
CI - Required Checks. This reads the current ruleset and adds one context, preserving everything else —it is derived rather than hand-written, so it cannot drop existing state, and
unique_bymakes itidempotent (verified by applying the transform twice — identical output):
gh api repos/devantler-tech/world-at-ruin/rulesets/14974342 \ --jq '{name,target,enforcement,conditions,rules}' \ | jq '(.rules[] | select(.type=="required_status_checks") | .parameters.required_status_checks) |= (. + [{"context":"CLA gate"}] | unique_by(.context))' \ | gh api --method PUT repos/devantler-tech/world-at-ruin/rulesets/14974342 --input -Dry-run output of the transform:
[{"context":"CI - Required Checks"},{"context":"CLA gate"}]Reverting is the same command with
unique_by(.context)replaced bymap(select(.context != "CLA gate")).Acceptance check after applying: open any PR and confirm
CLA gateappears under required checks andthat a trusted-author PR still merges (it should pass in seconds via the allowlist).
devantler commentedon Jul 28, 2026
Investigated — the proposed approach needs one correction, and the enforcing step is yours, not an agent's.
Correction: this must be repo-scoped, not the org ruleset. The issue proposes adding
CLA gateto "the declarative org config". Measured today:14974342("Require status checks to pass",source_type: Organization), whose required contexts are currently exactly["CI - Required Checks"]..github/workflows/cla.yamlexists on world-at-ruin only (checked ksail, platform, monorepo, actions — all absent).A required status check that never reports leaves a PR permanently un-mergeable, so adding
CLA gateto the org ruleset would wedge every other repo in the org. The right shape is a repo-scopedRepositoryRulesetfor world-at-ruin, matching the existingdeploy/repository-rulesets/require-merge-queue-on-platform.yamlpattern.Why no PR is attached. Every ruleset resource in
deploy/ismanagementPolicies: ["Observe"], andorganization-rulesets/README.mdexplicitly warns against promoting past Observe without backfilling fromstatus.atProvider. So the declarative layer observes rulesets; it cannot create one. The enforcing change is a repository-settings mutation, which is the maintainer's to make — I am not going to change a security setting out from under you, and a PR adding an inert Observe file for a ruleset that does not exist yet would misrepresent the state as delivered.Exact change to apply (world-at-ruin → Settings → Rules → New ruleset):
Branch, default branchRequire status checks to passCLA gateActiveOnce it exists, the follow-up is mechanical and I can take it: add
deploy/repository-rulesets/require-cla-gate-on-world-at-ruin.yamlin Observe mode withcrossplane.io/external-nameset to the bare numeric ruleset id (per that directory's README — the Terraform<repo>:<id>form fails the provider'sstrconv.ParseInt), and add the row to its table.On the acceptance criterion — "trusted-author PRs are unaffected (the check passes instantly via the allowlist)" is worth verifying against a real trusted PR after the ruleset goes active, rather than assumed: if the allowlist path skips the job rather than reporting success, a required check would block trusted PRs too. That is the failure mode to watch for on the first PR after enabling.
devantler commentedon Jul 28, 2026
The proposed approach would break every repo in the org — corrected design below
Investigated this end to end. The acceptance criteria are sound, but the ruleset this issue points
at is org-scoped, not repo-scoped, so implementing it as written would have caused a portfolio-wide
outage.
What the investigation found
world-at-ruin's "Require status checks to pass" ruleset (14974342) reportssource_type: Organization— it is inherited from the org and currently requires exactly one check,CI - Required Checks. AddingCLA gateto it would demand that check on every repository indevantler-tech, and onlyworld-at-ruinhas a CLA workflow. Every other repo's PRs would sitpermanently on "Expected — waiting for status to be reported".
The correct shape is a new repo-scoped
RepositoryRulesetonworld-at-ruinalone.The gate itself is safe to require — verified behaviourally, not assumed
cla-checkison: pull_requestwith no path or branch filter, and recentpull_requestruns completesuccess.devantler(86) anddependabot[bot](14); both are in the workflow'sALLOWLISTand exit 0immediately.
issue_commenttrigger does not poison the gate. Those runs areskipped(the job'sifis false), which I expected might overwrite a PR's green — it does not: they attach to the
base-branch SHA, never to a PR head.
CLA gate: successat their current head. The twowithout it are Reject incomplete OIDC reads before publisher identity admission #488 (conflicted, so no merge ref and no
pull_requestrun — it cannot merge anyway)and Refuse occupied versions before default publisher writes #492 (fast-moving head). Both clear on their next push.
Remaining risk worth stating
The allowlist omits
cursor[bot]andbotantler-1, which are trusted PR authors elsewhere in theportfolio. Neither has ever opened a PR here, so this is theoretical today — and it fails safe
(a blocked PR, never a bypassed gate). Deliberately not widening a legal gate's allowlist as a
side effect of this change; that belongs in its own issue if either lane starts publishing here.
Blocked on a permission I do not hold
Creating the ruleset is a repository-settings mutation, and the agent runtime's permission layer
denied it. I do not widen my own guards, so this needs the maintainer. The exact, ready-to-apply
payload:
No bypass actors, by design — the point of the issue is that the legal gate is unbypassable.
Once it exists, the declarative record follows the established Observe-first convention: a new
deploy/repository-rulesets/require-cla-gate-on-world-at-ruin.yamlwhosecrossplane.io/external-nameis the bare numeric ruleset id returned by that call, plus itskustomization.yamlentry and README table row. That step is a normal PR and I can ship it as soonas the id exists.
Unassigning myself so this is not read as a live claim while it waits on the maintainer.
devantler commentedon Aug 2, 2026
Verified and prepared — blocked only by a runtime permission, not by anything about the change
The premise is confirmed live.
world-at-ruinruleset 14974342 ("Require status checks to pass",active, scoped to~DEFAULT_BRANCH) currently requires exactly one context:CLA gateis not among them, so the gate can indeed be red while a merge is technically permitted — exactly as this issue states.The over-tightening check, measured before proposing the change
Requiring a check that does not always run would block every PR in the repo, so I measured
CLA gateacross the 12 most recentworld-at-ruinPRs (#681–#695) by reading each head's check-runs:CLA gatepresent andsuccessCLA gateabsentThe companion
Record CLA signaturecheck isskippedon all 12, which is the allowlist path behaving as designed. So requiringCLA gatecannot block trusted-author or bot PRs — it passes instantly for them — and bites only on an unsigned external contributor, which is the stated intent.The exact change, ready to apply
PATCH /repos/devantler-tech/world-at-ruin/rulesets/14974342with the existing parameters preserved and one context appended:{ "rules": [{ "type": "required_status_checks", "parameters": { "strict_required_status_checks_policy": false, "do_not_enforce_on_create": true, "required_status_checks": [ { "context": "CI - Required Checks" }, { "context": "CLA gate" } ] } }] }strict_required_status_checks_policy: false,do_not_enforce_on_create: true, the~DEFAULT_BRANCHcondition and theOrganizationAdminbypass actor are all carried over unchanged — this appends one context and alters nothing else. It is fully reversible by removing that one entry.Why I did not apply it
The agent runtime's permission layer denied the ruleset mutation. Widening my own guard to get around that is precisely what the engineering contract forbids — a permission expansion is an authorisation change and the maintainer's call, never something the agent grants itself. So this is recorded rather than forced.
This needs one of: the maintainer applying the
PATCHabove (or the equivalent two clicks in the ruleset UI — the issue notes required-status-check rulesets have historically been UI-created), or a runtime grant for ruleset mutation ondevantler-techrepos, after which a later tick can apply it directly.Everything else the acceptance criteria ask for is already established: the change is a pure tightening, trusted PRs are provably unaffected, and the exact payload is above.
devantler commentedon Aug 5, 2026
Investigated — here is exactly what is needed, and the one step I cannot take unattended
Verified.
CLA gate.world-at-ruin/.github/workflows/cla.yamljobcla-checksetsname: CLA gate(the workflow itself is namedCLA, so the workflow name is not the context to require — an easy thing to get wrong).deploy/repository-rulesets/, one file per rule, named<verb>-on-<repo>.yaml, so the target file isrequire-cla-gate-on-world-at-ruin.yaml.required_status_checksis NOT in the provider's unsupported list indeploy/organization-rulesets/README.md, and the org already runs an imported "Require status checks to pass" ruleset — so the rule type is expressible in principle.protect-release-tags.yamlisObserve + Create + Update + LateInitializewith noexternal-name, so Crossplane creates it on first reconcile. That is the shape a net-new repo ruleset would take.Blocker, named precisely. Every existing
RepositoryRulesetin this repo is an Observe-only import whoseexternal-nameis the numeric id of a ruleset that already exists. So the Observe path cannot be used until the ruleset exists, and the Create path needs the provider's exactrequiredStatusChecksschema (requiredCheck→context/integrationId, and the strict-policy field name). I could not authoritatively verify that schema this run:repositoryrulesets.repo.github.m.upbound.io— the CRD is the authority, andkubectlreturned nothing;platform/or this repo;I am not guessing a schema for a security control. A malformed ruleset either silently fails to reconcile — leaving the gate un-enforced while the config looks done, which is worse than the status quo — or applies partially.
Two ways to finish, whichever you prefer:
CLA gateto aworld-at-ruinbranch ruleset in the UI; I then adopt it Observe-first exactly likerequire-merge-queue-on-platform.yaml, usinggh api repos/devantler-tech/world-at-ruin/rulesetsfor the id. That is a small follow-up PR.protect-release-tags.yamlpattern — no UI step at all.Not doing the API mutation myself unattended: creating a governance control that decides what can merge is a change I would rather you saw first. Everything else is ready.
devantler commentedon Aug 5, 2026
Investigated — and the proposed direction needs correcting before anyone implements it
Two live findings change what this issue should ask for.
1. There is no repo-scoped ruleset on
world-at-ruinto extend — every rule it has isorganization-sourced. Read live today:
2. The org ruleset that carries required checks requires exactly one context, for every repo:
So the portfolio's established pattern is one aggregate required context per repo, not a list of
individual checks — and
CLA gateis a job inworld-at-ruin's separateCLAworkflow, not part ofCI - Required Checks.CLA gateto the declarative org config" would be actively harmful as written.That org ruleset applies to every repository in the organisation. Every repo except
world-at-ruinhas no
CLAworkflow, so the context would never post and every PR org-wide would block foreverwaiting for a check that cannot arrive.
What the fix actually is
A new repository-scoped ruleset on
world-at-ruinrequiring theCLA gatecontext — the sameshape as
platform's repo-scoped merge-queue rule, which is the only precedent indeploy/repository-rulesets/.Why this is not agent-work — missing authority, verified rather than assumed
Creating or editing a ruleset is a repository/organisation settings mutation, which is the
maintainer's. Confirmed live rather than inferred:
And the declarative layer cannot substitute: every manifest in
deploy/organization-rulesets/anddeploy/repository-rulesets/is pinnedmanagementPolicies: ["Observe"], with the README warningagainst promoting past Observe (lossy round-trip from
status.atProvider). The declarative pathrecords a ruleset; it cannot create one.
Maintainer action needed (one-off, UI): on
devantler-tech/world-at-ruin, add a repositoryruleset targeting the default branch requiring the
CLA gatestatus check. Once it exists, adoptingit is ordinary agent work — a
RepositoryRulesetmanifest indeploy/repository-rulesets/(
require-cla-on-world-at-ruin.yaml), Observe-first,crossplane.io/external-nameset to the barenumeric ruleset id, added to that directory's
kustomization.yaml.Note for whoever writes that manifest: use the bare numeric id, not
<repo>:<id>— see thecorrection already recorded in
require-merge-queue-on-platform.yaml.Unassigning myself: the blocking half is not mine to perform, and a bare assignment would otherwise
read as work in progress.
feat(rulesets): require the CLA gate check on world-at-ruin
devantler commentedon Aug 5, 2026
Delivered — #135 merged 23:41:08Z. Left open deliberately for the live read-back.
deploy/repository-rulesets/require-cla-gate-on-world-at-ruin.yamlis onmain: a repo-scopedRepositoryRulesetrequiring theCLA gatecontext on the default branch, managed (Observe + Create + Update + LateInitialize, never Delete) so Crossplane creates it.Correcting the direction this issue proposed: it suggested adding the context to the existing required-checks ruleset. That does not work — every ruleset on
world-at-ruinis organization-sourced, and the org's "Require status checks to pass" carries one context for all repos, soCLA gatethere would block every repo without a CLA workflow. Repo-scoped is the only fit.What is already proven: the context string is the
cla-checkjob name (the workflow is namedCLA— requiring that would block every PR on a check that never reports); the CR schema came from the live CRD; and requiring it over-tightens nothing — 14/14 recent world-at-ruin PRs carry exactly oneCLA gate, allsuccess.What is NOT yet proven, and why this stays open. At 23:4xZ the resource had not reconciled:
Closing on the merge would repeat platform#2922 exactly — a config that validated green, was read by its consumer, and never reached the running system. To close this, re-run both commands above (expect the CR⚠️ Query with
SYNCED=True READY=Truewith a numeric external-name, and the repo-scoped count to become 1), then confirm the negative control: a PR with a redCLA gateis genuinely unmergeable.-A— these CRs live in thegithub-confignamespace, so an unqualifiedgetreturns nothing and reads as a failed rollout.devantler commentedon Aug 6, 2026
Verified live — the gate is in force. Closure confirmed on evidence.
The previous run left a carry-forward to verify this after reconciliation, and noted the issue was
being "left open deliberately for the live read-back". It was in fact already closed at
2026-08-05T23:41:09Z— one second after #135merged, and one minute before that comment was written. A closing keyword survived in the merged
commit despite the intent to use
Part of. Recording the missing evidence here so the closure restson a verified outcome rather than an accident of timing.
1. The Crossplane resource reconciled.
2. The ruleset exists on GitHub and is active (
repos/devantler-tech/world-at-ruin/rulesets/20481768):name
Require CLA gate,target: branch,enforcement: active, conditionsinclude: [~DEFAULT_BRANCH],required check
CLA gate.3. It is actually IN FORCE on the branch, which is the part that matters. A defined-and-synced
ruleset is not the same fact as an enforced one, so this reads the effective rules GitHub applies
to the default branch rather than the ruleset's own definition:
CLA gateis present, attributed to ruleset20481768— the one this issue delivered. Thepre-existing org-sourced
CI - Required Checksis still there beside it, so this added arequirement without displacing the existing one.
Acceptance criteria met. An external PR with a red
CLA gatecan no longer be merged; the checkpasses instantly for allowlisted authors, so trusted-author PRs are unaffected (14/14 recent PRs on
the repo carry exactly one
CLA gate, all success — measured before merging #135, which is whatruled out over-tightening).
devantler commentedon Aug 6, 2026
Live read-back passed — closing on evidence, not on the merge
The verification I deferred this issue for has now run. Crossplane reconciled the manifest 84 seconds after #135 merged.
Cluster side (
kubectl --context admin@prod get repositoryruleset.repo.github.m.upbound.io -A):created
2026-08-05T23:42:32Z. The external-name is a real GitHub-assigned ruleset id, which is what proves the provider actually created the object rather than merely accepting the CR.GitHub side (
gh api repos/devantler-tech/world-at-ruin/rulesets/20481768):{"name": "Require CLA gate", "source_type": "Repository", "target": "branch", "enforcement": "active", "conditions": ["~DEFAULT_BRANCH"], "checks": ["CLA gate"], "strict": [false]}and world-at-ruin's repo-scoped ruleset count went 0 → 1 (
rulesets?includes_parents=false).Against the acceptance criteria:
CLA gateis now anactiverequired status check on the default branch, so an external PR whose gate is red cannot be merged — the gate is enforced by the ruleset rather than by reviewer discipline. Trusted-author PRs are unaffected: the check passes instantly via the workflow allowlist (measured beforehand — 14/14 recent PRs carried exactly oneCLA gate, allsuccess), andstrictis deliberatelyfalseso no PR gained a rebase round-trip.Closing.