@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.
- package/Cargo.lock +2 -2
- package/Cargo.toml +1 -1
- package/LICENSE +180 -21
- package/README.md +49 -6
- package/bin/naome-node.js +44 -4
- package/bin/naome.js +54 -16
- package/crates/naome-cli/Cargo.toml +1 -1
- package/crates/naome-cli/src/check_commands.rs +135 -0
- package/crates/naome-cli/src/cli_args.rs +5 -0
- package/crates/naome-cli/src/dispatcher.rs +36 -0
- package/crates/naome-cli/src/install_bridge.rs +83 -0
- package/crates/naome-cli/src/main.rs +57 -341
- package/crates/naome-cli/src/prompt_commands.rs +68 -0
- package/crates/naome-cli/src/quality_commands.rs +141 -0
- package/crates/naome-cli/src/simple_commands.rs +53 -0
- package/crates/naome-cli/src/workflow_commands.rs +153 -0
- package/crates/naome-core/Cargo.toml +1 -1
- package/crates/naome-core/src/harness_health/integrity.rs +96 -0
- package/crates/naome-core/src/harness_health.rs +14 -126
- package/crates/naome-core/src/install_plan.rs +3 -0
- package/crates/naome-core/src/intent/classifier.rs +171 -0
- package/crates/naome-core/src/intent/envelope.rs +108 -0
- package/crates/naome-core/src/intent/legacy.rs +138 -0
- package/crates/naome-core/src/intent/legacy_response.rs +76 -0
- package/crates/naome-core/src/intent/model.rs +71 -0
- package/crates/naome-core/src/intent/patterns.rs +170 -0
- package/crates/naome-core/src/intent/resolver.rs +162 -0
- package/crates/naome-core/src/intent/resolver_active.rs +17 -0
- package/crates/naome-core/src/intent/resolver_baseline.rs +55 -0
- package/crates/naome-core/src/intent/resolver_catalog.rs +167 -0
- package/crates/naome-core/src/intent/resolver_policy.rs +72 -0
- package/crates/naome-core/src/intent/resolver_shared.rs +55 -0
- package/crates/naome-core/src/intent/risk.rs +40 -0
- package/crates/naome-core/src/intent/segment.rs +170 -0
- package/crates/naome-core/src/intent.rs +64 -879
- package/crates/naome-core/src/journal.rs +9 -20
- package/crates/naome-core/src/lib.rs +13 -0
- package/crates/naome-core/src/quality/adapters.rs +178 -0
- package/crates/naome-core/src/quality/baseline.rs +75 -0
- package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +175 -0
- package/crates/naome-core/src/quality/checks/near_duplicates.rs +130 -0
- package/crates/naome-core/src/quality/checks.rs +228 -0
- package/crates/naome-core/src/quality/cleanup.rs +72 -0
- package/crates/naome-core/src/quality/config.rs +109 -0
- package/crates/naome-core/src/quality/mod.rs +90 -0
- package/crates/naome-core/src/quality/scanner/repo_paths.rs +103 -0
- package/crates/naome-core/src/quality/scanner.rs +367 -0
- package/crates/naome-core/src/quality/types.rs +289 -0
- package/crates/naome-core/src/route.rs +292 -17
- package/crates/naome-core/src/task_state/admission.rs +63 -0
- package/crates/naome-core/src/task_state/admission_proof.rs +72 -0
- package/crates/naome-core/src/task_state/api.rs +130 -0
- package/crates/naome-core/src/task_state/commit_gate.rs +138 -0
- package/crates/naome-core/src/task_state/compact_proof.rs +160 -0
- package/crates/naome-core/src/task_state/completed_refresh.rs +89 -0
- package/crates/naome-core/src/task_state/completion.rs +72 -0
- package/crates/naome-core/src/task_state/deleted_paths.rs +47 -0
- package/crates/naome-core/src/task_state/diff.rs +95 -0
- package/crates/naome-core/src/task_state/evidence.rs +154 -0
- package/crates/naome-core/src/task_state/git_io.rs +86 -0
- package/crates/naome-core/src/task_state/git_parse.rs +86 -0
- package/crates/naome-core/src/task_state/git_refs.rs +37 -0
- package/crates/naome-core/src/task_state/human_review_state.rs +31 -0
- package/crates/naome-core/src/task_state/mod.rs +38 -0
- package/crates/naome-core/src/task_state/process_guard.rs +40 -0
- package/crates/naome-core/src/task_state/progress.rs +123 -0
- package/crates/naome-core/src/task_state/proof.rs +139 -0
- package/crates/naome-core/src/task_state/proof_entry.rs +66 -0
- package/crates/naome-core/src/task_state/proof_model.rs +70 -0
- package/crates/naome-core/src/task_state/proof_sources.rs +76 -0
- package/crates/naome-core/src/task_state/push_gate.rs +49 -0
- package/crates/naome-core/src/task_state/reconcile.rs +7 -0
- package/crates/naome-core/src/task_state/repair.rs +168 -0
- package/crates/naome-core/src/task_state/shape.rs +117 -0
- package/crates/naome-core/src/task_state/task_diff_api.rs +170 -0
- package/crates/naome-core/src/task_state/task_records.rs +131 -0
- package/crates/naome-core/src/task_state/task_references.rs +126 -0
- package/crates/naome-core/src/task_state/types.rs +87 -0
- package/crates/naome-core/src/task_state/util.rs +137 -0
- package/crates/naome-core/src/verification/render.rs +122 -0
- package/crates/naome-core/src/verification.rs +176 -58
- package/crates/naome-core/src/verification_contract.rs +49 -21
- package/crates/naome-core/src/workflow/integrity.rs +123 -0
- package/crates/naome-core/src/workflow/integrity_normalize.rs +7 -0
- package/crates/naome-core/src/workflow/integrity_support.rs +110 -0
- package/crates/naome-core/src/workflow/mod.rs +18 -0
- package/crates/naome-core/src/workflow/mutation.rs +68 -0
- package/crates/naome-core/src/workflow/output.rs +111 -0
- package/crates/naome-core/src/workflow/phase_inference.rs +73 -0
- package/crates/naome-core/src/workflow/phases.rs +169 -0
- package/crates/naome-core/src/workflow/policy.rs +156 -0
- package/crates/naome-core/src/workflow/processes.rs +91 -0
- package/crates/naome-core/src/workflow/types.rs +42 -0
- package/crates/naome-core/tests/harness_health.rs +3 -0
- package/crates/naome-core/tests/intent.rs +97 -792
- package/crates/naome-core/tests/intent_support/mod.rs +133 -0
- package/crates/naome-core/tests/intent_v2.rs +90 -0
- package/crates/naome-core/tests/quality.rs +425 -0
- package/crates/naome-core/tests/route.rs +221 -4
- package/crates/naome-core/tests/task_state.rs +3 -0
- package/crates/naome-core/tests/task_state_compact.rs +110 -0
- package/crates/naome-core/tests/task_state_compact_support/mod.rs +5 -0
- package/crates/naome-core/tests/task_state_compact_support/repo.rs +130 -0
- package/crates/naome-core/tests/task_state_compact_support/states.rs +151 -0
- package/crates/naome-core/tests/workflow_integrity.rs +85 -0
- package/crates/naome-core/tests/workflow_policy.rs +139 -0
- package/crates/naome-core/tests/workflow_support/mod.rs +194 -0
- package/native/darwin-arm64/naome +0 -0
- package/native/linux-x64/naome +0 -0
- package/package.json +2 -2
- package/templates/naome-root/.naome/bin/check-harness-health.js +66 -85
- package/templates/naome-root/.naome/bin/check-task-state.js +9 -10
- package/templates/naome-root/.naome/bin/naome.js +34 -63
- package/templates/naome-root/.naome/manifest.json +20 -18
- package/templates/naome-root/.naome/repository-quality-baseline.json +5 -0
- package/templates/naome-root/.naome/repository-quality.json +24 -0
- package/templates/naome-root/.naome/task-contract.schema.json +93 -11
- package/templates/naome-root/.naome/upgrade-state.json +1 -1
- package/templates/naome-root/.naome/verification.json +37 -0
- package/templates/naome-root/AGENTS.md +3 -0
- package/templates/naome-root/docs/naome/agent-workflow.md +25 -12
- package/templates/naome-root/docs/naome/execution.md +25 -21
- package/templates/naome-root/docs/naome/index.md +4 -3
- package/templates/naome-root/docs/naome/repository-quality.md +43 -0
- package/templates/naome-root/docs/naome/testing.md +12 -0
- package/crates/naome-core/src/task_state.rs +0 -2210
|
@@ -203,6 +203,138 @@ fn execute_route_commits_user_diff_after_quality_gate_passes() {
|
|
|
203
203
|
assert!(route.user_message.contains("quality gates passed"));
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
+
#[test]
|
|
207
|
+
fn execute_route_runs_repository_quality_check_without_shelling_to_repo_command() {
|
|
208
|
+
let repo = TestRepo::new("route-repository-quality-check");
|
|
209
|
+
repo.init_git();
|
|
210
|
+
repo.write_file(
|
|
211
|
+
".naome/repository-quality.json",
|
|
212
|
+
r#"{
|
|
213
|
+
"schema": "naome.repository-quality.v1",
|
|
214
|
+
"version": 1,
|
|
215
|
+
"status": "ready",
|
|
216
|
+
"limits": {
|
|
217
|
+
"maxFileLines": 5,
|
|
218
|
+
"maxNewFileLines": 5,
|
|
219
|
+
"maxDiffAddedLines": 200,
|
|
220
|
+
"maxFunctionLines": 40,
|
|
221
|
+
"maxTopLevelSymbols": 30,
|
|
222
|
+
"duplicateBlockLines": 4,
|
|
223
|
+
"nearDuplicateSimilarity": 0.9
|
|
224
|
+
},
|
|
225
|
+
"disabledChecks": [],
|
|
226
|
+
"ignoredPaths": [],
|
|
227
|
+
"generatedPaths": []
|
|
228
|
+
}
|
|
229
|
+
"#,
|
|
230
|
+
);
|
|
231
|
+
repo.write_file(
|
|
232
|
+
"src/large.js",
|
|
233
|
+
"export function legacy() {\n one();\n two();\n three();\n four();\n}\n",
|
|
234
|
+
);
|
|
235
|
+
repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
|
|
236
|
+
repo.write_naome_json(
|
|
237
|
+
"verification.json",
|
|
238
|
+
json!({
|
|
239
|
+
"schema": "naome.verification.v1",
|
|
240
|
+
"version": 1,
|
|
241
|
+
"status": "ready",
|
|
242
|
+
"checks": [
|
|
243
|
+
{
|
|
244
|
+
"id": "repository-quality-check",
|
|
245
|
+
"command": "naome quality check --changed",
|
|
246
|
+
"cwd": ".",
|
|
247
|
+
"purpose": "Validate changed files against repository quality rules.",
|
|
248
|
+
"cost": "fast",
|
|
249
|
+
"source": "NAOME built-in",
|
|
250
|
+
"evidence": ["src/**"],
|
|
251
|
+
"lastVerified": null
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
"changeTypes": [
|
|
255
|
+
{
|
|
256
|
+
"id": "source",
|
|
257
|
+
"description": "Source changes.",
|
|
258
|
+
"paths": ["src/**"],
|
|
259
|
+
"requiredChecks": ["repository-quality-check"],
|
|
260
|
+
"recommendedChecks": [],
|
|
261
|
+
"humanReview": false
|
|
262
|
+
}
|
|
263
|
+
],
|
|
264
|
+
"releaseGates": []
|
|
265
|
+
}),
|
|
266
|
+
);
|
|
267
|
+
repo.git(&["add", "."]);
|
|
268
|
+
repo.git(&["commit", "-m", "baseline"]);
|
|
269
|
+
repo.write_file(
|
|
270
|
+
"src/large.js",
|
|
271
|
+
"export function legacy() {\n one();\n two();\n three();\n four();\n five();\n}\n",
|
|
272
|
+
);
|
|
273
|
+
let before_head = repo.git_stdout(&["rev-parse", "HEAD"]);
|
|
274
|
+
|
|
275
|
+
let route = evaluate_route(
|
|
276
|
+
repo.path(),
|
|
277
|
+
"commit my changes",
|
|
278
|
+
RouteOptions {
|
|
279
|
+
execute: true,
|
|
280
|
+
evaluation: EvaluationOptions::offline(),
|
|
281
|
+
},
|
|
282
|
+
)
|
|
283
|
+
.unwrap();
|
|
284
|
+
|
|
285
|
+
assert_eq!(route.policy_action, "commit_user_diff_with_quality_gate");
|
|
286
|
+
assert!(!route.allowed);
|
|
287
|
+
assert!(!route.mutation_performed);
|
|
288
|
+
assert_eq!(route.executed_actions, vec!["run_user_diff_quality_gate"]);
|
|
289
|
+
assert_eq!(repo.git_stdout(&["rev-parse", "HEAD"]), before_head);
|
|
290
|
+
assert!(route.user_message.contains("repository-quality-check"));
|
|
291
|
+
assert!(route.user_message.contains("file-length"));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
#[test]
|
|
295
|
+
fn execute_route_commits_product_diff_with_declared_safe_quality_checks() {
|
|
296
|
+
let repo = TestRepo::new("route-product-diff-quality-pass");
|
|
297
|
+
repo.init_git();
|
|
298
|
+
repo.write_file(
|
|
299
|
+
"packages/naome/crates/naome-core/src/route.rs",
|
|
300
|
+
"pub fn baseline() {}\n",
|
|
301
|
+
);
|
|
302
|
+
repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
|
|
303
|
+
repo.write_product_quality_verification();
|
|
304
|
+
repo.git(&["add", "."]);
|
|
305
|
+
repo.git(&["commit", "-m", "baseline"]);
|
|
306
|
+
repo.write_file(
|
|
307
|
+
"packages/naome/crates/naome-core/src/route.rs",
|
|
308
|
+
"pub fn changed() {}\n",
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
let route = evaluate_route(
|
|
312
|
+
repo.path(),
|
|
313
|
+
"commit my changes",
|
|
314
|
+
RouteOptions {
|
|
315
|
+
execute: true,
|
|
316
|
+
evaluation: EvaluationOptions::offline(),
|
|
317
|
+
},
|
|
318
|
+
)
|
|
319
|
+
.unwrap();
|
|
320
|
+
|
|
321
|
+
assert_eq!(route.policy_action, "commit_user_diff_with_quality_gate");
|
|
322
|
+
assert!(route.allowed);
|
|
323
|
+
assert!(route.mutation_performed);
|
|
324
|
+
assert_eq!(
|
|
325
|
+
route.executed_actions,
|
|
326
|
+
vec![
|
|
327
|
+
"run_user_diff_quality_gate".to_string(),
|
|
328
|
+
"commit_user_diff".to_string()
|
|
329
|
+
]
|
|
330
|
+
);
|
|
331
|
+
assert_eq!(repo.git_status_short(), "");
|
|
332
|
+
|
|
333
|
+
let committed_paths = repo.git_stdout(&["show", "--name-only", "--format=", "HEAD"]);
|
|
334
|
+
assert!(committed_paths.contains("packages/naome/crates/naome-core/src/route.rs"));
|
|
335
|
+
assert!(route.user_message.contains("quality gates passed"));
|
|
336
|
+
}
|
|
337
|
+
|
|
206
338
|
#[test]
|
|
207
339
|
fn execute_route_preserves_staged_rename_when_committing_user_diff() {
|
|
208
340
|
let repo = TestRepo::new("route-user-diff-staged-rename");
|
|
@@ -334,11 +466,17 @@ fn execute_route_refuses_user_diff_commit_when_check_mutates_after_diff_check()
|
|
|
334
466
|
assert_eq!(route.executed_actions, vec!["run_user_diff_quality_gate"]);
|
|
335
467
|
assert_eq!(repo.git_stdout(&["rev-parse", "HEAD"]), before_head);
|
|
336
468
|
assert!(repo.git_status_short().contains("README.md"));
|
|
337
|
-
|
|
469
|
+
assert_eq!(
|
|
470
|
+
fs::read_to_string(repo.path().join("README.md")).unwrap(),
|
|
471
|
+
"# Manual edit\n"
|
|
472
|
+
);
|
|
473
|
+
assert!(route
|
|
474
|
+
.user_message
|
|
475
|
+
.contains("will not execute repository-controlled verification commands"));
|
|
338
476
|
}
|
|
339
477
|
|
|
340
478
|
#[test]
|
|
341
|
-
fn
|
|
479
|
+
fn execute_route_refuses_user_diff_commit_when_diff_check_command_is_unsafe() {
|
|
342
480
|
let repo = TestRepo::new("route-user-diff-mutating-diff-check");
|
|
343
481
|
repo.init_git();
|
|
344
482
|
repo.write_file("README.md", "# Baseline\n");
|
|
@@ -394,10 +532,10 @@ fn execute_route_refuses_user_diff_commit_when_diff_check_adds_paths() {
|
|
|
394
532
|
assert!(!route.mutation_performed);
|
|
395
533
|
assert_eq!(route.executed_actions, vec!["run_user_diff_quality_gate"]);
|
|
396
534
|
assert_eq!(repo.git_stdout(&["rev-parse", "HEAD"]), before_head);
|
|
397
|
-
assert!(repo.
|
|
535
|
+
assert!(!repo.path().join("NEW.md").exists());
|
|
398
536
|
assert!(route
|
|
399
537
|
.user_message
|
|
400
|
-
.contains("Quality
|
|
538
|
+
.contains("Quality check diff-check has an unsafe command or cwd"));
|
|
401
539
|
}
|
|
402
540
|
|
|
403
541
|
#[test]
|
|
@@ -1050,6 +1188,85 @@ impl TestRepo {
|
|
|
1050
1188
|
);
|
|
1051
1189
|
}
|
|
1052
1190
|
|
|
1191
|
+
fn write_product_quality_verification(&self) {
|
|
1192
|
+
self.write_naome_json(
|
|
1193
|
+
"verification.json",
|
|
1194
|
+
json!({
|
|
1195
|
+
"schema": "naome.verification.v1",
|
|
1196
|
+
"version": 1,
|
|
1197
|
+
"status": "ready",
|
|
1198
|
+
"checks": [
|
|
1199
|
+
{
|
|
1200
|
+
"id": "installer-tests",
|
|
1201
|
+
"command": "npm run test:naome-installer",
|
|
1202
|
+
"cwd": ".",
|
|
1203
|
+
"purpose": "Validate installer behavior.",
|
|
1204
|
+
"cost": "medium",
|
|
1205
|
+
"source": "test",
|
|
1206
|
+
"evidence": ["scripts/naome-installer.test.js"],
|
|
1207
|
+
"lastVerified": null
|
|
1208
|
+
},
|
|
1209
|
+
{
|
|
1210
|
+
"id": "rust-build",
|
|
1211
|
+
"command": "npm run build:rust",
|
|
1212
|
+
"cwd": ".",
|
|
1213
|
+
"purpose": "Build native CLI.",
|
|
1214
|
+
"cost": "medium",
|
|
1215
|
+
"source": "test",
|
|
1216
|
+
"evidence": ["packages/naome/Cargo.toml"],
|
|
1217
|
+
"lastVerified": null
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
"id": "decision-engine-tests",
|
|
1221
|
+
"command": "npm run test:decision-engine",
|
|
1222
|
+
"cwd": ".",
|
|
1223
|
+
"purpose": "Validate route decisions.",
|
|
1224
|
+
"cost": "fast",
|
|
1225
|
+
"source": "test",
|
|
1226
|
+
"evidence": ["packages/naome/crates/naome-core/tests/route.rs"],
|
|
1227
|
+
"lastVerified": null
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
"id": "package-dry-run",
|
|
1231
|
+
"command": "npm run pack:dry-run",
|
|
1232
|
+
"cwd": ".",
|
|
1233
|
+
"purpose": "Validate package metadata.",
|
|
1234
|
+
"cost": "medium",
|
|
1235
|
+
"source": "test",
|
|
1236
|
+
"evidence": ["packages/naome/package.json"],
|
|
1237
|
+
"lastVerified": null
|
|
1238
|
+
},
|
|
1239
|
+
{
|
|
1240
|
+
"id": "diff-check",
|
|
1241
|
+
"command": "git diff --check",
|
|
1242
|
+
"cwd": ".",
|
|
1243
|
+
"purpose": "Reject whitespace errors.",
|
|
1244
|
+
"cost": "fast",
|
|
1245
|
+
"source": "test",
|
|
1246
|
+
"evidence": ["packages/naome/crates/naome-core/src/route.rs"],
|
|
1247
|
+
"lastVerified": null
|
|
1248
|
+
}
|
|
1249
|
+
],
|
|
1250
|
+
"changeTypes": [
|
|
1251
|
+
{
|
|
1252
|
+
"id": "product-installer-or-template",
|
|
1253
|
+
"description": "NAOME product changes.",
|
|
1254
|
+
"paths": ["packages/naome/**", "scripts/**"],
|
|
1255
|
+
"requiredChecks": [
|
|
1256
|
+
"installer-tests",
|
|
1257
|
+
"rust-build",
|
|
1258
|
+
"decision-engine-tests",
|
|
1259
|
+
"package-dry-run"
|
|
1260
|
+
],
|
|
1261
|
+
"recommendedChecks": [],
|
|
1262
|
+
"humanReview": false
|
|
1263
|
+
}
|
|
1264
|
+
],
|
|
1265
|
+
"releaseGates": []
|
|
1266
|
+
}),
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1053
1270
|
fn write_naome_json(&self, file_name: &str, value: serde_json::Value) {
|
|
1054
1271
|
let path = self.root.join(".naome").join(file_name);
|
|
1055
1272
|
fs::write(
|
|
@@ -32,9 +32,12 @@ const PROJECT_OWNED_PATHS: &[&str] = &[
|
|
|
32
32
|
".naome/task-state.json",
|
|
33
33
|
".naome/upgrade-state.json",
|
|
34
34
|
".naome/verification.json",
|
|
35
|
+
".naome/repository-quality.json",
|
|
36
|
+
".naome/repository-quality-baseline.json",
|
|
35
37
|
"docs/naome/architecture.md",
|
|
36
38
|
"docs/naome/decisions.md",
|
|
37
39
|
"docs/naome/repo-profile.md",
|
|
40
|
+
"docs/naome/repository-quality.md",
|
|
38
41
|
"docs/naome/security.md",
|
|
39
42
|
"docs/naome/testing.md",
|
|
40
43
|
];
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
mod task_state_compact_support;
|
|
2
|
+
|
|
3
|
+
use naome_core::{evaluate_route, EvaluationOptions, RouteOptions, TaskStateMode};
|
|
4
|
+
use serde_json::{json, Value};
|
|
5
|
+
use task_state_compact_support::{
|
|
6
|
+
compact_state, large_compact_state, large_expanded_state, legacy_state, MiniRepo,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn legacy_v1_proof_results_remain_valid() {
|
|
11
|
+
let repo = MiniRepo::new();
|
|
12
|
+
let head = repo.seed();
|
|
13
|
+
repo.change_readme();
|
|
14
|
+
repo.write_task_state(legacy_state(&head));
|
|
15
|
+
|
|
16
|
+
let report = repo.validate(TaskStateMode::State);
|
|
17
|
+
|
|
18
|
+
assert_eq!(report.errors, Vec::<String>::new());
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[test]
|
|
22
|
+
fn compact_path_sets_and_batches_cover_completion_commit_and_route() {
|
|
23
|
+
let repo = MiniRepo::new();
|
|
24
|
+
let head = repo.seed();
|
|
25
|
+
repo.change_readme();
|
|
26
|
+
repo.write_task_state(compact_state(&head));
|
|
27
|
+
|
|
28
|
+
let completion = repo.validate(TaskStateMode::State);
|
|
29
|
+
assert_eq!(completion.errors, Vec::<String>::new());
|
|
30
|
+
|
|
31
|
+
let route = evaluate_route(
|
|
32
|
+
repo.path(),
|
|
33
|
+
"new task",
|
|
34
|
+
RouteOptions {
|
|
35
|
+
execute: false,
|
|
36
|
+
evaluation: EvaluationOptions::offline(),
|
|
37
|
+
},
|
|
38
|
+
)
|
|
39
|
+
.unwrap();
|
|
40
|
+
assert_eq!(
|
|
41
|
+
route.policy_action,
|
|
42
|
+
"auto_commit_completed_task_then_create_new_task"
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
repo.git(["add", "README.md", ".naome/task-state.json"]);
|
|
46
|
+
let commit_gate = repo.validate(TaskStateMode::CommitGate);
|
|
47
|
+
assert_eq!(commit_gate.errors, Vec::<String>::new());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#[test]
|
|
51
|
+
fn compact_missing_required_proof_is_reported() {
|
|
52
|
+
let repo = MiniRepo::new();
|
|
53
|
+
let head = repo.seed();
|
|
54
|
+
repo.change_readme();
|
|
55
|
+
let mut state = compact_state(&head);
|
|
56
|
+
state["activeTask"]["proofBatches"][0]["proofs"] = json!([
|
|
57
|
+
{
|
|
58
|
+
"checkId": "alpha",
|
|
59
|
+
"exitCode": 0
|
|
60
|
+
}
|
|
61
|
+
]);
|
|
62
|
+
repo.write_task_state(state);
|
|
63
|
+
|
|
64
|
+
let report = repo.validate(TaskStateMode::State);
|
|
65
|
+
|
|
66
|
+
assert!(
|
|
67
|
+
report
|
|
68
|
+
.errors
|
|
69
|
+
.iter()
|
|
70
|
+
.any(|error| error.contains("missing proof result: beta")),
|
|
71
|
+
"{:?}",
|
|
72
|
+
report.errors
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#[test]
|
|
77
|
+
fn compact_missing_evidence_is_reported() {
|
|
78
|
+
let repo = MiniRepo::new();
|
|
79
|
+
let head = repo.seed();
|
|
80
|
+
repo.change_readme();
|
|
81
|
+
let mut state = compact_state(&head);
|
|
82
|
+
state["activeTask"]["proofBatches"][0]["evidencePathSet"] = Value::Null;
|
|
83
|
+
state["activeTask"]["proofBatches"][0]["proofs"][0]["evidencePathSet"] = Value::Null;
|
|
84
|
+
repo.write_task_state(state);
|
|
85
|
+
|
|
86
|
+
let report = repo.validate(TaskStateMode::State);
|
|
87
|
+
|
|
88
|
+
assert!(
|
|
89
|
+
report
|
|
90
|
+
.errors
|
|
91
|
+
.iter()
|
|
92
|
+
.any(|error| error.contains("evidence must be an evidence array or path-set reference")),
|
|
93
|
+
"{:?}",
|
|
94
|
+
report.errors
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#[test]
|
|
99
|
+
fn compact_writer_payload_is_smaller_than_expanded_equivalent() {
|
|
100
|
+
let compact = large_compact_state("abcdef");
|
|
101
|
+
let expanded = large_expanded_state("abcdef");
|
|
102
|
+
|
|
103
|
+
let compact_len = serde_json::to_string(&compact).unwrap().len();
|
|
104
|
+
let expanded_len = serde_json::to_string(&expanded).unwrap().len();
|
|
105
|
+
|
|
106
|
+
assert!(
|
|
107
|
+
compact_len * 100 / expanded_len < 75,
|
|
108
|
+
"compact={compact_len} expanded={expanded_len}"
|
|
109
|
+
);
|
|
110
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
use std::fs;
|
|
2
|
+
use std::path::{Path, PathBuf};
|
|
3
|
+
use std::process::Command;
|
|
4
|
+
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
5
|
+
use std::time::{SystemTime, UNIX_EPOCH};
|
|
6
|
+
|
|
7
|
+
use naome_core::{validate_task_state, TaskStateMode, TaskStateOptions};
|
|
8
|
+
use serde_json::{json, Value};
|
|
9
|
+
|
|
10
|
+
static REPO_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
11
|
+
|
|
12
|
+
pub struct MiniRepo {
|
|
13
|
+
root: PathBuf,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl MiniRepo {
|
|
17
|
+
pub fn new() -> Self {
|
|
18
|
+
let stamp = SystemTime::now()
|
|
19
|
+
.duration_since(UNIX_EPOCH)
|
|
20
|
+
.unwrap()
|
|
21
|
+
.as_nanos();
|
|
22
|
+
let counter = REPO_COUNTER.fetch_add(1, Ordering::SeqCst);
|
|
23
|
+
let root = std::env::temp_dir().join(format!("naome-compact-proof-{stamp}-{counter}"));
|
|
24
|
+
fs::create_dir_all(root.join(".naome")).unwrap();
|
|
25
|
+
let repo = Self { root };
|
|
26
|
+
repo.git(["init"]);
|
|
27
|
+
repo.git(["config", "user.email", "compact@example.test"]);
|
|
28
|
+
repo.git(["config", "user.name", "Compact Proof"]);
|
|
29
|
+
repo
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pub fn path(&self) -> &Path {
|
|
33
|
+
&self.root
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub fn seed(&self) -> String {
|
|
37
|
+
fs::write(self.root.join(".naomeignore"), "").unwrap();
|
|
38
|
+
fs::write(self.root.join("README.md"), "before\n").unwrap();
|
|
39
|
+
fs::write(
|
|
40
|
+
self.root.join(".naome/init-state.json"),
|
|
41
|
+
json_text(json!({
|
|
42
|
+
"schema": "naome.init-state.v1",
|
|
43
|
+
"version": 1,
|
|
44
|
+
"initialized": true,
|
|
45
|
+
"intakeStatus": "complete"
|
|
46
|
+
})),
|
|
47
|
+
)
|
|
48
|
+
.unwrap();
|
|
49
|
+
fs::write(
|
|
50
|
+
self.root.join(".naome/upgrade-state.json"),
|
|
51
|
+
json_text(json!({
|
|
52
|
+
"schema": "naome.upgrade-state.v1",
|
|
53
|
+
"version": 1,
|
|
54
|
+
"status": "complete",
|
|
55
|
+
"fromVersion": "1.1.2",
|
|
56
|
+
"toVersion": "1.2.0",
|
|
57
|
+
"pending": [],
|
|
58
|
+
"completed": []
|
|
59
|
+
})),
|
|
60
|
+
)
|
|
61
|
+
.unwrap();
|
|
62
|
+
fs::write(
|
|
63
|
+
self.root.join(".naome/verification.json"),
|
|
64
|
+
json_text(verification_contract()),
|
|
65
|
+
)
|
|
66
|
+
.unwrap();
|
|
67
|
+
self.git(["add", "."]);
|
|
68
|
+
self.git(["commit", "-m", "seed"]);
|
|
69
|
+
self.git_stdout(["rev-parse", "HEAD"])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
pub fn change_readme(&self) {
|
|
73
|
+
fs::write(self.root.join("README.md"), "after\n").unwrap();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub fn write_task_state(&self, state: Value) {
|
|
77
|
+
fs::write(self.root.join(".naome/task-state.json"), json_text(state)).unwrap();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pub fn validate(&self, mode: TaskStateMode) -> naome_core::TaskStateReport {
|
|
81
|
+
validate_task_state(
|
|
82
|
+
&self.root,
|
|
83
|
+
TaskStateOptions {
|
|
84
|
+
mode,
|
|
85
|
+
harness_health: None,
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
.unwrap()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub fn git<const N: usize>(&self, args: [&str; N]) {
|
|
92
|
+
let output = Command::new("git")
|
|
93
|
+
.args(args)
|
|
94
|
+
.current_dir(&self.root)
|
|
95
|
+
.output()
|
|
96
|
+
.unwrap();
|
|
97
|
+
assert!(
|
|
98
|
+
output.status.success(),
|
|
99
|
+
"{}",
|
|
100
|
+
String::from_utf8_lossy(&output.stderr)
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn git_stdout<const N: usize>(&self, args: [&str; N]) -> String {
|
|
105
|
+
let output = Command::new("git")
|
|
106
|
+
.args(args)
|
|
107
|
+
.current_dir(&self.root)
|
|
108
|
+
.output()
|
|
109
|
+
.unwrap();
|
|
110
|
+
assert!(output.status.success());
|
|
111
|
+
String::from_utf8_lossy(&output.stdout).trim().to_string()
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
fn verification_contract() -> Value {
|
|
116
|
+
json!({
|
|
117
|
+
"schema": "naome.verification.v1",
|
|
118
|
+
"version": 1,
|
|
119
|
+
"status": "ready",
|
|
120
|
+
"checks": [
|
|
121
|
+
{ "id": "alpha", "command": "npm run alpha", "cwd": ".", "evidence": ["README.md"] },
|
|
122
|
+
{ "id": "beta", "command": "npm run beta", "cwd": ".", "evidence": ["README.md"] }
|
|
123
|
+
],
|
|
124
|
+
"changeTypes": []
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fn json_text(value: Value) -> String {
|
|
129
|
+
format!("{}\n", serde_json::to_string_pretty(&value).unwrap())
|
|
130
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
use serde_json::{json, Value};
|
|
2
|
+
|
|
3
|
+
const T0: &str = "2026-05-07T00:00:00.000Z";
|
|
4
|
+
|
|
5
|
+
pub fn compact_state(admission_head: &str) -> Value {
|
|
6
|
+
base_state(
|
|
7
|
+
admission_head,
|
|
8
|
+
json!({
|
|
9
|
+
"proofPathSets": {
|
|
10
|
+
"changed-readme": ["README.md"]
|
|
11
|
+
},
|
|
12
|
+
"proofBatches": [
|
|
13
|
+
{
|
|
14
|
+
"checkedAt": T0,
|
|
15
|
+
"evidencePathSet": "changed-readme",
|
|
16
|
+
"proofs": [
|
|
17
|
+
{
|
|
18
|
+
"checkId": "alpha",
|
|
19
|
+
"exitCode": 0
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"checkId": "beta",
|
|
23
|
+
"command": "npm run beta -- --changed README.md",
|
|
24
|
+
"cwd": "tools",
|
|
25
|
+
"exitCode": 0
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}),
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pub fn legacy_state(admission_head: &str) -> Value {
|
|
35
|
+
let mut state = base_state(
|
|
36
|
+
admission_head,
|
|
37
|
+
json!({
|
|
38
|
+
"proofResults": [
|
|
39
|
+
proof("alpha", "npm run alpha", "."),
|
|
40
|
+
proof("beta", "npm run beta", ".")
|
|
41
|
+
]
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
state["schema"] = json!("naome.task-state.v1");
|
|
45
|
+
state["version"] = json!(1);
|
|
46
|
+
state
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
pub fn large_compact_state(admission_head: &str) -> Value {
|
|
50
|
+
let evidence: Vec<Value> = (0..12)
|
|
51
|
+
.map(|index| json!(format!("src/module_{index}.rs")))
|
|
52
|
+
.collect();
|
|
53
|
+
let proofs: Vec<Value> = (0..8)
|
|
54
|
+
.map(|index| {
|
|
55
|
+
json!({
|
|
56
|
+
"checkId": format!("check-{index}"),
|
|
57
|
+
"exitCode": 0
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
.collect();
|
|
61
|
+
base_state(
|
|
62
|
+
admission_head,
|
|
63
|
+
json!({
|
|
64
|
+
"proofPathSets": {
|
|
65
|
+
"large-change": evidence
|
|
66
|
+
},
|
|
67
|
+
"proofBatches": [
|
|
68
|
+
{
|
|
69
|
+
"checkedAt": T0,
|
|
70
|
+
"command": "cargo test",
|
|
71
|
+
"cwd": ".",
|
|
72
|
+
"evidencePathSet": "large-change",
|
|
73
|
+
"proofs": proofs
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}),
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pub fn large_expanded_state(admission_head: &str) -> Value {
|
|
81
|
+
let evidence: Vec<Value> = (0..12)
|
|
82
|
+
.map(|index| json!(format!("src/module_{index}.rs")))
|
|
83
|
+
.collect();
|
|
84
|
+
let proofs: Vec<Value> = (0..8)
|
|
85
|
+
.map(|index| {
|
|
86
|
+
json!({
|
|
87
|
+
"checkId": format!("check-{index}"),
|
|
88
|
+
"command": "cargo test",
|
|
89
|
+
"cwd": ".",
|
|
90
|
+
"exitCode": 0,
|
|
91
|
+
"checkedAt": T0,
|
|
92
|
+
"evidence": evidence
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
.collect();
|
|
96
|
+
base_state(
|
|
97
|
+
admission_head,
|
|
98
|
+
json!({
|
|
99
|
+
"proofResults": proofs
|
|
100
|
+
}),
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn base_state(admission_head: &str, proof_payload: Value) -> Value {
|
|
105
|
+
let mut task = json!({
|
|
106
|
+
"id": "compact-proof",
|
|
107
|
+
"request": "Update README.",
|
|
108
|
+
"userPrompt": {
|
|
109
|
+
"receivedAt": T0,
|
|
110
|
+
"text": "Update README."
|
|
111
|
+
},
|
|
112
|
+
"admission": {
|
|
113
|
+
"command": "node .naome/bin/check-task-state.js --admission",
|
|
114
|
+
"cwd": ".",
|
|
115
|
+
"exitCode": 0,
|
|
116
|
+
"checkedAt": T0,
|
|
117
|
+
"gitHead": admission_head,
|
|
118
|
+
"changedPaths": []
|
|
119
|
+
},
|
|
120
|
+
"allowedPaths": ["README.md"],
|
|
121
|
+
"declaredChangeTypes": ["docs"],
|
|
122
|
+
"requiredCheckIds": ["alpha", "beta"],
|
|
123
|
+
"humanReview": {
|
|
124
|
+
"required": false,
|
|
125
|
+
"approved": false,
|
|
126
|
+
"reason": null
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
task.as_object_mut()
|
|
130
|
+
.unwrap()
|
|
131
|
+
.extend(proof_payload.as_object().unwrap().clone());
|
|
132
|
+
json!({
|
|
133
|
+
"schema": "naome.task-state.v2",
|
|
134
|
+
"version": 2,
|
|
135
|
+
"status": "complete",
|
|
136
|
+
"activeTask": task,
|
|
137
|
+
"blocker": null,
|
|
138
|
+
"updatedAt": T0
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fn proof(check_id: &str, command: &str, cwd: &str) -> Value {
|
|
143
|
+
json!({
|
|
144
|
+
"checkId": check_id,
|
|
145
|
+
"command": command,
|
|
146
|
+
"cwd": cwd,
|
|
147
|
+
"exitCode": 0,
|
|
148
|
+
"checkedAt": T0,
|
|
149
|
+
"evidence": ["README.md"]
|
|
150
|
+
})
|
|
151
|
+
}
|