AgentSkillsCN

functional-rust-generator

以数据→计算→动作的层级结构为基础,采用类型驱动的领域驱动设计,实现函数式优先的 Rust 代码生成。源码无瑕疵、Clippy 无警告、零恐慌/无 unwrap/无 mut。测试方面,只要能成功编译即可。

SKILL.md
--- frontmatter
name: functional-rust-generator
description: Functional-first Rust generation with Data→Calculations→Actions hierarchy and type-driven DDD. Source: flawless clippy, zero panics/unwrap/mut. Tests: anything that compiles.
allowed-tools: ["bash"]
version: 3.1.0
jsonl
{"kind":"meta","skill":"functional-rust-generator","version":"3.1.0","updated":"2026-02","format":"markdown-with-embedded-jsonl"}
{"kind":"claude_best_practice","id":"explicit","text":"Be explicit about constraints, acceptance checks, and outputs."}
{"kind":"claude_best_practice","id":"context","text":"Include the why for constraints to reduce accidental violations."}
{"kind":"claude_best_practice","id":"avoid_overengineering","text":"Do not add abstractions or features unless required for correctness."}
{"kind":"claude_best_practice","id":"state_visibility","text":"Keep invariants and boundaries visible via types and small functions."}
{"kind":"claude_best_practice","id":"tool_discipline","text":"Use tools deliberately; keep changes minimal and localized."}

{"kind":"hierarchy","id":"data_calculations_actions","author":"Eric Normand","source":"Grokking Simplicity","text":"Organize code by the Data → Calculations → Actions hierarchy. This is the primary method for making code easier to understand, maintain, and test.","preference_order":["Data - #1 (Highest)","Calculations - #2","Actions - #3 (Lowest)"],"strategy":"Constantly refactor code to move logic RIGHT TO LEFT: Actions → Calculations → Data","refactoring_prompt":"How can I refactor my code to move logic from Actions into Calculations, and eventually into Data?"}
{"kind":"tier","id":"data","rank":1,"text":"Data is the most preferred category. It is inert and transparent.","characteristics":{"inert":"Does not run or execute; requires interpretation to have meaning","serializable":"Can easily be saved to disk or sent over a network","comparable":"Easy to compare two pieces of data for equality","open_for_interpretation":"The same data can be used for different purposes"},"examples":["JSON blob","Store receipt","Dollar amount from API","Log entry","Configuration map"],"rust_manifestation":["struct with derived traits (Clone, Debug, PartialEq, Serialize)","enum with no methods","Newtype wrappers","Persistent data structures (rpds/im)"],"goal":"Represent as much of the system as possible in simple data structures rather than specialized classes with behavior."}
{"kind":"tier","id":"calculations","rank":2,"text":"Calculations are computations that transform inputs into outputs. Pure functions.","characteristics":{"time_independent":"Does not depend on when run or how many times run","referential_transparency":"Same input → same output, always","no_side_effects":"Does not change system state or outside world","easy_to_test":"No implicit inputs/outputs, very easy to verify"},"examples":["Finding maximum in a list","Validating email format","Transforming data format","Computing shopping cart total"],"rust_manifestation":["fn(x: A) -> B with no I/O","Pure iterator pipelines","Result-returning validators","State → State transition functions"],"goal":"Write as much code as possible in this category. Extract from Actions."}
{"kind":"tier","id":"actions","rank":3,"text":"Actions are functions that depend on time—when run or how many times. Impure functions with side effects.","characteristics":{"time_dependent":"Outcome changes based on world state or changes world state","hard_to_test":"Requires environment setup (databases, mocks, network)","spreadable":"If a function calls an action, it becomes an action. Side effects spread easily."},"examples":["Sending email","Database read/write","HTTP request","Updating global state","Reading system clock"],"rust_manifestation":["async fn with tokio","File I/O functions","Database client calls","Network handlers"],"goal":"Minimize actions. Keep them small. Push to the edges of the program. Extract calculations from them.","containment":"Actions must be isolated in shell modules. They cannot call into core. Core calls out to shell via dependency injection or callback patterns."}

