hecks

Bin scripts become adapters on a Hecks bluebook

Status: Proposed. Date: 2026-09-28. Amends ADR 0066: the gem ships the development tooling too, and the dev_tooling filter in hecks.gemspec goes (section 3). Builds on ADR 0053 (the journal every command writes). Nothing below is built yet.

Context

bin/ holds 92 hand-written Ruby scripts. Each one parses its own arguments, performs its own checks, shells out to git, gh, cargo, RubyGems or the filesystem inline, and reports by printing and exiting. None of what they do reaches a journal, so a check that starts failing leaves no history, and the rules they enforce are invisible to model_check.

The pieces for doing this the domain way already exist:

Decision

Every bin script becomes a command or query on a bluebook, with its outside-world work in adapters behind ports. All 92 move in one pass.

1. A bin becomes adapters plus a generated launcher

2. One Hecks bluebook, in three parts

The framework gets its own domain, Hecks.bluebook "Hecks", in lib/hecks/hecks/. Following the language domain, it is spread over files that each open Hecks.bluebook "Hecks":

lib/hecks/hecks/
  hecks.bluebook        the root: vision, and the Hecks aggregate itself
  custodian.bluebook    Custodian: operating any domain
  codebase.bluebook     Codebase: working on this repository
  hecks.hecksagon       wiring for the whole domain
  adapters/             driven adapters; Codebase's under adapters/codebase/
  hecks                 the launcher project_cli generates
Part Files For Runs Holds
Root hecks.bluebook Everyone Anywhere The framework itself: its vision and version
Custodian custodian.bluebook Clients running their own domains Anywhere Looking at a domain, running it, building it for the Rust host, fuzzing it, caring for its stored data, vendoring bluebooks, entry points
Codebase codebase.bluebook, adapters/codebase/ Maintainers of Hecks In a hecks checkout; refuses elsewhere Evolving the language, Rust/Ruby conformance, comment style and codemods, test suite tooling, corpus-wide regeneration, releasing the gem

The split is by audience, not by packaging: all of it ships. The test for where a command goes is whether a client would run it against their own domain. If so it is Custodian’s, even when Hecks maintainers also use it; if it only makes sense on this repository, it is Codebase’s. Because project_cli names a launcher after its bluebook, the generated launcher is hecks, and exe/hecks becomes that launcher instead of a hand-written router. Verbs are the Hecks commands directly, for example hecks verify_engine_agreement or hecks era.merge_tail.

A script whose concern already has a bluebook joins that bluebook instead: the qa_* scripts go to QualityControl and the deploy scripts go to Deploy.

3. The gem ships all of it; nothing loads until asked

Everything in this ADR ships in the gem: the Hecks domain with Codebase and its adapters, every attached chapter including QualityControl, and the development tooling ADR 0066 kept out (fuzzing/, bench/, corpus.rb, codemod.rb, query_ir.rb, grammar/evolve.rb, doc/). The dev_tooling filter in hecks.gemspec goes, and spec/gemspec_packaging_spec.rb changes from asserting that those files are absent to asserting that lib/hecks.rb never loads them.

The gem also packages the Rust workspace, so a client can build their own domain for rust/host from an installed gem:

Keeping a deployment small does not depend on the package:

4. Hecks attaches the framework’s own chapters

The Hecks domain also takes in every chapter that describes the framework itself: the language declared in itself, Tenancy, Deploy and QualityControl. Each stays a chapter of its own: it keeps its name, its namespace (QualityControl::Patch, Deploy::Tenant, Bluebook::Aggregate), its store and its directory. The Hecks hecksagon attaches them the way uses_framework attaches Governance, and cross-chapter reactions go through translates.

