Skip to content

Latest commit

 

History

History
323 lines (271 loc) · 18.2 KB

reference.md

File metadata and controls

323 lines (271 loc) · 18.2 KB

Setup and CLI reference

Detailed setup, agent procedure, permissions, and operating limits.

Install and configure

Clone into a stable location and run the checks:

git clone https://github.com/HakAl/switch.git
cd switch
SWITCH_DIR="$PWD"
python3 switch.py --version
python3 switch_auto.py --version
python3 -B -m unittest discover -s tests -v

Keep both scripts and the Python interpreter at those paths: installed hooks refer to their absolute locations. In a new terminal, set SWITCH_DIR to the absolute path of this clone again. Choose one of the following hook setups. No live session is reset during installation.

Automatic context-limit hooks

From the project you want to manage:

cd /absolute/path/to/your/project
python3 "$SWITCH_DIR/switch_auto.py" install --dry-run
python3 "$SWITCH_DIR/switch_auto.py" install
python3 "$SWITCH_DIR/switch_auto.py" status

The default limit is 25% of the reported context window and the default state directory is $SWITCH_STATE_DIR, otherwise ~/.local/state/switch. Pass a limit such as 40% or 250k, --project, or --state-dir to override them.

The installer merges hooks, wraps the effective command statusLine, and adds the missing required_permissions to permissions.allow in the project's .claude/settings.local.json, keeping existing entries, key order, indentation and line endings. It warns before normalizing formatting it cannot reproduce. Review the permissions_to_add that --dry-run lists before installing. Also permit reads of the original task instructions and authority sources, and only the actions the task needs. See permissions below. A Bash allow rule does not grant OS filesystem or socket access.

Start a fresh Claude session inside a Herdr pane in that project after setup. Keep Herdr's own session-reporting integration enabled. Run herdr agent list and check that the row for the pane identifies Claude and the current session. If reporting is absent or names another process, fix Herdr's integration first. Use the agent procedure for a manually triggered request; the companion supplies preparation instructions when it observes the threshold.

For user scope, status, uninstall and retention details, see automatic setup. To remove this project installation:

python3 "$SWITCH_DIR/switch_auto.py" uninstall

Uninstall removes the permissions it added and preserves state and backups for recovery. Do not delete claims while an old session might be resumed, or change state roots to bypass a claim.

Manual-trigger hooks only

Skip this section if you installed the companion: it already records lifecycle hooks. Set SWITCH_STATE to the same private state directory used by requests:

SWITCH_STATE=/absolute/path/to/private/switch-state
python3 "$SWITCH_DIR/switch.py" hooks --state-dir "$SWITCH_STATE"

This prints a settings fragment without changing configuration. Add each entry to the corresponding hook array in Claude's settings, preserving existing hooks, permissions and statusLine. Alternatively save the fragment and launch Claude with --settings /absolute/path/switch-hooks.json after checking the effective configuration. Then configure the scoped permissions below and start a fresh Claude session under Herdr to obtain its SessionStart record.

Both setups use HERDR_PANE_ID from Herdr and session_id from hook input. Required events are SessionStart, UserPromptSubmit, Stop, PreToolUse, PostToolUse, PostToolUseFailure, SubagentStart, SubagentStop and PermissionRequest. Lifecycle snapshots contain identifiers, worker generations, the parent transcript path and state, not tool arguments or prompt text; the companion separately stores the inputs disclosed above. Use a private local state directory; avoid shared, synced or network storage.

Permissions for the complete path

The launching Claude Bash tool needs permission to execute the installed script's request and ack commands, and optionally preflight and status. The example below uses manual-trigger setup paths; for the companion, use its printed required_permissions, including its auto-checkpoints path. Substitute your actual interpreter and installation paths:

{
  "permissions": {
    "allow": [
      "Bash(/usr/bin/python3 /opt/switch/switch.py preflight *)",
      "Bash(/usr/bin/python3 /opt/switch/switch.py request *)",
      "Bash(/usr/bin/python3 /opt/switch/switch.py status *)",
      "Bash(/usr/bin/python3 /opt/switch/switch.py ack *)",
      "Read(//absolute/path/reset-state/requests/**)",
      "Edit(//absolute/path/project/checkpoint.json)"
    ]
  }
}

Use the same interpreter spelling for invocation; bootstrap uses Python's sys.executable, which can differ from python3 or a symlink. Verify the emitted command and permit that exact installed-script prefix too when necessary. The trailing * matches arguments; avoid pane-prefix patterns such as w8:*. File rules use // for filesystem-root paths, and Edit covers Write. Grant reads for the actual instruction/authority files and only the writes/commands needed by the task's next action. An acknowledgment does not grant that next action permission. Deny or managed rules still apply.

Run the bootstrap's ack command exactly as printed, alone in its own Bash call. Do not chain it with checksum checks or other verification; run those in a separate call. The installer's existing ack rule covers the generated interpreter and script prefix, not every command you might combine with it.

