hecks 3.10.0 → 3.11.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/examples/pizzas/bluebook/pizzas.bluebook +29 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
- data/exe/hecks +4 -4
- data/lib/hecks/adapters/driven/d1.rb +2 -10
- data/lib/hecks/adapters/driven/heki.adapter +1 -1
- data/lib/hecks/adapters/driven/heki.rb +2 -12
- data/lib/hecks/adapters/driven/lambda.adapter +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +3 -18
- data/lib/hecks/adapters/driven/postgres.rb +4 -11
- data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
- data/lib/hecks/adapters/driven/sqlite.rb +2 -10
- data/lib/hecks/adapters/driving/admission.rb +46 -0
- data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
- data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
- data/lib/hecks/adapters/driving/cli.rb +127 -0
- data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +155 -0
- data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
- data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
- data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +69 -0
- data/lib/hecks/adapters/driving/command_request.rb +121 -0
- data/lib/hecks/adapters/driving/handle.rb +214 -0
- data/lib/hecks/adapters/driving/json.rb +136 -0
- data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
- data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
- data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
- data/lib/hecks/adapters/driving/mcp.rb +159 -0
- data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
- data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
- data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
- data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
- data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
- data/lib/hecks/adapters/driving/ruby.rb +83 -0
- data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
- data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
- data/lib/hecks/behaviors/expectations/boot.rb +1 -1
- data/lib/hecks/bench/ruby_runner.rb +1 -1
- data/lib/hecks/bluebook/ask_resolution.rb +130 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
- data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
- data/lib/hecks/bluebook/assembly/marks.rb +1 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/command.rb +13 -8
- data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
- data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
- data/lib/hecks/bluebook/capabilities.rb +91 -9
- data/lib/hecks/bluebook/domain_port.rb +16 -4
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
- data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
- data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
- data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
- data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
- data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
- data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
- data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
- data/lib/hecks/bluebook/expression/projection.json +36 -0
- data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
- data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
- data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
- data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
- data/lib/hecks/bluebook/expression/resolver.rb +20 -1
- data/lib/hecks/bluebook/hecksagon.rb +23 -3
- data/lib/hecks/bluebook/lifecycle.rb +22 -3
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
- data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/readings.rb +18 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
- data/lib/hecks/bluebook/meta_validator.rb +12 -12
- data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
- data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
- data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
- data/lib/hecks/bluebook/model_check.rb +8 -2
- data/lib/hecks/bluebook/policy.rb +4 -2
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/bluebook/synthesizer.rb +2 -0
- data/lib/hecks/bluebook.rb +1 -0
- data/lib/hecks/cli/command_table.rb +1 -1
- data/lib/hecks/cli/console.rb +2 -1
- data/lib/hecks/cli/document.rb +1 -1
- data/lib/hecks/cli/launcher_words.rb +4 -4
- data/lib/hecks/cli/mcp.rb +4 -4
- data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
- data/lib/hecks/cli/project_cli/templates.rb +5 -5
- data/lib/hecks/cli/project_cli.rb +9 -6
- data/lib/hecks/cli/run.rb +2 -2
- data/lib/hecks/cli/smoke_http/checks.rb +4 -1
- data/lib/hecks/cli/smoke_http/transport.rb +3 -3
- data/lib/hecks/cli/smoke_http.rb +3 -0
- data/lib/hecks/codemod.rb +5 -2
- data/lib/hecks/corpus/routes.rb +7 -0
- data/lib/hecks/corpus/rust.rb +3 -2
- data/lib/hecks/deploy/bluebook/deploy.bluebook +18 -2
- data/lib/hecks/doc/reference/rendering.rb +6 -3
- data/lib/hecks/forms/app/commands.rb +8 -7
- data/lib/hecks/forms/app/queries.rb +2 -1
- data/lib/hecks/forms/app.rb +1 -1
- data/lib/hecks/forms/record_renderer.rb +1 -1
- data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
- data/lib/hecks/fuzzing/failure_triage.rb +2 -1
- data/lib/hecks/fuzzing/mutation/run.rb +11 -4
- data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
- data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
- data/lib/hecks/fuzzing/properties/guards.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency.rb +6 -2
- data/lib/hecks/fuzzing/shrinker.rb +3 -1
- data/lib/hecks/fuzzing/value_generator.rb +19 -5
- data/lib/hecks/gate/stages.yml +4 -10
- data/lib/hecks/grammar/evolve.rb +1 -1
- data/lib/hecks/grammar/expression_operators.json +372 -0
- data/lib/hecks/grammar/operators.rb +3 -1
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
- data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
- data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
- data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
- data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
- data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
- data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
- data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
- data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
- data/lib/hecks/hecks/adapters/local_files.rb +2 -2
- data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
- data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
- data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
- data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +129 -0
- data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
- data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
- data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
- data/lib/hecks/hecks/adapters/terminal.rb +13 -11
- data/lib/hecks/hecks/codebase.behaviors +13 -1
- data/lib/hecks/hecks/codebase.bluebook +239 -5
- data/lib/hecks/hecks/custodian.behaviors +24 -6
- data/lib/hecks/hecks/custodian.bluebook +6 -1227
- data/lib/hecks/hecks/deploy.behaviors +1 -1
- data/lib/hecks/hecks/deploy.world +1 -3
- data/lib/hecks/hecks/hecks.behaviors +1 -1
- data/lib/hecks/hecks/hecks.hecksagon +53 -18
- data/lib/hecks/hecks/hecks.world +10 -10
- data/lib/hecks/hecks/rehearsal.bluebook +477 -0
- data/lib/hecks/hecks/site.behaviors +1 -1
- data/lib/hecks/hecks/site.world +0 -1
- data/lib/hecks/hecks/tooling.bluebook +1238 -0
- data/lib/hecks/indifferent_key.rb +20 -0
- data/lib/hecks/language/adapter.bluebook +13 -3
- data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
- data/lib/hecks/language/bluebook/entity.bluebook +18 -0
- data/lib/hecks/language/bluebook/policy.bluebook +9 -1
- data/lib/hecks/language/bluebook/query.bluebook +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +11 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
- data/lib/hecks/language/oidc.json +10 -0
- data/lib/hecks/language/port.bluebook +4 -4
- data/lib/hecks/literal.rb +12 -3
- data/lib/hecks/ports/key_vault.rb +7 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
- data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/shared.rb +5 -2
- data/lib/hecks/projections/model/deviations.rb +2 -2
- data/lib/hecks/projections/model.rb +2 -2
- data/lib/hecks/projections/oidc.rb +13 -1
- data/lib/hecks/projections/shape.rb +5 -2
- data/lib/hecks/projections/site/cms_editor/attributes.rb +26 -6
- data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
- data/lib/hecks/projections/site/cms_editor/checks.rb +14 -1
- data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
- data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
- data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
- data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
- data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
- data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
- data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
- data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
- data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
- data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
- data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
- data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
- data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
- data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
- data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
- data/lib/hecks/projections/site/cms_editor/schema.rb +96 -19
- data/lib/hecks/projections/site/cms_editor/setting.rb +52 -9
- data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +18 -1
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +251 -57
- data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +32 -24
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +78 -24
- data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +6 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +19 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +31 -8
- data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +131 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +8 -5
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +229 -34
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +40 -81
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +29 -12
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +53 -17
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +178 -88
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
- data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +2 -1
- data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
- data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
- data/lib/hecks/projections/site/cms_editor.rb +49 -23
- data/lib/hecks/projections/site/role_probe.rb +2 -0
- data/lib/hecks/projector/cli_audience.rb +2 -1
- data/lib/hecks/projector/cli_projector/listing.rb +1 -1
- data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
- data/lib/hecks/projector/docs_projector/closing.rb +1 -1
- data/lib/hecks/projector/docs_projector.rb +1 -1
- data/lib/hecks/projector/exporter/bindings.rb +40 -10
- data/lib/hecks/projector/exporter/capability_values.rb +55 -0
- data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
- data/lib/hecks/projector/target.rb +11 -0
- data/lib/hecks/projector.rb +32 -5
- data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
- data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
- data/lib/hecks/quality_control/quality_control.bluebook +6 -2
- data/lib/hecks/release/approval.rb +64 -0
- data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
- data/lib/hecks/router/namespace_installer.rb +16 -16
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/best_effort.rb +22 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
- data/lib/hecks/runtime/command_interpreter.rb +3 -2
- data/lib/hecks/runtime/command_rules/emission.rb +1 -1
- data/lib/hecks/runtime/dispatcher.rb +4 -4
- data/lib/hecks/runtime/entity_element/location.rb +1 -1
- data/lib/hecks/runtime/event.rb +5 -0
- data/lib/hecks/runtime/identity.rb +7 -6
- data/lib/hecks/runtime/interpreting.rb +2 -1
- data/lib/hecks/runtime/invocation_builder.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
- data/lib/hecks/runtime/loader.rb +10 -10
- data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
- data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
- data/lib/hecks/runtime/policy_interpreter.rb +6 -6
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
- data/lib/hecks/runtime/reaction_invocation.rb +1 -1
- data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
- data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
- data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
- data/lib/hecks/runtime/registry/merging.rb +1 -1
- data/lib/hecks/runtime/registry/providers.rb +16 -0
- data/lib/hecks/runtime/registry/read_repository.rb +6 -4
- data/lib/hecks/runtime/registry/verification.rb +4 -1
- data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
- data/lib/hecks/runtime/registry.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
- data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter.rb +3 -3
- data/lib/hecks/runtime/tenant_check.rb +5 -4
- data/lib/hecks/runtime/value/coercion.rb +2 -2
- data/lib/hecks/runtime/value/validation.rb +16 -4
- data/lib/hecks/runtime.rb +9 -9
- data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
- data/lib/hecks/rust_build/wasm.rb +1 -1
- data/lib/hecks/rust_build/wasm_browser.rb +1 -1
- data/lib/hecks/rust_build.rb +11 -0
- data/lib/hecks/served_options.rb +53 -0
- data/lib/hecks/site/bluebook/site.bluebook +1 -0
- data/lib/hecks/storehouse/audit.rb +1 -1
- data/lib/hecks/storehouse/dispatching.rb +8 -6
- data/lib/hecks/storehouse/introspection.rb +2 -2
- data/lib/hecks/storehouse/querying.rb +6 -4
- data/lib/hecks/storehouse.rb +4 -4
- data/lib/hecks/tools/comment_style/source_file.rb +4 -1
- data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
- data/lib/hecks/tools/lanes/rows.rb +6 -0
- data/lib/hecks/tools/oidc_manifests.rb +4 -5
- data/lib/hecks/tools/regeneration_run.rb +11 -4
- data/lib/hecks/tools/site_routes/editor_files.rb +12 -5
- data/lib/hecks/tools/tenant_provisioning.rb +2 -2
- data/lib/hecks/tools/tools_doc.rb +4 -4
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +4 -4
- data/lib/hecks.rb +15 -15
- data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
- data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
- data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
- data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
- data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
- data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
- data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
- data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
- data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
- data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
- data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
- data/rust/.gitignore +6 -0
- data/rust/Cargo.toml +6 -0
- data/rust/build/src/binding_pass.rs +103 -0
- data/rust/build/src/cargo_sync.rs +6 -0
- data/rust/build/src/lineage_pass.rs +3 -3
- data/rust/build/src/main.rs +17 -5
- data/rust/build/src/pipeline.rs +48 -5
- data/rust/build/src/seams_pass.rs +123 -0
- data/rust/build/src/translation_pass.rs +167 -0
- data/rust/codegen/src/asks.rs +103 -0
- data/rust/codegen/src/commands.rs +13 -8
- data/rust/codegen/src/constraints.rs +25 -0
- data/rust/codegen/src/domain_generator.rs +2 -0
- data/rust/codegen/src/expr_emitter.rs +13 -0
- data/rust/codegen/src/json_codec.rs +54 -15
- data/rust/codegen/src/main.rs +1 -0
- data/rust/codegen/src/mutations.rs +128 -3
- data/rust/codegen/src/queries.rs +11 -2
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/types.rs +166 -8
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +4 -4
- data/rust/host/src/bin/validate_harness.rs +43 -0
- data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
- data/rust/host/src/boundary_fuzz/ir.rs +1 -0
- data/rust/host/src/boundary_fuzz/server.rs +1 -1
- data/rust/host/src/checkout.rs +35 -19
- data/rust/host/src/commerce_ir.rs +408 -2
- data/rust/host/src/expr_json.rs +118 -0
- data/rust/host/src/mint.rs +1 -1
- data/rust/host/src/payments/tests.rs +27 -1
- data/rust/host/src/payments.rs +12 -4
- data/rust/host/src/query_step.rs +22 -4
- data/rust/host/src/reference_validate.rs +653 -108
- data/rust/host/src/resend.rs +2 -0
- data/rust/host/src/server.rs +19 -6
- data/rust/host/src/ui_schema.rs +132 -5
- data/rust/host/src/web/newsletter.rs +7 -8
- data/rust/host/src/web/newsletter_send.rs +38 -13
- data/rust/host/src/web/registrations.rs +229 -26
- data/rust/host/src/web.rs +3 -3
- data/rust/parser/src/emit.rs +38 -6
- data/rust/parser/src/expr/ast_json.rs +7 -1
- data/rust/parser/src/expr/resolver.rs +171 -6
- data/rust/parser/src/ir.rs +7 -0
- data/rust/parser/src/keywords.rs +24 -0
- data/rust/parser/src/main.rs +3 -0
- data/rust/parser/src/parse/aggregate.rs +2 -1
- data/rust/parser/src/parse/chapter.rs +2 -2
- data/rust/parser/src/parse/command.rs +2 -0
- data/rust/parser/src/parse/hecksagon.rs +5 -2
- data/rust/parser/src/parse/lifecycle.rs +81 -0
- data/rust/parser/src/parse/mod.rs +15 -1
- data/rust/parser/src/parse/policy.rs +13 -2
- data/rust/parser/src/parse/value_object.rs +13 -8
- data/rust/parser/src/ruby_value.rs +1 -1
- data/rust/src/exemplar/commands.rs +4 -4
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/json.rs +43 -1
- data/rust/src/kernel/cli.rs +7 -5
- data/rust/src/kernel/dispatch.rs +2 -2
- data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
- data/rust/src/kernel/expr.rs +66 -3
- data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
- data/rust/src/kernel/expression_operators/text.rs +26 -2
- data/rust/src/kernel/json.rs +15 -0
- data/rust/src/kernel/mod.rs +1 -1
- data/rust/src/kernel/orchestrate.rs +54 -5
- data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
- data/rust/src/kernel/pattern.rs +60 -0
- data/rust/src/kernel/reference_lookup.rs +90 -7
- metadata +140 -38
- data/lib/hecks/doors/cli_door/nesting.rb +0 -34
- data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
- data/lib/hecks/doors/cli_door.rb +0 -124
- data/lib/hecks/doors/cli_runner/answers.rb +0 -138
- data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
- data/lib/hecks/doors/cli_runner/settling.rb +0 -115
- data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
- data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
- data/lib/hecks/doors/cli_runner.rb +0 -66
- data/lib/hecks/doors/command_request.rb +0 -119
- data/lib/hecks/doors/handle.rb +0 -208
- data/lib/hecks/doors/json_door.rb +0 -132
- data/lib/hecks/doors/launcher_options.rb +0 -247
- data/lib/hecks/doors/mcp_door/properties.rb +0 -47
- data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
- data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
- data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
- data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
- data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
- data/lib/hecks/doors/mcp_door.rb +0 -156
- data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
- data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
- data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
- data/lib/hecks/doors/mcp_door_scope.rb +0 -201
- data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
- data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
- data/lib/hecks/doors/ruby_door.rb +0 -80
- data/lib/hecks/doors/usage_cache.rb +0 -139
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_widget.js.tmpl +0 -282
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/media_picker.js.tmpl +0 -109
- data/prebuilt/c28d6d9db14eea3e17161e9d9c262efee30b256cf97693a7cfd552bce24b2f7f.marshal +0 -0
- data/prebuilt/cb375cba61ca0dbe8ddb9739c306510fe05e5f4b7a9b6955caff8224c63ea0e0.marshal +0 -0
- data/prebuilt/verdicts-d6d584ad43b5f3cebf88aafbbdbf024c38574acf18e06adffbc555f7786b1c2f.json +0 -1
data/rust/host/src/expr_json.rs
CHANGED
|
@@ -12,6 +12,14 @@ pub struct Comparison {
|
|
|
12
12
|
pub negated: bool,
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/// Which ends of a string `Expr::Strip` trims.
|
|
16
|
+
#[derive(Debug, Clone, Copy)]
|
|
17
|
+
pub enum StripSide {
|
|
18
|
+
Both,
|
|
19
|
+
Left,
|
|
20
|
+
Right,
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
#[derive(Debug, Clone, Copy)]
|
|
16
24
|
pub enum BlockMode {
|
|
17
25
|
All,
|
|
@@ -35,6 +43,9 @@ pub enum Expr {
|
|
|
35
43
|
Bool { value: bool },
|
|
36
44
|
Nil,
|
|
37
45
|
Add { left: Box<Expr>, right: Box<Expr> },
|
|
46
|
+
Sub { left: Box<Expr>, right: Box<Expr> },
|
|
47
|
+
Mul { left: Box<Expr>, right: Box<Expr> },
|
|
48
|
+
Div { left: Box<Expr>, right: Box<Expr> },
|
|
38
49
|
SignTest { cmp: Comparison, receiver: Box<Expr> },
|
|
39
50
|
Empty { receiver: Box<Expr> },
|
|
40
51
|
ToS { receiver: Box<Expr> },
|
|
@@ -48,6 +59,7 @@ pub enum Expr {
|
|
|
48
59
|
Presence { receiver: Box<Expr>, negated: bool },
|
|
49
60
|
Assignment { receiver: Box<Expr>, negated: bool },
|
|
50
61
|
Split { receiver: Box<Expr>, separator: String },
|
|
62
|
+
Strip { receiver: Box<Expr>, side: StripSide },
|
|
51
63
|
StartsWith { receiver: Box<Expr>, substring: String },
|
|
52
64
|
EndsWith { receiver: Box<Expr>, substring: String },
|
|
53
65
|
First { receiver: Box<Expr> },
|
|
@@ -95,6 +107,9 @@ pub fn parse(json: &Json) -> Result<Expr, String> {
|
|
|
95
107
|
"bool" => Expr::Bool { value: field("value")?.as_bool().ok_or_else(|| format!("bool's value isn't a boolean: {json}"))? },
|
|
96
108
|
"nil" => Expr::Nil,
|
|
97
109
|
"add" => Expr::Add { left: Box::new(expr("left")?), right: Box::new(expr("right")?) },
|
|
110
|
+
"sub" => Expr::Sub { left: Box::new(expr("left")?), right: Box::new(expr("right")?) },
|
|
111
|
+
"mul" => Expr::Mul { left: Box::new(expr("left")?), right: Box::new(expr("right")?) },
|
|
112
|
+
"div" => Expr::Div { left: Box::new(expr("left")?), right: Box::new(expr("right")?) },
|
|
98
113
|
"sign_test" => Expr::SignTest { cmp: comparison()?, receiver: Box::new(expr("receiver")?) },
|
|
99
114
|
"empty" => Expr::Empty { receiver: Box::new(expr("receiver")?) },
|
|
100
115
|
"to_s" => Expr::ToS { receiver: Box::new(expr("receiver")?) },
|
|
@@ -136,6 +151,15 @@ pub fn parse(json: &Json) -> Result<Expr, String> {
|
|
|
136
151
|
negated: field("negated")?.as_bool().ok_or_else(|| format!("assignment's negated isn't a boolean: {json}"))?,
|
|
137
152
|
},
|
|
138
153
|
"split" => Expr::Split { receiver: Box::new(expr("receiver")?), separator: str_field("separator")? },
|
|
154
|
+
"strip" => Expr::Strip {
|
|
155
|
+
receiver: Box::new(expr("receiver")?),
|
|
156
|
+
side: match str_field("side")?.as_str() {
|
|
157
|
+
"both" => StripSide::Both,
|
|
158
|
+
"left" => StripSide::Left,
|
|
159
|
+
"right" => StripSide::Right,
|
|
160
|
+
other => return Err(format!("strip's side {other:?} is none of both/left/right: {json}")),
|
|
161
|
+
},
|
|
162
|
+
},
|
|
139
163
|
"starts_with" => Expr::StartsWith { receiver: Box::new(expr("receiver")?), substring: str_field("substring")? },
|
|
140
164
|
"ends_with" => Expr::EndsWith { receiver: Box::new(expr("receiver")?), substring: str_field("substring")? },
|
|
141
165
|
"first" => Expr::First { receiver: Box::new(expr("receiver")?) },
|
|
@@ -374,6 +398,45 @@ fn add(left: &Value, right: &Value) -> Result<Value, String> {
|
|
|
374
398
|
}
|
|
375
399
|
}
|
|
376
400
|
|
|
401
|
+
/// `Resolver.checked_arithmetic`: the same 64-bit and finiteness limits `add` holds to, named for
|
|
402
|
+
/// the operation that overflowed.
|
|
403
|
+
fn arithmetic(
|
|
404
|
+
name: &str,
|
|
405
|
+
symbol: &str,
|
|
406
|
+
left: &Value,
|
|
407
|
+
right: &Value,
|
|
408
|
+
int_op: fn(i64, i64) -> Option<i64>,
|
|
409
|
+
float_op: fn(f64, f64) -> f64,
|
|
410
|
+
) -> Result<Value, String> {
|
|
411
|
+
let lhs = require_number(left, name)?;
|
|
412
|
+
let rhs = require_number(right, name)?;
|
|
413
|
+
if let (Value::Int(a), Value::Int(b)) = (&lhs, &rhs) {
|
|
414
|
+
return int_op(*a, *b)
|
|
415
|
+
.map(Value::Int)
|
|
416
|
+
.ok_or_else(|| format!("{name} overflowed: {a} {symbol} {b} does not fit in a 64-bit integer"));
|
|
417
|
+
}
|
|
418
|
+
let result = float_op(numeric(&lhs).unwrap_or_default(), numeric(&rhs).unwrap_or_default());
|
|
419
|
+
if result.is_finite() {
|
|
420
|
+
Ok(Value::Float(result))
|
|
421
|
+
} else {
|
|
422
|
+
Err(format!("{name} overflowed: {} {symbol} {} is not a finite number", to_s(&lhs)?, to_s(&rhs)?))
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/// `Resolver.divide`: a zero divisor faults first; integers floor toward negative infinity.
|
|
427
|
+
fn divide(left: &Value, right: &Value) -> Result<Value, String> {
|
|
428
|
+
if numeric(&require_number(right, "division")?) == Some(0.0) {
|
|
429
|
+
return Err("divided by 0".to_string());
|
|
430
|
+
}
|
|
431
|
+
arithmetic("division", "/", left, right, floor_div, |a, b| a / b)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/// Floored integer division; `None` only for `i64::MIN / -1`, which does not fit in 64 bits.
|
|
435
|
+
fn floor_div(a: i64, b: i64) -> Option<i64> {
|
|
436
|
+
let quotient = a.checked_div(b)?;
|
|
437
|
+
Some(if a % b != 0 && ((a < 0) != (b < 0)) { quotient - 1 } else { quotient })
|
|
438
|
+
}
|
|
439
|
+
|
|
377
440
|
/// `Resolver.apply_modulo`: floored like Ruby's `%`, so the result takes the divisor's sign.
|
|
378
441
|
fn modulo(receiver: &Value, divisor: &Value) -> Result<Value, String> {
|
|
379
442
|
let receiver = require_number(receiver, "modulo")?;
|
|
@@ -423,6 +486,18 @@ fn split(text: &str, separator: &str) -> Vec<Value> {
|
|
|
423
486
|
parts.into_iter().map(Value::Str).collect()
|
|
424
487
|
}
|
|
425
488
|
|
|
489
|
+
/// Ruby's `String#strip` family: trims null, tab, line feed, vertical tab, form feed, carriage
|
|
490
|
+
/// return and space, never a Unicode space (Rust's own `trim` would drop U+00A0 too).
|
|
491
|
+
fn strip(text: &str, side: StripSide) -> String {
|
|
492
|
+
let stripped = |c: char| matches!(c, '\0' | '\t' | '\n' | '\u{b}' | '\u{c}' | '\r' | ' ');
|
|
493
|
+
match side {
|
|
494
|
+
StripSide::Both => text.trim_matches(stripped),
|
|
495
|
+
StripSide::Left => text.trim_start_matches(stripped),
|
|
496
|
+
StripSide::Right => text.trim_end_matches(stripped),
|
|
497
|
+
}
|
|
498
|
+
.to_string()
|
|
499
|
+
}
|
|
500
|
+
|
|
426
501
|
/// Compiles a Ruby `Regexp` source for the `regex` crate.
|
|
427
502
|
///
|
|
428
503
|
/// Ruby's `\d \w \s \h` are ASCII and its `^ $` are always line anchors, where `regex` reads
|
|
@@ -545,6 +620,13 @@ fn eval(expr: &Expr, instance: &Json, scope: Option<&Scope>) -> Result<Value, St
|
|
|
545
620
|
}
|
|
546
621
|
}
|
|
547
622
|
Expr::Add { left, right } => add(&recur(left)?, &recur(right)?),
|
|
623
|
+
Expr::Sub { left, right } => {
|
|
624
|
+
arithmetic("subtraction", "-", &recur(left)?, &recur(right)?, i64::checked_sub, |a, b| a - b)
|
|
625
|
+
}
|
|
626
|
+
Expr::Mul { left, right } => {
|
|
627
|
+
arithmetic("multiplication", "*", &recur(left)?, &recur(right)?, i64::checked_mul, |a, b| a * b)
|
|
628
|
+
}
|
|
629
|
+
Expr::Div { left, right } => divide(&recur(left)?, &recur(right)?),
|
|
548
630
|
Expr::Modulo { receiver, divisor } => modulo(&recur(receiver)?, &recur(divisor)?),
|
|
549
631
|
Expr::SignTest { cmp, receiver } => {
|
|
550
632
|
let v = recur(receiver)?;
|
|
@@ -587,6 +669,10 @@ fn eval(expr: &Expr, instance: &Json, scope: Option<&Scope>) -> Result<Value, St
|
|
|
587
669
|
Value::Array(items) => Ok(items.into_iter().next_back().unwrap_or(Value::Nil)),
|
|
588
670
|
other => Err(format!("last expects a list, got {}", describe(&other))),
|
|
589
671
|
},
|
|
672
|
+
Expr::Strip { receiver, side } => match recur(receiver)? {
|
|
673
|
+
Value::Str(text) => Ok(Value::Str(strip(&text, *side))),
|
|
674
|
+
other => Err(format!("strip expects a string, got {}", describe(&other))),
|
|
675
|
+
},
|
|
590
676
|
Expr::StartsWith { receiver, substring } => match recur(receiver)? {
|
|
591
677
|
Value::Str(text) => Ok(Value::Bool(text.starts_with(substring.as_str()))),
|
|
592
678
|
other => Err(format!("start_with? expects a string, got {}", describe(&other))),
|
|
@@ -828,6 +914,25 @@ mod tests {
|
|
|
828
914
|
);
|
|
829
915
|
}
|
|
830
916
|
|
|
917
|
+
#[test]
|
|
918
|
+
fn sub_mul_div_follow_rubys_limits_and_floor_integer_division() {
|
|
919
|
+
let op = |name: &str, l: Json, r: Json| json!({"op":name,"left":l,"right":r});
|
|
920
|
+
assert_eq!(run(op("sub", int(5), int(2)), json!({})), Ok(Value::Int(3)));
|
|
921
|
+
assert_eq!(run(op("mul", int(6), json!({"op":"float","value":0.5})), json!({})), Ok(Value::Float(3.0)));
|
|
922
|
+
assert_eq!(run(op("div", int(-7), int(2)), json!({})), Ok(Value::Int(-4)));
|
|
923
|
+
assert_eq!(run(op("div", int(7), int(-2)), json!({})), Ok(Value::Int(-4)));
|
|
924
|
+
assert_eq!(run(op("div", int(7), int(0)), json!({})), Err("divided by 0".to_string()));
|
|
925
|
+
assert_eq!(
|
|
926
|
+
run(op("mul", int(i64::MAX), int(2)), json!({})),
|
|
927
|
+
Err("multiplication overflowed: 9223372036854775807 * 2 does not fit in a 64-bit integer".to_string())
|
|
928
|
+
);
|
|
929
|
+
assert_eq!(
|
|
930
|
+
run(op("div", int(i64::MIN), int(-1)), json!({})),
|
|
931
|
+
Err("division overflowed: -9223372036854775808 / -1 does not fit in a 64-bit integer".to_string())
|
|
932
|
+
);
|
|
933
|
+
assert_eq!(run(op("sub", lookup_of("a"), int(1)), json!({"a": null})), Err("subtraction expects a number, got nil".to_string()));
|
|
934
|
+
}
|
|
935
|
+
|
|
831
936
|
#[test]
|
|
832
937
|
fn modulo_floors_like_ruby_for_integers_and_floats() {
|
|
833
938
|
let modulo = |r: Json, d: Json| json!({"op":"modulo","receiver":r,"divisor":d});
|
|
@@ -1035,6 +1140,19 @@ mod tests {
|
|
|
1035
1140
|
assert_eq!(run(call("first", lookup_of("x")), json!({"x": null})), Err("first expects a list, got nil".to_string()));
|
|
1036
1141
|
}
|
|
1037
1142
|
|
|
1143
|
+
#[test]
|
|
1144
|
+
fn strip_trims_rubys_whitespace_and_needs_a_string() {
|
|
1145
|
+
let strip_of = |side: &str, text: &str| run(json!({"op":"strip","receiver":lookup_of("x"),"side":side}), json!({"x": text}));
|
|
1146
|
+
assert_eq!(strip_of("both", " \t a b \0"), Ok(Value::Str("a b".into())));
|
|
1147
|
+
assert_eq!(strip_of("left", " a "), Ok(Value::Str("a ".into())));
|
|
1148
|
+
assert_eq!(strip_of("right", " a "), Ok(Value::Str(" a".into())));
|
|
1149
|
+
assert_eq!(strip_of("both", "\u{a0}a"), Ok(Value::Str("\u{a0}a".into())));
|
|
1150
|
+
assert_eq!(
|
|
1151
|
+
run(json!({"op":"strip","receiver":lookup_of("x"),"side":"both"}), json!({"x": null})),
|
|
1152
|
+
Err("strip expects a string, got nil".to_string())
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1038
1156
|
#[test]
|
|
1039
1157
|
fn starts_with_and_ends_with_need_a_string() {
|
|
1040
1158
|
let starts = json!({"op":"starts_with","receiver":lookup_of("x"),"substring":"ab"});
|
data/rust/host/src/mint.rs
CHANGED
|
@@ -815,7 +815,7 @@ pub async fn audit_before_mint<C: GenericClient>(
|
|
|
815
815
|
// `layer_one!(violations, aggregate, after)` exactly.
|
|
816
816
|
if let Some(aggregate_ir) = ir_aggregates.get(aggregate.name.as_str()) {
|
|
817
817
|
for (id, state) in &after {
|
|
818
|
-
violations.extend(reference_validate::validate(aggregate_ir, id, state));
|
|
818
|
+
violations.extend(reference_validate::validate(ir, aggregate_ir, id, state));
|
|
819
819
|
}
|
|
820
820
|
}
|
|
821
821
|
|
|
@@ -735,6 +735,21 @@ async fn archiving_a_paid_registration_frees_its_seat_and_restoring_it_takes_the
|
|
|
735
735
|
assert_eq!(web::seats_left(&read, "CheckoutFixture", &crate::commerce_ir::fixture_payments(), "archive-me"), Some(0));
|
|
736
736
|
}
|
|
737
737
|
|
|
738
|
+
#[tokio::test]
|
|
739
|
+
async fn cancelling_a_paid_registration_reaches_a_state_the_fixture_declares_as_freeing_its_seat() {
|
|
740
|
+
let t = tenant("hecks_pay_test_capacity_cancel").await;
|
|
741
|
+
t.schedule_event_with_capacity("cancel-me", 1).await;
|
|
742
|
+
let (_, body) = t.register("cancel-me").await;
|
|
743
|
+
let reference = body["registration_id"].as_str().unwrap().to_string();
|
|
744
|
+
assert_eq!(t.settle(&reference).await, 200);
|
|
745
|
+
|
|
746
|
+
let cancelled = registration_command(&t, "Cancel", &reference).await;
|
|
747
|
+
assert!(cancelled.accepted, "{:?}", cancelled.result);
|
|
748
|
+
let state = registration_status(&t, &reference).await;
|
|
749
|
+
let declared = crate::commerce_ir::registrations_provider(&crate::commerce_ir::fixture_ir()).expect("registrations");
|
|
750
|
+
assert!(declared.frees_seat.contains(&state), "{state} is one of {:?}", declared.frees_seat);
|
|
751
|
+
}
|
|
752
|
+
|
|
738
753
|
#[tokio::test]
|
|
739
754
|
async fn only_an_active_registration_can_be_archived_and_only_an_archived_one_restored() {
|
|
740
755
|
let t = tenant("hecks_pay_test_archive_guards").await;
|
|
@@ -763,7 +778,7 @@ async fn a_stripe_session_expires_about_thirty_one_minutes_after_it_is_created()
|
|
|
763
778
|
let sent = t.fake.requests_to("/v1/checkout/sessions");
|
|
764
779
|
assert_eq!(sent.len(), 1);
|
|
765
780
|
let expires_at: i64 = sent[0].body.split('&').find_map(|pair| pair.strip_prefix("expires_at=")).expect("an expires_at form field").parse().unwrap();
|
|
766
|
-
let hold = crate::checkout::
|
|
781
|
+
let hold = crate::checkout::effective_hold_seconds(crate::commerce_ir::checkout_windows_binding().session_hold);
|
|
767
782
|
assert!((before + hold..=after + hold).contains(&expires_at), "expires_at {expires_at} is not {hold}s after creation ({before}..{after})");
|
|
768
783
|
assert!((30 * 60..=35 * 60).contains(&hold), "Stripe's minimum is 30 minutes, and the hold should stay close to it");
|
|
769
784
|
}
|
|
@@ -1398,3 +1413,14 @@ fn purpose_tokens_only_verify_for_their_own_purpose_and_secret() {
|
|
|
1398
1413
|
assert!(auth::verify_purpose_token("other", "one", &token).is_none());
|
|
1399
1414
|
assert!(auth::verify_account_token("k", &token).is_none(), "not a session cookie");
|
|
1400
1415
|
}
|
|
1416
|
+
|
|
1417
|
+
// ADR 0099: the processor and the modes are this adapter's own words, but the domain still has to
|
|
1418
|
+
// admit them. The fixture's source is the bluebook as the domain wrote it.
|
|
1419
|
+
#[test]
|
|
1420
|
+
fn the_adapter_words_are_ones_the_connection_domain_admits() {
|
|
1421
|
+
let ir = crate::commerce_ir::fixture_ir();
|
|
1422
|
+
let source = ir["source_text"].as_str().expect("the fixture carries its bluebook source");
|
|
1423
|
+
assert!(source.contains(&format!("one_of: {MODES:?}")), "ConnectionMode must admit exactly {MODES:?}");
|
|
1424
|
+
let payments = std::fs::read_to_string(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../spec/fixtures/rust_host/checkout_fixture/bluebook/payments.bluebook")).expect("payments bluebook");
|
|
1425
|
+
assert!(payments.contains(&format!("one_of: [\"mock_stripe\", \"{PROCESSOR}\"]")), "Payment's Processor must admit {PROCESSOR}");
|
|
1426
|
+
}
|
data/rust/host/src/payments.rs
CHANGED
|
@@ -19,7 +19,15 @@ use keystore::{KeyStore, StoredDocument, StoredKeys};
|
|
|
19
19
|
|
|
20
20
|
const CONNECTION_SLUG: &str = "payments";
|
|
21
21
|
const STRIPE_API_BASE: &str = "https://api.stripe.com";
|
|
22
|
+
/// ADAPTER FACT (ADR 0099): the two environments a Stripe account has, named by the key prefix
|
|
23
|
+
/// (`sk_test_`, `sk_live_`) and the env vars read here. The domain's `ConnectionMode` enumerates
|
|
24
|
+
/// the same words; `the_adapter_words_are_ones_the_connection_domain_admits` holds the two
|
|
25
|
+
/// together, so a renamed mode fails a test instead of a request.
|
|
22
26
|
const MODES: [&str; 2] = ["test", "live"];
|
|
27
|
+
/// ADAPTER FACT (ADR 0099): this module is the Stripe adapter, so the processor it reports to the
|
|
28
|
+
/// domain is its own name. The domain's `Processor` enumerates the processors it admits; the same
|
|
29
|
+
/// test holds the word to it.
|
|
30
|
+
const PROCESSOR: &str = "stripe";
|
|
23
31
|
/// The reserved `account_ref` of a connection that uses the business's own
|
|
24
32
|
/// Stripe account directly. It is the only kind of connection this host charges.
|
|
25
33
|
pub const SELF_ACCOUNT: &str = "self";
|
|
@@ -217,7 +225,7 @@ pub fn checkout_plan(connection: Option<&Connection>, platform: &PlatformConfig)
|
|
|
217
225
|
match connection.status.as_str() {
|
|
218
226
|
"enabled" => {
|
|
219
227
|
let (key, publishable_key) = platform.direct_pair(&connection.mode);
|
|
220
|
-
if connection.processor !=
|
|
228
|
+
if connection.processor != PROCESSOR || !connection.is_direct() || key.is_empty() {
|
|
221
229
|
CheckoutPlan::Paused
|
|
222
230
|
} else if publishable_key.is_empty() {
|
|
223
231
|
eprintln!(
|
|
@@ -298,7 +306,7 @@ fn require_operator(caller: &Caller) -> Result<(), Value> {
|
|
|
298
306
|
// modes have keys. Built field by field, so a credential can never appear here:
|
|
299
307
|
// none is ever read into this value.
|
|
300
308
|
fn connection_json(connection: Option<&Connection>, platform: &PlatformConfig, caller: &Caller) -> Value {
|
|
301
|
-
let label = connection.filter(|c| c.processor ==
|
|
309
|
+
let label = connection.filter(|c| c.processor == PROCESSOR).map(|_| "Stripe");
|
|
302
310
|
let present = |value: Option<&str>| value.filter(|s| !s.is_empty()).map(Value::from).unwrap_or(Value::Null);
|
|
303
311
|
json!({
|
|
304
312
|
"status": connection.map(|c| c.status.as_str()).unwrap_or("not_connected"),
|
|
@@ -442,7 +450,7 @@ async fn direct_route(caller: &Caller, raw_body: &str, platform: &PlatformConfig
|
|
|
442
450
|
}
|
|
443
451
|
let display_name = own_account_display_name(platform, platform.direct_key(&mode)).await;
|
|
444
452
|
let facts = json!({
|
|
445
|
-
"processor": {"value":
|
|
453
|
+
"processor": {"value": PROCESSOR},
|
|
446
454
|
"account_ref": {"value": SELF_ACCOUNT},
|
|
447
455
|
"mode": {"value": mode},
|
|
448
456
|
"display_name": {"value": display_name},
|
|
@@ -700,7 +708,7 @@ async fn save_keys_route(caller: &Caller, body: &Value, platform: &PlatformConfi
|
|
|
700
708
|
return connection_response(caller, &fresh, client, wasm_path, config).await;
|
|
701
709
|
}
|
|
702
710
|
let facts = json!({
|
|
703
|
-
"processor": {"value":
|
|
711
|
+
"processor": {"value": PROCESSOR},
|
|
704
712
|
"account_ref": {"value": SELF_ACCOUNT},
|
|
705
713
|
"mode": {"value": mode},
|
|
706
714
|
"display_name": {"value": display_name},
|
data/rust/host/src/query_step.rs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//! The wire's query step: `{"query": "Aggregate.Query", "args": {...}}`.
|
|
2
2
|
//!
|
|
3
3
|
//! A declared query that filters stored records is answered by the kernel against current
|
|
4
|
-
//! state, the way the Ruby launcher and
|
|
4
|
+
//! state, the way the Ruby launcher and entry points answer it. A query that `returns` a value object
|
|
5
5
|
//! is answered from outside the domain (a hecksagon `answers_query` binding); this host binds
|
|
6
6
|
//! no adapter, so it refuses that question rather than answer it from the aggregate's rows, and
|
|
7
7
|
//! it writes nothing to the journal for either.
|
|
@@ -25,15 +25,24 @@ pub enum Plan {
|
|
|
25
25
|
///
|
|
26
26
|
/// @param domain_ir the domain's IR, when this host has one (`HECKS_IR_PATH`)
|
|
27
27
|
/// @param domain the domain's name, used to qualify a short question
|
|
28
|
-
/// @param question `Aggregate.Query`, or `
|
|
28
|
+
/// @param question `Aggregate.Query`, or `Chapter::Aggregate.Query` for this domain or one of its
|
|
29
|
+
/// attached chapters
|
|
29
30
|
/// @return a kernel question for a declared derivable query, a refusal for an unknown query on a
|
|
30
31
|
/// known aggregate or for a query answered outside the domain
|
|
31
32
|
pub fn plan(domain_ir: Option<&Value>, domain: &str, question: &str) -> Plan {
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
// A question that names its own chapter (`Chapter::Aggregate.Query`) is asked of that chapter:
|
|
34
|
+
// the kernel's query table is keyed by the chapter's own domain name, so re-qualifying it with
|
|
35
|
+
// this host's would send an attached chapter's query to a table that does not hold it.
|
|
36
|
+
let (owner, short) = question.rsplit_once("::").unwrap_or((domain, question));
|
|
37
|
+
let qualified = format!("{owner}::{short}");
|
|
34
38
|
let Some((aggregate_name, query_name)) = short.split_once('.') else {
|
|
35
39
|
return Plan::Kernel(qualified);
|
|
36
40
|
};
|
|
41
|
+
// This host's IR describes only its own domain's aggregates; another chapter's question is the
|
|
42
|
+
// kernel's to answer or refuse.
|
|
43
|
+
if owner != domain {
|
|
44
|
+
return Plan::Kernel(qualified);
|
|
45
|
+
}
|
|
37
46
|
let aggregate = domain_ir
|
|
38
47
|
.and_then(|ir| ir.get("aggregates"))
|
|
39
48
|
.and_then(|v| v.as_array())
|
|
@@ -131,6 +140,15 @@ mod tests {
|
|
|
131
140
|
);
|
|
132
141
|
}
|
|
133
142
|
|
|
143
|
+
#[test]
|
|
144
|
+
fn a_question_naming_an_attached_chapter_keeps_that_chapter() {
|
|
145
|
+
let ir = domain();
|
|
146
|
+
assert_eq!(plan(Some(&ir), "Banking", "Media::MediaItem.Active"), Plan::Kernel("Media::MediaItem.Active".into()));
|
|
147
|
+
// The host's own aggregate of the same name is not mistaken for the chapter's.
|
|
148
|
+
assert_eq!(plan(Some(&ir), "Banking", "Media::Ledger.Ir"), Plan::Kernel("Media::Ledger.Ir".into()));
|
|
149
|
+
assert_eq!(plan(None, "Banking", "Media::MediaItem.Listing"), Plan::Kernel("Media::MediaItem.Listing".into()));
|
|
150
|
+
}
|
|
151
|
+
|
|
134
152
|
#[test]
|
|
135
153
|
fn what_this_ir_does_not_show_is_left_to_the_kernel() {
|
|
136
154
|
let ir = domain();
|