Skip to main content

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}