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:
diff; track has no dry-run flag.
b
reconcile
A receiptless target differs from a catalog-planned skill and catalog source should replace it. The prior target content is preserved as rollback state.
c
repair
A receipt-owned copy install went dirty by accident and the catalog should win. Dirty means stop and inspect first; repair only after explicit approval.
d
prune
A receipt-owned install is no longer assigned to the target. Dry-run returns a plan ID; apply repeats the exact skill list with that ID and quarantines directories transactionally.
e
promote
A brand-new target-created skill should become catalog source. Promote copies it into the catalog and symlinks the original back to it.
f
import-target
An intentional edit to a receipt-owned, catalog-owned skill should return to Git through review. The inverse of repair: it moves target content into the catalog.
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:
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.
import, validate --strict, targets, plan, status, diff, pack --dry-runpack --output into a temporary directory, staged fresh inside the approved sequenceapply --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-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-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.
status, diff, pack --dry-runpack --output into a temporary directory, staged fresh inside the approved sequenceapply --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.
status, diff as the preview evidencetrack 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.
reconcile --dry-runreconcile --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.
repair --dry-runrepair --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.
import-target --dry-runimport-target --apply writes catalog files; the Git diff stays uncommitted for reviewimport-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.
upstream check, upstream fetch --dry-run (isolated temporary workspace)upstream 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-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.