Group Chapter Lives in For Runs
Language Bluebook lib/hecks/language/bluebook/ Both Anywhere
Language Paging (extends Bluebook through attaches_to, comes with it) lib/hecks/language/bluebook/attaches/ Both Anywhere
Language Hecksagon lib/hecks/language/hecksagon/ Both Anywhere
Language World lib/hecks/language/world/ Both Anywhere
Language Adapter lib/hecks/language/adapter.bluebook Both Anywhere
Language Port lib/hecks/language/port.bluebook Both Anywhere
Language Translation lib/hecks/language/translation/ Both Anywhere
Language Expression lib/hecks/grammar/expression.bluebook Both Anywhere
Runtime Tenancy lib/hecks/tenancy/bluebook/ Clients Anywhere
Operations Deploy lib/hecks/deploy/bluebook/ Clients Anywhere
Operations QualityControl lib/hecks/quality_control/, moved from qa/ Maintainers In a hecks checkout

The language chapters serve both audiences: clients’ domains run on them, and maintainers change them.

QualityControl’s chapter, hecksagon and adapters move from qa/bluebook/ and qa/adapters/ into lib/hecks/quality_control/ so they ship with the rest. What belongs to this repository’s QA practice stays in qa/: the .world file naming the ledger database, settings.yml, the stress domains and the specs. The QA ledger keeps its PostgresEra era and tables, because they are keyed by the chapter name, which does not change.

Two chapters are named Translation today; Hecks attaches only the language’s (section 6).

Some chapters stay out on purpose:

The one hecks launcher reaches every chapter’s verbs, for example hecks quality_control log_bug or hecks deploy lint. No chapter gets a launcher of its own.

# lib/hecks/hecks/hecks.hecksagon
Hecks.hecksagon "Hecks" do
  uses_framework "Governance"

  attaches "Bluebook", "Hecksagon", "World", "Adapter", "Port", "Translation", "Expression"
  attaches "Tenancy"
  attaches "Deploy", "QualityControl"
end

attaches stands for the attaching word; see Open items.

5. The hand-written floor, and the aggregates around it

The Bluebook chapter’s own vision names what stays hand-written: “the interpreter — evaluating a predicate held as data — and IO”. The floor is only that. The parser’s output, the IR and the meta-validator’s judgments are already modelled: “Loading a domain becomes dispatching commands into this meta-domain; the IR it stores must equal the IR the DSL builder produces.” They are the attached Bluebook chapter’s aggregates.

Around the floor, every part that has identity and changing state becomes an aggregate, so its changes are journaled like any other domain’s:

Aggregate Held by Identified by State and lifecycle Takes over
Release Root gem version tagged → published → verified; carries the IR version (Bluebook::Chapter::IR_VERSION) it shipped Hecks::VERSION as the record of what is running and what was shipped
SyntaxBootCache Root digest of the grammar chapters fresh → stale → rebuilt; Invalidate, Rebuild the on-disk cache under Hecks::CacheDir, whose invalidations are silent today
Host Custodian stack or URL Observe records the version and era a running rust/host reports; emits EraChanged check_era, which becomes a command on it
FrameworkMember Custodian member name the capabilities it provides Hecks::Framework.members and providers_of, read as records; Hecks catalogs members without attaching them
Kernel Codebase kernel version the capability tables and a coverage result per run project_kernel_capabilities, rust_kernel_coverage
Adapter Adapter chapter (exists) adapter name gains the optional port operations it implements and the guarantees it gives (section 10), which a domain today only learns from a warning at boot the .adapter declarations lib/hecks/adapters/driven/ already holds

The Hecks domain’s own adapters (InProcessBoot, JournalStore, RustToolchain and the rest in this ADR) get .adapter declarations too, so they appear beside the built-in ones.

Release moves to the root because every install has a version, and “what am I running” and “what did we ship” become one record. The steps that publish a release (tagging, pushing to RubyGems and npm) stay in Codebase as Publishing, and advance the root’s Release through its lifecycle.

What stays code is the interpreter and the IO inside each adapter.

The Hecks door wraps operations on the runtime, never the runtime’s own dispatch

Three rules keep the Hecks domain beside a client domain, not in front of it:

  1. A client’s commands never pass through the Hecks domain. A client domain’s generated launcher and rust/host call the runtime directly. A client’s command takes no extra hop, writes no second journal entry, and depends on neither the Hecks domain’s store nor its version.
  2. The Hecks domain reaches a target domain only through the DomainRuntime port. It boots and operates on another domain the way any adapter-driven caller would, and never through the runtime’s internals.
  3. Booting the Hecks domain depends on no Hecks command. It boots the way any domain boots, without exe/hecks or any of its own verbs. The committed exe/hecks stays the bootstrap, so regenerating the launcher never needs a working launcher.

