@lamentis/naome 1.3.8 → 1.3.10

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 (53) hide show
  1. package/Cargo.lock +2 -2
  2. package/README.md +5 -0
  3. package/bin/naome.js +1 -1
  4. package/crates/naome-cli/Cargo.toml +1 -1
  5. package/crates/naome-cli/src/architecture_commands.rs +123 -0
  6. package/crates/naome-cli/src/cli_args.rs +4 -0
  7. package/crates/naome-cli/src/dispatcher.rs +2 -0
  8. package/crates/naome-cli/src/install_bridge.rs +56 -8
  9. package/crates/naome-cli/src/main.rs +6 -0
  10. package/crates/naome-core/Cargo.toml +1 -1
  11. package/crates/naome-core/src/architecture/config/parser/scalar.rs +26 -0
  12. package/crates/naome-core/src/architecture/config/parser/sections.rs +137 -0
  13. package/crates/naome-core/src/architecture/config/parser.rs +96 -0
  14. package/crates/naome-core/src/architecture/config.rs +114 -0
  15. package/crates/naome-core/src/architecture/model.rs +80 -0
  16. package/crates/naome-core/src/architecture/output.rs +178 -0
  17. package/crates/naome-core/src/architecture/rules.rs +140 -0
  18. package/crates/naome-core/src/architecture/scan/graph_builder/emit.rs +56 -0
  19. package/crates/naome-core/src/architecture/scan/graph_builder/facts.rs +88 -0
  20. package/crates/naome-core/src/architecture/scan/graph_builder.rs +134 -0
  21. package/crates/naome-core/src/architecture/scan/path_scan.rs +92 -0
  22. package/crates/naome-core/src/architecture/scan.rs +75 -0
  23. package/crates/naome-core/src/architecture.rs +31 -0
  24. package/crates/naome-core/src/harness_health/integrity.rs +41 -23
  25. package/crates/naome-core/src/harness_health/manifest.rs +97 -0
  26. package/crates/naome-core/src/harness_health.rs +58 -106
  27. package/crates/naome-core/src/install_plan.rs +2 -0
  28. package/crates/naome-core/src/lib.rs +16 -8
  29. package/crates/naome-core/src/quality/cache.rs +122 -19
  30. package/crates/naome-core/src/quality/scanner/analysis.rs +4 -2
  31. package/crates/naome-core/src/quality/scanner/repo_paths.rs +27 -3
  32. package/crates/naome-core/src/quality/scanner.rs +5 -2
  33. package/crates/naome-core/src/workflow/integrity_support.rs +10 -3
  34. package/crates/naome-core/tests/architecture.rs +209 -0
  35. package/crates/naome-core/tests/harness_health.rs +150 -0
  36. package/crates/naome-core/tests/quality_performance.rs +63 -2
  37. package/installer/filesystem.js +38 -0
  38. package/installer/flows.js +6 -1
  39. package/installer/harness-file-ops.js +36 -8
  40. package/installer/harness-files.js +3 -0
  41. package/installer/manifest-state.js +2 -2
  42. package/installer/native.js +63 -18
  43. package/native/darwin-arm64/naome +0 -0
  44. package/native/linux-x64/naome +0 -0
  45. package/package.json +1 -1
  46. package/templates/naome-root/.naome/bin/check-harness-health.js +23 -19
  47. package/templates/naome-root/.naome/bin/check-task-state.js +33 -40
  48. package/templates/naome-root/.naome/bin/naome.js +2 -2
  49. package/templates/naome-root/.naome/manifest.json +8 -6
  50. package/templates/naome-root/.naome/verification.json +15 -1
  51. package/templates/naome-root/docs/naome/architecture-fitness.md +97 -0
  52. package/templates/naome-root/docs/naome/index.md +4 -3
  53. package/templates/naome-root/docs/naome/testing.md +6 -3
@@ -6,6 +6,7 @@ import {
6
6
  lstatSync,
7
7
  mkdirSync,
8
8
  readFileSync,
9
+ unlinkSync,
9
10
  writeFileSync,
10
11
  } from "node:fs";
11
12
  import { dirname, join, resolve } from "node:path";
@@ -32,8 +33,8 @@ export function installNativeDecisionBinary(ctx) {
32
33
  }
33
34
 
34
35
  if (usesSourceNativeFallback(ctx)) {
35
- patchNaomeCommandNativeIntegrity(ctx, "sha256:generated");
36
- ctx.skipped.push(ctx.nativeBinaryRelativePath);
36
+ removeInstalledNativeDecisionBinary(ctx);
37
+ patchInstalledNativeIntegrity(ctx, "sha256:generated");
37
38
  return;
38
39
  }
39
40
 
@@ -52,7 +53,7 @@ export function installNativeDecisionBinary(ctx) {
52
53
  }
53
54
 
54
55
  chmodSync(targetPath, 0o755);
55
- patchNaomeCommandNativeIntegrity(ctx, `sha256:${sourceHash}`);
56
+ patchInstalledNativeIntegrity(ctx, `sha256:${sourceHash}`);
56
57
  }
