@lamentis/naome 1.3.0 → 1.3.2

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 (149) 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/intent/resolver_catalog/active.rs +38 -0
  23. package/crates/naome-core/src/intent/resolver_catalog/baseline.rs +44 -0
  24. package/crates/naome-core/src/intent/resolver_catalog/completed.rs +56 -0
  25. package/crates/naome-core/src/intent/resolver_catalog/dirty.rs +32 -0
  26. package/crates/naome-core/src/intent/resolver_catalog/ready.rs +32 -0
  27. package/crates/naome-core/src/intent/resolver_catalog/system.rs +20 -0
  28. package/crates/naome-core/src/intent/resolver_catalog.rs +12 -166
  29. package/crates/naome-core/src/journal.rs +2 -7
  30. package/crates/naome-core/src/lib.rs +33 -10
  31. package/crates/naome-core/src/quality/adapter_ios.rs +131 -0
  32. package/crates/naome-core/src/quality/adapter_support.rs +67 -0
  33. package/crates/naome-core/src/quality/adapters.rs +81 -18
  34. package/crates/naome-core/src/quality/cache.rs +7 -9
  35. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +4 -7
  36. package/crates/naome-core/src/quality/config.rs +21 -3
  37. package/crates/naome-core/src/quality/mod.rs +138 -7
  38. package/crates/naome-core/src/quality/reconcile.rs +138 -0
  39. package/crates/naome-core/src/quality/reconcile_anchors.rs +64 -0
  40. package/crates/naome-core/src/quality/scanner/analysis.rs +20 -5
  41. package/crates/naome-core/src/quality/scanner.rs +62 -17
  42. package/crates/naome-core/src/quality/semantic/checks.rs +17 -0
  43. package/crates/naome-core/src/quality/semantic/route.rs +1 -1
  44. package/crates/naome-core/src/quality/structure/adapter_ios.rs +149 -0
  45. package/crates/naome-core/src/quality/structure/adapters.rs +60 -42
  46. package/crates/naome-core/src/quality/structure/checks/directory.rs +6 -4
  47. package/crates/naome-core/src/quality/structure/classify/roles.rs +51 -5
  48. package/crates/naome-core/src/quality/structure/config.rs +24 -3
  49. package/crates/naome-core/src/quality/structure/mod.rs +3 -0
  50. package/crates/naome-core/src/quality/types.rs +20 -1
  51. package/crates/naome-core/src/repository_model/detect.rs +188 -0
  52. package/crates/naome-core/src/repository_model/explain.rs +121 -0
  53. package/crates/naome-core/src/repository_model/path_scan.rs +67 -0
  54. package/crates/naome-core/src/repository_model/path_support.rs +59 -0
  55. package/crates/naome-core/src/repository_model/types.rs +152 -0
  56. package/crates/naome-core/src/repository_model/world.rs +48 -0
  57. package/crates/naome-core/src/repository_model/world_adapters.rs +145 -0
  58. package/crates/naome-core/src/repository_model/world_path_facts.rs +55 -0
  59. package/crates/naome-core/src/repository_model/world_paths.rs +168 -0
  60. package/crates/naome-core/src/repository_model.rs +164 -0
  61. package/crates/naome-core/src/route/builtin_checks.rs +40 -1
  62. package/crates/naome-core/src/task_ledger/import.rs +142 -0
  63. package/crates/naome-core/src/task_ledger/model.rs +13 -0
  64. package/crates/naome-core/src/task_ledger/proof_record.rs +52 -0
  65. package/crates/naome-core/src/task_ledger/read.rs +118 -0
  66. package/crates/naome-core/src/task_ledger/render.rs +55 -0
  67. package/crates/naome-core/src/task_ledger/write.rs +38 -0
  68. package/crates/naome-core/src/task_ledger.rs +48 -0
  69. package/crates/naome-core/src/task_state/api.rs +4 -2
  70. package/crates/naome-core/src/task_state/completed_refresh.rs +5 -16
  71. package/crates/naome-core/src/task_state/diff.rs +2 -2
  72. package/crates/naome-core/src/task_state/evidence.rs +8 -3
  73. package/crates/naome-core/src/task_state/mod.rs +1 -1
  74. package/crates/naome-core/src/task_state/progress.rs +13 -0
  75. package/crates/naome-core/src/task_state/proof_model.rs +8 -8
  76. package/crates/naome-core/src/task_state/repair.rs +2 -2
  77. package/crates/naome-core/src/task_state/task_diff_api.rs +9 -18
  78. package/crates/naome-core/src/task_state/types.rs +24 -0
  79. package/crates/naome-core/src/verification.rs +29 -18
  80. package/crates/naome-core/src/workflow/agent/capability.rs +194 -0
  81. package/crates/naome-core/src/workflow/agent/context_delta.rs +42 -0
  82. package/crates/naome-core/src/workflow/agent/decision.rs +32 -0
  83. package/crates/naome-core/src/workflow/agent/execution.rs +80 -0
  84. package/crates/naome-core/src/workflow/agent/proof.rs +24 -0
  85. package/crates/naome-core/src/workflow/agent/support.rs +58 -0
  86. package/crates/naome-core/src/workflow/agent/watchdog.rs +47 -0
  87. package/crates/naome-core/src/workflow/agent.rs +34 -0
  88. package/crates/naome-core/src/workflow/agent_types.rs +105 -0
  89. package/crates/naome-core/src/workflow/doctor.rs +39 -0
  90. package/crates/naome-core/src/workflow/mod.rs +11 -0
  91. package/crates/naome-core/src/workflow/output.rs +8 -2
  92. package/crates/naome-core/src/workflow/phase_inference.rs +1 -1
  93. package/crates/naome-core/tests/context.rs +99 -0
  94. package/crates/naome-core/tests/harness_health.rs +33 -40
  95. package/crates/naome-core/tests/install_plan.rs +12 -0
  96. package/crates/naome-core/tests/quality.rs +178 -2
  97. package/crates/naome-core/tests/quality_performance.rs +39 -2
  98. package/crates/naome-core/tests/quality_structure_adapters.rs +39 -0
  99. package/crates/naome-core/tests/repo_support/mod.rs +7 -1
  100. package/crates/naome-core/tests/repo_support/verification_values.rs +148 -1
  101. package/crates/naome-core/tests/repository_model.rs +281 -0
  102. package/crates/naome-core/tests/route_user_diff.rs +49 -1
  103. package/crates/naome-core/tests/semantic_legacy.rs +72 -38
  104. package/crates/naome-core/tests/task_ledger.rs +328 -0
  105. package/crates/naome-core/tests/task_state.rs +34 -14
  106. package/crates/naome-core/tests/task_state_support/mod.rs +2 -1
  107. package/crates/naome-core/tests/task_state_support/states.rs +28 -11
  108. package/crates/naome-core/tests/verification.rs +14 -39
  109. package/crates/naome-core/tests/verification_contract.rs +6 -52
  110. package/crates/naome-core/tests/workflow_agent.rs +233 -0
  111. package/crates/naome-core/tests/workflow_agent_support/mod.rs +159 -0
  112. package/crates/naome-core/tests/workflow_doctor.rs +21 -0
  113. package/crates/naome-core/tests/workflow_integrity.rs +2 -20
  114. package/crates/naome-core/tests/workflow_support/mod.rs +59 -20
  115. package/installer/codex-hooks.js +121 -0
  116. package/installer/context.js +10 -0
  117. package/installer/filesystem.js +4 -0
  118. package/installer/flows.js +8 -4
  119. package/installer/harness-files.js +6 -0
  120. package/installer/install-plan.js +4 -0
  121. package/installer/main.js +1 -1
  122. package/installer/native.js +1 -1
  123. package/native/darwin-arm64/naome +0 -0
  124. package/native/linux-x64/naome +0 -0
  125. package/package.json +1 -1
  126. package/templates/naome-root/.codex/config.toml +2 -0
  127. package/templates/naome-root/.codex/hooks.json +70 -0
  128. package/templates/naome-root/.naome/bin/check-harness-health.js +8 -6
  129. package/templates/naome-root/.naome/bin/check-task-state.js +12 -7
  130. package/templates/naome-root/.naome/bin/codex-hook-io.js +122 -0
  131. package/templates/naome-root/.naome/bin/codex-hook-policy.js +180 -0
  132. package/templates/naome-root/.naome/bin/codex-hook-runtime.js +174 -0
  133. package/templates/naome-root/.naome/bin/codex-hook.js +6 -0
  134. package/templates/naome-root/.naome/bin/naome.js +35 -4
  135. package/templates/naome-root/.naome/manifest.json +12 -6
  136. package/templates/naome-root/.naome/repository-model.json +6 -0
  137. package/templates/naome-root/.naome/repository-quality.json +3 -1
  138. package/templates/naome-root/.naome/verification.json +15 -1
  139. package/templates/naome-root/AGENTS.md +38 -83
  140. package/templates/naome-root/docs/naome/agent-workflow.md +54 -18
  141. package/templates/naome-root/docs/naome/codex-hooks.md +82 -0
  142. package/templates/naome-root/docs/naome/context-economy.md +73 -0
  143. package/templates/naome-root/docs/naome/first-run.md +25 -14
  144. package/templates/naome-root/docs/naome/index.md +18 -10
  145. package/templates/naome-root/docs/naome/repository-model.md +92 -0
  146. package/templates/naome-root/docs/naome/repository-quality.md +47 -7
  147. package/templates/naome-root/docs/naome/repository-structure.md +10 -3
  148. package/templates/naome-root/docs/naome/task-ledger.md +71 -0
  149. package/templates/naome-root/docs/naome/testing.md +16 -3
