soturail 1.2.0 → 1.5.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.
Files changed (251) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +43 -720
  3. package/dist/cli.js +14 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/bench.js +4 -4
  6. package/dist/commands/bench.js.map +1 -1
  7. package/dist/commands/capabilities.d.ts +2 -0
  8. package/dist/commands/capabilities.js +18 -0
  9. package/dist/commands/capabilities.js.map +1 -0
  10. package/dist/commands/contract.d.ts +2 -0
  11. package/dist/commands/contract.js +61 -0
  12. package/dist/commands/contract.js.map +1 -0
  13. package/dist/commands/eval.js +14 -0
  14. package/dist/commands/eval.js.map +1 -1
  15. package/dist/commands/evidence.d.ts +2 -0
  16. package/dist/commands/evidence.js +18 -0
  17. package/dist/commands/evidence.js.map +1 -0
  18. package/dist/commands/governance.d.ts +2 -0
  19. package/dist/commands/governance.js +31 -0
  20. package/dist/commands/governance.js.map +1 -0
  21. package/dist/commands/knowledge.d.ts +2 -0
  22. package/dist/commands/knowledge.js +26 -0
  23. package/dist/commands/knowledge.js.map +1 -0
  24. package/dist/commands/mcp.js +2 -2
  25. package/dist/commands/mcp.js.map +1 -1
  26. package/dist/commands/raw.d.ts +2 -0
  27. package/dist/commands/raw.js +27 -0
  28. package/dist/commands/raw.js.map +1 -0
  29. package/dist/commands/read.js +2 -2
  30. package/dist/commands/read.js.map +1 -1
  31. package/dist/commands/run.js +12 -1
  32. package/dist/commands/run.js.map +1 -1
  33. package/dist/commands/skills.js +26 -0
  34. package/dist/commands/skills.js.map +1 -1
  35. package/dist/commands/tasklet.d.ts +2 -0
  36. package/dist/commands/tasklet.js +20 -0
  37. package/dist/commands/tasklet.js.map +1 -0
  38. package/dist/core/agent-qa.d.ts +26 -0
  39. package/dist/core/agent-qa.js +178 -0
  40. package/dist/core/agent-qa.js.map +1 -0
  41. package/dist/core/artifact-envelope.d.ts +63 -0
  42. package/dist/core/artifact-envelope.js +46 -0
  43. package/dist/core/artifact-envelope.js.map +1 -0
  44. package/dist/core/artifact-registry.d.ts +10 -0
  45. package/dist/core/artifact-registry.js +35 -0
  46. package/dist/core/artifact-registry.js.map +1 -0
  47. package/dist/core/artifact-store.d.ts +14 -0
  48. package/dist/core/artifact-store.js +161 -0
  49. package/dist/core/artifact-store.js.map +1 -0
  50. package/dist/core/capability-registry.d.ts +50 -0
  51. package/dist/core/capability-registry.js +72 -0
  52. package/dist/core/capability-registry.js.map +1 -0
  53. package/dist/core/change-contract.d.ts +56 -0
  54. package/dist/core/change-contract.js +61 -0
  55. package/dist/core/change-contract.js.map +1 -0
  56. package/dist/core/code-health.js +72 -19
  57. package/dist/core/code-health.js.map +1 -1
  58. package/dist/core/config.d.ts +36 -78
  59. package/dist/core/config.js +37 -11
  60. package/dist/core/config.js.map +1 -1
  61. package/dist/core/context-artifact.d.ts +37 -0
  62. package/dist/core/context-artifact.js +56 -0
  63. package/dist/core/context-artifact.js.map +1 -0
  64. package/dist/core/context-intelligence.js +1 -1
  65. package/dist/core/context-intelligence.js.map +1 -1
  66. package/dist/core/context-pack.d.ts +4 -0
  67. package/dist/core/context-pack.js +20 -7
  68. package/dist/core/context-pack.js.map +1 -1
  69. package/dist/core/evidence-provenance.d.ts +48 -0
  70. package/dist/core/evidence-provenance.js +150 -0
  71. package/dist/core/evidence-provenance.js.map +1 -0
  72. package/dist/core/execution-envelope.d.ts +60 -0
  73. package/dist/core/execution-envelope.js +52 -0
  74. package/dist/core/execution-envelope.js.map +1 -0
  75. package/dist/core/governance.d.ts +50 -0
  76. package/dist/core/governance.js +69 -0
  77. package/dist/core/governance.js.map +1 -0
  78. package/dist/core/harness-lifecycle.js +1 -1
  79. package/dist/core/harness-lifecycle.js.map +1 -1
  80. package/dist/core/harness-rail.js +2 -2
  81. package/dist/core/harness-rail.js.map +1 -1
  82. package/dist/core/knowledge-rail.d.ts +59 -0
  83. package/dist/core/knowledge-rail.js +244 -0
  84. package/dist/core/knowledge-rail.js.map +1 -0
  85. package/dist/core/mcp-server.d.ts +13 -2
  86. package/dist/core/mcp-server.js +58 -35
  87. package/dist/core/mcp-server.js.map +1 -1
  88. package/dist/core/mcp-tools.d.ts +9 -0
  89. package/dist/core/mcp-tools.js +58 -36
  90. package/dist/core/mcp-tools.js.map +1 -1
  91. package/dist/core/raw-lifecycle.d.ts +21 -0
  92. package/dist/core/raw-lifecycle.js +94 -0
  93. package/dist/core/raw-lifecycle.js.map +1 -0
  94. package/dist/core/raw-store.d.ts +6 -0
  95. package/dist/core/raw-store.js +4 -1
  96. package/dist/core/raw-store.js.map +1 -1
  97. package/dist/core/release-preflight.js +19 -19
  98. package/dist/core/release-preflight.js.map +1 -1
  99. package/dist/core/reverse-specification.js +2 -2
  100. package/dist/core/reverse-specification.js.map +1 -1
  101. package/dist/core/run-manifest.d.ts +80 -0
  102. package/dist/core/run-manifest.js +43 -0
  103. package/dist/core/run-manifest.js.map +1 -0
  104. package/dist/core/run-workspace.js +7 -3
  105. package/dist/core/run-workspace.js.map +1 -1
  106. package/dist/core/schema-readiness.js +22 -16
  107. package/dist/core/schema-readiness.js.map +1 -1
  108. package/dist/core/skill-rail-v2.d.ts +24 -0
  109. package/dist/core/skill-rail-v2.js +106 -0
  110. package/dist/core/skill-rail-v2.js.map +1 -0
  111. package/dist/core/skill-schema.d.ts +23 -33
  112. package/dist/core/tasklet-rail.d.ts +22 -0
  113. package/dist/core/tasklet-rail.js +69 -0
  114. package/dist/core/tasklet-rail.js.map +1 -0
  115. package/dist/core/version.d.ts +1 -1
  116. package/dist/core/version.js +1 -1
  117. package/dist/core/workspace-fingerprint.d.ts +20 -0
  118. package/dist/core/workspace-fingerprint.js +89 -0
  119. package/dist/core/workspace-fingerprint.js.map +1 -0
  120. package/dist/core/workspace-guard.d.ts +29 -0
  121. package/dist/core/workspace-guard.js +141 -0
  122. package/dist/core/workspace-guard.js.map +1 -0
  123. package/docs/README.md +67 -0
  124. package/docs/architecture/architecture.md +58 -0
  125. package/docs/architecture/artifact-model-and-lineage.md +30 -0
  126. package/docs/architecture/context-architecture.md +29 -0
  127. package/docs/architecture/contracts-and-verification.md +32 -0
  128. package/docs/architecture/governance-model.md +27 -0
  129. package/docs/{observability-rail.md → architecture/observability-rail.md} +5 -5
  130. package/docs/architecture/provider-architecture.md +29 -0
  131. package/docs/architecture/verified-control-plane.md +53 -0
  132. package/docs/audits/v1.5.0-preimplementation-audit.md +64 -0
  133. package/docs/{agent-harness-synthesis-2026.md → ecosystem/agent-harness-synthesis-2026.md} +3 -3
  134. package/docs/{conductor-mode.md → ecosystem/conductor-mode.md} +3 -3
  135. package/docs/{ecosystem-influences.md → ecosystem/ecosystem-influences.md} +13 -13
  136. package/docs/{external-projects-audit.md → ecosystem/external-projects-audit.md} +1 -1
  137. package/docs/getting-started/migration-v1.5.md +40 -0
  138. package/docs/{migration-v1.md → getting-started/migration-v1.md} +1 -1
  139. package/docs/{context-intelligence.md → rails/context/context-intelligence.md} +4 -4
  140. package/docs/{context-packs.md → rails/context/context-packs.md} +7 -7
  141. package/docs/{memory-rail.md → rails/context/memory-rail.md} +2 -2
  142. package/docs/{structured-payload-rail.md → rails/context/structured-payload-rail.md} +3 -3
  143. package/docs/{spec-driven-workflow.md → rails/design/spec-driven-workflow.md} +1 -1
  144. package/docs/rails/evaluation/agent-qa-rail.md +34 -0
  145. package/docs/rails/evaluation/benchmarking.md +50 -0
  146. package/docs/rails/evidence/evidence-provenance-rail.md +41 -0
  147. package/docs/{report-rail.md → rails/evidence/report-rail.md} +4 -4
  148. package/docs/{governance-cost-rail.md → rails/governance/governance-cost-rail.md} +5 -5
  149. package/docs/{rules.md → rails/governance/rules.md} +2 -2
  150. package/docs/{harness-lifecycle-rail.md → rails/harness/harness-lifecycle-rail.md} +2 -2
  151. package/docs/{harness-rail.md → rails/harness/harness-rail.md} +1 -1
  152. package/docs/{workflow-rail.md → rails/harness/workflow-rail.md} +4 -4
  153. package/docs/{agent-docs-hygiene.md → rails/hosts/agent-docs-hygiene.md} +1 -1
  154. package/docs/{agent-hosts.md → rails/hosts/agent-hosts.md} +4 -4
  155. package/docs/{agents.md → rails/hosts/agents.md} +10 -10
  156. package/docs/{mcp.md → rails/hosts/mcp.md} +8 -8
  157. package/docs/rails/knowledge/knowledge-rail.md +49 -0
  158. package/docs/rails/skills/skill-rail-2.md +42 -0
  159. package/docs/rails/tasklets/tasklet-rail.md +32 -0
  160. package/docs/{release-checklist.md → reference/commands/release-checklist.md} +1 -1
  161. package/docs/{stable-command-surface.md → reference/commands/stable-command-surface.md} +20 -13
  162. package/docs/reference/commands/v1.4-commands.md +51 -0
  163. package/docs/reference/commands/v1.5-commands.md +46 -0
  164. package/docs/{deprecation-policy.md → reference/contracts/deprecation-policy.md} +1 -1
  165. package/docs/{v1-contract.md → reference/contracts/v1-contract.md} +3 -3
  166. package/docs/{licensing-strategy.md → reference/licensing-strategy.md} +1 -1
  167. package/docs/{schema-contracts.md → reference/schemas/schema-contracts.md} +16 -2
  168. package/docs/releases/README.md +2 -0
  169. package/docs/releases/RELEASE_NOTES_v0.10.1.md +3 -3
  170. package/docs/releases/RELEASE_NOTES_v1.0.1.md +2 -2
  171. package/docs/releases/RELEASE_NOTES_v1.4.0.md +47 -0
  172. package/docs/releases/RELEASE_NOTES_v1.5.0.md +72 -0
  173. package/docs/{future-rails-index.md → roadmap/future-rails-index.md} +83 -91
  174. package/docs/{repo-docs-audit-2026-06-05.md → roadmap/repo-docs-audit-2026-06-05.md} +10 -10
  175. package/docs/{roadmap-docs-audit.md → roadmap/roadmap-docs-audit.md} +14 -14
  176. package/docs/{roadmap-harness-diagram-payload-addendum.md → roadmap/roadmap-harness-diagram-payload-addendum.md} +10 -10
  177. package/docs/roadmap/verified-control-plane-implementation-tracker.md +81 -0
  178. package/docs/{security-boundaries.md → security/security-boundaries.md} +8 -5
  179. package/docs/security/security-model.md +42 -0
  180. package/docs/security/threat-model.md +33 -0
  181. package/docs/{tutorial-codex.md → tutorials/tutorial-codex.md} +1 -1
  182. package/examples/workflows/agent-pipeline-workflow.md +11 -0
  183. package/package.json +9 -4
  184. package/docs/agent-qa-rail.md +0 -92
  185. package/docs/architecture.md +0 -47
  186. package/docs/benchmarking.md +0 -351
  187. package/docs/evidence-provenance-rail.md +0 -70
  188. package/docs/knowledge-rail.md +0 -76
  189. package/docs/security-model.md +0 -110
  190. package/docs/skill-rail-2.md +0 -110
  191. package/docs/tasklet-rail.md +0 -62
  192. /package/docs/{architecture-boundaries.md → architecture/architecture-boundaries.md} +0 -0
  193. /package/docs/{clean-code-guidelines.md → architecture/clean-code-guidelines.md} +0 -0
  194. /package/docs/{dashboard-rail.md → architecture/dashboard-rail.md} +0 -0
  195. /package/docs/{comparisons.md → ecosystem/comparisons.md} +0 -0
  196. /package/docs/{first-real-workflow.md → getting-started/first-real-workflow.md} +0 -0
  197. /package/docs/{migration-v0.5.md → getting-started/migration-v0.5.md} +0 -0
  198. /package/docs/{mvp.md → getting-started/mvp.md} +0 -0
  199. /package/docs/{quickstart.md → getting-started/quickstart.md} +0 -0
  200. /package/docs/{usage.md → getting-started/usage.md} +0 -0
  201. /package/docs/{windows.md → getting-started/windows.md} +0 -0
  202. /package/docs/{prompt-caching.md → rails/context/prompt-caching.md} +0 -0
  203. /package/docs/{reducers.md → rails/context/reducers.md} +0 -0
  204. /package/docs/{response-compression.md → rails/context/response-compression.md} +0 -0
  205. /package/docs/{design-rail.md → rails/design/design-rail.md} +0 -0
  206. /package/docs/{diagram-rail.md → rails/design/diagram-rail.md} +0 -0
  207. /package/docs/{eval-datasets.md → rails/evaluation/eval-datasets.md} +0 -0
  208. /package/docs/{evaluation-suite.md → rails/evaluation/evaluation-suite.md} +0 -0
  209. /package/docs/{golden-agent-tests.md → rails/evaluation/golden-agent-tests.md} +0 -0
  210. /package/docs/{llm-as-judge-policy.md → rails/evaluation/llm-as-judge-policy.md} +0 -0
  211. /package/docs/{metrics.md → rails/evaluation/metrics.md} +0 -0
  212. /package/docs/{agent-readable-reports.md → rails/evidence/agent-readable-reports.md} +0 -0
  213. /package/docs/{report-redaction.md → rails/evidence/report-redaction.md} +0 -0
  214. /package/docs/{agent-governance-rail.md → rails/governance/agent-governance-rail.md} +0 -0
  215. /package/docs/{baseline-snapshots.md → rails/governance/baseline-snapshots.md} +0 -0
  216. /package/docs/{native-performance-policy.md → rails/governance/native-performance-policy.md} +0 -0
  217. /package/docs/{native-runner.md → rails/governance/native-runner.md} +0 -0
  218. /package/docs/{policy-rail.md → rails/governance/policy-rail.md} +0 -0
  219. /package/docs/{rate-limit-and-fallback-policy.md → rails/governance/rate-limit-and-fallback-policy.md} +0 -0
  220. /package/docs/{resilience-rail.md → rails/governance/resilience-rail.md} +0 -0
  221. /package/docs/{filesystem-evidence-rail.md → rails/harness/filesystem-evidence-rail.md} +0 -0
  222. /package/docs/{deep-agents-patterns.md → rails/hosts/deep-agents-patterns.md} +0 -0
  223. /package/docs/{hooks.md → rails/hosts/hooks.md} +0 -0
  224. /package/docs/{host-compatibility-rail.md → rails/hosts/host-compatibility-rail.md} +0 -0
  225. /package/docs/{host-router-rail.md → rails/hosts/host-router-rail.md} +0 -0
  226. /package/docs/{mcp-host-manifest.md → rails/hosts/mcp-host-manifest.md} +0 -0
  227. /package/docs/{mcp-report-resources.md → rails/hosts/mcp-report-resources.md} +0 -0
  228. /package/docs/{code-graph.md → rails/knowledge/code-graph.md} +0 -0
  229. /package/docs/{knowledge-graph-rail.md → rails/knowledge/knowledge-graph-rail.md} +0 -0
  230. /package/docs/{knowledge-to-rules.md → rails/knowledge/knowledge-to-rules.md} +0 -0
  231. /package/docs/{project-brain.md → rails/knowledge/project-brain.md} +0 -0
  232. /package/docs/{reverse-specification-rail.md → rails/knowledge/reverse-specification-rail.md} +0 -0
  233. /package/docs/{skill-rail.md → rails/skills/skill-rail.md} +0 -0
  234. /package/docs/{multi-agent-workflow-templates.md → rails/tasklets/multi-agent-workflow-templates.md} +0 -0
  235. /package/docs/{branding.md → reference/branding.md} +0 -0
  236. /package/docs/{release-workflow.md → reference/commands/release-workflow.md} +0 -0
  237. /package/docs/{status-command.md → reference/commands/status-command.md} +0 -0
  238. /package/docs/{agent-export-contract.md → reference/contracts/agent-export-contract.md} +0 -0
  239. /package/docs/{media-guide.md → reference/media-guide.md} +0 -0
  240. /package/docs/{host-matrix-schema.md → reference/schemas/host-matrix-schema.md} +0 -0
  241. /package/docs/{public-roadmap.md → roadmap/public-roadmap.md} +0 -0
  242. /package/docs/{roadmap-agent-runtime-addendum.md → roadmap/roadmap-agent-runtime-addendum.md} +0 -0
  243. /package/docs/{tutorial-antigravity.md → tutorials/tutorial-antigravity.md} +0 -0
  244. /package/docs/{tutorial-claude-code.md → tutorials/tutorial-claude-code.md} +0 -0
  245. /package/docs/{tutorial-context-formats.md → tutorials/tutorial-context-formats.md} +0 -0
  246. /package/docs/{tutorial-cursor.md → tutorials/tutorial-cursor.md} +0 -0
  247. /package/docs/{tutorial-deep-agents-role-packs.md → tutorials/tutorial-deep-agents-role-packs.md} +0 -0
  248. /package/docs/{tutorial-diagram-spec.md → tutorials/tutorial-diagram-spec.md} +0 -0
  249. /package/docs/{tutorial-gemini-cli.md → tutorials/tutorial-gemini-cli.md} +0 -0
  250. /package/docs/{tutorial-harness-workflow.md → tutorials/tutorial-harness-workflow.md} +0 -0
  251. /package/docs/{tutorial-opencode.md → tutorials/tutorial-opencode.md} +0 -0
