hecks 3.14.0 → 5.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 (427) 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.hecksagon +5 -3
  7. data/examples/pizzas/config.ru +22 -0
  8. data/exe/hecks +1 -1
  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 +9 -16
  12. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
  13. data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
  14. data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
  15. data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
  16. data/lib/hecks/bluebook/hecksagon.rb +1 -1
  17. data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
  18. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
  19. data/lib/hecks/bluebook/meta_validator.rb +0 -11
  20. data/lib/hecks/bluebook/translation.rb +18 -10
  21. data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
  22. data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
  23. data/lib/hecks/bluebook/translation_expression.rb +39 -0
  24. data/lib/hecks/bluebook/translation_plan.rb +70 -0
  25. data/lib/hecks/bluebook.rb +2 -0
  26. data/lib/hecks/chapters.rb +3 -3
  27. data/lib/hecks/cli/project_cli/launcher_source.rb +1 -1
  28. data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
  29. data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
  30. data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
  31. data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
  32. data/lib/hecks/composer/adapters/jev.adapter +3 -0
  33. data/lib/hecks/composer/adapters/jev.rb +66 -0
  34. data/lib/hecks/composer/agent.rb +113 -0
  35. data/lib/hecks/composer/assistant.rb +110 -0
  36. data/lib/hecks/composer/behavior_run.rb +53 -0
  37. data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
  38. data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
  39. data/lib/hecks/composer/board.rb +65 -0
  40. data/lib/hecks/composer/board_edit/command_text.rb +39 -0
  41. data/lib/hecks/composer/board_edit/source.rb +84 -0
  42. data/lib/hecks/composer/board_edit.rb +144 -0
  43. data/lib/hecks/composer/check.rb +89 -0
  44. data/lib/hecks/composer/domains.rb +81 -0
  45. data/lib/hecks/composer/jobs.rb +134 -0
  46. data/lib/hecks/composer/layout.rb +37 -0
  47. data/lib/hecks/composer/live.rb +150 -0
  48. data/lib/hecks/composer/outline.rb +48 -0
  49. data/lib/hecks/composer/page.html +989 -0
  50. data/lib/hecks/composer/panels.rb +142 -0
  51. data/lib/hecks/composer/parse.rb +83 -0
  52. data/lib/hecks/composer/plain.rb +28 -0
  53. data/lib/hecks/composer/ports/composer_agent.port +7 -0
  54. data/lib/hecks/composer/session.rb +141 -0
  55. data/lib/hecks/composer/toolbox.rb +166 -0
  56. data/lib/hecks/composer/web.rb +156 -0
  57. data/lib/hecks/composer/workbench.rb +51 -0
  58. data/lib/hecks/composer/workspace.rb +81 -0
  59. data/lib/hecks/composer.rb +17 -0
  60. data/lib/hecks/corpus/adapters.rb +22 -0
  61. data/lib/hecks/corpus/rust.rb +1 -1
  62. data/lib/hecks/corpus/worlds.rb +19 -0
  63. data/lib/hecks/corpus.rb +5 -0
  64. data/lib/hecks/forms/app/commands.rb +2 -3
  65. data/lib/hecks/forms/app/queries.rb +2 -3
  66. data/lib/hecks/forms/app/records.rb +17 -5
  67. data/lib/hecks/forms/app/responses.rb +3 -1
  68. data/lib/hecks/forms/command_form_renderer.rb +42 -16
  69. data/lib/hecks/forms/field_controls.rb +2 -2
  70. data/lib/hecks/forms/field_renderer.rb +1 -1
  71. data/lib/hecks/forms/field_shape.rb +36 -17
  72. data/lib/hecks/forms/index_renderer.rb +2 -1
  73. data/lib/hecks/forms/page.rb +1 -1
  74. data/lib/hecks/forms/params.rb +0 -13
  75. data/lib/hecks/forms/query_form_renderer.rb +7 -6
  76. data/lib/hecks/forms/record_renderer.rb +11 -7
  77. data/lib/hecks/forms/rule_words.rb +100 -0
  78. data/lib/hecks/forms/value_object_shape.rb +2 -1
  79. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
  80. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
  81. data/lib/hecks/fuzzing/replay/filters.rb +2 -6
  82. data/lib/hecks/gate/stages.yml +25 -0
  83. data/lib/hecks/grammar/evolve/seeds.rb +0 -6
  84. data/lib/hecks/grammar/evolve.rb +0 -3
  85. data/lib/hecks/grammar/translation.bluebook +1 -1
  86. data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
  87. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
  88. data/lib/hecks/hecks/adapters/first_admin.rb +3 -3
  89. data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
  90. data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
  91. data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
  92. data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
  93. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
  94. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
  95. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
  96. data/lib/hecks/hecks/codebase.behaviors +6 -0
  97. data/lib/hecks/hecks/codebase.bluebook +21 -0
  98. data/lib/hecks/hecks/composer.behaviors +68 -0
  99. data/lib/hecks/hecks/context_map.hecksagon +1 -1
  100. data/lib/hecks/hecks/custodian.behaviors +0 -6
  101. data/lib/hecks/hecks/custodian.bluebook +3 -80
  102. data/lib/hecks/hecks/hecks.hecksagon +2 -2
  103. data/lib/hecks/hecks/hecks.world +1 -1
  104. data/lib/hecks/hecks/rehearsal.bluebook +7 -13
  105. data/lib/hecks/kernel_capabilities/templates.rb +4 -4
  106. data/lib/hecks/kernel_capabilities.rb +1 -1
  107. data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
  108. data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
  109. data/lib/hecks/ports/access_control.rb +2 -2
  110. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
  111. data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
  112. data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
  113. data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
  114. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
  115. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
  116. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
  117. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
  118. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
  119. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
  120. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
  121. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
  122. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
  123. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
  124. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
  125. data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
  126. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
  127. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
  128. data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
  129. data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
  130. data/lib/hecks/ports.rb +0 -1
  131. data/lib/hecks/projection_files.rb +3 -1
  132. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  133. data/lib/hecks/projections/glossary/entries.rb +10 -4
  134. data/lib/hecks/projections/glossary/groups.rb +42 -0
  135. data/lib/hecks/projections/glossary/html/page.rb +7 -2
  136. data/lib/hecks/projections/glossary/html/parser.rb +1 -1
  137. data/lib/hecks/projections/glossary/html/sections.rb +31 -11
  138. data/lib/hecks/projections/glossary/html.rb +1 -2
  139. data/lib/hecks/projections/glossary/markdown.rb +31 -28
  140. data/lib/hecks/projections/glossary/mermaid.rb +3 -0
  141. data/lib/hecks/projections/glossary/page.css +143 -153
  142. data/lib/hecks/projections/glossary/page.js +63 -3
  143. data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
  144. data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
  145. data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
  146. data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
  147. data/lib/hecks/projections/glossary/sentences.rb +13 -3
  148. data/lib/hecks/projections/glossary/slugs.rb +4 -1
  149. data/lib/hecks/projections/glossary.rb +2 -1
  150. data/lib/hecks/projections/plain_english.rb +97 -0
  151. data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
  152. data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
  153. data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
  154. data/lib/hecks/projections/rust_vocabulary.rb +14 -6
  155. data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
  156. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +7 -2
  157. data/lib/hecks/projections/statements/actions.rb +99 -0
  158. data/lib/hecks/projections/statements/relationships.rb +86 -0
  159. data/lib/hecks/projections/statements.rb +15 -24
  160. data/lib/hecks/projector/exporter/adapters.rb +112 -0
  161. data/lib/hecks/projector/exporter/translations.rb +18 -35
  162. data/lib/hecks/projector/exporter/worlds.rb +103 -0
  163. data/lib/hecks/projector/exporter.rb +5 -1
  164. data/lib/hecks/quality_control/adapters/agent.rb +6 -7
  165. data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
  166. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
  167. data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
  168. data/lib/hecks/runtime/outbox.rb +2 -9
  169. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
  170. data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
  171. data/lib/hecks/runtime/value/unwrapping.rb +60 -0
  172. data/lib/hecks/runtime/value.rb +10 -21
  173. data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
  174. data/lib/hecks/rust_build/wasm.rb +0 -12
  175. data/lib/hecks/rust_build/write_if_changed.rb +12 -12
  176. data/lib/hecks/rust_format.rb +77 -0
  177. data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
  178. data/lib/hecks/tools/fuzz_sweep.rb +1 -3
  179. data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
  180. data/lib/hecks/tools/translation_audit.rb +8 -10
  181. data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
  182. data/lib/hecks/version.rb +1 -1
  183. data/lib/hecks/vocabulary.rb +2 -1
  184. data/lib/hecks.rb +3 -0
  185. data/prebuilt/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → ad68f9e06c8068c2a4176b57abf3083d591d5d8980c2b3e897b97331f965b643.marshal} +0 -0
  186. data/prebuilt/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → ee22ec8642c4006717ba2b80f46e5efb81de459eef9dfec043e838c0ad4fd720.marshal} +0 -0
  187. data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-b69f5dcbba79c20daa0493476fa7c317004a30aa2bc3d3dc6a722ea70ae2b43b.json} +1 -1
  188. data/rust/Cargo.toml +40 -0
  189. data/rust/build/Cargo.toml +36 -0
  190. data/rust/build/clippy.toml +4 -0
  191. data/rust/build/src/adapters_pass.rs +196 -0
  192. data/rust/build/src/binding_pass.rs +54 -13
  193. data/rust/build/src/build_artifact.rs +23 -5
  194. data/rust/build/src/cargo_sync.rs +69 -26
  195. data/rust/build/src/lineage_pass.rs +101 -87
  196. data/rust/build/src/main.rs +11 -2
  197. data/rust/build/src/pipeline.rs +80 -19
  198. data/rust/build/src/reserved_names.rs +6 -50
  199. data/rust/build/src/resolve.rs +82 -35
  200. data/rust/build/src/rustfmt.rs +100 -0
  201. data/rust/build/src/seams_pass.rs +85 -16
  202. data/rust/build/src/subprocess.rs +30 -6
  203. data/rust/build/src/tmp.rs +5 -2
  204. data/rust/build/src/translation_pass.rs +72 -22
  205. data/rust/build/src/world_pass.rs +109 -0
  206. data/rust/clippy.toml +4 -0
  207. data/rust/codegen/Cargo.toml +36 -0
  208. data/rust/codegen/clippy.toml +4 -0
  209. data/rust/codegen/src/asks.rs +40 -14
  210. data/rust/codegen/src/attr.rs +2 -2
  211. data/rust/codegen/src/bridging.rs +676 -117
  212. data/rust/codegen/src/commands.rs +979 -373
  213. data/rust/codegen/src/constraints.rs +88 -25
  214. data/rust/codegen/src/dependency_planning.rs +128 -34
  215. data/rust/codegen/src/domain_generator.rs +306 -170
  216. data/rust/codegen/src/exemplar.rs +87 -32
  217. data/rust/codegen/src/expr_emitter.rs +54 -6
  218. data/rust/codegen/src/fielded.rs +193 -41
  219. data/rust/codegen/src/hecks_naming.rs +124 -16
  220. data/rust/codegen/src/hecks_naming_tests.rs +81 -0
  221. data/rust/codegen/src/json.rs +80 -44
  222. data/rust/codegen/src/json_codec.rs +585 -149
  223. data/rust/codegen/src/literal.rs +104 -138
  224. data/rust/codegen/src/main.rs +151 -52
  225. data/rust/codegen/src/manifest.rs +76 -14
  226. data/rust/codegen/src/mutations.rs +1212 -239
  227. data/rust/codegen/src/naming.rs +111 -60
  228. data/rust/codegen/src/optional_pass.rs +90 -28
  229. data/rust/codegen/src/ports.rs +5 -4
  230. data/rust/codegen/src/prelude.rs +139 -49
  231. data/rust/codegen/src/presenters.rs +335 -0
  232. data/rust/codegen/src/queries.rs +601 -226
  233. data/rust/codegen/src/reactions.rs +268 -74
  234. data/rust/codegen/src/read_models.rs +574 -229
  235. data/rust/codegen/src/reference_specs.rs +16 -3
  236. data/rust/codegen/src/registry.rs +128 -79
  237. data/rust/codegen/src/reserved_names.rs +6 -50
  238. data/rust/codegen/src/shared.rs +71 -22
  239. data/rust/codegen/src/sidecars.rs +15 -5
  240. data/rust/codegen/src/skip_reason.rs +8 -2
  241. data/rust/codegen/src/types.rs +306 -108
  242. data/rust/host/Cargo.lock +1 -0
  243. data/rust/host/Cargo.toml +40 -4
  244. data/rust/host/HECKS_RELEASE +1 -1
  245. data/rust/host/clippy.toml +4 -0
  246. data/rust/host/src/api.rs +698 -218
  247. data/rust/host/src/auth.rs +491 -214
  248. data/rust/host/src/b64.rs +91 -0
  249. data/rust/host/src/bin/expr_harness.rs +6 -2
  250. data/rust/host/src/bin/lineage_harness.rs +47 -11
  251. data/rust/host/src/bin/mint_harness.rs +70 -37
  252. data/rust/host/src/bin/validate_harness.rs +16 -8
  253. data/rust/host/src/boot.rs +144 -68
  254. data/rust/host/src/boundary_fuzz/api.rs +130 -32
  255. data/rust/host/src/boundary_fuzz/auth.rs +108 -23
  256. data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
  257. data/rust/host/src/boundary_fuzz/ir.rs +164 -12
  258. data/rust/host/src/boundary_fuzz/server.rs +283 -36
  259. data/rust/host/src/boundary_fuzz/web.rs +56 -8
  260. data/rust/host/src/checkout.rs +78 -32
  261. data/rust/host/src/clock.rs +8 -0
  262. data/rust/host/src/commerce_ir.rs +333 -95
  263. data/rust/host/src/crypto.rs +33 -0
  264. data/rust/host/src/dispatch.rs +1037 -331
  265. data/rust/host/src/expr_json.rs +1021 -273
  266. data/rust/host/src/extension.rs +24 -4
  267. data/rust/host/src/field_hints.rs +11 -7
  268. data/rust/host/src/fuzz_support.rs +85 -13
  269. data/rust/host/src/host.rs +100 -0
  270. data/rust/host/src/http.rs +22 -0
  271. data/rust/host/src/ir.rs +326 -16
  272. data/rust/host/src/journal.rs +277 -176
  273. data/rust/host/src/lambda_client.rs +219 -53
  274. data/rust/host/src/lib.rs +16 -4
  275. data/rust/host/src/log.rs +62 -11
  276. data/rust/host/src/mint.rs +1153 -326
  277. data/rust/host/src/needs.rs +185 -44
  278. data/rust/host/src/payments/keystore.rs +197 -45
  279. data/rust/host/src/payments/roles.rs +39 -7
  280. data/rust/host/src/payments/tests.rs +1427 -282
  281. data/rust/host/src/payments.rs +391 -112
  282. data/rust/host/src/platform_signin/attack_tests.rs +5 -5
  283. data/rust/host/src/platform_signin/config.rs +11 -11
  284. data/rust/host/src/platform_signin/cookie.rs +2 -2
  285. data/rust/host/src/platform_signin/extension.rs +2 -1
  286. data/rust/host/src/platform_signin/handler.rs +9 -8
  287. data/rust/host/src/platform_signin/jti.rs +4 -2
  288. data/rust/host/src/platform_signin/jwks.rs +2 -1
  289. data/rust/host/src/platform_signin/jws.rs +2 -37
  290. data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
  291. data/rust/host/src/platform_signin/mode_tests.rs +5 -3
  292. data/rust/host/src/platform_signin/pages.rs +20 -20
  293. data/rust/host/src/platform_signin/session.rs +8 -1
  294. data/rust/host/src/platform_signin/settings.rs +6 -6
  295. data/rust/host/src/platform_signin/strict_json.rs +12 -9
  296. data/rust/host/src/platform_signin/verify.rs +41 -41
  297. data/rust/host/src/platform_signin/wiring_support.rs +13 -18
  298. data/rust/host/src/presentation.rs +207 -63
  299. data/rust/host/src/presentation_write.rs +502 -187
  300. data/rust/host/src/query_step.rs +69 -18
  301. data/rust/host/src/rate_limit.rs +541 -142
  302. data/rust/host/src/reference_transform.rs +77 -25
  303. data/rust/host/src/reference_validate.rs +443 -104
  304. data/rust/host/src/resend.rs +190 -55
  305. data/rust/host/src/secrets.rs +116 -21
  306. data/rust/host/src/server.rs +555 -104
  307. data/rust/host/src/storage_shape.rs +88 -21
  308. data/rust/host/src/test_pg.rs +17 -7
  309. data/rust/host/src/ui_schema.rs +495 -207
  310. data/rust/host/src/wasm_runner.rs +21 -6
  311. data/rust/host/src/web/commerce.rs +101 -23
  312. data/rust/host/src/web/newsletter/cooldown.rs +10 -3
  313. data/rust/host/src/web/newsletter/template.rs +26 -7
  314. data/rust/host/src/web/newsletter/tests.rs +112 -23
  315. data/rust/host/src/web/newsletter.rs +209 -59
  316. data/rust/host/src/web/newsletter_send.rs +251 -79
  317. data/rust/host/src/web/registration_receipt/tests.rs +16 -4
  318. data/rust/host/src/web/registration_receipt.rs +100 -24
  319. data/rust/host/src/web/registrations.rs +1384 -332
  320. data/rust/host/src/web/resend_webhook.rs +138 -80
  321. data/rust/host/src/web.rs +2416 -508
  322. data/rust/lsp/Cargo.toml +36 -0
  323. data/rust/lsp/clippy.toml +4 -0
  324. data/rust/lsp/src/json.rs +29 -22
  325. data/rust/lsp/src/main.rs +81 -31
  326. data/rust/lsp/src/outline.rs +23 -7
  327. data/rust/lsp/src/rpc.rs +17 -4
  328. data/rust/parser/Cargo.toml +36 -0
  329. data/rust/parser/clippy.toml +4 -0
  330. data/rust/parser/src/build/closed_sets.rs +6 -1
  331. data/rust/parser/src/build/identity.rs +4 -3
  332. data/rust/parser/src/build/mod.rs +1 -1
  333. data/rust/parser/src/build/named_pattern.rs +4 -1
  334. data/rust/parser/src/build/naming.rs +2 -80
  335. data/rust/parser/src/build/pattern_subset.rs +6 -8
  336. data/rust/parser/src/build/query_derive.rs +3 -1
  337. data/rust/parser/src/build/query_inference.rs +14 -5
  338. data/rust/parser/src/build/read_model.rs +4 -1
  339. data/rust/parser/src/build/references.rs +4 -3
  340. data/rust/parser/src/canonical.rs +34 -15
  341. data/rust/parser/src/emit.rs +187 -105
  342. data/rust/parser/src/expr/ast_json.rs +134 -42
  343. data/rust/parser/src/expr/evaluator.rs +27 -9
  344. data/rust/parser/src/expr/mod.rs +54 -13
  345. data/rust/parser/src/expr/resolver.rs +299 -92
  346. data/rust/parser/src/ir.rs +35 -3
  347. data/rust/parser/src/keywords.rs +7168 -516
  348. data/rust/parser/src/lex.rs +11 -25
  349. data/rust/parser/src/lib.rs +1 -0
  350. data/rust/parser/src/main.rs +65 -6
  351. data/rust/parser/src/parse/adapter.rs +157 -0
  352. data/rust/parser/src/parse/aggregate.rs +19 -10
  353. data/rust/parser/src/parse/chapter.rs +16 -17
  354. data/rust/parser/src/parse/command.rs +35 -16
  355. data/rust/parser/src/parse/domain_port.rs +4 -7
  356. data/rust/parser/src/parse/entity.rs +21 -11
  357. data/rust/parser/src/parse/hecksagon.rs +10 -4
  358. data/rust/parser/src/parse/lifecycle.rs +9 -5
  359. data/rust/parser/src/parse/mod.rs +22 -91
  360. data/rust/parser/src/parse/needs.rs +21 -12
  361. data/rust/parser/src/parse/policy.rs +2 -2
  362. data/rust/parser/src/parse/process_manager.rs +9 -11
  363. data/rust/parser/src/parse/query.rs +8 -4
  364. data/rust/parser/src/parse/read_model.rs +57 -16
  365. data/rust/parser/src/parse/value_object.rs +7 -2
  366. data/rust/parser/src/parse/world.rs +322 -0
  367. data/rust/parser/src/ruby_value.rs +7 -6
  368. data/rust/src/exemplar/json.rs +4 -4
  369. data/rust/src/exemplar/mutations.rs +18 -0
  370. data/rust/src/exemplar/read_models.rs +1 -10
  371. data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
  372. data/rust/src/kernel/attribute_shapes/list.rs +2 -2
  373. data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
  374. data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
  375. data/rust/src/kernel/cli.rs +244 -114
  376. data/rust/src/kernel/dispatch.rs +323 -115
  377. data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
  378. data/rust/src/kernel/expr.rs +203 -86
  379. data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
  380. data/rust/src/kernel/expression_operators/comparison.rs +87 -2
  381. data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
  382. data/rust/src/kernel/expression_operators/logical.rs +9 -3
  383. data/rust/src/kernel/expression_operators/membership.rs +3 -1
  384. data/rust/src/kernel/expression_operators/mod.rs +34 -21
  385. data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
  386. data/rust/src/kernel/expression_operators/positional.rs +84 -30
  387. data/rust/src/kernel/expression_operators/presence.rs +52 -13
  388. data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
  389. data/rust/src/kernel/expression_operators/sized.rs +24 -8
  390. data/rust/src/kernel/expression_operators/text.rs +120 -27
  391. data/rust/src/kernel/expression_operators/to_string.rs +10 -4
  392. data/rust/src/kernel/json.rs +244 -148
  393. data/rust/src/kernel/mod.rs +47 -33
  394. data/rust/src/kernel/named_query.rs +63 -15
  395. data/rust/src/kernel/needs.rs +112 -25
  396. data/rust/src/kernel/orchestrate.rs +980 -581
  397. data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
  398. data/rust/src/kernel/pattern.rs +107 -34
  399. data/rust/src/kernel/present.rs +261 -0
  400. data/rust/src/kernel/query_comparators.rs +118 -41
  401. data/rust/src/kernel/query_ordering.rs +13 -5
  402. data/rust/src/kernel/read_model.rs +479 -181
  403. data/rust/src/kernel/reference_lookup.rs +122 -26
  404. data/rust/src/kernel/repository.rs +293 -66
  405. data/rust/src/kernel/routing.rs +58 -42
  406. data/rust/src/kernel/ruby_float.rs +139 -0
  407. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
  408. data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
  409. data/rust/src/kernel/vocab/field_hint.rs +19 -25
  410. data/rust/src/kernel/vocab/mod.rs +6 -6
  411. data/rust/src/kernel/vocab/query_comparator.rs +23 -24
  412. data/rust/src/kernel/vocab/refusal_template.rs +501 -297
  413. data/rust/src/lib.rs +50 -0
  414. data/rust/src/main.rs +4 -1
  415. data/rust/web/Cargo.toml +36 -0
  416. data/rust/web/clippy.toml +4 -0
  417. metadata +85 -15
  418. data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
  419. data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
  420. data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
  421. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
  422. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
  423. data/lib/hecks/ports/authentication.port +0 -6
  424. data/lib/hecks/ports/authentication.rb +0 -67
  425. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
  426. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
  427. data/rust/host/src/approval.rs +0 -652
@@ -10,21 +10,22 @@ module Hecks
10
10
  # Renders `glossary.md`, the source the glossary page is built from.
11
11
  # Uses only Markdown that GitHub renders as-is, so the file reads on its own.
12
12
  module Markdown
13
- LEDE = "Every term %<name>s uses, in the words of the people who work in it — grouped under the thing " \
14
- "each belongs to, and listed A to Z within it. This page is generated from the working " \
15
- "specification, so it says what the system does today, not what anyone hoped it would do. " \
16
- "If a sentence here reads wrong to you, the specification is wrong: say so.".freeze
13
+ LEDE = "The terms %<name>s uses, written the way the people who work in it would say them. " \
14
+ "Each term sits under the thing it belongs to: first what can be done with it, then " \
15
+ "what it records, the lists you can ask for, and what it is made of. The page is " \
16
+ "generated from the working specification, so it describes what the system does today. " \
17
+ "If a sentence reads wrong to you, the specification is wrong, and it is worth saying so.".freeze
17
18
 
18
19
  STANDING = {
19
- ROLES => "Who does what. A role is named once here rather than under every term it touches.",
20
- READ_MODELS => "Questions answered across more than one of the things above.",
21
- REACTIONS => "What happens on its own, in response to something this specification does not itself raise."
20
+ ROLES => "Who does what. Each role is listed once, with the actions it is responsible for.",
21
+ READ_MODELS => "Questions answered by reading more than one of the things above.",
22
+ REACTIONS => "What happens on its own when something arrives from outside this specification."
22
23
  }.freeze
23
24
 
24
25
  module_function
25
26
 
26
27
  # Titles the document after its chapter.
27
- def title(bluebook) = "#{bluebook.name} — Glossary"
28
+ def title(bluebook) = "#{bluebook.name} glossary"
28
29
 
29
30
  # Renders the full `glossary.md` document.
30
31
  #
@@ -41,13 +42,15 @@ module Hecks
41
42
  parts = ["# #{title(bluebook)}"]
42
43
  parts << "> #{bluebook.vision}" if bluebook.vision
43
44
  parts << format(LEDE, name: bluebook.name)
44
- parts << fence(Mermaid.map(bluebook))
45
+ parts << fence(Mermaid.map(bluebook)) if bluebook.aggregates.size > 1
45
46
  parts.join("\n\n")
46
47
  end
47
48
 
48
49
  # Renders one `##` section: its opening, then every term inside it.
49
50
  def section_text(section, document)
50
- terms = section.terms.map { |entry| term_text(entry, document.index) }
51
+ terms = Groups.walk(section).map do |kind, item|
52
+ kind == :group ? "### #{item}" : term_text(item, document.index)
53
+ end
51
54
  ["## #{section.title}", *section_opening(section, document), *terms].join("\n\n")
52
55
  end
53
56
 
@@ -67,11 +70,19 @@ module Hecks
67
70
  parts + diagrams(aggregate, bluebook) + facts(always_true(aggregate, bluebook), marked)
68
71
  end
69
72
 
70
- # The "How it fits" diagram, and "How it moves" when the aggregate has a lifecycle.
73
+ # The "How it fits" diagram when the aggregate has neighbours to draw, and "How it moves"
74
+ # with each move in words when it has a lifecycle.
71
75
  def diagrams(aggregate, bluebook)
72
- parts = ["**How it fits**", fence(Mermaid.context(aggregate, bluebook))]
73
- parts << "**How it moves**" << fence(Mermaid.lifecycle(aggregate)) if aggregate.lifecycle
74
- parts
76
+ parts = []
77
+ parts << "**How it fits**" << fence(Mermaid.context(aggregate, bluebook)) if Mermaid.neighbours?(aggregate, bluebook)
78
+ parts + moves(aggregate)
79
+ end
80
+
81
+ def moves(aggregate)
82
+ return [] unless aggregate.lifecycle
83
+
84
+ lines = Statements::Actions.lifecycle(aggregate).drop(1).map { |sentence| "- #{sentence}" }
85
+ ["**How it moves**", fence(Mermaid.lifecycle(aggregate)), lines.join("\n")]
75
86
  end
76
87
 
77
88
  # The "Always true" and "Handled as sensitive" lists, each only when it has lines.
@@ -85,25 +96,17 @@ module Hecks
85
96
  parts
