domainforge 0.13.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 (481) hide show
  1. package/.cargo/config.toml +6 -0
  2. package/.claude/settings.local.json +18 -0
  3. package/.coderabbit.yml +43 -0
  4. package/.codex/skills/release-management/SKILL.md +151 -0
  5. package/.codex/skills/release-management/agents/openai.yaml +4 -0
  6. package/.github/actions/decrypt-secrets/action.yml +121 -0
  7. package/.github/agents/Coder.agent.md +97 -0
  8. package/.github/agents/DeepResearch.agent.md +61 -0
  9. package/.github/chatmodes/tdd.vibepro.chatmode.md +1183 -0
  10. package/.github/copilot-instructions.md +13 -0
  11. package/.github/dependabot.yml +68 -0
  12. package/.github/workflows/README.md +165 -0
  13. package/.github/workflows/ci.yml +335 -0
  14. package/.github/workflows/dependabot-automerge.yml +114 -0
  15. package/.github/workflows/dependency-review.yml +27 -0
  16. package/.github/workflows/deploy.yml +87 -0
  17. package/.github/workflows/prepare-release.yml +168 -0
  18. package/.github/workflows/release-crates.yml +42 -0
  19. package/.github/workflows/release-npm.yml +137 -0
  20. package/.github/workflows/release-please.yml +29 -0
  21. package/.github/workflows/release-pypi.yml +96 -0
  22. package/.gitkeep +1 -0
  23. package/.release-please-manifest.json +5 -0
  24. package/.sea-registry.toml +10 -0
  25. package/.serena/project.yml +133 -0
  26. package/.sops.yaml +10 -0
  27. package/AGENTS.md +216 -0
  28. package/CHANGELOG.md +400 -0
  29. package/CLAUDE.md +62 -0
  30. package/CONTRIBUTING.md +323 -0
  31. package/Cargo.lock +3612 -0
  32. package/Cargo.toml +12 -0
  33. package/LICENSE +201 -0
  34. package/README.md +660 -0
  35. package/README_PYTHON.md +256 -0
  36. package/README_TYPESCRIPT.md +305 -0
  37. package/README_WASM.md +329 -0
  38. package/RELEASE_NOTES.md +41 -0
  39. package/bun.lock +378 -0
  40. package/bunfig.toml +11 -0
  41. package/check_output.txt +83 -0
  42. package/clippy_output.txt +80 -0
  43. package/commitlint.config.cjs +8 -0
  44. package/deny.toml +42 -0
  45. package/devbox.json +14 -0
  46. package/devbox.lock +76 -0
  47. package/docs/RELEASE_PROCESS.md +360 -0
  48. package/docs/diagnostics.md +161 -0
  49. package/docs/doc_guidelines.md +53 -0
  50. package/docs/explanations/README.md +21 -0
  51. package/docs/explanations/architecture-overview.md +109 -0
  52. package/docs/explanations/cross-language-binding-strategy.md +68 -0
  53. package/docs/explanations/graph-store-design.md +47 -0
  54. package/docs/explanations/performance-benchmarks.md +63 -0
  55. package/docs/explanations/policy-evaluation-logic.md +106 -0
  56. package/docs/explanations/semantic-modeling-concepts.md +109 -0
  57. package/docs/explanations/three-valued-logic.md +66 -0
  58. package/docs/explanations/versioning-strategy.md +45 -0
  59. package/docs/governance.md +168 -0
  60. package/docs/how-tos/README.md +46 -0
  61. package/docs/how-tos/ci-cd-validation.md +93 -0
  62. package/docs/how-tos/create-custom-units.md +125 -0
  63. package/docs/how-tos/define-policies.md +119 -0
  64. package/docs/how-tos/export-to-calm.md +110 -0
  65. package/docs/how-tos/export-to-protobuf.md +312 -0
  66. package/docs/how-tos/extend-grammar.md +133 -0
  67. package/docs/how-tos/generate-rdf-turtle.md +106 -0
  68. package/docs/how-tos/import-from-calm.md +114 -0
  69. package/docs/how-tos/import-from-sbvr.md +249 -0
  70. package/docs/how-tos/install-cli.md +126 -0
  71. package/docs/how-tos/parse-sea-files.md +132 -0
  72. package/docs/how-tos/policy-evaluation-modes.md +30 -0
  73. package/docs/how-tos/run-cross-language-tests.md +115 -0
  74. package/docs/how-tos/troubleshoot-napi-builds.md +55 -0
  75. package/docs/how-tos/use-modules-imports.md +285 -0
  76. package/docs/index.md +13 -0
  77. package/docs/plans/canonical-normalizer.md +121 -0
  78. package/docs/plans/cd_improvement.md +112 -0
  79. package/docs/plans/cli-ast.md +29 -0
  80. package/docs/plans/expression-bindings-and-normalizer-integration.md +174 -0
  81. package/docs/plans/protobuf_advanced_features_plan.md +597 -0
  82. package/docs/plans/protobuf_plan.yml +525 -0
  83. package/docs/plans/refactor_dsl_architecture.md +131 -0
  84. package/docs/plans/release-plan.md +163 -0
  85. package/docs/plans/sea_fmt_implementation_plan.md +516 -0
  86. package/docs/playbooks/README.md +18 -0
  87. package/docs/playbooks/adding-new-primitive.md +68 -0
  88. package/docs/playbooks/debugging-parser-failures.md +42 -0
  89. package/docs/playbooks/local-release-preparation.md +139 -0
  90. package/docs/playbooks/migrating-schema-versions.md +43 -0
  91. package/docs/playbooks/onboarding-contributors.md +64 -0
  92. package/docs/playbooks/releasing-beta.md +86 -0
  93. package/docs/playbooks/secret-management.md +64 -0
  94. package/docs/reference/README.md +199 -0
  95. package/docs/reference/ast-json-api.md +427 -0
  96. package/docs/reference/calm-mapping.md +519 -0
  97. package/docs/reference/cli-commands.md +588 -0
  98. package/docs/reference/configuration.md +202 -0
  99. package/docs/reference/error-codes.md +664 -0
  100. package/docs/reference/generated-artifacts-policy.md +53 -0
  101. package/docs/reference/grammar-spec.md +255 -0
  102. package/docs/reference/primitives-api.md +317 -0
  103. package/docs/reference/protobuf-api.md +426 -0
  104. package/docs/reference/python-api.md +485 -0
  105. package/docs/reference/registry.md +50 -0
  106. package/docs/reference/sea-dsl-ai-cheatsheet.yaml +913 -0
  107. package/docs/reference/security-model.md +74 -0
  108. package/docs/reference/typescript-api.md +508 -0
  109. package/docs/reference/wasm-api.md +420 -0
  110. package/docs/semantic-pack-review.md +144 -0
  111. package/docs/semantic-pack-signing.md +234 -0
  112. package/docs/semantic-packs.md +284 -0
  113. package/docs/specs/ADR-001-sea-dsl-semantic-source-of-truth.md +33 -0
  114. package/docs/specs/ADR-002-projection-first-class-construct.md +50 -0
  115. package/docs/specs/ADR-003-protobuf-projection-target.md +51 -0
  116. package/docs/specs/ADR-004-projection-compatibility-semantics.md +57 -0
  117. package/docs/specs/ADR-005-multi-language-support-strategy.md +112 -0
  118. package/docs/specs/ADR-006-error-handling-strategy.md +115 -0
  119. package/docs/specs/ADR-007-policy-evaluation-engine.md +95 -0
  120. package/docs/specs/ADR-008-knowledge-graph-integration.md +90 -0
  121. package/docs/specs/ADR-009-module-resolution-strategy.md +115 -0
  122. package/docs/specs/ADR-010-unit-system.md +106 -0
  123. package/docs/specs/PRD-001-sea-projection-framework.md +155 -0
  124. package/docs/specs/PRD-002-sea-cli-tooling.md +169 -0
  125. package/docs/specs/PRD-003-dsl-core-capabilities.md +275 -0
  126. package/docs/specs/README.md +62 -0
  127. package/docs/specs/SDS-001-protobuf-projection-engine.md +451 -0
  128. package/docs/specs/SDS-002-sea-core-architecture.md +268 -0
  129. package/docs/specs/SDS-003-parser-semantic-graph.md +377 -0
  130. package/docs/specs/SDS-004-policy-engine-design.md +362 -0
  131. package/docs/specs/SDS-005-knowledge-graph-module.md +364 -0
  132. package/docs/specs/SDS-006-calm-integration.md +367 -0
  133. package/docs/specs/SDS-007-sbvr-import.md +347 -0
  134. package/docs/templates/template_explanation.md +14 -0
  135. package/docs/templates/template_howto.md +21 -0
  136. package/docs/templates/template_playbook.md +21 -0
  137. package/docs/templates/template_reference.md +17 -0
  138. package/docs/templates/template_tutorial.md +24 -0
  139. package/docs/tutorials/README.md +12 -0
  140. package/docs/tutorials/first-sea-model.md +85 -0
  141. package/docs/tutorials/getting-started.md +98 -0
  142. package/docs/tutorials/python-binding-quickstart.md +107 -0
  143. package/docs/tutorials/typescript-binding-quickstart.md +91 -0
  144. package/docs/tutorials/wasm-in-browser.md +75 -0
  145. package/domainforge-core/CHANGELOG.md +138 -0
  146. package/domainforge-core/Cargo.toml +101 -0
  147. package/domainforge-core/MIGRATING.md +32 -0
  148. package/domainforge-core/README.md +197 -0
  149. package/domainforge-core/benchmark_results.txt +51 -0
  150. package/domainforge-core/build.rs +6 -0
  151. package/domainforge-core/deny.toml +31 -0
  152. package/domainforge-core/docs/specs/projections/sbvr_kg_mapping.md +43 -0
  153. package/domainforge-core/examples/basic.sea +7 -0
  154. package/domainforge-core/examples/cli/import_export_workflow.sh +38 -0
  155. package/domainforge-core/examples/cli/validate_example.sh +30 -0
  156. package/domainforge-core/examples/evolution_semantics.sea +31 -0
  157. package/domainforge-core/examples/parser_demo.rs +203 -0
  158. package/domainforge-core/grammar/sea.pest +408 -0
  159. package/domainforge-core/schemas/calm-v1.schema.json +170 -0
  160. package/domainforge-core/schemas/shacl/sea_shapes.ttl +19 -0
  161. package/domainforge-core/src/authority/compiler.rs +309 -0
  162. package/domainforge-core/src/authority/environment.rs +203 -0
  163. package/domainforge-core/src/authority/error.rs +164 -0
  164. package/domainforge-core/src/authority/fact_resolver.rs +224 -0
  165. package/domainforge-core/src/authority/mod.rs +25 -0
  166. package/domainforge-core/src/authority/pack.rs +133 -0
  167. package/domainforge-core/src/authority/policy.rs +224 -0
  168. package/domainforge-core/src/authority/resolver.rs +446 -0
  169. package/domainforge-core/src/authority/trace.rs +217 -0
  170. package/domainforge-core/src/authority/transform.rs +168 -0
  171. package/domainforge-core/src/authority/types.rs +617 -0
  172. package/domainforge-core/src/bin/domainforge.rs +25 -0
  173. package/domainforge-core/src/calm/export.rs +538 -0
  174. package/domainforge-core/src/calm/import.rs +1220 -0
  175. package/domainforge-core/src/calm/mod.rs +9 -0
  176. package/domainforge-core/src/calm/models.rs +108 -0
  177. package/domainforge-core/src/calm/sbvr_import.rs +9 -0
  178. package/domainforge-core/src/cli/authority.rs +149 -0
  179. package/domainforge-core/src/cli/format.rs +85 -0
  180. package/domainforge-core/src/cli/import.rs +133 -0
  181. package/domainforge-core/src/cli/mod.rs +64 -0
  182. package/domainforge-core/src/cli/normalize.rs +180 -0
  183. package/domainforge-core/src/cli/pack.rs +904 -0
  184. package/domainforge-core/src/cli/parse.rs +112 -0
  185. package/domainforge-core/src/cli/project.rs +294 -0
  186. package/domainforge-core/src/cli/registry.rs +41 -0
  187. package/domainforge-core/src/cli/test.rs +12 -0
  188. package/domainforge-core/src/cli/validate.rs +195 -0
  189. package/domainforge-core/src/cli/validate_kg.rs +80 -0
  190. package/domainforge-core/src/concept_id.rs +89 -0
  191. package/domainforge-core/src/error/diagnostics.rs +426 -0
  192. package/domainforge-core/src/error/fuzzy.rs +253 -0
  193. package/domainforge-core/src/error/mod.rs +13 -0
  194. package/domainforge-core/src/formatter/comments.rs +223 -0
  195. package/domainforge-core/src/formatter/config.rs +114 -0
  196. package/domainforge-core/src/formatter/mod.rs +22 -0
  197. package/domainforge-core/src/formatter/printer.rs +906 -0
  198. package/domainforge-core/src/graph/mod.rs +858 -0
  199. package/domainforge-core/src/graph/to_ast.rs +66 -0
  200. package/domainforge-core/src/kg.rs +1476 -0
  201. package/domainforge-core/src/kg_import.rs +251 -0
  202. package/domainforge-core/src/lib.rs +203 -0
  203. package/domainforge-core/src/module/mod.rs +1 -0
  204. package/domainforge-core/src/module/resolver.rs +260 -0
  205. package/domainforge-core/src/parser/ast.rs +2919 -0
  206. package/domainforge-core/src/parser/ast_convert.rs +494 -0
  207. package/domainforge-core/src/parser/ast_schema.rs +491 -0
  208. package/domainforge-core/src/parser/error.rs +291 -0
  209. package/domainforge-core/src/parser/lint.rs +39 -0
  210. package/domainforge-core/src/parser/mod.rs +193 -0
  211. package/domainforge-core/src/parser/printer.rs +702 -0
  212. package/domainforge-core/src/parser/profiles.rs +71 -0
  213. package/domainforge-core/src/parser/string_utils.rs +138 -0
  214. package/domainforge-core/src/patterns.rs +68 -0
  215. package/domainforge-core/src/policy/core.rs +1148 -0
  216. package/domainforge-core/src/policy/expression.rs +399 -0
  217. package/domainforge-core/src/policy/mod.rs +18 -0
  218. package/domainforge-core/src/policy/normalize.rs +1028 -0
  219. package/domainforge-core/src/policy/quantifier.rs +940 -0
  220. package/domainforge-core/src/policy/three_valued.rs +140 -0
  221. package/domainforge-core/src/policy/three_valued_microbench.rs +104 -0
  222. package/domainforge-core/src/policy/type_inference.rs +67 -0
  223. package/domainforge-core/src/policy/violation.rs +36 -0
  224. package/domainforge-core/src/primitives/concept_change.rs +61 -0
  225. package/domainforge-core/src/primitives/entity.rs +224 -0
  226. package/domainforge-core/src/primitives/flow.rs +111 -0
  227. package/domainforge-core/src/primitives/instance.rs +93 -0
  228. package/domainforge-core/src/primitives/mapping_contract.rs +50 -0
  229. package/domainforge-core/src/primitives/metric.rs +79 -0
  230. package/domainforge-core/src/primitives/mod.rs +25 -0
  231. package/domainforge-core/src/primitives/projection_contract.rs +50 -0
  232. package/domainforge-core/src/primitives/quantity.rs +56 -0
  233. package/domainforge-core/src/primitives/relation.rs +68 -0
  234. package/domainforge-core/src/primitives/resource.rs +237 -0
  235. package/domainforge-core/src/primitives/resource_instance.rs +88 -0
  236. package/domainforge-core/src/primitives/role.rs +49 -0
  237. package/domainforge-core/src/projection/buf.rs +404 -0
  238. package/domainforge-core/src/projection/contracts.rs +22 -0
  239. package/domainforge-core/src/projection/engine.rs +19 -0
  240. package/domainforge-core/src/projection/mod.rs +16 -0
  241. package/domainforge-core/src/projection/protobuf.rs +3331 -0
  242. package/domainforge-core/src/projection/registry.rs +43 -0
  243. package/domainforge-core/src/python/authority.rs +253 -0
  244. package/domainforge-core/src/python/error.rs +227 -0
  245. package/domainforge-core/src/python/formatter.rs +86 -0
  246. package/domainforge-core/src/python/graph.rs +366 -0
  247. package/domainforge-core/src/python/mod.rs +9 -0
  248. package/domainforge-core/src/python/policy.rs +651 -0
  249. package/domainforge-core/src/python/primitives.rs +796 -0
  250. package/domainforge-core/src/python/registry.rs +98 -0
  251. package/domainforge-core/src/python/semantic_pack.rs +619 -0
  252. package/domainforge-core/src/python/units.rs +96 -0
  253. package/domainforge-core/src/registry/mod.rs +432 -0
  254. package/domainforge-core/src/registry/tests.rs +210 -0
  255. package/domainforge-core/src/sbvr.rs +744 -0
  256. package/domainforge-core/src/semantic_pack/builder.rs +470 -0
  257. package/domainforge-core/src/semantic_pack/canonical_json.rs +184 -0
  258. package/domainforge-core/src/semantic_pack/diagnostics.rs +214 -0
  259. package/domainforge-core/src/semantic_pack/diff.rs +216 -0
  260. package/domainforge-core/src/semantic_pack/mod.rs +31 -0
  261. package/domainforge-core/src/semantic_pack/pack_set.rs +240 -0
  262. package/domainforge-core/src/semantic_pack/resolver.rs +437 -0
  263. package/domainforge-core/src/semantic_pack/review.rs +125 -0
  264. package/domainforge-core/src/semantic_pack/schema.rs +342 -0
  265. package/domainforge-core/src/semantic_pack/signing.rs +105 -0
  266. package/domainforge-core/src/semantic_pack/validator.rs +368 -0
  267. package/domainforge-core/src/semantic_version.rs +140 -0
  268. package/domainforge-core/src/test_utils.rs +12 -0
  269. package/domainforge-core/src/typescript/authority.rs +184 -0
  270. package/domainforge-core/src/typescript/error.rs +146 -0
  271. package/domainforge-core/src/typescript/formatter.rs +76 -0
  272. package/domainforge-core/src/typescript/graph.rs +391 -0
  273. package/domainforge-core/src/typescript/mod.rs +9 -0
  274. package/domainforge-core/src/typescript/policy.rs +564 -0
  275. package/domainforge-core/src/typescript/primitives.rs +784 -0
  276. package/domainforge-core/src/typescript/registry.rs +88 -0
  277. package/domainforge-core/src/typescript/semantic_pack.rs +470 -0
  278. package/domainforge-core/src/typescript/units.rs +76 -0
  279. package/domainforge-core/src/units/mod.rs +462 -0
  280. package/domainforge-core/src/uuid_module.rs +42 -0
  281. package/domainforge-core/src/validation_error.rs +818 -0
  282. package/domainforge-core/src/validation_result.rs +30 -0
  283. package/domainforge-core/src/wasm/authority.rs +192 -0
  284. package/domainforge-core/src/wasm/error.rs +145 -0
  285. package/domainforge-core/src/wasm/formatter.rs +69 -0
  286. package/domainforge-core/src/wasm/graph.rs +471 -0
  287. package/domainforge-core/src/wasm/mod.rs +16 -0
  288. package/domainforge-core/src/wasm/policy.rs +607 -0
  289. package/domainforge-core/src/wasm/primitives.rs +295 -0
  290. package/domainforge-core/src/wasm/semantic_pack.rs +471 -0
  291. package/domainforge-core/src/wasm/units.rs +62 -0
  292. package/domainforge-core/std/aws.sea +6 -0
  293. package/domainforge-core/std/core.sea +6 -0
  294. package/domainforge-core/std/http.sea +27 -0
  295. package/domainforge-core/tests/aggregation_enhanced_tests.rs +162 -0
  296. package/domainforge-core/tests/aggregation_eval_tests.rs +248 -0
  297. package/domainforge-core/tests/aggregation_integration_tests.rs +379 -0
  298. package/domainforge-core/tests/aggregation_parser_tests.rs +92 -0
  299. package/domainforge-core/tests/aggregation_tests.rs +102 -0
  300. package/domainforge-core/tests/authority_conformance_tests.rs +1173 -0
  301. package/domainforge-core/tests/calm_round_trip_tests.rs +283 -0
  302. package/domainforge-core/tests/calm_schema_validation_tests.rs +137 -0
  303. package/domainforge-core/tests/cast_operator_tests.rs +85 -0
  304. package/domainforge-core/tests/cli_binary_check.rs +37 -0
  305. package/domainforge-core/tests/cli_import_tests.rs +291 -0
  306. package/domainforge-core/tests/cli_path_traversal_tests.rs +124 -0
  307. package/domainforge-core/tests/cli_tests.rs +63 -0
  308. package/domainforge-core/tests/diagnostics_tests.rs +203 -0
  309. package/domainforge-core/tests/dimension_unit_tests.rs +80 -0
  310. package/domainforge-core/tests/entity_tests.rs +69 -0
  311. package/domainforge-core/tests/evolution_semantics_tests.rs +157 -0
  312. package/domainforge-core/tests/flow_tests.rs +78 -0
  313. package/domainforge-core/tests/flow_unit_validation_tests.rs +31 -0
  314. package/domainforge-core/tests/graph_integration_tests.rs +218 -0
  315. package/domainforge-core/tests/graph_tests.rs +626 -0
  316. package/domainforge-core/tests/import_parsing_tests.rs +23 -0
  317. package/domainforge-core/tests/instance_integration_tests.rs +98 -0
  318. package/domainforge-core/tests/instance_parsing_tests.rs +58 -0
  319. package/domainforge-core/tests/instance_tests.rs +61 -0
  320. package/domainforge-core/tests/kg_uri_encoding_tests.rs +53 -0
  321. package/domainforge-core/tests/lint_tests.rs +19 -0
  322. package/domainforge-core/tests/metric_tests.rs +143 -0
  323. package/domainforge-core/tests/module_resolution_tests.rs +100 -0
  324. package/domainforge-core/tests/namespace_registry_tests.rs +247 -0
  325. package/domainforge-core/tests/null_handling_tests.rs +26 -0
  326. package/domainforge-core/tests/parser_ast_v3.rs +53 -0
  327. package/domainforge-core/tests/parser_dimension_registry_tests.rs +20 -0
  328. package/domainforge-core/tests/parser_integration_tests.rs +294 -0
  329. package/domainforge-core/tests/parser_metadata_tests.rs +97 -0
  330. package/domainforge-core/tests/parser_resource_domain_only_graph_test.rs +21 -0
  331. package/domainforge-core/tests/parser_resource_limits_tests.rs +122 -0
  332. package/domainforge-core/tests/parser_tests.rs +512 -0
  333. package/domainforge-core/tests/pattern_semantics_tests.rs +87 -0
  334. package/domainforge-core/tests/phase_14_determinism_tests.rs +166 -0
  335. package/domainforge-core/tests/phase_15_validation_error_tests.rs +136 -0
  336. package/domainforge-core/tests/phase_16_unicode_tests.rs +248 -0
  337. package/domainforge-core/tests/phase_17_export_tests.rs +285 -0
  338. package/domainforge-core/tests/phase_17_round_trip_tests.rs +264 -0
  339. package/domainforge-core/tests/policy_tests.rs +635 -0
  340. package/domainforge-core/tests/primitives_integration_tests.rs +151 -0
  341. package/domainforge-core/tests/print_rdf_xml.rs +14 -0
  342. package/domainforge-core/tests/printer_tests.rs +204 -0
  343. package/domainforge-core/tests/profile_tests.rs +35 -0
  344. package/domainforge-core/tests/projection_contracts_tests.rs +154 -0
  345. package/domainforge-core/tests/protobuf_projection_tests.rs +199 -0
  346. package/domainforge-core/tests/quantity_tests.rs +41 -0
  347. package/domainforge-core/tests/rdf_xml_typed_literal_tests.rs +105 -0
  348. package/domainforge-core/tests/registry_schema_tests.rs +33 -0
  349. package/domainforge-core/tests/resource_tests.rs +50 -0
  350. package/domainforge-core/tests/resource_unit_tests.rs +24 -0
  351. package/domainforge-core/tests/roles_relations_tests.rs +61 -0
  352. package/domainforge-core/tests/round_trip_tests.rs +34 -0
  353. package/domainforge-core/tests/runtime_toggle_tests.rs +70 -0
  354. package/domainforge-core/tests/sbvr_fact_schema_tests.rs +60 -0
  355. package/domainforge-core/tests/sbvr_flow_facts_tests.rs +55 -0
  356. package/domainforge-core/tests/sbvr_parsing_tests.rs +53 -0
  357. package/domainforge-core/tests/semantic_pack_alias_resolution.rs +197 -0
  358. package/domainforge-core/tests/semantic_pack_build.rs +302 -0
  359. package/domainforge-core/tests/semantic_pack_consumer_smoke.rs +150 -0
  360. package/domainforge-core/tests/semantic_pack_pack_set.rs +160 -0
  361. package/domainforge-core/tests/semantic_pack_signing.rs +157 -0
  362. package/domainforge-core/tests/semantic_pack_three_valued.rs +250 -0
  363. package/domainforge-core/tests/semantic_pack_validate.rs +196 -0
  364. package/domainforge-core/tests/std_lib_tests.rs +37 -0
  365. package/domainforge-core/tests/temporal_evaluation_tests.rs +159 -0
  366. package/domainforge-core/tests/temporal_semantics_tests.rs +214 -0
  367. package/domainforge-core/tests/three_valued_quantifiers_tests.rs +164 -0
  368. package/domainforge-core/tests/turtle_entity_export_tests.rs +38 -0
  369. package/domainforge-core/tests/turtle_escaping_tests.rs +53 -0
  370. package/domainforge-core/tests/turtle_resource_export_tests.rs +34 -0
  371. package/domainforge-core/tests/type_inference_tests.rs +40 -0
  372. package/domainforge-core/tests/unicode_validation_tests.rs +169 -0
  373. package/domainforge-core/tests/unit_tests.rs +81 -0
  374. package/domainforge-core/tests/validate_tests.rs +38 -0
  375. package/domainforge-core/tests/validation_unit_mismatch_tests.rs +83 -0
  376. package/domainforge-core/tests/wasm_tests.rs +229 -0
  377. package/domainforge-python/CHANGELOG-python.md +12 -0
  378. package/domainforge-python/MIGRATING.md +24 -0
  379. package/domainforge-python/README.md +256 -0
  380. package/domainforge-python/domainforge/__init__.py +95 -0
  381. package/domainforge-python/domainforge/domainforge.pyi +519 -0
  382. package/domainforge-python/pyproject.toml +36 -0
  383. package/domainforge-typescript/CHANGELOG-typescript.md +12 -0
  384. package/domainforge-typescript/LICENSE +201 -0
  385. package/domainforge-typescript/MIGRATING.md +24 -0
  386. package/domainforge-typescript/README.md +305 -0
  387. package/domainforge-typescript/index.d.ts +452 -0
  388. package/domainforge-typescript/index.js +361 -0
  389. package/domainforge-typescript/package.json +60 -0
  390. package/example.js +61 -0
  391. package/examples/browser.html +366 -0
  392. package/examples/namespaces/finance/cashflow.sea +5 -0
  393. package/examples/namespaces/logistics/core.sea +7 -0
  394. package/examples/observability_metrics.sea +38 -0
  395. package/fixtures/semantic_packs/acme_procurement/domain/entities.sea +39 -0
  396. package/fixtures/semantic_packs/acme_procurement/domain/metrics.sea +11 -0
  397. package/fixtures/semantic_packs/acme_procurement/domain/relations.sea +7 -0
  398. package/fixtures/semantic_packs/acme_procurement/domain/resources.sea +9 -0
  399. package/fixtures/semantic_packs/acme_procurement/review/acme.procurement.semantic-review.jsonl +7 -0
  400. package/fixtures/semantic_packs/acme_procurement/tests/ambiguous_vendor_alias.sea +8 -0
  401. package/fixtures/semantic_packs/acme_procurement/tests/deprecated_vendor_alias.sea +8 -0
  402. package/fixtures/semantic_packs/acme_procurement/tests/invalid_relation.sea +3 -0
  403. package/fixtures/semantic_packs/acme_procurement/tests/proposed_concept.sea +8 -0
  404. package/fixtures/semantic_packs/acme_procurement/tests/rejected_concept.sea +8 -0
  405. package/fixtures/semantic_packs/acme_procurement/tests/unit_mismatch.sea +7 -0
  406. package/fixtures/semantic_packs/acme_procurement/tests/unknown_vendor_policy.sea +8 -0
  407. package/fixtures/semantic_packs/acme_procurement/tests/valid_purchase_policy.sea +8 -0
  408. package/index.d.ts +2 -0
  409. package/index.js +8 -0
  410. package/justfile +200 -0
  411. package/lefthook.yml +13 -0
  412. package/lib/validate_native_exports.d.ts +4 -0
  413. package/lib/validate_native_exports.js +12 -0
  414. package/package.json +22 -0
  415. package/pytest.ini +5 -0
  416. package/python/tests/test_registry.py +75 -0
  417. package/python/tests/test_units.py +18 -0
  418. package/release-please-config.json +49 -0
  419. package/requirements-dev.txt +3 -0
  420. package/requirements.txt +3 -0
  421. package/rust-toolchain.toml +3 -0
  422. package/schemas/ast-v1.schema.json +72 -0
  423. package/schemas/ast-v2.schema.json +1200 -0
  424. package/schemas/ast-v3.schema.json +1200 -0
  425. package/schemas/sea-registry.schema.json +45 -0
  426. package/scripts/build-python.sh +37 -0
  427. package/scripts/build-release.sh +279 -0
  428. package/scripts/build-typescript.sh +13 -0
  429. package/scripts/build-wasm.sh +113 -0
  430. package/scripts/bump-version.sh +245 -0
  431. package/scripts/check_unused_test_imports.py +85 -0
  432. package/scripts/ci_tasks.py +379 -0
  433. package/scripts/clear_debug_test.sh +10 -0
  434. package/scripts/create-github-release.sh +262 -0
  435. package/scripts/create-tag.sh +203 -0
  436. package/scripts/find_and_link_test_binary.sh +70 -0
  437. package/scripts/generate-changelog.sh +271 -0
  438. package/scripts/generate-release-notes.sh +205 -0
  439. package/scripts/lint_release_security.py +96 -0
  440. package/scripts/lint_release_workflows.py +82 -0
  441. package/scripts/lint_workflow_gates.py +113 -0
  442. package/scripts/optimized-wasm-build.sh +61 -0
  443. package/scripts/patch_napi_types.py +62 -0
  444. package/scripts/pre-release-check.sh +289 -0
  445. package/scripts/prepare_rust_debug.sh +52 -0
  446. package/scripts/release.sh +373 -0
  447. package/scripts/resolve_rust_binary.py +230 -0
  448. package/scripts/run_commitlint.sh +29 -0
  449. package/scripts/test-all.sh +77 -0
  450. package/scripts/update_launch_program.py +93 -0
  451. package/secrets/README.md +27 -0
  452. package/secrets/secrets.yaml +21 -0
  453. package/test_integration.py +67 -0
  454. package/tests/test_authority.py +328 -0
  455. package/tests/test_ci_tasks.py +143 -0
  456. package/tests/test_expression.py +256 -0
  457. package/tests/test_golden_payment_flow.py +42 -0
  458. package/tests/test_graph.py +127 -0
  459. package/tests/test_instance.py +136 -0
  460. package/tests/test_parser.py +82 -0
  461. package/tests/test_primitives.py +68 -0
  462. package/tests/test_role_relation_parity.py +56 -0
  463. package/tests/test_runtime_toggle.py +156 -0
  464. package/tests/test_semantic_pack.py +639 -0
  465. package/tests/test_three_valued_eval.py +159 -0
  466. package/tsconfig.json +30 -0
  467. package/typescript-tests/advanced.test.ts +165 -0
  468. package/typescript-tests/authority.test.ts +216 -0
  469. package/typescript-tests/expression.test.ts +228 -0
  470. package/typescript-tests/golden-payment-flow.test.ts +51 -0
  471. package/typescript-tests/graph.test.ts +142 -0
  472. package/typescript-tests/native-binding.test.ts +20 -0
  473. package/typescript-tests/primitives.test.ts +88 -0
  474. package/typescript-tests/registry.test.ts +122 -0
  475. package/typescript-tests/role_relation.test.ts +63 -0
  476. package/typescript-tests/runtime_toggle.test.ts +141 -0
  477. package/typescript-tests/semantic-pack.test.ts +556 -0
  478. package/typescript-tests/three_valued_eval.test.ts +135 -0
  479. package/typescript-tests/units.test.ts +36 -0
  480. package/vitest.config.ts +13 -0
  481. package/wasm_demo.html +225 -0
