Worktrunk: parallel worktrees, lifecycle control and source analysis
Read Worktrunk approval code: --yes, persistence and failed saves
Walk through approve_command_batch and approval storage to understand why accepting a command is different from remembering that acceptance.
What you will learn
- Begin at approve_command_batch, not at the prompt text
- Persistence is a second decision with its own failure mode
- Follow storage and matching before calling approval exact
Before you start
- Basic Git branches and command-line navigation
- A disposable repository for optional reader exercises
Explain worktree boundaries, verify a first checkout and review configured commands before adopting automation.
Key takeaways
- Accepting execution and persisting approval are different states.
- The batch --yes path does not persist approval.
- Template approval does not pin the code invoked by a template.
Begin at approve_command_batch, not at the prompt text
In src/commands/command_approval.rs, approve_command_batch receives candidate commands, a project identifier, approvals and flags. Unless the caller already filtered the list, it finds commands requiring approval. An empty remaining list returns true immediately. Otherwise --yes accepts for this invocation, or an interactive prompt determines the result; denial returns false.
That boolean alone does not describe the entire caller workflow. The hook documentation states that declining skips all project commands for the operation, including ones previously approved, while continuing without them. Existing saved approvals remain. Reading both the function and its documented integration avoids claiming that a refusal necessarily cancels creation or permanently revokes trust.
Persistence is a second decision with its own failure mode
After acceptance, the function saves approvals only when yes is false. Reload failure propagates as an error; a later save failure instead produces a warning without revoking acceptance for this invocation. Distinguish those two failures as well as rejection, acceptance only now and persisted acceptance. A later prompt can indicate a save problem rather than a new command.
This finding applies to this batch function. Do not extend it to every --yes flag: the dedicated approvals-add command is a different path. An independent teaching model accompanying these manuscripts tests only the simplified decision table. It does not execute upstream Rust, reproduce configuration matching or prove host integration behavior.
Follow storage and matching before calling approval exact
src/config/approvals.rs separates approvals.toml from portable config. load_with_fallback treats an existing approvals file as authoritative; legacy config approval entries are considered only when it is absent. A corrupt existing file is an error, not permission to silently recover older trust decisions. Mutations acquire a configuration lock, reload and save through the storage helper.
is_command_approved compares normalized template variables across matching project keys. Manually authored pattern keys can therefore cover more than one repository; matching is not simply raw string equality under an exact repository key. Saved templates also do not freeze the future contents of scripts they invoke. The source includes normalization and pattern tests, but we inspected rather than executed those Rust tests.
Decision guide
| Criterion | Option A | Option B |
|---|---|---|
| Best when | You need predictable behavior and easy auditing | You need adaptive optimization and have reliable telemetry |
| Main risk | May leave performance on the table | Can become difficult to explain or debug |
Implementation steps
- 1
Trace the empty-list, denial and acceptance branches.
- 2
Locate the separate persistence condition.
- 3
Check storage fallback and matching project keys.
- 4
Label teaching-model tests separately from upstream runtime tests.
Copy-ready example
// Independent teaching model, not upstream Worktrunk code.
function decision({ needed, yes, accepted, loadOk, saveOk }) {
if (!needed) return { run: true, save: false, warning: false };
if (!yes && !accepted) return { run: false, save: false, warning: false };
if (!yes && !loadOk) throw new Error("reload failed");
return { run: true, save: !yes && saveOk, warning: !yes && !saveOk };
}Frequently asked questions
Why am I prompted again after accepting?
Possibilities include changed commands or a failed save; inspect the warning and actual approval source before deciding.
Does clearing one exact project always eliminate inherited approvals?
Not necessarily. A manually authored matching pattern entry can still supply approval.
Sources
- Worktrunk / src/commands/command_approval.rsSource checked 2026-09-14
- Worktrunk / src/config/approvals.rsSource checked 2026-09-14
- Worktrunk / docs/src/content/docs/hook.mdSource checked 2026-09-14