@lamentis/naome 1.3.0 → 1.3.1

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 (137) hide show
  1. package/Cargo.lock +2 -2
  2. package/README.md +11 -2
  3. package/bin/naome.js +62 -24
  4. package/crates/naome-cli/Cargo.toml +1 -1
  5. package/crates/naome-cli/src/context_commands.rs +47 -0
  6. package/crates/naome-cli/src/dispatcher.rs +6 -0
  7. package/crates/naome-cli/src/main.rs +43 -6
  8. package/crates/naome-cli/src/quality_commands.rs +31 -46
  9. package/crates/naome-cli/src/quality_output.rs +34 -0
  10. package/crates/naome-cli/src/quality_reconcile_command.rs +45 -0
  11. package/crates/naome-cli/src/repository_model_commands.rs +84 -0
  12. package/crates/naome-cli/src/task_commands.rs +62 -0
  13. package/crates/naome-cli/src/workflow_commands.rs +100 -3
  14. package/crates/naome-core/Cargo.toml +1 -1
  15. package/crates/naome-core/src/context/helpers.rs +75 -0
  16. package/crates/naome-core/src/context/select.rs +134 -0
  17. package/crates/naome-core/src/context/types.rs +43 -0
  18. package/crates/naome-core/src/context.rs +6 -0
  19. package/crates/naome-core/src/decision/states.rs +1 -1
  20. package/crates/naome-core/src/decision.rs +4 -1
  21. package/crates/naome-core/src/install_plan.rs +18 -0
  22. package/crates/naome-core/src/journal.rs +2 -7
  23. package/crates/naome-core/src/lib.rs +33 -10
  24. package/crates/naome-core/src/quality/adapter_ios.rs +131 -0
  25. package/crates/naome-core/src/quality/adapter_support.rs +67 -0
  26. package/crates/naome-core/src/quality/adapters.rs +81 -18
  27. package/crates/naome-core/src/quality/cache.rs +7 -9
  28. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +4 -7
  29. package/crates/naome-core/src/quality/config.rs +21 -3
  30. package/crates/naome-core/src/quality/mod.rs +138 -7
  31. package/crates/naome-core/src/quality/reconcile.rs +138 -0
  32. package/crates/naome-core/src/quality/reconcile_anchors.rs +64 -0
  33. package/crates/naome-core/src/quality/scanner/analysis.rs +20 -5
  34. package/crates/naome-core/src/quality/scanner.rs +62 -17
  35. package/crates/naome-core/src/quality/semantic/checks.rs +17 -0
  36. package/crates/naome-core/src/quality/semantic/route.rs +1 -1
  37. package/crates/naome-core/src/quality/structure/adapter_ios.rs +149 -0
  38. package/crates/naome-core/src/quality/structure/adapters.rs +60 -42
  39. package/crates/naome-core/src/quality/structure/checks/directory.rs +6 -4
  40. package/crates/naome-core/src/quality/structure/classify/roles.rs +51 -5
  41. package/crates/naome-core/src/quality/structure/config.rs +24 -3
  42. package/crates/naome-core/src/quality/structure/mod.rs +3 -0
  43. package/crates/naome-core/src/quality/types.rs +20 -1
  44. package/crates/naome-core/src/repository_model/detect.rs +188 -0
  45. package/crates/naome-core/src/repository_model/explain.rs +121 -0
  46. package/crates/naome-core/src/repository_model/path_scan.rs +67 -0
  47. package/crates/naome-core/src/repository_model/path_support.rs +59 -0
  48. package/crates/naome-core/src/repository_model/types.rs +152 -0
  49. package/crates/naome-core/src/repository_model/world.rs +48 -0
  50. package/crates/naome-core/src/repository_model/world_adapters.rs +145 -0
  51. package/crates/naome-core/src/repository_model/world_path_facts.rs +55 -0
  52. package/crates/naome-core/src/repository_model/world_paths.rs +168 -0
  53. package/crates/naome-core/src/repository_model.rs +164 -0
  54. package/crates/naome-core/src/route/builtin_checks.rs +40 -1
  55. package/crates/naome-core/src/task_ledger/import.rs +142 -0
  56. package/crates/naome-core/src/task_ledger/model.rs +13 -0
  57. package/crates/naome-core/src/task_ledger/proof_record.rs +52 -0
  58. package/crates/naome-core/src/task_ledger/read.rs +118 -0
  59. package/crates/naome-core/src/task_ledger/render.rs +55 -0
  60. package/crates/naome-core/src/task_ledger/write.rs +38 -0
  61. package/crates/naome-core/src/task_ledger.rs +48 -0
  62. package/crates/naome-core/src/task_state/api.rs +4 -2
  63. package/crates/naome-core/src/task_state/completed_refresh.rs +5 -16
  64. package/crates/naome-core/src/task_state/diff.rs +2 -2
  65. package/crates/naome-core/src/task_state/evidence.rs +8 -3
  66. package/crates/naome-core/src/task_state/mod.rs +1 -1
  67. package/crates/naome-core/src/task_state/progress.rs +13 -0
  68. package/crates/naome-core/src/task_state/proof_model.rs +8 -8
  69. package/crates/naome-core/src/task_state/repair.rs +2 -2
  70. package/crates/naome-core/src/task_state/task_diff_api.rs +9 -18
  71. package/crates/naome-core/src/task_state/types.rs +24 -0
  72. package/crates/naome-core/src/verification.rs +29 -18
  73. package/crates/naome-core/src/workflow/agent/capability.rs +194 -0
  74. package/crates/naome-core/src/workflow/agent/context_delta.rs +42 -0
  75. package/crates/naome-core/src/workflow/agent/decision.rs +32 -0
  76. package/crates/naome-core/src/workflow/agent/execution.rs +80 -0
  77. package/crates/naome-core/src/workflow/agent/proof.rs +24 -0
  78. package/crates/naome-core/src/workflow/agent/support.rs +58 -0
  79. package/crates/naome-core/src/workflow/agent/watchdog.rs +47 -0
  80. package/crates/naome-core/src/workflow/agent.rs +34 -0
  81. package/crates/naome-core/src/workflow/agent_types.rs +105 -0
  82. package/crates/naome-core/src/workflow/doctor.rs +39 -0
  83. package/crates/naome-core/src/workflow/mod.rs +11 -0
  84. package/crates/naome-core/src/workflow/output.rs +8 -2
  85. package/crates/naome-core/src/workflow/phase_inference.rs +1 -1
  86. package/crates/naome-core/tests/context.rs +99 -0
  87. package/crates/naome-core/tests/harness_health.rs +4 -0
  88. package/crates/naome-core/tests/install_plan.rs +12 -0
  89. package/crates/naome-core/tests/quality.rs +178 -2
  90. package/crates/naome-core/tests/quality_performance.rs +39 -2
  91. package/crates/naome-core/tests/quality_structure_adapters.rs +39 -0
  92. package/crates/naome-core/tests/repo_support/mod.rs +5 -1
  93. package/crates/naome-core/tests/repo_support/verification_values.rs +55 -0
  94. package/crates/naome-core/tests/repository_model.rs +281 -0
  95. package/crates/naome-core/tests/route_user_diff.rs +49 -1
  96. package/crates/naome-core/tests/semantic_legacy.rs +72 -38
  97. package/crates/naome-core/tests/task_ledger.rs +328 -0
  98. package/crates/naome-core/tests/task_state.rs +28 -0
  99. package/crates/naome-core/tests/verification.rs +29 -36
  100. package/crates/naome-core/tests/workflow_agent.rs +233 -0
  101. package/crates/naome-core/tests/workflow_agent_support/mod.rs +159 -0
  102. package/crates/naome-core/tests/workflow_doctor.rs +21 -0
  103. package/installer/codex-hooks.js +121 -0
  104. package/installer/context.js +10 -0
  105. package/installer/filesystem.js +4 -0
  106. package/installer/flows.js +8 -4
  107. package/installer/harness-files.js +6 -0
  108. package/installer/install-plan.js +4 -0
  109. package/installer/main.js +1 -1
  110. package/installer/native.js +1 -1
  111. package/native/darwin-arm64/naome +0 -0
  112. package/native/linux-x64/naome +0 -0
  113. package/package.json +1 -1
  114. package/templates/naome-root/.codex/config.toml +2 -0
  115. package/templates/naome-root/.codex/hooks.json +70 -0
  116. package/templates/naome-root/.naome/bin/check-harness-health.js +8 -6
  117. package/templates/naome-root/.naome/bin/check-task-state.js +12 -7
  118. package/templates/naome-root/.naome/bin/codex-hook-io.js +122 -0
  119. package/templates/naome-root/.naome/bin/codex-hook-policy.js +180 -0
  120. package/templates/naome-root/.naome/bin/codex-hook-runtime.js +174 -0
  121. package/templates/naome-root/.naome/bin/codex-hook.js +6 -0
  122. package/templates/naome-root/.naome/bin/naome.js +35 -4
  123. package/templates/naome-root/.naome/manifest.json +12 -6
  124. package/templates/naome-root/.naome/repository-model.json +6 -0
  125. package/templates/naome-root/.naome/repository-quality.json +3 -1
  126. package/templates/naome-root/.naome/verification.json +15 -1
  127. package/templates/naome-root/AGENTS.md +38 -83
  128. package/templates/naome-root/docs/naome/agent-workflow.md +54 -18
  129. package/templates/naome-root/docs/naome/codex-hooks.md +82 -0
  130. package/templates/naome-root/docs/naome/context-economy.md +73 -0
  131. package/templates/naome-root/docs/naome/first-run.md +25 -14
  132. package/templates/naome-root/docs/naome/index.md +18 -10
  133. package/templates/naome-root/docs/naome/repository-model.md +92 -0
  134. package/templates/naome-root/docs/naome/repository-quality.md +47 -7
  135. package/templates/naome-root/docs/naome/repository-structure.md +10 -3
  136. package/templates/naome-root/docs/naome/task-ledger.md +71 -0
  137. package/templates/naome-root/docs/naome/testing.md +16 -3
