Repository navigation
daemon+web: continuous stuck-badge detector + orphan badge arbitration - #13
ranxianglei wants to merge 1 commit into
Conversation
#12) - web: issues.ai_status_since column stamped on every ai_status change; GET /api/v1/ai-status?status=processing fleet-wide badge endpoint; admin badge monitor view (badge list + per-badge sweep verdicts) - daemon: 1-min badge sweep enumerates all processing badges via the tracker, re-checks web truth, then verifies three independent signals (pid kill -0, session output age, model token delta) for engine-known sessions; badges with no engine record go straight to arbitration on the comment stream (bot delivery -> completed, else idle + [system] reset notice with dedup marker); every processing write now carries an expected_heartbeat_at TTL that expires into 'stale' if not renewed - daemon: sweep state exposed via getBadgeSweepState() for the admin view - tests: unit matrix for signal evaluation/action decision + integration harness with mock web covering flip/keep/orphan-completed/orphan-reset/ foreign-owner-skip/timer-driven auto-flip Closes #12
[bot] 🏷 收到,正在审查本 PR(不重做):拉取分支核对代码、跑测试套件、确认 CI/合并状态,稍后给出结论。 |
[bot] 🏷 ## Review conclusion: ✅ ready to merge Reviewed PR #13 (head CI & mergeability
Diff cleanliness
Test verification (local runs)
Code review highlights
Non-blocking observations (no changes requested)
All four spec items from #12 are implemented as written (three-signal truth check, 1-min fleet-wide sweep + orphan arbitration, TTL semantics, observability), the conservative no-auto-requeue stance is honored, and every verification claim in the description reproduced locally. Nothing found that blocks. 中文摘要:已核对 PR #13 的 CI(head 全绿、mergeable clean)、diff 整洁度(16 文件全部在题内,无锁文件/CI 污染)并本地复跑测试(daemon 277/277 通过;web 的 9 个失败经纯 master 基线比对确认为既有问题,本 PR 零新增失败);代码审查未发现阻塞项,TTL 优先于三信号的语义与规格一致且有测试锁定——可以合并。 |
What
Closes #12
The weekly recurrence class ("badge says processing, process is long dead") is rooted in one gap: after a processing badge is written, nothing continuously verifies whether it is true. The 5-min observer only covers issues owned by this daemon and trusts the engine's own memory/DB state — orphan badges (records wiped, session gone, process evaporated) are invisible to it forever.
This adds the runtime truth-check layer on top of the existing observer (which stays untouched as the fast path):
kill -0) ② recent session output (last_output_at) ③ recent model traffic (output-token delta). All three false ⇒ stuck.GET /api/v1/ai-status?status=processing), re-checks web state, then verifies signals for engine-known sessions. Badges with no engine record go straight to arbitration on the comment stream: bot delivery found ⇒ flipcompleted; none ⇒ flipidle+[system]notice "状态已复位,如需继续请回复" (conservative: status flip only, no auto-requeue).expected_heartbeat_at; an un-renewed badge expires intostaleregardless of what the engine believes. Live sessions renew it during the sweep.alive/stale/orphan/skipped).Changes
issues.ai_status_sincecolumn (stamped on every ai_status change),/api/v1/ai-statusendpoint, badge monitor viewWORK_BADGE_SWEEP_MSdefault 60s, TTLs 10/10/15 min),sweepStuckBadges()+ exported pure helpers (evaluateBadgeSignals,decideBadgeAction,BADGE_RESET_MARKER), heartbeat renewal, sweep-state getterVerification
tsc --noEmitclean, 277/277 pass; webtsc --noEmitclean, 182 pass / 9 fail — the 9 failures are pre-existing on master (verified identical failure list via stash-baseline run)Notes:
bun.lockchurn frombun install: committed lock saysework-aio 0.5.80whilepackages/aio/package.jsonsays0.5.81— pre-existing drift, left out of this diff to keep it focused.[system]notice invites the user to reply, which re-triggers normal pickup.