@lamentis/naome 1.4.3 → 1.4.4

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.
package/Cargo.lock CHANGED
@@ -76,7 +76,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
76
76
 
77
77
  [[package]]
78
78
  name = "naome-cli"
79
- version = "1.4.3"
79
+ version = "1.4.4"
80
80
  dependencies = [
81
81
  "naome-core",
82
82
  "serde_json",
@@ -84,7 +84,7 @@ dependencies = [
84
84
 
85
85
  [[package]]
86
86
  name = "naome-core"
87
- version = "1.4.3"
87
+ version = "1.4.4"
88
88
  dependencies = [
89
89
  "serde",
90
90
  "serde_json",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "naome-cli"
3
- version = "1.4.3"
3
+ version = "1.4.4"
4
4
  edition.workspace = true
5
5
  license.workspace = true
6
6
  repository.workspace = true
@@ -1,3 +1,4 @@
1
+ use std::collections::{BTreeMap, BTreeSet};
1
2
  use std::path::Path;
2
3
 
3
4
  use naome_core::{
@@ -77,23 +78,80 @@ pub(super) fn agent_snapshot(
77
78
  }
78
79
 
79
80
  fn merge_commands(left: Value, right: Vec<Value>) -> Vec<Value> {
80
- let mut commands = left
81
+ let commands = left
81
82
  .as_array()
82
83
  .cloned()
83
84
  .unwrap_or_default()
84
85
  .into_iter()
85
86
  .chain(right)
86
87
  .collect::<Vec<_>>();
87
- commands.sort_by(|a, b| {
88
- a.get("checkId")
88
+ let mut merged = BTreeMap::<String, Value>::new();
89
+ for command in commands {
90
+ let Some(check_id) = command
91
+ .get("checkId")
89
92
  .and_then(Value::as_str)
90
- .unwrap_or("")
91
- .cmp(b.get("checkId").and_then(Value::as_str).unwrap_or(""))
92
- });
93
- commands.dedup_by(|a, b| {
94
- a.get("checkId").and_then(Value::as_str) == b.get("checkId").and_then(Value::as_str)
95
- });
96
- commands
93
+ .map(ToString::to_string)
94
+ else {
95
+ continue;
96
+ };
97
+ if let Some(existing) = merged.get_mut(&check_id) {
98
+ merge_command(existing, &command);
99
+ } else {
100
+ merged.insert(check_id, command);
101
+ }
102
+ }
103
+ merged.into_values().collect()
104
+ }
105
+
106
+ fn merge_command(existing: &mut Value, incoming: &Value) {
107
+ merge_csv_field(existing, incoming, "reason");
108
+ existing["selectionReason"] = existing["reason"].clone();
109
+ merge_string_array_field(existing, incoming, "impactedPaths");
110
+ let safe = existing
111
+ .get("safeToExecute")
112
+ .and_then(Value::as_bool)
113
+ .unwrap_or(false)
114
+ && incoming
115
+ .get("safeToExecute")
116
+ .and_then(Value::as_bool)
117
+ .unwrap_or(false);
118
+ existing["safeToExecute"] = json!(safe);
119
+ }
120
+
121
+ fn merge_csv_field(existing: &mut Value, incoming: &Value, field: &str) {
122
+ let mut values = BTreeSet::new();
123
+ collect_csv(existing.get(field), &mut values);
124
+ collect_csv(incoming.get(field), &mut values);
125
+ existing[field] = json!(values.into_iter().collect::<Vec<_>>().join(","));
126
+ }
127
+
128
+ fn collect_csv(value: Option<&Value>, values: &mut BTreeSet<String>) {
129
+ if let Some(raw) = value.and_then(Value::as_str) {
130
+ values.extend(
131
+ raw.split(',')
132
+ .map(str::trim)
133
+ .filter(|part| !part.is_empty())
134
+ .map(ToString::to_string),
135
+ );
136
+ }
137
+ }
138
+
139
+ fn merge_string_array_field(existing: &mut Value, incoming: &Value, field: &str) {
140
+ let mut values = BTreeSet::new();
141
+ collect_string_array(existing.get(field), &mut values);
142
+ collect_string_array(incoming.get(field), &mut values);
143
+ existing[field] = json!(values.into_iter().collect::<Vec<_>>());
144
+ }
145
+
146
+ fn collect_string_array(value: Option<&Value>, values: &mut BTreeSet<String>) {
147
+ values.extend(
148
+ value
149
+ .and_then(Value::as_array)
150
+ .into_iter()
151
+ .flatten()
152
+ .filter_map(Value::as_str)
153
+ .map(ToString::to_string),
154
+ );
97
155
  }
98
156
 
99
157
  fn snapshot_state(status: &naome_core::TaskStatusReportV1) -> &'static str {
@@ -7,7 +7,7 @@ mod task_cli_support;
7
7
 
8
8
  use task_cli_support::{
9
9
  active_task, fixture_root, git, init_git, run_json, task_state, task_state_with_active_task,
10
- write_fixture_file, write_json,
10
+ write_fixture_file, write_json, write_verification_checks,
11
11
  };
12
12
 
13
13
  #[test]
@@ -24,6 +24,48 @@ fn agent_snapshot_reports_missing_proof_and_safe_commands() {
24
24
  assert_eq!(snapshot["checks"]["safeToRun"][0]["checkId"], "diff-check");
25
25
  }
26
26
 
27
+ #[test]
28
+ fn agent_snapshot_merges_duplicate_check_reasons_from_proof_and_impact_plans() {
29
+ let root = fixture_root(task_state_with_active_task(active_task(json!({
30
+ "allowedPaths": ["src/lib.rs"],
31
+ "requiredCheckIds": ["repository-quality-check"],
32
+ "proofResults": []
33
+ }))));
34
+ write_verification_checks(
35
+ &root,
36
+ json!([{
37
+ "id": "repository-quality-check",
38
+ "command": "node .naome/bin/naome.js quality check --changed",
39
+ "cwd": ".",
40
+ "purpose": "Validate changed-file quality.",
41
+ "cost": "fast",
42
+ "source": "NAOME",
43
+ "evidence": [".naome/repository-quality.json"],
44
+ "lastVerified": null
45
+ }]),
46
+ );
47
+ init_git(&root);
48
+ write_fixture_file(&root, "src/lib.rs", "pub fn changed() {}\n");
49
+
50
+ let snapshot = run_json(&root, ["task", "agent-snapshot", "--json"]);
51
+ let commands = snapshot["checks"]["recommended"].as_array().unwrap();
52
+ let quality_commands = commands
53
+ .iter()
54
+ .filter(|command| command["checkId"] == "repository-quality-check")
55
+ .collect::<Vec<_>>();
56
+
57
+ assert_eq!(quality_commands.len(), 1);
58
+ assert_eq!(
59
+ quality_commands[0]["reason"],
60
+ "changed_source,missing-proof"
61
+ );
62
+ assert_eq!(
63
+ quality_commands[0]["selectionReason"],
64
+ "changed_source,missing-proof"
65
+ );
66
+ assert_eq!(quality_commands[0]["impactedPaths"], json!(["src/lib.rs"]));
67
+ }
68
+
27
69
  #[test]
28
70
  fn preflight_reports_path_policy_and_check_plan() {
29
71
  let root = fixture_root(task_state_with_active_task(active_task(json!({
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "naome-core"
3
- version = "1.4.3"
3
+ version = "1.4.4"
4
4
  edition.workspace = true
5
5
  license.workspace = true
6
6
  repository.workspace = true
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lamentis/naome",
3
- "version": "1.4.3",
3
+ "version": "1.4.4",
4
4
  "description": "Native-first CLI for the NAOME agent harness.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",