@lamentis/naome 1.2.0 → 1.3.0

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 (139) hide show
  1. package/Cargo.lock +2 -2
  2. package/README.md +108 -47
  3. package/bin/naome-node.js +2 -1579
  4. package/bin/naome.js +34 -5
  5. package/crates/naome-cli/Cargo.toml +1 -1
  6. package/crates/naome-cli/src/dispatcher.rs +7 -2
  7. package/crates/naome-cli/src/main.rs +37 -22
  8. package/crates/naome-cli/src/quality_commands.rs +317 -10
  9. package/crates/naome-cli/src/workflow_commands.rs +21 -1
  10. package/crates/naome-core/Cargo.toml +1 -1
  11. package/crates/naome-core/src/decision/checks.rs +64 -0
  12. package/crates/naome-core/src/decision/idle.rs +67 -0
  13. package/crates/naome-core/src/decision/json.rs +36 -0
  14. package/crates/naome-core/src/decision/states.rs +165 -0
  15. package/crates/naome-core/src/decision.rs +131 -353
  16. package/crates/naome-core/src/git.rs +4 -2
  17. package/crates/naome-core/src/install_plan.rs +4 -0
  18. package/crates/naome-core/src/lib.rs +12 -6
  19. package/crates/naome-core/src/paths.rs +3 -1
  20. package/crates/naome-core/src/quality/adapter_support.rs +89 -0
  21. package/crates/naome-core/src/quality/adapters.rs +20 -67
  22. package/crates/naome-core/src/quality/baseline.rs +8 -0
  23. package/crates/naome-core/src/quality/cache.rs +153 -0
  24. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +25 -11
  25. package/crates/naome-core/src/quality/checks/near_duplicates.rs +4 -2
  26. package/crates/naome-core/src/quality/checks.rs +7 -8
  27. package/crates/naome-core/src/quality/cleanup.rs +48 -3
  28. package/crates/naome-core/src/quality/config.rs +8 -15
  29. package/crates/naome-core/src/quality/config_support.rs +24 -0
  30. package/crates/naome-core/src/quality/mod.rs +72 -6
  31. package/crates/naome-core/src/quality/scanner/analysis/normalize.rs +78 -0
  32. package/crates/naome-core/src/quality/scanner/analysis.rs +160 -0
  33. package/crates/naome-core/src/quality/scanner/repo_paths.rs +39 -3
  34. package/crates/naome-core/src/quality/scanner.rs +200 -215
  35. package/crates/naome-core/src/quality/semantic/checks.rs +134 -0
  36. package/crates/naome-core/src/quality/semantic/extract.rs +158 -0
  37. package/crates/naome-core/src/quality/semantic/model.rs +85 -0
  38. package/crates/naome-core/src/quality/semantic/route.rs +52 -0
  39. package/crates/naome-core/src/quality/semantic.rs +68 -0
  40. package/crates/naome-core/src/quality/structure/adapters.rs +84 -0
  41. package/crates/naome-core/src/quality/structure/checks/basic.rs +153 -0
  42. package/crates/naome-core/src/quality/structure/checks/directory.rs +134 -0
  43. package/crates/naome-core/src/quality/structure/checks/pairing.rs +63 -0
  44. package/crates/naome-core/src/quality/structure/checks.rs +124 -0
  45. package/crates/naome-core/src/quality/structure/classify/roles.rs +188 -0
  46. package/crates/naome-core/src/quality/structure/classify.rs +146 -0
  47. package/crates/naome-core/src/quality/structure/config.rs +89 -0
  48. package/crates/naome-core/src/quality/structure/defaults.rs +107 -0
  49. package/crates/naome-core/src/quality/structure/mod.rs +77 -0
  50. package/crates/naome-core/src/quality/structure/model.rs +131 -0
  51. package/crates/naome-core/src/quality/types.rs +43 -2
  52. package/crates/naome-core/src/route/builtin_checks.rs +141 -0
  53. package/crates/naome-core/src/route/builtin_context.rs +73 -0
  54. package/crates/naome-core/src/route/builtin_integrity.rs +49 -0
  55. package/crates/naome-core/src/route/builtin_require.rs +40 -0
  56. package/crates/naome-core/src/route/context.rs +180 -0
  57. package/crates/naome-core/src/route/execution.rs +96 -0
  58. package/crates/naome-core/src/route/execution_baselines.rs +146 -0
  59. package/crates/naome-core/src/route/execution_support.rs +57 -0
  60. package/crates/naome-core/src/route/execution_tasks.rs +71 -0
  61. package/crates/naome-core/src/route/git_ops.rs +72 -0
  62. package/crates/naome-core/src/route/quality_gate.rs +73 -0
  63. package/crates/naome-core/src/route/quality_gate_config.rs +126 -0
  64. package/crates/naome-core/src/route/quality_gate_snapshot.rs +69 -0
  65. package/crates/naome-core/src/route/worktree.rs +75 -0
  66. package/crates/naome-core/src/route/worktree_files.rs +32 -0
  67. package/crates/naome-core/src/route/worktree_plan.rs +131 -0
  68. package/crates/naome-core/src/route.rs +44 -1217
  69. package/crates/naome-core/src/verification.rs +1 -0
  70. package/crates/naome-core/src/workflow/doctor.rs +144 -0
  71. package/crates/naome-core/src/workflow/mod.rs +2 -0
  72. package/crates/naome-core/src/workflow/mutation.rs +1 -2
  73. package/crates/naome-core/tests/decision.rs +24 -118
  74. package/crates/naome-core/tests/harness_health.rs +2 -0
  75. package/crates/naome-core/tests/install_plan.rs +2 -0
  76. package/crates/naome-core/tests/quality.rs +26 -123
  77. package/crates/naome-core/tests/quality_performance.rs +231 -0
  78. package/crates/naome-core/tests/quality_structure.rs +116 -0
  79. package/crates/naome-core/tests/quality_structure_adapters.rs +98 -0
  80. package/crates/naome-core/tests/quality_structure_policy.rs +144 -0
  81. package/crates/naome-core/tests/quality_structure_support/mod.rs +249 -0
  82. package/crates/naome-core/tests/repo_support/mod.rs +16 -0
  83. package/crates/naome-core/tests/repo_support/repo.rs +113 -0
  84. package/crates/naome-core/tests/repo_support/repo_factories.rs +99 -0
  85. package/crates/naome-core/tests/repo_support/repo_helpers.rs +123 -0
  86. package/crates/naome-core/tests/repo_support/routes.rs +81 -0
  87. package/crates/naome-core/tests/repo_support/verification.rs +168 -0
  88. package/crates/naome-core/tests/repo_support/verification_values.rs +135 -0
  89. package/crates/naome-core/tests/route.rs +1 -1376
  90. package/crates/naome-core/tests/route_baseline.rs +86 -0
  91. package/crates/naome-core/tests/route_completion.rs +141 -0
  92. package/crates/naome-core/tests/route_harness_refresh.rs +135 -0
  93. package/crates/naome-core/tests/route_user_diff.rs +202 -0
  94. package/crates/naome-core/tests/route_worktree.rs +54 -0
  95. package/crates/naome-core/tests/semantic_legacy.rs +140 -0
  96. package/crates/naome-core/tests/task_state.rs +60 -432
  97. package/crates/naome-core/tests/task_state_compact_support/repo.rs +1 -1
  98. package/crates/naome-core/tests/task_state_support/mod.rs +163 -0
  99. package/crates/naome-core/tests/task_state_support/states.rs +84 -0
  100. package/crates/naome-core/tests/verification.rs +4 -45
  101. package/crates/naome-core/tests/verification_contract.rs +22 -78
  102. package/crates/naome-core/tests/workflow_doctor.rs +24 -0
  103. package/crates/naome-core/tests/workflow_policy.rs +6 -1
  104. package/crates/naome-core/tests/workflow_support/mod.rs +1 -1
  105. package/installer/agents.js +90 -0
  106. package/installer/context.js +67 -0
  107. package/installer/filesystem.js +166 -0
  108. package/installer/flows.js +84 -0
  109. package/installer/git-boundary.js +171 -0
  110. package/installer/git-hook-content.js +36 -0
  111. package/installer/git-hooks.js +134 -0
  112. package/installer/git-local.js +2 -0
  113. package/installer/git-shared.js +35 -0
  114. package/installer/harness-file-ops.js +140 -0
  115. package/installer/harness-files.js +56 -0
  116. package/installer/harness-verification.js +123 -0
  117. package/installer/install-plan.js +66 -0
  118. package/installer/main.js +25 -0
  119. package/installer/manifest-state.js +167 -0
  120. package/installer/native-build.js +24 -0
  121. package/installer/native-format.js +6 -0
  122. package/installer/native.js +162 -0
  123. package/installer/output.js +131 -0
  124. package/installer/version.js +32 -0
  125. package/native/darwin-arm64/naome +0 -0
  126. package/native/linux-x64/naome +0 -0
  127. package/package.json +2 -1
  128. package/templates/naome-root/.naome/bin/check-harness-health.js +3 -3
  129. package/templates/naome-root/.naome/bin/check-task-state.js +3 -3
  130. package/templates/naome-root/.naome/bin/naome.js +32 -21
  131. package/templates/naome-root/.naome/manifest.json +5 -3
  132. package/templates/naome-root/.naome/repository-structure.json +90 -0
  133. package/templates/naome-root/.naome/verification.json +1 -0
  134. package/templates/naome-root/.naomeignore +1 -0
  135. package/templates/naome-root/docs/naome/agent-workflow.md +16 -14
  136. package/templates/naome-root/docs/naome/index.md +4 -3
  137. package/templates/naome-root/docs/naome/repository-quality.md +66 -4
  138. package/templates/naome-root/docs/naome/repository-structure.md +51 -0
  139. package/templates/naome-root/docs/naome/testing.md +2 -1
