Skip to content

tooling: hapi-resurrect-session + auto-migrate chain-forward#36

Open
heavygee wants to merge 2 commits into
mainfrom
tooling/legacy-chat-acp-alignment
Open

tooling: hapi-resurrect-session + auto-migrate chain-forward#36
heavygee wants to merge 2 commits into
mainfrom
tooling/legacy-chat-acp-alignment

Conversation

@heavygee
Copy link
Copy Markdown
Owner

@heavygee heavygee commented Jun 6, 2026

Summary

Force-pushed 2026-06-08 to drop the obsolete post-attach hint commit. PR #34 evolved between 2026-06-06 and 2026-06-08 from "operator-driven CLI/button migrator" to "invisible auto-migrate inside SyncEngine.resumeSession". The post-attach hint's reference to hapi cursor migrate <id> was already wrong by the time #34 settled, so dropping it is the honest fix.

Two-commit branch now:

  • tooling(resurrect) (2ed1bb30) — commits scripts/tooling/hapi-resurrect-session.sh (361 lines, operator-authored 2026-06-06). Recovers HAPI sessions auto-archived on agent crash with cursorSessionId=NULL (the failure mode where POST /api/sessions/:id/resume returns 500 "Resume session ID unavailable").
  • tooling(resurrect) chain-forward (228757ce) — adds 18 lines of stdout polish at the end of the script that points the operator at Reopen → auto-migrate (feat(cursor): operator-driven legacy stream-json -> ACP session migrator (transplant) #34) as the next step. Pure UX polish; no code-path change.

Why this matters relative to #34

#34's maybeAutoMigrateLegacyCursorSession fires inside SyncEngine.resumeSession. But resumeSession never reaches the auto-migrate call if it errors out at the cursorSessionId lookup first. A crashed-archived session with cursorSessionId=NULL is permanently stranded as un-resumable — auto-migrate has nothing to migrate because there's no resume token to look up the legacy store with.

hapi-resurrect-session.sh fixes the null first (path-hash discovery against ~/.cursor/chats/<md5(metadata.path)>/, sqlite metadata patch, hub restart, POST /resume). After this script succeeds, the session is back to lifecycleState=inactive with cursorSessionProtocol=stream-json. The operator clicks Reopen → #34's auto-migrate fires transparently → session ends up on ACP.

The chain is: resurrect → Reopen → auto-migrate. The polish commit's stdout lines literally walk the operator through that chain so they don't have to remember step 3 exists.

End-to-end flow (crash branch, after #34 lands)

# 1. Operator notices a session is stuck "archived, Session crashed"
# 2. Run the script
scripts/tooling/hapi-resurrect-session.sh <hapi-session-id>
# 3. Script prints "next: open this session in the HAPI web UI and click Reopen"
#    plus the kill-switch env var, plus the in-flight banner expectation
# 4. Operator clicks Reopen in web UI
# 5. SyncEngine.resumeSession -> maybeAutoMigrateLegacyCursorSession -> transparent ACP transplant
# 6. Done. Session is now on ACP.

Refusal contract for hapi-resurrect-session.sh

Exit Meaning
0 Session resurrected (or dry-run plan printed)
1 Discovery failed (no matching cursor chat on disk)
2 sqlite patch failed
3 /api/sessions/<id>/resume returned non-2xx
4 Spawned cursor-agent crashed within 8s of spawn

Idempotent: re-running on an already-resurrected session is safe. --dry-run prints the discovery + planned patch without writing.

Why this stays as a script (not a hub endpoint)

  • This is fork-only operator tooling for the current failure mode. Upstream has zero issues filed about session resurrection / cursorSessionId=NULL recovery (verified via gh search issues --repo tiann/hapi).
  • The CONCEPT belongs upstream — either as a POST /api/sessions/:id/resurrect endpoint, or better, as prevention: capture cursorSessionId to a session-events log BEFORE archive so the token is never lost in the first place. Either of those is a separate upstream issue/PR.
  • Script form ships today, no review cycle, operator-iterable. Right MVP shape.

Test plan

Companion branch

tooling/backfill-truncation-warnings (#37) — raises backfill MAX_MESSAGES cap from 2000 to 50_000 and surfaces a BACKFILL TRUNCATED warning when transcripts exceed the cap. Independent of migration concerns; should land on its own merits.

Operator hand-built this 361-line script on 2026-06-06 to recover a
HAPI session whose `cursorSessionId` was set to NULL when the hub
auto-archived on agent crash. The procedure (path-hash discovery
against ~/.cursor/chats/<md5(path)>/, sqlite metadata patch,
hapi-restart-hub, POST /api/sessions/<id>/resume) was used twice
in the same session and works; committing before it gets clobbered
by a `git clean` the way the attach scripts did last week.

Why this matters for the ACP migration (PR #34 / upstream tiann#824):
the migrator refuses sessions in `lifecycleState='running'` and is
not designed to recover crashed sessions with `cursorSessionId=NULL`.
A crashed legacy session has to be resurrected (back to
`lifecycleState='inactive'` with `cursorSessionProtocol='stream-json'`)
BEFORE `hapi cursor migrate <id>` becomes applicable. This script
closes that gap.

Surface:
  hapi-resurrect-session <hapi-session-id-or-prefix>
      [--cursor-uuid <uuid>]                 # skip auto-discovery
      [--symlink-old-path <old> --as <new>]  # moved worktrees
      [--name '<friendly label>']
      [--dry-run] [--no-restart-hub]

Exit codes: 0 ok, 1 discovery failed, 2 sqlite patch failed,
3 resume call failed, 4 cursor-agent crashed within 8s of spawn.

No new dependencies (Python 3 stdlib + sqlite3 + curl).

Co-authored-by: Cursor <cursoragent@cursor.com>
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

After successful resurrection the session is back to inactive +
cursorSessionProtocol=stream-json by design (legacy launcher knows
how to dispatch the stream-json store). Once #34's invisible auto-
migrate ships, SyncEngine.resumeSession will transplant this to ACP
transparently on the operator's next Reopen — no manual migrate
command required.

This adds three terminal-stdout lines at the end of the script so the
operator sees the next step without having to remember #34 exists:

  [hapi-resurrect-session] session is on legacy stream-json.
  [hapi-resurrect-session]   next: open this session in the HAPI web UI and click Reopen.
  [hapi-resurrect-session]   once #34 (tiann#824) auto-migrate is live, that
  [hapi-resurrect-session]   Reopen will transparently transplant this session to ACP
  [hapi-resurrect-session]   (cp store.db, verify session/load, flip protocol; ~15-20s banner).
  [hapi-resurrect-session]   kill-switch: HAPI_CURSOR_LEGACY_AUTO_MIGRATE=0 in hub env.

If LIVE_PROTO is already 'acp' (e.g. operator re-ran resurrect on a
session that auto-migrate had already handled), the script just prints
"session is already on ACP. No further action needed." instead.

Pure UX polish. No code-path change. Replaces the earlier
attach-agent-chat.ts post-attach hint that I dropped — that approach
was obsoleted by #34 moving migration from operator-driven CLI to
invisible auto-fire inside resumeSession.

Co-authored-by: Cursor <cursoragent@cursor.com>
@heavygee heavygee force-pushed the tooling/legacy-chat-acp-alignment branch from c143c0e to 228757c Compare June 8, 2026 14:31
@heavygee heavygee changed the title tooling: shepherd legacy chats toward ACP migration (closes the loop for #34) tooling: hapi-resurrect-session + auto-migrate chain-forward Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant