@lamentis/naome 1.3.17 → 1.4.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/crates/naome-cli/Cargo.toml +1 -1
- package/crates/naome-cli/src/architecture_commands.rs +16 -3
- package/crates/naome-cli/src/main.rs +1 -1
- package/crates/naome-core/Cargo.toml +1 -1
- package/crates/naome-core/src/architecture/output.rs +196 -0
- package/crates/naome-core/src/architecture/scan/cache.rs +1 -1
- package/crates/naome-core/src/architecture.rs +1 -0
- package/crates/naome-core/src/lib.rs +8 -7
- package/crates/naome-core/tests/architecture_cache.rs +1 -1
- package/crates/naome-core/tests/architecture_config.rs +68 -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 +2 -2
- package/templates/naome-root/docs/naome/architecture-fitness.md +23 -23
package/Cargo.lock
CHANGED
|
@@ -76,7 +76,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|
|
76
76
|
|
|
77
77
|
[[package]]
|
|
78
78
|
name = "naome-cli"
|
|
79
|
-
version = "1.
|
|
79
|
+
version = "1.4.0"
|
|
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.
|
|
87
|
+
version = "1.4.0"
|
|
88
88
|
dependencies = [
|
|
89
89
|
"serde",
|
|
90
90
|
"serde_json",
|
|
@@ -2,9 +2,9 @@ use std::fs;
|
|
|
2
2
|
use std::path::{Path, PathBuf};
|
|
3
3
|
|
|
4
4
|
use naome_core::{
|
|
5
|
-
config_findings_for, format_architecture_explain,
|
|
6
|
-
format_architecture_validation, scan_architecture,
|
|
7
|
-
ArchitectureScanOptions, ARCHITECTURE_RULE_IDS,
|
|
5
|
+
architecture_validation_sarif_with_root, config_findings_for, format_architecture_explain,
|
|
6
|
+
format_architecture_scan, format_architecture_validation, scan_architecture,
|
|
7
|
+
validate_architecture, ArchitectureScanOptions, ARCHITECTURE_RULE_IDS,
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
use crate::architecture_init::architecture_init_config_text;
|
|
@@ -88,6 +88,19 @@ fn run_arch_validate(root: &Path, args: &[String]) -> Result<(), Box<dyn std::er
|
|
|
88
88
|
root,
|
|
89
89
|
scan_options(root, args, has_flag(args, "--changed-only")),
|
|
90
90
|
)?;
|
|
91
|
+
if has_flag(args, "--sarif") {
|
|
92
|
+
let output =
|
|
93
|
+
serde_json::to_string_pretty(&architecture_validation_sarif_with_root(&report, root))?;
|
|
94
|
+
if let Some(path) = option_value(args, "--output") {
|
|
95
|
+
fs::write(root.join(path), output)?;
|
|
96
|
+
} else {
|
|
97
|
+
println!("{output}");
|
|
98
|
+
}
|
|
99
|
+
if report.status == "fail" {
|
|
100
|
+
std::process::exit(1);
|
|
101
|
+
}
|
|
102
|
+
return Ok(());
|
|
103
|
+
}
|
|
91
104
|
let json = has_flag(args, "--json") || has_flag(args, "--agent-feedback");
|
|
92
105
|
if json {
|
|
93
106
|
if has_flag(args, "--agent-feedback") {
|
|
@@ -62,7 +62,7 @@ const HELP: &str = r#"Usage:
|
|
|
62
62
|
naome arch init [--config <path>] [--json]
|
|
63
63
|
naome arch explain [--config <path>] [--json]
|
|
64
64
|
naome arch scan [--config <path>] [--changed-only] [--write] [--output <path>] [--json]
|
|
65
|
-
naome arch validate [--config <path>] [--changed-only] [--agent-feedback] [--json]
|
|
65
|
+
naome arch validate [--config <path>] [--changed-only] [--agent-feedback] [--json|--sarif] [--output <path>]
|
|
66
66
|
naome workflow search-profile [--json]
|
|
67
67
|
naome workflow agent-plan [--json]
|
|
68
68
|
naome workflow context-delta [--read-path <path>...] [--json]
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
use serde::{Deserialize, Serialize};
|
|
2
|
+
use serde_json::{json, Value};
|
|
3
|
+
use std::collections::BTreeSet;
|
|
4
|
+
use std::path::Path;
|
|
2
5
|
|
|
3
6
|
use super::model::{Severity, SourceRange};
|
|
4
7
|
use super::scan::ArchitectureScanReport;
|
|
5
8
|
|
|
9
|
+
const SARIF_REPO_ROOT_BASE_ID: &str = "REPO_ROOT";
|
|
10
|
+
const ARCHITECTURE_CONFIG_PATH: &str = "naome.arch.yaml";
|
|
11
|
+
|
|
6
12
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
7
13
|
#[serde(rename_all = "camelCase")]
|
|
8
14
|
pub struct ViolationSummary {
|
|
@@ -216,3 +222,193 @@ pub fn format_architecture_scan(report: &ArchitectureScanReport) -> String {
|
|
|
216
222
|
}
|
|
217
223
|
output
|
|
218
224
|
}
|
|
225
|
+
|
|
226
|
+
pub fn architecture_validation_sarif(report: &ArchitectureValidation) -> Value {
|
|
227
|
+
architecture_validation_sarif_with_root(report, Path::new("/"))
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
pub fn architecture_validation_sarif_with_root(
|
|
231
|
+
report: &ArchitectureValidation,
|
|
232
|
+
repo_root: &Path,
|
|
233
|
+
) -> Value {
|
|
234
|
+
let mut rule_ids = ARCHITECTURE_RULE_IDS
|
|
235
|
+
.iter()
|
|
236
|
+
.map(|rule| rule.to_string())
|
|
237
|
+
.collect::<BTreeSet<_>>();
|
|
238
|
+
for finding in &report.config_findings {
|
|
239
|
+
rule_ids.insert(finding.id.clone());
|
|
240
|
+
}
|
|
241
|
+
for violation in &report.violations {
|
|
242
|
+
rule_ids.insert(violation.id.clone());
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let rules = rule_ids
|
|
246
|
+
.iter()
|
|
247
|
+
.map(|id| {
|
|
248
|
+
json!({
|
|
249
|
+
"id": id,
|
|
250
|
+
"name": id,
|
|
251
|
+
"shortDescription": {
|
|
252
|
+
"text": id
|
|
253
|
+
},
|
|
254
|
+
"helpUri": "https://github.com/Lamentis-O/naome/blob/main/docs/naome/architecture-fitness.md"
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
.collect::<Vec<_>>();
|
|
258
|
+
|
|
259
|
+
let mut results = Vec::new();
|
|
260
|
+
for violation in &report.violations {
|
|
261
|
+
results.push(sarif_result_for_violation(violation));
|
|
262
|
+
}
|
|
263
|
+
for finding in &report.config_findings {
|
|
264
|
+
results.push(sarif_result_for_config_finding(finding));
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
json!({
|
|
268
|
+
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
|
269
|
+
"version": "2.1.0",
|
|
270
|
+
"runs": [
|
|
271
|
+
{
|
|
272
|
+
"tool": {
|
|
273
|
+
"driver": {
|
|
274
|
+
"name": "NAOME Architecture Fitness",
|
|
275
|
+
"informationUri": "https://github.com/Lamentis-O/naome",
|
|
276
|
+
"rules": rules
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
"originalUriBaseIds": {
|
|
280
|
+
"REPO_ROOT": {
|
|
281
|
+
"uri": file_uri_for_directory(repo_root)
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
"results": results,
|
|
285
|
+
"properties": {
|
|
286
|
+
"status": report.status,
|
|
287
|
+
"filesScanned": report.files_scanned,
|
|
288
|
+
"graphNodes": report.graph_nodes,
|
|
289
|
+
"graphEdges": report.graph_edges,
|
|
290
|
+
"changedOnlyRequested": report.changed_only_requested,
|
|
291
|
+
"changedOnlyMode": report.changed_only_mode,
|
|
292
|
+
"changedOnlyDegradedToFullScan": report.changed_only_degraded_to_full_scan,
|
|
293
|
+
"changedOnlyDegradationReason": report.changed_only_degradation_reason
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
fn sarif_result_for_violation(violation: &ArchitectureViolation) -> Value {
|
|
301
|
+
json!({
|
|
302
|
+
"ruleId": violation.id,
|
|
303
|
+
"level": sarif_level_for_severity(violation.severity),
|
|
304
|
+
"message": {
|
|
305
|
+
"text": violation.message
|
|
306
|
+
},
|
|
307
|
+
"locations": sarif_locations(
|
|
308
|
+
violation.path.as_deref().or_else(|| violation_file_endpoint(violation)),
|
|
309
|
+
violation.source_range.as_ref()
|
|
310
|
+
),
|
|
311
|
+
"properties": {
|
|
312
|
+
"type": violation.violation_type,
|
|
313
|
+
"from": violation.from,
|
|
314
|
+
"to": violation.to,
|
|
315
|
+
"suggestion": violation.suggestion,
|
|
316
|
+
"agentInstruction": violation.agent_instruction
|
|
317
|
+
}
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
fn sarif_result_for_config_finding(finding: &ArchitectureConfigFinding) -> Value {
|
|
322
|
+
let path = config_finding_path(finding);
|
|
323
|
+
json!({
|
|
324
|
+
"ruleId": finding.id,
|
|
325
|
+
"level": sarif_level_for_config_finding(&finding.severity),
|
|
326
|
+
"message": {
|
|
327
|
+
"text": finding.message
|
|
328
|
+
},
|
|
329
|
+
"locations": sarif_locations(Some(path.as_str()), None),
|
|
330
|
+
"properties": {
|
|
331
|
+
"subject": finding.subject,
|
|
332
|
+
"suggestion": finding.suggestion,
|
|
333
|
+
"agentInstruction": finding.agent_instruction
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
fn sarif_locations(path: Option<&str>, source_range: Option<&SourceRange>) -> Vec<Value> {
|
|
339
|
+
let Some(path) = path else {
|
|
340
|
+
return Vec::new();
|
|
341
|
+
};
|
|
342
|
+
let mut physical_location = json!({
|
|
343
|
+
"artifactLocation": {
|
|
344
|
+
"uri": path,
|
|
345
|
+
"uriBaseId": SARIF_REPO_ROOT_BASE_ID
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
if let Some(range) = source_range {
|
|
349
|
+
physical_location["region"] = json!({
|
|
350
|
+
"startLine": range.start_line,
|
|
351
|
+
"startColumn": range.start_column,
|
|
352
|
+
"endLine": range.end_line,
|
|
353
|
+
"endColumn": range.end_column
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
vec![json!({
|
|
357
|
+
"physicalLocation": physical_location
|
|
358
|
+
})]
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
fn violation_file_endpoint(violation: &ArchitectureViolation) -> Option<&str> {
|
|
362
|
+
[&violation.from, &violation.to]
|
|
363
|
+
.into_iter()
|
|
364
|
+
.flatten()
|
|
365
|
+
.find_map(|endpoint| endpoint.strip_prefix("file:"))
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
fn config_finding_path(finding: &ArchitectureConfigFinding) -> String {
|
|
369
|
+
finding
|
|
370
|
+
.subject
|
|
371
|
+
.strip_prefix("file:")
|
|
372
|
+
.unwrap_or(ARCHITECTURE_CONFIG_PATH)
|
|
373
|
+
.to_string()
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
fn sarif_level_for_severity(severity: Severity) -> &'static str {
|
|
377
|
+
match severity {
|
|
378
|
+
Severity::Error => "error",
|
|
379
|
+
Severity::Warning => "warning",
|
|
380
|
+
Severity::Info => "note",
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
fn sarif_level_for_config_finding(severity: &str) -> &'static str {
|
|
385
|
+
match severity {
|
|
386
|
+
"error" => "error",
|
|
387
|
+
"warning" => "warning",
|
|
388
|
+
_ => "note",
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
fn file_uri_for_directory(path: &Path) -> String {
|
|
393
|
+
let mut normalized = path.to_string_lossy().replace('\\', "/");
|
|
394
|
+
if !normalized.starts_with('/') {
|
|
395
|
+
normalized.insert(0, '/');
|
|
396
|
+
}
|
|
397
|
+
while normalized.ends_with('/') && normalized.len() > 1 {
|
|
398
|
+
normalized.pop();
|
|
399
|
+
}
|
|
400
|
+
format!("file://{}/", encode_uri_path(&normalized))
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
fn encode_uri_path(path: &str) -> String {
|
|
404
|
+
let mut encoded = String::new();
|
|
405
|
+
for byte in path.as_bytes() {
|
|
406
|
+
match *byte {
|
|
407
|
+
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'/' | b'-' | b'.' | b'_' | b'~' => {
|
|
408
|
+
encoded.push(*byte as char)
|
|
409
|
+
}
|
|
410
|
+
value => encoded.push_str(&format!("%{value:02X}")),
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
encoded
|
|
414
|
+
}
|
|
@@ -9,7 +9,7 @@ use super::FileFact;
|
|
|
9
9
|
use crate::models::NaomeError;
|
|
10
10
|
|
|
11
11
|
const CACHE_SCHEMA: &str = "naome.architecture-cache.v1";
|
|
12
|
-
const EXTRACTOR_VERSION: &str = "architecture-cache-v1.
|
|
12
|
+
const EXTRACTOR_VERSION: &str = "architecture-cache-v1.4.0";
|
|
13
13
|
const CACHE_PATH: &str = ".naome/cache/architecture/cache.json";
|
|
14
14
|
|
|
15
15
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
@@ -20,6 +20,7 @@ pub use model::{
|
|
|
20
20
|
ArchitectureNode, ArchitectureNodeKind, Severity, SourceRange,
|
|
21
21
|
};
|
|
22
22
|
pub use output::{
|
|
23
|
+
architecture_validation_sarif, architecture_validation_sarif_with_root,
|
|
23
24
|
format_architecture_scan, format_architecture_validation, ArchitectureAgentFeedback,
|
|
24
25
|
ArchitectureConfigFinding, ArchitectureValidation, ArchitectureViolation, ViolationSummary,
|
|
25
26
|
ARCHITECTURE_RULE_IDS,
|
|
@@ -21,13 +21,14 @@ mod verification_contract_policy;
|
|
|
21
21
|
mod workflow;
|
|
22
22
|
|
|
23
23
|
pub use architecture::{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
LayerConfig, RuleConfig, Severity, SourceRange,
|
|
24
|
+
architecture_validation_sarif, architecture_validation_sarif_with_root, config_findings_for,
|
|
25
|
+
default_architecture_config_text, format_architecture_explain, format_architecture_scan,
|
|
26
|
+
format_architecture_validation, scan_architecture, validate_architecture,
|
|
27
|
+
ArchitectureAgentFeedback, ArchitectureConfig, ArchitectureConfigFinding, ArchitectureEdge,
|
|
28
|
+
ArchitectureEdgeKind, ArchitectureGraph, ArchitectureMetadata, ArchitectureNode,
|
|
29
|
+
ArchitectureNodeKind, ArchitectureScanOptions, ArchitectureScanReport, ArchitectureValidation,
|
|
30
|
+
ArchitectureViolation, ContextConfig, LayerConfig, RuleConfig, Severity, SourceRange,
|
|
31
|
+
ViolationSummary, ARCHITECTURE_RULE_IDS,
|
|
31
32
|
};
|
|
32
33
|
pub use context::{
|
|
33
34
|
select_context_for_changed_paths, select_context_for_prompt, ContextBudgetLedger,
|
|
@@ -210,7 +210,7 @@ fn changed_only_degrades_when_cache_extractor_version_is_stale() {
|
|
|
210
210
|
let cache_path = repo.path().join(".naome/cache/architecture/cache.json");
|
|
211
211
|
let stale_cache = std::fs::read_to_string(&cache_path)
|
|
212
212
|
.unwrap()
|
|
213
|
-
.replace("architecture-cache-v1.
|
|
213
|
+
.replace("architecture-cache-v1.4.0", "architecture-cache-v1.3.17");
|
|
214
214
|
std::fs::write(cache_path, stale_cache).unwrap();
|
|
215
215
|
|
|
216
216
|
let report = validate_architecture(repo.path(), changed_only()).unwrap();
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
use naome_core::{
|
|
1
|
+
use naome_core::{
|
|
2
|
+
architecture_validation_sarif_with_root, format_architecture_validation, validate_architecture,
|
|
3
|
+
ArchitectureScanOptions,
|
|
4
|
+
};
|
|
2
5
|
|
|
3
6
|
mod architecture_support;
|
|
4
7
|
|
|
@@ -152,3 +155,67 @@ fn validation_reports_unresolved_repo_absolute_imports() {
|
|
|
152
155
|
.any(|item| item.contains("unresolved imports"))
|
|
153
156
|
}));
|
|
154
157
|
}
|
|
158
|
+
|
|
159
|
+
#[test]
|
|
160
|
+
fn validation_sarif_includes_violations_and_config_findings() {
|
|
161
|
+
let repo = FixtureRepo::new();
|
|
162
|
+
repo.write(
|
|
163
|
+
"naome.arch.yaml",
|
|
164
|
+
r#"
|
|
165
|
+
layers:
|
|
166
|
+
domain:
|
|
167
|
+
paths:
|
|
168
|
+
- "src/domain/**"
|
|
169
|
+
infrastructure:
|
|
170
|
+
paths:
|
|
171
|
+
- "src/infrastructure/**"
|
|
172
|
+
unused:
|
|
173
|
+
paths:
|
|
174
|
+
- "src/unused/**"
|
|
175
|
+
allowed_dependencies:
|
|
176
|
+
domain:
|
|
177
|
+
infrastructure:
|
|
178
|
+
rules:
|
|
179
|
+
no_forbidden_layer_dependencies:
|
|
180
|
+
enabled: true
|
|
181
|
+
severity: error
|
|
182
|
+
"#,
|
|
183
|
+
);
|
|
184
|
+
repo.write(
|
|
185
|
+
"src/domain/event.ts",
|
|
186
|
+
"import { db } from '../infrastructure/db';\nimport missing from './missing';\n",
|
|
187
|
+
);
|
|
188
|
+
repo.write("src/infrastructure/db.ts", "export const db = 1;\n");
|
|
189
|
+
|
|
190
|
+
let report = validate_architecture(repo.path(), ArchitectureScanOptions::default()).unwrap();
|
|
191
|
+
let sarif = architecture_validation_sarif_with_root(&report, repo.path());
|
|
192
|
+
let results = sarif["runs"][0]["results"].as_array().unwrap();
|
|
193
|
+
|
|
194
|
+
assert_eq!(sarif["version"], "2.1.0");
|
|
195
|
+
assert!(sarif["runs"][0]["originalUriBaseIds"]["REPO_ROOT"]["uri"]
|
|
196
|
+
.as_str()
|
|
197
|
+
.is_some_and(|uri| uri.starts_with("file://") && uri.ends_with('/')));
|
|
198
|
+
assert!(results.iter().any(|result| {
|
|
199
|
+
result["ruleId"] == "arch.no_forbidden_layer_dependencies"
|
|
200
|
+
&& result["level"] == "error"
|
|
201
|
+
&& result["locations"][0]["physicalLocation"]["artifactLocation"]["uri"]
|
|
202
|
+
== "src/domain/event.ts"
|
|
203
|
+
&& result["locations"][0]["physicalLocation"]["artifactLocation"]["uriBaseId"]
|
|
204
|
+
== "REPO_ROOT"
|
|
205
|
+
}));
|
|
206
|
+
assert!(results.iter().any(|result| {
|
|
207
|
+
result["ruleId"] == "arch.import.unresolved"
|
|
208
|
+
&& result["level"] == "warning"
|
|
209
|
+
&& result["properties"]["agentInstruction"]
|
|
210
|
+
.as_str()
|
|
211
|
+
.is_some_and(|instruction| instruction.contains("./missing"))
|
|
212
|
+
}));
|
|
213
|
+
assert!(results.iter().any(|result| {
|
|
214
|
+
result["ruleId"] == "arch.config.layer_matches_no_files"
|
|
215
|
+
&& result["level"] == "warning"
|
|
216
|
+
&& result["locations"][0]["physicalLocation"]["artifactLocation"]["uri"]
|
|
217
|
+
== "naome.arch.yaml"
|
|
218
|
+
&& result["locations"][0]["physicalLocation"]["artifactLocation"]["uriBaseId"]
|
|
219
|
+
== "REPO_ROOT"
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
Binary file
|
package/native/linux-x64/naome
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@ const expectedMachineOwnedIntegrity = Object.freeze({
|
|
|
17
17
|
".naome/task-contract.schema.json": "sha256:1b3b62350328d0d6d660e36d1d1baaa2b88718530db774f9ab2a9e2fcba369c8",
|
|
18
18
|
"AGENTS.md": "sha256:e8b2fc786c1c72b69ba8f2b2ffce4f459e799c7453ce9ff4a9f6448a8f9e6b4f",
|
|
19
19
|
"docs/naome/agent-workflow.md": "sha256:0be1c29adfbcd3fd73c4f904080ffc67237692fe413871a30243538c4db38ac7",
|
|
20
|
-
"docs/naome/architecture-fitness.md": "sha256:
|
|
20
|
+
"docs/naome/architecture-fitness.md": "sha256:be38e0c6dea8b1ebeee3bfd4a2e4e17008829360b1b4649ef45f2ce61e7287bd",
|
|
21
21
|
"docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
|
|
22
22
|
"docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
|
|
23
23
|
"docs/naome/first-run.md": "sha256:1466ce8c65e19a1514885f917db14e8a772350e3f6d1c03a66326963365919e1",
|
|
@@ -17,7 +17,7 @@ const expectedMachineOwnedIntegrity = Object.freeze({
|
|
|
17
17
|
".naome/task-contract.schema.json": "sha256:1b3b62350328d0d6d660e36d1d1baaa2b88718530db774f9ab2a9e2fcba369c8",
|
|
18
18
|
"AGENTS.md": "sha256:e8b2fc786c1c72b69ba8f2b2ffce4f459e799c7453ce9ff4a9f6448a8f9e6b4f",
|
|
19
19
|
"docs/naome/agent-workflow.md": "sha256:0be1c29adfbcd3fd73c4f904080ffc67237692fe413871a30243538c4db38ac7",
|
|
20
|
-
"docs/naome/architecture-fitness.md": "sha256:
|
|
20
|
+
"docs/naome/architecture-fitness.md": "sha256:be38e0c6dea8b1ebeee3bfd4a2e4e17008829360b1b4649ef45f2ce61e7287bd",
|
|
21
21
|
"docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
|
|
22
22
|
"docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
|
|
23
23
|
"docs/naome/first-run.md": "sha256:1466ce8c65e19a1514885f917db14e8a772350e3f6d1c03a66326963365919e1",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"harnessVersion": "1.
|
|
2
|
+
"harnessVersion": "1.4.0",
|
|
3
3
|
"installedAt": null,
|
|
4
4
|
"integrity": {
|
|
5
5
|
".naome/bin/check-harness-health.js": "sha256:802d7419774981a6af1826b3882270ff8f41259d516f98c52a02b4ddc184c467",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
".naome/task-contract.schema.json": "sha256:1b3b62350328d0d6d660e36d1d1baaa2b88718530db774f9ab2a9e2fcba369c8",
|
|
10
10
|
"AGENTS.md": "sha256:e8b2fc786c1c72b69ba8f2b2ffce4f459e799c7453ce9ff4a9f6448a8f9e6b4f",
|
|
11
11
|
"docs/naome/agent-workflow.md": "sha256:0be1c29adfbcd3fd73c4f904080ffc67237692fe413871a30243538c4db38ac7",
|
|
12
|
-
"docs/naome/architecture-fitness.md": "sha256:
|
|
12
|
+
"docs/naome/architecture-fitness.md": "sha256:be38e0c6dea8b1ebeee3bfd4a2e4e17008829360b1b4649ef45f2ce61e7287bd",
|
|
13
13
|
"docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
|
|
14
14
|
"docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
|
|
15
15
|
"docs/naome/first-run.md": "sha256:1466ce8c65e19a1514885f917db14e8a772350e3f6d1c03a66326963365919e1",
|
|
@@ -14,6 +14,8 @@ facts instead of prompt text.
|
|
|
14
14
|
- `naome arch validate` runs architecture rules with human output.
|
|
15
15
|
- `naome arch validate --json` emits stable machine-readable output.
|
|
16
16
|
- `naome arch validate --agent-feedback` emits compact repair instructions.
|
|
17
|
+
- `naome arch validate --sarif [--output architecture.sarif]` emits SARIF 2.1.0
|
|
18
|
+
for CI code-scanning while preserving failing exit codes.
|
|
17
19
|
- `naome arch validate --changed-only` uses changed paths with the persistent
|
|
18
20
|
architecture cache when available and safely degrades to a full scan when the
|
|
19
21
|
cache cannot prove graph-level soundness.
|
|
@@ -110,15 +112,14 @@ edges as clean architecture.
|
|
|
110
112
|
|
|
111
113
|
## Configuration Findings
|
|
112
114
|
|
|
113
|
-
Validation can pass while still reporting configuration findings
|
|
114
|
-
|
|
115
|
+
Validation can pass while still reporting non-blocking configuration findings
|
|
116
|
+
about weak architecture policy, such as:
|
|
115
117
|
|
|
116
118
|
- broad catch-all layers or contexts overlapping narrower boundaries;
|
|
117
119
|
- layers or contexts whose path patterns match no files;
|
|
118
120
|
- unresolved imports that could hide real dependency edges.
|
|
119
121
|
|
|
120
|
-
Use
|
|
121
|
-
`configFindings` deterministically in CI or agent loops.
|
|
122
|
+
Use JSON output to inspect `configFindings` deterministically in CI or agents.
|
|
122
123
|
|
|
123
124
|
## Incremental Cache
|
|
124
125
|
|
|
@@ -148,7 +149,11 @@ The command is deterministic in both cache-backed and fallback modes; CI can
|
|
|
148
149
|
inspect the JSON fields above when it needs to distinguish performance from
|
|
149
150
|
soundness fallbacks.
|
|
150
151
|
|
|
151
|
-
|
|
152
|
+
Use `node .naome/bin/naome.js arch validate --sarif --output architecture.sarif`
|
|
153
|
+
when CI can upload SARIF. It contains blocking violations, configuration
|
|
154
|
+
findings, unresolved imports, and `agentInstruction` properties for repair loops.
|
|
155
|
+
|
|
156
|
+
For v1.4.0, architecture fitness is a release gate whenever source,
|
|
152
157
|
manifest, alias, config, or structure changes can alter dependency edges. Treat
|
|
153
158
|
error-level violations as blocking. Warning-level config findings are advisory
|
|
154
159
|
unless the repository chooses stricter policy, but unresolved imports should be
|
|
@@ -156,29 +161,24 @@ triaged before claiming a clean architecture result.
|
|
|
156
161
|
|
|
157
162
|
## Language Support
|
|
158
163
|
|
|
159
|
-
The v1.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
It also extracts package and dependency facts from `package.json`,
|
|
171
|
-
`Cargo.toml`, `pyproject.toml`, `go.mod`, lightweight `pom.xml` / Gradle
|
|
172
|
-
manifests, `Package.swift`, and `.xcodeproj/project.pbxproj`, then emits
|
|
173
|
-
manifest-identity package nodes and manifest-owned `DependsOn` edges to
|
|
174
|
-
external dependencies.
|
|
164
|
+
The v1.4.0 foundation classifies TypeScript, JavaScript, Rust, Python, Go, Java,
|
|
165
|
+
Kotlin, and Swift files by path extension. It extracts imports for TypeScript,
|
|
166
|
+
JavaScript, Rust, Python, Go, and Swift, resolving relative paths,
|
|
167
|
+
repository-absolute aliases, TS/JS `paths`, Python package roots, Go modules,
|
|
168
|
+
SwiftPM targets, and Rust crate/self/super module paths where possible.
|
|
169
|
+
Unresolved imports become explicit graph nodes. Swift/iOS repositories get Apple
|
|
170
|
+
framework imports, SwiftPM manifests, and Xcode Swift package references in the
|
|
171
|
+
normalized graph; Apple SDK frameworks are treated as platform APIs. Manifest
|
|
172
|
+
facts from `package.json`, `Cargo.toml`, `pyproject.toml`, `go.mod`, lightweight
|
|
173
|
+
`pom.xml` / Gradle manifests, `Package.swift`, and `.xcodeproj/project.pbxproj`
|
|
174
|
+
emit manifest-identity package nodes and manifest-owned dependency edges.
|
|
175
175
|
Architecture rules now cover layers, bounded contexts, public APIs, cycles,
|
|
176
176
|
transitive layer reach, import/fan-out budgets, and external dependency
|
|
177
177
|
policies.
|
|
178
178
|
|
|
179
179
|
## Acceptance Coverage
|
|
180
180
|
|
|
181
|
-
v1.4 readiness is covered with deterministic fixture repositories for
|
|
181
|
+
v1.4.0 readiness is covered with deterministic fixture repositories for
|
|
182
182
|
TypeScript/JavaScript aliases, Rust crate paths, Python package roots, Go
|
|
183
183
|
modules, Swift/iOS targets, and mixed monorepos. These fixtures assert both
|
|
184
184
|
positive local-edge resolution and negative forbidden-dependency findings.
|
|
@@ -187,7 +187,7 @@ positive local-edge resolution and negative forbidden-dependency findings.
|
|
|
187
187
|
|
|
188
188
|
This release intentionally keeps validation file-graph based. Manifest
|
|
189
189
|
extractors are dependency-owner oriented and do not yet parse every build-tool
|
|
190
|
-
feature. Deep symbol-level call analysis
|
|
190
|
+
feature. Deep symbol-level call analysis remains follow-up work.
|
|
191
191
|
|
|
192
192
|
## v1.4 Migration Checklist
|
|
193
193
|
|