@@ -243,6 +243,7 @@ fn builtin_checks() -> Vec<BuiltinCheck> {
243
243
  purpose: "Validate changed files against deterministic NAOME repository quality rules.",
244
244
  evidence: &[
245
245
  ".naome/repository-quality.json",
246
+ ".naome/repository-structure.json",
246
247
  ".naome/repository-quality-baseline.json",
247
248
  ],
248
249
  },
@@ -0,0 +1,144 @@
1
+ use std::fs;
2
+ use std::path::Path;
3
+
4
+ use serde::Serialize;
5
+
6
+ use crate::harness_health::{validate_harness_health, HarnessHealthOptions};
7
+ use crate::models::NaomeError;
8
+ use crate::task_state::{validate_task_state, TaskStateMode, TaskStateOptions};
9
+ use crate::verification_contract::validate_verification_contract;
10
+
11
+ use super::phases::verification_phase_plan;
12
+
13
+ #[derive(Debug, Clone, Serialize)]
14
+ #[serde(rename_all = "camelCase")]
15
+ pub struct DoctorReport {
16
+ pub schema: String,
17
+ pub ok: bool,
18
+ pub harness_health: DoctorSection,
19
+ pub task_state: DoctorSection,
20
+ pub verification: DoctorSection,
21
+ pub repository_quality: RepositoryPolicySection,
22
+ pub repository_structure: RepositoryPolicySection,
23
+ pub recommended_check_ids: Vec<String>,
24
+ pub withheld_check_ids: Vec<String>,
25
+ pub next_action: String,
26
+ }
27
+
28
+ #[derive(Debug, Clone, Serialize)]
29
+ #[serde(rename_all = "camelCase")]
30
+ pub struct DoctorSection {
31
+ pub status: String,
32
+ pub messages: Vec<String>,
33
+ }
34
+
35
+ #[derive(Debug, Clone, Serialize)]
36
+ #[serde(rename_all = "camelCase")]
37
+ pub struct RepositoryPolicySection {
38
+ pub config_present: bool,
39
+ pub baseline_present: bool,
40
+ pub status: String,
41
+ }
42
+
43
+ pub fn doctor_report(root: &Path) -> Result<DoctorReport, NaomeError> {
44
+ let harness_errors = validate_harness_health(
45
+ root,
46
+ HarnessHealthOptions {
47
+ allow_missing_integrity: true,
48
+ allow_missing_archive: true,
49
+ ..HarnessHealthOptions::default()
50
+ },
51
+ )?;
52
+ let task_report = validate_task_state(
53
+ root,
54
+ TaskStateOptions {
55
+ mode: TaskStateMode::State,
56
+ harness_health: None,
57
+ },
58
+ )?;
59
+ let verification_errors = validate_verification_contract(root)?;
60
+ let phase_plan = verification_phase_plan(root, &[])?;
61
+
62
+ let repository_quality = policy_section(
63
+ root,
64
+ ".naome/repository-quality.json",
65
+ ".naome/repository-quality-baseline.json",
66
+ );
67
+ let repository_structure = policy_section(root, ".naome/repository-structure.json", "");
68
+
69
+ let harness_health = section(harness_errors);
70
+ let task_state = section(task_report.errors);
71
+ let verification = section(verification_errors);
72
+ let ok = harness_health.status == "ok"
73
+ && task_state.status == "ok"
74
+ && verification.status == "ok"
75
+ && repository_quality.status == "ok"
76
+ && repository_structure.status == "ok";
77
+ let next_action = next_action(
78
+ &harness_health,
79
+ &task_state,
80
+ &verification,
81
+ &repository_quality,
82
+ &repository_structure,
83
+ );
84
+
85
+ Ok(DoctorReport {
86
+ schema: "naome.doctor.v1".to_string(),
87
+ ok,
88
+ harness_health,
89
+ task_state,
90
+ verification,
91
+ repository_quality,
92
+ repository_structure,
93
+ recommended_check_ids: phase_plan.recommended_check_ids,
94
+ withheld_check_ids: phase_plan.withheld_check_ids,
95
+ next_action,
96
+ })
97
+ }
98
+
99
+ fn section(messages: Vec<String>) -> DoctorSection {
100
+ DoctorSection {
101
+ status: if messages.is_empty() { "ok" } else { "blocked" }.to_string(),
102
+ messages,
103
+ }
104
+ }
105
+
106
+ fn policy_section(root: &Path, config_path: &str, baseline_path: &str) -> RepositoryPolicySection {
107
+ let config_present =
108
+ fs::metadata(root.join(config_path)).is_ok_and(|metadata| metadata.is_file());
109
+ let baseline_present = baseline_path.is_empty()
110
+ || fs::metadata(root.join(baseline_path)).is_ok_and(|metadata| metadata.is_file());
111
+ RepositoryPolicySection {
112
+ config_present,
113
+ baseline_present,
114
+ status: if config_present && baseline_present {
115
+ "ok"
116
+ } else {
117
+ "missing"
118
+ }
119
+ .to_string(),
120
+ }
121
+ }
122
+
123
+ fn next_action(
124
+ harness_health: &DoctorSection,
125
+ task_state: &DoctorSection,
126
+ verification: &DoctorSection,
127
+ repository_quality: &RepositoryPolicySection,
128
+ repository_structure: &RepositoryPolicySection,
129
+ ) -> String {
130
+ if task_state.status != "ok" {
131
+ return "Finish or resolve the active task before starting new work.".to_string();
132
+ }
133
+ if harness_health.status != "ok" {
134
+ return "Repair the NAOME harness before feature work.".to_string();
135
+ }
136
+ if verification.status != "ok" {
137
+ return "Fix .naome/verification.json before relying on gates.".to_string();
138
+ }
139
+ if repository_quality.status != "ok" || repository_structure.status != "ok" {
140
+ return "Run naome quality init or naome sync to seed repository quality policy."
141
+ .to_string();
142
+ }
143
+ "NAOME is ready for task work; follow the recommended verification phase order.".to_string()
144
+ }
@@ -1,3 +1,4 @@
1
+ mod doctor;
1
2
  mod integrity;