{"kind":"strategy","id":"extract_calculations","text":"Extract Calculations from Actions: Code that makes decisions or computes values should be extracted from actions into pure functions.","technique":"Replace implicit inputs (global variables, I/O reads) with function arguments. Replace implicit outputs (mutated state, I/O writes) with return values.","example_before":"async fn process_order(id: u64) -> Result<()> { let order = db.get_order(id).await?; if order.total > 1000 { email.send_discount().await?; } Ok(()) }","example_after":"// Calculation (pure) fn should_send_discount(order: &Order) -> bool { order.total > 1000 } // Action (impure, minimal) async fn process_order(id: u64) -> Result<()> { let order = db.get_order(id).await?; if should_send_discount(&order) { email.send_discount().await?; } Ok(()) }"}
{"kind":"strategy","id":"immutable_data","text":"Treat data as immutable. Use copy-on-write patterns to prevent mutations, allowing more operations to be calculations rather than actions.","technique":"Instead of mutating in place, return new values. Use persistent data structures for efficiency.","rust_pattern":"fn update(state: State, event: Event) -> State { State { events: state.events.push_back(event), ..state } }"}

{"kind":"philosophy","id":"wlaschin_ddd","author":"Scott Wlaschin","source":"Domain Modeling Made Functional","text":"Use the type system to model the domain. Types are documentation. Types are tests. Types prevent bugs at compile time."}
{"kind":"principle","id":"make_illegal_states_unrepresentable","author":"Scott Wlaschin","text":"If a combination of fields should never exist, the type system should make it impossible to construct.","bad_example":"struct Order { shipped: bool, shipping_address: Option<Address> } // Can have shipped=true but no address!","good_example":"enum Order { Draft(DraftOrder), Validated(ValidatedOrder), Shipped(ShippedOrder) } // Each state has required fields","rust_pattern":"Use enums to represent state machines. Each variant has exactly the fields valid for that state."}
{"kind":"principle","id":"parse_dont_validate","author":"Scott Wlaschin","text":"Instead of validating data and throwing errors, parse it into a type that guarantees validity. Once parsed, the data is always valid.","bad_pattern":"fn process(input: &str) -> Result<()> { if input.is_empty() { return Err(...); } if !input.contains('@') { return Err(...); } // use input everywhere, must re-validate }","good_pattern":"struct Email(String); impl Email { fn parse(s: String) -> Result<Self, EmailError> { /* validate once */ } } fn process(email: Email) { /* email is guaranteed valid, no re-checking */ }","rust_pattern":"Use newtypes with fallible constructors. The type existence proves validity."}
{"kind":"principle","id":"types_as_documentation","author":"Scott Wlaschin","text":"A function signature should tell you everything you need to know. Types are better than comments because they can't go out of sync.","example":"// Bad: what does bool mean? fn process(order: Order, skip_validation: bool) -> Result<()>\n\n// Good: self-documenting fn process(order: Order, mode: ProcessingMode) -> Result<()> enum ProcessingMode { ValidateFirst, SkipValidation }","rust_pattern":"Replace primitive types (bool, String, i32) with semantic newtypes and enums."}
{"kind":"principle","id":"workflows","author":"Scott Wlaschin","text":"Model business processes as workflows: functions that transform domain types from one state to another.","pattern":"Workflow = Data → Calculation → Data → Calculation → ... → Final Data","example":"PlaceOrder workflow: UnvalidatedOrder → ValidateOrder → ValidatedOrder → PriceOrder → PricedOrder → AcknowledgeOrder → PlacedOrder","rust_pattern":"Each step is a pure function. The workflow composes them. State transitions are explicit in types."}

{"kind":"pattern","id":"single_case_union","author":"Scott Wlaschin","text":"Use newtypes to prevent primitive obsession. A CustomerId is not just a String.","example":"#[derive(Debug, Clone, PartialEq)] pub struct CustomerId(pub String); impl std::fmt::Display for CustomerId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, \"{}\", self.0) } }","benefits":["Type safety: can't mix up CustomerId with OrderId","Explicit intent: function signatures are self-documenting","Validation boundary: constructor can enforce format"]}
{"kind":"pattern","id":"choice_types","author":"Scott Wlaschin","text":"Use enums (sum types) to model choices and state machines. The compiler ensures all cases are handled.","example":"enum PaymentMethod { CreditCard(CreditCardInfo), BankTransfer(BankInfo), PayPal(PayPalEmail), }","rust_benefit":"match must handle all variants. Can't forget a case."}
{"kind":"pattern","id":"capability_based","author":"Scott Wlaschin","text":"Functions can only do what they have capabilities for. Pass in only what's needed.","bad":"fn process(db: &Database) { /* can do ANY database operation */ }","good":"fn process(lookup_user: impl Fn(UserId) -> Result<User>) { /* can ONLY lookup users */ }","rust_pattern":"Use trait bounds or closure injection to limit capabilities."}