The helper and hooks need filesystem access to their state directory, the checkpoint, instruction and authority sources, plus access to the local Herdr socket and executable. The helper invokes only herdr agent list, herdr pane get, herdr pane read, and herdr agent prompt. Claude's Bash allow rule does not itself configure an OS sandbox's filesystem or socket access. Test this setup with preflight and a disposable seat first. No broad Bash/Herdr allow rule, bypass permission mode, or permission editing by the agent is required.

For recovery, status and reading saved checkpoint/bootstrap files need the same read permissions. Manual bootstrap submission uses the operator's normal Claude input (or their existing authorized Herdr prompt channel); it is never retried automatically. The new context still needs the scoped ack command permission.

Agent procedure

  1. Finish useful foreground tools. Collect outstanding workers and their results. Finish waits, or deliberately stop disposable waits through the owning tool's normal interface. Do not cancel useful work to meet a reset deadline. Inspect Claude's background task list and any external worker/workflow system you used. Herdr idle and Stop alone cannot prove those resources are finished.
  2. Write a concise checkpoint based on examples/checkpoint.json. Include task, role, absolute worktree, refs, completed and unfinished work, concrete next action, instruction paths, original authority-source paths, and an explicit preparation inventory. quiescent: true attests that active tools, workers and waits are all empty except the upcoming request command itself. Hooks also check tools/workers, but cannot discover arbitrary external jobs or a process detached by a completed Bash call. This attestation is necessary.
  3. Run preflight, then request from the Claude session being reset. Herdr supplies HERDR_PANE_ID; --pane must match it, and --session must be that pane's current live session. Missing or mismatched caller identity is refused. Do not set or override this variable to target another pane. Preflight is read-only and reports busy/input conditions without treating them as readiness.
  4. After request reports scheduled, end the turn immediately. Additional user input cancels the pending reset. Start no more tools, workers, or background waits. The helper waits for a subsequent Stop, no tracked active resources, fresh lifecycle telemetry, idle/done Herdr status, and a readable empty prompt. Keep this pane exclusively owned during reset: no typing, scrolling, manual session switching, or other automation.
python3 /opt/switch/switch.py preflight \
  --state-dir /path/reset-state --pane PANE_ID --session OLD_SESSION \
  --checkpoint /path/project/checkpoint.json

python3 /opt/switch/switch.py request \
  --state-dir /path/reset-state --pane PANE_ID --session OLD_SESSION \
  --checkpoint /path/project/checkpoint.json \
  --resume 'Run the next action recorded in the checkpoint.' \
  --idle-deadline 300 --clear-deadline 60 --ack-deadline 120

# From outside the resetting turn, using the returned request ID:
python3 /opt/switch/switch.py status \
  --state-dir /path/reset-state --request-id REQUEST_ID

The caller-pane check prevents accidental cross-pane requests; it is not OS-level authentication. Code with arbitrary local execution can change its environment. Keep command permissions narrow and do not grant environment overrides.

All deadlines are finite, from 1 to 3600 seconds. clear-deadline gives clear confirmation and subsequent replacement readiness separate intervals of that length (60 seconds each by default, at most 120 seconds combined). The readiness interval starts once, after correlated clear confirmation; both its loop and Herdr subprocess limits use the new budget. Missing Herdr reads during readiness or acknowledgment polling consume the existing interval; confirmed identity changes still refuse immediately. No prompt is resent. Lifecycle evidence must still be at most 120 seconds old: a longer deadline does not make stale evidence usable, and slow startup without further hook events can exhaust that freshness window. ack-deadline bounds confirmation of resumption, not completion of the resumed task. Each Herdr call is capped at five seconds and the remaining phase budget. A detached process survives the launching tool/turn ending, but not arbitrary host shutdown or OS sandbox teardown.

Local checkpoints are capped at 32 KiB and copied to private request storage; the actual copy is reread and hashed before clearing and bootstrapping. Existing handoff systems can supply the content, but an identifier in terminal output is not a checkpoint. Export and verify the actual content first. Authority source paths reference the operator's real instructions/grants, with their original scope and expiry; agent-authored notes are separate and cannot extend a grant. Empty resource inventories mean explicitly none, not unknown.

Worker completion evidence (0.3.2)

A successful TaskStop for a local agent can close its tracked worker even when Claude emits no SubagentStop. A completed foreground Agent result must match an observed worker generation. For older stale entries, Switch reads at most 8 MiB of the parent transcript and requires paired structured TaskStop success, matching session identity, timestamps and transcript order. Resuming a worker invalidates earlier stop evidence, including when timestamps tie. Raw transcript content is never copied into lifecycle state. Missing, incomplete or changing evidence keeps the worker blocking. Refusal reasons identify remaining workers.

Parent Stop clears foreground tools but does not end workers. Useful live or unknown work still blocks reset; the 300-second default is unchanged. Finish work before requesting reset, or explicitly choose a longer bounded idle deadline. Do not delete worker state or cancel useful work just to meet the deadline.

Results and recovery

