hecks 1.3.0 → 1.5.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 +72 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +83 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
- data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
- data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +106 -9
- data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
- data/lib/hecks/adapters/driven/lambda.rb +82 -35
- data/lib/hecks/adapters/driven/local_storage.rb +83 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -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/prism.rb +50 -11
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- 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/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +6 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
- data/lib/hecks/behaviors/dsl.rb +60 -2
- data/lib/hecks/behaviors/expectations.rb +190 -29
- data/lib/hecks/behaviors/ir.rb +12 -1
- data/lib/hecks/behaviors/rspec.rb +9 -1
- data/lib/hecks/behaviors/runner.rb +21 -2
- data/lib/hecks/behaviors.rb +9 -1
- data/lib/hecks/bluebook/aggregate.rb +43 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
- data/lib/hecks/bluebook/assembly/build.rb +26 -5
- data/lib/hecks/bluebook/assembly/contract.rb +98 -23
- data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
- data/lib/hecks/bluebook/assembly/marks.rb +159 -30
- data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
- data/lib/hecks/bluebook/assembly.rb +32 -14
- data/lib/hecks/bluebook/attribute.rb +26 -12
- data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
- data/lib/hecks/bluebook/behaviour/command.rb +55 -25
- data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
- data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
- data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
- data/lib/hecks/bluebook/behaviour/query.rb +6 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +51 -9
- data/lib/hecks/bluebook/command.rb +62 -17
- data/lib/hecks/bluebook/domain_port.rb +34 -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 +282 -123
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
- 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 +191 -61
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
- 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 +111 -47
- data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
- data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
- data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
- 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 +40 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
- data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
- data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
- data/lib/hecks/bluebook/expression/resolver.rb +369 -128
- data/lib/hecks/bluebook/hexagon.rb +35 -1
- data/lib/hecks/bluebook/lifecycle.rb +12 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
- data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
- data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
- data/lib/hecks/bluebook/meta_validator.rb +235 -139
- data/lib/hecks/bluebook/model_check.rb +434 -104
- data/lib/hecks/bluebook/pattern_subset.rb +32 -10
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +27 -14
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +50 -6
- data/lib/hecks/bluebook/query.rb +31 -4
- data/lib/hecks/bluebook/read_model.rb +35 -15
- data/lib/hecks/bluebook/reference.rb +26 -13
- data/lib/hecks/bluebook/smoke_test.rb +46 -23
- data/lib/hecks/bluebook/synthesizer.rb +46 -12
- data/lib/hecks/bluebook/translation.rb +34 -5
- data/lib/hecks/bluebook/value_object.rb +29 -11
- data/lib/hecks/bluebook.rb +5 -6
- data/lib/hecks/codemod.rb +138 -50
- data/lib/hecks/construct.rb +21 -7
- data/lib/hecks/corpus.rb +438 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/doc/reference.rb +200 -31
- data/lib/hecks/embryonaut_bluebook.rb +38 -15
- 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 +155 -35
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +50 -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 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +14 -2
- data/lib/hecks/forms/query_form_renderer.rb +65 -2
- data/lib/hecks/forms/record_renderer.rb +60 -2
- data/lib/hecks/forms/record_table.rb +28 -1
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +13 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +59 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +79 -25
- data/lib/hecks/freezer.rb +27 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
- data/lib/hecks/fuzzing/combination_miner.rb +178 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
- data/lib/hecks/fuzzing/differential.rb +192 -0
- data/lib/hecks/fuzzing/domain_generator.rb +871 -0
- data/lib/hecks/fuzzing/era_boundary.rb +68 -19
- data/lib/hecks/fuzzing/form_census.rb +121 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
- data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
- data/lib/hecks/fuzzing/properties/guards.rb +86 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
- data/lib/hecks/fuzzing/properties/querying.rb +82 -28
- data/lib/hecks/fuzzing/properties.rb +84 -51
- data/lib/hecks/fuzzing/qa_settings.rb +164 -0
- data/lib/hecks/fuzzing/replay.rb +241 -133
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
- data/lib/hecks/fuzzing/self_consistency.rb +315 -137
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
- data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
- data/lib/hecks/fuzzing/shrinker.rb +309 -0
- data/lib/hecks/fuzzing/structural_skips.rb +37 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
- data/lib/hecks/fuzzing/value_generator.rb +110 -22
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +188 -12
- data/lib/hecks/grammar.rb +53 -7
- data/lib/hecks/ir.rb +51 -20
- 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 +348 -13
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +41 -9
- data/lib/hecks/naming.rb +112 -31
- data/lib/hecks/ports/access_control.rb +53 -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 +50 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +23 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +17 -1
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +58 -0
- 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 +118 -66
- 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 +34 -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 +33 -9
- 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 +95 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
- 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 +48 -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 +58 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -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 +23 -3
- 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 +41 -8
- data/lib/hecks/ports/query/ordering.rb +21 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +147 -0
- data/lib/hecks/projections/diagrams.rb +315 -79
- data/lib/hecks/projections/glossary/html.rb +91 -3
- data/lib/hecks/projections/glossary/markdown.rb +50 -3
- data/lib/hecks/projections/glossary/mermaid.rb +49 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +110 -5
- data/lib/hecks/projections/glossary.rb +173 -31
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +62 -17
- data/lib/hecks/projections/model.rb +75 -21
- data/lib/hecks/projections/oidc.rb +45 -8
- data/lib/hecks/projections/parser_table.rb +53 -12
- data/lib/hecks/projections/reference.rb +15 -3
- data/lib/hecks/projections/rust_vocabulary.rb +646 -0
- data/lib/hecks/projections/shape.rb +10 -2
- data/lib/hecks/projections/statements.rb +69 -22
- data/lib/hecks/projections/vocabulary.rb +26 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +245 -37
- data/lib/hecks/projector/docs_projector.rb +154 -28
- data/lib/hecks/projector/exporter.rb +104 -29
- data/lib/hecks/projector/ir_projector.rb +7 -1
- data/lib/hecks/projector/narrate_projector.rb +144 -23
- data/lib/hecks/projector/target.rb +42 -18
- data/lib/hecks/projector.rb +86 -18
- data/lib/hecks/query_ir.rb +94 -47
- data/lib/hecks/query_specification/common/comparators.rb +19 -3
- data/lib/hecks/query_specification/common/comparison.rb +109 -27
- data/lib/hecks/query_specification/common/dsl.rb +65 -9
- data/lib/hecks/query_specification/common/null_policy.rb +61 -15
- 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 +9 -3
- data/lib/hecks/router/namespace_installer.rb +16 -3
- data/lib/hecks/router.rb +56 -1
- data/lib/hecks/runtime/aggregate_lock.rb +20 -11
- data/lib/hecks/runtime/boot_gates.rb +21 -3
- data/lib/hecks/runtime/caller.rb +40 -8
- data/lib/hecks/runtime/capability_graph.rb +13 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
- data/lib/hecks/runtime/command_interpreter.rb +195 -113
- data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
- data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
- 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 +145 -55
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +56 -11
- data/lib/hecks/runtime/dispatcher.rb +263 -92
- data/lib/hecks/runtime/entity_element.rb +304 -67
- data/lib/hecks/runtime/entity_interpreter.rb +149 -88
- data/lib/hecks/runtime/errors.rb +37 -23
- data/lib/hecks/runtime/event.rb +15 -10
- data/lib/hecks/runtime/identity.rb +91 -23
- data/lib/hecks/runtime/instance.rb +103 -18
- data/lib/hecks/runtime/interpreting.rb +24 -16
- data/lib/hecks/runtime/invocation.rb +358 -0
- data/lib/hecks/runtime/loader.rb +105 -19
- data/lib/hecks/runtime/outbox.rb +164 -26
- data/lib/hecks/runtime/policy_interpreter.rb +73 -60
- data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
- data/lib/hecks/runtime/query_interpreter.rb +93 -74
- data/lib/hecks/runtime/reaction_invocation.rb +73 -28
- data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
- data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
- data/lib/hecks/runtime/reference_hop.rb +48 -6
- data/lib/hecks/runtime/refusal_wording.rb +142 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
- data/lib/hecks/runtime/registry/verification.rb +153 -28
- data/lib/hecks/runtime/registry.rb +202 -32
- data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
- data/lib/hecks/runtime/routing.rb +36 -154
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
- data/lib/hecks/runtime/saga_interpreter.rb +90 -76
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +33 -13
- data/lib/hecks/runtime/tenant_scope.rb +23 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +555 -142
- data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
- data/lib/hecks/runtime/value.rb +71 -21
- data/lib/hecks/runtime.rb +39 -7
- data/lib/hecks/storehouse.rb +368 -72
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +91 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
- metadata +28 -2
|
@@ -1,35 +1,42 @@
|
|
|
1
|
+
require "digest"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
1
4
|
module Hecks
|
|
2
5
|
module Bluebook
|
|
3
6
|
module MetaValidator
|
|
4
|
-
# S14, ADR 0026 —
|
|
5
|
-
#
|
|
7
|
+
# S14, ADR 0026 — dispatches the language's own grammar table into
|
|
8
|
+
# itself, once, so `Keyword`/`Argument`'s own `status` genuinely is a
|
|
6
9
|
# lifecycle — checked at real dispatch time (the same admission/
|
|
7
10
|
# coercion/lifecycle-guard door every other command goes through),
|
|
8
11
|
# not merely declared and never exercised.
|
|
9
12
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
13
|
+
# ## The source stays static
|
|
14
|
+
#
|
|
15
|
+
# Aggregate-local `KeywordSeed`/`ArgumentSeed` value objects (still
|
|
16
|
+
# hand-written `member` rows — now beside the concepts they spell)
|
|
17
|
+
# are what gets written ; this is what turns them into what gets
|
|
18
|
+
# read. `Judge`/`Reconstruction` already draw exactly this line
|
|
19
|
+
# everywhere else in the meta-domain (a chapter's own declarations
|
|
20
|
+
# versus what gets dispatched from them) — this runs the same
|
|
21
|
+
# distinction one level further out, for the language's own grammar
|
|
22
|
+
# table.
|
|
23
|
+
#
|
|
24
|
+
# ## A dedicated runtime
|
|
18
25
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
26
|
+
# Not `MetaValidator.fresh_runtime`. That one is reserved for
|
|
27
|
+
# `Judge`'s own bootstrap (dispatching a chapter's declarations into
|
|
28
|
+
# the meta-domain's grammar) — a different act from this one
|
|
29
|
+
# (dispatching the meta-domain's own grammar table data into a live
|
|
30
|
+
# "Bluebook" domain instance). Sharing the runtime would let one
|
|
31
|
+
# boot's own repository state leak into the other's.
|
|
25
32
|
#
|
|
26
|
-
# Usage
|
|
33
|
+
# ## Usage
|
|
27
34
|
#
|
|
28
35
|
# MetaValidator.syntax_table # => { keywords: [...], arguments: [...] }
|
|
29
36
|
#
|
|
30
37
|
# each row a plain Hash, string values, `status` included — the
|
|
31
38
|
# exact shape `ParserTable`/`syntax_conformance_spec` already read
|
|
32
|
-
# off the
|
|
39
|
+
# off the old closed-set members, so neither consumer had to change
|
|
33
40
|
# what it does with a row, only where the row comes from.
|
|
34
41
|
module SyntaxBoot
|
|
35
42
|
module_function
|
|
@@ -40,21 +47,21 @@ module Hecks
|
|
|
40
47
|
# conformance_spec, bin/reference) should each pay for on every
|
|
41
48
|
# call.
|
|
42
49
|
#
|
|
43
|
-
#
|
|
50
|
+
# Keyed by the grammar registry's own chapter set, not by a
|
|
44
51
|
# "ready" flag. `boot` reads exactly two things: the registry's
|
|
45
52
|
# "Bluebook" chapter (the core seed rows) and every chapter in the
|
|
46
53
|
# registry that `attaches_to` a core context (Paging's rows). So
|
|
47
|
-
# the table is a pure function of which chapter
|
|
54
|
+
# the table is a pure function of which chapter objects the
|
|
48
55
|
# registry holds — and that is the cache key: the same chapters,
|
|
49
56
|
# by identity, mean the same table; a chapter replaced (the
|
|
50
57
|
# fixpoint judge swapping a raw chapter for its assembled self)
|
|
51
58
|
# or added (`load_attached_grammar_into`) means a fresh boot.
|
|
52
59
|
#
|
|
53
|
-
#
|
|
60
|
+
# Why not the earlier `grammar_registry_ready?` guard. That guard
|
|
54
61
|
# was right about the hazard — a snapshot taken mid-build, before
|
|
55
62
|
# Paging attached, would be missing limit/offset/cursor/nulls
|
|
56
63
|
# forever — and wrong about the cost of its cure. It refused to
|
|
57
|
-
# cache
|
|
64
|
+
# cache anything until the whole grammar registry had finished
|
|
58
65
|
# building, calling that window "narrow" and recomputing in it
|
|
59
66
|
# "cheap". Measured (hecks_ai_training, a six-entity chess domain,
|
|
60
67
|
# 2026-08-22): every `word_gate_dispatch` landing in that window
|
|
@@ -69,20 +76,37 @@ module Hecks
|
|
|
69
76
|
# chapter objects, so it invalidates this the same way it always
|
|
70
77
|
# invalidated `grammar_registry_ready?`.
|
|
71
78
|
#
|
|
72
|
-
# `equal?`,
|
|
79
|
+
# `equal?`, not `==`, on the chapters — identity is the fact being
|
|
73
80
|
# tracked. Holding the chapter objects themselves (not their ids)
|
|
74
81
|
# in the key also means a collected chapter can never hand its id
|
|
75
82
|
# to a newcomer behind this cache's back.
|
|
83
|
+
# @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:keywords`
|
|
84
|
+
# and `:arguments`, each an array of plain, string-valued row
|
|
85
|
+
# hashes (`status` included)
|
|
76
86
|
def call
|
|
77
87
|
chapters = MetaValidator.grammar_registry.bluebooks.to_a
|
|
78
88
|
return @call if @call && same_chapters?(@call_chapters, chapters)
|
|
79
89
|
|
|
80
|
-
result =
|
|
90
|
+
result = read_disk_cache(chapters) || begin
|
|
91
|
+
fresh = boot
|
|
92
|
+
write_disk_cache(chapters, fresh)
|
|
93
|
+
fresh
|
|
94
|
+
end
|
|
95
|
+
|
|
81
96
|
@call = result
|
|
82
97
|
@call_chapters = chapters
|
|
83
98
|
result
|
|
84
99
|
end
|
|
85
100
|
|
|
101
|
+
# Compares the in-process cache key against the registry's current
|
|
102
|
+
# chapters.
|
|
103
|
+
#
|
|
104
|
+
# @param cached [Array<Array(String, Object)>] the `[name, chapter]`
|
|
105
|
+
# pairs `call` last cached against
|
|
106
|
+
# @param current [Array<Array(String, Object)>] the registry's
|
|
107
|
+
# current `[name, chapter]` pairs
|
|
108
|
+
# @return [Boolean] whether `cached` and `current` name the same
|
|
109
|
+
# chapters, by identity, in the same order
|
|
86
110
|
def same_chapters?(cached, current)
|
|
87
111
|
cached.size == current.size &&
|
|
88
112
|
cached.zip(current).all? do |(cached_name, cached_chapter), (name, chapter)|
|
|
@@ -90,14 +114,155 @@ module Hecks
|
|
|
90
114
|
end
|
|
91
115
|
end
|
|
92
116
|
|
|
117
|
+
# Cross-process persistence for the same ~1s-per-build work `call`
|
|
118
|
+
# above already memoizes in-process — this file's own chapter-
|
|
119
|
+
# identity keying only ever helps callers sharing one process (the
|
|
120
|
+
# in-memory fixpoint window, or `bin/reference`'s single run); every
|
|
121
|
+
# fresh process (a `qa_sweep --all` child, a `parallel_rspec`
|
|
122
|
+
# worker, `bin/qa_sweep` itself) pays both real builds again from
|
|
123
|
+
# scratch even when the grammar source hasn't changed at all since
|
|
124
|
+
# the last process that built it.
|
|
125
|
+
#
|
|
126
|
+
# Not a fix for the two-builds-per-process shape — investigated and
|
|
127
|
+
# confirmed genuine, not waste (see this module's own header
|
|
128
|
+
# history: the previous "wait for the whole registry" design cost
|
|
129
|
+
# 42 rebuilds/32s per process precisely because real callers need a
|
|
130
|
+
# correct table mid-bootstrap, before Paging attaches). Both builds
|
|
131
|
+
# still happen once each per process; this only makes each of them
|
|
132
|
+
# a cheap disk read instead of a real ~1s dispatch, once some
|
|
133
|
+
# earlier process has already paid for that exact chapter-set +
|
|
134
|
+
# grammar-content combination.
|
|
135
|
+
#
|
|
136
|
+
# Keyed the same way `same_chapters?` is, translated across process
|
|
137
|
+
# boundaries — chapter *object identity* (what the in-memory cache
|
|
138
|
+
# keys on) means nothing to a different process; chapter *names*,
|
|
139
|
+
# in the same order, do. Combined with a content hash of every
|
|
140
|
+
# grammar file `boot` can read from (`seed_chapters` walks every
|
|
141
|
+
# bluebook the registry holds, so this must cover the core chapters
|
|
142
|
+
# and every attached one, not just "Bluebook" + "Paging") — a
|
|
143
|
+
# source edit anywhere in that set correctly misses the old cache
|
|
144
|
+
# entry rather than silently serving a stale table.
|
|
145
|
+
#
|
|
146
|
+
# Fails toward a real boot, never toward a wrong table — same
|
|
147
|
+
# loud-not-silent discipline this codebase already holds CI to
|
|
148
|
+
# (`postgres_io_relevant_changed`'s own header). A missing file, a
|
|
149
|
+
# corrupt Marshal blob, a permission error, an unwritable `tmp/` —
|
|
150
|
+
# every one of these degrades to "no disk cache today", never to a
|
|
151
|
+
# crash or a served-but-wrong table. `tmp/` is already gitignored
|
|
152
|
+
# for exactly this kind of local, disposable-but-useful-while-it-
|
|
153
|
+
# lasts artifact (`Storehouse::LOG_ROOT`'s own header names the
|
|
154
|
+
# same convention).
|
|
155
|
+
#
|
|
156
|
+
# **Atomic write, not a lock** — `qa_sweep --all` spawns up to 4
|
|
157
|
+
# children at once, any of which could reach a cold cache
|
|
158
|
+
# simultaneously and each compute the identical real boot result
|
|
159
|
+
# for the identical key. Writing to a PID-suffixed temp file and
|
|
160
|
+
# `File.rename`ing it into place (a single atomic syscall on the
|
|
161
|
+
# same filesystem) means a concurrent second writer's rename just
|
|
162
|
+
# overwrites the first with byte-identical content — never a torn
|
|
163
|
+
# or partially-written file a concurrent reader could observe.
|
|
164
|
+
#
|
|
165
|
+
# `HECKS_SYNTAX_BOOT_CACHE=off` — an escape hatch needing no code
|
|
166
|
+
# change, the same shape `Storehouse::BOOT_ROOT`'s own
|
|
167
|
+
# `HECKS_STOREHOUSE_ROOT` override uses, for the day this needs to
|
|
168
|
+
# be ruled out while debugging something else entirely.
|
|
169
|
+
CACHE_DIR = File.expand_path("../../../../tmp/hecks_syntax_boot_cache", __dir__).freeze
|
|
170
|
+
|
|
171
|
+
# Reports whether the cross-process disk cache is turned on.
|
|
172
|
+
#
|
|
173
|
+
# @return [Boolean] whether the cross-process disk cache is turned
|
|
174
|
+
# on (`HECKS_SYNTAX_BOOT_CACHE=off` turns it off)
|
|
175
|
+
def disk_cache_enabled? = ENV["HECKS_SYNTAX_BOOT_CACHE"] != "off"
|
|
176
|
+
|
|
177
|
+
# Reads an already-written cache entry for `chapters`, if any.
|
|
178
|
+
#
|
|
179
|
+
# @param chapters [Array<Array(String, Object)>] the `[name,
|
|
180
|
+
# chapter]` pairs to key the cache entry on
|
|
181
|
+
# @return [Hash{Symbol => Array<Hash{Symbol => String}>}, nil] the
|
|
182
|
+
# cached syntax table, or `nil` if the cache is disabled, no entry
|
|
183
|
+
# exists yet, or the entry could not be read
|
|
184
|
+
def read_disk_cache(chapters)
|
|
185
|
+
return nil unless disk_cache_enabled?
|
|
186
|
+
|
|
187
|
+
path = disk_cache_path(chapters)
|
|
188
|
+
return nil unless File.exist?(path)
|
|
189
|
+
|
|
190
|
+
Marshal.load(File.binread(path)) # rubocop:disable Security/MarshalLoad -- own process-local cache, never external input
|
|
191
|
+
rescue StandardError
|
|
192
|
+
nil
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Writes `result` to the disk cache for `chapters`, atomically.
|
|
196
|
+
#
|
|
197
|
+
# @param chapters [Array<Array(String, Object)>] the `[name,
|
|
198
|
+
# chapter]` pairs to key the cache entry on
|
|
199
|
+
# @param result [Hash{Symbol => Array<Hash{Symbol => String}>}] the
|
|
200
|
+
# syntax table to persist
|
|
201
|
+
# @return [void]
|
|
202
|
+
def write_disk_cache(chapters, result)
|
|
203
|
+
return unless disk_cache_enabled?
|
|
204
|
+
|
|
205
|
+
path = disk_cache_path(chapters)
|
|
206
|
+
FileUtils.mkdir_p(CACHE_DIR)
|
|
207
|
+
tmp_path = "#{path}.#{Process.pid}.tmp"
|
|
208
|
+
File.binwrite(tmp_path, Marshal.dump(result))
|
|
209
|
+
File.rename(tmp_path, path)
|
|
210
|
+
rescue StandardError
|
|
211
|
+
nil
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Builds the disk cache file path for `chapters`.
|
|
215
|
+
#
|
|
216
|
+
# @param chapters [Array<Array(String, Object)>] the `[name,
|
|
217
|
+
# chapter]` pairs to key the cache entry on
|
|
218
|
+
# @return [String] the disk cache file path for `chapters`
|
|
219
|
+
def disk_cache_path(chapters)
|
|
220
|
+
File.join(CACHE_DIR, "#{disk_cache_key(chapters)}.marshal")
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Builds the cache key for `chapters`.
|
|
224
|
+
#
|
|
225
|
+
# @param chapters [Array<Array(String, Object)>] the `[name,
|
|
226
|
+
# chapter]` pairs to key the cache entry on
|
|
227
|
+
# @return [String] a SHA-256 digest of the chapter names and the
|
|
228
|
+
# current grammar source
|
|
229
|
+
def disk_cache_key(chapters)
|
|
230
|
+
names = chapters.map { |name, _chapter| name }
|
|
231
|
+
Digest::SHA256.hexdigest("#{names.join(',')}:#{grammar_content_digest}")
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Every file `boot` can possibly read from, via `seed_chapters`
|
|
235
|
+
# walking every bluebook the registry holds — not just "Bluebook"
|
|
236
|
+
# and "Paging" (the two chapters this module's own comments name
|
|
237
|
+
# most often), because World/Hecksagon/any future attached chapter
|
|
238
|
+
# are equally eligible to carry their own `KeywordSeed`/
|
|
239
|
+
# `ArgumentSeed` value object. Coarser than strictly necessary (any
|
|
240
|
+
# one file changing invalidates every cached key, not just the
|
|
241
|
+
# chapter it belongs to) — deliberately, since under-covering this
|
|
242
|
+
# set is a correctness bug (a stale table survives a real grammar
|
|
243
|
+
# edit) and over-covering it is only ever a wasted cache miss.
|
|
244
|
+
# @return [String] a SHA-256 digest of every grammar file's own
|
|
245
|
+
# current content, sorted for a stable digest
|
|
246
|
+
def grammar_content_digest
|
|
247
|
+
files = (MetaValidator::GRAMMAR_FILES + MetaValidator::WORLD_GRAMMAR + MetaValidator::HECKSAGON_GRAMMAR +
|
|
248
|
+
Dir.glob(File.join(MetaValidator::ATTACHED_GRAMMAR_DIR, "*.bluebook"))).sort
|
|
249
|
+
Digest::SHA256.hexdigest(files.map { |file| File.read(file) }.join("\0"))
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Dispatches every seed row into a fresh "Bluebook" instance and
|
|
253
|
+
# reads the result back.
|
|
254
|
+
#
|
|
255
|
+
# @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:keywords`
|
|
256
|
+
# and `:arguments`, each an array of plain, string-valued row
|
|
257
|
+
# hashes (`status` included)
|
|
93
258
|
def boot
|
|
94
259
|
bluebook = MetaValidator.grammar_registry.bluebook("Bluebook")
|
|
95
260
|
# `MetaValidator.fresh_runtime`, not a brand-new `Runtime::
|
|
96
261
|
# Registry` — the grammar registry's own copy already has
|
|
97
262
|
# "Bluebook" registered (`grammar_registry`'s own boot already
|
|
98
|
-
# ran `registry.add_bluebook`)
|
|
263
|
+
# ran `registry.add_bluebook`) and its adapter ports already
|
|
99
264
|
# loaded ; a standalone registry would need both wired by hand.
|
|
100
|
-
# `fresh_runtime` resets `@repositories` on the
|
|
265
|
+
# `fresh_runtime` resets `@repositories` on the same registry —
|
|
101
266
|
# the identical isolation `Judge.new` already relies on for
|
|
102
267
|
# every real domain it judges, proven safe by every dispatch
|
|
103
268
|
# this session has ever made.
|
|
@@ -110,28 +275,42 @@ module Hecks
|
|
|
110
275
|
read_back(runtime, bluebook)
|
|
111
276
|
end
|
|
112
277
|
|
|
278
|
+
# Wraps `text` as a single-field value-object payload, for a
|
|
279
|
+
# dispatch's own `with:`.
|
|
280
|
+
#
|
|
113
281
|
# An Integer stays an Integer — `position` is `Position`-typed
|
|
114
282
|
# (`attribute :value, Integer`), so stringifying it fails the type
|
|
115
283
|
# gate rather than feeding it, the same reading `Judge#v` gives.
|
|
284
|
+
#
|
|
285
|
+
# @param text [String, Integer] the scalar to wrap
|
|
286
|
+
# @return [Hash{Symbol => String, Integer}] `{value: text}`,
|
|
287
|
+
# stringified unless `text` is already an Integer
|
|
116
288
|
def v(text)
|
|
117
289
|
return { value: text } if text.is_a?(Integer)
|
|
118
290
|
|
|
119
291
|
{ value: text.to_s }
|
|
120
292
|
end
|
|
121
293
|
|
|
122
|
-
#
|
|
294
|
+
# Wraps `text` as an `optional: true` command argument's own
|
|
295
|
+
# payload, or omits it.
|
|
296
|
+
#
|
|
297
|
+
# Absent stays absent. A seed row's own optional columns ("was",
|
|
123
298
|
# "at", "named", ...) are empty strings, not nil — `Literal`/CSV-
|
|
124
299
|
# shaped grammar data has no `nil` to write — so this is the one
|
|
125
300
|
# place that decides "" means "not given" for the purpose of an
|
|
126
301
|
# `optional: true` command argument, the same reading `Judge#v`
|
|
127
302
|
# makes for the meta-domain's own dispatches.
|
|
303
|
+
#
|
|
304
|
+
# @param text [String, nil] the seed row's own column value
|
|
305
|
+
# @return [Hash{Symbol => String}, nil] `v(text)`, or `nil` when
|
|
306
|
+
# `text` is `nil` or empty
|
|
128
307
|
def optional(text)
|
|
129
308
|
return nil if text.nil? || text.to_s.empty?
|
|
130
309
|
|
|
131
310
|
v(text)
|
|
132
311
|
end
|
|
133
312
|
|
|
134
|
-
# `Syntax.Declare`'s own `reference_to Bluebook` names the
|
|
313
|
+
# `Syntax.Declare`'s own `reference_to Bluebook` names the chapter
|
|
135
314
|
# it belongs to — the same fact every other top-level aggregate's
|
|
136
315
|
# own creating command carries (`ProcessManager.Declare`,
|
|
137
316
|
# `Policy.Declare`, ...). This fresh runtime holds no chapter
|
|
@@ -139,25 +318,39 @@ module Hecks
|
|
|
139
318
|
# declared first, named after the real chapter, purely to satisfy
|
|
140
319
|
# the reference — its own vision/classification are never read
|
|
141
320
|
# by anything this boot does.
|
|
321
|
+
#
|
|
322
|
+
# @param runtime [Runtime::Dispatcher] the fresh runtime to
|
|
323
|
+
# dispatch into
|
|
324
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
325
|
+
# "Bluebook" chapter, read for its `hecks_name` and `"Syntax"`
|
|
326
|
+
# aggregate
|
|
327
|
+
# @return [void]
|
|
142
328
|
def declare_syntax(runtime, bluebook)
|
|
143
329
|
syntax = bluebook.aggregate("Syntax")
|
|
144
|
-
runtime.dispatch("Bluebook::Bluebook.Declare",
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
330
|
+
runtime.dispatch("Bluebook::Bluebook.Declare",
|
|
331
|
+
with: { name: v(bluebook.hecks_name),
|
|
332
|
+
vision: v("the language's own grammar table, " \
|
|
333
|
+
"dispatched into itself"),
|
|
334
|
+
classification: v("core") })
|
|
335
|
+
runtime.dispatch("Bluebook::Syntax.Declare",
|
|
336
|
+
with: { bluebook: bluebook.hecks_name, name: v(syntax.hecks_name) })
|
|
149
337
|
end
|
|
150
338
|
|
|
151
|
-
# `to: "Syntax"`
|
|
152
|
-
#
|
|
153
|
-
# of it.
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
339
|
+
# `to: "Syntax"` names the record already opened by `declare_syntax`
|
|
340
|
+
# above — an append onto an existing aggregate, not a second creation
|
|
341
|
+
# of it. Smuggling `name: v("Syntax")` into the payload instead — the
|
|
342
|
+
# pre-routing convention `Judge#appends` (the same append shape, for
|
|
343
|
+
# `ValueObject.Member`/`ProcessManager.Handler`) already left behind
|
|
344
|
+
# for `to:`/`with:` — would carry the receiver in the payload,
|
|
345
|
+
# making `Syntax.Keyword`'s own `command.creates?` (true: it
|
|
346
|
+
# declares no `reference_to`) look like a fresh identity to mint,
|
|
347
|
+
# colliding with the very "Syntax" row `declare_syntax` just opened.
|
|
348
|
+
#
|
|
349
|
+
# @param runtime [Runtime::Dispatcher] the fresh runtime to
|
|
350
|
+
# dispatch into
|
|
351
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
352
|
+
# "Bluebook" chapter, read for its `KeywordSeed` rows
|
|
353
|
+
# @return [void]
|
|
161
354
|
def admit_keywords(runtime, bluebook)
|
|
162
355
|
all_rows(bluebook, "KeywordSeed").each_with_index do |row, index|
|
|
163
356
|
runtime.dispatch("Bluebook::Syntax.Keyword", to: "Syntax",
|
|
@@ -175,6 +368,14 @@ module Hecks
|
|
|
175
368
|
end
|
|
176
369
|
end
|
|
177
370
|
|
|
371
|
+
# Admits every `ArgumentSeed` row as a `Syntax.Argument`, the same
|
|
372
|
+
# `to: "Syntax"` append shape `admit_keywords` uses one type over.
|
|
373
|
+
#
|
|
374
|
+
# @param runtime [Runtime::Dispatcher] the fresh runtime to
|
|
375
|
+
# dispatch into
|
|
376
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
377
|
+
# "Bluebook" chapter, read for its `ArgumentSeed` rows
|
|
378
|
+
# @return [void]
|
|
178
379
|
def admit_arguments(runtime, bluebook)
|
|
179
380
|
all_rows(bluebook, "ArgumentSeed").each_with_index do |row, index|
|
|
180
381
|
runtime.dispatch("Bluebook::Syntax.Argument", to: "Syntax",
|
|
@@ -194,13 +395,21 @@ module Hecks
|
|
|
194
395
|
end
|
|
195
396
|
end
|
|
196
397
|
|
|
197
|
-
#
|
|
398
|
+
# Every aggregate-local table in every loaded language chapter.
|
|
198
399
|
# The table's presence is the registration: no chapter, aggregate,
|
|
199
400
|
# filename, or context catalog is maintained here. This finds the core
|
|
200
401
|
# Bluebook concepts, the sibling artifact languages (World, Hecksagon,
|
|
201
402
|
# Port, Adapter, Translation), and attached sub-languages alike.
|
|
202
|
-
# Concatenated into
|
|
403
|
+
# Concatenated into one sequence because `position` is minted from the
|
|
203
404
|
# walk index and must not collide across concepts.
|
|
405
|
+
#
|
|
406
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
407
|
+
# "Bluebook" chapter, read first, before every other loaded
|
|
408
|
+
# chapter
|
|
409
|
+
# @param name [String] the local value-object name to collect rows
|
|
410
|
+
# of, such as `"KeywordSeed"`
|
|
411
|
+
# @return [Array<Hash{Symbol => String}>] every matching row, across
|
|
412
|
+
# every loaded chapter, in chapter order
|
|
204
413
|
def all_rows(bluebook, name)
|
|
205
414
|
seed_chapters(bluebook).flat_map { |chapter| rows(chapter, name) }
|
|
206
415
|
end
|
|
@@ -209,6 +418,11 @@ module Hecks
|
|
|
209
418
|
# sibling/extension. The name rejection avoids reading the same core
|
|
210
419
|
# object twice without relying on object identity across fixpoint
|
|
211
420
|
# assembly.
|
|
421
|
+
#
|
|
422
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
423
|
+
# "Bluebook" chapter
|
|
424
|
+
# @return [Array<Bluebook::Chapter>] `bluebook` first, then every
|
|
425
|
+
# other chapter the grammar registry holds
|
|
212
426
|
def seed_chapters(bluebook)
|
|
213
427
|
[bluebook] + MetaValidator.grammar_registry.bluebooks.values.reject { |chapter| chapter.name == bluebook.name }
|
|
214
428
|
end
|
|
@@ -217,6 +431,13 @@ module Hecks
|
|
|
217
431
|
# every aggregate that owns a same-named local value object. Repeating
|
|
218
432
|
# the value-object shape is deliberate: each concept remains readable
|
|
219
433
|
# by itself, while this discovery is the only grouping mechanism.
|
|
434
|
+
#
|
|
435
|
+
# @param bluebook [Bluebook::Chapter] the chapter to search
|
|
436
|
+
# @param name [String] the local value-object name to collect rows
|
|
437
|
+
# of, such as `"KeywordSeed"`
|
|
438
|
+
# @return [Array<Hash{Symbol => String}>] one hash per member row,
|
|
439
|
+
# `[]` if no aggregate in `bluebook` owns a value object named
|
|
440
|
+
# `name`
|
|
220
441
|
def rows(bluebook, name)
|
|
221
442
|
bluebook.aggregates.flat_map do |aggregate|
|
|
222
443
|
value_object = aggregate.value_objects.find { |vo| vo.hecks_name == name }
|
|
@@ -226,10 +447,18 @@ module Hecks
|
|
|
226
447
|
end
|
|
227
448
|
end
|
|
228
449
|
|
|
229
|
-
# Reads the dispatched result back into the
|
|
450
|
+
# Reads the dispatched result back into the same shape `rows`
|
|
230
451
|
# above hands the seed data in as — plain hashes, string values,
|
|
231
452
|
# `status` included — so `ParserTable`/`syntax_conformance_spec`
|
|
232
453
|
# need not know or care that a real dispatch happened in between.
|
|
454
|
+
#
|
|
455
|
+
# @param runtime [Runtime::Dispatcher] the runtime `boot` dispatched
|
|
456
|
+
# every seed row into
|
|
457
|
+
# @param bluebook [Bluebook::Chapter] the language's own assembled
|
|
458
|
+
# "Bluebook" chapter, read for its `"Syntax"` aggregate
|
|
459
|
+
# @return [Hash{Symbol => Array<Hash{Symbol => String}>}] `:keywords`
|
|
460
|
+
# and `:arguments`, each an array of plain, string-valued row
|
|
461
|
+
# hashes (`status` included)
|
|
233
462
|
def read_back(runtime, bluebook)
|
|
234
463
|
syntax = bluebook.aggregate("Syntax")
|
|
235
464
|
repository = runtime.registry.repository("Bluebook", syntax)
|
|
@@ -241,12 +470,23 @@ module Hecks
|
|
|
241
470
|
}
|
|
242
471
|
end
|
|
243
472
|
|
|
473
|
+
# Flattens one dispatched record into a plain, string-valued row.
|
|
474
|
+
#
|
|
475
|
+
# @param row [Object] one dispatched keyword or argument record,
|
|
476
|
+
# answering `to_h`
|
|
477
|
+
# @return [Hash{Symbol => String}] `row`'s own fields, each value
|
|
478
|
+
# unwrapped and stringified
|
|
244
479
|
def stringify(row)
|
|
245
480
|
row.to_h.transform_values do |cell|
|
|
246
481
|
scalar(cell).to_s
|
|
247
482
|
end
|
|
248
483
|
end
|
|
249
484
|
|
|
485
|
+
# Unwraps one field's stored value to its bare scalar.
|
|
486
|
+
#
|
|
487
|
+
# @param cell [Object] one field's stored value
|
|
488
|
+
# @return [Object] `cell`'s sole wrapped value, if `cell` is a
|
|
489
|
+
# to_h-able non-String; `cell` unchanged otherwise
|
|
250
490
|
def scalar(cell)
|
|
251
491
|
return cell.to_h.values.first if cell.respond_to?(:to_h) && !cell.is_a?(String)
|
|
252
492
|
|
|
@@ -10,7 +10,7 @@ module Hecks
|
|
|
10
10
|
# AdapterJudge already are — its own file, its own door, judged
|
|
11
11
|
# through its own self-hosted language (translation.bluebook)
|
|
12
12
|
# rather than left as plain Ruby structs nothing checks. Walks the
|
|
13
|
-
#
|
|
13
|
+
# whole built translation in one pass — the top-level Declare/
|
|
14
14
|
# Retire commands, then every nested aggregate's own Declare plus
|
|
15
15
|
# one Add* command per rule it carries — the same "one judge, every
|
|
16
16
|
# nested record" shape WorldJudge already gives Wiring. Field names
|
|
@@ -22,6 +22,7 @@ module Hecks
|
|
|
22
22
|
class TranslationJudge
|
|
23
23
|
attr_reader :refusals
|
|
24
24
|
|
|
25
|
+
# @param translation [Bluebook::Translation] the built translation to judge
|
|
25
26
|
def initialize(translation)
|
|
26
27
|
@translation = translation
|
|
27
28
|
@refusals = []
|
|
@@ -35,18 +36,18 @@ module Hecks
|
|
|
35
36
|
|
|
36
37
|
def args(pairs) = pairs.compact
|
|
37
38
|
|
|
38
|
-
# `Runtime::AlreadyExists` is rescued
|
|
39
|
+
# `Runtime::AlreadyExists` is rescued here but not by World/Port/
|
|
39
40
|
# Adapter's own sibling judges — found live, not by inspection.
|
|
40
|
-
# Those three each judge a
|
|
41
|
+
# Those three each judge a single aggregate per build (Port/Adapter)
|
|
41
42
|
# or a Hash keyed by verb (World's own settings, deduped by
|
|
42
43
|
# construction), so a duplicate-identity Declare can never reach
|
|
43
44
|
# their own runtime. `TranslationBuilder#aggregate` appends every
|
|
44
45
|
# block to a plain Array (`@aggregates << builder.build`) with no
|
|
45
46
|
# dedup — two `aggregate "Account" do ... end` blocks in the same
|
|
46
|
-
# translation are syntactically legal and reach here for real.
|
|
47
|
-
# unrescued, the second Declare's `AlreadyExists`
|
|
48
|
-
# through `call_translation` instead of becoming a clean
|
|
49
|
-
# confirmed via direct dispatch
|
|
47
|
+
# translation are syntactically legal and reach here for real.
|
|
48
|
+
# Left unrescued, the second Declare's `AlreadyExists` would crash
|
|
49
|
+
# straight through `call_translation` instead of becoming a clean
|
|
50
|
+
# refusal — confirmed via direct dispatch.
|
|
50
51
|
def offer(label)
|
|
51
52
|
yield
|
|
52
53
|
rescue Runtime::GivenNotMet, Runtime::InvariantViolation, Runtime::TypeMismatch,
|
|
@@ -65,13 +66,13 @@ module Hecks
|
|
|
65
66
|
send_to("Translation::Translation.Declare", t.domain,
|
|
66
67
|
with: { domain: v(t.domain), from: v(t.from), to: v(t.to) })
|
|
67
68
|
|
|
68
|
-
# `id:`, computed the
|
|
69
|
-
# to])` — Translation's own identity is
|
|
69
|
+
# `id:`, computed the same way — `Naming.identity([domain, from,
|
|
70
|
+
# to])` — Translation's own identity is composite
|
|
70
71
|
# (`identified_by :domain, :from, :to`), the same reason
|
|
71
72
|
# `Wiring.Set`'s own self-reference dispatch (world.bluebook,
|
|
72
73
|
# `WorldJudge#judge_wiring`) needs a computed `id:` rather than
|
|
73
74
|
# a bare field value — a single-field identity (TranslationAggregate's
|
|
74
|
-
# own `name`) is the
|
|
75
|
+
# own `name`) is the one case where the bare value itself is the
|
|
75
76
|
# id, confirmed live via direct dispatch testing, not assumed.
|
|
76
77
|
translation_id = Naming.identity([t.domain, t.from, t.to])
|
|
77
78
|
Array(t.retired).each do |name|
|
|
@@ -3,13 +3,14 @@ module Hecks
|
|
|
3
3
|
module MetaValidator
|
|
4
4
|
# Offers a built .world to the language that describes worlds.
|
|
5
5
|
#
|
|
6
|
-
# A world is a
|
|
6
|
+
# A world is a sibling of a bluebook — the same domain runs in many of
|
|
7
7
|
# them — so it is judged through its own door, against its own language
|
|
8
8
|
# file, and its settings normalise the same way a mutation's fields do:
|
|
9
9
|
# an open map becomes one Wiring per verb and one Setting row per value.
|
|
10
10
|
class WorldJudge
|
|
11
11
|
attr_reader :refusals
|
|
12
12
|
|
|
13
|
+
# @param world [Bluebook::World] the built world to judge
|
|
13
14
|
def initialize(world)
|
|
14
15
|
@world = world
|
|
15
16
|
@refusals = []
|
|
@@ -51,14 +52,14 @@ module Hecks
|
|
|
51
52
|
end
|
|
52
53
|
|
|
53
54
|
def judge_wiring(domain, verb, values)
|
|
54
|
-
#
|
|
55
|
+
# The same join the language itself derives. Wiring is
|
|
55
56
|
# `identified_by do world; verb.value end` — `Wiring.Declare`
|
|
56
|
-
# (a creating command) ignores this `id:` entirely and computes its
|
|
57
|
+
# (a creating command) ignores this `id:` entirely and computes its own
|
|
57
58
|
# from `world`/`verb`, so a locally minted "#{domain}.#{verb}" named a
|
|
58
59
|
# record `Wiring.Set` could never find : the id passed here has to be
|
|
59
|
-
# the
|
|
60
|
+
# the same derivation, not a second guess at what it must be.
|
|
60
61
|
id = Naming.identity([domain, verb])
|
|
61
|
-
# `world_ref` is the
|
|
62
|
+
# `world_ref` is the world's id and goes bare, the way every reference
|
|
62
63
|
# does now ; the language gives it that explicit `as:` because `world`
|
|
63
64
|
# beside it is an ordinary text attribute that happens to hold the same
|
|
64
65
|
# string, and stays a value object. The two look alike and are not —
|