@lamentis/naome 1.1.1 → 1.2.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 (126) hide show
  1. package/Cargo.lock +2 -2
  2. package/Cargo.toml +1 -1
  3. package/LICENSE +180 -21
  4. package/README.md +49 -6
  5. package/bin/naome-node.js +44 -4
  6. package/bin/naome.js +54 -16
  7. package/crates/naome-cli/Cargo.toml +1 -1
  8. package/crates/naome-cli/src/check_commands.rs +135 -0
  9. package/crates/naome-cli/src/cli_args.rs +5 -0
  10. package/crates/naome-cli/src/dispatcher.rs +36 -0
  11. package/crates/naome-cli/src/install_bridge.rs +83 -0
  12. package/crates/naome-cli/src/main.rs +57 -341
  13. package/crates/naome-cli/src/prompt_commands.rs +68 -0
  14. package/crates/naome-cli/src/quality_commands.rs +141 -0
  15. package/crates/naome-cli/src/simple_commands.rs +53 -0
  16. package/crates/naome-cli/src/workflow_commands.rs +153 -0
  17. package/crates/naome-core/Cargo.toml +1 -1
  18. package/crates/naome-core/src/harness_health/integrity.rs +96 -0
  19. package/crates/naome-core/src/harness_health.rs +14 -126
  20. package/crates/naome-core/src/install_plan.rs +3 -0
  21. package/crates/naome-core/src/intent/classifier.rs +171 -0
  22. package/crates/naome-core/src/intent/envelope.rs +108 -0
  23. package/crates/naome-core/src/intent/legacy.rs +138 -0
  24. package/crates/naome-core/src/intent/legacy_response.rs +76 -0
  25. package/crates/naome-core/src/intent/model.rs +71 -0
  26. package/crates/naome-core/src/intent/patterns.rs +170 -0
  27. package/crates/naome-core/src/intent/resolver.rs +162 -0
  28. package/crates/naome-core/src/intent/resolver_active.rs +17 -0
  29. package/crates/naome-core/src/intent/resolver_baseline.rs +55 -0
  30. package/crates/naome-core/src/intent/resolver_catalog.rs +167 -0
  31. package/crates/naome-core/src/intent/resolver_policy.rs +72 -0
  32. package/crates/naome-core/src/intent/resolver_shared.rs +55 -0
  33. package/crates/naome-core/src/intent/risk.rs +40 -0
  34. package/crates/naome-core/src/intent/segment.rs +170 -0
  35. package/crates/naome-core/src/intent.rs +64 -879
  36. package/crates/naome-core/src/journal.rs +9 -20
  37. package/crates/naome-core/src/lib.rs +13 -0
  38. package/crates/naome-core/src/quality/adapters.rs +178 -0
  39. package/crates/naome-core/src/quality/baseline.rs +75 -0
  40. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +175 -0
  41. package/crates/naome-core/src/quality/checks/near_duplicates.rs +130 -0
  42. package/crates/naome-core/src/quality/checks.rs +228 -0
  43. package/crates/naome-core/src/quality/cleanup.rs +72 -0
  44. package/crates/naome-core/src/quality/config.rs +109 -0
  45. package/crates/naome-core/src/quality/mod.rs +90 -0
  46. package/crates/naome-core/src/quality/scanner/repo_paths.rs +103 -0
  47. package/crates/naome-core/src/quality/scanner.rs +367 -0
  48. package/crates/naome-core/src/quality/types.rs +289 -0
  49. package/crates/naome-core/src/route.rs +292 -17
  50. package/crates/naome-core/src/task_state/admission.rs +63 -0
  51. package/crates/naome-core/src/task_state/admission_proof.rs +72 -0
  52. package/crates/naome-core/src/task_state/api.rs +130 -0
  53. package/crates/naome-core/src/task_state/commit_gate.rs +138 -0
  54. package/crates/naome-core/src/task_state/compact_proof.rs +160 -0
  55. package/crates/naome-core/src/task_state/completed_refresh.rs +89 -0
  56. package/crates/naome-core/src/task_state/completion.rs +72 -0
  57. package/crates/naome-core/src/task_state/deleted_paths.rs +47 -0
  58. package/crates/naome-core/src/task_state/diff.rs +95 -0
  59. package/crates/naome-core/src/task_state/evidence.rs +154 -0
  60. package/crates/naome-core/src/task_state/git_io.rs +86 -0
  61. package/crates/naome-core/src/task_state/git_parse.rs +86 -0
  62. package/crates/naome-core/src/task_state/git_refs.rs +37 -0
  63. package/crates/naome-core/src/task_state/human_review_state.rs +31 -0
  64. package/crates/naome-core/src/task_state/mod.rs +38 -0
  65. package/crates/naome-core/src/task_state/process_guard.rs +40 -0
  66. package/crates/naome-core/src/task_state/progress.rs +123 -0
  67. package/crates/naome-core/src/task_state/proof.rs +139 -0
  68. package/crates/naome-core/src/task_state/proof_entry.rs +66 -0
  69. package/crates/naome-core/src/task_state/proof_model.rs +70 -0
  70. package/crates/naome-core/src/task_state/proof_sources.rs +76 -0
  71. package/crates/naome-core/src/task_state/push_gate.rs +49 -0
  72. package/crates/naome-core/src/task_state/reconcile.rs +7 -0
  73. package/crates/naome-core/src/task_state/repair.rs +168 -0
  74. package/crates/naome-core/src/task_state/shape.rs +117 -0
  75. package/crates/naome-core/src/task_state/task_diff_api.rs +170 -0
  76. package/crates/naome-core/src/task_state/task_records.rs +131 -0
  77. package/crates/naome-core/src/task_state/task_references.rs +126 -0
  78. package/crates/naome-core/src/task_state/types.rs +87 -0
  79. package/crates/naome-core/src/task_state/util.rs +137 -0
  80. package/crates/naome-core/src/verification/render.rs +122 -0
  81. package/crates/naome-core/src/verification.rs +176 -58
  82. package/crates/naome-core/src/verification_contract.rs +49 -21
  83. package/crates/naome-core/src/workflow/integrity.rs +123 -0
  84. package/crates/naome-core/src/workflow/integrity_normalize.rs +7 -0
  85. package/crates/naome-core/src/workflow/integrity_support.rs +110 -0
  86. package/crates/naome-core/src/workflow/mod.rs +18 -0
  87. package/crates/naome-core/src/workflow/mutation.rs +68 -0
  88. package/crates/naome-core/src/workflow/output.rs +111 -0
  89. package/crates/naome-core/src/workflow/phase_inference.rs +73 -0
  90. package/crates/naome-core/src/workflow/phases.rs +169 -0
  91. package/crates/naome-core/src/workflow/policy.rs +156 -0
  92. package/crates/naome-core/src/workflow/processes.rs +91 -0
  93. package/crates/naome-core/src/workflow/types.rs +42 -0
  94. package/crates/naome-core/tests/harness_health.rs +3 -0
  95. package/crates/naome-core/tests/intent.rs +97 -792
  96. package/crates/naome-core/tests/intent_support/mod.rs +133 -0
  97. package/crates/naome-core/tests/intent_v2.rs +90 -0
  98. package/crates/naome-core/tests/quality.rs +425 -0
  99. package/crates/naome-core/tests/route.rs +221 -4
  100. package/crates/naome-core/tests/task_state.rs +3 -0
  101. package/crates/naome-core/tests/task_state_compact.rs +110 -0
  102. package/crates/naome-core/tests/task_state_compact_support/mod.rs +5 -0
  103. package/crates/naome-core/tests/task_state_compact_support/repo.rs +130 -0
  104. package/crates/naome-core/tests/task_state_compact_support/states.rs +151 -0
  105. package/crates/naome-core/tests/workflow_integrity.rs +85 -0
  106. package/crates/naome-core/tests/workflow_policy.rs +139 -0
  107. package/crates/naome-core/tests/workflow_support/mod.rs +194 -0
  108. package/native/darwin-arm64/naome +0 -0
  109. package/native/linux-x64/naome +0 -0
  110. package/package.json +2 -2
  111. package/templates/naome-root/.naome/bin/check-harness-health.js +66 -85
  112. package/templates/naome-root/.naome/bin/check-task-state.js +9 -10
  113. package/templates/naome-root/.naome/bin/naome.js +34 -63
  114. package/templates/naome-root/.naome/manifest.json +20 -18
  115. package/templates/naome-root/.naome/repository-quality-baseline.json +5 -0
  116. package/templates/naome-root/.naome/repository-quality.json +24 -0
  117. package/templates/naome-root/.naome/task-contract.schema.json +93 -11
  118. package/templates/naome-root/.naome/upgrade-state.json +1 -1
  119. package/templates/naome-root/.naome/verification.json +37 -0
  120. package/templates/naome-root/AGENTS.md +3 -0
  121. package/templates/naome-root/docs/naome/agent-workflow.md +25 -12
  122. package/templates/naome-root/docs/naome/execution.md +25 -21
  123. package/templates/naome-root/docs/naome/index.md +4 -3
  124. package/templates/naome-root/docs/naome/repository-quality.md +43 -0
  125. package/templates/naome-root/docs/naome/testing.md +12 -0
  126. package/crates/naome-core/src/task_state.rs +0 -2210
@@ -0,0 +1,141 @@
1
+ use std::path::Path;
2
+
3
+ use naome_core::{
4
+ check_repository_quality, init_repository_quality, plan_quality_cleanup, route_quality_cleanup,
5
+ QualityMode,
6
+ };
7
+
8
+ use crate::cli_args::option_value;
9
+
10
+ pub fn run_quality_command(root: &Path, args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
11
+ let Some(subcommand) = args.get(1).map(String::as_str) else {
12
+ return Err("naome quality requires init, check, or report.".into());
13
+ };
14
+ let json = args.iter().any(|arg| arg == "--json");
15
+
16
+ match subcommand {
17
+ "init" => {
18
+ let result = init_repository_quality(root)?;
19
+ if json {
20
+ println!("{}", serde_json::to_string_pretty(&result)?);
21
+ } else {
22
+ println!("NAOME repository quality initialized.");
23
+ println!("Baseline violations: {}", result.baseline_violations);
24
+ }
25
+ }
26
+ "check" => run_quality_check(root, args, json)?,
27
+ "report" => run_quality_report(root, json)?,
28
+ _ => return Err(format!("unknown naome quality command: {subcommand}").into()),
29
+ }
30
+ Ok(())
31
+ }
32
+
33
+ pub fn run_cleanup_command(root: &Path, args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
34
+ let Some(subcommand) = args.get(1).map(String::as_str) else {
35
+ return Err("naome cleanup requires plan or route.".into());
36
+ };
37
+ let json = args.iter().any(|arg| arg == "--json");
38
+
39
+ match subcommand {
40
+ "plan" => run_cleanup_plan(root, json)?,
41
+ "route" => run_cleanup_route(root, args, json)?,
42
+ _ => return Err(format!("unknown naome cleanup command: {subcommand}").into()),
43
+ }
44
+ Ok(())
45
+ }
46
+
47
+ fn run_quality_check(
48
+ root: &Path,
49
+ args: &[String],
50
+ json: bool,
51
+ ) -> Result<(), Box<dyn std::error::Error>> {
52
+ if !args.iter().any(|arg| arg == "--changed") {
53
+ return Err("naome quality check requires --changed.".into());
54
+ }
55
+ let report = check_repository_quality(root, QualityMode::Changed)?;
56
+ if json {
57
+ println!("{}", serde_json::to_string_pretty(&report)?);
58
+ } else if report.ok {
59
+ println!("NAOME repository quality OK.");
60
+ } else {
61
+ eprintln!(
62
+ "NAOME repository quality failed with {} violation(s).",
63
+ report.violations.len()
64
+ );
65
+ for violation in report.violations.iter().take(20) {
66
+ print_quality_violation(violation);
67
+ }
68
+ }
69
+ if !report.ok {
70
+ std::process::exit(1);
71
+ }
72
+ Ok(())
73
+ }
74
+
75
+ fn run_quality_report(root: &Path, json: bool) -> Result<(), Box<dyn std::error::Error>> {
76
+ let report = check_repository_quality(root, QualityMode::Report)?;
77
+ if json {
78
+ println!("{}", serde_json::to_string_pretty(&report)?);
79
+ } else if report.violations.is_empty() {
80
+ println!("NAOME repository quality report: no debt found.");
81
+ } else {
82
+ println!(
83
+ "NAOME repository quality report: {} violation(s) across {} scanned file(s).",
84
+ report.violations.len(),
85
+ report.summary.scanned_files
86
+ );
87
+ for violation in report.violations.iter().take(20) {
88
+ print_quality_violation(violation);
89
+ }
90
+ }
91
+ Ok(())
92
+ }
93
+
94
+ fn run_cleanup_plan(root: &Path, json: bool) -> Result<(), Box<dyn std::error::Error>> {
95
+ let plan = plan_quality_cleanup(root)?;
96
+ if json {
97
+ println!("{}", serde_json::to_string_pretty(&plan)?);
98
+ } else if plan.tasks.is_empty() {
99
+ println!("NAOME cleanup plan: no repository-quality debt found.");
100
+ } else {
101
+ println!("NAOME cleanup plan:");
102
+ for task in plan.tasks.iter().take(20) {
103
+ println!(
104
+ "- {}: {} violation(s) [{}]",
105
+ task.path,
106
+ task.violation_count,
107
+ task.check_ids.join(", ")
108
+ );
109
+ }
110
+ }
111
+ Ok(())
112
+ }
113
+
114
+ fn run_cleanup_route(
115
+ root: &Path,
116
+ args: &[String],
117
+ json: bool,
118
+ ) -> Result<(), Box<dyn std::error::Error>> {
119
+ let Some(path) = option_value(args, "--path") else {
120
+ return Err("naome cleanup route requires --path <path>.".into());
121
+ };
122
+ let route = route_quality_cleanup(root, path)?;
123
+ if json {
124
+ println!("{}", serde_json::to_string_pretty(&route)?);
125
+ } else {
126
+ println!("NAOME cleanup route for {}", route.path);
127
+ println!("{}", route.agent_instructions);
128
+ if !route.related_paths.is_empty() {
129
+ println!("Related paths: {}", route.related_paths.join(", "));
130
+ }
131
+ }
132
+ Ok(())
133
+ }
134
+
135
+ fn print_quality_violation(violation: &naome_core::QualityViolation) {
136
+ let location = violation
137
+ .line
138
+ .map(|line| format!("{}:{line}", violation.path))
139
+ .unwrap_or_else(|| violation.path.clone());
140
+ eprintln!("- {location} {}: {}", violation.check_id, violation.message);
141
+ }
@@ -0,0 +1,53 @@
1
+ use std::path::Path;
2
+
3
+ use naome_core::{install_plan, seed_builtin_verification_checks};
4
+
5
+ use crate::cli_args::option_value;
6
+
7
+ pub fn print_install_plan(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
8
+ let harness_version = option_value(args, "--harness-version")
9
+ .or_else(|| option_value(args, "--version"))
10
+ .unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string());
11
+ println!(
12
+ "{}",
13
+ serde_json::to_string_pretty(&install_plan(harness_version))?
14
+ );
15
+ Ok(())
16
+ }
17
+
18
+ pub fn seed_verification(root: &Path) -> Result<(), Box<dyn std::error::Error>> {
19
+ if seed_builtin_verification_checks(root)? {
20
+ println!("NAOME verification checks updated.");
21
+ } else {
22
+ println!("NAOME verification checks already present.");
23
+ }
24
+ Ok(())
25
+ }
26
+
27
+ pub fn run_journal_task(root: &Path, args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
28
+ let outcome =
29
+ option_value(args, "--outcome").unwrap_or_else(|| "naome_commit_baseline".to_string());
30
+ let commit_before = option_value(args, "--commit-before");
31
+ let commit_after = option_value(args, "--commit-after");
32
+ let entry = naome_core::append_task_journal(root, &outcome, commit_before, commit_after)?;
33
+ if args.iter().any(|arg| arg == "--json") {
34
+ println!("{}", serde_json::to_string_pretty(&entry)?);
35
+ } else if entry.is_some() {
36
+ println!("NAOME task journal updated.");
37
+ } else {
38
+ println!("NAOME task journal had no active task to record.");
39
+ }
40
+ Ok(())
41
+ }
42
+
43
+ pub fn run_commit_paths(root: &Path, args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
44
+ let paths = naome_core::completed_task_commit_paths(root)?;
45
+ if args.iter().any(|arg| arg == "--json") {
46
+ println!("{}", serde_json::to_string_pretty(&paths)?);
47
+ } else {
48
+ for path in paths {
49
+ println!("{path}");
50
+ }
51
+ }
52
+ Ok(())
53
+ }
@@ -0,0 +1,153 @@
1
+ use std::path::Path;
2
+
3
+ use naome_core::{
4
+ classify_mutations, refresh_integrity, safe_rg_args, tracked_process_report,
5
+ validate_search_command, verification_phase_plan,
6
+ };
7
+
8
+ use crate::cli_args::option_value;
9
+
10
+ pub fn run_refresh_integrity(
11
+ root: &Path,
12
+ args: &[String],
13
+ ) -> Result<(), Box<dyn std::error::Error>> {
14
+ let report = refresh_integrity(root)?;
15
+ if args.iter().any(|arg| arg == "--json") {
16
+ println!("{}", serde_json::to_string_pretty(&report)?);
17
+ } else if report.updated {
18
+ println!(
19
+ "NAOME integrity refreshed: {}.",
20
+ report.changed_paths.join(", ")
21
+ );
22
+ } else {
23
+ println!("NAOME integrity already current.");
24
+ }
25
+ Ok(())
26
+ }
27
+
28
+ pub fn run_workflow_command(
29
+ root: &Path,
30
+ args: &[String],
31
+ ) -> Result<(), Box<dyn std::error::Error>> {
32
+ let Some(subcommand) = args.get(1).map(String::as_str) else {
33
+ return Err("naome workflow requires search-profile, check-search, phases, processes, or mutations.".into());
34
+ };
35
+ let json = args.iter().any(|arg| arg == "--json");
36
+
37
+ match subcommand {
38
+ "search-profile" => {
39
+ let args = safe_rg_args(root)?;
40
+ if json {
41
+ println!("{}", serde_json::to_string_pretty(&args)?);
42
+ } else {
43
+ println!(
44
+ "{}",
45
+ args.iter()
46
+ .map(|arg| shell_quote(arg))
47
+ .collect::<Vec<_>>()
48
+ .join(" ")
49
+ );
50
+ }
51
+ }
52
+ "check-search" => run_check_search(root, args, json)?,
53
+ "phases" => {
54
+ let plan = verification_phase_plan(root, &[])?;
55
+ if json {
56
+ println!("{}", serde_json::to_string_pretty(&plan)?);
57
+ } else {
58
+ println!(
59
+ "Recommended checks: {}",
60
+ empty_label(&plan.recommended_check_ids)
61
+ );
62
+ if !plan.withheld_check_ids.is_empty() {
63
+ println!("Withheld checks: {}", plan.withheld_check_ids.join(", "));
64
+ }
65
+ }
66
+ }
67
+ "processes" => {
68
+ let report = tracked_process_report(root)?;
69
+ if json {
70
+ println!("{}", serde_json::to_string_pretty(&report)?);
71
+ } else if report.active.is_empty() {
72
+ println!("NAOME tracked processes OK.");
73
+ } else {
74
+ println!("Active tracked processes:");
75
+ for process in report.active {
76
+ println!("- {} [{}]", process.command, process.cwd);
77
+ }
78
+ }
79
+ }
80
+ "mutations" => run_mutations(root, args, json)?,
81
+ _ => return Err(format!("unknown naome workflow command: {subcommand}").into()),
82
+ }
83
+ Ok(())
84
+ }
85
+
86
+ fn run_check_search(
87
+ root: &Path,
88
+ args: &[String],
89
+ json: bool,
90
+ ) -> Result<(), Box<dyn std::error::Error>> {
91
+ let Some(command) = option_value(args, "--command") else {
92
+ return Err("naome workflow check-search requires --command <command>.".into());
93
+ };
94
+ let findings = validate_search_command(root, &command)?;
95
+ if !json && findings.is_empty() {
96
+ println!("NAOME search command OK.");
97
+ }
98
+ if json {
99
+ println!("{}", serde_json::to_string_pretty(&findings)?);
100
+ } else {
101
+ for finding in &findings {
102
+ println!("- {}: {}", finding.check_id, finding.message);
103
+ }
104
+ }
105
+ if !findings.is_empty() {
106
+ std::process::exit(1);
107
+ }
108
+ Ok(())
109
+ }
110
+
111
+ fn run_mutations(
112
+ root: &Path,
113
+ args: &[String],
114
+ json: bool,
115
+ ) -> Result<(), Box<dyn std::error::Error>> {
116
+ let paths = args
117
+ .iter()
118
+ .skip_while(|arg| arg.as_str() != "--path")
119
+ .skip(1)
120
+ .filter(|arg| !arg.starts_with("--"))
121
+ .cloned()
122
+ .collect::<Vec<_>>();
123
+ if paths.is_empty() {
124
+ return Err("naome workflow mutations requires --path <path> [path...]".into());
125
+ }
126
+ let classes = classify_mutations(root, &paths)?;
127
+ if json {
128
+ println!("{}", serde_json::to_string_pretty(&classes)?);
129
+ } else {
130
+ for entry in classes {
131
+ println!("{}: {}", entry.path, entry.mutation_class);
132
+ }
133
+ }
134
+ Ok(())
135
+ }
136
+
137
+ fn empty_label(values: &[String]) -> String {
138
+ if values.is_empty() {
139
+ "none".to_string()
140
+ } else {
141
+ values.join(", ")
142
+ }
143
+ }
144
+
145
+ fn shell_quote(value: &str) -> String {
146
+ if value
147
+ .chars()
148
+ .all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | '.' | '/' | ':'))
149
+ {
150
+ return value.to_string();
151
+ }
152
+ format!("'{}'", value.replace('\'', "'\\''"))
153
+ }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "naome-core"
3
- version = "1.1.1"
3
+ version = "1.2.0"
4
4
  edition.workspace = true
