Skill Suitcasedocs

06Operate · Agent workflows

Audit first, mutate on approval.

Agents run the read-only audit loop freely, report what they find, and cross a mutation boundary only after a human approves the exact source, target, and mode. The recipes below turn each observed state into the exact command sequence, so no step is guessed.

The audit loop

# read-only: the catalog gates
skill-suitcase import --source "$SRC" --json
skill-suitcase validate --source "$SRC" --strict --json
skill-suitcase targets --source "$SRC" --json
skill-suitcase status --source "$SRC" --json

# read-only: then per target, with explicit path overrides
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json
skill-suitcase diff --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

Every command here is read-only. Repeat the same shape for claude, agents, and grok with their overrides. Treat read_only_target from provider-backed targets such as OpenCode and Pi as the expected boundary, not an error to work around.

The recovery decision tree

When a target does not match the catalog, the target state and ownership model select exactly one command:

Run the matching dry-run before reconcile, repair, prune, promote, or import-target. Use receipt-backed rollback only to reverse eligible apply, reconcile, or repair state. The recipes below execute this tree state by state.

How to read a recipe

Every recipe uses the same conventions. SRC points at the approved catalog checkout, for example SRC="/path/to/skills-catalog". Examples use the codex target with its --codex-home override; for another runtime, swap only the target ID and override flag from the target registry. Every invocation passes --json and returns a deterministic result object on stdout, including structured ok: false refusals.

Each step carries every write classification that applies:

read-onlyDoes not persist changes to the catalog or live targets. A command may use an isolated temporary workspace, and is safe to run and repeat without approval.
staging-onlyWrites only a disposable output directory outside the catalog and every resolved target root. The live target and catalog are untouched.
catalog-mutatingWrites the catalog working tree as ordinary uncommitted Git changes. Nothing auto-commits; review the diff.
live-target-mutatingWrites a live agent skill root or its receipt. Runs only after the approval named in the recipe.

Approval

No catalog-mutating or live-target-mutating step runs until a human gives explicit approval naming the source catalog, target, exact skills, action, and mode, with the recipe's dry-run or diff evidence in front of them. Read-only steps never need approval. Staging-only steps do not mutate the catalog or target, but the artifact recipes place fresh staging inside the approved sequence so apply follows it immediately. Work one target at a time.

Recipes show the canonical command shapes; the command reference documents exhaustive options and refusal codes.

The recipes

1. New-machine setup from an approved catalog

Select when a fresh machine has agent runtimes but no Suitcase-managed skills yet, and the operator has named an approved catalog. New-machine setup installs from the skills repo through Suitcase, never by letting upstream tooling write into managed runtime roots. Install the CLI, the packaged operator skill, and the approved catalog checkout at a portable root such as $HOME/.skill-suitcase/skills by following INSTALL.md, then run this recipe once per target.

read-onlyimport, validate --strict, targets, plan, status, diff, pack --dry-run
staging-onlypack --output into a temporary directory, staged fresh inside the approved sequence
live-target-mutatingapply --artifact --mode copy, only after approval
# read-only: prove the catalog and this target, then preview the bundle plan
skill-suitcase import --source "$SRC" --json
skill-suitcase validate --source "$SRC" --strict --json
skill-suitcase targets --source "$SRC" --json
skill-suitcase plan --source "$SRC" --target codex --json
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json
skill-suitcase diff --source "$SRC" --target codex --codex-home "$HOME/.codex" --json
skill-suitcase pack --source "$SRC" --target codex --codex-home "$HOME/.codex" --dry-run --json

Approval

The staging and apply steps below run only after explicit approval naming the catalog, the target and its resolved install root, the planned skills from the bundle plan, artifact mode, and the install mode, encoded below as --mode copy; a symlink install needs its own approval and --mode symlink. Ordinary missing and behind writes are rebuilt from current catalog source, so a bundle staged before approval is not byte-for-byte authorization: stage fresh and apply immediately.

# read-only: after approval, re-check drift immediately before staging
skill-suitcase diff --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