86
97
  end
87
98
 
88
- # The statements projection's sentences for `aggregate` and its entities, spoken.
89
- # Invariants stay verbatim; construct names in other sentences are replaced with
90
- # how they are said, because this page never shows an identifier.
91
- def always_true(aggregate, bluebook)
92
- names = bluebook.aggregates.flat_map { |other| [other.hecks_name, *other.entities.map(&:hecks_name)] }
99
+ # The statements projection's relationship and invariant sentences for `aggregate` and
100
+ # its entities, each said once.
101
+ def always_true(aggregate, _bluebook)
93
102
  [aggregate, *aggregate.entities].flat_map do |holder|
94
- Statements.attribute_statements(holder).map { |sentence| spoken(sentence, names) } +
95
- Statements.invariant_statements(holder)
103
+ Statements.attribute_statements(holder) + Statements.invariant_statements(holder)
96
104
  end.uniq
97
105
  end
98
106
 
99
- # Replaces every whole-word construct name in `sentence` with how it is said.
100
- def spoken(sentence, names)
101
- names.reduce(sentence) { |text, name| text.gsub(/\b#{Regexp.escape(name)}\b/, Naming.words(name)) }
102
- end
103
-
104
- # Renders one term's `###` heading and its paragraphs.
107
+ # Renders one term's `####` heading and its paragraphs.
105
108
  def term_text(entry, index)
106
- ["### #{entry.headword}", *Sentences.paragraphs(entry, index)].join("\n\n")
109
+ ["#### #{entry.headword}", *Sentences.paragraphs(entry, index)].join("\n\n")
107
110
  end
108
111
 
109
112
  # Wraps a Mermaid diagram's source in a fenced code block.
@@ -35,6 +35,9 @@ module Hecks
35
35
  (lines << " classDef focus stroke-width:3px").uniq.join("\n")
36
36
  end
37
37
 
38
+ # @return [Boolean] whether the aggregate has anything around it worth drawing
39
+ def neighbours?(aggregate, bluebook) = !neighbours(aggregate, bluebook).empty?
40
+
38
41
  # The nodes and edges for the aggregate's entities, what it references, and what
39
42
  # references it.
40
43
  def neighbours(aggregate, bluebook)
@@ -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;