Skip to main content

faf_kernel/
types.rs

1//! Type definitions for the FAF format.
2//!
3//! These structs are the in-memory shape of a parsed `.faf` file. Field names
4//! map directly to the YAML keys; most are `Option` because `.faf` is
5//! progressive — a project fills in as much context as it has, and the score
6//! reflects how complete that picture is.
7
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11/// A complete parsed `.faf` file — the root of the context document.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct FafData {
14    /// The `.faf` format version this file was written against (e.g. `"2.5.0"`).
15    pub faf_version: String,
16    /// Core project identity (name, goal, language). Always present.
17    pub project: Project,
18
19    /// Cached AI-readiness score, if one was written by a generator.
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub ai_score: Option<String>,
22
23    /// Cached confidence band for the score, if present.
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub ai_confidence: Option<String>,
26
27    /// Short, AI-facing summary lines keyed by topic.
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub ai_tldr: Option<HashMap<String, String>>,
30
31    /// The fast-path context an assistant reads first (what/stack/files).
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub instant_context: Option<InstantContext>,
34
35    /// Self-reported completeness metrics for the context.
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub context_quality: Option<ContextQuality>,
38
39    /// The technical stack (frontend, backend, database, build, …).
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub stack: Option<Stack>,
42
43    /// The human side — the 6 W's (who/what/why/how/where/when).
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub human_context: Option<HumanContext>,
46
47    /// Working preferences (quality bar, testing, docs, code style).
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub preferences: Option<Preferences>,
50
51    /// Where the project is right now (phase, version, focus, milestones).
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub state: Option<State>,
54
55    /// Free-form classification tags.
56    #[serde(default, skip_serializing_if = "Vec::is_empty")]
57    pub tags: Vec<String>,
58
59    /// Top-level named commands (build/test/lint/dev) → shell invocations.
60    /// Some `.faf` files nest these under `instant_context.commands` instead;
61    /// see `FafFile::commands()` for the fallback that reads either shape.
62    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
63    pub commands: HashMap<String, String>,
64
65    /// Secrets handling guidance — what agents must never read or commit.
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub security: Option<Security>,
68
69    /// Agent-facing guardrails and working-style notes.
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub ai_instructions: Option<AiInstructions>,
72
73    /// Real repo conventions an agent should follow (not human↔assistant prefs).
74    #[serde(default, skip_serializing_if = "Vec::is_empty")]
75    pub conventions: Vec<String>,
76}
77
78/// Secrets handling guidance — what agents must never read or commit.
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct Security {
81    /// Where secrets live (e.g. `.env`).
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub secrets: Option<String>,
84
85    /// A safe example file to point at instead (e.g. `.env.example`).
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub example: Option<String>,
88
89    /// Specific paths an agent must never read or commit.
90    #[serde(default, skip_serializing_if = "Vec::is_empty")]
91    pub never: Vec<String>,
92}
93
94/// Agent-facing guardrails and working-style notes, surfaced in AGENTS.md.
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct AiInstructions {
97    /// Free-form warnings an agent should see up front (e.g. "use bun, not npm").
98    #[serde(default, skip_serializing_if = "Vec::is_empty")]
99    pub warnings: Vec<String>,
100
101    /// Loose key/value working-style notes (human↔assistant interaction, not repo rules).
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub working_style: Option<HashMap<String, String>>,
104}
105
106/// Core project identity — the one block every `.faf` file has.
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct Project {
109    /// The project's name.
110    pub name: String,
111
112    /// One sentence on what the project is for — the seed for the 6 W's.
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub goal: Option<String>,
115
116    /// Primary implementation language (e.g. `"Rust"`).
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub main_language: Option<String>,
119
120    /// How the project is built or approached (architecture, methodology).
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub approach: Option<String>,
123
124    /// The project's own version string.
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub version: Option<String>,
127
128    /// SPDX license identifier (e.g. `"MIT"`).
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub license: Option<String>,
131}
132
133/// The fast-path context an AI assistant reads first.
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct InstantContext {
136    /// A one-line description of what is being built.
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub what_building: Option<String>,
139
140    /// The stack in brief, as a single human-readable line.
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub tech_stack: Option<String>,
143
144    /// How and where the project is deployed.
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub deployment: Option<String>,
147
148    /// The files most worth reading first to understand the project.
149    #[serde(default, skip_serializing_if = "Vec::is_empty")]
150    pub key_files: Vec<String>,
151
152    /// Named commands (e.g. `build`, `test`) → their shell invocations.
153    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
154    pub commands: HashMap<String, String>,
155}
156
157/// The technical stack — one field per layer.
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct Stack {
160    /// Frontend framework or library.
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub frontend: Option<String>,
163
164    /// Backend framework or runtime.
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub backend: Option<String>,
167
168    /// Database or data store.
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub database: Option<String>,
171
172    /// Hosting / infrastructure platform.
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub infrastructure: Option<String>,
175
176    /// Build tool or bundler.
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub build_tool: Option<String>,
179
180    /// Test framework or runner.
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub testing: Option<String>,
183
184    /// CI/CD platform.
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub cicd: Option<String>,
187}
188
189/// Self-reported metrics on how complete the context is.
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct ContextQuality {
192    /// How many of the 33 slots are filled, as reported by the generator.
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub slots_filled: Option<String>,
195
196    /// Confidence band for the reported completeness.
197    #[serde(skip_serializing_if = "Option::is_none")]
198    pub confidence: Option<String>,
199
200    /// Whether the context is considered ready to hand off to an AI.
201    #[serde(default)]
202    pub handoff_ready: bool,
203
204    /// Slots the author knows are still missing.
205    #[serde(default, skip_serializing_if = "Vec::is_empty")]
206    pub missing_context: Vec<String>,
207}
208
209/// The human side of the project — the 6 W's.
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct HumanContext {
212    /// Who the project is for / who works on it.
213    #[serde(skip_serializing_if = "Option::is_none")]
214    pub who: Option<String>,
215
216    /// What the project is.
217    #[serde(skip_serializing_if = "Option::is_none")]
218    pub what: Option<String>,
219
220    /// Why it exists. Serialized as `why` (`why_field` avoids the keyword).
221    #[serde(rename = "why", skip_serializing_if = "Option::is_none")]
222    pub why_field: Option<String>,
223
224    /// How it works or is built.
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub how: Option<String>,
227
228    /// Where it runs / lives. Serialized as `where`.
229    #[serde(rename = "where", skip_serializing_if = "Option::is_none")]
230    pub where_field: Option<String>,
231
232    /// When — timeline, stage, or cadence.
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub when: Option<String>,
235}
236
237/// Working preferences that shape how the project should be developed.
238#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct Preferences {
240    /// The quality bar to hold (e.g. zero-errors, championship-grade).
241    #[serde(skip_serializing_if = "Option::is_none")]
242    pub quality_bar: Option<String>,
243
244    /// Testing expectations.
245    #[serde(skip_serializing_if = "Option::is_none")]
246    pub testing: Option<String>,
247
248    /// Documentation expectations.
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub documentation: Option<String>,
251
252    /// Code-style conventions.
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub code_style: Option<String>,
255}
256
257/// Where the project is right now.
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct State {
260    /// Current phase (e.g. `"production"`, `"prototype"`).
261    #[serde(skip_serializing_if = "Option::is_none")]
262    pub phase: Option<String>,
263
264    /// Version associated with the current state.
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub version: Option<String>,
267
268    /// What the work is focused on right now.
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub focus: Option<String>,
271
272    /// Recent or upcoming milestones.
273    #[serde(default, skip_serializing_if = "Vec::is_empty")]
274    pub milestones: Vec<String>,
275}