Skip to content

tooling: shepherd legacy chats toward ACP migration (closes the loop for #34)#36

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

tooling: shepherd legacy chats toward ACP migration (closes the loop for #34)#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

Two-commit branch that aligns the find-attach-backfill pipeline with #34's ACP migrator so the end-to-end flow has no gaps.

  • tooling(resurrect): 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. Was untracked in the working tree - same disappearing-act risk as attach-agent-chat.sh had until last week.
  • tooling(attach): adds a 28-line post-attach check that reads metadata.cursorSessionProtocol from sqlite and prints a hapi cursor migrate <id> hint when the session lands as stream-json. Non-fatal, captures the protocol in the JSON report for wrapper scripts.

Why this matters for #34

#34's migrator refuses sessions in lifecycleState='running' and has no recovery path for cursorSessionId=NULL. A crashed legacy session has to be resurrected back to lifecycleState='inactive' BEFORE hapi cursor migrate <id> is applicable. The resurrect script closes that prerequisite gap.

The post-attach hint closes the operator-UX gap on the other end: every freshly-attached cursor session is on legacy stream-json by definition (it came from ~/.cursor/chats/<wsh>/<uuid>/, the pre-ACP directory layout). Without an explicit nudge, operators leave sessions on the legacy path indefinitely and burn 5-30K preamble tokens on every resume.

End-to-end flow (after #34 lands)

# 1. Find a legacy chat (any of: rg the transcripts, resumeagent --find, chat index)
# 2. Reattach it (synthesizes the HAPI metadata row, backfills the transcript)
scripts/attach-agent-chat.sh <chat-uuid> --project ~/coding/foo
#    -> prints: "note: session <id> is on legacy stream-json. ... hapi cursor migrate <id>"
# 3. Migrate it to ACP
hapi cursor migrate <hapi-session-id>

# Crash recovery branch:
# 1. Find an archived session with cursorSessionId=NULL
# 2. Resurrect (path-hash discovery, sqlite patch, hub restart, /resume)
scripts/tooling/hapi-resurrect-session.sh <hapi-session-id>
# 3. Migrate the now-resurrected session to ACP
hapi cursor migrate <new-hapi-session-id>

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.

Test plan

  • bash -n scripts/tooling/hapi-resurrect-session.sh syntax-checks
  • --help prints the documented usage block
  • attach-agent-chat.ts parses cleanly post-edit
  • Existing scripts/backfill-agent-transcript.test.ts still 8/8 pass
  • Cross-reviewer: trace attach-agent-chat.ts post-attach hint code path against the spawn return path - confirm sessionToName is the post-spawn HAPI session id, not the legacy chat UUID
  • After hapi cursor migrate ships on main, re-run the attach script and confirm the hint fires (it should, since attach lands as stream-json before migration)
  • Smoke hapi-resurrect-session.sh --dry-run <known-archived-session> against a real archived row to confirm path-hash discovery still picks the right candidate

What this does NOT do

Companion branch

tooling/backfill-truncation-warnings (commit aa223ee, pushed) — raises the backfill MAX_MESSAGES cap from 2000 to 50_000 and surfaces a BACKFILL TRUNCATED warning when transcripts exceed the cap. Should land first so attaches of large chats (jessica-story-class, 3k+ turns) aren't silently lossy before being migrated.

Made with Cursor

heavygee and others added 2 commits June 6, 2026 23:09
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>
…tach

Every legacy chat that lands via attach-agent-chat.sh comes in as
`cursorSessionProtocol = 'stream-json'` by definition (it came from
~/.cursor/chats/<wsh>/<uuid>/, the pre-ACP directory layout).
Without explicit shepherding, the operator burns 5-30K preamble
tokens on every subsequent resume.

This adds a non-fatal post-attach check that:
  1. Reads the session's metadata.cursorSessionProtocol from sqlite
     (after the hub spawn returns, so the row exists).
  2. If it's anything other than 'acp', prints a one-block hint
     pointing at #34's `hapi cursor migrate <id>` command.
  3. Captures the resulting protocol in the JSON report
     (`cursorSessionProtocol`) so wrapper scripts can act on it
     programmatically.

The check is wrapped in try/catch and only fires for `agent=cursor`
attaches in non-dry-run mode. A protocol-check error never blocks
the attach (already-succeeded) and is surfaced as
`cursorProtocolCheckError` in the report instead.

Doesn't ship the `hapi cursor migrate` binary itself — that's #34's
lane. This just connects the find-attach-backfill pipeline to the
migration follow-up so the operator doesn't have to remember the
two-stage flow.

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.

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