fix(daemon): rotation-manager recovery restarts write no marker, misclassify as crash - #187
wyre-agent-fleet[bot] wants to merge 2 commits into
Conversation
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Code Review Summary — Hermes AgentVerdict: Approve ✅ Looks Good
💡 Suggestions
Approve. |
asachs01
left a comment
There was a problem hiding this comment.
Review: rotation-manager recovery marker fix
Verdict: approve
Correctness vs. title: Title claims recovery restarts wrote no marker and got misclassified as crashes. Confirmed in the diff: both restart loops in doRotation() (candidate-rotation-success at line ~328 and active-account-recovered-in-place at ~367) previously called restartAgent() directly with no marker write. The fix adds writeRestartMarker()/restartBlockedAgents() which writes a .rotation-recovered marker to state/<agent>/ before calling restartAgent() — correct ordering since the restart call is what kills the old PTY and triggers the SessionEnd hook that reads the marker. hook-crash-alert.ts gets a matching classification entry + QUIET_SUPPRESSED_TYPES addition + a distinct (non-misleading) Telegram message, and heartbeat.ts's END_TYPE_MARKERS is updated so the marker clears on the next heartbeat rather than relying on a 5-min TTL. This directly closes the described bug on both affected code paths, not just one.
Code quality: Good — extracted the two near-duplicate restart loops into a single restartBlockedAgents() helper, reducing duplication rather than copy-pasting the marker write twice. The marker write is correctly best-effort (try/catch, never blocks the restart), matching the existing .user-restart pattern in src/cli/bus.ts.
Security: No secrets, no injection surface — this is local filesystem marker writes with a hardcoded reason string. Nothing to flag.
Tests: New/updated tests cover the actual behavior that matters: marker exists before restartAgent is called (not just eventually), classifyFromMarkers recognizes the new marker type, and clearEndMarkers retires it. Full suite reported clean aside from one pre-existing chokidar flake noted as unrelated/known.
Docs: CHANGELOG entry accurately describes the bug and fix, matches the diff.
No issues found.
Code Review Summary — Claude Code (update)Verdict: Approve CriticalNone WarningsNone SuggestionsNone Looks Good
|
asachs01
left a comment
There was a problem hiding this comment.
Reviewed by Hermes Agent. Both rotation-manager restart loops now write a .rotation-recovered marker before restartAgent(), and hook-crash-alert.ts/heartbeat.ts gain matching classification + quiet-suppression + end-marker entries, correctly preventing routine OAuth-rotation recoveries from misclassifying as crashes. Fixes both restart branches (not just the one named in the task), includes marker-write-order assertions and classification tests.
Code Review Summary (Reviewed by Hermes Agent)Looks Good. Fixes a real false-positive: rotation-manager's recovery restarts wrote no marker file, so Suggestions:
Solid test coverage across marker classification, heartbeat clearing, and both rotation-manager code paths (asserting marker exists synchronously before restartAgent fires). No security or correctness concerns. LGTM. |
Review: #187 — rotation-manager recovery marker fixRoot cause analysis: Correct and well-evidenced. Both restart loops in CriticalNone. Warnings
Suggestions
Looks Good
Verdict: Correct, well-tested fix for a real false-positive alerting bug. Approve. Reviewed by Claude Code |
Review — headRefOid
|
ReviewSummary: Fixes a real false-positive-crash-alert bug: Correctness
Tests
Quality/Docs
Verdict: Approve. Solid root-cause fix with good test coverage for both affected code paths and a reasonable refactor to prevent recurrence. Reviewed SHA: f161e76 |
Claude Code ReviewVerdict: Approve CriticalNone. WarningsNone. Suggestions
Looks Good
|
…lassify as crash Both restart loops in rotation-manager.ts's doRotation() (candidate rotation and active-account-in-place recovery) now write a .rotation-recovered marker before restartAgent(), mirroring soft-restart-all's .user-restart pattern. hook-crash-alert.ts gains a matching marker entry, quiet-hours suppression, and a distinct Telegram message; heartbeat.ts's END_TYPE_MARKERS gains it too so it clears on the next heartbeat instead of relying on the 5min TTL. Fixes the roughly-nightly false 10-13-agent CRASH-alert burst every time the shared OAuth seat cycles through exhaustion-then-recovery. task_1789351994840_86202746
…n-manager /simplify review of the prior commit found the two restart loops in doRotation() had become near-identical (rotate-to-new-candidate vs. active-account-recovered-in-place), and this diff's own writeRestartMarker() call made the duplication worse rather than better. Extracted into a single restartBlockedAgents(state, markerReason) helper. Also folds two new marker-assertion tests into their existing sibling tests (same setup, one extra assertion) rather than duplicating the arrange/act — matches this file's own one-scenario-per-test convention. task_1789351994840_86202746
f161e76 to
4761d1f
Compare
Code Review Summary (Reviewed by Hermes Agent, updated for new commits — sha 4761d1f)Fix verified: Critical: None. Warnings: None. Marker write is wrapped in try/catch and explicitly documented as best-effort — a failed write only costs classification accuracy, never blocks the restart. Code quality: Good — the two previously-duplicated restart loops in Testing: Suggestions: None blocking. Looks good. Approving from a review-comment perspective (no formal PR review submitted per bot policy). Reviewed by Hermes Agent |
asachs01
left a comment
There was a problem hiding this comment.
Review update (Hermes sweep, delta since f161e76d): approved - extraction verified line by line
Delta on this PR's own branch since last review: 4761d1fb "refactor(daemon): extract shared restartBlockedAgents loop in rotation-manager". Verified the patch:
- The new
restartBlockedAgents(state, markerReason)private method is a faithful extraction of the two previously-duplicated loops indoRotation()(rotation-onto-new-account branch and active-account-recovery branch): same marker-write -> restart ->delete state.limitBlocked[agent]-> catch-and-log-stays-blocked sequence, same log message text, marker reason passed as parameter preserving each branch's distinct message. - Both call sites reload
statevialoadState()before calling (as before), and both still setlastRotationAt/retryAt/alertedHaltafter. No ordering change. - Tests updated in the same commit; CI green on head (Build/Type Check, Unit Tests, rotation-manager suite included).
Pure dedupe, no behavior change. See the same note I left on #188 about the shared base commits between these two branches - whichever merges second rebases.
asachs01
left a comment
There was a problem hiding this comment.
Hermes Agent Review
Verdict: Approve
Correctly fixes the rotation-recovery false-crash misclassification: both restart loops in doRotation() now write a .rotation-recovered marker before calling restartAgent(), mirroring the existing .user-restart convention, and hook-crash-alert.ts/heartbeat.ts are updated in lockstep (QUIET_SUPPRESSED_TYPES, END_TYPE_MARKERS) so the marker both suppresses the false alert and clears promptly. Good refactor pulling the duplicated restart loop into restartBlockedAgents() while fixing the bug in one place. Marker write is correctly best-effort (try/catch, never blocks the restart). Tests included for both the hook classification and rotation-manager.
Reviewed by Hermes Agent
Summary
rotation-manager.ts'sdoRotation()restarts every previously limit-blocked agent when a bench candidate (or the active account itself) recovers — a routine, expected daemon action — but neither restart loop wrote any of the marker fileshook-crash-alert.ts'sclassifyFromMarkers()checks for. Restarted agents fell through totype=crash reason=noneincrashes.log, or worse got swept intotype=rate-limitedby the stdout-tail substring scan. This pages a false 10-13-agent CRASH-alert burst roughly nightly, whenever the shared OAuth seat cycles through exhaustion-then-recovery..rotation-recoveredmarker (via a sharedrestartBlockedAgents()/writeRestartMarker()helper) before callingrestartAgent(), mirroringsoft-restart-all's existing.user-restartwrite insrc/cli/bus.ts.hook-crash-alert.tsgains the matching marker-classification entry, aQUIET_SUPPRESSED_TYPESaddition (routine event, same treatment asuser-restart/rate-limited), and a distinct Telegram message rather than reusing.user-restart's misleading "restarted by user" text.src/bus/heartbeat.ts'sEND_TYPE_MARKERSalso gains the new marker so it clears on the next post-restart heartbeat instead of relying solely on the hook's 5-minute staleness TTL.doRotation()(candidate-rotation-success and active-account-recovered-in-place), not just the one named in the task description — same bug, same file, same root cause; leaving one unfixed would have left the exact same false-crash-alert shape live on the other path.Review notes
Ran this through
/simplify(4 parallel review agents: reuse, simplification, efficiency, altitude) before opening:restartBlockedAgents()helper; folded two new marker-assertion tests into their existing sibling tests instead of duplicating arrange/act.state/<agent>/" implementation across the codebase (src/cli/stop.ts,src/daemon/agent-manager.ts,src/daemon/index.ts, and nowrotation-manager.ts) — 3 pre-date this PR, consolidation is a separate refactor.soft-restart-allinsrc/cli/bus.tsas a stale/wrong pointer, both having run a plain grep that returned zero hits. Verified viagit grep(and a byte-level NUL check) that this is this repo's own documented Bash-tool-grep NUL-byte false-zero bug (bus.tscarries an intentional NUL sentinel — seeCLAUDE.md's 2026-08-26 entry) — the code and the comment are both correct. Left unchanged.Test plan
npx tsc --noEmit— cleannpm run build— cleannpx vitest run— full suite: 2678 passed, 4 skipped, 1 failure (a real-chokidar filesystem-watcher test unrelated to this change, confirmed to pass in isolation — a known class of environment flake under full-suite load on this box, not a regression)restartAgent()is called,classifyFromMarkers()recognizing.rotation-recovered, andclearEndMarkers()retiring ittask_1789351994840_86202746
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.