Skip to content

feat(klaud): status labels, close-to-stop, run-end wakes and clearer outcomes / feat(klaud):状态标签、关闭即停止、运行结束即唤醒与更清晰的结果 - #4484

Merged
adibarra merged 2 commits into
mainfrom
feat/klaud-ignore-human-prs
Oct 25, 2026
Merged

adibarra merged 2 commits into
mainfrom
feat/klaud-ignore-human-prs

Conversation

@adibarra

Copy link
Copy Markdown
Collaborator

Makes Klaud Cold easier to read and steer from the PR list, and stops it waiting on things that no longer matter.

Changes

  • Other people's PRs no longer block a family. The overlap review only sees open Klaud PRs (klaud/auto-*). --stale-pr-days is gone.
  • Close a Klaud PR to stop it. klaud-abort is removed (no PR ever carried it). A Klaud PR closed unmerged by anyone other than Klaud-Cold finishes as closed-by-maintainer, and the planner leaves that family alone for 7 days.
  • Status labels. klaud-in-progress, klaud-parked, klaud-finished, klaud-infra, klaud-incompatible and klaud-error. They are set in code after each session and in every recovery pass, using the workflow token so label events start no Run Sweep runs. Klaud never reads them back, so they are display only. The candidate and recovery jobs get issues: write.
  • Clearer outcome names. readiness-blocked → missing-assets, uncertain → inconclusive, unexpected-error → no-outcome, capacity-deferred → no-capacity, aborted → closed-by-maintainer. Records already on PRs and in artifacts read back under the new names.
  • Infra cooldown. missing-assets and weights-missing now cool down 7 days instead of 24 hours. no-capacity still retries next wave.
  • Faster resumes. New klaud-wake.yml dispatches a recovery-only planner pass as soon as a Klaud-owned targeted run or PR sweep ends, instead of waiting for the :15/:45 wake (which ran 15 to 45 minutes late). Wakes share one concurrency group, so bursts collapse. The scheduled wakes stay as a backstop.
  • Planner waves every 4 hours instead of 3.
  • Fix. Finishing a maintainer-closed PR that was ready for review no longer runs gh pr ready --undo, which fails on closed PRs and left the session stuck.

Validation

After merge

  • Open Klaud PR bodies still mention klaud-abort; they will be edited to say "close this PR".
  • Watch the Klaud-Cold token's API budget. If run-end wakes push it, move the :15/:45 cron to hourly.
中文

让 Klaud Cold 更易于从 PR 列表查看和控制,并且不再等待已无关的事项。

  • 他人的 PR 不再阻塞配置族。 重叠审查只看打开的 Klaud PR(klaud/auto-*),移除 --stale-pr-days。
  • 关闭 Klaud PR 即停止。 移除 klaud-abort(从未有 PR 使用过)。由 Klaud-Cold 以外的人关闭且未合并的 Klaud PR 以 closed-by-maintainer 结束,规划器 7 天内不再处理该配置族。
  • 状态标签。 klaud-in-progress、klaud-parked、klaud-finished、klaud-infra、klaud-incompatible、klaud-error,由代码在每个会话结束后和每次恢复时设置,使用工作流令牌,因此不会触发 Run Sweep。Klaud 从不读取它们,仅用于展示。
  • 更清晰的结果名称。 readiness-blocked → missing-assets,uncertain → inconclusive,unexpected-error → no-outcome,capacity-deferred → no-capacity,aborted → closed-by-maintainer。已有记录按新名称读取。
  • 基础设施冷却。 missing-assets 与 weights-missing 冷却 7 天(原为 24 小时);no-capacity 仍在下一轮重试。
  • 更快恢复。 新增 klaud-wake.yml:Klaud 自有的定向运行或 PR sweep 结束时立即触发仅恢复的规划运行;定时唤醒保留为兜底。
  • 规划轮次改为每 4 小时。
  • 修复。 维护者关闭已就绪的 PR 后,收尾不再执行在已关闭 PR 上会失败的 gh pr ready --undo。
AI model disclosure

Prepared with Claude Code using anthropic/claude-opus-5-5, including a read-only review agent on the same model.

…tcomes

- Overlap review indexes only open Klaud PRs; other PRs never block a family.
- Remove klaud-abort. A Klaud PR closed unmerged by anyone else stops the
  candidate, and the planner leaves its family alone for 7 days.
