Skip to content

fix(start): reclaim a PTY's own name on orphan recovery, and rename the pane on rebind - #139

Open
gzur wants to merge 2 commits into
aannoo:mainfrom
gzur:fix/orphan-name-reclaim
Open

gzur wants to merge 2 commits into
aannoo:mainfrom
gzur:fix/orphan-name-reclaim

Conversation

@gzur

@gzur gzur commented Sep 14, 2026

Copy link
Copy Markdown

Two fixes to identity handling in hcom start, found while tracing a live incident where one agent ended up split across two names.

1. Orphan recovery mints instead of reclaiming

start_from_orphan refuses to reuse the name a PTY last carried whenever an instance row still holds that name:

let can_reuse = !preferred_name.is_empty()
    && identity::is_valid_base_name(&preferred_name)
    && db.get_instance_full(&preferred_name)?.is_none();

An orphan has usually lost its process binding but kept its row, so this is the ordinary case rather than the exception, and recovery takes the mint branch.

What that looks like in practice, from my own events table:

38806 | 2026-09-12T10:25:11 | life | voni | {"action":"started","by":"cli",
                                            "reason":"orphan_recover","orphan_pid":81661}

The row it should have reclaimed was alive the whole time and is still there:

riko | 15a2aff1-7f0b-437c-81e1-a66200c41907 | active | ~/vast/polaris-decomm | claude

One running agent, two identities. riko kept receiving replies while the recovered session answered as voni. The command even reports the cause — "Recovered orphan PID {} as new identity '{}' (name conflict/unavailable)." — but it reads as a name collision rather than the PTY failing to recognise its own row.

An existing row is now a conflict only when it belongs to a different PTY. Two pieces of evidence allow the reclaim:

  • the live process binding still names the row
  • the row still carries the orphan's session_id

The session_id path is the one that fires in practice, precisely because the process binding is what an orphan has already lost.

The decision moves into orphan_can_reuse_name so it can be tested against a database rather than only through a real pidfile.

2. start --as and start --orphan leave the pane name stale

Every hook bind path calls runtime_env::set_terminal_title, so a pane tracks the identity its session resolves to. The two CLI paths that change an identity did not. After a rebind or a recovery the pane kept advertising the name that was just replaced.

Both paths now rename the pane. Under cfg(test) the title is recorded rather than written, so a test can assert that a path renames its pane and the suite cannot repaint the terminal it runs in.

Tests

9 new tests: 5 unit tests over the reuse decision, 2 end-to-end tests driving start_from_orphan from a real pidfile, and 2 asserting the pane rename on both paths. Each was written first and observed failing before the fix.

cargo test 2233 passed, cargo test --test cli_smoke 27 passed, cargo clippy --all-targets -- -D warnings clean, cargo fmt --check clean.

Note

A third change came out of the same investigation — a warning when --name disagrees with the identity the shell resolves to. It changes output rather than correcting a defect, so I am raising it as an issue instead of folding it in here.

Orphan recovery refused to reuse the name a PTY last carried whenever an
instance row still held that name. An orphan has usually lost its process
binding but kept its row, so the common case took the mint branch: one
running agent ended up split across two identities. The old name kept
receiving replies while the recovered session answered under the new one,
and the operator saw the split only as a pane whose name no longer matched
the agent it was talking to.

An existing row is now a conflict only when it belongs to a different PTY.
Two pieces of evidence allow the reclaim: the live process binding still
names the row, or the row still carries the orphan's session_id. The
session_id path is the one that fires in practice, precisely because the
process binding is what an orphan has already lost.

The reuse decision moves into orphan_can_reuse_name so it can be tested
against a database instead of only through a real pidfile. OrphanProcess
gains Default so a test can name the two fields a case is about.
Every hook bind path already calls set_terminal_title, so a pane tracks
the identity its session resolves to. The two CLI paths that change an
identity did not: after `hcom start --as` or an orphan recovery the pane
kept advertising the name that was just replaced. That is one of the three
disagreeing sources an operator sees when an identity drifts, and the only
one they look at while working.

Under cfg(test) the title is recorded rather than written. A test can then
assert that a path renames its pane, and the suite cannot repaint the
terminal it is running in.
@gzur
gzur force-pushed the fix/orphan-name-reclaim branch from 99314b9 to 594b240 Compare September 14, 2026 12:07
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