The distill wrapper (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/cad0p/pi-napkin/llms.txt
Use this file to discover all available pages before exploring further.
extensions/distill/scripts/distill-wrapper.sh) reads a set of environment variables that exist solely to make integration tests deterministic. Production code never sets any of them — they are absent from normal pi-napkin installations by design. If you find one of these variables set in your environment by accident, unset it and re-run: the wrapper detects each variable’s absence and falls back to its normal production behavior automatically. This page documents them for maintainers and contributors so that a future-you grepping for one of these names can land here.
Environment Variables
| Variable | Purpose |
|---|---|
NAPKIN_DISTILL_PI_BIN | Override the pi binary path. Integration tests point this at a bash stub that simulates a specific agent behavior class (clean-distill, conflict-resolve-clean, agent-timeout, etc.) so the wrapper completes its full lifecycle without contacting a real LLM. |
NAPKIN_DISTILL_SKIP_PI=1 | Skip both the napkin shim install and the pi invocation. Used by tests that pre-stage file changes manually and only want to exercise the wrapper’s lifecycle (validation, salvage, sidecar emission) without running an agent at all. |
NAPKIN_DISTILL_NO_RECURSE=1 | Exported by the wrapper into the agent’s environment so a nested pi invocation won’t auto-distill recursively. Tests sometimes set it directly to suppress recursion when invoking the wrapper from inside another distill. |
NAPKIN_DISTILL_HALT_AFTER_META=1 | Halt right after rewriting meta.json’s pid field to the wrapper’s pid. Lets tests inspect the updated meta without the cleanup trap wiping the worktree. Clears the EXIT trap so cleanup is skipped — the caller is responsible for tearing down the worktree afterwards. |
NAPKIN_DISTILL_HALT_AFTER_SHIM=1 | Halt right after the per-distill napkin shim is installed at <worktree>/.napkin/distill/bin/napkin. Lets tests inspect the shim contents and PATH injection without the cleanup trap firing. |
NAPKIN_DISTILL_FORCE_CLEANUP=1 | Force the salvage / cleanup path to run unconditionally, even on success. Used to test salvage idempotency. Unlike the HALT_AFTER_* hooks, this does not clear the EXIT trap — cleanup fires normally and tests assert on the post-cleanup filesystem state. |
NAPKIN_DISTILL_TIMEOUT_KILL_GRACE_SECS=<n> | Override the timeout(1) -k grace window (production default: 30 seconds) — the delay between SIGTERM and SIGKILL when the agent exceeds distill.maxDurationMinutes. Used to keep timeout tests fast; a SIGTERM-ignoring stub can be tested without waiting the full 30-second grace period. |
End-to-End Test (bun run verify:e2e)
The e2e gate (scripts/verify-e2e.ts) verifies that the full production pipeline — JS-side session handler, bash wrapper subprocess, real setInterval poller — walks cleanly together. It is manual-only and is not run in CI. Cost is roughly $0.50 per LLM-driven variant.
The script:
- Creates a fresh tmpdir vault via the real
napkin initCLI. - Drives the production
session_starthandler so auto-init’sgit init, managed.gitignoreblock installation, and initial commit all run end-to-end. - Triggers the
/distillcommand handler with a real wrapper subprocess and a real 2-secondsetIntervalpoller. - Asserts post-conditions: no conflict markers in tracked
*.md, HEAD on the default branch, agent’s squash commit landed, distill branch removed, worktree removed, outcome sidecar with classmerged-content, andorigin/<default>advanced.
--variant <name> (or --all) to exercise the full-level health-check decision points end-to-end — including a loud-error path (config-outside-block, which aborts before LLM dispatch and costs nothing) and an auto-recover path (orphaned-worktree, which prunes the orphan and then runs the full LLM-driven distill).
0 on PASS, 1 on FAIL.
The e2e test is the only gate that exercises the wrapper↔JS-poller seam — where worktree teardown and outcome-write race — which is invisible to a prompt-only harness or unit tests that mock the spawn. Run it after any change to the wrapper, the poller loop in
index.ts, or the workspace lifecycle in distill-workspace.ts.Available Test Stubs
The stub scripts live inextensions/distill/test-fixtures/agent-stubs/. Each is a self-contained bash script that reads state from NAPKIN_STUB_* environment variables and produces the filesystem effects of a specific agent behavior class, without contacting a real LLM.
Point the wrapper at a stub via NAPKIN_DISTILL_PI_BIN=<path-to-stub>.
Behavior class stubs
| Stub | Simulated behavior | Expected outcome |
|---|---|---|
clean-distill.sh | Commits one file directly on the default branch | merged-content |
no-distill.sh | Exits 0 without producing any commits | no-content |
conflict-resolve-clean.sh | Commits to the distill branch, merges the default branch with a conflict, resolves it cleanly, then squashes | merged-content |
conflict-leave-markers.sh | Commits a file containing all three conflict marker types to the default branch | failed:markers-after-agent-exit |
agent-crashes.sh | Exits non-zero with diagnostic output on stderr | failed:agent-exit-nonzero |
agent-timeout.sh | Sleeps past maxDurationSecs (tests pass a very low budget) | failed:agent-timeout |
push-fail-merged-local.sh | Commits without pushing while an origin remote exists | merged-local |
push-fail-pull-merge-success.sh | First push fails because origin advanced; agent recovers via git pull --no-rebase and re-pushes | merged-content |
pushed-success.sh | Full happy path: commits and pushes to origin (requires test-side origin setup) | merged-content |
multiple-commits-on-main.sh | Commits two or more times directly on the default branch (squash-invariant violation, accepted) | merged-content |
squash-skipped.sh | Commits to the distill branch but never squashes to the default branch | no-content |
Race-window scaffolding stubs
These two stubs are used bywrapper-invariant.test.ts to widen the [agent-exit, worktree-removed] time window so the test can snapshot the outcome sidecar at the exact moment the worktree disappears.
| Stub | Behavior |
|---|---|
step10-race.sh | Happy-path commit + squash, then sleep 0.5 to widen the cleanup window |
salvage-race.sh | Commits a file with conflict markers in vault *.md, then sleep 0.5 |