lean_ctx/core/rule_artifacts.rs
1//! The committed `LEAN-CTX.md` rule artifacts as `(relative_path, content)`
2//! pairs — the single source shared by the regenerator (`gen_rules` example)
3//! and the drift gate (`tests/rules_drift.rs`) so the project copy and the
4//! `rust/` copy can never disagree.
5//!
6//! Content is forced to the default profile (non-shadow, compression `Off`) so
7//! the committed bytes are independent of the developer's local lean-ctx config
8//! and stay deterministic (#498). The live writer
9//! (`hooks::ensure_project_agents_integration`) renders with the *user's* config
10//! instead — that is each user's own copy, not this repo's checked-in artifact.
11
12use crate::core::config::CompressionLevel;
13use crate::core::rules_canonical::{self, Wrapper};
14
15/// Project-relative paths of every committed dedicated-rules artifact. Add new
16/// real rule artifacts here — not docs examples or templates.
17pub const ARTIFACT_PATHS: &[&str] = &["LEAN-CTX.md", "rust/LEAN-CTX.md"];
18
19/// Canonical body of a project `LEAN-CTX.md`: the owner banner, the long-form
20/// rules block (non-shadow, compression `Off`), and a trailing newline.
21/// Inverse of what the drift gate reads back. Longform because `LEAN-CTX.md`
22/// is opened on demand (AGENTS.md pointer), never auto-loaded — it can afford
23/// the verbose teaching sections the injected profiles fold away (#578).
24#[must_use]
25pub fn canonical_body() -> String {
26 format!(
27 "{}\n{}\n",
28 rules_canonical::PROJECT_LEAN_CTX_OWNED_MARKER,
29 rules_canonical::render(
30 false,
31 Wrapper::Longform,
32 CompressionLevel::Off,
33 &super::tool_profiles::ToolProfile::Power,
34 )
35 )
36}
37
38/// `(relative_path, content)` for every artifact the generator writes. All
39/// artifacts share one canonical body today; the shape leaves room for
40/// per-path bodies later without changing callers.
41#[must_use]
42pub fn artifacts() -> Vec<(&'static str, String)> {
43 let body = canonical_body();
44 ARTIFACT_PATHS.iter().map(|p| (*p, body.clone())).collect()
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn canonical_body_is_owned_versioned_and_current() {
53 let body = canonical_body();
54 assert!(body.starts_with(rules_canonical::PROJECT_LEAN_CTX_OWNED_MARKER));
55 assert!(body.contains(&format!(
56 "<!-- version: {} -->",
57 rules_canonical::RULES_VERSION
58 )));
59 // The body must carry the v3 guidance it exists to ship.
60 assert!(body.contains("AGENT LOOP"));
61 assert!(body.contains("NAVIGATION PARADOX"));
62 assert!(body.ends_with('\n'));
63 }
64
65 #[test]
66 fn artifacts_cover_every_declared_path() {
67 let arts = artifacts();
68 assert_eq!(arts.len(), ARTIFACT_PATHS.len());
69 for (path, body) in arts {
70 assert!(ARTIFACT_PATHS.contains(&path));
71 assert!(!body.is_empty());
72 }
73 }
74}