# Exact-checkpoint CLI execution

## Supported boundary

The helper uses the installed `@selesai/code` SessionManager only to extract a
historical checkpoint and inspect worker sessions. Workers themselves are
ordinary separate `sai --fork` CLI processes; no SDK agent loop or pi-subagent
framework executes their work. There is no invented `--at` CLI flag.

Requirements: Node >=22.19, installed `sai` or `selesai`, and POSIX (macOS/Linux)
for detached lifecycle and process-group cancellation. Set `SELESAI_CLI` to an
absolute installed executable if PATH discovery is ambiguous. Missing or
incompatible SDK APIs fail closed; do not install or change packages silently.
The helper currently requires version-3 session files. It never migrates or
rewrites the source parent session.

Read installed Selesai CLI/session docs if behavior differs from these
requirements. Do not substitute another agent framework or approximate a fork
with copied prompt text.

## 1. Prepare private control storage and find the turn

Use a unique parent-owned directory outside the source checkout, for example
`~/.selesai/agent/implement-plan/<run-id>/`, with mode 0700. Keep plans, pool,
board, launch configs, prompts, and receipts there. Artifacts may contain private
conversation/code; do not commit or publish them, and do not put secrets in
prompts or metadata. The helper creates private files but is not a security
sandbox against an agent that can run shell commands.

Resolve the absolute helper path once:

```sh
node <skill>/scripts/cli-workers.mjs inspect /absolute/source-session.jsonl
node <skill>/scripts/cli-workers.mjs catalog
```

`inspect` lists the last 50 completed/partial assistant-message candidates with
IDs, timestamps, and previews. It does not choose a plan turn. For an older turn,
read/search the source JSONL directly, then inspect that exact entry and its
ancestry. The planning turn must be a completed textual assistant message with
no unresolved tool calls. Match its planning work to the plan/todo pair; a matching
filename alone is not proof. Do not use a prefix ID or select the latest leaf.
If source persistence ends on a partial JSON line, retry after persistence
finishes rather than trimming history silently.

## 2. Create the immutable checkpoint

Write a config with absolute paths:

```json
{
  "source": "/absolute/source-session.jsonl",
  "entry": "<exact-completed-plan-entry-id>",
  "cwd": "/absolute/repository",
  "plan": "/absolute/repository/.selesai/tasks/plan.md",
  "todo": "/absolute/repository/.selesai/tasks/todo.md"
}
```

```sh
node <skill>/scripts/cli-workers.mjs checkpoint /absolute/checkpoint-config.json /absolute/control/checkpoint
```

The destination must not exist. The helper verifies the exact ancestry, excludes
later and sibling entries (including later labels/context edits), and calls the
installed `SessionManager.createBranchedSession(entryId)` on a temporary private
copy. It writes:
- `checkpoint.jsonl`: conversation ancestry through the exact plan turn.
- `plan.md`, `todo.md`: read-only artifact snapshots.
- `checkpoint.json`: provenance, original paths, and SHA-256 bindings.

Existing compaction/context-edit entries on that ancestry remain effective;
forking is not a way to recover model context already summarized earlier.
The helper cannot prove a file's historical contents from an assistant claim.
The parent must reconcile artifact versions before pinning. It rejects changed
checkpoint/snapshot hashes and changed authoritative plan content at launch.
Original todo checkboxes may evolve under parent ownership; the pinned todo
retains original task definitions. A changed task definition needs explicit
reconciliation, not an unnoticed checkbox edit.

## 3. Approve the pool and write a bounded assignment

Follow `model-routing.md`; save the approved pool and routing receipts. Write
one prompt file per task/attempt, including its role, owned files, accepted
prerequisites, constraints, acceptance, validation, and stop conditions.

Require a final report with:
- Task ID and result: ready for review or blocked.
- Changed files and substantive behavior/interface changes.
- Acceptance evidence and exact commands with exit codes/results.
- Known defects, unresolved decisions, and downstream handoff details.

Reports are evidence, not acceptance. Keep source plan/todo and control files
read-only in worker instructions. Inherited history may contain unrelated prior
instructions; the appended assignment narrows current authority. Do not allow
nested agent launches through shell or tools.

## 4. Launch a detached CLI fork

Write this request with absolute paths:

```json
{
  "checkpoint": "/absolute/control/checkpoint/checkpoint.json",
  "pool": "/absolute/control/pool.json",
  "candidate": "writer-medium",
  "taskId": "T01",
  "role": "implementation",
  "mode": "write",
  "cwd": "/absolute/task-worktree",
  "promptFile": "/absolute/control/T01-attempt1.md",
  "timeoutSeconds": 1800,
  "routing": {
    "source": "ask_sai",
    "candidate": "writer-medium",
    "confidence": 0.84,
    "evidenceTimestamp": "<actual-timestamp>"
  }
}
```

The timeout/confidence above are illustrative, not fabricated execution evidence.
Use `mode: "read-only"` for review/research and an appropriate approved role.
The helper enforces candidate role restrictions when present. Read-only mode
omits edit/write tools but still allows bash for inspection/validation; it is an
instruction/tool boundary, not filesystem isolation. Never run destructive
validation under the guise of read-only work.

```sh
node <skill>/scripts/cli-workers.mjs start /absolute/T01-request.json /absolute/control/T01-attempt1
```