{"kind":"stack","crate":"itertools","use":"iterator pipelines","why":"loop-free transforms","when":"core + shell"}
{"kind":"stack","crate":"tap","use":"suffix pipelines","why":"pipe/tap/conv ergonomics","when":"core (pipe/conv) + shell (tap for observation)"}
{"kind":"stack","crate":"rpds","use":"persistent state (default)","why":"structural sharing snapshots","when":"agent state"}
{"kind":"stack","crate":"im","use":"persistent state (thread-share)","why":"Arc-backed persistence","when":"only when truly sharing across threads"}
{"kind":"stack","crate":"thiserror","use":"domain errors","why":"semantic enumerable errors","when":"core"}
{"kind":"stack","crate":"anyhow","use":"boundary errors","why":"context-rich failures","when":"shell / main"}
{"kind":"stack","crate":"tokio","use":"async runtime","why":"I/O + concurrency","when":"shell only"}
{"kind":"stack","crate":"futures-util","use":"async combinators","why":"StreamExt/TryStreamExt","when":"shell only"}
{"kind":"stack","crate":"tokio-util","use":"async utilities","why":"codec/compat","when":"shell only"}

{"kind":"bifurcation","id":"source_vs_test","text":"Two completely different standards: source code vs test code.","source":{"clippy":"MANDATORY - fix ALL errors and warnings, no exceptions, no lazy shortcuts","quality":"Flawless. Take time to do it right.","unwrap":"BANNED in all forms","mut":"Avoid - use pure transforms","libraries":"Use all 6 core libraries whenever possible","hierarchy":"Data → Calculations → Actions"},"test":{"clippy":"IGNORE - if it compiles, ship it","quality":"Does not matter at all","unwrap":"ALLOWED - use freely","panic":"ALLOWED - use freely","mut":"Whatever works","philosophy":"Tests are dirty, ugly, and that's fine"}}

