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
|
@@ -2,57 +2,31 @@ require_relative "../runtime/errors"
|
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Facade
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# `JsonDoor` beside this one translates for a caller holding parsed JSON:
|
|
8
|
-
# String keys, already-nested objects, real Integers. A command line has
|
|
9
|
-
# neither of those. It has `sequence.value=99` — one flat string, with the
|
|
10
|
-
# nesting spelled as a path and the type not spelled at all.
|
|
11
|
-
#
|
|
12
|
-
# So this does the two things that turns into: rebuild the nesting, and
|
|
13
|
-
# give every leaf the type the chapter declared for it.
|
|
14
|
-
#
|
|
15
|
-
# The type comes from the projection, never from the value. A door that
|
|
16
|
-
# guessed — "99 looks like a number" — would send the Integer 99 for a
|
|
17
|
-
# version string of "99", and be wrong in a way nothing downstream could
|
|
18
|
-
# detect, because both are perfectly good arguments. `Projector::CliProjector`
|
|
19
|
-
# already read the declared field type out of the value object; this only
|
|
20
|
-
# applies it.
|
|
5
|
+
# Turns `path=value` command-line words into a nested, typed argument Hash.
|
|
6
|
+
# Types come from the projection, never from guessing at the value ("99" may be a String).
|
|
21
7
|
module CliDoor
|
|
22
8
|
module_function
|
|
23
9
|
|
|
24
|
-
# Builds a verb's nested, typed argument Hash
|
|
25
|
-
# command line.
|
|
10
|
+
# Builds a verb's nested, typed argument Hash from its `name=value` words.
|
|
26
11
|
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
12
|
+
# arguments(spec, ["reference.value=A-1", "sequence.value=99"])
|
|
13
|
+
# # => { reference: { value: "A-1" }, sequence: { value: 99 } }
|
|
29
14
|
#
|
|
30
|
-
# @param spec [Hash{Symbol => Object}] one verb's entry from `Projector::CliProjector
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# @
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
# @return [Hash{Symbol => Object}] the arguments nested by path, each leaf cast to
|
|
37
|
-
# its declared type and each list option collected into an Array; `{}` for no pairs
|
|
38
|
-
# @raise [Runtime::NotFound] if a pair has no `=`, or names a path the verb does
|
|
39
|
-
# not take
|
|
40
|
-
# @raise [Runtime::TypeMismatch] if a value cannot be read as the declared Integer
|
|
41
|
-
# or Float
|
|
15
|
+
# @param spec [Hash{Symbol => Object}] one verb's entry from `Projector::CliProjector`
|
|
16
|
+
# @param pairs [Array<String>] the words after the verb; a path may be the short form
|
|
17
|
+
# of a single-field value object (`reference` for `reference.value`)
|
|
18
|
+
# @return [Hash{Symbol => Object}] the arguments nested by path, leaves cast to type
|
|
19
|
+
# @raise [Runtime::NotFound] if a pair has no `=` or names a path the verb lacks
|
|
20
|
+
# @raise [Runtime::TypeMismatch] if a value does not parse as its Integer or Float
|
|
42
21
|
def arguments(spec, pairs)
|
|
43
|
-
#
|
|
44
|
-
# existing id=... aggregate invocation cross the new receiver boundary
|
|
45
|
-
# while the projected surface teaches to=... exclusively.
|
|
22
|
+
# Extra accepted arguments stay out of help, which teaches only to=....
|
|
46
23
|
options = (spec[:arguments] + Array(spec[:legacy_arguments])).to_h do |argument|
|
|
47
24
|
[argument[:path], argument]
|
|
48
25
|
end
|
|
49
26
|
|
|
50
27
|
pairs.each_with_object({}) do |pair, args|
|
|
51
28
|
path, value = split(pair)
|
|
52
|
-
# key?
|
|
53
|
-
# bare path, or its one-argument expansion) actually declares
|
|
54
|
-
# this option, and the lookup below must hold to that same
|
|
55
|
-
# decision rather than re-guessing which one exists.
|
|
29
|
+
# key? rather than `||`, so the lookup below honors the same spelling `full` chose.
|
|
56
30
|
full = options.key?(path) ? path : expand(path, options)
|
|
57
31
|
argument = options.key?(full) ? options[full] : raise(Runtime::NotFound, unknown(path, options.keys))
|
|
58
32
|
|
|
@@ -62,12 +36,7 @@ module Hecks
|
|
|
62
36
|
end
|
|
63
37
|
end
|
|
64
38
|
|
|
65
|
-
# Cuts one
|
|
66
|
-
#
|
|
67
|
-
# @param pair [String] one word, such as `"reference.value=BUG#1"`
|
|
68
|
-
# @return [Array(String, String)] the path and the value; the value is `""` for
|
|
69
|
-
# `"name="`
|
|
70
|
-
# @raise [Runtime::NotFound] if the word contains no `=`
|
|
39
|
+
# Cuts one word at its first `=`, so a value may itself contain `=`.
|
|
71
40
|
def split(pair)
|
|
72
41
|
name, value = pair.split("=", 2)
|
|
73
42
|
raise Runtime::NotFound, "#{pair.inspect} is not name=value" if value.nil?
|
|
@@ -75,32 +44,15 @@ module Hecks
|
|
|
75
44
|
[name, value]
|
|
76
45
|
end
|
|
77
46
|
|
|
78
|
-
# Expands a bare value-object name into the
|
|
79
|
-
#
|
|
80
|
-
# The short form, for the common case. Almost every value object in this
|
|
81
|
-
# corpus has exactly one field, so `reference=BUG#1` is unambiguous and
|
|
82
|
-
# is what anybody types. Expanded only when precisely one option starts
|
|
83
|
-
# with that prefix — two would be a guess, and a guess about which field
|
|
84
|
-
# a caller meant is worse than asking them to say.
|
|
85
|
-
#
|
|
86
|
-
# @param path [String] the path as typed, such as `"reference"`
|
|
87
|
-
# @param options [Hash{String => Hash}] the verb's options keyed by full path
|
|
88
|
-
# @return [String] the one full path beginning `"#{path}."`, or `path` unchanged when
|
|
89
|
-
# none or several do
|
|
47
|
+
# Expands a bare value-object name into the path of its only field.
|
|
48
|
+
# Left unchanged when several fields match, since guessing which was meant is worse.
|
|
90
49
|
def expand(path, options)
|
|
91
50
|
candidates = options.keys.select { |key| key.start_with?("#{path}.") }
|
|
92
51
|
candidates.length == 1 ? candidates.first : path
|
|
93
52
|
end
|
|
94
53
|
|
|
95
|
-
# Converts one
|
|
96
|
-
#
|
|
97
|
-
# @param value [String] the text after the `=`
|
|
98
|
-
# @param type [String] the declared type name; `"Integer"`, `"Float"` and
|
|
99
|
-
# `"Boolean"` are converted, anything else leaves the value a String
|
|
100
|
-
# @return [Integer, Float, Boolean, String] the typed value; a Boolean is true only
|
|
101
|
-
# for `true`, `yes` or `1` in any letter case, and false for every other text
|
|
102
|
-
# @raise [Runtime::TypeMismatch] if the text does not parse as the declared Integer
|
|
103
|
-
# or Float
|
|
54
|
+
# Converts one value to its declared type; only Integer, Float and Boolean convert.
|
|
55
|
+
# A Boolean is true only for `true`, `yes` or `1` in any letter case.
|
|
104
56
|
def cast(value, type)
|
|
105
57
|
case type
|
|
106
58
|
when "Integer" then Integer(value)
|
|
@@ -112,36 +64,12 @@ module Hecks
|
|
|
112
64
|
raise Runtime::TypeMismatch, "#{value.inspect} is not #{type} — the chapter declares this field as #{type}"
|
|
113
65
|
end
|
|
114
66
|
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
# A list grows rather than overwrites, and getting this wrong is silent.
|
|
67
|
+
# Appends one element to a list argument, creating the list on first use.
|
|
118
68
|
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
# way to notice.
|
|
124
|
-
#
|
|
125
|
-
# A list of one is still a list. `tags.value=flaky` produces
|
|
126
|
-
# `[{ value: "flaky" }]`, not `{ value: "flaky" }`, because the chapter
|
|
127
|
-
# declared a collection and a caller who sent one element did not
|
|
128
|
-
# thereby declare a different shape. A bare object must never reach a
|
|
129
|
-
# `list_of` attribute: everything downstream that walks
|
|
130
|
-
# it — a query's `contains`, a projection, the Postgres adapter's own
|
|
131
|
-
# array handling — is entitled to assume it can iterate.
|
|
132
|
-
#
|
|
133
|
-
# Multi-field elements are not supported here, deliberately. A flat
|
|
134
|
-
# command line has no way to say which `a.x=` goes with which `a.y=`,
|
|
135
|
-
# and inventing an index syntax would be a language nobody asked for.
|
|
136
|
-
# Every list in this corpus is a list of single-field value objects; a
|
|
137
|
-
# richer one is a job for `JsonDoor`, which has real nesting.
|
|
138
|
-
#
|
|
139
|
-
# @param hash [Hash{Symbol => Object}] the arguments built so far; mutated in place
|
|
140
|
-
# @param path [Array<String>] the option's path segments, at least two: the last
|
|
141
|
-
# names the element's field, the one before it names the list
|
|
142
|
-
# @param value [Integer, Float, Boolean, String] the cast value for the new element
|
|
143
|
-
# @return [Hash{Symbol => Object}] `hash`, with `{ leaf => value }` appended to the
|
|
144
|
-
# Array at the list's key
|
|
69
|
+
# Repeats must grow the list (`bury` would silently keep only the last), and a single
|
|
70
|
+
# element is still a one-item list, since a `list_of` attribute must never see a bare
|
|
71
|
+
# object. Multi-field elements are left to `JsonDoor`: a flat command line cannot say
|
|
72
|
+
# which `a.x=` pairs with which `a.y=`.
|
|
145
73
|
def append(hash, path, value)
|
|
146
74
|
*branches, leaf = path.map(&:to_sym)
|
|
147
75
|
holder = branches[0..-2].reduce(hash) { |node, key| node[key] ||= {} }
|
|
@@ -151,13 +79,7 @@ module Hecks
|
|
|
151
79
|
hash
|
|
152
80
|
end
|
|
153
81
|
|
|
154
|
-
# Sets one value at a nested path, creating
|
|
155
|
-
# overwriting whatever the leaf already held.
|
|
156
|
-
#
|
|
157
|
-
# @param hash [Hash{Symbol => Object}] the arguments built so far; mutated in place
|
|
158
|
-
# @param path [Array<String>] the option's path segments, outermost first
|
|
159
|
-
# @param value [Integer, Float, Boolean, String] the cast value to store at the leaf
|
|
160
|
-
# @return [Hash{Symbol => Object}] `hash`, with the value set under Symbol keys
|
|
82
|
+
# Sets one value at a nested path, creating intermediate Hashes and overwriting the leaf.
|
|
161
83
|
def bury(hash, path, value)
|
|
162
84
|
*branches, leaf = path.map(&:to_sym)
|
|
163
85
|
target = branches.reduce(hash) { |node, key| node[key] ||= {} }
|
|
@@ -166,10 +88,6 @@ module Hecks
|
|
|
166
88
|
end
|
|
167
89
|
|
|
168
90
|
# Words the refusal for an argument the verb does not take, listing what it does.
|
|
169
|
-
#
|
|
170
|
-
# @param path [String] the path as the caller typed it
|
|
171
|
-
# @param known [Array<String>] every path the verb accepts, in any order
|
|
172
|
-
# @return [String] a one-line message naming `path` and the sorted accepted paths
|
|
173
91
|
def unknown(path, known)
|
|
174
92
|
"no argument #{path.inspect} — this verb takes #{known.sort.join(', ')}"
|
|
175
93
|
end
|
|
@@ -7,44 +7,21 @@ require_relative "../ports/clock"
|
|
|
7
7
|
|
|
8
8
|
module Hecks
|
|
9
9
|
module Facade
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# `Projector::CliProjector` answers what a domain's command line looks
|
|
13
|
-
# like; this is the twenty lines that parse against it and dispatch. It
|
|
14
|
-
# lives in `lib/` rather than in a `bin/` because more than one front door
|
|
15
|
-
# wants it — `bin/run` for whichever domain you are standing in, `bin/qc`
|
|
16
|
-
# pinned to the QA ledger — and a second copy of the parse-and-dispatch
|
|
17
|
-
# would be the exact duplication the projection exists to avoid.
|
|
18
|
-
#
|
|
19
|
-
# No IO. It answers `[text, status]` and never prints or exits, so a spec
|
|
20
|
-
# can call it without capturing streams or trapping SystemExit. The `bin/`
|
|
21
|
-
# scripts do the printing, the same division `Router` and `JsonDoor`
|
|
22
|
-
# already keep against HTTP.
|
|
10
|
+
# Parses and dispatches a command line against a `Projector::CliProjector` projection.
|
|
11
|
+
# Does no IO: it answers `[text, status]` and leaves printing and exiting to `bin/` scripts.
|
|
23
12
|
module CliRunner
|
|
24
13
|
module_function
|
|
25
14
|
|
|
26
|
-
# Runs one command line against a booted domain
|
|
27
|
-
#
|
|
28
|
-
# to print.
|
|
15
|
+
# Runs one command line against a booted domain and answers the text to print.
|
|
16
|
+
# Only the first bluebook in the registry, the booted domain's own, is projected.
|
|
29
17
|
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# into verbs. `--help`, `-h`, `help` or an empty `argv` answers the usage text; a
|
|
34
|
-
# verb followed by `--help` answers that verb's help.
|
|
35
|
-
#
|
|
36
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted domain,
|
|
37
|
-
# as `Hecks.boot` returns it
|
|
38
|
-
# @param argv [Array<String>] the words after the program name: an optional `ask`,
|
|
39
|
-
# the verb or question name (bare or aggregate-qualified), then `path=value` pairs
|
|
18
|
+
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted domain
|
|
19
|
+
# @param argv [Array<String>] an optional `ask`, the verb or question name, then
|
|
20
|
+
# `path=value` pairs; `--help`, `-h`, `help` or nothing answers usage
|
|
40
21
|
# @param program [String] how the caller was invoked, echoed in usage and hints
|
|
41
|
-
# @return [Array(String, Integer)] the text
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
# @raise [Runtime::WiringError] if a repository, or the clock adapter a `now`
|
|
45
|
-
# argument needs, cannot be resolved
|
|
46
|
-
# @raise [Runtime::StaleWrite] if concurrent writers beat the command through every
|
|
47
|
-
# retry
|
|
22
|
+
# @return [Array(String, Integer)] the text and the status: 0 answered, 1 refused or misused
|
|
23
|
+
# @raise [Runtime::WiringError] if a repository or the clock adapter cannot be resolved
|
|
24
|
+
# @raise [Runtime::StaleWrite] if concurrent writers beat the command through every retry
|
|
48
25
|
def call(runtime:, argv:, program: "bin/run")
|
|
49
26
|
bluebook = runtime.registry.bluebooks.values.first
|
|
50
27
|
cli = Projector.call(:cli, bluebook: bluebook, options: { program: program })
|
|
@@ -52,16 +29,14 @@ module Hecks
|
|
|
52
29
|
name = argv.first
|
|
53
30
|
return [cli[:usage], 0] if name.nil? || %w[--help -h help].include?(name)
|
|
54
31
|
|
|
55
|
-
# `ask`
|
|
56
|
-
#
|
|
32
|
+
# `ask` gives questions their own namespace; a chapter may declare a command and query
|
|
33
|
+
# of one name.
|
|
57
34
|
asking = name == "ask"
|
|
58
35
|
argv = argv[1..] if asking
|
|
59
36
|
name = argv.first
|
|
60
37
|
return [cli[:usage], 1] if name.nil?
|
|
61
38
|
|
|
62
|
-
#
|
|
63
|
-
# `pizzas order.create_pizza` reach the same verb — the aggregate is
|
|
64
|
-
# worth typing only when two of them declare the same word.
|
|
39
|
+
# The alias map lets `create_pizza` and `order.create_pizza` reach the same verb.
|
|
65
40
|
pool = asking ? cli[:questions] : cli[:verbs]
|
|
66
41
|
key = cli[:names][asking ? :question : :command][name]
|
|
67
42
|
spec = pool[key]
|
|
@@ -77,26 +52,8 @@ module Hecks
|
|
|
77
52
|
dispatch(runtime, spec, name, rest, program, asking)
|
|
78
53
|
end
|
|
79
54
|
|
|
80
|
-
# Parses one resolved verb's arguments, runs it as a query or
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted domain
|
|
84
|
-
# @param spec [Hash{Symbol => Object}] the verb's entry from `Projector::CliProjector`;
|
|
85
|
-
# read for `:kind`, `:verb`, `:arguments`, `:receiver` and `:legacy_receiver`
|
|
86
|
-
# @param name [String] the verb as the caller typed it, echoed in the `--help` hint
|
|
87
|
-
# @param rest [Array<String>] the `path=value` words after the verb
|
|
88
|
-
# @param program [String] how the caller was invoked, echoed in the `--help` hint
|
|
89
|
-
# @param asking [Boolean] whether the verb was reached through `ask`, which only
|
|
90
|
-
# changes the wording of the hint
|
|
91
|
-
# @return [Array(String, Integer)] status 0 with pretty-printed JSON: the materialized
|
|
92
|
-
# rows for a query; `id`, `state` and event names for a command; `id` and events
|
|
93
|
-
# with payloads for a port operation. Status 1 with the message of a
|
|
94
|
-
# `Runtime::DOMAIN_REFUSALS` error, followed by a `--help` hint when it is a
|
|
95
|
-
# `Runtime::NotFound` or `Runtime::TypeMismatch`
|
|
96
|
-
# @raise [Runtime::WiringError] if a repository, or the clock adapter a `now`
|
|
97
|
-
# argument needs, cannot be resolved
|
|
98
|
-
# @raise [Runtime::StaleWrite] if concurrent writers beat the command through every
|
|
99
|
-
# retry
|
|
55
|
+
# Parses one resolved verb's arguments, runs it as a query or command, and turns the
|
|
56
|
+
# outcome, or the domain's refusal, into `[json_or_message, status]`.
|
|
100
57
|
def dispatch(runtime, spec, name, rest, program, asking)
|
|
101
58
|
args = stamp_time(runtime, spec, CliDoor.arguments(spec, rest))
|
|
102
59
|
|
|
@@ -105,10 +62,7 @@ module Hecks
|
|
|
105
62
|
return [JSON.pretty_generate(rows.map { |row| JsonDoor.materialize(row) }), 0]
|
|
106
63
|
end
|
|
107
64
|
|
|
108
|
-
#
|
|
109
|
-
# reports the whole store because a corpus run is judged on all of it;
|
|
110
|
-
# somebody who issued one verb wants that verb's outcome, and against a
|
|
111
|
-
# Postgres-backed domain the full dump is every record there has been.
|
|
65
|
+
# Answers with only this verb's outcome; a full store dump is every record there is.
|
|
112
66
|
request = CommandRequest.normalize(args, receiver: spec[:receiver],
|
|
113
67
|
legacy_receiver: spec[:legacy_receiver])
|
|
114
68
|
handle = runtime.dispatch_flat(spec[:verb], request)
|
|
@@ -118,62 +72,17 @@ module Hecks
|
|
|
118
72
|
state: JsonDoor.materialize(handle.state),
|
|
119
73
|
events: handle.events.map(&:name)), 0]
|
|
120
74
|
rescue Runtime::NotFound, Runtime::TypeMismatch => e
|
|
121
|
-
# A bad argument and a missing record both
|
|
122
|
-
# same next step: read what the verb actually takes.
|
|
75
|
+
# A bad argument and a missing record both need the same next step: read the help.
|
|
123
76
|
["#{e.message}\n\n #{program} #{'ask ' if asking}#{name} --help", 1]
|
|
124
77
|
rescue *Runtime::DOMAIN_REFUSALS => e
|
|
125
|
-
#
|
|
78
|
+
# The refusal is the chapter's own sentence, verbatim.
|
|
126
79
|
[e.message, 1]
|
|
127
80
|
end
|
|
128
81
|
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
# The clock, filled in at the door.
|
|
132
|
-
#
|
|
133
|
-
# A staleness rule needs the time, and the sublanguage cannot ask for it
|
|
134
|
-
# — a `given` that read the clock would judge the same record differently
|
|
135
|
-
# on two runs, and every replay, audit and fuzz oracle here assumes it
|
|
136
|
-
# does not. So `now` stays an argument the predicate merely reads, and
|
|
137
|
-
# the question becomes who types it. Left to the caller, that is:
|
|
138
|
-
#
|
|
139
|
-
# qa/quality_control target.claim id=QC held_by.value=me \
|
|
140
|
-
# now.value=$(date +%s) window.value=900
|
|
141
|
-
#
|
|
142
|
-
# which is a shell incantation in front of every claim, and one an agent
|
|
143
|
-
# gets wrong by pasting a stale number.
|
|
144
|
-
#
|
|
145
|
-
# At the door, not in the runtime, and the distinction is load-bearing.
|
|
146
|
-
# `Ports::IdentityGeneration` reasons the same question through for a
|
|
147
|
-
# minted uuid and lands on "the value is baked into the caller's args at
|
|
148
|
-
# the first live dispatch". A clock consulted inside the interpreter
|
|
149
|
-
# would not have that property — a recorded corpus step replayed
|
|
150
|
-
# tomorrow would quietly get tomorrow's time, and the fuzzer's oracle and
|
|
151
|
-
# the adapter-agreement gate both compare runs of exactly that shape. Here
|
|
152
|
-
# it fills only what a person or an agent is typing, and `runtime.dispatch`
|
|
153
|
-
# is left alone.
|
|
82
|
+
# Fills in a `now` argument the caller left out, from the clock port; an explicit one wins.
|
|
154
83
|
#
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
# By name, which is the one uncomfortable part. `now` is a plausible
|
|
159
|
-
# domain word and nothing declares that it means the clock. It is
|
|
160
|
-
# tolerable because this is a convenience layer rather than semantics —
|
|
161
|
-
# the verb's own help says the argument exists, dispatch is unchanged,
|
|
162
|
-
# and passing it explicitly is always available. The honest version is a
|
|
163
|
-
# declaration in the chapter (`attribute :now, Instant, from: :clock`),
|
|
164
|
-
# which is a language change: DSL, IR, the self-hosted grammar and its
|
|
165
|
-
# goldens. Worth doing; not worth smuggling in here.
|
|
166
|
-
#
|
|
167
|
-
# @param runtime [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted domain,
|
|
168
|
-
# whose registry names the clock adapter
|
|
169
|
-
# @param spec [Hash{Symbol => Object}] the verb's projected entry; only an option whose
|
|
170
|
-
# `:path` starts `"now."` makes this method act
|
|
171
|
-
# @param args [Hash{Symbol => Object}] the parsed arguments; not mutated
|
|
172
|
-
# @return [Hash{Symbol => Object}] `args` itself when the verb takes no `now` or the
|
|
173
|
-
# caller gave one; otherwise a copy with `now: { value: seconds }`, the clock port's
|
|
174
|
-
# current time in whole seconds
|
|
175
|
-
# @raise [Runtime::WiringError] if the time is needed and zero or several adapters
|
|
176
|
-
# implement the clock port
|
|
84
|
+
# Done at the door, not in the runtime: a clock read inside the interpreter would give a
|
|
85
|
+
# replayed corpus step the replay day's time. Matched by the argument's name alone.
|
|
177
86
|
def stamp_time(runtime, spec, args)
|
|
178
87
|
return args unless spec[:arguments].any? { |argument| argument[:path].start_with?("now.") }
|
|
179
88
|
return args if args.key?(:now)
|
|
@@ -181,69 +90,22 @@ module Hecks
|
|
|
181
90
|
args.merge(now: { value: Ports::Clock.now(runtime.registry) })
|
|
182
91
|
end
|
|
183
92
|
|
|
184
|
-
# Shapes a port operation's outcome
|
|
185
|
-
# event with its full payload.
|
|
186
|
-
#
|
|
187
|
-
# A port operation has no state, and its payload is the entire point.
|
|
188
|
-
#
|
|
189
|
-
# A command answers with the record it changed, so naming the events is
|
|
190
|
-
# enough — the interesting part is in `state`. A port operation changes
|
|
191
|
-
# no record: it asked something outside and came back with what was
|
|
192
|
-
# said, and that lives only in the event payload. Reporting names alone
|
|
193
|
-
# would print `SpecsCompleted` and drop the spec output on the floor.
|
|
194
|
-
#
|
|
195
|
-
# This is what makes a projected CLI usable as somebody's only door. An
|
|
196
|
-
# agent that may not shell out cannot run `rspec` and read the terminal;
|
|
197
|
-
# it asks the port and reads the answer, and if the answer is a bare
|
|
198
|
-
# event name then the door leads nowhere and it needs a shell after all.
|
|
93
|
+
# Shapes a port operation's outcome, which has no state: each event with its full payload.
|
|
199
94
|
#
|
|
200
|
-
#
|
|
201
|
-
#
|
|
202
|
-
# are answers the caller asked for, correctly delivered. Exit 1 is for
|
|
203
|
-
# "you typed something wrong", and conflating the two would have a
|
|
204
|
-
# scripted agent treat a healthy no as a broken call.
|
|
205
|
-
#
|
|
206
|
-
# @param handle [Runtime::Dispatcher::Result, Runtime::RemoteDispatcher::Result] the
|
|
207
|
-
# dispatch result, which for a port operation has a nil `state`
|
|
208
|
-
# @return [Hash{Symbol => Object}] `:id`, the result's id or else the first event's
|
|
209
|
-
# (nil when there is neither), and `:events`, an Array of `{ name:, payload: }`
|
|
210
|
-
# Hashes with each payload materialized to plain data
|
|
95
|
+
# The answer lives only in the payloads, so names alone would drop it. Exit status stays 0
|
|
96
|
+
# for refusal events too, since a healthy no is an answer, not a misuse.
|
|
211
97
|
def answered(handle)
|
|
212
|
-
#
|
|
213
|
-
# instance and the handle's own `id` is nil by design. The event knows
|
|
214
|
-
# which record was asked about — it was stamped with it — and printing
|
|
215
|
-
# `null` beside a payload that plainly says `SW-TOOL` would read as a
|
|
216
|
-
# bug in something.
|
|
98
|
+
# A port operation hydrates no instance, so the handle's `id` is nil by design.
|
|
217
99
|
{ id: handle.id || handle.events.first&.id,
|
|
218
100
|
events: handle.events.map do |event|
|
|
219
101
|
{ name: event.name, payload: JsonDoor.materialize(event.payload) }
|
|
220
102
|
end }
|
|
221
103
|
end
|
|
222
104
|
|
|
223
|
-
# Words the answer
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
# A near miss is worth more than a list. Somebody who typed
|
|
227
|
-
# `bug.discovr` wants one line, not eighty-seven of them.
|
|
228
|
-
#
|
|
229
|
-
# Ranked by shared prefix, not by substring. Substring matching finds
|
|
230
|
-
# nothing for the commonest typo of all — a dropped
|
|
231
|
-
# letter, `order.create_piza`, which is a substring of nothing. Prefix
|
|
232
|
-
# length survives an error anywhere after it, which is where errors are.
|
|
233
|
-
#
|
|
234
|
-
# @param cli [Hash{Symbol => Object}] the `Projector::CliProjector` projection; read
|
|
235
|
-
# for `cli[:names]`, the alias maps under `:command` and `:question`
|
|
236
|
-
# @param name [String] what the caller typed
|
|
237
|
-
# @param asking [Boolean] whether it was typed after `ask`, which picks the question
|
|
238
|
-
# names and the wording
|
|
239
|
-
# @param program [String] how the caller was invoked, echoed in the closing hint
|
|
240
|
-
# @return [String] a multi-line message; candidates must share a prefix of at least
|
|
241
|
-
# half of `name`'s length, and never fewer than three characters
|
|
105
|
+
# Words the answer for an unknown verb or question, suggesting up to five near names.
|
|
106
|
+
# Ranked by shared prefix, which survives a dropped letter where a substring match would not.
|
|
242
107
|
def unknown(cli, name, asking, program)
|
|
243
|
-
#
|
|
244
|
-
# form with a typo — `order.create_piza` — shares no prefix with the
|
|
245
|
-
# short name `create_pizza`, so pooling only one of them suggests
|
|
246
|
-
# nothing for half the mistakes anybody makes.
|
|
108
|
+
# Both spellings are candidates: `order.create_piza` shares no prefix with `create_pizza`.
|
|
247
109
|
pool = cli[:names][asking ? :question : :command].keys
|
|
248
110
|
near = pool.map { |candidate| [shared_prefix(candidate, name), candidate] }
|
|
249
111
|
.select { |shared, _| shared >= [name.length / 2, 3].max }
|
|
@@ -256,12 +118,6 @@ module Hecks
|
|
|
256
118
|
lines.join("\n")
|
|
257
119
|
end
|
|
258
120
|
|
|
259
|
-
# Counts how many leading characters two names have in common.
|
|
260
|
-
#
|
|
261
|
-
# @param one [String] a candidate name
|
|
262
|
-
# @param other [String] the name to compare it with
|
|
263
|
-
# @return [Integer] the length of the common prefix, from 0 up to the shorter
|
|
264
|
-
# name's length
|
|
265
121
|
def shared_prefix(one, other)
|
|
266
122
|
length = [one.length, other.length].min
|
|
267
123
|
(0...length).find { |index| one[index] != other[index] } || length
|
|
@@ -2,42 +2,24 @@ require_relative "../runtime/errors"
|
|
|
2
2
|
|
|
3
3
|
module Hecks
|
|
4
4
|
module Facade
|
|
5
|
-
# Turns an external command request into the dispatcher's
|
|
6
|
-
# envelope. Human-facing doors may accept their old flat id spelling at
|
|
7
|
-
# the edge, but every call leaving this boundary has one shape:
|
|
5
|
+
# Turns an external command request into the dispatcher's `to:`/`with:` envelope.
|
|
8
6
|
#
|
|
9
7
|
# aggregate command: { to: "record-id", with: { declared: "facts" } }
|
|
10
|
-
# entity command: { to: { aggregate: "...", entity: "..." },
|
|
11
|
-
# with: { declared: "facts" } }
|
|
12
|
-
#
|
|
13
|
-
# The command interpreter remains the authority on which facts are
|
|
14
|
-
# declared. This helper only prevents routing fields from leaking into the
|
|
15
|
-
# fact payload and gives every external door the same wire contract.
|
|
8
|
+
# entity command: { to: { aggregate: "...", entity: "..." }, with: { ... } }
|
|
16
9
|
module CommandRequest
|
|
17
10
|
module_function
|
|
18
11
|
|
|
19
|
-
# Splits
|
|
20
|
-
#
|
|
21
|
-
# not fit the kind of command being called.
|
|
22
|
-
#
|
|
23
|
-
# Keys are symbolized at every depth first, so a parsed JSON body and a Ruby Hash
|
|
24
|
-
# are treated alike. In a flat Hash everything but `to` (or the legacy receiver
|
|
25
|
-
# keys) is a fact.
|
|
12
|
+
# Splits a request, explicit envelope or flat Hash, into routing (`to:`) and facts (`with:`).
|
|
13
|
+
# Keys are symbolized at every depth; in a flat Hash everything but the receiver is a fact.
|
|
26
14
|
#
|
|
27
|
-
# @param input [Hash
|
|
28
|
-
#
|
|
29
|
-
# @param
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
# @
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
# an `:entity` receiver; `nil` accepts no legacy spelling
|
|
36
|
-
# @return [Hash{Symbol => Object}] `{ with: facts }`, plus `to:` holding the route
|
|
37
|
-
# whenever `receiver` is not `nil`; ready to pass to `Dispatcher#dispatch_flat`
|
|
38
|
-
# @raise [Runtime::TypeMismatch] if `input` or its `with:` is not a Hash, if an
|
|
39
|
-
# explicit envelope carries keys other than `to:` and `with:`, or if the route
|
|
40
|
-
# is missing, blank, malformed, or given to a command that takes no receiver
|
|
15
|
+
# @param input [Hash] the request, with String or Symbol keys
|
|
16
|
+
# @param receiver [Symbol, nil] `:aggregate`, `:entity`, or `nil` for a command with none
|
|
17
|
+
# @param legacy_receiver [Symbol, String, Hash, nil] the flat key that may carry the
|
|
18
|
+
# receiver instead of `to:`: a name for `:aggregate`, `{ aggregate:, entity: }` for
|
|
19
|
+
# `:entity`
|
|
20
|
+
# @return [Hash{Symbol => Object}] `{ with: facts }`, plus `to:` when `receiver` is set
|
|
21
|
+
# @raise [Runtime::TypeMismatch] if the request is malformed or the route does not fit
|
|
22
|
+
# the receiver kind
|
|
41
23
|
# @raise [ArgumentError] if `receiver` is not `nil`, `:aggregate` or `:entity`
|
|
42
24
|
def normalize(input, receiver:, legacy_receiver: nil)
|
|
43
25
|
request = symbolize(input)
|
|
@@ -87,9 +69,7 @@ module Hecks
|
|
|
87
69
|
end
|
|
88
70
|
private_class_method :take_legacy_route
|
|
89
71
|
|
|
90
|
-
#
|
|
91
|
-
# :aggregate, :entity) plus the impossible-kind backstop — each
|
|
92
|
-
# branch is its own self-contained validation for that one kind.
|
|
72
|
+
# One self-contained check per closed receiver kind, plus a backstop.
|
|
93
73
|
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
94
74
|
def validate_route!(route, receiver)
|
|
95
75
|
case receiver
|