1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! `yog --editor-apply` shim: the `$EDITOR` litany execs (DESIGN §9.3 Y21).
//!
//! **TASK-0 FINDING** — litany's exact `$EDITOR` invocation shape
//! (`litany/src/bin/litany/cli.rs:20-27`, `edit_in_editor`, source-read
//! 2026-07-17). `litany config` hands the authoring checkout to `$EDITOR` as
//! ```text
//! sh -c 'exec {EDITOR} "$1"' sh <checkout-dir>
//! ```
//! so `$EDITOR` is **word-split** by `sh` and receives exactly ONE positional
//! argument — the checkout **directory** (`<workspace>/.config-author`,
//! `template/authoring/mod.rs:121,132`), never per-file — quoted `"$1"` so a
//! spaced path stays one argv element. The process cwd is inherited (unset),
//! so the shim must take the checkout from **argv**, never cwd. Crucially,
//! litany has already refreshed `descriptions/**` INTO that checkout before
//! calling `$EDITOR` (`authoring/mod.rs:131`) and commits the whole checkout
//! after — so the shim copies **only the staged files** over it and never
//! deletes, or litany's fresh `descriptions/**` (and any unedited config)
//! would be clobbered.
//!
//! Contract: argv is `<yog> --editor-apply <checkout>`; env `YOG_EDIT_SRC` is
//! the staging dir. Every regular file under the staging dir is copied into
//! the checkout at the same relative path (parent dirs created); every other
//! checkout path is left untouched. Exit 0 on success; non-zero + a stderr
//! diagnostic on any failure — which aborts litany's commit cleanly.
//!
//! **Symlink / special-file hygiene.** Staging holds the plain files yog's UI
//! wrote. Only regular files and directories are mirrored; a symlink or
//! special file (fifo, socket, device) is **skipped** — never followed into a
//! config commit, so nothing outside staging can be smuggled in and no link
//! can escape the checkout.
use fs;
use io;
use ;
/// The argv flag selecting shim mode; also the tail of the composed `$EDITOR`
/// value ([`editor_env_value`](super::branch::edit::editor_env_value)). The
/// one authoritative home for the string both sides must agree on.
pub const EDITOR_APPLY_FLAG: &str = "--editor-apply";
/// Shim entry mapped to a process exit code (the value `main` exits with).
/// `edit_src` is `YOG_EDIT_SRC` (env), `checkout` the argv the shim received.
/// A missing input or any copy error is exit 1 with a `yog --editor-apply:`
/// diagnostic on stderr — the non-zero exit aborts litany's commit.
/// The fallible core of [`run_shim`]: validate both inputs are present, then
/// copy the staged files over the checkout.
/// Copy every regular file under `staging` into `checkout` at the same
/// relative path, creating parent dirs; nothing in `checkout` is ever deleted
/// (the "only drafted files" rule — litany's freshly-refreshed
/// `descriptions/**` must survive). Nested dirs are mirrored recursively;
/// symlinks and special files are skipped (see the module hygiene note).
/// Returns the checkout-relative paths written, sorted, for assertions.