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
|
@@ -6,25 +6,10 @@ module Hecks
|
|
|
6
6
|
module Adapters
|
|
7
7
|
class PostgresEra
|
|
8
8
|
class Lineage
|
|
9
|
-
# CRUD over `hecks_eras`/`hecks_era_texts`/`hecks_approvals`:
|
|
10
|
-
#
|
|
11
|
-
# version, recording a Layer-3 approval, and minting an era's name
|
|
12
|
-
# once (never recomputed after).
|
|
9
|
+
# CRUD over `hecks_eras`/`hecks_era_texts`/`hecks_approvals`: era text
|
|
10
|
+
# integrity, archiving, Layer-3 approvals, and once-only era naming.
|
|
13
11
|
module EraStore
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# Every held text is verified against its raw-byte digest on the
|
|
17
|
-
# way out — an edited storage fact refuses rather than silently
|
|
18
|
-
# reporting "no drift". This is not the era name (which hashes
|
|
19
|
-
# the canonical projection, minted once, never recomputed): it
|
|
20
|
-
# is a plain integrity check over bytes, and a row that carries
|
|
21
|
-
# no digest yet is backfilled, not refused.
|
|
22
|
-
#
|
|
23
|
-
# @return [Array<Hash{Symbol => Object}>] one Hash per era in ordinal order, `[]` when
|
|
24
|
-
# the domain holds none; keys are `:ordinal` (Integer), `:hash` and `:label` (String,
|
|
25
|
-
# nil until `mint_name!` names the era), `:held_text` (String) and `:watermark`
|
|
26
|
-
# (Integer journal ordinal the era was cut at, nil for era 1)
|
|
27
|
-
# @raise [Runtime::WiringError] if a held text no longer matches its stored digest
|
|
12
|
+
# Verifies each held text's digest before returning it; a missing digest is backfilled.
|
|
28
13
|
def eras
|
|
29
14
|
@db.exec_params(
|
|
30
15
|
"SELECT ordinal, hash, label, held_text, watermark, held_digest, held_projection::text " \
|
|
@@ -37,15 +22,7 @@ module Hecks
|
|
|
37
22
|
end
|
|
38
23
|
end
|
|
39
24
|
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
# The unverified row — what bin/reattest_era shows an operator
|
|
43
|
-
# after the integrity check fires.
|
|
44
|
-
#
|
|
45
|
-
# @param ordinal [Integer] ordinal of the era to read
|
|
46
|
-
# @return [Hash{Symbol => String, nil}, nil] `:held_text`, `:held_digest` (SHA-256 hex,
|
|
47
|
-
# nil if never stored), `:hash` (nil until minted) and `:held_projection` (JSON text,
|
|
48
|
-
# nil if never stored); nil when the domain holds no such era
|
|
25
|
+
# Skips the integrity check `eras` runs, so bin/reattest_era can show the raw row first.
|
|
49
26
|
def raw_era(ordinal)
|
|
50
27
|
rows = @db.exec_params(
|
|
51
28
|
"SELECT held_text, held_digest, hash, held_projection::text FROM hecks_eras WHERE domain = $1 AND ordinal = $2",
|
|
@@ -57,17 +34,7 @@ module Hecks
|
|
|
57
34
|
held_projection: rows[0]["held_projection"] }
|
|
58
35
|
end
|
|
59
36
|
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
# The recovery path: tamper-evidence (against accident and
|
|
63
|
-
# drift, not adversaries) gets resolved by a human acknowledging
|
|
64
|
-
# the text as it now stands — recorded in an append-only
|
|
65
|
-
# attestation table, never by a bare psql `UPDATE`.
|
|
66
|
-
#
|
|
67
|
-
# @param ordinal [Integer] ordinal of the era to re-attest
|
|
68
|
-
# @return [String] the new SHA-256 hex digest of the held text
|
|
69
|
-
# @raise [Runtime::WiringError] if the domain holds no era with that ordinal
|
|
70
|
-
# @raise [PG::Error] if Postgres refuses the attestation DDL or a write
|
|
37
|
+
# Resolves drift via an append-only attestation record — never patch the digest.
|
|
71
38
|
def reattest!(ordinal)
|
|
72
39
|
era = raw_era(ordinal)
|
|
73
40
|
raise Runtime::WiringError, "#{@domain} holds no era #{ordinal} to re-attest" unless era
|
|
@@ -94,19 +61,7 @@ module Hecks
|
|
|
94
61
|
fresh
|
|
95
62
|
end
|
|
96
63
|
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
# A text that passes (or that had no digest to fail against) also gets
|
|
100
|
-
# `backfill_frozen_facts!`; a text that fails gets nothing written.
|
|
101
|
-
#
|
|
102
|
-
# @param ordinal [Integer] ordinal of the era the text belongs to
|
|
103
|
-
# @param text [String] the era's held bluebook source text
|
|
104
|
-
# @param stored_digest [String, nil] SHA-256 hex digest stored with the row; nil when
|
|
105
|
-
# the row carries none yet
|
|
106
|
-
# @param stored_projection_json [String, nil] the row's stored projection as JSON text;
|
|
107
|
-
# nil when the row carries none yet
|
|
108
|
-
# @return [void]
|
|
109
|
-
# @raise [Runtime::WiringError] if `stored_digest` is present and does not match `text`
|
|
64
|
+
# A row with no stored digest yet is backfilled, not refused; only a mismatch raises.
|
|
110
65
|
def verify_integrity!(ordinal, text, stored_digest, stored_projection_json)
|
|
111
66
|
digest = Digest::SHA256.hexdigest(text)
|
|
112
67
|
if stored_digest.nil?
|
|
@@ -125,17 +80,7 @@ module Hecks
|
|
|
125
80
|
raise Runtime::WiringError, Runtime::EraTamper.refusal(domain: @domain, ordinal: ordinal)
|
|
126
81
|
end
|
|
127
82
|
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# A verified-authentic text backfills what a row may lack: its
|
|
131
|
-
# projection and its archive copy. Never run on a text that
|
|
132
|
-
# failed verification — that would bless the edit.
|
|
133
|
-
#
|
|
134
|
-
# @param ordinal [Integer] ordinal of the era the text belongs to
|
|
135
|
-
# @param text [String] the era's verified held source text
|
|
136
|
-
# @param stored_projection_json [String, nil] the row's stored projection as JSON text;
|
|
137
|
-
# nil makes this derive one from `text` and store it, unless `text` does not load
|
|
138
|
-
# @return [void]
|
|
83
|
+
# Call only on already-verified text; this would bless an unverified edit.
|
|
139
84
|
def backfill_frozen_facts!(ordinal, text, stored_projection_json)
|
|
140
85
|
archive_text!(ordinal, text)
|
|
141
86
|
return if stored_projection_json
|
|
@@ -150,27 +95,12 @@ module Hecks
|
|
|
150
95
|
)
|
|
151
96
|
end
|
|
152
97
|
|
|
153
|
-
# Reports the ordinal of the newest era the domain holds.
|
|
154
|
-
#
|
|
155
|
-
# @return [Integer] the highest held ordinal, or 1 when no era is held yet
|
|
156
|
-
# @raise [Runtime::WiringError] if a held text no longer matches its stored digest
|
|
157
98
|
def current_era
|
|
158
99
|
held = eras
|
|
159
100
|
held.empty? ? 1 : held.last[:ordinal]
|
|
160
101
|
end
|
|
161
102
|
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
# Call only while the domain holds no era — every caller checks `eras.empty?` first.
|
|
165
|
-
# An existing era 1 row is kept (`ON CONFLICT DO NOTHING`), but `advance_era!(1)` runs
|
|
166
|
-
# regardless, so calling this on a domain past era 1 would roll the write fence back.
|
|
167
|
-
#
|
|
168
|
-
# @param text [String] the bluebook source text to freeze as era 1
|
|
169
|
-
# @param projection [Hash{String => Object}, nil] the storage-shape projection of the
|
|
170
|
-
# bluebook, as `Runtime::StorageShape.project` returns it; nil stores none
|
|
171
|
-
# @return [void]
|
|
172
|
-
# @raise [PG::Error] if Postgres refuses the insert or the policy change, as it does
|
|
173
|
-
# for a role that does not own the journal
|
|
103
|
+
# Call only while the domain holds no era; this always advances the write fence to era 1.
|
|
174
104
|
def hold_first!(text, projection: nil)
|
|
175
105
|
@db.exec_params(
|
|
176
106
|
"INSERT INTO hecks_eras (domain, ordinal, held_text, held_digest, held_projection) " \
|
|
@@ -178,20 +108,11 @@ module Hecks
|
|
|
178
108
|
[@domain, text, Digest::SHA256.hexdigest(text), projection && JSON.generate(projection)]
|
|
179
109
|
)
|
|
180
110
|
archive_text!(1, text)
|
|
181
|
-
#
|
|
182
|
-
# here, not left to whichever role happens to boot first.
|
|
111
|
+
# Set explicitly here, not left to whichever role happens to boot first.
|
|
183
112
|
advance_era!(1)
|
|
184
113
|
end
|
|
185
114
|
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
# A version already archived is left alone (`ON CONFLICT DO NOTHING`), so every
|
|
189
|
-
# distinct text an era ever held survives a re-attestation.
|
|
190
|
-
#
|
|
191
|
-
# @param ordinal [Integer] ordinal of the era the text belongs to
|
|
192
|
-
# @param text [String] the source text to archive
|
|
193
|
-
# @return [void]
|
|
194
|
-
# @raise [PG::Error] if Postgres refuses the insert
|
|
115
|
+
# Left alone if already archived, so re-attestation never duplicates a text version.
|
|
195
116
|
def archive_text!(ordinal, text)
|
|
196
117
|
@db.exec_params(
|
|
197
118
|
"INSERT INTO hecks_era_texts (domain, ordinal, digest, held_text) VALUES ($1, $2, $3, $4) " \
|
|
@@ -200,19 +121,7 @@ module Hecks
|
|
|
200
121
|
)
|
|
201
122
|
end
|
|
202
123
|
|
|
203
|
-
#
|
|
204
|
-
#
|
|
205
|
-
# A Layer-3 approval, recorded in the database it was reviewed
|
|
206
|
-
# against — bound to the edge's parsed content and the journal's
|
|
207
|
-
# high-water ordinal at review time. The latest row for a shape
|
|
208
|
-
# pair wins (re-approval supersedes).
|
|
209
|
-
#
|
|
210
|
-
# @param from [String] label of the era shape the edge leaves
|
|
211
|
-
# @param to [String] label of the era shape the edge leads to
|
|
212
|
-
# @param edge_digest [String] SHA-256 hex digest of the edge's parsed content, as
|
|
213
|
-
# `Translation::Audit.edge_digest` computes it
|
|
214
|
-
# @return [void]
|
|
215
|
-
# @raise [PG::Error] if Postgres refuses the insert
|
|
124
|
+
# Always inserts a new row; `approval_for` picks the latest for a shape pair.
|
|
216
125
|
def record_approval!(from:, to:, edge_digest:)
|
|
217
126
|
@db.exec_params(
|
|
218
127
|
"INSERT INTO hecks_approvals (domain, from_label, to_label, edge_digest, reviewed_ordinal) " \
|
|
@@ -221,13 +130,6 @@ module Hecks
|
|
|
221
130
|
)
|
|
222
131
|
end
|
|
223
132
|
|
|
224
|
-
# Finds the most recent approval recorded for the edge between two era shapes.
|
|
225
|
-
#
|
|
226
|
-
# @param from [String] label of the era shape the edge leaves
|
|
227
|
-
# @param to [String] label of the era shape the edge leads to
|
|
228
|
-
# @return [Hash{Symbol => Object}, nil] `:edge_digest` (String, SHA-256 hex of the edge
|
|
229
|
-
# that was reviewed) and `:reviewed_ordinal` (Integer, the journal's highest ordinal at
|
|
230
|
-
# review time); nil when no approval is recorded for the pair
|
|
231
133
|
def approval_for(from:, to:)
|
|
232
134
|
rows = @db.exec_params(
|
|
233
135
|
"SELECT edge_digest, reviewed_ordinal FROM hecks_approvals " \
|
|
@@ -239,16 +141,7 @@ module Hecks
|
|
|
239
141
|
{ edge_digest: rows[0]["edge_digest"], reviewed_ordinal: rows[0]["reviewed_ordinal"].to_i }
|
|
240
142
|
end
|
|
241
143
|
|
|
242
|
-
#
|
|
243
|
-
#
|
|
244
|
-
# Era identity is minted once and stored; nothing ever recomputes
|
|
245
|
-
# a stored name to verify it.
|
|
246
|
-
#
|
|
247
|
-
# @param ordinal [Integer] ordinal of the era to name
|
|
248
|
-
# @param hash [String] the era's shape hash, SHA-256 hex
|
|
249
|
-
# @param label [String] the short label edges and refusals use, a prefix of `hash`
|
|
250
|
-
# @return [void]
|
|
251
|
-
# @raise [PG::Error] if Postgres refuses the update
|
|
144
|
+
# Sets hash/label only once; nothing ever recomputes a stored name to verify it.
|
|
252
145
|
def mint_name!(ordinal, hash, label)
|
|
253
146
|
@db.exec_params(
|
|
254
147
|
"UPDATE hecks_eras SET hash = $3, label = $4, canon_form = $5 " \
|
|
@@ -257,9 +150,6 @@ module Hecks
|
|
|
257
150
|
)
|
|
258
151
|
end
|
|
259
152
|
|
|
260
|
-
# Reads the journal's high-water mark across every era's partition.
|
|
261
|
-
#
|
|
262
|
-
# @return [Integer] the highest ordinal in the journal, or 0 when the journal is empty
|
|
263
153
|
def last_ordinal
|
|
264
154
|
@db.exec("SELECT COALESCE(max(ordinal), 0) AS o FROM #{quoted_journal}")[0]["o"].to_i
|
|
265
155
|
end
|
|
@@ -4,105 +4,39 @@ module Hecks
|
|
|
4
4
|
module Adapters
|
|
5
5
|
class PostgresEra
|
|
6
6
|
class Lineage
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# Per-`where`-field cache tables that let a declared query find ids without reducing
|
|
8
|
+
# the whole aggregate. Holds (id, ordinal, value); lists are not cached
|
|
9
|
+
# (see `eligible_for_cache?`).
|
|
9
10
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# second era, `head_view` is a `DISTINCT ON`/`UNION ALL`
|
|
14
|
-
# reduction — and a `where` clause on anything but `id` (its own
|
|
15
|
-
# partition key) cannot be pushed through that reduction, so every
|
|
16
|
-
# declared query pays the cost of reducing the whole aggregate
|
|
17
|
-
# before it can filter anything (see docs/implemented/postgres-era-adapter-
|
|
18
|
-
# split-plan.md's own trigger section — this is a real
|
|
19
|
-
# SQL-semantics wall, not a missing index; adding one to
|
|
20
|
-
# `head_view` itself changes nothing, because the planner cannot
|
|
21
|
-
# prove a predicate on a non-partition column commutes with
|
|
22
|
-
# `DISTINCT ON` without risking a stale row winning).
|
|
23
|
-
#
|
|
24
|
-
# ## The table
|
|
25
|
-
#
|
|
26
|
-
# **The workaround**: one narrow table per declared `where`-field
|
|
27
|
-
# (never `order_by`-only fields — sorting the reduced output is
|
|
28
|
-
# not blocked by the reduction; only filtering is), holding
|
|
29
|
-
# exactly (id, ordinal, value) — the current extracted value for
|
|
30
|
-
# every live id, maintained transactionally by `append` alongside
|
|
31
|
-
# the head snapshot it already upserts (same ordinal-guard idiom:
|
|
32
|
-
# `WHERE ordinal < EXCLUDED.ordinal`). A query whose sole `where`
|
|
33
|
-
# is on a cached field never runs the reduction at all: `SELECT id
|
|
34
|
-
# FROM <field>_cache WHERE value <op> $1`, then `SELECT id, state
|
|
35
|
-
# FROM head_view WHERE id = ANY($ids)` — safe through the
|
|
36
|
-
# reduction because `id` is its own partition key, unlike the
|
|
37
|
-
# original field.
|
|
38
|
-
#
|
|
39
|
-
# ## What it does not cover
|
|
40
|
-
#
|
|
41
|
-
# Scoped to non-list fields only, same reasoning Track A (plain
|
|
42
|
-
# Postgres) and Track B (Sqlite/D1) both landed on independently
|
|
43
|
-
# for their own automatic indexing: this codebase's `contains`
|
|
44
|
-
# compiles to element-membership (`jsonb_array_elements`/
|
|
45
|
-
# `json_each` + `EXISTS`), which a (id, ordinal, value) cache table
|
|
46
|
-
# keyed on one scalar value per id has no way to represent — a
|
|
47
|
-
# list field would need one row per (id, element), a different
|
|
48
|
-
# shape this plan does not build. `PostgresEra#eligible_for_cache?`
|
|
49
|
-
# is where that boundary lives.
|
|
11
|
+
# A `where` on a non-`id` field cannot be pushed through the `DISTINCT ON` reduction in
|
|
12
|
+
# `head_view`, so a query on a cached field reads `<field>_cache` first, then `head_view`
|
|
13
|
+
# by `id` (its partition key).
|
|
50
14
|
module FieldCache
|
|
51
15
|
# Names the cache table for one field of one aggregate in one era.
|
|
52
16
|
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
# bytes, and a value-object member path (`compliance.review.
|
|
56
|
-
# deadline_at`) combined with a long storage name could exceed
|
|
57
|
-
# that if built any less defensively. Not meant to be
|
|
58
|
-
# human-readable; `hecks_backfill_progress`/catalog lookups are
|
|
59
|
-
# always driven by this same computed name, never typed by hand.
|
|
60
|
-
#
|
|
61
|
-
# `@domain` is part of the hash input — this table is exposed to
|
|
62
|
-
# the exact same collision this whole file's siblings
|
|
63
|
-
# (`head_view`/`head_snapshot`/`matview`, `lineage.rb`) are
|
|
64
|
-
# domain-qualified against in docs/decisions/0059: without it,
|
|
65
|
-
# two domains bound to PostgresEra against the same database,
|
|
66
|
-
# each with an aggregate sharing a storage_name and a cached
|
|
67
|
-
# where-field of the same name, derive the identical
|
|
68
|
-
# `hecks_fc_<hash>` table and silently share cached rows across
|
|
69
|
-
# domains. Already fully hashed, so folding the domain in costs
|
|
70
|
-
# nothing readability could lose.
|
|
17
|
+
# Hashed to stay under Postgres's 63-byte identifier limit; `@domain` is hashed in
|
|
18
|
+
# so two domains sharing a database never share a table (ADR 0059).
|
|
71
19
|
#
|
|
72
20
|
# @param storage_name [String] the aggregate's snake-cased storage name
|
|
73
21
|
# @param era [Integer] ordinal of the era the cache belongs to
|
|
74
|
-
# @param field [String] the cached `where` field,
|
|
75
|
-
# value-object member such as `"compliance.review.deadline_at"`
|
|
22
|
+
# @param field [String] the cached `where` field, dotted for a value-object member
|
|
76
23
|
# @return [String] unquoted table name, `hecks_fc_` plus 20 hex characters
|
|
77
24
|
def field_cache(storage_name, era, field)
|
|
78
25
|
"hecks_fc_#{Digest::SHA256.hexdigest("#{@domain}\0#{storage_name}\0#{era}\0#{field}")[0, 20]}"
|
|
79
26
|
end
|
|
80
27
|
|
|
81
|
-
# Creates one field's cache table if
|
|
82
|
-
# from the current head until the backfill is recorded complete.
|
|
28
|
+
# Creates one field's cache table if missing, then backfills it from the current head.
|
|
83
29
|
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
# backfill runs outside it, chunk by chunk, each chunk its own
|
|
88
|
-
# short lock — never one transaction spanning the scan, per
|
|
89
|
-
# principle 1. `value_expression` is the exact SQL
|
|
90
|
-
# `PostgresEra#query_expression(field)` already compiles for
|
|
91
|
-
# this field, applied against a `state` column this module's own
|
|
92
|
-
# SQL always makes available in scope (see `field_cache_source_sql`)
|
|
93
|
-
# — one source of truth for "what does this field mean", not a
|
|
94
|
-
# second hand-rolled copy that could silently drift from what a
|
|
95
|
-
# live query actually filters on.
|
|
30
|
+
# Safe to call on every boot. Creation holds a short lock; the backfill runs outside it,
|
|
31
|
+
# one chunk per lock, never one transaction over the scan. `value_expression` is the
|
|
32
|
+
# SQL `query_expression(field)` compiles, so cache and live queries agree on the value.
|
|
96
33
|
#
|
|
97
34
|
# @param storage_name [String] the aggregate's snake-cased storage name
|
|
98
35
|
# @param era [Integer] ordinal of the era the cache belongs to
|
|
99
36
|
# @param field [String] the cached `where` field, dotted for a value-object member
|
|
100
|
-
# @param value_expression [String] SQL
|
|
101
|
-
# value as text from a jsonb column named `state`
|
|
37
|
+
# @param value_expression [String] SQL extracting the value as text from jsonb `state`
|
|
102
38
|
# @return [String] unquoted name of the cache table, as `field_cache` derives it
|
|
103
39
|
# @raise [PG::Error] if Postgres refuses the DDL, a source read, or an upsert
|
|
104
|
-
# @raise [Runtime::WiringError] if `era` is past 1, a chunk is still to be
|
|
105
|
-
# backfilled, and a held era's text fails its digest check (see `eras`)
|
|
106
40
|
def ensure_field_cache!(storage_name, era, field, value_expression)
|
|
107
41
|
name = field_cache(storage_name, era, field)
|
|
108
42
|
unless table_exists?(name)
|
|
@@ -124,19 +58,15 @@ module Hecks
|
|
|
124
58
|
name
|
|
125
59
|
end
|
|
126
60
|
|
|
127
|
-
# Fills an existing cache table from the reduced head, one committed chunk
|
|
128
|
-
#
|
|
129
|
-
# held in `hecks_backfill_progress` and doing nothing once it is complete.
|
|
61
|
+
# Fills an existing cache table from the reduced head, one committed chunk at a time,
|
|
62
|
+
# resuming from the cursor in `hecks_backfill_progress`; a no-op once complete.
|
|
130
63
|
#
|
|
131
64
|
# @param name [String] unquoted cache table name, from `field_cache`
|
|
132
65
|
# @param storage_name [String] the aggregate's snake-cased storage name
|
|
133
66
|
# @param era [Integer] ordinal of the era whose head is read
|
|
134
|
-
# @param value_expression [String] SQL
|
|
135
|
-
# value as text from a jsonb column named `state`
|
|
67
|
+
# @param value_expression [String] SQL extracting the value as text from jsonb `state`
|
|
136
68
|
# @return [void]
|
|
137
69
|
# @raise [PG::Error] if Postgres refuses a source read or an upsert
|
|
138
|
-
# @raise [Runtime::WiringError] if `era` is past 1, a chunk is still to be
|
|
139
|
-
# backfilled, and a held era's text fails its digest check (see `eras`)
|
|
140
70
|
def backfill_field_cache!(name, storage_name, era, value_expression)
|
|
141
71
|
chunked_backfill!(
|
|
142
72
|
name,
|
|
@@ -153,27 +83,16 @@ module Hecks
|
|
|
153
83
|
)
|
|
154
84
|
end
|
|
155
85
|
|
|
156
|
-
# Upserts one id's cached value from the state being written,
|
|
157
|
-
#
|
|
86
|
+
# Upserts one id's cached value from the state being written, unless the row already
|
|
87
|
+
# carries an equal or newer ordinal.
|
|
158
88
|
#
|
|
159
|
-
#
|
|
160
|
-
# transaction as the journal insert and the head-snapshot
|
|
161
|
-
# upsert, for every field this aggregate has a cache table for.
|
|
162
|
-
# `state_json` is the entry's own new state (already the thing
|
|
163
|
-
# `append` is about to write) — bound as a literal jsonb value
|
|
164
|
-
# and aliased `state`, so `value_expression` (built to read a
|
|
165
|
-
# column literally named `state`) evaluates identically here and
|
|
166
|
-
# in every backfill/query-time use, with no separate Ruby-side
|
|
167
|
-
# re-implementation of "how to pick a value-object's member" to
|
|
168
|
-
# keep in sync.
|
|
89
|
+
# Runs in the journal insert's transaction; `state_json` binds as jsonb `state`.
|
|
169
90
|
#
|
|
170
91
|
# @param name [String] unquoted cache table name, from `field_cache`
|
|
171
92
|
# @param id [String] the aggregate id the entry belongs to
|
|
172
|
-
# @param ordinal [String, Integer] the journal ordinal the entry was written
|
|
173
|
-
# at; `append` passes the decimal text Postgres returned from its INSERT
|
|
93
|
+
# @param ordinal [String, Integer] the journal ordinal the entry was written at
|
|
174
94
|
# @param state_json [String] the entry's encoded state, as JSON text
|
|
175
|
-
# @param value_expression [String] SQL
|
|
176
|
-
# value as text from a jsonb column named `state`
|
|
95
|
+
# @param value_expression [String] SQL extracting the value as text from jsonb `state`
|
|
177
96
|
# @return [void]
|
|
178
97
|
# @raise [PG::Error] if Postgres refuses the upsert
|
|
179
98
|
def upsert_field_cache_row!(name, id, ordinal, state_json, value_expression)
|
|
@@ -186,38 +105,21 @@ module Hecks
|
|
|
186
105
|
SQL
|
|
187
106
|
end
|
|
188
107
|
|
|
189
|
-
# Removes a deleted id's row
|
|
190
|
-
#
|
|
191
|
-
# same transaction as the journal insert and the head-snapshot tombstone.
|
|
108
|
+
# Removes a deleted id's row so a cached-field query stops offering it. Called from
|
|
109
|
+
# `append` for a delete entry, in the same transaction as the journal insert.
|
|
192
110
|
#
|
|
193
111
|
# @param name [String] unquoted cache table name, from `field_cache`
|
|
194
112
|
# @param id [String] the aggregate id whose row is removed
|
|
195
113
|
# @return [void]
|
|
196
|
-
# @raise [PG::Error] if Postgres refuses the delete
|
|
197
114
|
def delete_field_cache_row!(name, id)
|
|
198
115
|
@db.exec_params("DELETE FROM #{quote(name)} WHERE id = $1", [id])
|
|
199
116
|
end
|
|
200
117
|
|
|
201
118
|
private
|
|
202
119
|
|
|
203
|
-
# The reduced (id, ordinal, state) source a
|
|
204
|
-
#
|
|
205
|
-
#
|
|
206
|
-
# which for era > 1 can come from either this era's own live
|
|
207
|
-
# writes or an untouched-this-era id whose value still comes
|
|
208
|
-
# from the translated ancestor tail. Era 1 has no ancestor tail
|
|
209
|
-
# (the snapshot table is the head, verbatim); era N reduces the
|
|
210
|
-
# same two sources `compile_head!`'s own final view definition
|
|
211
|
-
# unions (head_compiler.rb) — this is deliberately the identical
|
|
212
|
-
# shape, ordinal kept in the SELECT list instead of dropped,
|
|
213
|
-
# because a backfilled cache row needs a real ordinal to guard
|
|
214
|
-
# against a concurrent live write the same way every other
|
|
215
|
-
# upsert in this adapter already does. Ordinals are safe to
|
|
216
|
-
# compare across that union because they come from one spanning
|
|
217
|
-
# sequence per domain (see lineage.rb's own header comment) —
|
|
218
|
-
# an ancestor-era ordinal is always numerically smaller than any
|
|
219
|
-
# current-era one, so the ordinary `ordinal <` guard already
|
|
220
|
-
# means the right thing with no era-aware special case.
|
|
120
|
+
# The reduced (id, ordinal, state) source a cache backfills from. Unlike the head
|
|
121
|
+
# snapshot's source it reduces the ancestor tail too (era N > 1 mirrors `compile_head!`)
|
|
122
|
+
# with ordinal kept; ordinals share one sequence per domain, so `ordinal <` holds.
|
|
221
123
|
def field_cache_source_sql(storage_name, era, value_expression)
|
|
222
124
|
era = era.to_i
|
|
223
125
|
if era == 1
|
|
@@ -231,9 +133,7 @@ module Hecks
|
|
|
231
133
|
if view && view_exists?(view)
|
|
232
134
|
"SELECT ordinal, aggregate_id AS id, state FROM #{quote(view)} WHERE operation = 'save'"
|
|
233
135
|
else
|
|
234
|
-
# No compiled ancestor matview yet (
|
|
235
|
-
# minted and compile_head! hasn't run for this aggregate) —
|
|
236
|
-
# the live snapshot side alone is the whole truth so far.
|
|
136
|
+
# No compiled ancestor matview yet (era just minted): snapshot side only.
|
|
237
137
|
"SELECT NULL::bigint AS ordinal, NULL::text AS id, NULL::jsonb AS state WHERE FALSE"
|
|
238
138
|
end
|
|
239
139
|
|