@mnstry/atelier 0.2.0-alpha.1

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 (260) hide show
  1. package/CHANGELOG.md +383 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +5 -0
  4. package/README.md +262 -0
  5. package/SECURITY.md +93 -0
  6. package/TRADEMARKS.md +88 -0
  7. package/announcements/2026-08-06-announcements-channel.v1.json +12 -0
  8. package/announcements/keys/mnstry-announcements.public.v1.json +9 -0
  9. package/bin/atelier.mjs +2 -0
  10. package/bin/mnstry-atelier.mjs +3 -0
  11. package/contracts/analysis-adapter.v1.schema.json +248 -0
  12. package/contracts/atelier-action-intent.v1.schema.json +143 -0
  13. package/contracts/atelier-analysis-adapter.v1.schema.json +360 -0
  14. package/contracts/atelier-attestation.v1.schema.json +239 -0
  15. package/contracts/atelier-boundary-policy.v1.schema.json +339 -0
  16. package/contracts/atelier-claim.v1.schema.json +97 -0
  17. package/contracts/atelier-export.v1.schema.json +1015 -0
  18. package/contracts/atelier-extension-pack.v1.schema.json +290 -0
  19. package/contracts/atelier-kit-manifest.v1.schema.json +273 -0
  20. package/contracts/atelier-lock.v1.schema.json +364 -0
  21. package/contracts/atelier-migration.v1.schema.json +162 -0
  22. package/contracts/atelier-project-config.v1.schema.json +352 -0
  23. package/contracts/atelier-readiness-protocol.v1.schema.json +517 -0
  24. package/contracts/atelier-readiness-run.v1.schema.json +229 -0
  25. package/contracts/atelier-readiness.v1.schema.json +457 -0
  26. package/contracts/compat-baseline.json +10 -0
  27. package/contracts/git-promote-event.v1.schema.json +145 -0
  28. package/contracts/knowledge-source-sidecar.v1.schema.json +157 -0
  29. package/contracts/mnstry-atelier-semantic-profile.v1.json +254 -0
  30. package/docs/announcements.md +101 -0
  31. package/docs/atelier-runtime.md +34 -0
  32. package/docs/attestation.md +201 -0
  33. package/docs/continuity.md +55 -0
  34. package/docs/contract-stability.md +111 -0
  35. package/docs/distributions.md +220 -0
  36. package/docs/install.md +140 -0
  37. package/docs/knowledge-graph.md +140 -0
  38. package/docs/ontology.md +206 -0
  39. package/docs/release-engineering.md +151 -0
  40. package/docs/repo-boundary-guard.md +199 -0
  41. package/docs/tenant-readiness.md +64 -0
  42. package/docs/upgrade.md +124 -0
  43. package/fixtures/analysis/invalid/native-graph.v1.json +4 -0
  44. package/fixtures/analysis/invalid/promoted-claim.v1.json +17 -0
  45. package/fixtures/analysis/invalid/runtime-import.v1.json +20 -0
  46. package/fixtures/analysis/valid/output.claims.v1.json +17 -0
  47. package/fixtures/analysis-adapter/invalid/manifest-hidden-provider.v1.json +24 -0
  48. package/fixtures/analysis-adapter/invalid/output-frontmatter-mutation.v1.json +21 -0
  49. package/fixtures/analysis-adapter/invalid/output-native-graph.v1.json +14 -0
  50. package/fixtures/analysis-adapter/manifest.disabled.v1.json +22 -0
  51. package/fixtures/analysis-adapter/manifest.enabled-local.v1.json +23 -0
  52. package/fixtures/analysis-adapter/output.claims.v1.json +21 -0
  53. package/fixtures/atelier-action-intent/invalid/runtime-mutation-authority.v1.json +16 -0
  54. package/fixtures/atelier-action-intent/invalid/unknown-top-level-field.v1.json +17 -0
  55. package/fixtures/atelier-action-intent/invalid/writes-beyond-clipboard.v1.json +16 -0
  56. package/fixtures/atelier-action-intent/valid/copy-agent-prompt.v1.json +16 -0
  57. package/fixtures/atelier-action-intent/valid/copy-repo-path.v1.json +22 -0
  58. package/fixtures/atelier-analysis-adapter/invalid/hidden-model-provider.v1.json +24 -0
  59. package/fixtures/atelier-analysis-adapter/invalid/inert-with-execute-command.v1.json +24 -0
  60. package/fixtures/atelier-analysis-adapter/invalid/proposals-with-runtime-import.v1.json +30 -0
  61. package/fixtures/atelier-analysis-adapter/valid/manifest-inert.v1.json +24 -0
  62. package/fixtures/atelier-analysis-adapter/valid/proposals-claim-only.v1.json +27 -0
  63. package/fixtures/atelier-attestation/invalid/bad-digest.v1.json +23 -0
  64. package/fixtures/atelier-attestation/invalid/conformance-scope.v1.json +23 -0
  65. package/fixtures/atelier-attestation/invalid/missing-signature.v1.json +22 -0
  66. package/fixtures/atelier-attestation/invalid/unknown-root-field.v1.json +24 -0
  67. package/fixtures/atelier-attestation/keys/roundtrip-issuer.public.v1.json +9 -0
  68. package/fixtures/atelier-attestation/valid/rejected-with-reasons.v1.json +49 -0
  69. package/fixtures/atelier-attestation/valid/signed-admitted.v1.json +35 -0
  70. package/fixtures/atelier-attestation/valid/signed-roundtrip.v1.json +29 -0
  71. package/fixtures/atelier-attestation/valid/unsigned-advisory.v1.json +23 -0
  72. package/fixtures/atelier-claim/invalid/unknown-provider.v1.json +15 -0
  73. package/fixtures/atelier-claim/invalid/unknown-top-level-field.v1.json +16 -0
  74. package/fixtures/atelier-claim/invalid/wrong-schema-const.v1.json +15 -0
  75. package/fixtures/atelier-claim/valid/adapter-style-proposed-relation.v1.json +19 -0
  76. package/fixtures/atelier-claim/valid/codex-proposed-relation.v1.json +16 -0
  77. package/fixtures/atelier-claim/valid/manual-proposed-relation.v1.json +16 -0
  78. package/fixtures/atelier-claim/valid/readiness-proposed-relation.v1.json +18 -0
  79. package/fixtures/atelier-export/invalid/dirty-unforced-empty-taint.v1.json +19 -0
  80. package/fixtures/atelier-export/invalid/object-local-audience-visibility.v1.json +11 -0
  81. package/fixtures/atelier-export/invalid/open-object-class-owner.v1.json +23 -0
  82. package/fixtures/atelier-export/invalid/pricing-legacy-amount.v1.json +23 -0
  83. package/fixtures/atelier-export/invalid/public-disguised-internal-source-ref.v1.json +11 -0
  84. package/fixtures/atelier-export/invalid/public-nested-private-source-ref.v1.json +17 -0
  85. package/fixtures/atelier-export/invalid/public-resolved-private-source-ref.v1.json +13 -0
  86. package/fixtures/atelier-export/invalid/public-resolved-sensitive-source-ref.v1.json +13 -0
  87. package/fixtures/atelier-export/invalid/public-source-ref-missing-provenance.v1.json +20 -0
  88. package/fixtures/atelier-export/invalid/public-source-team-kg.json +12 -0
  89. package/fixtures/atelier-export/invalid/source-node-legacy-visibility.v1.json +21 -0
  90. package/fixtures/atelier-export/invalid/source-node-unknown-field.v1.json +18 -0
  91. package/fixtures/atelier-export/invalid/unresolved-public-source-ref.v1.json +11 -0
  92. package/fixtures/atelier-export/sample-studio-offer.v1.json +397 -0
  93. package/fixtures/atelier-extension-pack/invalid/bundled-slug-collision.v1.json +22 -0
  94. package/fixtures/atelier-extension-pack/invalid/escapes-root.v1.json +22 -0
  95. package/fixtures/atelier-extension-pack/invalid/posture-tampered.v1.json +22 -0
  96. package/fixtures/atelier-extension-pack/invalid/protocols/autonomous-authority.v1.json +118 -0
  97. package/fixtures/atelier-extension-pack/invalid/protocols/offer-map.v1.json +109 -0
  98. package/fixtures/atelier-extension-pack/invalid/redefines-runtime-term.v1.json +19 -0
  99. package/fixtures/atelier-extension-pack/invalid/wrong-namespace.v1.json +19 -0
  100. package/fixtures/atelier-extension-pack/valid/protocols/contract-gate.v1.json +109 -0
  101. package/fixtures/atelier-extension-pack/valid/sample-pack.v1.json +48 -0
  102. package/fixtures/atelier-kit-manifest/invalid/runtime-copy-without-canonical-role.v1.json +53 -0
  103. package/fixtures/atelier-kit-manifest/invalid/telemetry-send-path.v1.json +21 -0
  104. package/fixtures/atelier-kit-manifest/valid/mnstry-atelier.valid.v1.json +76 -0
  105. package/fixtures/atelier-lock/invalid/absolute-contract-path.v1.json +40 -0
  106. package/fixtures/atelier-lock/invalid/runtime-mutation-enabled.v1.json +41 -0
  107. package/fixtures/atelier-lock/invalid/unknown-top-level-field.v1.json +42 -0
  108. package/fixtures/atelier-lock/valid/extension-pack.v1.json +55 -0
  109. package/fixtures/atelier-lock/valid/minimal.v1.json +44 -0
  110. package/fixtures/atelier-migration/invalid/live-apply-mode.v1.json +21 -0
  111. package/fixtures/atelier-migration/invalid/missing-review-evidence.v1.json +20 -0
  112. package/fixtures/atelier-migration/invalid/runtime-mutation-enabled.v1.json +21 -0
  113. package/fixtures/atelier-migration/valid/minimal.v1.json +32 -0
  114. package/fixtures/atelier-project-config/invalid/absolute-repo-path.v1.json +11 -0
  115. package/fixtures/atelier-project-config/invalid/ext-not-object.v1.json +12 -0
  116. package/fixtures/atelier-project-config/invalid/external-kind-read-boundary.v1.json +12 -0
  117. package/fixtures/atelier-project-config/invalid/missing-repos-and-alignment.v1.json +7 -0
  118. package/fixtures/atelier-project-config/invalid/unknown-top-level-field.v1.json +14 -0
  119. package/fixtures/atelier-project-config/valid/alignment-scaffold.v1.json +15 -0
  120. package/fixtures/atelier-project-config/valid/ext-extension-packs.v1.json +36 -0
  121. package/fixtures/atelier-project-config/valid/repo-workspace.v1.json +23 -0
  122. package/fixtures/atelier-project-config/validator-only/ext-member-not-object.v1.json +14 -0
  123. package/fixtures/atelier-readiness/invalid/dry-run-only-false.v1.json +87 -0
  124. package/fixtures/atelier-readiness/invalid/support-bundle-send-path.v1.json +87 -0
  125. package/fixtures/atelier-readiness/invalid/unknown-top-level-field.v1.json +88 -0
  126. package/fixtures/atelier-readiness/valid/ready-workspace.v1.json +84 -0
  127. package/fixtures/boundary-policy/invalid/content-rule-exception-blanket-path.v1.json +67 -0
  128. package/fixtures/boundary-policy/invalid/invalid-actor-private-repo-mapping.v1.json +20 -0
  129. package/fixtures/boundary-policy/invalid/legacy-warning-missing-warning.v1.json +20 -0
  130. package/fixtures/boundary-policy/invalid/missing-repo-coverage.v1.json +19 -0
  131. package/fixtures/boundary-policy/invalid/unknown-key.v1.json +12 -0
  132. package/fixtures/boundary-policy/valid/content-rule-exception.v1.json +67 -0
  133. package/fixtures/boundary-policy/valid/legacy-warning.v1.json +33 -0
  134. package/fixtures/boundary-policy/valid/strict-private-shared.v1.json +40 -0
  135. package/fixtures/git-promote/invalid/provision-named-revocable.v1.json +29 -0
  136. package/fixtures/git-promote/private-to-team.v1.json +29 -0
  137. package/fixtures/knowledge-source-sidecar/invalid/legacy-visibility-field.v1.json +17 -0
  138. package/fixtures/knowledge-source-sidecar/invalid/missing-audience.v1.json +15 -0
  139. package/fixtures/knowledge-source-sidecar/invalid/unknown-relation-kind.v1.json +18 -0
  140. package/fixtures/knowledge-source-sidecar/valid/html-source.v1.json +18 -0
  141. package/fixtures/knowledge-source-sidecar/valid/pdf-evidence.v1.json +18 -0
  142. package/fixtures/projects/neutral-project/atelier.project.json +15 -0
  143. package/fixtures/projects/neutral-project/ops/config/repo-access.v1.json +12 -0
  144. package/fixtures/projects/neutral-project/project-knowledge.graph.json +69 -0
  145. package/fixtures/projects/sample-workspace/atelier.project.json +23 -0
  146. package/fixtures/projects/sample-workspace/boundary-policy.v1.json +59 -0
  147. package/fixtures/projects/sample-workspace/content/README.md +16 -0
  148. package/fixtures/projects/sample-workspace/content/source.html +5 -0
  149. package/fixtures/projects/sample-workspace/content/source.html.kg.json +15 -0
  150. package/fixtures/projects/sample-workspace/repo-access.v1.json +9 -0
  151. package/fixtures/projects/source-formats-workspace/atelier.project.json +23 -0
  152. package/fixtures/projects/source-formats-workspace/content/README.md +17 -0
  153. package/fixtures/projects/source-formats-workspace/content/data.json +7 -0
  154. package/fixtures/projects/source-formats-workspace/content/data.json.kg.json +15 -0
  155. package/fixtures/projects/source-formats-workspace/content/logo.png.kg.json +15 -0
  156. package/fixtures/projects/source-formats-workspace/content/metrics.csv +3 -0
  157. package/fixtures/projects/source-formats-workspace/content/metrics.csv.kg.json +15 -0
  158. package/fixtures/projects/source-formats-workspace/content/pipeline.yaml +4 -0
  159. package/fixtures/projects/source-formats-workspace/content/pipeline.yaml.kg.json +15 -0
  160. package/fixtures/projects/source-formats-workspace/repo-access.v1.json +9 -0
  161. package/fixtures/readiness-protocols/extension-pack/invalid/protocol-missing-path.v1.json +20 -0
  162. package/fixtures/readiness-protocols/extension-pack/valid/with-protocol.v1.json +24 -0
  163. package/fixtures/readiness-protocols/protocol/invalid/missing-check-target.v1.json +103 -0
  164. package/fixtures/readiness-protocols/protocol/invalid/runtime-mutation-enabled.v1.json +107 -0
  165. package/fixtures/readiness-protocols/protocol/valid/minimal.v1.json +100 -0
  166. package/fixtures/readiness-protocols/run/invalid/missing-evidence.v1.json +37 -0
  167. package/fixtures/readiness-protocols/run/invalid/pass-with-blocker.v1.json +22 -0
  168. package/fixtures/readiness-protocols/run/invalid/runtime-mutation-enabled.v1.json +22 -0
  169. package/fixtures/readiness-protocols/run/valid/pass.v1.json +44 -0
  170. package/fixtures/support/invalid/leaky-support.v1.json +13 -0
  171. package/fixtures/support/valid/minimal-safe.v1.json +20 -0
  172. package/package.json +98 -0
  173. package/skills/claude/atelier-open/SKILL.md +68 -0
  174. package/skills/claude/mnstry-readiness/SKILL.md +41 -0
  175. package/skills/codex/atelier-open/SKILL.md +68 -0
  176. package/skills/codex/mnstry-readiness/SKILL.md +41 -0
  177. package/src/analysis/adapter.mjs +20 -0
  178. package/src/analysis/analysis-claim-contract.mjs +335 -0
  179. package/src/attestation/jcs.mjs +64 -0
  180. package/src/attestation/sign.mjs +305 -0
  181. package/src/boundary/content-rules.mjs +262 -0
  182. package/src/boundary/policy.mjs +806 -0
  183. package/src/check-atelier-export-contract.mjs +116 -0
  184. package/src/cli/run.mjs +283 -0
  185. package/src/collaboration/proposals.mjs +262 -0
  186. package/src/commands/analysis.mjs +8 -0
  187. package/src/commands/announcements.mjs +308 -0
  188. package/src/commands/attestation.mjs +198 -0
  189. package/src/commands/boundary.mjs +25 -0
  190. package/src/commands/config.mjs +4 -0
  191. package/src/commands/context.mjs +3 -0
  192. package/src/commands/distribution.mjs +224 -0
  193. package/src/commands/egress.mjs +11 -0
  194. package/src/commands/extension-pack.mjs +209 -0
  195. package/src/commands/feedback.mjs +3 -0
  196. package/src/commands/graph.mjs +3 -0
  197. package/src/commands/init.mjs +130 -0
  198. package/src/commands/lock.mjs +3 -0
  199. package/src/commands/project.mjs +3 -0
  200. package/src/commands/promote.mjs +4 -0
  201. package/src/commands/readiness.mjs +3 -0
  202. package/src/commands/server.mjs +6 -0
  203. package/src/commands/setup.mjs +250 -0
  204. package/src/commands/support.mjs +3 -0
  205. package/src/commands/upgrade.mjs +3 -0
  206. package/src/contracts/corpus.mjs +171 -0
  207. package/src/egress/check.mjs +45 -0
  208. package/src/egress/forbidden-egress.mjs +323 -0
  209. package/src/export/atelier-export-contract.mjs +722 -0
  210. package/src/extension-packs/loader.mjs +491 -0
  211. package/src/graph/graph.mjs +370 -0
  212. package/src/graph/knowledge-graph.mjs +887 -0
  213. package/src/harness/context-client.mjs +144 -0
  214. package/src/harness/context.mjs +27 -0
  215. package/src/index.mjs +88 -0
  216. package/src/project/config.mjs +557 -0
  217. package/src/project/file-class.mjs +113 -0
  218. package/src/project/git-ignore.mjs +31 -0
  219. package/src/project/path-match.mjs +30 -0
  220. package/src/project/repo-identity.mjs +176 -0
  221. package/src/projection/alignment-projection.mjs +169 -0
  222. package/src/projection/policy.mjs +90 -0
  223. package/src/projection/project.mjs +152 -0
  224. package/src/readiness/readiness.mjs +312 -0
  225. package/src/readiness-protocols/bundled-pack.mjs +766 -0
  226. package/src/readiness-protocols/runtime.mjs +419 -0
  227. package/src/server/local-sidecar.mjs +640 -0
  228. package/src/server/security.mjs +229 -0
  229. package/src/server/server.mjs +42 -0
  230. package/src/support/feedback-report.mjs +414 -0
  231. package/src/support/support-bundle.mjs +226 -0
  232. package/src/ui/html-primitives.mjs +104 -0
  233. package/src/upgrade/upgrade.mjs +701 -0
  234. package/src/validate-atelier-export-dry-run.mjs +75 -0
  235. package/templates/atelier.project.example.json +23 -0
  236. package/templates/distribution-workspace/.claude/launch.json +12 -0
  237. package/templates/distribution-workspace/README.md +86 -0
  238. package/templates/distribution-workspace/atelier.project.json +36 -0
  239. package/templates/distribution-workspace/boundary-policy.v1.json +61 -0
  240. package/templates/distribution-workspace/gitignore +29 -0
  241. package/templates/distribution-workspace/governance/repo-boundary-ledger.md +5 -0
  242. package/templates/distribution-workspace/project/README.md +19 -0
  243. package/templates/distribution-workspace/repo-access.v1.json +9 -0
  244. package/templates/launch.json +12 -0
  245. package/templates/private-domain-workspace/.claude/launch.json +12 -0
  246. package/templates/private-domain-workspace/README.md +50 -0
  247. package/templates/private-domain-workspace/atelier.project.json +28 -0
  248. package/templates/private-domain-workspace/boundary-policy.v1.json +54 -0
  249. package/templates/private-domain-workspace/domain/README.md +19 -0
  250. package/templates/private-domain-workspace/gitignore +29 -0
  251. package/templates/private-domain-workspace/governance/repo-boundary-ledger.md +4 -0
  252. package/templates/private-domain-workspace/repo-access.v1.json +9 -0
  253. package/templates/shared-project-workspace/.claude/launch.json +12 -0
  254. package/templates/shared-project-workspace/README.md +51 -0
  255. package/templates/shared-project-workspace/atelier.project.json +28 -0
  256. package/templates/shared-project-workspace/boundary-policy.v1.json +61 -0
  257. package/templates/shared-project-workspace/gitignore +29 -0
  258. package/templates/shared-project-workspace/governance/repo-boundary-ledger.md +5 -0
  259. package/templates/shared-project-workspace/project/README.md +19 -0
  260. package/templates/shared-project-workspace/repo-access.v1.json +9 -0
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@mnstry/atelier",
3
+ "version": "0.2.0-alpha.1",
4
+ "private": false,
5
+ "description": "Local-first MNSTRY Atelier workspace for agentic authoring, graph projection, and dry-run runtime readiness.",
6
+ "type": "module",
7
+ "bin": {
8
+ "atelier": "bin/atelier.mjs",
9
+ "mnstry-atelier": "bin/mnstry-atelier.mjs"
10
+ },
11
+ "exports": {
12
+ ".": "./src/index.mjs",
13
+ "./cli": "./src/cli/run.mjs",
14
+ "./project": "./src/project/config.mjs",
15
+ "./graph": "./src/graph/graph.mjs",
16
+ "./projection": "./src/projection/project.mjs",
17
+ "./readiness": "./src/readiness/readiness.mjs",
18
+ "./readiness-protocols": "./src/readiness-protocols/bundled-pack.mjs",
19
+ "./server": "./src/server/local-sidecar.mjs",
20
+ "./harness": "./src/harness/context.mjs",
21
+ "./support": "./src/support/support-bundle.mjs",
22
+ "./egress": "./src/egress/forbidden-egress.mjs",
23
+ "./boundary": "./src/boundary/policy.mjs",
24
+ "./upgrade": "./src/upgrade/upgrade.mjs",
25
+ "./attestation": "./src/attestation/sign.mjs",
26
+ "./extension-packs": "./src/extension-packs/loader.mjs",
27
+ "./analysis/adapter": "./src/analysis/adapter.mjs",
28
+ "./export-contract": "./src/export/atelier-export-contract.mjs",
29
+ "./dry-run": "./src/validate-atelier-export-dry-run.mjs",
30
+ "./contracts/atelier-export.v1.schema.json": "./contracts/atelier-export.v1.schema.json",
31
+ "./fixtures/atelier-export/sample-studio-offer.v1.json": "./fixtures/atelier-export/sample-studio-offer.v1.json"
32
+ },
33
+ "scripts": {
34
+ "contract": "node src/check-atelier-export-contract.mjs",
35
+ "dry-run": "node src/validate-atelier-export-dry-run.mjs fixtures/atelier-export/sample-studio-offer.v1.json",
36
+ "test": "node --test test/*.test.mjs",
37
+ "syntax:check": "find bin src -name '*.mjs' -print0 | xargs -0 -n1 node --check",
38
+ "graph:test": "node --test test/graph.test.mjs test/graph-knowledge-graph.test.mjs test/graph-determinism.test.mjs test/graph-source-formats.test.mjs",
39
+ "project:test": "node --test test/project.test.mjs test/project-config.test.mjs test/projection-policy.test.mjs test/projection-alignment.test.mjs",
40
+ "server:security:test": "node --test test/server.test.mjs test/server-security.test.mjs test/server-proposals.test.mjs",
41
+ "browser:smoke": "node --test test/server.test.mjs",
42
+ "support-bundle:test": "node --test test/support-bundle.test.mjs test/support-egress-analysis.test.mjs",
43
+ "egress:check": "node bin/atelier.mjs egress check",
44
+ "readiness:test": "node --test test/readiness.test.mjs test/readiness-summary.test.mjs test/readiness-protocol-contract.test.mjs test/readiness-protocols.test.mjs test/readiness-protocol-runtime.test.mjs",
45
+ "harness:test": "node --test test/harness.test.mjs test/harness-context.test.mjs",
46
+ "boundary:test": "node --test test/boundary-policy-contract.test.mjs test/boundary-guard.test.mjs",
47
+ "upgrade:test": "node --test test/upgrade-contract.test.mjs test/upgrade.test.mjs",
48
+ "release:audit": "node scripts/check-release-tarball.mjs",
49
+ "repo:check": "node scripts/check-repo-disclosure.mjs",
50
+ "migrations:check": "node scripts/check-breaking-migrations.mjs",
51
+ "contract:compat": "node scripts/check-contract-compat.mjs",
52
+ "consumer:smoke": "node scripts/consumer-smoke.mjs",
53
+ "distribution:smoke": "node scripts/distribution-smoke.mjs",
54
+ "prepublishOnly": "npm run release:audit && npm run repo:check && npm run migrations:check && npm run contract:compat && npm run syntax:check && npm test && npm run consumer:smoke && npm run distribution:smoke"
55
+ },
56
+ "dependencies": {
57
+ "ajv": "8.20.0",
58
+ "ajv-formats": "3.0.1"
59
+ },
60
+ "devDependencies": {
61
+ "@types/node": "^22.0.0"
62
+ },
63
+ "license": "Apache-2.0",
64
+ "repository": {
65
+ "type": "git",
66
+ "url": "git+https://github.com/MNSTRY/atelier.git"
67
+ },
68
+ "homepage": "https://mnstry.org",
69
+ "bugs": {
70
+ "url": "https://github.com/MNSTRY/atelier/issues"
71
+ },
72
+ "engines": {
73
+ "node": ">=22.18.0 <23"
74
+ },
75
+ "files": [
76
+ "README.md",
77
+ "CHANGELOG.md",
78
+ "LICENSE",
79
+ "NOTICE",
80
+ "TRADEMARKS.md",
81
+ "SECURITY.md",
82
+ "bin/",
83
+ "contracts/",
84
+ "fixtures/",
85
+ "skills/",
86
+ "src/",
87
+ "templates/",
88
+ "docs/",
89
+ "announcements/",
90
+ "!fixtures/projects/source-formats-workspace/content/logo.png"
91
+ ],
92
+ "publishConfig": {
93
+ "access": "public"
94
+ },
95
+ "overrides": {
96
+ "fast-uri": "3.1.5"
97
+ }
98
+ }
@@ -0,0 +1,68 @@
1
+ # Open the Atelier
2
+
3
+ Use this skill when someone says "open the atelier", "pull up the atelier",
4
+ "show me the portal", or asks what page they are currently viewing.
5
+
6
+ "Open" means **the client's native preview surface**, not the system browser.
7
+ An Atelier is a local sidecar serving a workspace projection; the point is to
8
+ render it where the person is already working.
9
+
10
+ ## Route by client
11
+
12
+ | Client | How to open |
13
+ |---|---|
14
+ | Claude Code / Cowork | `preview_start` with the `atelier` launch config, then screenshot |
15
+ | Codex | the harness preview pane if present, otherwise report the URL |
16
+ | Editor with no preview pane | print the URL; do not shell out to `open` |
17
+
18
+ Never use the system `open` command and never fetch the page and describe it —
19
+ that answers a different question than the one being asked.
20
+
21
+ ## Starting it
22
+
23
+ The workspace ships `.claude/launch.json` with `autoPort: true`. Start it by
24
+ name (`atelier`) and use the port the harness returns. The equivalent manual
25
+ command is:
26
+
27
+ ```
28
+ ./node_modules/.bin/atelier server
29
+ ```
30
+
31
+ Use the local binary path. Do **not** run `npx atelier` — this package is not
32
+ yet published to npm, and the unscoped name `atelier` belongs to an unrelated
33
+ third-party package that `npx` would download and execute.
34
+
35
+ `atelier server` resolves its port as **argv > `PORT` env > 8137**, so a
36
+ supervisor can hand it a free port without a config edit.
37
+
38
+ ## Do not fight for the canonical port
39
+
40
+ A workspace often keeps a long-lived instance on 8137 for humans and
41
+ cross-service links, frequently under a supervisor that respawns it. A preview
42
+ instance must coexist on its own port rather than evict it:
43
+
44
+ - Keep `autoPort: true`. Never pin a preview instance to the canonical port.
45
+ - "Port 8137 in use by a non-preview server" is the supervisor working, not an
46
+ error to clear. Start on the assigned port instead.
47
+ - Never kill the canonical instance to pick up new code without asking — on
48
+ most setups it is respawned by a supervisor whose rules you may not own.
49
+
50
+ ## Boot time is not hang time
51
+
52
+ An Atelier may build or refresh its projection at startup. While it does, the
53
+ port is not yet open and browser calls fail with connection or policy errors
54
+ that read like permission problems. Poll the port or `/api/health` rather than
55
+ retrying the browser, and give a cold workspace room before declaring it broken.
56
+
57
+ ## Which view to open
58
+
59
+ - Working inside one repo → that repo's portal, `/<repo>/index.html`
60
+ - Workspace-level work, or "everything" → `/`
61
+ - A repo's file audit → `/<repo>/atelier-ledger.html`
62
+
63
+ ## What is the user looking at?
64
+
65
+ Prefer the session-aware context flow over any global "current view" file. A
66
+ recorded current view is a hint, not authority: trust it only when a
67
+ session-bound API response ties the view to the same workspace and a fresh
68
+ observation. Otherwise ask for an explicit target rather than guessing.
@@ -0,0 +1,41 @@
1
+ # MNSTRY Readiness
2
+
3
+ Use this skill when preparing or reviewing MNSTRY Atelier readiness outputs for
4
+ a tenant or project workspace.
5
+
6
+ ## Protocol
7
+
8
+ - Use the repository's Atelier readiness protocols: `docs/tenant-readiness.md`,
9
+ `docs/atelier-runtime.md`, `contracts/atelier-readiness.v1.schema.json`, and
10
+ `contracts/atelier-claim.v1.schema.json`.
11
+ - Treat local source, Git state, project config, repo access, boundary policy,
12
+ and lockfiles as review authority.
13
+ - Treat generated graph, projection, and readiness output as evidence from the
14
+ current package checks, not source authority.
15
+ - Treat model/provider output as proposed `atelier-claim@v1` records only.
16
+ - Keep runtime identity, consent, visibility, provisioning, bookings, commerce,
17
+ sessions, audit, and client-grade sharing under MNSTRY runtime authority.
18
+
19
+ ## Output Shape
20
+
21
+ Produce claim-first outputs:
22
+
23
+ - Claim: the readiness statement being made.
24
+ - Evidence: file paths, schema names, commands run, and observed behavior.
25
+ - Authority: source, generated evidence, proposed claim, or runtime-owned.
26
+ - Status: ready, warning, blocked, or proposed.
27
+ - Fix path: the smallest next reviewable change.
28
+
29
+ Do not introduce independent tenant semantics, new visibility terms, hidden
30
+ provider boundaries, or runtime mutation claims. If a check cannot run, report
31
+ the exact command, stderr, and blocker.
32
+
33
+ ## Safety And Hygiene
34
+
35
+ - Keep package-facing output generic and placeholder-only.
36
+ - Do not include person-specific, tenant-private, project-private, transcript,
37
+ support-bundle, local absolute path, agent-local state, key, token, or
38
+ credential material.
39
+ - Prefer static inspection and existing defensive tests for security-sensitive
40
+ review work.
41
+ - Frame checks as verifying that a control refuses, blocks, or fails closed.
@@ -0,0 +1,68 @@
1
+ # Open the Atelier
2
+
3
+ Use this skill when someone says "open the atelier", "pull up the atelier",
4
+ "show me the portal", or asks what page they are currently viewing.
5
+
6
+ "Open" means **the client's native preview surface**, not the system browser.
7
+ An Atelier is a local sidecar serving a workspace projection; the point is to
8
+ render it where the person is already working.
9
+
10
+ ## Route by client
11
+
12
+ | Client | How to open |
13
+ |---|---|
14
+ | Claude Code / Cowork | `preview_start` with the `atelier` launch config, then screenshot |
15
+ | Codex | the harness preview pane if present, otherwise report the URL |
16
+ | Editor with no preview pane | print the URL; do not shell out to `open` |
17
+
18
+ Never use the system `open` command and never fetch the page and describe it —
19
+ that answers a different question than the one being asked.
20
+
21
+ ## Starting it
22
+
23
+ The workspace ships `.claude/launch.json` with `autoPort: true`. Start it by
24
+ name (`atelier`) and use the port the harness returns. The equivalent manual
25
+ command is:
26
+
27
+ ```
28
+ ./node_modules/.bin/atelier server
29
+ ```
30
+
31
+ Use the local binary path. Do **not** run `npx atelier` — this package is not
32
+ yet published to npm, and the unscoped name `atelier` belongs to an unrelated
33
+ third-party package that `npx` would download and execute.
34
+
35
+ `atelier server` resolves its port as **argv > `PORT` env > 8137**, so a
36
+ supervisor can hand it a free port without a config edit.
37
+
38
+ ## Do not fight for the canonical port
39
+
40
+ A workspace often keeps a long-lived instance on 8137 for humans and
41
+ cross-service links, frequently under a supervisor that respawns it. A preview
42
+ instance must coexist on its own port rather than evict it:
43
+
44
+ - Keep `autoPort: true`. Never pin a preview instance to the canonical port.
45
+ - "Port 8137 in use by a non-preview server" is the supervisor working, not an
46
+ error to clear. Start on the assigned port instead.
47
+ - Never kill the canonical instance to pick up new code without asking — on
48
+ most setups it is respawned by a supervisor whose rules you may not own.
49
+
50
+ ## Boot time is not hang time
51
+
52
+ An Atelier may build or refresh its projection at startup. While it does, the
53
+ port is not yet open and browser calls fail with connection or policy errors
54
+ that read like permission problems. Poll the port or `/api/health` rather than
55
+ retrying the browser, and give a cold workspace room before declaring it broken.
56
+
57
+ ## Which view to open
58
+
59
+ - Working inside one repo → that repo's portal, `/<repo>/index.html`
60
+ - Workspace-level work, or "everything" → `/`
61
+ - A repo's file audit → `/<repo>/atelier-ledger.html`
62
+
63
+ ## What is the user looking at?
64
+
65
+ Prefer the session-aware context flow over any global "current view" file. A
66
+ recorded current view is a hint, not authority: trust it only when a
67
+ session-bound API response ties the view to the same workspace and a fresh
68
+ observation. Otherwise ask for an explicit target rather than guessing.
@@ -0,0 +1,41 @@
1
+ # MNSTRY Readiness
2
+
3
+ Use this skill when preparing or reviewing MNSTRY Atelier readiness outputs for
4
+ a tenant or project workspace.
5
+
6
+ ## Protocol
7
+
8
+ - Use the repository's Atelier readiness protocols: `docs/tenant-readiness.md`,
9
+ `docs/atelier-runtime.md`, `contracts/atelier-readiness.v1.schema.json`, and
10
+ `contracts/atelier-claim.v1.schema.json`.
11
+ - Treat local source, Git state, project config, repo access, boundary policy,
12
+ and lockfiles as review authority.
13
+ - Treat generated graph, projection, and readiness output as evidence from the
14
+ current package checks, not source authority.
15
+ - Treat model/provider output as proposed `atelier-claim@v1` records only.
16
+ - Keep runtime identity, consent, visibility, provisioning, bookings, commerce,
17
+ sessions, audit, and client-grade sharing under MNSTRY runtime authority.
18
+
19
+ ## Output Shape
20
+
21
+ Produce claim-first outputs:
22
+
23
+ - Claim: the readiness statement being made.
24
+ - Evidence: file paths, schema names, commands run, and observed behavior.
25
+ - Authority: source, generated evidence, proposed claim, or runtime-owned.
26
+ - Status: ready, warning, blocked, or proposed.
27
+ - Fix path: the smallest next reviewable change.
28
+
29
+ Do not introduce independent tenant semantics, new visibility terms, hidden
30
+ provider boundaries, or runtime mutation claims. If a check cannot run, report
31
+ the exact command, stderr, and blocker.
32
+
33
+ ## Safety And Hygiene
34
+
35
+ - Keep package-facing output generic and placeholder-only.
36
+ - Do not include person-specific, tenant-private, project-private, transcript,
37
+ support-bundle, local absolute path, agent-local state, key, token, or
38
+ credential material.
39
+ - Prefer static inspection and existing defensive tests for security-sensitive
40
+ review work.
41
+ - Frame checks as verifying that a control refuses, blocks, or fails closed.
@@ -0,0 +1,20 @@
1
+ import fs from 'node:fs'
2
+
3
+ export function analysisAdapterDryRun(manifestPath = null) {
4
+ const manifest = manifestPath && fs.existsSync(manifestPath) ? JSON.parse(fs.readFileSync(manifestPath, 'utf8')) : {}
5
+ if (manifest.enabled === true) throw new Error('model-assisted analysis execution is disabled in this release')
6
+ if (manifest.endpoint || manifest.token || manifest.apiBase || manifest.proxyUrl) throw new Error('model-assisted analysis manifest must not declare network/provider egress')
7
+ return {
8
+ schema: 'mnstry.atelier-analysis-adapter-dry-run@v1',
9
+ provider: 'analysis',
10
+ enabled: false,
11
+ canonicalMutation: false,
12
+ output: 'atelier-claim@v1 proposed claims only',
13
+ }
14
+ }
15
+
16
+ export function runAnalysisAdapterCommand(argv = process.argv.slice(2)) {
17
+ const manifestArg = argv.find((arg) => arg.startsWith('--manifest='))
18
+ const report = analysisAdapterDryRun(manifestArg ? manifestArg.slice('--manifest='.length) : null)
19
+ console.log(JSON.stringify(report, null, 2))
20
+ }
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
8
+ const packageRoot = path.resolve(__dirname, '../..')
9
+
10
+ export const manifestSchema = 'analysis-adapter-manifest@v1'
11
+ export const claimSchema = 'atelier-claim@v1'
12
+ export const provider = 'analysis'
13
+ export const harnessPolicy = 'claim-only-local-disabled'
14
+ export const localAnalysisRoot = '.mnstry/atelier/analysis'
15
+ export const fixtureRoot = path.join(packageRoot, 'fixtures/analysis')
16
+
17
+ export const defaultAnalysisClaimContract = {
18
+ schema: 'mnstry.atelier-analysis-claim-contract@v1',
19
+ provider,
20
+ claimSchema,
21
+ harnessPolicy,
22
+ authority: {
23
+ claimOnly: true,
24
+ frontMatterMutation: false,
25
+ graphRelationMutation: false,
26
+ publicExportFeed: false,
27
+ mcpFeed: false,
28
+ runtimeImport: false,
29
+ directCanonicalWrite: false,
30
+ },
31
+ }
32
+
33
+ const authorityPolicyKeys = new Set([
34
+ 'claimOnly',
35
+ 'frontMatterMutation',
36
+ 'graphRelationMutation',
37
+ 'publicExportFeed',
38
+ 'mcpFeed',
39
+ 'runtimeImport',
40
+ 'directCanonicalWrite',
41
+ ])
42
+
43
+ const forbiddenAuthorityKeys = new Map([
44
+ ['frontMatterMutation', 'front matter mutation'],
45
+ ['frontmatterMutation', 'front matter mutation'],
46
+ ['frontmatter', 'canonical front matter'],
47
+ ['frontMatter', 'canonical front matter'],
48
+ ['graphRelationMutation', 'graph relation mutation'],
49
+ ['graphRelations', 'graph relations'],
50
+ ['relations', 'graph relations'],
51
+ ['edges', 'graph relation mutation'],
52
+ ['publicExportFeed', 'public export feed'],
53
+ ['publicExport', 'public exports'],
54
+ ['publicExports', 'public exports'],
55
+ ['mcpFeed', 'MCP feed'],
56
+ ['runtimeImport', 'runtime import'],
57
+ ['runtimeApply', 'runtime import'],
58
+ ['directCanonicalWrite', 'direct canonical write'],
59
+ ['canonicalWrite', 'direct canonical write'],
60
+ ])
61
+
62
+ const hiddenProviderKeys = new Map([
63
+ ['modelProvider', 'hidden model provider'],
64
+ ['apiKey', 'hidden model provider credential'],
65
+ ['apiToken', 'hidden model provider credential'],
66
+ ['secret', 'hidden model provider credential'],
67
+ ])
68
+
69
+ const executionKeys = new Map([
70
+ ['execute', 'model-assisted analysis execution'],
71
+ ['executeCommand', 'model-assisted analysis execution command'],
72
+ ['runCommand', 'model-assisted analysis execution command'],
73
+ ['shellCommand', 'model-assisted analysis execution command'],
74
+ ['installCommand', 'model-assisted analysis install command'],
75
+ ['network', 'network access'],
76
+ ])
77
+
78
+ const manifestRootKeys = new Set(['schema', 'provider', 'enabled', 'outputRoot', 'claimContract', 'harness', 'canonicalMutation', 'authority'])
79
+ const manifestHarnessKeys = new Set(['policy', 'hiddenModelProvider', 'network', 'analysisExecution'])
80
+ const manifestAuthorityKeys = new Set([...authorityPolicyKeys])
81
+ const claimKeys = new Set(['schema', 'claimId', 'subject', 'predicate', 'object', 'provider', 'status', 'promoted', 'confidence', 'evidence', 'createdAt', 'notes'])
82
+
83
+ function isPlainObject(value) {
84
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
85
+ }
86
+
87
+ function joinPath(base, key) {
88
+ return base ? `${base}.${key}` : String(key)
89
+ }
90
+
91
+ function valueAttemptsAuthority(value) {
92
+ return value === true || value === 'true' || value === 'enabled' || value === 'apply' || value === 'write'
93
+ }
94
+
95
+ function readJson(file) {
96
+ return JSON.parse(fs.readFileSync(file, 'utf8'))
97
+ }
98
+
99
+ export function collectContractAttempts(value, {
100
+ pathLabel = '',
101
+ attempts = [],
102
+ skipExecutionNetworkKey = false,
103
+ } = {}) {
104
+ if (!value || typeof value !== 'object') return attempts
105
+ for (const [key, item] of Object.entries(value)) {
106
+ const current = joinPath(pathLabel, key)
107
+ if (forbiddenAuthorityKeys.has(key) && valueAttemptsAuthority(item)) {
108
+ attempts.push(`${current} attempts ${forbiddenAuthorityKeys.get(key)}`)
109
+ }
110
+ if (hiddenProviderKeys.has(key) && item != null && item !== false) {
111
+ attempts.push(`${current} attempts ${hiddenProviderKeys.get(key)}`)
112
+ }
113
+ if (executionKeys.has(key) && item != null && item !== false && !(skipExecutionNetworkKey && key === 'network')) {
114
+ if (key === 'network' && item === 'none') {
115
+ // Explicit no-network posture is allowed.
116
+ } else {
117
+ attempts.push(`${current} attempts ${executionKeys.get(key)}`)
118
+ }
119
+ }
120
+ if (item && typeof item === 'object') {
121
+ collectContractAttempts(item, {
122
+ pathLabel: current,
123
+ attempts,
124
+ skipExecutionNetworkKey,
125
+ })
126
+ }
127
+ }
128
+ return attempts
129
+ }
130
+
131
+ function validateLocalOutputRoot(outputRoot) {
132
+ const errors = []
133
+ if (typeof outputRoot !== 'string' || !outputRoot) {
134
+ errors.push('outputRoot is required')
135
+ return errors
136
+ }
137
+ if (path.isAbsolute(outputRoot)) errors.push('outputRoot must not be absolute')
138
+ const normalized = outputRoot.replaceAll('\\', '/')
139
+ if (normalized.split('/').includes('..')) errors.push('outputRoot must not escape via ..')
140
+ if (!(normalized === localAnalysisRoot || normalized.startsWith(`${localAnalysisRoot}/`))) {
141
+ errors.push(`outputRoot must live under ${localAnalysisRoot}`)
142
+ }
143
+ return errors
144
+ }
145
+
146
+ export function validateContractShape(contract = defaultAnalysisClaimContract) {
147
+ const errors = []
148
+ if (contract?.schema !== 'mnstry.atelier-analysis-claim-contract@v1') {
149
+ errors.push('contract schema must be mnstry.atelier-analysis-claim-contract@v1')
150
+ }
151
+ if (contract?.provider !== provider) errors.push('contract provider must be analysis')
152
+ if (contract?.claimSchema !== claimSchema) errors.push('contract claimSchema must be atelier-claim@v1')
153
+ if (contract?.harnessPolicy !== harnessPolicy) errors.push(`contract harnessPolicy must be ${harnessPolicy}`)
154
+ if (contract?.authority?.claimOnly !== true) errors.push('authority.claimOnly must be true')
155
+ for (const key of authorityPolicyKeys) {
156
+ if (key === 'claimOnly') continue
157
+ if (contract?.authority?.[key] !== false) errors.push(`authority.${key} must be false`)
158
+ }
159
+ errors.push(...collectContractAttempts(contract, { skipExecutionNetworkKey: true }))
160
+ return errors
161
+ }
162
+
163
+ export function validateManifest(manifest, { contract = defaultAnalysisClaimContract } = {}) {
164
+ const errors = validateContractShape(contract)
165
+ const warnings = []
166
+ const normalized = structuredClone(manifest || {})
167
+ if (!isPlainObject(normalized)) {
168
+ return { errors: ['manifest must be an object'], warnings, manifest: null }
169
+ }
170
+ for (const key of Object.keys(normalized)) {
171
+ if (!manifestRootKeys.has(key)) errors.push(`manifest has unknown property ${key}`)
172
+ }
173
+ if (normalized.schema !== manifestSchema) errors.push(`schema must be ${manifestSchema}`)
174
+ if (normalized.provider !== provider) errors.push('provider must be analysis')
175
+ if (normalized.enabled == null) {
176
+ normalized.enabled = false
177
+ warnings.push('enabled missing; defaulting to false')
178
+ }
179
+ if (normalized.enabled !== false) errors.push('enabled must be false until the operator explicitly installs an adapter')
180
+ errors.push(...validateLocalOutputRoot(normalized.outputRoot))
181
+ if (normalized.claimContract !== claimSchema) errors.push(`claimContract must be ${claimSchema}`)
182
+
183
+ const harness = normalized.harness || {}
184
+ if (!isPlainObject(harness)) errors.push('harness must be an object')
185
+ for (const key of Object.keys(harness)) {
186
+ if (!manifestHarnessKeys.has(key)) errors.push(`harness has unknown property ${key}`)
187
+ }
188
+ if (harness.policy !== harnessPolicy) errors.push(`harness.policy must be ${harnessPolicy}`)
189
+ if (harness.hiddenModelProvider !== false) errors.push('harness.hiddenModelProvider must be false')
190
+ if (harness.network !== 'none') errors.push('harness.network must be none')
191
+ if (harness.analysisExecution !== false) errors.push('harness.analysisExecution must be false')
192
+
193
+ if (normalized.canonicalMutation !== false) errors.push('canonicalMutation must be false')
194
+ const authority = normalized.authority || {}
195
+ if (!isPlainObject(authority)) errors.push('authority must be an object')
196
+ for (const key of Object.keys(authority)) {
197
+ if (!manifestAuthorityKeys.has(key)) errors.push(`authority has unknown property ${key}`)
198
+ }
199
+ if (authority.claimOnly !== true) errors.push('authority.claimOnly must be true')
200
+ for (const key of authorityPolicyKeys) {
201
+ if (key === 'claimOnly') continue
202
+ if (authority[key] !== false) errors.push(`authority.${key} must be false`)
203
+ }
204
+ errors.push(...collectContractAttempts(normalized, { skipExecutionNetworkKey: true }))
205
+ return { errors, warnings, manifest: normalized }
206
+ }
207
+
208
+ function normalizeOutputClaims(output) {
209
+ if (Array.isArray(output)) return output
210
+ if (isPlainObject(output) && Array.isArray(output.claims)) return output.claims
211
+ return null
212
+ }
213
+
214
+ export function validateAdapterOutput(output, { nodes = null, contract = defaultAnalysisClaimContract } = {}) {
215
+ const errors = validateContractShape(contract)
216
+ if (isPlainObject(output) && (Array.isArray(output.nodes) || Array.isArray(output.edges))) {
217
+ errors.push('model-assisted analysis output must be a single atelier-claim@v1 claim list, not native graph nodes/edges')
218
+ errors.push('native graph output attempts graph relation mutation')
219
+ }
220
+ const claims = normalizeOutputClaims(output)
221
+ if (!claims) {
222
+ errors.push('model-assisted analysis output must be a single atelier-claim@v1 claim list')
223
+ return { errors, claimCount: 0 }
224
+ }
225
+
226
+ for (const [index, claim] of claims.entries()) {
227
+ const label = `claims[${index}]`
228
+ if (!isPlainObject(claim)) {
229
+ errors.push(`${label} must be an object`)
230
+ continue
231
+ }
232
+ for (const key of Object.keys(claim)) {
233
+ if (!claimKeys.has(key)) errors.push(`${label} has unknown key ${key}`)
234
+ }
235
+ if (claim.schema !== claimSchema) errors.push(`${label}.schema must be ${claimSchema}`)
236
+ if (claim.provider !== provider) errors.push(`${label}.provider must be analysis`)
237
+ if (typeof claim.claimId !== 'string' || !claim.claimId.startsWith('claim:analysis:')) {
238
+ errors.push(`${label}.claimId must start with claim:analysis:`)
239
+ }
240
+ for (const key of ['subject', 'predicate', 'object']) {
241
+ if (typeof claim[key] !== 'string' || !claim[key]) errors.push(`${label}.${key} is required`)
242
+ }
243
+ if (claim.status !== 'proposed') errors.push(`${label}.status must be proposed`)
244
+ if (claim.promoted !== false) errors.push(`${label}.promoted must remain false`)
245
+ if (typeof claim.confidence !== 'number' || claim.confidence < 0 || claim.confidence > 1) {
246
+ errors.push(`${label}.confidence must be a number between 0 and 1`)
247
+ }
248
+ if (!Array.isArray(claim.evidence) || claim.evidence.length === 0) {
249
+ errors.push(`${label}.evidence must include at least one source reference`)
250
+ }
251
+ if (nodes && !nodes.some((node) => node?.id === claim.subject)) {
252
+ errors.push(`${label}.subject does not match a known graph node`)
253
+ }
254
+ errors.push(...collectContractAttempts(claim).map((attempt) => `${label}.${attempt}`))
255
+ }
256
+
257
+ return { errors, claimCount: claims.length }
258
+ }
259
+
260
+ function isJsonFile(file) {
261
+ return file.endsWith('.json')
262
+ }
263
+
264
+ export function checkFixtureSet({ root = fixtureRoot, nodes = null } = {}) {
265
+ const errors = []
266
+ let fixtureCount = 0
267
+ let invalidFixtureCount = 0
268
+ const validDir = path.join(root, 'valid')
269
+ const invalidDir = path.join(root, 'invalid')
270
+
271
+ if (fs.existsSync(validDir)) {
272
+ for (const name of fs.readdirSync(validDir).filter(isJsonFile).sort()) {
273
+ fixtureCount += 1
274
+ const result = validateAdapterOutput(readJson(path.join(validDir, name)), { nodes })
275
+ errors.push(...result.errors.map((error) => `${name}: ${error}`))
276
+ }
277
+ }
278
+ if (fs.existsSync(invalidDir)) {
279
+ for (const name of fs.readdirSync(invalidDir).filter(isJsonFile).sort()) {
280
+ invalidFixtureCount += 1
281
+ const result = validateAdapterOutput(readJson(path.join(invalidDir, name)), { nodes })
282
+ if (result.errors.length === 0) errors.push(`${name}: invalid fixture unexpectedly passed`)
283
+ }
284
+ }
285
+ return { errors, fixtureCount, invalidFixtureCount }
286
+ }
287
+
288
+ export function runAnalysisClaimContractCheck({
289
+ manifestPath = null,
290
+ fixtures = fixtureRoot,
291
+ contract = defaultAnalysisClaimContract,
292
+ } = {}) {
293
+ const report = {
294
+ status: 'disabled',
295
+ enabled: false,
296
+ analysisExecuted: false,
297
+ networkCalls: false,
298
+ canonicalMutation: false,
299
+ errors: [],
300
+ warnings: [],
301
+ }
302
+
303
+ report.errors.push(...validateContractShape(contract))
304
+ if (manifestPath && fs.existsSync(manifestPath)) {
305
+ const manifestResult = validateManifest(readJson(manifestPath), { contract })
306
+ report.errors.push(...manifestResult.errors)
307
+ report.warnings.push(...manifestResult.warnings)
308
+ report.enabled = manifestResult.manifest?.enabled === true
309
+ } else if (manifestPath) {
310
+ report.warnings.push('manifest missing; model-assisted analysis remains disabled')
311
+ }
312
+ const fixtureResult = checkFixtureSet({ root: fixtures })
313
+ report.errors.push(...fixtureResult.errors)
314
+ report.fixtureCount = fixtureResult.fixtureCount
315
+ report.invalidFixtureCount = fixtureResult.invalidFixtureCount
316
+ report.status = report.errors.length ? 'failed' : 'disabled'
317
+ return report
318
+ }
319
+
320
+ function formatReport(report) {
321
+ return JSON.stringify(report, null, 2)
322
+ }
323
+
324
+ function main() {
325
+ const manifestArg = process.argv.find((arg) => arg.startsWith('--manifest='))
326
+ const manifestPath = manifestArg ? manifestArg.slice('--manifest='.length) : null
327
+ const report = runAnalysisClaimContractCheck({ manifestPath })
328
+ console.log(formatReport(report))
329
+ if (report.errors.length) process.exitCode = 1
330
+ }
331
+
332
+ const currentFile = fileURLToPath(import.meta.url)
333
+ if (process.argv[1] && path.resolve(process.argv[1]) === currentFile) {
334
+ main()
335
+ }