6. Translation: attach the language’s chapter only

Two chapters are named Translation: the grammar’s register of rule kinds (lib/hecks/grammar/translation.bluebook) and the language’s meta-model of an edge file (lib/hecks/language/translation/). They are two halves of one concept, and ADR 0082 merges them.

3.0 does not wait for that. Hecks attaches only the language’s Translation chapter. The grammar chapter stays unattached, as it is today, since it only ever loads alone in Hecks::Grammar.grammar_chapters. The names never meet, so nothing is renamed.

7. Where each script goes

Every script becomes one or more commands or queries, named for the domain action (section 12). The launcher column shows the 3.0 form; the Hecks domain’s own verbs are bare, and an attached chapter’s verbs follow its name (hecks quality_control …, hecks deploy …). <x> is the positional identifying argument, --flag a boolean, name=value everything else. Destructive commands dry-run without --confirm.

Custodian, for clients

Script Aggregate · command Launcher
ir Introspection · IR (query) hecks ir [domain] [--translations] [--meta]
shape Introspection · Shape (query) hecks introspection.shape <domain>
stores Introspection · Stores (query) hecks stores <domain>
history Introspection · History (query) hecks introspection.history <domain>
statements Introspection · Statements (query) hecks introspection.statements <domain> chapter=Name
narrate Introspection · Narrative (query) hecks narrate [domain] [aggregate=Name]
docs Introspection · Document (query) hecks docs [domain] [aggregate=Name]
project_diagrams Introspection · Diagrams (query) hecks project_diagrams <domain> chapter=Name
project_glossary Introspection · Glossary (query) hecks introspection.glossary <domain> chapter=Name
model_check Introspection · ModelCheck hecks model_check [domains=a,b] [--strict] [profile=client]
run Operation · Run hecks run [domain] script=steps.json, or hecks run [domain] <verb> name=value …
project Operation · RefreshProjections hecks operation.refresh_projections <domain>
behaviors Operation · RunBehaviors hecks operation.run_behaviors <path>
console Operation · OpenConsole hecks console [domain]
follow Operation · Follow (query, streams) hecks operation.follow <domain> [aggregate=Name] [interval=0.5] [--from-now]
smoke_test Operation · SmokeTest hecks smoke_test [domain]
smoke_http Operation · SmokeHttp hecks operation.smoke_http path=/p secret=… [url=] [header=] [scheme=timestamped] [payload=] [payload_file=] [health_path=] [state_path=]
check_era Host · CheckEra hecks host.check_era <url> expected=era-file [timeout=10]
merge_tail Era · MergeTail hecks era.merge_tail <domain> winners=id:old,id:new --confirm
reattest_era Era · Reattest hecks era.reattest <domain> era=N --confirm
backfill_era_projections Era · BackfillProjections hecks era.backfill_projections <domain>
scaffold_translation Era · ScaffoldTranslation (query) hecks era.scaffold_translation <domain>
translation_audit Era · AuditTranslation (query) and Era · ApproveTranslation hecks era.audit_translation <domain>; hecks era.approve_translation <domain> --confirm
compact Era · Compact hecks era.compact <domain> [aggregates=A,B] --confirm
heki_compact Era · CompactHeki hecks era.compact_heki <domain> [aggregates=A,B] --confirm
(new) Era · HoldFirst hecks era.hold_first <domain> --confirm
vendor_bluebook Package · Vendor hecks package.vendor <package[@version]> [from=path] [root=path]
project_cli Door · ProjectCli hecks project_cli [domains=a,b]
hecks_mcp_door Door · ServeMcp hecks mcp (stdio)
project_rust Build · ProjectRust hecks build.project_rust <domain>
project_wasm Build · BuildWasm hecks build.build_wasm <domain>
project_wasm_browser Build · BuildBrowserWasm hecks build.build_browser_wasm <domain>
rust_coverage Build · RustCoverage (query) and Build · CheckCoverageAllowlist hecks build.rust_coverage <module> [codegen=ruby]; hecks build.check_coverage_allowlist
rust_conformance Build · CheckConformance hecks build.check_conformance <domain> script=steps.json [artifact=native]
rust_conformance_fuzz Build · FuzzConformance hecks build.fuzz_conformance <domain> artifact=native [seeds=10] [steps=25]
fuzz Fuzzing · Fuzz hecks fuzz_run.fuzz [domain] [seeds=20] [steps=30] [workers=] [adapter=memory]
generate Fuzzing · GenerateSequence (query) hecks fuzz_run.generate_sequence <domain> [seed=1] [steps=30] [adversarial=0.0]
bench Fuzzing · Bench hecks fuzz_run.bench [domains=pizzas,banking] [targets=] [iterations=1000] [warmup=200] [runs=3] [rust_binary=] [format=markdown] [output=]

Deploy, for clients (attached chapter)

Script Aggregate · command Launcher
project_deploy Deploy · Project hecks deploy project <domain> [tenant=] [schema=] [out=] [environment=]
lint_deploy_recipes Deploy · Lint hecks deploy lint [makefiles=a,b]
deploy_template_diff Deploy · Diff (query) hecks deploy diff before=a.yaml after=b.yaml [--json] [--strict]
project_oidc Deploy · ProjectOidc hecks deploy project_oidc [domains=a,b]
project_tenant Tenant · Provision hecks deploy provision <domain_dir> slug=s domain= realm= schema= database= [adapter=PostgresEra]

Codebase, for maintaining Hecks

Script Aggregate · command Launcher
project_model Language · ProjectModel hecks language_run.project_model
project_vocabulary Language · ProjectVocabulary hecks language_run.project_vocabulary
project_rust_vocabulary Language · ProjectRustVocabulary hecks language_run.project_rust_vocabulary
project_refusal_wording the same command, a second name hecks language_run.project_refusal_wording
project_reserved_names Language · ProjectReservedNames hecks language_run.project_reserved_names
project_parser_table Language · ProjectParserTable hecks language_run.project_parser_table
project_bootstrap_table Language · ProjectBootstrapTable hecks language_run.project_bootstrap_table
project_field_hints Language · ProjectFieldHints hecks language_run.project_field_hints
expression_projection Language · ProjectExpressionTables hecks language_run.project_expression_tables [--stdout]
reference Language · ProjectReference hecks language_run.project_reference
evolve Language · WordStatus (query); Propose, Admit, Deprecate, Retire, Rename; ProposeArgument, AdmitArgument, DeprecateArgument, RetireArgument hecks language_run.word_status; hecks language_run.propose <word> context=X [body=none] [inner=] [opens=] [fills=]; hecks language_run.rename <word> context=X to=Y; hecks language_run.propose_argument <word> context=X kind=K [required=false] [at=N] [named=] [pairs_shape=]; the rest take <word> context=X
project_kernel_capabilities Kernel · ProjectCapabilities hecks kernel_run.project_kernel_capabilities
rust_kernel_coverage Kernel · MeasureCoverage hecks kernel_run.measure_kernel_coverage
check_engine_agreement Conformance · CheckEngineAgreement hecks conformance_run.check_engine_agreement
doc_coverage Conformance · MeasureDocCoverage hecks conformance_run.measure_doc_coverage
argument_gate_matrix Conformance · ArgumentGateMatrix hecks conformance_run.argument_gate_matrix [--confirm] (writes only with --confirm)
regen_codegen_domains Regeneration · RegenerateCorpus hecks regeneration_run.regenerate_corpus [--check]
standardize_comments Style · ReportComments (query), CheckComments, FixComments, WriteCommentBaseline, CheckCommentsUnchanged hecks style_run.report_comments paths=a,b [only=] [--json] [top=20]; hecks style_run.check_comments paths=…; hecks style_run.fix_comments paths=… --confirm; hecks style_run.write_comment_baseline --confirm; hecks style_run.check_comments_unchanged ref=REF
standardize_comments_rust Style · ReportRustComments (query), CheckRustComments, FixRustComments hecks style_run.report_rust_comments paths=…; hecks style_run.check_rust_comments paths=…; hecks style_run.fix_rust_comments paths=… --confirm
canonicalise Style · Canonicalise hecks style_run.canonicalise <file.json>
codemod_hoist_local_givens Codemod · HoistLocalGivens hecks codemod_run.hoist_local_givens --confirm
codemod_implicit_append_fields Codemod · DropImplicitAppendFields hecks codemod_run.drop_implicit_append_fields --confirm
rspec_shard_files TestSuite · ShardSpecs (query) hecks test_suite_run.shard_specs group=1 groups=N [runtime_log=]
rspec_io_parallel_files TestSuite · ListIoParallelSpecs (query) hecks test_suite_run.list_io_parallel_specs exclude=REGEX [tags=] [check=file] [write=file]
refresh_rspec_runtime_baseline TestSuite · RefreshRuntimeBaseline hecks test_suite_run.refresh_runtime_baseline [workers=6] [from_run=ID]
spec_example TestSuite · RunSpecExample hecks test_suite_run.run_spec_example file=path example=text
stress_concurrency_specs TestSuite · StressConcurrency hecks test_suite_run.stress_concurrency [runs=30] [parallel=] [seed_start=1]
regenerate_persistence_legacy_fixtures TestSuite · RegenerateLegacyFixtures hecks test_suite_run.regenerate_legacy_fixtures --confirm
seed_semantics_corpus TestSuite · SeedSemanticsCorpus hecks test_suite_run.seed_semantics_corpus
pattern-cases TestSuite · RecordPatternCases hecks test_suite_run.record_pattern_cases
corpus Corpus · RustDomains, RegenOrder, RustCoverage (queries) hecks corpus_run.rust_domains; hecks corpus_run.regen_order; hecks corpus_run.corpus_rust_coverage
query_ir Corpus · IrConstructs, IrDuplicates, IrImpact (queries) hecks corpus_run.ir_constructs [names=a,b]; hecks corpus_run.ir_duplicates [domains=a,b] [--meta]; hecks corpus_run.ir_impact name=N field=F
hecks_query_ir_mcp Corpus · ServeQueryIrMcp hecks corpus_run.serve_query_ir_mcp (stdio)
present Corpus · Present hecks corpus_run.present [port=4567]
release Publishing · Publish hecks publishing_run.publish [--gem-only] [--npm-only] [--npm-local] [--no-wait] --confirm (without --confirm, the old --dry-run)
release_gem Publishing · PublishGem hecks publishing_run.publish_gem --confirm

QualityControl, for maintainers (attached chapter)

Script Aggregate · command Launcher
qa_tick Sweep · Tick hecks quality_control tick
qa_sweep Sweep · Run, and Target · Release hecks quality_control sweep [target] [--all] [seeds=] [steps=] [adversarial=] [role_draw=] [dry_run_share=] [self_consistency=] [modes=a,b] [--persistence-parity] [--no-parity]; hecks quality_control release_target <target> notes=text
qa_pr_check Clearance · CheckPullRequests hecks quality_control check_pull_requests
qa_open_pr Patch · Open, and Improvement · Open hecks quality_control open_patch bug=BUG#n title=… [body=]; hecks quality_control open_improvement angle=ANGLE-n title=… [body=]
qa_log_bug Bug · Log hecks quality_control log_bug sweep= title= demonstration= symptom= expectation= submitter= triage=self_contained [tags=a,b] [reproduced=yes]
qa_seed_angles Angle · Seed hecks quality_control seed_angles
qa_seed_targets Target · Seed hecks quality_control seed_targets
qa_generated_domains Target · CheckGeneratedDomains hecks quality_control check_generated_domains [domains=3] [start=] [forms=a,b] [seeds=5] [steps=25] [adversarial=0.3] [--rust] [shrink_budget=200] [domain_shrink_budget=40] [promote=dir name=N] [--from-dials] [blueprint=] [sources=a,b]
qa_mine_combinations Target · MineCombinations hecks quality_control mine_combinations [candidates=3] [--rust] [seeds=5] [steps=25] [adversarial=0.3] [repair_rounds=1] [agent=cmd] [from=dir] [against=a,b] [--brief]
qa_domain_novelty Target · JudgeNovelty (query) hecks quality_control judge_novelty <domain> against=a,b
qa_discover_external_domains Target · DiscoverExternalDomains (query) hecks quality_control discover_external_domains [projects_dir=~/Projects] [max_depth=3] [known_paths=a,b]
qa_postgres_migrate Sweep · MigrateLedgerFromHeki hecks quality_control migrate_ledger domain_dir= heki_dir= [aggregates=A,B] --confirm
qa_postgres_role Sweep · CreateLedgerRole hecks quality_control create_ledger_role <database> [role=hecks_qa]
qa_concurrency_racer Sweep · Race (run only as a child process) not user-facing; ProcessPool starts it with domain= database= schema= verb= args=

