Use for Tilde deployment, provisioning, and home-management work. Trigger for Tilde prompt markers such as `/tilde ...` or `$tilde ...`, `~ ...`, messages whose first word is `tilde`, or requests to create, initialize, deploy, update, diagnose managed state, adopt app/config files, customize data-layer policy, or work with Tilde home behavior.
Resources
9Install
npx skillscat add roktas/tilde Install via the SkillsCat registry.
Tilde
Tilde is a standalone agent skill and control plane. The canonical contract is
Specification. Read that file before changing behavior or executing nontrivial Tilde
work. The .agents/specs/tilde.md path is only a symlink to the same canonical file.
The provisioning model is:
- Desired state comes from the current committed public/private home repositories.
- The only persistent Tilde state file is
~/.local/state/tilde/state.yml. state.ymlstores repository bindings and the last fully converged commit anchors.- Planning evaluates current manifests and targeted live facts on every run.
- Every run compares current desired manifests with targeted live checks and idempotent actions.
- Failed, conflicted, or deferred runs do not advance
appliedanchors. - The host-local lock is process coordination, not cleanup state.
When existing docs, code, habits, or memory conflict with references/specification.md, the specification wins.
Agent Quickstart
When the user invokes a Tilde prompt such as /tilde <command> [target] [qualifiers...]:
- Treat
/tilde,$tilde, and similar Tilde prompt markers as agent prompt contracts. They are not shell commands. - Resolve the controller-side runtime entrypoint before shell execution. Use the loaded skill directory's
bin/tilde;
if that path is not available, use~/.agents/skills/tilde/bin/tilde. Do not rely on baretildebeing onPATH. - Identify the target: current host, the current host name,
host,ssh:host, or an all-caps host group such asALL,HOME, orWORK. - Classify the command from the Command Reference below.
- Map agent-orchestrated commands to the Workflow Matrix before running helpers.
- For remote targets, run the Remote Freshness Preflight before reading target state, generating plans, or applying
results. - Use the resolved runtime entrypoint for script delivery. Generate plans, run live checks, and apply results on the
target host, not on the controller. - Keep proposal-first behavior for destructive, preference-sensitive, privilege-requiring, or remote mutations.
- After a successful mutating remote apply, run a cheap final status read on the target before closeout.
- After the run, summarize successful, changed, deferred, conflicted, and failed work.
If a direct runtime call says a command is agent-orchestrated, stop trying shell variants of that command. Load this
skill, classify the command, and run the appropriate agent workflow.
Do not execute /tilde ... or $tilde ... in a terminal. /tilde is a prompt marker, and $tilde may be a Codex skill
trigger or shell variable expansion depending on the environment. Shell execution uses the resolved runtime entrypoint,
written as "$TILDE" in examples:
TILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
"$TILDE" help
"$TILDE" ssh spinozaIf the skill was loaded from another directory, set TILDE to that directory's bin/tilde instead. If a controller-sidetilde command is not found, do not search the filesystem for it; switch to the resolved runtime entrypoint.
For remote targets, the first target-state read must happen on the target through Tilde SSH transport. Do not
inspect the controller's ~/.local/state/tilde/state.yml to discover a remote host's repository bindings, applied
anchors, level, platform, or bootstrap state. That file belongs only to the controller host.
Target Grammar
Host-aware prompt commands accept these target forms:
- no target: current host, also called localhost.
host: remote host shorthand forssh:host, except when it names the current host.ssh:host: explicit remote host target.GROUP: a bare all-caps host group defined by the active home policy, such asALL,HOME, orWORK.
If a bare host token equals the current host name, such as hostname -s, treat it as the current host and run the local
workflow. Use explicit ssh:host only when the user really wants SSH transport to that host, including self-SSH.
The host-aware prompt commands are deploy, update, repair, upgrade, align, status, and doctor.
Commands with their own subject syntax, such as adopt APP_OR_PATH, clean SUBJECT, organize SUBJECT, create, andinit, do not treat a bare argument as a host unless the command's own policy says so.
Bare all-caps targets are not hostnames. Expand them from the active ~/AGENTS.md home policy before running any remote
work. Group expansion applies only to unprefixed target tokens; ssh:host is always an explicit host target. If the
policy does not define the requested group, ask the user for the host list. For explicitly requested configured groups,
report the expanded host list and continue; the prompt itself is consent to run the requested workflow. Ask only when
the group is undefined, ambiguous, unexpectedly expands outside active policy, or a later step requires separate
explicit confirmation. Run each host as a separate target workflow and report per-host results.
When traversing a host group, perform a bounded noninteractive reachability check before each host workflow. Skip
unreachable hosts and continue with the remaining hosts. Report skipped hosts separately; an unreachable host inside a
group is not a failure for reachable hosts. If every expanded host is unreachable, stop with a clear deferred result.
Use Tilde SSH transport for the reachability check itself. Do not use raw ssh, even for this cheap probe.
TILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
host=spinoza
"$TILDE" ssh -o BatchMode=yes -o ConnectTimeout=5 "$host" << 'SCRIPT'
printf 'ok\n'
SCRIPTRuntime
The PATH-visible runtime surface is bin/tilde, bin/sudo, and helper commands intentionally exposed in the Tilde
runtime PATH. Normal command implementations live in libexec/ and are dispatched through bin/tilde.
Controller-side runtime calls must go through the resolved entrypoint. Bare tilde is valid only when the Tilde runtime
PATH is already active, such as inside a remote script delivered by Tilde SSH transport.
bin/tilde has direct runtime routes for helper and diagnostic commands. It intentionally refuses agent-orchestrated
prompt commands; those commands are interpreted and orchestrated by the loaded Tilde skill.
Implementation routes such as plan and apply are stable primitives for agent workflows. They are not user-facing
prompt commands. Use them only inside the workflow patterns in this skill and the specification.
Command Reference
Commands are grouped by execution model.
Agent-Orchestrated Prompt Commands
The agent interprets these high-level commands and orchestrates the workflow. They have no direct runtime route.
deploy: prepare a local or remote host and apply desired state.update: run explicit update behavior from the current desired state.repair: apply the current desired state again; it does not read a repair queue or module-level state.upgrade: run the widest explicitly requested upgrade path.adopt: inspect a requested app, config, package, or path and propose public/private repository placement.create,init,clean, andorganize: follow the specification and user policy; keep destructive or
preference-sensitive work proposal-first.
Treat dry-run and plan-only as qualifiers. Do not invent a separate stateful planning model for them.
Direct Runtime Commands
These commands have direct bin/tilde runtime routes. For remote targets, deliver the command through
Tilde SSH transport so the target host supplies live facts.
help: show public commands or one command's usage.doctor: diagnose state, repository, target, and managedness problems without executing module code or mutating
targets.handoff: copy and print the privilege handoff command for the local or remote host.status: show compact repository bindings and last fully converged anchors.
Dual Command
align: local link, copy, seed, and reset reconciliation can run directly through the resolved runtime entrypoint.
Remote align remains agent-orchestrated: use/tilde align spinozaprompt semantics and deliver the target-side
workflow through Tilde SSH transport. Do not runtilde align --format jsonon a remote target;alignhas no--formatoption.
Implementation Routes
ssh: deliver scripts to a remote host with the Tilde runtime environment active.plan: produce a plan from current committed repository content and target-host facts.apply: apply one or more plan files.sudo: classify privilege needs and support handoff.boot,checkout,preflight, andsmoke: specialized runtime helpers.
Do not present implementation routes as user-facing Tilde prompt commands. When using them on the controller, use the
resolved runtime entrypoint with undotted route names such as "$TILDE" plan and "$TILDE" apply.
Workflow Matrix
Agent commands map to planning modes and module sections as follows.
| Prompt command | Plan mode | Module behavior |
|---|---|---|
deploy |
apply |
Prerequisites, Install, Post Install when Install changed, then Configure |
update |
refresh; repair for state recovery |
Prerequisites, Update, then Configure; state recovery uses repair behavior |
repair |
repair |
Prerequisites, Install, Post Install when Install changed, then Configure |
upgrade |
upgrade |
broad refresh behavior, then package upgrades |
align |
align |
directories, links, copies, seeds, and resets only; no bootstrap, packages, or module code |
create, init, clean, organize, and adopt are proposal-first operator workflows unless a direct helper is
explicitly documented for the requested step.
The prompt command is update; the ordinary plan mode is refresh. Do not call or invent --mode update. If target
status shows missing state.yml or no applied anchors, recover state with plan --mode repair for that run; do not
use refresh merely to recreate state, and do not tell the user to run deploy solely to initialize state. Missingstate.yml is not proof that the host was never deployed; describe it as missing state and state recovery unless
bounded evidence proves otherwise.
When an agent workflow materializes plan JSON files, create them under a per-run temporary directory and remove that
directory at exit. Do not write fixed plan paths such as /tmp/opencode/HOST-public.json, and do not leave plan files
behind after tilde apply:
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
"$TILDE" plan --mode refresh --repo ~/Dropbox/home --host newton --format json > "$tmpdir/public.json"
"$TILDE" apply --plan "$tmpdir/public.json"refresh mode does not advance applied anchors. After a successful /tilde update, final status may show target
repository HEAD ahead of the stored applied anchor; report that as expected refresh behavior, not as anchor advancement
and not as failed convergence.
Remote Script Execution
Remote Freshness Preflight
Remote Freshness Preflight is the first remote workflow step. Before any remote status, plan, apply, or
state-recovery decision, confirm that the target runtime is current. The controller-side loaded skill is the source for
the expected Tilde runtime commit.
For mutating remote prompt workflows such as deploy, update, repair, upgrade, and remote align:
- Compare the controller runtime commit with the target
~/.agents/skills/tildecommit before the first target-state
read. - If the target runtime is a stale Git checkout, refresh it from the controller checkout with
"$TILDE" checkout remote
before running targettilde status,tilde plan, ortilde apply. - If the target runtime is stale but cannot be refreshed safely because it is dirty, missing, not a Git checkout, or
otherwise ambiguous, stop withdeferred. Do not continue into state recovery with the stale runtime. - On Git-backed remote hosts, also refresh the target public/private desired-state checkouts from the controller
repositories before planning. If a target checkout is dirty or cannot be fast-forwarded from the controller bundle,
stop withdeferred. - On Dropbox-backed remote hosts, do not replace synced repositories with controller bundles; report stale or unsynced
target checkouts asdeferredunless the user asks for a Git-backed checkout refresh.
When using checkout remote, always pass the complete source and target binding. For target runtime freshness, the
controller repository is the loaded skill root and the target is ~/.agents/skills/tilde:
TILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
controller_runtime=${TILDE%/bin/tilde}
"$TILDE" checkout remote --host spinoza --repo "$controller_runtime" --target ~/.agents/skills/tildeFor Git-backed desired-state checkout freshness, pass the selected controller data repository and the target checkout:
"$TILDE" checkout remote --host vps --repo ~/Dropbox/home --target ~/.local/src/homeDo not call checkout remote with only --host; the route cannot infer which controller repository maps to which
target checkout.
For read-only remote workflows such as status and doctor, detecting a stale target runtime is a reportable stale
runtime condition, not permission to mutate the remote host. Report it and stop unless the user requested repair or
update behavior.
A status output that mentions state paths outside this specification, such as ~/.local/state/tilde/config.yml or~/.local/state/tilde/hosts/HOST/state.md means the target runtime is stale. Stop and refresh or report the stale
runtime before any plan --mode repair step. Do not describe state writes outside the state.yml model as successful
state recovery.
Use the resolved runtime entrypoint for all remote script execution:
TILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
"$TILDE" ssh spinozaIt sets up the correct Tilde runtime environment (locale, non-secret ~/.config/environment.d/*.conf values, PATH,
TILDE_ROOT, TILDE_SUDO) and delivers the script body through ssh host sh -s --.
Do not use raw ssh, sh -c, or bash -lc for multi-command remote work.
These bypass Tilde's PATH setup and sudo interceptor.
After the Remote Freshness Preflight, plan and execute on the target host. Platform detection, package inventory,
repository bindings, and live checks come from the target. A controller-side plan for a remote host is invalid.
After the target runtime is known current, read target status in JSON when a workflow needs repository bindings:
TILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
"$TILDE" ssh spinoza << 'SCRIPT'
tilde status --format json
SCRIPTUse the returned state.public and state.private paths exactly when generating target-local plans. Do not hard-code~/Dropbox/home, ~/Dropbox/home-, ~/.local/src/home, or ~/.local/src/home- unless those paths came from target
status, explicit user arguments, or active home policy for a stale Git-backed target that must be refreshed before
status can be trusted.
For Git-backed targets whose status binds repositories outside Dropbox, controller-side ~/Dropbox/... source paths are
not target paths. Do not create target-side ~/Dropbox for repository binding, cleanup, shared app state, or convenience
paths unless active target policy explicitly says that host has Dropbox-backed storage.
When a remote workflow needs repository bindings in a target-side script, bind them from target status JSON before
planning. Do not retype host-convention paths in tilde plan --repo ... commands:
TILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
"$TILDE" ssh spinoza << 'SCRIPT'
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
status_json=$tmpdir/status.json
tilde status --format json > "$status_json"
public=$(ruby -rjson -e 'data = JSON.parse(File.read(ARGV[0])); print data.dig("state", "public").to_s' "$status_json")
private=$(ruby -rjson -e 'data = JSON.parse(File.read(ARGV[0])); print data.dig("state", "private").to_s' "$status_json")
if [ -z "$public" ]; then
printf '%s\n' 'STATUS_FAILED: missing state.public' >&2
exit 1
fi
SCRIPTRemote state is target-local. For /tilde update spinoza, /tilde update ssh:spinoza, /tilde deploy spinoza,/tilde deploy ssh:spinoza, /tilde doctor spinoza, /tilde status spinoza, and /tilde align spinoza, do not read the controller's~/.local/state/tilde/state.yml. If state or repository bindings are needed, read them on the target:
TILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
"$TILDE" ssh spinoza << 'SCRIPT'
tilde status --format markdown
SCRIPTTILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
"$TILDE" ssh spinoza << 'SCRIPT'
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
status_json=$tmpdir/status.json
mode=refresh
# Use mode=repair when target status reports missing state.yml or no applied anchors.
tilde status --format json > "$status_json"
public=$(ruby -rjson -e 'data = JSON.parse(File.read(ARGV[0])); print data.dig("state", "public").to_s' "$status_json")
private=$(ruby -rjson -e 'data = JSON.parse(File.read(ARGV[0])); print data.dig("state", "private").to_s' "$status_json")
if [ -z "$public" ]; then
printf '%s\n' 'STATUS_FAILED: missing state.public' >&2
exit 1
fi
tilde plan --mode "$mode" --repo "$public" --host spinoza --format json > "$tmpdir/public.json" || exit $?
if [ -n "$private" ]; then
tilde plan --mode "$mode" --repo "$private" --host spinoza --format json > "$tmpdir/private.json" || exit $?
tilde apply --plan "$tmpdir/public.json" --plan "$tmpdir/private.json"
else
tilde apply --plan "$tmpdir/public.json"
fi
SCRIPTReplace repository paths with the target host's configured bindings. Omit the private plan when the target has no
private repository.
For remote align, use the same target-local plan/apply shape with mode=align. Do not call the target's directtilde align route for a remote prompt workflow, and do not suppress diagnostics:
TILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
"$TILDE" ssh spinoza << 'SCRIPT'
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
status_json=$tmpdir/status.json
mode=align
tilde status --format json > "$status_json"
public=$(ruby -rjson -e 'data = JSON.parse(File.read(ARGV[0])); print data.dig("state", "public").to_s' "$status_json")
private=$(ruby -rjson -e 'data = JSON.parse(File.read(ARGV[0])); print data.dig("state", "private").to_s' "$status_json")
if [ -z "$public" ]; then
printf '%s\n' 'STATUS_FAILED: missing state.public' >&2
exit 1
fi
tilde plan --mode "$mode" --repo "$public" --host spinoza --format json > "$tmpdir/public.json" || exit $?
if [ -n "$private" ]; then
tilde plan --mode "$mode" --repo "$private" --host spinoza --format json > "$tmpdir/private.json" || exit $?
tilde apply --plan "$tmpdir/public.json" --plan "$tmpdir/private.json"
else
tilde apply --plan "$tmpdir/public.json"
fi
SCRIPTEvery remote Tilde step must treat a non-zero exit as failed, deferred, or conflicted, even when stdout is empty. Do not
redirect stderr to /dev/null for remote status, doctor, checkout, plan, apply, align, or verification
work. A later successful status read does not turn a failed plan, apply, checkout, or align step into success.
After a successful mutating remote apply, verify target convergence with a separate cheap status read so the apply JSON
stays easy to parse:
TILDE=${TILDE:-"$HOME/.agents/skills/tilde/bin/tilde"}
"$TILDE" ssh spinoza << 'SCRIPT'
tilde status --format markdown
SCRIPTUse target HEAD or target current commit for the repository commit currently checked out on the remote host. Useapplied anchor for the commit recorded under applied in the target's state.yml. Do not call a remote repository
HEAD local in summaries; that word is ambiguous from the controller.
Inside the remote script body, bare tilde is valid because Tilde SSH transport places the target runtime directory at
the front of PATH.
Remote scripts must be sh-compatible by default. Use the plain "$TILDE" ssh HOST << 'SCRIPT' form for ordinary
status, plan, apply, and verification work. Do not pass -- bash for macOS targets or for scripts that can run undersh. Passing an interpreter after -- is reserved for a verified non-macOS target and a script that truly needs syntax
absent from sh.
In the default remote form, sh-compatible means POSIX sh, not "Bash without a Bash shebang." Do not use the Bash
skill prelude in these remote scripts. Avoid common Bashisms:
| Bashism | POSIX sh form |
|---|---|
set -o pipefail |
avoid critical pipelines; check each command separately |
[[ -n $x ]] |
[ -n "$x" ] |
[[ $x =~ re ]] |
case $x in pattern) ... ;; esac |
arr=(a b) and ${arr[@]} |
newline-separated strings, positional parameters, or repeated commands |
source file |
. file |
function name { ...; } |
name() { ...; } |
local x=..., declare, mapfile, readarray, select |
simple variables and explicit loops |
< <(cmd) and <<< "$text" |
temp files, pipes, or here-documents |
{foo,bar} brace expansion |
spell out the words |
$'...\n...' strings |
printf '%s\n' ... |
Also remember that dash is a common /bin/sh; scripts that only work in Bash are not acceptable in the plain remote
form.
Use "$TILDE" ssh --tty spinoza for interactive remote sessions that require a pseudo-terminal (e.g., sudo password
prompts).
Module Model
Module README code sections use this stateless contract:
Prerequisites: external requirements and read-only checks only. Failed checks returndeferred.Install: idempotently ensure packages, tools, directories, applications, repositories, or local resources are present.Post Install: runs only whenInstallchanged something in the current run; correctness must not depend on it.Configure: idempotent desired configuration and drift repair.Update: explicit refresh or upgrade work.Notes: informational only; never executed.
Code blocks must be idempotent or guarded. If an effect cannot be detected from live target facts, represent it as a
prerequisite, note, or explicit proposal-first operator action.
Package managers are the source of truth for package presence. Package inventories are ephemeral, in-memory
optimizations only.
Module code blocks call Tilde helpers by their plain names, such as line and span. Privileged helper calls use the
same plain form with sudo, such as sudo line ....
README frontmatter may declare directories along with links, copies, seeds, resets, and packages. Link
sources are repository-relative by default. A source that starts with ~/ or / is a target-home source and must stay
inside the target home. Use target-home sources for live home paths such as Dropbox-backed shared state directories and
XDG entrypoints; Dropbox-to-Dropbox symlink values must be relative to the target directory.
Safety
Proposal-first behavior is required before replacing unmanaged files, removing stale links or managed spans, backing up
conflicting paths, uninstalling packages, running non-idempotent code, requiring privileges, mutating remote hosts, or
performing one-off operator workflows.
Managedness must be proven before destructive cleanup. No proof means no destructive mutation.
When apply reports conflict, stop after reporting the exact conflicted targets and wait for an explicit user response
before replacing files, forcing links, copying repository versions over targets, or rerunning apply. Do not infer consent
from silence, from the fact that a conflict exists, or from the agent's own question. Conflict resolution may reveal
additional conflicts in a later apply; repeat the same explicit-approval step for each newly revealed conflict.
If the user chooses repository replacement for a conflict, back up the exact target when practical, remove only the exact
conflicting target, and rerun apply so the normal declarative action recreates it. Do not rerun apply expecting a
default conflict result to overwrite the target. Broad cleanup, such as clearing an entire font directory after
package-manager file conflicts, requires a separate explicit approval that names the directory and effect.
If an apply command times out or reports state lock busy, do not delete the lock file as a first response. A lock held
with flock belongs to a process, and removing the pathname does not prove the process stopped. Treat this asdeferred or an active-run check: use bounded read-only process inspection on the target, wait when a Tilde/apply
process is still active, and remove a leftover lock only after proving no process holds it and getting explicit approval.
If sudo is required and noninteractive sudo fails, report deferred and present:
"$TILDE" handoff --host spinoza --copyRun the helper on the controller, not inside "$TILDE" ssh. The helper prints one shell-neutral command line and
reports whether it copied the command to the controller clipboard. The command must work when pasted into bash, zsh, or
fish. Copy or present the Handoff command: line exactly; do not rewrite it, split it into a TILDE=... assignment
plus an ssh command, replace the remote path with a local variable, or add extra quoting. Wait for the user to run it,
then rerun the affected action and verify cleanup. Do not collect passwords in chat.
Weak-Model Guardrails
For weaker or low-context agents:
- Plan from current committed repositories and targeted live facts.
- Treat
Prerequisitesas read-only checks. - Advance
appliedanchors only after every required action succeeds. - Keep
appliedanchors unchanged afterdeferred,conflict, ornotok. - On timeout or
state lock busy, do not remove~/.local/state/tilde/lockblindly. Check whether an apply process is
still active and treat uncertainty asdeferred. - Never plan a remote host from the controller.
- For mutating remote workflows, verify target Tilde runtime freshness and Git-backed desired-state checkout freshness
before the first targetstatus,plan, orapply. - Do not run target
tilde status,tilde plan, ortilde applythrough a stale target runtime. Refresh the target
runtime first, or stop withdeferredif it cannot be safely refreshed. - Never read controller
~/.local/state/tilde/state.ymlfor a remote target. - Never execute
/tilde ...or$tilde ...in a shell; they are prompt markers, not runtime commands. - Before any controller-side runtime call, resolve
TILDEto the loaded skill directory'sbin/tilde, falling back to~/.agents/skills/tilde/bin/tilde. - If bare
tildeis not found on the controller, do not search for it; rerun through"$TILDE". - Do not run agent-orchestrated prompt routes through
"$TILDE":deploy,update,repair,upgrade,adopt,create,init,clean, andorganize.alignis a direct route only for current-host reconciliation; remote
align is still a prompt workflow. - For remote
/tilde align HOST, do not runtilde align --format jsonon the target. Read target bindings on the
target, then runtilde plan --mode align --format jsonfor each target repository andtilde applythe plan files. - Never redirect remote Tilde stderr to
/dev/null. Non-zero remote exit status means the step failed, deferred, or
conflicted, even when the tool output pane says(no output). - A final status read is verification only; it does not make an earlier failed remote
checkout,plan,apply, or
align step successful. - Call
checkout remoteonly with the complete mapping:--host HOST --repo CONTROLLER_REPO --target TARGET_REPO.
Do not expect it to infer target paths from--host. - Use the
plan --mode refreshimplementation route for theupdateprompt command only when target status has
existingappliedanchors. If target status shows missingstate.ymlor noappliedanchors, useplan --mode repairfor state recovery. - For host-aware prompt commands, omitted target means current host. Treat bare
hostasssh:host, except when it
names the current host; then run the local workflow. Use explicitssh:hostto force SSH transport. - Missing
state.ymlor missingappliedanchors means state recovery, not proof that the host was never deployed. - Remote status paths outside the
state.ymlmodel, such asconfig.ymlorhosts/HOST/state.md, mean stale target
runtime, not successful current state recovery. - Treat bare all-caps targets such as
ALL,HOME, andWORKas home-policy host groups, not hostnames. For defined
groups, report the expanded host list and continue; do not ask for permission to start the exact requested workflow.
Ask only if the active home policy does not define the requested group, the expansion is ambiguous, or it unexpectedly
leaves active policy. - When traversing a host group, skip unreachable hosts after a bounded reachability check and continue with reachable
hosts. Run this check through"$TILDE" ssh; do not use rawsshfor Tilde remote workflow probes. - After remote runtime freshness is verified, read
tilde status --format jsonon the target, bind the returned
repository paths to shell variables, and use those variables for remote plan paths. Do not guess Dropbox or
Git-backed checkout paths when status or explicit policy can supply them. - Do not create
~/Dropboxon a target just because controller source repositories live under Dropbox, an example uses~/Dropbox/home, or post-update cleanup mentions Dropbox. If target status binds Git-backed repositories under~/.local/src, use those paths and skip Dropbox-only maintenance when~/Dropboxis absent. - Use
mktemp -dandtrapfor all local and remote plan JSON files. Do not write fixed/tmp/opencode/...plan
paths or leave generated plan files behind after apply. - Do not say
appliedanchors advanced after a successfulrefresh/updaterun. Refresh mode does not write anchors;
distinguish target HEAD from applied anchors in final status. - Keep remote scripts
sh-compatible unless Bash is explicitly required and the target is known to support it. Do not
use Bash-only options such asset -o pipefailin the default"$TILDE" ssh HOST << 'SCRIPT'form. - In default remote scripts, avoid Bashisms such as
[[ ]], arrays,source,local,pipefail, process
substitution, here-strings, brace expansion,mapfile, and$'...'strings. - Do not pass
-- bashfor macOS targets or ordinary remote status, plan, apply, and verification scripts. - Treat package-manager metadata refresh warnings that report incomplete indexes, signature failures, or missing
repository keys asdeferred; do not summarize them as successful package refreshes. - After a successful mutating remote apply, run a final target status read before the final answer.
- In remote summaries, distinguish
target HEADfromapplied anchor; do not call the target repository commitlocal. - After an apply conflict, stop and wait for an explicit user answer before copying repo files over targets, forcing
links, or rerunning apply. Newly revealed conflicts need a new explicit answer. - After the user chooses repository replacement for a conflict, back up and remove only the exact conflicted target, then
rerun apply. Broad directory cleanup needs a separate explicit approval. - Run
"$TILDE" handoff --host HOST --copyon the controller after sudo deferral. Do not run handoff through"$TILDE" ssh, do not invent a two-lineTILDE=...command, and do not rewrite the printedHandoff command:. - Remove packages, file materializations, files, or spans only with explicit managedness proof and proposal-first
confirmation. - Prefer a safe
deferredorconflictresult over guessing.