@@ -18,7 +18,9 @@ const MACHINE_OWNED_PATHS: &[&str] = &[
18
18
  "docs/naome/index.md",
19
19
  "docs/naome/first-run.md",
20
20
  "docs/naome/agent-workflow.md",
21
+ "docs/naome/context-economy.md",
21
22
  "docs/naome/execution.md",
23
+ "docs/naome/task-ledger.md",
22
24
  "docs/naome/upgrade.md",
23
25
  ];
24
26
 
@@ -29,12 +31,14 @@ const PROJECT_OWNED_PATHS: &[&str] = &[
29
31
  ".naome/task-state.json",
30
32
  ".naome/upgrade-state.json",
31
33
  ".naome/verification.json",
34
+ ".naome/repository-model.json",
32
35
  ".naome/repository-quality.json",
33
36
  ".naome/repository-structure.json",
34
37
  ".naome/repository-quality-baseline.json",
35
38
  "docs/naome/architecture.md",
36
39
  "docs/naome/decisions.md",
37
40
  "docs/naome/repo-profile.md",
41
+ "docs/naome/repository-model.md",
38
42
  "docs/naome/repository-quality.md",
39
43
  "docs/naome/repository-structure.md",
40
44
  "docs/naome/security.md",
@@ -152,50 +156,13 @@ impl HarnessFixture {
152
156
  write_file(
153
157
  &root,
154
158
  ".naome/manifest.json",
155
- &format!(
156
- "{}\n",
157
- serde_json::to_string_pretty(&json!({
158
- "name": "naome",
159
- "harnessVersion": "0.6.1",
160
- "profile": "standard",
161
- "installedAt": "2026-05-05T00:00:00.000Z",
162
- "machineOwned": MACHINE_OWNED_PATHS,
163
- "projectOwned": PROJECT_OWNED_PATHS,
164
- "integrity": integrity
165
- }))
166
- .unwrap()
167
- ),
168
- );
169
- write_file(
170
- &root,
171
- ".naome/task-state.json",
172
- &format!(
173
- "{}\n",
174
- serde_json::to_string_pretty(&json!({
175
- "schema": "naome.task-state.v1",
176
- "version": 1,
177
- "status": "idle",
178
- "activeTask": null,
179
- "blocker": null,
180
- "updatedAt": null
181
- }))
182
- .unwrap()
183
- ),
159
+ &pretty_json(manifest_fixture(&integrity)),
184
160
  );
161
+ write_file(&root, ".naome/task-state.json", "{}\n");
185
162
  write_file(
186
163
  &root,
187
164
  ".naome/upgrade-state.json",
188
- &format!(
189
- "{}\n",
190
- serde_json::to_string_pretty(&json!({
191
- "status": "complete",
192
- "fromVersion": null,
193
- "toVersion": "0.6.1",
194
- "pending": [],
195
- "completed": []
196
- }))
197
- .unwrap()
198
- ),
165
+ &pretty_json(complete_upgrade_state_fixture()),
199
166
  );
200
167
 
201
168
  Self { root, integrity }
@@ -210,6 +177,32 @@ impl HarnessFixture {
210
177
  }
211
178
  }
212
179
 
180
+ fn manifest_fixture(integrity: &HashMap<String, String>) -> serde_json::Value {
181
+ json!({
182
+ "name": "naome",
183
+ "harnessVersion": "0.6.1",
184
+ "profile": "standard",
185
+ "installedAt": "2026-05-05T00:00:00.000Z",
186
+ "machineOwned": MACHINE_OWNED_PATHS,
187
+ "projectOwned": PROJECT_OWNED_PATHS,
188
+ "integrity": integrity
189
+ })
190
+ }
191
+
192
+ fn complete_upgrade_state_fixture() -> serde_json::Value {
193
+ json!({
194
+ "status": "complete",
195
+ "fromVersion": null,
196
+ "toVersion": "0.6.1",
197
+ "pending": [],
198
+ "completed": []
199
+ })
200
+ }
201
+
202
+ fn pretty_json(value: serde_json::Value) -> String {
203
+ format!("{}\n", serde_json::to_string_pretty(&value).unwrap())
204
+ }
205
+
213
206
  fn machine_content(relative_path: &str) -> String {
214
207
  match relative_path {
215
208
  "AGENTS.md" => [
@@ -8,6 +8,7 @@ fn install_plan_marks_machine_docs_and_bins_local_only() {
8
8
  assert_eq!(plan.harness_version, "1.0.0");
9
9
  assert!(plan.machine_owned.contains(&"docs/naome/execution.md"));
10
10
  assert!(plan.project_owned.contains(&"docs/naome/architecture.md"));
11
+ assert!(!plan.project_owned.contains(&".naome/tasks/**"));
11
12
  assert!(plan
12
13
  .local_only_machine_owned
13
14
  .contains(&".naome/bin/check-task-state.js"));
@@ -18,6 +19,17 @@ fn install_plan_marks_machine_docs_and_bins_local_only() {
18
19
  assert!(!plan
19
20
  .local_only_machine_owned
20
21
  .contains(&"docs/naome/architecture.md"));
22
+ assert!(plan
23
+ .optional_codex_hook_paths
24
+ .contains(&".codex/hooks.json"));
25
+ assert!(plan
26
+ .optional_codex_hook_paths
27
+ .contains(&".codex/config.toml"));
28
+ assert!(plan
29
+ .optional_codex_hook_paths
30
+ .contains(&".naome/bin/codex-hook.js"));
31
+ assert!(!plan.machine_owned.contains(&".naome/bin/codex-hook.js"));
32
+ assert!(!plan.project_owned.contains(&".codex/hooks.json"));
21
33
  }
22
34
 
23
35
  #[test]
@@ -2,8 +2,8 @@ use std::fs;
2
2
 
3
3
  use naome_core::{
4
4
  check_repository_quality, init_repository_quality, init_repository_quality_with_mode,
5
- plan_quality_cleanup, route_quality_cleanup, seed_builtin_verification_checks, QualityInitMode,
6
- QualityMode,
5
+ plan_quality_cleanup, reconcile_repository_quality, route_quality_cleanup,
6
+ seed_builtin_verification_checks, QualityInitMode, QualityMode,
7
7
  };
8
8
  use serde_json::Value;
9
9
 
@@ -248,6 +248,174 @@ fn init_generates_adapter_selection_without_product_specific_path_rules() {
248
248
  assert!(!serialized_config.contains("naome-template-harness"));
249
249
  }
250
250
 
251
+ #[test]
252
+ fn init_auto_detects_ios_swift_adapter_set() {
253
+ let repo = QualityFixture::new("quality-ios-swift-adapters");
254
+ repo.write(
255
+ "Package.swift",
256
+ "// swift-tools-version: 5.9\nimport PackageDescription\n",
257
+ );
258
+ repo.write(
259
+ "Demo.xcodeproj/project.pbxproj",
260
+ "// !$*UTF8*$!\n{ archiveVersion = 1; }\n",
261
+ );
262
+ repo.write(
263
+ "Sources/App/App.swift",
264
+ "import SwiftUI\n@main struct DemoApp: App { var body: some Scene { WindowGroup { ContentView() } } }\n",
265
+ );
266
+ repo.write(
267
+ "Tests/AppTests/AppTests.swift",
268
+ "import XCTest\nfinal class AppTests: XCTestCase {}\n",
269
+ );
270
+ repo.write(
271
+ "Resources/Assets.xcassets/AppIcon.appiconset/Contents.json",
272
+ "{}\n",
273
+ );
274
+ repo.write("Generated/R.generated.swift", "enum R {}\n");
275
+ repo.commit_all("ios swift project");
276
+
277
+ init_repository_quality(repo.path()).unwrap();
278
+ let quality: Value = serde_json::from_str(
279
+ &fs::read_to_string(repo.path().join(".naome/repository-quality.json")).unwrap(),
280
+ )
281
+ .unwrap();
282
+ let structure: Value = serde_json::from_str(
283
+ &fs::read_to_string(repo.path().join(".naome/repository-structure.json")).unwrap(),
284
+ )
285
+ .unwrap();
286
+
287
+ for adapter in [
288
+ "swift",
289
+ "xcode",
290
+ "xctest",
291
+ "swiftui",
292
+ "ios-app-structure",
293
+ "swift-package",
294
+ "ios-resources",
295
+ "generated-ios",
296
+ ] {
297
+ assert!(
298
+ has_adapter(&quality, adapter),
299
+ "missing quality adapter {adapter}: {quality:#?}"
300
+ );
301
+ assert!(
302
+ has_adapter(&structure, adapter),
303
+ "missing structure adapter {adapter}: {structure:#?}"
304
+ );
305
+ }
306
+ }
307
+
308
+ #[test]
309
+ fn changed_check_reports_stale_adapter_policy_after_stack_changes() {
310
+ let repo = QualityFixture::new("quality-stale-adapter-policy");
311
+ repo.write_quality_config_with_adapters(450, 100, 10, &[]);
312
+ repo.write_structure_config(serde_json::json!({
313
+ "enabledAdapters": []
314
+ }));
315
+ repo.commit_all("initial quality policy");
316
+ repo.write(
317
+ "Package.swift",
318
+ "// swift-tools-version: 5.9\nimport PackageDescription\n",
319
+ );
320
+ repo.write(
321
+ "Sources/App/App.swift",
322
+ "public struct DemoApp { public init() {} }\n",
323
+ );
324
+
325
+ let report = check_repository_quality(repo.path(), QualityMode::Changed).unwrap();
326
+
327
+ assert!(!report.ok);
328
+ assert!(report.violations.iter().any(|violation| {
329
+ violation.check_id == "adapter-policy-stale"
330
+ && violation.path == ".naome/repository-quality.json"
331
+ && violation.message.contains("swift")
332
+ && violation
333
+ .related_paths
334
+ .contains(&".naome/repository-structure.json".to_string())
335
+ }));
336
+ }
337
+
338
+ #[test]
339
+ fn changed_check_reports_stale_repository_model_after_stack_changes() {
340
+ let repo = QualityFixture::new("quality-repository-model-stale");
341
+ repo.write_quality_config();
342
+ repo.write_structure_config(serde_json::json!({}));
343
+ repo.write(
344
+ ".naome/repository-model.json",
345
+ "{\"schema\":\"naome.repository-model.v1\",\"version\":1,\"status\":\"ready\",\"facts\":[]}\n",
346
+ );
347
+ repo.write("README.md", "# Baseline\n");
348
+ repo.commit_all("baseline");
349
+
350
+ repo.write("Package.swift", "// swift-tools-version: 5.9\n");
351
+
352
+ let report = check_repository_quality(repo.path(), QualityMode::Changed).unwrap();
353
+
354
+ assert!(!report.ok);
355
+ assert!(report
356
+ .summary
357
+ .reason_codes
358
+ .contains(&"repository_model_stale".to_string()));
359
+ assert!(report.violations.iter().any(|violation| {
360
+ violation.check_id == "repository-model-stale"
361
+ && violation.path == ".naome/repository-model.json"
362
+ && violation
363
+ .related_paths
364
+ .contains(&"Package.swift".to_string())
365
+ }));
366
+ }
367
+
368
+ #[test]
369
+ fn reconcile_write_updates_stale_quality_and_structure_adapter_policy() {
370
+ let repo = QualityFixture::new("quality-reconcile-adapters");
371
+ repo.write_quality_config_with_adapters(450, 100, 10, &[]);
372
+ repo.write_structure_config(serde_json::json!({
373
+ "enabledAdapters": []
374
+ }));
375
+ repo.write(
376
+ "Package.swift",
377
+ "// swift-tools-version: 5.9\nimport PackageDescription\n",
378
+ );
379
+ repo.write(
380
+ "Sources/App/App.swift",
381
+ "public struct DemoApp { public init() {} }\n",
382
+ );
383
+
384
+ let planned = reconcile_repository_quality(repo.path(), false).unwrap();
385
+ assert!(!planned.ok);
386
+ assert!(planned.stale);
387
+ assert!(planned
388
+ .missing_quality_adapters
389
+ .contains(&"swift".to_string()));
390
+ assert!(planned.updated_paths.is_empty());
391
+
392
+ let written = reconcile_repository_quality(repo.path(), true).unwrap();
393
+ assert!(written.ok);
394
+ assert!(!written.stale);
395
+ assert_eq!(
396
+ written.updated_paths,
397
+ vec![
398
+ ".naome/repository-quality.json".to_string(),
399
+ ".naome/repository-structure.json".to_string()
400
+ ]
401
+ );
402
+
403
+ let quality: Value = serde_json::from_str(
404
+ &fs::read_to_string(repo.path().join(".naome/repository-quality.json")).unwrap(),
405
+ )
406
+ .unwrap();
407
+ let structure: Value = serde_json::from_str(
408
+ &fs::read_to_string(repo.path().join(".naome/repository-structure.json")).unwrap(),
409
+ )
410
+ .unwrap();
411
+ assert!(has_adapter(&quality, "swift"));
412
+ assert!(has_adapter(&structure, "swift"));
413
+
414
+ let repeated = reconcile_repository_quality(repo.path(), true).unwrap();
415
+ assert!(repeated.ok);
416
+ assert!(repeated.updated_paths.is_empty());
417
+ }
418
+
251
419
  #[test]
252
420
  fn enabled_adapters_apply_their_rules_without_materialized_path_rules() {
253
421
  let repo = QualityFixture::new("quality-adapter-runtime-config");
@@ -266,6 +434,14 @@ fn enabled_adapters_apply_their_rules_without_materialized_path_rules() {
266
434
  }));
267
435
  }
268
436
 
437
+ fn has_adapter(config: &Value, adapter: &str) -> bool {
438
+ config["enabledAdapters"]
439
+ .as_array()
440
+ .unwrap()
441
+ .iter()
442
+ .any(|value| value.as_str() == Some(adapter))
443
+ }
444
+
269
445
  #[test]
270
446
  fn seeding_builtin_verification_wires_repository_quality_into_change_types() {
271
447
  let repo = QualityFixture::new("quality-verification-wiring");
@@ -3,8 +3,9 @@ mod repo_support;
3
3
  use std::fs;
4
4
 
5
5
  use naome_core::{
6
- check_repository_quality, check_semantic_legacy, init_repository_quality,
7
- init_repository_quality_with_mode, quality_cache_status, QualityInitMode, QualityMode,
6
+ check_repository_quality, check_repository_quality_paths, check_semantic_legacy,
7
+ init_repository_quality, init_repository_quality_with_mode, quality_cache_status,
8
+ QualityInitMode, QualityMode,
8
9
  };
9
10
 
10
11
  use repo_support::TestRepo;
@@ -67,6 +68,42 @@ fn changed_fast_scans_only_changed_file_contents() {
67
68
  assert!(report.changed_paths.contains(&"src/changed.js".to_string()));
68
69
  }
69
70
 
71
+ #[test]
72
+ fn path_scoped_quality_scans_only_requested_file_after_write() {
73
+ let repo = quality_repo("quality-path-scoped-focused");
74
+ repo.write_file("src/target.js", "export const before = 1;\n");
75
+ repo.write_file("src/unrelated.js", "export const before = 1;\n");
76
+ repo.commit_all("baseline");
77
+ repo.write_file("src/target.js", &large_file());
78
+ repo.write_file("src/unrelated.js", &large_file());
79
+
80
+ let report = check_repository_quality_paths(repo.path(), &["src/target.js"]).unwrap();
81
+
82
+ assert_eq!(report.mode, "path");
83
+ assert_eq!(report.changed_paths, vec!["src/target.js"]);
84
+ assert_eq!(report.summary.scanned_files, 1);
85
+ assert_eq!(report.summary.scanned_path_count, 1);
86
+ assert!(report
87
+ .violations
88
+ .iter()
89
+ .any(|violation| violation.check_id == "file-length" && violation.path == "src/target.js"));
90
+ assert!(!report
91
+ .violations
92
+ .iter()
93
+ .any(|violation| violation.path == "src/unrelated.js"));
94
+ }
95
+
96
+ #[test]
97
+ fn path_scoped_quality_rejects_paths_outside_repository() {
98
+ let repo = quality_repo("quality-path-scoped-boundary");
99
+ repo.write_file("src/target.js", "export const before = 1;\n");
100
+ repo.commit_all("baseline");
101
+
102
+ let error = check_repository_quality_paths(repo.path(), &["../outside.js"]).unwrap_err();
103
+
104
+ assert!(error.to_string().contains("repository-relative path"));
105
+ }
106
+
70
107
  #[test]
71
108
  fn semantic_changed_check_scans_only_changed_file_contents() {
72
109
  let repo = quality_repo("semantic-changed-fast-focused");
@@ -40,6 +40,45 @@ fn javascript_typescript_adapter_recognizes_package_source_and_tests() {
40
40
  assert_eq!(test.role, "test");
41
41
  }
42
42
 
43
+ #[test]
44
+ fn ios_swift_adapters_recognize_xcode_swiftui_tests_resources_and_generated_code() {
45
+ let repo = StructureFixture::new("structure-ios-swift-adapters");
46
+ repo.write(
47
+ "Package.swift",
48
+ "// swift-tools-version: 5.9\nimport PackageDescription\n",
49
+ );
50
+ repo.write(
51
+ "Demo.xcodeproj/project.pbxproj",
52
+ "// !$*UTF8*$!\n{ archiveVersion = 1; }\n",
53
+ );
54
+ repo.write(
55
+ "Sources/App/ContentView.swift",
56
+ "import SwiftUI\nstruct ContentView: View {}\n",
57
+ );
58
+ repo.write(
59
+ "Tests/AppTests/ContentViewTests.swift",
60
+ "import XCTest\nfinal class ContentViewTests: XCTestCase {}\n",
61
+ );
62
+ repo.write("Resources/Info.plist", "<?xml version=\"1.0\"?>\n");
63
+ repo.write("Resources/Assets.xcassets/Contents.json", "{}\n");
64
+ repo.write("Generated/R.generated.swift", "enum R {}\n");
65
+
66
+ let source =
67
+ explain_repository_structure(repo.path(), "Sources/App/ContentView.swift").unwrap();
68
+ let test =
69
+ explain_repository_structure(repo.path(), "Tests/AppTests/ContentViewTests.swift").unwrap();
70
+ let plist = explain_repository_structure(repo.path(), "Resources/Info.plist").unwrap();
71
+ let generated =
72
+ explain_repository_structure(repo.path(), "Generated/R.generated.swift").unwrap();
73
+
74
+ assert_eq!(source.role, "source");
75
+ assert_eq!(source.module.as_deref(), Some("App"));
76
+ assert_eq!(source.language.as_deref(), Some("swift"));
77
+ assert_eq!(test.role, "test");
78
+ assert_eq!(plist.role, "config");
79
+ assert_eq!(generated.role, "generated");
80
+ }
81
+
43
82
  #[test]
44
83
  fn unknown_repository_gets_generic_roles() {
45
84
  let repo = StructureFixture::new("structure-generic-roles");
@@ -13,4 +13,10 @@ pub use routes::{
13
13
  route_commit_request, route_new_task, route_readme_task, try_route_new_task,
14
14
  try_route_readme_task,
15
15
  };
16
- pub use verification_values::{change_type, diff_check, verification_value};
16
+ pub use verification_values::{
17
+ change_type, check_missing_last_verified_fixture, diff_check,
18
+ placeholder_verification_contract_fixture, quality_check, repo_docs_verification_fixture,
19
+ repository_quality_config_source,
20
+ repository_quality_config_value, semantic_repository_quality_fixture_source,
21
+ verification_value,
22
+ };
@@ -1,4 +1,4 @@
1
- use serde_json::json;
1
+ use serde_json::{json, Value};
2
2
 
3
3
  pub fn completed_task_state(admission_head: &str) -> serde_json::Value {
4
4
  json!({
@@ -67,12 +67,42 @@ pub fn verification_value(
67
67
  "schema": "naome.verification.v1",
68
68
  "version": 1,
69
69
  "status": status,
70
+ "lastUpdated": null,
70
71
  "checks": checks,
71
72
  "changeTypes": change_types,
72
73
  "releaseGates": []
73
74
  })
74
75
  }
75
76
 
77
+ pub fn repository_quality_config_value() -> serde_json::Value {
78
+ json!({
79
+ "schema": "naome.repository-quality.v1",
80
+ "version": 1,
81
+ "status": "ready",
82
+ "limits": {
83
+ "maxFileLines": 450,
84
+ "maxNewFileLines": 300,
85
+ "maxDiffAddedLines": 180,
86
+ "maxFunctionLines": 100,
87
+ "maxTopLevelSymbols": 30,
88
+ "duplicateBlockLines": 10,
89
+ "nearDuplicateSimilarity": 0.9
90
+ },
91
+ "enabledAdapters": [],
92
+ "disabledChecks": [],
93
+ "ignoredPaths": [],
94
+ "generatedPaths": [],
95
+ "pathRules": []
96
+ })
97
+ }
98
+
99
+ pub fn repository_quality_config_source() -> String {
100
+ format!(
101
+ "{}\n",
102
+ serde_json::to_string_pretty(&repository_quality_config_value()).unwrap()
103
+ )
104
+ }
105
+
76
106
  pub fn change_type(
77
107
  id: &str,
78
108
  description: &str,
@@ -89,6 +119,97 @@ pub fn change_type(
89
119
  })
90
120
  }
91
121
 
122
+ pub fn repo_docs_verification_fixture() -> Value {
123
+ json!({
124
+ "schema": "naome.verification.v1",
125
+ "version": 1,
126
+ "status": "ready",
127
+ "lastUpdated": "2026-05-05",
128
+ "checks": [
129
+ {
130
+ "id": "repo-docs",
131
+ "command": "npm run docs:test",
132
+ "cwd": ".",
133
+ "purpose": "Validate repository documentation.",
134
+ "cost": "fast",
135
+ "source": "repository intake",
136
+ "evidence": ["package.json"],
137
+ "lastVerified": null
138
+ }
139
+ ],
140
+ "changeTypes": [
141
+ {
142
+ "id": "repo-docs",
143
+ "description": "Repository documentation changes.",
144
+ "paths": ["docs/**"],
145
+ "requiredChecks": ["repo-docs"],
146
+ "recommendedChecks": [],
147
+ "humanReview": false
148
+ }
149
+ ],
150
+ "releaseGates": [
151
+ {
152
+ "checkId": "repo-docs",
153
+ "requiredWhen": "Before merging repository documentation changes."
154
+ }
155
+ ]
156
+ })
157
+ }
158
+
159
+ pub fn placeholder_verification_contract_fixture() -> Value {
160
+ json!({
161
+ "schema": "naome.verification.v1",
162
+ "version": 1,
163
+ "status": "ready",
164
+ "lastUpdated": "2026-05-05",
165
+ "checks": [
166
+ {
167
+ "id": "example-check",
168
+ "command": "replace with real command",
169
+ "cwd": ".",
170
+ "purpose": "Replace with what this proves.",
171
+ "cost": "fast",
172
+ "source": "replace with evidence source",
173
+ "evidence": [],
174
+ "lastVerified": null
175
+ }
176
+ ],
177
+ "changeTypes": [
178
+ {
179
+ "id": "docs",
180
+ "description": "Documentation changes.",
181
+ "paths": ["docs/**"],
182
+ "requiredChecks": ["missing-check"],
183
+ "recommendedChecks": [],
184
+ "humanReview": false
185
+ }
186
+ ],
187
+ "releaseGates": []
188
+ })
189
+ }
190
+
191
+ pub fn check_missing_last_verified_fixture() -> Value {
192
+ json!({
193
+ "schema": "naome.verification.v1",
194
+ "version": 1,
195
+ "status": "uninitialized",
196
+ "lastUpdated": null,
197
+ "checks": [
198
+ {
199
+ "id": "diff-check",
200
+ "command": "git diff --check",
201
+ "cwd": ".",
202
+ "purpose": "Reject whitespace errors.",
203
+ "cost": "fast",
204
+ "source": "git",
205
+ "evidence": []
206
+ }
207
+ ],
208
+ "changeTypes": [],
209
+ "releaseGates": []
210
+ })
211
+ }
212
+
92
213
  pub fn quality_check(
93
214
  id: &str,
94
215
  command: &str,
@@ -121,6 +242,32 @@ pub fn repository_quality_check() -> serde_json::Value {
121
242
  })
122
243
  }
123
244
 
245
+ pub fn semantic_repository_quality_fixture_source(name: &str) -> String {
246
+ [
247
+ format!("const {name} = {{"),
248
+ " schema: \"naome.repository-quality.v1\",".to_string(),
249
+ " version: 1,".to_string(),
250
+ " status: \"ready\",".to_string(),
251
+ " limits: {".to_string(),
252
+ " maxFileLines: 450,".to_string(),
253
+ " maxNewFileLines: 300,".to_string(),
254
+ " maxDiffAddedLines: 180,".to_string(),
255
+ " maxFunctionLines: 100,".to_string(),
256
+ " maxTopLevelSymbols: 30,".to_string(),
257
+ " duplicateBlockLines: 10,".to_string(),
258
+ " nearDuplicateSimilarity: 0.9".to_string(),
259
+ " },".to_string(),
260
+ " enabledAdapters: [],".to_string(),
261
+ " disabledChecks: [],".to_string(),
262
+ " ignoredPaths: [],".to_string(),
263
+ " generatedPaths: [],".to_string(),
264
+ " pathRules: []".to_string(),
265
+ "};".to_string(),
266
+ String::new(),
267
+ ]
268
+ .join("\n")
269
+ }
270
+
124
271
  pub fn mutating_diff_check() -> serde_json::Value {
125
272
  json!({
126
273
  "id": "diff-check",