Skip to content

Add 'hyp leave': first-class inverse of 'hyp join' (designed but unimplemented) #218

Description

@bgmcmullen

Summary

There is no command to leave a centrally-managed fleet. hyp join has three side effects (central seed, daemon install, daemon-driven client attach/backfill), but no first-class inverse. Reversing a join today requires an error-prone manual sequence including raw rm of internal kernel state files.

hyp leave is already named in the design corpus as the host-level inverse of join — it was deliberately deferred ("when that lands"), not forgotten:

  • LLP 0041 §Undo on leave: "reverse(requestKey) runs when desired() no longer names a request key that the marker records as applied - i.e. the central config stopped naming the effect, or the machine left the fleet (hyp leave/detach, when that lands)."
  • LLP 0044 references hyp leave repeatedly (as the trigger that "clears the central layer" and restores client settings on leave).

This issue tracks promoting that deferred command into a real spec + implementation.

Current state: no coherent "leave" path

hyp join (src/core/cli/core_commands.js):

  1. Writes the central seed ~/.hyp/hypaware/config-control/seed.json (server URL + bootstrap token).
  2. Installs the daemon (LaunchAgent com.hyperparam.hypaware).
  3. The daemon pulls central config, applies it, and the reconciler attaches clients (modifies ~/.claude/settings.json, codex config), backfills, starts sinks.

To reverse this today, an operator must run, in the right order:

hyp daemon stop
hyp daemon uninstall
hyp detach all
rm ~/.hyp/hypaware/config-control/seed.json
rm ~/.hyp/hypaware/config-control/config.a.json ~/.hyp/hypaware/config-control/config.b.json \
   ~/.hyp/hypaware/config-control/config.b.etag ~/.hyp/hypaware/config-control/active

This is undiscoverable, order-sensitive, and reaches into kernel-internal state files by hand.

Why this is a real gap, not just sugar

The reverse machinery already exists. The reconciler's reversible-handler path (src/core/config/action_reconciler.js:198-237) detaches clients automatically when desired() stops naming them, and crucially deletes the marker (delete markers[requestKey], line 212), keeping disk + markers coherent.

So hyp leave is mostly wiring an existing path:

  1. Drop the central layer (remove seed + applied central config slots).
  2. desired() no longer names the attached clients.
  3. The reconciler's reverse() detaches every client and clears its attach marker.
  4. Stop + uninstall the daemon (keep recordings/cache/logs, like daemon uninstall).

Relationship to #217

#217 (manual hyp detach orphans a done attach marker, so a later join never re-attaches) is a symptom of the same root cause: there is no coherent on-the-way-out lifecycle operation, so users reverse piecemeal via detach + rm, outside the path that keeps markers and disk in sync. A hyp leave that routes through the reconciler reverse() would not hit #217 by construction. Consider #217 subsumed by this work (or fixed alongside it).

Proposed behavior (for the spec to refine)

hyp leave:

  • Reverses all client attaches (via the reconciler reverse() path, not ad-hoc disk edits) so client settings are restored and markers cleared.
  • Removes the central layer (seed + applied central config) so the host no longer pulls central config or rejoins.
  • Stops and uninstalls the daemon.
  • Keeps local config (hypaware-config.json), recordings, cache, and logs — same retention stance as daemon uninstall.
  • Flags: likely --dry-run and --json for parity with attach/detach; possibly --keep-daemon to leave the fleet but keep the daemon for local-only capture.

Open design questions

  • Central notification: should leaving notify the central server, or is local leave silent? (Deferrable - the consent model in LLP 0044 treats leave as the user's prerogative.)
  • Locked config: leave acts on central-locked config locally; confirm that is consistent with the LLP 0044 consent footing (attach is consent-based and user-reversible).
  • Partial leave vs full: is there a use for "leave the fleet but keep local capture running" (--keep-daemon), or is leave always a full teardown?

Suggested process (per repo norms)

  1. Promote the deferred hyp leave from LLP 0041 §Undo on leave / LLP 0044 into an actual spec or plan LLP; run /llp-grill against 0031 (layered config / central seed), 0041 (reconciler + undo on leave), and 0044 (attach-on-join consent).
  2. Implement, landing the LLP edit in the same commit as the code (@ref annotations on the new command).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions