hecks 2.6.0 → 2.8.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 +13 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +1 -1
- data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
- data/lib/hecks/adapters/driven/memory.rb +1 -1
- data/lib/hecks/adapters/driven/postgres.rb +1 -1
- data/lib/hecks/adapters/driven/prism.rb +2 -2
- data/lib/hecks/adapters/driven/sqlite/projection.rb +1 -1
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +31 -10
- data/lib/hecks/behaviors/expectations.rb +2 -2
- data/lib/hecks/bluebook/assembly/contract.rb +1 -1
- data/lib/hecks/bluebook/assembly/contracts.rb +5 -5
- data/lib/hecks/bluebook/assembly/specializer.rb +2 -2
- data/lib/hecks/bluebook/assembly.rb +1 -1
- data/lib/hecks/bluebook/behaviour/chapter.rb +5 -2
- data/lib/hecks/bluebook/behaviour/read_model.rb +18 -0
- data/lib/hecks/bluebook/capabilities.rb +2 -2
- data/lib/hecks/bluebook/chapter.rb +1 -1
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +2 -2
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +6 -1
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +2 -2
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +3 -3
- data/lib/hecks/bluebook/dsl/entity_builder.rb +1 -1
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +1 -1
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +14 -9
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +1 -1
- data/lib/hecks/bluebook/dsl/rule_reference.rb +3 -3
- data/lib/hecks/bluebook/dsl/translation_builder.rb +1 -1
- data/lib/hecks/bluebook/dsl/world_builder.rb +47 -3
- data/lib/hecks/bluebook/hexagon.rb +15 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +3 -3
- data/lib/hecks/bluebook/meta_validator/plan.rb +4 -4
- data/lib/hecks/bluebook/meta_validator/readings.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +2 -2
- data/lib/hecks/bluebook/meta_validator/shapes.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +11 -10
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +3 -1
- data/lib/hecks/bluebook/meta_validator.rb +1 -1
- data/lib/hecks/bluebook/model_check/client_profile.rb +4 -70
- data/lib/hecks/bluebook/model_check.rb +13 -14
- data/lib/hecks/cache_dir.rb +103 -0
- data/lib/hecks/cli/document.rb +104 -0
- data/lib/hecks/cli/ir.rb +47 -0
- data/lib/hecks/cli/mcp.rb +32 -0
- data/lib/hecks/cli/mcp_door.rb +461 -0
- data/lib/hecks/cli/model_check.rb +270 -0
- data/lib/hecks/cli/project_cli.rb +123 -0
- data/lib/hecks/cli/project_diagrams.rb +43 -0
- data/lib/hecks/cli/run.rb +219 -0
- data/lib/hecks/cli/smoke_test.rb +59 -0
- data/lib/hecks/cli/stores.rb +54 -0
- data/lib/hecks/cli.rb +166 -0
- data/lib/hecks/embryonaut_bluebook/lock.rb +72 -0
- data/lib/hecks/embryonaut_bluebook/shape.rb +47 -0
- data/lib/hecks/embryonaut_bluebook/vendor.rb +194 -0
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +88 -0
- data/lib/hecks/embryonaut_bluebook.rb +29 -3
- data/lib/hecks/facade/handle.rb +2 -2
- data/lib/hecks/facade/surface/aggregate_door.rb +1 -1
- data/lib/hecks/forms/value_object_shape.rb +1 -1
- data/lib/hecks/framework/bluebook/console_settings.bluebook +3 -3
- data/lib/hecks/language/bluebook/vocabulary.bluebook +16 -0
- data/lib/hecks/language/world/world.bluebook +32 -2
- data/lib/hecks/mcp_door_scope.rb +176 -0
- data/lib/hecks/ports/key_vault.rb +1 -1
- data/lib/hecks/ports/persistence/append_only.rb +1 -1
- data/lib/hecks/ports/persistence/binding_policy.rb +16 -5
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -2
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +132 -0
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +13 -7
- data/lib/hecks/ports/persistence/plugins/era.rb +7 -4
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +73 -0
- data/lib/hecks/ports/persistence/postgres_dump.rb +125 -0
- data/lib/hecks/ports/persistence/repository_factory.rb +2 -2
- data/lib/hecks/ports/persistence/state_codec.rb +1 -1
- data/lib/hecks/ports/persistence.rb +4 -0
- data/lib/hecks/projections/deploy/fargate/assembly.rb +193 -0
- data/lib/hecks/projections/deploy/fargate/cdn.rb +318 -0
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +87 -0
- data/lib/hecks/projections/deploy/fargate/check.rb +172 -0
- data/lib/hecks/projections/deploy/fargate/containers.rb +412 -0
- data/lib/hecks/projections/deploy/fargate/extras.rb +268 -0
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +448 -0
- data/lib/hecks/projections/deploy/fargate/settings.rb +235 -0
- data/lib/hecks/projections/deploy/fargate/yaml.rb +116 -0
- data/lib/hecks/projections/deploy/fargate.rb +188 -139
- data/lib/hecks/projections/deploy/lambda.rb +8 -8
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +145 -0
- data/lib/hecks/projections/deploy/preview/containers.rb +218 -0
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +446 -0
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +216 -0
- data/lib/hecks/projections/deploy/preview/script.rb +40 -0
- data/lib/hecks/projections/deploy/preview/settings.rb +140 -0
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +215 -0
- data/lib/hecks/projections/deploy/preview/template.rb +247 -0
- data/lib/hecks/projections/deploy/preview/yaml_text.rb +48 -0
- data/lib/hecks/projections/deploy/preview.rb +103 -0
- data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +182 -0
- data/lib/hecks/projections/deploy/scripts/expected-era.header +17 -0
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +54 -0
- data/lib/hecks/projections/deploy/scripts/settings.rb +114 -0
- data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +232 -0
- data/lib/hecks/projections/deploy/scripts.rb +171 -0
- data/lib/hecks/projections/deploy/smoke/harness.js +366 -0
- data/lib/hecks/projections/deploy/smoke/workflow.yml.tmpl +56 -0
- data/lib/hecks/projections/deploy/smoke.rb +159 -0
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +231 -0
- data/lib/hecks/projections/deploy/template_diff/loader.rb +105 -0
- data/lib/hecks/projections/deploy/template_diff.rb +173 -0
- data/lib/hecks/projections/glossary/sensitivity.rb +1 -1
- data/lib/hecks/projections/reference.rb +7 -1
- data/lib/hecks/projections.rb +2 -0
- data/lib/hecks/projector/exporter.rb +4 -4
- data/lib/hecks/projector/target.rb +1 -1
- data/lib/hecks/release/gem_pin/published_versions.rb +58 -0
- data/lib/hecks/release/gem_pin.rb +112 -0
- data/lib/hecks/release/runner/ci_publisher.rb +96 -0
- data/lib/hecks/release/runner/commands.rb +68 -0
- data/lib/hecks/release/runner/console.rb +52 -0
- data/lib/hecks/release/runner/gem_publisher.rb +44 -0
- data/lib/hecks/release/runner/git.rb +48 -0
- data/lib/hecks/release/runner/npm_publisher.rb +89 -0
- data/lib/hecks/release/runner/preflight.rb +119 -0
- data/lib/hecks/release/runner/published.rb +55 -0
- data/lib/hecks/release/runner/tagger.rb +87 -0
- data/lib/hecks/release/runner.rb +244 -0
- data/lib/hecks/router/namespace_installer.rb +1 -1
- data/lib/hecks/runtime/command_interpreter.rb +3 -3
- data/lib/hecks/runtime/command_rules/admissibility.rb +2 -2
- data/lib/hecks/runtime/command_rules/arithmetic.rb +2 -2
- data/lib/hecks/runtime/entity_element.rb +1 -1
- data/lib/hecks/runtime/entity_interpreter.rb +1 -1
- data/lib/hecks/runtime/invocation.rb +1 -1
- data/lib/hecks/runtime/loader.rb +27 -3
- data/lib/hecks/runtime/outbox.rb +1 -1
- data/lib/hecks/runtime/reaction_invocation.rb +3 -3
- data/lib/hecks/runtime/read_model_interpreter.rb +37 -15
- data/lib/hecks/runtime/reference_hop.rb +4 -4
- data/lib/hecks/runtime/registry/saga_persistence.rb +8 -2
- data/lib/hecks/runtime/registry/verification.rb +15 -13
- data/lib/hecks/runtime/registry/world_defaults.rb +106 -0
- data/lib/hecks/runtime/registry.rb +22 -13
- data/lib/hecks/runtime/saga_interpreter.rb +1 -1
- data/lib/hecks/runtime/tenant_check.rb +1 -1
- data/lib/hecks/runtime/value/coercion.rb +3 -3
- data/lib/hecks/runtime/value/entity_list_coercion.rb +1 -1
- data/lib/hecks/storehouse.rb +18 -12
- data/lib/hecks/vendoring/git_environment.rb +35 -0
- data/lib/hecks/vendoring/git_source.rb +102 -0
- data/lib/hecks/vendoring.rb +102 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +5 -0
- data/lib/hecks.rb +8 -2
- metadata +72 -58
- 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 -604
- 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
|
@@ -13,10 +13,10 @@ module Hecks
|
|
|
13
13
|
#
|
|
14
14
|
# This module and its `uses_embryonaut_bluebook`
|
|
15
15
|
# DSL word (hecksagon_builder.rb) were built on a prior commit of this
|
|
16
|
-
# repo (933d1dd), vendored out to a real consumer (
|
|
16
|
+
# repo (933d1dd), vendored out to a real consumer (a client site's domain,
|
|
17
17
|
# for embryonaut_bluebooks/payments), and then lost from this repo's own
|
|
18
18
|
# reachable history — a hard reset or rebase left no branch containing
|
|
19
|
-
# that commit. The
|
|
19
|
+
# that commit. The consumer's vendor snapshot (a `git archive` of that
|
|
20
20
|
# commit, committed into their repo) was the only surviving copy; this
|
|
21
21
|
# file is ported forward from it, checked against current `main`'s own
|
|
22
22
|
# conventions rather than copied wholesale, since the two trees had
|
|
@@ -64,7 +64,18 @@ module Hecks
|
|
|
64
64
|
# => "Payments"), not a separate ledger. A vendored package's directory
|
|
65
65
|
# name and its declared `Hecks.bluebook` name are the one convention
|
|
66
66
|
# this reuses from Framework rather than reinventing.
|
|
67
|
+
#
|
|
68
|
+
# ## Vendoring
|
|
69
|
+
#
|
|
70
|
+
# `vendor!` is the other direction: it pins a package from a local checkout
|
|
71
|
+
# of the registry into a consuming project's own `vendor/` tree, and is the
|
|
72
|
+
# one implementation behind `bin/vendor_bluebook` (see Vendoring and Vendor).
|
|
67
73
|
module EmbryonautBluebook
|
|
74
|
+
autoload :Lock, File.join(__dir__, "embryonaut_bluebook/lock")
|
|
75
|
+
autoload :Shape, File.join(__dir__, "embryonaut_bluebook/shape")
|
|
76
|
+
autoload :Vendor, File.join(__dir__, "embryonaut_bluebook/vendor")
|
|
77
|
+
autoload :VendorCli, File.join(__dir__, "embryonaut_bluebook/vendor_cli")
|
|
78
|
+
|
|
68
79
|
# Loads a vendored embryonaut bluebook package's `.bluebook` files, once
|
|
69
80
|
# per registry.
|
|
70
81
|
#
|
|
@@ -89,10 +100,25 @@ module Hecks
|
|
|
89
100
|
if files.empty?
|
|
90
101
|
raise Runtime::WiringError,
|
|
91
102
|
"no vendored embryonaut bluebook named #{name.inspect} at #{dir} — " \
|
|
92
|
-
"
|
|
103
|
+
"vendor it with bin/vendor_bluebook #{name}"
|
|
93
104
|
end
|
|
94
105
|
|
|
95
106
|
files.each { |file| Kernel.load(file) }
|
|
96
107
|
end
|
|
108
|
+
|
|
109
|
+
# Pins one package of the registry into a project's `vendor/` tree.
|
|
110
|
+
#
|
|
111
|
+
# @param name [String, Symbol] the package's directory name, such as `"payments"`
|
|
112
|
+
# @param from [String] path of a local checkout of the registry repository
|
|
113
|
+
# @param ref [String, nil] a release version (`"1.2.0"`), a release tag name, any
|
|
114
|
+
# commit-ish, or nil for the newest release
|
|
115
|
+
# @param root [String] the consuming project's root
|
|
116
|
+
# @param allow_downgrade [Boolean] whether a release older than the vendored one is allowed
|
|
117
|
+
# @return [Vendor::Result] the commit, version and storage shape now vendored
|
|
118
|
+
# @raise [Vendoring::Error] if the source, release or files are unusable, or a
|
|
119
|
+
# version-policy refusal applies (see Vendor)
|
|
120
|
+
def self.vendor!(name, from:, root:, ref: nil, allow_downgrade: false)
|
|
121
|
+
Vendor.new(name, from: from, root: root, ref: ref, allow_downgrade: allow_downgrade).call
|
|
122
|
+
end
|
|
97
123
|
end
|
|
98
124
|
end
|
data/lib/hecks/facade/handle.rb
CHANGED
|
@@ -142,14 +142,14 @@ module Hecks
|
|
|
142
142
|
# privacy.bluebook): a marking's presence is what makes a read
|
|
143
143
|
# redacted, not a separate flag this class carries itself.
|
|
144
144
|
#
|
|
145
|
-
#
|
|
145
|
+
# Always the strong check, never the weak string-only fallback
|
|
146
146
|
# `CommandRules::Authorization#refuse_role_mismatch` allows an
|
|
147
147
|
# unidentified caller — a read gate gone wrong is a leak, not a
|
|
148
148
|
# refused command, so an ambient caller with no `actor_id` (or no
|
|
149
149
|
# caller at all) is masked here, full stop, rather than waved
|
|
150
150
|
# through the way a self-asserted `role` string is for a command.
|
|
151
151
|
#
|
|
152
|
-
#
|
|
152
|
+
# One level of nesting only — `attendee.medications` masks inside
|
|
153
153
|
# the returned `Runtime::Value` via its own `#with`; a marking two
|
|
154
154
|
# levels deep is not supported and is left unmasked rather than
|
|
155
155
|
# silently mishandled, since nothing in this corpus needs it yet.
|
|
@@ -165,7 +165,7 @@ module Hecks
|
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
door.define_singleton_method(:method_missing) do |verb, *args, **kwargs, &block|
|
|
168
|
-
# A
|
|
168
|
+
# A bare call starts a Privacy marking chain — see
|
|
169
169
|
# `Bluebook::DSL::BindingProxy#method_missing`'s own header;
|
|
170
170
|
# this is the same mechanism, reached when the constant is
|
|
171
171
|
# already a real, installed door (a second boot in-process)
|
|
@@ -2,7 +2,7 @@ module Hecks
|
|
|
2
2
|
module Forms
|
|
3
3
|
# The value-object classification every consumer of a resolved VO
|
|
4
4
|
# shape needs, spelled once — `field_shape.rb` (a command's own
|
|
5
|
-
# form), `ui_schema.rb` (
|
|
5
|
+
# form), `ui_schema.rb` (the console app's table/detail-panel
|
|
6
6
|
# renderer, a separate app, not this repo), and
|
|
7
7
|
# `adapters/driven/sql_query_builder.rb` (a query's own ORDER BY/
|
|
8
8
|
# where compiler) each grew their own copy of "is this VO money-
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Hecks.bluebook "ConsoleSettings" do
|
|
2
|
-
vision "A generic admin console's own curated UI facts — nav, columns, state tones, which rows a detail panel shows — as real dispatched data instead of a hand-rewritten YAML file. A UI opinion the DOMAIN it's pointed at has no way to answer, never a business record; see
|
|
2
|
+
vision "A generic admin console's own curated UI facts — nav, columns, state tones, which rows a detail panel shows — as real dispatched data instead of a hand-rewritten YAML file. A UI opinion the DOMAIN it's pointed at has no way to answer, never a business record; see the console app's own presentation_config.rb for the fuller story."
|
|
3
3
|
supporting
|
|
4
4
|
|
|
5
5
|
# A FRAMEWORK MEMBER LIKE IDENTITY/GOVERNANCE — attach with
|
|
@@ -7,7 +7,7 @@ Hecks.bluebook "ConsoleSettings" do
|
|
|
7
7
|
# `.hecksagon`, plus a `Hecks.hecksagon "ConsoleSettings" do ... end`
|
|
8
8
|
# persistence binding (Heki is the common case — a small, local,
|
|
9
9
|
# per-app settings store, no shared database needed). This used to
|
|
10
|
-
# boot in a SECOND, independent registry (
|
|
10
|
+
# boot in a SECOND, independent registry (the console app's own
|
|
11
11
|
# console_settings_domain.rb) specifically so no consuming app needed
|
|
12
12
|
# any change at all — that property is real and now given up on
|
|
13
13
|
# purpose, in favor of the one binding mechanism every other
|
|
@@ -30,7 +30,7 @@ Hecks.bluebook "ConsoleSettings" do
|
|
|
30
30
|
# (Overview, below) and every `extra_json`/`identity_extra_json`
|
|
31
31
|
# field throughout this chapter were BOTH originally scoped out on an
|
|
32
32
|
# "confirmed unused in any real presentation.yml" basis that turned
|
|
33
|
-
# out to be checked against the wrong file —
|
|
33
|
+
# out to be checked against the wrong file — the console app's own
|
|
34
34
|
# fixture, never this app's actual, live one. Migrating the real file
|
|
35
35
|
# found both very much in use.
|
|
36
36
|
|
|
@@ -339,6 +339,12 @@ Hecks.bluebook "Bluebook" do
|
|
|
339
339
|
member refusal: "InvariantViolation", site: "undeclared_set",
|
|
340
340
|
template: "{name} admits {admits}, which this chapter does not declare — a closed set is named " \
|
|
341
341
|
"Aggregate::SetName, and it must be one the bluebook actually holds"
|
|
342
|
+
# ADR 0061 decision D1: a group_by leaf holds one row, so two rows
|
|
343
|
+
# reaching the same full key path refuse at dispatch rather than one
|
|
344
|
+
# of them being dropped.
|
|
345
|
+
member refusal: "InvariantViolation", site: "group_by_collision",
|
|
346
|
+
template: "{read_model} groups by {fields}, but rows {ids} share {key} — a group_by " \
|
|
347
|
+
"leaf holds one row; add a field that tells them apart"
|
|
342
348
|
member refusal: "Unauthorized", site: "tenant_required",
|
|
343
349
|
template: "{query} declares authorize with tenant: {field} — pass {field}: to name which " \
|
|
344
350
|
"{field} this ask is scoped to"
|
|
@@ -620,6 +626,16 @@ Hecks.bluebook "Bluebook" do
|
|
|
620
626
|
shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
|
|
621
627
|
member refusal: "InvariantViolation", site: "undeclared_set", argument: "admits",
|
|
622
628
|
shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
|
|
629
|
+
member refusal: "InvariantViolation", site: "group_by_collision", argument: "read_model",
|
|
630
|
+
shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
|
|
631
|
+
member refusal: "InvariantViolation", site: "group_by_collision", argument: "fields",
|
|
632
|
+
shape: "list", quoting: "none", separator: ", ", sorted: "false", when_empty: ""
|
|
633
|
+
member refusal: "InvariantViolation", site: "group_by_collision", argument: "ids",
|
|
634
|
+
shape: "list", quoting: "inspect", separator: ", ", sorted: "true", when_empty: ""
|
|
635
|
+
# "field = value" per key-path level, joined with ", " by the caller:
|
|
636
|
+
# each value is its own `to_s`, the same text the nested Hash keys it by.
|
|
637
|
+
member refusal: "InvariantViolation", site: "group_by_collision", argument: "key",
|
|
638
|
+
shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
|
|
623
639
|
member refusal: "Unauthorized", site: "tenant_required", argument: "query",
|
|
624
640
|
shape: "scalar", quoting: "none", separator: "", sorted: "false", when_empty: ""
|
|
625
641
|
member refusal: "Unauthorized", site: "tenant_required", argument: "field",
|
|
@@ -4,8 +4,10 @@ Hecks.bluebook "World" do
|
|
|
4
4
|
|
|
5
5
|
identified_by WorldName, as: :domain
|
|
6
6
|
|
|
7
|
-
attribute :realm,
|
|
8
|
-
attribute :latest,
|
|
7
|
+
attribute :realm, Realm
|
|
8
|
+
attribute :latest, Latest
|
|
9
|
+
attribute :default_database, DefaultDatabase
|
|
10
|
+
attribute :default_adapter, DefaultAdapter
|
|
9
11
|
|
|
10
12
|
value_object "WorldName" do
|
|
11
13
|
attribute :value, String
|
|
@@ -22,6 +24,22 @@ Hecks.bluebook "World" do
|
|
|
22
24
|
invariant("a latest version says something") { !value.to_s.empty? }
|
|
23
25
|
end
|
|
24
26
|
|
|
27
|
+
# The connection every chapter's database-taking persistence adapter
|
|
28
|
+
# uses unless that chapter's own world says otherwise — a world's
|
|
29
|
+
# per-chapter `persisted_by ... database` block, written once.
|
|
30
|
+
value_object "DefaultDatabase" do
|
|
31
|
+
attribute :value, String
|
|
32
|
+
invariant("a default database says something") { !value.to_s.empty? }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The persistence adapter every aggregate binds to unless its own
|
|
36
|
+
# chapter's hecksagon binds it — between an explicit bind and the
|
|
37
|
+
# framework's in-memory fallback.
|
|
38
|
+
value_object "DefaultAdapter" do
|
|
39
|
+
attribute :value, String
|
|
40
|
+
invariant("a default adapter names an adapter") { !value.to_s.empty? }
|
|
41
|
+
end
|
|
42
|
+
|
|
25
43
|
command "Declare" do
|
|
26
44
|
role "Deployment"
|
|
27
45
|
goal "Open a world for a domain"
|
|
@@ -29,10 +47,14 @@ Hecks.bluebook "World" do
|
|
|
29
47
|
attribute :domain, WorldName
|
|
30
48
|
attribute :realm, Realm, optional: true
|
|
31
49
|
attribute :latest, Latest, optional: true
|
|
50
|
+
attribute :default_database, DefaultDatabase, optional: true
|
|
51
|
+
attribute :default_adapter, DefaultAdapter, optional: true
|
|
32
52
|
|
|
33
53
|
sets :domain
|
|
34
54
|
sets :realm
|
|
35
55
|
sets :latest
|
|
56
|
+
sets :default_database
|
|
57
|
+
sets :default_adapter
|
|
36
58
|
|
|
37
59
|
emits "WorldDeclared"
|
|
38
60
|
end
|
|
@@ -55,6 +77,12 @@ Hecks.bluebook "World" do
|
|
|
55
77
|
member word: "world", context: "File", body: "keywords", inner: "World", opens: "World", fills: ""
|
|
56
78
|
member word: "realm", context: "World", body: "none", inner: "", opens: "", fills: "realm", calls: "realm_impl"
|
|
57
79
|
member word: "latest", context: "World", body: "none", inner: "", opens: "", fills: "latest", calls: "latest_impl"
|
|
80
|
+
# THE TWO CROSS-CHAPTER DEFAULTS — the same connection string and the
|
|
81
|
+
# same persistence adapter every chapter of a project repeats, written
|
|
82
|
+
# once. A chapter's own world (`persisted_by ... database`) and its own
|
|
83
|
+
# hecksagon bind still win; see docs/implemented/reference/world.md.
|
|
84
|
+
member word: "default_database", context: "World", body: "none", inner: "", opens: "", fills: "default_database", calls: "default_database_impl"
|
|
85
|
+
member word: "default_adapter", context: "World", body: "none", inner: "", opens: "", fills: "default_adapter", calls: "default_adapter_impl"
|
|
58
86
|
|
|
59
87
|
end
|
|
60
88
|
|
|
@@ -78,6 +106,8 @@ Hecks.bluebook "World" do
|
|
|
78
106
|
member keyword: "world", context: "File", at: "1", named: "", kind: "text", required: "true", fills: "domain"
|
|
79
107
|
member keyword: "realm", context: "World", at: "1", named: "", kind: "text", required: "true", fills: "realm"
|
|
80
108
|
member keyword: "latest", context: "World", at: "1", named: "", kind: "text", required: "true", fills: "latest"
|
|
109
|
+
member keyword: "default_database", context: "World", at: "1", named: "", kind: "text", required: "true", fills: "default_database"
|
|
110
|
+
member keyword: "default_adapter", context: "World", at: "1", named: "", kind: "text", required: "true", fills: "default_adapter"
|
|
81
111
|
|
|
82
112
|
end
|
|
83
113
|
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
require_relative "runtime/errors"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
# What one spawned `bin/hecks_mcp_door` may do: every tool against any domain under
|
|
5
|
+
# `Storehouse::BOOT_ROOT` (the default), or, in reader mode, only the reader tools
|
|
6
|
+
# against the domains its spawner named. ADR 0072, decision 2.
|
|
7
|
+
#
|
|
8
|
+
# ## Reach, not identity
|
|
9
|
+
#
|
|
10
|
+
# This is a capability allowlist, not authentication. It identifies no one and adds no
|
|
11
|
+
# secret: whoever can spawn the door chooses its mode, and whoever can write to its stdin
|
|
12
|
+
# gets whatever that mode allows. What it buys is a smaller surface per agent. A reader
|
|
13
|
+
# door cannot dispatch, cannot run `.behaviors` files, and cannot `Kernel.load` Ruby from
|
|
14
|
+
# a domain its spawner did not name.
|
|
15
|
+
#
|
|
16
|
+
# ## Why `HECKS_DOOR_*` and not `HECKS_MCP_*`
|
|
17
|
+
#
|
|
18
|
+
# `McpStdioGuard` refuses every `HECKS_MCP_*` variable except
|
|
19
|
+
# `HECKS_MCP_TRANSPORT=stdio`, and every argument except `--stdio`, so that no option can
|
|
20
|
+
# ask a server for another transport. Those refusals belong to ADR 0062. The settings
|
|
21
|
+
# here live under their own prefix so the guard stays exactly as that ADR describes it,
|
|
22
|
+
# and an unknown `HECKS_DOOR_*` name is refused here, the same fail-closed way.
|
|
23
|
+
#
|
|
24
|
+
# ## The settings
|
|
25
|
+
#
|
|
26
|
+
# - `HECKS_DOOR_TOOLS=readers` turns reader mode on. Unset means every tool, as before.
|
|
27
|
+
# Any other value refuses to start.
|
|
28
|
+
# - `HECKS_DOOR_DOMAINS` lists the domains a reader door may name, separated by
|
|
29
|
+
# `File::PATH_SEPARATOR` (`:` on Unix), each resolved against `Storehouse::BOOT_ROOT`.
|
|
30
|
+
# Required in reader mode and refused without it.
|
|
31
|
+
#
|
|
32
|
+
# ## The allowed set is named, not loaded
|
|
33
|
+
#
|
|
34
|
+
# The door boots a domain afresh on every call and keeps nothing loaded between calls,
|
|
35
|
+
# so there is no set of already-loaded domains to check against. The allowed set is the
|
|
36
|
+
# list the spawner named, resolved to absolute paths at startup. A call's `domain:` is
|
|
37
|
+
# resolved the same way and compared before anything boots, so a path outside the set
|
|
38
|
+
# never reaches `Kernel.load`. The comparison is on resolved path text: a symlink under a
|
|
39
|
+
# named path is followed when that domain boots, as it is for every domain today.
|
|
40
|
+
class McpDoorScope
|
|
41
|
+
ENV_PREFIX = "HECKS_DOOR_".freeze
|
|
42
|
+
TOOLS_VARIABLE = "HECKS_DOOR_TOOLS".freeze
|
|
43
|
+
DOMAINS_VARIABLE = "HECKS_DOOR_DOMAINS".freeze
|
|
44
|
+
READERS = "readers".freeze
|
|
45
|
+
EXIT_STATUS = 2
|
|
46
|
+
|
|
47
|
+
# The tools a reader door serves. Each reads a domain or the door's own audit log and
|
|
48
|
+
# writes no domain state. `query` and `state` still append their call to that audit
|
|
49
|
+
# log. `validate` boots the domain it is given, so it is held to the allowed set like
|
|
50
|
+
# every other domain-scoped tool. `domains` lists directories and loads nothing.
|
|
51
|
+
READER_TOOLS = %w[query events state catalog describe validate domains history follow].freeze
|
|
52
|
+
|
|
53
|
+
# Reads the door's mode from its environment.
|
|
54
|
+
#
|
|
55
|
+
# @param env [Hash{String => String}] the process environment
|
|
56
|
+
# @return [McpDoorScope] an unrestricted scope when no `HECKS_DOOR_*` variable is set;
|
|
57
|
+
# otherwise a reader scope over the named domains
|
|
58
|
+
# @raise [ArgumentError] with every problem found, one per line, when the settings are
|
|
59
|
+
# not a mode this door knows
|
|
60
|
+
# @raise [Runtime::TypeMismatch] if a named domain resolves outside
|
|
61
|
+
# `Storehouse::BOOT_ROOT`
|
|
62
|
+
def self.from_env(env = ENV)
|
|
63
|
+
problems = setting_problems(env)
|
|
64
|
+
raise ArgumentError, problems.join("\n") unless problems.empty?
|
|
65
|
+
return new(allowed_domains: nil) unless env[TOOLS_VARIABLE]
|
|
66
|
+
|
|
67
|
+
new(allowed_domains: env[DOMAINS_VARIABLE].split(File::PATH_SEPARATOR).reject(&:empty?))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Reads the door's mode, or refuses to start on settings it does not know.
|
|
71
|
+
#
|
|
72
|
+
# Everything goes to `stderr`, because stdout carries the MCP protocol.
|
|
73
|
+
#
|
|
74
|
+
# @param server [String] the server's name, used as the line prefix
|
|
75
|
+
# @param env [Hash{String => String}] the process environment
|
|
76
|
+
# @param stderr [IO] where a refusal is written
|
|
77
|
+
# @return [McpDoorScope] the door's scope
|
|
78
|
+
# @raise [SystemExit] with status `EXIT_STATUS` when `from_env` refuses the settings
|
|
79
|
+
def self.start!(server:, env: ENV, stderr: $stderr)
|
|
80
|
+
from_env(env)
|
|
81
|
+
rescue ArgumentError, Runtime::TypeMismatch => e
|
|
82
|
+
e.message.each_line { |line| stderr.puts("#{server}: refusing to start: #{line.chomp}") }
|
|
83
|
+
exit(EXIT_STATUS)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @api private
|
|
87
|
+
def self.setting_problems(env)
|
|
88
|
+
unknown = env.keys.select { |name| name.start_with?(ENV_PREFIX) } - [TOOLS_VARIABLE, DOMAINS_VARIABLE]
|
|
89
|
+
problems = unknown.map do |name|
|
|
90
|
+
"environment #{name} is not accepted; the door takes only #{TOOLS_VARIABLE} and #{DOMAINS_VARIABLE}"
|
|
91
|
+
end
|
|
92
|
+
problems + mode_problems(env[TOOLS_VARIABLE], env[DOMAINS_VARIABLE])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @api private
|
|
96
|
+
def self.mode_problems(tools, domains)
|
|
97
|
+
if tools.nil?
|
|
98
|
+
domains.nil? ? [] : ["#{DOMAINS_VARIABLE} is set without #{TOOLS_VARIABLE}=#{READERS}; it only narrows a reader door"]
|
|
99
|
+
elsif tools != READERS
|
|
100
|
+
["#{TOOLS_VARIABLE}=#{tools.inspect} is not accepted; the only value is #{READERS.inspect}"]
|
|
101
|
+
elsif domains.to_s.split(File::PATH_SEPARATOR).none? { |path| !path.empty? }
|
|
102
|
+
["#{TOOLS_VARIABLE}=#{READERS} needs #{DOMAINS_VARIABLE}, the domains this door may read"]
|
|
103
|
+
else
|
|
104
|
+
[]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# The named domains, resolved against `Storehouse::BOOT_ROOT`; nil when unrestricted.
|
|
109
|
+
#
|
|
110
|
+
# @return [Array<String>, nil]
|
|
111
|
+
attr_reader :allowed_domains
|
|
112
|
+
|
|
113
|
+
# @param allowed_domains [Array<String>, nil] the domains a reader door may name, as
|
|
114
|
+
# given; nil for an unrestricted door
|
|
115
|
+
# @raise [Runtime::TypeMismatch] if a named domain resolves outside `Storehouse::BOOT_ROOT`
|
|
116
|
+
def initialize(allowed_domains:)
|
|
117
|
+
@allowed_domains = allowed_domains&.map { |path| Storehouse.confine!(path, DOMAINS_VARIABLE) }.freeze
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Tells whether this door runs in reader mode.
|
|
121
|
+
#
|
|
122
|
+
# @return [Boolean] true when the spawner set `HECKS_DOOR_TOOLS=readers`
|
|
123
|
+
def restricted?
|
|
124
|
+
!@allowed_domains.nil?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Tells whether this door serves a tool.
|
|
128
|
+
#
|
|
129
|
+
# @param name [String] the tool name from a `tools/call` request
|
|
130
|
+
# @return [Boolean] true for every name on an unrestricted door; on a reader door, true
|
|
131
|
+
# only for `READER_TOOLS`
|
|
132
|
+
def permits_tool?(name)
|
|
133
|
+
!restricted? || READER_TOOLS.include?(name)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Builds the answer to a call for a tool this door does not serve.
|
|
137
|
+
#
|
|
138
|
+
# @param name [String] the tool name from a `tools/call` request
|
|
139
|
+
# @return [Hash{Symbol => Object}] `{ok: false, error:}`, the error naming the mode and
|
|
140
|
+
# the tools it serves
|
|
141
|
+
def refusal(name)
|
|
142
|
+
{ ok: false,
|
|
143
|
+
error: "#{name.inspect} is refused: this door runs in reader mode (#{TOOLS_VARIABLE}=#{READERS}), " \
|
|
144
|
+
"which serves only #{READER_TOOLS.join(', ')}" }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Checks a call's `domain:` against the allowed set, before anything boots.
|
|
148
|
+
#
|
|
149
|
+
# @param domain [String, nil] the caller's `domain:` argument
|
|
150
|
+
# @return [String, nil] `domain` unchanged, so an unrestricted door boots exactly what
|
|
151
|
+
# it did before
|
|
152
|
+
# @raise [Runtime::TypeMismatch] on a reader door, if `domain` resolves outside
|
|
153
|
+
# `Storehouse::BOOT_ROOT` or to a path its spawner did not name
|
|
154
|
+
def admit_domain!(domain)
|
|
155
|
+
return domain unless restricted?
|
|
156
|
+
return domain if @allowed_domains.include?(Storehouse.confine!(domain, "domain"))
|
|
157
|
+
|
|
158
|
+
raise Runtime::TypeMismatch,
|
|
159
|
+
"domain: #{domain.inspect} is refused: this door runs in reader mode (#{TOOLS_VARIABLE}=#{READERS}) " \
|
|
160
|
+
"and reads only #{DOMAINS_VARIABLE}: #{@allowed_domains.join(', ')}"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# The startup warning lines that describe this door's mode.
|
|
164
|
+
#
|
|
165
|
+
# @return [Array<String>] empty when unrestricted; on a reader door, what it serves,
|
|
166
|
+
# what it refuses, the domains it reads, and that it identifies no one
|
|
167
|
+
def notes
|
|
168
|
+
return [] unless restricted?
|
|
169
|
+
|
|
170
|
+
["Reader mode (#{TOOLS_VARIABLE}=#{READERS}): serves #{READER_TOOLS.join(', ')}; " \
|
|
171
|
+
"refuses dispatch (with dry_run and steps), behaviors and every other tool.",
|
|
172
|
+
"domain: boots only #{DOMAINS_VARIABLE}: #{@allowed_domains.join(', ')}.",
|
|
173
|
+
"Reader mode limits reach and identifies no one; it is not authentication."]
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -47,7 +47,7 @@ module Hecks
|
|
|
47
47
|
#
|
|
48
48
|
# @param dispatcher [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted
|
|
49
49
|
# dispatcher both the lookup query and the `Shred` dispatch run through
|
|
50
|
-
# @param domain [String] the subject's own domain FQN, e.g. `"
|
|
50
|
+
# @param domain [String] the subject's own domain FQN, e.g. `"Site::Registration"`
|
|
51
51
|
# @param subject_id [String] the data subject to erase
|
|
52
52
|
# @return [Boolean] true when a live key was found and shredded by this call; false
|
|
53
53
|
# when no `SubjectKey` record exists for this subject, or it was already shredded
|
|
@@ -308,7 +308,7 @@ module Hecks
|
|
|
308
308
|
#
|
|
309
309
|
# @param id [Integer] the row id assigned by `outbox_enqueue`
|
|
310
310
|
# @return [Boolean] true when this call claimed the row; false when it is missing or
|
|
311
|
-
#
|
|
311
|
+
# not pending
|
|
312
312
|
def outbox_claim(id) = @adapter.outbox_claim(id)
|
|
313
313
|
|
|
314
314
|
# Records the final status of a claimed outbox row.
|
|
@@ -15,6 +15,13 @@ module Hecks
|
|
|
15
15
|
# A domain with no hecksagon gets the default in-memory bind. A domain that has one
|
|
16
16
|
# must bind the aggregate exactly once without a role.
|
|
17
17
|
#
|
|
18
|
+
# A `default_adapter` a world declares (`Registry#default_adapter_for` — the
|
|
19
|
+
# chapter's own world, else the project's) stands in wherever the hecksagon binds
|
|
20
|
+
# nothing for the aggregate, and replaces the in-memory fallback for a domain
|
|
21
|
+
# with no hecksagon. It is never consulted once the hecksagon binds the aggregate
|
|
22
|
+
# or declares a domain-level default of its own, so an explicit chapter decision
|
|
23
|
+
# always wins.
|
|
24
|
+
#
|
|
18
25
|
# @param registry [Runtime::Registry] the registry holding the domain's hecksagon
|
|
19
26
|
# @param domain [String, Symbol] name of the domain the aggregate belongs to
|
|
20
27
|
# @param aggregate [Bluebook::Aggregate] the aggregate whose binding is wanted
|
|
@@ -23,10 +30,12 @@ module Hecks
|
|
|
23
30
|
# @raise [Runtime::WiringError] if the hecksagon has no `persisted_by` bind for the
|
|
24
31
|
# aggregate, more or fewer than one bind without a role, or any bind with a role
|
|
25
32
|
def resolve(registry, domain, aggregate)
|
|
26
|
-
hexagon
|
|
27
|
-
|
|
33
|
+
hexagon = registry.hecksagon(domain)
|
|
34
|
+
declared = registry.default_adapter_for(domain)
|
|
35
|
+
return default_binding(aggregate, declared || DEFAULT_ADAPTER) unless hexagon
|
|
28
36
|
|
|
29
37
|
bindings = hexagon.binds_for(aggregate.hecks_name, VERB)
|
|
38
|
+
bindings = [default_binding(aggregate, declared)] if bindings.empty? && declared
|
|
30
39
|
raise missing_binding(domain, aggregate) if bindings.empty?
|
|
31
40
|
|
|
32
41
|
authoritative = bindings.select { |bind| bind.role.nil? || bind.role.empty? }
|
|
@@ -39,9 +48,11 @@ module Hecks
|
|
|
39
48
|
# Builds the bind an aggregate gets when its domain declares no hecksagon.
|
|
40
49
|
#
|
|
41
50
|
# @param aggregate [Bluebook::Aggregate] the aggregate to bind
|
|
42
|
-
# @
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
# @param adapter [String] the adapter to bind it to; the framework's in-memory
|
|
52
|
+
# default unless a world declared one
|
|
53
|
+
# @return [Bluebook::Bind] a roleless `persisted_by` bind to `adapter`
|
|
54
|
+
def default_binding(aggregate, adapter = DEFAULT_ADAPTER)
|
|
55
|
+
Bluebook::Bind.new(aggregate: aggregate.hecks_name, verb: VERB, adapter: adapter)
|
|
45
56
|
end
|
|
46
57
|
|
|
47
58
|
# Builds, without raising, the error for an aggregate a hecksagon leaves unbound.
|
|
@@ -4,6 +4,7 @@ require_relative "lineage"
|
|
|
4
4
|
require_relative "../../../../naming"
|
|
5
5
|
require_relative "../../../../framework"
|
|
6
6
|
require_relative "../../../../runtime/registry"
|
|
7
|
+
require_relative "expected_era"
|
|
7
8
|
|
|
8
9
|
module Hecks
|
|
9
10
|
module Runtime
|
|
@@ -122,7 +123,7 @@ module Hecks
|
|
|
122
123
|
#
|
|
123
124
|
# The domain's own directory first, matched by name — a real app's
|
|
124
125
|
# directory may hold more than one file once `uses_framework`
|
|
125
|
-
# exists, so ".first" alone
|
|
126
|
+
# exists, so ".first" alone cannot be trusted, the exact
|
|
126
127
|
# way it silently wasn't the day this was found: three domains
|
|
127
128
|
# booted together, one real source text read once (the domain's
|
|
128
129
|
# own, ".first"'d), and every other domain's era-1 held that text
|
|
@@ -295,7 +296,7 @@ module Hecks
|
|
|
295
296
|
"it could be found (checked #{directory.inspect} and the framework registry)"
|
|
296
297
|
end
|
|
297
298
|
|
|
298
|
-
settings = registry.
|
|
299
|
+
settings = registry.binding_settings(bluebook.name, Ports::Persistence::VERB, adapter_name)
|
|
299
300
|
registry.adapter_class(adapter_name).era_check!(
|
|
300
301
|
registry: registry, bluebook: bluebook, current_text: current_text, settings: settings,
|
|
301
302
|
directory: directory
|
|
@@ -131,7 +131,7 @@ module Hecks
|
|
|
131
131
|
# Normal parse first, shadow only as a fallback — never shadow-parsing
|
|
132
132
|
# unconditionally. A handful of DSL
|
|
133
133
|
# defaults fork on `MetaValidator.shadow_parsing?` for a reason
|
|
134
|
-
# that has nothing to do with syntax the live grammar
|
|
134
|
+
# that has nothing to do with syntax the live grammar cannot
|
|
135
135
|
# read at all (`identified_by { }`, `belongs_to`, `has_one`,
|
|
136
136
|
# `has_many` — genuinely removed spellings, exactly what shadow-
|
|
137
137
|
# parsing exists to keep readable): `reference_to`'s own default
|
|
@@ -158,7 +158,7 @@ module Hecks
|
|
|
158
158
|
# interpretation, the same one `label_of`/`mint_hash` on the same
|
|
159
159
|
# source text always computes, whoever's asking. Only on a
|
|
160
160
|
# `Malformed` refusal — the one signal that actually means "this
|
|
161
|
-
# spelling
|
|
161
|
+
# spelling is not in the live grammar" — fall back to the legacy
|
|
162
162
|
# grammar. Any other exception (a
|
|
163
163
|
# genuine syntax error, an unrelated validation refusal) propagates
|
|
164
164
|
# unchanged; swallowing it here to retry under shadow mode would
|
|
@@ -5,7 +5,7 @@ require_relative "storage_shape"
|
|
|
5
5
|
|
|
6
6
|
module Hecks
|
|
7
7
|
module Runtime
|
|
8
|
-
# The refusal wording for a held era whose digest
|
|
8
|
+
# The refusal wording for a held era whose digest does not match its
|
|
9
9
|
# frozen text — the digest mismatch alone is what detects tampering (a
|
|
10
10
|
# plain SHA256 comparison over raw bytes, unrelated to any of this);
|
|
11
11
|
# this only supplies the wording once that's already fired.
|
|
@@ -28,7 +28,7 @@ module Hecks
|
|
|
28
28
|
module EraTamper
|
|
29
29
|
module_function
|
|
30
30
|
|
|
31
|
-
# Words the boot refusal for a held era text whose digest
|
|
31
|
+
# Words the boot refusal for a held era text whose digest does not match.
|
|
32
32
|
#
|
|
33
33
|
# @param domain [String] name of the domain whose era was edited
|
|
34
34
|
# @param ordinal [Integer] the edited era's ordinal in `hecks_eras`
|