hecks 2.7.0 → 2.9.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/exe/hecks +11 -0
- data/lib/hecks/adapters/driven/claude_code.rb +4 -104
- data/lib/hecks/adapters/driven/d1.rb +44 -237
- data/lib/hecks/adapters/driven/folder.rb +17 -76
- data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
- data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
- data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
- data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
- data/lib/hecks/adapters/driven/heki.rb +13 -42
- data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
- data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
- data/lib/hecks/adapters/driven/lambda.rb +10 -84
- data/lib/hecks/adapters/driven/local_storage.rb +5 -40
- data/lib/hecks/adapters/driven/memory.rb +18 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
- data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres.rb +145 -134
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +7 -30
- data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
- data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
- data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
- data/lib/hecks/adapters/driven/sqlite.rb +128 -65
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +15 -37
- data/lib/hecks/adapters/driven.rb +5 -36
- data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
- data/lib/hecks/behaviors/dsl.rb +7 -25
- data/lib/hecks/behaviors/expectations.rb +32 -142
- data/lib/hecks/behaviors/ir.rb +7 -20
- data/lib/hecks/behaviors/rspec.rb +6 -17
- data/lib/hecks/behaviors/runner.rb +10 -34
- data/lib/hecks/behaviors.rb +4 -8
- data/lib/hecks/bluebook/aggregate.rb +9 -75
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
- data/lib/hecks/bluebook/assembly/build.rb +4 -15
- data/lib/hecks/bluebook/assembly/contract.rb +15 -136
- data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
- data/lib/hecks/bluebook/assembly/marks.rb +31 -223
- data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
- data/lib/hecks/bluebook/assembly.rb +6 -45
- data/lib/hecks/bluebook/attribute.rb +12 -61
- data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
- data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
- data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
- data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
- data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
- data/lib/hecks/bluebook/behaviour/read_model.rb +38 -26
- data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
- data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
- data/lib/hecks/bluebook/capabilities.rb +17 -49
- data/lib/hecks/bluebook/chapter.rb +10 -48
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +14 -63
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +17 -65
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
- data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
- data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -189
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
- data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
- data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
- data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
- data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
- data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
- data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
- data/lib/hecks/bluebook/dsl/world_builder.rb +19 -106
- data/lib/hecks/bluebook/entity.rb +13 -71
- data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
- data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
- data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
- data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
- data/lib/hecks/bluebook/expression/resolver.rb +37 -660
- data/lib/hecks/bluebook/hexagon.rb +5 -28
- data/lib/hecks/bluebook/lifecycle.rb +5 -16
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
- data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
- data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +79 -349
- data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
- data/lib/hecks/bluebook/model_check.rb +102 -600
- data/lib/hecks/bluebook/pattern_subset.rb +11 -69
- data/lib/hecks/bluebook/process_manager.rb +13 -83
- data/lib/hecks/bluebook/project_loader.rb +3 -39
- data/lib/hecks/bluebook/project_register.rb +12 -60
- data/lib/hecks/bluebook/query.rb +9 -48
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +8 -98
- data/lib/hecks/bluebook/synthesizer.rb +13 -62
- data/lib/hecks/bluebook/translation.rb +26 -62
- data/lib/hecks/bluebook/value_object.rb +11 -36
- data/lib/hecks/bluebook.rb +5 -22
- data/lib/hecks/cache_dir.rb +84 -0
- data/lib/hecks/cli/document.rb +86 -0
- data/lib/hecks/cli/ir.rb +45 -0
- data/lib/hecks/cli/mcp.rb +31 -0
- data/lib/hecks/cli/mcp_door.rb +366 -0
- data/lib/hecks/cli/model_check.rb +203 -0
- data/lib/hecks/cli/project_cli.rb +90 -0
- data/lib/hecks/cli/project_diagrams.rb +36 -0
- data/lib/hecks/cli/run.rb +173 -0
- data/lib/hecks/cli/smoke_test.rb +45 -0
- data/lib/hecks/cli/stores.rb +53 -0
- data/lib/hecks/cli.rb +160 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
- data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
- data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
- data/lib/hecks/embryonaut_bluebook.rb +4 -71
- data/lib/hecks/facade/cli_door.rb +24 -106
- data/lib/hecks/facade/cli_runner.rb +28 -172
- data/lib/hecks/facade/command_request.rb +13 -33
- data/lib/hecks/facade/handle.rb +34 -121
- data/lib/hecks/facade/json_door.rb +31 -146
- data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
- data/lib/hecks/facade/surface/chapter.rb +10 -54
- data/lib/hecks/facade/surface.rb +8 -23
- data/lib/hecks/facade.rb +10 -25
- data/lib/hecks/forms/app.rb +15 -70
- data/lib/hecks/forms/command_form_renderer.rb +10 -29
- data/lib/hecks/forms/field_renderer.rb +17 -156
- data/lib/hecks/forms/field_shape.rb +30 -222
- data/lib/hecks/forms/html.rb +12 -47
- data/lib/hecks/forms/index_renderer.rb +1 -4
- data/lib/hecks/forms/page.rb +3 -9
- data/lib/hecks/forms/params.rb +14 -60
- data/lib/hecks/forms/port_argument.rb +2 -19
- data/lib/hecks/forms/query_form_renderer.rb +4 -79
- data/lib/hecks/forms/record_renderer.rb +8 -75
- data/lib/hecks/forms/record_table.rb +7 -50
- data/lib/hecks/forms/reference_options.rb +3 -30
- data/lib/hecks/forms/value_object_shape.rb +8 -35
- data/lib/hecks/forms.rb +5 -33
- data/lib/hecks/fqn.rb +5 -12
- data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
- data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
- data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
- data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
- data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
- data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
- data/lib/hecks/framework.rb +11 -80
- data/lib/hecks/freezer.rb +5 -39
- data/lib/hecks/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- data/lib/hecks/ir.rb +14 -83
- data/lib/hecks/language/adapter.bluebook +2 -7
- data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
- data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
- data/lib/hecks/language/bluebook/command.bluebook +27 -144
- data/lib/hecks/language/bluebook/entity.bluebook +24 -97
- data/lib/hecks/language/bluebook/policy.bluebook +16 -67
- data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
- data/lib/hecks/language/bluebook/projection.bluebook +126 -60
- data/lib/hecks/language/bluebook/query.bluebook +12 -43
- data/lib/hecks/language/bluebook/shape.bluebook +23 -73
- data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
- data/lib/hecks/language/bluebook/vocabulary.bluebook +98 -197
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
- data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
- data/lib/hecks/language/oidc.json +35 -0
- data/lib/hecks/language/port.bluebook +4 -17
- data/lib/hecks/language/translation/translation.bluebook +3 -9
- data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
- data/lib/hecks/language/world/wiring.bluebook +1 -3
- data/lib/hecks/language/world/world.bluebook +5 -7
- data/lib/hecks/literal.rb +8 -50
- data/lib/hecks/mcp_door_scope.rb +95 -0
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/ports/access_control.rb +7 -23
- data/lib/hecks/ports/agent/answers.rb +5 -23
- data/lib/hecks/ports/agent.rb +47 -172
- data/lib/hecks/ports/authentication.rb +12 -39
- data/lib/hecks/ports/authorization.rb +16 -43
- data/lib/hecks/ports/clock.rb +5 -45
- data/lib/hecks/ports/extraction.rb +7 -12
- data/lib/hecks/ports/identity_assignment.rb +8 -29
- data/lib/hecks/ports/identity_generation.rb +6 -29
- data/lib/hecks/ports/identity_resolution.rb +2 -10
- data/lib/hecks/ports/key_vault.rb +9 -45
- data/lib/hecks/ports/persistence/append_only.rb +62 -65
- data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
- data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
- data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
- data/lib/hecks/ports/persistence/plugin.rb +13 -42
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
- data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
- data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
- data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
- data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
- data/lib/hecks/ports/persistence/state_codec.rb +23 -103
- data/lib/hecks/ports/persistence.rb +7 -14
- data/lib/hecks/ports/projection.rb +42 -84
- data/lib/hecks/ports/query/in_memory.rb +10 -58
- data/lib/hecks/ports/query/ordering.rb +11 -32
- data/lib/hecks/ports/query.rb +14 -37
- data/lib/hecks/ports.rb +2 -8
- data/lib/hecks/projections/bootstrap_table.rb +11 -43
- data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
- data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
- data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
- data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
- data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
- data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
- data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
- data/lib/hecks/projections/deploy/fargate.rb +35 -202
- data/lib/hecks/projections/deploy/lambda.rb +151 -679
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
- data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
- data/lib/hecks/projections/deploy/preview/script.rb +2 -14
- data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
- data/lib/hecks/projections/deploy/preview/template.rb +2 -58
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +47 -146
- data/lib/hecks/projections/deploy/smoke.rb +4 -61
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
- data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
- data/lib/hecks/projections/deploy/template_diff.rb +5 -57
- data/lib/hecks/projections/diagrams.rb +20 -582
- data/lib/hecks/projections/glossary/html.rb +14 -130
- data/lib/hecks/projections/glossary/markdown.rb +8 -69
- data/lib/hecks/projections/glossary/mermaid.rb +9 -66
- data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
- data/lib/hecks/projections/glossary/sentences.rb +14 -159
- data/lib/hecks/projections/glossary.rb +30 -222
- data/lib/hecks/projections/ir.rb +3 -10
- data/lib/hecks/projections/model/deviations.rb +15 -42
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +9 -28
- data/lib/hecks/projections/reference.rb +9 -16
- data/lib/hecks/projections/rust_vocabulary.rb +12 -250
- data/lib/hecks/projections/shape.rb +3 -29
- data/lib/hecks/projections/statements.rb +7 -86
- data/lib/hecks/projections/vocabulary.rb +6 -22
- data/lib/hecks/projections.rb +2 -16
- data/lib/hecks/projector/cli_projector.rb +60 -375
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +46 -138
- data/lib/hecks/projector/ir_projector.rb +2 -9
- data/lib/hecks/projector/narrate_projector.rb +15 -189
- data/lib/hecks/projector/target.rb +13 -76
- data/lib/hecks/projector.rb +30 -121
- data/lib/hecks/query_specification/common/comparators.rb +5 -26
- data/lib/hecks/query_specification/common/comparison.rb +35 -143
- data/lib/hecks/query_specification/common/dsl.rb +8 -24
- data/lib/hecks/query_specification/common/null_policy.rb +20 -70
- data/lib/hecks/query_specification/common/options.rb +2 -6
- data/lib/hecks/query_specification/common/where_clause.rb +2 -10
- data/lib/hecks/query_specification/field_path.rb +37 -77
- data/lib/hecks/query_specification/hop_path.rb +12 -76
- data/lib/hecks/release/gem_pin.rb +2 -26
- data/lib/hecks/release/runner/ci_publisher.rb +93 -0
- data/lib/hecks/release/runner/commands.rb +66 -0
- data/lib/hecks/release/runner/console.rb +52 -0
- data/lib/hecks/release/runner/gem_publisher.rb +43 -0
- data/lib/hecks/release/runner/git.rb +48 -0
- data/lib/hecks/release/runner/npm_publisher.rb +75 -0
- data/lib/hecks/release/runner/preflight.rb +104 -0
- data/lib/hecks/release/runner/published.rb +55 -0
- data/lib/hecks/release/runner/tagger.rb +80 -0
- data/lib/hecks/release/runner.rb +215 -0
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +12 -36
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/aggregate_lock.rb +8 -33
- data/lib/hecks/runtime/boot_gates.rb +3 -18
- data/lib/hecks/runtime/caller.rb +19 -59
- data/lib/hecks/runtime/capability_graph.rb +5 -21
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
- data/lib/hecks/runtime/command_interpreter.rb +125 -307
- data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
- data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
- data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
- data/lib/hecks/runtime/command_rules/emission.rb +8 -21
- data/lib/hecks/runtime/command_rules/references.rb +23 -202
- data/lib/hecks/runtime/dependency_planning.rb +30 -102
- data/lib/hecks/runtime/dispatcher.rb +82 -241
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +65 -220
- data/lib/hecks/runtime/errors.rb +32 -115
- data/lib/hecks/runtime/event.rb +3 -25
- data/lib/hecks/runtime/identity.rb +18 -154
- data/lib/hecks/runtime/instance.rb +18 -150
- data/lib/hecks/runtime/interpreting.rb +11 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +48 -186
- data/lib/hecks/runtime/outbox.rb +38 -261
- data/lib/hecks/runtime/policy_interpreter.rb +38 -200
- data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
- data/lib/hecks/runtime/query_interpreter.rb +45 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
- data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
- data/lib/hecks/runtime/reference_hop.rb +8 -81
- data/lib/hecks/runtime/refusal_wording.rb +8 -89
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +52 -225
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +103 -244
- data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
- data/lib/hecks/runtime/routing.rb +4 -15
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
- data/lib/hecks/runtime/saga_interpreter.rb +79 -263
- data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
- data/lib/hecks/runtime/tenant_check.rb +6 -68
- data/lib/hecks/runtime/tenant_scope.rb +3 -28
- data/lib/hecks/runtime/value/admission.rb +16 -67
- data/lib/hecks/runtime/value/coercion.rb +155 -641
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value.rb +12 -84
- data/lib/hecks/runtime.rb +17 -65
- data/lib/hecks/storehouse.rb +146 -567
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- data/lib/hecks/vendoring/git_environment.rb +2 -8
- data/lib/hecks/vendoring/git_source.rb +4 -10
- data/lib/hecks/vendoring.rb +7 -46
- data/lib/hecks/version.rb +3 -14
- data/lib/hecks/vocabulary.rb +5 -0
- data/lib/hecks.rb +32 -82
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
- metadata +30 -59
- data/lib/hecks/bench/cli.rb +0 -74
- data/lib/hecks/bench/environment.rb +0 -100
- data/lib/hecks/bench/postgres_probe.rb +0 -41
- data/lib/hecks/bench/report.rb +0 -116
- data/lib/hecks/bench/ruby_runner.rb +0 -151
- data/lib/hecks/bench/run.rb +0 -55
- data/lib/hecks/bench/rust_runner.rb +0 -165
- data/lib/hecks/bench/stats.rb +0 -75
- data/lib/hecks/bench/suite.rb +0 -172
- data/lib/hecks/bench/workload.rb +0 -137
- data/lib/hecks/bench.rb +0 -28
- data/lib/hecks/codemod.rb +0 -443
- data/lib/hecks/corpus.rb +0 -479
- data/lib/hecks/doc/reference.rb +0 -585
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
- data/lib/hecks/fuzzing/combination_miner.rb +0 -178
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
- data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
- data/lib/hecks/fuzzing/differential.rb +0 -192
- data/lib/hecks/fuzzing/domain_generator.rb +0 -871
- data/lib/hecks/fuzzing/era_boundary.rb +0 -154
- data/lib/hecks/fuzzing/form_census.rb +0 -285
- data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
- data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
- data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
- data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
- data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
- data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
- data/lib/hecks/fuzzing/properties/guards.rb +0 -398
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
- data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
- data/lib/hecks/fuzzing/properties/querying.rb +0 -260
- data/lib/hecks/fuzzing/properties.rb +0 -267
- data/lib/hecks/fuzzing/qa_settings.rb +0 -164
- data/lib/hecks/fuzzing/replay.rb +0 -930
- data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
- data/lib/hecks/fuzzing/self_consistency.rb +0 -825
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
- data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
- data/lib/hecks/fuzzing/shrinker.rb +0 -309
- data/lib/hecks/fuzzing/structural_skips.rb +0 -53
- data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
- data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
- data/lib/hecks/fuzzing/value_generator.rb +0 -340
- data/lib/hecks/fuzzing.rb +0 -23
- data/lib/hecks/grammar/evolve.rb +0 -524
- data/lib/hecks/query_ir.rb +0 -482
|
@@ -4,67 +4,14 @@ require_relative "resolver"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
6
|
module Expression
|
|
7
|
-
# Walks the
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# grammar") and emits plain, JSON-serializable Ruby Hashes, tagged
|
|
11
|
-
# by `"op"`.
|
|
12
|
-
#
|
|
13
|
-
# ## Why this exists beside `rust/project/expr_emitter.rb`
|
|
14
|
-
#
|
|
15
|
-
# The same two-method walk `rust/project/expr_emitter.rb`'s own
|
|
16
|
-
# `emit_bool`/`emit_resolver` already do, over the same AST — that
|
|
17
|
-
# file's own methods build Rust source-code strings for `rust/
|
|
18
|
-
# project`'s codegen (`rust/src/kernel::expr::Expr` literals, baked
|
|
19
|
-
# into a generated domain's own compiled binary); this builds data
|
|
20
|
-
# instead, for a genuinely different consumer with a genuinely
|
|
21
|
-
# different constraint: `rust/host` can never link the `rust`
|
|
22
|
-
# (kernel) crate at all (a real, load-bearing build constraint —
|
|
23
|
-
# `reference_validate.rs`'s own header has the full reasoning: one
|
|
24
|
-
# path dependency would statically bake every domain's generated
|
|
25
|
-
# dispatch code into every Lambda binary), so a value object's own
|
|
26
|
-
# `invariant` predicate has to travel as something `rust/host` can
|
|
27
|
-
# deserialize and interpret itself, at runtime, from `ir.json` — the
|
|
28
|
-
# exact same relationship `rust/project`'s own `Expr` literals
|
|
29
|
-
# already have to the compiled kernel, one layer further out.
|
|
30
|
-
#
|
|
31
|
-
# ## Why it lives in core `lib/hecks`, not `rust/project/`
|
|
32
|
-
#
|
|
33
|
-
# `rust/project.rb` is a separate, downstream toolchain
|
|
34
|
-
# (`lib/hecks/projector.rb`'s own header: "a whole separate Ruby
|
|
35
|
-
# program"), never `require`d by core `lib/hecks/bluebook/*.rb`
|
|
36
|
-
# files (confirmed: no core file does). `value_object.rb`'s own
|
|
37
|
-
# `invariants:` IR emission needs this for every domain's ordinary
|
|
38
|
-
# `to_h`/`ir.json` export — golden fixtures, `hecks-parse`'s parity
|
|
39
|
-
# comparisons, and any deploy artifact, not only a `bin/project_rust`
|
|
40
|
-
# run — so it belongs beside `Evaluator`/`Resolver` themselves, not
|
|
41
|
-
# bolted onto a tool that only sometimes runs.
|
|
42
|
-
#
|
|
43
|
-
# ## Complete, not corpus-scoped
|
|
44
|
-
#
|
|
45
|
-
# Every node this grammar admits gets a real arm, the identical
|
|
46
|
-
# "raise, don't silently drop" discipline `expr_emitter.rb`'s own
|
|
47
|
-
# `emit_bool`/`emit_resolver` already hold to — even though, as of
|
|
48
|
-
# this writing, no real corpus value object invariant exercises
|
|
49
|
-
# `Include`/`Modulo`/`BlockPredicate`/`Find`/`Array`/`MatchesRegex`/
|
|
50
|
-
# `Presence`/`Assignment`/`Split`/`StartsWith`/`EndsWith`/`First`/
|
|
51
|
-
# `Last` (only `given`/`ensures` clauses do, elsewhere in the corpus
|
|
52
|
-
# — a different consumer of this same grammar). `rust/host/src/
|
|
53
|
-
# expr_json.rs`'s own header names exactly which of these its
|
|
54
|
-
# interpreter evaluates for real today versus refuses cleanly — a
|
|
55
|
-
# narrower, deliberate, documented boundary on the interpreting
|
|
56
|
-
# side, not on this emitting side: an author is free to write any
|
|
57
|
-
# real expression in a value object's own `invariant`, and this
|
|
58
|
-
# always emits it faithfully; whether `rust/host` can yet check it
|
|
59
|
-
# at mint time is that file's own question to answer, not this
|
|
60
|
-
# one's to pre-empt by refusing to even try.
|
|
7
|
+
# Walks the Evaluator/Resolver AST and emits JSON-serializable Hashes tagged by `"op"`.
|
|
8
|
+
# Lives in core because rust/host cannot link the kernel crate and interprets this from
|
|
9
|
+
# ir.json.
|
|
61
10
|
module AstJson
|
|
62
11
|
module_function
|
|
63
12
|
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
# not know instead of guessing. A new node kind is a new entry
|
|
67
|
-
# here, a new arm below, and a new arm in every reader.
|
|
13
|
+
# The closed roster of `"op"` tags. A new node kind needs an entry here and an arm in
|
|
14
|
+
# each walker and reader; unhandled nodes raise rather than being dropped.
|
|
68
15
|
OPS = %w[
|
|
69
16
|
or and not compare include
|
|
70
17
|
int float str bool nil array lookup
|
|
@@ -73,48 +20,20 @@ module Hecks
|
|
|
73
20
|
matches_regex presence assignment split starts_with ends_with
|
|
74
21
|
].freeze
|
|
75
22
|
|
|
76
|
-
# One rule row,
|
|
77
|
-
# canonical text (what every reader has always had) plus the
|
|
78
|
-
# structured form, derived from the same text. `ast` is a pure
|
|
79
|
-
# function of `canonical`: the IR carries both so a reader that
|
|
80
|
-
# only displays keeps the text, and a reader that evaluates never
|
|
81
|
-
# re-parses it.
|
|
82
|
-
#
|
|
83
|
-
# @param rule [Bluebook::Given, Bluebook::Invariant] the rule to
|
|
84
|
-
# render; `rule.ast` is used when already computed, otherwise
|
|
85
|
-
# `rule.canonical` is parsed and emitted fresh
|
|
86
|
-
# @return [Hash{Symbol => Object}] `:description` (`String`),
|
|
87
|
-
# `:canonical` (`String`), and `:ast` (`Hash`, the JSON AST)
|
|
23
|
+
# One rule row: description, canonical text, and the AST derived from that text.
|
|
88
24
|
def rule_row(rule)
|
|
89
25
|
{ description: rule.description, canonical: rule.canonical, ast: rule.ast || emit_predicate(rule.canonical) }
|
|
90
26
|
end
|
|
91
27
|
|
|
92
28
|
# Parses `canonical` and emits its JSON AST in one step.
|
|
93
|
-
#
|
|
94
|
-
# @param canonical [String] a canonical predicate source string
|
|
95
|
-
# @return [Hash] the JSON-shaped boolean AST, tagged by `"op"`
|
|
96
29
|
def emit_predicate(canonical)
|
|
97
30
|
emit_bool(Evaluator.parse(canonical))
|
|
98
31
|
end
|
|
99
32
|
|
|
100
|
-
#
|
|
101
|
-
# pattern
|
|
102
|
-
# attribute's own `pattern:` already is (`AttributeCollector#
|
|
103
|
-
# refuse_unshared_pattern`): a regex whose meaning depends on the
|
|
104
|
-
# engine reading it is a defect in the bluebook, refused at build.
|
|
105
|
-
# Walks the emitted AST, so every rule site (givens, ensures,
|
|
106
|
-
# invariants, preconditions, a policy's where) gets the one check.
|
|
33
|
+
# Holds every `.match?` pattern a rule carries to `PatternSubset`, as an attribute's
|
|
34
|
+
# `pattern:` is; walking the AST gives every rule site the one check.
|
|
107
35
|
#
|
|
108
|
-
# @
|
|
109
|
-
# or `emit_bool`
|
|
110
|
-
# @param owner [String] the declaring bluebook's name, for the
|
|
111
|
-
# refusal message
|
|
112
|
-
# @param word [String] the rule's own kind and description
|
|
113
|
-
# (such as `"given \"total is positive\""`), for the refusal
|
|
114
|
-
# message
|
|
115
|
-
# @return [Hash] `ast`, unchanged
|
|
116
|
-
# @raise [Bluebook::DSL::Malformed] if any `matches_regex` node's
|
|
117
|
-
# pattern uses a construct `PatternSubset` does not admit
|
|
36
|
+
# @raise [Bluebook::DSL::Malformed] if a `matches_regex` pattern is outside `PatternSubset`
|
|
118
37
|
def refuse_unshared_patterns!(ast, owner:, word:)
|
|
119
38
|
return ast if Hecks::Bluebook::MetaValidator.shadow_parsing? # frozen era text is history
|
|
120
39
|
|
|
@@ -132,30 +51,18 @@ module Hecks
|
|
|
132
51
|
end
|
|
133
52
|
|
|
134
53
|
# What a `lookup` path can never contain: a call's parentheses, or an argument list's
|
|
135
|
-
# commas and spaces.
|
|
136
|
-
# call with arguments that the grammar has no node for.
|
|
54
|
+
# commas and spaces.
|
|
137
55
|
#
|
|
138
|
-
# A bare `?` is deliberately not in the set
|
|
139
|
-
#
|
|
140
|
-
# declare it, so refusing it would stop them loading.
|
|
56
|
+
# A bare `?` is deliberately not in the set: `x.nil?` also parses as a lookup, loads, and
|
|
57
|
+
# evaluates without raising, and real bluebooks declare it.
|
|
141
58
|
UNRESOLVABLE_PATH = /[(),\s]/
|
|
142
59
|
private_constant :UNRESOLVABLE_PATH
|
|
143
60
|
|
|
144
|
-
# Refuses a rule that calls a method the
|
|
145
|
-
#
|
|
146
|
-
# An unrecognised call such as `value.between?(100, 599)` parses as a `lookup` whose path
|
|
147
|
-
# names an attribute that can never exist, so it loads and then fails on the first
|
|
148
|
-
# dispatch. Refusing it here moves the failure to build time, where the author is looking.
|
|
149
|
-
# The supported spellings (comparisons joined with `&&`/`||`, `.positive?`, `.empty?`,
|
|
150
|
-
# `.include?`, ...) are the rows of `projection.json`.
|
|
61
|
+
# Refuses a rule that calls a method the grammar lacks, such as `value.between?(100, 599)`.
|
|
62
|
+
# It would parse as a `lookup` of an attribute that cannot exist and fail on first dispatch.
|
|
151
63
|
#
|
|
152
|
-
# @
|
|
153
|
-
#
|
|
154
|
-
# @param word [String] the rule's own kind and description (such as
|
|
155
|
-
# `"invariant \"a status code\""`), for the refusal message
|
|
156
|
-
# @return [Hash] `ast`, unchanged
|
|
157
|
-
# @raise [Bluebook::DSL::Malformed] if any `lookup` node's path contains a parenthesis,
|
|
158
|
-
# comma or whitespace
|
|
64
|
+
# @raise [Bluebook::DSL::Malformed] if a `lookup` path contains a parenthesis, comma or
|
|
65
|
+
# space
|
|
159
66
|
def refuse_unresolvable_lookups!(ast, owner:, word:)
|
|
160
67
|
return ast if Hecks::Bluebook::MetaValidator.shadow_parsing? # frozen era text is history
|
|
161
68
|
|
|
@@ -174,13 +81,7 @@ module Hecks
|
|
|
174
81
|
ast
|
|
175
82
|
end
|
|
176
83
|
|
|
177
|
-
# Every name a rule resolves at its root
|
|
178
|
-
# each `lookup` path, unique, in first-seen order.
|
|
179
|
-
#
|
|
180
|
-
# @param ast [Hash] a JSON AST node, as emitted by `emit_predicate`
|
|
181
|
-
# or `emit_bool`
|
|
182
|
-
# @return [Array<String>] each `lookup` node's root name, unique,
|
|
183
|
-
# in first-seen order
|
|
84
|
+
# Every name a rule resolves at its root: the first segment of each `lookup` path, unique.
|
|
184
85
|
def lookup_heads(ast)
|
|
185
86
|
heads = []
|
|
186
87
|
each_node(ast) do |node|
|
|
@@ -189,13 +90,7 @@ module Hecks
|
|
|
189
90
|
heads.uniq
|
|
190
91
|
end
|
|
191
92
|
|
|
192
|
-
# Visits `node` and every Hash
|
|
193
|
-
#
|
|
194
|
-
# @param node [Hash, Array, Object] a JSON AST node, or an Array or
|
|
195
|
-
# scalar value found while walking one; a scalar is a silent no-op
|
|
196
|
-
# @yieldparam node [Hash] each Hash node reached, including `node`
|
|
197
|
-
# itself
|
|
198
|
-
# @return [void]
|
|
93
|
+
# Visits `node` and every Hash nested inside it, depth-first.
|
|
199
94
|
def each_node(node, &block)
|
|
200
95
|
case node
|
|
201
96
|
when ::Hash
|
|
@@ -207,16 +102,6 @@ module Hecks
|
|
|
207
102
|
end
|
|
208
103
|
|
|
209
104
|
# Emits the JSON form of one boolean-position Evaluator node.
|
|
210
|
-
#
|
|
211
|
-
# @param node [Bluebook::Expression::Evaluator::Or,
|
|
212
|
-
# Bluebook::Expression::Evaluator::And,
|
|
213
|
-
# Bluebook::Expression::Evaluator::Not,
|
|
214
|
-
# Bluebook::Expression::Evaluator::Compare,
|
|
215
|
-
# Bluebook::Expression::Evaluator::Include,
|
|
216
|
-
# Bluebook::Expression::Evaluator::Resolve] the boolean-position
|
|
217
|
-
# AST node to emit
|
|
218
|
-
# @return [Hash] the JSON AST for `node`, tagged by `"op"`
|
|
219
|
-
# @raise [RuntimeError] if `node` is not one of the handled classes
|
|
220
105
|
def emit_bool(node)
|
|
221
106
|
case node
|
|
222
107
|
when Evaluator::Or then { "op" => "or", "left" => emit_bool(node.left), "right" => emit_bool(node.right) }
|
|
@@ -236,36 +121,16 @@ module Hecks
|
|
|
236
121
|
end
|
|
237
122
|
|
|
238
123
|
# Emits the JSON form of one comparison operator.
|
|
239
|
-
#
|
|
240
|
-
# @param comparator [Bluebook::Expression::Evaluator::Operator] the
|
|
241
|
-
# operator to emit
|
|
242
|
-
# @return [Hash{String => Boolean}] `"less_than"`, `"equal"`, and
|
|
243
|
-
# `"negated"`, exactly as `comparator` carries them
|
|
244
124
|
def emit_comparison(comparator)
|
|
245
125
|
{ "less_than" => comparator.compares_less_than, "equal" => comparator.compares_equal, "negated" => comparator.negated }
|
|
246
126
|
end
|
|
247
127
|
|
|
248
|
-
#
|
|
249
|
-
# `
|
|
250
|
-
# reasoning (a literal array haystack has no `Expr::Include`-
|
|
251
|
-
# representable shape on either target, kernel or host, so both
|
|
252
|
-
# rewrite it identically into an or-of-equalities at emission
|
|
253
|
-
# time rather than carrying a shape neither interpreter could
|
|
254
|
-
# evaluate). A non-literal haystack still emits `include`
|
|
255
|
-
# unchanged.
|
|
128
|
+
# Mirrors `expr_emitter.rb`'s `emit_include`: no target can represent a literal-array
|
|
129
|
+
# haystack in `include`, so it is rewritten to an or of equalities at emission.
|
|
256
130
|
EQ = Evaluator::OPERATORS.find { |op| op.symbol == "==" }
|
|
257
131
|
private_constant :EQ
|
|
258
132
|
|
|
259
|
-
# Emits
|
|
260
|
-
#
|
|
261
|
-
# @param node [Bluebook::Expression::Evaluator::Include] the
|
|
262
|
-
# `include` node to emit
|
|
263
|
-
# @return [Hash] the JSON AST: `{"op" => "include", ...}` for a
|
|
264
|
-
# non-literal haystack, `{"op" => "bool", "value" => false}` for
|
|
265
|
-
# an empty literal array haystack, or an `"or"`-of-`"compare"`
|
|
266
|
-
# tree of equalities for a non-empty literal array haystack
|
|
267
|
-
# @raise [RuntimeError] if `node.haystack`, `node.needle`, or one of
|
|
268
|
-
# the haystack's literal elements is an unhandled resolver node
|
|
133
|
+
# Emits one `include` node; an empty literal haystack is `false`.
|
|
269
134
|
def emit_include(node)
|
|
270
135
|
return { "op" => "include", "haystack" => emit_resolver(node.haystack), "needle" => emit_resolver(node.needle) } \
|
|
271
136
|
unless node.haystack.is_a?(Resolver::ArrayLiteral)
|
|
@@ -279,38 +144,8 @@ module Hecks
|
|
|
279
144
|
equalities.reduce { |left, right| { "op" => "or", "left" => left, "right" => right } }
|
|
280
145
|
end
|
|
281
146
|
|
|
282
|
-
# One case arm per Resolver node type
|
|
283
|
-
#
|
|
284
|
-
# ("every node this grammar admits gets a real arm"); splitting it
|
|
285
|
-
# into several methods would hide whether the set is still
|
|
286
|
-
# exhaustive instead of making that visible at a glance.
|
|
287
|
-
#
|
|
288
|
-
# @param node [Bluebook::Expression::Resolver::IntegerLiteral,
|
|
289
|
-
# Bluebook::Expression::Resolver::FloatLiteral,
|
|
290
|
-
# Bluebook::Expression::Resolver::StringLiteral,
|
|
291
|
-
# Bluebook::Expression::Resolver::BoolLiteral,
|
|
292
|
-
# Bluebook::Expression::Resolver::NilLiteral,
|
|
293
|
-
# Bluebook::Expression::Resolver::Lookup,
|
|
294
|
-
# Bluebook::Expression::Resolver::Addition,
|
|
295
|
-
# Bluebook::Expression::Resolver::SignTest,
|
|
296
|
-
# Bluebook::Expression::Resolver::Empty,
|
|
297
|
-
# Bluebook::Expression::Resolver::ToS,
|
|
298
|
-
# Bluebook::Expression::Resolver::Modulo,
|
|
299
|
-
# Bluebook::Expression::Resolver::Size,
|
|
300
|
-
# Bluebook::Expression::Resolver::BlockPredicate,
|
|
301
|
-
# Bluebook::Expression::Resolver::Find,
|
|
302
|
-
# Bluebook::Expression::Resolver::ArrayLiteral,
|
|
303
|
-
# Bluebook::Expression::Resolver::MatchesRegex,
|
|
304
|
-
# Bluebook::Expression::Resolver::Presence,
|
|
305
|
-
# Bluebook::Expression::Resolver::Assignment,
|
|
306
|
-
# Bluebook::Expression::Resolver::Split,
|
|
307
|
-
# Bluebook::Expression::Resolver::StartsWith,
|
|
308
|
-
# Bluebook::Expression::Resolver::EndsWith,
|
|
309
|
-
# Bluebook::Expression::Resolver::First,
|
|
310
|
-
# Bluebook::Expression::Resolver::Last] the value-position AST
|
|
311
|
-
# node to emit
|
|
312
|
-
# @return [Hash] the JSON AST for `node`, tagged by `"op"`
|
|
313
|
-
# @raise [RuntimeError] if `node` is not one of the handled classes
|
|
147
|
+
# One case arm per Resolver node type, kept in one method so exhaustiveness shows at a
|
|
148
|
+
# glance.
|
|
314
149
|
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
315
150
|
def emit_resolver(node)
|
|
316
151
|
case node
|
|
@@ -319,8 +154,7 @@ module Hecks
|
|
|
319
154
|
when Resolver::StringLiteral then { "op" => "str", "value" => node.value }
|
|
320
155
|
when Resolver::BoolLiteral then { "op" => "bool", "value" => node.value }
|
|
321
156
|
when Resolver::NilLiteral then { "op" => "nil" }
|
|
322
|
-
# `path`
|
|
323
|
-
# a dotted string a reader would have to split by its own rule.
|
|
157
|
+
# Segments, as `find.path` has, so a reader need not split a dotted string.
|
|
324
158
|
when Resolver::Lookup then { "op" => "lookup", "path" => node.path.split(".") }
|
|
325
159
|
when Resolver::Addition then { "op" => "add", "left" => emit_resolver(node.left), "right" => emit_resolver(node.right) }
|
|
326
160
|
when Resolver::SignTest
|
|
@@ -4,41 +4,20 @@ require_relative "resolver"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
6
|
module Expression
|
|
7
|
-
# The inverse of `AstJson
|
|
8
|
-
#
|
|
9
|
-
# Structs `Evaluator.parse` builds from `canonical`. This is how the
|
|
10
|
-
# runtime evaluates a rule without re-parsing its text: the one
|
|
11
|
-
# parse happened at DSL-build time, behind `AstJson`; dispatch walks
|
|
12
|
-
# the structured form (`Evaluator.call_rule`), and `canonical` stays
|
|
13
|
-
# what it always displayed as — text for humans and refusal wording.
|
|
14
|
-
#
|
|
15
|
-
# Written as the obvious mirror of `ast_json.rb`'s two walkers, arm
|
|
16
|
-
# for arm, so a new op there is a new arm here and
|
|
17
|
-
# spec/expression_ast_spec.rb's roster contract fails until it
|
|
18
|
-
# lands. Promoted from spec/support once the runtime switched —
|
|
19
|
-
# the spec proved the round trip first, then the runtime adopted it.
|
|
20
|
-
#
|
|
21
|
-
# One deliberate asymmetry, inherited: `AstJson` rewrites a
|
|
22
|
-
# literal-array `.include?` into an or of equalities (see
|
|
23
|
-
# `emit_include`), so reading never produces an `Include` over an
|
|
24
|
-
# `ArrayLiteral`. Evaluation is unchanged by that rewrite, which is
|
|
25
|
-
# exactly what the equivalence spec pins.
|
|
7
|
+
# The inverse of `AstJson`: reads a rule row's `"op"`-tagged `ast` back into the
|
|
8
|
+
# `Evaluator`/`Resolver` nodes, so dispatch evaluates a rule without re-parsing its text.
|
|
26
9
|
module AstReader
|
|
27
10
|
module_function
|
|
28
11
|
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
#
|
|
12
|
+
# Arm for arm with `AstJson`: a new op there fails spec/expression_ast_spec.rb until added
|
|
13
|
+
# here.
|
|
14
|
+
# A literal-array `.include?` is emitted as an or of equalities, so it never reads back as
|
|
15
|
+
# an `Include` over an `ArrayLiteral`; evaluation is unchanged.
|
|
16
|
+
|
|
17
|
+
# Reads a whole predicate's `"op"`-tagged JSON back into its AST.
|
|
35
18
|
def read_predicate(json) = read_bool(json)
|
|
36
19
|
|
|
37
20
|
# Reads one boolean/comparison node, recursing into its own children.
|
|
38
|
-
#
|
|
39
|
-
# @param json [Hash{String => Object}] the `"op"`-tagged JSON for one node
|
|
40
|
-
# @return [Evaluator::Or, Evaluator::And, Evaluator::Not, Evaluator::Compare,
|
|
41
|
-
# Evaluator::Include, Evaluator::Resolve] the boolean/comparison AST node
|
|
42
21
|
def read_bool(json)
|
|
43
22
|
case json.fetch("op")
|
|
44
23
|
when "or" then Evaluator::Or.new(left: read_bool(json["left"]), right: read_bool(json["right"]))
|
|
@@ -53,14 +32,8 @@ module Hecks
|
|
|
53
32
|
end
|
|
54
33
|
end
|
|
55
34
|
|
|
56
|
-
# The
|
|
57
|
-
#
|
|
58
|
-
# have chosen, because the roster (`expression/projection.json`)
|
|
59
|
-
# holds one symbol per triple.
|
|
60
|
-
#
|
|
61
|
-
# @param cmp [Hash{String => Boolean}] `{"less_than"=>, "equal"=>, "negated"=>}`
|
|
62
|
-
# @return [Evaluator::Operator] the operator carrying that exact triple
|
|
63
|
-
# @raise [RuntimeError] if no operator in `Evaluator::OPERATORS` has that triple
|
|
35
|
+
# The roster (`expression/projection.json`) holds one symbol per flag triple,
|
|
36
|
+
# so the triple identifies the operator `parse` would have chosen.
|
|
64
37
|
def operator(cmp)
|
|
65
38
|
Evaluator::OPERATORS.find do |op|
|
|
66
39
|
op.compares_less_than == cmp.fetch("less_than") &&
|
|
@@ -70,14 +43,6 @@ module Hecks
|
|
|
70
43
|
end
|
|
71
44
|
|
|
72
45
|
# Reads one dotted/arithmetic leaf node, recursing into its own children.
|
|
73
|
-
#
|
|
74
|
-
# @param json [Hash{String => Object}] the `"op"`-tagged JSON for one leaf node
|
|
75
|
-
# @return [Object] a `Resolver` AST node — one of `IntegerLiteral`, `FloatLiteral`,
|
|
76
|
-
# `StringLiteral`, `BoolLiteral`, `NilLiteral`, `ArrayLiteral`, `Lookup`,
|
|
77
|
-
# `Addition`, `SignTest`, `Empty`, `ToS`, `Modulo`, `Size`, `First`, `Last`,
|
|
78
|
-
# `BlockPredicate`, `Find`, `MatchesRegex`, `Presence`, `Assignment`, `Split`,
|
|
79
|
-
# `StartsWith`, or `EndsWith`
|
|
80
|
-
# @raise [RuntimeError] if `json["op"]` names no known resolver op
|
|
81
46
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity -- one arm
|
|
82
47
|
# per AstJson op is the point; splitting the case would hide the roster.
|
|
83
48
|
def read_resolver(json)
|
|
@@ -118,15 +83,8 @@ module Hecks
|
|
|
118
83
|
end
|
|
119
84
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
120
85
|
|
|
121
|
-
# `SignTest#test` is only wording
|
|
122
|
-
#
|
|
123
|
-
# vocabulary so a rebuilt node refuses with the same message the
|
|
124
|
-
# parsed one would.
|
|
125
|
-
#
|
|
126
|
-
# @param operator [Evaluator::Operator] the comparison operator a sign test's own
|
|
127
|
-
# triple resolved to
|
|
128
|
-
# @return [String] the sign test's own vocabulary name, or `operator.symbol` when
|
|
129
|
-
# no sign test shares its triple
|
|
86
|
+
# `SignTest#test` is only refusal wording; recovering it keeps a rebuilt node's message
|
|
87
|
+
# identical to the parsed one's.
|
|
130
88
|
def sign_test_name(operator)
|
|
131
89
|
Resolver::SIGN_TEST_OPERATORS.key(operator.symbol) || operator.symbol
|
|
132
90
|
end
|
|
@@ -4,35 +4,21 @@ require_relative "../../vocabulary"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Bluebook
|
|
6
6
|
module Expression
|
|
7
|
-
# Rewrites
|
|
8
|
-
#
|
|
9
|
-
# folding admitted synonyms (e.g. `.length` → `.size`) via the
|
|
10
|
-
# `RULES` table projected from the grammar chapter, so two byte-
|
|
11
|
-
# different but equivalent predicates compare and cache identically.
|
|
12
|
-
# Never touches text inside quoted string literals
|
|
13
|
-
# (`map_outside_strings`).
|
|
7
|
+
# Rewrites predicate source into one canonical spelling (whitespace, admitted synonyms)
|
|
8
|
+
# so equivalent predicates compare and hash alike. Quoted string literals are left alone.
|
|
14
9
|
module CanonicalForm
|
|
15
10
|
Rule = Struct.new(:strategy, :source_token, :replacement, :boundary, :position, keyword_init: true)
|
|
16
11
|
|
|
17
12
|
STRATEGIES = Hecks::Vocabulary.fetch("NormalisationStrategy")
|
|
18
13
|
|
|
19
|
-
#
|
|
20
|
-
# from the grammar chapter by bin/expression_projection exactly as
|
|
21
|
-
# the evaluator's operator table is. See Evaluator::PROJECTION for
|
|
22
|
-
# why a projection rather than a boot.
|
|
14
|
+
# Normalisation rules projected from the grammar chapter by bin/expression_projection.
|
|
23
15
|
RULES = JSON.parse(
|
|
24
16
|
File.read(File.join(__dir__, "projection.json")), symbolize_names: true
|
|
25
17
|
).fetch(:normalisations).map { |row| Rule.new(**row) }.freeze
|
|
26
18
|
|
|
27
19
|
module_function
|
|
28
20
|
|
|
29
|
-
#
|
|
30
|
-
# embedding in a bluebook's read-back contract.
|
|
31
|
-
#
|
|
32
|
-
# @return [Array<Hash{Symbol => String}>] one hash per rule, in
|
|
33
|
-
# declared `position` order, with keys `:strategy`,
|
|
34
|
-
# `:source_token`, `:replacement`, `:boundary`, and `:position`
|
|
35
|
-
# (stringified)
|
|
21
|
+
# The normalisation rules as plain Hashes, in `position` order, for a read-back contract.
|
|
36
22
|
def table
|
|
37
23
|
RULES.sort_by(&:position).map do |rule|
|
|
38
24
|
{
|
|
@@ -45,25 +31,11 @@ module Hecks
|
|
|
45
31
|
end
|
|
46
32
|
end
|
|
47
33
|
|
|
48
|
-
#
|
|
49
|
-
# admitted `RULES` entry, in declared `position` order.
|
|
50
|
-
#
|
|
51
|
-
# @param source [String] the raw predicate source to normalise
|
|
52
|
-
# @return [String] the canonicalised text, stripped of leading and
|
|
53
|
-
# trailing whitespace
|
|
34
|
+
# Applies every `RULES` entry to `source` in `position` order; returns the stripped result.
|
|
54
35
|
def apply(source)
|
|
55
36
|
RULES.sort_by(&:position).reduce(source.to_s) { |text, rule| step(text, rule) }.strip
|
|
56
37
|
end
|
|
57
38
|
|
|
58
|
-
# Applies one normalisation `rule` to `text`, dispatching on the
|
|
59
|
-
# rule's strategy.
|
|
60
|
-
#
|
|
61
|
-
# @param text [String] the source text (or string-literal segment) to
|
|
62
|
-
# transform
|
|
63
|
-
# @param rule [Rule] the normalisation rule to apply
|
|
64
|
-
# @return [String] `text` after the rule is applied
|
|
65
|
-
# @raise [ArgumentError] if `rule.strategy` names a strategy this
|
|
66
|
-
# method does not implement
|
|
67
39
|
def step(text, rule)
|
|
68
40
|
case rule.strategy
|
|
69
41
|
when "collapse_whitespace" then map_outside_strings(text) { |segment| segment.gsub(/\s+/, " ") }
|
|
@@ -73,14 +45,6 @@ module Hecks
|
|
|
73
45
|
end
|
|
74
46
|
end
|
|
75
47
|
|
|
76
|
-
# Substitutes every occurrence of `rule.source_token` in `text` with
|
|
77
|
-
# `rule.replacement`, honoring the rule's word-boundary setting.
|
|
78
|
-
#
|
|
79
|
-
# @param text [String] the source text (or string-literal segment) to
|
|
80
|
-
# transform
|
|
81
|
-
# @param rule [Rule] the `"replace"`-strategy rule supplying the
|
|
82
|
-
# token, replacement, and boundary
|
|
83
|
-
# @return [String] `text` with the substitution applied
|
|
84
48
|
def replace(text, rule)
|
|
85
49
|
map_outside_strings(text) do |segment|
|
|
86
50
|
if rule.boundary == "none"
|
|
@@ -91,30 +55,9 @@ module Hecks
|
|
|
91
55
|
end
|
|
92
56
|
end
|
|
93
57
|
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
# quote-aware: run it quote-blind and `"a b"` collapses to `"a b"`,
|
|
98
|
-
# `"a.length"` folds to `"a.size"`, just as readily as the real
|
|
99
|
-
# source outside the quotes — silently rewriting what a predicate
|
|
100
|
-
# compares a string attribute against, not merely how the predicate
|
|
101
|
-
# itself is spelled. A canonical string literal's contents are data,
|
|
102
|
-
# never syntax to normalise.
|
|
103
|
-
#
|
|
104
|
-
# Handles both `"` and `'` delimiters (this grammar's own
|
|
105
|
-
# `Resolver.quoted?` admits either), quote-aware exactly the way
|
|
106
|
-
# `Evaluator.top_level_index`/`Resolver.array_elements` already are
|
|
107
|
-
# elsewhere in this sublanguage. An unterminated quote (malformed
|
|
108
|
-
# input) is passed through raw rather than risk mangling it further.
|
|
109
|
-
#
|
|
110
|
-
# @param text [String] the source text to scan for quoted string
|
|
111
|
-
# literals
|
|
112
|
-
# @yieldparam segment [String] a run of `text` outside any quoted
|
|
113
|
-
# literal
|
|
114
|
-
# @yieldreturn [String] the transformed replacement for `segment`
|
|
115
|
-
# @return [String] `text` with the block applied to every unquoted
|
|
116
|
-
# run, and every quoted run (`"`- or `'`-delimited) copied through
|
|
117
|
-
# unchanged
|
|
58
|
+
# Yields each run of `text` outside quoted literals, copying quoted runs through verbatim.
|
|
59
|
+
# Quote-blind rewriting would change what a predicate compares a string against.
|
|
60
|
+
# Handles `"` and `'`; an unterminated quote is passed through raw.
|
|
118
61
|
def map_outside_strings(text)
|
|
119
62
|
result = +""
|
|
120
63
|
buffer = +""
|
|
@@ -130,29 +73,9 @@ module Hecks
|
|
|
130
73
|
end
|
|
131
74
|
elsif ['"', "'"].include?(char)
|
|
132
75
|
result << yield(buffer)
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
#
|
|
136
|
-
# yielded strings aren't frozen, so `+char` is just as
|
|
137
|
-
# much a no-op here). Without a real copy, `buffer` and
|
|
138
|
-
# `quote` alias the same mutable object: the very next
|
|
139
|
-
# `buffer << char` grows `quote` right along with it, so
|
|
140
|
-
# `char == quote` can only ever compare a single character
|
|
141
|
-
# against an ever-lengthening string and never closes the
|
|
142
|
-
# literal — everything after a predicate's first quoted
|
|
143
|
-
# string silently skipped normalisation for the rest of
|
|
144
|
-
# the text, undetected because passing text through
|
|
145
|
-
# unnormalised is silent. Masked by every existing spec
|
|
146
|
-
# here, which only checks that quoted contents survive
|
|
147
|
-
# untouched (the M7 fix this method exists for) — that
|
|
148
|
-
# still holds by accident once the bug makes the "outside"
|
|
149
|
-
# branch unreachable. Found live: a multi-line `given`/
|
|
150
|
-
# `ensures` block whose only quoted literal closes before a
|
|
151
|
-
# later line — the newline and that later line's own
|
|
152
|
-
# indentation went uncollapsed, diverging from
|
|
153
|
-
# `hecks-parse`'s own (correct) single-space join.
|
|
154
|
-
# `spec/parser_parity_spec.rb`, roster's "a front-row seat
|
|
155
|
-
# takes a member of age".
|
|
76
|
+
# Must be a real copy: aliasing `buffer` and `quote` makes `buffer << char` grow
|
|
77
|
+
# `quote`, so the literal never closes and later text skips normalisation.
|
|
78
|
+
# Pinned by spec/parser_parity_spec.rb (multi-line `given`/`ensures`).
|
|
156
79
|
buffer = char.dup
|
|
157
80
|
quote = char.dup
|
|
158
81
|
else
|