@lamentis/naome 1.3.13 → 1.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/Cargo.lock +2 -2
  2. package/crates/naome-cli/Cargo.toml +1 -1
  3. package/crates/naome-cli/src/architecture_commands.rs +1 -1
  4. package/crates/naome-cli/src/quality_commands.rs +4 -4
  5. package/crates/naome-core/Cargo.toml +1 -1
  6. package/crates/naome-core/src/architecture/config/parser/sections.rs +1 -1
  7. package/crates/naome-core/src/architecture/config.rs +1 -1
  8. package/crates/naome-core/src/architecture/model.rs +19 -0
  9. package/crates/naome-core/src/architecture/output.rs +27 -24
  10. package/crates/naome-core/src/architecture/rules/budgets.rs +2 -1
  11. package/crates/naome-core/src/architecture/rules.rs +3 -1
  12. package/crates/naome-core/src/architecture/scan/cache.rs +145 -0
  13. package/crates/naome-core/src/architecture/scan/graph_builder.rs +31 -10
  14. package/crates/naome-core/src/architecture/scan.rs +232 -14
  15. package/crates/naome-core/src/architecture.rs +3 -3
  16. package/crates/naome-core/src/intent/legacy.rs +1 -1
  17. package/crates/naome-core/src/intent/model.rs +27 -0
  18. package/crates/naome-core/src/intent/resolver.rs +3 -28
  19. package/crates/naome-core/src/intent/resolver_baseline.rs +1 -2
  20. package/crates/naome-core/src/intent/resolver_policy.rs +1 -2
  21. package/crates/naome-core/src/intent.rs +2 -1
  22. package/crates/naome-core/src/quality/adapter_ios.rs +3 -1
  23. package/crates/naome-core/src/quality/adapter_support.rs +32 -0
  24. package/crates/naome-core/src/quality/adapters.rs +8 -25
  25. package/crates/naome-core/src/quality/analysis_model.rs +33 -0
  26. package/crates/naome-core/src/quality/cache.rs +1 -1
  27. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +2 -1
  28. package/crates/naome-core/src/quality/checks/near_duplicates.rs +2 -1
  29. package/crates/naome-core/src/quality/mod.rs +1 -0
  30. package/crates/naome-core/src/quality/scanner/analysis.rs +1 -1
  31. package/crates/naome-core/src/quality/scanner.rs +1 -32
  32. package/crates/naome-core/src/quality/semantic/extract.rs +2 -1
  33. package/crates/naome-core/src/quality/structure/adapter_ios.rs +1 -1
  34. package/crates/naome-core/src/quality/structure/adapter_model.rs +23 -0
  35. package/crates/naome-core/src/quality/structure/adapters.rs +2 -23
  36. package/crates/naome-core/src/quality/structure/config.rs +2 -1
  37. package/crates/naome-core/src/quality/structure/defaults.rs +2 -2
  38. package/crates/naome-core/src/quality/structure/mod.rs +1 -0
  39. package/crates/naome-core/src/quality/structure/model.rs +6 -6
  40. package/crates/naome-core/src/repository_model.rs +6 -1
  41. package/crates/naome-core/src/route/builtin_checks.rs +1 -1
  42. package/crates/naome-core/src/route/builtin_require.rs +1 -1
  43. package/crates/naome-core/src/route/execution.rs +2 -34
  44. package/crates/naome-core/src/route/execution_baselines.rs +1 -1
  45. package/crates/naome-core/src/route/execution_model.rs +37 -0
  46. package/crates/naome-core/src/route/execution_support.rs +1 -1
  47. package/crates/naome-core/src/route/execution_tasks.rs +1 -1
  48. package/crates/naome-core/src/route/quality_gate.rs +0 -1
  49. package/crates/naome-core/src/route.rs +3 -1
  50. package/crates/naome-core/src/task_state/api.rs +5 -4
  51. package/crates/naome-core/src/task_state/commit_gate.rs +1 -1
  52. package/crates/naome-core/src/task_state/compact_proof.rs +2 -2
  53. package/crates/naome-core/src/task_state/completed_refresh.rs +0 -13
  54. package/crates/naome-core/src/task_state/completion.rs +13 -4
  55. package/crates/naome-core/src/task_state/mod.rs +1 -1
  56. package/crates/naome-core/src/task_state/progress.rs +1 -1
  57. package/crates/naome-core/src/task_state/proof.rs +1 -4
  58. package/crates/naome-core/src/task_state/proof_entry.rs +1 -1
  59. package/crates/naome-core/src/task_state/proof_model.rs +2 -18
  60. package/crates/naome-core/src/task_state/proof_sources.rs +2 -2
  61. package/crates/naome-core/src/task_state/proof_types.rs +17 -0
  62. package/crates/naome-core/src/task_state/shape.rs +1 -1
  63. package/crates/naome-core/src/task_state/task_diff_api.rs +1 -1
  64. package/crates/naome-core/src/verification_contract_policy.rs +2 -6
  65. package/crates/naome-core/src/workflow/agent/proof.rs +1 -1
  66. package/crates/naome-core/src/workflow/agent.rs +1 -1
  67. package/crates/naome-core/src/workflow/mod.rs +3 -1
  68. package/crates/naome-core/src/workflow/phase_inference.rs +1 -1
  69. package/crates/naome-core/src/workflow/phase_model.rs +22 -0
  70. package/crates/naome-core/src/workflow/phases.rs +1 -21
  71. package/crates/naome-core/tests/architecture_cache.rs +212 -0
  72. package/native/darwin-arm64/naome +0 -0
  73. package/native/linux-x64/naome +0 -0
  74. package/package.json +1 -1
  75. package/templates/naome-root/.naome/manifest.json +1 -1
  76. package/templates/naome-root/docs/naome/architecture-fitness.md +29 -8
  77. package/crates/naome-core/src/task_state/reconcile.rs +0 -7
