ferrify_domain/change.rs
1//! Change-intent, planning, and verification types.
2//!
3//! This module describes the lifecycle of a governed change as data. It starts
4//! with operator-facing intent such as [`TaskKind`] and [`ScopeBoundary`], then
5//! refines that intent into planning artifacts like [`ChangePlan`] and
6//! [`PatchPlan`]. By keeping these records serializable and explicit, Ferrify
7//! can explain its scope and verification decisions without relying on hidden
8//! prompt state.
9
10use std::collections::BTreeSet;
11
12use serde::{Deserialize, Serialize};
13
14use crate::{ModeSlug, RepoPath};
15
16/// The kind of change the operator is requesting.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18pub enum TaskKind {
19 /// Fix an incorrect behavior.
20 BugFix,
21 /// Add new user-visible behavior.
22 FeatureAdd,
23 /// Restructure code without changing behavior.
24 Refactor,
25 /// Extend or adjust CLI behavior.
26 CliEnhancement,
27 /// Add, remove, or update a dependency.
28 DependencyChange,
29 /// Increase verification coverage.
30 TestHardening,
31 /// Improve failure handling or safety margins.
32 ReliabilityHardening,
33 /// Create an initial project or subsystem skeleton.
34 Scaffold,
35}
36
37impl TaskKind {
38 /// Maps a task kind to the semantic concern used by patch planning.
39 #[must_use]
40 pub fn concern(self) -> SemanticConcern {
41 match self {
42 Self::BugFix => SemanticConcern::BugFix,
43 Self::FeatureAdd | Self::CliEnhancement => SemanticConcern::FeatureAdd,
44 Self::Refactor => SemanticConcern::Refactor,
45 Self::DependencyChange => SemanticConcern::BuildChange,
46 Self::TestHardening | Self::ReliabilityHardening => SemanticConcern::TestHardening,
47 Self::Scaffold => SemanticConcern::FeatureAdd,
48 }
49 }
50}
51
52/// A repository item used to bound scope.
53#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
54pub struct ScopeItem(pub RepoPath);
55
56/// The maximum acceptable blast radius for a change.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58pub enum BlastRadius {
59 /// The change should stay tightly scoped.
60 Small,
61 /// The change may span a small subsystem.
62 Medium,
63 /// The change may touch multiple subsystems.
64 Large,
65}
66
67/// The desired outcome stated for the task.
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub struct OutcomeSpec {
70 /// A short human-readable outcome description.
71 pub summary: String,
72}
73
74/// A verification requirement requested by the operator or planner.
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76pub struct EvidenceRequirement {
77 /// The verification step that should produce the evidence.
78 pub kind: VerificationKind,
79 /// Why this evidence matters for the task.
80 pub detail: String,
81}
82
83/// The severity attached to a residual risk.
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
85pub enum RiskLevel {
86 /// The risk is worth noting but not blocking.
87 Low,
88 /// The risk could affect correctness or scope.
89 Medium,
90 /// The risk could invalidate the outcome.
91 High,
92}
93
94/// A risk item preserved across planning and reporting.
95#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
96pub struct RiskItem {
97 /// The severity of the risk.
98 pub level: RiskLevel,
99 /// A concise explanation of the risk.
100 pub summary: String,
101}
102
103/// The explicit scope boundary for a task.
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105pub struct ScopeBoundary {
106 /// Items the change is allowed to touch.
107 pub in_scope: Vec<ScopeItem>,
108 /// Items the change must not touch.
109 pub out_of_scope: Vec<ScopeItem>,
110 /// The allowed blast radius for the change.
111 pub blast_radius_limit: BlastRadius,
112}
113
114/// The intake object that describes the requested software change.
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
116pub struct ChangeIntent {
117 /// The broad category of work.
118 pub task_kind: TaskKind,
119 /// The operator's natural-language goal.
120 pub goal: String,
121 /// The outcome that the run is trying to produce.
122 pub desired_outcome: OutcomeSpec,
123 /// Explicit scope constraints for the run.
124 pub scope_boundary: ScopeBoundary,
125 /// Evidence required before the run can claim success.
126 pub success_evidence: Vec<EvidenceRequirement>,
127 /// Risks that must survive into the final report.
128 pub primary_risks: Vec<RiskItem>,
129}
130
131/// The semantic concern for a patch.
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
133pub enum SemanticConcern {
134 /// Correct a defect.
135 BugFix,
136 /// Add new behavior.
137 FeatureAdd,
138 /// Rearrange existing code.
139 Refactor,
140 /// Improve or extend tests.
141 TestHardening,
142 /// Change manifests or dependency settings.
143 BuildChange,
144 /// Limit the patch to documentation.
145 DocsOnly,
146}
147
148/// The expected API impact for the change.
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
150pub enum ApiImpact {
151 /// No API impact is expected.
152 None,
153 /// Only internal APIs should change.
154 InternalOnly,
155 /// Public APIs may change compatibly.
156 PublicCompatible,
157 /// The change is expected to break public APIs.
158 PublicBreaking,
159}
160
161/// The patch budget used to keep edits bounded.
162#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
163pub struct PatchBudget {
164 /// Maximum number of files the patch may touch.
165 pub max_files: u16,
166 /// Maximum number of changed lines.
167 pub max_changed_lines: u32,
168 /// Whether manifest changes are allowed.
169 pub allow_manifest_changes: bool,
170}
171
172/// The verification steps supported by Ferrify.
173#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
174pub enum VerificationKind {
175 /// Run `cargo fmt --check`.
176 CargoFmtCheck,
177 /// Run `cargo check`.
178 CargoCheck,
179 /// Run `cargo clippy --workspace --all-targets --all-features -- -D warnings`.
180 CargoClippy,
181 /// Run the currently configured test command.
182 TargetedTests,
183}
184
185/// The verification steps required for a plan.
186#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
187pub struct VerificationPlan {
188 /// Verification steps that should run before the report is emitted.
189 #[serde(default)]
190 pub required: BTreeSet<VerificationKind>,
191}
192
193/// The architect-stage change plan.
194#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
195pub struct ChangePlan {
196 /// The original intake object.
197 pub intent: ChangeIntent,
198 /// The semantic concern chosen for the run.
199 pub concern: SemanticConcern,
200 /// Repository-relative files selected for the change.
201 pub target_files: BTreeSet<RepoPath>,
202 /// The mode that produced the plan.
203 pub selected_mode: ModeSlug,
204 /// The expected API impact.
205 pub api_impact: ApiImpact,
206 /// The patch budget inherited from policy.
207 pub patch_budget: PatchBudget,
208 /// Verification required for the task.
209 pub verification_plan: VerificationPlan,
210 /// Planner notes that justify the chosen scope.
211 pub notes: Vec<String>,
212}
213
214/// A file-level anchor used by patch planning.
215#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
216pub struct PatchAnchor {
217 /// The anchored file.
218 pub file: RepoPath,
219 /// Why this file was selected.
220 pub reason: String,
221}
222
223/// The implementer-stage patch plan.
224#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
225pub struct PatchPlan {
226 /// The semantic concern carried into patching.
227 pub concern: SemanticConcern,
228 /// The files the patch is allowed to touch.
229 pub target_files: BTreeSet<RepoPath>,
230 /// File-level anchors that explain the scope.
231 pub anchors: Vec<PatchAnchor>,
232 /// The patch budget enforced during implementation.
233 pub budget: PatchBudget,
234 /// The expected API impact of the patch.
235 pub api_impact: ApiImpact,
236 /// Verification required after patching.
237 pub required_validation: VerificationPlan,
238}