- Display-only status labels (klaud-in-progress, klaud-parked, klaud-finished,
  klaud-infra, klaud-incompatible, klaud-error) set in code with the workflow
  token so label events start no sweep runs.
- Rename outcomes: readiness-blocked -> missing-assets, uncertain ->
  inconclusive, unexpected-error -> no-outcome, capacity-deferred ->
  no-capacity, aborted -> closed-by-maintainer. Older records read back under
  the new names.
- missing-assets and weights-missing cool down 7 days instead of 24 hours.
- Planner waves every 4 hours. klaud-wake.yml starts a recovery-only pass as
  soon as a Klaud-owned run ends.
- Finishing a maintainer-closed ready PR no longer tries to redraft it.
@github-actions

Copy link
Copy Markdown
Contributor

inferencex-e2e/infx diff: +468 -253

Generally most InferenceX submissions don't need to add changes to the infx package. On the off chance they do, generally the diff is small. Please prompt your agent to clean your PR to keep your diff to only necessary & minimal changes too. We have an increased code quality bar for changes to the infx package.

中文

大多数 InferenceX 提交通常不需要修改 infx 包。即使确实需要,改动通常也很小。请让你的 agent 清理 PR,使改动只包含必要且最小的内容。infx 包的改动有更高的代码质量要求。

@claude claude Bot left a comment

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.

Beyond the inline findings, I also checked the new workflow_run trigger in .github/workflows/klaud-wake.yml for injection. Event fields only gate the job's if:, and the shell gets nothing from the event. The dispatch is pinned to --ref main, the job is limited to same-repo head repositories, and its token is limited to actions: write, so that path looks safe.

Extended reasoning...

