Dry-run by default, and plan → review → apply flows
State: open/active · Genre: analysis
Use case: dry-run-plan-apply RFC mapping: cross-cutting (dry-run); partially new
Source: cross-repo survey of sksizer/* (2026-07-05), companion to the workflow-constellation
RFC (PR #682).
The need
Section titled “The need”Dry-run-default with an —execute flag is re-implemented in essentially every orchestration script in the fleet, each hand-formatting its own preview and threading if(!dryRun) around every mutation site — with visible drift between what the dry-run shows and what execute does. Several tools extend the same need into a two-phase plan → human review → apply flow with a persisted plan artifact between the phases.
Evidence
Section titled “Evidence”rust-agent-adapt
Section titled “rust-agent-adapt”All 13 scripts (and their 6 fan-out drivers) parse —execute and default to dry-run, each hand-writing its own preview of prompt/runner/tools/command; quality_check.sh’s dry-run runs the checks but previews the fixer.
Where: scripts/bring_up_to_date.sh · scripts/quality_check.sh · justfile
Pain: N copies of an if/else that drift in detail and coloring; no guarantee the dry branch mirrors the execute branch. The identical pattern is vendored into rust-ontogen, kiosk, rust-markdown, rust-fluent-git and siblings.
agent-dashboard
Section titled “agent-dashboard”vault-sync.mjs threads —dry-run manually as if(!dryRun) guards around 6+ mutation sites; migrate-permissions.mjs prints a grouped dry-run plan before its batched write phase.
Where: scripts/vault-sync.mjs · scripts/migrate-permissions.mjs
Pain: Every mutation site individually guarded; forgetting one is silent.
Two-phase plan/apply dedup flow: analyze pass stamps keep/delete statuses into an intermediate JSON plan, deletions only happen on re-invocation with —delete; the NudeNet tagger likewise defaults to DRY RUN with —persist.
Where: images/cziwak_similar_deletions.py · images/nudity/nudity_tag.py
Pain: Plan/apply separation and the human review between them re-implemented via argparse flags and a JSON file.
Per-script dry-run blocks copy-pasted across four scripts; the dry-run prints the prompt but cannot preview the gate/PR phases the execute path would run.
Where: scripts/bring_up_to_date.sh · scripts/deps_update.sh · scripts/sync_cousins.sh
Pain: Dry-run fidelity is structurally impossible when the plan isn’t a data structure.
monosync-private
Section titled “monosync-private”Dry-run gate duplicated between forward and reverse sync with different defaults (—dry-run off for sync, on for reverse); the SyncPlan print is the de-facto plan artifact.
Where: src/sync_cmd.rs · src/reverse.rs
Pain: Inconsistent defaults for the same concern in the same binary.
Flowline fit
Section titled “Flowline fit”Flowline’s cross-cutting dry-run covers the flag — but only if dry-run renders from the same declarative step graph the execute path runs, which is exactly what the hand-rolled versions cannot guarantee. The plan → typed-plan-artifact → approve → apply shape (2b, agent-dashboard, monosync) goes beyond a flag and is a taxonomy gap: it needs the plan to be a first-class typed output one run produces and a second run consumes.
Proposed API
Section titled “Proposed API”Builds on: 03, 07 Introduces: run(wf, { dryRun }); extension: planRun() / applyRun()
Dry-run renders from the same declarative graph the execute path runs — the fidelity guarantee
no hand-threaded if (!dryRun) copy can make:
const preview = await run(wf, { dryRun: true }); // every step, from the same graph
// Extension — plan -> review -> apply as a typed two-phase flow: one run produces// a persisted, typed plan artifact; a later run consumes exactly it.const p = await planRun(wf, { seed }); // p: Plan — serializable, versionedawait Bun.write("sync.plan.json", Plan.serialize(p));// ... human review, or a confirm() gate (07), between the phases ...await applyRun(Plan.parse(await Bun.file("sync.plan.json").text()));The two-phase shape is the taxonomy gap: 2b’s dedup plan JSON, rust-ontogen’s REVIEW.md
joins, and monosync’s SyncPlan print are all reaching for Plan as a first-class artifact.