@@ -0,0 +1,149 @@
1
+ use crate::quality::adapter_support::{
2
+ detects_generated_ios_project, detects_ios_app_structure_project,
3
+ detects_ios_resources_project, detects_swift_package_project, detects_swift_project,
4
+ detects_swiftui_project, detects_xcode_project, detects_xctest_project,
5
+ };
6
+
7
+ use super::adapters::StructureAdapter;
8
+
9
+ pub(super) fn adapters() -> Vec<StructureAdapter> {
10
+ let mut adapters = Vec::new();
11
+ adapters.extend_from_slice(IOS_CORE_STRUCTURE_ADAPTERS);
12
+ adapters.extend_from_slice(IOS_APP_STRUCTURE_ADAPTERS);
13
+ adapters
14
+ }
15
+
16
+ const IOS_CORE_STRUCTURE_ADAPTERS: &[StructureAdapter] = &[
17
+ StructureAdapter {
18
+ id: "swift",
19
+ detect: detects_swift_project,
20
+ source_roots: &[
21
+ "Sources/**",
22
+ "**/Sources/**",
23
+ "Source/**",
24
+ "App/**",
25
+ "Features/**",
26
+ "Modules/**",
27
+ "Shared/**",
28
+ ],
29
+ test_roots: &[],
30
+ generated_roots: &[],
31
+ artifact_roots: &[],
32
+ module_roots: &[
33
+ "Sources/*/**",
34
+ "**/Sources/*/**",
35
+ "App/**",
36
+ "Features/*/**",
37
+ "Modules/*/**",
38
+ ],
39
+ allowed_root_files: &[],
40
+ },
41
+ StructureAdapter {
42
+ id: "xcode",
43
+ detect: detects_xcode_project,
44
+ source_roots: &[],
45
+ test_roots: &[],
46
+ generated_roots: &[],
47
+ artifact_roots: &[
48
+ "DerivedData/**",
49
+ "**/DerivedData/**",
50
+ "Build/**",
51
+ "**/Build/**",
52
+ "**/*.xcarchive/**",
53
+ ],
54
+ module_roots: &[],
55
+ allowed_root_files: &["*.xcodeproj", "*.xcworkspace"],
56
+ },
57
+ StructureAdapter {
58
+ id: "xctest",
59
+ detect: detects_xctest_project,
60
+ source_roots: &[],
61
+ test_roots: &[
62
+ "Tests/**",
63
+ "**/Tests/**",
64
+ "*Tests/**",
65
+ "**/*Tests/**",
66
+ "*UITests/**",
67
+ "**/*UITests/**",
68
+ ],
69
+ generated_roots: &[],
70
+ artifact_roots: &[],
71
+ module_roots: &[],
72
+ allowed_root_files: &[],
73
+ },
74
+ ];
75
+
76
+ const IOS_APP_STRUCTURE_ADAPTERS: &[StructureAdapter] = &[
77
+ StructureAdapter {
78
+ id: "swiftui",
79
+ detect: detects_swiftui_project,
80
+ source_roots: &[
81
+ "Views/**",
82
+ "**/Views/**",
83
+ "Preview Content/**",
84
+ "**/Preview Content/**",
85
+ ],
86
+ test_roots: &[],
87
+ generated_roots: &[],
88
+ artifact_roots: &[],
89
+ module_roots: &["Views/*/**", "**/Views/*/**"],
90
+ allowed_root_files: &[],
91
+ },
92
+ StructureAdapter {
93
+ id: "ios-app-structure",
94
+ detect: detects_ios_app_structure_project,
95
+ source_roots: &[
96
+ "App/**",
97
+ "Application/**",
98
+ "Features/**",
99
+ "Modules/**",
100
+ "Shared/**",
101
+ ],
102
+ test_roots: &[],
103
+ generated_roots: &[],
104
+ artifact_roots: &[],
105
+ module_roots: &["Features/*/**", "Modules/*/**", "App/**"],
106
+ allowed_root_files: &["Info.plist", "*.entitlements", "Podfile", "Cartfile"],
107
+ },
108
+ StructureAdapter {
109
+ id: "swift-package",
110
+ detect: detects_swift_package_project,
111
+ source_roots: &["Sources/**", "**/Sources/**"],
112
+ test_roots: &["Tests/**", "**/Tests/**"],
113
+ generated_roots: &[],
114
+ artifact_roots: &[],
115
+ module_roots: &["Sources/*/**", "**/Sources/*/**"],
116
+ allowed_root_files: &["Package.swift", "Package.resolved"],
117
+ },
118
+ StructureAdapter {
119
+ id: "ios-resources",
120
+ detect: detects_ios_resources_project,
121
+ source_roots: &["Resources/**", "**/Resources/**"],
122
+ test_roots: &[],
123
+ generated_roots: &[],
124
+ artifact_roots: &[],
125
+ module_roots: &["Resources/**", "**/Resources/**"],
126
+ allowed_root_files: &["Info.plist", "*.entitlements"],
127
+ },
128
+ StructureAdapter {
129
+ id: "generated-ios",
130
+ detect: detects_generated_ios_project,
131
+ source_roots: &[],
132
+ test_roots: &[],
133
+ generated_roots: &[
134
+ "Generated/**",
135
+ "**/Generated/**",
136
+ "SwiftGen/**",
137
+ "**/SwiftGen/**",
138
+ "Sourcery/**",
139
+ "**/Sourcery/**",
140
+ "**/*.generated.swift",
141
+ "**/*.pb.swift",
142
+ "**/*.grpc.swift",
143
+ "**/R.generated.swift",
144
+ ],
145
+ artifact_roots: &[],
146
+ module_roots: &[],
147
+ allowed_root_files: &[],
148
+ },
149
+ ];
@@ -5,17 +5,21 @@ use crate::quality::adapter_support::{
5
5
  find_adapter_by_id, validate_ids, AdapterDescriptor, RepoSignals,
6
6
  };