Inline findings are already posted. Separately, the new workflow_run workflow checks out no code and puts no event data into its shell step, so the dangerous-trigger pattern does not apply here.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Comment on lines +228 to 234
now = datetime.now(UTC)
cutoff = now - timedelta(hours=cooldown_hours)
window = min(cutoff, now - timedelta(days=INFRA_COOLDOWN_DAYS))
since = window - timedelta(hours=MAX_SESSIONS * WAVE_HOURS)
try:
runs = github_items(
repository,

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.

🔴 Infra cooldowns can silently drop: the planner may stop seeing 7-day-old missing-assets/weights-missing outcomes and retry those candidates early. The history read now covers about 9-10 days of klaud-plan runs, INFRA_COOLDOWN_DAYS plus MAX_SESSIONS * WAVE_HOURS. It uses a created=>= filter, and GitHub returns at most 1000 runs for that filter. Wake runs are filtered out only after listing (line 246), and the new run-end wakes add many more runs. Fix: list only the runs the cooldown needs, e.g. add event=schedule or a title filter. Otherwise detect the 1000-result cap and warn instead of under-counting.

Why this was flagged

The condition is the number of klaud-plan runs in the lookback, which this checkout cannot count. GitHub's list-workflow-runs endpoint returns at most 1,000 results when created is used. Line 235 lists actions/workflows/klaud-plan.yml/runs?created=>=<since.date()>, and since = now - 7 days - 48h (line 230-231), rounded down to a date, so roughly 9-10 days. Scheduled wakes alone are 48/day (~430-480 runs). Waves add 6/day. The new klaud-wake.yml dispatches one more klaud-plan run for every Klaud-owned e2e or Run Sweep completion, and runs cancelled by the concurrency group still count. The listing comes back newest first, so once the cap is hit the oldest runs are dropped. Their klaud-candidate-* artifacts never reach latest (lines 255-274), and infra holds near the 7-day edge vanish without any warning. On base the window was 24h+36h (~2.5 days, ~150 runs), far under the cap. The WAKE_TITLE check at line 246 runs only after the capped listing, so it does not help.

Verification: In __main__.py:228-231 the lookback becomes 7 days + 48 h, and line 235 rounds it down to a date, giving about 9-10 days. WAKE_TITLE runs are only filtered after listing (line 246), so they count toward GitHub's 1,000-result cap. Past the cap, line 238 catches ValueError and returns {}, set(), or the oldest runs silently drop out.

Comment on lines +9 to +11
# burst of endings collapses into a single recovery pass. Every other run is its own group.
concurrency:
group: ${{ inputs.wake && 'klaud-wake' || github.run_id }}

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.

🟡 (optional) Recovery passes can now overlap more often, which can push concurrent Klaud sessions past the MAX_CANDIDATES_PER_RUN cap of 5. Only dispatched wakes share the 'klaud-wake' concurrency group. Scheduled :15/:45 wakes and the 4-hourly planner each run in their own github.run_id group. Run-end wakes now arrive at any minute, so they can run recover alongside those passes. Fix: serialize every recovery pass, for example with job-level concurrency: {group: klaud-recover, cancel-in-progress: false} on the recover job. Schedule-only runs keep their own group today.

Why this was flagged

klaud-wake.yml:237 dispatches klaud-plan.yml -f wake=true whenever a Klaud-owned E2E run or klaud/auto-* Run Sweep completes, at any minute. klaud-plan.yml:11 puts only those runs in the 'klaud-wake' group. A scheduled 15,45 * * * * wake or the 0 */4 * * * planner uses github.run_id, so its recover job runs at the same time as a dispatched wake's recover. In lifecycle.py recover(), each pass counts running_sessions() (lifecycle.py:1285). Each then resumes approved sessions until running + resumed >= max_sessions (lifecycle.py:1293). Two overlapping passes both see, say, 4 running sessions, and each grants a different parked candidate, so 6 sessions run against a cap of 5. recovery_lease is per candidate (claims.py:126), so it does not stop this. On the base branch, recovery ran only at :00, :15 and :45, so passes rarely overlapped. The PR text says bursts collapse, but that holds only among dispatched wakes.

Verification: klaud-plan.yml:11 sets group: ${{ inputs.wake && 'klaud-wake' || github.run_id }}, so only dispatched wakes queue behind each other. In lifecycle.py recover(), each pass reads running = len(running_sessions(repository)) once (line 1285) and only counts its own resumed (line 1293). claims.recovery_lease (claims.py:126-155) is per candidate.

Comment on lines 443 to +445
if pull and (
pull["state"] != "closed"
or not pull["draft"]
or (not pull["draft"] and outcome.outcome != "closed-by-maintainer")

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.

🟣 pre-existing, not blocking: Pre-existing, partly fixed: a ready-for-review Klaud PR that a maintainer closes after a cleanup request exists still never finishes. Sessions already stuck this way on main stay stuck. The draft exemption at lifecycle.py:445 applies only to outcome closed-by-maintainer. reconcile reaches that outcome only when no pending cleanup exists (not pending, lifecycle.py:1105). Fix: in the failure branch of verify, ignore the draft flag whenever a closed PR was closed by someone other than Klaud, whatever the pending outcome (for example, call self.closed_by_maintainer(pull) instead of checking the outcome name).
A small fix can ride a push you are already making; otherwise a short reply is enough.

Why this was flagged

The trigger is a closed, non-draft Klaud PR that already has a klaud-cleanup: comment with an outcome other than closed-by-maintainer. On the base branch this state comes from the very bug the PR says it fixes. finish posted the pending unexpected-error request, and then gh pr ready --undo failed on the closed PR. It can also come from a maintainer marking a draft ready and closing it while a finish is in progress. After merge, reconcile reads that pending request as no-outcome and skips the closed_by_maintainer branch because of not pending (lifecycle.py:1105). finish skips the undo (lifecycle.py:544) and the close. Then verify raises "Failure/deferral PR cleanup is incomplete" at lifecycle.py:445, because the PR is not a draft and the outcome is not closed-by-maintainer. Every recovery pass, wakes included, repeats this and reports "needs inspection". The completion report is never posted and the ownership artifact is never deleted, which is the same as on base apart from the error text.

Verification: Pre-existing. Trigger: a closed, non-draft Klaud PR that already has a pending klaud-cleanup: comment whose outcome is not closed-by-maintainer. verify's failure branch raises VerificationError("Failure/deferral PR cleanup is incomplete"), because (not pull["draft"] and outcome.outcome != "closed-by-maintainer") (lifecycle.py:445) is true when the outcome is no-outcome. The session never finishes.

@adibarra
adibarra merged commit f21225e into main Oct 25, 2026
20 checks passed
@adibarra
adibarra deleted the feat/klaud-ignore-human-prs branch October 25, 2026 06:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant