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,93 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Update `.vscode/launch.json` program path for "Debug Rust Test (auto)" configuration
4
+ to point at the prepared debug binary `target/debug/deps/sea_debug_test`.
5
+
6
+ Usage:
7
+ python3 scripts/update_launch_program.py --program <path>
8
+
9
+ If the file does not exist, writes the default program path.
10
+ """
11
+
12
+ import argparse
13
+ import json
14
+ import os
15
+ import sys
16
+
17
+
18
+ def find_config_index(configs, name):
19
+ for idx, c in enumerate(configs):
20
+ if c.get("name") == name:
21
+ return idx
22
+ return -1
23
+
24
+
25
+ def main():
26
+ parser = argparse.ArgumentParser()
27
+ parser.add_argument("--program", help="Path to the rust test binary (symlink)")
28
+ parser.add_argument("--launch", default=".vscode/launch.json")
29
+ parser.add_argument("--config-name", default="Debug Rust Test (auto)")
30
+ args = parser.parse_args()
31
+
32
+ # Prefer a workspace-relative program path so launch.json remains portable
33
+ program_path = args.program or os.path.join(
34
+ "${workspaceFolder}", "target", "debug", "deps", "sea_debug_test"
35
+ )
36
+ # If program_path is absolute and inside the workspace, replace the prefix with ${workspaceFolder}
37
+ workspace_prefix = os.path.abspath(".")
38
+ if "${workspaceFolder}" not in program_path:
39
+ abs_program_path = os.path.abspath(program_path)
40
+ normalized_workspace = os.path.normcase(os.path.normpath(workspace_prefix))
41
+ normalized_program = os.path.normcase(os.path.normpath(abs_program_path))
42
+ if normalized_program.startswith(normalized_workspace):
43
+ rel_path = os.path.relpath(abs_program_path, workspace_prefix)
44
+ program_path = os.path.join("${workspaceFolder}", rel_path).replace("\\", "/")
45
+ else:
46
+ program_path = abs_program_path.replace("\\", "/")
47
+ launch_path = args.launch
48
+
49
+ if not os.path.exists(launch_path):
50
+ print(
51
+ f"Warning: {launch_path} not found, creating a new launch.json with default config"
52
+ )
53
+ launch = {"version": "0.2.0", "configurations": []}
54
+ else:
55
+ try:
56
+ with open(launch_path, "r") as f:
57
+ launch = json.load(f)
58
+ except json.JSONDecodeError as err:
59
+ print(f"Failed to parse {launch_path}: {err}")
60
+ sys.exit(1)
61
+
62
+ configs = launch.setdefault("configurations", [])
63
+ idx = find_config_index(configs, args.config_name)
64
+
65
+ if idx == -1:
66
+ # If not found, add a new configuration stub
67
+ configs.append(
68
+ {
69
+ "name": args.config_name,
70
+ "type": "codelldb",
71
+ "request": "launch",
72
+ "program": program_path,
73
+ "args": ["--nocapture", "${input:rustTestName}"],
74
+ "cwd": "${workspaceFolder}",
75
+ "stopAtEntry": False,
76
+ "terminal": "integrated",
77
+ }
78
+ )
79
+ print(f"Added {args.config_name} to launch.json with program={program_path}")
80
+ else:
81
+ configs[idx]["program"] = program_path
82
+ print(f"Updated {args.config_name} program to {program_path}")
83
+
84
+ launch_dir = os.path.dirname(launch_path)
85
+ if launch_dir:
86
+ os.makedirs(launch_dir, exist_ok=True)
87
+ with open(launch_path, "w") as f:
88
+ json.dump(launch, f, indent=2)
89
+ print(f"Wrote updated launch.json: {launch_path}")
90
+
91
+
92
+ if __name__ == "__main__":
93
+ main()
@@ -0,0 +1,27 @@
1
+ # Secrets directory
2
+
3
+ This folder contains templates for project secrets and instructions to encrypt them using SOPS and Age.
4
+
5
+ - `secrets.template.yaml` — placeholders only. Do not add real secrets here.
6
+ - `secrets.yaml` — recommended final encrypted file, committed to the repo as encrypted by sops.
7
+
8
+ Quick usage:
9
+
10
+ ```bash
11
+ # copy the template to a file, populate it with real values (locally only)
12
+ cp secrets/secrets.template.yaml secrets/secrets.yaml
13
+ # encrypt in-place (replace AGE recipient)
14
+ sops --encrypt --age "age1PUBKEY" --in-place secrets/secrets.yaml
15
+ # verify, commit
16
+ git add secrets/secrets.yaml && git commit -m "Add encrypted secrets"
17
+ ```
18
+
19
+ Get your age public key (example):
20
+
21
+ ```bash
22
+ # generate a new key pair and display the public key
23
+ age-keygen -o ~/.config/age/identity.key
24
+ # the command prints "Public key: age1..." which you can use in .sops.yaml or sops --age
25
+ ```
26
+
27
+ If you accidentally commit unencrypted secrets, remove them from git history, rotate any keys, and inform the team.
@@ -0,0 +1,21 @@
1
+ #ENC[AES256_GCM,data:6pK7hcKpV/T/PZUR6LoVAFhLk/QI2HS7y9743J+IQRcEzzaNCIAg8f86POhGLwC1tl7m4nIcRA==,iv:7dZQwgSghllovVRcTylOifbL8jV+WV8kJOijpGyPg6M=,tag:TlNZlriOr9yQLwmaBeGrmA==,type:comment]
2
+ #ENC[AES256_GCM,data:RxrXMhdDFDkWjGUZMXMUGp+esb6lcIooaEuG3wAUi0R4pmzjD6BzQktHo5pcHrqA5UmvE5+rZg==,iv:OqpXUv5NsmOAllRFC/axsR8BFHx48iQzJauo3ZrGPUE=,tag:TVY8WqkSSWQivmVTbMWSZw==,type:comment]
3
+ PYPI_TEST_API_TOKEN: ENC[AES256_GCM,data:pfbrKqZpTIOmvrjbPR8PwysN8PskPLSgKBmeYtL/HVhPpQVRM6zs/gAB1Kvey2sx4YaL8KfQdJd+T9JnOQuMDgH0EqYsMS7I7tuP4dJbQtOL0hFRuJBPs+nTNeSQ6LV95zW6curqNhXC9shYIg3jQNiCej2mewlYEshnb19MwSVyCLKZ3eejEEmfFadQN0KPPJWX9l6nxsepZ9ke8v9gVRMjT37Sf/sN5oGDPv18ry72bNBLPvNvxjc=,iv:fWLP0faTnWPrWfvyd2GEr3+Qn0Ls7Rk4VbKosMioKEA=,tag:YUm3PYmCQFSoUoZ3Php9+w==,type:str]
4
+ PYPI_API_TOKEN: ENC[AES256_GCM,data:ErgZWsXEeNvpYFjOooqzIXei8uBUpZkm7NmTS6FrOc/Cn3V53uSWs0Z96eBVh/4DbZCEOBze5EUstWYNjgGpmug/D7a4AE7Y171k/oDDPBfXVQyuQi1FZhGz2eRBxq7KjVyJbc850nyDGGoVpxpGMplXogRS1sQvg+kiV50bxgD9PzkFi5dvzrwoRxONu0keJjgDT50hEev18w4M85paJ4dXufD6gJazuI4sMOGV7U1Mn1Q=,iv:meNgGdAfDMYtPOdtZdXKngF2vOu5wFvkeMN6vvMeRwQ=,tag:tEBH4jhMbW9tFb6ukQ3oqg==,type:str]
5
+ NPM_TOKEN: ENC[AES256_GCM,data:cHmw67qQfoMEVyOR2a0gW6y2mTlU1E5ujbY5t0SHV9DoS2wY9MDiOg==,iv:T5iTNP0jZtf+nsa8HTC8yepqdjmUPISg9biU1p5EMC4=,tag:2yJc5aLgxvIWEUooZdPgYg==,type:str]
6
+ CARGO_REGISTRY_TOKEN: ENC[AES256_GCM,data:8bf8hkiv0myqffATBNYdvJm5tf3P5llxxdyo8C9/YuYeD0k=,iv:zg1qdDwClMSs4LmXR1pqSlla4jOoxaGRE1aNQnDl/s4=,tag:UOekLRYsxcxTLw/0AphXOg==,type:str]
7
+ sops:
8
+ age:
9
+ - recipient: age1mq5sj8gj4k5vqtgefkuvs05nghanzhgmcqkxxspk0vffq9hxm5ssnj93a5
10
+ enc: |
11
+ -----BEGIN AGE ENCRYPTED FILE-----
12
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhZGNoaWFnK0VUR3Y4WWlq
13
+ V2pvU25WZjZPam1WenRzNmtCc0dtOFBQbDBVCjBrd2pXQnlnY0JUVWsrdGZPcFVy
14
+ cXFoUThNWjdDNTJOTXFERWlHK2ZzRUkKLS0tIFUzblkveE5DYXRVUldQemE4T3pC
15
+ dGZabzFkMEVzZDZYcmFZTWFSTkNXejAKPHhdfuiXu4ZCpoM3cQi48Pc0i17yCqph
16
+ QD8lsKfQXdFvbzz6XxXwpsYcXH+eQpvFiMOgrIkmuz7Zegfp+Oa++g==
17
+ -----END AGE ENCRYPTED FILE-----
18
+ lastmodified: "2025-12-07T02:12:19Z"
19
+ mac: ENC[AES256_GCM,data:h9h0FqOkoZFutnzkJ4w4zZ/Yg2aTspObl/Hyt6PcGylZNTsWpaw2VNgvO0pNkzpLO3RoEvcws74YrWAMycP4NR5xml73LXFPEDYNiDnSHqwSaWTJla6ED9oGGEmG2Zs8GDS/UyhtqMsk+pEllmi4C+1TcAp5JhDwVNdLmhHvRVE=,iv:PbWb/uSG/hUciJRGNhPORXITCahACR5HDL6g+5dRzQs=,tag:fA/X0hgGcjlzvTzF0GlHHQ==,type:str]
20
+ unencrypted_suffix: _unencrypted
21
+ version: 3.11.0
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env python3
2
+ import domainforge
3
+
4
+ # Create a supply chain model
5
+ graph = domainforge.Graph()
6
+
7
+ # Create entities
8
+ supplier = domainforge.Entity("Supplier", "supply_chain")
9
+ warehouse = domainforge.Entity("Warehouse", "supply_chain")
10
+ store = domainforge.Entity("Store", "retail")
11
+
12
+ # Create resources
13
+ widgets = domainforge.Resource("Widgets", "units")
14
+ gadgets = domainforge.Resource("Gadgets", "units")
15
+
16
+ # Add to graph
17
+ graph.add_entity(supplier)
18
+ graph.add_entity(warehouse)
19
+ graph.add_entity(store)
20
+ graph.add_resource(widgets)
21
+ graph.add_resource(gadgets)
22
+
23
+ # Create flows
24
+ flow1 = domainforge.Flow(widgets.id, supplier.id, warehouse.id, 500.0)
25
+ flow2 = domainforge.Flow(widgets.id, warehouse.id, store.id, 300.0)
26
+ flow3 = domainforge.Flow(gadgets.id, supplier.id, warehouse.id, 200.0)
27
+
28
+ graph.add_flow(flow1)
29
+ graph.add_flow(flow2)
30
+ graph.add_flow(flow3)
31
+
32
+ # Query the graph
33
+ print(f"Graph Statistics:")
34
+ print(f" Entities: {graph.entity_count()}")
35
+ print(f" Resources: {graph.resource_count()}")
36
+ print(f" Flows: {graph.flow_count()}")
37
+ print()
38
+
39
+ # Find entity by name
40
+ warehouse_id = graph.find_entity_by_name("Warehouse")
41
+ if warehouse_id is None:
42
+ raise ValueError("Warehouse entity not found in graph")
43
+ print(f"Warehouse ID: {warehouse_id}")
44
+ print()
45
+
46
+ # Get flows from warehouse
47
+ flows_from_warehouse = graph.flows_from(warehouse_id)
48
+ print(f"Flows from Warehouse: {len(flows_from_warehouse)}")
49
+ for flow in flows_from_warehouse:
50
+ print(f" - {flow.quantity} units")
51
+ print()
52
+
53
+ # Parse DSL source
54
+ dsl_source = '''
55
+ Entity "Supplier" in supply_chain
56
+ Entity "Factory" in manufacturing
57
+ Resource "Steel" tons
58
+ Flow "Steel" from "Supplier" to "Factory" quantity 100
59
+ '''
60
+
61
+ parsed = domainforge.Graph.parse(dsl_source)
62
+ print(f"Parsed Graph:")
63
+ print(f" Entities: {parsed.entity_count()}")
64
+ print(f" Resources: {parsed.resource_count()}")
65
+ print(f" Flows: {parsed.flow_count()}")
66
+
67
+ print("\nAll tests passed! Python bindings are working correctly.")
@@ -0,0 +1,328 @@
1
+ """
2
+ Tests for the new Policy Authority API exports added in this PR.
3
+
4
+ Covers:
5
+ - Enum exports: FinalDecision, PolicyModality, SourceClass, ClaimLevel
6
+ - Class: AuthorityEnvironment
7
+ - Function: evaluate_authority
8
+ """
9
+
10
+ import json
11
+ import pytest
12
+ import domainforge
13
+
14
+
15
+ # ---------------------------------------------------------------------------
16
+ # Helpers
17
+ # ---------------------------------------------------------------------------
18
+
19
+ def make_minimal_config_json(packs=None) -> str:
20
+ if packs is None:
21
+ packs = []
22
+ return json.dumps({
23
+ "resolver_semantics_version": "0.4",
24
+ "specificity_profile": {
25
+ "id": "default",
26
+ "dimensions": [],
27
+ "scoring_rules": {},
28
+ "hash": "",
29
+ },
30
+ "unknown_handling": {
31
+ "permission": {"default": "escalate"},
32
+ "prohibition": {"default": "deny"},
33
+ "obligation": {"default": "escalate"},
34
+ "override_": {"default": "not_applicable"},
35
+ },
36
+ "fact_sources": [],
37
+ "fact_transforms": [],
38
+ "authority_packs": packs,
39
+ "strict_mode": False,
40
+ "compatibility_lowering_version": "0.4",
41
+ "resolver_version": "0.1",
42
+ })
43
+
44
+
45
+ def make_minimal_request_json(operation="TestAction", resource_type="Order") -> str:
46
+ return json.dumps({
47
+ "request_id": "req-001",
48
+ "actor": {"id": "user-1"},
49
+ "operation": operation,
50
+ "resource": {"type": resource_type},
51
+ "context": {},
52
+ "requested_at": "2026-06-07T00:00:00Z",
53
+ })
54
+
55
+
56
+ # ---------------------------------------------------------------------------
57
+ # Enum import tests
58
+ # ---------------------------------------------------------------------------
59
+
60
+ def test_final_decision_enum_importable():
61
+ """FinalDecision enum is importable from domainforge."""
62
+ assert hasattr(domainforge, "FinalDecision")
63
+
64
+
65
+ def test_final_decision_enum_values():
66
+ """FinalDecision has Allow, Deny, Escalate, NotApplicable, Reject members."""
67
+ assert hasattr(domainforge.FinalDecision, "Allow")
68
+ assert hasattr(domainforge.FinalDecision, "Deny")
69
+ assert hasattr(domainforge.FinalDecision, "Escalate")
70
+ assert hasattr(domainforge.FinalDecision, "NotApplicable")
71
+ assert hasattr(domainforge.FinalDecision, "Reject")
72
+
73
+
74
+ def test_final_decision_members_are_distinct():
75
+ """FinalDecision enum members are not equal to each other."""
76
+ assert domainforge.FinalDecision.Allow != domainforge.FinalDecision.Deny
77
+ assert domainforge.FinalDecision.Allow != domainforge.FinalDecision.Escalate
78
+ assert domainforge.FinalDecision.Allow != domainforge.FinalDecision.NotApplicable
79
+ assert domainforge.FinalDecision.Allow != domainforge.FinalDecision.Reject
80
+ assert domainforge.FinalDecision.Deny != domainforge.FinalDecision.Reject
81
+
82
+
83
+ def test_policy_modality_enum_importable():
84
+ """PolicyModality enum is importable from domainforge."""
85
+ assert hasattr(domainforge, "PolicyModality")
86
+
87
+
88
+ def test_policy_modality_enum_values():
89
+ """PolicyModality has Permission, Prohibition, Obligation, Override members."""
90
+ assert hasattr(domainforge.PolicyModality, "Permission")
91
+ assert hasattr(domainforge.PolicyModality, "Prohibition")
92
+ assert hasattr(domainforge.PolicyModality, "Obligation")
93
+ assert hasattr(domainforge.PolicyModality, "Override")
94
+
95
+
96
+ def test_policy_modality_members_are_distinct():
97
+ """PolicyModality enum members are not equal to each other."""
98
+ assert domainforge.PolicyModality.Permission != domainforge.PolicyModality.Prohibition
99
+ assert domainforge.PolicyModality.Obligation != domainforge.PolicyModality.Override
100
+
101
+
102
+ def test_source_class_enum_importable():
103
+ """SourceClass enum is importable from domainforge."""
104
+ assert hasattr(domainforge, "SourceClass")
105
+
106
+
107
+ def test_source_class_enum_values():
108
+ """SourceClass has all seven members."""
109
+ for member in [
110
+ "CallerSupplied", "RuntimeObserved", "SystemOfRecord",
111
+ "Attested", "ManualApproval", "Derived", "UnknownSource",
112
+ ]:
113
+ assert hasattr(domainforge.SourceClass, member), f"Missing SourceClass.{member}"
114
+
115
+
116
+ def test_source_class_members_are_distinct():
117
+ """SourceClass enum members are not equal to each other."""
118
+ assert domainforge.SourceClass.CallerSupplied != domainforge.SourceClass.SystemOfRecord
119
+ assert domainforge.SourceClass.Derived != domainforge.SourceClass.UnknownSource
120
+
121
+
122
+ def test_claim_level_enum_importable():
123
+ """ClaimLevel enum is importable from domainforge."""
124
+ assert hasattr(domainforge, "ClaimLevel")
125
+
126
+
127
+ def test_claim_level_enum_values():
128
+ """ClaimLevel has AuditBacked, Validated, FormallyProven members."""
129
+ assert hasattr(domainforge.ClaimLevel, "AuditBacked")
130
+ assert hasattr(domainforge.ClaimLevel, "Validated")
131
+ assert hasattr(domainforge.ClaimLevel, "FormallyProven")
132
+
133
+
134
+ def test_claim_level_members_are_distinct():
135
+ """ClaimLevel enum members are not equal to each other."""
136
+ assert domainforge.ClaimLevel.AuditBacked != domainforge.ClaimLevel.Validated
137
+ assert domainforge.ClaimLevel.Validated != domainforge.ClaimLevel.FormallyProven
138
+
139
+
140
+ # ---------------------------------------------------------------------------
141
+ # AuthorityEnvironment class
142
+ # ---------------------------------------------------------------------------
143
+
144
+ def test_authority_environment_class_importable():
145
+ """AuthorityEnvironment class is importable from domainforge."""
146
+ assert hasattr(domainforge, "AuthorityEnvironment")
147
+
148
+
149
+ def test_authority_environment_instantiation():
150
+ """AuthorityEnvironment can be instantiated from a minimal config JSON."""
151
+ env = domainforge.AuthorityEnvironment(make_minimal_config_json())
152
+ assert env is not None
153
+
154
+
155
+ def test_authority_environment_raises_on_invalid_config():
156
+ """AuthorityEnvironment raises ValueError on invalid config JSON."""
157
+ with pytest.raises((ValueError, Exception)):
158
+ domainforge.AuthorityEnvironment("not valid json")
159
+
160
+
161
+ def test_authority_environment_validate():
162
+ """AuthorityEnvironment.validate() succeeds for a valid config."""
163
+ env = domainforge.AuthorityEnvironment(make_minimal_config_json())
164
+ # Should not raise
165
+ env.validate()
166
+
167
+
168
+ def test_authority_environment_evaluate_returns_tuple():
169
+ """AuthorityEnvironment.evaluate() returns a (trace_json, decision_json) tuple."""
170
+ env = domainforge.AuthorityEnvironment(make_minimal_config_json())
171
+ env.validate()
172
+ result = env.evaluate(make_minimal_request_json())
173
+ assert isinstance(result, tuple)
174
+ assert len(result) == 2
175
+
176
+
177
+ def test_authority_environment_evaluate_returns_valid_json():
178
+ """AuthorityEnvironment.evaluate() returns valid JSON strings."""
179
+ env = domainforge.AuthorityEnvironment(make_minimal_config_json())
180
+ env.validate()
181
+ trace_json, decision_json = env.evaluate(make_minimal_request_json())
182
+ json.loads(trace_json) # must parse
183
+ json.loads(decision_json) # must parse
184
+
185
+
186
+ def test_authority_environment_evaluate_raises_on_invalid_request():
187
+ """AuthorityEnvironment.evaluate() raises on invalid request JSON."""
188
+ env = domainforge.AuthorityEnvironment(make_minimal_config_json())
189
+ env.validate()
190
+ with pytest.raises((ValueError, Exception)):
191
+ env.evaluate("not valid json")
192
+
193
+
194
+ def test_authority_environment_evaluate_with_empty_facts():
195
+ """AuthorityEnvironment.evaluate() accepts empty facts array."""
196
+ env = domainforge.AuthorityEnvironment(make_minimal_config_json())
197
+ env.validate()
198
+ trace_json, decision_json = env.evaluate(make_minimal_request_json(), "[]")
199
+ decision = json.loads(decision_json)
200
+ assert "final_decision" in decision
201
+
202
+
203
+ def test_authority_environment_not_applicable_with_no_packs():
204
+ """AuthorityEnvironment returns not_applicable when no packs are loaded."""
205
+ env = domainforge.AuthorityEnvironment(make_minimal_config_json([]))
206
+ env.validate()
207
+ _, decision_json = env.evaluate(make_minimal_request_json())
208
+ decision = json.loads(decision_json)
209
+ assert decision["final_decision"] == "not_applicable"
210
+
211
+
212
+ def test_authority_environment_repr():
213
+ """AuthorityEnvironment has a string representation."""
214
+ env = domainforge.AuthorityEnvironment(make_minimal_config_json())
215
+ assert isinstance(str(env), str)
216
+
217
+
218
+ # ---------------------------------------------------------------------------
219
+ # evaluate_authority function
220
+ # ---------------------------------------------------------------------------
221
+
222
+ def test_evaluate_authority_is_callable():
223
+ """evaluate_authority is a callable function in domainforge."""
224
+ assert callable(domainforge.evaluate_authority)
225
+
226
+
227
+ def test_evaluate_authority_returns_tuple():
228
+ """evaluate_authority returns a (trace_json, decision_json) tuple."""
229
+ result = domainforge.evaluate_authority(
230
+ make_minimal_config_json(),
231
+ make_minimal_request_json(),
232
+ )
233
+ assert isinstance(result, tuple)
234
+ assert len(result) == 2
235
+
236
+
237
+ def test_evaluate_authority_returns_valid_json():
238
+ """evaluate_authority returns valid JSON strings in both elements."""
239
+ trace_json, decision_json = domainforge.evaluate_authority(
240
+ make_minimal_config_json(),
241
+ make_minimal_request_json(),
242
+ )
243
+ json.loads(trace_json)
244
+ json.loads(decision_json)
245
+
246
+
247
+ def test_evaluate_authority_raises_on_invalid_config():
248
+ """evaluate_authority raises on invalid config JSON."""
249
+ with pytest.raises((ValueError, Exception)):
250
+ domainforge.evaluate_authority("not valid json", make_minimal_request_json())
251
+
252
+
253
+ def test_evaluate_authority_raises_on_invalid_request():
254
+ """evaluate_authority raises on invalid request JSON."""
255
+ with pytest.raises((ValueError, Exception)):
256
+ domainforge.evaluate_authority(make_minimal_config_json(), "not valid json")
257
+
258
+
259
+ def test_evaluate_authority_raises_on_invalid_facts():
260
+ """evaluate_authority raises on invalid facts JSON."""
261
+ with pytest.raises((ValueError, Exception)):
262
+ domainforge.evaluate_authority(
263
+ make_minimal_config_json(),
264
+ make_minimal_request_json(),
265
+ "not valid json",
266
+ )
267
+
268
+
269
+ def test_evaluate_authority_not_applicable_with_no_packs():
270
+ """evaluate_authority returns not_applicable when no packs are loaded."""
271
+ _, decision_json = domainforge.evaluate_authority(
272
+ make_minimal_config_json([]),
273
+ make_minimal_request_json(),
274
+ )
275
+ decision = json.loads(decision_json)
276
+ assert decision["final_decision"] == "not_applicable"
277
+
278
+
279
+ def test_evaluate_authority_decision_has_final_decision_field():
280
+ """evaluate_authority decision contains a final_decision field."""
281
+ _, decision_json = domainforge.evaluate_authority(
282
+ make_minimal_config_json(),
283
+ make_minimal_request_json(),
284
+ )
285
+ decision = json.loads(decision_json)
286
+ assert "final_decision" in decision
287
+ valid_decisions = {"allow", "deny", "escalate", "not_applicable", "reject"}
288
+ assert decision["final_decision"] in valid_decisions
289
+
290
+
291
+ def test_evaluate_authority_trace_references_request_id():
292
+ """evaluate_authority trace references the request_id from the request."""
293
+ trace_json, _ = domainforge.evaluate_authority(
294
+ make_minimal_config_json(),
295
+ make_minimal_request_json(),
296
+ )
297
+ assert "req-001" in trace_json
298
+
299
+
300
+ def test_evaluate_authority_with_explicit_empty_facts():
301
+ """evaluate_authority accepts explicit empty facts JSON array."""
302
+ trace_json, decision_json = domainforge.evaluate_authority(
303
+ make_minimal_config_json(),
304
+ make_minimal_request_json(),
305
+ "[]",
306
+ )
307
+ assert isinstance(trace_json, str)
308
+ assert isinstance(decision_json, str)
309
+
310
+
311
+ def test_evaluate_authority_is_deterministic():
312
+ """Two calls with identical inputs produce the same final_decision."""
313
+ config = make_minimal_config_json()
314
+ request = make_minimal_request_json()
315
+ _, d1 = domainforge.evaluate_authority(config, request)
316
+ _, d2 = domainforge.evaluate_authority(config, request)
317
+ assert json.loads(d1)["final_decision"] == json.loads(d2)["final_decision"]
318
+
319
+
320
+ def test_evaluate_authority_handles_different_operations():
321
+ """evaluate_authority processes requests with different operations."""
322
+ for operation in ["CreateOrder", "DeleteOrder", "UpdateOrder"]:
323
+ _, decision_json = domainforge.evaluate_authority(
324
+ make_minimal_config_json(),
325
+ make_minimal_request_json(operation=operation),
326
+ )
327
+ decision = json.loads(decision_json)
328
+ assert "final_decision" in decision
@@ -0,0 +1,143 @@
1
+ import io
2
+ import os
3
+ import tarfile
4
+ import tempfile
5
+ import zipfile
6
+
7
+ import pytest
8
+
9
+ from scripts.ci_tasks import (
10
+ _is_safe_path,
11
+ _validate_tar_member,
12
+ _validate_zip_member,
13
+ unpack_and_verify,
14
+ )
15
+
16
+
17
+ class TestSafePath:
18
+ def test_normal_path_is_safe(self):
19
+ assert _is_safe_path("/tmp/dest", "file.txt")
20
+
21
+ def test_subdirectory_is_safe(self):
22
+ assert _is_safe_path("/tmp/dest", "sub/file.txt")
23
+
24
+ def test_traversal_is_unsafe(self):
25
+ assert not _is_safe_path("/tmp/dest", "../outside.txt")
26
+
27
+ def test_deep_traversal_is_unsafe(self):
28
+ assert not _is_safe_path("/tmp/dest", "foo/../../outside.txt")
29
+
30
+ def test_absolute_path_is_unsafe(self):
31
+ assert not _is_safe_path("/tmp/dest", "/etc/passwd")
32
+
33
+
34
+ class TestTarValidation:
35
+ def test_safe_member_passes(self):
36
+ member = tarfile.TarInfo(name="safe_file.txt")
37
+ _validate_tar_member("/tmp/dest", member)
38
+
39
+ def test_traversal_rejected(self):
40
+ member = tarfile.TarInfo(name="../outside.txt")
41
+ with pytest.raises(ValueError, match="Unsafe path"):
42
+ _validate_tar_member("/tmp/dest", member)
43
+
44
+ def test_absolute_path_rejected(self):
45
+ member = tarfile.TarInfo(name="/etc/passwd")
46
+ with pytest.raises(ValueError, match="Unsafe path"):
47
+ _validate_tar_member("/tmp/dest", member)
48
+
49
+ def test_symlink_rejected(self):
50
+ member = tarfile.TarInfo(name="link")
51
+ member.type = tarfile.SYMTYPE
52
+ member.linkname = "/etc/passwd"
53
+ with pytest.raises(ValueError, match="Symlink"):
54
+ _validate_tar_member("/tmp/dest", member)
55
+
56
+ def test_hardlink_rejected(self):
57
+ member = tarfile.TarInfo(name="link")
58
+ member.type = tarfile.LNKTYPE
59
+ member.linkname = "/etc/passwd"
60
+ with pytest.raises(ValueError, match="Hardlink|Symlink"):
61
+ _validate_tar_member("/tmp/dest", member)
62
+
63
+ def test_deep_traversal_rejected(self):
64
+ member = tarfile.TarInfo(name="foo/../../outside.txt")
65
+ with pytest.raises(ValueError, match="Unsafe path"):
66
+ _validate_tar_member("/tmp/dest", member)
67
+
68
+
69
+ class TestZipValidation:
70
+ def test_safe_entry_passes(self):
71
+ _validate_zip_member("/tmp/dest", "safe_file.txt")
72
+
73
+ def test_traversal_rejected(self):
74
+ with pytest.raises(ValueError, match="Unsafe path"):
75
+ _validate_zip_member("/tmp/dest", "../outside.txt")
76
+
77
+ def test_absolute_path_rejected(self):
78
+ with pytest.raises(ValueError, match="Unsafe path"):
79
+ _validate_zip_member("/tmp/dest", "/etc/passwd")
80
+
81
+
82
+ class TestMaliciousArchiveExtraction:
83
+ def test_malicious_tar_with_traversal_fails(self, tmp_path):
84
+ tar_path = tmp_path / "malicious.tar.gz"
85
+ with tarfile.open(str(tar_path), "w:gz") as tf:
86
+ data = b"malicious content"
87
+ member = tarfile.TarInfo(name="../outside.txt")
88
+ member.size = len(data)
89
+ tf.addfile(member, io.BytesIO(data))
90
+
91
+ with pytest.raises(ValueError, match="Unsafe path"):
92
+ unpack_and_verify(str(tar_path), "domainforge")
93
+
94
+ def test_malicious_tar_with_symlink_fails(self, tmp_path):
95
+ tar_path = tmp_path / "symlink.tar.gz"
96
+ with tarfile.open(str(tar_path), "w:gz") as tf:
97
+ member = tarfile.TarInfo(name="evil_link")
98
+ member.type = tarfile.SYMTYPE
99
+ member.linkname = "/etc/passwd"
100
+ tf.addfile(member)
101
+
102
+ with pytest.raises(ValueError, match="Symlink"):
103
+ unpack_and_verify(str(tar_path), "domainforge")
104
+
105
+ def test_malicious_tar_with_absolute_path_fails(self, tmp_path):
106
+ tar_path = tmp_path / "absolute.tar.gz"
107
+ with tarfile.open(str(tar_path), "w:gz") as tf:
108
+ data = b"absolute path content"
109
+ member = tarfile.TarInfo(name="/tmp/evil.txt")
110
+ member.size = len(data)
111
+ tf.addfile(member, io.BytesIO(data))
112
+
113
+ with pytest.raises(ValueError, match="Unsafe path"):
114
+ unpack_and_verify(str(tar_path), "domainforge")
115
+
116
+ def test_malicious_zip_with_traversal_fails(self, tmp_path):
117
+ zip_path = tmp_path / "malicious.zip"
118
+ with zipfile.ZipFile(str(zip_path), "w") as zf:
119
+ zf.writestr("../outside.txt", "malicious content")
120
+
121
+ with pytest.raises(ValueError, match="Unsafe path"):
122
+ unpack_and_verify(str(zip_path), "domainforge")
123
+
124
+ def test_safe_tar_extracts_successfully(self, tmp_path):
125
+ tar_path = tmp_path / "safe.tar.gz"
126
+ with tarfile.open(str(tar_path), "w:gz") as tf:
127
+ data = b"#!/bin/sh\necho v1.0.0"
128
+ member = tarfile.TarInfo(name="domainforge")
129
+ member.size = len(data)
130
+ member.mode = 0o755
131
+ tf.addfile(member, io.BytesIO(data))
132
+
133
+ with patch_verify_cli(tmp_path):
134
+ result = unpack_and_verify(str(tar_path), "domainforge")
135
+ assert result == 0
136
+
137
+
138
+ def patch_verify_cli(tmp_path):
139
+ import unittest.mock
140
+
141
+ return unittest.mock.patch(
142
+ "scripts.ci_tasks.verify_cli_binary", return_value=0
143
+ )