# staging-only: stage a fresh bundle outside catalog and target roots
OUT="$(mktemp -d "${TMPDIR:-/tmp}/skill-suitcase-pack.XXXXXX")"
skill-suitcase pack --source "$SRC" --target codex --codex-home "$HOME/.codex" --output "$OUT" --json
for ARTIFACT in "$OUT"/.skill-suitcase/artifacts/*/skill-suitcase-bundle.json; do
  break
done

# live-target-mutating: apply the just-staged artifact immediately
skill-suitcase apply --source "$SRC" --target codex --codex-home "$HOME/.codex" --artifact "$ARTIFACT" --mode copy --json

# read-only: verify the target state
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

Evidence. import and validate --strict must return ok: true with no error-level findings. On a fresh machine the status entries read missing; after apply, every planned skill reads current. The apply result's installRoot identifies the receipt location as <installRoot>/.skill-suitcase-receipt.json; ApplyResult does not expose a separate receipt-path field. The post-approval diff must still match the approved bundle plan; new drift there voids the approval. Refusals such as source_untracked_files, unsafe_target_state, or read_only_target arrive as structured ok: false JSON.

Verify, then stop. Re-run status and confirm zero non-current entries for the target, then repeat the recipe for the next target, one at a time. Stop and escalate if strict validation reports errors or if the supposedly fresh target already reports unknown or dirty entries. Unknown entries route to recipe 5 or 6; dirty entries need inspection to choose recipe 4, 7, or 8 instead of being overwritten by apply.

2. Read-only catalog and target audit

Select when asked to report the state of the catalog and targets, or as the mandatory first phase of every other recipe. Every step is read-only, so this recipe never needs approval.

read-onlyevery step in this recipe
# read-only: the catalog gates
skill-suitcase import --source "$SRC" --json
skill-suitcase validate --source "$SRC" --strict --json
skill-suitcase targets --source "$SRC" --json
skill-suitcase status --source "$SRC" --json

# read-only: per target, with explicit path overrides
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json
skill-suitcase diff --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

Approval

No approval or apply step exists in this recipe. Keep every result as inspection evidence and route any proposed mutation to its state-specific recipe.

Evidence. Successful inspection returns ok: true. A structured ok: false result means the audit is incomplete and blocks mutation. Inspect finding levels from import and validate, safety classification per target from targets, the seven-state status enum (current, missing, version, behind, dirty, blocked, unknown), and per-file diff actions (create, update, unchanged, extra, missing, blocked).

Verify, then stop. Report catalog branch and SHA, target IDs inspected, and summary counts. Any state other than current is a report, not permission to mutate: route missing/behind/version to recipe 4, exact receiptless matches to recipe 5, receiptless mismatches to recipe 6, ordinary dirty edits to recipe 7 or 8, and the guarded dirty-behind case to recipe 4. Stop on catalog error findings before touching any target.

3. Periodic target drift report

Select when running the recurring drift-audit heartbeat: at session start, on a schedule, or after any change to the catalog checkout. Every step is read-only.

read-onlyevery step in this recipe
# read-only: re-run per writable target and compare with the previous report
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json
skill-suitcase diff --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

Heartbeat

Reporting drift is automatic; importing or overwriting it is not. Drift detection is never permission to mutate either side.

Approval

No approval or apply step exists in a heartbeat. Report the evidence and wait for a separately approved recovery recipe.

Evidence. A complete heartbeat returns ok: true; retain any structured ok: false refusal as the report evidence and stop. Compare the new status entries with the previous heartbeat: a skill that moved to behind or version means the catalog advanced; a move to dirty needs inspection to distinguish live drift from the guarded dirty-behind case; a new unknown means receiptless target state appeared.

Verify, then stop. Report each drifted skill with its target, resolved path, and status, then stop. Escalate by naming the matching recipe (4 for behind, missing, version, or guarded dirty-behind; 5 or 6 for unknown; 7 or 8 for ordinary dirty) and wait for a decision; a heartbeat run must never trigger an implicit repair or import.

4. Missing or behind install, staged then applied

Select when status reports missing, behind, or version for catalog-planned skills on a writable target. Missing and behind route to pack plus apply, as does version; none routes to repair or reconcile.

read-onlystatus, diff, pack --dry-run
staging-onlypack --output into a temporary directory, staged fresh inside the approved sequence
live-target-mutatingapply --artifact --mode copy, only after approval
# read-only: confirm the states and preview the bundle plan
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json
skill-suitcase diff --source "$SRC" --target codex --codex-home "$HOME/.codex" --json
skill-suitcase pack --source "$SRC" --target codex --codex-home "$HOME/.codex" --dry-run --json

Approval

Apply runs only after explicit approval naming the target, the skills the bundle plan writes, and the install mode, encoded below as --mode copy; a symlink install needs its own approval and --mode symlink. Ordinary missing and behind writes are rebuilt from current catalog source, so a bundle staged before approval is not byte-for-byte authorization: the approved sequence re-checks diff, stages a fresh bundle, and applies it immediately.

# read-only: after approval, re-check drift immediately before staging
skill-suitcase diff --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

# staging-only: stage a fresh bundle outside catalog and target roots
OUT="$(mktemp -d "${TMPDIR:-/tmp}/skill-suitcase-pack.XXXXXX")"
skill-suitcase pack --source "$SRC" --target codex --codex-home "$HOME/.codex" --output "$OUT" --json
for ARTIFACT in "$OUT"/.skill-suitcase/artifacts/*/skill-suitcase-bundle.json; do
  break