{"kind":"rule","id":"no_unwrap_anywhere","level":"error","scope":"source_only","text":"Never use unwrap in any form in source code. Tests may use freely.","bans":["unwrap","expect","panic!","unwrap_unchecked","unwrap_or","unwrap_or_else","unwrap_or_default"],"preferred":["match","if let","map","and_then","ok_or_else","map_or","map_or_else","Result::map_or_else"],"notes":["This ban includes Option::unwrap_or*_ and Result::unwrap_or*_ variants.","Use map_or/map_or_else or match; do not call anything named unwrap.","TESTS ARE EXEMPT: unwrap/panic freely in test code."]}
{"kind":"rule","id":"no_mut_by_default","level":"error","scope":"source_only","text":"Avoid mut in source code. Prefer pure transforms, folds, and persistent structures. Tests may mutate freely.","bans":["let mut","mut "],"preferred":["fold","scan","map","filter","collect","rpds/im persistent updates"],"notes":["If mutation is absolutely forced by an external API, isolate it in the smallest possible shell adapter and do not surface mut into core types.","TESTS ARE EXEMPT: mutate freely in test code."]}
{"kind":"rule","id":"pure_core","level":"error","scope":"source_only","text":"Functional core is pure + deterministic. Shell wraps impurity at system boundary like Haskell/Gleam IO.","bans":["I/O in core","logging in core","randomness in core","global mutation","shared mutable state spreading from shell"],"preferred":["pure functions","newtypes","enums","type-state","persistent state","shell adapters that wrap IO monad-style"],"notes":["Async belongs in the shell; core stays sync and total.","Impurity lives at the edge - it cannot spread inward.","Like Haskell IO or Gleam effects: contained at boundary.","TESTS ARE EXEMPT."]}
{"kind":"rule","id":"no_indexing_operators","level":"error","scope":"source_only","text":"Never use indexing operators (arr[i], slice[i]) - they panic on out-of-bounds. Use .get(i) which returns Option.","bans":["[] indexing on slices/arrays/vecs","slice[i]","arr[i]"],"preferred":["slice.get(i)","slice.get_mut(i)","Iterator::nth(i)"],"example_bad":"let first = items[0]; // panics if empty!","example_good":"let first = items.get(0)?; // Returns Option, handled explicitly"}
{"kind":"rule","id":"no_interior_mutability","level":"error","scope":"source_only","text":"No interior mutability in core. RefCell, Cell, OnceCell, Lazy are mutation with extra steps.","bans":["RefCell","Cell","OnceCell","Lazy","OnceLock"],"preferred":["Pure functions returning new values","Persistent data structures","Explicit mut at shell boundary only"],"notes":["Interior mutability is a code smell in functional code.","If you need to 'mutate through a reference', restructure your code.","Allowed in shell for FFI interop or caching, never in core.","TESTS ARE EXEMPT."]}
{"kind":"rule","id":"no_boolean_parameters","level":"warn","scope":"source_only","text":"Avoid boolean parameters. bool is the 'primitive obsession' of control flow. Use enums instead.","bans":["fn process(data: Data, flag: bool)","fn validate(input: &str, strict: bool)"],"preferred":["enum ProcessingMode { Strict, Lenient }","enum ValidationLevel { Full, Quick }","fn process(data: Data, mode: ProcessingMode)"],"example_bad":"process(order, true); // What does true mean??","example_good":"process(order, ProcessingMode::ValidateFirst); // Self-documenting!"}
{"kind":"rule","id":"no_stringly_typed","level":"warn","scope":"source_only","text":"Don't use String for domain concepts. Use newtypes.","bans":["user_id: String","email: String","amount: i64 // without unit"],"preferred":["user_id: UserId","email: Email","amount: Cents or Money<Currency>"],"notes":["String is the 'any' of text. It tells you nothing about format or validation.","Newtypes give you parse_dont_validate for free at boundaries."]}
{"kind":"rule","id":"no_hidden_allocations","level":"warn","scope":"source_only","text":"Allocations should be intentional and visible. Be aware of .to_string(), .clone(), .collect().","patterns_to_mind":["iterator.collect() allocates","format!() allocates","String::from() allocates","Arc::new() allocates","Box::new() allocates"],"preferred":["Use &str where possible","Use references with lifetimes","Use Cow for conditional cloning","Document why allocation is needed"],"notes":["Not banned, but be intentional. Functional code can be allocation-heavy; profile if needed."]}
{"kind":"rule","id":"no_arc_mutex","level":"error","scope":"source_only","text":"No Arc<Mutex<_>> or Arc<RwLock<_>>. These are shared mutable state - the enemy of functional programming.","bans":["Arc<Mutex<T>>","Arc<RwLock<T>>","parking_lot::Mutex in Arc"],"preferred":["Message passing (tokio channels)","Persistent data structures (rpds)","Single-threaded state with explicit handoff","Actor patterns"],"notes":["Arc<Mutex> is 'global variable with extra steps'.","It causes deadlocks, races, and makes reasoning impossible.","If you think you need it, restructure your architecture."]}
{"kind":"rule","id":"expression_based","level":"warn","scope":"source_only","text":"Prefer expressions over statements. Rust is expression-oriented; embrace it.","bans":["let x; if condition { x = a; } else { x = b; } // imperative"],"preferred":["let x = if condition { a } else { b }; // expression","match as expression","?. for early error return, then expression for success path"],"notes":["Early return with ? is fine for error handling.","But the happy path should be expression-based where practical."]}
{"kind":"rule","id":"property_based_testing","level":"warn","scope":"source_only","text":"Pure functions (calculations) should have property-based tests. Unit tests verify examples; property tests verify invariants.","library":"proptest","patterns":["For all inputs satisfying precondition, output satisfies postcondition","f(f⁻¹(x)) == x (round-trip)","f(a ⊕ b) == f(a) ⊕ f(b) (structure preservation)"],"example":"proptest!(|(a in 0..1000i64, b in 0..1000i64)| { prop_assert!(add(a, b) == add(b, a)); // commutativity });"}
{"kind":"rule","id":"clippy_mandatory","level":"error","scope":"source_only","text":"ALL clippy warnings and errors in source files MUST be fixed. No exceptions. No lazy shortcuts. Take the time to do it right.","bans":["ignoring clippy lints","#![allow(clippy::"],"notes":["If clippy complains, fix it properly.","Do not add allow/deny attributes to silence lints - fix the code.","This applies to SOURCE CODE only. Test code can be messy."]}
{"kind":"rule","id":"use_all_core_libraries","level":"warn","scope":"source_only","text":"Use all 6 core libraries whenever they provide value.","libraries":["itertools - iterator pipelines (Calculations)","tap - suffix pipelines for pipe/tap/conv ergonomics","rpds - persistent data structures (Data)","im - persistent data structures for thread-sharing (Data)","thiserror - domain errors (Calculations/Data)","anyhow - boundary errors in shell (Actions)"],"notes":["These libraries enable functional patterns. Use them.","TESTS need not follow this - whatever compiles is fine."]}
{"kind":"rule","id":"errors","level":"error","text":"Domain/core uses thiserror; shell uses anyhow with context.","preferred":["thiserror enums for DomainError","anyhow::Result in main/handlers"],"bans":["Result<T,String> in core","Box<dyn Error> in core","anyhow::Error in core"],"notes":["Add context when crossing I/O/parsing boundaries."]}
{"kind":"rule","id":"functional_style","level":"warn","text":"Prefer iterator/combinator pipelines over loops and indexing.","preferred":["itertools adapters","Iterator combinators"],"bans":["for in core","while in core","manual indexing"],"notes":["Loops are usually mutation magnets; pipelines keep invariants tight."]}
{"kind":"rule","id":"no_unsafe","level":"error","text":"No unsafe unless explicitly requested with justification.","bans":["unsafe"],"notes":["If requested, confine unsafe to the smallest module and wrap it in a safe API."]}

