feat: handoff lifecycle — list / resume / clear from the phone#21
Merged
Conversation
A failed step already writes a resume handoff, but there was no way to act on one without opening Jump Desktop. Now Jaiden can work them from Telegram: - `handoffs` — list pending handoffs (project, task, age) newest-first - `resume <n>` — seed a fresh agent with handoff #n IN THE RIGHT REPO and pick up where it stopped; archives the handoff to handoffs/resolved/ so the pending list always reflects reality (a fresh failure writes a new one). Accepts a project override: `resume 2 alphabot`. - `handoffs clear` — archive all pending at once. To route a resume, `_finalize_step_failure` now prepends a machine-readable `<!-- hermes-meta project=... -->` marker to the handoff; resume parses it (or falls back to inferring the project from the original task). Legacy handoffs with no marker still list and can resume with an explicit project. Also fixes the handoff MIRROR folder: it used project.capitalize(), which misroutes hyphenated projects (finance-tracker → Finance-tracker orphan) — now resolved via the registry's raphbrain_dir, matching agent_runner. Tests: +8 (152 pass / 1 skip) — meta parse incl. legacy, pending-vs-resolved separation + newest-first, resume dispatches-into-repo-then-archives, unresolvable-project no-dispatch, clear. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fresh review found two Mediums in the resume command:
- Out-of-range numeric index silently fell through to a substring match and
resumed the WRONG handoff: stems embed unix timestamps, so `resume 9` with 2
pending matched "9" inside a timestamp and dispatched + archived the wrong one.
A numeric selector is now STRICTLY an index — out of range fails cleanly.
- The `resume ` prefix hijacked any natural task starting with the word
("resume the deploy refactor") → intercepted as a command, never routed to the
planner, task silently dropped. The dispatch guard now only fires on bare
`resume` or `resume <digit…>`; anything else falls through to the planner.
Also: bare `resume` now shows the pending list instead of "matches ''", and
_list_handoffs / _resume_handoff guard per-file reads so one unreadable handoff
can't crash the whole command.
Tests: 154 pass / 1 skip (+2): out-of-range index doesn't substring-match a
timestamp stem; bare resume lists.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The "system to go through handoffs properly" you asked for. A failed step already writes a resume handoff (PR #15) — but acting on one meant opening Jump Desktop. Now it's phone-driven:
handoffs[project] task — age (id), newest firstresume <n>handoffs/resolved/handoffs clearresume 2 alphabotoverrides the project if routing guesses wrong.How resume knows the repo
_finalize_step_failurenow prepends a machine marker —<!-- hermes-meta project="…" -->— to the handoff.resumeparses it →resolve_project→ the repo tocdinto. No marker (legacy handoffs)? It infers the project from the original task, or you pass one explicitly. Archiving on resume keeps the pending list honest: if the resumed run fails again, a new handoff appears.Ride-along fix
The handoff mirror used
project.capitalize(), which misroutes hyphenated projects (finance-tracker→ aFinance-trackerorphan folder nothing reads). Now registry-resolved viaraphbrain_dir, matchingagent_runner. (Fable flagged the same class of bug in #20 — fixing #20's copy separately.)Tests
+8 (152 pass / 1 skip): meta parse incl. legacy-no-marker, pending-excludes-resolved + newest-first, resume dispatches-into-repo-then-archives, unresolvable-project → no dispatch + not archived, clear.
Independent of #20
Branched off
main, touches different functions than #20 (no stacking). Merge order doesn't matter.Deploy
Reload after merge. Held for your review — not merged.
🤖 Generated with Claude Code