5
5
  license.workspace = true
6
6
  repository.workspace = true
@@ -0,0 +1,96 @@
1
+ use sha2::{Digest, Sha256};
2
+
3
+ const HEALTH_CHECKER_RELATIVE_PATH: &str = ".naome/bin/check-harness-health.js";
4
+ const TASK_STATE_CHECKER_RELATIVE_PATH: &str = ".naome/bin/check-task-state.js";
5
+
6
+ pub(crate) const NAOME_COMMAND_RELATIVE_PATH: &str = ".naome/bin/naome.js";
7
+
8
+ #[cfg(windows)]
9
+ pub(crate) const NATIVE_BINARY_RELATIVE_PATH: &str = ".naome/bin/naome-rust.exe";
10
+ #[cfg(not(windows))]
11
+ pub(crate) const NATIVE_BINARY_RELATIVE_PATH: &str = ".naome/bin/naome-rust";
12
+
13
+ pub(crate) fn machine_integrity_hash(relative_path: &str, content: &[u8]) -> String {
14
+ format!("sha256:{}", machine_sha256(relative_path, content))
15
+ }
16
+
17
+ pub(crate) fn sha256_bytes(content: &[u8]) -> String {
18
+ let mut hasher = Sha256::new();
19
+ hasher.update(content);
20
+ format!("{:x}", hasher.finalize())
21
+ }
22
+
23
+ pub(crate) fn is_integrity_hash(value: &str) -> bool {
24
+ value.len() == "sha256:".len() + 64
25
+ && value.starts_with("sha256:")
26
+ && value["sha256:".len()..]
27
+ .chars()
28
+ .all(|ch| ch.is_ascii_hexdigit() && !ch.is_ascii_uppercase())
29
+ }
30
+
31
+ pub(crate) fn native_integrity_from_naome_command(content: &str) -> Option<String> {
32
+ let prefix = "const expectedNativeBinaryIntegrity = \"";
33
+ let start = content.find(prefix)? + prefix.len();
34
+ let rest = &content[start..];
35
+ let end = rest.find("\";")?;
36
+ let value = &rest[..end];
37
+ is_integrity_hash(value).then(|| value.to_string())
38
+ }
39
+
40
+ fn machine_sha256(relative_path: &str, content: &[u8]) -> String {
41
+ let normalized = normalize_machine_owned_content(relative_path, content);
42
+ sha256_bytes(&normalized)
43
+ }
44
+
45
+ fn normalize_machine_owned_content(relative_path: &str, content: &[u8]) -> Vec<u8> {
46
+ if relative_path == HEALTH_CHECKER_RELATIVE_PATH
47
+ || relative_path == TASK_STATE_CHECKER_RELATIVE_PATH
48
+ {
49
+ return replace_expected_integrity_block(content);
50
+ }
51
+
52
+ if relative_path == NAOME_COMMAND_RELATIVE_PATH {
53
+ return replace_native_integrity_line(content);
54
+ }
55
+
56
+ content.to_vec()
57
+ }
58
+
59
+ fn replace_expected_integrity_block(content: &[u8]) -> Vec<u8> {
60
+ let text = String::from_utf8_lossy(content);
61
+ let start_marker = "const expectedMachineOwnedIntegrity = Object.freeze({\n";
62
+ let normalized =
63
+ "const expectedMachineOwnedIntegrity = Object.freeze({\n __generated__: \"sha256:generated\"\n});\n";
64
+ let Some(start) = text.find(start_marker) else {
65
+ return content.to_vec();
66
+ };
67
+ let after_start = start + start_marker.len();
68
+ let Some(relative_end) = text[after_start..].find("\n});\n") else {
69
+ return content.to_vec();
70
+ };
71
+ let end = after_start + relative_end + "\n});\n".len();
72
+
73
+ let mut next = String::with_capacity(text.len());
74
+ next.push_str(&text[..start]);
75
+ next.push_str(normalized);
76
+ next.push_str(&text[end..]);
77
+ next.into_bytes()
78
+ }
79
+
80
+ fn replace_native_integrity_line(content: &[u8]) -> Vec<u8> {
81
+ let text = String::from_utf8_lossy(content);
82
+ let prefix = "const expectedNativeBinaryIntegrity = \"";
83
+ let Some(start) = text.find(prefix) else {
84
+ return content.to_vec();
85
+ };
86
+ let Some(relative_end) = text[start..].find(";\n") else {
87
+ return content.to_vec();
88
+ };
89
+ let end = start + relative_end + ";\n".len();
90
+
91
+ let mut next = String::with_capacity(text.len());
92
+ next.push_str(&text[..start]);
93
+ next.push_str("const expectedNativeBinaryIntegrity = \"sha256:generated\";\n");
94
+ next.push_str(&text[end..]);
95
+ next.into_bytes()
96
+ }
@@ -1,49 +1,19 @@
1
+ mod integrity;
2
+
1
3
  use std::collections::{HashMap, HashSet};
