Skip to content

RFC: tracked hcom resume retains principal #87

Description

@zane668

Depends on #86.

Summary

Assuming RFC-P1's durable principal id exists, this RFC proposes that a tracked hcom resume <name> preserve the principal of that name's current or uniquely identifiable most-recently-stopped tracked lifecycle and reinject it into the new process. The lookup is lifecycle-aware; it is not an unqualified "last principal ever seen for this string" guess.

This closes the common hcom-managed resume path without changing tool-level session-id semantics. It does not generalize to adoption, fork, vanilla restart, or explicit name reclaim. Those paths mint a fresh principal.

Problem statement

hcom resume <name> starts a new OS process for what the user experiences as "the same agent instance." Anything carried only in env (HCOM_PROCESS_ID, and HCOM_PRINCIPAL_ID from RFC-P1) disappears with the old process, so env alone cannot retain the principal.

But hcom's tracked resume path already preserves the registered instance name. do_resume (src/commands/resume.rs:118) resolves the argument through resolve_display_name_or_stopped, and the tracked branch passes launch_name = Some(display_name) (src/commands/resume.rs:508-513). RFC-P1's live instance row and durable binding record therefore provide a bounded hand-off point: use the live row when it exists; after stop has removed that row, use the uniquely newest surviving binding for that stopped lifecycle; inject that exact principal into the replacement process.

Proposed capability

  • On hcom resume <name>, when the resolved name corresponds to a tracked instance lifecycle, resolve its principal from RFC-P1 state and inject the same value into the replacement process.
  • With a live row, require agreement between instances.principal and its binding. With no live row after stop, use only the uniquely newest surviving binding for that name; an equal-timestamp tie or inconsistent state is ambiguous and must not be retained.
  • The read and resume update must be lifecycle-safe: if the row/binding is reclaimed or changes before the replacement is committed, resume must fail or mint a new principal according to the normal creation path. It must not select an older lifecycle merely because its name string matches.
  • This requires no change to how any underlying tool (Claude, Codex, Gemini, etc.) manages its own session id — the mechanism rides entirely on hcom's own resume entry point and its own name-based lookup, which is tool-agnostic.

The three-way split (must be stated honestly, no hand-waving)

hcom's own resume/fork code already distinguishes exactly the cases this RFC needs to distinguish — this is not something we're introducing, it's already there in src/commands/resume.rs:

  1. Tracked resume (launch_name = Some(display_name), decided at src/commands/resume.rs:508-513): the user resumes the current or uniquely identifiable most-recently-stopped tracked lifecycle under its registered hcom name. This is the only case this RFC covers. Its principal is reattached.

  2. Adoption resume (launch_name = None, same branch at src/commands/resume.rs:508): hcom adopts a session that has no tracked instance lifecycle to continue. A fresh principal is minted. No principal is inferred from tool session id, working directory, recency, or a historical name.

  3. Fork (prior_session_id: (!fork).then(|| session_id.clone()), src/commands/resume.rs:543): forking deliberately starts a new lineage. A fork always gets a fresh principal, even when it starts from a tracked instance and even if the user chooses a similar name.

Any presentation of this capability that blurs these three into "resume keeps your identity" would be inaccurate. Only case 1 gets a kept principal by this mechanism; cases 2 and 3 get a fresh one, by design, not by omission.

Name reuse and explicit reclaim

Names are not globally unique for all time, so P2 must not blindly implement name → any historical principal. A live instances row is authoritative when present and must agree with its binding. If stop has removed the row, the uniquely newest surviving binding represents the most recently stopped tracked lifecycle; a top-timestamp tie or inconsistent pair yields no reusable principal. This gives each name at most one eligible answer at the resume boundary and makes ambiguity fail closed.

hcom start --as <name> is an explicit reclaim/rebind, not a resume. It creates a new lifecycle and therefore a new principal. While its row is live, that row disambiguates all older bindings; after it stops, its binding is the uniquely newest candidate. A later tracked resume therefore retains the post-rebind principal, never the pre-rebind one. If the implementation cannot prove which lifecycle is current, it must not retain a value.

Example

Assume tracked instance luna currently has principal p-a. hcom resume luna starts a new process but reinjects p-a. A fork created from luna receives a different principal. If luna is later explicitly reclaimed with hcom start --as luna, the reclaimed lifecycle receives p-b; its next tracked hcom resume luna retains p-b, never historical p-a.

Implementation cost

  • The launcher needs a principal lookup-and-reinject step on the tracked resume path.
  • Every creation/rebind path still needs RFC-P1's principal write; P2 must not repair missing or ambiguous state by choosing an older binding.
  • The read/update boundary needs a lifecycle check or transaction/CAS equivalent so concurrent stop/reclaim/name reuse cannot attach a stale principal to a new row.
  • Failures must be observable. A missing/inconsistent principal may fail the tracked resume or explicitly downgrade to a fresh principal, but must not be silently guessed.

What this does NOT cover

  • Vanilla / non-hcom-launched session restarts with no hcom resume invocation. There is no hcom-managed admission boundary here; P2 does not retain the principal. A separate admission design may later prove continuity, otherwise the case remains new/unresolved.
  • Cross-machine / relay resume. Out of scope for this RFC.
  • Any guarantee that the party invoking hcom resume <name> is actually the same real-world operator who created the original instance. This RFC retains a principal on the basis of "resumed under the previously-registered name," which is a claim, not a cryptographic proof of continuity. Consumers that need stronger assurance than "same name, hcom's own records say this was previously this principal" should not treat this as an authentication mechanism.

Acceptance criteria

  1. Launch a tracked instance, record its principal, stop it, and run hcom resume <same-name>: the replacement process, current instance row, and principal mapping all carry exactly the recorded principal.
  2. Repeat tracked resume more than once: the principal remains constant while process_id is re-minted per process.
  3. Resume by adoption (launch_name = None): the resulting principal is fresh and differs from any source session's principal.
  4. Fork a tracked instance: the child principal is fresh and differs from the source principal; the source mapping remains unchanged.
  5. Reclaim a previously used name with hcom start --as <name>: the new lifecycle gets a fresh principal. A later tracked resume retains this post-rebind principal, never the old historical one.
  6. Race the tracked-resume lookup against stop/reclaim/name reuse: a stale principal is never attached to the replacement. The operation either retains the current lifecycle's principal, fails, or explicitly mints a fresh one.
  7. Delete or corrupt the current principal mapping, or create an equal-timestamp orphan tie, before tracked resume: the command reports the failure/downgrade and never reconstructs a principal from tool session id, working directory, or an older ambiguous binding.
  8. Tests exercise all three resume/fork branches plus repeated resume, name reclaim, and the lifecycle race.

Relationship to RFC-P1

This RFC cannot be implemented without RFC-P1's principal id and persistent mapping. P1 defines and exposes the key; P2 adds one continuity rule for tracked hcom resume <name>. It does not change P1's fork, adoption, reclaim, or vanilla-restart boundaries.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions