hecks 3.0.5 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/hecks +24 -12
- data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +1 -1
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +2 -2
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +59 -32
- data/lib/hecks/bluebook/hexagon.rb +45 -25
- data/lib/hecks/bluebook/model_check.rb +7 -7
- data/lib/hecks/chapters.rb +31 -3
- data/lib/hecks/cli/check_era.rb +1 -1
- data/lib/hecks/cli/deploy_template_diff.rb +1 -1
- data/lib/hecks/cli/doc_coverage.rb +1 -1
- data/lib/hecks/cli/domain_stub.rb +128 -0
- data/lib/hecks/cli/interview_draft.rb +246 -0
- data/lib/hecks/cli/model_check.rb +1 -1
- data/lib/hecks/cli/project_cli.rb +51 -29
- data/lib/hecks/cli/reference.rb +2 -1
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +1 -1
- data/lib/hecks/cli/seed_semantics_corpus.rb +3 -3
- data/lib/hecks/cli/spec_example.rb +1 -1
- data/lib/hecks/cli/stress_concurrency_specs.rb +14 -5
- data/lib/hecks/corpus.rb +11 -8
- data/lib/hecks/deploy/bluebook/deploy.bluebook +85 -12
- data/lib/hecks/deploy/oidc.json +5 -0
- data/lib/hecks/doc/reference.rb +4 -3
- data/lib/hecks/doors/cli_door.rb +13 -13
- data/lib/hecks/doors/cli_runner.rb +52 -32
- data/lib/hecks/doors/launcher_options.rb +5 -5
- data/lib/hecks/doors/mcp_door.rb +40 -18
- data/lib/hecks/doors/mcp_door_scope.rb +244 -24
- data/lib/hecks/doors.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +2 -2
- data/lib/hecks/forms/banking_presentation.rb +1 -1
- data/lib/hecks/framework/bluebook/compliance.bluebook +1 -1
- data/lib/hecks/framework/bluebook/console_settings.bluebook +1 -1
- data/lib/hecks/framework/bluebook/framework.hecksagon +4 -4
- data/lib/hecks/framework.rb +1 -1
- data/lib/hecks/fuzzing/combination_miner.rb +1 -1
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +1 -1
- data/lib/hecks/fuzzing/differential.rb +4 -2
- data/lib/hecks/fuzzing/era_boundary.rb +1 -1
- data/lib/hecks/fuzzing/form_census.rb +2 -2
- data/lib/hecks/fuzzing/generated_domain_check.rb +1 -1
- data/lib/hecks/fuzzing/isolated_boot.rb +10 -3
- data/lib/hecks/fuzzing/persistence_parity.rb +2 -2
- data/lib/hecks/fuzzing/target_capabilities.rb +3 -3
- data/lib/hecks/gate/stages.yml +5 -5
- data/lib/hecks/grammar/grammar.hecksagon +2 -2
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +4 -4
- data/lib/hecks/hecks/adapters/host_http.rb +4 -3
- data/lib/hecks/hecks/adapters/local_files.rb +59 -0
- data/lib/hecks/hecks/adapters/site_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +54 -0
- data/lib/hecks/hecks/codebase.bluebook +4 -3
- data/lib/hecks/hecks/context_map.hecksagon +2 -2
- data/lib/hecks/hecks/custodian.bluebook +45 -3
- data/lib/hecks/hecks/hecks.hecksagon +10 -4
- data/lib/hecks/hecks/hecks.world +6 -6
- data/lib/hecks/hecks/site.behaviors +17 -0
- data/lib/hecks/hecks/site.world +10 -0
- data/lib/hecks/language/bluebook/bluebook.hecksagon +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +24 -47
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +4 -4
- data/lib/hecks/projections/deploy/box/settings.rb +248 -0
- data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +267 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +70 -0
- data/lib/hecks/projections/deploy/box/templates/fetch-secrets.sh +21 -0
- data/lib/hecks/projections/deploy/box/templates/rds.yaml.tmpl +165 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +43 -0
- data/lib/hecks/projections/deploy/box.rb +387 -0
- data/lib/hecks/projections/deploy/fargate/cdn.rb +13 -1
- data/lib/hecks/projections/deploy/fargate.rb +1 -1
- data/lib/hecks/projections/deploy/lambda.rb +11 -10
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +1 -1
- data/lib/hecks/projections/diagrams.rb +2 -2
- data/lib/hecks/projections/site/edge/behaviours.rb +107 -0
- data/lib/hecks/projections/site/edge/checks.rb +137 -0
- data/lib/hecks/projections/site/edge/pattern.rb +50 -0
- data/lib/hecks/projections/site/edge/rules.rb +128 -0
- data/lib/hecks/projections/site/edge.rb +136 -0
- data/lib/hecks/projections/site/regions.rb +44 -0
- data/lib/hecks/projections/site/routes_ts.rb +284 -0
- data/lib/hecks/projections/site/site_cdn.rb +78 -0
- data/lib/hecks/projections/site/table/checks.rb +124 -0
- data/lib/hecks/projections/site/table/row_builder.rb +155 -0
- data/lib/hecks/projections/site/table.rb +98 -0
- data/lib/hecks/projections.rb +2 -0
- data/lib/hecks/projector/cli_projector.rb +122 -104
- data/lib/hecks/quality_control/adapters/agent.adapter +1 -1
- data/lib/hecks/quality_control/adapters/agent.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +8 -8
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +4 -4
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +6 -5
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +5 -5
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +3 -3
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep.rb +10 -9
- data/lib/hecks/quality_control/cli/qa_tick.rb +3 -3
- data/lib/hecks/quality_control/quality_control.bluebook +18 -18
- data/lib/hecks/quality_control/quality_control.hecksagon +1 -1
- data/lib/hecks/runtime/registry/verification.rb +15 -29
- data/lib/hecks/runtime/registry.rb +14 -20
- data/lib/hecks/rust_build/append_optionals.rb +79 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +2 -1
- data/lib/hecks/rust_build/domain_name.rb +26 -0
- data/lib/hecks/rust_build/kernel_input.rb +58 -0
- data/lib/hecks/rust_build/project_rust/codegen_run.rb +143 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +1 -1
- data/lib/hecks/rust_build/project_rust.rb +57 -24
- data/lib/hecks/rust_build/rust_literal.rb +33 -0
- data/lib/hecks/rust_build/write_if_changed.rb +86 -0
- data/lib/hecks/site/bluebook/site.bluebook +205 -0
- data/lib/hecks/site/bluebook/site.hecksagon +5 -0
- data/lib/hecks/site/bluebook/site.ports.hecksagon +15 -0
- data/lib/hecks/sme/bluebook/sme.bluebook +336 -0
- data/lib/hecks/sme/bluebook/sme.hecksagon +5 -0
- data/lib/hecks/storehouse.rb +37 -7
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +1 -1
- data/lib/hecks/three_zero/forms.yml +88 -88
- data/lib/hecks/tools/ci_gates.rb +1 -1
- data/lib/hecks/tools/deploy_recipe.rb +14 -6
- data/lib/hecks/tools/oidc_manifests.rb +2 -1
- data/lib/hecks/tools/sequence_script.rb +2 -2
- data/lib/hecks/tools/site_routes.rb +152 -0
- data/lib/hecks/tools/tenant_provisioning.rb +1 -1
- data/lib/hecks/tools.rb +1 -0
- data/lib/hecks/version.rb +1 -1
- data/rust/build/src/cargo_sync.rs +1 -1
- data/rust/build/src/pipeline.rs +23 -16
- data/rust/build/src/resolve.rs +39 -6
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +1 -1
- data/rust/host/src/dispatch.rs +87 -7
- data/rust/host/src/needs.rs +111 -0
- data/rust/parser/src/keywords.rs +7 -6
- data/rust/parser/src/main.rs +4 -3
- data/rust/parser/src/parse/chapter.rs +1 -1
- data/rust/parser/src/parse/hecksagon.rs +25 -10
- data/rust/project/mutations.rb +4 -30
- data/rust/project/naming.rb +8 -28
- data/rust/project/write_if_changed.rb +4 -80
- data/rust/project_rust_pipeline.rb +16 -12
- data/rust/src/kernel/cli.rs +2 -0
- data/rust/src/kernel/needs.rs +74 -1
- metadata +37 -1
|
@@ -4,12 +4,12 @@ require "pg"
|
|
|
4
4
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module QualityControlCli
|
|
7
|
-
# The command behind `hecks quality_control create_ledger_role`: creates the ordinary
|
|
8
|
-
# role the QA ledger connects as and makes it own the database. It is idempotent, and
|
|
9
|
-
# superuser or `BYPASSRLS` role, which the era write-fence cannot bind.
|
|
7
|
+
# The command behind `hecks quality_control sweep.create_ledger_role`: creates the ordinary
|
|
8
|
+
# Postgres role the QA ledger connects as and makes it own the database. It is idempotent, and
|
|
9
|
+
# refuses a superuser or `BYPASSRLS` role, which the era write-fence cannot bind.
|
|
10
10
|
#
|
|
11
|
-
# hecks quality_control create_ledger_role <database> [--role <role>] # role defaults
|
|
12
|
-
# hecks_qa
|
|
11
|
+
# hecks quality_control sweep.create_ledger_role <database> [--role <role>] # role defaults
|
|
12
|
+
# to hecks_qa
|
|
13
13
|
class QaPostgresRole
|
|
14
14
|
USAGE = "usage: hecks quality_control create_ledger_role <database> [--role <role>]"
|
|
15
15
|
|
|
@@ -7,9 +7,9 @@ require_relative "../adapters/git_pr"
|
|
|
7
7
|
|
|
8
8
|
module Hecks
|
|
9
9
|
module QualityControlCli
|
|
10
|
-
# The command behind `hecks quality_control check_pull_requests`: checks CI for every
|
|
11
|
-
# QualityControl ledger tracks as open (`Patch` and `Improvement`). It starts a
|
|
12
|
-
# unsettled head commit and lets the CI port's policies record the verdict.
|
|
10
|
+
# The command behind `hecks quality_control clearance.check_pull_requests`: checks CI for every
|
|
11
|
+
# PR the QualityControl ledger tracks as open (`Patch` and `Improvement`). It starts a
|
|
12
|
+
# `Clearance` per unsettled head commit and lets the CI port's policies record the verdict.
|
|
13
13
|
#
|
|
14
14
|
# Exit codes: 0 nothing to act on; 1 operational error (`gh`, bad sha, ledger boot);
|
|
15
15
|
# 2 a newly red PR, with the details on stdout. A commit's `Clearance` is minted once and never
|
|
@@ -76,9 +76,9 @@ module Hecks
|
|
|
76
76
|
query("Target.Held").select { |row| row[:claimed_at][:value] + row[:window][:value] <= now }
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
# Spawns `hecks quality_control
|
|
80
|
-
# Seeds/steps forward only when given, so each child derives its own depth from its
|
|
81
|
-
# streak.
|
|
79
|
+
# Spawns `hecks quality_control query sweep.run target=<target>` as a fresh process, not a
|
|
80
|
+
# fork. Seeds/steps forward only when given, so each child derives its own depth from its
|
|
81
|
+
# own streak.
|
|
82
82
|
def spawn_sweep_child(target_reference, seeds_override, steps_override, self_consistency,
|
|
83
83
|
modes:, parity: false)
|
|
84
84
|
# Unlinked at once: the open fd stays readable, nothing is left on disk, and no fixed path
|
|
@@ -304,8 +304,8 @@ module Hecks
|
|
|
304
304
|
|
|
305
305
|
# Coverage-guided generation's corpus, one JSON file per (target, mode): `--all`'s waves can
|
|
306
306
|
# run a target's primary sweep and its persistence-parity/concurrency variants as separate
|
|
307
|
-
# `hecks quality_control
|
|
308
|
-
# conceptually independent axis of the same domain, so each keeps its own file rather than
|
|
307
|
+
# `hecks quality_control query sweep.run` processes within the same tick, and each explores
|
|
308
|
+
# a conceptually independent axis of the same domain, so each keeps its own file rather than
|
|
309
309
|
# sharing one that nothing here locks.
|
|
310
310
|
def coverage_corpus_path(target_reference, mode)
|
|
311
311
|
File.join(@coverage_corpus_dir, "#{filesystem_safe_component(target_reference)}-#{mode}.json")
|
|
@@ -22,10 +22,10 @@ require_relative "qa_sweep/finding_report"
|
|
|
22
22
|
|
|
23
23
|
module Hecks
|
|
24
24
|
module QualityControlCli
|
|
25
|
-
# The command behind `hecks quality_control
|
|
26
|
-
# release cycle against a `QualityControl` target (`USAGE` lists the forms, from a bare
|
|
27
|
-
# of the least recently swept target to `--all` and `--release`). It stops with a report
|
|
28
|
-
# exit 2 on the first surprised check, and never logs a `Bug` itself.
|
|
25
|
+
# The command behind `hecks quality_control query sweep.run`: runs one claim, sweep, conclude
|
|
26
|
+
# and release cycle against a `QualityControl` target (`USAGE` lists the forms, from a bare
|
|
27
|
+
# sweep of the least recently swept target to `--all` and `--release`). It stops with a report
|
|
28
|
+
# and exit 2 on the first surprised check, and never logs a `Bug` itself.
|
|
29
29
|
#
|
|
30
30
|
# Exit codes: 0 clean; 1 operational error; 2 found something (the sweep stays open and the
|
|
31
31
|
# target is suspended by the ledger's `SuspendOnSurprise` policy until `--release`).
|
|
@@ -136,11 +136,12 @@ module Hecks
|
|
|
136
136
|
path.start_with?("/") ? File.expand_path(path) : File.expand_path(path, @root)
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
-
# A target reference may itself contain `/` (`hecks quality_control
|
|
140
|
-
# suggests `repo/entity`-shaped references for an external
|
|
141
|
-
# gets folded into a single filename component: a log
|
|
142
|
-
# coverage-corpus filename. Left raw, an embedded `/` is
|
|
143
|
-
# nothing creates, breaking the write. This never changes
|
|
139
|
+
# A target reference may itself contain `/` (`hecks quality_control
|
|
140
|
+
# target.discover_external_domains` suggests `repo/entity`-shaped references for an external
|
|
141
|
+
# domain), but every reference also gets folded into a single filename component: a log
|
|
142
|
+
# prefix, a shrunk-repro filename, a coverage-corpus filename. Left raw, an embedded `/` is
|
|
143
|
+
# read as an extra path segment that nothing creates, breaking the write. This never changes
|
|
144
|
+
# what is stored as the
|
|
144
145
|
# `Target`/`Sweep` reference itself.
|
|
145
146
|
def filesystem_safe_component(reference)
|
|
146
147
|
reference.gsub(/[^A-Za-z0-9_.-]/, "-")
|
|
@@ -9,9 +9,9 @@ require_relative "child"
|
|
|
9
9
|
|
|
10
10
|
module Hecks
|
|
11
11
|
module QualityControlCli
|
|
12
|
-
# The command behind `hecks quality_control tick`: one QA tick. It needs a clean tree,
|
|
13
|
-
# on `origin/main`, then runs `QaPrCheck`, `QaSweep --all` and `QaGeneratedDomains
|
|
14
|
-
# and prints one report.
|
|
12
|
+
# The command behind `hecks quality_control sweep.tick`: one QA tick. It needs a clean tree,
|
|
13
|
+
# rebases on `origin/main`, then runs `QaPrCheck`, `QaSweep --all` and `QaGeneratedDomains
|
|
14
|
+
# --from-dials` and prints one report.
|
|
15
15
|
#
|
|
16
16
|
# It exits 2 if any step found something, 1 if none did but a step errored or it refused, 0 if
|
|
17
17
|
# all are clean. The step order is enforced here, not in prose. `QA_REPO_DIR` picks the
|
|
@@ -22,7 +22,7 @@ unless defined?(QualityControlDials)
|
|
|
22
22
|
# `SweepDepth.for_streak`. The last row (`upto: Float::INFINITY`) is the ceiling.
|
|
23
23
|
WIDENING_TIERS = settings.widening_tiers
|
|
24
24
|
|
|
25
|
-
# How many `hecks quality_control
|
|
25
|
+
# How many `hecks quality_control query sweep.run <target>` children `--all` keeps running at once.
|
|
26
26
|
SWEEP_MAX_PARALLEL = settings.sweep_max_parallel
|
|
27
27
|
|
|
28
28
|
# How long the `/loop hecks_qa` caller waits for a tick's report before assuming it stalled.
|
|
@@ -53,11 +53,11 @@ unless defined?(QualityControlDials)
|
|
|
53
53
|
# How many of the least-hit verbs the next seed weights up; 0 turns the lever off.
|
|
54
54
|
FAVOR_RARE_VERBS = settings.favor_rare_verbs
|
|
55
55
|
|
|
56
|
-
# Whether `hecks quality_control
|
|
56
|
+
# Whether `hecks quality_control query sweep.run` also runs the self-consistency pass (cold rehydration, replay
|
|
57
57
|
# idempotency, value-object JSON round trip) on the same generated sequence.
|
|
58
58
|
SELF_CONSISTENCY_CHECKS = settings.self_consistency_checks
|
|
59
59
|
|
|
60
|
-
# Candidate replays `hecks quality_control
|
|
60
|
+
# Candidate replays `hecks quality_control query sweep.run` may spend shrinking one surprised check; 0 turns shrinking
|
|
61
61
|
# off. `concurrency` is never shrunk: a race does not reproduce on demand.
|
|
62
62
|
SHRINK_BUDGET = settings.shrink_budget
|
|
63
63
|
|
|
@@ -74,7 +74,7 @@ unless defined?(QualityControlDials)
|
|
|
74
74
|
ROTATION_STALE_FLOOR_SECONDS = settings.rotation_stale_floor_seconds
|
|
75
75
|
|
|
76
76
|
# Ceiling on `--seeds` for `--persistence-parity`, which pays for real Postgres per dispatch.
|
|
77
|
-
# `hecks quality_control
|
|
77
|
+
# `hecks quality_control query sweep.run` clamps down, never up, and prints a note.
|
|
78
78
|
PERSISTENCE_PARITY_SEED_CAP = settings.persistence_parity_seed_cap
|
|
79
79
|
|
|
80
80
|
# Ceiling on `--seeds` for the concurrency mode, which needs two disposable schemas per seed.
|
|
@@ -85,11 +85,11 @@ unless defined?(QualityControlDials)
|
|
|
85
85
|
# disposable database.
|
|
86
86
|
ADAPTER_PARITY_PAIRS = settings.adapter_parity_pairs
|
|
87
87
|
|
|
88
|
-
# Which comparison modes `hecks quality_control
|
|
88
|
+
# Which comparison modes `hecks quality_control query sweep.run` may run: the enabled ones intersected with what
|
|
89
89
|
# `TargetCapabilities.infer(path)` finds in the target. `--modes a,b,c` overrides for one run.
|
|
90
90
|
#
|
|
91
91
|
# `wasm_front` and `adapter_parity_postgres` are off because nothing runs them; the others are
|
|
92
|
-
# built and off because their I/O cost across the rotation is unmeasured. `hecks quality_control
|
|
92
|
+
# built and off because their I/O cost across the rotation is unmeasured. `hecks quality_control query sweep.run`
|
|
93
93
|
# refuses to start when a mode outside `TargetCapabilities::RUNNABLE_MODES` is enabled.
|
|
94
94
|
MODES = settings.modes
|
|
95
95
|
|
|
@@ -101,7 +101,7 @@ unless defined?(QualityControlDials)
|
|
|
101
101
|
# off. Kept low: a dry run creates no state for later steps to act on.
|
|
102
102
|
DRY_RUN_FRACTION = settings.dry_run_fraction
|
|
103
103
|
|
|
104
|
-
# How many generated domains one `hecks quality_control tick` checks after the rotation sweep; 0 skips the
|
|
104
|
+
# How many generated domains one `hecks quality_control sweep.tick` checks after the rotation sweep; 0 skips the
|
|
105
105
|
# step.
|
|
106
106
|
GENERATED_DOMAINS_PER_TICK = settings.generated_domains_per_tick
|
|
107
107
|
|
|
@@ -140,7 +140,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
140
140
|
attribute :last_swept, SweptIn
|
|
141
141
|
# Recency-decayed count of how often this chapter was worth sweeping; see `YieldScore`.
|
|
142
142
|
attribute :yield_score, YieldScore
|
|
143
|
-
# Consecutive `Release`s that came back clean; `hecks quality_control
|
|
143
|
+
# Consecutive `Release`s that came back clean; `hecks quality_control query sweep.run` reads it to choose sweep depth.
|
|
144
144
|
# Zero means no streak yet, shared on purpose by a never-swept target and a just-surprised one.
|
|
145
145
|
attribute :clean_streak, CleanStreak
|
|
146
146
|
# Comma-joined mode names recorded at release from what the runner inferred. Stored so
|
|
@@ -367,7 +367,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
367
367
|
end
|
|
368
368
|
|
|
369
369
|
query "Suspended" do
|
|
370
|
-
description "Chapters a check surprised, waiting for a person — each with the reason the policy wrote. Nothing automatic touches these; hecks quality_control
|
|
370
|
+
description "Chapters a check surprised, waiting for a person — each with the reason the policy wrote. Nothing automatic touches these; hecks quality_control query sweep.run <target> --release --notes is how one comes back."
|
|
371
371
|
where(status: "suspended")
|
|
372
372
|
order_by :reference
|
|
373
373
|
end
|
|
@@ -854,7 +854,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
854
854
|
# `default:` keeps rows already on file hydrating as `undecided`.
|
|
855
855
|
attribute :disposition, one_of("undecided", "self_contained", "bigger"), default: { value: "undecided" }
|
|
856
856
|
# `default: yes` is true of every bug on file, since `Log` always required a failing
|
|
857
|
-
# demonstration. `no` is the escape hatch for a real but flaky finding (`hecks quality_control log
|
|
857
|
+
# demonstration. `no` is the escape hatch for a real but flaky finding (`hecks quality_control bug.log
|
|
858
858
|
# --reproduced no`).
|
|
859
859
|
attribute :reproduced, one_of("yes", "no"), default: { value: "yes" }
|
|
860
860
|
# Bugs are claimed like chapters so two agents do not fix the same one. A `given` rather than a
|
|
@@ -1023,7 +1023,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
1023
1023
|
# Optional; `Name` covers bugs logged without one. No `sets`: on a creation command an
|
|
1024
1024
|
# argument sharing the aggregate field's name lands there directly.
|
|
1025
1025
|
attribute :name, BugName, optional: true
|
|
1026
|
-
# Optional; omitted, the aggregate's default `yes` applies. `hecks quality_control log --reproduced no`
|
|
1026
|
+
# Optional; omitted, the aggregate's default `yes` applies. `hecks quality_control bug.log --reproduced no`
|
|
1027
1027
|
# uses it to skip its must-fail check for a finding with no reliable pass/fail signal.
|
|
1028
1028
|
attribute :reproduced, one_of("yes", "no"), optional: true
|
|
1029
1029
|
|
|
@@ -1059,7 +1059,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
1059
1059
|
end
|
|
1060
1060
|
|
|
1061
1061
|
# Not a lifecycle move (the `Rank` shape); `undecided` is only ever the birth value.
|
|
1062
|
-
# `hecks quality_control log --triage` dispatches this right after `Log`.
|
|
1062
|
+
# `hecks quality_control bug.log --triage` dispatches this right after `Log`.
|
|
1063
1063
|
command "Triage" do
|
|
1064
1064
|
role "QA engineer"
|
|
1065
1065
|
goal "Write down whether this can be fixed on the spot or needs a bigger judgment"
|
|
@@ -1712,7 +1712,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
1712
1712
|
end
|
|
1713
1713
|
end
|
|
1714
1714
|
|
|
1715
|
-
# A PR opened for a Bug's own fix, recorded at open so `hecks quality_control check_pull_requests` looks it up by number.
|
|
1715
|
+
# A PR opened for a Bug's own fix, recorded at open so `hecks quality_control clearance.check_pull_requests` looks it up by number.
|
|
1716
1716
|
# Not named `PullRequest`: `Ticket::PullRequest` exists and aggregate names install on Object.
|
|
1717
1717
|
aggregate "Patch" do
|
|
1718
1718
|
description "One pull request opened for a Bug's own fix — number, branch, and the exact commit that makes checking its CI status a lookup instead of a guess."
|
|
@@ -1814,9 +1814,9 @@ Hecks.bluebook "QualityControl" do
|
|
|
1814
1814
|
emits "PatchClosed"
|
|
1815
1815
|
end
|
|
1816
1816
|
|
|
1817
|
-
# The worklist for `hecks quality_control check_pull_requests`: ask `gh` about each row's exact number, never search.
|
|
1817
|
+
# The worklist for `hecks quality_control clearance.check_pull_requests`: ask `gh` about each row's exact number, never search.
|
|
1818
1818
|
query "Open" do
|
|
1819
|
-
description "Every PR we've opened that's still open, by number — hecks quality_control check_pull_requests's own worklist. For each of these, ask gh for exactly this number's CI status; nothing here is a guess."
|
|
1819
|
+
description "Every PR we've opened that's still open, by number — hecks quality_control clearance.check_pull_requests's own worklist. For each of these, ask gh for exactly this number's CI status; nothing here is a guess."
|
|
1820
1820
|
where(status: "opened")
|
|
1821
1821
|
order_by :number
|
|
1822
1822
|
end
|
|
@@ -1979,7 +1979,7 @@ Hecks.bluebook "QualityControl" do
|
|
|
1979
1979
|
end
|
|
1980
1980
|
|
|
1981
1981
|
query "Open" do
|
|
1982
|
-
description "Every PR we've landed for deliberate, non-bugfix work that's still open, by number — hecks quality_control check_pull_requests's own second worklist, the same shape Patch.Open already gives for a bug's own fix. For each of these, ask gh for exactly this number's CI status; nothing here is a guess."
|
|
1982
|
+
description "Every PR we've landed for deliberate, non-bugfix work that's still open, by number — hecks quality_control clearance.check_pull_requests's own second worklist, the same shape Patch.Open already gives for a bug's own fix. For each of these, ask gh for exactly this number's CI status; nothing here is a guess."
|
|
1983
1983
|
where(status: { in: "opened,landed,needs_fix" })
|
|
1984
1984
|
order_by :number
|
|
1985
1985
|
end
|
|
@@ -2217,14 +2217,14 @@ Hecks.bluebook "QualityControl" do
|
|
|
2217
2217
|
# Nothing reacts to `TicketRaised` on purpose: a ticket waits until somebody submits it.
|
|
2218
2218
|
|
|
2219
2219
|
# The rotation's one automatic reaction. Releasing on `SweepConcluded` cannot work: that event
|
|
2220
|
-
# carries no target id, `next_streak` or yield score, so `hecks quality_control
|
|
2220
|
+
# carries no target id, `next_streak` or yield score, so `hecks quality_control query sweep.run` still releases.
|
|
2221
2221
|
# `on Sweep::CheckSurprised`, not `Sweep::Check::...`: an entity command's event is stamped with
|
|
2222
2222
|
# its parent aggregate. `Check.Surprised` restates `target` so the id rides in.
|
|
2223
2223
|
policy "SuspendOnSurprise" do
|
|
2224
2224
|
on Sweep::CheckSurprised
|
|
2225
2225
|
trigger Target::Suspend, with: {
|
|
2226
2226
|
target: :target,
|
|
2227
|
-
reason: { value: "a check surprised — a person releases with hecks quality_control
|
|
2227
|
+
reason: { value: "a check surprised — a person releases with hecks quality_control query sweep.run <target> --release --notes" }
|
|
2228
2228
|
}
|
|
2229
2229
|
end
|
|
2230
2230
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Hecks chapter binds it through its world's `default_adapter`; the ports every one of them shares
|
|
4
4
|
# are in quality_control.ports.hecksagon.
|
|
5
5
|
Hecks.hecksagon "QualityControl" do
|
|
6
|
-
|
|
6
|
+
attaches "Governance"
|
|
7
7
|
persisted_by "Memory"
|
|
8
8
|
end
|
|
9
9
|
|
|
@@ -20,8 +20,7 @@ module Hecks
|
|
|
20
20
|
|
|
21
21
|
@hecksagons.each_value do |hexagon|
|
|
22
22
|
refuse_ungoverned_roles!(hexagon)
|
|
23
|
-
|
|
24
|
-
refuse_unwired_vendored_bluebooks!(hexagon)
|
|
23
|
+
refuse_unwired_attachments!(hexagon)
|
|
25
24
|
refuse_bounded_without_acl!(hexagon)
|
|
26
25
|
|
|
27
26
|
hexagon.binds.each do |bind|
|
|
@@ -153,7 +152,7 @@ module Hecks
|
|
|
153
152
|
private
|
|
154
153
|
|
|
155
154
|
# Checked once against the merged hecksagon so a domain split across multiple
|
|
156
|
-
# hecksagon files sees every
|
|
155
|
+
# hecksagon files sees every `attaches` declaration first. A role is real
|
|
157
156
|
# access control only once an authorization provider exists to check it against
|
|
158
157
|
# (ADR 0025); a provider is recognized by declaring authorization, not by name.
|
|
159
158
|
def refuse_ungoverned_roles!(hexagon)
|
|
@@ -172,16 +171,18 @@ module Hecks
|
|
|
172
171
|
"without that it is silent decoration, the exact defect this refusal exists to catch"
|
|
173
172
|
end
|
|
174
173
|
|
|
175
|
-
# `
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
174
|
+
# `attaches` loads a bounded context; the consumer must declare the sibling hecksagon
|
|
175
|
+
# that is its ACL (Governance/Identity/Privacy already do). A vendored package only
|
|
176
|
+
# loads its `.bluebook` files, so persistence, Governance and the `translates` ACL live
|
|
177
|
+
# on that sibling too, or cross-context field mapping has nowhere to be written.
|
|
178
|
+
def refuse_unwired_attachments!(hexagon)
|
|
179
|
+
hexagon.attachments.each do |attachment|
|
|
180
|
+
chapter_name = attachment.chapter_name
|
|
181
181
|
next if hecksagon(chapter_name)
|
|
182
182
|
|
|
183
|
+
what = attachment.vendor? ? "vendored bluebook #{attachment.name.inspect}" : chapter_name.inspect
|
|
183
184
|
raise WiringError,
|
|
184
|
-
"#{hexagon.domain} attaches
|
|
185
|
+
"#{hexagon.domain} attaches #{what} " \
|
|
185
186
|
"(bounded context #{chapter_name}) but never declared " \
|
|
186
187
|
"Hecks.hecksagon #{chapter_name.inspect} — put that sibling " \
|
|
187
188
|
"(and any `translates` ACL) in context_map.hecksagon; " \
|
|
@@ -189,23 +190,8 @@ module Hecks
|
|
|
189
190
|
end
|
|
190
191
|
end
|
|
191
192
|
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
def refuse_unwired_framework_members!(hexagon)
|
|
195
|
-
hexagon.member_chapters.each do |member|
|
|
196
|
-
next if hecksagon(member)
|
|
197
|
-
|
|
198
|
-
raise WiringError,
|
|
199
|
-
"#{hexagon.domain} attaches #{member.inspect} " \
|
|
200
|
-
"(bounded context) but never declared Hecks.hecksagon " \
|
|
201
|
-
"#{member.inspect} — put that sibling (and any `translates` " \
|
|
202
|
-
"ACL) in context_map.hecksagon; same-name blocks merge, " \
|
|
203
|
-
"order-independent."
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# An explicit `bounded` mark on a consumer chapter always needs an ACL. `uses_framework`
|
|
208
|
-
# / `uses_embryonaut_bluebook` mark the attached chapter bounded and require the sibling
|
|
193
|
+
# An explicit `bounded` mark on a consumer chapter always needs an ACL. `attaches`
|
|
194
|
+
# marks the attached chapter bounded and requires the sibling
|
|
209
195
|
# hecksagon above; they don't require a `translates` on it unless the consumer also
|
|
210
196
|
# wrote `bounded`.
|
|
211
197
|
# rust/host Google sign-in reads ir.json's membership/identity keys, never a deploy-time
|
|
@@ -217,7 +203,7 @@ module Hecks
|
|
|
217
203
|
raise WiringError,
|
|
218
204
|
"a chapter that provides \"membership\" is loaded, but none provides " \
|
|
219
205
|
"\"identity\" — rust/host Google sign-in cannot register or link an " \
|
|
220
|
-
"identity from the hecksagon/world. Attach Identity (`
|
|
206
|
+
"identity from the hecksagon/world. Attach Identity (`attaches " \
|
|
221
207
|
"\"Identity\"` plus a sibling Hecks.hecksagon \"Identity\") so the " \
|
|
222
208
|
"identity verbs are exported onto ir.json, not guessed at deploy."
|
|
223
209
|
end
|
|
@@ -387,7 +373,7 @@ module Hecks
|
|
|
387
373
|
providers = Framework.providers_of(Bluebook::Capabilities::AUTHORIZATION)
|
|
388
374
|
return "attaches a chapter that provides \"authorization\" (no framework member declares one)" if providers.empty?
|
|
389
375
|
|
|
390
|
-
providers.map { |name| "
|
|
376
|
+
providers.map { |name| "attaches #{name.inspect}" }.join(" or ")
|
|
391
377
|
end
|
|
392
378
|
|
|
393
379
|
# Every command this domain declares, an aggregate's own and every entity nested
|
|
@@ -139,8 +139,7 @@ module Hecks
|
|
|
139
139
|
mark_bounded(item.domain) if item.bounded?
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
# Marks `name` as a bounded context — called automatically by
|
|
143
|
-
# `uses_framework` / `uses_embryonaut_bluebook`, and by
|
|
142
|
+
# Marks `name` as a bounded context — called automatically by `attaches`, and by
|
|
144
143
|
# `add_hecksagon` when the block itself declared `bounded`.
|
|
145
144
|
# A bounded chapter wraps in its own module (no Object shortcut)
|
|
146
145
|
# and must have a `translates` ACL or boot refuses.
|
|
@@ -151,8 +150,7 @@ module Hecks
|
|
|
151
150
|
@bounded_chapters[name.to_s] = true
|
|
152
151
|
end
|
|
153
152
|
|
|
154
|
-
# Says whether `name` is a bounded context — attached via
|
|
155
|
-
# `uses_framework` / `uses_embryonaut_bluebook`, or a consumer
|
|
153
|
+
# Says whether `name` is a bounded context — attached via `attaches`, or a consumer
|
|
156
154
|
# chapter that declared `bounded` on its own hecksagon.
|
|
157
155
|
#
|
|
158
156
|
# @param name [String, Symbol] the chapter name to check
|
|
@@ -242,7 +240,7 @@ module Hecks
|
|
|
242
240
|
def verbs = @bluebooks.values.flat_map(&:verbs).sort
|
|
243
241
|
|
|
244
242
|
# The chapter that answers a role check for `domain`: the domain's own chapter, or
|
|
245
|
-
# a
|
|
243
|
+
# a chapter its hecksagon attaches, that declares `provides "authorization"`.
|
|
246
244
|
#
|
|
247
245
|
# @param domain [String, Symbol] the domain whose role checks are being resolved
|
|
248
246
|
# @return [Bluebook::Chapter, nil] the chapter that answers `domain`'s role
|
|
@@ -275,7 +273,7 @@ module Hecks
|
|
|
275
273
|
return attached if attached
|
|
276
274
|
|
|
277
275
|
# Falls back to any loaded chapter: a consuming domain often wires Identity as
|
|
278
|
-
# Hecks.hecksagon "Identity" rather than via
|
|
276
|
+
# Hecks.hecksagon "Identity" rather than via `attaches`, so it's loaded but
|
|
279
277
|
# not listed on the consumer's own hexagon.
|
|
280
278
|
@bluebooks.values.find { |chapter| chapter.provides?(Bluebook::Capabilities::IDENTITY) }
|
|
281
279
|
end
|
|
@@ -284,9 +282,8 @@ module Hecks
|
|
|
284
282
|
# chapter, a framework member, or a vendored embryonaut bluebook it attaches,
|
|
285
283
|
# that declares `provides "membership"`.
|
|
286
284
|
#
|
|
287
|
-
# Vendored packages are included
|
|
288
|
-
#
|
|
289
|
-
# framework member.
|
|
285
|
+
# Vendored packages are included, since membership ships as a vendored
|
|
286
|
+
# embryonaut_bluebooks chapter, not a framework member.
|
|
290
287
|
#
|
|
291
288
|
# @param domain [String, Symbol] the domain whose sign-in aggregate is being resolved
|
|
292
289
|
# @return [Bluebook::Chapter, nil] the chapter that answers `domain`'s membership
|
|
@@ -356,7 +353,7 @@ module Hecks
|
|
|
356
353
|
#
|
|
357
354
|
# Falls back to any loaded chapter, since a consuming domain often wires a
|
|
358
355
|
# vendored chapter as its own `Hecks.hecksagon "Name"` rather than via
|
|
359
|
-
# `
|
|
356
|
+
# `attaches ... from: :vendor`, so it's loaded but not listed on the consumer's hexagon.
|
|
360
357
|
#
|
|
361
358
|
# @param domain [String, Symbol] the domain the provider is resolved for
|
|
362
359
|
# @param capability [String] the capability's name, such as
|
|
@@ -364,8 +361,7 @@ module Hecks
|
|
|
364
361
|
# @return [Bluebook::Chapter, nil] the providing chapter, or nil if none loaded does
|
|
365
362
|
def vendored_provider_for(domain, capability)
|
|
366
363
|
hexagon = hecksagon(domain)
|
|
367
|
-
|
|
368
|
-
names = [domain.to_s, *Array(hexagon&.member_chapters), *vendored]
|
|
364
|
+
names = [domain.to_s, *Array(hexagon&.member_chapters)]
|
|
369
365
|
attached = names.filter_map { |name| bluebook(name) }
|
|
370
366
|
.find { |chapter| chapter.provides?(capability) }
|
|
371
367
|
return attached if attached
|
|
@@ -517,14 +513,12 @@ module Hecks
|
|
|
517
513
|
# Order-independent: list facts uniq, so loading context_map before or after the
|
|
518
514
|
# domain file yields the same merged hecksagon.
|
|
519
515
|
Bluebook::Hecksagon.new(
|
|
520
|
-
domain:
|
|
521
|
-
binds:
|
|
522
|
-
subscriptions:
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
bounded: base.bounded? || overlay.bounded?,
|
|
527
|
-
translates: (base.translates + overlay.translates).uniq
|
|
516
|
+
domain: base.domain,
|
|
517
|
+
binds: base.binds + overlay.binds,
|
|
518
|
+
subscriptions: (base.subscriptions + overlay.subscriptions).uniq,
|
|
519
|
+
attachments: (base.attachments + overlay.attachments).uniq,
|
|
520
|
+
bounded: base.bounded? || overlay.bounded?,
|
|
521
|
+
translates: (base.translates + overlay.translates).uniq
|
|
528
522
|
)
|
|
529
523
|
end
|
|
530
524
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module RustBuild
|
|
5
|
+
# Makes an appended element's field `Option<T>` in the IR when any command sources it from an
|
|
6
|
+
# optional argument, since Ruby stores the omitted nil without complaint. `ir.json` records
|
|
7
|
+
# the marking, so it runs before the IR is written or generated from.
|
|
8
|
+
#
|
|
9
|
+
# `rust/build/src/optional_pass.rs` is the Ruby-free build's port of this; it looks only at an
|
|
10
|
+
# aggregate's own value objects, where this reads the domain-wide set.
|
|
11
|
+
module AppendOptionals
|
|
12
|
+
# Marks every aggregate in the IR, in place.
|
|
13
|
+
#
|
|
14
|
+
# @param tree [Hash] a domain's IR, symbol-keyed
|
|
15
|
+
# @return [Hash] the same IR
|
|
16
|
+
def self.mark(tree)
|
|
17
|
+
domain_wide = by_name(tree[:aggregates].flat_map { |aggregate| aggregate[:value_objects] })
|
|
18
|
+
tree[:aggregates].each do |aggregate|
|
|
19
|
+
mark_aggregate(aggregate, domain_wide.merge(by_name(aggregate[:value_objects])))
|
|
20
|
+
end
|
|
21
|
+
tree
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Resolves an `append` target to its entity or value object, or nil if it is neither. A
|
|
25
|
+
# local entity wins over a domain-wide value object of the same name: Syntax's local
|
|
26
|
+
# `Argument` entity shares a name with Command's `Argument` value object.
|
|
27
|
+
#
|
|
28
|
+
# @param aggregate [Hash] the aggregate owning the command
|
|
29
|
+
# @param target_type [String] the appended-to attribute's element type
|
|
30
|
+
# @param value_objects_by_name [Hash{String => Hash}] the value objects in scope
|
|
31
|
+
# @return [Hash, nil] the element, or nil
|
|
32
|
+
def self.element(aggregate, target_type, value_objects_by_name)
|
|
33
|
+
aggregate[:entities].find { |entity| entity[:name] == target_type } ||
|
|
34
|
+
value_objects_by_name[target_type]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @param aggregate [Hash] one aggregate of the IR
|
|
38
|
+
# @param value_objects_by_name [Hash{String => Hash}] the value objects in scope
|
|
39
|
+
# @return [void]
|
|
40
|
+
def self.mark_aggregate(aggregate, value_objects_by_name)
|
|
41
|
+
aggregate[:attributes].each do |target_attr|
|
|
42
|
+
target = element(aggregate, target_attr[:type], value_objects_by_name)
|
|
43
|
+
next unless target
|
|
44
|
+
|
|
45
|
+
aggregate[:commands].each do |command|
|
|
46
|
+
appends = command[:mutations].select { |m| append_to?(m, target_attr) }
|
|
47
|
+
appends.each { |mutation| mark_fields(mutation, command, target) }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.append_to?(mutation, target_attr)
|
|
53
|
+
mutation[:op].to_s == "append" && mutation[:target].to_s == target_attr[:name].to_s
|
|
54
|
+
end
|
|
55
|
+
private_class_method :append_to?
|
|
56
|
+
|
|
57
|
+
# A Symbol is an argument name, and an optional one is what this pass wants; anything
|
|
58
|
+
# else is a literal.
|
|
59
|
+
def self.mark_fields(mutation, command, target)
|
|
60
|
+
mutation[:fields].each do |field_name, source|
|
|
61
|
+
parsed = Hecks::Bluebook::Assembly::Marks.read(source)
|
|
62
|
+
next unless parsed.is_a?(Symbol)
|
|
63
|
+
|
|
64
|
+
source_attr = command[:attributes].find { |attr| attr[:name].to_s == parsed.to_s }
|
|
65
|
+
next unless source_attr && source_attr[:optional]
|
|
66
|
+
|
|
67
|
+
field_attr = target[:attributes].find { |attr| attr[:name].to_s == field_name.to_s }
|
|
68
|
+
field_attr[:optional] = true if field_attr
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
private_class_method :mark_fields
|
|
72
|
+
|
|
73
|
+
def self.by_name(value_objects)
|
|
74
|
+
value_objects.to_h { |value_object| [value_object[:name], value_object] }
|
|
75
|
+
end
|
|
76
|
+
private_class_method :by_name
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -6,6 +6,7 @@ require "tmpdir"
|
|
|
6
6
|
require_relative "../cache_dir"
|
|
7
7
|
require_relative "../rust_build"
|
|
8
8
|
require_relative "conformance"
|
|
9
|
+
require_relative "kernel_input"
|
|
9
10
|
|
|
10
11
|
module Hecks
|
|
11
12
|
module RustBuild
|
|
@@ -59,7 +60,7 @@ module Hecks
|
|
|
59
60
|
def replay(domain, artifact, seed, seeds, steps, scratch)
|
|
60
61
|
sequence = Hecks::Fuzzing::SequenceGenerator.generate(domain, seed: seed, steps: steps)
|
|
61
62
|
path = File.join(scratch, "seed-#{seed}.json")
|
|
62
|
-
File.write(path,
|
|
63
|
+
File.write(path, KernelInput.json(domain, sequence))
|
|
63
64
|
return if Conformance.call([domain, path, artifact]).zero?
|
|
64
65
|
|
|
65
66
|
raise Failure, "hecks fuzz_conformance: seed #{seed}/#{seeds} diverged against #{artifact} " \
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hecks/vocabulary"
|
|
4
|
+
require "hecks/bluebook/model_check"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module RustBuild
|
|
8
|
+
# Whether a domain's directory name can double as a Rust module identifier and a Cargo
|
|
9
|
+
# feature name. The reserved-word half is `ModelCheck.rust_reserved_name_findings`, the one
|
|
10
|
+
# shared check.
|
|
11
|
+
module DomainName
|
|
12
|
+
# Every key `rust/Cargo.toml` uses outside `[features]`, plus `default`, which Cargo
|
|
13
|
+
# reserves for the auto-enabled feature set. Declared once as the `CargoReservedName`
|
|
14
|
+
# vocabulary.
|
|
15
|
+
CARGO_RESERVED = Hecks::Vocabulary.fetch("CargoReservedName")
|
|
16
|
+
|
|
17
|
+
# @param name [String] the domain's directory name
|
|
18
|
+
# @return [Boolean] whether it is a plain lowercase identifier and no reserved word
|
|
19
|
+
def self.valid?(name)
|
|
20
|
+
text = name.to_s
|
|
21
|
+
text.match?(/\A[a-z_][a-z0-9_]*\z/) &&
|
|
22
|
+
Hecks::Bluebook::ModelCheck.rust_reserved_name_findings(domain_name: text).empty?
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "../rust_build"
|
|
5
|
+
|
|
6
|
+
module Hecks
|
|
7
|
+
module RustBuild
|
|
8
|
+
# The stdin a compiled domain binary reads: the steps to run, plus the declared argument
|
|
9
|
+
# defaults the host would hand its kernel.
|
|
10
|
+
#
|
|
11
|
+
# The kernel has no IR of its own, so a command's declared defaults reach it as a top-level
|
|
12
|
+
# `"defaults"` table, `{ "Domain::Aggregate.Command" => { "attribute" => value } }`, the same
|
|
13
|
+
# one the Rust host builds from the domain's IR. A harness that sends only `"steps"` would
|
|
14
|
+
# leave the kernel refusing an argument Ruby fills, so every conformance caller builds its
|
|
15
|
+
# input here.
|
|
16
|
+
module KernelInput
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# @param domain_path [String] the domain's directory; its basename names the generated module
|
|
20
|
+
# @param steps [Array<Hash>] the steps to run
|
|
21
|
+
# @return [Hash{String => Object}] the input; `"defaults"` is there only when one is declared
|
|
22
|
+
def build(domain_path, steps)
|
|
23
|
+
input = { "steps" => steps }
|
|
24
|
+
defaults = defaults_for(domain_path)
|
|
25
|
+
input["defaults"] = defaults unless defaults.empty?
|
|
26
|
+
input
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @param domain_path [String] the domain's directory
|
|
30
|
+
# @return [String] the input as JSON, ready for the binary's stdin
|
|
31
|
+
def json(domain_path, steps) = JSON.generate(build(domain_path, steps))
|
|
32
|
+
|
|
33
|
+
# Each command's declared defaults, read from the domain's generated IR.
|
|
34
|
+
#
|
|
35
|
+
# @param domain_path [String] the domain's directory
|
|
36
|
+
# @return [Hash{String => Hash{String => Object}}] empty when the IR is absent or has none
|
|
37
|
+
def defaults_for(domain_path)
|
|
38
|
+
ir = generated_ir(domain_path) or return {}
|
|
39
|
+
domain = ir["name"].to_s
|
|
40
|
+
Array(ir["aggregates"]).each_with_object({}) do |aggregate, table|
|
|
41
|
+
Array(aggregate["commands"]).each do |command|
|
|
42
|
+
held = Array(command["attributes"]).reject { |attribute| attribute["default"].nil? }
|
|
43
|
+
.to_h { |attribute| [attribute["name"], attribute["default"]] }
|
|
44
|
+
table["#{domain}::#{aggregate['name']}.#{command['name']}"] = held unless held.empty?
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @param domain_path [String] the domain's directory
|
|
50
|
+
# @return [Hash, nil] the parsed generated IR, or nil when the domain has none
|
|
51
|
+
def generated_ir(domain_path)
|
|
52
|
+
feature = File.basename(domain_path.to_s.chomp("/")).downcase
|
|
53
|
+
path = File.join(RustBuild.rust_dir, "src", "generated", feature, "ir.json")
|
|
54
|
+
File.file?(path) ? JSON.parse(File.read(path)) : nil
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|