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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require_relative "../../hecks"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module CLI
|
|
5
|
+
# The command behind `bin/project_diagrams` and `hecks project_diagrams`: boots a
|
|
6
|
+
# domain, finds the chapter it names, and writes its Mermaid diagrams
|
|
7
|
+
# (`Projections::Diagrams`) under `docs/generated/diagrams/<chapter>/` below the
|
|
8
|
+
# root.
|
|
9
|
+
module ProjectDiagrams
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def call(argv, program:, root:)
|
|
13
|
+
usage = "usage: #{program} <domain-path> <ChapterName>"
|
|
14
|
+
domain_path = argv[0] or abort usage
|
|
15
|
+
chapter_name = argv[1] or abort usage
|
|
16
|
+
|
|
17
|
+
runtime = Hecks.boot(File.expand_path(domain_path, Dir.pwd))
|
|
18
|
+
chapter = runtime.registry.bluebook(chapter_name)
|
|
19
|
+
hecksagon = runtime.registry.hecksagon(chapter_name)
|
|
20
|
+
directory = File.expand_path("docs/generated/diagrams/#{Naming.snake(chapter_name)}", root)
|
|
21
|
+
|
|
22
|
+
written = Projector.write(
|
|
23
|
+
Projector.call(:diagrams, bluebook: chapter, options: { hecksagon: hecksagon }),
|
|
24
|
+
directory, as: :files
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if written.empty?
|
|
28
|
+
puts "#{chapter_name} declares no lifecycle, relationship, or dispatch to project yet"
|
|
29
|
+
else
|
|
30
|
+
puts "wrote #{written.size} diagram(s) to #{directory}:"
|
|
31
|
+
written.each { |path| puts " #{path}" }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "../../hecks"
|
|
3
|
+
# ADR 0033 — a domain wired to PostgresEra needs this plugin loaded
|
|
4
|
+
# explicitly; the era/lineage subsystem does not load with core.
|
|
5
|
+
require_relative "../ports/persistence/plugins/era"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module CLI
|
|
9
|
+
# The command behind `bin/run` and `hecks run`: dispatches one verb from the
|
|
10
|
+
# command line, or executes a JSON step list and reports instances, events,
|
|
11
|
+
# refusals, reactions, sagas and query rows.
|
|
12
|
+
#
|
|
13
|
+
# `run bug.discover reference=BUG#1 severity=high …` needs no JSON; the verb
|
|
14
|
+
# tree and its arguments are projected from the bluebook, so this knows nothing
|
|
15
|
+
# about any domain and its help cannot go stale. The step-list form takes a
|
|
16
|
+
# script path, `-` for stdin, or the JSON itself. The first argument is a
|
|
17
|
+
# domain only when it is a directory; otherwise the nearest enclosing one is
|
|
18
|
+
# used.
|
|
19
|
+
module Run
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
# Runs `argv` as a verb invocation or a step list against a domain.
|
|
23
|
+
#
|
|
24
|
+
# @param argv [Array<String>] the arguments: an optional domain directory, then
|
|
25
|
+
# a verb with `name=value` pairs, a script path, `-`, or inline JSON
|
|
26
|
+
# @param program [String] the name usage and help messages call this command by
|
|
27
|
+
# @return [void]
|
|
28
|
+
# @raise [SystemExit] on any refusal, usage error or unmet expectation, and
|
|
29
|
+
# after the command-line form answers
|
|
30
|
+
def call(argv, program:)
|
|
31
|
+
usage = "usage: #{program} [domain] <verb [name=value …] | script.json | - | '{\"steps\":[…]}'>"
|
|
32
|
+
argv = argv.dup
|
|
33
|
+
here = Adapters::Folder.new.domain_root
|
|
34
|
+
domain = argv.first && File.directory?(argv.first) ? argv.shift : here
|
|
35
|
+
abort "no bluebook here — #{Dir.pwd} is not inside a domain. #{usage}" unless domain
|
|
36
|
+
|
|
37
|
+
# The command-line form answers unless the first argument is script-shaped,
|
|
38
|
+
# in which case the step-list form takes over.
|
|
39
|
+
cli_form(domain, argv, program) unless argv.first && script_shaped?(argv.first)
|
|
40
|
+
|
|
41
|
+
script = argv.first or abort usage
|
|
42
|
+
document = parse(read_source(script))
|
|
43
|
+
steps = document["steps"] or abort %(the script has no "steps" — #{usage})
|
|
44
|
+
report = execute(Hecks.boot(domain), steps)
|
|
45
|
+
|
|
46
|
+
# The run is reported before it is judged: an expectation that fails says
|
|
47
|
+
# what was wanted and never what happened, so the evidence goes to stdout
|
|
48
|
+
# first and every unmet expectation is collected rather than aborted on.
|
|
49
|
+
puts JSON.pretty_generate(report)
|
|
50
|
+
unmet = unmet_expectations(document["expectations"] || {}, report)
|
|
51
|
+
abort unmet.join("\n") unless unmet.empty?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# No fall-through: reached only when the argument is not script-shaped, so a
|
|
55
|
+
# near miss deserves the runner's "did you mean" rather than the step-list
|
|
56
|
+
# form's "no such script".
|
|
57
|
+
def cli_form(domain, argv, program)
|
|
58
|
+
runtime = Hecks.boot(domain, install_facade: false)
|
|
59
|
+
text, status = Facade::CliRunner.call(runtime: runtime, argv: argv, program: program)
|
|
60
|
+
status.zero? ? puts(text) : abort(text)
|
|
61
|
+
exit 0
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def script_shaped?(arg) = arg == "-" || arg.match?(/\A\s*\{/) || File.file?(arg)
|
|
65
|
+
|
|
66
|
+
# Decided by looking, because a script beginning with `{` is not a filename
|
|
67
|
+
# anybody meant.
|
|
68
|
+
def read_source(script)
|
|
69
|
+
case script
|
|
70
|
+
when "-" then $stdin.read
|
|
71
|
+
when /\A\s*\{/ then script
|
|
72
|
+
else
|
|
73
|
+
File.exist?(script) ? File.read(script) : abort("no such script: #{script}")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Parsed before anything boots, because booting opens a real store and a typo
|
|
78
|
+
# in the JSON should cost a sentence, not a connection.
|
|
79
|
+
def parse(source)
|
|
80
|
+
JSON.parse(source)
|
|
81
|
+
rescue JSON::ParserError => e
|
|
82
|
+
abort "that is not JSON: #{e.message.lines.first.strip}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def execute(runtime, steps)
|
|
86
|
+
refusals = []
|
|
87
|
+
queries = []
|
|
88
|
+
steps.each { |step| run_step(runtime, step, refusals, queries) }
|
|
89
|
+
|
|
90
|
+
{ instances: instances(runtime), events: events(runtime), refusals: refusals,
|
|
91
|
+
reactions: runtime.reactions, sagas: runtime.sagas, queries: queries }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @api private
|
|
95
|
+
def run_step(runtime, step, refusals, queries)
|
|
96
|
+
args = (step["args"] || {}).transform_keys(&:to_sym)
|
|
97
|
+
if (question = step["query"])
|
|
98
|
+
queries << ask(runtime, question, args)
|
|
99
|
+
return
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
verb = step["verb"]
|
|
103
|
+
begin
|
|
104
|
+
runtime.dispatch_flat(verb, args)
|
|
105
|
+
rescue StandardError => e
|
|
106
|
+
refusals << { verb: verb, error: e.message.to_s }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# @api private
|
|
111
|
+
def ask(runtime, question, args)
|
|
112
|
+
{ query: question, args: args, rows: runtime.query(question, **args) }
|
|
113
|
+
rescue StandardError => e
|
|
114
|
+
{ query: question, args: args, error: e.message }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @api private
|
|
118
|
+
def instances(runtime)
|
|
119
|
+
runtime.registry.bluebooks.each_with_object({}) do |(domain_name, bluebook), all|
|
|
120
|
+
bluebook.aggregates.each do |aggregate|
|
|
121
|
+
runtime.registry.repository(domain_name, aggregate).all.each do |record|
|
|
122
|
+
all["#{domain_name}::#{aggregate.name}##{record.id}"] = record.state
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @api private
|
|
129
|
+
def events(runtime)
|
|
130
|
+
runtime.events.map do |event|
|
|
131
|
+
{ name: event.name, aggregate: event.aggregate, id: event.id, payload: event.payload }
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def unmet_expectations(expectations, report)
|
|
136
|
+
missing_events = Array(expectations["event_names"]) - report[:events].map { |event| event[:name] }
|
|
137
|
+
unmet = []
|
|
138
|
+
unmet << "matrix expected events missing: #{missing_events.join(', ')}" unless missing_events.empty?
|
|
139
|
+
unmet.concat(unmet_refusals(Array(expectations["refusals"]), report[:refusals]))
|
|
140
|
+
unmet.concat(unmet_instances(expectations["instances"] || {}, report[:instances]))
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# @api private
|
|
144
|
+
def unmet_refusals(expected_refusals, refusals)
|
|
145
|
+
expected_refusals.filter_map do |expected|
|
|
146
|
+
verb = expected.fetch("verb")
|
|
147
|
+
matched = refusals.any? do |refusal|
|
|
148
|
+
refusal[:verb] == verb && refusal[:error].include?(expected.fetch("includes"))
|
|
149
|
+
end
|
|
150
|
+
next if matched
|
|
151
|
+
|
|
152
|
+
# A refusal that changed its words is a different story from one that never
|
|
153
|
+
# happened, so the actual errors are printed beside the wanted one.
|
|
154
|
+
said = refusals.select { |refusal| refusal[:verb] == verb }.map { |refusal| refusal[:error] }.uniq
|
|
155
|
+
"matrix expected refusal missing: #{expected}\n " \
|
|
156
|
+
"#{verb} actually refused with: #{said.empty? ? '(nothing — every attempt was accepted)' : said.inspect}"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @api private
|
|
161
|
+
def unmet_instances(expected_instances, instances)
|
|
162
|
+
expected_instances.flat_map do |key, fields|
|
|
163
|
+
actual = instances[key] || {}
|
|
164
|
+
fields.filter_map do |field, value|
|
|
165
|
+
next if actual[field.to_sym] == value
|
|
166
|
+
|
|
167
|
+
"matrix expected #{key}.#{field}=#{value.inspect}, got #{actual[field.to_sym].inspect}"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require_relative "../../hecks"
|
|
2
|
+
require_relative "../bluebook/smoke_test"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module CLI
|
|
6
|
+
# The command behind `bin/smoke_test` and `hecks smoke_test`: boots a real domain
|
|
7
|
+
# and dispatches one synthesized call per declared command and report
|
|
8
|
+
# (`Bluebook::SmokeTest`), printing every failure rather than the first.
|
|
9
|
+
#
|
|
10
|
+
# Needs a real `.hecksagon`; a failure may be a domain bug or a value the
|
|
11
|
+
# synthesizer's naive choices cannot satisfy — smoke-testing proves nothing
|
|
12
|
+
# crashes, not that an answer is right.
|
|
13
|
+
module SmokeTest
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def call(argv, root:)
|
|
17
|
+
dir_arg = argv.first
|
|
18
|
+
targets = dir_arg ? [dir_arg] : Dir.glob("#{root}/examples/*/").map { |d| d.chomp("/") }.select { |d| wired?(d) }
|
|
19
|
+
abort "no wired example domains found (none of examples/* has a .hecksagon)" if targets.empty?
|
|
20
|
+
|
|
21
|
+
ok = targets.reduce(true) { |all_ok, dir| clean?(dir) && all_ok }
|
|
22
|
+
|
|
23
|
+
puts
|
|
24
|
+
puts ok ? "Every declared command and report dispatched cleanly." : "SMOKE TEST FOUND PROBLEMS."
|
|
25
|
+
exit(ok ? 0 : 1)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def wired?(dir) = !Dir.glob(File.join(dir, "**/*.hecksagon")).empty?
|
|
29
|
+
|
|
30
|
+
# @api private
|
|
31
|
+
def clean?(dir)
|
|
32
|
+
puts "── #{File.basename(dir)}"
|
|
33
|
+
failures = Bluebook::SmokeTest.call(dir)
|
|
34
|
+
if failures.empty?
|
|
35
|
+
puts " clean — every declared command and report dispatched cleanly"
|
|
36
|
+
return true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
puts " #{failures.size} failure(s)"
|
|
40
|
+
failures.each { |f| puts " #{f}" }
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "../../hecks"
|
|
3
|
+
|
|
4
|
+
module Hecks
|
|
5
|
+
module CLI
|
|
6
|
+
# The command behind `bin/stores` and `hecks stores`: every aggregate's current
|
|
7
|
+
# records as JSON (the head, not the journal — see `bin/history` for that).
|
|
8
|
+
module Stores
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
# Boots the domain `argv` names and prints its stores as one JSON document.
|
|
12
|
+
#
|
|
13
|
+
# @param argv [Array<String>] the domain directory, first
|
|
14
|
+
# @param program [String] the name the error message calls this command by
|
|
15
|
+
# @return [void]
|
|
16
|
+
# @raise [IndexError] when `argv` is empty
|
|
17
|
+
# @raise [SystemExit] when the domain directory does not exist
|
|
18
|
+
def call(argv, program:)
|
|
19
|
+
domain = argv.fetch(0)
|
|
20
|
+
unless Dir.exist?(domain)
|
|
21
|
+
warn "#{program}: no such domain #{domain.inspect}"
|
|
22
|
+
exit 1
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
registry = Hecks.boot(domain).registry
|
|
26
|
+
stores = registry.bluebooks.each_with_object({}) do |(name, bluebook), all|
|
|
27
|
+
bluebook.aggregates.each do |aggregate|
|
|
28
|
+
all[aggregate.storage_name] = data_for(registry.repository(name, aggregate))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
puts JSON.generate(stores)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Reads one aggregate's current records as plain JSON-ready hashes, ordered by id.
|
|
36
|
+
#
|
|
37
|
+
# @param repository [Ports::Persistence::AppendOnly] the aggregate's repository
|
|
38
|
+
# @return [Array<Hash{String => Object}>] one hash per stored `Runtime::Instance`,
|
|
39
|
+
# sorted by id; `[]` when the aggregate has no records
|
|
40
|
+
def records(repository)
|
|
41
|
+
repository.all.sort_by(&:id).map { |instance| JSON.parse(JSON.generate(instance.to_h)) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Shapes one aggregate's dump for the top-level stores document.
|
|
45
|
+
#
|
|
46
|
+
# @param repository [Ports::Persistence::AppendOnly] the aggregate's repository
|
|
47
|
+
# @return [Hash{String => Object}] `"authoritative"` mapped to `records(repository)`
|
|
48
|
+
def data_for(repository)
|
|
49
|
+
{ "authoritative" => records(repository) }
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
data/lib/hecks/cli.rb
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
# The `hecks` command an installed gem puts on the path: one router over the
|
|
3
|
+
# domain-operator tools, each also runnable from a checkout as `bin/<name>`.
|
|
4
|
+
#
|
|
5
|
+
# Every subcommand's logic lives under `cli/`, with `bin/<name>` a thin wrapper
|
|
6
|
+
# over the same entry point, so the two can't drift apart.
|
|
7
|
+
#
|
|
8
|
+
# See `docs/decisions/0066-the-gem-ships-a-hecks-executable-and-dev-tooling-stays-in-the-repo.md`.
|
|
9
|
+
module CLI
|
|
10
|
+
# One subcommand's help: a one-line summary and its usage line.
|
|
11
|
+
Command = Struct.new(:summary, :usage)
|
|
12
|
+
|
|
13
|
+
# Every subcommand, in the order the help lists them.
|
|
14
|
+
COMMANDS = {
|
|
15
|
+
"run" => Command.new(
|
|
16
|
+
"Dispatch a verb, or run a JSON step list, against a domain.",
|
|
17
|
+
"hecks run [domain] <verb [name=value …] | script.json | - | '{\"steps\":[…]}'>"
|
|
18
|
+
),
|
|
19
|
+
"docs" => Command.new(
|
|
20
|
+
"Print a domain's usage document, projected from its bluebook.",
|
|
21
|
+
"hecks docs [domain-path] [aggregate]"
|
|
22
|
+
),
|
|
23
|
+
"narrate" => Command.new(
|
|
24
|
+
"Print a domain read back in English, projected from its bluebook.",
|
|
25
|
+
"hecks narrate [domain-path] [aggregate]"
|
|
26
|
+
),
|
|
27
|
+
"ir" => Command.new(
|
|
28
|
+
"Print a booted domain's IR as JSON.",
|
|
29
|
+
"hecks ir <domain> [--translations] | hecks ir --meta"
|
|
30
|
+
),
|
|
31
|
+
"stores" => Command.new(
|
|
32
|
+
"Print every aggregate's current records as JSON.",
|
|
33
|
+
"hecks stores <domain>"
|
|
34
|
+
),
|
|
35
|
+
"model_check" => Command.new(
|
|
36
|
+
"Statically check a domain's IR for dead states and unreachable steps.",
|
|
37
|
+
"hecks model_check [--strict] [--profile client] <domain> [<domain> …]"
|
|
38
|
+
),
|
|
39
|
+
"smoke_test" => Command.new(
|
|
40
|
+
"Boot a domain and dispatch every declared command and report once.",
|
|
41
|
+
"hecks smoke_test [domain]"
|
|
42
|
+
),
|
|
43
|
+
"project_diagrams" => Command.new(
|
|
44
|
+
"Write a domain's Mermaid diagrams under ./docs/generated/diagrams/.",
|
|
45
|
+
"hecks project_diagrams <domain-path> <ChapterName>"
|
|
46
|
+
),
|
|
47
|
+
"project_cli" => Command.new(
|
|
48
|
+
"Write a command-line launcher beside each domain, named after its bluebook.",
|
|
49
|
+
"hecks project_cli [domain-path …]"
|
|
50
|
+
),
|
|
51
|
+
"mcp" => Command.new(
|
|
52
|
+
"Serve the MCP door over stdio (no authentication; stdio only).",
|
|
53
|
+
"hecks mcp [--stdio]"
|
|
54
|
+
)
|
|
55
|
+
}.freeze
|
|
56
|
+
|
|
57
|
+
HELP_FLAGS = ["--help", "-h"].freeze
|
|
58
|
+
|
|
59
|
+
# Printed above the usage line by `overview`, on every path that reaches it
|
|
60
|
+
# (a bare `hecks`, `hecks --help`, and an unknown subcommand alike).
|
|
61
|
+
HERO = <<~BANNER.freeze
|
|
62
|
+
# # ##### #### # # ####
|
|
63
|
+
# # # # # # #
|
|
64
|
+
##### #### # ## ###
|
|
65
|
+
# # # # # # #
|
|
66
|
+
# # ##### #### # # ####
|
|
67
|
+
|
|
68
|
+
It's all about the specs
|
|
69
|
+
BANNER
|
|
70
|
+
|
|
71
|
+
# Exit status for a missing or unknown subcommand.
|
|
72
|
+
USAGE_STATUS = 2
|
|
73
|
+
|
|
74
|
+
module_function
|
|
75
|
+
|
|
76
|
+
# Routes `argv` to its subcommand and runs it.
|
|
77
|
+
#
|
|
78
|
+
# A subcommand followed only by `--help`/`-h` prints its usage without
|
|
79
|
+
# running it; other arguments still reach the verb itself.
|
|
80
|
+
#
|
|
81
|
+
# @param argv [Array<String>] the command line, subcommand first
|
|
82
|
+
# @param out [#puts] where help goes
|
|
83
|
+
# @param err [#puts] where usage errors go
|
|
84
|
+
# @return [Integer] the process exit status for a subcommand that returns; a
|
|
85
|
+
# subcommand that fails exits the process itself
|
|
86
|
+
def start(argv, out: $stdout, err: $stderr)
|
|
87
|
+
name, *rest = argv
|
|
88
|
+
if name.nil? || !COMMANDS.key?(name)
|
|
89
|
+
asked = HELP_FLAGS.include?(name) || name == "help"
|
|
90
|
+
err.puts "hecks: unknown command #{name.inspect}" unless asked || name.nil?
|
|
91
|
+
overview(asked ? out : err)
|
|
92
|
+
return asked ? 0 : USAGE_STATUS
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if rest.length == 1 && HELP_FLAGS.include?(rest.first)
|
|
96
|
+
help(COMMANDS.fetch(name), out)
|
|
97
|
+
else
|
|
98
|
+
dispatch(name, rest)
|
|
99
|
+
end
|
|
100
|
+
0
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Runs one known subcommand with the working directory as its root.
|
|
104
|
+
#
|
|
105
|
+
# @param name [String] a key of `COMMANDS`
|
|
106
|
+
# @param argv [Array<String>] the arguments after the subcommand
|
|
107
|
+
# @return [void]
|
|
108
|
+
# @raise [SystemExit] when the subcommand exits the process itself
|
|
109
|
+
def dispatch(name, argv)
|
|
110
|
+
program = "hecks #{name}"
|
|
111
|
+
case name
|
|
112
|
+
when "run"
|
|
113
|
+
require_relative "cli/run"
|
|
114
|
+
Run.call(argv, program: program)
|
|
115
|
+
when "docs", "narrate"
|
|
116
|
+
require_relative "cli/document"
|
|
117
|
+
Document.call(argv, projection: name.to_sym, program: program, root: Dir.pwd)
|
|
118
|
+
when "ir"
|
|
119
|
+
require_relative "cli/ir"
|
|
120
|
+
Ir.call(argv, program: program)
|
|
121
|
+
when "stores"
|
|
122
|
+
require_relative "cli/stores"
|
|
123
|
+
Stores.call(argv, program: program)
|
|
124
|
+
when "model_check"
|
|
125
|
+
require_relative "cli/model_check"
|
|
126
|
+
ModelCheck.call(argv, program: program)
|
|
127
|
+
when "smoke_test"
|
|
128
|
+
require_relative "cli/smoke_test"
|
|
129
|
+
SmokeTest.call(argv, root: Dir.pwd)
|
|
130
|
+
when "project_diagrams"
|
|
131
|
+
require_relative "cli/project_diagrams"
|
|
132
|
+
ProjectDiagrams.call(argv, program: program, root: Dir.pwd)
|
|
133
|
+
when "project_cli"
|
|
134
|
+
require_relative "cli/project_cli"
|
|
135
|
+
ProjectCli.call(argv, program: program, root: Dir.pwd, remove_stale_bin: false)
|
|
136
|
+
when "mcp"
|
|
137
|
+
require_relative "cli/mcp"
|
|
138
|
+
Mcp.call(argv)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# @api private
|
|
143
|
+
def overview(io)
|
|
144
|
+
io.puts HERO
|
|
145
|
+
io.puts "usage: hecks <command> [arguments]"
|
|
146
|
+
io.puts ""
|
|
147
|
+
width = COMMANDS.keys.map(&:length).max
|
|
148
|
+
COMMANDS.each { |name, command| io.puts " #{name.ljust(width)} #{command.summary}" }
|
|
149
|
+
io.puts ""
|
|
150
|
+
io.puts "Run `hecks <command> --help` for one command's usage."
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# @api private
|
|
154
|
+
def help(command, io)
|
|
155
|
+
io.puts "usage: #{command.usage}"
|
|
156
|
+
io.puts ""
|
|
157
|
+
io.puts command.summary
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
data/lib/hecks/construct.rb
CHANGED
|
@@ -1,29 +1,12 @@
|
|
|
1
1
|
module Hecks
|
|
2
|
-
# The invisible
|
|
2
|
+
# The invisible identity a built construct carries: an owner chain under `hecks_`-prefixed
|
|
3
|
+
# fields, not an attribute and not a key in `to_h`.
|
|
3
4
|
#
|
|
4
|
-
#
|
|
5
|
+
# The fully-qualified name walks owners and matches the id `MetaValidator::Judge#identify` mints.
|
|
5
6
|
#
|
|
6
|
-
#
|
|
7
|
-
# owns its aggregates, an aggregate owns everything declared on it — and the
|
|
8
|
-
# bluebook identity is carried in its own field, under a `hecks_` prefix that
|
|
9
|
-
# no domain attribute can collide with. Invisible means exactly that: not an
|
|
10
|
-
# attribute, not a key in `to_h`, not a reader on instances. Framework
|
|
11
|
-
# metadata about the construct, not part of the domain it describes.
|
|
12
|
-
#
|
|
13
|
-
# The identity is computed by walking owners rather than stamped, so nothing
|
|
14
|
-
# has to be re-stamped when a chapter is assembled after its aggregates:
|
|
15
|
-
#
|
|
16
|
-
# Pizzas the chapter — no owner
|
|
7
|
+
# Pizzas the chapter, no owner
|
|
17
8
|
# Pizzas::Pizza an aggregate joins its chapter with ::
|
|
18
9
|
# Pizzas::Pizza.Price everything else joins its owner with .
|
|
19
|
-
#
|
|
20
|
-
# That spelling is not invented here. It is the id `MetaValidator::Judge`
|
|
21
|
-
# already mints in `#identify`, so a construct and the meta-domain's
|
|
22
|
-
# record of that construct carry the same identity, and there is no
|
|
23
|
-
# translation table between them to be quietly wrong in.
|
|
24
|
-
#
|
|
25
|
-
# ## Usage
|
|
26
|
-
#
|
|
27
10
|
# price = Class.new(ValueObject) # a declaration holder
|
|
28
11
|
# price.hecks_name = "Price"
|
|
29
12
|
# price.hecks_owner = pizza_ir # the Aggregate that declares it
|
|
@@ -32,15 +15,12 @@ module Hecks
|
|
|
32
15
|
# A construct asked for an identity it cannot compute.
|
|
33
16
|
class Unowned < StandardError; end
|
|
34
17
|
|
|
35
|
-
# What declares this
|
|
36
|
-
# above a value object. nil for a chapter, which is the top.
|
|
18
|
+
# What declares this construct; nil for a chapter, which is the top.
|
|
37
19
|
attr_accessor :hecks_owner
|
|
38
20
|
|
|
39
21
|
attr_writer :hecks_name
|
|
40
22
|
|
|
41
|
-
#
|
|
42
|
-
# else is declared in something, so a missing owner is an unstamped construct
|
|
43
|
-
# rather than a top — see hecks_fqn.
|
|
23
|
+
# Marks a chapter, the only construct that legitimately has no owner.
|
|
44
24
|
attr_writer :hecks_root
|
|
45
25
|
|
|
46
26
|
# Whether this construct is the top of its own owner chain.
|
|
@@ -53,18 +33,12 @@ module Hecks
|
|
|
53
33
|
# @return [String] the construct's own name, without any owner prefix
|
|
54
34
|
def hecks_name = @hecks_name
|
|
55
35
|
|
|
56
|
-
# How this construct joins its owner
|
|
57
|
-
# chapter's namespace (`::`) ; everything else is declared on its owner
|
|
58
|
-
# (`.`). Overridden by Aggregate, defaulted here for every other construct.
|
|
36
|
+
# How this construct joins its owner: `::` for an aggregate, `.` for everything else.
|
|
59
37
|
#
|
|
60
38
|
# @return [String] `"."`, the separator this construct uses in `hecks_fqn`
|
|
61
39
|
def hecks_separator = "."
|
|
62
40
|
|
|
63
|
-
# Refuses rather than
|
|
64
|
-
# chapter has simply not been stamped yet — entity commands are in that state
|
|
65
|
-
# while entities are still IR objects — and answering the bare name would be a
|
|
66
|
-
# plausible half-truth that no test would notice. That shape of falsehood is
|
|
67
|
-
# what this repo keeps finding, so it goes red instead.
|
|
41
|
+
# Refuses rather than answering a bare name for an unstamped construct.
|
|
68
42
|
#
|
|
69
43
|
# @return [String] the fully-qualified name, joining every owner from the
|
|
70
44
|
# chapter down to this construct
|