57
58
 
58
59
  export function findNativeDecisionBinary(ctx) {
@@ -75,7 +76,7 @@ export function findNativeDecisionBinary(ctx) {
75
76
  }
76
77
 
77
78
  export function patchInstalledMachineOwnedIntegrity(ctx) {
78
- const integrityBlock = formatExpectedIntegrityBlock(templateIntegrity(ctx));
79
+ const integrityBlock = formatExpectedIntegrityBlock(installedMachineOwnedIntegrity(ctx));
79
80
 
80
81
  for (const relativePath of [ctx.healthCheckerRelativePath, ctx.taskStateCheckerRelativePath]) {
81
82
  const targetPath = join(ctx.targetRoot, relativePath);
@@ -110,6 +111,23 @@ export function installedNativeBinaryHash(ctx) {
110
111
  return sha256(readFileSync(targetPath));
111
112
  }
112
113
 
114
+ function removeInstalledNativeDecisionBinary(ctx) {
115
+ const targetPath = join(ctx.targetRoot, ctx.nativeBinaryRelativePath);
116
+ if (!existsSync(targetPath)) {
117
+ ctx.skipped.push(ctx.nativeBinaryRelativePath);
118
+ return;
119
+ }
120
+
121
+ if (hasSymlinkInTargetPath(ctx, ctx.nativeBinaryRelativePath) || !lstatSync(targetPath).isFile()) {
122
+ ctx.skipped.push(ctx.nativeBinaryRelativePath);
123
+ ctx.unsafeSkipped.push(ctx.nativeBinaryRelativePath);
124
+ return;
125
+ }
126
+
127
+ unlinkSync(targetPath);
128
+ ctx.updated.push(ctx.nativeBinaryRelativePath);
129
+ }
130
+
113
131
  export function templateIntegrity(ctx) {
114
132
  const integrity = {};
115
133
 
@@ -121,6 +139,17 @@ export function templateIntegrity(ctx) {
121
139
  return integrity;
122
140
  }
123
141
 
142
+ export function installedMachineOwnedIntegrity(ctx) {
143
+ const integrity = templateIntegrity(ctx);
144
+ const nativeHash = installedNativeBinaryHash(ctx);
145
+
146
+ if (!usesSourceNativeFallback(ctx) && nativeHash) {
147
+ integrity[ctx.nativeBinaryRelativePath] = `sha256:${nativeHash}`;
148
+ }
149
+
150
+ return integrity;
151
+ }
152
+
124
153
  export function sha256(content) {
125
154
  return createHash("sha256").update(content).digest("hex");
126
155
  }
@@ -132,7 +161,7 @@ export function machineFileHash(ctx, relativePath, content) {
132
161
  normalized = normalized.toString("utf8").replace(ctx.integrityBlockPattern, ctx.normalizedIntegrityBlock);
133
162
  }
134
163
 
135
- if (relativePath === ctx.naomeCommandRelativePath) {
164
+ if (hasGeneratedNativeIntegrity(ctx, relativePath)) {
136
165
  normalized = normalized.toString("utf8").replace(ctx.nativeIntegrityPattern, ctx.normalizedNativeIntegrity);
137
166
  }
138
167
 
@@ -143,20 +172,36 @@ export function hasGeneratedIntegrity(ctx, relativePath) {
143
172
  return relativePath === ctx.healthCheckerRelativePath || relativePath === ctx.taskStateCheckerRelativePath;
144
173
  }
145
174
 
146
- function patchNaomeCommandNativeIntegrity(ctx, expectedIntegrity) {
147
- const commandPath = join(ctx.targetRoot, ctx.naomeCommandRelativePath);
148
- if (!existsSync(commandPath) || hasSymlinkInTargetPath(ctx, ctx.naomeCommandRelativePath)) {
149
- return;
150
- }
175
+ export function hasGeneratedNativeIntegrity(ctx, relativePath) {
176
+ return [
177
+ ctx.healthCheckerRelativePath,
178
+ ctx.taskStateCheckerRelativePath,
179
+ ctx.naomeCommandRelativePath,
180
+ ].includes(relativePath);
181
+ }
182
+
183
+ function patchInstalledNativeIntegrity(ctx, expectedIntegrity) {
184
+ const nativeIntegrityPaths = [
185
+ ctx.healthCheckerRelativePath,
186
+ ctx.taskStateCheckerRelativePath,
187
+ ctx.naomeCommandRelativePath,
188
+ ];
189
+
190
+ for (const relativePath of nativeIntegrityPaths) {
191
+ const targetPath = join(ctx.targetRoot, relativePath);
192
+ if (!existsSync(targetPath) || hasSymlinkInTargetPath(ctx, relativePath)) {
193
+ continue;
194
+ }
151
195
 
152
- const content = readFileSync(commandPath, "utf8");
153
- const nextContent = content.replace(
154
- ctx.nativeIntegrityPattern,
155
- `const expectedNativeBinaryIntegrity = "${expectedIntegrity}";\n`,
156
- );
196
+ const content = readFileSync(targetPath, "utf8");
197
+ const nextContent = content.replace(
198
+ ctx.nativeIntegrityPattern,
199
+ `const expectedNativeBinaryIntegrity = "${expectedIntegrity}";\n`,
200
+ );
157
201
 
158
- if (nextContent !== content) {
159
- writeFileSync(commandPath, nextContent);
160
- ctx.updated.push(ctx.naomeCommandRelativePath);
202
+ if (nextContent !== content) {
203
+ writeFileSync(targetPath, nextContent);
204
+ ctx.updated.push(relativePath);
205
+ }
161
206
  }
162
207
  }
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lamentis/naome",
3
- "version": "1.3.8",
3
+ "version": "1.3.10",
4
4
  "description": "Native-first CLI for the NAOME agent harness.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -7,20 +7,21 @@ const path = require("node:path");
7
7
 
8
8
  const nativeBinaryPath = process.platform === "win32" ? ".naome/bin/naome-rust.exe" : ".naome/bin/naome-rust";
9
9
  const nativeBinaryName = process.platform === "win32" ? "naome.exe" : "naome";
10
+ const expectedNativeBinaryIntegrity = "sha256:generated";
10
11
 
11
12
  const expectedMachineOwnedIntegrity = Object.freeze({
12
- ".naome/bin/check-harness-health.js": "sha256:dc4de52b79c69600b9ba47b924e2c2b8de61a2cbfab6d1ccc0f1924d963db657",
13
- ".naome/bin/check-task-state.js": "sha256:df54489a22b426180266e5e0fb5f9ec381477419f688435248afbf2b9b38ea81",
14
- ".naome/bin/naome.js": "sha256:a34c2e50a68d15ff2722a57590ccddc504e025d3c98ea62fd700d7ec1a789b9a",
13
+ ".naome/bin/check-harness-health.js": "sha256:802d7419774981a6af1826b3882270ff8f41259d516f98c52a02b4ddc184c467",
14
+ ".naome/bin/check-task-state.js": "sha256:2612577b7e4ab45d9d39dd5ac54c8e7ed749d237d78f1a8d252f4dfa0b4eaaab",
15
+ ".naome/bin/naome.js": "sha256:f129c580fb70b3a1d92d0ab0de9fa6c131b2fc51a8dcdebe9d6f5a812a3be61b",
15
16
  ".naome/package.json": "sha256:8005a3491db7d92f36ac66369861589f9c47123d3a7c71e643fc2c06168cd45a",
16
17
  ".naome/task-contract.schema.json": "sha256:1b3b62350328d0d6d660e36d1d1baaa2b88718530db774f9ab2a9e2fcba369c8",
17
18
  "AGENTS.md": "sha256:e8b2fc786c1c72b69ba8f2b2ffce4f459e799c7453ce9ff4a9f6448a8f9e6b4f",
18
- "docs/naome/agent-workflow.md": "sha256:2fd1fd02eb6849133b9e8227421914580b1c469a60388a063c1b6ed48016b48d",
19
+ "docs/naome/agent-workflow.md": "sha256:0be1c29adfbcd3fd73c4f904080ffc67237692fe413871a30243538c4db38ac7",
19
20
  "docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
20
21
  "docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
21
22
  "docs/naome/first-run.md": "sha256:1466ce8c65e19a1514885f917db14e8a772350e3f6d1c03a66326963365919e1",
22
- "docs/naome/index.md": "sha256:07ef776f49130319a5280bdb3ae38af22141708253f38eb983a4336fbae1b25a",
23
- "docs/naome/task-ledger.md": "sha256:ac637a31abdd13eee15a49086594e63f5c88fe12a5cf621b227310788ae7e583",
23
+ "docs/naome/index.md": "sha256:cac748ed375d86d288460456fc5d606b29bd99d91148522c303d5400a083dbc5",
24
+ "docs/naome/task-ledger.md": "sha256:6ca7222c80079b4662fb718d3c71d686770646f1fa52b83b0e90aed1c5a1101b",
24
25
  "docs/naome/upgrade.md": "sha256:2c60f0441bbd98bd528d109b30a7ded4b0ad55d61ffb9f52edac9e93b7999cb1"
25
26
  });
26
27
 
@@ -39,7 +40,7 @@ const result = childProcess.spawnSync(binary, ["check-harness-health", "--root",
39
40
  process.exit(result.status === null ? 1 : result.status);
40
41
 
41
42
  function selectBinary(rootDir) {
42
- const wantedHash = manifestNativeHash(rootDir);
43
+ const wantedHash = expectedNativeHash();
43
44
  const candidates = [
44
45
  process.env.NAOME_NATIVE_BIN,
45
46
  path.join(rootDir, nativeBinaryPath),
@@ -47,13 +48,13 @@ function selectBinary(rootDir) {
47
48
  path.join(rootDir, "packages", "naome", "target", "debug", nativeBinaryName)
48
49
  ].filter(Boolean);
49
50
 
50
- for (const candidate of candidates) {
51
- if (canRun(candidate, rootDir, wantedHash)) {
52
- return candidate;
51
+ if (wantedHash) {
52
+ for (const candidate of candidates) {
53
+ if (canRun(candidate, rootDir, wantedHash)) {
54
+ return candidate;
55
+ }
53
56
  }
54
- }
55
57
 
56
- if (wantedHash) {
57
58
  stop(`No runnable NAOME native harness health binary matched ${wantedHash}. Run naome sync again.`);
58
59
  }
59
60
 
@@ -62,20 +63,19 @@ function selectBinary(rootDir) {
62
63
  return built;
63
64
  }
64
65
 
65
- stop("NAOME native harness health binary is missing or incompatible. Run naome sync again.");
66
+ stop("NAOME native harness health binary has no trusted packaged integrity. Run naome sync again.");
66
67
  }
67
68
 
68
- function manifestNativeHash(rootDir) {
69
+ function expectedNativeHash() {
69
70
  if (isSha(process.env.NAOME_EXPECTED_NATIVE_INTEGRITY)) {
70
71
  return process.env.NAOME_EXPECTED_NATIVE_INTEGRITY;
71
72
  }
72
73
 
73
- try {
74
- const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, ".naome", "manifest.json"), "utf8"));
75
- return isSha(manifest.integrity?.[nativeBinaryPath]) ? manifest.integrity[nativeBinaryPath] : null;
76
- } catch {
77
- return null;
74
+ if (isSha(expectedNativeBinaryIntegrity)) {
75
+ return expectedNativeBinaryIntegrity;
78
76
  }
77
+
78
+ return null;
79
79
  }
80
80
 
81
81
  function canRun(candidate, rootDir, wantedHash) {
@@ -96,6 +96,10 @@ function canRun(candidate, rootDir, wantedHash) {
96
96
  }
97
97
 
98
98
  function buildFromSource(rootDir) {
99
+ if (expectedNativeBinaryIntegrity !== "sha256:generated") {
100
+ return null;
101
+ }
102
+
99
103
  const manifest = path.join(rootDir, "packages", "naome", "Cargo.toml");
100
104
  if (!fs.existsSync(manifest)) {
101
105
  return null;
@@ -7,20 +7,21 @@ const { spawnSync } = require("node:child_process");
7
7
 
8
8
  const nativeBinaryRelativePath = process.platform === "win32" ? ".naome/bin/naome-rust.exe" : ".naome/bin/naome-rust";
9
9
  const nativeBinaryName = process.platform === "win32" ? "naome.exe" : "naome";
10
+ const expectedNativeBinaryIntegrity = "sha256:generated";
10
11
 
11
12
  const expectedMachineOwnedIntegrity = Object.freeze({
12
- ".naome/bin/check-harness-health.js": "sha256:dc4de52b79c69600b9ba47b924e2c2b8de61a2cbfab6d1ccc0f1924d963db657",
13
- ".naome/bin/check-task-state.js": "sha256:df54489a22b426180266e5e0fb5f9ec381477419f688435248afbf2b9b38ea81",
14
- ".naome/bin/naome.js": "sha256:a34c2e50a68d15ff2722a57590ccddc504e025d3c98ea62fd700d7ec1a789b9a",
13
+ ".naome/bin/check-harness-health.js": "sha256:802d7419774981a6af1826b3882270ff8f41259d516f98c52a02b4ddc184c467",
14
+ ".naome/bin/check-task-state.js": "sha256:2612577b7e4ab45d9d39dd5ac54c8e7ed749d237d78f1a8d252f4dfa0b4eaaab",
15
+ ".naome/bin/naome.js": "sha256:f129c580fb70b3a1d92d0ab0de9fa6c131b2fc51a8dcdebe9d6f5a812a3be61b",
15
16
  ".naome/package.json": "sha256:8005a3491db7d92f36ac66369861589f9c47123d3a7c71e643fc2c06168cd45a",
16
17
  ".naome/task-contract.schema.json": "sha256:1b3b62350328d0d6d660e36d1d1baaa2b88718530db774f9ab2a9e2fcba369c8",
17
18
  "AGENTS.md": "sha256:e8b2fc786c1c72b69ba8f2b2ffce4f459e799c7453ce9ff4a9f6448a8f9e6b4f",
18
- "docs/naome/agent-workflow.md": "sha256:2fd1fd02eb6849133b9e8227421914580b1c469a60388a063c1b6ed48016b48d",
19
+ "docs/naome/agent-workflow.md": "sha256:0be1c29adfbcd3fd73c4f904080ffc67237692fe413871a30243538c4db38ac7",
19
20
  "docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
20
21
  "docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
21
22
  "docs/naome/first-run.md": "sha256:1466ce8c65e19a1514885f917db14e8a772350e3f6d1c03a66326963365919e1",
22
- "docs/naome/index.md": "sha256:07ef776f49130319a5280bdb3ae38af22141708253f38eb983a4336fbae1b25a",
23
- "docs/naome/task-ledger.md": "sha256:ac637a31abdd13eee15a49086594e63f5c88fe12a5cf621b227310788ae7e583",
23
+ "docs/naome/index.md": "sha256:cac748ed375d86d288460456fc5d606b29bd99d91148522c303d5400a083dbc5",
24
+ "docs/naome/task-ledger.md": "sha256:6ca7222c80079b4662fb718d3c71d686770646f1fa52b83b0e90aed1c5a1101b",
24
25
  "docs/naome/upgrade.md": "sha256:2c60f0441bbd98bd528d109b30a7ded4b0ad55d61ffb9f52edac9e93b7999cb1"
25
26
  });
26
27
 
@@ -62,7 +63,7 @@ function modeArgs(argv) {
62
63
  }
63
64
 
64
65
  function resolveNativeDecisionBinary(root) {
65
- const expectedIntegrity = expectedNativeIntegrity(root);
66
+ const expectedIntegrity = expectedNativeIntegrity();
66
67
  const candidates = [
67
68
  process.env.NAOME_NATIVE_BIN,
68
69
  join(root, nativeBinaryRelativePath),
@@ -70,13 +71,13 @@ function resolveNativeDecisionBinary(root) {
70
71
  join(root, "packages", "naome", "target", "debug", nativeBinaryName)
71
72
  ].filter(Boolean);
72
73
 
73
- for (const candidate of candidates) {
74
- if (isUsableNativeBinary(candidate, root, expectedIntegrity)) {
75
- return candidate;
74
+ if (expectedIntegrity) {
75
+ for (const candidate of candidates) {
76
+ if (isUsableNativeBinary(candidate, root, expectedIntegrity)) {
77
+ return candidate;
78
+ }
76
79
  }
77
- }
78
80
 
79
- if (expectedIntegrity) {
80
81
  fail(`No runnable NAOME native task-state binary matched ${expectedIntegrity}. Run naome sync again.`);
81
82
  }
82
83
 
@@ -88,29 +89,16 @@ function resolveNativeDecisionBinary(root) {
88
89
  fail("NAOME native task-state binary is missing or incompatible. Run naome sync again.");
89
90
  }
90
91
 
91
- function expectedNativeIntegrity(root) {
92
+ function expectedNativeIntegrity() {
92
93
  if (isIntegrityHash(process.env.NAOME_EXPECTED_NATIVE_INTEGRITY)) {
93
94
  return process.env.NAOME_EXPECTED_NATIVE_INTEGRITY;
94
95
  }
95
96
 
96
- const manifestPath = join(root, ".naome", "manifest.json");
97
- if (!existsSync(manifestPath)) {
98
- return null;
99
- }
100
-
101
- let manifest;
102
- try {
103
- manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
104
- } catch {
105
- return null;
106
- }
107
-
108
- const expected = manifest.integrity?.[nativeBinaryRelativePath];
109
- if (!isIntegrityHash(expected)) {
97
+ if (expectedNativeBinaryIntegrity === "sha256:generated") {
110
98
  return null;
111
99
  }
112
100
 
113
- return expected;
101
+ return expectedNativeBinaryIntegrity;
114
102
  }
115
103
 
116
104
  function isUsableNativeBinary(candidate, root, expectedIntegrity) {
@@ -125,30 +113,35 @@ function isUsableNativeBinary(candidate, root, expectedIntegrity) {
125
113
  }
126
114
  }
127
115
 
128
- const probe = spawnSync(candidate, [], {
129
- cwd: root,
130
- encoding: "utf8",
131
- stdio: "ignore"
132
- });
116
+ const probe = spawnSync(candidate, [], { cwd: root, stdio: ["ignore", "ignore", "ignore"] });
133
117
  return !probe.error && probe.status !== null;
134
118
  }
135
119
 
136
120
  function buildSourceNativeBinary(root) {
137
- const manifestPath = join(root, "packages", "naome", "Cargo.toml");
121
+ if (expectedNativeBinaryIntegrity !== "sha256:generated") {
122
+ return null;
123
+ }
124
+
125
+ const packageRoot = join(root, "packages", "naome");
126
+ const manifestPath = join(packageRoot, "Cargo.toml");
138
127
  if (!existsSync(manifestPath)) {
139
128
  return null;
140
129
  }
141
130
 
142
- const result = spawnSync("cargo", ["build", "--release", "--manifest-path", manifestPath, "-p", "naome-cli"], {
143
- cwd: root,
144
- encoding: "utf8",
145
- stdio: "ignore"
146
- });
131
+ const result = spawnSync(
132
+ "cargo",
133
+ ["build", "--release", "--manifest-path", manifestPath, "-p", "naome-cli"],
134
+ {
135
+ cwd: root,
136
+ encoding: "utf8",
137
+ stdio: "ignore"
138
+ }
139
+ );
147
140
  if (result.status !== 0) {
148
141
  return null;
149
142
  }
150
143
 
151
- const builtPath = join(root, "packages", "naome", "target", "release", nativeBinaryName);
144
+ const builtPath = join(packageRoot, "target", "release", nativeBinaryName);
152
145
  return existsSync(builtPath) ? builtPath : null;
153
146
  }
154
147
 
@@ -27,7 +27,7 @@ function main(argv) {
27
27
  return;
28
28
  }
29
29
 
30
- if (["status", "next", "intent", "route", "explain", "context", "doctor", "task", "quality", "semantic", "repo", "structure", "cleanup", "refresh-integrity", "workflow"].includes(command)) {
30
+ if (["status", "next", "intent", "route", "explain", "context", "doctor", "task", "quality", "semantic", "arch", "repo", "structure", "cleanup", "refresh-integrity", "workflow"].includes(command)) {
31
31
  runNativeDecisionCommand(command, args);
32
32
  return;
33
33
  }
@@ -367,7 +367,7 @@ function findAncestorWithAnyMarker(startPath, markers) {
367
367
  }
368
368
 
369
369
  function printHelp() {
370
- const commands = "naome status [--json]|naome next [--json]|naome intent --prompt-file <path> [--json]|naome intent --prompt <text> [--json]|naome route --prompt-file <path> [--execute] [--json]|naome route --prompt <text> [--execute] [--json]|naome explain --prompt-file <path> [--json]|naome explain --prompt <text> [--json]|naome context select --changed [--json]|naome context select --prompt-file <path> [--json]|naome context select --prompt <text> [--json]|naome doctor [--json]|naome task render-state [--write] [--json]|naome task migrate-ledger [--write] [--json]|naome quality init [--baseline|--deep-baseline] [--json]|naome quality check --changed [--include-scanned-paths] [--json]|naome quality check --path <path> [--path <path>...] [--include-scanned-paths] [--json]|naome quality report [--deep] [--include-scanned-paths] [--json]|naome quality cache status [--json]|naome quality cache clear|naome semantic report [--deep] [--json]|naome semantic check --changed [--json]|naome semantic check --path <path> [--path <path>...] [--json]|naome semantic route --finding <id> [--json]|naome semantic loop [--json]|naome repo model [--write] [--json]|naome repo check [--json]|naome repo explain --path <path> [--json]|naome structure report [--json]|naome structure explain --path <path> [--json]|naome cleanup plan [--json]|naome cleanup route --path <path> [--json]|naome refresh-integrity [--json]|naome workflow agent-plan|context-delta|proof-plan|capabilities|edit-watchdog|decision-gate|digest [--json]|naome workflow search-profile|check-search|phases|processes|mutations [--json]|naome install|naome sync|naome commit -m \"type(scope): message\"|node .naome/bin/naome.js commit -m \"type(scope): message\"".split("|");
370
+ const commands = "naome status [--json]|naome next [--json]|naome intent --prompt-file <path> [--json]|naome intent --prompt <text> [--json]|naome route --prompt-file <path> [--execute] [--json]|naome route --prompt <text> [--execute] [--json]|naome explain --prompt-file <path> [--json]|naome explain --prompt <text> [--json]|naome context select --changed [--json]|naome context select --prompt-file <path> [--json]|naome context select --prompt <text> [--json]|naome doctor [--json]|naome task render-state [--write] [--json]|naome task migrate-ledger [--write] [--json]|naome quality init [--baseline|--deep-baseline] [--json]|naome quality check --changed [--include-scanned-paths] [--json]|naome quality check --path <path> [--path <path>...] [--include-scanned-paths] [--json]|naome quality report [--deep] [--include-scanned-paths] [--json]|naome quality cache status [--json]|naome quality cache clear|naome semantic report [--deep] [--json]|naome semantic check --changed [--json]|naome semantic check --path <path> [--path <path>...] [--json]|naome semantic route --finding <id> [--json]|naome semantic loop [--json]|naome arch init [--config <path>] [--json]|naome arch explain [--config <path>] [--json]|naome arch scan [--config <path>] [--changed-only] [--write] [--output <path>] [--json]|naome arch validate [--config <path>] [--changed-only] [--json|--agent-feedback]|naome repo model [--write] [--json]|naome repo check [--json]|naome repo explain --path <path> [--json]|naome structure report [--json]|naome structure explain --path <path> [--json]|naome cleanup plan [--json]|naome cleanup route --path <path> [--json]|naome refresh-integrity [--json]|naome workflow agent-plan|context-delta|proof-plan|capabilities|edit-watchdog|decision-gate|digest [--json]|naome workflow search-profile|check-search|phases|processes|mutations [--json]|naome install|naome sync|naome commit -m \"type(scope): message\"|node .naome/bin/naome.js commit -m \"type(scope): message\"".split("|");
371
371
  console.log(["Usage:", ...commands.map((command) => ` ${command}`)].join("\n"));
372
372
  }
373
373
 
@@ -1,18 +1,19 @@
1
1
  {
2
- "harnessVersion": "1.3.8",
2
+ "harnessVersion": "1.3.10",
3
3
  "installedAt": null,
4
4
  "integrity": {
5
- ".naome/bin/check-harness-health.js": "sha256:dc4de52b79c69600b9ba47b924e2c2b8de61a2cbfab6d1ccc0f1924d963db657",
6
- ".naome/bin/check-task-state.js": "sha256:df54489a22b426180266e5e0fb5f9ec381477419f688435248afbf2b9b38ea81",
7
- ".naome/bin/naome.js": "sha256:a34c2e50a68d15ff2722a57590ccddc504e025d3c98ea62fd700d7ec1a789b9a",
5
+ ".naome/bin/check-harness-health.js": "sha256:802d7419774981a6af1826b3882270ff8f41259d516f98c52a02b4ddc184c467",
6
+ ".naome/bin/check-task-state.js": "sha256:2612577b7e4ab45d9d39dd5ac54c8e7ed749d237d78f1a8d252f4dfa0b4eaaab",
7
+ ".naome/bin/naome.js": "sha256:f129c580fb70b3a1d92d0ab0de9fa6c131b2fc51a8dcdebe9d6f5a812a3be61b",
8
8
  ".naome/package.json": "sha256:8005a3491db7d92f36ac66369861589f9c47123d3a7c71e643fc2c06168cd45a",
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:ec2cff9f2090d9b6bfeb2464a650cff97790c4d3e626fd40a42374d5f79c7c38",
12
13
  "docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
13
14
  "docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
14
15
  "docs/naome/first-run.md": "sha256:1466ce8c65e19a1514885f917db14e8a772350e3f6d1c03a66326963365919e1",
15
- "docs/naome/index.md": "sha256:07ef776f49130319a5280bdb3ae38af22141708253f38eb983a4336fbae1b25a",
16
+ "docs/naome/index.md": "sha256:cac748ed375d86d288460456fc5d606b29bd99d91148522c303d5400a083dbc5",
16
17
  "docs/naome/task-ledger.md": "sha256:6ca7222c80079b4662fb718d3c71d686770646f1fa52b83b0e90aed1c5a1101b",
17
18
  "docs/naome/upgrade.md": "sha256:2c60f0441bbd98bd528d109b30a7ded4b0ad55d61ffb9f52edac9e93b7999cb1"
18
19
  },
@@ -27,8 +28,9 @@
27
28
  "docs/naome/first-run.md",
28
29
  "docs/naome/agent-workflow.md",
29
30
  "docs/naome/context-economy.md",
30
- "docs/naome/execution.md",
31
31
  "docs/naome/task-ledger.md",
32
+ "docs/naome/architecture-fitness.md",
33
+ "docs/naome/execution.md",
32
34
  "docs/naome/upgrade.md"
33
35
  ],
34
36
  "name": "naome",
@@ -65,6 +65,19 @@
65
65
  ".naome/repository-quality-baseline.json"
66
66
  ],
67
67
  "lastVerified": null
68
+ },
69
+ {
70
+ "id": "architecture-fitness-check",
71
+ "command": "node .naome/bin/naome.js arch validate --changed-only",
72
+ "cwd": ".",
73
+ "purpose": "Validate changed files against deterministic NAOME architecture fitness rules.",
74
+ "cost": "fast",
75
+ "source": "NAOME built-in",
76
+ "evidence": [
77
+ "naome.arch.yaml",
78
+ "docs/naome/architecture-fitness.md"
79
+ ],
80
+ "lastVerified": null
68
81
  }
69
82
  ],
70
83
  "phases": [
@@ -81,7 +94,8 @@
81
94
  "order": 20,
82
95
  "checkIds": [
83
96
  "repository-quality-check",
84
- "repository-semantic-check"
97
+ "repository-semantic-check",
98
+ "architecture-fitness-check"
85
99
  ]
86
100
  },
87
101
  {
@@ -0,0 +1,97 @@
1
+ # Architecture Fitness
2
+
3
+ NAOME architecture fitness validates repository structure with deterministic
4
+ rules before agent changes are accepted. The rule layer is language-agnostic:
5
+ language-specific extractors feed a normalized graph, and rules evaluate graph
6
+ facts instead of prompt text.
7
+
8
+ ## Commands
9
+
10
+ - `naome arch init` writes a starter `naome.arch.yaml`.
11
+ - `naome arch explain` prints inferred layers, contexts, rules, and extractors.
12
+ - `naome arch scan --json` emits the normalized graph.
13
+ - `naome arch scan --write` writes `.naome/architecture-graph.json`.
14
+ - `naome arch validate` runs architecture rules with human output.
15
+ - `naome arch validate --json` emits stable machine-readable output.
16
+ - `naome arch validate --agent-feedback` emits compact repair instructions.
17
+ - `naome arch validate --changed-only` uses changed paths when available and
18
+ safely degrades to a full scan for graph-level soundness.
19
+
20
+ ## Graph Model
21
+
22
+ The foundation graph uses stable IDs and normalized node and edge kinds. Nodes
23
+ cover repositories, directories, files, layers, bounded contexts, modules,
24
+ symbols, packages, and external dependencies. Edges cover containment and the
25
+ dependency edge kinds required by later extractor slices.
26
+
27
+ Each node and edge carries path, language, source range when known, confidence,
28
+ extractor name, raw origin data, stable ID, and a human-readable label.
29
+
30
+ ## Configuration
31
+
32
+ `naome.arch.yaml` defines layers, bounded contexts, rules, and ignored paths.
33
+ Ignored paths require a reason so agents cannot silently suppress architecture
34
+ ownership.
35
+
36
+ ```yaml
37
+ layers:
38
+ domain:
39
+ paths:
40
+ - "src/domain/**"
41
+ infrastructure:
42
+ paths:
43
+ - "src/infrastructure/**"
44
+
45
+ contexts:
46
+ billing:
47
+ paths:
48
+ - "src/billing/**"
49
+ public_api:
50
+ - "src/billing/index.ts"
51
+
52
+ rules:
53
+ max_file_lines:
54
+ enabled: true
55
+ value: 400
56
+ severity: warning
57
+ generated_manual_boundary:
58
+ enabled: true
59
+ severity: error
60
+
61
+ ignore:
62
+ - path: "generated/**"
63
+ reason: "Generated code is not architecture-owned."
64
+ ```
65
+
66
+ ## Current Rules
67
+
68
+ - `arch.max_file_lines` enforces the configured file line budget.
69
+ - `arch.generated_manual_boundary` prevents changed files under explicitly
70
+ ignored generated paths from being accepted without regeneration or a config
71
+ change.
72
+
73
+ ## Agent Integration
74
+
75
+ Agents should run `naome arch validate --changed-only --json` after changing
76
+ source or template files. JSON output includes status, severity counts,
77
+ violations, concrete suggestions, and `agentFeedback` entries optimized for
78
+ repair loops.
79
+
80
+ ## CI Integration
81
+
82
+ Use `node .naome/bin/naome.js arch validate --changed-only` as a fast gate.
83
+ The first foundation release reports warnings for existing file-size debt and
84
+ fails only on configured error rules.
85
+
86
+ ## Language Support
87
+
88
+ The v1.3.10 foundation classifies TypeScript, JavaScript, Rust, Python, Go,
89
+ Java, Kotlin, and Swift files by path extension. Import extractors, resolver
90
+ rules, cycle detection, layer dependency rules, and manifest extraction are
91
+ planned follow-up slices before v1.4.0.
92
+
93
+ ## Limitations
94
+
95
+ This release intentionally starts with path extraction, graph construction,
96
+ file budgets, generated/manual boundaries, JSON output, human output, and CLI
97
+ integration. It does not yet resolve imports or validate cross-layer imports.
@@ -22,9 +22,10 @@ for the current step.
22
22
  15. `testing.md`
23
23
  16. `repository-quality.md`, when repository-quality checks or cleanup are relevant
24
24
  17. `repository-structure.md`, when path roles or structure cleanup are relevant
25
- 18. `security.md`
26
- 19. `agent-workflow.md`
27
- 20. `decisions.md`, when changing durable project policy
25
+ 18. `architecture-fitness.md`, when architecture graph or rule feedback matters
26
+ 19. `security.md`
27
+ 20. `agent-workflow.md`
28
+ 21. `decisions.md`, when changing durable project policy
28
29
 
29
30
  ## Source Types
30
31
 
@@ -26,6 +26,7 @@ and stale-policy issues before the task grows. It does not replace the final
26
26
  | naome-task-state | `node .naome/bin/check-task-state.js` | `.` | fast | null |
27
27
  | repository-quality-check | `node .naome/bin/naome.js quality check --changed` | `.` | fast | null |
28
28
  | repository-semantic-check | `node .naome/bin/naome.js semantic check --changed` | `.` | fast | null |
29
+ | architecture-fitness-check | `node .naome/bin/naome.js arch validate --changed-only` | `.` | fast | null |
29
30
 
30
31
  ## Verification Phases
31
32
 
@@ -52,6 +53,7 @@ phase is failing or missing.
52
53
  - `.naome/bin/check-harness-health.js`
53
54
  - `.naome/bin/check-task-state.js`
54
55
  - `.naome/task-contract.schema.json`
56
+ - `docs/naome/architecture-fitness.md`
55
57
 
56
58
  ## Rules
57
59
 
@@ -66,8 +68,9 @@ phase is failing or missing.
66
68
  and 12 release gates.
67
69
  - Store long command output as a compact summary that preserves command, cwd,
68
70
  exit code, relevant lines, affected paths, and artifacts.
69
- - When intake defines change types, include `repository-quality-check` and
70
- `repository-semantic-check` as required checks for source, structure,
71
- documentation, harness, template, and CI changes.
71
+ - When intake defines change types, include `repository-quality-check`,
72
+ `repository-semantic-check`, and `architecture-fitness-check` as required
73
+ checks for source, structure, documentation, harness, template, and CI
74
+ changes.
72
75
  - Before completion, select proof from the Verification Map when possible.
73
76
  - Report exact commands and results. Do not claim proof that did not run.