hecks 1.2.0 → 1.4.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.
- checksums.yaml +4 -4
- data/lib/hecks/adapters/driven/claude_code.rb +7 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +10 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +8 -8
- data/lib/hecks/adapters/driven/governance_authorization.rb +31 -13
- data/lib/hecks/adapters/driven/heki/journal.rb +60 -2
- data/lib/hecks/adapters/driven/heki/saga_store.rb +5 -5
- data/lib/hecks/adapters/driven/heki.rb +13 -7
- data/lib/hecks/adapters/driven/identity_registry.rb +2 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +3 -3
- data/lib/hecks/adapters/driven/lambda/client.rb +34 -9
- data/lib/hecks/adapters/driven/lambda.rb +39 -33
- data/lib/hecks/adapters/driven/local_storage.rb +17 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +1 -1
- data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
- data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
- data/lib/hecks/adapters/driven/postgres.rb +175 -28
- data/lib/hecks/adapters/driven/postgres_era.adapter +5 -0
- data/lib/hecks/adapters/driven/prism.rb +4 -4
- data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
- data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
- data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
- data/lib/hecks/adapters/driven/sqlite.rb +181 -21
- data/lib/hecks/adapters/driven.rb +4 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +145 -0
- data/lib/hecks/behaviors/dsl.rb +2 -2
- data/lib/hecks/behaviors/expectations.rb +51 -23
- data/lib/hecks/behaviors/ir.rb +1 -1
- data/lib/hecks/behaviors/rspec.rb +1 -1
- data/lib/hecks/behaviors/runner.rb +2 -2
- data/lib/hecks/behaviors.rb +1 -1
- data/lib/hecks/bluebook/aggregate.rb +13 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +10 -10
- data/lib/hecks/bluebook/assembly/build.rb +1 -1
- data/lib/hecks/bluebook/assembly/contract.rb +39 -16
- data/lib/hecks/bluebook/assembly/contracts.rb +40 -38
- data/lib/hecks/bluebook/assembly/marks.rb +18 -18
- data/lib/hecks/bluebook/assembly/specializer.rb +19 -21
- data/lib/hecks/bluebook/assembly.rb +9 -9
- data/lib/hecks/bluebook/attribute.rb +9 -9
- data/lib/hecks/bluebook/behaviour/aggregate.rb +11 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +5 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +23 -5
- data/lib/hecks/bluebook/behaviour/command.rb +23 -23
- data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -3
- data/lib/hecks/bluebook/behaviour/entity.rb +8 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +4 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +5 -5
- data/lib/hecks/bluebook/behaviour/policy.rb +12 -12
- data/lib/hecks/bluebook/behaviour/process_manager.rb +7 -7
- data/lib/hecks/bluebook/behaviour/query.rb +1 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +8 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +12 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +6 -6
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +28 -9
- data/lib/hecks/bluebook/command.rb +12 -12
- data/lib/hecks/bluebook/domain_port.rb +9 -9
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +96 -96
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +41 -41
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +22 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +111 -74
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +48 -30
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +103 -103
- data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
- data/lib/hecks/bluebook/dsl/entity_builder.rb +56 -56
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +89 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +17 -17
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +30 -21
- data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +35 -35
- data/lib/hecks/bluebook/dsl/query_builder.rb +5 -5
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +34 -34
- data/lib/hecks/bluebook/dsl/rule_reference.rb +41 -39
- data/lib/hecks/bluebook/dsl/translation_builder.rb +9 -9
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +16 -16
- data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
- data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
- data/lib/hecks/bluebook/entity.rb +11 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +20 -20
- data/lib/hecks/bluebook/expression/ast_reader.rb +3 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +9 -9
- data/lib/hecks/bluebook/expression/evaluator.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +18 -18
- data/lib/hecks/bluebook/expression/resolver.rb +60 -62
- data/lib/hecks/bluebook/hexagon.rb +1 -1
- data/lib/hecks/bluebook/lifecycle.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +122 -86
- data/lib/hecks/bluebook/meta_validator/plan.rb +39 -39
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +43 -43
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +44 -37
- data/lib/hecks/bluebook/meta_validator/shapes.rb +25 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +145 -31
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +6 -6
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +5 -5
- data/lib/hecks/bluebook/meta_validator.rb +70 -70
- data/lib/hecks/bluebook/model_check.rb +301 -84
- data/lib/hecks/bluebook/pattern_subset.rb +9 -9
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +14 -14
- data/lib/hecks/bluebook/project_register.rb +6 -6
- data/lib/hecks/bluebook/query.rb +4 -4
- data/lib/hecks/bluebook/read_model.rb +14 -14
- data/lib/hecks/bluebook/reference.rb +8 -8
- data/lib/hecks/bluebook/smoke_test.rb +19 -19
- data/lib/hecks/bluebook/synthesizer.rb +12 -12
- data/lib/hecks/bluebook/translation.rb +4 -4
- data/lib/hecks/bluebook/value_object.rb +6 -6
- data/lib/hecks/bluebook.rb +2 -2
- data/lib/hecks/codemod/legacy_dispatch_args.rb +299 -0
- data/lib/hecks/codemod/legacy_dispatch_recorder.rb +186 -0
- data/lib/hecks/codemod.rb +36 -35
- data/lib/hecks/construct.rb +6 -6
- data/lib/hecks/corpus.rb +317 -0
- data/lib/hecks/deprecation.rb +95 -0
- data/lib/hecks/doc/reference.rb +19 -19
- data/lib/hecks/embryonaut_bluebook.rb +11 -11
- data/lib/hecks/facade/cli_door.rb +69 -10
- data/lib/hecks/facade/cli_runner.rb +105 -24
- data/lib/hecks/facade/command_request.rb +23 -0
- data/lib/hecks/facade/handle.rb +79 -32
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +42 -27
- data/lib/hecks/facade/surface/chapter.rb +26 -17
- data/lib/hecks/facade/surface.rb +16 -3
- data/lib/hecks/facade.rb +15 -4
- data/lib/hecks/forms/app.rb +46 -30
- data/lib/hecks/forms/command_form_renderer.rb +70 -9
- data/lib/hecks/forms/field_renderer.rb +142 -6
- data/lib/hecks/forms/field_shape.rb +183 -20
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +2 -2
- data/lib/hecks/forms/query_form_renderer.rb +2 -2
- data/lib/hecks/forms/record_renderer.rb +2 -2
- data/lib/hecks/forms/record_table.rb +1 -1
- data/lib/hecks/forms/value_object_shape.rb +3 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +1 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework.rb +48 -17
- data/lib/hecks/freezer.rb +11 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +90 -90
- data/lib/hecks/fuzzing/combination_miner.rb +119 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +361 -0
- data/lib/hecks/fuzzing/coverage_campaign.rb +118 -0
- data/lib/hecks/fuzzing/differential.rb +158 -0
- data/lib/hecks/fuzzing/domain_generator.rb +694 -0
- data/lib/hecks/fuzzing/era_boundary.rb +124 -0
- data/lib/hecks/fuzzing/form_census.rb +199 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +95 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +6 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +226 -38
- data/lib/hecks/fuzzing/nondeterministic.rb +67 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +161 -0
- data/lib/hecks/fuzzing/properties/corrections.rb +100 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +321 -41
- data/lib/hecks/fuzzing/properties/guards.rb +129 -26
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +32 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +31 -11
- data/lib/hecks/fuzzing/properties/outbox.rb +142 -0
- data/lib/hecks/fuzzing/properties/querying.rb +17 -17
- data/lib/hecks/fuzzing/properties.rb +84 -39
- data/lib/hecks/fuzzing/qa_settings.rb +152 -0
- data/lib/hecks/fuzzing/replay.rb +224 -114
- data/lib/hecks/fuzzing/rotation_priority.rb +94 -0
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +113 -0
- data/lib/hecks/fuzzing/self_consistency.rb +676 -0
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +526 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +101 -30
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +55 -8
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +31 -11
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +123 -32
- data/lib/hecks/fuzzing/sequence_generator.rb +152 -31
- data/lib/hecks/fuzzing/shrinker.rb +197 -0
- data/lib/hecks/fuzzing/structural_skips.rb +39 -0
- data/lib/hecks/fuzzing/sweep_depth.rb +53 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +180 -0
- data/lib/hecks/fuzzing/value_generator.rb +66 -14
- data/lib/hecks/fuzzing.rb +11 -0
- data/lib/hecks/grammar/evolve.rb +10 -10
- data/lib/hecks/grammar.rb +7 -7
- data/lib/hecks/ir.rb +13 -13
- data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
- data/lib/hecks/language/bluebook/policy.bluebook +11 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +365 -15
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +9 -9
- data/lib/hecks/naming.rb +89 -21
- data/lib/hecks/ports/access_control.rb +58 -2
- data/lib/hecks/ports/agent/answers.rb +83 -6
- data/lib/hecks/ports/agent.rb +119 -35
- data/lib/hecks/ports/authentication.rb +44 -4
- data/lib/hecks/ports/authorization.rb +53 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +24 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +18 -1
- data/lib/hecks/ports/loading.rb +4 -0
- data/lib/hecks/ports/persistence/append_only.rb +172 -8
- data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
- data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
- data/lib/hecks/ports/persistence/execution.rb +4 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
- data/lib/hecks/ports/persistence/plugin.rb +42 -4
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +160 -58
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +28 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +44 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +57 -4
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +338 -83
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +42 -8
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +5 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
- data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
- data/lib/hecks/ports/persistence/repository_factory.rb +29 -6
- data/lib/hecks/ports/persistence/state_codec.rb +319 -0
- data/lib/hecks/ports/persistence.rb +36 -1
- data/lib/hecks/ports/projection.rb +61 -7
- data/lib/hecks/ports/query/in_memory.rb +3 -3
- data/lib/hecks/ports/query/ordering.rb +6 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/projections/bootstrap_table.rb +112 -0
- data/lib/hecks/projections/diagrams.rb +75 -75
- data/lib/hecks/projections/glossary/html.rb +250 -0
- data/lib/hecks/projections/glossary/markdown.rb +105 -0
- data/lib/hecks/projections/glossary/mermaid.rb +110 -0
- data/lib/hecks/projections/glossary/page.css +271 -0
- data/lib/hecks/projections/glossary/page.js +72 -0
- data/lib/hecks/projections/glossary/sections.rb +17 -0
- data/lib/hecks/projections/glossary/sentences.rb +205 -0
- data/lib/hecks/projections/glossary.rb +214 -286
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +18 -17
- data/lib/hecks/projections/model.rb +25 -21
- data/lib/hecks/projections/oidc.rb +7 -7
- data/lib/hecks/projections/parser_table.rb +5 -5
- data/lib/hecks/projections/reference.rb +3 -3
- data/lib/hecks/projections/rust_vocabulary.rb +443 -0
- data/lib/hecks/projections/shape.rb +2 -2
- data/lib/hecks/projections/statements.rb +11 -11
- data/lib/hecks/projections/vocabulary.rb +9 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +29 -29
- data/lib/hecks/projector/docs_projector.rb +13 -13
- data/lib/hecks/projector/exporter.rb +42 -21
- data/lib/hecks/projector/ir_projector.rb +1 -1
- data/lib/hecks/projector/narrate_projector.rb +15 -22
- data/lib/hecks/projector/target.rb +13 -13
- data/lib/hecks/projector.rb +15 -15
- data/lib/hecks/query_ir.rb +47 -47
- data/lib/hecks/query_specification/common/comparators.rb +19 -3
- data/lib/hecks/query_specification/common/comparison.rb +132 -24
- data/lib/hecks/query_specification/common/dsl.rb +65 -9
- data/lib/hecks/query_specification/common/null_policy.rb +57 -13
- data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
- data/lib/hecks/query_specification/common/options.rb +25 -0
- data/lib/hecks/query_specification/field_path.rb +69 -15
- data/lib/hecks/query_specification/hop_path.rb +57 -20
- data/lib/hecks/query_specification/read_model/specification.rb +4 -0
- data/lib/hecks/rendering.rb +3 -3
- data/lib/hecks/router/namespace_installer.rb +3 -3
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/aggregate_lock.rb +11 -11
- data/lib/hecks/runtime/boot_gates.rb +3 -3
- data/lib/hecks/runtime/caller.rb +8 -8
- data/lib/hecks/runtime/capability_graph.rb +2 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +22 -25
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +64 -67
- data/lib/hecks/runtime/command_interpreter.rb +159 -102
- data/lib/hecks/runtime/command_rules/admissibility.rb +69 -69
- data/lib/hecks/runtime/command_rules/arithmetic.rb +137 -91
- data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
- data/lib/hecks/runtime/command_rules/emission.rb +18 -1
- data/lib/hecks/runtime/command_rules/references.rb +94 -19
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +11 -11
- data/lib/hecks/runtime/dispatcher.rb +283 -90
- data/lib/hecks/runtime/entity_element.rb +282 -46
- data/lib/hecks/runtime/entity_interpreter.rb +160 -69
- data/lib/hecks/runtime/errors.rb +19 -19
- data/lib/hecks/runtime/event.rb +6 -6
- data/lib/hecks/runtime/identity.rb +22 -22
- data/lib/hecks/runtime/instance.rb +39 -14
- data/lib/hecks/runtime/interpreting.rb +12 -12
- data/lib/hecks/runtime/invocation.rb +276 -0
- data/lib/hecks/runtime/loader.rb +14 -14
- data/lib/hecks/runtime/outbox.rb +23 -23
- data/lib/hecks/runtime/policy_interpreter.rb +54 -54
- data/lib/hecks/runtime/port_operation_interpreter.rb +22 -19
- data/lib/hecks/runtime/query_interpreter.rb +111 -56
- data/lib/hecks/runtime/reaction_invocation.rb +76 -9
- data/lib/hecks/runtime/read_model_interpreter.rb +40 -40
- data/lib/hecks/runtime/rebuild_sweep.rb +4 -4
- data/lib/hecks/runtime/reference_hop.rb +6 -6
- data/lib/hecks/runtime/refusal_wording.rb +92 -112
- data/lib/hecks/runtime/registry/saga_persistence.rb +21 -21
- data/lib/hecks/runtime/registry/verification.rb +36 -26
- data/lib/hecks/runtime/registry.rb +56 -27
- data/lib/hecks/runtime/remote_dispatcher.rb +38 -23
- data/lib/hecks/runtime/routing.rb +10 -88
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +17 -17
- data/lib/hecks/runtime/saga_interpreter.rb +93 -56
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +9 -9
- data/lib/hecks/runtime/tenant_scope.rb +5 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +379 -226
- data/lib/hecks/runtime/value/entity_list_coercion.rb +248 -0
- data/lib/hecks/runtime/value.rb +28 -23
- data/lib/hecks/runtime.rb +7 -7
- data/lib/hecks/storehouse.rb +64 -64
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +207 -5
- data/lib/hecks.rb +13 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +11 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +14 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +11 -11
- metadata +43 -2
|
@@ -2,15 +2,15 @@ require_relative "../naming"
|
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Projector
|
|
5
|
-
# A
|
|
5
|
+
# A bluebook, projected as its own command-line surface.
|
|
6
6
|
#
|
|
7
7
|
# Every verb a domain declares is a subcommand; every argument is an
|
|
8
|
-
# option whose
|
|
8
|
+
# option whose type, whose admitted values and whose required-ness are
|
|
9
9
|
# already stated in the chapter. A hand-written CLI restates all of it and
|
|
10
10
|
# then drifts — and the first thing to drift is the help text, which is the
|
|
11
11
|
# only part anybody reads.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
13
|
+
# What is projected, and what is not. This answers the surface — the verb
|
|
14
14
|
# tree, the argument spec, the usage text — and nothing executes here. One
|
|
15
15
|
# small generic runner (`bin/run`) boots a domain, asks for this, parses
|
|
16
16
|
# against it and dispatches.
|
|
@@ -23,7 +23,7 @@ module Hecks
|
|
|
23
23
|
# reads. Projecting the surface keeps one dispatcher and a help text that
|
|
24
24
|
# cannot be stale, because it is computed at the moment it is printed.
|
|
25
25
|
#
|
|
26
|
-
#
|
|
26
|
+
# **The typing is the point**. A CLI hands everything over as a String.
|
|
27
27
|
# `sequence.value=99` has to become the Integer 99 or the runtime refuses
|
|
28
28
|
# it, and the only honest place to learn that is the value object's own
|
|
29
29
|
# declared field type. A CLI that guessed — "it looks like a number" —
|
|
@@ -32,7 +32,7 @@ module Hecks
|
|
|
32
32
|
module CliProjector
|
|
33
33
|
module_function
|
|
34
34
|
|
|
35
|
-
#
|
|
35
|
+
# Two namespaces, not one — `{ verbs:, questions:, usage: }`.
|
|
36
36
|
#
|
|
37
37
|
# A chapter may legally declare a command and a query of one name: the
|
|
38
38
|
# language namespaces them and `Banking::Account.Open` is both, in the
|
|
@@ -62,32 +62,32 @@ module Hecks
|
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
# A
|
|
65
|
+
# A port is a verb too, and leaving it off the map was a real gap
|
|
66
66
|
# rather than a tasteful omission. The runtime has always dispatched
|
|
67
67
|
# a port operation by exactly the same name as a command — the
|
|
68
68
|
# projection simply never listed one, so `run_specs` and `file`
|
|
69
69
|
# answered "no such verb" while working perfectly through Ruby.
|
|
70
70
|
#
|
|
71
71
|
# It matters most for the caller with no other door. An agent that
|
|
72
|
-
# may not shell out reaches this domain
|
|
72
|
+
# may not shell out reaches this domain only through the projected
|
|
73
73
|
# CLI, and a port it cannot see is a capability it does not have.
|
|
74
74
|
aggregate.ports.each do |port|
|
|
75
75
|
port.operations.each { |o| claim(verbs, name_for(aggregate, o), port_spec(bluebook, aggregate, port, o)) }
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
# A
|
|
79
|
+
# A report is a question too, and leaving it off was the same gap the
|
|
80
80
|
# ports had: `Dispatcher#query` has always answered `Domain.ReportName`,
|
|
81
81
|
# the projection simply never listed one — so the composed reads worked
|
|
82
82
|
# from Ruby and did not exist for anybody whose only door is the command
|
|
83
83
|
# line.
|
|
84
84
|
#
|
|
85
|
-
# It matters most for exactly what a report is
|
|
85
|
+
# It matters most for exactly what a report is for. Every other question
|
|
86
86
|
# here answers with rows and leaves the arithmetic to the reader; a
|
|
87
87
|
# `group_by` report is the one that counts. An agent that cannot reach
|
|
88
88
|
# it can list bugs all day and never answer "how are we doing".
|
|
89
89
|
#
|
|
90
|
-
#
|
|
90
|
+
# **One dot, not two** — a report belongs to the chapter rather than to any
|
|
91
91
|
# aggregate (that is what rootless means), so it is addressed
|
|
92
92
|
# `QualityControl.BugsByStatus` where a query is
|
|
93
93
|
# `QualityControl::Bug.Queue`. `Dispatcher#query` splits on precisely
|
|
@@ -96,7 +96,7 @@ module Hecks
|
|
|
96
96
|
claim(questions, Naming.snake(model.hecks_name), report_spec(bluebook, model))
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
#
|
|
99
|
+
# The short spelling, where it cannot be ambiguous. `pizzas
|
|
100
100
|
# create_pizza` rather than `pizzas order.create_pizza` — the
|
|
101
101
|
# aggregate is worth typing only when two of them declare the same
|
|
102
102
|
# verb, and in a one-aggregate domain it never is. Both spellings are
|
|
@@ -129,7 +129,7 @@ module Hecks
|
|
|
129
129
|
end
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
-
# A
|
|
132
|
+
# A name is claimed once. A command and a query of one name are legal in
|
|
133
133
|
# a chapter — the language namespaces them — and ambiguous as
|
|
134
134
|
# subcommands. Refused here rather than silently resolving to whichever
|
|
135
135
|
# was walked first, which is how `Ticket.Filed` (a command) and
|
|
@@ -156,7 +156,7 @@ module Hecks
|
|
|
156
156
|
entity ? "#{entity.hecks_name}." : "", verb.hecks_name].join
|
|
157
157
|
end
|
|
158
158
|
|
|
159
|
-
#
|
|
159
|
+
# The arguments a receiver adds, before any verb-specific one. Shared by
|
|
160
160
|
# `command_spec` and `port_spec` — a port operation always addresses an
|
|
161
161
|
# aggregate record (`port_spec` passes `receiver: :aggregate`, never
|
|
162
162
|
# `:entity` or `nil`, because a port is declared on an aggregate, never
|
|
@@ -194,7 +194,7 @@ module Hecks
|
|
|
194
194
|
(command.creates? ? nil : :aggregate)
|
|
195
195
|
end
|
|
196
196
|
|
|
197
|
-
#
|
|
197
|
+
# The receiver is not a command argument. An aggregate command names
|
|
198
198
|
# its record through to; an entity command needs both the aggregate
|
|
199
199
|
# record and the entity element within it. Keeping those paths in the
|
|
200
200
|
# projected option list makes the human-facing request complete while
|
|
@@ -215,7 +215,7 @@ module Hecks
|
|
|
215
215
|
refusals: refusals(command, holder), arguments: arguments }
|
|
216
216
|
end
|
|
217
217
|
|
|
218
|
-
# A
|
|
218
|
+
# A port operation reads as a verb but reports as a boundary.
|
|
219
219
|
#
|
|
220
220
|
# `creates: false` because it makes no record, and `refusals: []`
|
|
221
221
|
# because it has none in the sense every other verb means: a command's
|
|
@@ -223,7 +223,7 @@ module Hecks
|
|
|
223
223
|
# outbound operation's failure is somebody else's sentence, unknowable
|
|
224
224
|
# from here.
|
|
225
225
|
#
|
|
226
|
-
#
|
|
226
|
+
# The summary names both endings, which is the one thing a caller most
|
|
227
227
|
# needs and cannot infer. `run_specs` looks like it either works or
|
|
228
228
|
# errors; what it actually does is answer `SpecsCompleted` even when the
|
|
229
229
|
# suite is red, and refuse only when rspec could not run. Somebody
|
|
@@ -233,7 +233,7 @@ module Hecks
|
|
|
233
233
|
arguments = receiver_options(:aggregate, aggregate, nil) +
|
|
234
234
|
operation.attributes.flat_map { |a| options_for(a, aggregate, aggregate) }
|
|
235
235
|
|
|
236
|
-
#
|
|
236
|
+
# The wire name carries the port, the typed name does not.
|
|
237
237
|
#
|
|
238
238
|
# `Dispatcher#dispatch` splits a verb into head and sub and looks the
|
|
239
239
|
# head up as a port, so a port operation is addressed
|
|
@@ -244,7 +244,7 @@ module Hecks
|
|
|
244
244
|
# names it short, which is the same split `shorten` already makes.
|
|
245
245
|
{ verb: [fqn(bluebook, aggregate, operation).sub(/\.[^.]+\z/, ""), port.name, operation.hecks_name].join("."),
|
|
246
246
|
kind: :command, creates: false, receiver: :aggregate, refusals: [],
|
|
247
|
-
# `role:`
|
|
247
|
+
# `role:` here is descriptive text, not an authorization gate —
|
|
248
248
|
# who calls whom through the port, for `--help`/`verb_help`'s
|
|
249
249
|
# "issued by" line. A port operation never reaches
|
|
250
250
|
# `CommandRules::Authorization#refuse_role_mismatch` (only
|
|
@@ -266,7 +266,7 @@ module Hecks
|
|
|
266
266
|
"Ask #{port.name} — answers #{operation.answers}, refuses #{operation.refuses}"
|
|
267
267
|
end
|
|
268
268
|
|
|
269
|
-
# A
|
|
269
|
+
# A rootless report takes nothing; a rooted one takes the id of the
|
|
270
270
|
# record it is a view of, under the name the model gave that reference.
|
|
271
271
|
def report_spec(bluebook, model)
|
|
272
272
|
arguments =
|
|
@@ -293,14 +293,14 @@ module Hecks
|
|
|
293
293
|
|
|
294
294
|
# ── one argument, flattened ───────────────────────────────────────
|
|
295
295
|
|
|
296
|
-
# A
|
|
296
|
+
# A value object becomes one option per field, dotted. `commit` typed
|
|
297
297
|
# `CommitRef` is `--commit.value`, because that is the shape the runtime
|
|
298
298
|
# wants and a flat `--commit` would have to guess which field it meant.
|
|
299
299
|
# Single-field value objects — almost all of them — read fine either way,
|
|
300
300
|
# and the runner accepts the short form for exactly those.
|
|
301
|
-
#
|
|
301
|
+
# Recursive, and it has to be. A value object may hold another one —
|
|
302
302
|
# pizzas' `Pizza` holds a `Price` and a `Size` — so stopping after one
|
|
303
|
-
# level produced `pizza.price_cents=1500` and sent the
|
|
303
|
+
# level produced `pizza.price_cents=1500` and sent the string "1500"
|
|
304
304
|
# where `{ cents: 1500 }` belonged.
|
|
305
305
|
#
|
|
306
306
|
# The runtime took it. `qa/FINDINGS.md` #2 is exactly that gap —
|
|
@@ -316,13 +316,13 @@ module Hecks
|
|
|
316
316
|
value_object = value_object_for(attribute, holder, aggregate)
|
|
317
317
|
return [scalar_option(path, attribute, optional)] unless value_object
|
|
318
318
|
|
|
319
|
-
# A
|
|
319
|
+
# A list says so, all the way down to its leaves.
|
|
320
320
|
#
|
|
321
321
|
# Without this a `list_of(Tag)` projected exactly like a single Tag:
|
|
322
322
|
# one option, `tags.value`, indistinguishable from a scalar. So the
|
|
323
323
|
# help said to pass one, `CliDoor#bury` overwrote the leaf each time,
|
|
324
|
-
# and passing two tags stored the second and lost the first
|
|
325
|
-
#
|
|
324
|
+
# and passing two tags stored the second and lost the first without
|
|
325
|
+
# saying anything. A missing argument is refused loudly; a forgotten
|
|
326
326
|
# one is not, which makes it the more expensive of the two by far.
|
|
327
327
|
#
|
|
328
328
|
# The flag is carried on the leaf rather than kept beside the
|
|
@@ -390,7 +390,7 @@ module Hecks
|
|
|
390
390
|
program = options[:program] || "bin/run"
|
|
391
391
|
only = options[:verb]
|
|
392
392
|
|
|
393
|
-
#
|
|
393
|
+
# Which namespace, when both hold the name. `options[:ask]` says so;
|
|
394
394
|
# without it a `--help` for a question would print the command that
|
|
395
395
|
# shares its name, which banking has and which is how this was found.
|
|
396
396
|
if only
|
|
@@ -421,16 +421,16 @@ module Hecks
|
|
|
421
421
|
name ? "#{spec[:short]} is also #{name}" : ""
|
|
422
422
|
end
|
|
423
423
|
|
|
424
|
-
# A
|
|
424
|
+
# A query's `description` is written as a paragraph — it argues for why
|
|
425
425
|
# the list is worth reading. A verb table wants the first sentence of
|
|
426
426
|
# that argument; `--help` still prints the whole thing.
|
|
427
427
|
def first_sentence(text)
|
|
428
428
|
text.to_s.split(/(?<=\.)\s/).first.to_s
|
|
429
429
|
end
|
|
430
430
|
|
|
431
|
-
#
|
|
431
|
+
# Four text blocks, in fixed display order — meta (name/kind/role),
|
|
432
432
|
# invocation, arguments, refusals. Each block is independent of the
|
|
433
|
-
# others' content (only the
|
|
433
|
+
# others' content (only the output order is fixed, and stays fixed
|
|
434
434
|
# below), so each is its own method returning the lines it
|
|
435
435
|
# contributes — `[]` when it contributes none — concatenated in the
|
|
436
436
|
# same order the original inline version built them in.
|
|
@@ -2,38 +2,38 @@ require_relative "../naming"
|
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Projector
|
|
5
|
-
# A
|
|
5
|
+
# A bluebook, projected as its own usage documentation.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
7
|
+
# **What this is for**. A chapter in this corpus already contains everything a
|
|
8
8
|
# user of it needs: what each aggregate is (`description`), what each verb
|
|
9
9
|
# is for (`goal`) and who issues it (`role`), which states it moves
|
|
10
10
|
# between, what it refuses and in whose words (`given`, `invariant`,
|
|
11
11
|
# `ensures`), and what each list is worth reading (`description` on a
|
|
12
|
-
# query). None of that reaches the person who has to
|
|
12
|
+
# query). None of that reaches the person who has to call the domain.
|
|
13
13
|
# They read the source, or they read a document somebody wrote beside the
|
|
14
14
|
# source and stopped updating.
|
|
15
15
|
#
|
|
16
16
|
# `docs/implemented/reference/` is the precedent and the contrast. `bin/reference`
|
|
17
17
|
# generates it from the language's own Syntax chapter, so the reference
|
|
18
18
|
# for the DSL cannot drift from the DSL. This is the same trick one level
|
|
19
|
-
# down: the usage document for a
|
|
19
|
+
# down: the usage document for a domain, generated from that domain, so it
|
|
20
20
|
# cannot drift from the domain either.
|
|
21
21
|
#
|
|
22
|
-
#
|
|
22
|
+
# Why it is a projection and not a generator script. `Projector` is the
|
|
23
23
|
# repository's registry of "canonical IR in, external artifact out" (§30
|
|
24
24
|
# of the implementation plan), and this is exactly that shape: one
|
|
25
25
|
# bluebook's IR in, markdown out, no runtime needed and no store touched.
|
|
26
26
|
# Registered as `:docs` beside `:ir`, and reachable the way every
|
|
27
27
|
# projector is — `Projector.call(:docs, bluebook: ...)`.
|
|
28
28
|
#
|
|
29
|
-
#
|
|
29
|
+
# And as a method, which is the half that makes it get used.
|
|
30
30
|
# `Facade::Surface` already installs a module per chapter carrying
|
|
31
31
|
# `vision` and `aggregates`; `docs` joins them, so a booted domain answers
|
|
32
32
|
# `QualityControl.docs` and an aggregate door answers
|
|
33
33
|
# `QualityControl::Bug.docs`. A document you have to remember a script for
|
|
34
34
|
# is a document nobody reads.
|
|
35
35
|
#
|
|
36
|
-
#
|
|
36
|
+
# **What it deliberately does not do**: invent. Every sentence below comes out
|
|
37
37
|
# of the chapter. Where a chapter says nothing — an aggregate with no
|
|
38
38
|
# `description`, a command with no `goal` — the document says nothing
|
|
39
39
|
# rather than filling the gap with a restatement of the name, because a
|
|
@@ -55,7 +55,7 @@ module Hecks
|
|
|
55
55
|
"#{out.compact.join("\n").rstrip}\n"
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
# A
|
|
58
|
+
# A name that names nothing is refused, not answered with an empty
|
|
59
59
|
# document. Shipped the other way first: `options[:aggregate]` that
|
|
60
60
|
# matched no head returned "" and exit 0, which is the silent-wrong-
|
|
61
61
|
# answer shape this repository has already been bitten by twice in the
|
|
@@ -75,7 +75,7 @@ module Hecks
|
|
|
75
75
|
|
|
76
76
|
def chapter_header(bluebook, depth)
|
|
77
77
|
out = [h(depth, bluebook.name), ""]
|
|
78
|
-
#
|
|
78
|
+
# The vision first and as a quote. It is the one sentence in a chapter
|
|
79
79
|
# written for somebody who does not know the domain yet.
|
|
80
80
|
out += ["> #{bluebook.vision}", ""] if bluebook.vision
|
|
81
81
|
out << "#{bluebook.classification.to_s.capitalize} domain." if bluebook.classification
|
|
@@ -88,7 +88,7 @@ module Hecks
|
|
|
88
88
|
|
|
89
89
|
def anchor(name) = Naming.snake(name).tr("_", "-")
|
|
90
90
|
|
|
91
|
-
#
|
|
91
|
+
# What happens without anybody asking — the part of a domain a caller
|
|
92
92
|
# cannot discover from any verb list, and the part most likely to surprise
|
|
93
93
|
# them. A policy means one dispatch causes another, sometimes into a
|
|
94
94
|
# different domain entirely; a saga means a sequence is being driven on
|
|
@@ -145,7 +145,7 @@ module Hecks
|
|
|
145
145
|
def entity_section(aggregate, entity, depth)
|
|
146
146
|
out = [h(depth, "#{entity.hecks_name} (within #{aggregate.hecks_name})"), ""]
|
|
147
147
|
out += [entity.description, ""] if entity.description
|
|
148
|
-
#
|
|
148
|
+
# The thing a caller gets wrong first. An entity has no door of its
|
|
149
149
|
# own: its verb is spelled through the aggregate that holds it, and
|
|
150
150
|
# the parent's id travels alongside the entity's own identity.
|
|
151
151
|
out << "Addressed through its holder — `#{aggregate.hecks_name}.#{entity.hecks_name}.<Verb>`, " \
|
|
@@ -171,7 +171,7 @@ module Hecks
|
|
|
171
171
|
table(%w[attribute shape rules], rows)
|
|
172
172
|
end
|
|
173
173
|
|
|
174
|
-
# A
|
|
174
|
+
# A value object's fields, not its name. `commit` typed `CommitRef` tells
|
|
175
175
|
# a caller nothing; `{ value: String }` tells them what to send, which is
|
|
176
176
|
# the single most common thing to get wrong at this boundary — a bare
|
|
177
177
|
# scalar where an object is wanted.
|
|
@@ -277,7 +277,7 @@ module Hecks
|
|
|
277
277
|
end
|
|
278
278
|
end
|
|
279
279
|
|
|
280
|
-
#
|
|
280
|
+
# Every way this verb can say no, gathered from the three places a
|
|
281
281
|
# chapter states them — the lifecycle it is an edge of, its own
|
|
282
282
|
# `given`s, and the fact that a reference has to resolve. A caller
|
|
283
283
|
# reading only the argument list learns none of these, and they are
|
|
@@ -5,9 +5,9 @@ module Hecks
|
|
|
5
5
|
module Projector
|
|
6
6
|
# Registry-wide serialization to Hash/JSON: bluebook IR
|
|
7
7
|
# (`call`/`json`), era-adapter lineage-capability flags (`lineage`),
|
|
8
|
-
# and translation edges in both their
|
|
8
|
+
# and translation edges in both their digest-relevant declared shape
|
|
9
9
|
# (`translation_hash`, what ApprovalDigest hashes) and their
|
|
10
|
-
# consumer-ready
|
|
10
|
+
# consumer-ready compiled shape with precompiled SQL attached
|
|
11
11
|
# (`translations`/`compiled_translation_aggregate`). Read directly by
|
|
12
12
|
# bin/ir, bin/project_rust, and the translation/audit approval digest.
|
|
13
13
|
module Exporter
|
|
@@ -21,15 +21,15 @@ module Hecks
|
|
|
21
21
|
JSON.pretty_generate(call(registry))
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
# A
|
|
24
|
+
# A binding fact, deliberately not folded into `call`/`bluebook.to_h`
|
|
25
25
|
# above — the canonical IR is runtime-independent by design (ADR
|
|
26
|
-
# 0001: it describes what a bluebook
|
|
26
|
+
# 0001: it describes what a bluebook declares, never which adapter
|
|
27
27
|
# a deployment happens to bind it to), and "is this aggregate bound
|
|
28
28
|
# to a lineage-capable adapter" is exactly the kind of fact that
|
|
29
29
|
# answer can change per-deployment without the bluebook's own shape
|
|
30
30
|
# changing at all. Consumers that need it (bin/project_rust's own
|
|
31
31
|
# `ir.json` sidecar, rust/host's runtime era-aware seed overlay —
|
|
32
|
-
# dispatch.rs) merge this in as a
|
|
32
|
+
# dispatch.rs) merge this in as a separate top-level key, the same
|
|
33
33
|
# way `translations` already sits beside `call`'s output rather than
|
|
34
34
|
# inside it.
|
|
35
35
|
#
|
|
@@ -54,13 +54,13 @@ module Hecks
|
|
|
54
54
|
{ capable_aggregates: capable.map { |aggregate| { name: aggregate.name, storage_name: aggregate.storage_name } } }
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
# A
|
|
58
|
-
# aggregate's
|
|
57
|
+
# A binding fact, same shape/reasoning as `lineage` above: every
|
|
58
|
+
# aggregate's declared persistence adapter name (`persisted_by`),
|
|
59
59
|
# not part of the canonical bluebook shape `call` exports (ADR
|
|
60
60
|
# 0001 — the IR describes what's declared, never which adapter a
|
|
61
61
|
# deployment binds it to). Unlike `lineage`, this needs no era
|
|
62
62
|
# plugin — `BindingPolicy` is core, always loaded — and covers
|
|
63
|
-
#
|
|
63
|
+
# every aggregate, not just lineage-capable ones: `rust/host`
|
|
64
64
|
# (`ir.rs`'s own `refuse_unsupported_persistence_adapters`) reads
|
|
65
65
|
# this to refuse loudly, at boot, against a domain bound to an
|
|
66
66
|
# adapter it has no backend for (Heki, Memory, Sqlite, D1,
|
|
@@ -78,7 +78,28 @@ module Hecks
|
|
|
78
78
|
{ aggregates: aggregates }
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
#
|
|
81
|
+
# A binding fact, same shape/reasoning as `lineage`/`persistence`
|
|
82
|
+
# above: which chapter this domain's role checks resolve against
|
|
83
|
+
# (`Registry#authorization_provider_for` — the domain's own chapter
|
|
84
|
+
# or a framework member it attaches that declares `provides
|
|
85
|
+
# "authorization"`), with that chapter's declared verbs qualified.
|
|
86
|
+
# `rust/host` (auth.rs) reads this instead of naming Governance.
|
|
87
|
+
# `{}` when nothing this domain attaches provides authorization.
|
|
88
|
+
def authorization(registry, domain_name)
|
|
89
|
+
provider = registry.authorization_provider_for(domain_name)
|
|
90
|
+
return {} unless provider
|
|
91
|
+
|
|
92
|
+
capability = Bluebook::Capabilities::AUTHORIZATION
|
|
93
|
+
assignments = provider.provided_verb(capability, :assignments)
|
|
94
|
+
{
|
|
95
|
+
provider: provider.name,
|
|
96
|
+
grant: provider.provided_verb(capability, :grant),
|
|
97
|
+
assignments: assignments,
|
|
98
|
+
assignment_aggregate: assignments&.split(".")&.first
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Translation IR, always as an array, with each aggregate's
|
|
82
103
|
# precompiled SQL attached (`compiled_translation_aggregate`) —
|
|
83
104
|
# this is the export a consumer embeds (`ir.json`'s `translations`
|
|
84
105
|
# key), never the bare digest-relevant shape `edge_digest` hashes
|
|
@@ -104,16 +125,16 @@ module Hecks
|
|
|
104
125
|
JSON.pretty_generate(translations(registry))
|
|
105
126
|
end
|
|
106
127
|
|
|
107
|
-
#
|
|
108
|
-
#
|
|
128
|
+
# The digest-relevant shape — `ApprovalDigest.edge_digest` hashes
|
|
129
|
+
# exactly this, and only this, for exactly the reason `compiled_
|
|
109
130
|
# translation_aggregate` below must never be used for that
|
|
110
|
-
# purpose: a digest bound to the
|
|
111
|
-
#
|
|
131
|
+
# purpose: a digest bound to the compiled SQL, not just the
|
|
132
|
+
# declared rules, would invalidate an existing human approval the
|
|
112
133
|
# moment `Translation::RuleCompiler`'s own output format changed
|
|
113
134
|
# for any reason — a compiler refactor, a cosmetic SQL-formatting
|
|
114
135
|
# change — even when the declared rules an approver actually
|
|
115
|
-
# reviewed never changed at all. The approval binds to
|
|
116
|
-
#
|
|
136
|
+
# reviewed never changed at all. The approval binds to what was
|
|
137
|
+
# declared, not to what a particular compiler build happened to
|
|
117
138
|
# emit from it.
|
|
118
139
|
def translation_hash(translation)
|
|
119
140
|
{
|
|
@@ -137,15 +158,15 @@ module Hecks
|
|
|
137
158
|
drops: aggregate.drops.map(&:to_s),
|
|
138
159
|
retypes: aggregate.retypes.map { |retype| { from: retype.from, to: retype.to } },
|
|
139
160
|
computes: aggregate.computes.map { |compute| { from: compute.from, to: compute.to, sql: compute.sql } },
|
|
140
|
-
#
|
|
161
|
+
# **Previously missing** — found live while planning Rust-side mint
|
|
141
162
|
# support. An edge carrying only a rekey (no compute) had its
|
|
142
163
|
# approval bind to nothing rekey-specific at all: any two
|
|
143
164
|
# rekey edges with otherwise-identical renames/moves/converts/
|
|
144
|
-
# drops/retypes/computes produced the
|
|
165
|
+
# drops/retypes/computes produced the same digest regardless
|
|
145
166
|
# of what their `rekey sql:` actually said, and a rekey's own
|
|
146
167
|
# SQL could change without invalidating an existing approval.
|
|
147
168
|
# Same bug shape for `backfills` (present, just never
|
|
148
|
-
# exported). Fixing this
|
|
169
|
+
# exported). Fixing this changes every existing rekey/
|
|
149
170
|
# backfill edge's digest — any approval already recorded for
|
|
150
171
|
# one is invalidated by this fix and must be re-reviewed.
|
|
151
172
|
rekeys: aggregate.rekeys.map { |rekey| { sql: rekey.sql } },
|
|
@@ -153,14 +174,14 @@ module Hecks
|
|
|
153
174
|
}
|
|
154
175
|
end
|
|
155
176
|
|
|
156
|
-
#
|
|
177
|
+
# The export shape — `translation_aggregate`'s own digest-relevant
|
|
157
178
|
# fields, plus the precompiled SQL (`compiled_state_expression`/
|
|
158
179
|
# `compiled_id_expression`) a consumer embedding this JSON
|
|
159
180
|
# (rust/host's own boot-time mint) needs to execute the edge
|
|
160
|
-
# without compiling SQL itself. The
|
|
181
|
+
# without compiling SQL itself. The same call head_compiler.rb's
|
|
161
182
|
# own `compile_rules(declared)`/`id_case(guard, declared)` make at
|
|
162
183
|
# mint time, run here once at build/export time instead —
|
|
163
|
-
# `Translation::RuleCompiler` is the
|
|
184
|
+
# `Translation::RuleCompiler` is the one place this expression is
|
|
164
185
|
# built, called from both here and from head_compiler.rb's real
|
|
165
186
|
# per-mint assembly, so a consumer gets Ruby's own compiler's
|
|
166
187
|
# output verbatim, never a second, independently-authored SQL
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Projector
|
|
3
|
-
# The trivial case, on purpose: canonical IR projected
|
|
3
|
+
# The trivial case, on purpose: canonical IR projected as canonical
|
|
4
4
|
# IR. `Bluebook#to_h` already satisfies every §30 acceptance
|
|
5
5
|
# criterion on its own — no live runtime needed to call it, output is
|
|
6
6
|
# deterministic (the same golden fixtures `spec/ir_golden_spec.rb`
|
|
@@ -3,20 +3,20 @@ require_relative "../forms/field_shape"
|
|
|
3
3
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Projector
|
|
6
|
-
# A
|
|
6
|
+
# A bluebook, projected as prose an SME can read back and confirm.
|
|
7
7
|
#
|
|
8
|
-
#
|
|
8
|
+
# **What this is for**. `DocsProjector` already answers "what can I call and
|
|
9
9
|
# what does it want" for the person implementing against a domain —
|
|
10
10
|
# tables of arguments, shapes, refusal reasons. That is the wrong
|
|
11
11
|
# register for the person who can actually say whether the domain is
|
|
12
|
-
#
|
|
12
|
+
# right: the subject-matter expert who knows what an account is and has
|
|
13
13
|
# never read a markdown table in their life. This projects the same IR
|
|
14
14
|
# as sentences instead — "Debit — take money out. Issued by a Teller. It
|
|
15
15
|
# only goes through if the balance covers it." — so a domain can be
|
|
16
16
|
# read back to the person who can validate it without them learning the
|
|
17
17
|
# DSL first.
|
|
18
18
|
#
|
|
19
|
-
#
|
|
19
|
+
# Same source, same guarantee `DocsProjector` gives: nothing here is
|
|
20
20
|
# invented. Every sentence quotes a `description`, `goal`, or `given`
|
|
21
21
|
# already declared in the chapter; where a chapter says nothing, this
|
|
22
22
|
# says nothing rather than manufacturing a sentence out of an
|
|
@@ -24,7 +24,7 @@ module Hecks
|
|
|
24
24
|
# (`Projector.call(:narrate, bluebook: ...)`), same aggregate-scoping
|
|
25
25
|
# via `options[:aggregate]`.
|
|
26
26
|
#
|
|
27
|
-
#
|
|
27
|
+
# **What it does not do**: replace `DocsProjector`. A shape table still says
|
|
28
28
|
# "id of a Customer" more precisely than any sentence would, and an
|
|
29
29
|
# implementer still wants that. This is the other document the same IR
|
|
30
30
|
# is owed — one written for the reader who is being asked "is this
|
|
@@ -128,7 +128,7 @@ module Hecks
|
|
|
128
128
|
"#{header}\n\n#{body}"
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
#
|
|
131
|
+
# One paragraph, built from independent sentences — each sentence
|
|
132
132
|
# below states one unrelated fact about `command` (its goal, who
|
|
133
133
|
# issues it, whether it creates the holder, what it takes, what it
|
|
134
134
|
# references, what gates it, what it guarantees, what it emits), in
|
|
@@ -149,7 +149,7 @@ module Hecks
|
|
|
149
149
|
].compact.join(" ")
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
#
|
|
152
|
+
# The goal, verbatim — same rule `DocsProjector` holds to: quoted
|
|
153
153
|
# exactly as declared, not recased to fit mid-sentence, because the
|
|
154
154
|
# promise this whole projector makes is that a sentence here is a
|
|
155
155
|
# sentence the chapter actually wrote.
|
|
@@ -163,8 +163,8 @@ module Hecks
|
|
|
163
163
|
"Issued by #{a_or_an(command.role)} #{command.role}."
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
-
# `acts_on.nil?`,
|
|
167
|
-
# verb an
|
|
166
|
+
# `acts_on.nil?`, not `creates?` — `creates?` answers true for every
|
|
167
|
+
# verb an entity declares (it never references itself; see
|
|
168
168
|
# `Command#acts_on`'s own comment), so reading it directly here would
|
|
169
169
|
# tell an SME that `LedgerEntry.Amend` brings a new ledger entry into
|
|
170
170
|
# being, which is exactly backwards.
|
|
@@ -208,7 +208,7 @@ module Hecks
|
|
|
208
208
|
"It records `#{command.emits.join('`, `')}` as a fact."
|
|
209
209
|
end
|
|
210
210
|
|
|
211
|
-
#
|
|
211
|
+
# Every required condition, stated as something that must be true —
|
|
212
212
|
# the same three sources `DocsProjector#refusals_of` reads (the
|
|
213
213
|
# lifecycle edge, a reference's existence, and the command's own
|
|
214
214
|
# `given`s), but kept positive rather than phrased as a refusal
|
|
@@ -241,7 +241,7 @@ module Hecks
|
|
|
241
241
|
lines = queries.map do |query|
|
|
242
242
|
shape = query.to_h
|
|
243
243
|
takes = Array(shape[:attributes]).map { |a| Forms::Humanize.label(a[:name].to_s).downcase }
|
|
244
|
-
# `w[:value]`
|
|
244
|
+
# `w[:value]` already wears its own quotes or colon — it is a
|
|
245
245
|
# `Literal.render`ed string (see lib/hecks/literal.rb), not a raw
|
|
246
246
|
# Ruby value, so wrapping it in `.inspect` here would quote an
|
|
247
247
|
# already-quoted string a second time.
|
|
@@ -284,18 +284,11 @@ module Hecks
|
|
|
284
284
|
|
|
285
285
|
# ── small sentence carpentry ──────────────────────────────────────
|
|
286
286
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
when 1 then items[0].to_s
|
|
291
|
-
when 2 then "#{items[0]} #{conj} #{items[1]}"
|
|
292
|
-
else "#{items[0..-2].join(', ')}, #{conj} #{items[-1]}"
|
|
293
|
-
end
|
|
294
|
-
end
|
|
287
|
+
# Both now live in `Naming` (a second projection, the glossary,
|
|
288
|
+
# needed them); kept here as names so this file reads as it did.
|
|
289
|
+
def to_sentence_list(items, conj: "and") = Naming.to_sentence_list(items, conj: conj)
|
|
295
290
|
|
|
296
|
-
def a_or_an(word)
|
|
297
|
-
%w[a e i o u].include?(word.to_s[0].to_s.downcase) ? "an" : "a"
|
|
298
|
-
end
|
|
291
|
+
def a_or_an(word) = Naming.a_or_an(word)
|
|
299
292
|
end
|
|
300
293
|
end
|
|
301
294
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Projector
|
|
3
|
-
#
|
|
3
|
+
# What makes a module a projection target. `Projector.register` has
|
|
4
4
|
# always accepted anything answering `call(bluebook:, options:)` —
|
|
5
5
|
# this only removes the second step, so a target declares its own key
|
|
6
6
|
# beside its own implementation instead of being registered from
|
|
@@ -15,20 +15,20 @@ module Hecks
|
|
|
15
15
|
# def call(bluebook:, options: {}) = { ... }
|
|
16
16
|
# end
|
|
17
17
|
#
|
|
18
|
-
#
|
|
18
|
+
# Named `Target`, not `Projection`, on purpose. "Projection" already
|
|
19
19
|
# means two other things in this codebase: `Ports::Projection` is
|
|
20
20
|
# read-model catch-up (events folded into view state), and `bin/project`
|
|
21
21
|
# forces that catch-up by hand. Neither has anything to do with
|
|
22
22
|
# "canonical IR in, external artifact out". A third meaning under the
|
|
23
23
|
# same word would make the two impossible to grep apart — and from a
|
|
24
|
-
# domain's point of view `project(X)` really does read as "project
|
|
24
|
+
# domain's point of view `project(X)` really does read as "project to
|
|
25
25
|
# a target", so the narrower word is also the more accurate one.
|
|
26
26
|
module Target
|
|
27
27
|
# Registering at declaration time means `require`ing a target is
|
|
28
28
|
# the whole of installing it — there is no separate manifest that
|
|
29
29
|
# can silently disagree about which targets exist.
|
|
30
30
|
#
|
|
31
|
-
# `requires:`
|
|
31
|
+
# `requires:` names a capability, not a shape word.
|
|
32
32
|
#
|
|
33
33
|
# This began as `from: :chapter` / `from: :any` — two hand-kept
|
|
34
34
|
# symbols, admitted by duck-typing on `.aggregates`, which is a
|
|
@@ -44,26 +44,26 @@ module Hecks
|
|
|
44
44
|
# It also composes: a projection needing two capabilities names
|
|
45
45
|
# both, instead of a third symbol being invented for the pair.
|
|
46
46
|
#
|
|
47
|
-
#
|
|
48
|
-
# construct emits its own IR, so handing a projector an
|
|
47
|
+
# The fail-quiet this closes, unchanged in substance: every
|
|
48
|
+
# construct emits its own IR, so handing a projector an aggregate
|
|
49
49
|
# instead of a chapter is the natural thing to try. `bluebook:` was
|
|
50
|
-
# only ever a
|
|
50
|
+
# only ever a parameter name, never a contract. `:oidc` failed
|
|
51
51
|
# loudly (no `aggregates` method), but `:shape` returned
|
|
52
52
|
# `{"name" => "Order", "aggregates" => []}` — well-formed,
|
|
53
53
|
# confident, and wrong.
|
|
54
|
-
# `declares:`
|
|
54
|
+
# `declares:` names an aggregate the chapter must have.
|
|
55
55
|
#
|
|
56
|
-
# A capability says what a construct can
|
|
57
|
-
#
|
|
56
|
+
# A capability says what a construct can do; this says what it must
|
|
57
|
+
# carry. `:vocabulary` needs a chapter declaring a Vocabulary
|
|
58
58
|
# aggregate, `:parser_table` one declaring Syntax — and both used
|
|
59
59
|
# to state that as a `raise` in their own body, which is a
|
|
60
60
|
# requirement written as behaviour instead of declared. Stated
|
|
61
61
|
# here, the registry refuses before the projection runs and the
|
|
62
62
|
# projection stops carrying a guard about its own admission.
|
|
63
|
-
# `emits:`
|
|
63
|
+
# `emits:` says what kind of artifact comes back.
|
|
64
64
|
#
|
|
65
65
|
# `:artifact` (the default) is one thing — a Hash, or a String.
|
|
66
|
-
# `:files` is a
|
|
66
|
+
# `:files` is a tree: a Hash of relative path => contents, which is
|
|
67
67
|
# what a reference-page or codegen projection produces.
|
|
68
68
|
#
|
|
69
69
|
# Declared rather than sniffed, deliberately. Inferring a tree from
|
|
@@ -81,7 +81,7 @@ module Hecks
|
|
|
81
81
|
|
|
82
82
|
def projection_key = @projection_key
|
|
83
83
|
|
|
84
|
-
# Empty means "a chapter" — resolved
|
|
84
|
+
# Empty means "a chapter" — resolved here rather than as a default
|
|
85
85
|
# argument, because Behaviour::Chapter is not loaded yet when this
|
|
86
86
|
# file is.
|
|
87
87
|
def projection_declares = @projection_declares || []
|