@@ -0,0 +1,35 @@
1
+ import path from "node:path";
2
+ import { getWorkspacePaths } from "./config.js";
3
+ export class ArtifactRegistry {
4
+ paths;
5
+ constructor(root = process.cwd()) {
6
+ const workspace = getWorkspacePaths(root);
7
+ this.paths = {
8
+ artifacts: workspace.artifactsDir,
9
+ brain: workspace.brainDir,
10
+ capabilities: workspace.capabilitiesDir,
11
+ contracts: workspace.contractsDir,
12
+ evals: workspace.evalsDir,
13
+ evidence: workspace.evidenceDir,
14
+ index: workspace.localIndexDir,
15
+ knowledge: workspace.knowledgeDir,
16
+ migrations: workspace.migrationsDir,
17
+ outcomes: workspace.outcomesDir,
18
+ raw: workspace.rawDir,
19
+ receipts: workspace.receiptsDir,
20
+ runs: workspace.runsDir
21
+ };
22
+ }
23
+ resolve(id, ...segments) {
24
+ const base = this.paths[id];
25
+ const target = path.resolve(base, ...segments);
26
+ const relative = path.relative(base, target);
27
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
28
+ throw new Error(`Artifact path escapes registry root: ${id}`);
29
+ return target;
30
+ }
31
+ entries() {
32
+ return Object.entries(this.paths).map(([id, value]) => ({ id: id, path: value }));
33
+ }
34
+ }
35
+ //# sourceMappingURL=artifact-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-registry.js","sourceRoot":"","sources":["../../src/core/artifact-registry.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiBhD,MAAM,OAAO,gBAAgB;IACV,KAAK,CAAiC;IAEvD,YAAY,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;QAC9B,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,GAAG;YACX,SAAS,EAAE,SAAS,CAAC,YAAY;YACjC,KAAK,EAAE,SAAS,CAAC,QAAQ;YACzB,YAAY,EAAE,SAAS,CAAC,eAAe;YACvC,SAAS,EAAE,SAAS,CAAC,YAAY;YACjC,KAAK,EAAE,SAAS,CAAC,QAAQ;YACzB,QAAQ,EAAE,SAAS,CAAC,WAAW;YAC/B,KAAK,EAAE,SAAS,CAAC,aAAa;YAC9B,SAAS,EAAE,SAAS,CAAC,YAAY;YACjC,UAAU,EAAE,SAAS,CAAC,aAAa;YACnC,QAAQ,EAAE,SAAS,CAAC,WAAW;YAC/B,GAAG,EAAE,SAAS,CAAC,MAAM;YACrB,QAAQ,EAAE,SAAS,CAAC,WAAW;YAC/B,IAAI,EAAE,SAAS,CAAC,OAAO;SACxB,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,EAAkB,EAAE,GAAG,QAAkB;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,QAAQ,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,EAAE,EAAE,CAAC,CAAC;QAC1J,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO;QACL,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAoB,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACtG,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ import type { ZodType } from "zod";
2
+ export interface JsonlReadResult<T> {
3
+ records: T[];
4
+ recoveredPartialTail: boolean;
5
+ corruptLine?: number;
6
+ }
7
+ export declare class ArtifactStore {
8
+ writeText(filePath: string, content: string): Promise<void>;
9
+ writeJson<T>(filePath: string, value: T, schema?: ZodType<T>): Promise<void>;
10
+ appendJsonl(filePath: string, value: unknown): Promise<void>;
11
+ readJsonl<T>(filePath: string): Promise<JsonlReadResult<T>>;
12
+ repairJsonl(filePath: string): Promise<JsonlReadResult<unknown>>;
13
+ }
14
+ export declare const artifactStore: ArtifactStore;
@@ -0,0 +1,161 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { promises as fs } from "node:fs";
3
+ import path from "node:path";
4
+ export class ArtifactStore {
5
+ async writeText(filePath, content) {
6
+ await atomicWrite(filePath, Buffer.from(content, "utf8"));
7
+ }
8
+ async writeJson(filePath, value, schema) {
9
+ const validated = schema ? schema.parse(value) : value;
10
+ await this.writeText(filePath, `${JSON.stringify(validated, null, 2)}\n`);
11
+ }
12
+ async appendJsonl(filePath, value) {
13
+ await withFileLock(filePath, async () => {
14
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
15
+ await recoverPartialJsonlTail(filePath);
16
+ const handle = await fs.open(filePath, "a");
17
+ try {
18
+ await handle.writeFile(`${JSON.stringify(value)}\n`, "utf8");
19
+ await handle.sync();
20
+ }
21
+ finally {
22
+ await handle.close();
23
+ }
24
+ });
25
+ }
26
+ async readJsonl(filePath) {
27
+ try {
28
+ const raw = await fs.readFile(filePath, "utf8");
29
+ return parseJsonl(raw);
30
+ }
31
+ catch (error) {
32
+ if (error.code === "ENOENT")
33
+ return { records: [], recoveredPartialTail: false };
34
+ throw error;
35
+ }
36
+ }
37
+ async repairJsonl(filePath) {
38
+ return withFileLock(filePath, async () => {
39
+ const recoveredPartialTail = await recoverPartialJsonlTail(filePath);
40
+ const result = await this.readJsonl(filePath);
41
+ return { ...result, recoveredPartialTail: recoveredPartialTail || result.recoveredPartialTail };
42
+ });
43
+ }
44
+ }
45
+ export const artifactStore = new ArtifactStore();
46
+ async function atomicWrite(filePath, content) {
47
+ const dir = path.dirname(filePath);
48
+ await fs.mkdir(dir, { recursive: true });
49
+ const temp = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
50
+ const handle = await fs.open(temp, "wx", 0o600);
51
+ try {
52
+ await handle.writeFile(content);
53
+ await handle.sync();
54
+ }
55
+ finally {
56
+ await handle.close();
57
+ }
58
+ try {
59
+ await fs.rename(temp, filePath);
60
+ }
61
+ catch (error) {
62
+ await fs.unlink(temp).catch(() => undefined);
63
+ throw error;
64
+ }
65
+ const directoryHandle = await fs.open(dir, "r").catch(() => null);
66
+ if (directoryHandle) {
67
+ try {
68
+ await directoryHandle.sync().catch(() => undefined);
69
+ }
70
+ finally {
71
+ await directoryHandle.close();
72
+ }
73
+ }
74
+ }
75
+ function parseJsonl(raw) {
76
+ const lines = raw.split(/\r?\n/);
77
+ const records = [];
78
+ const hasTerminatingNewline = raw.length === 0 || /\r?\n$/.test(raw);
79
+ for (let index = 0; index < lines.length; index += 1) {
80
+ const line = lines[index] ?? "";
81
+ if (!line.trim())
82
+ continue;
83
+ try {
84
+ records.push(JSON.parse(line));
85
+ }
86
+ catch {
87
+ const isPartialTail = index === lines.length - 1 && !hasTerminatingNewline;
88
+ if (isPartialTail)
89
+ return { records, recoveredPartialTail: true };
90
+ return { records, recoveredPartialTail: false, corruptLine: index + 1 };
91
+ }
92
+ }
93
+ return { records, recoveredPartialTail: false };
94
+ }
95
+ async function recoverPartialJsonlTail(filePath) {
96
+ let raw;
97
+ try {
98
+ raw = await fs.readFile(filePath);
99
+ }
100
+ catch (error) {
101
+ if (error.code === "ENOENT")
102
+ return false;
103
+ throw error;
104
+ }
105
+ if (raw.length === 0 || raw.at(-1) === 0x0a)
106
+ return false;
107
+ const lastNewline = raw.lastIndexOf(0x0a);
108
+ const tail = raw.subarray(lastNewline + 1).toString("utf8");
109
+ try {
110
+ JSON.parse(tail);
111
+ const handle = await fs.open(filePath, "a");
112
+ try {
113
+ await handle.writeFile("\n", "utf8");
114
+ await handle.sync();
115
+ }
116
+ finally {
117
+ await handle.close();
118
+ }
119
+ return false;
120
+ }
121
+ catch {
122
+ const handle = await fs.open(filePath, "r+");
123
+ try {
124
+ await handle.truncate(lastNewline + 1);
125
+ await handle.sync();
126
+ }
127
+ finally {
128
+ await handle.close();
129
+ }
130
+ return true;
131
+ }
132
+ }
133
+ async function withFileLock(filePath, operation) {
134
+ const lockPath = `${filePath}.lock`;
135
+ await fs.mkdir(path.dirname(lockPath), { recursive: true });
136
+ let lock;
137
+ for (let attempt = 0; attempt < 100; attempt += 1) {
138
+ try {
139
+ lock = await fs.open(lockPath, "wx", 0o600);
140
+ break;
141
+ }
142
+ catch (error) {
143
+ if (error.code !== "EEXIST")
144
+ throw error;
145
+ const stat = await fs.stat(lockPath).catch(() => null);
146
+ if (stat && Date.now() - stat.mtimeMs > 30_000)
147
+ await fs.unlink(lockPath).catch(() => undefined);
148
+ await new Promise((resolve) => setTimeout(resolve, 10));
149
+ }
150
+ }
151
+ if (!lock)
152
+ throw new Error(`Timed out waiting for artifact writer lock: ${filePath}`);
153
+ try {
154
+ return await operation();
155
+ }
156
+ finally {
157
+ await lock.close();
158
+ await fs.unlink(lockPath).catch(() => undefined);
159
+ }
160
+ }
161
+ //# sourceMappingURL=artifact-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-store.js","sourceRoot":"","sources":["../../src/core/artifact-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ export type CapabilityMaturity = "experimental" | "stable" | "deprecated";
2
+ export type SideEffectScope = "none" | "local-state" | "workspace" | "external";
3
+ export interface CapabilityDefinition {
4
+ id: string;
5
+ description: string;
6
+ maturity: CapabilityMaturity;
7
+ since: string;
8
+ cli: {
9
+ command: string;
10
+ } | null;
11
+ mcp: {
12
+ exposed: boolean;
13
+ tool?: string;
14
+ };
15
+ permissions: {
16
+ filesystemRead: "none" | "project" | "workspace";
17
+ filesystemWrite: "none" | "soturail-state" | "project";
18
+ shell: "none" | "restricted";
19
+ network: "none" | "optional";
20
+ };
21
+ sideEffects: {
22
+ scope: SideEffectScope;
23
+ reversible: boolean;
24
+ consequence: "low" | "medium" | "high";
25
+ };
26
+ approvalRequired: boolean;
27
+ outputs: string[];
28
+ docs: string[];
29
+ }
30
+ export declare const CAPABILITY_REGISTRY: readonly CapabilityDefinition[];
31
+ export declare function getCapabilityDefinition(id: string): CapabilityDefinition | undefined;
32
+ export declare function capabilityRegistryDigest(): string;
33
+ export interface CapabilityEpoch {
34
+ schemaVersion: "soturail.capability-epoch.v1";
35
+ id: string;
36
+ phase: "plan" | "implement" | "review" | "release";
37
+ registryDigest: string;
38
+ capabilityIds: string[];
39
+ createdAt: string;
40
+ }
41
+ export declare function createCapabilityEpoch(phase: CapabilityEpoch["phase"]): CapabilityEpoch;
42
+ export declare function inspectToolCapabilities(): Promise<Array<{
43
+ tool: string;
44
+ version: string;
45
+ ready: boolean;
46
+ path: string;
47
+ provider: string;
48
+ verification: string;
49
+ risk: string;
50
+ }>>;
@@ -0,0 +1,72 @@
1
+ import { createHash } from "node:crypto";
2
+ import { execFile } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+ const execFileAsync = promisify(execFile);
5
+ export const CAPABILITY_REGISTRY = [
6
+ capability("repo.index", "Build a deterministic local repository map.", "index", "soturail.index", "soturail.repo-map.v1", "local-state"),
7
+ capability("project.read", "Read non-sensitive project files through WorkspaceGuard.", "read <file>", "soturail.read", "text/plain", "none"),
8
+ capability("context.pack", "Build a fingerprinted context artifact within a hard budget.", "context build", "soturail.context.pack", "soturail.context-artifact.v1", "local-state"),
9
+ capability("evidence.collect", "Collect workspace-bound local evidence without executing checks.", "evidence collect", null, "soturail.evidence.provenance.v1", "local-state"),
10
+ capability("raw.inspect.redacted", "Inspect raw logs with mandatory redaction.", "raw inspect <id> --redacted", "soturail.expand", "text/plain", "none"),
11
+ capability("command.run", "Execute a user-supplied command through the restricted runner.", "run <command...>", null, "soturail.run.v1", "workspace", true, "high"),
12
+ capability("governance.evaluate", "Evaluate authority and readiness gates deterministically.", "governance evaluate", null, "soturail.governance.verdict.v1", "none"),
13
+ capability("contract.verify", "Verify change-contract fidelity, freshness and evidence requirements.", "contract verify <file>", null, "soturail.change-contract.verification.v1", "none")
14
+ ];
15
+ export function getCapabilityDefinition(id) {
16
+ return CAPABILITY_REGISTRY.find((item) => item.id === id);
17
+ }
18
+ export function capabilityRegistryDigest() {
19
+ return createHash("sha256").update(JSON.stringify(CAPABILITY_REGISTRY)).digest("hex");
20
+ }
21
+ export function createCapabilityEpoch(phase) {
22
+ const registryDigest = capabilityRegistryDigest();
23
+ return {
24
+ schemaVersion: "soturail.capability-epoch.v1",
25
+ id: `epoch-${phase}-${registryDigest.slice(0, 12)}`,
26
+ phase,
27
+ registryDigest,
28
+ capabilityIds: CAPABILITY_REGISTRY.map((item) => item.id),
29
+ createdAt: new Date().toISOString()
30
+ };
31
+ }
32
+ export async function inspectToolCapabilities() {
33
+ return Promise.all([
34
+ inspectTool("node", ["--version"], "native-runtime", "low"),
35
+ inspectTool("npm", ["--version"], "native-package-manager", "medium"),
36
+ inspectTool("git", ["--version"], "native-vcs", "medium"),
37
+ inspectTool("cargo", ["--version"], "optional-native", "medium")
38
+ ]);
39
+ }
40
+ async function inspectTool(tool, args, provider, risk) {
41
+ try {
42
+ const [{ stdout }, { stdout: resolved }] = await Promise.all([
43
+ execFileAsync(tool, args, { timeout: 5000, windowsHide: true, encoding: "utf8" }),
44
+ execFileAsync(process.platform === "win32" ? "where" : "which", [tool], { timeout: 5000, windowsHide: true, encoding: "utf8" })
45
+ ]);
46
+ return { tool, version: stdout.trim(), ready: true, path: resolved.split(/\r?\n/)[0] ?? "UNKNOWN", provider, verification: "executed-version-probe", risk };
47
+ }
48
+ catch {
49
+ return { tool, version: "UNAVAILABLE", ready: false, path: "UNAVAILABLE", provider, verification: "probe-failed", risk };
50
+ }
51
+ }
52
+ function capability(id, description, command, mcpTool, output, sideEffect, approvalRequired = false, consequence = "low") {
53
+ return {
54
+ id,
55
+ description,
56
+ maturity: "stable",
57
+ since: "1.5.0",
58
+ cli: { command },
59
+ mcp: mcpTool ? { exposed: true, tool: mcpTool } : { exposed: false },
60
+ permissions: {
61
+ filesystemRead: sideEffect === "none" ? "project" : "workspace",
62
+ filesystemWrite: sideEffect === "none" ? "none" : sideEffect === "workspace" ? "project" : "soturail-state",
63
+ shell: id === "command.run" ? "restricted" : "none",
64
+ network: "none"
65
+ },
66
+ sideEffects: { scope: sideEffect, reversible: sideEffect !== "external", consequence },
67
+ approvalRequired,
68
+ outputs: [output],
69
+ docs: ["docs/architecture/verified-control-plane.md"]
70
+ };
71
+ }
72
+ //# sourceMappingURL=capability-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-registry.js","sourceRoot":"","sources":["../../src/core/capability-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ import type { GovernanceVerdict } from "./governance.js";
3
+ export declare const ChangeContractSchema: z.ZodObject<{
4
+ schemaVersion: z.ZodLiteral<"soturail.change-contract.v1">;
5
+ id: z.ZodString;
6
+ title: z.ZodString;
7
+ intent: z.ZodString;
8
+ risk: z.ZodEnum<{
9
+ low: "low";
10
+ medium: "medium";
11
+ high: "high";
12
+ critical: "critical";
13
+ }>;
14
+ workspaceFingerprint: z.ZodString;
15
+ decisions: z.ZodArray<z.ZodObject<{
16
+ id: z.ZodString;
17
+ decision: z.ZodString;
18
+ rationale: z.ZodString;
19
+ }, z.core.$strip>>;
20
+ acceptanceCriteria: z.ZodArray<z.ZodString>;
21
+ requiredChecks: z.ZodArray<z.ZodString>;
22
+ evidencePolicy: z.ZodObject<{
23
+ runtimeEvidenceRequired: z.ZodBoolean;
24
+ independentReviewRequired: z.ZodBoolean;
25
+ humanApprovalRequired: z.ZodBoolean;
26
+ }, z.core.$strip>;
27
+ createdAt: z.ZodString;
28
+ }, z.core.$strip>;
29
+ export type ChangeContract = z.infer<typeof ChangeContractSchema>;
30
+ export interface ReadinessSnapshot {
31
+ workspaceFingerprint: string;
32
+ acceptanceCriteriaPassed: string[];
33
+ checksPassed: string[];
34
+ evidenceFreshness: "current" | "stale" | "unknown";
35
+ runtimeEvidence: boolean;
36
+ independentReview: boolean;
37
+ humanApproval: boolean;
38
+ blockers: string[];
39
+ }
40
+ export interface ReadinessVerdict {
41
+ schemaVersion: "soturail.readiness.verdict.v1";
42
+ verdict: "ready" | "not-ready";
43
+ reasons: string[];
44
+ evaluatedAt: string;
45
+ }
46
+ export interface DualGateVerdict {
47
+ schemaVersion: "soturail.dual-gate.v1";
48
+ authority: GovernanceVerdict["verdict"];
49
+ readiness: ReadinessVerdict["verdict"];
50
+ verdict: "allow" | "deny";
51
+ reasons: string[];
52
+ }
53
+ export declare function createChangeContract(input: Omit<ChangeContract, "schemaVersion" | "workspaceFingerprint" | "createdAt">, root?: string): Promise<ChangeContract>;
54
+ export declare function evaluateReadiness(contract: ChangeContract, snapshot: ReadinessSnapshot): ReadinessVerdict;
55
+ export declare function evaluateDualGate(authority: GovernanceVerdict, readiness: ReadinessVerdict): DualGateVerdict;
56
+ export declare function changeContractDigest(contract: ChangeContract): string;
@@ -0,0 +1,61 @@
1
+ import { createHash } from "node:crypto";
2
+ import { z } from "zod";
3
+ import { createWorkspaceFingerprint } from "./workspace-fingerprint.js";
4
+ export const ChangeContractSchema = z.object({
5
+ schemaVersion: z.literal("soturail.change-contract.v1"),
6
+ id: z.string().min(1),
7
+ title: z.string().min(1),
8
+ intent: z.string().min(1),
9
+ risk: z.enum(["low", "medium", "high", "critical"]),
10
+ workspaceFingerprint: z.string().min(1),
11
+ decisions: z.array(z.object({ id: z.string().min(1), decision: z.string().min(1), rationale: z.string().min(1) })),
12
+ acceptanceCriteria: z.array(z.string().min(1)).min(1),
13
+ requiredChecks: z.array(z.string().min(1)),
14
+ evidencePolicy: z.object({ runtimeEvidenceRequired: z.boolean(), independentReviewRequired: z.boolean(), humanApprovalRequired: z.boolean() }),
15
+ createdAt: z.string().datetime()
16
+ });
17
+ export async function createChangeContract(input, root = process.cwd()) {
18
+ const workspace = await createWorkspaceFingerprint(root);
19
+ return ChangeContractSchema.parse({ ...input, schemaVersion: "soturail.change-contract.v1", workspaceFingerprint: workspace.fingerprint, createdAt: new Date().toISOString() });
20
+ }
21
+ export function evaluateReadiness(contract, snapshot) {
22
+ const reasons = [...snapshot.blockers];
23
+ if (contract.workspaceFingerprint !== snapshot.workspaceFingerprint)
24
+ reasons.push("Contract workspace fingerprint is stale.");
25
+ if (snapshot.evidenceFreshness !== "current")
26
+ reasons.push(`Evidence freshness is ${snapshot.evidenceFreshness}.`);
27
+ for (const criterion of contract.acceptanceCriteria)
28
+ if (!snapshot.acceptanceCriteriaPassed.includes(criterion))
29
+ reasons.push(`Acceptance criterion not satisfied: ${criterion}`);
30
+ for (const check of contract.requiredChecks)
31
+ if (!snapshot.checksPassed.includes(check))
32
+ reasons.push(`Required check missing: ${check}`);
33
+ if (contract.evidencePolicy.runtimeEvidenceRequired && !snapshot.runtimeEvidence)
34
+ reasons.push("Runtime evidence is required.");
35
+ if (contract.evidencePolicy.independentReviewRequired && !snapshot.independentReview)
36
+ reasons.push("Independent review is required.");
37
+ if (contract.evidencePolicy.humanApprovalRequired && !snapshot.humanApproval)
38
+ reasons.push("Human approval is required.");
39
+ return { schemaVersion: "soturail.readiness.verdict.v1", verdict: reasons.length === 0 ? "ready" : "not-ready", reasons: reasons.length ? reasons : ["All deterministic readiness requirements passed."], evaluatedAt: new Date().toISOString() };
40
+ }
41
+ export function evaluateDualGate(authority, readiness) {
42
+ const allow = authority.verdict === "allow" && readiness.verdict === "ready";
43
+ return {
44
+ schemaVersion: "soturail.dual-gate.v1",
45
+ authority: authority.verdict,
46
+ readiness: readiness.verdict,
47
+ verdict: allow ? "allow" : "deny",
48
+ reasons: allow ? ["Authority and readiness gates passed."] : [...authority.reasons, ...readiness.reasons]
49
+ };
50
+ }
51
+ export function changeContractDigest(contract) {
52
+ return createHash("sha256").update(canonicalJson(contract)).digest("hex");
53
+ }
54
+ function canonicalJson(value) {
55
+ if (Array.isArray(value))
56
+ return `[${value.map(canonicalJson).join(",")}]`;
57
+ if (value && typeof value === "object")
58
+ return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
59
+ return JSON.stringify(value);
60
+ }
61
+ //# sourceMappingURL=change-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-contract.js","sourceRoot":"","sources":["../../src/core/change-contract.ts"],"names":[],"mappings":"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"}
@@ -19,18 +19,18 @@ const requiredV1Docs = [
19
19
  "README.md",
20
20
  "ROADMAP.md",
21
21
  "CHANGELOG.md",
22
- "docs/quickstart.md",
23
- "docs/v1-contract.md",
24
- "docs/schema-contracts.md",
25
- "docs/stable-command-surface.md",
26
- "docs/deprecation-policy.md",
27
- "docs/migration-v1.md",
28
- "docs/agent-hosts.md",
29
- "docs/host-matrix-schema.md",
30
- "docs/agent-export-contract.md",
31
- "docs/mcp-host-manifest.md",
32
- "docs/clean-code-guidelines.md",
33
- "docs/architecture-boundaries.md"
22
+ "docs/getting-started/quickstart.md",
23
+ "docs/reference/contracts/v1-contract.md",
24
+ "docs/reference/schemas/schema-contracts.md",
25
+ "docs/reference/commands/stable-command-surface.md",
26
+ "docs/reference/contracts/deprecation-policy.md",
27
+ "docs/getting-started/migration-v1.md",
28
+ "docs/rails/hosts/agent-hosts.md",
29
+ "docs/reference/schemas/host-matrix-schema.md",
30
+ "docs/reference/contracts/agent-export-contract.md",
31
+ "docs/rails/hosts/mcp-host-manifest.md",
32
+ "docs/architecture/clean-code-guidelines.md",
33
+ "docs/architecture/architecture-boundaries.md"
34
34
  ];
