hecks 3.13.0 → 4.0.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 (440) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/adapters/fake_processor.rb +34 -0
  3. data/examples/pizzas/adapters/payment_signature.rb +66 -0
  4. data/examples/pizzas/adapters/payment_webhook.rb +87 -0
  5. data/examples/pizzas/bluebook/pizzas.behaviors +33 -0
  6. data/examples/pizzas/bluebook/pizzas.bluebook +4 -4
  7. data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
  8. data/examples/pizzas/config.ru +22 -0
  9. data/lib/hecks/adapters/driven/prism.rb +0 -2
  10. data/lib/hecks/adapters/driven.rb +0 -2
  11. data/lib/hecks/adapters/driving/json.rb +1 -7
  12. data/lib/hecks/bluebook/dsl/attribute_collector.rb +15 -5
  13. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
  14. data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
  15. data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
  16. data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
  17. data/lib/hecks/bluebook/hecksagon.rb +1 -1
  18. data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
  19. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
  20. data/lib/hecks/bluebook/meta_validator.rb +0 -11
  21. data/lib/hecks/bluebook/named_pattern.rb +33 -0
  22. data/lib/hecks/bluebook/translation.rb +18 -10
  23. data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
  24. data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
  25. data/lib/hecks/bluebook/translation_expression.rb +39 -0
  26. data/lib/hecks/bluebook/translation_plan.rb +70 -0
  27. data/lib/hecks/bluebook.rb +3 -0
  28. data/lib/hecks/chapters.rb +3 -3
  29. data/lib/hecks/cli/domain_stub.rb +1 -1
  30. data/lib/hecks/cli/interview_aggregate.rb +4 -4
  31. data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
  32. data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
  33. data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
  34. data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
  35. data/lib/hecks/composer/adapters/jev.adapter +3 -0
  36. data/lib/hecks/composer/adapters/jev.rb +66 -0
  37. data/lib/hecks/composer/agent.rb +113 -0
  38. data/lib/hecks/composer/assistant.rb +110 -0
  39. data/lib/hecks/composer/behavior_run.rb +53 -0
  40. data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
  41. data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
  42. data/lib/hecks/composer/board.rb +65 -0
  43. data/lib/hecks/composer/board_edit/command_text.rb +39 -0
  44. data/lib/hecks/composer/board_edit/source.rb +84 -0
  45. data/lib/hecks/composer/board_edit.rb +144 -0
  46. data/lib/hecks/composer/check.rb +89 -0
  47. data/lib/hecks/composer/domains.rb +81 -0
  48. data/lib/hecks/composer/jobs.rb +134 -0
  49. data/lib/hecks/composer/layout.rb +37 -0
  50. data/lib/hecks/composer/live.rb +150 -0
  51. data/lib/hecks/composer/outline.rb +48 -0
  52. data/lib/hecks/composer/page.html +989 -0
  53. data/lib/hecks/composer/panels.rb +142 -0
  54. data/lib/hecks/composer/parse.rb +83 -0
  55. data/lib/hecks/composer/plain.rb +28 -0
  56. data/lib/hecks/composer/ports/composer_agent.port +7 -0
  57. data/lib/hecks/composer/session.rb +141 -0
  58. data/lib/hecks/composer/toolbox.rb +166 -0
  59. data/lib/hecks/composer/web.rb +156 -0
  60. data/lib/hecks/composer/workbench.rb +51 -0
  61. data/lib/hecks/composer/workspace.rb +81 -0
  62. data/lib/hecks/composer.rb +17 -0
  63. data/lib/hecks/corpus/adapters.rb +22 -0
  64. data/lib/hecks/corpus/rust.rb +1 -1
  65. data/lib/hecks/corpus/worlds.rb +19 -0
  66. data/lib/hecks/corpus.rb +5 -0
  67. data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -34
  68. data/lib/hecks/forms/app/commands.rb +2 -3
  69. data/lib/hecks/forms/app/queries.rb +2 -3
  70. data/lib/hecks/forms/app/records.rb +17 -5
  71. data/lib/hecks/forms/command_form_renderer.rb +42 -16
  72. data/lib/hecks/forms/field_controls.rb +2 -2
  73. data/lib/hecks/forms/field_renderer.rb +1 -1
  74. data/lib/hecks/forms/field_shape.rb +36 -17
  75. data/lib/hecks/forms/index_renderer.rb +2 -1
  76. data/lib/hecks/forms/page.rb +1 -1
  77. data/lib/hecks/forms/params.rb +0 -13
  78. data/lib/hecks/forms/query_form_renderer.rb +7 -6
  79. data/lib/hecks/forms/record_renderer.rb +11 -7
  80. data/lib/hecks/forms/rule_words.rb +100 -0
  81. data/lib/hecks/forms/value_object_shape.rb +2 -1
  82. data/lib/hecks/framework/bluebook/change.bluebook +2 -2
  83. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
  84. data/lib/hecks/fuzzing/domain_generator/block_rendering.rb +1 -1
  85. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
  86. data/lib/hecks/fuzzing/replay/filters.rb +2 -6
  87. data/lib/hecks/gate/stages.yml +25 -0
  88. data/lib/hecks/grammar/evolve/seeds.rb +0 -6
  89. data/lib/hecks/grammar/evolve.rb +0 -3
  90. data/lib/hecks/grammar/translation.bluebook +1 -1
  91. data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
  92. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
  93. data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
  94. data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
  95. data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
  96. data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
  97. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
  98. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
  99. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
  100. data/lib/hecks/hecks/codebase.behaviors +6 -0
  101. data/lib/hecks/hecks/codebase.bluebook +58 -37
  102. data/lib/hecks/hecks/composer.behaviors +68 -0
  103. data/lib/hecks/hecks/context_map.hecksagon +1 -1
  104. data/lib/hecks/hecks/custodian.behaviors +0 -6
  105. data/lib/hecks/hecks/custodian.bluebook +15 -92
  106. data/lib/hecks/hecks/hecks.bluebook +2 -2
  107. data/lib/hecks/hecks/hecks.hecksagon +2 -2
  108. data/lib/hecks/hecks/hecks.world +1 -1
  109. data/lib/hecks/hecks/rehearsal.bluebook +10 -16
  110. data/lib/hecks/hecks/tooling.bluebook +22 -22
  111. data/lib/hecks/kernel_capabilities/templates.rb +4 -4
  112. data/lib/hecks/kernel_capabilities.rb +1 -1
  113. data/lib/hecks/language/bluebook/aggregate.bluebook +1 -0
  114. data/lib/hecks/language/bluebook/command.bluebook +1 -0
  115. data/lib/hecks/language/bluebook/entity.bluebook +1 -0
  116. data/lib/hecks/language/bluebook/query.bluebook +1 -0
  117. data/lib/hecks/language/bluebook/shape.bluebook +8 -7
  118. data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
  119. data/lib/hecks/language/hecksagon/port_operation.bluebook +1 -0
  120. data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
  121. data/lib/hecks/ports/access_control.rb +2 -2
  122. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
  123. data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
  124. data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
  125. data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
  126. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
  127. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
  128. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
  129. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
  130. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
  131. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
  132. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
  133. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
  134. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
  135. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
  136. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
  137. data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
  138. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
  139. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
  140. data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
  141. data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
  142. data/lib/hecks/ports.rb +0 -1
  143. data/lib/hecks/projection_files.rb +3 -1
  144. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  145. data/lib/hecks/projections/glossary/entries.rb +10 -4
  146. data/lib/hecks/projections/glossary/groups.rb +42 -0
  147. data/lib/hecks/projections/glossary/html/page.rb +7 -2
  148. data/lib/hecks/projections/glossary/html/parser.rb +1 -1
  149. data/lib/hecks/projections/glossary/html/sections.rb +31 -11
  150. data/lib/hecks/projections/glossary/html.rb +1 -2
  151. data/lib/hecks/projections/glossary/markdown.rb +31 -28
  152. data/lib/hecks/projections/glossary/mermaid.rb +3 -0
  153. data/lib/hecks/projections/glossary/page.css +143 -153
  154. data/lib/hecks/projections/glossary/page.js +63 -3
  155. data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
  156. data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
  157. data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
  158. data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
  159. data/lib/hecks/projections/glossary/sentences.rb +13 -3
  160. data/lib/hecks/projections/glossary/slugs.rb +4 -1
  161. data/lib/hecks/projections/glossary.rb +2 -1
  162. data/lib/hecks/projections/plain_english.rb +97 -0
  163. data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
  164. data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
  165. data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
  166. data/lib/hecks/projections/rust_vocabulary.rb +14 -6
  167. data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
  168. data/lib/hecks/projections/statements/actions.rb +99 -0
  169. data/lib/hecks/projections/statements/relationships.rb +86 -0
  170. data/lib/hecks/projections/statements.rb +15 -24
  171. data/lib/hecks/projector/exporter/adapters.rb +112 -0
  172. data/lib/hecks/projector/exporter/translations.rb +18 -35
  173. data/lib/hecks/projector/exporter/worlds.rb +103 -0
  174. data/lib/hecks/projector/exporter.rb +5 -1
  175. data/lib/hecks/quality_control/adapters/agent.rb +6 -7
  176. data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
  177. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
  178. data/lib/hecks/quality_control/quality_control.bluebook +51 -51
  179. data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
  180. data/lib/hecks/runtime/outbox.rb +2 -9
  181. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
  182. data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
  183. data/lib/hecks/runtime/value.rb +13 -0
  184. data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
  185. data/lib/hecks/rust_build/wasm.rb +0 -12
  186. data/lib/hecks/rust_build/write_if_changed.rb +12 -12
  187. data/lib/hecks/rust_format.rb +77 -0
  188. data/lib/hecks/site/bluebook/site.bluebook +5 -5
  189. data/lib/hecks/sme/bluebook/sme.bluebook +12 -12
  190. data/lib/hecks/tickets/bluebook/tickets.bluebook +3 -3
  191. data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
  192. data/lib/hecks/tools/fuzz_sweep.rb +1 -3
  193. data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
  194. data/lib/hecks/tools/translation_audit.rb +8 -10
  195. data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
  196. data/lib/hecks/version.rb +1 -1
  197. data/lib/hecks/vocabulary.rb +2 -1
  198. data/lib/hecks.rb +3 -0
  199. data/prebuilt/{58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
  200. data/prebuilt/{2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
  201. data/prebuilt/verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json +1 -0
  202. data/rust/Cargo.toml +40 -0
  203. data/rust/build/Cargo.toml +36 -0
  204. data/rust/build/clippy.toml +4 -0
  205. data/rust/build/src/adapters_pass.rs +196 -0
  206. data/rust/build/src/binding_pass.rs +54 -13
  207. data/rust/build/src/build_artifact.rs +23 -5
  208. data/rust/build/src/cargo_sync.rs +69 -26
  209. data/rust/build/src/lineage_pass.rs +101 -87
  210. data/rust/build/src/main.rs +11 -2
  211. data/rust/build/src/pipeline.rs +80 -19
  212. data/rust/build/src/reserved_names.rs +6 -50
  213. data/rust/build/src/resolve.rs +82 -35
  214. data/rust/build/src/rustfmt.rs +100 -0
  215. data/rust/build/src/seams_pass.rs +85 -16
  216. data/rust/build/src/subprocess.rs +30 -6
  217. data/rust/build/src/tmp.rs +5 -2
  218. data/rust/build/src/translation_pass.rs +72 -22
  219. data/rust/build/src/world_pass.rs +109 -0
  220. data/rust/clippy.toml +4 -0
  221. data/rust/codegen/Cargo.toml +36 -0
  222. data/rust/codegen/clippy.toml +4 -0
  223. data/rust/codegen/src/asks.rs +40 -14
  224. data/rust/codegen/src/attr.rs +2 -2
  225. data/rust/codegen/src/bridging.rs +676 -117
  226. data/rust/codegen/src/commands.rs +979 -373
  227. data/rust/codegen/src/constraints.rs +88 -25
  228. data/rust/codegen/src/dependency_planning.rs +128 -34
  229. data/rust/codegen/src/domain_generator.rs +300 -170
  230. data/rust/codegen/src/exemplar.rs +87 -32
  231. data/rust/codegen/src/expr_emitter.rs +54 -6
  232. data/rust/codegen/src/fielded.rs +193 -41
  233. data/rust/codegen/src/hecks_naming.rs +124 -16
  234. data/rust/codegen/src/hecks_naming_tests.rs +81 -0
  235. data/rust/codegen/src/json.rs +80 -44
  236. data/rust/codegen/src/json_codec.rs +585 -149
  237. data/rust/codegen/src/literal.rs +104 -138
  238. data/rust/codegen/src/main.rs +145 -52
  239. data/rust/codegen/src/manifest.rs +76 -14
  240. data/rust/codegen/src/mutations.rs +1212 -239
  241. data/rust/codegen/src/naming.rs +111 -60
  242. data/rust/codegen/src/optional_pass.rs +90 -28
  243. data/rust/codegen/src/ports.rs +5 -4
  244. data/rust/codegen/src/prelude.rs +139 -49
  245. data/rust/codegen/src/queries.rs +601 -226
  246. data/rust/codegen/src/reactions.rs +268 -74
  247. data/rust/codegen/src/read_models.rs +606 -205
  248. data/rust/codegen/src/reference_specs.rs +16 -3
  249. data/rust/codegen/src/registry.rs +128 -79
  250. data/rust/codegen/src/reserved_names.rs +6 -50
  251. data/rust/codegen/src/shared.rs +71 -22
  252. data/rust/codegen/src/sidecars.rs +15 -5
  253. data/rust/codegen/src/skip_reason.rs +8 -2
  254. data/rust/codegen/src/types.rs +306 -108
  255. data/rust/host/Cargo.lock +1 -0
  256. data/rust/host/Cargo.toml +40 -4
  257. data/rust/host/HECKS_RELEASE +1 -1
  258. data/rust/host/clippy.toml +4 -0
  259. data/rust/host/src/api.rs +698 -218
  260. data/rust/host/src/auth.rs +491 -214
  261. data/rust/host/src/b64.rs +91 -0
  262. data/rust/host/src/bin/expr_harness.rs +6 -2
  263. data/rust/host/src/bin/lineage_harness.rs +47 -11
  264. data/rust/host/src/bin/mint_harness.rs +70 -37
  265. data/rust/host/src/bin/validate_harness.rs +16 -8
  266. data/rust/host/src/boot.rs +144 -68
  267. data/rust/host/src/boundary_fuzz/api.rs +130 -32
  268. data/rust/host/src/boundary_fuzz/auth.rs +108 -23
  269. data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
  270. data/rust/host/src/boundary_fuzz/ir.rs +164 -12
  271. data/rust/host/src/boundary_fuzz/server.rs +283 -36
  272. data/rust/host/src/boundary_fuzz/web.rs +56 -8
  273. data/rust/host/src/checkout.rs +78 -32
  274. data/rust/host/src/clock.rs +8 -0
  275. data/rust/host/src/commerce_ir.rs +333 -95
  276. data/rust/host/src/crypto.rs +33 -0
  277. data/rust/host/src/dispatch.rs +990 -331
  278. data/rust/host/src/expr_json.rs +1021 -273
  279. data/rust/host/src/extension.rs +24 -4
  280. data/rust/host/src/field_hints.rs +11 -7
  281. data/rust/host/src/fuzz_support.rs +85 -13
  282. data/rust/host/src/host.rs +100 -0
  283. data/rust/host/src/http.rs +22 -0
  284. data/rust/host/src/ir.rs +326 -16
  285. data/rust/host/src/journal.rs +277 -176
  286. data/rust/host/src/lambda_client.rs +219 -53
  287. data/rust/host/src/lib.rs +16 -4
  288. data/rust/host/src/log.rs +62 -11
  289. data/rust/host/src/mint.rs +1153 -326
  290. data/rust/host/src/needs.rs +185 -44
  291. data/rust/host/src/payments/keystore.rs +197 -45
  292. data/rust/host/src/payments/roles.rs +191 -0
  293. data/rust/host/src/payments/tests.rs +1445 -275
  294. data/rust/host/src/payments.rs +409 -121
  295. data/rust/host/src/platform_signin/attack_tests.rs +5 -5
  296. data/rust/host/src/platform_signin/config.rs +11 -11
  297. data/rust/host/src/platform_signin/cookie.rs +2 -2
  298. data/rust/host/src/platform_signin/extension.rs +2 -1
  299. data/rust/host/src/platform_signin/handler.rs +9 -8
  300. data/rust/host/src/platform_signin/jti.rs +4 -2
  301. data/rust/host/src/platform_signin/jwks.rs +2 -1
  302. data/rust/host/src/platform_signin/jws.rs +2 -37
  303. data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
  304. data/rust/host/src/platform_signin/mode_tests.rs +5 -3
  305. data/rust/host/src/platform_signin/pages.rs +20 -20
  306. data/rust/host/src/platform_signin/session.rs +8 -1
  307. data/rust/host/src/platform_signin/settings.rs +6 -6
  308. data/rust/host/src/platform_signin/strict_json.rs +12 -9
  309. data/rust/host/src/platform_signin/verify.rs +41 -41
  310. data/rust/host/src/platform_signin/wiring_support.rs +13 -18
  311. data/rust/host/src/presentation.rs +207 -63
  312. data/rust/host/src/presentation_write.rs +502 -187
  313. data/rust/host/src/query_step.rs +69 -18
  314. data/rust/host/src/rate_limit.rs +541 -142
  315. data/rust/host/src/reference_transform.rs +77 -25
  316. data/rust/host/src/reference_validate.rs +443 -104
  317. data/rust/host/src/resend.rs +190 -55
  318. data/rust/host/src/secrets.rs +116 -21
  319. data/rust/host/src/server.rs +483 -104
  320. data/rust/host/src/storage_shape.rs +88 -21
  321. data/rust/host/src/test_pg.rs +17 -7
  322. data/rust/host/src/ui_schema.rs +495 -207
  323. data/rust/host/src/wasm_runner.rs +21 -6
  324. data/rust/host/src/web/commerce.rs +101 -23
  325. data/rust/host/src/web/newsletter/cooldown.rs +10 -3
  326. data/rust/host/src/web/newsletter/template.rs +26 -7
  327. data/rust/host/src/web/newsletter/tests.rs +112 -23
  328. data/rust/host/src/web/newsletter.rs +209 -59
  329. data/rust/host/src/web/newsletter_send.rs +251 -79
  330. data/rust/host/src/web/registration_receipt/tests.rs +16 -4
  331. data/rust/host/src/web/registration_receipt.rs +100 -24
  332. data/rust/host/src/web/registrations.rs +1384 -332
  333. data/rust/host/src/web/resend_webhook.rs +138 -80
  334. data/rust/host/src/web.rs +2416 -508
  335. data/rust/lsp/Cargo.toml +36 -0
  336. data/rust/lsp/clippy.toml +4 -0
  337. data/rust/lsp/src/json.rs +29 -22
  338. data/rust/lsp/src/main.rs +81 -31
  339. data/rust/lsp/src/outline.rs +23 -7
  340. data/rust/lsp/src/rpc.rs +17 -4
  341. data/rust/parser/Cargo.toml +36 -0
  342. data/rust/parser/clippy.toml +4 -0
  343. data/rust/parser/src/build/closed_sets.rs +6 -1
  344. data/rust/parser/src/build/identity.rs +4 -3
  345. data/rust/parser/src/build/mod.rs +1 -0
  346. data/rust/parser/src/build/named_pattern.rs +26 -0
  347. data/rust/parser/src/build/naming.rs +2 -80
  348. data/rust/parser/src/build/pattern_subset.rs +6 -8
  349. data/rust/parser/src/build/query_derive.rs +3 -1
  350. data/rust/parser/src/build/query_inference.rs +14 -5
  351. data/rust/parser/src/build/read_model.rs +4 -1
  352. data/rust/parser/src/build/references.rs +4 -3
  353. data/rust/parser/src/canonical.rs +34 -15
  354. data/rust/parser/src/emit.rs +187 -105
  355. data/rust/parser/src/expr/ast_json.rs +134 -42
  356. data/rust/parser/src/expr/evaluator.rs +27 -9
  357. data/rust/parser/src/expr/mod.rs +54 -13
  358. data/rust/parser/src/expr/resolver.rs +299 -92
  359. data/rust/parser/src/ir.rs +35 -3
  360. data/rust/parser/src/keywords.rs +7168 -510
  361. data/rust/parser/src/lex.rs +11 -25
  362. data/rust/parser/src/lib.rs +1 -0
  363. data/rust/parser/src/main.rs +65 -6
  364. data/rust/parser/src/parse/adapter.rs +157 -0
  365. data/rust/parser/src/parse/aggregate.rs +19 -10
  366. data/rust/parser/src/parse/chapter.rs +16 -17
  367. data/rust/parser/src/parse/command.rs +35 -16
  368. data/rust/parser/src/parse/domain_port.rs +4 -7
  369. data/rust/parser/src/parse/entity.rs +21 -11
  370. data/rust/parser/src/parse/hecksagon.rs +10 -4
  371. data/rust/parser/src/parse/lifecycle.rs +9 -5
  372. data/rust/parser/src/parse/mod.rs +43 -91
  373. data/rust/parser/src/parse/needs.rs +21 -12
  374. data/rust/parser/src/parse/policy.rs +2 -2
  375. data/rust/parser/src/parse/process_manager.rs +9 -11
  376. data/rust/parser/src/parse/query.rs +8 -4
  377. data/rust/parser/src/parse/read_model.rs +57 -16
  378. data/rust/parser/src/parse/value_object.rs +7 -2
  379. data/rust/parser/src/parse/world.rs +322 -0
  380. data/rust/parser/src/ruby_value.rs +7 -6
  381. data/rust/src/exemplar/json.rs +4 -4
  382. data/rust/src/exemplar/mutations.rs +18 -0
  383. data/rust/src/exemplar/read_models.rs +1 -10
  384. data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
  385. data/rust/src/kernel/attribute_shapes/list.rs +2 -2
  386. data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
  387. data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
  388. data/rust/src/kernel/cli.rs +229 -112
  389. data/rust/src/kernel/dispatch.rs +323 -115
  390. data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
  391. data/rust/src/kernel/expr.rs +203 -86
  392. data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
  393. data/rust/src/kernel/expression_operators/comparison.rs +87 -2
  394. data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
  395. data/rust/src/kernel/expression_operators/logical.rs +9 -3
  396. data/rust/src/kernel/expression_operators/membership.rs +3 -1
  397. data/rust/src/kernel/expression_operators/mod.rs +34 -21
  398. data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
  399. data/rust/src/kernel/expression_operators/positional.rs +84 -30
  400. data/rust/src/kernel/expression_operators/presence.rs +52 -13
  401. data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
  402. data/rust/src/kernel/expression_operators/sized.rs +24 -8
  403. data/rust/src/kernel/expression_operators/text.rs +120 -27
  404. data/rust/src/kernel/expression_operators/to_string.rs +10 -4
  405. data/rust/src/kernel/json.rs +230 -148
  406. data/rust/src/kernel/mod.rs +46 -33
  407. data/rust/src/kernel/named_query.rs +63 -15
  408. data/rust/src/kernel/needs.rs +112 -25
  409. data/rust/src/kernel/orchestrate.rs +980 -581
  410. data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
  411. data/rust/src/kernel/pattern.rs +107 -34
  412. data/rust/src/kernel/query_comparators.rs +118 -41
  413. data/rust/src/kernel/query_ordering.rs +13 -5
  414. data/rust/src/kernel/read_model.rs +479 -181
  415. data/rust/src/kernel/reference_lookup.rs +122 -26
  416. data/rust/src/kernel/repository.rs +293 -66
  417. data/rust/src/kernel/routing.rs +58 -42
  418. data/rust/src/kernel/ruby_float.rs +139 -0
  419. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
  420. data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
  421. data/rust/src/kernel/vocab/field_hint.rs +19 -25
  422. data/rust/src/kernel/vocab/mod.rs +6 -6
  423. data/rust/src/kernel/vocab/query_comparator.rs +23 -24
  424. data/rust/src/kernel/vocab/refusal_template.rs +501 -297
  425. data/rust/src/lib.rs +50 -0
  426. data/rust/src/main.rs +4 -1
  427. data/rust/web/Cargo.toml +36 -0
  428. data/rust/web/clippy.toml +4 -0
  429. metadata +85 -15
  430. data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
  431. data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
  432. data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
  433. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
  434. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
  435. data/lib/hecks/ports/authentication.port +0 -6
  436. data/lib/hecks/ports/authentication.rb +0 -67
  437. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
  438. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
  439. data/prebuilt/verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json +0 -1
  440. data/rust/host/src/approval.rs +0 -652
@@ -1,41 +1,50 @@
1
- /* The glossary page. Ledger paper: faint blue horizontal rules between
2
- entries, one red vertical margin rule down the navigation. The rules
3
- carry information — the red marks the section in view, the blue
4
- separates entries — and nothing else on the page is decoration. */
1
+ /* The glossary page, set for someone reviewing a domain: each thing is a
2
+ section, its groups of terms sit under it, and every rule is a line the
3
+ reader can tick. Colour marks only what a reviewer acts on: links, rules
4
+ and what has been ticked. */
5
5
 
6
6
  :root {
7
- --paper: #f3f5f8;
7
+ --paper: #f7f7f5;
8
8
  --panel: #ffffff;
9
- --ink: #1a2b4a;
10
- --ink-soft: #4a5873;
11
- --ink-faint: #8592a8;
12
- --rule: #c9d3e3;
13
- --red: #b0413e;
14
- --red-wash: rgba(176, 65, 62, 0.09);
9
+ --ink: #1d2433;
10
+ --ink-soft: #465066;
11
+ --ink-faint: #6b7489;
12
+ --rule: #dfe2e8;
13
+ --accent: #2557a7;
14
+ --rule-mark: #b5651d;
15
+ --rule-wash: #fbf3ea;
16
+ --done: #2f7d4f;
17
+ --done-wash: #edf7f0;
15
18
  }
16
19
 
17
20
  @media (prefers-color-scheme: dark) {
18
21
  :root:not([data-theme="light"]) {
19
- --paper: #0f1726;
20
- --panel: #141e33;
21
- --ink: #e3e7ee;
22
- --ink-soft: #b4bdd0;
23
- --ink-faint: #7f8ba6;
24
- --rule: #2a3854;
25
- --red: #d9635f;
26
- --red-wash: rgba(217, 99, 95, 0.14);
22
+ --paper: #14171d;
23
+ --panel: #1b1f27;
24
+ --ink: #e7e9ee;
25
+ --ink-soft: #bcc2cf;
26
+ --ink-faint: #8f97a8;
27
+ --rule: #2e3440;
28
+ --accent: #8db4f5;
29
+ --rule-mark: #e2a368;
30
+ --rule-wash: #2a231c;
31
+ --done: #7ccf9c;
32
+ --done-wash: #1a2a21;
27
33
  }
28
34
  }
29
35
 
30
36
  :root[data-theme="dark"] {
31
- --paper: #0f1726;
32
- --panel: #141e33;
33
- --ink: #e3e7ee;
34
- --ink-soft: #b4bdd0;
35
- --ink-faint: #7f8ba6;
36
- --rule: #2a3854;
37
- --red: #d9635f;
38
- --red-wash: rgba(217, 99, 95, 0.14);
37
+ --paper: #14171d;
38
+ --panel: #1b1f27;
39
+ --ink: #e7e9ee;
40
+ --ink-soft: #bcc2cf;
41
+ --ink-faint: #8f97a8;
42
+ --rule: #2e3440;
43
+ --accent: #8db4f5;
44
+ --rule-mark: #e2a368;
45
+ --rule-wash: #2a231c;
46
+ --done: #7ccf9c;
47
+ --done-wash: #1a2a21;
39
48
  }
40
49
 
41
50
  html { color-scheme: light dark; scroll-behavior: smooth; }
@@ -44,22 +53,21 @@ body {
44
53
  margin: 0;
45
54
  background: var(--paper);
46
55
  color: var(--ink);
47
- font-family: "Newsreader", Georgia, "Times New Roman", serif;
56
+ font-family: "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
48
57
  font-size: 17px;
49
- line-height: 1.55;
50
- font-variant-numeric: oldstyle-nums;
58
+ line-height: 1.6;
51
59
  }
52
60
 
53
- a { color: var(--red); }
54
- :focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
61
+ a { color: var(--accent); text-underline-offset: 2px; }
62
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
55
63
 
56
64
  .page {
57
65
  display: grid;
58
- grid-template-columns: 15.5rem minmax(0, 1fr);
66
+ grid-template-columns: 16rem minmax(0, 1fr);
59
67
  min-height: 100vh;
60
68
  }
61
69
 
62
- /* ── the rail ───────────────────────────────────────────────────── */
70
+ /* the rail */
63
71
 
64
72
  .rail {
65
73
  position: sticky;
@@ -67,174 +75,150 @@ a { color: var(--red); }
67
75
  height: 100vh;
68
76
  box-sizing: border-box;
69
77
  overflow-y: auto;
70
- padding: 2.4rem 1.3rem 2rem 1.6rem;
78
+ padding: 2rem 1.25rem;
71
79
  border-right: 1px solid var(--rule);
72
- font-family: "Instrument Sans", system-ui, -apple-system, sans-serif;
73
- font-size: 0.92rem;
74
- }
75
-
76
- .rail .domain {
77
- font-family: "Newsreader", Georgia, serif;
78
- font-size: 1.55rem;
79
- font-weight: 600;
80
- line-height: 1.1;
81
- margin: 0;
80
+ background: var(--panel);
81
+ font-size: 0.95rem;
82
82
  }
83
83
 
84
- .rail .sub { color: var(--ink-faint); margin: 0.15rem 0 1.4rem; font-size: 0.82rem; }
84
+ .rail .domain { font-size: 1.4rem; font-weight: 700; line-height: 1.2; margin: 0; }
85
+ .rail .sub { color: var(--ink-faint); margin: 0.1rem 0 1.2rem; }
85
86
 
86
87
  .rail input {
87
88
  width: 100%;
88
89
  box-sizing: border-box;
89
90
  font: inherit;
90
- padding: 0.45rem 0.65rem;
91
+ padding: 0.5rem 0.7rem;
91
92
  border: 1px solid var(--rule);
92
- border-radius: 4px;
93
- background: var(--panel);
93
+ border-radius: 6px;
94
+ background: var(--paper);
94
95
  color: var(--ink);
95
96
  }
96
97
 
97
98
  .rail input::placeholder { color: var(--ink-faint); }
99
+ .rail .count, .rail .progress { color: var(--ink-faint); font-size: 0.85rem; margin: 0.5rem 0 0; }
100
+ .rail .progress { color: var(--done); font-weight: 600; }
98
101
 
99
- .rail .count { color: var(--ink-faint); font-size: 0.78rem; margin: 0.45rem 0 1.2rem; }
100
-
101
- .rail ol {
102
- list-style: none;
103
- margin: 0;
104
- padding: 0;
105
- border-left: 2px solid var(--red);
106
- }
102
+ .rail ol { list-style: none; margin: 1.2rem 0 0; padding: 0; }
107
103
 
108
104
  .rail li a {
109
105
  display: block;
110
- padding: 0.28rem 0.85rem;
111
- margin-left: -2px;
112
- border-left: 2px solid transparent;
106
+ padding: 0.35rem 0.6rem;
107
+ border-radius: 6px;
113
108
  color: var(--ink-soft);
114
109
  text-decoration: none;
115
110
  }
116
111
 
117
- .rail li a:hover { color: var(--ink); }
112
+ .rail li a:hover { background: var(--paper); color: var(--ink); }
113
+ .rail li a[aria-current="true"] { background: var(--paper); color: var(--ink); font-weight: 600; }
118
114
 
119
- .rail li a[aria-current="true"] {
120
- color: var(--ink);
121
- font-weight: 600;
122
- border-left-color: var(--ink);
123
- }
115
+ /* the text column */
124
116
 
125
- /* ── the text column ────────────────────────────────────────────── */
117
+ main { padding: 2.5rem 3rem 6rem; max-width: 50rem; }
126
118
 
127
- main {
128
- padding: 3.2rem 4rem 7rem 3.6rem;
129
- max-width: 47rem;
130
- }
119
+ header h1 { font-size: 2.4rem; font-weight: 700; line-height: 1.15; margin: 0 0 0.75rem; }
120
+ .vision { font-size: 1.2rem; color: var(--ink-soft); margin: 0 0 1rem; }
121
+ .lede { color: var(--ink-soft); margin: 0 0 1.5rem; max-width: 64ch; }
131
122
 
132
- header h1 {
133
- font-size: 3rem;
134
- font-weight: 500;
135
- line-height: 1.02;
136
- letter-spacing: -0.012em;
137
- margin: 0 0 1.1rem;
138
- }
139
-
140
- .vision {
141
- font-size: 1.28rem;
142
- font-style: italic;
143
- line-height: 1.45;
144
- color: var(--ink-soft);
145
- margin: 0 0 1.2rem;
146
- }
147
-
148
- .lede { color: var(--ink-soft); margin: 0 0 1.6rem; max-width: 62ch; }
149
-
150
- figure { margin: 1.2rem 0 1.8rem; }
123
+ figure { margin: 1rem 0 1.25rem; }
151
124
 
152
125
  figcaption, .caption {
153
- font-family: "Instrument Sans", system-ui, sans-serif;
154
- font-size: 0.8rem;
155
- color: var(--ink-faint);
156
- margin: 0 0 0.4rem;
126
+ font-size: 1rem;
127
+ font-weight: 600;
128
+ color: var(--ink);
129
+ margin: 0 0 0.5rem;
157
130
  }
158
131
 
159
- .caption { margin-top: 1.6rem; }
132
+ .caption { margin-top: 1.5rem; }
160
133
 
161
134
  pre.mermaid {
162
135
  margin: 0;
163
- padding: 1rem 1.2rem;
136
+ padding: 1rem;
164
137
  background: var(--panel);
165
138
  border: 1px solid var(--rule);
166
- border-radius: 6px;
139
+ border-radius: 8px;
167
140
  overflow-x: auto;
168
- font-family: "Instrument Sans", system-ui, sans-serif;
169
- font-size: 0.85rem;
141
+ font-family: inherit;
142
+ font-size: 0.9rem;
170
143
  line-height: 1.4;
171
144
  color: var(--ink-soft);
172
145
  }
173
146
 
174
147
  pre.mermaid svg { display: block; max-width: 100%; height: auto; }
175
148
 
176
- section { margin-top: 4.5rem; scroll-margin-top: 1.5rem; }
177
-
178
- section h2 {
179
- display: inline-block;
180
- font-size: 2.1rem;
181
- font-weight: 500;
182
- line-height: 1.1;
183
- margin: 0 0 0.5rem;
184
- padding-bottom: 0.3rem;
185
- border-bottom: 2px solid var(--red);
149
+ section {
150
+ margin-top: 3rem;
151
+ padding: 1.75rem 2rem 1.5rem;
152
+ background: var(--panel);
153
+ border: 1px solid var(--rule);
154
+ border-radius: 10px;
155
+ scroll-margin-top: 1rem;
186
156
  }
187
157
 
188
- .about {
189
- font-size: 1.18rem;
190
- color: var(--ink-soft);
191
- margin: 0.5rem 0 0.7rem;
192
- max-width: 60ch;
193
- }
158
+ section h2 { font-size: 1.9rem; font-weight: 700; line-height: 1.2; margin: 0 0 0.4rem; }
159
+ .about { font-size: 1.1rem; color: var(--ink-soft); margin: 0 0 1rem; max-width: 64ch; }
160
+ section > p:not(.about):not(.caption) { margin: 0 0 0.8rem; max-width: 64ch; }
194
161
 
195
- section > p:not(.about):not(.caption) { margin: 0 0 0.8rem; max-width: 62ch; }
162
+ /* rules a reviewer ticks */
196
163
 
197
- .rules {
198
- list-style: none;
199
- margin: 0 0 1.2rem;
200
- padding: 0;
201
- max-width: 62ch;
202
- }
164
+ .rules { list-style: none; margin: 0 0 1rem; padding: 0; }
203
165
 
204
166
  .rules li {
205
- padding: 0.38rem 0;
167
+ display: flex;
168
+ gap: 0.6rem;
169
+ align-items: flex-start;
170
+ padding: 0.45rem 0.6rem;
206
171
  border-bottom: 1px solid var(--rule);
207
172
  }
208
173
 
209
- .terms { margin-top: 1.8rem; border-top: 1px solid var(--rule); }
174
+ .rules li:last-child { border-bottom: 0; }
175
+ .rules li.done { background: var(--done-wash); }
176
+
177
+ input.tick {
178
+ flex: none;
179
+ width: 1.1rem;
180
+ height: 1.1rem;
181
+ margin: 0.25rem 0 0;
182
+ accent-color: var(--done);
183
+ cursor: pointer;
184
+ }
185
+
186
+ /* groups and terms */
187
+
188
+ h3.group {
189
+ font-size: 1.25rem;
190
+ font-weight: 700;
191
+ margin: 2rem 0 0.25rem;
192
+ padding-top: 1rem;
193
+ border-top: 2px solid var(--rule);
194
+ scroll-margin-top: 1rem;
195
+ }
196
+
197
+ .terms { margin: 0; }
210
198
 
211
199
  .term {
212
200
  display: grid;
213
- grid-template-columns: 12.5rem minmax(0, 1fr);
214
- column-gap: 1.6rem;
201
+ grid-template-columns: 11rem minmax(0, 1fr);
202
+ column-gap: 1.25rem;
215
203
  padding: 0.9rem 0;
216
204
  border-bottom: 1px solid var(--rule);
217
- scroll-margin-top: 1.5rem;
205
+ scroll-margin-top: 1rem;
218
206
  }
219
207
 
220
- .term h3 {
221
- font-size: 1.08rem;
222
- font-weight: 600;
223
- line-height: 1.35;
224
- margin: 0.05rem 0 0;
225
- }
208
+ .term:last-child { border-bottom: 0; }
209
+ .term.done h4::after { content: " \2713"; color: var(--done); }
226
210
 
227
- .term p { margin: 0 0 0.4rem; }
211
+ .term h4 { grid-column: 1; grid-row: 1 / span 20; font-size: 1.05rem; font-weight: 700; line-height: 1.4; margin: 0; }
212
+ .term > :not(h4) { grid-column: 2; }
213
+ .term h4 label { display: flex; gap: 0.5rem; align-items: flex-start; cursor: pointer; }
214
+ .term p { margin: 0 0 0.45rem; }
228
215
  .term p:last-child { margin-bottom: 0; }
229
216
 
230
- .term p.rule { color: var(--ink-soft); font-size: 0.95rem; }
231
-
232
- .term a, .rules a {
233
- color: var(--red);
234
- font-style: italic;
235
- text-decoration: underline;
236
- text-decoration-thickness: 1px;
237
- text-underline-offset: 2px;
217
+ .term p.rule {
218
+ padding: 0.35rem 0.6rem;
219
+ border-left: 3px solid var(--rule-mark);
220
+ background: var(--rule-wash);
221
+ border-radius: 0 4px 4px 0;
238
222
  }
239
223
 
240
224
  /* A search narrows the page to the terms that match. */
@@ -242,30 +226,36 @@ section.searching figure,
242
226
  section.searching .rules,
243
227
  section.searching .caption,
244
228
  section.searching .about,
229
+ section.searching h3.group,
245
230
  section.searching > p { display: none; }
246
231
 
247
- section.searching .terms { border-top: 0; margin-top: 0.8rem; }
248
-
249
- /* Landing on a term from a link. */
250
232
  .landed { animation: land 1.4s ease-out; }
251
233
 
252
234
  @keyframes land {
253
- from { background: var(--red-wash); }
235
+ from { background: var(--rule-wash); }
254
236
  to { background: transparent; }
255
237
  }
256
238
 
257
239
  @media (max-width: 760px) {
240
+ body { font-size: 16px; }
258
241
  .page { grid-template-columns: 1fr; }
259
- .rail { position: static; height: auto; border-right: 0; border-bottom: 1px solid var(--rule); }
260
- .rail ol { display: flex; flex-wrap: wrap; gap: 0.2rem 0.6rem; border-left: 0; }
261
- .rail li a { padding: 0.2rem 0.4rem; border-left: 0; }
262
- .rail li a[aria-current="true"] { border-bottom: 2px solid var(--red); }
263
- main { padding: 1.8rem 1.2rem 4rem; }
264
- header h1 { font-size: 2.3rem; }
265
- .term { grid-template-columns: 1fr; row-gap: 0.25rem; }
242
+ .rail { position: static; height: auto; border-right: 0; border-bottom: 1px solid var(--rule); padding: 1.25rem 16px; }
243
+ .rail ol { display: flex; flex-wrap: wrap; gap: 0.25rem; }
244
+ .rail li a { padding: 0.25rem 0.5rem; }
245
+ main { padding: 1.25rem 16px 4rem; }
246
+ header h1 { font-size: 1.9rem; }
247
+ section { padding: 1.25rem 1rem; margin-top: 1.5rem; }
248
+ .term { grid-template-columns: 1fr; row-gap: 0.35rem; }
249
+ .term h4, .term > :not(h4) { grid-column: 1; grid-row: auto; }
266
250
  }
267
251
 
268
252
  @media (prefers-reduced-motion: reduce) {
269
253
  html { scroll-behavior: auto; }
270
254
  .landed { animation: none; }
271
255
  }
256
+
257
+ @media print {
258
+ .rail { display: none; }
259
+ .page { grid-template-columns: 1fr; }
260
+ section { break-inside: avoid-page; border: 0; }
261
+ }
@@ -1,6 +1,7 @@
1
- // The page's three behaviours: draw the diagrams, find a term, and
2
- // keep the rail pointing at the section in view. Nothing here changes
3
- // what the page says — that is all in the Markdown it was rendered from.
1
+ // The page's behaviours: draw the diagrams, find a term, keep the rail
2
+ // pointing at the section in view, and let a reviewer tick each rule and
3
+ // term as checked. Ticks stay in this browser only; nothing here changes
4
+ // what the page says, which is all in the Markdown it was rendered from.
4
5
  (function () {
5
6
  var root = document.documentElement;
6
7
  var dark = root.dataset.theme === "dark" ||
@@ -17,6 +18,7 @@
17
18
 
18
19
  var input = document.querySelector(".rail input");
19
20
  var count = document.querySelector(".rail .count");
21
+ var progress = document.querySelector(".rail .progress");
20
22
  var terms = Array.prototype.slice.call(document.querySelectorAll(".term"));
21
23
  var sections = Array.prototype.slice.call(document.querySelectorAll("main section"));
22
24
  var links = {};
@@ -24,6 +26,64 @@
24
26
  links[a.getAttribute("href").slice(1)] = a;
25
27
  });
26
28
 
29
+ // Ticks are kept per page, keyed by the section and the text ticked.
30
+ var storeKey = "glossary-ticks:" + document.title;
31
+ var ticked = {};
32
+ try { ticked = JSON.parse(window.localStorage.getItem(storeKey) || "{}") || {}; } catch (e) { ticked = {}; }
33
+
34
+ function save() {
35
+ try { window.localStorage.setItem(storeKey, JSON.stringify(ticked)); } catch (e) { /* not kept */ }
36
+ }
37
+
38
+ var ticks = [];
39
+
40
+ function addTick(holder, before, key, label) {
41
+ var box = document.createElement("input");
42
+ box.type = "checkbox";
43
+ box.className = "tick";
44
+ box.setAttribute("aria-label", "Checked: " + label);
45
+ box.checked = !!ticked[key];
46
+ holder.classList.toggle("done", box.checked);
47
+ box.addEventListener("change", function () {
48
+ if (box.checked) { ticked[key] = true; } else { delete ticked[key]; }
49
+ holder.classList.toggle("done", box.checked);
50
+ save();
51
+ tally();
52
+ });
53
+ before.parentNode.insertBefore(box, before);
54
+ ticks.push(box);
55
+ }
56
+
57
+ sections.forEach(function (section) {
58
+ Array.prototype.forEach.call(section.querySelectorAll(".rules li"), function (li) {
59
+ var text = li.textContent.trim();
60
+ var span = document.createElement("span");
61
+ while (li.firstChild) span.appendChild(li.firstChild);
62
+ li.appendChild(span);
63
+ addTick(li, span, section.id + "|" + text, text);
64
+ });
65
+ });
66
+
67
+ terms.forEach(function (term) {
68
+ var heading = term.querySelector("h4");
69
+ var text = heading.textContent.trim();
70
+ var label = document.createElement("label");
71
+ var span = document.createElement("span");
72
+ span.textContent = text;
73
+ heading.textContent = "";
74
+ label.appendChild(span);
75
+ heading.appendChild(label);
76
+ addTick(term, span, "term|" + term.id, text);
77
+ });
78
+
79
+ function tally() {
80
+ var done = ticks.filter(function (box) { return box.checked; }).length;
81
+ progress.hidden = ticks.length === 0;
82
+ progress.textContent = done + " of " + ticks.length + " checked";
83
+ }
84
+
85
+ tally();
86
+
27
87
  function filter() {
28
88
  var query = input.value.trim().toLowerCase();
29
89
  var shown = 0;
@@ -0,0 +1,88 @@
1
+ require_relative "../../plain_english"
2
+ require_relative "../../statements"
3
+
4
+ module Hecks
5
+ module Projections
6
+ module Glossary
7
+ module Sentences
8
+ # The paragraphs an action's term carries: what it is for and who does it, what it needs,
9
+ # what it checks first, what it changes, what it guarantees after, and what it records.
10
+ module Actions
11
+ # How each kind of change is said, before the field it changes.
12
+ CHANGES = {
13
+ "set" => "sets", "append" => "adds to", "increment" => "raises", "decrement" => "lowers",
14
+ "multiply" => "scales", "clamp" => "limits", "remove" => "removes from",
15
+ "delegate" => "passes on to", "corrects" => "corrects"
16
+ }.freeze
17
+
18
+ module_function
19
+
20
+ # @param facts [Hash] the entry's facts: `:command`, `:holder` and `:value_objects`
21
+ # @param index [Glossary::Index] resolves links to other terms
22
+ # @return [Array<String>] the paragraphs, each present only when it has something to say
23
+ def paragraphs(facts, index)
24
+ command, holder = facts.values_at(:command, :holder)
25
+ [Sentences.command_sentence(command), needs(command, index, facts[:within]),
26
+ checks(holder, command), changes(holder, command), afterwards(command),
27
+ records(command, index)].compact
28
+ end
29
+
30
+ # "Needs a topping (Topping name) and an amount (Topping amount)."
31
+ def needs(command, index, within)
32
+ fields = command.attributes.map { |field| need_phrase(field, index, within) }
33
+ fields.empty? ? nil : "Needs #{Naming.to_sentence_list(fields)}."
34
+ end
35
+
36
+ def need_phrase(field, index, within)
37
+ name = PlainEnglish.noun(field.name)
38
+ name = PlainEnglish.a(name) unless field.list?
39
+ detail = [ValueObjects.type_words(field, index, within), ("optional" if field.optional?)].compact
40
+ "#{name} (#{detail.join(", ")})"
41
+ end
42
+
43
+ # "Allowed only when: the order is available; at most 10 toppings."
44
+ def checks(holder, command)
45
+ rules = Statements::Actions.checks(holder, command, said_by_move: false)
46
+ rules.empty? ? nil : "Allowed only when: #{rules.join("; ")}."
47
+ end
48
+
49
+ # "Sets the customer name and moves the order from available to sold."
50
+ def changes(holder, command)
51
+ parts = command.mutations.group_by { |mutation| CHANGES.fetch(mutation.op.to_s, "changes") }
52
+ .map { |verb, mutations| change_phrase(verb, mutations) }
53
+ parts << move(holder, command)
54
+ parts.compact!
55
+ parts.empty? ? nil : "#{PlainEnglish.upper(Naming.to_sentence_list(parts))}."
56
+ end
57
+
58
+ # "sets the name and the pizza"
59
+ def change_phrase(verb, mutations)
60
+ fields = mutations.map { |mutation| "the #{PlainEnglish.noun(mutation.target.to_s.split(".").last)}" }
61
+ "#{verb} #{Naming.to_sentence_list(fields.uniq)}"
62
+ end
63
+
64
+ # The lifecycle move a command makes, as "moves the order from available to sold".
65
+ def move(holder, command)
66
+ lifecycle = holder.respond_to?(:lifecycle) && holder.lifecycle
67
+ return nil unless lifecycle
68
+
69
+ found = Statements::Actions.moves(lifecycle).find { |name, _move| name == command.hecks_name }
70
+ found && Statements::Actions.move_phrase(found.last, "the #{PlainEnglish.noun(holder.hecks_name)}")
71
+ end
72
+
73
+ # "Afterwards, always: the balance fell by exactly the amount."
74
+ def afterwards(command)
75
+ rules = command.ensures.map { |rule| Statements::Actions.text(rule) }
76
+ rules.empty? ? nil : "Afterwards, always: #{rules.join("; ")}."
77
+ end
78
+
79
+ # "Records Topping added."
80
+ def records(command, index)
81
+ events = command.emits.map { |event| index.link(:event, event.to_s.split(/::|\./).last) }
82
+ events.empty? ? nil : "Records #{Naming.to_sentence_list(events)}."
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,77 @@
1
+ require_relative "../../plain_english"
2
+
3
+ module Hecks
4
+ module Projections
5
+ module Glossary
6
+ module Sentences
7
+ # Speaks a dotted field path the way a reader names it. A one-field value object is
8
+ # named by its own name, so `pizza.price_cents.cents` is "price" and
9
+ # `customer_name.value` is "customer name".
10
+ class Paths
11
+ # @param holder [Bluebook::Aggregate, Bluebook::Entity] the construct the path starts on
12
+ # @param value_objects [Array<Bluebook::ValueObject>] the value objects types resolve to
13
+ def initialize(holder, value_objects)
14
+ @holder = holder
15
+ @by_name = value_objects.to_h { |value_object| [value_object.hecks_name, value_object] }
16
+ end
17
+
18
+ # @param path [String, Symbol] a dotted field path
19
+ # @return [Boolean] whether it is the holder's lifecycle field
20
+ def lifecycle?(path)
21
+ lifecycle = @holder.respond_to?(:lifecycle) && @holder.lifecycle
22
+ lifecycle ? lifecycle.field.to_s == path.to_s : false
23
+ end
24
+
25
+ # @param path [String, Symbol] a dotted field path
26
+ # @return [String] how a reader names the field
27
+ def label(path)
28
+ hops = path.to_s.split("/")
29
+ return hops.map { |hop| PlainEnglish.noun(hop) }.join("'s ") if hops.size > 1
30
+
31
+ walk(path).first
32
+ end
33
+
34
+ # @param path [String, Symbol] a dotted field path
35
+ # @return [String, nil] the type the path ends on, such as "String", when it resolves
36
+ def type(path) = walk(path).last
37
+
38
+ private
39
+
40
+ # Follows each segment through the attributes and value objects it names. Inside a
41
+ # one-field value object the label already said is kept.
42
+ def walk(path)
43
+ segments = path.to_s.split(".")
44
+ state = { label: PlainEnglish.noun(segments.last), type: nil, attributes: @holder.attributes, named: false }
45
+ segments.each do |segment|
46
+ field = state[:attributes].find { |attribute| attribute.name.to_s == segment }
47
+ return [state[:label], nil] unless field
48
+
49
+ state = step(field, state)
50
+ end
51
+ [state[:label], leaf_type(state)]
52
+ end
53
+
54
+ # The primitive a path ends on: a value object compares by its one number, when it has
55
+ # exactly one, as the query runtime does.
56
+ def leaf_type(state)
57
+ attributes = state[:attributes]
58
+ return attributes.first.type.to_s if state[:named]
59
+
60
+ numbers = attributes.select { |attribute| %w[Integer Float].include?(attribute.type.to_s) }
61
+ numbers.one? ? numbers.first.type.to_s : state[:type]
62
+ end
63
+
64
+ # The state after one segment: its label, type, and where the next segment is looked up.
65
+ def step(field, state)
66
+ value_object = @by_name[field.type.to_s]
67
+ label = state[:named] ? state[:label] : PlainEnglish.noun(field.name)
68
+ single = value_object && value_object.attributes.size == 1
69
+ label = PlainEnglish.noun(value_object.hecks_name) if single
70
+ { label: label, type: field.type.to_s, attributes: value_object ? value_object.attributes : [],
71
+ named: single || false }
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end