2
4
  use std::fs;
3
5
  use std::path::{Component, Path};
4
6
 
5
7
  use serde_json::Value;
6
- use sha2::{Digest, Sha256};
7
8
 
9
+ pub(crate) use self::integrity::machine_integrity_hash;
10
+ use self::integrity::{
11
+ is_integrity_hash, native_integrity_from_naome_command, sha256_bytes,
12
+ NAOME_COMMAND_RELATIVE_PATH, NATIVE_BINARY_RELATIVE_PATH,
13
+ };
14
+ use crate::install_plan::{MACHINE_OWNED_PATHS, PROJECT_OWNED_PATHS};
8
15
  use crate::models::NaomeError;
9
16
 
10
- const HEALTH_CHECKER_RELATIVE_PATH: &str = ".naome/bin/check-harness-health.js";
11
- const TASK_STATE_CHECKER_RELATIVE_PATH: &str = ".naome/bin/check-task-state.js";
12
- const NAOME_COMMAND_RELATIVE_PATH: &str = ".naome/bin/naome.js";
13
-
14
- #[cfg(windows)]
15
- const NATIVE_BINARY_RELATIVE_PATH: &str = ".naome/bin/naome-rust.exe";
16
- #[cfg(not(windows))]
17
- const NATIVE_BINARY_RELATIVE_PATH: &str = ".naome/bin/naome-rust";
18
-
19
- const EXPECTED_MACHINE_OWNED_PATHS: &[&str] = &[
20
- "AGENTS.md",
21
- ".naome/package.json",
22
- ".naome/bin/naome.js",
23
- ".naome/bin/check-task-state.js",
24
- ".naome/bin/check-harness-health.js",
25
- ".naome/task-contract.schema.json",
26
- "docs/naome/index.md",
27
- "docs/naome/first-run.md",
28
- "docs/naome/agent-workflow.md",
29
- "docs/naome/execution.md",
30
- "docs/naome/upgrade.md",
31
- ];
32
-
33
- const EXPECTED_PROJECT_OWNED_PATHS: &[&str] = &[
34
- ".naomeignore",
35
- ".naome/init-state.json",
36
- ".naome/manifest.json",
37
- ".naome/task-state.json",
38
- ".naome/upgrade-state.json",
39
- ".naome/verification.json",
40
- "docs/naome/architecture.md",
41
- "docs/naome/decisions.md",
42
- "docs/naome/repo-profile.md",
43
- "docs/naome/security.md",
44
- "docs/naome/testing.md",
45
- ];
46
-
47
17
  #[derive(Debug, Clone, Default)]
