Skip to content

governance: merge queue 改 repo 级声明与对账(P2-7 修正,ADR-0042) - #118

Merged
randypanding merged 4 commits into
mainfrom
p2-7-merge-queue-repo-scope-fix
Aug 20, 2026
Merged

governance: merge queue 改 repo 级声明与对账(P2-7 修正,ADR-0042)#118
randypanding merged 4 commits into
mainfrom
p2-7-merge-queue-repo-scope-fix

Conversation

@randypanding

Copy link
Copy Markdown
Contributor

摘要

实测发现 org rulesets API 不支持 merge_queue 规则类型(REST+GraphQL 均 422 "Invalid rules";且 merge_queue 必须与 pull_request/required_status_checks 同集、mergeMethod 须为 repo 允许的 squash)——PR #117 的 org 级 rulesets/merge-queue.json 无法生效且会让 drift-check §1 误报。本 PR 修正为 repo 级声明模式(与 P1-1 repo settings 对账同型)。

变更

  1. 删除 governance/rulesets/merge-queue.json(org 级声明不可行)
  2. expected-state.jsonmerge_queue 段(repos + params,单一真源)
  3. drift-check.sh 新增 §14:repo 级 merge-queue ruleset 存在性+参数对账;未声明仓出现 merge-queue ruleset = 漂移(扩围须修订 ADR-0042)
  4. apply.sh 新增 step6:幂等创建(GraphQL 写入——REST 不支持该规则类型;存在即跳过,参数对账由 §14 执法)

现状

两仓 ruleset 已实际创建(agent-registry id=21076429 / template-service id=21076438,GraphQL,SQUASH+串行保守参数);merge_group 触发已合入两仓(agent-registry#60 / template-service#18)。

验证

  • bash -n / JSON 校验
  • 合并后 dispatch drift-check:§14 两仓 OK 且 §1 不再报 merge-queue 缺失(T4 对账)
  • 入队冒烟(gh pr merge --auto 走队列路径)

C1:governance/ 路径,ADR-0042 背书(org→repo 级范围为实施性修正,ADR 修订随批)。

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@randypanding, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 3 minutes

Limit details: You’ve used all 10 included reviews currently available.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6e66ba35-264e-4e45-8d54-48b06eeea6b9

📥 Commits

Reviewing files that changed from the base of the PR and between 7ed737c and 02757c7.

📒 Files selected for processing (4)
  • governance/apply.sh
  • governance/drift-check.sh
  • governance/expected-state.json
  • governance/rulesets/merge-queue.json

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

将 merge queue 治理迁移至仓库级声明与对账

🐞 Bug fix ⚙️ Configuration changes ✨ Enhancement 🕐 20-40 Minutes

Grey Divider

AI Description

• 将 merge queue 从不可用的组织级规则集迁移为仓库级声明。
• 通过 GraphQL 幂等创建规则集,并由 REST 检测参数漂移。
• 集中声明启用仓库和队列参数,阻止未经 ADR 批准的扩围。
Diagram

graph TD
  E["期望状态"] -->|仓库与参数| A["应用脚本"] -->|创建| G["GraphQL API"] --> R["仓库级规则集"]
  R -->|读取| X["REST API"] --> D["漂移检查"]
  E -->|期望参数| D
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. GraphQL 创建与更新统一对账
  • ➕ 让 apply.sh 能直接修复已存在规则集的参数漂移
  • ➕ 保持应用脚本与“幂等修复”职责一致
  • ➖ 更新时必须完整保留关联的 pull_request 和 required_status_checks 规则
  • ➖ 增加规则集 ID、GraphQL 输入转换及失败恢复复杂度
2. 从 expected-state 动态生成 mutation
  • ➕ 避免队列参数同时存在于 JSON 和硬编码 GraphQL mutation 中
  • ➕ 后续调整参数只需修改一个声明源
  • ➖ 需要将 snake_case JSON 值转换为 GraphQL camelCase 字段和枚举
  • ➖ Shell 中动态构造复杂 GraphQL 输入的可维护性较低
3. 采用 Terraform GitHub provider
  • ➕ 提供声明式计划、状态记录和统一生命周期管理
  • ➕ 可减少自定义 Shell 对账逻辑
  • ➖ merge_queue 规则支持可能继续受 provider 或 GitHub API 限制
  • ➖ 引入状态迁移、凭据管理和新的治理工具链

Recommendation: 当前采用“GraphQL 写、REST 读”的仓库级方案是针对 GitHub API 限制的合理修正,也比继续维护无效的组织级声明更安全。建议合并当前方案;后续优先从 expected-state 生成创建参数,并在确认不会覆盖组合规则后补充 GraphQL 更新能力,使 apply.sh 能修复而不只是报告参数漂移。

Files changed (3) +64 / -1

Enhancement (1) +33 / -0
drift-check.sh新增仓库级 merge queue 漂移对账 +33/-0

新增仓库级 merge queue 漂移对账

• 新增 §14,通过 REST 检查声明仓库的 merge-queue ruleset 是否存在并逐项比较队列参数。受管仓库若未被声明却启用了同名规则集,也会被判定为漂移。

governance/drift-check.sh

Bug fix (1) +14 / -0
apply.sh通过 GraphQL 创建仓库级 merge queue 规则集 +14/-0

通过 GraphQL 创建仓库级 merge queue 规则集

• 新增第六步,遍历 expected-state 中声明的仓库并查询现有 merge-queue ruleset。规则集缺失时通过 GraphQL 创建包含 pull request、gate 状态检查和保守 merge queue 参数的组合规则,已存在时跳过。

governance/apply.sh

Other (1) +17 / -1
expected-state.json声明 merge queue 仓库范围与期望参数 +17/-1

声明 merge queue 仓库范围与期望参数

• 新增 merge_queue 配置段,将 agent-registry、template-service 及其 squash、串行合并和超时参数设为统一期望状态,并记录 ADR-0042 的扩围约束。

governance/expected-state.json

@randypanding
randypanding merged commit abfc0ce into main Aug 20, 2026
7 checks passed
@randypanding
randypanding deleted the p2-7-merge-queue-repo-scope-fix branch August 20, 2026 06:17
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (6) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Action required

1. Merge method always mismatches 🐞 Bug ≡ Correctness
Description
The expected state stores merge_method as lowercase squash, while GitHub’s merge-queue REST
representation uses the uppercase enum SQUASH. Consequently §14 reports parameter drift for the
GraphQL-created ruleset even when it has exactly the intended merge method.
Code

governance/expected-state.json[90]

+      "merge_method": "squash",
Relevance

●●● Strong

The declared lowercase enum and produced uppercase enum are a deterministic exact-comparison
mismatch.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The mutation creates mergeMethod: SQUASH, while §14 performs an exact JSON comparison against
lowercase squash. GitHub’s REST rules schema documents MERGE, SQUASH, and REBASE as the
valid merge-queue values.

governance/apply.sh[151-151]
governance/drift-check.sh[575-580]
governance/expected-state.json[89-96]
🌐 GitHub documents merge-queue merge_method values as the uppercase enums MERGE, SQUASH, and REBASE.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The expected merge-queue method uses lowercase `squash`, but the REST ruleset response uses the uppercase merge-queue enum `SQUASH`, causing permanent false drift.

## Issue Context
GraphQL creates the rule with `mergeMethod: SQUASH`, and drift-check compares the REST response directly with expected-state JSON.

## Fix Focus Areas
- governance/expected-state.json[89-96]
- governance/drift-check.sh[575-580]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Undeclared scan fails open 🐞 Bug ☼ Reliability
Description
When an undeclared repository’s rulesets request fails or returns an error object, line 587 silently
skips that repository without recording drift. The overall check can therefore exit successfully
while being unable to determine whether that repository has an unauthorized merge queue.
Code

governance/drift-check.sh[R586-587]

+  RS=$(api "https://api.github.com/repos/$ORG/$r/rulesets?per_page=100")
+  jq -e 'type == "array"' <<<"$RS" >/dev/null 2>&1 || continue
Relevance

●●● Strong

PR #19 explicitly accepted fail-closed handling for governance API errors instead of silently
passing checks.

PR-#19

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The API helper returns GitHub error bodies without checking HTTP status, and line 587 converts any
non-array response into continue; only calls to drift affect the final nonzero exit. This
repeats the fail-open API-response pattern previously fixed in PR #19.

governance/drift-check.sh[19-20]
governance/drift-check.sh[565-568]
governance/drift-check.sh[583-588]
PR-#19

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The undeclared merge-queue scan silently skips repositories whose rulesets cannot be read, allowing an unverifiable run to pass.

## Issue Context
The declared-repository branch already records the same non-array response as drift. Apply equivalent fail-closed behavior to the undeclared scan.

## Fix Focus Areas
- governance/drift-check.sh[583-588]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Disabled queue passes reconciliation 🐞 Bug ≡ Correctness
Description
Section 14 compares only the merge_queue rule parameters and never verifies ruleset enforcement,
target, or default-branch conditions. A ruleset changed to disabled evaluation or made inapplicable
to the default branch therefore still produces an OK result despite the expected merge queue no
longer being enforced.
Code

governance/drift-check.sh[R576-579]

+  got_p=$(jq -c '.rules[] | select(.type == "merge_queue") | .parameters
+    | {merge_method, check_response_timeout_minutes, max_entries_to_build,
+       min_entries_to_merge, max_entries_to_merge, min_entries_to_merge_wait_minutes,
+       grouping_strategy}' <<<"$detail")
Relevance

●● Moderate

The missing enforcement and condition checks are semantically important, but no close historical
precedent establishes reviewer treatment.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The apply mutation creates an ACTIVE, BRANCH ruleset targeting ~DEFAULT_BRANCH, but the
detector extracts and compares only seven merge-queue parameters before printing OK.

governance/apply.sh[151-151]
governance/drift-check.sh[573-581]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The merge-queue drift check validates only queue parameters, allowing disabled or inapplicable rulesets to pass.

## Issue Context
The creation mutation explicitly requires active enforcement, a branch target, and the default-branch condition, so these fields are part of the effective desired state even though §14 ignores them.

## Fix Focus Areas
- governance/drift-check.sh[573-581]
- governance/apply.sh[151-151]
- governance/expected-state.json[83-97]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (1)
4. Renamed queues evade detection 🐞 Bug ≡ Correctness
Description
The undeclared-repository check identifies merge queues solely by the arbitrary ruleset name
merge-queue, rather than by a contained rule whose type is merge_queue. An undeclared repository
can therefore enable a queue under any other ruleset name without producing drift.
Code

governance/drift-check.sh[588]

+  jq -e '.[] | select(.name == "merge-queue")' <<<"$RS" >/dev/null 2>&1     && drift "repo '$r' 存在未声明的 merge-queue ruleset(expected-state.merge_queue.repos 未列——扩围须修订 ADR-0042)"
Relevance

●● Moderate

Behavior-based rule detection is plausible, but available history does not establish acceptance for
this naming-versus-type change.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The undeclared scan checks only .name == "merge-queue", while the declared path itself recognizes
queue behavior through .rules[].type == "merge_queue". GitHub’s rules schema likewise defines
merge queue as a rule type, independently of the ruleset name.

governance/drift-check.sh[569-580]
governance/drift-check.sh[583-588]
🌐 GitHub defines merge queue as a ruleset rule with type: merge_queue; the enclosing ruleset name is not the behavior identifier.

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Unauthorized merge queues evade detection when their repository ruleset has a name other than `merge-queue`.

## Issue Context
Ruleset names are labels; merge-queue behavior is represented by a rule with type `merge_queue`. The list response does not include full rules, so matching candidates must be fetched and inspected by rule type.

## Fix Focus Areas
- governance/drift-check.sh[583-588]
- governance/drift-check.sh[569-580]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Configured organization is ignored 🐞 Bug ≡ Correctness
Description
Step 6 hardcodes Cloudbird-Software in both GraphQL lookups instead of using the script’s
configurable ORG. Running with an overridden organization makes earlier steps operate on the
requested organization while merge-queue creation fails against, or targets repositories in,
Cloudbird-Software.
Code

governance/apply.sh[R145-146]

+  SRC_ID=$(curl -sS -H "Authorization: Bearer ${GH_TOKEN}" -H "Content-Type: application/json"         -d '{"query":"{repository(owner:\"Cloudbird-Software\", name:\"'"$r"'\" ){id}}"}' https://api.github.com/graphql | jq -r '.data.repository.id')
+  RID=$(curl -sS -H "Authorization: Bearer ${GH_TOKEN}" -H "Content-Type: application/json"         -d '{"query":"query($r:String!){repository(owner:\"Cloudbird-Software\", name:$r){rulesets(first:50){nodes{name databaseId}}}}","variables":{"r":"'"$r"'"}}'         https://api.github.com/graphql | jq -r '.data.repository.rulesets.nodes[]? | select(.name == "merge-queue") | .databaseId')
Relevance

●●● Strong

Hardcoding configurable organization identity creates a deterministic cross-organization correctness
failure.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
ORG is configurable at script initialization and is used for repository baseline writes, but both
newly added GraphQL repository queries contain a literal owner.

governance/apply.sh[26-32]
governance/apply.sh[138-140]
governance/apply.sh[145-146]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The merge-queue GraphQL calls ignore the configured `ORG` and always query Cloudbird-Software.

## Issue Context
`apply.sh` explicitly supports overriding `ORG`, and all preceding REST operations use that value. Pass owner and repository name as GraphQL variables rather than interpolating literals.

## Fix Focus Areas
- governance/apply.sh[26-32]
- governance/apply.sh[143-146]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Apply ignores declared parameters 🐞 Bug ⚙ Maintainability
Description
The creation mutation hardcodes every merge-queue parameter rather than reading
.merge_queue.params from expected-state.json. A future parameter change in the declared single
source creates the old configuration, after which drift-check immediately rejects the result.
Code

governance/apply.sh[R151-152]

+  MUT='mutation($src: ID!) { createRepositoryRuleset(input: { sourceId: $src, name: "merge-queue", target: BRANCH, enforcement: ACTIVE, conditions: { refName: { include: ["~DEFAULT_BRANCH"], exclude: [] } }, rules: [ { type: PULL_REQUEST, parameters: { pullRequest: { requiredApprovingReviewCount: 0, dismissStaleReviewsOnPush: true, requireCodeOwnerReview: false, requireLastPushApproval: false, requiredReviewThreadResolution: false, allowedMergeMethods: [SQUASH] } } }, { type: REQUIRED_STATUS_CHECKS, parameters: { requiredStatusChecks: { requiredStatusChecks: [{ context: "gate", integrationId: null }], strictRequiredStatusChecksPolicy: false } } }, { type: MERGE_QUEUE, parameters: { mergeQueue: { mergeMethod: SQUASH, checkResponseTimeoutMinutes: 60, maxEntriesToBuild: 5, minEntriesToMerge: 1, maxEntriesToMerge: 1, minEntriesToMergeWaitMinutes: 0, groupingStrategy: ALLGREEN } } } ] }) { ruleset { databaseId } } }'
+  RESP=$(jq -n --arg q "$MUT" --arg src "$SRC_ID" '{query:$q, variables:{src:$src}}')
Relevance

●●● Strong

The repository consistently accepts findings requiring governance checks and declarations to share
one authoritative source.

PR-#19

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Expected state declares the seven queue parameters and drift-check reads them dynamically, but the
mutation independently embeds the current values. Thus the two implementations diverge as soon as
the declaration changes.

governance/expected-state.json[83-97]
governance/apply.sh[151-153]
governance/drift-check.sh[575-580]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Merge-queue creation duplicates all expected parameters as hardcoded GraphQL values instead of consuming the expected-state declaration.

## Issue Context
The expected-state file describes itself as the source used by both drift-check and apply. Drift-check already reads these parameters dynamically, while apply does not.

## Fix Focus Areas
- governance/apply.sh[151-153]
- governance/expected-state.json[83-97]
- governance/drift-check.sh[575-580]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

7. GH_TOKEN bypasses App identity 📘 Rule violation ⛨ Security
Description
The new merge-queue automation authenticates every repository request with a caller-supplied
GH_TOKEN instead of obtaining a cloudbrid-agent token through scripts/gh-app-token.sh. Reusing
one token across the repository loop also fails the required single-repository scoping.
Code

governance/apply.sh[R145-146]

+  SRC_ID=$(curl -sS -H "Authorization: Bearer ${GH_TOKEN}" -H "Content-Type: application/json"         -d '{"query":"{repository(owner:\"Cloudbird-Software\", name:\"'"$r"'\" ){id}}"}' https://api.github.com/graphql | jq -r '.data.repository.id')
+  RID=$(curl -sS -H "Authorization: Bearer ${GH_TOKEN}" -H "Content-Type: application/json"         -d '{"query":"query($r:String!){repository(owner:\"Cloudbird-Software\", name:$r){rulesets(first:50){nodes{name databaseId}}}}","variables":{"r":"'"$r"'"}}'         https://api.github.com/graphql | jq -r '.data.repository.rulesets.nodes[]? | select(.name == "merge-queue") | .databaseId')
Relevance

● Weak

A close token-scope finding was explicitly rejected in PR #49 despite the same governance GH_TOKEN
concern.

PR-#49

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 2778539 requires GitHub automation to obtain credentials exclusively through
scripts/gh-app-token.sh with a token restricted to one repository. The added GraphQL calls use the
same externally supplied GH_TOKEN for every repository, while the token helper explicitly requires
REPO and requests a repository-scoped installation token.

Rule 2778539: Agent operations must use the cloudbrid-agent GitHub App identity via scripts/gh-app-token.sh with single-repo tokens
governance/apply.sh[143-153]
governance/drift-check.sh[563-588]
scripts/gh-app-token.sh[20-29]
scripts/gh-app-token.sh[60-74]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The added merge-queue API operations reuse a generic `GH_TOKEN` and bypass the required cloudbrid-agent identity and single-repository token scope.

## Issue Context
Before accessing each repository, invoke `scripts/gh-app-token.sh` with `REPO` set to that repository and use the returned token only for that repository's requests. Ensure the App has the repository permissions needed to read and manage rulesets, and apply the same identity model to drift checking.

## Fix Focus Areas
- governance/apply.sh[143-154]
- governance/drift-check.sh[563-588]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 12 rules
✅ Web pages:
  +15 more
Review mode: ⚖️ Balanced: This changes executable governance scripts and API-driven ruleset reconciliation across apply and drift-check paths, with subtle GraphQL/REST, idempotency, and parameter-contract risks; it is not clearly dense enough to warrant redundant extended review.
ⓘ  6 issues published inline · 7 in summary

Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

"template-service"
],
"params": {
"merge_method": "squash",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Merge method always mismatches 🐞 Bug ≡ Correctness

The expected state stores merge_method as lowercase squash, while GitHub’s merge-queue REST
representation uses the uppercase enum SQUASH. Consequently §14 reports parameter drift for the
GraphQL-created ruleset even when it has exactly the intended merge method.
Agent Prompt
## Issue description
The expected merge-queue method uses lowercase `squash`, but the REST ruleset response uses the uppercase merge-queue enum `SQUASH`, causing permanent false drift.

## Issue Context
GraphQL creates the rule with `mergeMethod: SQUASH`, and drift-check compares the REST response directly with expected-state JSON.

## Fix Focus Areas
- governance/expected-state.json[89-96]
- governance/drift-check.sh[575-580]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/drift-check.sh
Comment on lines +576 to +579
got_p=$(jq -c '.rules[] | select(.type == "merge_queue") | .parameters
| {merge_method, check_response_timeout_minutes, max_entries_to_build,
min_entries_to_merge, max_entries_to_merge, min_entries_to_merge_wait_minutes,
grouping_strategy}' <<<"$detail")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Disabled queue passes reconciliation 🐞 Bug ≡ Correctness

Section 14 compares only the merge_queue rule parameters and never verifies ruleset enforcement,
target, or default-branch conditions. A ruleset changed to disabled evaluation or made inapplicable
to the default branch therefore still produces an OK result despite the expected merge queue no
longer being enforced.
Agent Prompt
## Issue description
The merge-queue drift check validates only queue parameters, allowing disabled or inapplicable rulesets to pass.

## Issue Context
The creation mutation explicitly requires active enforcement, a branch target, and the default-branch condition, so these fields are part of the effective desired state even though §14 ignores them.

## Fix Focus Areas
- governance/drift-check.sh[573-581]
- governance/apply.sh[151-151]
- governance/expected-state.json[83-97]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/drift-check.sh
jq -e --arg r "$r" '.merge_queue.repos // [] | index($r) != null' "$EXPECTED" >/dev/null && continue
RS=$(api "https://api.github.com/repos/$ORG/$r/rulesets?per_page=100")
jq -e 'type == "array"' <<<"$RS" >/dev/null 2>&1 || continue
jq -e '.[] | select(.name == "merge-queue")' <<<"$RS" >/dev/null 2>&1 && drift "repo '$r' 存在未声明的 merge-queue ruleset(expected-state.merge_queue.repos 未列——扩围须修订 ADR-0042)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Renamed queues evade detection 🐞 Bug ≡ Correctness

The undeclared-repository check identifies merge queues solely by the arbitrary ruleset name
merge-queue, rather than by a contained rule whose type is merge_queue. An undeclared repository
can therefore enable a queue under any other ruleset name without producing drift.
Agent Prompt
## Issue description
Unauthorized merge queues evade detection when their repository ruleset has a name other than `merge-queue`.

## Issue Context
Ruleset names are labels; merge-queue behavior is represented by a rule with type `merge_queue`. The list response does not include full rules, so matching candidates must be fetched and inspected by rule type.

## Fix Focus Areas
- governance/drift-check.sh[583-588]
- governance/drift-check.sh[569-580]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/drift-check.sh
Comment on lines +586 to +587
RS=$(api "https://api.github.com/repos/$ORG/$r/rulesets?per_page=100")
jq -e 'type == "array"' <<<"$RS" >/dev/null 2>&1 || continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

5. Undeclared scan fails open 🐞 Bug ☼ Reliability

When an undeclared repository’s rulesets request fails or returns an error object, line 587 silently
skips that repository without recording drift. The overall check can therefore exit successfully
while being unable to determine whether that repository has an unauthorized merge queue.
Agent Prompt
## Issue description
The undeclared merge-queue scan silently skips repositories whose rulesets cannot be read, allowing an unverifiable run to pass.

## Issue Context
The declared-repository branch already records the same non-array response as drift. Apply equivalent fail-closed behavior to the undeclared scan.

## Fix Focus Areas
- governance/drift-check.sh[583-588]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/apply.sh
Comment on lines +145 to +146
SRC_ID=$(curl -sS -H "Authorization: Bearer ${GH_TOKEN}" -H "Content-Type: application/json" -d '{"query":"{repository(owner:\"Cloudbird-Software\", name:\"'"$r"'\" ){id}}"}' https://api.github.com/graphql | jq -r '.data.repository.id')
RID=$(curl -sS -H "Authorization: Bearer ${GH_TOKEN}" -H "Content-Type: application/json" -d '{"query":"query($r:String!){repository(owner:\"Cloudbird-Software\", name:$r){rulesets(first:50){nodes{name databaseId}}}}","variables":{"r":"'"$r"'"}}' https://api.github.com/graphql | jq -r '.data.repository.rulesets.nodes[]? | select(.name == "merge-queue") | .databaseId')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

6. Configured organization is ignored 🐞 Bug ≡ Correctness

Step 6 hardcodes Cloudbird-Software in both GraphQL lookups instead of using the script’s
configurable ORG. Running with an overridden organization makes earlier steps operate on the
requested organization while merge-queue creation fails against, or targets repositories in,
Cloudbird-Software.
Agent Prompt
## Issue description
The merge-queue GraphQL calls ignore the configured `ORG` and always query Cloudbird-Software.

## Issue Context
`apply.sh` explicitly supports overriding `ORG`, and all preceding REST operations use that value. Pass owner and repository name as GraphQL variables rather than interpolating literals.

## Fix Focus Areas
- governance/apply.sh[26-32]
- governance/apply.sh[143-146]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread governance/apply.sh
Comment on lines +151 to +152
MUT='mutation($src: ID!) { createRepositoryRuleset(input: { sourceId: $src, name: "merge-queue", target: BRANCH, enforcement: ACTIVE, conditions: { refName: { include: ["~DEFAULT_BRANCH"], exclude: [] } }, rules: [ { type: PULL_REQUEST, parameters: { pullRequest: { requiredApprovingReviewCount: 0, dismissStaleReviewsOnPush: true, requireCodeOwnerReview: false, requireLastPushApproval: false, requiredReviewThreadResolution: false, allowedMergeMethods: [SQUASH] } } }, { type: REQUIRED_STATUS_CHECKS, parameters: { requiredStatusChecks: { requiredStatusChecks: [{ context: "gate", integrationId: null }], strictRequiredStatusChecksPolicy: false } } }, { type: MERGE_QUEUE, parameters: { mergeQueue: { mergeMethod: SQUASH, checkResponseTimeoutMinutes: 60, maxEntriesToBuild: 5, minEntriesToMerge: 1, maxEntriesToMerge: 1, minEntriesToMergeWaitMinutes: 0, groupingStrategy: ALLGREEN } } } ] }) { ruleset { databaseId } } }'
RESP=$(jq -n --arg q "$MUT" --arg src "$SRC_ID" '{query:$q, variables:{src:$src}}')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

7. Apply ignores declared parameters 🐞 Bug ⚙ Maintainability

The creation mutation hardcodes every merge-queue parameter rather than reading
.merge_queue.params from expected-state.json. A future parameter change in the declared single
source creates the old configuration, after which drift-check immediately rejects the result.
Agent Prompt
## Issue description
Merge-queue creation duplicates all expected parameters as hardcoded GraphQL values instead of consuming the expected-state declaration.

## Issue Context
The expected-state file describes itself as the source used by both drift-check and apply. Drift-check already reads these parameters dynamically, while apply does not.

## Fix Focus Areas
- governance/apply.sh[151-153]
- governance/expected-state.json[83-97]
- governance/drift-check.sh[575-580]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

This was referenced Aug 20, 2026
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