done

# live-target-mutating: apply the just-staged artifact immediately
skill-suitcase apply --source "$SRC" --target codex --codex-home "$HOME/.codex" --artifact "$ARTIFACT" --mode copy --json

# read-only: verify the target state
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

Evidence. The pack manifest lists source provenance, selected skills, file hashes, and planned target entries; blocked entries appear explicitly. A successful apply returns ok: true and updates the target receipt. Expected refusals include source_untracked_files, unsafe_target_state, artifact_destination_mismatch, and read_only_target.

Verify, then stop. Confirm the selected skills now read current and derive the receipt path as <installRoot>/.skill-suitcase-receipt.json for rollback. Stop and escalate if apply refuses or if pack reports blocked or denied source paths. An ordinary dirty edit is never fixed by apply: route it to recipe 7 or 8. The one exception is the guarded dirty-behind case: a receipt-owned copy install whose receipt hash is behind the catalog while its live receipt-owned files still match the last install may take this same staged route, because matching per-file artifact hashes prove apply overwrites nothing unrecorded. repair and import-target refuse that diverged-catalog state, so routing it to recipes 7 or 8 is a dead end.

5. Exact unreceipted match adopted with track

Select when status reports unknown for a catalog-planned skill and diff proves the live directory already matches catalog source exactly. Exact unknown matches route to track, which writes only a receipt and never rewrites skill files.

read-onlystatus, diff as the preview evidence
live-target-mutatingtrack writes the receipt file, only after approval
# read-only: track has no --dry-run flag, so diff is its preview
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json
skill-suitcase diff --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

Approval

Track mutates the target receipt on invocation. Run it only after explicit approval naming the target and the exact --skill list, with the diff evidence showing every planned file for those skills as unchanged.

# read-only: after approval, re-run the exact-match evidence; stop if it changed
skill-suitcase diff --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

# live-target-mutating: track immediately after the matching re-check
skill-suitcase track --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --json

# read-only: verify the adopted install
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

Evidence. Before track, the diff for the selected skill must return ok: true and contain no create, update, extra, or missing file action. After a successful ok: true track, the skill reads current and the receipt records the adopted install. A failed exact-match contract returns structured ok: false JSON without adopting the files.

Verify, then stop. Confirm current status. Stop if the diff shows any content difference: that is not an exact match, so use recipe 6 instead of forcing a receipt onto mismatched files. Stop if track itself refuses; the refusal names the state that failed its exact-match contract.

6. Receiptless mismatch reconciled from the catalog

Select when status reports unknown for a receiptless catalog-planned skill whose live directory differs from catalog source, and catalog source should replace it. Only this receiptless mismatched-directory case routes to reconcile; receipt-owned drift routes to recipe 7 or 8 instead.

read-onlyreconcile --dry-run
live-target-mutatingreconcile --apply, only after approval
# read-only: inspect the planned replacement and its backup path
skill-suitcase reconcile --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --dry-run --json

Approval

Apply runs only after explicit approval naming the target, the exact skill list, and the outcome: the live receiptless content is replaced by catalog source, with the prior content preserved as rollback state.

# read-only: after approval, repeat the preview; stop if its evidence changed
skill-suitcase reconcile --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --dry-run --json

# live-target-mutating: apply immediately after the matching re-check
skill-suitcase reconcile --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --apply --json

# read-only: verify the reconciled install
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

Evidence. An accepted dry-run returns ok: true with the target and catalog differences plus the backup path template. A successful ok: true apply preserves the prior target as rollback state, installs catalog source, writes the receipt, and verifies status itself; the result names the backup location. An ineligible state returns structured ok: false JSON without replacing the target.