@@ -1,14 +1,19 @@
1
1
  use std::collections::BTreeMap;
2
2
  use std::path::{Path, PathBuf};
3
3
 
4
- use serde::Serialize;
4
+ use serde::{Deserialize, Serialize};
5
5
 
6
6
  use crate::git;
7
7
  use crate::models::NaomeError;
8
8
 
9
- use super::config::{read_architecture_config, ArchitectureConfig};
9
+ use cache::CacheStatus;
10
+
11
+ use super::config::{
12
+ default_architecture_config_text, read_architecture_config, ArchitectureConfig,
13
+ };
10
14
  use super::model::ArchitectureGraph;
11
15
 
16
+ mod cache;
12
17
  mod graph_builder;
13
18
  mod imports;
14
19
  mod manifest;
@@ -28,6 +33,8 @@ pub struct ArchitectureScanReport {
28
33
  pub files_scanned: usize,
29
34
  pub changed_only_requested: bool,
30
35
  pub changed_only_degraded_to_full_scan: bool,
36
+ pub changed_only_mode: String,
37
+ pub changed_only_degradation_reason: Option<String>,
31
38
  pub changed_paths: Vec<String>,
32
39
  #[serde(skip_serializing)]
33
40
  pub config: ArchitectureConfig,
@@ -37,7 +44,7 @@ pub struct ArchitectureScanReport {
37
44
  pub manifest_facts: Vec<ManifestFact>,
38
45
  }
39
46
 
40
- #[derive(Debug, Clone, Serialize)]
47
+ #[derive(Debug, Clone, Serialize, Deserialize)]
41
48
  #[serde(rename_all = "camelCase")]
42
49
  pub struct FileFact {
43
50
  pub path: String,
@@ -49,7 +56,7 @@ pub struct FileFact {
49
56
  pub imports: Vec<ImportFact>,
50
57
  }
51
58
 
52
- #[derive(Debug, Clone, Serialize)]
59
+ #[derive(Debug, Clone, Serialize, Deserialize)]
53
60
  #[serde(rename_all = "camelCase")]
54
61
  pub struct ImportFact {
55
62
  pub specifier: String,
@@ -59,7 +66,7 @@ pub struct ImportFact {
59
66
  pub extractor: String,
60
67
  }
61
68
 
62
- #[derive(Debug, Clone, Serialize, PartialEq, Eq)]
69
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
63
70
  #[serde(rename_all = "camelCase")]
64
71
  pub enum ImportTarget {
65
72
  File(String),
@@ -67,7 +74,7 @@ pub enum ImportTarget {
67
74
  Unknown(String),
68
75
  }
69
76
 
70
- #[derive(Debug, Clone, Serialize)]
77
+ #[derive(Debug, Clone, Serialize, Deserialize)]
71
78
  #[serde(rename_all = "camelCase")]
72
79
  pub struct ManifestFact {
73
80
  pub path: String,
@@ -78,7 +85,7 @@ pub struct ManifestFact {
78
85
  pub extractor: String,
79
86
  }
80
87
 
81
- #[derive(Debug, Clone, Serialize)]
88
+ #[derive(Debug, Clone, Serialize, Deserialize)]
82
89
  #[serde(rename_all = "camelCase")]
83
90
  pub struct ManifestDependency {
84
91
  pub name: String,
@@ -94,24 +101,235 @@ pub fn scan_architecture(
94
101
  let config = read_architecture_config(root, options.config_path.as_deref())?;
95
102
  let changed_paths = changed_paths(root, options.changed_only)?;
96
103
  let files = path_scan::repository_files(root, &config)?;
104
+ let config_hash = cache::config_hash(
105
+ root,
106
+ options.config_path.as_deref(),
107
+ default_architecture_config_text(),
108
+ )?;
109
+ let file_list_hash = cache::file_list_hash(&files);
97
110
  let manifest_facts = manifest::extract_manifests(root, &files);
98
- let (mut graph, file_facts) =
99
- graph_builder::build_path_graph(root, files, &config, &manifest_facts);
100
111
 
112
+ let scan = if options.changed_only {
113
+ changed_only_scan(
114
+ root,
115
+ files,
116
+ &config,
117
+ &manifest_facts,
118
+ &changed_paths,
119
+ config_hash,
120
+ file_list_hash,
121
+ )?
122
+ } else {
123
+ full_scan(
124
+ root,
125
+ files,
126
+ &config,
127
+ &manifest_facts,
128
+ config_hash,
129
+ file_list_hash,
130
+ false,
131
+ None,
132
+ "full_scan",
133
+ )?
134
+ };
135
+
136
+ Ok(scan.with_changed_paths(changed_paths))
137
+ }
138
+
139
+ fn changed_only_scan(
140
+ root: &Path,
141
+ files: Vec<String>,
142
+ config: &ArchitectureConfig,
143
+ manifest_facts: &[ManifestFact],
144
+ changed_paths: &[String],
145
+ config_hash: String,
146
+ file_list_hash: String,
147
+ ) -> Result<ArchitectureScanReport, NaomeError> {
148
+ let Some(cache) = cache::read(root) else {
149
+ return degraded_full_scan(
150
+ root,
151
+ files,
152
+ config,
153
+ manifest_facts,
154
+ config_hash,
155
+ file_list_hash,
156
+ "cache_miss",
157
+ );
158
+ };
159
+ match cache.is_compatible(&config_hash, &file_list_hash) {
160
+ CacheStatus::Miss(reason) => degraded_full_scan(
161
+ root,
162
+ files,
163
+ config,
164
+ manifest_facts,
165
+ config_hash,
166
+ file_list_hash,
167
+ reason,
168
+ ),
169
+ CacheStatus::Hit => incremental_scan(
170
+ root,
171
+ files,
172
+ config,
173
+ manifest_facts,
174
+ changed_paths,
175
+ config_hash,
176
+ file_list_hash,
177
+ cache,
178
+ ),
179
+ }
180
+ }
181
+
182
+ fn incremental_scan(
183
+ root: &Path,
184
+ files: Vec<String>,
185
+ config: &ArchitectureConfig,
186
+ manifest_facts: &[ManifestFact],
187
+ changed_paths: &[String],
188
+ config_hash: String,
189
+ file_list_hash: String,
190
+ cache: cache::ArchitectureCache,
191
+ ) -> Result<ArchitectureScanReport, NaomeError> {
192
+ let file_set = files.iter().cloned().collect();
193
+ let changed = cache::changed_set(changed_paths);
194
+ if import_resolver_context_changed(changed_paths) {
195
+ return degraded_full_scan(
196
+ root,
197
+ files,
198
+ config,
199
+ manifest_facts,
200
+ config_hash,
201
+ file_list_hash,
202
+ "resolver_context_changed",
203
+ );
204
+ }
205
+ if cached_clean_content_changed(root, &files, &changed, &cache) {
206
+ return degraded_full_scan(
207
+ root,
208
+ files,
209
+ config,
210
+ manifest_facts,
211
+ config_hash,
212
+ file_list_hash,
213
+ "content_changed",
214
+ );
215
+ }
216
+ let mut file_facts = BTreeMap::new();
217
+ let mut scanned = 0;
218
+
219
+ for path in &files {
220
+ if changed.contains(path) || !cache.files.contains_key(path) {
221
+ let fact = graph_builder::scan_file_fact(root, path, config, &file_set);
222
+ scanned += 1;
223
+ file_facts.insert(path.clone(), fact);
224
+ } else if let Some(cached) = cache.files.get(path) {
225
+ file_facts.insert(path.clone(), cached.fact.clone());
226
+ }
227
+ }
228
+
229
+ let mut graph =
230
+ graph_builder::build_graph_from_facts(&files, &file_facts, config, manifest_facts);
101
231
  graph.sort_stable();
232
+ let _ = cache::write(root, config_hash, file_list_hash, &file_facts);
102
233
  Ok(ArchitectureScanReport {
103
234
  schema: "naome.arch.scan.v1".to_string(),
104
- files_scanned: file_facts.len(),
235
+ files_scanned: scanned,
105
236
  graph,
106
- changed_only_requested: options.changed_only,
107
- changed_only_degraded_to_full_scan: options.changed_only,
108
- changed_paths,
109
- config,
237
+ changed_only_requested: true,
238
+ changed_only_degraded_to_full_scan: false,
239
+ changed_only_mode: "incremental_cache".to_string(),
240
+ changed_only_degradation_reason: None,
241
+ changed_paths: Vec::new(),
242
+ config: config.clone(),
110
243
  file_facts,
244
+ manifest_facts: manifest_facts.to_vec(),
245
+ })
246
+ }
247
+
248
+ fn cached_clean_content_changed(
249
+ root: &Path,
250
+ files: &[String],
251
+ changed: &std::collections::BTreeSet<String>,
252
+ cache: &cache::ArchitectureCache,
253
+ ) -> bool {
254
+ files.iter().any(|path| {
255
+ !changed.contains(path)
256
+ && cache.files.get(path).is_some_and(|cached| {
257
+ cache::content_hash(root, path)
258
+ .map(|current| current != cached.content_hash)
259
+ .unwrap_or(true)
260
+ })
261
+ })
262
+ }
263
+
264
+ fn import_resolver_context_changed(changed_paths: &[String]) -> bool {
265
+ changed_paths.iter().any(|path| {
266
+ Path::new(path)
267
+ .file_name()
268
+ .and_then(|value| value.to_str())
269
+ .is_some_and(|name| matches!(name, "go.mod" | "tsconfig.json" | "jsconfig.json"))
270
+ })
271
+ }
272
+
273
+ fn degraded_full_scan(
274
+ root: &Path,
275
+ files: Vec<String>,
276
+ config: &ArchitectureConfig,
277
+ manifest_facts: &[ManifestFact],
278
+ config_hash: String,
279
+ file_list_hash: String,
280
+ reason: &'static str,
281
+ ) -> Result<ArchitectureScanReport, NaomeError> {
282
+ full_scan(
283
+ root,
284
+ files,
285
+ config,
111
286
  manifest_facts,
287
+ config_hash,
288
+ file_list_hash,
289
+ true,
290
+ Some(reason),
291
+ "degraded_full_scan",
292
+ )
293
+ }
294
+
295
+ fn full_scan(
296
+ root: &Path,
297
+ files: Vec<String>,
298
+ config: &ArchitectureConfig,
299
+ manifest_facts: &[ManifestFact],
300
+ config_hash: String,
301
+ file_list_hash: String,
302
+ changed_only_degraded_to_full_scan: bool,
303
+ changed_only_degradation_reason: Option<&str>,
304
+ changed_only_mode: &str,
305
+ ) -> Result<ArchitectureScanReport, NaomeError> {
306
+ let (mut graph, file_facts) =
307
+ graph_builder::build_path_graph(root, files, config, manifest_facts);
308
+ graph.sort_stable();
309
+ let files_scanned = file_facts.len();
310
+ let _ = cache::write(root, config_hash, file_list_hash, &file_facts);
311
+ Ok(ArchitectureScanReport {
312
+ schema: "naome.arch.scan.v1".to_string(),
313
+ files_scanned,
314
+ graph,
315
+ changed_only_requested: changed_only_degraded_to_full_scan,
316
+ changed_only_degraded_to_full_scan,
317
+ changed_only_mode: changed_only_mode.to_string(),
318
+ changed_only_degradation_reason: changed_only_degradation_reason.map(str::to_string),
319
+ changed_paths: Vec::new(),
320
+ config: config.clone(),
321
+ file_facts,
322
+ manifest_facts: manifest_facts.to_vec(),
112
323
  })
113
324
  }
114
325
 
326
+ impl ArchitectureScanReport {
327
+ fn with_changed_paths(mut self, changed_paths: Vec<String>) -> Self {
328
+ self.changed_paths = changed_paths;
329
+ self
330
+ }
331
+ }
332
+
115
333
  fn changed_paths(root: &Path, changed_only: bool) -> Result<Vec<String>, NaomeError> {
116
334
  if changed_only {
117
335
  git::changed_paths(root)
@@ -13,12 +13,12 @@ pub use config::{
13
13
  };
14
14
  pub use model::{
15
15
  ArchitectureEdge, ArchitectureEdgeKind, ArchitectureGraph, ArchitectureMetadata,
16
- ArchitectureNode, ArchitectureNodeKind, SourceRange,
16
+ ArchitectureNode, ArchitectureNodeKind, Severity, SourceRange,
17
17
  };
18
18
  pub use output::{
19
19
  format_architecture_explain, format_architecture_scan, format_architecture_validation,
20
- ArchitectureAgentFeedback, ArchitectureValidation, ArchitectureViolation, Severity,
21
- ViolationSummary, ARCHITECTURE_RULE_IDS,
20
+ ArchitectureAgentFeedback, ArchitectureValidation, ArchitectureViolation, ViolationSummary,
21
+ ARCHITECTURE_RULE_IDS,
22
22
  };
23
23
  pub use scan::{scan_architecture, ArchitectureScanOptions, ArchitectureScanReport};
24
24
 
@@ -4,8 +4,8 @@ use crate::models::Decision;
4
4
 
5
5
  use super::classifier::{has_candidate, winning_intent};
6
6
  use super::legacy_response::response_policy;
7
+ use super::model::ResolvedIntent;
7
8
  use super::model::{CanonicalIntent, IntentKind};
8
- use super::resolver::ResolvedIntent;
9
9
 
10
10
  #[derive(Debug, Clone, Serialize, PartialEq, Eq)]
11
11
  #[serde(rename_all = "camelCase")]
@@ -75,3 +75,30 @@ pub(crate) struct CanonicalIntent {
75
75
  pub has_workflow_conflict: bool,
76
76
  pub validation_errors: Vec<String>,
77
77
  }
78
+
79
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
80
+ pub(crate) enum CompletedTaskReadiness {
81
+ Valid,
82
+ ValidAfterHarnessRefresh,
83
+ ValidWithUnrelatedDirty,
84
+ Invalid,
85
+ }
86
+
87
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
88
+ pub(crate) enum DirtyDiffReadiness {
89
+ Unclassified,
90
+ HarnessRefreshOnly,
91
+ HarnessRefreshWithUnrelatedDirty,
92
+ }
93
+
94
+ #[derive(Debug, Clone, PartialEq, Eq)]
95
+ pub(crate) struct ResolvedIntent {
96
+ pub prompt_intent: IntentKind,
97
+ pub certainty: String,
98
+ pub policy_action: String,
99
+ pub allowed: bool,
100
+ pub summary: String,
101
+ pub next_action: String,
102
+ pub reason_codes: Vec<String>,
103
+ pub risk_codes: Vec<String>,
104
+ }
@@ -1,37 +1,12 @@
1
1
  use crate::models::Decision;
2
2
 
3
3
  use super::classifier::winning_intent;
4
- use super::model::{CanonicalIntent, IntentKind};
4
+ use super::model::{
5
+ CanonicalIntent, CompletedTaskReadiness, DirtyDiffReadiness, IntentKind, ResolvedIntent,
6
+ };
5
7
  use super::resolver_policy::state_policy;
6
8
  use super::resolver_shared::{ambiguous_conflict, ambiguous_empty, unsafe_policy, Policy};
7
9
 
8
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
9
- pub(crate) enum CompletedTaskReadiness {
10
- Valid,
11
- ValidAfterHarnessRefresh,
12
- ValidWithUnrelatedDirty,
13
- Invalid,
14
- }
15
-
16
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
17
- pub(crate) enum DirtyDiffReadiness {
18
- Unclassified,
19
- HarnessRefreshOnly,
20
- HarnessRefreshWithUnrelatedDirty,
21
- }
22
-
23
- #[derive(Debug, Clone, PartialEq, Eq)]
24
- pub(crate) struct ResolvedIntent {
25
- pub prompt_intent: IntentKind,
26
- pub certainty: String,
27
- pub policy_action: String,
28
- pub allowed: bool,
29
- pub summary: String,
30
- pub next_action: String,
31
- pub reason_codes: Vec<String>,
32
- pub risk_codes: Vec<String>,
33
- }
34
-
35
10
  pub(crate) fn resolve_intent(
36
11
  decision: &Decision,
37
12
  canonical: &CanonicalIntent,
@@ -1,5 +1,4 @@
1
- use super::model::IntentKind;
2
- use super::resolver::DirtyDiffReadiness;
1
+ use super::model::{DirtyDiffReadiness, IntentKind};
3
2
  use super::resolver_catalog::{
4
3
  POLICY_BASELINE_BLOCK, POLICY_BASELINE_CANCEL, POLICY_BASELINE_COMMIT_UPGRADE,
5
4
  POLICY_BASELINE_NEW_REFRESH, POLICY_BASELINE_NEW_UPGRADE, POLICY_BASELINE_NO_COMMIT,
@@ -1,7 +1,6 @@
1
1
  use crate::models::Decision;
2
2
 
3
- use super::model::IntentKind;
4
- use super::resolver::{CompletedTaskReadiness, DirtyDiffReadiness};
3
+ use super::model::{CompletedTaskReadiness, DirtyDiffReadiness, IntentKind};
5
4
  use super::resolver_active::active_policy;
6
5
  use super::resolver_baseline::{baseline_policy, dirty_policy};
7
6
  use super::resolver_catalog::{
@@ -24,7 +24,8 @@ use crate::task_state::{
24
24
  use classifier::canonical_intent;
25
25
  pub(crate) use envelope::prompt_envelope_json;
26
26
  pub use legacy::{format_intent, IntentDecision, PromptEvidence};
27
- use resolver::{resolve_intent, CompletedTaskReadiness, DirtyDiffReadiness};
27
+ use model::{CompletedTaskReadiness, DirtyDiffReadiness};
28
+ use resolver::resolve_intent;
28
29
 
29
30
  pub fn evaluate_intent(
30
31
  root: &Path,
@@ -1,4 +1,6 @@
1
- use super::adapters::{path_rule, test_file_limits};
1
+ use super::adapter_support::{
2
+ quality_path_rule as path_rule, quality_test_file_limits as test_file_limits,
3
+ };
2
4
  use super::types::{QualityLimitOverrides, QualityPathRule};
3
5
 
4
6
  pub(super) fn swift_path_rules() -> Vec<QualityPathRule> {
@@ -2,6 +2,8 @@ use std::collections::HashSet;
2
2
 
3
3
  use crate::models::NaomeError;
4
4
 
5
+ use super::types::{QualityLimitOverrides, QualityPathRule};
6
+
5
7
  pub(crate) struct RepoSignals<'a> {
6
8
  paths: &'a [String],
7
9
  }
@@ -98,6 +100,36 @@ pub(crate) fn extend_unique(target: &mut Vec<String>, values: &[&str]) {
98
100
  }
99
101
  }
100
102
 
103
+ pub(crate) fn quality_test_file_limits(max_top_level_symbols: usize) -> QualityLimitOverrides {
104
+ QualityLimitOverrides {
105
+ max_file_lines: Some(650),
106
+ max_diff_added_lines: Some(220),
107
+ max_function_lines: Some(140),
108
+ max_top_level_symbols: Some(max_top_level_symbols),
109
+ duplicate_block_lines: Some(14),
110
+ near_duplicate_similarity: Some(0.96),
111
+ ..QualityLimitOverrides::default()
112
+ }
113
+ }
114
+
115
+ pub(crate) fn quality_path_rule(
116
+ id: &str,
117
+ paths: &[&str],
118
+ limits: QualityLimitOverrides,
119
+ disabled_checks: &[&str],
120
+ ) -> QualityPathRule {
121
+ QualityPathRule {
122
+ id: id.to_string(),
123
+ paths: string_list(paths),
124
+ limits,
125
+ disabled_checks: string_list(disabled_checks),
126
+ }
127
+ }
128
+
129
+ fn string_list(values: &[&str]) -> Vec<String> {
130
+ values.iter().map(|value| (*value).to_string()).collect()
131
+ }
132
+
101
133
  pub(crate) fn detects_rust_project(signals: &RepoSignals<'_>) -> bool {
102
134
  signals.has_manifest("Cargo.toml") || signals.has_extension(&[".rs"])
103
135
  }
@@ -5,10 +5,10 @@ use super::adapter_support::{
5
5
  detected_ids, detects_generated_ios_project, detects_ios_app_structure_project,
6
6
  detects_ios_resources_project, detects_javascript_typescript_project, detects_rust_project,
7
7
  detects_swift_package_project, detects_swift_project, detects_swiftui_project,
8
- detects_xcode_project, detects_xctest_project, extend_unique, find_adapter_by_id, validate_ids,
9
- AdapterDescriptor, RepoSignals,
8
+ detects_xcode_project, detects_xctest_project, extend_unique, find_adapter_by_id,
9
+ quality_path_rule, quality_test_file_limits, validate_ids, AdapterDescriptor, RepoSignals,
10
10
  };
11
- use super::types::{QualityLimitOverrides, QualityPathRule, RepositoryQualityConfig};
11
+ use super::types::{QualityPathRule, RepositoryQualityConfig};
12
12
 
13
13
  const CONFIG_PATH: &str = ".naome/repository-quality.json";
14
14
 
@@ -163,32 +163,15 @@ fn javascript_typescript_test_paths() -> Vec<String> {
163
163
  paths
164
164
  }
165
165
 
166
- pub(super) fn test_file_limits(max_top_level_symbols: usize) -> QualityLimitOverrides {
167
- QualityLimitOverrides {
168
- max_file_lines: Some(650),
169
- max_diff_added_lines: Some(220),
170
- max_function_lines: Some(140),
171
- max_top_level_symbols: Some(max_top_level_symbols),
172
- duplicate_block_lines: Some(14),
173
- near_duplicate_similarity: Some(0.96),
174
- ..QualityLimitOverrides::default()
175
- }
166
+ fn test_file_limits(max_top_level_symbols: usize) -> super::types::QualityLimitOverrides {
167
+ quality_test_file_limits(max_top_level_symbols)
176
168
  }
177
169
 
178
- pub(super) fn path_rule(
170
+ fn path_rule(
179
171
  id: &str,
180
172
  paths: &[&str],
181
- limits: QualityLimitOverrides,
173
+ limits: super::types::QualityLimitOverrides,
182
174
  disabled_checks: &[&str],
183
175
  ) -> QualityPathRule {
184
- QualityPathRule {
185
- id: id.to_string(),
186
- paths: string_list(paths),
187
- limits,
188
- disabled_checks: string_list(disabled_checks),
189
- }
190
- }
191
-
192
- fn string_list(values: &[&str]) -> Vec<String> {
193
- values.iter().map(|value| (*value).to_string()).collect()
176
+ quality_path_rule(id, paths, limits, disabled_checks)
194
177
  }
@@ -0,0 +1,33 @@
1
+ use std::collections::HashSet;
2
+
3
+ #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
4
+ pub struct FileAnalysis {
5
+ pub path: String,
6
+ pub line_count: usize,
7
+ pub added_lines: usize,
8
+ pub raw_lines: Vec<String>,
9
+ pub normalized_lines: Vec<NormalizedLine>,
10
+ pub symbols: Vec<SymbolAnalysis>,
11
+ }
12
+
13
+ #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
14
+ pub struct NormalizedLine {
15
+ pub line_number: usize,
16
+ pub value: String,
17
+ }
18
+
19
+ #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
20
+ pub struct SymbolAnalysis {
21
+ pub kind: String,
22
+ pub name: String,
23
+ pub start_line: usize,
24
+ pub end_line: usize,
25
+ pub indent: usize,
26
+ pub tokens: HashSet<String>,
27
+ }
28
+
29
+ impl SymbolAnalysis {
30
+ pub fn line_count(&self) -> usize {
31
+ self.end_line.saturating_sub(self.start_line) + 1
32
+ }
33
+ }
@@ -7,7 +7,7 @@ use sha2::{Digest, Sha256};
7
7
 
8
8
  use crate::models::NaomeError;
9
9
 
10
- use super::scanner::FileAnalysis;
10
+ use super::analysis_model::FileAnalysis;
11
11
  use super::types::RepositoryQualityConfig;
12
12
 
13
13
  const CACHE_RELATIVE_PATH: &str = ".naome/cache/quality";
@@ -2,7 +2,8 @@ use std::collections::{HashMap, HashSet};
2
2
 
3
3
  use sha2::{Digest, Sha256};
4
4
 
5
- use super::super::scanner::{stable_fingerprint, NormalizedLine, QualityContext};
5
+ use super::super::analysis_model::NormalizedLine;
6
+ use super::super::scanner::{stable_fingerprint, QualityContext};
6
7
  use super::super::types::QualityViolation;
7
8
  use super::{is_code_like_path, QualityCheck};
8
9
 
@@ -1,6 +1,7 @@
1
1
  use std::collections::HashSet;
2
2
 
3
- use super::super::scanner::{FileAnalysis, QualityContext, SymbolAnalysis};
3
+ use super::super::analysis_model::{FileAnalysis, SymbolAnalysis};
4
+ use super::super::scanner::QualityContext;
4
5
  use super::super::types::QualityViolation;
5
6
  use super::{is_code_like_path, violation, QualityCheck};
6
7
 
@@ -1,6 +1,7 @@
1
1
  mod adapter_ios;
2
2
  mod adapter_support;
3
3
  mod adapters;
4
+ mod analysis_model;
4
5
  mod baseline;
5
6
  mod cache;
6
7
  mod checks;
@@ -4,7 +4,7 @@ use std::collections::{HashMap, HashSet};
4
4
  use std::fs;
5
5
  use std::path::Path;
6
6
 
7
- use super::{FileAnalysis, NormalizedLine, SymbolAnalysis};
7
+ use crate::quality::analysis_model::{FileAnalysis, NormalizedLine, SymbolAnalysis};
8
8
  use crate::quality::cache::{content_hash, QualityCache};
9
9
  use normalize::{normalize_line, token_set};
10
10
 
@@ -11,6 +11,7 @@ use crate::{git, models::NaomeError, paths};
11
11
  pub(crate) use repo_paths::collect_repo_paths;
12
12
  use repo_paths::{added_lines_by_path, regular_repo_file_path, tracked_blob_hashes};
13
13
 
14
+ use super::analysis_model::FileAnalysis;
14
15
  use super::cache::QualityCache;
15
16
  use super::types::{
16
17
  default_generated_paths, default_ignored_paths, QualityLimits, QualityMode,
@@ -55,38 +56,6 @@ impl QualityContext {
55
56
  }
56
57
  }
57
58
 
58
- #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
59
- pub struct FileAnalysis {
60
- pub path: String,
61
- pub line_count: usize,
62
- pub added_lines: usize,
63
- pub raw_lines: Vec<String>,
64
- pub normalized_lines: Vec<NormalizedLine>,
65
- pub symbols: Vec<SymbolAnalysis>,
66
- }
67
-
68
- #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
69
- pub struct NormalizedLine {
70
- pub line_number: usize,
71
- pub value: String,
72
- }
73
-
74
- #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
75
- pub struct SymbolAnalysis {
76
- pub kind: String,
77
- pub name: String,
78
- pub start_line: usize,
79
- pub end_line: usize,
80
- pub indent: usize,
81
- pub tokens: HashSet<String>,
82
- }
83
-
84
- impl SymbolAnalysis {
85
- pub fn line_count(&self) -> usize {
86
- self.end_line.saturating_sub(self.start_line) + 1
87
- }
88
- }
89
-
90
59
  pub fn scan_repository(
91
60
  root: &Path,
92
61
  mode: QualityMode,