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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "color"
|
|
4
|
+
|
|
5
|
+
module Hecks
|
|
6
|
+
module Projections
|
|
7
|
+
module Site
|
|
8
|
+
module CmsEditor
|
|
9
|
+
# What the `Editor` row's look settings must satisfy: an accent that is a six-digit hex
|
|
10
|
+
# colour, a logo that is a relative path to a picture file the server can read from its
|
|
11
|
+
# own directory, and a brand that is one line of text.
|
|
12
|
+
module ThemeChecks
|
|
13
|
+
# The file types a logo may be.
|
|
14
|
+
LOGO_TYPES = %w[png jpg jpeg webp gif avif svg].freeze
|
|
15
|
+
|
|
16
|
+
# The longest brand the header has room for.
|
|
17
|
+
BRAND_LENGTH = 60
|
|
18
|
+
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
# @param row [Hash{Symbol => String}] the row, defaults filled
|
|
22
|
+
# @return [Array<String>] every problem with `brand`, `accent` and `logo`
|
|
23
|
+
def problems(row)
|
|
24
|
+
[*accent_problems(row[:accent].strip), *logo_problems(row[:logo].strip), *brand_problems(row[:brand].strip)]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [Array<String>] the problem with an accent that is not blank or six hex digits
|
|
28
|
+
def accent_problems(accent)
|
|
29
|
+
return [] if accent.empty? || Color::HEX.match?(accent)
|
|
30
|
+
|
|
31
|
+
["accent #{accent.inspect} must be a six-digit hex colour such as #1f7a6d"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Array<String>] the problem with a logo that is not a safe relative picture path
|
|
35
|
+
def logo_problems(logo)
|
|
36
|
+
return [] if logo.empty? || safe_path?(logo)
|
|
37
|
+
|
|
38
|
+
["logo #{logo.inspect} must be a relative path to a #{LOGO_TYPES.join(", ")} file, with no .. or hidden part"]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Array<String>] the problem with a brand that is long or has more than one line
|
|
42
|
+
def brand_problems(brand)
|
|
43
|
+
return [] if brand.length <= BRAND_LENGTH && !brand.match?(/[[:cntrl:]]/)
|
|
44
|
+
|
|
45
|
+
["brand must be one line of at most #{BRAND_LENGTH} characters"]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [Boolean] whether `path` is relative, has no hidden part, and is a picture
|
|
49
|
+
def safe_path?(path)
|
|
50
|
+
LOGO_TYPES.include?(File.extname(path).delete_prefix(".").downcase) && !path.start_with?("/") &&
|
|
51
|
+
path.split("/", -1).none? { |part| part.empty? || part.start_with?(".") } && !path.match?(/[\\[:cntrl:]]/)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -5,6 +5,8 @@ require_relative "../../projector"
|
|
|
5
5
|
require_relative "cms_editor/setting"
|
|
6
6
|
require_relative "cms_editor/schema"
|
|
7
7
|
require_relative "cms_editor/pretty"
|
|
8
|
+
require_relative "cms_editor/icons"
|
|
9
|
+
require_relative "cms_editor/theme"
|
|
8
10
|
|
|
9
11
|
module Hecks
|
|
10
12
|
module Projections
|
|
@@ -28,29 +30,36 @@ module Hecks
|
|
|
28
30
|
|
|
29
31
|
# The files, by path relative to the directory they are written to.
|
|
30
32
|
FILES = %w[
|
|
31
|
-
package.json tsconfig.json src/config.ts src/schema.ts src/
|
|
32
|
-
src/auth/membership.ts src/auth/session.ts src/auth/sso.ts
|
|
33
|
-
src/ui/html.ts src/ui/
|
|
34
|
-
src/ui/
|
|
33
|
+
package.json tsconfig.json src/config.ts src/schema.ts src/names.ts src/host.ts src/commands.ts src/choices.ts
|
|
34
|
+
src/app.ts src/server.ts src/assets.ts src/flash.ts src/auth/membership.ts src/auth/session.ts src/auth/sso.ts
|
|
35
|
+
src/ui/html.ts src/ui/words.ts src/ui/icons.ts src/ui/shell.ts src/ui/outline.ts src/ui/fields.ts
|
|
36
|
+
src/ui/input.ts src/ui/table.ts src/ui/paging.ts src/ui/pickers.ts src/ui/moments.ts src/ui/detail.ts
|
|
37
|
+
src/ui/pages.ts src/ui/theme.js src/ui/body_model.js src/ui/body_parse.js src/ui/availability.ts src/ui/dialogs.ts
|
|
38
|
+
src/ui/links.ts src/ui/preview.ts src/ui/related.ts src/ui/schedule.ts src/ui/values.ts src/ui/world.ts
|
|
39
|
+
src/browser/app.css src/browser/main.js src/browser/shell.js src/browser/forms.js src/browser/toast.js
|
|
40
|
+
src/browser/lists.js src/browser/pickers.js src/browser/epoch.js src/browser/moments.js
|
|
41
|
+
src/browser/body_doc.js src/browser/body_editor.js src/browser/autosave.js src/browser/preview.js
|
|
42
|
+
src/ui/blocks.ts src/ui/block_list.js src/browser/blocks.js
|
|
35
43
|
].freeze
|
|
36
44
|
|
|
37
45
|
# The files only an editor of a chapter with a picture aggregate has: the upload, the
|
|
38
|
-
# storage port with its local-disk adapter, and the picker the rich-text
|
|
46
|
+
# storage port with its local-disk adapter, and the picker the rich-text editor opens.
|
|
39
47
|
MEDIA_FILES = %w[
|
|
40
|
-
src/media/sniff.ts src/media/multipart.ts src/media/storage.ts src/media/handler.ts src/
|
|
48
|
+
src/media/sniff.ts src/media/multipart.ts src/media/storage.ts src/media/handler.ts src/browser/media_picker.js
|
|
41
49
|
].freeze
|
|
42
50
|
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# What `app.ts` holds for a picture upload; each is left out when the chapter has none.
|
|
51
|
+
# What `app.ts` and the body editor hold for a picture upload; each is left out when the
|
|
52
|
+
# chapter has none.
|
|
47
53
|
MEDIA_LINES = {
|
|
48
|
-
"__MEDIA_IMPORT__"
|
|
49
|
-
|
|
50
|
-
"__MEDIA_OPTION__"
|
|
51
|
-
|
|
52
|
-
"__MEDIA_SETUP__"
|
|
53
|
-
"__MEDIA_ROUTE__"
|
|
54
|
+
"__MEDIA_IMPORT__" => ["import { mediaHandler, mediaOpen } from \"./media/handler.ts\";",
|
|
55
|
+
"import type { MediaStorage } from \"./media/storage.ts\";"].join("\n"),
|
|
56
|
+
"__MEDIA_OPTION__" => [" /** Where picture bytes are kept: the local disk unless given (see media/storage.ts). */",
|
|
57
|
+
" storage?: MediaStorage;"].join("\n"),
|
|
58
|
+
"__MEDIA_SETUP__" => " const media = mediaHandler(options.storage, { url: options.url, fetch: options.fetch });",
|
|
59
|
+
"__MEDIA_ROUTE__" => [" if (segments[0] === \"media\") {",
|
|
60
|
+
" if (!mediaOpen(role)) return plain(\"Pictures are not open to your role.\", 403);",
|
|
61
|
+
" return media(client, request, segments.slice(1));", " }"].join("\n"),
|
|
62
|
+
"__PICKER_LOADER__" => "() => import(\"./media_picker.js\")"
|
|
54
63
|
}.freeze
|
|
55
64
|
|
|
56
65
|
module_function
|
|
@@ -58,8 +67,11 @@ module Hecks
|
|
|
58
67
|
# Renders the editor's files for a project that declares an `Editor` row.
|
|
59
68
|
#
|
|
60
69
|
# @param bluebook [Bluebook::Chapter] the chapter that declares the route table and the row
|
|
61
|
-
# @param options [Hash{Symbol => Object}] `:
|
|
62
|
-
#
|
|
70
|
+
# @param options [Hash{Symbol => Object}] `:domain_chapters` the chapters to edit, in the
|
|
71
|
+
# order the row names them (`:domain_chapter` for one);
|
|
72
|
+
# `:media_chapter` the chapter the row's `media` names, whose picture aggregate the
|
|
73
|
+
# pictures use; `:table` the checked route table, when the login page is to be checked
|
|
74
|
+
# against it
|
|
63
75
|
# @return [Hash{String => String}] each file's path relative to the editor's directory to
|
|
64
76
|
# its text; empty when the project declares no `Editor` row
|
|
65
77
|
# @raise [Table::Invalid] when the row is refused
|
|
@@ -68,18 +80,32 @@ module Hecks
|
|
|
68
80
|
setting = Setting.read(bluebook, table: options[:table])
|
|
69
81
|
return {} unless setting
|
|
70
82
|
|
|
71
|
-
|
|
83
|
+
chapters = options[:domain_chapters] || options.fetch(:domain_chapter)
|
|
84
|
+
schema = Schema.read(chapters, skip: setting.skip, pictures: options[:media_chapter], roles: setting.chapter_roles)
|
|
72
85
|
media = schema.key?("media")
|
|
73
|
-
tokens =
|
|
86
|
+
tokens = tokens(setting, schema, media)
|
|
74
87
|
[*FILES, *(MEDIA_FILES if media)].to_h { |path| [path, fill(File.read(File.join(TEMPLATES, "#{path}.tmpl")), tokens)] }
|
|
75
88
|
end
|
|
76
89
|
|
|
90
|
+
# @return [Hash{String => String}] each placeholder of the templates and its text
|
|
91
|
+
def tokens(setting, schema, media)
|
|
92
|
+
setting.tokens.merge(look_tokens(setting, schema), "__SCHEMA__" => Pretty.generate(schema), **media_tokens(media))
|
|
93
|
+
end
|
|
94
|
+
|
|
77
95
|
# @param media [Boolean] whether the chapter has a picture aggregate
|
|
78
96
|
# @return [Hash{String => String}] each media placeholder to its text, empty when none
|
|
79
97
|
def media_tokens(media)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
{ "
|
|
98
|
+
return MEDIA_LINES if media
|
|
99
|
+
|
|
100
|
+
MEDIA_LINES.transform_values { "" }.merge("__PICKER_LOADER__" => "null")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @param setting [Setting] the checked row
|
|
104
|
+
# @param schema [Hash{String => Object}] the chapter as the editor reads it
|
|
105
|
+
# @return [Hash{String => String}] the brand, the icon sprite and the theme's colours
|
|
106
|
+
def look_tokens(setting, schema)
|
|
107
|
+
{ "__BRAND__" => JSON.generate(setting.brand || schema["domain"]), "__ICONS__" => Icons.sprite,
|
|
108
|
+
"__CSS_BANNER__" => "/* #{RoutesTs::BANNER.delete_prefix("// ")} */", **Theme.tokens(setting.accent) }
|
|
83
109
|
end
|
|
84
110
|
|
|
85
111
|
# @param text [String] a template with `__NAME__` placeholders
|
|
@@ -2,7 +2,8 @@ module Hecks
|
|
|
2
2
|
module Projector
|
|
3
3
|
# What a launcher's help leaves out and points at for whoever typed the line: the aggregates
|
|
4
4
|
# only a maintainer of a hecks checkout runs, and the attached chapters worth a line each.
|
|
5
|
-
# `CliProjector` renders the help; the runner decides the audience
|
|
5
|
+
# `CliProjector` renders the help; the runner decides the audience
|
|
6
|
+
# (`Adapters::Driving::LauncherOptions`).
|
|
6
7
|
module CliAudience
|
|
7
8
|
module_function
|
|
8
9
|
|
|
@@ -67,7 +67,7 @@ module Hecks
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
# " (also: mcp!)" for a spec the chapter gave a short name, else nothing. A short name that
|
|
70
|
-
# is only the command's own name (`init` for `
|
|
70
|
+
# is only the command's own name (`init` for `launch.init`) is already in the line.
|
|
71
71
|
def alias_note(spec)
|
|
72
72
|
return "" if spec[:short_was].nil? || spec[:short_was].split(".").last == spec[:short]
|
|
73
73
|
|
|
@@ -29,7 +29,7 @@ module Hecks
|
|
|
29
29
|
# One option per field of a value object, recursing into a field that is itself one.
|
|
30
30
|
#
|
|
31
31
|
# The list flag rides on each leaf: without it a repeated flag overwrote the
|
|
32
|
-
# leaf silently, and
|
|
32
|
+
# leaf silently, and Cli only ever sees a path and a spec.
|
|
33
33
|
def leaf_options(value_object, aggregate, path, optional)
|
|
34
34
|
value_object.attributes.flat_map do |field|
|
|
35
35
|
nested = value_object_for(field, value_object, aggregate)
|
|
@@ -18,7 +18,7 @@ module Hecks
|
|
|
18
18
|
return [] if bluebook.policies.empty?
|
|
19
19
|
|
|
20
20
|
rows = bluebook.policies.map do |policy|
|
|
21
|
-
["`#{policy.on_event}`", "`#{policy.
|
|
21
|
+
["`#{policy.on_event}`", "`#{policy.reaches}`", policy.target_domain || bluebook.name]
|
|
22
22
|
end
|
|
23
23
|
[DocsProjector.h(depth, "Reactions"), "",
|
|
24
24
|
"These fire on their own. Issuing the verb on the left also causes the one on the right.", "",
|
|
@@ -110,7 +110,7 @@ module Hecks
|
|
|
110
110
|
out.compact.join("\n")
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
# An entity has no
|
|
113
|
+
# An entity has no entry point of its own; its verbs go through the holding aggregate.
|
|
114
114
|
def entity_addressing(aggregate, entity)
|
|
115
115
|
"Addressed through its holder — `#{aggregate.hecks_name}.#{entity.hecks_name}.<Verb>`, " \
|
|
116
116
|
"passing the #{aggregate.hecks_name}'s `id` and this element's " \
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require_relative "capability_values"
|
|
2
|
+
|
|
1
3
|
module Hecks
|
|
2
4
|
module Projector
|
|
3
5
|
module Exporter
|
|
@@ -5,6 +7,8 @@ module Hecks
|
|
|
5
7
|
# membership, identity, newsletter, registrations, payments): a per-deployment binding fact,
|
|
6
8
|
# not part of the declared IR. Each answers `{}` when nothing the domain attaches provides it.
|
|
7
9
|
module Bindings
|
|
10
|
+
include CapabilityValues
|
|
11
|
+
|
|
8
12
|
# Which chapter this domain's role checks resolve against
|
|
9
13
|
# (`Registry#authorization_provider_for`); `{}` if none does.
|
|
10
14
|
#
|
|
@@ -70,16 +74,20 @@ module Hecks
|
|
|
70
74
|
# @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
|
|
71
75
|
# @param domain_name [String] the domain to export the newsletter binding for
|
|
72
76
|
# @return [Hash{Symbol => String, nil}] `:provider` (name), `:subscribe`, `:add_name`,
|
|
73
|
-
# `:confirm`, `:unsubscribe` (qualified verbs),
|
|
74
|
-
# leading qualified aggregate name)
|
|
75
|
-
#
|
|
77
|
+
# `:confirm`, `:unsubscribe` (qualified verbs), `:aggregate` (`:subscribe`'s own
|
|
78
|
+
# leading qualified aggregate name) and, when declared, `:awaiting_confirmation`,
|
|
79
|
+
# `:receives_issues` and `:left` (the states of the lifecycle marks they name) and
|
|
80
|
+
# `:confirm_window` and `:unsubscribe_window` (seconds); `{}` if nothing this domain
|
|
81
|
+
# attaches provides newsletter
|
|
76
82
|
def newsletter(registry, domain_name)
|
|
77
83
|
provider = registry.newsletter_provider_for(domain_name)
|
|
78
84
|
return {} unless provider
|
|
79
85
|
|
|
80
86
|
verbs = verbs_of(provider, Bluebook::Capabilities::NEWSLETTER,
|
|
81
87
|
:subscribe, :add_name, :confirm, :unsubscribe)
|
|
82
|
-
{ provider: provider.name, **verbs, aggregate: aggregate_of(verbs[:subscribe])
|
|
88
|
+
{ provider: provider.name, **verbs, aggregate: aggregate_of(verbs[:subscribe]),
|
|
89
|
+
**all_marked_states(provider, Bluebook::Capabilities::NEWSLETTER),
|
|
90
|
+
**all_durations(provider, Bluebook::Capabilities::NEWSLETTER) }
|
|
83
91
|
end
|
|
84
92
|
|
|
85
93
|
# Which chapter answers sending a newsletter issue
|
|
@@ -107,15 +115,18 @@ module Hecks
|
|
|
107
115
|
# @param domain_name [String] the domain to export the registrations binding for
|
|
108
116
|
# @return [Hash{Symbol => String, nil}] `:provider` (name), `:schedule`, `:request`
|
|
109
117
|
# (qualified verbs), `:event_aggregate` and `:registration_aggregate` (each verb's own
|
|
110
|
-
# leading qualified aggregate name)
|
|
111
|
-
#
|
|
118
|
+
# leading qualified aggregate name) and, when declared, `:frees_seat` (the states of the
|
|
119
|
+
# lifecycle mark it names) and `:registered_at` (the attribute that stamps a request);
|
|
120
|
+
# `{}` if nothing this domain attaches provides registrations
|
|
112
121
|
def registrations(registry, domain_name)
|
|
113
122
|
provider = registry.registrations_provider_for(domain_name)
|
|
114
123
|
return {} unless provider
|
|
115
124
|
|
|
116
125
|
verbs = verbs_of(provider, Bluebook::Capabilities::REGISTRATIONS, :schedule, :request)
|
|
117
126
|
{ provider: provider.name, **verbs,
|
|
118
|
-
**aggregates_of(verbs, event_aggregate: :schedule, registration_aggregate: :request)
|
|
127
|
+
**aggregates_of(verbs, event_aggregate: :schedule, registration_aggregate: :request),
|
|
128
|
+
**all_marked_states(provider, Bluebook::Capabilities::REGISTRATIONS),
|
|
129
|
+
**all_words(provider, Bluebook::Capabilities::REGISTRATIONS) }
|
|
119
130
|
end
|
|
120
131
|
|
|
121
132
|
# Which chapter owns the payment-processor connection
|
|
@@ -142,14 +153,33 @@ module Hecks
|
|
|
142
153
|
# @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
|
|
143
154
|
# @param domain_name [String] the domain to export the payments binding for
|
|
144
155
|
# @return [Hash{Symbol => String, nil}] `:provider` (name), `:initiate`, `:succeeded`,
|
|
145
|
-
# `:failed` (qualified verbs),
|
|
146
|
-
# aggregate name)
|
|
156
|
+
# `:failed` (qualified verbs), `:aggregate` (`:initiate`'s own leading qualified
|
|
157
|
+
# aggregate name) and, when declared, `:holds_seat` (the states of the lifecycle mark
|
|
158
|
+
# it names) and `:lapse_reason` (the word a lapsed hold
|
|
159
|
+
# records); `{}` if nothing this domain attaches provides payments
|
|
147
160
|
def payments(registry, domain_name)
|
|
148
161
|
provider = registry.payments_provider_for(domain_name)
|
|
149
162
|
return {} unless provider
|
|
150
163
|
|
|
151
164
|
verbs = verbs_of(provider, Bluebook::Capabilities::PAYMENTS, :initiate, :succeeded, :failed)
|
|
152
|
-
{ provider: provider.name, **verbs, aggregate: aggregate_of(verbs[:initiate])
|
|
165
|
+
{ provider: provider.name, **verbs, aggregate: aggregate_of(verbs[:initiate]),
|
|
166
|
+
**marked_states(provider, Bluebook::Capabilities::PAYMENTS, :holds_seat),
|
|
167
|
+
**all_words(provider, Bluebook::Capabilities::PAYMENTS) }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Which chapter owns the checkout boundary (`Registry#checkout_provider_for`); `{}` if
|
|
171
|
+
# none does.
|
|
172
|
+
#
|
|
173
|
+
# @param registry [Runtime::Registry] the booted registry `domain_name` is loaded in
|
|
174
|
+
# @param domain_name [String] the domain to export the checkout binding for
|
|
175
|
+
# @return [Hash{Symbol => Object}] `:provider` (name) and, when declared,
|
|
176
|
+
# `:webhook_tolerance` and `:session_hold` (whole seconds); `{}` if nothing this
|
|
177
|
+
# domain attaches provides checkout
|
|
178
|
+
def checkout(registry, domain_name)
|
|
179
|
+
provider = registry.checkout_provider_for(domain_name)
|
|
180
|
+
return {} unless provider
|
|
181
|
+
|
|
182
|
+
{ provider: provider.name, **all_durations(provider, Bluebook::Capabilities::CHECKOUT) }
|
|
153
183
|
end
|
|
154
184
|
|
|
155
185
|
private
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Projector
|
|
3
|
+
module Exporter
|
|
4
|
+
# The values a capability's optional contract entries resolve to in the exported fact: the
|
|
5
|
+
# states of a lifecycle `:mark` (ADR 0097), the whole seconds of a `:duration` (ADR 0098), and
|
|
6
|
+
# the word of a `:text` or `:attribute` entry (ADR 0099).
|
|
7
|
+
module CapabilityValues
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# `{ key => states }` for each optional `:mark` entry `provider` declares under
|
|
11
|
+
# `capability`.
|
|
12
|
+
def all_marked_states(provider, capability)
|
|
13
|
+
mark_keys = declared_keys(capability, :mark)
|
|
14
|
+
mark_keys.reduce({}) { |marks, key| marks.merge(marked_states(provider, capability, key)) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# `{ key => seconds }` for each optional `:duration` entry `provider` declares under
|
|
18
|
+
# `capability`: the whole-seconds default of the attribute it names.
|
|
19
|
+
def all_durations(provider, capability)
|
|
20
|
+
declared_keys(capability, :duration).each_with_object({}) do |key, durations|
|
|
21
|
+
verb = provider.provision(capability)&.fetch(key, nil)
|
|
22
|
+
durations[key] = Bluebook::Capabilities.duration_of(provider, verb) if verb
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# `{ key => word }` for each optional `:text` entry and `{ key => attribute_name }` for each
|
|
27
|
+
# optional `:attribute` entry `provider` declares under `capability` (ADR 0099).
|
|
28
|
+
def all_words(provider, capability)
|
|
29
|
+
words = { text: :text_of, attribute: :attribute_of }.flat_map do |kind, resolver|
|
|
30
|
+
declared_keys(capability, kind).filter_map do |key|
|
|
31
|
+
verb = provider.provision(capability)&.fetch(key, nil)
|
|
32
|
+
[key, Bluebook::Capabilities.public_send(resolver, provider, verb)] if verb
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
words.to_h
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# `{ key => states }` for an optional `:mark` entry `provider` declares under `capability`:
|
|
39
|
+
# the states of the named aggregate's lifecycle mark. `{}` when the entry is not declared.
|
|
40
|
+
def marked_states(provider, capability, key)
|
|
41
|
+
verb = provider.provision(capability)&.fetch(key, nil)
|
|
42
|
+
return {} unless verb
|
|
43
|
+
|
|
44
|
+
aggregate_name, mark = verb.split(".", 2)
|
|
45
|
+
{ key => provider.aggregate(aggregate_name).lifecycle.marked(mark) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The contract keys of `capability` whose kind is `kind`.
|
|
49
|
+
def declared_keys(capability, kind)
|
|
50
|
+
Bluebook::Capabilities::CONTRACTS.fetch(capability).select { |_, entry| entry == kind }.keys
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -16,7 +16,7 @@ module Hecks
|
|
|
16
16
|
# A policy as the sentence saying it fires on its own.
|
|
17
17
|
def policy_sentence(policy)
|
|
18
18
|
elsewhere = policy.target_domain ? " in #{policy.target_domain}" : ""
|
|
19
|
-
"Whenever `#{policy.on_event}` happens, `#{policy.
|
|
19
|
+
"Whenever `#{policy.on_event}` happens, `#{policy.reaches}` fires on its own#{elsewhere} — " \
|
|
20
20
|
"nobody has to ask for it."
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -58,6 +58,17 @@ module Hecks
|
|
|
58
58
|
# before `projects_as` is called
|
|
59
59
|
def projection_needs_world? = @projection_needs_world || false
|
|
60
60
|
|
|
61
|
+
# Derives what kind of target this is from flags it already declares,
|
|
62
|
+
# so there is no second spelling to drift.
|
|
63
|
+
#
|
|
64
|
+
# @return [Symbol] `:export` if it needs a world, `:meta` if it needs a
|
|
65
|
+
# chapter declaring particular aggregates, otherwise `:projection`
|
|
66
|
+
def projection_category
|
|
67
|
+
return :export if projection_needs_world?
|
|
68
|
+
|
|
69
|
+
projection_declares.empty? ? :projection : :meta
|
|
70
|
+
end
|
|
71
|
+
|
|
61
72
|
# Names the capability module(s) a construct must satisfy, defaulting
|
|
62
73
|
# to plain chapter-hood when `projects_as` named none.
|
|
63
74
|
#
|
data/lib/hecks/projector.rb
CHANGED
|
@@ -4,10 +4,13 @@ module Hecks
|
|
|
4
4
|
#
|
|
5
5
|
# Projector.call(:ir, bluebook: chapter)
|
|
6
6
|
#
|
|
7
|
-
# A
|
|
8
|
-
#
|
|
9
|
-
# `
|
|
10
|
-
# `
|
|
7
|
+
# A target's category is derived from the flags it declares (`category`):
|
|
8
|
+
#
|
|
9
|
+
# - `:projection` is inert and derived from any chapter's declaration.
|
|
10
|
+
# - `:meta` also needs a chapter declaring particular aggregates (`declares:`),
|
|
11
|
+
# so it only fits hecks's own grammar chapters.
|
|
12
|
+
# - `:export` (CloudFormation, ...) also needs `.world` bindings and declares
|
|
13
|
+
# `needs_world: true`; that is adapter wiring, not a projection of the domain.
|
|
11
14
|
module Projector
|
|
12
15
|
class UnknownProjector < StandardError; end
|
|
13
16
|
# A chapter-scoped projector was handed something that is not a chapter.
|
|
@@ -38,7 +41,9 @@ module Hecks
|
|
|
38
41
|
# @raise [WrongConstruct] if `bluebook` lacks what the projector requires
|
|
39
42
|
def call(name, bluebook:, options: {}, world: nil)
|
|
40
43
|
projector = registry.fetch(name.to_sym) do
|
|
41
|
-
raise UnknownProjector,
|
|
44
|
+
raise UnknownProjector,
|
|
45
|
+
"no projector registered for #{name.inspect} — registered: " \
|
|
46
|
+
"#{registered_by_category.map { |kind, keys| "#{kind} #{keys.inspect}" }.join(", ")}"
|
|
42
47
|
end
|
|
43
48
|
admits!(name, projector, bluebook, world)
|
|
44
49
|
projector.call(bluebook: bluebook, options: world ? options.merge(world: world) : options)
|
|
@@ -114,6 +119,28 @@ module Hecks
|
|
|
114
119
|
# @return [Array<Symbol>] every key currently registered
|
|
115
120
|
def registered = registry.keys
|
|
116
121
|
|
|
122
|
+
# Gives the category of a registered target, derived from its flags.
|
|
123
|
+
# A target that answers no `projection_category` (the built-ins, which do
|
|
124
|
+
# not extend `Target`) is a plain projection.
|
|
125
|
+
#
|
|
126
|
+
# @param name [String, Symbol] the registered projector's key
|
|
127
|
+
# @return [Symbol] `:projection`, `:meta` or `:export` (`:projection` for an
|
|
128
|
+
# unregistered `name`)
|
|
129
|
+
def category(name)
|
|
130
|
+
projector = registry.fetch(name.to_sym) { return :projection }
|
|
131
|
+
projector.respond_to?(:projection_category) ? projector.projection_category : :projection
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Lists every registered key under its category.
|
|
135
|
+
#
|
|
136
|
+
# @return [Hash{Symbol => Array<Symbol>}] `:projection`, `:meta` and `:export`
|
|
137
|
+
# (always all three), each with its sorted keys
|
|
138
|
+
def registered_by_category
|
|
139
|
+
grouped = { projection: [], meta: [], export: [] }
|
|
140
|
+
registered.sort.each { |key| grouped[category(key)] << key }
|
|
141
|
+
grouped
|
|
142
|
+
end
|
|
143
|
+
|
|
117
144
|
# Gives the live registry, initializing it on first use.
|
|
118
145
|
#
|
|
119
146
|
# @return [Hash{Symbol => Module, Class, #call}] the live key => projector registry
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
require "open3"
|
|
4
4
|
require "json"
|
|
5
|
+
require_relative "../../indifferent_key"
|
|
5
6
|
|
|
6
7
|
module Hecks
|
|
7
8
|
module Adapters
|
|
8
9
|
# The `CI` port adapter: answers `Clearance.CI.Run` from GitHub's check runs for a commit.
|
|
9
10
|
# Raises when no checks exist, any are still running, or any failed.
|
|
10
11
|
class GithubChecks
|
|
12
|
+
# Raised when check runs cannot be read or have not settled.
|
|
13
|
+
class Error < RuntimeError; end
|
|
14
|
+
|
|
11
15
|
# All keywords are unused; the adapter takes no per-boot configuration.
|
|
12
16
|
def initialize(aggregate: nil, settings: {}, root: nil); end
|
|
13
17
|
|
|
@@ -29,7 +33,7 @@ module Hecks
|
|
|
29
33
|
#
|
|
30
34
|
# @param commit [Hash, String] a materialized value object (`{value: sha}`) or a bare sha
|
|
31
35
|
# @return [Hash] `{ summary: { value: ... } }` when every check is green
|
|
32
|
-
# @raise [
|
|
36
|
+
# @raise [Error] when the sha is malformed, `gh` is missing or fails, no checks
|
|
33
37
|
# exist, any are incomplete, or any failed
|
|
34
38
|
def run(commit:, **)
|
|
35
39
|
sha = valid_sha(commit)
|
|
@@ -49,7 +53,7 @@ module Hecks
|
|
|
49
53
|
# @param names [Array<String>] the checks to look up
|
|
50
54
|
# @return [Hash{String => Symbol}] each name to `:passed`, `:failed`, `:pending` (reported but
|
|
51
55
|
# not completed) or `:missing` (not reported at all)
|
|
52
|
-
# @raise [
|
|
56
|
+
# @raise [Error] when the sha is malformed or `gh` is missing or fails
|
|
53
57
|
def states(commit:, names:)
|
|
54
58
|
reported = check_runs(valid_sha(commit)).select { |run| actions?(run) }.group_by { |run| run["name"] }
|
|
55
59
|
latest = reported.transform_values { |runs| runs.max_by { |run| run["id"].to_i } }
|
|
@@ -62,7 +66,7 @@ module Hecks
|
|
|
62
66
|
|
|
63
67
|
def valid_sha(commit)
|
|
64
68
|
sha = sha_of(commit)
|
|
65
|
-
raise "not a commit sha: #{sha.inspect}" unless sha.match?(SHA_PATTERN)
|
|
69
|
+
raise Error, "not a commit sha: #{sha.inspect}" unless sha.match?(SHA_PATTERN)
|
|
66
70
|
|
|
67
71
|
sha
|
|
68
72
|
end
|
|
@@ -70,11 +74,11 @@ module Hecks
|
|
|
70
74
|
# The check runs against `sha`, once none is still running.
|
|
71
75
|
def settled_runs(sha)
|
|
72
76
|
runs = check_runs(sha)
|
|
73
|
-
raise "gh reports no checks at all against #{sha}" if runs.empty?
|
|
77
|
+
raise Error, "gh reports no checks at all against #{sha}" if runs.empty?
|
|
74
78
|
|
|
75
79
|
# Refuse rather than answer on an incomplete run; asking again once it settles is safe.
|
|
76
80
|
incomplete = runs.reject { |run| run["status"] == "completed" }
|
|
77
|
-
raise "checks against #{sha} are still running — asked before they settled" if incomplete.any?
|
|
81
|
+
raise Error, "checks against #{sha} are still running — asked before they settled" if incomplete.any?
|
|
78
82
|
|
|
79
83
|
runs
|
|
80
84
|
end
|
|
@@ -95,7 +99,7 @@ module Hecks
|
|
|
95
99
|
def sha_of(commit)
|
|
96
100
|
return commit if commit.is_a?(String)
|
|
97
101
|
|
|
98
|
-
|
|
102
|
+
IndifferentKey.read(commit, :value).to_s
|
|
99
103
|
end
|
|
100
104
|
|
|
101
105
|
# Every page of check runs: a red run past the first page must still be seen.
|
|
@@ -115,11 +119,11 @@ module Hecks
|
|
|
115
119
|
def check_run_page(sha, page)
|
|
116
120
|
path = "repos/{owner}/{repo}/commits/#{sha}/check-runs?per_page=#{PER_PAGE}&page=#{page}"
|
|
117
121
|
out, err, status = Open3.capture3("gh", "api", path)
|
|
118
|
-
raise "gh api check-runs failed for #{sha}: #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
|
|
122
|
+
raise Error, "gh api check-runs failed for #{sha}: #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
|
|
119
123
|
|
|
120
124
|
JSON.parse(out)
|
|
121
125
|
rescue Errno::ENOENT
|
|
122
|
-
raise "gh is not installed or not on PATH — cannot read check runs for #{sha}"
|
|
126
|
+
raise Error, "gh is not installed or not on PATH — cannot read check runs for #{sha}"
|
|
123
127
|
end
|
|
124
128
|
end
|
|
125
129
|
end
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "open3"
|
|
4
|
+
require_relative "../../indifferent_key"
|
|
4
5
|
|
|
5
6
|
module Hecks
|
|
6
7
|
module Adapters
|
|
7
8
|
# The `IssueTracker` port's adapter: files a `QualityControl::Ticket` as a GitHub issue.
|
|
8
9
|
# Raises when `gh` refuses, which the runtime records as the ticket's refusal.
|
|
9
10
|
class GithubIssues
|
|
11
|
+
# Raised when `gh` cannot file the issue.
|
|
12
|
+
class Error < RuntimeError; end
|
|
13
|
+
|
|
10
14
|
# @param aggregate [Object, nil] unused
|
|
11
15
|
# @param settings [Hash] unused
|
|
12
16
|
# @param root [String, nil] unused
|
|
@@ -18,7 +22,7 @@ module Hecks
|
|
|
18
22
|
# value objects (`{value: ...}`) or bare strings, and `pull_request` names a proposed fix
|
|
19
23
|
# @return [Hash{Symbol => Hash}] `number:` and `url:` of the issue, in the shape the
|
|
20
24
|
# ticket's `Filed` command takes
|
|
21
|
-
# @raise [
|
|
25
|
+
# @raise [Error] when `gh` exits non-zero or prints no issue URL
|
|
22
26
|
def file(**held)
|
|
23
27
|
out = create_issue(plain(held[:repository]), plain(held[:title]).to_s, issue_body(held))
|
|
24
28
|
url, number = issue_url_and_number(out)
|
|
@@ -37,7 +41,7 @@ module Hecks
|
|
|
37
41
|
def issue_url_and_number(out)
|
|
38
42
|
url = out.strip.lines.last.to_s.strip
|
|
39
43
|
number = url[%r{/issues/(\d+)\z}, 1]
|
|
40
|
-
raise "gh issue create printed no issue URL: #{out.strip.inspect}" unless number
|
|
44
|
+
raise Error, "gh issue create printed no issue URL: #{out.strip.inspect}" unless number
|
|
41
45
|
|
|
42
46
|
[url, number]
|
|
43
47
|
end
|
|
@@ -45,7 +49,7 @@ module Hecks
|
|
|
45
49
|
def create_issue(repository, title, body)
|
|
46
50
|
out, err, status = Open3.capture3("gh", "issue", "create", "--repo", repository,
|
|
47
51
|
"--title", title, "--body", body)
|
|
48
|
-
raise "gh issue create failed — #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
|
|
52
|
+
raise Error, "gh issue create failed — #{err.strip.empty? ? out.strip : err.strip}" unless status.success?
|
|
49
53
|
|
|
50
54
|
out
|
|
51
55
|
end
|
|
@@ -54,7 +58,7 @@ module Hecks
|
|
|
54
58
|
def plain(field)
|
|
55
59
|
return field unless field.is_a?(Hash)
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
IndifferentKey.read(field, :value)
|
|
58
62
|
end
|
|
59
63
|
end
|
|
60
64
|
end
|
|
@@ -11,8 +11,8 @@ require_relative "qa_open_pr/recording"
|
|
|
11
11
|
module Hecks
|
|
12
12
|
module QualityControlCli
|
|
13
13
|
# The command behind `hecks quality_control patch.open`: opens a PR and records it in the
|
|
14
|
-
# QualityControl ledger in one step. It is the only
|
|
15
|
-
# `QualityControl::Improvement.Open`.
|
|
14
|
+
# QualityControl ledger in one step. It is the only entry point into
|
|
15
|
+
# `QualityControl::Patch.Open` and `QualityControl::Improvement.Open`.
|
|
16
16
|
#
|
|
17
17
|
# It refuses (exit 1, nothing opened or recorded) unless the ledger's own `Open` command would
|
|
18
18
|
# take the record and the `GitPr` adapter agrees. The command's `given`s are the rules, asked
|
|
@@ -10,7 +10,7 @@ module Hecks
|
|
|
10
10
|
private
|
|
11
11
|
|
|
12
12
|
# Re-runs only the comparison that surprised; `seat` matters only for `self_consistency`,
|
|
13
|
-
# which reads the Rust binary's rehydration
|
|
13
|
+
# which reads the Rust binary's rehydration entry point under a differential seat.
|
|
14
14
|
def candidate_divergences(check_mode, seat, steps)
|
|
15
15
|
case check_mode
|
|
16
16
|
when :differential
|
|
@@ -37,7 +37,7 @@ module Hecks
|
|
|
37
37
|
|
|
38
38
|
def print_finding_instructions
|
|
39
39
|
puts "No Bug was logged (that needs a real failing-test demonstration, which"
|
|
40
|
-
puts "needs judgment this script does not have — hecks quality_control log is the
|
|
40
|
+
puts "needs judgment this script does not have — hecks quality_control log is the entry point)"
|
|
41
41
|
puts "and the sweep was not concluded. The ledger's own SuspendOnSurprise"
|
|
42
42
|
puts "policy took the target out of the rotation; it stays out until a PERSON"
|
|
43
43
|
puts "runs: hecks quality_control ask run target=#{@target_reference} " \
|