{"kind":"lint","id":"file_header","scope":"source_only","text":"Every generated SOURCE file (not tests) includes these lints.","lines":["#![deny(clippy::unwrap_used)]","#![deny(clippy::expect_used)]","#![deny(clippy::panic)]","#![warn(clippy::pedantic)]","#![warn(clippy::nursery)]","#![forbid(unsafe_code)]"],"notes":["Test files do NOT need these lints. Tests can be messy."]}
{"kind":"gate","id":"quality_gates","text":"Source code must be flawless. Tests just need to compile.","commands":["cargo fmt --check -- src/","cargo clippy -- -D warnings -W clippy::pedantic -W clippy::nursery -- src/","cargo test","cargo build --release"],"notes":["Clippy MUST pass with ZERO warnings on source code (src/).","Fix every warning perfectly - no lazy shortcuts.","Test code quality is irrelevant - if tests compile and pass, that's enough."]}

{"kind":"philosophy","id":"test_code","text":"Test code is disposable. Quality does not matter. If it compiles and tests the behavior, ship it.","allowed_in_tests":["unwrap","expect","panic!","mut","loops","indexing","any clippy warning","any style issue"],"disallowed_in_tests":["nothing - tests are free-for-all"],"notes":["We only care about production code quality.","Tests are tools, not products.","Dirty tests that verify clean code are valuable.","Clean tests that verify broken code are worthless."]}

{"kind":"cargo_template","id":"sync","toml":"[dependencies]\nitertools = \"0.14\"\ntap = \"1.0\"\nrpds = \"1.2\"\nthiserror = \"2.0\"\nanyhow = \"1.0\"\n\n[dev-dependencies]\nproptest = \"1.0\"\n"}
{"kind":"cargo_template","id":"async_shell","toml":"[dependencies]\nitertools = \"0.14\"\ntap = \"1.0\"\nrpds = \"1.2\" # or: im = \"15.1\"\nthiserror = \"2.0\"\nanyhow = \"1.0\"\n\ntokio = { version = \"1.49\", features = [\"full\"] }\nfutures-util = \"0.3\"\ntokio-util = { version = \"0.7\", features = [\"codec\", \"compat\", \"io\"] }\n\n[dev-dependencies]\nproptest = \"1.0\"\n"}

{"kind":"pattern","id":"default_without_unwrap","text":"Defaulting without calling unwrap.*","result_example":"result.map_or_else(|_e| default_value(), |v| v)","option_example":"option.map_or_else(|| default_value(), |v| v)","notes":["Do not call any unwrap_* method; match/map_or(_else) are allowed."]}
{"kind":"pattern","id":"persistent_state","text":"State transitions are state -> state (no mut).","example":"State { events: state.events.push_back(event) }","crates":["rpds","im"],"notes":["Prefer rpds for snapshots; im only if thread-sharing matters."]}
{"kind":"pattern","id":"railway","text":"Compose fallible steps without unwrap.","example":"validate(x).and_then(parse).map(transform)","crates":["thiserror"],"notes":["Use explicit error enums; keep core total."]}
{"kind":"pattern","id":"async_shell_boundary","text":"Async I/O in shell, pure core sync.","notes":["Shell reads bytes/lines -> validates -> calls sync core functions -> returns response."]}