@@ -0,0 +1,639 @@
1
+ """
2
+ Tests for the new Semantic Pack API exports added in this PR.
3
+
4
+ Covers:
5
+ - Enum exports: SemanticTruth, DiagnosticSeverity, ValidationMode, ApprovalState,
6
+ SignatureState, ConceptStatus, ConceptKind, AliasStatus, SemanticValidationStatus
7
+ - Functions: normalize_lookup_key, build_semantic_pack, validate_semantic_pack,
8
+ validate_graph_with_pack, compute_pack_hash, diff_packs, resolve_concept,
9
+ sign_pack, verify_pack_signature
10
+ - Classes: SemanticPack, SemanticValidationResult
11
+ """
12
+
13
+ import json
14
+ import pytest
15
+ import domainforge
16
+
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Helpers
20
+ # ---------------------------------------------------------------------------
21
+
22
+ def make_minimal_pack_json() -> str:
23
+ return json.dumps({
24
+ "schema_version": "0.3",
25
+ "pack_id": "test-org/test-domain/1.0.0",
26
+ "org_id": "test-org",
27
+ "domain_id": "test-domain",
28
+ "pack_version": "1.0.0",
29
+ "meaning_version": "1.0.0",
30
+ "meaning_fingerprint": "",
31
+ "source_graph_hash": "sha256:test",
32
+ "build_config_hash": "sha256:cfg",
33
+ "review_manifest_hash": "sha256:rev",
34
+ "created_at": "2026-06-07T00:00:00Z",
35
+ "generator": {"name": "domainforge-core", "version": "0.3"},
36
+ "trust": {"approval_state": "candidate", "signature_state": "unsigned"},
37
+ "concepts": [
38
+ {
39
+ "id": "supplier",
40
+ "canonical_name": "Supplier",
41
+ "kind": "entity",
42
+ "status": "active",
43
+ "definition": {
44
+ "text": "A party that provides goods or services.",
45
+ "definition_hash": "",
46
+ "decision_ref": "dec_supplier",
47
+ },
48
+ "owner": "owner@test.com",
49
+ "source_refs": [],
50
+ "examples": [],
51
+ "counterexamples": [],
52
+ "allowed_predicates": [],
53
+ "valid_contexts": [],
54
+ }
55
+ ],
56
+ "relations": [],
57
+ "metrics": [],
58
+ "dimensions": [],
59
+ "units": [],
60
+ "aliases": [],
61
+ "mapping_rules": [],
62
+ "compatibility": {},
63
+ })
64
+
65
+
66
+ def make_minimal_build_input_json() -> str:
67
+ return json.dumps({
68
+ "org_id": "test-org",
69
+ "domain_id": "test-domain",
70
+ "pack_version": "1.0.0",
71
+ "meaning_version": "1.0.0",
72
+ "approval": "candidate",
73
+ "concepts": [
74
+ {
75
+ "id": "supplier",
76
+ "canonical_name": "Supplier",
77
+ "kind": "entity",
78
+ "status": "active",
79
+ "definition": {
80
+ "text": "A party that provides goods or services.",
81
+ "definition_hash": "",
82
+ "decision_ref": "dec_supplier",
83
+ },
84
+ "owner": "owner@test.com",
85
+ "source_refs": [],
86
+ "examples": [],
87
+ "counterexamples": [],
88
+ "allowed_predicates": [],
89
+ "valid_contexts": [],
90
+ }
91
+ ],
92
+ "relations": [],
93
+ "metrics": [],
94
+ "dimensions": [],
95
+ "units": [],
96
+ "aliases": [],
97
+ "mapping_rules": [],
98
+ "review_records": [],
99
+ "previous_pack": None,
100
+ "allow_first_approved_version": False,
101
+ "source_graph_hash": "sha256:test",
102
+ })
103
+
104
+
105
+ def make_default_options_json() -> str:
106
+ return json.dumps({"mode": "warn", "deprecated_policy": "warn"})
107
+
108
+
109
+ # ---------------------------------------------------------------------------
110
+ # Enum import tests
111
+ # ---------------------------------------------------------------------------
112
+
113
+ def test_semantic_truth_enum_importable():
114
+ """SemanticTruth enum is importable from domainforge."""
115
+ assert hasattr(domainforge, "SemanticTruth")
116
+
117
+
118
+ def test_semantic_truth_enum_values():
119
+ """SemanticTruth has Valid, Invalid, Unknown members."""
120
+ assert hasattr(domainforge.SemanticTruth, "Valid")
121
+ assert hasattr(domainforge.SemanticTruth, "Invalid")
122
+ assert hasattr(domainforge.SemanticTruth, "Unknown")
123
+
124
+
125
+ def test_semantic_truth_equality():
126
+ """SemanticTruth enum members are equal to themselves."""
127
+ assert domainforge.SemanticTruth.Valid == domainforge.SemanticTruth.Valid
128
+ assert domainforge.SemanticTruth.Invalid != domainforge.SemanticTruth.Unknown
129
+
130
+
131
+ def test_diagnostic_severity_enum_importable():
132
+ """DiagnosticSeverity enum is importable from domainforge."""
133
+ assert hasattr(domainforge, "DiagnosticSeverity")
134
+
135
+
136
+ def test_diagnostic_severity_enum_values():
137
+ """DiagnosticSeverity has Error, Warning, Info, Hint members."""
138
+ assert hasattr(domainforge.DiagnosticSeverity, "Error")
139
+ assert hasattr(domainforge.DiagnosticSeverity, "Warning")
140
+ assert hasattr(domainforge.DiagnosticSeverity, "Info")
141
+ assert hasattr(domainforge.DiagnosticSeverity, "Hint")
142
+
143
+
144
+ def test_validation_mode_enum_importable():
145
+ """ValidationMode enum is importable from domainforge."""
146
+ assert hasattr(domainforge, "ValidationMode")
147
+
148
+
149
+ def test_validation_mode_enum_values():
150
+ """ValidationMode has Off, Warn, Strict members."""
151
+ assert hasattr(domainforge.ValidationMode, "Off")
152
+ assert hasattr(domainforge.ValidationMode, "Warn")
153
+ assert hasattr(domainforge.ValidationMode, "Strict")
154
+
155
+
156
+ def test_approval_state_enum_values():
157
+ """ApprovalState has Candidate, Approved, Rejected members."""
158
+ assert hasattr(domainforge.ApprovalState, "Candidate")
159
+ assert hasattr(domainforge.ApprovalState, "Approved")
160
+ assert hasattr(domainforge.ApprovalState, "Rejected")
161
+
162
+
163
+ def test_signature_state_enum_values():
164
+ """SignatureState has Unsigned, Signed, InvalidSignature members."""
165
+ assert hasattr(domainforge.SignatureState, "Unsigned")
166
+ assert hasattr(domainforge.SignatureState, "Signed")
167
+ assert hasattr(domainforge.SignatureState, "InvalidSignature")
168
+
169
+
170
+ def test_concept_status_enum_values():
171
+ """ConceptStatus has all five status values."""
172
+ assert hasattr(domainforge.ConceptStatus, "Active")
173
+ assert hasattr(domainforge.ConceptStatus, "Proposed")
174
+ assert hasattr(domainforge.ConceptStatus, "Deprecated")
175
+ assert hasattr(domainforge.ConceptStatus, "Rejected")
176
+ assert hasattr(domainforge.ConceptStatus, "ExternalOnly")
177
+
178
+
179
+ def test_concept_kind_enum_values():
180
+ """ConceptKind has all nine kind values."""
181
+ for kind in ["Entity", "Resource", "Role", "Flow", "Policy",
182
+ "Metric", "Dimension", "Unit", "External"]:
183
+ assert hasattr(domainforge.ConceptKind, kind), f"Missing ConceptKind.{kind}"
184
+
185
+
186
+ def test_alias_status_enum_values():
187
+ """AliasStatus has Approved, Deprecated, Ambiguous, Blocked members."""
188
+ assert hasattr(domainforge.AliasStatus, "Approved")
189
+ assert hasattr(domainforge.AliasStatus, "Deprecated")
190
+ assert hasattr(domainforge.AliasStatus, "Ambiguous")
191
+ assert hasattr(domainforge.AliasStatus, "Blocked")
192
+
193
+
194
+ def test_semantic_validation_status_enum_values():
195
+ """SemanticValidationStatus has Passed, Failed, Unknown, Blocked members."""
196
+ assert hasattr(domainforge.SemanticValidationStatus, "Passed")
197
+ assert hasattr(domainforge.SemanticValidationStatus, "Failed")
198
+ assert hasattr(domainforge.SemanticValidationStatus, "Unknown")
199
+ assert hasattr(domainforge.SemanticValidationStatus, "Blocked")
200
+
201
+
202
+ def test_enum_members_are_distinct():
203
+ """Different enum members are not equal to each other."""
204
+ assert domainforge.SemanticTruth.Valid != domainforge.SemanticTruth.Invalid
205
+ assert domainforge.ValidationMode.Off != domainforge.ValidationMode.Strict
206
+ assert domainforge.ApprovalState.Candidate != domainforge.ApprovalState.Approved
207
+
208
+
209
+ # ---------------------------------------------------------------------------
210
+ # normalize_lookup_key
211
+ # ---------------------------------------------------------------------------
212
+
213
+ def test_normalize_lookup_key_is_callable():
214
+ """normalize_lookup_key is a callable function in domainforge."""
215
+ assert callable(domainforge.normalize_lookup_key)
216
+
217
+
218
+ def test_normalize_lookup_key_lowercases():
219
+ """normalize_lookup_key lowercases the input."""
220
+ result = domainforge.normalize_lookup_key("Supplier")
221
+ assert result == "supplier"
222
+
223
+
224
+ def test_normalize_lookup_key_trims_and_collapses_spaces():
225
+ """normalize_lookup_key trims and collapses multiple spaces."""
226
+ result = domainforge.normalize_lookup_key(" Hello World ")
227
+ assert result == "hello world"
228
+
229
+
230
+ def test_normalize_lookup_key_empty_string():
231
+ """normalize_lookup_key handles empty string input."""
232
+ result = domainforge.normalize_lookup_key("")
233
+ assert result == ""
234
+
235
+
236
+ def test_normalize_lookup_key_single_word():
237
+ """normalize_lookup_key handles single word input."""
238
+ assert domainforge.normalize_lookup_key("Warehouse") == "warehouse"
239
+
240
+
241
+ def test_normalize_lookup_key_is_idempotent():
242
+ """normalize_lookup_key is idempotent."""
243
+ first = domainforge.normalize_lookup_key(" Purchase Order ")
244
+ second = domainforge.normalize_lookup_key(first)
245
+ assert first == second
246
+
247
+
248
+ def test_normalize_lookup_key_preserves_underscores():
249
+ """normalize_lookup_key passes through underscores."""
250
+ result = domainforge.normalize_lookup_key("PURCHASE_ORDER")
251
+ assert result == "purchase_order"
252
+
253
+
254
+ # ---------------------------------------------------------------------------
255
+ # build_semantic_pack
256
+ # ---------------------------------------------------------------------------
257
+
258
+ def test_build_semantic_pack_is_callable():
259
+ """build_semantic_pack is a callable function in domainforge."""
260
+ assert callable(domainforge.build_semantic_pack)
261
+
262
+
263
+ def test_build_semantic_pack_returns_tuple():
264
+ """build_semantic_pack returns a tuple of (pack_json, errors)."""
265
+ result = domainforge.build_semantic_pack(make_minimal_build_input_json())
266
+ assert isinstance(result, tuple)
267
+ assert len(result) == 2
268
+
269
+
270
+ def test_build_semantic_pack_returns_valid_json():
271
+ """build_semantic_pack returns a valid JSON pack string."""
272
+ pack_json, errors = domainforge.build_semantic_pack(make_minimal_build_input_json())
273
+ parsed = json.loads(pack_json)
274
+ assert parsed["org_id"] == "test-org"
275
+ assert parsed["domain_id"] == "test-domain"
276
+
277
+
278
+ def test_build_semantic_pack_schema_version():
279
+ """Built pack has schema_version 0.3."""
280
+ pack_json, _ = domainforge.build_semantic_pack(make_minimal_build_input_json())
281
+ pack = json.loads(pack_json)
282
+ assert pack["schema_version"] == "0.3"
283
+
284
+
285
+ def test_build_semantic_pack_approval_state():
286
+ """Built candidate pack has approval_state=candidate."""
287
+ pack_json, _ = domainforge.build_semantic_pack(make_minimal_build_input_json())
288
+ pack = json.loads(pack_json)
289
+ assert pack["trust"]["approval_state"] == "candidate"
290
+
291
+
292
+ def test_build_semantic_pack_errors_is_list():
293
+ """build_semantic_pack errors is a list."""
294
+ _, errors = domainforge.build_semantic_pack(make_minimal_build_input_json())
295
+ assert isinstance(errors, list)
296
+
297
+
298
+ def test_build_semantic_pack_raises_on_invalid_json():
299
+ """build_semantic_pack raises ValueError on invalid JSON."""
300
+ with pytest.raises((ValueError, Exception)):
301
+ domainforge.build_semantic_pack("not valid json")
302
+
303
+
304
+ def test_build_semantic_pack_is_deterministic():
305
+ """Two builds from identical inputs produce the same pack content hash."""
306
+ input_json = make_minimal_build_input_json()
307
+ pack1, _ = domainforge.build_semantic_pack(input_json)
308
+ pack2, _ = domainforge.build_semantic_pack(input_json)
309
+ p1 = json.loads(pack1)
310
+ p2 = json.loads(pack2)
311
+ # The meaning fingerprint should be identical
312
+ assert p1["meaning_fingerprint"] == p2["meaning_fingerprint"]
313
+
314
+
315
+ # ---------------------------------------------------------------------------
316
+ # validate_semantic_pack
317
+ # ---------------------------------------------------------------------------
318
+
319
+ def test_validate_semantic_pack_is_callable():
320
+ """validate_semantic_pack is a callable function in domainforge."""
321
+ assert callable(domainforge.validate_semantic_pack)
322
+
323
+
324
+ def test_validate_semantic_pack_returns_list():
325
+ """validate_semantic_pack returns a list of diagnostic strings."""
326
+ result = domainforge.validate_semantic_pack(make_minimal_pack_json())
327
+ assert isinstance(result, list)
328
+
329
+
330
+ def test_validate_semantic_pack_raises_on_invalid_json():
331
+ """validate_semantic_pack raises on invalid JSON."""
332
+ with pytest.raises((ValueError, Exception)):
333
+ domainforge.validate_semantic_pack("not json")
334
+
335
+
336
+ def test_validate_semantic_pack_reports_schema_mismatch():
337
+ """validate_semantic_pack reports errors for wrong schema version."""
338
+ pack = json.loads(make_minimal_pack_json())
339
+ pack["schema_version"] = "9.9"
340
+ diagnostics = domainforge.validate_semantic_pack(json.dumps(pack))
341
+ assert len(diagnostics) > 0
342
+
343
+
344
+ # ---------------------------------------------------------------------------
345
+ # compute_pack_hash
346
+ # ---------------------------------------------------------------------------
347
+
348
+ def test_compute_pack_hash_is_callable():
349
+ """compute_pack_hash is a callable function in domainforge."""
350
+ assert callable(domainforge.compute_pack_hash)
351
+
352
+
353
+ def test_compute_pack_hash_returns_sha256_string():
354
+ """compute_pack_hash returns a string starting with sha256:."""
355
+ result = domainforge.compute_pack_hash(make_minimal_pack_json())
356
+ assert isinstance(result, str)
357
+ assert result.startswith("sha256:")
358
+
359
+
360
+ def test_compute_pack_hash_is_consistent():
361
+ """compute_pack_hash returns the same hash for the same pack."""
362
+ pack_json = make_minimal_pack_json()
363
+ h1 = domainforge.compute_pack_hash(pack_json)
364
+ h2 = domainforge.compute_pack_hash(pack_json)
365
+ assert h1 == h2
366
+
367
+
368
+ def test_compute_pack_hash_raises_on_invalid_json():
369
+ """compute_pack_hash raises on invalid JSON."""
370
+ with pytest.raises((ValueError, Exception)):
371
+ domainforge.compute_pack_hash("not json")
372
+
373
+
374
+ def test_compute_pack_hash_excludes_signature_fields():
375
+ """compute_pack_hash is stable regardless of signature fields."""
376
+ pack1 = json.loads(make_minimal_pack_json())
377
+ h1 = domainforge.compute_pack_hash(json.dumps(pack1))
378
+
379
+ # Add a signature field — the hash should remain unchanged
380
+ pack1["trust"]["signature"] = "some-base64-signature"
381
+ pack1["trust"]["signature_state"] = "signed"
382
+ h2 = domainforge.compute_pack_hash(json.dumps(pack1))
383
+
384
+ assert h1 == h2
385
+
386
+
387
+ # ---------------------------------------------------------------------------
388
+ # validate_graph_with_pack
389
+ # ---------------------------------------------------------------------------
390
+
391
+ def test_validate_graph_with_pack_is_callable():
392
+ """validate_graph_with_pack is a callable function in domainforge."""
393
+ assert callable(domainforge.validate_graph_with_pack)
394
+
395
+
396
+ def test_validate_graph_with_pack_returns_json_string():
397
+ """validate_graph_with_pack returns a parseable JSON string."""
398
+ result = domainforge.validate_graph_with_pack(
399
+ make_minimal_pack_json(),
400
+ "test://source",
401
+ make_default_options_json(),
402
+ )
403
+ assert isinstance(result, str)
404
+ parsed = json.loads(result)
405
+ assert "status" in parsed
406
+
407
+
408
+ def test_validate_graph_with_pack_raises_on_invalid_pack_json():
409
+ """validate_graph_with_pack raises on invalid pack JSON."""
410
+ with pytest.raises((ValueError, Exception)):
411
+ domainforge.validate_graph_with_pack("bad json", "test://source", make_default_options_json())
412
+
413
+
414
+ def test_validate_graph_with_pack_raises_on_invalid_options_json():
415
+ """validate_graph_with_pack raises on invalid options JSON."""
416
+ with pytest.raises((ValueError, Exception)):
417
+ domainforge.validate_graph_with_pack(make_minimal_pack_json(), "test://source", "bad options")
418
+
419
+
420
+ # ---------------------------------------------------------------------------
421
+ # diff_packs
422
+ # ---------------------------------------------------------------------------
423
+
424
+ def test_diff_packs_is_callable():
425
+ """diff_packs is a callable function in domainforge."""
426
+ assert callable(domainforge.diff_packs)
427
+
428
+
429
+ def test_diff_packs_returns_json_string():
430
+ """diff_packs returns a parseable JSON string."""
431
+ pack_json = make_minimal_pack_json()
432
+ result = domainforge.diff_packs(pack_json, pack_json)
433
+ assert isinstance(result, str)
434
+ json.loads(result) # must parse without error
435
+
436
+
437
+ def test_diff_packs_same_pack_has_no_breaking_changes():
438
+ """diff_packs reports no breaking changes when comparing a pack to itself."""
439
+ pack_json = make_minimal_pack_json()
440
+ diff = json.loads(domainforge.diff_packs(pack_json, pack_json))
441
+ entries = diff.get("entries", diff) if isinstance(diff, dict) else diff
442
+ if isinstance(entries, list):
443
+ breaking = [e for e in entries if e.get("classification") == "breaking"]
444
+ assert len(breaking) == 0
445
+
446
+
447
+ def test_diff_packs_detects_added_concept():
448
+ """diff_packs detects a new concept added to the new pack."""
449
+ old_pack = json.loads(make_minimal_pack_json())
450
+ new_pack = json.loads(make_minimal_pack_json())
451
+ new_pack["concepts"].append({
452
+ "id": "warehouse",
453
+ "canonical_name": "Warehouse",
454
+ "kind": "entity",
455
+ "status": "active",
456
+ "definition": {
457
+ "text": "A storage facility.",
458
+ "definition_hash": "",
459
+ "decision_ref": "dec_warehouse",
460
+ },
461
+ "owner": "owner@test.com",
462
+ "source_refs": [],
463
+ "examples": [],
464
+ "counterexamples": [],
465
+ "allowed_predicates": [],
466
+ "valid_contexts": [],
467
+ })
468
+ diff_str = domainforge.diff_packs(json.dumps(old_pack), json.dumps(new_pack))
469
+ diff = json.loads(diff_str)
470
+ entries = diff.get("entries", diff) if isinstance(diff, dict) else diff
471
+ if isinstance(entries, list):
472
+ additive = [e for e in entries if e.get("classification") in ("additive", "add")]
473
+ assert len(additive) > 0
474
+ else:
475
+ assert diff_str # at minimum, a non-empty response
476
+
477
+
478
+ def test_diff_packs_raises_on_invalid_old_json():
479
+ """diff_packs raises on invalid old pack JSON."""
480
+ with pytest.raises((ValueError, Exception)):
481
+ domainforge.diff_packs("bad json", make_minimal_pack_json())
482
+
483
+
484
+ def test_diff_packs_raises_on_invalid_new_json():
485
+ """diff_packs raises on invalid new pack JSON."""
486
+ with pytest.raises((ValueError, Exception)):
487
+ domainforge.diff_packs(make_minimal_pack_json(), "bad json")
488
+
489
+
490
+ # ---------------------------------------------------------------------------
491
+ # resolve_concept
492
+ # ---------------------------------------------------------------------------
493
+
494
+ def test_resolve_concept_is_callable():
495
+ """resolve_concept is a callable function in domainforge."""
496
+ assert callable(domainforge.resolve_concept)
497
+
498
+
499
+ def test_resolve_concept_returns_json_string():
500
+ """resolve_concept returns a parseable JSON string."""
501
+ result = domainforge.resolve_concept("Supplier", make_minimal_pack_json(), make_default_options_json())
502
+ assert isinstance(result, str)
503
+ parsed = json.loads(result)
504
+ assert "semantic_truth" in parsed
505
+
506
+
507
+ def test_resolve_concept_known_term_is_valid():
508
+ """resolve_concept returns valid semantic_truth for a known active concept."""
509
+ result = domainforge.resolve_concept("Supplier", make_minimal_pack_json(), make_default_options_json())
510
+ parsed = json.loads(result)
511
+ assert "valid" in parsed["semantic_truth"].lower()
512
+
513
+
514
+ def test_resolve_concept_unknown_term_is_unknown():
515
+ """resolve_concept returns unknown semantic_truth for an unrecognized term."""
516
+ result = domainforge.resolve_concept("CompletelyUnknownTerm", make_minimal_pack_json(), make_default_options_json())
517
+ parsed = json.loads(result)
518
+ assert "unknown" in parsed["semantic_truth"].lower()
519
+ assert parsed["resolved_concept_id"] is None
520
+
521
+
522
+ def test_resolve_concept_normalizes_lookup_key():
523
+ """resolve_concept normalizes the input term before lookup."""
524
+ r1 = json.loads(domainforge.resolve_concept("Supplier", make_minimal_pack_json(), make_default_options_json()))
525
+ r2 = json.loads(domainforge.resolve_concept(" supplier ", make_minimal_pack_json(), make_default_options_json()))
526
+ assert r1["resolved_concept_id"] == r2["resolved_concept_id"]
527
+
528
+
529
+ def test_resolve_concept_raises_on_invalid_pack_json():
530
+ """resolve_concept raises on invalid pack JSON."""
531
+ with pytest.raises((ValueError, Exception)):
532
+ domainforge.resolve_concept("Supplier", "bad json", make_default_options_json())
533
+
534
+
535
+ def test_resolve_concept_raises_on_invalid_options_json():
536
+ """resolve_concept raises on invalid options JSON."""
537
+ with pytest.raises((ValueError, Exception)):
538
+ domainforge.resolve_concept("Supplier", make_minimal_pack_json(), "bad options")
539
+
540
+
541
+ # ---------------------------------------------------------------------------
542
+ # SemanticPack class
543
+ # ---------------------------------------------------------------------------
544
+
545
+ def test_semantic_pack_class_importable():
546
+ """SemanticPack class is importable from domainforge."""
547
+ assert hasattr(domainforge, "SemanticPack")
548
+
549
+
550
+ def test_semantic_pack_from_json():
551
+ """SemanticPack.from_json parses a valid pack JSON string."""
552
+ pack = domainforge.SemanticPack.from_json(make_minimal_pack_json())
553
+ assert pack is not None
554
+
555
+
556
+ def test_semantic_pack_pack_id():
557
+ """SemanticPack.pack_id() returns the pack identifier."""
558
+ pack = domainforge.SemanticPack.from_json(make_minimal_pack_json())
559
+ assert pack.pack_id() == "test-org/test-domain/1.0.0"
560
+
561
+
562
+ def test_semantic_pack_schema_version():
563
+ """SemanticPack.schema_version() returns '0.3'."""
564
+ pack = domainforge.SemanticPack.from_json(make_minimal_pack_json())
565
+ assert pack.schema_version() == "0.3"
566
+
567
+
568
+ def test_semantic_pack_approval_state():
569
+ """SemanticPack.approval_state() returns ApprovalState.Candidate."""
570
+ pack = domainforge.SemanticPack.from_json(make_minimal_pack_json())
571
+ assert pack.approval_state() == domainforge.ApprovalState.Candidate
572
+
573
+
574
+ def test_semantic_pack_signature_state():
575
+ """SemanticPack.signature_state() returns SignatureState.Unsigned."""
576
+ pack = domainforge.SemanticPack.from_json(make_minimal_pack_json())
577
+ assert pack.signature_state() == domainforge.SignatureState.Unsigned
578
+
579
+
580
+ def test_semantic_pack_concept_count():
581
+ """SemanticPack.concept_count() returns 1 for the minimal pack."""
582
+ pack = domainforge.SemanticPack.from_json(make_minimal_pack_json())
583
+ assert pack.concept_count() == 1
584
+
585
+
586
+ def test_semantic_pack_alias_count():
587
+ """SemanticPack.alias_count() returns 0 for the minimal pack with no aliases."""
588
+ pack = domainforge.SemanticPack.from_json(make_minimal_pack_json())
589
+ assert pack.alias_count() == 0
590
+
591
+
592
+ def test_semantic_pack_to_json_roundtrip():
593
+ """SemanticPack.to_json() produces valid JSON that can be re-parsed."""
594
+ pack = domainforge.SemanticPack.from_json(make_minimal_pack_json())
595
+ json_out = pack.to_json()
596
+ reparsed = json.loads(json_out)
597
+ assert reparsed["pack_id"] == "test-org/test-domain/1.0.0"
598
+
599
+
600
+ def test_semantic_pack_from_json_raises_on_invalid_json():
601
+ """SemanticPack.from_json raises on invalid JSON."""
602
+ with pytest.raises((ValueError, Exception)):
603
+ domainforge.SemanticPack.from_json("not json")
604
+
605
+
606
+ def test_semantic_pack_pack_content_hash_starts_with_sha256():
607
+ """SemanticPack.pack_content_hash() returns a sha256: prefixed string."""
608
+ pack = domainforge.SemanticPack.from_json(make_minimal_pack_json())
609
+ h = pack.pack_content_hash()
610
+ assert isinstance(h, str)
611
+ assert h.startswith("sha256:")
612
+
613
+
614
+ # ---------------------------------------------------------------------------
615
+ # sign_pack / verify_pack_signature
616
+ # ---------------------------------------------------------------------------
617
+
618
+ def test_sign_pack_is_callable():
619
+ """sign_pack is a callable function in domainforge."""
620
+ assert callable(domainforge.sign_pack)
621
+
622
+
623
+ def test_verify_pack_signature_is_callable():
624
+ """verify_pack_signature is a callable function in domainforge."""
625
+ assert callable(domainforge.verify_pack_signature)
626
+
627
+
628
+ def test_sign_pack_raises_on_invalid_pack_json():
629
+ """sign_pack raises on invalid pack JSON."""
630
+ fake_pem = "-----BEGIN PRIVATE KEY-----\nnot-real\n-----END PRIVATE KEY-----"
631
+ with pytest.raises((ValueError, Exception)):
632
+ domainforge.sign_pack("bad json", fake_pem)
633
+
634
+
635
+ def test_verify_pack_signature_raises_on_invalid_pack_json():
636
+ """verify_pack_signature raises on invalid pack JSON."""
637
+ fake_pem = "-----BEGIN PUBLIC KEY-----\nnot-real\n-----END PUBLIC KEY-----"
638
+ with pytest.raises((ValueError, Exception)):
639
+ domainforge.verify_pack_signature("bad json", fake_pem)