7
7
 
8
+ use super::adapter_ios;
8
9
  use super::model::RepositoryStructureConfig;
9
10
 
10
11
  const CONFIG_PATH: &str = ".naome/repository-structure.json";
11
12
 
12
- struct StructureAdapter {
13
- id: &'static str,
14
- detect: fn(&RepoSignals<'_>) -> bool,
15
- source_roots: &'static [&'static str],
16
- test_roots: &'static [&'static str],
17
- module_roots: &'static [&'static str],
18
- allowed_root_files: &'static [&'static str],
13
+ #[derive(Clone, Copy)]
14
+ pub(super) struct StructureAdapter {
15
+ pub(super) id: &'static str,
16
+ pub(super) detect: fn(&RepoSignals<'_>) -> bool,
17
+ pub(super) source_roots: &'static [&'static str],
18
+ pub(super) test_roots: &'static [&'static str],
19
+ pub(super) generated_roots: &'static [&'static str],
20
+ pub(super) artifact_roots: &'static [&'static str],
21
+ pub(super) module_roots: &'static [&'static str],
22
+ pub(super) allowed_root_files: &'static [&'static str],
19
23
  }
20
24
 
21
25
  impl AdapterDescriptor for StructureAdapter {
@@ -29,17 +33,21 @@ impl AdapterDescriptor for StructureAdapter {
29
33
  }
30
34
 
31
35
  pub fn detected_structure_adapter_ids(paths: &[String]) -> Vec<String> {
32
- detected_ids(paths, registry())
36
+ let registry = registry();
37
+ detected_ids(paths, &registry)
33
38
  }
34
39
 
35
40
  pub fn apply_structure_adapters(
36
41
  mut config: RepositoryStructureConfig,
37
42
  ) -> Result<RepositoryStructureConfig, NaomeError> {
38
- validate_structure_adapter_ids(&config.enabled_adapters)?;
43
+ let registry = registry();
44
+ validate_ids(&config.enabled_adapters, &registry, CONFIG_PATH)?;
39
45
  for adapter_id in config.enabled_adapters.clone() {
40
- let adapter = find_adapter_by_id(registry(), &adapter_id, CONFIG_PATH)?;
46
+ let adapter = find_adapter_by_id(&registry, &adapter_id, CONFIG_PATH)?;
41
47
  extend_unique(&mut config.source_roots, adapter.source_roots);
42
48
  extend_unique(&mut config.test_roots, adapter.test_roots);
49
+ extend_unique(&mut config.generated_roots, adapter.generated_roots);
50
+ extend_unique(&mut config.artifact_roots, adapter.artifact_roots);
43
51
  extend_unique(&mut config.module_roots, adapter.module_roots);
44
52
  extend_unique(&mut config.allowed_root_files, adapter.allowed_root_files);
45
53
  }
@@ -47,38 +55,48 @@ pub fn apply_structure_adapters(
47
55
  }
48
56
 
49
57
  pub fn validate_structure_adapter_ids(ids: &[String]) -> Result<(), NaomeError> {
50
- validate_ids(ids, registry(), CONFIG_PATH)
58
+ let registry = registry();
59
+ validate_ids(ids, &registry, CONFIG_PATH)
51
60
  }
52
61
 
53
- fn registry() -> &'static [StructureAdapter] {
54
- &[
55
- StructureAdapter {
56
- id: "rust",
57
- detect: detects_rust_project,
58
- source_roots: &["src/**", "crates/*/src/**", "**/crates/*/src/**"],
59
- test_roots: &["tests/**", "crates/*/tests/**", "**/crates/*/tests/**"],
60
- module_roots: &["src/**", "crates/*/src/**", "**/crates/*/src/**"],
61
- allowed_root_files: &["Cargo.toml", "Cargo.lock"],
62
- },
63
- StructureAdapter {
64
- id: "javascript-typescript",
65
- detect: detects_javascript_typescript_project,
66
- source_roots: &[
67
- "src/**",
68
- "app/**",
69
- "pages/**",
70
- "components/**",
71
- "packages/*/src/**",
72
- "**/packages/*/src/**",
73
- ],
74
- test_roots: &["test/**", "tests/**", "__tests__/**", "packages/*/tests/**"],
75
- module_roots: &["src/**", "app/**", "packages/*/src/**"],
76
- allowed_root_files: &[
77
- "package.json",
78
- "tsconfig.json",
79
- "vite.config.ts",
80
- "next.config.js",
81
- ],
82
- },
83
- ]
62
+ const BASE_STRUCTURE_ADAPTERS: &[StructureAdapter] = &[
63
+ StructureAdapter {
64
+ id: "rust",
65
+ detect: detects_rust_project,
66
+ source_roots: &["src/**", "crates/*/src/**", "**/crates/*/src/**"],
67
+ test_roots: &["tests/**", "crates/*/tests/**", "**/crates/*/tests/**"],
68
+ generated_roots: &[],
69
+ artifact_roots: &[],
70
+ module_roots: &["src/**", "crates/*/src/**", "**/crates/*/src/**"],
71
+ allowed_root_files: &["Cargo.toml", "Cargo.lock"],
72
+ },
73
+ StructureAdapter {
74
+ id: "javascript-typescript",
75
+ detect: detects_javascript_typescript_project,
76
+ source_roots: &[
77
+ "src/**",
78
+ "app/**",
79
+ "pages/**",
80
+ "components/**",
81
+ "packages/*/src/**",
82
+ "**/packages/*/src/**",
83
+ ],
84
+ test_roots: &["test/**", "tests/**", "__tests__/**", "packages/*/tests/**"],
85
+ generated_roots: &[],
86
+ artifact_roots: &["coverage/**", "**/coverage/**", ".next/**", "**/.next/**"],
87
+ module_roots: &["src/**", "app/**", "packages/*/src/**"],
88
+ allowed_root_files: &[
89
+ "package.json",
90
+ "tsconfig.json",
91
+ "vite.config.ts",
92
+ "next.config.js",
93
+ ],
94
+ },
95
+ ];
96
+
97
+ fn registry() -> Vec<StructureAdapter> {
98
+ let mut adapters = Vec::new();
99
+ adapters.extend_from_slice(BASE_STRUCTURE_ADAPTERS);
100
+ adapters.extend(adapter_ios::adapters());
101
+ adapters
84
102
  }
@@ -83,10 +83,12 @@ pub(super) fn case_collisions(
83
83
  mode: QualityMode,
84
84
  violations: &mut Vec<QualityViolation>,
85
85
  ) {
86
- for group in model.lowercase_paths.values().filter(|group| group.len() > 1) {
87
- let changed_group = group.iter().any(|path| {
88
- model.changed_paths.contains(path)
89
- });
86
+ for group in model
87
+ .lowercase_paths
88
+ .values()
89
+ .filter(|group| group.len() > 1)
90
+ {
91
+ let changed_group = group.iter().any(|path| model.changed_paths.contains(path));
90
92
  if mode.is_changed() && !changed_group {
91
93
  continue;
92
94
  }
@@ -2,13 +2,37 @@ use crate::paths;
2
2
 
3
3
  use crate::quality::structure::model::RepositoryStructureConfig;
4
4
 
5
+ const ROLE_SEGMENTS: &[&str] = &[
6
+ "src",
7
+ "source",
8
+ "sources",
9
+ "test",
10
+ "tests",
11
+ "docs",
12
+ "doc",
13
+ "scripts",
14
+ "generated",
15
+ "resources",
16
+ "views",
17
+ ];
18
+
5
19
  pub fn role_for(path: &str, segments: &[String], config: &RepositoryStructureConfig) -> String {
6
20
  let lower = path.to_ascii_lowercase();
7
21
  if has_segment(segments, &["node_modules", "vendor", "third_party"]) {
8
22
  return "dependency/vendor".to_string();
9
23
  }
10
24
  if paths::matches_any(path, &config.generated_roots)
11
- || has_segment(segments, &["generated", "__generated__", "codegen"])
25
+ || has_segment(
26
+ segments,
27
+ &[
28
+ "generated",
29
+ "__generated__",
30
+ "codegen",
31
+ "swiftgen",
32
+ "sourcery",
33
+ ],
34
+ )
35
+ || is_generated_ios_path(&lower)
12
36
  {
13
37
  return "generated".to_string();
14
38
  }
@@ -131,7 +155,13 @@ fn is_test_path(lower: &str, segments: &[String]) -> bool {
131
155
  lower.contains(".test.")
132
156
  || lower.contains(".spec.")
133
157
  || lower.ends_with("_test.rs")
158
+ || lower.ends_with("tests.swift")
159
+ || lower.ends_with("uitests.swift")
134
160
  || has_segment(segments, &["test", "tests", "__tests__"])
161
+ || segments.iter().any(|segment| {
162
+ let lower = segment.to_ascii_lowercase();
163
+ lower.ends_with("tests") || lower.ends_with("uitests")
164
+ })
135
165
  }
136
166
 
137
167
  fn is_docs_path(lower: &str, segments: &[String]) -> bool {
@@ -150,8 +180,16 @@ fn is_config_path(
150
180
  paths::matches_any(path, &config.allowed_root_files)
151
181
  || segments.len() == 1 && (lower.starts_with('.') || lower.ends_with(".toml"))
152
182
  || lower.ends_with(".json")
183
+ || lower.ends_with(".plist")
184
+ || lower.ends_with(".entitlements")
185
+ || lower.ends_with(".xcconfig")
186
+ || lower.ends_with(".strings")
187
+ || lower.ends_with(".stringsdict")
153
188
  || lower.ends_with(".yaml")
154
189
  || lower.ends_with(".yml")
190
+ || lower.ends_with("package.swift")
191
+ || lower.ends_with("podfile")
192
+ || lower.ends_with("cartfile")
155
193
  }
156
194
 
157
195
  fn is_script_path(lower: &str, segments: &[String]) -> bool {
@@ -163,6 +201,16 @@ fn is_artifact_path(lower: &str) -> bool {
163
201
  || lower.ends_with(".log")
164
202
  || lower.ends_with(".map")
165
203
  || lower.ends_with(".min.js")
204
+ || lower.contains("/deriveddata/")
205
+ || lower.ends_with(".xcarchive")
206
+ || lower.ends_with(".dsym")
207
+ }
208
+
209
+ fn is_generated_ios_path(lower: &str) -> bool {
210
+ lower.ends_with(".generated.swift")
211
+ || lower.ends_with(".pb.swift")
212
+ || lower.ends_with(".grpc.swift")
213
+ || lower.ends_with("/r.generated.swift")
166
214
  }
167
215
 
168
216
  fn has_segment(segments: &[String], expected: &[&str]) -> bool {
@@ -173,10 +221,8 @@ fn has_segment(segments: &[String], expected: &[&str]) -> bool {
173
221
  }
174
222
 
175
223
  fn is_role_segment(segment: &str) -> bool {
176
- matches!(
177
- segment,
178
- "src" | "test" | "tests" | "docs" | "doc" | "scripts" | "generated"
179
- )
224
+ let lower = segment.to_ascii_lowercase();
225
+ ROLE_SEGMENTS.contains(&lower.as_str())
180
226
  }
181
227
 
182
228
  fn stem(file_name: &str) -> Option<String> {
@@ -19,6 +19,15 @@ pub fn structure_config_relative_path() -> &'static str {
19
19
  pub fn read_structure_config(
20
20
  root: &Path,
21
21
  paths: &[String],
22
+ ) -> Result<RepositoryStructureConfig, NaomeError> {
23
+ let config = read_policy_structure_config(root, paths)?;
24
+ validate_structure_config(&config)?;
25
+ apply_structure_adapters(config)
26
+ }
27
+
28
+ pub(crate) fn read_policy_structure_config(
29
+ root: &Path,
30
+ paths: &[String],
22
31
  ) -> Result<RepositoryStructureConfig, NaomeError> {
23
32
  let path = root.join(CONFIG_RELATIVE_PATH);
24
33
  let config = if path.is_file() {
@@ -27,7 +36,20 @@ pub fn read_structure_config(
27
36
  generated_structure_config(paths)
28
37
  };
29
38
  validate_structure_config(&config)?;
30
- apply_structure_adapters(config)
39
+ Ok(config)
40
+ }
41
+
42
+ pub(crate) fn write_policy_structure_config(
43
+ root: &Path,
44
+ config: &RepositoryStructureConfig,
45
+ ) -> Result<(), NaomeError> {
46
+ let path = root.join(CONFIG_RELATIVE_PATH);
47
+ if let Some(parent) = path.parent() {
48
+ fs::create_dir_all(parent)?;
49
+ }
50
+ let content = serde_json::to_string_pretty(config)?;
51
+ fs::write(path, format!("{content}\n"))?;
52
+ Ok(())
31
53
  }
32
54
 
33
55
  pub fn write_default_structure_config_if_missing(
@@ -41,8 +63,7 @@ pub fn write_default_structure_config_if_missing(
41
63
  if let Some(parent) = path.parent() {
42
64
  fs::create_dir_all(parent)?;
43
65
  }
44
- let content = serde_json::to_string_pretty(&generated_structure_config(paths))?;
45
- fs::write(path, format!("{content}\n"))?;
66
+ write_policy_structure_config(root, &generated_structure_config(paths))?;
46
67
  Ok(true)
47
68
  }
48
69
 
@@ -1,3 +1,4 @@
1
+ mod adapter_ios;
1
2
  mod adapters;
2
3
  mod checks;
3
4
  mod classify;
@@ -12,9 +13,11 @@ use crate::models::NaomeError;
12
13
 
13
14
  use super::scanner::QualityContext;
14
15
  use super::types::QualityViolation;
16
+ pub(crate) use adapters::detected_structure_adapter_ids;
15
17
  use checks::run_structure_checks;
16
18
  use classify::build_structure_model;
17
19
  use config::read_structure_config;
20
+ pub(crate) use config::{read_policy_structure_config, write_policy_structure_config};
18
21
  pub use config::{structure_config_relative_path, write_default_structure_config_if_missing};
19
22
  pub use model::{RepositoryStructureConfig, StructurePathExplanation};
20
23
 
@@ -5,6 +5,7 @@ use crate::paths;
5
5
  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
6
6
  pub enum QualityMode {
7
7
  ChangedFast,
8
+ PathScoped,
8
9
  Report,
9
10
  DeepReport,
10
11
  }
@@ -16,13 +17,14 @@ impl QualityMode {
16
17
  pub fn as_str(self) -> &'static str {
17
18
  match self {
18
19
  Self::ChangedFast => "changed",
20
+ Self::PathScoped => "path",
19
21
  Self::Report => "report",
20
22
  Self::DeepReport => "deep-report",
21
23
  }
22
24
  }
23
25
 
24
26
  pub fn is_changed(self) -> bool {
25
- self == Self::ChangedFast
27
+ matches!(self, Self::ChangedFast | Self::PathScoped)
26
28
  }
27
29
 
28
30
  pub fn is_deep(self) -> bool {
@@ -257,6 +259,23 @@ pub struct QualityInitResult {
257
259
  pub baseline_path: String,
258
260
  }
259
261
 
262
+ #[derive(Debug, Clone, Serialize)]
263
+ #[serde(rename_all = "camelCase")]
264
+ pub struct QualityReconcileReport {
265
+ pub schema: String,
266
+ pub ok: bool,
267
+ pub stale: bool,
268
+ pub write: bool,
269
+ pub detected_quality_adapters: Vec<String>,
270
+ pub enabled_quality_adapters: Vec<String>,
271
+ pub missing_quality_adapters: Vec<String>,
272
+ pub detected_structure_adapters: Vec<String>,
273
+ pub enabled_structure_adapters: Vec<String>,
274
+ pub missing_structure_adapters: Vec<String>,
275
+ pub updated_paths: Vec<String>,
276
+ pub reason_codes: Vec<String>,
277
+ }
278
+
260
279
  #[derive(Debug, Clone, Serialize)]
261
280
  #[serde(rename_all = "camelCase")]
262
281
  pub struct QualityCleanupPlan {