hecks 0.2.0 → 0.3.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 +5 -5
- data/lib/hecks/adapters/driven/claude_code.adapter +3 -0
- data/lib/hecks/adapters/driven/claude_code.rb +127 -0
- data/lib/hecks/adapters/driven/d1.adapter +12 -0
- data/lib/hecks/adapters/driven/d1.rb +403 -0
- data/lib/hecks/adapters/driven/folder.adapter +3 -0
- data/lib/hecks/adapters/driven/folder.rb +199 -0
- data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +100 -0
- data/lib/hecks/adapters/driven/governance_authorization.adapter +3 -0
- data/lib/hecks/adapters/driven/governance_authorization.rb +62 -0
- data/lib/hecks/adapters/driven/heki/journal.rb +51 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +0 -0
- data/lib/hecks/adapters/driven/heki/snapshot.rb +38 -0
- data/lib/hecks/adapters/driven/heki.adapter +4 -0
- data/lib/hecks/adapters/driven/heki.rb +148 -0
- data/lib/hecks/adapters/driven/identity_registry.adapter +3 -0
- data/lib/hecks/adapters/driven/identity_registry.rb +26 -0
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +51 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +63 -0
- data/lib/hecks/adapters/driven/lambda.adapter +4 -0
- data/lib/hecks/adapters/driven/lambda.rb +131 -0
- data/lib/hecks/adapters/driven/memory.adapter +3 -0
- data/lib/hecks/adapters/driven/memory.rb +103 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +3 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +28 -0
- data/lib/hecks/adapters/driven/postgres/codec.rb +80 -0
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +168 -0
- data/lib/hecks/adapters/driven/postgres.adapter +5 -0
- data/lib/hecks/adapters/driven/postgres.rb +391 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/era_store.rb +171 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/field_cache.rb +190 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/head_compiler.rb +429 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/mint_transaction.rb +166 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/provisioning.rb +286 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/resumable_backfill.rb +168 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/tail_merge.rb +163 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage/transform_installer.rb +114 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage.rb +137 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/coverage_check.rb +89 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/era_resolver.rb +82 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager/minter.rb +132 -0
- data/lib/hecks/adapters/driven/postgres_era/lineage_manager.rb +74 -0
- data/lib/hecks/adapters/driven/postgres_era.adapter +6 -0
- data/lib/hecks/adapters/driven/postgres_era.rb +671 -0
- data/lib/hecks/adapters/driven/prism.adapter +3 -0
- data/lib/hecks/adapters/driven/prism.rb +80 -0
- data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
- data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
- data/lib/hecks/adapters/driven/sqlite/codec.rb +63 -0
- data/lib/hecks/adapters/driven/sqlite/projection.rb +105 -0
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +180 -0
- data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
- data/lib/hecks/adapters/driven/sqlite.rb +285 -0
- data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
- data/lib/hecks/adapters/driven/system_clock.rb +14 -0
- data/lib/hecks/adapters/driven.rb +36 -0
- data/lib/hecks/adapters.rb +6 -0
- data/lib/hecks/behaviors/dsl.rb +96 -0
- data/lib/hecks/behaviors/expectations.rb +280 -0
- data/lib/hecks/behaviors/ir.rb +31 -0
- data/lib/hecks/behaviors/rspec.rb +42 -0
- data/lib/hecks/behaviors/runner.rb +96 -0
- data/lib/hecks/behaviors.rb +25 -0
- data/lib/hecks/bluebook/aggregate.rb +108 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
- data/lib/hecks/bluebook/assembly/build.rb +48 -0
- data/lib/hecks/bluebook/assembly/contract.rb +112 -0
- data/lib/hecks/bluebook/assembly/contracts.rb +409 -0
- data/lib/hecks/bluebook/assembly/marks.rb +227 -0
- data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
- data/lib/hecks/bluebook/assembly.rb +78 -0
- data/lib/hecks/bluebook/attribute.rb +96 -0
- data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
- data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
- data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
- data/lib/hecks/bluebook/behaviour/command.rb +116 -0
- data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
- data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
- data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +51 -0
- data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
- data/lib/hecks/bluebook/behaviour/process_manager.rb +38 -0
- data/lib/hecks/bluebook/behaviour/query.rb +10 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
- data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
- data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
- data/lib/hecks/bluebook/chapter.rb +68 -0
- data/lib/hecks/bluebook/command.rb +124 -0
- data/lib/hecks/bluebook/domain_port.rb +102 -0
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +906 -0
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +327 -0
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1018 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +626 -0
- data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
- data/lib/hecks/bluebook/dsl/entity_builder.rb +305 -0
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
- data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
- data/lib/hecks/bluebook/dsl/policy_builder.rb +120 -0
- data/lib/hecks/bluebook/dsl/port_builder.rb +30 -0
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +207 -0
- data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
- data/lib/hecks/bluebook/dsl/rule_reference.rb +172 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
- data/lib/hecks/bluebook/dsl/word_gate.rb +221 -0
- data/lib/hecks/bluebook/dsl/world_builder.rb +77 -0
- data/lib/hecks/bluebook/dsl.rb +45 -0
- data/lib/hecks/bluebook/entity.rb +103 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -0
- data/lib/hecks/bluebook/expression/evaluator.rb +261 -0
- data/lib/hecks/bluebook/expression/projection.json +170 -0
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
- data/lib/hecks/bluebook/expression/resolver.rb +630 -0
- data/lib/hecks/bluebook/expression.rb +13 -0
- data/lib/hecks/bluebook/hexagon.rb +60 -0
- data/lib/hecks/bluebook/lifecycle.rb +42 -0
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +559 -0
- data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +47 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +350 -0
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +312 -0
- data/lib/hecks/bluebook/meta_validator/shapes.rb +246 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
- data/lib/hecks/bluebook/meta_validator.rb +452 -0
- data/lib/hecks/bluebook/model_check.rb +318 -0
- data/lib/hecks/bluebook/pattern_subset.rb +120 -0
- data/lib/hecks/bluebook/policy.rb +41 -0
- data/lib/hecks/bluebook/process_manager.rb +86 -0
- data/lib/hecks/bluebook/project_discovery.rb +30 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +82 -0
- data/lib/hecks/bluebook/query.rb +61 -0
- data/lib/hecks/bluebook/read_model.rb +109 -0
- data/lib/hecks/bluebook/reference.rb +74 -0
- data/lib/hecks/bluebook/smoke_test.rb +166 -0
- data/lib/hecks/bluebook/synthesizer.rb +95 -0
- data/lib/hecks/bluebook/translation.rb +92 -0
- data/lib/hecks/bluebook/value_object.rb +58 -0
- data/lib/hecks/bluebook.rb +74 -0
- data/lib/hecks/codemod.rb +342 -0
- data/lib/hecks/construct.rb +71 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
- data/lib/hecks/deploy/oidc.json +18 -0
- data/lib/hecks/doc/reference.rb +389 -0
- data/lib/hecks/embryonaut_bluebook.rb +75 -0
- data/lib/hecks/facade/cli_door.rb +116 -0
- data/lib/hecks/facade/cli_runner.rb +190 -0
- data/lib/hecks/facade/command_request.rb +105 -0
- data/lib/hecks/facade/handle.rb +173 -0
- data/lib/hecks/facade/json_door.rb +154 -0
- data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
- data/lib/hecks/facade/surface/chapter.rb +107 -0
- data/lib/hecks/facade/surface.rb +48 -0
- data/lib/hecks/facade.rb +44 -0
- data/lib/hecks/forms/app.rb +300 -0
- data/lib/hecks/forms/command_form_renderer.rb +113 -0
- data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
- data/lib/hecks/forms/field_renderer.rb +170 -0
- data/lib/hecks/forms/field_shape.rb +232 -0
- data/lib/hecks/forms/html.rb +53 -0
- data/lib/hecks/forms/index_renderer.rb +35 -0
- data/lib/hecks/forms/page.rb +157 -0
- data/lib/hecks/forms/params.rb +132 -0
- data/lib/hecks/forms/query_form_renderer.rb +114 -0
- data/lib/hecks/forms/record_renderer.rb +115 -0
- data/lib/hecks/forms/record_table.rb +63 -0
- data/lib/hecks/forms/reference_options.rb +30 -0
- data/lib/hecks/forms/value_object_shape.rb +46 -0
- data/lib/hecks/forms.rb +54 -0
- data/lib/hecks/fqn.rb +94 -0
- data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
- data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
- data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
- data/lib/hecks/framework/bluebook/governance.bluebook +130 -0
- data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
- data/lib/hecks/framework/oidc.json +39 -0
- data/lib/hecks/framework.rb +90 -0
- data/lib/hecks/freezer.rb +67 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +92 -0
- data/lib/hecks/fuzzing/properties.rb +1146 -0
- data/lib/hecks/fuzzing/replay.rb +635 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +54 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +185 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +130 -0
- data/lib/hecks/fuzzing/value_generator.rb +177 -0
- data/lib/hecks/fuzzing.rb +12 -0
- data/lib/hecks/grammar/evolve.rb +291 -0
- data/lib/hecks/grammar/expression.bluebook +417 -0
- data/lib/hecks/grammar/expression_operators.json +1152 -0
- data/lib/hecks/grammar/grammar.hecksagon +20 -0
- data/lib/hecks/grammar/oidc.json +69 -0
- data/lib/hecks/grammar/translation.bluebook +203 -0
- data/lib/hecks/grammar.rb +178 -0
- data/lib/hecks/ir.rb +126 -0
- data/lib/hecks/language/adapter.bluebook +116 -0
- data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
- data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
- data/lib/hecks/language/bluebook/command.bluebook +437 -0
- data/lib/hecks/language/bluebook/entity.bluebook +316 -0
- data/lib/hecks/language/bluebook/policy.bluebook +172 -0
- data/lib/hecks/language/bluebook/process_manager.bluebook +264 -0
- data/lib/hecks/language/bluebook/projection.bluebook +267 -0
- data/lib/hecks/language/bluebook/query.bluebook +245 -0
- data/lib/hecks/language/bluebook/shape.bluebook +292 -0
- data/lib/hecks/language/bluebook/syntax.bluebook +447 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +364 -0
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
- data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
- data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
- data/lib/hecks/language/oidc.json +313 -0
- data/lib/hecks/language/port.bluebook +92 -0
- data/lib/hecks/language/translation/translation.bluebook +110 -0
- data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
- data/lib/hecks/language/world/wiring.bluebook +62 -0
- data/lib/hecks/language/world/world.bluebook +84 -0
- data/lib/hecks/literal.rb +125 -0
- data/lib/hecks/naming.rb +121 -0
- data/lib/hecks/ports/access_control.port +4 -0
- data/lib/hecks/ports/access_control.rb +62 -0
- data/lib/hecks/ports/agent/answers.rb +104 -0
- data/lib/hecks/ports/agent.port +4 -0
- data/lib/hecks/ports/agent.rb +167 -0
- data/lib/hecks/ports/authentication.port +4 -0
- data/lib/hecks/ports/authentication.rb +50 -0
- data/lib/hecks/ports/authorization.port +4 -0
- data/lib/hecks/ports/authorization.rb +59 -0
- data/lib/hecks/ports/clock.port +4 -0
- data/lib/hecks/ports/clock.rb +62 -0
- data/lib/hecks/ports/extraction.port +4 -0
- data/lib/hecks/ports/extraction.rb +37 -0
- data/lib/hecks/ports/identity_assignment.port +4 -0
- data/lib/hecks/ports/identity_assignment.rb +45 -0
- data/lib/hecks/ports/identity_generation.port +4 -0
- data/lib/hecks/ports/identity_generation.rb +49 -0
- data/lib/hecks/ports/identity_resolution.port +4 -0
- data/lib/hecks/ports/identity_resolution.rb +40 -0
- data/lib/hecks/ports/loading.port +4 -0
- data/lib/hecks/ports/loading.rb +13 -0
- data/lib/hecks/ports/persistence/append_only.rb +102 -0
- data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
- data/lib/hecks/ports/persistence/execution.rb +18 -0
- data/lib/hecks/ports/persistence/lineage.rb +277 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
- data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
- data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
- data/lib/hecks/ports/persistence.port +4 -0
- data/lib/hecks/ports/persistence.rb +39 -0
- data/lib/hecks/ports/projection.port +4 -0
- data/lib/hecks/ports/projection.rb +76 -0
- data/lib/hecks/ports/query/in_memory.rb +60 -0
- data/lib/hecks/ports/query/ordering.rb +41 -0
- data/lib/hecks/ports/query.rb +36 -0
- data/lib/hecks/ports.rb +27 -0
- data/lib/hecks/projections/diagrams.rb +413 -0
- data/lib/hecks/projections/ir.rb +18 -0
- data/lib/hecks/projections/model/deviations.rb +98 -0
- data/lib/hecks/projections/model.rb +145 -0
- data/lib/hecks/projections/oidc.rb +110 -0
- data/lib/hecks/projections/parser_table.rb +159 -0
- data/lib/hecks/projections/reference.rb +38 -0
- data/lib/hecks/projections/shape.rb +31 -0
- data/lib/hecks/projections/statements.rb +110 -0
- data/lib/hecks/projections/vocabulary.rb +100 -0
- data/lib/hecks/projections.rb +32 -0
- data/lib/hecks/projector/cli_projector.rb +437 -0
- data/lib/hecks/projector/docs_projector.rb +321 -0
- data/lib/hecks/projector/exporter.rb +141 -0
- data/lib/hecks/projector/ir_projector.rb +18 -0
- data/lib/hecks/projector/narrate_projector.rb +243 -0
- data/lib/hecks/projector/target.rb +97 -0
- data/lib/hecks/projector.rb +186 -0
- data/lib/hecks/query_ir.rb +392 -0
- data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
- data/lib/hecks/query_specification/common/comparators.rb +25 -0
- data/lib/hecks/query_specification/common/comparison.rb +174 -0
- data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
- data/lib/hecks/query_specification/common/dsl.rb +58 -0
- data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
- data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
- data/lib/hecks/query_specification/common/null_policy.rb +80 -0
- data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
- data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
- data/lib/hecks/query_specification/common/options.rb +32 -0
- data/lib/hecks/query_specification/common/order_by.rb +9 -0
- data/lib/hecks/query_specification/common/specification.rb +10 -0
- data/lib/hecks/query_specification/common/where_clause.rb +9 -0
- data/lib/hecks/query_specification/field_path.rb +89 -0
- data/lib/hecks/query_specification/hop_path.rb +130 -0
- data/lib/hecks/query_specification/read_model/specification.rb +18 -0
- data/lib/hecks/query_specification.rb +14 -0
- data/lib/hecks/rendering.rb +48 -0
- data/lib/hecks/router/namespace_installer.rb +157 -0
- data/lib/hecks/router.rb +70 -0
- data/lib/hecks/runtime/caller.rb +50 -0
- data/lib/hecks/runtime/capability_graph.rb +44 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +124 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +248 -0
- data/lib/hecks/runtime/command_interpreter.rb +413 -0
- data/lib/hecks/runtime/command_rules/admissibility.rb +307 -0
- data/lib/hecks/runtime/command_rules/arithmetic.rb +254 -0
- data/lib/hecks/runtime/command_rules/authorization.rb +64 -0
- data/lib/hecks/runtime/command_rules/emission.rb +34 -0
- data/lib/hecks/runtime/command_rules/references.rb +181 -0
- data/lib/hecks/runtime/command_rules.rb +28 -0
- data/lib/hecks/runtime/dependency_planning.rb +231 -0
- data/lib/hecks/runtime/dispatcher.rb +286 -0
- data/lib/hecks/runtime/entity_element.rb +253 -0
- data/lib/hecks/runtime/entity_interpreter.rb +228 -0
- data/lib/hecks/runtime/era_check.rb +136 -0
- data/lib/hecks/runtime/era_guard/shape_diff.rb +128 -0
- data/lib/hecks/runtime/era_guard.rb +193 -0
- data/lib/hecks/runtime/era_tamper.rb +61 -0
- data/lib/hecks/runtime/errors.rb +100 -0
- data/lib/hecks/runtime/event.rb +51 -0
- data/lib/hecks/runtime/identity.rb +128 -0
- data/lib/hecks/runtime/instance.rb +120 -0
- data/lib/hecks/runtime/interpreting.rb +69 -0
- data/lib/hecks/runtime/loader.rb +143 -0
- data/lib/hecks/runtime/policy_interpreter.rb +366 -0
- data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
- data/lib/hecks/runtime/query_interpreter.rb +254 -0
- data/lib/hecks/runtime/reaction_invocation.rb +245 -0
- data/lib/hecks/runtime/read_model_interpreter.rb +266 -0
- data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
- data/lib/hecks/runtime/reference_hop.rb +99 -0
- data/lib/hecks/runtime/refusal_wording.rb +124 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +70 -0
- data/lib/hecks/runtime/registry/verification.rb +158 -0
- data/lib/hecks/runtime/registry.rb +218 -0
- data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
- data/lib/hecks/runtime/routing.rb +96 -0
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
- data/lib/hecks/runtime/saga_interpreter.rb +301 -0
- data/lib/hecks/runtime/storage_shape.rb +120 -0
- data/lib/hecks/runtime/tenant_check.rb +84 -0
- data/lib/hecks/runtime/tenant_scope.rb +55 -0
- data/lib/hecks/runtime/value/admission.rb +112 -0
- data/lib/hecks/runtime/value/coercion.rb +500 -0
- data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
- data/lib/hecks/runtime/value.rb +125 -0
- data/lib/hecks/runtime.rb +95 -0
- data/lib/hecks/translation/audit/approval_digest.rb +31 -0
- data/lib/hecks/translation/audit/layer_one.rb +37 -0
- data/lib/hecks/translation/audit/layer_two.rb +77 -0
- data/lib/hecks/translation/audit/unfed_report.rb +42 -0
- data/lib/hecks/translation/audit.rb +70 -0
- data/lib/hecks/translation/reattest.rb +72 -0
- data/lib/hecks/translation/rule_compiler.rb +120 -0
- data/lib/hecks/translation/scaffold/differ.rb +183 -0
- data/lib/hecks/translation/scaffold/renderer.rb +41 -0
- data/lib/hecks/translation/scaffold/writer.rb +28 -0
- data/lib/hecks/translation/scaffold.rb +30 -0
- data/lib/hecks/translation.rb +11 -0
- data/lib/hecks/version.rb +16 -0
- data/lib/hecks/vocabulary.rb +213 -0
- data/lib/hecks.rb +118 -6
- metadata +401 -222
- data/bin/hecks +0 -7
- data/bin/hecks-package +0 -65
- data/bin/hecks_console +0 -12
- data/bin/hecks_serverless +0 -6
- data/lib/cli/build.rb +0 -14
- data/lib/cli/command_runner.rb +0 -28
- data/lib/cli/console.rb +0 -10
- data/lib/cli/generate.rb +0 -37
- data/lib/cli/hecks-cli.rb +0 -27
- data/lib/cli/test.rb +0 -57
- data/lib/console/commands.rb +0 -8
- data/lib/console/hecks-console.rb +0 -1
- data/lib/packager/README.md +0 -0
- data/lib/packager/app_runner.rb +0 -21
- data/lib/packager/args.rb +0 -26
- data/lib/packager/compatibility/fixnum.rb +0 -6
- data/lib/packager/hecks.rb +0 -39
- data/lib/packager/query_runner.rb +0 -21
- data/lib/packager/resources/Dockerfile +0 -11
- data/lib/packager/resources/app_binary +0 -7
- data/lib/packager/resources/bundle_config +0 -3
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
- data/lib/serverless/Domain +0 -32
- data/lib/serverless/cli.rb +0 -75
- data/lib/serverless/resources/command_name.js +0 -5
- data/lib/serverless/resources/environment.js +0 -7
- data/lib/serverless/resources/handler.js.tt +0 -28
- data/lib/serverless/resources/run_binary.js +0 -22
- data/lib/serverless/resources/serverless.yml +0 -20
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module MetaValidator
|
|
4
|
+
# Offers a built .world to the language that describes worlds.
|
|
5
|
+
#
|
|
6
|
+
# A world is a SIBLING of a bluebook — the same domain runs in many of
|
|
7
|
+
# them — so it is judged through its own door, against its own language
|
|
8
|
+
# file, and its settings normalise the same way a mutation's fields do:
|
|
9
|
+
# an open map becomes one Wiring per verb and one Setting row per value.
|
|
10
|
+
class WorldJudge
|
|
11
|
+
attr_reader :refusals
|
|
12
|
+
|
|
13
|
+
def initialize(world)
|
|
14
|
+
@world = world
|
|
15
|
+
@refusals = []
|
|
16
|
+
@runtime = MetaValidator.fresh_runtime
|
|
17
|
+
judge!
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def v(text) = text.nil? ? nil : { value: text.to_s }
|
|
23
|
+
|
|
24
|
+
def args(pairs) = pairs.reject { |_, value| value.nil? }
|
|
25
|
+
|
|
26
|
+
def offer(label)
|
|
27
|
+
yield
|
|
28
|
+
rescue Runtime::GivenNotMet, Runtime::InvariantViolation,
|
|
29
|
+
Runtime::TypeMismatch, Runtime::NotFound => e
|
|
30
|
+
@refusals << "#{label}: #{e.message}"
|
|
31
|
+
rescue Runtime::UnknownVerb
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def send_to(verb, label, to: nil, **payload)
|
|
36
|
+
offer(label) { @runtime.dispatch(verb, to: to, with: args(payload)) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def judge!
|
|
40
|
+
domain = @world.domain
|
|
41
|
+
send_to("World::World.Declare", domain, domain: v(domain),
|
|
42
|
+
realm: v(@world.realm), latest: v(@world.latest))
|
|
43
|
+
|
|
44
|
+
Hash(@world.settings).each do |verb, values|
|
|
45
|
+
# the DSL records each binding twice — once by verb, once by
|
|
46
|
+
# "verb:adapter" — so only the plain verb is offered
|
|
47
|
+
next if verb.to_s.include?(":")
|
|
48
|
+
|
|
49
|
+
judge_wiring(domain, verb, values)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def judge_wiring(domain, verb, values)
|
|
54
|
+
# THE SAME JOIN THE LANGUAGE ITSELF DERIVES. Wiring is
|
|
55
|
+
# `identified_by do world; verb.value end` — `Wiring.Declare`
|
|
56
|
+
# (a creating command) ignores this `id:` entirely and computes its OWN
|
|
57
|
+
# from `world`/`verb`, so a locally minted "#{domain}.#{verb}" named a
|
|
58
|
+
# record `Wiring.Set` could never find : the id passed here has to be
|
|
59
|
+
# the SAME derivation, not a second guess at what it must be.
|
|
60
|
+
id = Naming.identity([domain, verb])
|
|
61
|
+
# `world_ref` is the WORLD's id and goes bare, the way every reference
|
|
62
|
+
# does now ; the language gives it that explicit `as:` because `world`
|
|
63
|
+
# beside it is an ordinary text attribute that happens to hold the same
|
|
64
|
+
# string, and stays a value object. The two look alike and are not —
|
|
65
|
+
# which is exactly why the judge asks the type rather than the name.
|
|
66
|
+
send_to("World::Wiring.Declare", id, world_ref: v(domain),
|
|
67
|
+
world: v(domain), verb: v(verb), adapter: v(Hash(values)[:adapter]))
|
|
68
|
+
|
|
69
|
+
Hash(values).each do |key, value|
|
|
70
|
+
next if key.to_sym == :adapter
|
|
71
|
+
|
|
72
|
+
send_to("World::Wiring.Set", id, to: id, key: v(key), value: v(value))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
require "digest"
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module Bluebook
|
|
6
|
+
# Judges a bluebook by DISPATCHING it into the language declared in itself.
|
|
7
|
+
#
|
|
8
|
+
# `lib/hecks/language/bluebook/` declares what a bluebook IS —
|
|
9
|
+
# Chapter, Root, Verb, Shape, Ask, Piece, and the rest, split across files
|
|
10
|
+
# by domain concept and merged into one chapter at load time (see
|
|
11
|
+
# GRAMMAR_FILES below) — and carries the language's rules as `given` and
|
|
12
|
+
# `invariant` rather than as `raise Malformed` scattered across seven
|
|
13
|
+
# builder files. This replays a built IR into that domain and turns any
|
|
14
|
+
# refusal into a Malformed, so the meta-domain is what actually judges
|
|
15
|
+
# rather than a description sitting beside the code.
|
|
16
|
+
#
|
|
17
|
+
# Delete language/bluebook/ and validation stops. That is the whole
|
|
18
|
+
# point : a self-description that only describes is indistinguishable from
|
|
19
|
+
# enforcement, and the first version of this file was deleted for exactly
|
|
20
|
+
# that reason.
|
|
21
|
+
#
|
|
22
|
+
# The meta-domain is loaded ONCE and its registry reused ; each bluebook is
|
|
23
|
+
# judged in a fresh in-memory store so no domain can see another's records.
|
|
24
|
+
module MetaValidator
|
|
25
|
+
# THE FOLDER IS THE CHAPTER. Files are grouped by the domain concept they
|
|
26
|
+
# describe and every one reopens the same `Hecks.bluebook "Bluebook"`.
|
|
27
|
+
# `BluebookBuilder.build` keeps one builder open per chapter name across
|
|
28
|
+
# calls, so the sorted folder accumulates one domain. Adding or renaming a
|
|
29
|
+
# concept file requires no second catalog here; deterministic filename
|
|
30
|
+
# order is the source order exported to IR.
|
|
31
|
+
GRAMMAR_DIR = File.expand_path("../language/bluebook", __dir__).freeze
|
|
32
|
+
GRAMMAR_FILES = Dir.glob(File.join(GRAMMAR_DIR, "*.bluebook")).sort.freeze
|
|
33
|
+
# Sibling artifact languages use the same folder-is-the-chapter rule.
|
|
34
|
+
# Their arrays are discovered, sorted source sets—not filename catalogs.
|
|
35
|
+
WORLD_GRAMMAR = Dir.glob(File.expand_path("../language/world/*.bluebook", __dir__)).sort.freeze
|
|
36
|
+
HECKSAGON_GRAMMAR = Dir.glob(File.expand_path("../language/hecksagon/*.bluebook", __dir__)).sort.freeze
|
|
37
|
+
# so is a port — whole-project table-unification survey, item #13's
|
|
38
|
+
# remaining builders. Backs the new PortJudge door the same way
|
|
39
|
+
# WORLD_GRAMMAR backs WorldJudge.
|
|
40
|
+
PORT_GRAMMAR = File.expand_path("../language/port.bluebook", __dir__).freeze
|
|
41
|
+
# so is an adapter — same reasoning, one file over. Backs AdapterJudge.
|
|
42
|
+
ADAPTER_GRAMMAR = File.expand_path("../language/adapter.bluebook", __dir__).freeze
|
|
43
|
+
# so is a translation — same reasoning, one file over. Backs
|
|
44
|
+
# TranslationJudge.
|
|
45
|
+
TRANSLATION_GRAMMAR = Dir.glob(File.expand_path("../language/translation/*.bluebook", __dir__)).sort.freeze
|
|
46
|
+
|
|
47
|
+
# ADR 0026's OWN SEAM: THE CORE DOES NOT NAME ITS EXTENSION POINTS.
|
|
48
|
+
#
|
|
49
|
+
# A sub-language chapter (Paging, so far the only one) is an ORDINARY
|
|
50
|
+
# bluebook — declared with the same `aggregate`/`value_object`/
|
|
51
|
+
# `attaches_to` words every domain has, judged through the language
|
|
52
|
+
# the normal way, not bootstrapped raw the way GRAMMAR_FILES is. What
|
|
53
|
+
# makes it special is only where it LIVES: any file in this directory
|
|
54
|
+
# is discovered and loaded here, by the directory's own existence,
|
|
55
|
+
# never by a name this file would have to know. Add a chapter here
|
|
56
|
+
# and it is attached ; nothing in this file changes.
|
|
57
|
+
ATTACHED_GRAMMAR_DIR = File.expand_path("../language/bluebook/attaches", __dir__).freeze
|
|
58
|
+
|
|
59
|
+
# The chapters that ARE the language — loaded raw during bootstrap, then
|
|
60
|
+
# judged through themselves and replaced by their own assembled graphs
|
|
61
|
+
# (see grammar_registry). Each is named after its file : Bluebook describes
|
|
62
|
+
# bluebooks (language/bluebook/) ; World describes worlds (world.bluebook),
|
|
63
|
+
# and backs the WorldJudge door ; Hecksagon describes hecksagons
|
|
64
|
+
# (hecksagon.bluebook) — declared for the same self-description reasons as
|
|
65
|
+
# World, but WITHOUT a judge door of its own : nothing dispatches a real
|
|
66
|
+
# .hecksagon file through it yet, so HecksagonBuilder's own behavior is
|
|
67
|
+
# unchanged. What this buys is what syntax.bluebook needed — a real shape
|
|
68
|
+
# `subscribe`'s `fills: "subscriptions"` can point at — not new validation
|
|
69
|
+
# on top of the corpus's existing .hecksagon files.
|
|
70
|
+
LANGUAGE_CHAPTERS = %w[Bluebook World Hecksagon].freeze
|
|
71
|
+
|
|
72
|
+
# The meta-domain is itself a bluebook. Judging it while loading it would
|
|
73
|
+
# recurse, so the load path marks the bootstrap and skips — but the skip
|
|
74
|
+
# is only the FIRST pass. Once every grammar file is loaded and merged,
|
|
75
|
+
# grammar_registry judges the language through itself and keeps the
|
|
76
|
+
# assembled result (the fixpoint, made load-bearing).
|
|
77
|
+
def self.bootstrapping? = @bootstrapping
|
|
78
|
+
|
|
79
|
+
# A CHAPTER MAY BE SPLIT ACROSS FILES, so it cannot be judged until
|
|
80
|
+
# every file has been read.
|
|
81
|
+
#
|
|
82
|
+
# `BluebookBuilder.build` already MERGES — `registry.bluebook_builder
|
|
83
|
+
# (name)` memoises one builder per chapter name, so nine files each
|
|
84
|
+
# saying `Hecks.bluebook "Bluebook"` accumulate into one. What it
|
|
85
|
+
# also does is call `MetaValidator.call` once PER FILE, judging a
|
|
86
|
+
# chapter that is still eight files short: `Aggregate`'s reference
|
|
87
|
+
# to `Bluebook` dangles because `Bluebook` has not been declared
|
|
88
|
+
# yet, and the load dies.
|
|
89
|
+
#
|
|
90
|
+
# The language's own grammar has always needed this and got it
|
|
91
|
+
# privately, through `@bootstrapping` (see load_grammar_into) —
|
|
92
|
+
# which is exactly why the language could not boot the way the
|
|
93
|
+
# domains it describes boot. This is that same two-phase load,
|
|
94
|
+
# available to anything: `Folder#load_domain` reads every
|
|
95
|
+
# `*.bluebook` inside `defer`, then judges each composed chapter
|
|
96
|
+
# once, before hecksagons and worlds load (DOMAIN_ORDER already
|
|
97
|
+
# puts every chapter ahead of those).
|
|
98
|
+
#
|
|
99
|
+
# Only chapters DECLARED INSIDE the window are judged afterwards.
|
|
100
|
+
# Re-judging one already assembled — a framework member pulled in
|
|
101
|
+
# earlier by `uses_framework`, say — would re-run Assembly and hand
|
|
102
|
+
# out a second set of classes for a graph something already holds.
|
|
103
|
+
def self.deferring? = @deferring
|
|
104
|
+
|
|
105
|
+
def self.defer
|
|
106
|
+
previous = @deferring
|
|
107
|
+
@deferring = true
|
|
108
|
+
yield
|
|
109
|
+
ensure
|
|
110
|
+
@deferring = previous
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def self.deferred_chapters = @deferred_chapters ||= []
|
|
114
|
+
|
|
115
|
+
def self.judge_deferred!(registry)
|
|
116
|
+
pending = deferred_chapters.uniq
|
|
117
|
+
@deferred_chapters = []
|
|
118
|
+
return unless registry
|
|
119
|
+
|
|
120
|
+
pending.each do |name|
|
|
121
|
+
chapter = registry.bluebook(name)
|
|
122
|
+
next unless chapter
|
|
123
|
+
|
|
124
|
+
# A bare chapter-given left PENDING by any file of this
|
|
125
|
+
# chapter (`AggregateBuilder#pending_chapter_given`) resolves
|
|
126
|
+
# first — before anything below reads a `Given`'s fields.
|
|
127
|
+
# `registry.bluebook_builder(name)` is the SAME instance every
|
|
128
|
+
# one of this chapter's own files built onto (`#self.build`'s
|
|
129
|
+
# own comment); it is guaranteed already open here, since it
|
|
130
|
+
# is what produced `chapter` in the first place — the block is
|
|
131
|
+
# dead code, never actually invoked.
|
|
132
|
+
builder = registry.bluebook_builder(name) { raise "internal: no open builder for #{name}" }
|
|
133
|
+
builder.resolve_pending_chapter_givens!
|
|
134
|
+
|
|
135
|
+
# `BluebookBuilder#build` skipped its own whole-chapter battery
|
|
136
|
+
# (hops, projected fields, correlation keys, event shapes,
|
|
137
|
+
# `with:` projections) for every file of THIS chapter while
|
|
138
|
+
# `deferring?` was true, the same reason `call` below queued
|
|
139
|
+
# instead of judging — each of those checks needs every file
|
|
140
|
+
# loaded first (see `BluebookBuilder.validate_assembled!`'s own
|
|
141
|
+
# comment). `chapter` here is exactly that: whatever the LAST
|
|
142
|
+
# file's own `add_bluebook` left in the registry, which by now
|
|
143
|
+
# holds every aggregate/policy/process_manager the whole
|
|
144
|
+
# chapter declares. Run once, here, instead of once per file.
|
|
145
|
+
DSL::BluebookBuilder.validate_assembled!(chapter)
|
|
146
|
+
registry.add_bluebook(call(chapter))
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def self.disabled? = ENV["HECKS_META_VALIDATION"] == "off"
|
|
151
|
+
|
|
152
|
+
# ADR 0025's own prerequisite (docs/dsl-work-slices.md, S0a): a word
|
|
153
|
+
# a later slice removes from the LIVE grammar must still parse
|
|
154
|
+
# FROZEN ERA TEXT — `EraGuard.shadow_parse` (runtime/era_guard.rb)
|
|
155
|
+
# is a plain `Kernel.eval` of stored source, run at boot, at mint,
|
|
156
|
+
# and during tamper detection, against whatever grammar is live
|
|
157
|
+
# TODAY, not whatever grammar was live when that text was written.
|
|
158
|
+
# Judging it again here would refuse history the day a spelling it
|
|
159
|
+
# used is removed — proved with a rule that already lives ONLY in
|
|
160
|
+
# the meta-domain, never duplicated as a builder's own `raise
|
|
161
|
+
# Malformed` (`vision`'s own comment: "moved to the language").
|
|
162
|
+
#
|
|
163
|
+
# Mirrors `defer`'s own stack-restore shape, not `disabled?`'s bare
|
|
164
|
+
# env toggle — this must never leak past the one shadow-parse call
|
|
165
|
+
# that set it, the same reason `ConstShim.with`/`.active?`
|
|
166
|
+
# (bluebook/dsl/const_shim.rb) restores in an `ensure` rather than
|
|
167
|
+
# being flipped and left.
|
|
168
|
+
def self.shadow_parsing? = @shadow_parsing
|
|
169
|
+
|
|
170
|
+
def self.while_shadow_parsing
|
|
171
|
+
previous = @shadow_parsing
|
|
172
|
+
@shadow_parsing = true
|
|
173
|
+
yield
|
|
174
|
+
ensure
|
|
175
|
+
@shadow_parsing = previous
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# The same bluebook judged twice gets the same verdict, and a suite reloads
|
|
179
|
+
# its fixtures constantly — banking alone is ~200 dispatches per build.
|
|
180
|
+
# Keyed on the IR itself, so a CHANGED bluebook is always re-judged.
|
|
181
|
+
def self.verdicts = @verdicts ||= {}
|
|
182
|
+
|
|
183
|
+
# A world is not a bluebook, so it gets its own door. Same judge, same
|
|
184
|
+
# meta-domain registry — a different artifact and a different language file.
|
|
185
|
+
def self.call_world(world)
|
|
186
|
+
return world if disabled? || bootstrapping? || shadow_parsing?
|
|
187
|
+
|
|
188
|
+
key = Digest::SHA256.hexdigest(JSON.generate([world.domain, world.realm, world.latest, world.settings]))
|
|
189
|
+
refusals = verdicts[key] ||= WorldJudge.new(world).refusals
|
|
190
|
+
return world if refusals.empty?
|
|
191
|
+
|
|
192
|
+
raise DSL::Malformed,
|
|
193
|
+
"#{world.domain}'s world is not well formed; #{refusals.join('; ')}"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# A port is not a bluebook either — same door shape as call_world,
|
|
197
|
+
# one artifact over. Whole-project table-unification survey, item
|
|
198
|
+
# #13's remaining builders.
|
|
199
|
+
def self.call_port(port)
|
|
200
|
+
return port if disabled? || bootstrapping? || shadow_parsing?
|
|
201
|
+
|
|
202
|
+
key = Digest::SHA256.hexdigest(JSON.generate([port.name, port.verb, port.signal]))
|
|
203
|
+
refusals = verdicts[key] ||= PortJudge.new(port).refusals
|
|
204
|
+
return port if refusals.empty?
|
|
205
|
+
|
|
206
|
+
raise DSL::Malformed,
|
|
207
|
+
"#{port.name}'s port is not well formed; #{refusals.join('; ')}"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# An adapter is not a bluebook either — same door shape, one more
|
|
211
|
+
# artifact over. Whole-project table-unification survey, item
|
|
212
|
+
# #13's remaining builders.
|
|
213
|
+
def self.call_adapter(adapter)
|
|
214
|
+
return adapter if disabled? || bootstrapping? || shadow_parsing?
|
|
215
|
+
|
|
216
|
+
key = Digest::SHA256.hexdigest(JSON.generate([adapter.name, adapter.port, adapter.fields, adapter.secrets]))
|
|
217
|
+
refusals = verdicts[key] ||= AdapterJudge.new(adapter).refusals
|
|
218
|
+
return adapter if refusals.empty?
|
|
219
|
+
|
|
220
|
+
raise DSL::Malformed,
|
|
221
|
+
"#{adapter.name}'s adapter is not well formed; #{refusals.join('; ')}"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# A translation is not a bluebook either — same door shape, one more
|
|
225
|
+
# artifact over (a `translations/*.bluebook` edge — `data_translation`'s
|
|
226
|
+
# own real, established convention; there is no separate extension).
|
|
227
|
+
# TranslationJudge walks the WHOLE built translation (every nested
|
|
228
|
+
# aggregate's own rule table) in one pass — only
|
|
229
|
+
# TranslationBuilder's own top-level `build` calls this;
|
|
230
|
+
# TranslationAggregateBuilder#build stays a plain struct constructor,
|
|
231
|
+
# the same way `WorldJudge` judges every `Wiring` a `.world` declares
|
|
232
|
+
# in ONE pass over `World::World.Declare`'s own caller, not from a
|
|
233
|
+
# separate door per binding. Whole-project table-unification survey,
|
|
234
|
+
# item #13's remaining builders.
|
|
235
|
+
def self.call_translation(translation)
|
|
236
|
+
return translation if disabled? || bootstrapping? || shadow_parsing?
|
|
237
|
+
|
|
238
|
+
# `Translation`/`TranslationAggregate` are plain classes (attr_
|
|
239
|
+
# reader, not Struct — lib/hecks/bluebook/translation.rb),
|
|
240
|
+
# so neither carries a `.to_h`; `.inspect` is JSON-safe (a plain
|
|
241
|
+
# string) and, unlike a Struct's own memoization-friendly `.to_h`,
|
|
242
|
+
# doesn't need one — the small correctness cost is that its
|
|
243
|
+
# embedded object-id prefix makes two structurally-identical
|
|
244
|
+
# translations hash differently, so `verdicts` simply never
|
|
245
|
+
# cache-hits here (an efficiency loss, not a correctness one —
|
|
246
|
+
# judged fresh every time instead of memoized).
|
|
247
|
+
key = Digest::SHA256.hexdigest(translation.inspect)
|
|
248
|
+
refusals = verdicts[key] ||= TranslationJudge.new(translation).refusals
|
|
249
|
+
return translation if refusals.empty?
|
|
250
|
+
|
|
251
|
+
raise DSL::Malformed,
|
|
252
|
+
"#{translation.domain}'s translation is not well formed; #{refusals.join('; ')}"
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# THE LANGUAGE HANDS THE GRAPH BACK.
|
|
256
|
+
#
|
|
257
|
+
# This used to return the bluebook it was given — dispatch every declaration
|
|
258
|
+
# in, collect refusals, throw the records away — which is all JUDGING needs
|
|
259
|
+
# and exactly why the language could only validate. It returns what the
|
|
260
|
+
# meta-domain HOLDS instead, assembled into the graph the runtime runs. The
|
|
261
|
+
# builder's own object graph now exists only to be dispatched; nothing keeps
|
|
262
|
+
# it.
|
|
263
|
+
#
|
|
264
|
+
# `Hecks.bluebook` registers whatever comes back from here, so this one line
|
|
265
|
+
# is the difference between a language that checks a domain and a language
|
|
266
|
+
# that is the source of one.
|
|
267
|
+
#
|
|
268
|
+
# What is CACHED is the declarations, not the graph. A hash carries no Ruby
|
|
269
|
+
# classes, so a second load of the same chapter re-assembles fresh ones —
|
|
270
|
+
# which is the behaviour `Namespace.install` and `spec/construct_spec` both
|
|
271
|
+
# expect. Caching the graph would hand two boots the same classes.
|
|
272
|
+
# THE LANGUAGE IS THE SOURCE. This is the line that makes it one.
|
|
273
|
+
#
|
|
274
|
+
# `Hecks.bluebook` registers whatever comes back from here, so returning the
|
|
275
|
+
# assembled graph rather than the bluebook it was handed is the whole swap: the
|
|
276
|
+
# runtime runs what the meta-domain HOLDS. The builder's own graph exists only
|
|
277
|
+
# to be dispatched in ; nothing keeps it.
|
|
278
|
+
#
|
|
279
|
+
# It stayed unlanded for one wrong belief, worth naming because it looked so
|
|
280
|
+
# much like a wall: that the language may only hold what `to_h` carries.
|
|
281
|
+
# `ReadModel#to_h` omitted a read model's filters until 2026-08-11, so
|
|
282
|
+
# read-model filtering seemed impossible to read back —
|
|
283
|
+
# and hoisted policies lost which head declared them for the same reason.
|
|
284
|
+
# But `to_h` is a PROJECTION and the language is the
|
|
285
|
+
# SOURCE. They must agree about everything to_h spells ; they need not be the
|
|
286
|
+
# same size. Both were held even before the wire format carried them, as
|
|
287
|
+
# declarations the wire format didn't yet see.
|
|
288
|
+
#
|
|
289
|
+
# UPDATE, 2026-08-11: the wire format DID move, on purpose, for a reason
|
|
290
|
+
# unrelated to this file — a Rust-codegen task needed `wheres`/
|
|
291
|
+
# `order_by`/`limit` on the wire to compile a read model's real declared
|
|
292
|
+
# filtering, and the boundary described above was never load-bearing for
|
|
293
|
+
# THIS mechanism (`option_rows`/`filter_options` in meta_validator/
|
|
294
|
+
# readings.rb read `node.wheres`/`node.order_by`/`node.limit` off the
|
|
295
|
+
# live object directly, never off `to_h`), so extending `to_h` changed
|
|
296
|
+
# nothing here. `ReadModel#to_h` now spells all three explicitly, the
|
|
297
|
+
# same mechanism `Query#to_h` already used — purely additive, still
|
|
298
|
+
# agreeing with the language about everything it spells.
|
|
299
|
+
#
|
|
300
|
+
# What is CACHED is the declarations, not the graph. A hash carries no Ruby
|
|
301
|
+
# classes, so a second load of the same chapter assembles fresh ones — which is
|
|
302
|
+
# what `Namespace.install` and `spec/construct_spec` both expect. Caching the
|
|
303
|
+
# graph would hand two boots the same classes.
|
|
304
|
+
def self.call(bluebook)
|
|
305
|
+
return bluebook if disabled? || bootstrapping? || shadow_parsing?
|
|
306
|
+
|
|
307
|
+
if deferring?
|
|
308
|
+
deferred_chapters << bluebook.hecks_name
|
|
309
|
+
return bluebook
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
key = Digest::SHA256.hexdigest(JSON.generate(bluebook.to_h))
|
|
313
|
+
held = verdicts[key] ||= hold(bluebook)
|
|
314
|
+
|
|
315
|
+
unless held[:refusals].empty?
|
|
316
|
+
raise DSL::Malformed,
|
|
317
|
+
"#{bluebook.hecks_name} is not a well-formed bluebook; #{held[:refusals].join('; ')}"
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
Assembly.call(held[:declaration])
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Dispatch it in and READ IT BACK. A refused chapter has no declarations to
|
|
324
|
+
# read — the records are half-written by definition — so it carries refusals
|
|
325
|
+
# and nothing else.
|
|
326
|
+
def self.hold(bluebook)
|
|
327
|
+
judge = Judge.new(bluebook)
|
|
328
|
+
return { refusals: judge.refusals } unless judge.refusals.empty?
|
|
329
|
+
|
|
330
|
+
{ refusals: [], declaration: Reconstruction.of(judge.runtime, bluebook.hecks_name) }
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def self.grammar_registry
|
|
334
|
+
@grammar_registry ||= begin
|
|
335
|
+
registry = load_grammar_into(Runtime::Registry.new)
|
|
336
|
+
# Assigned BEFORE the fixpoint judge below: judging re-enters
|
|
337
|
+
# grammar_registry through fresh_runtime (judge.rb) and Plan.for
|
|
338
|
+
# (judge.rb, reconstruction.rb) — a bare ||= would still be nil
|
|
339
|
+
# while its right-hand side evaluates, and recurse forever. That
|
|
340
|
+
# reentrancy window is real: a caller landing here BEFORE the
|
|
341
|
+
# fixpoint/attach below have run sees the SAME registry object,
|
|
342
|
+
# correctly, but one still missing the attached chapters (Paging's
|
|
343
|
+
# `attaches_to` among them) — see grammar_registry_ready? below.
|
|
344
|
+
@grammar_registry = registry
|
|
345
|
+
# THE FIXPOINT MADE LOAD-BEARING. The bootstrap loaded the language
|
|
346
|
+
# raw ; now the language judges itself, its records are read back,
|
|
347
|
+
# and the ASSEMBLED graph replaces the raw one — so every bluebook
|
|
348
|
+
# judged from here on is judged by the language the language itself
|
|
349
|
+
# produced. Outside load_grammar_into on purpose : its ensure clears
|
|
350
|
+
# @bootstrapping, and call() must see bootstrapping? == false to do
|
|
351
|
+
# anything at all.
|
|
352
|
+
LANGUAGE_CHAPTERS.each { |name| registry.add_bluebook(call(registry.bluebook(name))) }
|
|
353
|
+
load_attached_grammar_into(registry)
|
|
354
|
+
# Stamped LAST, keyed by this registry's own identity rather than
|
|
355
|
+
# a bare boolean — a manual reset (fixpoint_spec.rb's own
|
|
356
|
+
# `@grammar_registry = nil`) makes @grammar_registry not equal
|
|
357
|
+
# this object_id again until a fresh build finishes, so a stale
|
|
358
|
+
# "ready" from the PREVIOUS cycle can never leak into the next.
|
|
359
|
+
@grammar_ready_for = registry.object_id
|
|
360
|
+
registry
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# A REENTRANT CALL DURING THE FIXPOINT/ATTACH WINDOW ABOVE gets a
|
|
365
|
+
# real, correctly-mutating registry object back — no infinite loop,
|
|
366
|
+
# no wrong data for THAT caller's own purposes. But anything that
|
|
367
|
+
# MEMOIZES a snapshot derived from it must not lock that snapshot
|
|
368
|
+
# in forever : this is one signal such a cache can check. Found
|
|
369
|
+
# live — SyntaxBoot.call had cached a Query keyword list missing
|
|
370
|
+
# every Paging-attached word (limit/offset/cursor/nulls) because
|
|
371
|
+
# something called it inside this exact window.
|
|
372
|
+
#
|
|
373
|
+
# SyntaxBoot.call NO LONGER USES THIS. Gating its cache on "the
|
|
374
|
+
# whole registry is finished" meant nothing was cached for the
|
|
375
|
+
# entire window, and the window is not narrow — every word routed
|
|
376
|
+
# through `word_gate_dispatch` while the language judged itself
|
|
377
|
+
# re-ran the ~284-dispatch syntax boot (42 times, 32 seconds, per
|
|
378
|
+
# process — see SyntaxBoot.call's own comment). It keys its cache
|
|
379
|
+
# on the registry's chapter set instead, which is the actual input
|
|
380
|
+
# the snapshot is a function of. Kept here for any other derived
|
|
381
|
+
# cache that genuinely needs "is the build finished" rather than
|
|
382
|
+
# "have my inputs changed".
|
|
383
|
+
def self.grammar_registry_ready?
|
|
384
|
+
@grammar_registry && @grammar_ready_for == @grammar_registry.object_id
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# ATTACHED CHAPTERS LOAD AFTER THE FIXPOINT, NOT DURING BOOTSTRAP —
|
|
388
|
+
# they are declared IN the language the language just finished
|
|
389
|
+
# judging itself through, so they are ordinary bluebooks, judged the
|
|
390
|
+
# ordinary way (`Hecks.bluebook` → `BluebookBuilder#build` →
|
|
391
|
+
# `MetaValidator.call`, `bootstrapping?` already false). A directory
|
|
392
|
+
# with nothing in it loads nothing ; this is a no-op until a chapter
|
|
393
|
+
# is added there.
|
|
394
|
+
def self.load_attached_grammar_into(registry)
|
|
395
|
+
Hecks.with_registry(registry) do
|
|
396
|
+
Dir.glob(File.join(ATTACHED_GRAMMAR_DIR, "*.bluebook")).sort.each { |file| Kernel.load(file) }
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
# THE ONE PLACE THE GRAMMAR'S OWN BOOT SEQUENCE IS SPELLED — ports, the
|
|
401
|
+
# memory/prism adapters, the (now nine-file) chapter itself, then the
|
|
402
|
+
# sibling world grammar. `grammar_registry` uses this for its memoised
|
|
403
|
+
# singleton ; anything that needs an ISOLATED registry (a spec wanting a
|
|
404
|
+
# fresh store per example, say) calls this directly instead of hand-
|
|
405
|
+
# copying the sequence.
|
|
406
|
+
#
|
|
407
|
+
# THE BOOTSTRAP GUARD LIVES HERE, not just around the singleton. Splitting
|
|
408
|
+
# the chapter into several files means each file's own `Hecks.bluebook
|
|
409
|
+
# "Bluebook"` call now runs `BluebookBuilder#build` once per file — and
|
|
410
|
+
# `MetaValidator.call` judges whatever it is handed unless `bootstrapping?`
|
|
411
|
+
# is true. A caller that loaded the grammar files by hand into its own
|
|
412
|
+
# registry, without this guard, would get each file DISPATCHED AND JUDGED
|
|
413
|
+
# ALONE the moment it loaded — and a lone file like `aggregate.bluebook`
|
|
414
|
+
# refuses immediately, since `Aggregate.Attribute` references `ValueObject`
|
|
415
|
+
# and `Aggregate.Holds` references `Entity`, both declared in later files.
|
|
416
|
+
# Every caller of the grammar must go through here for exactly that reason.
|
|
417
|
+
def self.load_grammar_into(registry)
|
|
418
|
+
@bootstrapping = true
|
|
419
|
+
Hecks.with_registry(registry) do
|
|
420
|
+
Kernel.load(File.expand_path("../ports/persistence.port", __dir__))
|
|
421
|
+
Kernel.load(File.expand_path("../ports/extraction.port", __dir__))
|
|
422
|
+
Kernel.load(File.expand_path("../adapters/driven/memory.adapter", __dir__))
|
|
423
|
+
Kernel.load(File.expand_path("../adapters/driven/prism.adapter", __dir__))
|
|
424
|
+
GRAMMAR_FILES.each { |file| Kernel.load(file) }
|
|
425
|
+
WORLD_GRAMMAR.each { |file| Kernel.load(file) }
|
|
426
|
+
HECKSAGON_GRAMMAR.each { |file| Kernel.load(file) }
|
|
427
|
+
Kernel.load(PORT_GRAMMAR)
|
|
428
|
+
Kernel.load(ADAPTER_GRAMMAR)
|
|
429
|
+
TRANSLATION_GRAMMAR.each { |file| Kernel.load(file) }
|
|
430
|
+
end
|
|
431
|
+
registry
|
|
432
|
+
ensure
|
|
433
|
+
@bootstrapping = false
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# A FRESH STORE per bluebook. The registry memoises repositories, so
|
|
437
|
+
# reusing it let every bluebook see the records of every bluebook judged
|
|
438
|
+
# before it. The parsed grammar is reused ; only the records are cleared.
|
|
439
|
+
# (During grammar_registry's own fixpoint judge this clear runs on the
|
|
440
|
+
# singleton mid-memoisation — harmless for the same reason : the grammar
|
|
441
|
+
# is what is kept, the records were never meant to survive a judging.)
|
|
442
|
+
# No `bind_runtime` here : judging dispatches by FQN and never opens the
|
|
443
|
+
# door, and binding would re-install the language's own facade constants
|
|
444
|
+
# once per judged chapter.
|
|
445
|
+
def self.fresh_runtime
|
|
446
|
+
registry = grammar_registry
|
|
447
|
+
registry.instance_variable_set(:@repositories, {})
|
|
448
|
+
Runtime::Dispatcher.new(registry)
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
end
|