35
35
  export async function runCodeHealth(root = process.cwd(), options = {}) {
36
36
  const resolvedRoot = path.resolve(root);
@@ -63,6 +63,58 @@ export async function runCodeHealth(root = process.cwd(), options = {}) {
63
63
  output: renderQualityOutput("SotuRail code health", report, resolvedRoot, jsonPath, markdownPath)
64
64
  };
65
65
  }
66
+ async function checkVerifiedControlPlaneInvariants(root) {
67
+ const findings = [];
68
+ // Older/minimal fixtures do not model the verified-control-plane surface. Keep
69
+ // the generic architecture checks compatible while enforcing these invariants
70
+ // whenever the canonical capability registry is present.
71
+ if (!existsSync(path.join(root, "src", "core", "capability-registry.ts")))
72
+ return findings;
73
+ const packageText = await fs.readFile(path.join(root, "package.json"), "utf8").catch(() => "{}");
74
+ const packageJson = JSON.parse(packageText);
75
+ const cargo = await fs.readFile(path.join(root, "native", "soturail-native", "Cargo.toml"), "utf8").catch(() => "");
76
+ const ci = await fs.readFile(path.join(root, ".github", "workflows", "ci.yml"), "utf8").catch(() => "");
77
+ const mcpServer = await fs.readFile(path.join(root, "src", "core", "mcp-server.ts"), "utf8").catch(() => "");
78
+ const mcpTools = await fs.readFile(path.join(root, "src", "core", "mcp-tools.ts"), "utf8").catch(() => "");
79
+ const config = await fs.readFile(path.join(root, "src", "core", "config.ts"), "utf8").catch(() => "");
80
+ const guardedModules = ["src/commands/read.ts", "src/core/mcp-tools.ts", "src/core/knowledge-rail.ts", "src/core/raw-store.ts"];
81
+ const criticalStores = ["src/core/context-pack.ts", "src/core/evidence-provenance.ts", "src/core/knowledge-rail.ts", "src/core/run-workspace.ts"];
82
+ if (!packageJson.dependencies?.["@modelcontextprotocol/server"]?.startsWith("^2") && packageJson.dependencies?.["@modelcontextprotocol/server"] !== "2.0.0") {
83
+ findings.push(finding("mcp_official_sdk", "control_plane_invariant", "blocking", "package.json", undefined, "Official MCP server SDK v2 is required."));
84
+ }
85
+ if (!mcpServer.includes("@modelcontextprotocol/server") || !mcpServer.includes("2026-07-28")) {
86
+ findings.push(finding("mcp_modern_protocol", "control_plane_invariant", "blocking", "src/core/mcp-server.ts", undefined, "MCP must use the official SDK and declare the stable modern protocol revision."));
87
+ }
88
+ if (/allow_raw|allowRaw/.test(mcpTools)) {
89
+ findings.push(finding("mcp_raw_self_authorization", "control_plane_invariant", "blocking", "src/core/mcp-tools.ts", undefined, "MCP must not accept caller-controlled raw disclosure authorization."));
90
+ }
91
+ if (!mcpTools.includes("capabilityId") || !mcpTools.includes("getCapabilityDefinition")) {
92
+ findings.push(finding("mcp_capability_registry", "control_plane_invariant", "blocking", "src/core/mcp-tools.ts", undefined, "MCP tools must map to the canonical capability registry."));
93
+ }
94
+ if (!config.includes('evalsDir: path.resolve(workspace, "evals")')) {
95
+ findings.push(finding("evals_canonical_path", "control_plane_invariant", "blocking", "src/core/config.ts", undefined, "The canonical evaluation artifact directory must remain evals/."));
96
+ }
97
+ const nodeRange = packageJson.engines?.node ?? "";
98
+ if (!/>=22/.test(nodeRange))
99
+ findings.push(finding("node_eol_baseline", "control_plane_invariant", "blocking", "package.json", undefined, "Node baseline must exclude EOL Node 20 and require Node 22 or newer."));
100
+ if (!/node:\s*\[22, 24\]/.test(ci) || /node:\s*\[[^\]]*20/.test(ci))
101
+ findings.push(finding("ci_node_lts", "control_plane_invariant", "blocking", ".github/workflows/ci.yml", undefined, "CI must test maintained Node 22 and 24 LTS baselines without Node 20."));
102
+ const cargoVersion = /^version\s*=\s*"([^"]+)"/m.exec(cargo)?.[1];
103
+ const cargoLicense = /^license\s*=\s*"([^"]+)"/m.exec(cargo)?.[1];
104
+ if (cargoVersion !== packageJson.version || cargoLicense !== "Apache-2.0")
105
+ findings.push(finding("native_metadata_sync", "control_plane_invariant", "blocking", "native/soturail-native/Cargo.toml", undefined, "Native version/license must match the package release and Apache-2.0 repository license."));
106
+ for (const relative of guardedModules) {
107
+ const text = await fs.readFile(path.join(root, relative), "utf8").catch(() => "");
108
+ if (!text.includes("WorkspaceGuard"))
109
+ findings.push(finding("workspace_guard_missing", "control_plane_invariant", "blocking", relative, undefined, "Caller-controlled filesystem paths must pass through WorkspaceGuard."));
110
+ }
111
+ for (const relative of criticalStores) {
112
+ const text = await fs.readFile(path.join(root, relative), "utf8").catch(() => "");
113
+ if (!text.includes("artifactStore") && !text.includes("writeJson"))
114
+ findings.push(finding("artifact_store_missing", "control_plane_invariant", "blocking", relative, undefined, "Critical artifacts must use the atomic ArtifactStore path."));
115
+ }
116
+ return findings;
117
+ }
66
118
  export async function runArchitectureCheck(root = process.cwd(), options = {}) {
67
119
  const resolvedRoot = path.resolve(root);
68
120
  const files = await listFiles(resolvedRoot, ["src"], [".ts"]);
@@ -78,6 +130,7 @@ export async function runArchitectureCheck(root = process.cwd(), options = {}) {
78
130
  findings.push(finding("command_business_logic", "architecture_boundary", "warning", relative, 1, "Command file is large; keep CLI parsing in commands/ and move domain logic to core/."));
79
131
  }
80
132
  }