48
18
  pub struct HarnessHealthOptions {
49
19
  pub expected_integrity: HashMap<String, String>,
@@ -57,11 +27,11 @@ pub fn validate_harness_health(
57
27
  ) -> Result<Vec<String>, NaomeError> {
58
28
  let mut errors = Vec::new();
59
29
 
60
- for relative_path in EXPECTED_MACHINE_OWNED_PATHS {
30
+ for relative_path in MACHINE_OWNED_PATHS {
61
31
  validate_regular_file(root, relative_path, &mut errors)?;
62
32
  }
63
33
 
64
- for relative_path in EXPECTED_PROJECT_OWNED_PATHS {
34
+ for relative_path in PROJECT_OWNED_PATHS {
65
35
  validate_regular_file(root, relative_path, &mut errors)?;
66
36
  }
67
37
 
@@ -130,13 +100,13 @@ fn validate_manifest_ownership(manifest: &Value, errors: &mut Vec<String>) {
130
100
 
131
101
  validate_contains_all(
132
102
  &machine_owned,
133
- EXPECTED_MACHINE_OWNED_PATHS,
103
+ MACHINE_OWNED_PATHS,
134
104
  ".naome/manifest.json machineOwned",
135
105
  errors,
136
106
  );
137
107
  validate_contains_all(
138
108
  &project_owned,
139
- EXPECTED_PROJECT_OWNED_PATHS,
109
+ PROJECT_OWNED_PATHS,
140
110
  ".naome/manifest.json projectOwned",
141
111
  errors,
142
112
  );
@@ -152,7 +122,7 @@ fn validate_manifest_integrity(
152
122
  return Ok(());
153
123
  };
154
124
 
155
- for relative_path in EXPECTED_MACHINE_OWNED_PATHS {
125
+ for relative_path in MACHINE_OWNED_PATHS {
156
126
  let packaged_expected = options.expected_integrity.get(*relative_path);
157
127
  let manifest_expected = integrity.get(*relative_path).and_then(Value::as_str);
158
128
 
@@ -190,7 +160,7 @@ fn validate_manifest_integrity(
190
160
  }
191
161
 
192
162
  let content = fs::read(root.join(relative_path))?;
193
- let actual = format!("sha256:{}", machine_sha256(relative_path, &content));
163
+ let actual = machine_integrity_hash(relative_path, &content);
194
164
  if actual != *packaged_expected {
195
165
  errors.push(format!(
196
166
  "{relative_path} integrity mismatch. Expected {packaged_expected}, got {actual}."
@@ -475,85 +445,3 @@ fn is_version(value: &str) -> bool {
475
445
  .iter()
476
446
  .all(|part| !part.is_empty() && part.chars().all(|ch| ch.is_ascii_digit()))
477
447
  }
478
-
479
- fn is_integrity_hash(value: &str) -> bool {
480
- value.len() == "sha256:".len() + 64
481
- && value.starts_with("sha256:")
482
- && value["sha256:".len()..]
483
- .chars()
484
- .all(|ch| ch.is_ascii_hexdigit() && !ch.is_ascii_uppercase())
485
- }
486
-
487
- fn machine_sha256(relative_path: &str, content: &[u8]) -> String {
488
- let normalized = normalize_machine_owned_content(relative_path, content);
489
- sha256_bytes(&normalized)
490
- }
491
-
492
- fn sha256_bytes(content: &[u8]) -> String {
493
- let mut hasher = Sha256::new();
494
- hasher.update(content);
495
- format!("{:x}", hasher.finalize())
496
- }
497
-
498
- fn normalize_machine_owned_content(relative_path: &str, content: &[u8]) -> Vec<u8> {
499
- if relative_path == HEALTH_CHECKER_RELATIVE_PATH
500
- || relative_path == TASK_STATE_CHECKER_RELATIVE_PATH
501
- {
502
- return replace_expected_integrity_block(content);
503
- }
504
-
505
- if relative_path == NAOME_COMMAND_RELATIVE_PATH {
506
- return replace_native_integrity_line(content);
507
- }
508
-
509
- content.to_vec()
510
- }
511
-
512
- fn replace_expected_integrity_block(content: &[u8]) -> Vec<u8> {
513
- let text = String::from_utf8_lossy(content);
514
- let start_marker = "const expectedMachineOwnedIntegrity = Object.freeze({\n";
515
- let normalized =
516
- "const expectedMachineOwnedIntegrity = Object.freeze({\n __generated__: \"sha256:generated\"\n});\n";
517
- let Some(start) = text.find(start_marker) else {
518
- return content.to_vec();
519
- };
520
- let after_start = start + start_marker.len();
521
- let Some(relative_end) = text[after_start..].find("\n});\n") else {
522
- return content.to_vec();
523
- };
524
- let end = after_start + relative_end + "\n});\n".len();
525
-
526
- let mut next = String::with_capacity(text.len());
527
- next.push_str(&text[..start]);
528
- next.push_str(normalized);
529
- next.push_str(&text[end..]);
530
- next.into_bytes()
531
- }
532
-
533
- fn replace_native_integrity_line(content: &[u8]) -> Vec<u8> {
534
- let text = String::from_utf8_lossy(content);
535
- let prefix = "const expectedNativeBinaryIntegrity = \"";
536
- let Some(start) = text.find(prefix) else {
537
- return content.to_vec();
538
- };
539
- let Some(relative_end) = text[start..].find(";\n") else {
540
- return content.to_vec();
541
- };
542
- let end = start + relative_end + ";\n".len();
543
- let replacement = "const expectedNativeBinaryIntegrity = \"sha256:generated\";\n";
544
-
545
- let mut next = String::with_capacity(text.len());
546
- next.push_str(&text[..start]);
547
- next.push_str(replacement);
548
- next.push_str(&text[end..]);
549
- next.into_bytes()
550
- }
551
-
552
- fn native_integrity_from_naome_command(content: &str) -> Option<String> {
553
- let prefix = "const expectedNativeBinaryIntegrity = \"";
554
- let start = content.find(prefix)? + prefix.len();
555
- let rest = &content[start..];
556
- let end = rest.find("\";")?;
557
- let value = &rest[..end];
558
- is_integrity_hash(value).then(|| value.to_string())
559
- }
@@ -21,9 +21,12 @@ pub const PROJECT_OWNED_PATHS: &[&str] = &[
21
21
  ".naome/task-state.json",
22
22
  ".naome/upgrade-state.json",
23
23
  ".naome/verification.json",
24
+ ".naome/repository-quality.json",
25
+ ".naome/repository-quality-baseline.json",
24
26
  "docs/naome/architecture.md",
25
27
  "docs/naome/decisions.md",
26
28
  "docs/naome/repo-profile.md",
29
+ "docs/naome/repository-quality.md",
27
30
  "docs/naome/security.md",
28
31
  "docs/naome/testing.md",
29
32
  ];