@lamentis/naome 1.3.2 → 1.3.3
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 +2 -2
- package/crates/naome-cli/Cargo.toml +1 -1
- package/crates/naome-core/Cargo.toml +1 -1
- package/crates/naome-core/src/lib.rs +1 -0
- package/crates/naome-core/src/route/quality_gate_config.rs +18 -0
- package/crates/naome-core/src/task_state/diff.rs +2 -2
- package/crates/naome-core/src/task_state/git_io.rs +5 -2
- package/crates/naome-core/src/task_state/types.rs +4 -0
- package/crates/naome-core/src/verification_contract.rs +3 -1
- package/crates/naome-core/src/verification_contract_policy.rs +52 -0
- package/crates/naome-core/tests/repo_support/mod.rs +2 -1
- package/crates/naome-core/tests/repo_support/verification.rs +28 -42
- package/crates/naome-core/tests/repo_support/verification_values.rs +13 -0
- package/crates/naome-core/tests/route_user_diff.rs +52 -4
- package/crates/naome-core/tests/task_state.rs +6 -12
- package/crates/naome-core/tests/verification_contract.rs +37 -1
- package/native/darwin-arm64/naome +0 -0
- package/native/linux-x64/naome +0 -0
- package/package.json +1 -1
- package/templates/naome-root/.naome/bin/check-harness-health.js +1 -1
- package/templates/naome-root/.naome/bin/check-task-state.js +1 -1
- package/templates/naome-root/.naome/manifest.json +1 -1
- package/templates/naome-root/docs/naome/first-run.md +1 -1
- package/templates/naome-root/docs/naome/testing.md +1 -1
package/Cargo.lock
CHANGED
|
@@ -76,7 +76,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|
|
76
76
|
|
|
77
77
|
[[package]]
|
|
78
78
|
name = "naome-cli"
|
|
79
|
-
version = "1.3.
|
|
79
|
+
version = "1.3.3"
|
|
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.3.
|
|
87
|
+
version = "1.3.3"
|
|
88
88
|
dependencies = [
|
|
89
89
|
"serde",
|
|
90
90
|
"serde_json",
|
|
@@ -98,6 +98,7 @@ pub(super) fn user_diff_check_ids(
|
|
|
98
98
|
"No quality checks are configured for these changed paths.",
|
|
99
99
|
));
|
|
100
100
|
}
|
|
101
|
+
enforce_repository_semantic_companion(&mut ids, checks)?;
|
|
101
102
|
|
|
102
103
|
Ok(ids)
|
|
103
104
|
}
|
|
@@ -124,3 +125,20 @@ fn push_unique_string(values: &mut Vec<String>, value: &str) {
|
|
|
124
125
|
values.push(value.to_string());
|
|
125
126
|
}
|
|
126
127
|
}
|
|
128
|
+
|
|
129
|
+
fn enforce_repository_semantic_companion(
|
|
130
|
+
ids: &mut Vec<String>,
|
|
131
|
+
checks: &HashMap<&str, QualityCheck>,
|
|
132
|
+
) -> Result<(), NaomeError> {
|
|
133
|
+
if !ids.iter().any(|item| item == "repository-quality-check") {
|
|
134
|
+
return Ok(());
|
|
135
|
+
}
|
|
136
|
+
if checks.contains_key("repository-semantic-check") {
|
|
137
|
+
push_unique_string(ids, "repository-semantic-check");
|
|
138
|
+
return Ok(());
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
Err(NaomeError::new(
|
|
142
|
+
"repository-quality-check requires repository-semantic-check so semantic findings are mandatory quality-gate failures.",
|
|
143
|
+
))
|
|
144
|
+
}
|
|
@@ -6,7 +6,7 @@ use serde_json::Value;
|
|
|
6
6
|
use crate::models::NaomeError;
|
|
7
7
|
|
|
8
8
|
use super::git_io::read_git_changed_entries;
|
|
9
|
-
use super::types::{is_control_state_path, ChangedEntry, TaskDiff};
|
|
9
|
+
use super::types::{is_control_state_path, is_local_runtime_path, ChangedEntry, TaskDiff};
|
|
10
10
|
use super::util::{matches_any_pattern, normalize_path, string_array};
|
|
11
11
|
pub(super) fn validate_changed_paths(
|
|
12
12
|
active_task: &Value,
|
|
@@ -80,7 +80,7 @@ pub(super) fn task_diff_from_entries(active_task: &Value, entries: &[ChangedEntr
|
|
|
80
80
|
let diff_paths: Vec<String> = entries
|
|
81
81
|
.iter()
|
|
82
82
|
.map(|entry| entry.path.clone())
|
|
83
|
-
.filter(|path| !is_control_state_path(path))
|
|
83
|
+
.filter(|path| !is_control_state_path(path) && !is_local_runtime_path(path))
|
|
84
84
|
.collect();
|
|
85
85
|
let outside_paths = diff_paths
|
|
86
86
|
.iter()
|
|
@@ -6,7 +6,7 @@ use crate::models::NaomeError;
|
|
|
6
6
|
|
|
7
7
|
pub(super) use super::git_parse::{parse_name_status_output, split_nul, upsert_changed_entry};
|
|
8
8
|
pub(super) use super::git_refs::{command_output, git_commit_exists, read_git_head, run_git};
|
|
9
|
-
use super::types::ChangedEntry;
|
|
9
|
+
use super::types::{is_local_runtime_path, ChangedEntry};
|
|
10
10
|
use super::util::normalize_path;
|
|
11
11
|
pub(super) fn read_git_changed_paths(root: &Path) -> Result<Vec<String>, NaomeError> {
|
|
12
12
|
Ok(read_git_changed_entries(root)?
|
|
@@ -55,6 +55,9 @@ pub(super) fn read_git_changed_entries(root: &Path) -> Result<Vec<ChangedEntry>,
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
for entry in parse_name_status_output(&output.stdout) {
|
|
58
|
+
if is_local_runtime_path(&entry.path) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
58
61
|
upsert_changed_entry(&mut entries, entry);
|
|
59
62
|
}
|
|
60
63
|
}
|
|
@@ -69,7 +72,7 @@ pub(super) fn read_git_changed_entries(root: &Path) -> Result<Vec<ChangedEntry>,
|
|
|
69
72
|
|
|
70
73
|
for token in split_nul(&untracked.stdout) {
|
|
71
74
|
let path = normalize_path(token.trim());
|
|
72
|
-
if !path.is_empty() {
|
|
75
|
+
if !path.is_empty() && !is_local_runtime_path(&path) {
|
|
73
76
|
upsert_changed_entry(
|
|
74
77
|
&mut entries,
|
|
75
78
|
ChangedEntry {
|
|
@@ -97,6 +97,10 @@ pub(super) fn is_control_state_path(path: &str) -> bool {
|
|
|
97
97
|
path == CONTROL_STATE_PATH || path.starts_with(TASK_LEDGER_PATH_PREFIX)
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
pub(super) fn is_local_runtime_path(path: &str) -> bool {
|
|
101
|
+
path == ".naome/tmp" || path.starts_with(".naome/tmp/")
|
|
102
|
+
}
|
|
103
|
+
|
|
100
104
|
pub(super) fn read_complete_active_task(root: &Path) -> Result<Option<(Value, Value)>, NaomeError> {
|
|
101
105
|
let Some(task_state) = read_task_state_projection(root)? else {
|
|
102
106
|
return Ok(None);
|
|
@@ -5,6 +5,7 @@ use std::path::Path;
|
|
|
5
5
|
use serde_json::Value;
|
|
6
6
|
|
|
7
7
|
use crate::models::NaomeError;
|
|
8
|
+
use crate::verification_contract_policy::validate_semantic_quality_gate_policy;
|
|
8
9
|
|
|
9
10
|
const REQUIRED_TESTING_HEADINGS: &[&str] = &[
|
|
10
11
|
"# Testing And Verification",
|
|
@@ -29,7 +30,7 @@ const ALLOWED_TOP_LEVEL_KEYS: &[&str] = &[
|
|
|
29
30
|
const MAX_CHECKS: usize = 20;
|
|
30
31
|
const MAX_PHASES: usize = 8;
|
|
31
32
|
const MAX_CHANGE_TYPES: usize = 12;
|
|
32
|
-
const MAX_RELEASE_GATES: usize =
|
|
33
|
+
const MAX_RELEASE_GATES: usize = 12;
|
|
33
34
|
|
|
34
35
|
pub fn validate_verification_contract(root: &Path) -> Result<Vec<String>, NaomeError> {
|
|
35
36
|
let mut errors = Vec::new();
|
|
@@ -135,6 +136,7 @@ fn validate_contract_shape(contract: &Value, errors: &mut Vec<String>) {
|
|
|
135
136
|
}
|
|
136
137
|
validate_change_types(change_types, &check_ids, errors);
|
|
137
138
|
validate_release_gates(release_gates, &check_ids, errors);
|
|
139
|
+
validate_semantic_quality_gate_policy(change_types, release_gates, &check_ids, errors);
|
|
138
140
|
|
|
139
141
|
if status == Some("ready") && contains_example_placeholders(contract) {
|
|
140
142
|
errors.push(
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
use std::collections::HashSet;
|
|
2
|
+
|
|
3
|
+
use serde_json::Value;
|
|
4
|
+
|
|
5
|
+
pub(crate) fn validate_semantic_quality_gate_policy(
|
|
6
|
+
change_types: &[Value],
|
|
7
|
+
release_gates: &[Value],
|
|
8
|
+
check_ids: &HashSet<String>,
|
|
9
|
+
errors: &mut Vec<String>,
|
|
10
|
+
) {
|
|
11
|
+
if check_ids.contains("repository-quality-check")
|
|
12
|
+
&& !check_ids.contains("repository-semantic-check")
|
|
13
|
+
{
|
|
14
|
+
errors.push(
|
|
15
|
+
"checks must include repository-semantic-check whenever repository-quality-check is configured.".to_string(),
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for (index, change_type) in change_types.iter().enumerate() {
|
|
20
|
+
let Some(required_checks) = change_type
|
|
21
|
+
.get("requiredChecks")
|
|
22
|
+
.and_then(Value::as_array)
|
|
23
|
+
else {
|
|
24
|
+
continue;
|
|
25
|
+
};
|
|
26
|
+
let has_quality = contains_string(required_checks, "repository-quality-check");
|
|
27
|
+
let has_semantic = contains_string(required_checks, "repository-semantic-check");
|
|
28
|
+
if has_quality && !has_semantic {
|
|
29
|
+
errors.push(format!(
|
|
30
|
+
"changeTypes[{index}].requiredChecks must include repository-semantic-check whenever repository-quality-check is required."
|
|
31
|
+
));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let has_quality_release_gate = release_gates.iter().any(|gate| {
|
|
36
|
+
gate.get("checkId").and_then(Value::as_str) == Some("repository-quality-check")
|
|
37
|
+
});
|
|
38
|
+
let has_semantic_release_gate = release_gates.iter().any(|gate| {
|
|
39
|
+
gate.get("checkId").and_then(Value::as_str) == Some("repository-semantic-check")
|
|
40
|
+
});
|
|
41
|
+
if has_quality_release_gate && !has_semantic_release_gate {
|
|
42
|
+
errors.push(
|
|
43
|
+
"releaseGates must include repository-semantic-check whenever repository-quality-check is a release gate.".to_string(),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fn contains_string(values: &[Value], expected: &str) -> bool {
|
|
49
|
+
values
|
|
50
|
+
.iter()
|
|
51
|
+
.any(|value| value.as_str() == Some(expected))
|
|
52
|
+
}
|
|
@@ -17,6 +17,7 @@ pub use verification_values::{
|
|
|
17
17
|
change_type, check_missing_last_verified_fixture, diff_check,
|
|
18
18
|
placeholder_verification_contract_fixture, quality_check, repo_docs_verification_fixture,
|
|
19
19
|
repository_quality_config_source,
|
|
20
|
-
repository_quality_config_value,
|
|
20
|
+
repository_quality_config_value, repository_semantic_check,
|
|
21
|
+
semantic_repository_quality_fixture_source,
|
|
21
22
|
verification_value,
|
|
22
23
|
};
|
|
@@ -2,7 +2,8 @@ use serde_json::{json, Value};
|
|
|
2
2
|
|
|
3
3
|
use super::repo::TestRepo;
|
|
4
4
|
use super::verification_values::{
|
|
5
|
-
change_type, diff_check, mutating_diff_check, repository_quality_check,
|
|
5
|
+
change_type, diff_check, mutating_diff_check, repository_quality_check,
|
|
6
|
+
repository_semantic_check, verification_value,
|
|
6
7
|
};
|
|
7
8
|
|
|
8
9
|
impl TestRepo {
|
|
@@ -33,54 +34,39 @@ impl TestRepo {
|
|
|
33
34
|
) {
|
|
34
35
|
let mut checks = vec![diff_check(vec!["README.md"])];
|
|
35
36
|
checks.extend(extra_checks);
|
|
36
|
-
|
|
37
37
|
self.write_naome_json(
|
|
38
38
|
"verification.json",
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"requiredChecks": required_checks,
|
|
50
|
-
"recommendedChecks": [],
|
|
51
|
-
"humanReview": false
|
|
52
|
-
}
|
|
53
|
-
],
|
|
54
|
-
"releaseGates": []
|
|
55
|
-
}),
|
|
39
|
+
verification_value(
|
|
40
|
+
"ready",
|
|
41
|
+
checks,
|
|
42
|
+
vec![change_type(
|
|
43
|
+
"readme",
|
|
44
|
+
"README changes.",
|
|
45
|
+
vec!["README.md"],
|
|
46
|
+
required_checks,
|
|
47
|
+
)],
|
|
48
|
+
),
|
|
56
49
|
);
|
|
57
50
|
}
|
|
58
51
|
|
|
59
52
|
pub fn write_product_quality_verification(&self) {
|
|
60
53
|
self.write_naome_json(
|
|
61
54
|
"verification.json",
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
],
|
|
78
|
-
"recommendedChecks": [],
|
|
79
|
-
"humanReview": false
|
|
80
|
-
}
|
|
81
|
-
],
|
|
82
|
-
"releaseGates": []
|
|
83
|
-
}),
|
|
55
|
+
verification_value(
|
|
56
|
+
"ready",
|
|
57
|
+
product_quality_checks(),
|
|
58
|
+
vec![change_type(
|
|
59
|
+
"product-installer-or-template",
|
|
60
|
+
"NAOME product changes.",
|
|
61
|
+
vec!["packages/naome/**", "scripts/**"],
|
|
62
|
+
vec![
|
|
63
|
+
"installer-tests",
|
|
64
|
+
"rust-build",
|
|
65
|
+
"decision-engine-tests",
|
|
66
|
+
"package-dry-run",
|
|
67
|
+
],
|
|
68
|
+
)],
|
|
69
|
+
),
|
|
84
70
|
);
|
|
85
71
|
}
|
|
86
72
|
|
|
@@ -89,7 +75,7 @@ impl TestRepo {
|
|
|
89
75
|
"verification.json",
|
|
90
76
|
verification_value(
|
|
91
77
|
"ready",
|
|
92
|
-
vec![repository_quality_check()],
|
|
78
|
+
vec![repository_quality_check(), repository_semantic_check()],
|
|
93
79
|
vec![change_type(
|
|
94
80
|
"source",
|
|
95
81
|
"Source changes.",
|
|
@@ -242,6 +242,19 @@ pub fn repository_quality_check() -> serde_json::Value {
|
|
|
242
242
|
})
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
pub fn repository_semantic_check() -> serde_json::Value {
|
|
246
|
+
json!({
|
|
247
|
+
"id": "repository-semantic-check",
|
|
248
|
+
"command": "naome semantic check --changed",
|
|
249
|
+
"cwd": ".",
|
|
250
|
+
"purpose": "Validate changed files against repository semantic rules.",
|
|
251
|
+
"cost": "fast",
|
|
252
|
+
"source": "NAOME built-in",
|
|
253
|
+
"evidence": ["src/**"],
|
|
254
|
+
"lastVerified": null
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
245
258
|
pub fn semantic_repository_quality_fixture_source(name: &str) -> String {
|
|
246
259
|
[
|
|
247
260
|
format!("const {name} = {{"),
|
|
@@ -73,14 +73,14 @@ fn execute_route_runs_repository_quality_check_without_shelling_to_repo_command(
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
#[test]
|
|
76
|
-
fn
|
|
77
|
-
let repo = TestRepo::new("route-
|
|
76
|
+
fn execute_route_requires_semantic_gate_with_repository_quality_gate() {
|
|
77
|
+
let repo = TestRepo::new("route-repository-quality-requires-semantic");
|
|
78
78
|
repo.init_git();
|
|
79
79
|
repo.write_file(
|
|
80
80
|
".naome/repository-quality.json",
|
|
81
81
|
&repository_quality_config_source(),
|
|
82
82
|
);
|
|
83
|
-
repo.write_file("
|
|
83
|
+
repo.write_file("src/clean.js", "export const clean = true;\n");
|
|
84
84
|
repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
|
|
85
85
|
repo.write_naome_json(
|
|
86
86
|
"verification.json",
|
|
@@ -91,8 +91,56 @@ fn execute_route_refuses_semantic_changed_findings_through_repository_quality_ga
|
|
|
91
91
|
"naome quality check --changed",
|
|
92
92
|
"Validate changed files against repository quality rules.",
|
|
93
93
|
"fast",
|
|
94
|
-
vec!["
|
|
94
|
+
vec!["src/**"],
|
|
95
95
|
)],
|
|
96
|
+
vec![change_type(
|
|
97
|
+
"source",
|
|
98
|
+
"Source changes.",
|
|
99
|
+
vec!["src/**"],
|
|
100
|
+
vec!["repository-quality-check"],
|
|
101
|
+
)],
|
|
102
|
+
),
|
|
103
|
+
);
|
|
104
|
+
repo.commit_all("baseline");
|
|
105
|
+
repo.write_file("src/clean.js", "export const clean = false;\n");
|
|
106
|
+
let before_head = repo.git_stdout(&["rev-parse", "HEAD"]);
|
|
107
|
+
|
|
108
|
+
let route = route_commit_request(&repo);
|
|
109
|
+
|
|
110
|
+
assert_quality_gate_blocked(&repo, &route, before_head, "src/clean.js");
|
|
111
|
+
assert!(route.user_message.contains("repository-semantic-check"));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#[test]
|
|
115
|
+
fn execute_route_refuses_semantic_changed_findings_through_repository_quality_gate() {
|
|
116
|
+
let repo = TestRepo::new("route-semantic-quality-gate");
|
|
117
|
+
repo.init_git();
|
|
118
|
+
repo.write_file(
|
|
119
|
+
".naome/repository-quality.json",
|
|
120
|
+
&repository_quality_config_source(),
|
|
121
|
+
);
|
|
122
|
+
repo.write_file("scripts/baseline.test.js", "const ok = { value: 1 };\n");
|
|
123
|
+
repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
|
|
124
|
+
repo.write_naome_json(
|
|
125
|
+
"verification.json",
|
|
126
|
+
verification_value(
|
|
127
|
+
"ready",
|
|
128
|
+
vec![
|
|
129
|
+
quality_check(
|
|
130
|
+
"repository-quality-check",
|
|
131
|
+
"naome quality check --changed",
|
|
132
|
+
"Validate changed files against repository quality rules.",
|
|
133
|
+
"fast",
|
|
134
|
+
vec!["scripts/**"],
|
|
135
|
+
),
|
|
136
|
+
quality_check(
|
|
137
|
+
"repository-semantic-check",
|
|
138
|
+
"naome semantic check --changed",
|
|
139
|
+
"Validate changed files against repository semantic rules.",
|
|
140
|
+
"fast",
|
|
141
|
+
vec!["scripts/**"],
|
|
142
|
+
),
|
|
143
|
+
],
|
|
96
144
|
vec![change_type(
|
|
97
145
|
"scripts",
|
|
98
146
|
"Script changes.",
|
|
@@ -410,26 +410,20 @@ fn progress_report(repo: &TaskFixture) -> TaskStateReport {
|
|
|
410
410
|
}
|
|
411
411
|
|
|
412
412
|
fn implementing_readme_task_fixture() -> serde_json::Value {
|
|
413
|
-
|
|
414
|
-
"schema": "naome.task-state.v1",
|
|
415
|
-
"version": 1,
|
|
416
|
-
"status": "implementing",
|
|
417
|
-
"activeTask": active_task(json!({
|
|
418
|
-
"allowedPaths": ["README.md"],
|
|
419
|
-
"proofResults": []
|
|
420
|
-
})),
|
|
421
|
-
"blocker": null,
|
|
422
|
-
"updatedAt": "2026-05-04T12:00:00.000Z"
|
|
423
|
-
})
|
|
413
|
+
implementing_task_fixture(vec!["README.md"])
|
|
424
414
|
}
|
|
425
415
|
|
|
426
416
|
fn implementing_package_task() -> serde_json::Value {
|
|
417
|
+
implementing_task_fixture(vec!["Package.swift"])
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
fn implementing_task_fixture(allowed_paths: Vec<&str>) -> serde_json::Value {
|
|
427
421
|
json!({
|
|
428
422
|
"schema": "naome.task-state.v1",
|
|
429
423
|
"version": 1,
|
|
430
424
|
"status": "implementing",
|
|
431
425
|
"activeTask": active_task(json!({
|
|
432
|
-
"allowedPaths":
|
|
426
|
+
"allowedPaths": allowed_paths,
|
|
433
427
|
"proofResults": []
|
|
434
428
|
})),
|
|
435
429
|
"blocker": null,
|
|
@@ -5,7 +5,7 @@ mod repo_support;
|
|
|
5
5
|
|
|
6
6
|
use repo_support::{
|
|
7
7
|
change_type, check_missing_last_verified_fixture, diff_check,
|
|
8
|
-
placeholder_verification_contract_fixture, verification_value, TestRepo,
|
|
8
|
+
placeholder_verification_contract_fixture, quality_check, verification_value, TestRepo,
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
#[test]
|
|
@@ -52,6 +52,42 @@ fn rejects_missing_check_references_and_ready_placeholders() {
|
|
|
52
52
|
assert!(joined.contains("must not contain example placeholders"));
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
#[test]
|
|
56
|
+
fn rejects_repository_quality_without_semantic_required_check() {
|
|
57
|
+
let repo = TestRepo::new("verification-contract-semantic-required");
|
|
58
|
+
repo.write_testing_markdown(default_testing_markdown());
|
|
59
|
+
repo.write_naome_json("verification.json", {
|
|
60
|
+
let mut value = verification_value(
|
|
61
|
+
"ready",
|
|
62
|
+
vec![quality_check(
|
|
63
|
+
"repository-quality-check",
|
|
64
|
+
"node .naome/bin/naome.js quality check --changed",
|
|
65
|
+
"Validate changed files against repository quality rules.",
|
|
66
|
+
"fast",
|
|
67
|
+
vec![".naome/repository-quality.json"],
|
|
68
|
+
)],
|
|
69
|
+
vec![change_type(
|
|
70
|
+
"code",
|
|
71
|
+
"Code changes.",
|
|
72
|
+
vec!["src/**"],
|
|
73
|
+
vec!["repository-quality-check"],
|
|
74
|
+
)],
|
|
75
|
+
);
|
|
76
|
+
value["lastUpdated"] = json!("2026-05-11");
|
|
77
|
+
value["releaseGates"] = json!([{
|
|
78
|
+
"checkId": "repository-quality-check",
|
|
79
|
+
"requiredWhen": "Before release."
|
|
80
|
+
}]);
|
|
81
|
+
value
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
let errors = validate_verification_contract(repo.path()).unwrap();
|
|
85
|
+
let joined = errors.join("\n");
|
|
86
|
+
|
|
87
|
+
assert!(joined.contains("repository-semantic-check"));
|
|
88
|
+
assert!(joined.contains("changeTypes[0].requiredChecks"));
|
|
89
|
+
}
|
|
90
|
+
|
|
55
91
|
#[test]
|
|
56
92
|
fn rejects_checks_without_explicit_last_verified_value() {
|
|
57
93
|
let repo = TestRepo::new("verification-contract-last-verified");
|
|
Binary file
|
package/native/linux-x64/naome
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ const expectedMachineOwnedIntegrity = Object.freeze({
|
|
|
18
18
|
"docs/naome/agent-workflow.md": "sha256:78faeb4eed157b60f0b60bc43da36c713fc4a3436b93bd963ce5f3c12dc91f22",
|
|
19
19
|
"docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
|
|
20
20
|
"docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
|
|
21
|
-
"docs/naome/first-run.md": "sha256:
|
|
21
|
+
"docs/naome/first-run.md": "sha256:1466ce8c65e19a1514885f917db14e8a772350e3f6d1c03a66326963365919e1",
|
|
22
22
|
"docs/naome/index.md": "sha256:07ef776f49130319a5280bdb3ae38af22141708253f38eb983a4336fbae1b25a",
|
|
23
23
|
"docs/naome/task-ledger.md": "sha256:1e524085a2f88811941fd9f2f48ca826bc3e0e4816039d07e795637847714cbd",
|
|
24
24
|
"docs/naome/upgrade.md": "sha256:2c60f0441bbd98bd528d109b30a7ded4b0ad55d61ffb9f52edac9e93b7999cb1"
|
|
@@ -18,7 +18,7 @@ const expectedMachineOwnedIntegrity = Object.freeze({
|
|
|
18
18
|
"docs/naome/agent-workflow.md": "sha256:78faeb4eed157b60f0b60bc43da36c713fc4a3436b93bd963ce5f3c12dc91f22",
|
|
19
19
|
"docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
|
|
20
20
|
"docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
|
|
21
|
-
"docs/naome/first-run.md": "sha256:
|
|
21
|
+
"docs/naome/first-run.md": "sha256:1466ce8c65e19a1514885f917db14e8a772350e3f6d1c03a66326963365919e1",
|
|
22
22
|
"docs/naome/index.md": "sha256:07ef776f49130319a5280bdb3ae38af22141708253f38eb983a4336fbae1b25a",
|
|
23
23
|
"docs/naome/task-ledger.md": "sha256:1e524085a2f88811941fd9f2f48ca826bc3e0e4816039d07e795637847714cbd",
|
|
24
24
|
"docs/naome/upgrade.md": "sha256:2c60f0441bbd98bd528d109b30a7ded4b0ad55d61ffb9f52edac9e93b7999cb1"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"docs/naome/agent-workflow.md": "sha256:78faeb4eed157b60f0b60bc43da36c713fc4a3436b93bd963ce5f3c12dc91f22",
|
|
12
12
|
"docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
|
|
13
13
|
"docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
|
|
14
|
-
"docs/naome/first-run.md": "sha256:
|
|
14
|
+
"docs/naome/first-run.md": "sha256:1466ce8c65e19a1514885f917db14e8a772350e3f6d1c03a66326963365919e1",
|
|
15
15
|
"docs/naome/index.md": "sha256:07ef776f49130319a5280bdb3ae38af22141708253f38eb983a4336fbae1b25a",
|
|
16
16
|
"docs/naome/task-ledger.md": "sha256:1e524085a2f88811941fd9f2f48ca826bc3e0e4816039d07e795637847714cbd",
|
|
17
17
|
"docs/naome/upgrade.md": "sha256:2c60f0441bbd98bd528d109b30a7ded4b0ad55d61ffb9f52edac9e93b7999cb1"
|
|
@@ -102,7 +102,7 @@ Rules:
|
|
|
102
102
|
real check.
|
|
103
103
|
- This JSON is machine state, not prose context. It is exempt from the
|
|
104
104
|
200-line instruction-file budget, but must stay bounded: at most 20 checks, 12
|
|
105
|
-
change types, and
|
|
105
|
+
change types, and 12 release gates.
|
|
106
106
|
- Do not read `.naome/verification.json` as long-form context during normal
|
|
107
107
|
work. Select the relevant change type or check id, then read only the matching
|
|
108
108
|
entries needed for proof.
|
|
@@ -67,7 +67,7 @@ phase is failing or missing.
|
|
|
67
67
|
- Use dates as `YYYY-MM-DD` or `null`.
|
|
68
68
|
- Keep instruction files under 200 lines. `.naome/verification.json` is machine
|
|
69
69
|
state instead; keep it schema-valid and bounded to 20 checks, 12 change types,
|
|
70
|
-
and
|
|
70
|
+
and 12 release gates.
|
|
71
71
|
- Store long command output as a compact summary that preserves command, cwd,
|
|
72
72
|
exit code, relevant lines, affected paths, and artifacts.
|
|
73
73
|
- When intake defines change types, include `repository-quality-check` and
|