2
3
  mod integrity_normalize;
3
4
  mod integrity_support;
@@ -9,6 +10,7 @@ mod policy;
9
10
  mod processes;
10
11
  mod types;
11
12
 
13
+ pub use doctor::{doctor_report, DoctorReport, DoctorSection, RepositoryPolicySection};
12
14
  pub use integrity::{refresh_integrity, IntegrityRefreshReport};
13
15
  pub use mutation::classify_mutations;
14
16
  pub use output::{summarize_command_output, CommandOutputSummary};
@@ -34,13 +34,12 @@ fn mutation_class(path: &str) -> &'static str {
34
34
  {
35
35
  return "local-only repair";
36
36
  }
37
- if path.starts_with("packages/naome/templates/") || path.starts_with(".naome/bin/") {
37
+ if path.starts_with(".naome/bin/") {
38
38
  return "harness template";
39
39
  }
40
40
  if path.ends_with(".tgz")
41
41
  || path.starts_with("dist/")
42
42
  || path.contains("/dist/")
43
- || path.starts_with("packages/naome/native/")
44
43
  || path.starts_with("native/")
45
44
  {
46
45
  return "release artifact";
@@ -1,12 +1,14 @@
1
1
  use std::fs;
2
- use std::path::{Path, PathBuf};
3
- use std::process::Command;
4
2
  use std::sync::Mutex;
5
3
  use std::time::{SystemTime, UNIX_EPOCH};
6
4
 
7
5
  use naome_core::{evaluate_decision, format_decision, EvaluationOptions};
8
6
  use serde_json::json;
9
7
 
8
+ mod repo_support;
9
+
10
+ use repo_support::TestRepo;
11
+
10
12
  static ENV_LOCK: Mutex<()> = Mutex::new(());
11
13
 
12
14
  #[test]
@@ -34,20 +36,7 @@ fn completed_task_diff_returns_baseline_choices() {
34
36
  let repo = TestRepo::new("completed-task");
35
37
  repo.init_git();
36
38
  repo.write_file("README.md", "# Baseline\n");
37
- repo.write_naome_json("init-state.json", json!({ "initialized": true }));
38
- repo.write_naome_json("upgrade-state.json", json!({ "status": "complete" }));
39
- repo.write_naome_json(
40
- "task-state.json",
41
- json!({
42
- "status": "complete",
43
- "activeTask": {
44
- "id": "readme-task",
45
- "allowedPaths": ["README.md"],
46
- "requiredCheckIds": ["diff-check"],
47
- "proofResults": []
48
- }
49
- }),
50
- );
39
+ repo.write_base_naome_state(completed_without_proof_state());
51
40
  repo.git(&["add", "."]);
52
41
  repo.git(&["commit", "-m", "baseline"]);
53
42
  repo.write_file("README.md", "# Changed\n");
@@ -71,17 +60,7 @@ fn completed_task_diff_returns_baseline_choices() {
71
60
 
72
61
  #[test]
73
62
  fn idle_unowned_diff_blocks_new_feature_work() {
74
- let repo = TestRepo::new("unowned-diff");
75
- repo.init_git();
76
- repo.write_file("README.md", "# Baseline\n");
77
- repo.write_naome_json("init-state.json", json!({ "initialized": true }));
78
- repo.write_naome_json("upgrade-state.json", json!({ "status": "complete" }));
79
- repo.write_naome_json(
80
- "task-state.json",
81
- json!({ "status": "idle", "activeTask": null }),
82
- );
83
- repo.git(&["add", "."]);
84
- repo.git(&["commit", "-m", "baseline"]);
63
+ let repo = TestRepo::idle_readme("unowned-diff");
85
64
  repo.write_file("README.md", "# Manual change\n");
86
65
 
87
66
  let decision = evaluate_decision(repo.path(), EvaluationOptions::offline()).unwrap();
@@ -96,12 +75,7 @@ fn changed_paths_handle_worktree_renames_without_corrupting_old_path_tokens() {
96
75
  let repo = TestRepo::new("worktree-rename");
97
76
  repo.init_git();
98
77
  repo.write_file("packages/old/file.txt", "baseline\n");
99
- repo.write_naome_json("init-state.json", json!({ "initialized": true }));
100
- repo.write_naome_json("upgrade-state.json", json!({ "status": "complete" }));
101
- repo.write_naome_json(
102
- "task-state.json",
103
- json!({ "status": "idle", "activeTask": null }),
104
- );
78
+ repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
105
79
  repo.git(&["add", "."]);
106
80
  repo.git(&["commit", "-m", "baseline"]);
107
81
 
@@ -129,19 +103,10 @@ fn active_task_allows_control_state_and_untracked_files_inside_scope() {
129
103
  let repo = TestRepo::new("active-scope");
130
104
  repo.init_git();
131
105
  repo.write_file("README.md", "# Baseline\n");
132
- repo.write_naome_json("init-state.json", json!({ "initialized": true }));
133
- repo.write_naome_json("upgrade-state.json", json!({ "status": "complete" }));
134
- repo.write_naome_json(
135
- "task-state.json",
136
- json!({
137
- "status": "implementing",
138
- "activeTask": {
139
- "id": "rust-task",
140
- "allowedPaths": ["packages/naome/crates/naome-core/src/lib.rs"],
141
- "requiredCheckIds": ["decision-engine-tests"],
142
- "proofResults": []
143
- }
144
- }),
106
+ repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
107
+ repo.write_implementing_task_state(
108
+ "packages/naome/crates/naome-core/src/lib.rs",
109
+ "decision-engine-tests",
145
110
  );
146
111
  repo.git(&["add", "."]);
147
112
  repo.git(&["commit", "-m", "baseline"]);
@@ -149,17 +114,9 @@ fn active_task_allows_control_state_and_untracked_files_inside_scope() {
149
114
  "packages/naome/crates/naome-core/src/lib.rs",
150
115
  "pub fn marker() {}\n",
151
116
  );
152
- repo.write_naome_json(
153
- "task-state.json",
154
- json!({
155
- "status": "implementing",
156
- "activeTask": {
157
- "id": "rust-task",
158
- "allowedPaths": ["packages/naome/crates/naome-core/src/lib.rs"],
159
- "requiredCheckIds": ["decision-engine-tests"],
160
- "proofResults": []
161
- }
162
- }),
117
+ repo.write_implementing_task_state(
118
+ "packages/naome/crates/naome-core/src/lib.rs",
119
+ "decision-engine-tests",
163
120
  );
164
121
 
165
122
  let decision = evaluate_decision(repo.path(), EvaluationOptions::offline()).unwrap();
@@ -202,12 +159,7 @@ fn online_checks_use_explicit_node_binary_from_environment() {
202
159
  let _guard = ENV_LOCK.lock().unwrap();
203
160
  let repo = TestRepo::new("explicit-node-bin");
204
161
  repo.init_git();
205
- repo.write_naome_json("init-state.json", json!({ "initialized": true }));
206
- repo.write_naome_json("upgrade-state.json", json!({ "status": "complete" }));
207
- repo.write_naome_json(
208
- "task-state.json",
209
- json!({ "status": "idle", "activeTask": null }),
210
- );
162
+ repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
211
163
  repo.git(&["add", "."]);
212
164
  repo.git(&["commit", "-m", "baseline"]);
213
165
  let fake_node = std::env::temp_dir().join(format!(
@@ -240,60 +192,14 @@ fn online_checks_use_explicit_node_binary_from_environment() {
240
192
  );
241
193
  }
242
194
 
243
- struct TestRepo {
244
- root: PathBuf,
245
- }
246
-
247
- impl TestRepo {
248
- fn new(name: &str) -> Self {
249
- let nonce = SystemTime::now()
250
- .duration_since(UNIX_EPOCH)
251
- .unwrap()
252
- .as_nanos();
253
- let root = std::env::temp_dir().join(format!("naome-core-{name}-{nonce}"));
254
- fs::create_dir_all(root.join(".naome")).unwrap();
255
- Self { root }
256
- }
257
-
258
- fn path(&self) -> &Path {
259
- &self.root
260
- }
261
-
262
- fn write_file(&self, relative_path: &str, content: &str) {
263
- let path = self.root.join(relative_path);
264
- if let Some(parent) = path.parent() {
265
- fs::create_dir_all(parent).unwrap();
195
+ fn completed_without_proof_state() -> serde_json::Value {
196
+ json!({
197
+ "status": "complete",
198
+ "activeTask": {
199
+ "id": "readme-task",
200
+ "allowedPaths": ["README.md"],
201
+ "requiredCheckIds": ["diff-check"],
202
+ "proofResults": []
266
203
  }
267
- fs::write(path, content).unwrap();
268
- }
269
-
270
- fn write_naome_json(&self, file_name: &str, value: serde_json::Value) {
271
- let path = self.root.join(".naome").join(file_name);
272
- fs::write(
273
- path,
274
- format!("{}\n", serde_json::to_string_pretty(&value).unwrap()),
275
- )
276
- .unwrap();
277
- }
278
-
279
- fn init_git(&self) {
280
- self.git(&["init"]);
281
- self.git(&["config", "user.email", "naome@example.com"]);
282
- self.git(&["config", "user.name", "NAOME Test"]);
283
- }
284
-
285
- fn git(&self, args: &[&str]) {
286
- let output = Command::new("git")
287
- .args(args)
288
- .current_dir(&self.root)
289
- .output()
290
- .unwrap();
291
- assert!(
292
- output.status.success(),
293
- "git {:?} failed\nstdout:\n{}\nstderr:\n{}",
294
- args,
295
- String::from_utf8_lossy(&output.stdout),
296
- String::from_utf8_lossy(&output.stderr)
297
- );
298
- }
204
+ })
299
205
  }
@@ -30,11 +30,13 @@ const PROJECT_OWNED_PATHS: &[&str] = &[
30
30
  ".naome/upgrade-state.json",
31
31
  ".naome/verification.json",
32
32
  ".naome/repository-quality.json",
33
+ ".naome/repository-structure.json",
33
34
  ".naome/repository-quality-baseline.json",
34
35
  "docs/naome/architecture.md",
35
36
  "docs/naome/decisions.md",
36
37
  "docs/naome/repo-profile.md",
37
38
  "docs/naome/repository-quality.md",
39
+ "docs/naome/repository-structure.md",
38
40
  "docs/naome/security.md",
39
41
  "docs/naome/testing.md",
40
42
  ];
@@ -28,6 +28,7 @@ fn install_plan_includes_git_exclude_and_untrack_policy() {
28
28
  .gitignore_entries
29
29
  .contains(&".naome/task-journal.jsonl"));
30
30
  assert!(plan.git_exclude_entries.contains(&".naome/archive/"));
31
+ assert!(plan.git_exclude_entries.contains(&".naome/cache/"));
31
32
  assert!(plan.git_exclude_entries.contains(&".naome/bin/naome-rust*"));
32
33
  assert!(plan
33
34
  .git_exclude_entries
@@ -36,6 +37,7 @@ fn install_plan_includes_git_exclude_and_untrack_policy() {
36
37
  .git_exclude_entries
37
38
  .contains(&".naome/bin/check-harness-health.js"));
38
39
  assert!(plan.git_untrack_paths.contains(&".naome/archive"));
40
+ assert!(plan.git_untrack_paths.contains(&".naome/cache"));
39
41
  assert!(plan.git_untrack_paths.contains(&".naome/bin/naome-rust"));
40
42
  assert!(plan
41
43
  .git_untrack_paths
@@ -1,20 +1,20 @@
1
1
  use std::fs;
2
- use std::path::{Path, PathBuf};
3
- use std::process::Command;
4
- use std::sync::atomic::{AtomicU64, Ordering};
5
2
 
6
3
  use naome_core::{
7
- check_repository_quality, init_repository_quality, plan_quality_cleanup, route_quality_cleanup,
8
- seed_builtin_verification_checks, QualityMode,
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,
9
7
  };
10
- use serde_json::{json, Value};
8
+ use serde_json::Value;
11
9
 
12
- static FIXTURE_COUNTER: AtomicU64 = AtomicU64::new(0);
10
+ mod quality_structure_support;
11
+
12
+ use quality_structure_support::StructureFixture as QualityFixture;
13
13
 
14
14
  #[test]
15
15
  fn changed_check_blocks_touched_legacy_file_until_it_conforms() {
16
16
  let repo = QualityFixture::new("quality-touched-legacy");
17
- repo.write_quality_config(5, 40, 4);
17
+ repo.write_quality_limits(5, 40, 4);
18
18
  repo.write(
19
19
  "src/large.js",
20
20
  "export function legacy() {\n one();\n two();\n three();\n four();\n}\n",
@@ -38,7 +38,7 @@ fn changed_check_blocks_touched_legacy_file_until_it_conforms() {
38
38
  #[test]
39
39
  fn whole_repo_debt_is_visible_without_blocking_clean_changed_check() {
40
40
  let repo = QualityFixture::new("quality-clean-legacy-report");
41
- repo.write_quality_config(5, 40, 4);
41
+ repo.write_quality_limits(5, 40, 4);
42
42
  repo.write(
43
43
  "src/large.js",
44
44
  "export function legacy() {\n one();\n two();\n three();\n four();\n}\n",
@@ -60,12 +60,13 @@ fn whole_repo_debt_is_visible_without_blocking_clean_changed_check() {
60
60
  #[test]
61
61
  fn changed_check_blocks_new_duplicate_blocks_against_existing_code() {
62
62
  let repo = QualityFixture::new("quality-duplicate-block");
63
- repo.write_quality_config(200, 40, 4);
63
+ repo.write_quality_limits(200, 40, 4);
64
64
  repo.write(
65
65
  "src/existing.js",
66
66
  "export function existing() {\n const alpha = input.alpha;\n const beta = input.beta;\n const total = alpha + beta;\n return total;\n}\n",
67
67
  );
68
68
  repo.commit_all("existing helper");
69
+ let _ = check_repository_quality(repo.path(), QualityMode::DeepReport).unwrap();
69
70
 
70
71
  repo.write(
71
72
  "src/copied.js",
@@ -87,12 +88,13 @@ fn changed_check_blocks_new_duplicate_blocks_against_existing_code() {
87
88
  #[test]
88
89
  fn duplicate_blocks_report_one_finding_per_overlapping_region() {
89
90
  let repo = QualityFixture::new("quality-duplicate-region");
90
- repo.write_quality_config(200, 80, 4);
91
+ repo.write_quality_limits(200, 80, 4);
91
92
  repo.write(
92
93
  "src/existing.js",
93
94
  "export function existing() {\n const alpha = input.alpha;\n const beta = input.beta;\n const gamma = input.gamma;\n const total = alpha + beta + gamma;\n return total;\n}\n",
94
95
  );
95
96
  repo.commit_all("existing helper");
97
+ let _ = check_repository_quality(repo.path(), QualityMode::DeepReport).unwrap();
96
98
 
97
99
  repo.write(
98
100
  "src/copied.js",
@@ -114,7 +116,7 @@ fn duplicate_blocks_report_one_finding_per_overlapping_region() {
114
116
  #[test]
115
117
  fn duplicate_blocks_detect_repeated_regions_in_the_same_file() {
116
118
  let repo = QualityFixture::new("quality-same-file-duplicate-region");
117
- repo.write_quality_config(200, 80, 4);
119
+ repo.write_quality_limits(200, 80, 4);
118
120
  repo.commit_all("empty baseline");
119
121
 
120
122
  repo.write(
@@ -150,7 +152,7 @@ fn duplicate_blocks_detect_repeated_regions_in_the_same_file() {
150
152
  #[test]
151
153
  fn duplicate_blocks_ignore_generated_sha256_maps() {
152
154
  let repo = QualityFixture::new("quality-generated-hash-map");
153
- repo.write_quality_config(200, 80, 4);
155
+ repo.write_quality_limits(200, 80, 4);
154
156
  let generated_map = concat!(
155
157
  "const expected = Object.freeze({\n",
156
158
  " \"one.js\": \"sha256:1111111111111111111111111111111111111111111111111111111111111111\",\n",
@@ -173,7 +175,7 @@ fn duplicate_blocks_ignore_generated_sha256_maps() {
173
175
  #[test]
174
176
  fn near_duplicate_functions_do_not_compare_containers_with_child_symbols() {
175
177
  let repo = QualityFixture::new("quality-container-child-similarity");
176
- repo.write_quality_config(200, 120, 10);
178
+ repo.write_quality_limits(200, 120, 10);
177
179
  repo.write(
178
180
  "src/lib.rs",
179
181
  "pub struct Runner;\n\nimpl Runner {\n pub fn execute(&self) {\n alpha();\n beta();\n gamma();\n delta();\n epsilon();\n zeta();\n eta();\n theta();\n iota();\n kappa();\n lambda();\n mu();\n nu();\n xi();\n omicron();\n pi();\n }\n}\n",
@@ -187,7 +189,7 @@ fn near_duplicate_functions_do_not_compare_containers_with_child_symbols() {
187
189
  }
188
190
 
189
191
  #[test]
190
- fn init_writes_default_config_and_baselines_existing_debt() {
192
+ fn init_writes_default_config_and_marks_baseline_pending() {
191
193
  let repo = QualityFixture::new("quality-init-baseline");
192
194
  let large_file = (0..520)
193
195
  .map(|index| format!("export const value{index} = {index};\n"))
@@ -198,13 +200,19 @@ fn init_writes_default_config_and_baselines_existing_debt() {
198
200
  let init = init_repository_quality(repo.path()).unwrap();
199
201
 
200
202
  assert!(init.config_written);
201
- assert!(init.baseline_written);
203
+ assert!(!init.baseline_written);
204
+ assert!(init.baseline_pending);
202
205
  assert!(repo.path().join(".naome/repository-quality.json").is_file());
203
206
  assert!(repo
204
207
  .path()
205
208
  .join(".naome/repository-quality-baseline.json")
206
209
  .is_file());
207
- assert!(init.baseline_violations > 0);
210
+ assert_eq!(init.baseline_violations, 0);
211
+
212
+ let baseline =
213
+ init_repository_quality_with_mode(repo.path(), QualityInitMode::Baseline).unwrap();
214
+ assert!(baseline.baseline_written);
215
+ assert!(baseline.baseline_violations > 0);
208
216
  }
209
217
 
210
218
  #[test]
@@ -300,7 +308,7 @@ fn seeding_builtin_verification_wires_repository_quality_into_change_types() {
300
308
  #[test]
301
309
  fn cleanup_plan_and_route_turn_debt_into_agent_work() {
302
310
  let repo = QualityFixture::new("quality-cleanup-plan");
303
- repo.write_quality_config(5, 40, 4);
311
+ repo.write_quality_limits(5, 40, 4);
304
312
  repo.write(
305
313
  "src/large.js",
306
314
  "export function legacy() {\n one();\n two();\n three();\n four();\n}\n",
@@ -318,108 +326,3 @@ fn cleanup_plan_and_route_turn_debt_into_agent_work() {
318
326
  .required_checks
319
327
  .contains(&"naome quality check --changed".to_string()));
320
328
  }
321
-
322
- struct QualityFixture {
323
- root: PathBuf,
324
- }
325
-
326
- impl QualityFixture {
327
- fn new(name: &str) -> Self {
328
- let root = std::env::temp_dir().join(format!(
329
- "naome-{name}-{}-{}",
330
- std::process::id(),
331
- FIXTURE_COUNTER.fetch_add(1, Ordering::SeqCst)
332
- ));
333
- let _ = fs::remove_dir_all(&root);
334
- fs::create_dir_all(root.join(".naome")).unwrap();
335
- fs::write(root.join(".naomeignore"), ".naome/archive/\n").unwrap();
336
- git(&root, &["init"]);
337
- git(&root, &["config", "user.email", "test@example.com"]);
338
- git(&root, &["config", "user.name", "Test User"]);
339
- Self { root }
340
- }
341
-
342
- fn path(&self) -> &Path {
343
- &self.root
344
- }
345
-
346
- fn write(&self, relative_path: &str, content: &str) {
347
- let path = self.root.join(relative_path);
348
- if let Some(parent) = path.parent() {
349
- fs::create_dir_all(parent).unwrap();
350
- }
351
- fs::write(path, content).unwrap();
352
- }
353
-
354
- fn write_quality_config(
355
- &self,
356
- max_file_lines: usize,
357
- max_function_lines: usize,
358
- duplicate_block_lines: usize,
359
- ) {
360
- self.write_quality_config_with_adapters(
361
- max_file_lines,
362
- max_function_lines,
363
- duplicate_block_lines,
364
- &[],
365
- );
366
- }
367
-
368
- fn write_quality_config_with_adapters(
369
- &self,
370
- max_file_lines: usize,
371
- max_function_lines: usize,
372
- duplicate_block_lines: usize,
373
- enabled_adapters: &[&str],
374
- ) {
375
- let config = json!({
376
- "schema": "naome.repository-quality.v1",
377
- "version": 1,
378
- "status": "ready",
379
- "limits": {
380
- "maxFileLines": max_file_lines,
381
- "maxNewFileLines": max_file_lines,
382
- "maxDiffAddedLines": 200,
383
- "maxFunctionLines": max_function_lines,
384
- "maxTopLevelSymbols": 30,
385
- "duplicateBlockLines": duplicate_block_lines,
386
- "nearDuplicateSimilarity": 0.9
387
- },
388
- "enabledAdapters": enabled_adapters,
389
- "disabledChecks": [],
390
- "ignoredPaths": [],
391
- "generatedPaths": [],
392
- "pathRules": []
393
- });
394
- self.write(
395
- ".naome/repository-quality.json",
396
- &format!("{}\n", serde_json::to_string_pretty(&config).unwrap()),
397
- );
398
- }
399
-
400
- fn commit_all(&self, message: &str) {
401
- git(&self.root, &["add", "."]);
402
- git(&self.root, &["commit", "-m", message]);
403
- }
404
- }
405
-
406
- impl Drop for QualityFixture {
407
- fn drop(&mut self) {
408
- let _ = fs::remove_dir_all(&self.root);
409
- }
410
- }
411
-
412
- fn git(root: &Path, args: &[&str]) {
413
- let output = Command::new("git")
414
- .args(args)
415
- .current_dir(root)
416
- .output()
417
- .unwrap();
418
- assert!(
419
- output.status.success(),
420
- "git {} failed\nstdout: {}\nstderr: {}",
421
- args.join(" "),
422
- String::from_utf8_lossy(&output.stdout),
423
- String::from_utf8_lossy(&output.stderr)
424
- );
425
- }