RFC-003: Generate Command Decomposition Plan (Q1 2026)¶
- Status: Completed (G1-G6 merged)
- Date: 2026-02-09
- Owner: DX/Core
- Scope:
crates/assay-cli/src/cli/commands/generate.rs - Related:
docs/architecture/RFC-002-code-health-remediation-q1-2026.md(E5)docs/architecture/CODE-ANALYSIS-REPORT.md(#27)
0. Status Evidence (mechanical)¶
| Item | Status | Reference | Merge SHA | Date |
|---|---|---|---|---|
| G1 | Merged | PR #260 | 99588b59 | 2026-02-09 |
| G2 | Merged | PR #262 | 545fcd09 | 2026-02-09 |
| G3 | Merged | PR #264 | 059e23d2 | 2026-02-09 |
| G4 | Merged | PR #266 | a661b911 | 2026-02-09 |
| G5 | Merged | PR #268 | b3d386bf | 2026-02-09 |
| G6 | Merged | PR #271 | f21c85ef | 2026-02-09 |
| Generate validate finite checks | Merged | PR #270 | 7cc96a8a | 2026-02-10 |
1. Context¶
generate.rs is currently ~1166 lines and combines multiple concerns:
- CLI argument model and validation
- Policy/output DTOs and serialization
- Trace ingestion (
read_events) and aggregation - Profile-based classification logic
- Policy diffing and reporting
- Top-level command orchestration (
run) - Unit tests for several subsystems
This makes behavior changes harder to review and increases accidental drift risk when touching one concern.
2. Current Shape (Verified)¶
Current file: crates/assay-cli/src/cli/commands/generate.rs (1166 lines).
High-level concern map:
- Args + validation:
GenerateArgs,validate - Model DTOs:
Policy,Meta,Section,NetSection,Entry - Single-run path:
read_events,aggregate,generate_from_trace - Profile path:
generate_from_profile,classify_entry,make_entry_profile - Diff path:
PolicyDiffhelpers,diff_policies,print_policy_diff - Entry point:
run - Tests: classification + diff behavior
3. Constraints (Hard Stop-Lines)¶
- No output contract changes:
- No schema drift in generated policy format (yaml/json)
- No CLI flag behavior changes
- No change in exit code behavior (
runstill returnsResult<i32>, withOk(0)on success) - Golden assertions must prove policy output,
--diffoutput, andread_eventsdiagnostics remain stable - No semantic drift in classification:
- Wilson/laplace/min_runs/new_is_risky logic remains identical
- No diff-output semantic drift:
- Added/removed/changed calculation remains identical
- Output order remains deterministic independent of insertion order
- No hidden tolerance changes:
read_eventsparse/skip/error-rate behavior unchanged
4. Research Baseline (Best Practices, Feb 2026)¶
Primary guidance used for this plan:
- Rust API Guidelines: keep modules focused and APIs explicit (
C-CONV,C-STRUCT,C-ITER). - https://rust-lang.github.io/api-guidelines/checklist.html
- Clippy docs: prefer specific maintainability lints and tests over abstract complexity scores.
- https://rust-lang.github.io/rust-clippy/stable/index.html
- Test execution discipline: deterministic, fast feedback loops (
nextestand targeted gates). - https://www.nexte.st/
Applied policy for this RFC:
- Test-first characterization on behavior-sensitive paths
- Extract-only changes per phase
- Small, linear PRs with explicit non-goals
5. Target Module Layout¶
Proposed end-state under crates/assay-cli/src/cli/commands/generate/:
mod.rs:- public
run(args: GenerateArgs) -> Result<i32> - module wiring/re-exports
args.rs:GenerateArgsGenerateArgs::validatemodel.rs:Policy,Meta,Section,NetSection,Entryserializeingest.rs:Stats,Aggregatedread_events,aggregateprofile.rs:generate_from_trace,generate_from_profileclassify_entry,make_entry_profile,make_entry_simplediff.rs:EntryFingerprint,EntryChange,SectionDiff,PolicyDiffparse_existing_policy,diff_policies,print_policy_diff- tests:
- prefer per-module
#[cfg(test)]for locality and to avoid widening visibility - keep helper visibility minimal (
pub(crate)only when required)
Compatibility requirement:
- Keep command dispatch path unchanged (
generate::runremains callable from command router).
6. Execution Plan¶
G1 - Freeze Tests (Behavior Characterization)¶
Add targeted characterization tests before moving logic:
read_eventscontract:- skip empty/comment lines
- unparsable lines count/warn behavior
- hard error when all lines invalid
- warnings snapshot: stable core diagnostics for skipped lines and high-error-rate path
runmode gating:- requires exactly one of
--inputor--profile - explicit edge cases: both-set, neither-set,
--diffwith missing output file - assert error class and expected exit behavior at command boundary
- classification invariants:
- stable allow/review/skip transitions
- risk override precedence
- min-runs gate behavior
- diff invariants:
- added/removed/changed semantics unchanged
- deterministic stderr output independent of insertion order
- output goldens:
- generated policy YAML snapshot with normalized dynamic fields
--diffstderr snapshot with stable summary line and(no changes)path
G1 minimal test matrix (must exist before any extraction PR):
generate_contract_policy_yaml_goldengenerate_contract_diff_stderr_goldengenerate_contract_read_events_warnings_goldengenerate_contract_mode_gating_none_or_bothgenerate_contract_mode_gating_diff_missing_outputgenerate_contract_diff_deterministic_on_shuffled_input
Gate:
cargo test -p assay-cli generate -- --nocapturecargo check -p assay-cli
G2 - Extract DTO/Args Layer (No Logic Moves Yet)¶
Scope:
- Move args + model DTOs +
serializetoargs.rs/model.rs - Keep function bodies unchanged
- Preserve serde/clap attrs exactly (no rename/default/order drift)
- Preserve default values exactly (including float defaults and bool switches)
Gate:
cargo test -p assay-cli generate -- --nocapturecargo clippy -p assay-cli -- -D warnings
G3 - Extract Ingestion/Aggregation¶
Scope:
- Move
Stats,Aggregated,read_events,aggregateintoingest.rs - Preserve all warning/error strings
- Consolidate diagnostics through local constants/helpers to reduce accidental wording drift
Gate:
cargo test -p assay-cli generate -- --nocapturecargo check -p assay-cli
G4 - Extract Profile/Classification Logic¶
Scope:
- Move profile generation/classification helpers into
profile.rs - Keep algorithm and ordering unchanged
- Guardrail: if
profile.rsgrows beyond ~400 LOC, split follow-up intoclassify.rsandprofile_emit.rs
Gate:
cargo test -p assay-cli generate -- --nocapturecargo clippy -p assay-cli -- -D warnings
G5 - Extract Diff Subsystem¶
Scope:
- Move diff structs/helpers into
diff.rs - Keep summary output format and counts unchanged
Gate:
cargo test -p assay-cli generate -- --nocapturecargo check -p assay-cli
G6 - Final Orchestration Cleanup¶
Scope:
- Reduce
mod.rsto orchestration only - Keep
runsignature and return semantics unchanged - Keep dispatch compatibility:
crate::cli::commands::generate::runremains unchanged for callers - No non-generate command import churn beyond module path rewiring
Gate:
cargo test -p assay-cli generate -- --nocapturecargo test -p assay-cli --lib -- --nocapturecargo clippy -p assay-cli -- -D warnings
7. PR Slicing Strategy¶
Recommended PR sequence:
- PR-G1: tests-only freeze
- PR-G2: args/model extraction
- PR-G3: ingest extraction
- PR-G4: profile extraction
- PR-G5: diff extraction
- PR-G6: final
mod.rscleanup
Each PR must include:
- In-scope section
- Non-goals section
- Contract gates section
- Output impact statement (
none) - Acceptance checks section with explicit old->new mapping for moved functions
8. Risks and Mitigations¶
- Risk: subtle classification drift during extraction
- Mitigation: G1 characterization tests and unchanged helper signatures
- Risk: diff noise or changed reporting semantics
- Mitigation: freeze diff tests and keep summary formatting assertions
- Risk: accidental CLI behavior drift
- Mitigation: mode-gating tests and unchanged
runentrypoint - Risk: snapshot brittleness from dynamic fields
- Mitigation: normalize timestamp/path fields in golden helpers, assert stable core diagnostics
9. Definition of Done¶
RFC-003 is done when:
generate.rsorchestration module is reduced and concerns are split into focused modules- Existing behavior is preserved under frozen tests
- No output/schema/exit behavior changes are introduced
- All G1-G6 gates are green on CI