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
/// Gate a checked-in projection against the live one — the 潛移 staleness reaction, reusable by
/// adopters. Pair it with [`super::constitution_markdown`]: generate a Markdown projection of your
/// constitution into a checked-in file (so your agents read the reacted law in-context, the
/// **projection** track), keep a short hand-written spine (`AGENTS.md`-style, the **prose** track),
/// and gate the projection's freshness in a `cargo test` so the file can never drift from the
/// declared law — the same mechanism Tianheng runs on its own `AGENTS.self-law.md`.
///
/// - `live` — the freshly generated projection (e.g. `constitution_markdown(&c)`).
/// - `path` — the checked-in artifact.
/// - `regenerate` — the command echoed in the error (e.g. `"BLESS=1 cargo test --test law"`).
/// - `bless` — when `true`, overwrite `path` with `live` (creating any missing parent directories);
/// when `false`, compare. The **caller** supplies this (typically
/// `std::env::var_os("BLESS").is_some()`), so this function reads no environment and is a pure
/// function of its arguments — no process-global state, safe under parallel tests.
///
/// Returns `Ok(())` when the file byte-matches `live` (or was blessed); `Err` — naming both the path
/// and `regenerate` — when the file differs, is missing, or is unreadable (a projection that cannot
/// be confirmed fresh is "cannot judge", never a silent pass), or when a bless write fails.
///
/// ```no_run
/// use tianheng::prelude::*;
/// let c = Constitution::new("my-project"); // … your declared boundaries
/// let live = tianheng::constitution_markdown(&c);
/// let bless = std::env::var_os("BLESS").is_some();
/// // Call this inside a `#[test]`:
/// tianheng::projection_gate(
/// &live,
/// std::path::Path::new("AGENTS.my-law.md"),
/// "BLESS=1 cargo test",
/// bless,
/// )
/// .unwrap();
/// ```
/// The shared repair hint every gate error ends with, so the "regenerate it with `…`" wording lives
/// **once** rather than hand-copied at each failure site (the twin-drift the project retires
/// everywhere). Each caller supplies its own prefix/separator (an error *cause* joins with ` — `,
/// the stale case with `; `); only the hint is shared.