Internal flags that exist only for one tool to call itself (qa_generated_domains --check/--binary/--match) become a child command the ProcessPool adapter starts, not launcher arguments. Where a script’s usage header disagreed with its code, the table follows the code.

Publishing goes to Codebase rather than Custodian because it publishes this repository’s gem, so it only runs in a checkout. The Release it advances lives in the root (section 5).

Building for the Rust host is split by the same test. Custodian’s Build generates and compiles one domain, the way a client deploying to rust/host does, and checks that build against the Ruby engine: rust_conformance replays a script through both, rust_conformance_fuzz does the same with generated sequences, and rust_coverage reports which of the domain’s constructs are routed. Codebase’s Regeneration rebuilds every corpus domain’s committed output, which only this repository has, and Codebase’s Conformance keeps the checks on the language itself (the kernel, the query engines, the reference docs).

project_refusal_wording is an alias for project_rust_vocabulary; it becomes a second name for the same Language command, not a command of its own.

8. QualityControl

The rules hand-coded in qa_open_pr and qa_pr_check move onto Patch.Open and Improvement.Open. Three are givens today: the branch prefix (a pattern: on the branch), the bug being fixed and the angle being under investigation (givens that read through the reference, as customer.status == "active" does in the banking example). The other two follow ADR 0081, and stay in the GitPr adapter until it lands in a 3.x minor after 3.0:

9. This ships as 3.0.0

Under ADR 0068 a major version means a breaking DSL or runtime change with a CHANGELOG Breaking: entry, and this ADR makes several:

ADR 0068’s rule 4 gives a break that reaches an installed client site one release of warning where a warning is possible. The two parts that reach client sites get one:

Inside this repository the move is one pull request, so there is never a second way for a maintainer to run a tool. It is built as ordered commits, each keeping the whole suite green, so it can be reviewed and bisected one step at a time:

  1. The runtime-boundary guard spec.
  2. The launcher changes (section 12) and routing to attached chapters.
  3. The Hecks root, with the DomainRuntime and Workspace adapters.
  4. Custodian’s aggregates, one commit each.
  5. Codebase’s aggregates, one commit each.
  6. Attaching the language chapters, Tenancy and Deploy.
  7. Moving QualityControl into lib/hecks/quality_control/.
  8. Packaging the Rust workspace, and the gemspec.
  9. The generated exe/hecks.
  10. CI workflows, hooks and docs, pointed at the new commands.
  11. Deleting bin/.

Client pins move to 3.0.0 explicitly, because deploys pin exactly (ADR 0068, rule 3). 3.0.0 is the first release the root’s Release aggregate records.

10. Details the layout settles

11. The adapters, from a scan of the code

This list comes from a static scan, not an estimate. The scan starts from each of the 92 scripts and follows require/require_relative into lib/ and qa/, recording every subprocess, network, database, file, environment, clock, randomness, terminal and process-control call with its file and line. It then resolves by hand every call whose command is built at runtime, and every subprocess helper (git(...), gh(...), Release::Runner::Commands, Vendoring::GitSource). The core runtime that require "hecks" loads is scanned once, separately, since every script shares it.

