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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Managed organisation policy loader (Claude-Code parity).
//!
//! PMAT-CODE-ORG-POLICY-001: Claude Code honours a highest-tier
//! "enforced settings" file that an organisation admin drops in
//! `/etc/claude-code/CLAUDE.md`. Its content is treated as
//! instructions whose precedence sits ABOVE project and user
//! scopes, so corporate security/compliance rules cannot be
//! overridden by a developer-level `~/.claude/CLAUDE.md` or a
//! repo-level `CLAUDE.md`.
//!
//! This module ships the pure file-load primitive that the REPL
//! prompt builder layers on top of the existing
//! `load_project_instructions` / user-scope ladder. Canonical
//! paths are injected by the caller so tests can use
//! `tempfile::tempdir()` without touching `/etc`.
//!
//! # Claude-Code compatibility
//!
//! Two canonical paths are consulted in this order, first-wins:
//! 1. `/etc/apr-code/CLAUDE.md` — native path
//! 2. `/etc/claude-code/CLAUDE.md` — Claude-Code cross-compat
//!
//! The tier wins over project and user scopes. Missing files are
//! not an error — the policy is simply absent.
//!
//! # Example
//!
//! ```rust,ignore
//! use aprender_orchestrate::agent::org_policy::load_org_policy;
//! use std::path::Path;
//!
//! let roots = [
//! Path::new("/etc/apr-code"),
//! Path::new("/etc/claude-code"),
//! ];
//! if let Some(policy) = load_org_policy(&roots, "CLAUDE.md", 65_536) {
//! // prepend to prompt at the enforced tier
//! }
//! ```
use ;
/// Precedence tier at which the loaded policy content belongs.
///
/// Managed org policy always wins — this enum exists so the
/// prompt builder can tag each instruction block with its tier
/// and the merge step is total-ordered.
/// A loaded org-policy document and its source.
/// Canonical system-wide roots in Claude-Code-parity precedence.
///
/// Returned as owned `PathBuf`s so callers can freely pass them
/// into [`load_org_policy`] or test-time shadows. The order is
/// `apr-code` first (native), `claude-code` second (cross-compat)
/// so a site that installs both sees the `apr-code` file win.
/// Load the first existing `<root>/<filename>` from `roots`.
///
/// - First-wins: `roots` is consulted in order and the first file
/// that exists and reads successfully is returned.
/// - `max_bytes == 0` disables the loader (returns `None`).
/// - `max_bytes > 0` truncates on a UTF-8 char boundary and
/// annotates with a `(truncated from N bytes)` tail so the
/// prompt builder can surface budget pressure to the user.
/// - Missing file or I/O error on a given root is silently
/// skipped — the loader does not panic or propagate errors so
/// a world-readable `/etc` cannot ransom the REPL on boot.
///
/// Callers that want the canonical system roots can pass
/// [`canonical_system_roots`] directly; tests inject `tempdir()`
/// paths so no global state is touched.
/// Truncate `content` on a UTF-8 char boundary, appending a
/// `(truncated from N bytes)` tail when the original exceeded
/// `max_bytes`. Used by [`load_org_policy`] so the budget check
/// is deterministic even in the face of multi-byte characters.