The run directory must not exist. The detached supervisor validates the request
again, then launches the equivalent of:

```sh
sai --fork /absolute/checkpoint.jsonl \
  --session-id implement-<unique-id> --session-dir /absolute/run/sessions \
  --name "implement-plan T01" \
  --provider <exact-provider> --model <exact-model> --thinking <supported-level> \
  --tools read,bash,edit,write,grep,find,ls --no-mcp --mode json --print
```

The prompt is delivered through stdin, not shell interpolation or an argv string.
The worker receives its own session identity; inherited parent session/intercom/
subagent identity environment variables are removed. Normal credentials remain
inherited but are not written to receipts. Project trust remains the CLI's
responsibility: never add an automatic approval flag just to bypass a prompt.
Base tools only are enabled; MCP is disabled. If a task needs additional tools,
resolve and review that capability change explicitly rather than bypassing the
helper with an unrestricted command.

## 5. Workspace and integration ownership

Conversation forks do not fork files. Record the actual cwd/ref and initial dirty
state for every assignment. A checkout may have only one writer at a time. The
helper takes an atomic per-canonical-cwd writer lease in the OS temp directory;
competing writes fail rather than race. The parent must also avoid editing that
checkout while the worker runs. The lease does not prevent unrelated external
programs from writing.

For concurrent writers, prepare separate Git worktrees with `git worktree add`
on named branches outside source/extension discovery, from the intended current
base. Do not assume a worktree contains dirty/untracked files or dependencies.
Preserve user work; if a task depends on uncommitted changes, resolve the base or
serialize in the original checkout rather than quietly dropping them or
committing unrelated edits. Bootstrap dependencies deliberately.

Downstream work must contain accepted prerequisite changes: integrate them or
prepare the next worktree from the accepted integration ref before launch. A
shared conversation checkpoint does not transfer a sibling's filesystem changes.
The parent reviews patches/commits, then one integration owner assembles them.
Do not automatically cherry-pick, commit, discard, or remove worktrees.

## 6. Observe, stop, and recover

```sh
node <skill>/scripts/cli-workers.mjs status /absolute/control/T01-attempt1
node <skill>/scripts/cli-workers.mjs wait 60 /absolute/control/T01-attempt1 /absolute/control/T02-attempt1
node <skill>/scripts/cli-workers.mjs stop /absolute/control/T01-attempt1
```

Artifacts:
- `request.json`: checkpoint, task, prompt, chosen triple, routing, cwd, timeout.
- `launcher.json`: detached supervisor identity.
- `status.json`: lifecycle, process/session identity, resolved settings and failure.
- `events.jsonl`, `stderr.log`, `supervisor.log`: full diagnostics.
- `report.md`: last assistant response, not a trusted verification receipt.
- `sessions/`: worker JSONL session for audit/recovery.

Lifecycle: `queued` → `running` → `finished-unreviewed`, `failed`, `stopped`, or
`timed-out`. A missing supervisor yields `unknown`; that is neither completion
nor permission to retry. The helper checks JSON settlement and the final
assistant stop reason as well as exit status: CLI exit 0 alone can conceal a
provider error. It checks actual assistant provider/model and the saved session's
resolved provider/model/reasoning; a mismatch is failure, not a silent fallback.
This final audit is not a sandbox preventing all possible extension behavior.

Stop writes a control request, signals the worker process group, and escalates
if necessary. Observe terminal status before reassigning its files. Preserve the
partial diff and inspect logs after cancellation/failure; do not assume rollback.
A machine/supervisor crash can leave a worker or writer lease behind. Inspect the
recorded process command, session, worktree, and lease owner before explicitly
terminating or removing anything. Never kill a PID merely because an old receipt
contains it; PIDs may be reused. Never auto-delete a stale lease.

Raw CLI workers have no native parent wake delivery. The helper's `wait SECONDS
DIR...` watches status-file changes (no polling) and returns when any listed run
is terminal/unknown, or the bounded wait expires. Pass only active run directories;
remove consumed results before waiting again. It returns `waiting: true` on a
wait timeout, which is not a worker timeout or completion claim. Maximum wait is
3600 seconds; choose a duration within the host tool's own timeout.

Attach this wait command to a real host background completion facility when
available; a notification then belongs to the tracked wait command, not the raw
worker PID. Otherwise use a bounded foreground wait after safe independent work,
or yield with run paths and an explicit wait/status action to resume. Do not
invent `bg_wait` support for unregistered raw PIDs, busy-poll, or mistake a
detached launch for a finished plan. A supervisor crash may not change its status
file; a bounded wait always rechecks process existence when expiring.

Retry with a new run directory and the same immutable checkpoint, supplying
current diff/evidence in a bounded correction prompt. Do not silently resume a
worker from a later conversational leaf. New scope needs explicit approval and,
if it supersedes the plan, a new planning checkpoint.

## Runnable check

```sh
node <skill>/scripts/check.mjs
```

Uses the installed SessionManager and fake CLI/model fixtures. It exercises exact
ancestry extraction, unchanged parent history, model/reasoning checks, literal
stdin delivery, process/session receipts, provider failure despite exit 0,
writer conflicts, stop, timeout, and event-driven waiting. It makes no paid model calls. A real provider
smoke test is separate and requires user authorization and an approved model.