133
+ findings.push(...await checkVerifiedControlPlaneInvariants(resolvedRoot));
81
134
  const report = makeArchitectureReport(files.length, findings, options.strict === true);
82
135
  const dir = path.join(getWorkspacePaths(resolvedRoot).workspace, "architecture");
83
136
  const jsonPath = path.join(dir, "check.json");
@@ -177,12 +230,12 @@ async function checkScopeContamination(root) {
177
230
  const files = [
178
231
  "README.md",
179
232
  "ROADMAP.md",
180
- "docs/quickstart.md",
181
- "docs/v1-contract.md",
182
- "docs/schema-contracts.md",
183
- "docs/stable-command-surface.md",
184
- "docs/migration-v1.md",
185
- "docs/future-rails-index.md"
233
+ "docs/getting-started/quickstart.md",
234
+ "docs/reference/contracts/v1-contract.md",
235
+ "docs/reference/schemas/schema-contracts.md",
236
+ "docs/reference/commands/stable-command-surface.md",
237
+ "docs/getting-started/migration-v1.md",
238
+ "docs/roadmap/future-rails-index.md"
186
239
  ];
187
240
  const terms = /\b(SoturAI|trading|finance|backtest)\b/i;
188
241
  const findings = [];
@@ -232,7 +285,7 @@ function makeArchitectureReport(filesScanned, findings, strict) {
232
285
  nextCommands: [
233
286
  "soturail self architecture --check --json",
234
287
  "soturail self code-health",
235
- "docs/architecture-boundaries.md"
288
+ "docs/architecture/architecture-boundaries.md"
236
289
  ]
237
290
  };
238
291
  }