A published request has request.json and the durable checkpoint.json. helper.log is opened when the CLI launches the helper; allocation alone does not establish it. bootstrap.txt is prepared only after the replacement session passes readiness, not merely after clear. It contains the exact bytes bound to the resume attempt. ack.json appears only when acknowledgment succeeds. Request records include identity and stage; send results, timestamps and new session evidence appear as those stages are reached. status derives recovery advice from the recorded stage and the currently available bootstrap artifact.

Outcome Meaning
resumed New session confirmed, bootstrap submission observed, and agent acknowledgment received. Task completion is separate.
not_cleared This helper sent no clear. The original session should be inspected and preparation repaired.
clear_uncertain Clear was attempted but not confirmed by both Herdr and a new SessionStart with source clear. Do not resend.
cleared_not_resumed Clear was confirmed; bootstrap or acknowledgment failed/timed out. Inspect the pane and receipt before manual recovery.
interrupted_uncertain Status found an abandoned helper, including one killed before its final write. Inspect saved intent and live evidence.

Exit 0 means the command succeeded (scheduling is not reset completion); 3 is a validation/setup refusal (including brief claim-lock contention); 4 is a recorded unsuccessful or uncertain outcome. Only resumed is a completed reset-and-resume result. A successful Herdr send alone is not proof of submission, and a working status is not an acknowledgment. The agent's receipt attests that it read the checkpoint/instructions and rechecked authority; it is not an independent proof of comprehension.

Bootstrap submission evidence survives ordinary follow-up prompts in the same session. Ack accepts either matching session telemetry or the worker's saved submitted_at confirmation for that request. The latter also supports recovery from older hooks that erased telemetry on a follow-up. The input provenance archive (auto/PANE/SESSION.inputs.json) excludes full Switch bootstraps bound to a recorded resume in that pane/session, including recognized paste wrappers. Unverified marker-prefixed inputs remain untrusted provenance. Fresh and bootstrap-only sessions may have no archive; generated preparation text advertises it only when a usable record exists. Retain real authority sources from the prior checkpoint rather than listing an absent archive. Its absence does not mean submission was missed, and its presence confers no authority.

After a timeout, a valid manual ack can write ack.json, but it does not restart the helper or change its finished cleared_not_resumed outcome to resumed. Inspect both records during recovery. Session/terminal identity, checkpoint and permission-mode checks still apply; changing permission mode can produce a separate refusal. No automatic denial retry or deadline extension is performed.

If a refusal occurred before allocation and inspection confirms no request or session claim was allocated, an explicit user request to try again permits fresh preparation and an ordinary request without --retry-of. No predecessor ID exists in that case. An error without an ID is not itself proof of no allocation; inspect the session claim and request state when uncertain. Published uncertain or abandoned requests remain held. Helper-launch refusals return the allocated request_id so its receipt can be inspected through status.

A finished not_cleared request can be retried explicitly if status reports retry_eligible: true. Revalidate preparation and update the checkpoint, then repeat request with --retry-of FAILED_REQUEST_ID. This creates a new request and preserves the old receipt. A fresh Stop is required; new input cancels the new attempt too. Status reports current_request_id when an attempt has been superseded. Eligibility is advisory and is rechecked under locks at scheduling.

Never retry automatically, delete claims, or change state roots to bypass a claim. ack acknowledges a resumed session; it cannot unlock a cancelled request. If clear was attempted or its outcome is uncertain, inspect the live session and saved evidence before recovery: a timed-out send can still arrive. If clear succeeded, verify the recorded pane/session/terminal, preserve any draft, read the durable checkpoint, verify its recorded hash, and recheck applicable instructions and original authority sources for scope and expiry. Stop if those checks fail. Then follow the reached stage:

  • No resume attempt: after those checks, continue the checkpoint's next_action manually in that session. No bootstrap or acknowledgment is required; do not invent resume evidence or run ack for an unattempted resume.
  • Resume attempted and the saved bootstrap matches its bound hash: only after checking it was not already processed, manually submit its exact contents once and follow the embedded acknowledgment command. Stop on refusal.
  • Resume attempted but the saved bootstrap is missing or mismatched: do not resend. Reconcile live submission/ack evidence before continuing manually from the checkpoint.

Never clear that session again as a recovery shortcut.

Support and limits

Herdr has no atomic session-compare-and-prompt operation. The helper rechecks identity and input immediately before sending, but cannot exclude an external writer in the last instant. Exclusive pane ownership is a required operating condition. Scrolled views, unfamiliar prompt layouts, permission dialogs, and missing/stale telemetry are refused. Only actual SGR 2 dim runs are ignored; RGB/palette color alone never proves text is a suggestion. The full bordered input box is checked, including continuation lines.

Switch sends /clear in the existing process. It never restarts Claude, changes its model/mode, or cancels work. See validation for what was actually observed in the tested CLI; do not infer arbitrary resource survival from a successful clear. State files and claims should be retained while an old session might be resumed. Kernel locks disappear on process death; status uses those locks rather than a PID that could have been reused. No process can promise a final disk write after SIGKILL or power loss, so interrupted results remain explicit.

The base engine uses Claude's SessionStart hook as clear evidence. The companion uses fresh session-bound status-line measurements to request preparation. See the design and validation. Permission examples follow the Claude permission reference.