Driven adapters the scripts need:

Port Adapter wraps Scripts that reach it
Git git qa_open_pr, qa_tick, qa_mine_combinations, regen_codegen_domains, standardize_comments, bench (commit id), release (preflight, tagging), vendor_bluebook (git archive, through Vendoring::GitSource)
GitHub gh qa_open_pr, qa_pr_check, refresh_rspec_runtime_baseline
GemRegistry gem build, gem push, the RubyGems versions API over curl release, release_gem
NpmRegistry npm ci, npm publish, npm view release
SecretVault 1Password op run release, release_gem
RustToolchain cargo, rustup, wasm-bindgen, wasmtime, and the binaries they build (hecks-build, the parser, each domain’s binary) project_rust, project_wasm, project_wasm_browser, rust_coverage, rust_conformance, rust_conformance_fuzz, fuzz, bench, qa_generated_domains
RubyChild bundle exec ruby / bundle exec rspec, ruby corpus, evolve, lint_deploy_recipes, rust_coverage, rspec_io_parallel_files, stress_concurrency_specs, refresh_rspec_runtime_baseline, qa_concurrency_racer, qa_discover_external_domains, qa_domain_novelty, qa_generated_domains, qa_mine_combinations, qa_sweep, qa_tick
TestRunner RSpec::Core::Runner in process spec_example
Agent claude -p (QA_MINER_AGENT overrides) qa_mine_combinations
Shell sh -c, rsync, tar qa_log_bug (a bug’s demonstration), qa_generated_domains, vendor_bluebook
PgAdmin PG.connect outside the persistence adapters: roles, scratch databases qa_postgres_role, fuzz, bench, qa_sweep, regenerate_persistence_legacy_fixtures
SqliteFixture sqlite3 and SQLite3::Database regenerate_persistence_legacy_fixtures
HostHttp Net::HTTP check_era, smoke_http
ProcessPool fork, Process.wait, signal traps fuzz, follow, qa_mine_combinations, qa_sweep, qa_tick, regen_codegen_domains
Terminal IRB, stdin, y/N prompts console, run, release, standardize_comments, bench
Workspace file writes (52 scripts) and reads (68) most scripts; SourceTree is the part that rewrites tracked source
clock (exists) Time.now, sleep fuzz, bench, release, smoke_http, qa_open_pr, qa_sweep, qa_tick, qa_seed_angles, qa_generated_domains, qa_mine_combinations

Environment variables are read by 25 scripts; they become world configuration, not an adapter. The fuzzing generators’ Random is seeded and deterministic, so it is logic, not IO.

RubyChild mostly disappears. Most bundle exec ruby bin/<name> calls are one hecks tool running another. Once both are commands on the same domain, the call becomes a dispatch. It stays a subprocess only where isolation is the point: the concurrency racer, the stress specs, and checking a generated domain in a clean process.

What the scan rules out:

Already adapters in the runtime (reached through Hecks.boot, used by Custodian’s DomainRuntime and JournalStore):

Ports::Persistence::PostgresDump runs pg_dump and pg_restore, but no script reaches it.

Driving adapters:

12. Commands and arguments

13. Era operations in production: decide in the repository, apply at deploy

Today the host mints eras and checks approvals itself, at every boot (ADR 0030). The Ruby era tools reach a production database only from a laptop, through a temporary bastion and an SSM tunnel that make mint-era sets up. For a domain on a shared database, not even that is generated. merge_tail, compact, heki_compact, reattest_era and backfill_era_projections have no production path at all, although the host’s own tamper refusal tells an operator to run reattest_era. A shared-database domain also cannot approve an edge in production, so an edge with compute or rekey rules would stop it booting.

Production follows the database-migrations model, the direction ADR 0030 already took for minting. Nothing reaches into a production database from outside.

What lands when:

Consequences

Alternatives considered

Open items

Implementation notes (3.0 build)

Where the build settled a detail this ADR left open or spelled differently. The design above is unchanged; the code is on hecks-3-0, under lib/hecks/hecks/.