Skip to main content

aion_awl/
workspace_root.rs

1//! The `{workspace_root}` placeholder, and the two forms a harness path may take.
2//!
3//! # Why a document may not simply write the path
4//!
5//! A `harness` section's path-valued settings — an ACP agent's `command`, either kind's
6//! `cwd`, a Norn `binary` — are absolute by rule, because a worker is started from
7//! whatever directory a supervisor or a shell happens to be in and a relative path names
8//! a different place on every launch.
9//!
10//! That rule is right for a document an operator writes for THEIR box, and impossible
11//! for a document that SHIPS. A document compiled into the server binary must be correct
12//! on every machine that runs it, and there is no absolute literal that is right on more
13//! than one. Held to absolute-only, such a document cannot be written at all.
14//!
15//! So a second accepted form exists, and exactly one: a value that LEADS with the
16//! literal [`WORKSPACE_ROOT_PLACEHOLDER`]. The document states the shape of the path and
17//! the box supplies its root, which is the same division of labour the server already
18//! uses for declared action bodies (`aion_server::worker::WorkspaceRoot`).
19//!
20//! # Where each end of that division is enforced
21//!
22//! This module is the ACCEPT-SET only: it says which of the two forms a value is, and
23//! refuses everything else with a reason. It deliberately cannot expand anything —
24//! resolving the root is a property of the box, which the language layer has no business
25//! knowing.
26//!
27//! The composition root that launches the harness expands the placeholder against the
28//! box's own root and THEN demands the result be absolute. Both ends therefore hold:
29//! nothing about the absolute requirement is softened, and a placeholder that expands to
30//! a non-absolute path — or one on a box whose root cannot be resolved at all — is
31//! refused there, loudly, before anything is spawned.
32//!
33//! # Why the placeholder's spelling lives HERE
34//!
35//! It is a token of the language documents are written in, and the server's declared-body
36//! expansion, the checker, and the compiler's backstop must all agree on it to the byte.
37//! One definition in the crate every one of them already depends on is that agreement;
38//! a second copy anywhere is the drift.
39
40/// The literal a harness path setting carries where the box's workspace root belongs.
41///
42/// Braces are literal text to the AWL lexer and to the worker SDK's command template
43/// (`$name`/`${name}` are its only parameter forms), so a value carrying this checks
44/// clean and — if it ever reached a launch unexpanded — would fail loudly on a
45/// nonexistent `{workspace_root}` path rather than silently launching somewhere else.
46pub const WORKSPACE_ROOT_PLACEHOLDER: &str = "{workspace_root}";
47
48/// Which of the two accepted forms a harness path setting is written in.
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum HarnessPathForm {
51    /// An absolute path, usable exactly as written.
52    Absolute,
53    /// A path leading with [`WORKSPACE_ROOT_PLACEHOLDER`], absolute only once the
54    /// composition root has expanded it against the box's own workspace root.
55    WorkspaceRooted,
56}
57
58/// Why a harness path setting is neither accepted form.
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum HarnessPathRefusal {
61    /// The value is empty or nothing but whitespace.
62    Empty,
63    /// The value is a relative path and carries no placeholder to make it otherwise.
64    Relative,
65    /// The value carries the placeholder, but not at its start — so however the box
66    /// resolves, the expansion cannot produce an absolute path.
67    PlaceholderNotLeading,
68}
69
70impl HarnessPathRefusal {
71    /// The refusal's reason, a self-contained sentence opening with "it".
72    ///
73    /// One phrasing per refusal, read by both the checker's operator-facing message and
74    /// the compiler's backstop, so the two cannot come to describe the same fault
75    /// differently. It opens with "it" because both slots introduce the offending setting
76    /// first and the reason second.
77    #[must_use]
78    pub const fn reason(self) -> &'static str {
79        match self {
80            Self::Empty => {
81                "it is empty; a path setting names an absolute path, or leads with \
82                 `{workspace_root}` for the launching box to expand"
83            }
84            Self::Relative => {
85                "it must be an ABSOLUTE path. A worker is started from whatever directory a \
86                 supervisor or a shell happens to be in, so a relative path names a different \
87                 place on every launch. A document that must be correct on more than one box \
88                 leads the value with `{workspace_root}` instead, which the launching worker \
89                 expands against that box's own workspace root."
90            }
91            Self::PlaceholderNotLeading => {
92                "it carries `{workspace_root}` somewhere other than its start, so expanding it \
93                 cannot produce an absolute path. The placeholder stands at the beginning of \
94                 the value or not at all."
95            }
96        }
97    }
98}
99
100/// Which accepted form `value` is written in, or why it is neither.
101///
102/// The accept-set is EXACTLY two forms and is stated only here: the checker refuses a
103/// document by this rule, and the compiler's backstop refuses a value that reached it
104/// by any other route by the same one.
105///
106/// # Errors
107///
108/// Returns the [`HarnessPathRefusal`] naming which of the three ways the value failed to
109/// be either accepted form.
110pub fn harness_path_form(value: &str) -> Result<HarnessPathForm, HarnessPathRefusal> {
111    if value.trim().is_empty() {
112        return Err(HarnessPathRefusal::Empty);
113    }
114    if value.starts_with(WORKSPACE_ROOT_PLACEHOLDER) {
115        return Ok(HarnessPathForm::WorkspaceRooted);
116    }
117    if value.contains(WORKSPACE_ROOT_PLACEHOLDER) {
118        return Err(HarnessPathRefusal::PlaceholderNotLeading);
119    }
120    if std::path::Path::new(value).is_absolute() {
121        return Ok(HarnessPathForm::Absolute);
122    }
123    Err(HarnessPathRefusal::Relative)
124}
125
126#[cfg(test)]
127mod tests {
128    use super::{
129        HarnessPathForm, HarnessPathRefusal, WORKSPACE_ROOT_PLACEHOLDER, harness_path_form,
130    };
131
132    #[test]
133    fn an_absolute_path_is_the_first_accepted_form() {
134        assert_eq!(
135            harness_path_form("/srv/agents/workspace"),
136            Ok(HarnessPathForm::Absolute)
137        );
138    }
139
140    #[test]
141    fn a_leading_placeholder_is_the_second_accepted_form() {
142        assert_eq!(
143            harness_path_form(WORKSPACE_ROOT_PLACEHOLDER),
144            Ok(HarnessPathForm::WorkspaceRooted)
145        );
146        assert_eq!(
147            harness_path_form("{workspace_root}/assistant"),
148            Ok(HarnessPathForm::WorkspaceRooted)
149        );
150    }
151
152    #[test]
153    fn a_relative_path_is_refused_as_it_always_was() {
154        assert_eq!(
155            harness_path_form("workspace"),
156            Err(HarnessPathRefusal::Relative)
157        );
158        assert_eq!(
159            harness_path_form("./workspace"),
160            Err(HarnessPathRefusal::Relative)
161        );
162        assert_eq!(
163            harness_path_form("../workspace"),
164            Err(HarnessPathRefusal::Relative)
165        );
166    }
167
168    #[test]
169    fn the_new_form_did_not_widen_the_old_refusal() {
170        // The placeholder buys a document ONE shape: the root at the front. A value that
171        // merely mentions it is still refused, because no root the box resolves can make
172        // `agents/{workspace_root}` absolute — accepting it here would hand the launch a
173        // value the composition root must refuse, which is the checker-green/launch-red
174        // split the checker exists to prevent.
175        assert_eq!(
176            harness_path_form("agents/{workspace_root}"),
177            Err(HarnessPathRefusal::PlaceholderNotLeading)
178        );
179        assert_eq!(
180            harness_path_form("/srv/{workspace_root}/agents"),
181            Err(HarnessPathRefusal::PlaceholderNotLeading),
182            "an absolute path carrying the placeholder is still refused: the expansion \
183             would splice a second root into the middle of it"
184        );
185    }
186
187    #[test]
188    fn an_empty_value_is_refused_before_either_form_is_considered() {
189        assert_eq!(harness_path_form(""), Err(HarnessPathRefusal::Empty));
190        assert_eq!(harness_path_form("   "), Err(HarnessPathRefusal::Empty));
191    }
192}