Verify, then stop. Confirm current status and record the backup path. Stop if reconcile refuses because the skill does not meet its receiptless-unknown contract, and stop if the operator wants to keep the live content: keeping receiptless content is a promotion or import decision, not a reconcile.

7. Accidental dirty copy repaired from the catalog

Select when status reports dirty for a receipt-owned copy install and the operator confirms the edit was accidental, so the catalog should win. Dirty means stop and inspect first; accidental dirty routes to repair. A skill whose receipt hash is also behind the catalog while its live files still match the last install is the guarded dirty-behind case and routes through recipe 4's staged apply instead.

read-onlyrepair --dry-run
live-target-mutatingrepair --apply, only after approval
# read-only: inspect hashes, file changes, and the backup plan
skill-suitcase repair --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --dry-run --json

Approval

Apply runs only after explicit approval that names the target, the exact skill list, and the outcome: the live edit is discarded and catalog source wins. Never bulk-repair every target after one mixed status report.

# read-only: after approval, repeat the preview; stop if its evidence changed
skill-suitcase repair --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --dry-run --json

# live-target-mutating: apply immediately after the matching re-check
skill-suitcase repair --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --apply --json

# read-only: verify the repaired install
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

Evidence. An accepted dry-run returns ok: true with receipt and catalog tree hashes, source and live hash evidence in its file entries, and the backup plan. Use that evidence to decide whether an edit was accidental or intentional. A successful ok: true apply backs up the live content, installs catalog source, refreshes the receipt, and verifies current status. An ineligible state returns structured ok: false JSON without discarding the edit.

Verify, then stop. Confirm current status and keep the backup path. Receipt-backed rollback restores the pre-repair dirty content if the replacement was wrong. Stop and switch to recipe 8 if inspection suggests the edit was intentional or the operator is unsure; repair and import-target see the same dirty state, and only the operator knows which outcome is right.

8. Intentional dirty edit imported into the catalog

Select when a receipt-owned, catalog-owned skill reads dirty and the operator confirms the live edit is intentional and should become the repo version. Intentional dirty routes to import-target, the inverse of repair: it moves target content into the catalog through ordinary Git review.

read-onlyimport-target --dry-run
catalog-mutatingimport-target --apply writes catalog files; the Git diff stays uncommitted for review
live-target-mutatingimport-target --apply refreshes the live target receipt, only after approval
# read-only: inspect hashes and the planned repository writes
skill-suitcase import-target --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --dry-run --json

Approval

Apply runs only after explicit approval that names the target, the exact skill list, and the outcome: the live edit is kept and copied into the catalog. A drift report alone must never trigger this import.

# read-only: after approval, repeat the preview; stop if its evidence changed
skill-suitcase import-target --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --dry-run --json

# catalog-mutating + live-target-mutating: apply immediately after the matching re-check
skill-suitcase import-target --source "$SRC" --target codex --codex-home "$HOME/.codex" --skill existing-skill --apply --json

# read-only: verify the target state
skill-suitcase status --source "$SRC" --target codex --codex-home "$HOME/.codex" --json

# read-only: enumerate the imported scope without contents; stop on unexpected or ignored names
git -C "$SRC" status --short --ignored --untracked-files=all -- skills/existing-skill

# read-only: after names pass review, inspect one reviewed non-secret tracked path at a time
git -C "$SRC" diff -- skills/existing-skill/SKILL.md

Evidence. An accepted dry-run returns ok: true with the receipt hash, catalog hash, and target hash plus the planned repository writes. A successful ok: true apply copies the target content into the catalog atomically, refreshes the receipt, and leaves normal uncommitted Git changes; nothing auto-commits. An ineligible or diverged state returns structured ok: false JSON without importing the edit.

Verify, then stop. Confirm the skill reads current and review the catalog diff through the ordinary Git review flow before committing. The scoped status --short --ignored --untracked-files=all listing enumerates every untracked (??) and ignored (!!) names under the selected skill without printing any contents. Check that every repoWrites create action appears as an untracked name, then open only those reviewed, non-secret paths individually. Use the scoped git diff once per reviewed tracked path, substituting that exact path for the example. Never render ignored-file contents into tool output: an ignored name under the imported skill means deliberately excluded state reached the catalog, and that is a stop condition. Stop if review rejects the change. Revert only the imported skill paths listed in repoWrites, including added untracked files, and preserve every unrelated working-tree path. The receipt still records the imported content, so status reads behind or version after that scoped revert and repair will refuse it. Restore the live target from the reverted catalog through recipe 4's fresh pack --dry-run, pack --output, and separately approved apply flow.