{"kind":"example","id":"itertools_pipeline","crates":["itertools"],"code":"use itertools::Itertools;\n\nfn top_names(input: &[String]) -> Vec<String> {\n    input\n        .iter()\n        .map(|s| s.trim())\n        .filter(|s| !s.is_empty())\n        .map(|s| s.to_string())\n        .unique()\n        .sorted()\n        .take(10)\n        .collect_vec()\n}\n"}
{"kind":"example","id":"rpds_state","crates":["rpds"],"code":"use rpds::Vector;\n\n#[derive(Clone, Debug)]\nstruct State {\n    events: Vector<String>,\n}\n\nfn append_event(state: State, event: String) -> State {\n    State {\n        events: state.events.push_back(event),\n    }\n}\n"}
{"kind":"example","id":"thiserror_domain","crates":["thiserror"],"code":"use thiserror::Error;\n\n#[derive(Debug, Error)]\npub enum DomainError {\n    #[error(\"invalid input\")]\n    InvalidInput,\n\n    #[error(\"not found: {0}\")]\n    NotFound(String),\n}\n\nfn validate_non_empty(s: &str) -> Result<&str, DomainError> {\n    match s.is_empty() {\n        true => Err(DomainError::InvalidInput),\n        false => Ok(s),\n    }\n}\n"}
{"kind":"example","id":"async_shell_lines","crates":["tokio","futures-util","tokio-util","anyhow"],"code":"use anyhow::Result;\nuse futures_util::{StreamExt, TryStreamExt};\nuse tokio::net::TcpStream;\nuse tokio_util::codec::{FramedRead, LinesCodec};\n\nasync fn read_non_empty_lines(stream: TcpStream) -> Result<Vec<String>> {\n    FramedRead::new(stream, LinesCodec::new())\n        .map_err(anyhow::Error::from)\n        .try_filter(|line| futures_util::future::ready(!line.trim().is_empty()))\n        .try_collect::<Vec<_>>()\n        .await\n}\n"}
{"kind":"mode","id":"strict_scott_ddd_types","text":"Default operating mode for AI agents: aggressively enforce DDD and type-driven modeling. Be intentionally dogmatic unless user explicitly opts out."}
{"kind":"rule","id":"strict_domain_newtypes","level":"error","scope":"source_only","text":"Domain-facing APIs must not expose primitive placeholders for domain concepts.","bans":["pub fn*(..., id: String, ...)","pub fn*(..., email: String, ...)","pub fn*(..., amount: i64, ...)","pub struct* { pub *: String } for domain concepts"],"preferred":["CustomerId","Email","Money/Cents","value objects with smart constructors"]}
{"kind":"rule","id":"strict_option_state_ban","level":"error","scope":"source_only","text":"Do not encode lifecycle or workflow state in Option fields.","bans":["status + Option<...> coupling","multiple Option fields representing state progression"],"preferred":["enum variants with state-specific payloads","type-state structs"]}
{"kind":"rule","id":"strict_boundary_parsing","level":"error","scope":"source_only","text":"Parse and constrain at boundaries only. Core receives trusted types, never raw String/JSON/DB rows.","preferred":["TryFrom at boundary","parse(...) constructors","DTO -> Domain conversion layer"]}
{"kind":"rule","id":"strict_transition_functions","level":"error","scope":"source_only","text":"Business workflows must be modeled as typed transitions: StateA -> Result<StateB, DomainError>.","preferred":["validate: Unvalidated -> Result<Validated, E>","price: Validated -> Priced","place: Priced -> Result<Placed, E>"]}
{"kind":"rule","id":"strict_exhaustive_match","level":"error","scope":"source_only","text":"All domain enum matches in core must be exhaustive with no wildcard arm that hides new states.","bans":["_ => in domain state transitions"],"preferred":["explicit arm per variant"]}
{"kind":"detector","id":"ddd_primitive_obsession_detector","query":"Scan domain modules for String/bool/i64 signatures and propose semantic replacements."}
{"kind":"detector","id":"ddd_hidden_state_detector","query":"Scan for Option-based state encoding and status-flag coupling."}
{"kind":"detector","id":"ddd_boundary_leak_detector","query":"Scan for raw input types crossing shell into core."}
{"kind":"gate","id":"strict_ddd_type_gates","text":"AI output is not acceptable unless DDD/type gates pass.","checks":["No primitive obsession in domain interfaces","No Option-as-state encoding","No bool control flags for domain branching","Boundary parsing complete before core","Explicit typed workflow transitions","Exhaustive state handling"]}
{"kind":"ref","file":"references/scott-ddd-types.md","use":"Strict DDD + type-system doctrine for AI refactors"}
{"kind":"ref","file":"references/typing-refactor-checklist.md","use":"Stepwise checklist for migrating primitives to semantic domain types"}
{"kind":"ref","file":"references/complete-workflow.md","use":"Full Data→Calculations→Actions example"}

Additional resources for progressive disclosure: