Skip to content

feat(drill): 注入/独立验证/台账引擎(W4-C4 .github#223,ADR-0069) - #245

Merged
randypanding merged 1 commit into
mainfrom
w4c4-seed-drill-engine
Aug 21, 2026
Merged

feat(drill): 注入/独立验证/台账引擎(W4-C4 .github#223,ADR-0069)#245
randypanding merged 1 commit into
mainfrom
w4c4-seed-drill-engine

Conversation

@randypanding

Copy link
Copy Markdown
Contributor

动机

样本选出来之后要能注入、能独立判定红绿、能留痕进 dashboard 数据源(ADR-0069 决策 2/3)。本 PR 补全演习引擎:git 隔离分支注入 + 独立验证脚本 + append-only 台账与红率聚合。

变更清单

  • governance/drill/drill.py 补全:inject(浅克隆→演习分支→写缺陷→push 3 次重试,只开分支不开 PR)、record(append-only:时间戳严格递增 + 同 run 去重)、redrate(红率 + 难度按周分布 + 零分母 null 诚实口径 + --fail-unhealthy 告警出口)
  • governance/drill/verify_gate.py独立验证步骤(与样本库分离)——不读样本库、不注入,只断言"该缺陷应触发关卡 X 失败":RED=演习成功(0) / GREEN=演习失败(1) / NO-SURFACE / MISSING-GATE / TIMEOUT(3),静默期 120s 防"workflow 排队中"假阴性
  • 测试:test-select.sh(同 seed 确定性、holdout 永不入池、github-scope 只打治理总仓、钉选通道)+ test-verify.sh(红绿判定核 8 断言)+ governance/tests/test-drill.sh CI 聚合入口;gate.yml bash -n 登记

首次演习实录(AC-1 证据,2026-08-21 UTC)

  1. select --seed 20260822 --sample-id hygiene-gitleaks-aws-key --target-repo .github
  2. inject → 分支 drill/seed-20260822 @ 78ee220(缺陷文件 drill/leak-aws-20260821.ini
  3. push 面验证 → NO-SURFACE(局限实测:org gate 触发面为 pull_request+push:main,push 分支不跑 CI——已按卡面要求记录该局限)
  4. 降级 draft PR 面(ADR-0069 决策 2 原始形态"向随机 PR 注入")→ PR drill(seed): hygiene-gitleaks-aws-key 演习注入(ADR-0069)——勿合并,验后即删 #239
  5. 独立验证 → {"verdict": "RED", "check_name": "org-hygiene / hygiene", "conclusion": "failure"}——gitleaks 抓到伪造 AWS 键,关卡活着,红=演习成功
  6. 清理:PR drill(seed): hygiene-gitleaks-aws-key 演习注入(ADR-0069)——勿合并,验后即删 #239 关闭 + 分支删除(注入物不长期存续)

AC 映射

  • AC-1(逻辑面+实录) Given 周演习,When 注入已知缺陷,Then 对应关卡变红且结果进台账(history.jsonl 首条 seed-drill 记录:verdict=red, surface=draft-pr, limitation=NO-SURFACE 实测);绿→P0 的自动化开单在 PR4 workflow(GREEN 判定路径已被 test-verify 断言)
  • AC-4 redrate:红率 = red/(red+green)(首演后 =1.0),难度按周分布可见,--fail-unhealthy 红率<100% 非零退出

测试方法

bash governance/tests/test-drill.sh → 3 套 24 断言全绿(本 PR 时点);台账操作见 history.jsonl 首演两行实录

风险与回滚

注入只落演习分支(不进 agent 工作区);台账 append-only 由 record 命令强制。回滚:摘除脚本即停,台账留档无害。

Card: #223

Copilot AI lite review requested due to automatic review settings August 21, 2026 19:27
@coderabbitai

coderabbitai Bot commented Aug 21, 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: 1 minute

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: bf177d3e-ef3e-4dda-b9e1-63fce4e47f2e

📥 Commits

Reviewing files that changed from the base of the PR and between cdbfafd and 1320285.

📒 Files selected for processing (6)
  • .github/workflows/gate.yml
  • governance/drill/drill.py
  • governance/drill/tests/test-select.sh
  • governance/drill/tests/test-verify.sh
  • governance/drill/verify_gate.py
  • governance/tests/test-drill.sh

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

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

feat(drill): add inject + independent gate verification + drill ledger aggregation

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add drill branch injection, append-only ledger recording, and red-rate aggregation (ADR-0069
 AC-4).
• Introduce independent gate verification that never reads samples or performs injection (ADR-0069
 decision 2).
• Add CI-covered bash/Python self-tests to lock determinism, fail-closed semantics, and verdict
 mapping.
Diagram

graph TD
  CI["CI: gate.yml"] --> Tests["Drill tests (test-drill.sh)"] --> DrillCLI["drill.py (inject/record/redrate)"] --> Repo[("Target repo (drill branch)")] --> Verifier["verify_gate.py"] --> API{{"GitHub Checks API"}}
  DrillCLI --> Ledger[("history.jsonl ledger")]
  subgraph Legend
    direction LR
    _wf["Workflow/Test"] ~~~ _db[("Repo/Ledger")] ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use GitHub CLI/API for injection instead of raw git push
  • ➕ Avoids embedding git auth header logic and push retry plumbing
  • ➕ Can create refs/commits via API with clearer audit semantics
  • ➖ More API surface area and pagination/error handling to maintain
  • ➖ Potentially harder to keep payload generation and commit metadata consistent across clients
2. Store ledger in SQLite (or a small structured store) instead of JSONL
  • ➕ Enforces uniqueness/monotonic constraints at the storage layer
  • ➕ Simplifies aggregation queries and reduces full-file scans as history grows
  • ➖ Adds operational complexity (file locking, migrations)
  • ➖ JSONL is easier to inspect, diff, and ship as a dashboard source
3. Adopt a minimal HTTP client library (e.g., requests) for check-runs polling
  • ➕ Clearer error handling/timeouts and less boilerplate than urllib
  • ➕ Easier to add retries/backoff and structured logging
  • ➖ Introduces a dependency surface that may be undesirable for governance scripts
  • ➖ Current urllib usage keeps the verifier dependency-free

Recommendation: The PR’s approach (git-based injection + dependency-free verifier + JSONL append-only ledger) is a good fit for a governance repo: low dependency footprint, explicit fail-closed behavior, and auditable artifacts. If this grows beyond weekly runs (large history.jsonl or more complex verification rules), consider moving the ledger to SQLite and/or switching verification HTTP handling to a more ergonomic client; for now, keeping scripts simple is a reasonable tradeoff.

Files changed (6) +375 / -6

Enhancement (2) +236 / -5
drill.pyAdd inject/record/redrate subcommands for the drill engine +133/-5

Add inject/record/redrate subcommands for the drill engine

• Implements git-isolated defect injection into a dedicated drill branch with shallow clone and push retry logic. Adds an append-only JSONL ledger writer enforcing strictly increasing timestamps and per-run de-duplication, plus a red-rate/difficulty trend aggregator with honest null semantics and optional unhealthy failure.

governance/drill/drill.py

verify_gate.pyIntroduce independent gate verification via GitHub check-runs polling +103/-0

Introduce independent gate verification via GitHub check-runs polling

• Adds a standalone verifier that polls GitHub check-runs (or reads an offline JSON fixture) and returns a structured verdict. Enforces a grace period to avoid false NO-SURFACE, treats missing/ambiguous gates as indeterminate (not GREEN), and maps RED/GREEN to exit codes 0/1 with all other states as 3.

governance/drill/verify_gate.py

Tests (3) +135 / -0
test-select.shAdd deterministic selection and target-pool constraint tests +49/-0

Add deterministic selection and target-pool constraint tests

• Adds a bash test suite asserting deterministic output for the same seed, holdout isolation, github-scope targeting rules, and fail-closed behavior for invalid pinned sample IDs. Verifies required output fields for later auditing/aggregation.

governance/drill/tests/test-select.sh

test-verify.shAdd offline verdict-mapping tests for verify_gate +72/-0

Add offline verdict-mapping tests for verify_gate

• Introduces an offline check-runs injection harness to assert RED/GREEN/NO-SURFACE/MISSING-GATE/TIMEOUT semantics and exit codes. Verifies substring matching behavior, ambiguity handling, and fail-closed credential requirements for API mode.

governance/drill/tests/test-verify.sh

test-drill.shAdd drill test aggregator entrypoint for CI +14/-0

Add drill test aggregator entrypoint for CI

• Adds a governance-level test runner that discovers and executes all governance/drill/tests/test-*.sh scripts. Fails closed if the drill test directory contains no tests, preventing silent loss of the drill test surface.

governance/tests/test-drill.sh

Other (1) +4 / -1
gate.ymlWire drill self-tests into governance script validation +4/-1

Wire drill self-tests into governance script validation

• Extends the gate workflow’s bash syntax-check list to include the new drill test entrypoint and drill test scripts. Ensures drill test surface is enforced in CI as part of governance script checks.

.github/workflows/gate.yml

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR implements the core “seed drill” engine for governance gate health checks (ADR-0069 / Card #223): injecting known defects into an isolated branch, independently verifying whether a target gate fails, and producing an append-only history source suitable for dashboard aggregation.

Changes:

  • Extend governance/drill/drill.py with inject, record (append-only ledger), and redrate aggregation.
  • Add an independent verifier governance/drill/verify_gate.py that polls GitHub check-runs and outputs RED/GREEN/NO-SURFACE/MISSING-GATE/TIMEOUT with strict fail-closed behavior.
  • Add offline self-tests for selection determinism and verification semantics, and wire drill tests into the gate workflow’s script checks.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
governance/tests/test-drill.sh Aggregates and fail-closes if governance/drill/tests/test-*.sh coverage disappears.
governance/drill/verify_gate.py Independent verifier that polls check-runs (or offline JSON) and emits a drill verdict/exit code.
governance/drill/tests/test-verify.sh Offline assertions for verifier verdict semantics and fail-closed behavior.
governance/drill/tests/test-select.sh Offline assertions for deterministic selection and target pool constraints.
governance/drill/drill.py Adds injection, ledger append, and red-rate aggregation capabilities to the drill engine.
.github/workflows/gate.yml Registers the new drill scripts for bash syntax checking in CI.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread governance/drill/drill.py
data = b"\0" * s["size_bytes"]
with open(path, "wb") as f:
f.write(data)
_git({}, "add", path, cwd=tmp)
Comment thread governance/drill/drill.py
Comment on lines +246 to +256
weeks = {}
for r in seeds:
wk = r["ts"][:10]
d = r.get("difficulty", "?")
weeks.setdefault(wk, {}).setdefault(d, 0)
weeks[wk][d] += 1
fails = [r for r in rows if r.get("kind") == "failclose-drill"]
out = {"total_records": len(rows), "seed_drills": len(seeds), "verdicts": verdicts,
"red_rate": rate, "red_rate_note": None if denom else "N/A(尚无可判定演习)",
"difficulty_trend_by_day": {k: dict(sorted(v.items())) for k, v in sorted(weeks.items())},
"failclose_drills": len(fails),
Comment thread governance/drill/drill.py
for l in lines:
prev = json.loads(l) # 既有行畸形=台账已损坏,拒绝追加(fail-closed)
if prev["ts"] >= rec["ts"]:
die(f"append-only 破坏: 新 ts {rec['ts']} 不晚于末行 {prev['ts']}")
f"{API}/repos/{org}/{repo}/commits/{sha}/check-runs?per_page=100",
headers={"Authorization": f"Bearer {token}", "Accept": "application/vnd.github+json",
"User-Agent": "seed-drill-verify"})
with urllib.request.urlopen(req) as r:
hits = match_gate(checks, gate)
if hits and hits[0].get("status") == "completed":
c = hits[0]
v = "RED" if c.get("conclusion") == "failure" else "GREEN"
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (2) 📜 Skill insights (0)

Grey Divider


Action required

1. Non-success marked GREEN 🐞 Bug ≡ Correctness
Description
verify_gate.py 在目标 check-run status=completed 时,将除 failure 之外的所有 conclusion 都判为 GREEN,导致
cancelled/timed_out/neutral/skipped 等非 success 的情况被误判为“关卡通过”。这会把中间态/异常态当成“关卡死了”触发错误的 GREEN(退出码1)
与错误台账口径。
Code

governance/drill/verify_gate.py[R56-59]

+        if hits and hits[0].get("status") == "completed":
+            c = hits[0]
+            v = "RED" if c.get("conclusion") == "failure" else "GREEN"
+            return {"verdict": v, "check_name": c.get("name"),
Relevance

●●● Strong

Treating every non-failure conclusion as success violates fail-closed verification; team favors
explicit abnormal-state handling.

PR-#19

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
代码在 completed 状态下仅区分 conclusion==failure,其它全部落入 GREEN;而 GitHub 的 check conclusion 允许多种非 success
的终止状态,因此会发生误判。

governance/drill/verify_gate.py[45-63]
🌐 GitHub 文档说明当 status 为 completed 时,conclusion 可能是 action_required/cancelled/timed_out/failure/neutral/skipped/stale/startup_failure/success 等多种值,并非只有 success/failure。

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

### Issue description
`verify_gate.py` currently maps any completed check run whose `conclusion != "failure"` to `GREEN`. GitHub check-run conclusions include multiple non-success terminal states (cancelled/timed_out/neutral/skipped/action_required/startup_failure/stale, etc.). Treating those as `GREEN` produces false “gate is green” alarms.

### Issue Context
The verifier is intended to assert “this defect should make gate X fail”. For completed runs:
- `failure` => `RED`
- `success` => `GREEN`
- other completed conclusions should be treated as an intermediate/indeterminate verdict (e.g., `TIMEOUT`/`MISSING-GATE`/new `INDETERMINATE`) with exit code 3, because they are not evidence that the gate is passing.

### Fix Focus Areas
- governance/drill/verify_gate.py[56-62]

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


2. No API timeout handling 🐞 Bug ☼ Reliability
Description
verify_gate.py 调用 urllib.request.urlopen 拉取 check-runs 时未设置 timeout
且未捕获网络异常,可能在网络抖动/代理问题/连接卡死时无限阻塞或直接抛异常终止,导致验证步骤与上游 workflow 不可预测地挂起或无结构化输出。
Code

governance/drill/verify_gate.py[R29-32]

+        f"{API}/repos/{org}/{repo}/commits/{sha}/check-runs?per_page=100",
+        headers={"Authorization": f"Bearer {token}", "Accept": "application/vnd.github+json",
+                 "User-Agent": "seed-drill-verify"})
+    with urllib.request.urlopen(req) as r:
Relevance

●● Moderate

Timeout handling is reasonable, but similar network-hardening requests were recently rejected as
nonessential.

PR-#174

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
代码在 fetch_check_runs 中直接 urlopen 且无 timeout;Python 文档说明 timeout 未指定时使用全局默认值(可能无限等待),从而可能突破本脚本的
deadline 语义。

governance/drill/verify_gate.py[27-34]
🌐 Python 文档:urlopen 的 timeout 参数用于阻塞操作;若不指定则使用全局默认 timeout(global default timeout setting)。

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

### Issue description
`fetch_check_runs()` uses `urllib.request.urlopen(req)` without a timeout and without exception handling. In CI, a stuck TCP connection or slow proxy can block indefinitely (ignoring the intended `--timeout` polling window), or crash the process with an unhandled exception.

### Issue Context
The verifier already has a polling loop and a user-facing `--timeout` budget. Network calls should respect that budget by:
- passing a per-request timeout
- catching `URLError`/`HTTPError` and continuing the poll (or returning an indeterminate verdict) rather than crashing
- optionally backing off / limiting retries

### Fix Focus Areas
- governance/drill/verify_gate.py[27-34]
- governance/drill/verify_gate.py[45-80]

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



Remediation recommended

3. Inject path traversal risk 🐞 Bug ⛨ Security
Description
drill.py 的 inject 用 samples.registry.yaml 的 payload_path 直接 split/join 到临时工作区路径,未禁止绝对路径或 '..'
片段,样本库一旦被误写/被恶意提交可导致写入临时目录外任意路径。即使最终 git add/commit 失败,也会在 runner 上发生越界写文件。
Code

governance/drill/drill.py[R173-174]

+        path = os.path.join(tmp, *s["payload_path"].format(DATE=date).split("/"))
+        os.makedirs(os.path.dirname(path), exist_ok=True)
Relevance

●●● Strong

Unsanitized payload paths permit deterministic directory traversal; fail-closed path validation
matches repo priorities.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
validate_samples 对 payload_path 只做了包含 {DATE} 的校验;inject 直接用 split("/") 拼接到 tmp 前缀下,未过滤 '..'
或绝对路径,因此存在目录穿越写入风险。

governance/drill/drill.py[45-97]
governance/drill/drill.py[154-181]

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

### Issue description
`cmd_inject()` constructs the destination path from `payload_path` without sanitization. A `payload_path` containing absolute paths or `..` can escape the clone directory and write arbitrary files on the machine running the command.

### Issue Context
`validate_samples()` currently checks only that `payload_path` is non-empty and contains `{DATE}`. It does not enforce that the path is relative and normalized.

### Fix Focus Areas
- governance/drill/drill.py[87-90]
- governance/drill/drill.py[169-181]

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


4. Record JSON type unchecked 🐞 Bug ☼ Reliability
Description
drill.py 的 record 对 --json 只做 json.loads 并假设其为 dict 且 ts 可比较字符串;传入 null/false/数组或非字符串 ts 会触发
AttributeError/TypeError/KeyError,导致非预期栈追踪而非一致的 fail-closed 错误信息。
Code

governance/drill/drill.py[R213-216]

+        rec = json.loads(a.json)
+    except Exception as e:
+        die(f"--json 解析失败: {e}")
+    if not rec.get("ts") or not rec.get("kind"):
Relevance

●●● Strong

Unchecked JSON container/field types cause deterministic crashes; explicit validation is a localized
reliability fix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
record 直接对 json.loads 的返回值调用 rec.get 且随后进行 prev['ts'] >= rec['ts'] 比较,未验证 rec/prev 为对象、ts
为同类型可比较值;此前本仓已接受过“顶层 scalar JSON 应拒绝”的同类缺陷修复建议。

governance/drill/drill.py[210-227]
PR-#61

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

### Issue description
`cmd_record()` assumes the parsed JSON is a dict with string fields. If `--json` is a top-level scalar (`null`, `false`) or a list, `.get` will crash; if `ts` types differ across lines, comparisons can raise `TypeError`. This breaks the intended consistent fail-closed behavior.

### Issue Context
There is an established repo pattern to harden JSON validation by explicitly asserting top-level objects (rejecting `None`/`False`/scalars) before continuing.

### Fix Focus Areas
- governance/drill/drill.py[210-227]

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



Informational

5. DRILL_TOKEN used for git push 📘 Rule violation ⛨ Security
Description
cmd_inject() performs authenticated git push by reading DRILL_TOKEN from the environment and
constructing an auth header, instead of obtaining a constrained, short-lived cloudbrid-agent
GitHub App token via scripts/ghcb or scripts/gh-app-token.sh. This can allow use of
PAT/long-lived or incorrectly-scoped credentials for agent GitHub operations.
Code

governance/drill/drill.py[R163-166]

+    tok = os.environ.get("DRILL_TOKEN")
+    if tok:  # CI: GOVERNANCE_TOKEN 经 git env-config 下发 extraheader,不落 remote URL/日志
+        hdr = "Authorization: Basic " + base64.b64encode(f"x-access-token:{tok}".encode()).decode()
+        auth = {"GIT_CONFIG_COUNT": "1",
Relevance

● Weak

Recent precedent rejected the same approved-token replacement request for direct environment-token
use.

PR-#173

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2778539 requires agent GitHub operations to authenticate via scripts/ghcb or
scripts/gh-app-token.sh with a constrained, short-lived cloudbrid-agent app token, and to avoid
direct use of arbitrary tokens. The new cmd_inject() code reads DRILL_TOKEN and builds an
Authorization header for git operations, which does not enforce the required token
provenance/scope/TTL.

Rule 2778539: Agent GitHub operations must use cloudbrid-agent app tokens with constrained scope
governance/drill/drill.py[160-168]

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

## Issue description
`governance/drill/drill.py` performs GitHub git operations using an arbitrary env var (`DRILL_TOKEN`) rather than retrieving a constrained, short-lived token from the approved mechanisms (`scripts/ghcb` or `scripts/gh-app-token.sh`) under the `cloudbrid-agent` GitHub App identity.

## Issue Context
Compliance requires agent GitHub operations to avoid PATs / long-lived credentials and to use app tokens with single-repo scope and <= 1h TTL.

## Fix Focus Areas
- governance/drill/drill.py[160-168]

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


6. GH_TOKEN used for API 📘 Rule violation ⛨ Security
Description
verify_gate.py calls the GitHub API using a generic GH_TOKEN from the environment, rather than
retrieving a constrained, short-lived cloudbrid-agent GitHub App token via scripts/ghcb or
scripts/gh-app-token.sh. This risks running agent verification with PAT/incorrectly-scoped
credentials.
Code

governance/drill/verify_gate.py[R92-95]

+    token = os.environ.get("GH_TOKEN", "")
+    if not a.checkruns_file and not token:
+        print("::error::缺 GH_TOKEN(API 模式必需;离线测试用 --checkruns-file)", file=sys.stderr)
+        raise SystemExit(2)
Relevance

● Weak

Recent precedent rejected the same request to replace direct GH_TOKEN usage with approved App-token
tooling.

PR-#173

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2778539 requires agent GitHub API operations to authenticate via approved app-token
tooling and forbids direct use of nonconstrained credentials. The new script reads GH_TOKEN and
uses it in the Authorization header for GitHub API calls.

Rule 2778539: Agent GitHub operations must use cloudbrid-agent app tokens with constrained scope
governance/drill/verify_gate.py[27-33]
governance/drill/verify_gate.py[92-95]

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

## Issue description
`governance/drill/verify_gate.py` depends on `GH_TOKEN` being set externally and uses it directly for GitHub API authentication, without enforcing that it is a constrained, short-lived `cloudbrid-agent` GitHub App token obtained via approved tooling.

## Issue Context
Compliance requires GitHub API operations performed by agent automation to use `cloudbrid-agent` app tokens with single-repo scope and <= 1 hour TTL, retrieved via `scripts/ghcb` or `scripts/gh-app-token.sh`.

## Fix Focus Areas
- governance/drill/verify_gate.py[27-33]
- governance/drill/verify_gate.py[92-96]

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


Grey Divider

Context sources
✅ Compliance rules (platform): 16 rules
✅ Web pages:
  +17 more
Review mode: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 9/18, lines 381/200; both must reach the floor). Router rationale: This is a behavior-changing drill engine spanning injection/push authentication, independent gate verification, append-only ledger aggregation, CI integration, and multiple test paths, creating several independent, easy-to-miss failure modes.
ⓘ  4 issues published inline · 6 in summary

Grey Divider

Tip of the day
💡 Did you know, you can tweak Display preferences with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +56 to +59
if hits and hits[0].get("status") == "completed":
c = hits[0]
v = "RED" if c.get("conclusion") == "failure" else "GREEN"
return {"verdict": v, "check_name": c.get("name"),

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. Non-success marked green 🐞 Bug ≡ Correctness

verify_gate.py 在目标 check-run status=completed 时,将除 failure 之外的所有 conclusion 都判为 GREEN,导致
cancelled/timed_out/neutral/skipped 等非 success 的情况被误判为“关卡通过”。这会把中间态/异常态当成“关卡死了”触发错误的 GREEN(退出码1)
与错误台账口径。
Agent Prompt
### Issue description
`verify_gate.py` currently maps any completed check run whose `conclusion != "failure"` to `GREEN`. GitHub check-run conclusions include multiple non-success terminal states (cancelled/timed_out/neutral/skipped/action_required/startup_failure/stale, etc.). Treating those as `GREEN` produces false “gate is green” alarms.

### Issue Context
The verifier is intended to assert “this defect should make gate X fail”. For completed runs:
- `failure` => `RED`
- `success` => `GREEN`
- other completed conclusions should be treated as an intermediate/indeterminate verdict (e.g., `TIMEOUT`/`MISSING-GATE`/new `INDETERMINATE`) with exit code 3, because they are not evidence that the gate is passing.

### Fix Focus Areas
- governance/drill/verify_gate.py[56-62]

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

Comment on lines +29 to +32
f"{API}/repos/{org}/{repo}/commits/{sha}/check-runs?per_page=100",
headers={"Authorization": f"Bearer {token}", "Accept": "application/vnd.github+json",
"User-Agent": "seed-drill-verify"})
with urllib.request.urlopen(req) as r:

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. No api timeout handling 🐞 Bug ☼ Reliability

verify_gate.py 调用 urllib.request.urlopen 拉取 check-runs 时未设置 timeout
且未捕获网络异常,可能在网络抖动/代理问题/连接卡死时无限阻塞或直接抛异常终止,导致验证步骤与上游 workflow 不可预测地挂起或无结构化输出。
Agent Prompt
### Issue description
`fetch_check_runs()` uses `urllib.request.urlopen(req)` without a timeout and without exception handling. In CI, a stuck TCP connection or slow proxy can block indefinitely (ignoring the intended `--timeout` polling window), or crash the process with an unhandled exception.

### Issue Context
The verifier already has a polling loop and a user-facing `--timeout` budget. Network calls should respect that budget by:
- passing a per-request timeout
- catching `URLError`/`HTTPError` and continuing the poll (or returning an indeterminate verdict) rather than crashing
- optionally backing off / limiting retries

### Fix Focus Areas
- governance/drill/verify_gate.py[27-34]
- governance/drill/verify_gate.py[45-80]

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

Comment thread governance/drill/drill.py
Comment on lines +173 to +174
path = os.path.join(tmp, *s["payload_path"].format(DATE=date).split("/"))
os.makedirs(os.path.dirname(path), exist_ok=True)

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

5. Inject path traversal risk 🐞 Bug ⛨ Security

drill.py 的 inject 用 samples.registry.yaml 的 payload_path 直接 split/join 到临时工作区路径,未禁止绝对路径或 '..'
片段,样本库一旦被误写/被恶意提交可导致写入临时目录外任意路径。即使最终 git add/commit 失败,也会在 runner 上发生越界写文件。
Agent Prompt
### Issue description
`cmd_inject()` constructs the destination path from `payload_path` without sanitization. A `payload_path` containing absolute paths or `..` can escape the clone directory and write arbitrary files on the machine running the command.

### Issue Context
`validate_samples()` currently checks only that `payload_path` is non-empty and contains `{DATE}`. It does not enforce that the path is relative and normalized.

### Fix Focus Areas
- governance/drill/drill.py[87-90]
- governance/drill/drill.py[169-181]

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

Comment thread governance/drill/drill.py
Comment on lines +213 to +216
rec = json.loads(a.json)
except Exception as e:
die(f"--json 解析失败: {e}")
if not rec.get("ts") or not rec.get("kind"):

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. Record json type unchecked 🐞 Bug ☼ Reliability

drill.py 的 record 对 --json 只做 json.loads 并假设其为 dict 且 ts 可比较字符串;传入 null/false/数组或非字符串 ts 会触发
AttributeError/TypeError/KeyError,导致非预期栈追踪而非一致的 fail-closed 错误信息。
Agent Prompt
### Issue description
`cmd_record()` assumes the parsed JSON is a dict with string fields. If `--json` is a top-level scalar (`null`, `false`) or a list, `.get` will crash; if `ts` types differ across lines, comparisons can raise `TypeError`. This breaks the intended consistent fail-closed behavior.

### Issue Context
There is an established repo pattern to harden JSON validation by explicitly asserting top-level objects (rejecting `None`/`False`/scalars) before continuing.

### Fix Focus Areas
- governance/drill/drill.py[210-227]

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

Base automatically changed from w4c4-seed-drill to main August 21, 2026 19:36
- drill.py 补全 inject(git 隔离分支注入,push 3 次重试)/record(append-only
  台账:时间戳单调+run 去重)/redrate(红率聚合+难度按周分布+零分母 null 口径)
- verify_gate.py 独立验证步骤(与样本库分离——ADR-0069 决策 2):RED=演习成功/
  GREEN=演习失败/NO-SURFACE/MISSING-GATE/TIMEOUT,静默期防假阴性
- tests: test-select(确定性/holdout 隔离/钉选)+ test-verify(红绿判定核)+
  governance/tests/test-drill.sh CI 聚合入口;gate.yml bash -n 登记
- 首演实录(PR body 详):注入 drill/seed-20260822 → push 面 NO-SURFACE(局限
  记录)→ draft PR#239 → org-hygiene 变红=演习成功 → 即关即删

Card: #223
@randypanding
randypanding force-pushed the w4c4-seed-drill-engine branch from 1b689da to 1320285 Compare August 21, 2026 19:38
@randypanding
randypanding merged commit bb6bbd7 into main Aug 21, 2026
11 checks passed
@randypanding
randypanding deleted the w4c4-seed-drill-engine branch August 21, 2026 19:40
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.

2 participants