9. Upstream-managed source refreshed into the catalog

Select when the task explicitly asks to refresh an upstream-managed skill's catalog source from its pinned provider. This catalog-only upstream lane is deliberately separate from target synchronization: it never installs into a live target, and upstream tooling never writes into managed runtime roots.

read-onlyupstream check, upstream fetch --dry-run (isolated temporary workspace)
catalog-mutatingupstream import --apply writes only the selected skill directory and the upstream lock, only after approval
# read-only: report declarations, then fetch into an isolated temp workspace
skill-suitcase upstream check --source "$SRC" --json
skill-suitcase upstream fetch --source "$SRC" --skill existing-skill --dry-run --json

Approval

Import runs only after explicit approval naming the catalog and the selected skill, for a catalog-only source refresh. It is not approval to touch any live target.

# read-only: after approval, repeat the fetch preview; stop if its evidence changed
skill-suitcase upstream fetch --source "$SRC" --skill existing-skill --dry-run --json

# catalog-mutating: import immediately after the matching re-check
skill-suitcase upstream import --source "$SRC" --skill existing-skill --apply --json

# read-only: enumerate the imported scope without contents; stop on unexpected or ignored names
git -C "$SRC" status --short --ignored --untracked-files=all -- skills/existing-skill .skill-suitcase/upstream-lock.json

# read-only: after names pass review, inspect one reviewed non-secret tracked path at a time
git -C "$SRC" diff -- skills/existing-skill/SKILL.md
git -C "$SRC" diff -- .skill-suitcase/upstream-lock.json

Evidence. Successful inspection and import steps return ok: true. upstream check reports the validated lock declarations and imported lineage; upstream fetch --dry-run reports the file-level diff between the pinned upstream and the catalog. A successful import writes only skills/<name> plus .skill-suitcase/upstream-lock.json and does not auto-commit. Dirty selected catalog source is a refusal, not something import overwrites, and returns structured ok: false JSON without changing the catalog.

Verify, then stop. Finish the lane with ordinary Git review and an explicit commit decision. The scoped status --short --ignored --untracked-files=all listing enumerates every untracked and ignored name under the selected skill and the lock path without printing any contents; confirm the import wrote only the expected paths, then use the scoped git diff once per reviewed tracked path, substituting that exact path for the examples. Open created files only after correlating their names to the import result. Never render ignored-file contents into tool output. Only after that review does normal catalog-to-target synchronization start, as a separate flow through recipes 2 and 4. Stop if the fetched diff looks unexpected for the pinned version, and stop rather than point any upstream installer at a live agent home.

10. Provider-owned read-only target reported and skipped

Select when targets classifies a target as provider-backed and read-only, such as the OpenCode and Pi compatibility surfaces, or when any target-aware flow returns read_only_target. This recipe has no mutation step by design: the boundary is reported and the target is skipped without attempting a write.

read-onlyevery step in this recipe; there is no staging or mutation step
# read-only: report the safety classification and any fallback inventory
skill-suitcase targets --source "$SRC" --json
skill-suitcase status --source "$SRC" --json

Approval

No approval or apply step exists for a provider-owned read-only target. Report and skip it; approval cannot override the provider boundary.

Evidence. Successful discovery returns ok: true, and the targets report carries each target's safety classification. Provider fallback inventory without a catalog assignment appears with statusCount: 0 and no status entries. A custom provider path tied to a catalog assignment may show ordinary status entries while remaining read-only: target-aware materialization and mutation flows refuse it with read_only_target in structured ok: false JSON.

Verify, then stop. Record the provider-owned target as skipped in the session report, with the classification or refusal JSON as evidence. A read_only_target refusal is the correct final state, not an error: never retry with different flags, manual file copies, or path overrides to work around it.

Verify and report

# read-only: the session-closing verification
skill-suitcase status --source "$SRC" --json

Finish every session by reporting the catalog branch and SHA, the target IDs inspected, any live mutations run, final summary counts, receipt or backup paths, and anything skipped. Keep secrets, tokens, and private prompts out of chat, issues, and logs.

For installation and first-run setup, see INSTALL.md. The packaged operator skill owns the agent-facing operating policy behind these recipes.