Skip to content

fix: complete Klaud final sweep lifecycle / 完善 Klaud 最终扫描流程 - #2843

Open
adibarra wants to merge 1 commit into
mainfrom
fix/klaud-final-sweep-lifecycle
Open

fix: complete Klaud final sweep lifecycle / 完善 Klaud 最终扫描流程#2843
adibarra wants to merge 1 commit into
mainfrom
fix/klaud-final-sweep-lifecycle

Conversation

@adibarra

@adibarra adibarra commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Summary / 摘要

Klaud previously kept its candidate PR draft while applying full-sweep-enabled, so run-sweep.yml skipped the final reusable sweep. It also had no mechanical guard against stopping during that final validation.

此前 Klaud 会在候选 PR 仍为草稿时添加 full-sweep-enabled,导致 run-sweep.yml 跳过最终可复用扫描;同时也缺少阻止 agent 在最终验证期间提前结束的机制。

This change makes Klaud mark the PR ready before applying the final-sweep label. If the sweep fails, Klaud removes the label and returns the PR to draft before pushing repairs, preventing each intermediate push from launching another full sweep. The Stop hook now tracks the candidate's open PR, exact head SHA, final run-sweep.yml state and reusable artifacts.

本次修改要求 Klaud 在添加最终扫描标签前将 PR 标记为 ready。扫描失败时,Klaud 会先移除标签并将 PR 改回草稿,再推送修复,避免每次中间推送触发新的完整扫描。Stop hook 现在会检查候选的开放 PR、精确 head SHA、最终 run-sweep.yml 状态和可复用产物。

Validation / 验证

  • actionlint .github/workflows/klaude-plan.yml .github/workflows/klaude-candidate.yml

  • Python compile check with uv

  • Manual Stop-hook scenarios covering targeted work, draft/missing/running/failed final sweeps, missing artifacts and successful reusable completion

  • zizmor 1.30.0 standard and auditor modes: no new findings; existing baseline is 4 low, 3 medium and 0 high findings in auditor mode

  • 使用 uv 完成 Python 编译检查

  • 手动覆盖定向运行、草稿/缺失/运行中/失败的最终扫描、缺少产物及成功复用等 Stop-hook 场景

  • zizmor 1.30.0 标准与 auditor 模式均无新增问题;auditor 模式的既有基线为 4 个 low、3 个 medium、0 个 high


Note

Medium Risk
Changes Klaud Cold agent prompts and the Stop hook that gates session completion; misbehavior could skip final sweeps or leave agents running longer, but scope is limited to auto-sweep CI orchestration rather than production serving or auth.

Overview
Fixes a gap where Klaud could label a draft PR with full-sweep-enabled, so run-sweep.yml never ran the final reusable validation, and the agent could stop before that work finished.

The candidate workflow now sets KLAUDE_BRANCH, keeps the PR draft through targeted e2e attempts, and after a passing targeted run instructs Klaud to push the changelog, run gh pr ready (without requesting review), then apply full-sweep-enabled. If the final sweep fails, it must remove the label and return the PR to draft before any repair push so intermediate commits do not re-trigger full sweeps.

check-stop still blocks on in-flight owned e2e runs, and when full-sweep-enabled is present it also blocks until the open PR is non-draft, a successful run-sweep.yml exists for the exact head SHA, and that run has non-expired reusable benchmark/eval artifacts. Docs (docs/klaude*.md, KLAUD_DEBUG.md §7.3) describe the same lifecycle.

Reviewed by Cursor Bugbot for commit 3d93801. Bugbot is set up for automated code reviews on this repo. Configure here.

定向验证后将候选 PR 标记为 ready,并在修复前移除最终扫描标签、恢复草稿状态;Stop hook 同时验证精确 head 的可复用最终扫描。
Comment thread utils/klaude/__main__.py
Comment on lines +179 to +192
query = urlencode({'event': 'pull_request', 'branch': branch, 'per_page': 100,
'created': '>=' + os.environ['KLAUDE_STARTED_AT']})
sweep_pages = github_read(repository, 'actions/workflows/run-sweep.yml/runs?' + query, paginate=True)
sweep_runs = [run for page in sweep_pages for run in page['workflow_runs']]
if any(page['total_count'] > len(sweep_runs) for page in sweep_pages):
raise ValueError('Incomplete sweep run listing')
exact_runs = [run for run in sweep_runs if run['head_sha'] == pull['head']['sha']]
if not exact_runs:
return {'decision': 'block', 'reason': 'No final run-sweep.yml run exists for the exact PR head. Keep full-sweep-enabled applied and wait for the labeled run to appear.'}
sweep = max(exact_runs, key=lambda run: run['created_at'])
if sweep['status'] != 'completed' or not sweep.get('conclusion'):
return {'decision': 'block', 'reason': 'The final run-sweep.yml run is still queued or running. Continue monitoring every job and do not stop before it is terminal.'}
if sweep['conclusion'] != 'success':
return {'decision': 'block', 'reason': 'The final run-sweep.yml run did not succeed. Remove full-sweep-enabled and return the PR to draft before any repair push, then diagnose, repair within budget and repeat final validation.'}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 check_stop() treats the highest-created_at run-sweep.yml run for the PR's exact head SHA as authoritative, but run-sweep.yml also re-triggers (via 'labeled'/'unlabeled' pull_request events, including labels unrelated to full-sweep e.g. skip_queue, ci-patchwork, ci-checklist-complete) without a new commit. A run whose check-changelog job is skipped (label not in its if-list) concludes 'skipped', which never equals 'success'; if such a run lands after the real successful full sweep, max() picks it and check_stop wrongly reports the final sweep failed, telling the agent to strip full-sweep-enabled and revert to draft/repair despite a real prior success. …

Extended reasoning...

…Fix: only compare against runs whose jobs actually executed the check-changelog logic (e.g. filter out event actions that the workflow's own if-condition would skip, or key off run['status']/job outcomes rather than the newest timestamp) so an unrelated label churn can't mask a completed successful full sweep.

PR has full-sweep-enabled, is ready, and its real run-sweep.yml run for head H succeeds with reusable artifacts. Afterward some other tracked label (e.g. ci-checklist-complete) is added or removed on the same PR with no new commit; run-sweep.yml's pull_request 'labeled'/'unlabeled' trigger fires again for head H, but check-changelog's job-level if evaluates false for that label, so the job (and everything depending on it) is skipped and the run concludes 'skipped'. check_stop() at main.py:185-192 selects exact_runs by head_sha==H, takes max by created_at (the new skipped run), sees status=='completed' but conclusion=='skipped' != 'success', and returns the block reason at line 192 instructing the agent to remove full-sweep-enabled and return to draft before repairing —…

Verification: normal — new code added by this PR (utils/klaude/main.py:179-192) selects the "final" sweep with sweep = max(exact_runs, key=created_at) and blocks stop whenever that run's conclusion != 'success', with no filter on conclusion or job outcome. run-sweep.yml triggers on pull_request labeled/unlabeled (lines 33-42) gated by paths: perf-changelog.yaml — which a full-sweep PR always modifies…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant