Skip to main content

alloy_devtools/
api.rs

1//! Stable public API surface for developer-tool contracts.
2
3/// Describes the current Sprint 9 scope for this crate.
4#[must_use]
5pub const fn scope() -> &'static str {
6    "phase-4-v1"
7}
8
9/// Shared developer-tool verbs across the platform.
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum ToolVerb {
12    /// Inspect a plan or config.
13    Inspect,
14    /// Validate inputs or environment.
15    Validate,
16    /// Benchmark a workload.
17    Benchmark,
18    /// Report evidence or summaries.
19    Report,
20}
21
22impl ToolVerb {
23    /// Returns the canonical CLI spelling.
24    #[must_use]
25    pub const fn as_str(self) -> &'static str {
26        match self {
27            Self::Inspect => "inspect",
28            Self::Validate => "validate",
29            Self::Benchmark => "benchmark",
30            Self::Report => "report",
31        }
32    }
33}
34
35/// Shared command metadata used by top-level packages and downstream tools.
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub struct CommandSpec<'a> {
38    /// Canonical verb.
39    pub verb: ToolVerb,
40    /// Artifact class produced or consumed by the command.
41    pub artifact_class: &'a str,
42    /// Short command purpose.
43    pub description: &'a str,
44}
45
46const CORE_COMMANDS: [CommandSpec<'static>; 4] = [
47    CommandSpec {
48        verb: ToolVerb::Inspect,
49        artifact_class: "plan",
50        description: "Inspect a plan, config, or evaluator surface.",
51    },
52    CommandSpec {
53        verb: ToolVerb::Validate,
54        artifact_class: "validation",
55        description: "Validate environment, inputs, or supported capabilities.",
56    },
57    CommandSpec {
58        verb: ToolVerb::Benchmark,
59        artifact_class: "benchmark",
60        description: "Run benchmark capture for the selected workload.",
61    },
62    CommandSpec {
63        verb: ToolVerb::Report,
64        artifact_class: "report",
65        description: "Produce evidence, summaries, and operator-facing reports.",
66    },
67];
68
69/// Returns the required shared command catalog.
70#[must_use]
71pub const fn core_command_catalog() -> &'static [CommandSpec<'static>] {
72    &CORE_COMMANDS
73}
74
75/// Builds a stable artifact stem for developer-facing outputs.
76#[must_use]
77pub fn artifact_name(
78    package_name: &str,
79    version: &str,
80    maturity_tier: &str,
81    artifact_class: &str,
82) -> String {
83    format!("{package_name}-{version}-{maturity_tier}-{artifact_class}")
84}
85
86/// Shared ownership record for integrated acceptance and release packet assets.
87#[derive(Clone, Copy, Debug, Eq, PartialEq)]
88pub struct OwnershipRecord<'a> {
89    /// Capability or asset area being owned.
90    pub area: &'a str,
91    /// Primary owning repo or team.
92    pub owner: &'a str,
93    /// Artifact or packet input expected from the owner.
94    pub artifact: &'a str,
95}
96
97impl<'a> OwnershipRecord<'a> {
98    /// Builds a stable ownership record.
99    #[must_use]
100    pub const fn new(area: &'a str, owner: &'a str, artifact: &'a str) -> Self {
101        Self {
102            area,
103            owner,
104            artifact,
105        }
106    }
107}
108
109/// Shared topology assembly packet metadata used to freeze Phase 3 interfaces.
110#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111pub struct TopologyAssemblyPacket<'a> {
112    /// Stable packet identifier.
113    pub packet_id: &'a str,
114    /// Shared topology profile name.
115    pub topology_profile: &'a str,
116    /// Version or scope marker for the packet.
117    pub scope: &'a str,
118    /// Primary ownership records referenced by the packet.
119    pub ownership: &'a [OwnershipRecord<'a>],
120}
121
122impl<'a> TopologyAssemblyPacket<'a> {
123    /// Builds a stable topology assembly packet.
124    #[must_use]
125    pub const fn new(
126        packet_id: &'a str,
127        topology_profile: &'a str,
128        scope: &'a str,
129        ownership: &'a [OwnershipRecord<'a>],
130    ) -> Self {
131        Self {
132            packet_id,
133            topology_profile,
134            scope,
135            ownership,
136        }
137    }
138
139    /// Returns whether the packet declares at least one owning surface.
140    #[must_use]
141    pub fn has_owners(&self) -> bool {
142        !self.ownership.is_empty()
143    }
144}
145
146/// Shared release checklist item for Phase 3 assembly and later Sprint 7 fill-in.
147#[derive(Clone, Copy, Debug, Eq, PartialEq)]
148pub struct ReleaseChecklistItem<'a> {
149    /// Checklist item identifier.
150    pub item_id: &'a str,
151    /// Responsible owner for the item.
152    pub owner: &'a str,
153    /// Evidence or artifact required before the item can be marked done.
154    pub required_artifact: &'a str,
155}
156
157impl<'a> ReleaseChecklistItem<'a> {
158    /// Builds a release checklist item.
159    #[must_use]
160    pub const fn new(item_id: &'a str, owner: &'a str, required_artifact: &'a str) -> Self {
161        Self {
162            item_id,
163            owner,
164            required_artifact,
165        }
166    }
167}
168
169/// Shared closeout packet metadata for Phase 4 appliance and support releases.
170#[derive(Clone, Copy, Debug, Eq, PartialEq)]
171pub struct ReleaseCloseoutPacket<'a> {
172    /// Stable packet identifier.
173    pub packet_id: &'a str,
174    /// Shared release family or profile name.
175    pub release_family: &'a str,
176    /// Scope marker for the packet.
177    pub scope: &'a str,
178    /// Attachment points owned by producing repos.
179    pub attachments: &'a [OwnershipRecord<'a>],
180}
181
182impl<'a> ReleaseCloseoutPacket<'a> {
183    /// Builds a stable closeout packet.
184    #[must_use]
185    pub const fn new(
186        packet_id: &'a str,
187        release_family: &'a str,
188        scope: &'a str,
189        attachments: &'a [OwnershipRecord<'a>],
190    ) -> Self {
191        Self {
192            packet_id,
193            release_family,
194            scope,
195            attachments,
196        }
197    }
198
199    /// Returns whether the packet names at least one downstream attachment.
200    #[must_use]
201    pub fn has_attachments(&self) -> bool {
202        !self.attachments.is_empty()
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::{
209        OwnershipRecord, ReleaseChecklistItem, ReleaseCloseoutPacket, ToolVerb,
210        TopologyAssemblyPacket, artifact_name, core_command_catalog, scope,
211    };
212
213    #[test]
214    fn sprint_9_scope_is_exposed() {
215        assert_eq!(scope(), "phase-4-v1");
216    }
217
218    #[test]
219    fn catalog_contains_required_shared_verbs() {
220        let verbs: Vec<_> = core_command_catalog()
221            .iter()
222            .map(|command| command.verb)
223            .collect();
224
225        assert!(verbs.contains(&ToolVerb::Inspect));
226        assert!(verbs.contains(&ToolVerb::Validate));
227        assert!(verbs.contains(&ToolVerb::Benchmark));
228        assert!(verbs.contains(&ToolVerb::Report));
229    }
230
231    #[test]
232    fn artifact_name_is_stable_and_portfolio_readable() {
233        assert_eq!(
234            artifact_name(
235                "graph-rag-evaluator",
236                "0.1.0",
237                "embedded-evaluator",
238                "report"
239            ),
240            "graph-rag-evaluator-0.1.0-embedded-evaluator-report"
241        );
242    }
243
244    #[test]
245    fn topology_assembly_packet_tracks_ownership_inputs() {
246        let owners = [OwnershipRecord::new(
247            "compiled-plan-cache",
248            "rhodium",
249            "compiled-plan-cache-contract",
250        )];
251        let packet = TopologyAssemblyPacket::new(
252            "phase-3-topology-packet",
253            "reference-stack-v1",
254            "phase-3-v1",
255            &owners,
256        );
257
258        assert!(packet.has_owners());
259        assert_eq!(packet.topology_profile, "reference-stack-v1");
260        assert_eq!(packet.ownership[0].owner, "rhodium");
261    }
262
263    #[test]
264    fn release_checklist_item_carries_owner_and_artifact() {
265        let item = ReleaseChecklistItem::new(
266            "flagship-benchmark-evidence",
267            "rhodium",
268            "benchmark-evidence-packet",
269        );
270
271        assert_eq!(item.owner, "rhodium");
272        assert_eq!(item.required_artifact, "benchmark-evidence-packet");
273    }
274
275    #[test]
276    fn closeout_packet_tracks_attachment_points() {
277        let attachments = [
278            OwnershipRecord::new("appliance-profile", "rhodium", "appliance-package"),
279            OwnershipRecord::new(
280                "certification-support",
281                "strontium",
282                "certification-starter-pack",
283            ),
284        ];
285        let packet = ReleaseCloseoutPacket::new(
286            "phase-4-closeout-packet",
287            "appliance-and-support",
288            "phase-4-v1",
289            &attachments,
290        );
291
292        assert!(packet.has_attachments());
293        assert_eq!(packet.release_family, "appliance-and-support");
294        assert_eq!(packet.attachments[0].owner, "rhodium");
295    }
296}