neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗hecks is an executable domain specification language. A
.bluebook file declares a business domain — its aggregates,
rules, and events — and that declaration is the running system, not a
spec that code is later written from:
given("at most 10 toppings") { toppings.size < 10 }
sets :toppings, append: { name: :topping, amount: :amount }
emits ToppingAdded
given refuses a command before it runs; sets is the only way state
changes; emits is the only way anything downstream finds out. There
is no handler body behind those three lines — the runtime dispatches
directly from the declaration. A domain is data, so it can be read,
diffed, statically checked, run against generated fuzz sequences, and
compiled into another language, the same way any other data can.
The project uses its own words. Six of them are the whole model; read Concepts first, then the Glossary for the rest.
Status: Current release: 6.0.0. See Project status
for what the stability promise made at 1.0.0 covers and what it explicitly
doesn't yet.
These terms make up the model.
.bluebook
file is a program written in it. It never names a backend. A bluebook is all
of the files that declare one name (Pizzas), the unit a domain is built
from; a hecksagon attaches framework bluebooks such as Governance by name.driven_by), and its ports. It lives in a .hecksagon file..world file. Like an .env file it holds only
values, but each one is checked against the adapter it answers.Order.create_pizza!), the CLI, JSON, or MCP for an agent. Like a
controller layer it is the way in, but boot installs it from the
declaration; nobody writes it.hecks era.audit_translation replays
against real records before boot, and boot refuses a shape change that has
none. Eras are tracked by the PostgresEra adapter (and the Rust host's Postgres store).You write a domain in a bluebook, wire it with a hecksagon, and give a world
the deployment's values. Hecks.boot reads the three, runs the domain, and
installs its Ruby driving adapter.
command "AddTopping", from: "available" do
role "Chef"
goal "Customize a pizza with an ingredient"
reference_to Order
attribute :topping, ToppingName
attribute :amount, ToppingAmount
given("at most 10 toppings") { toppings.size < 10 }
sets :toppings, append: { name: :topping, amount: :amount }
emits ToppingAdded
end
Heki, PostgresEra, Corpus, Storehouse and Embryonaut bluebook are adapters or maintainer tooling; the Glossary has them.
gem install hecks
Or in a Gemfile: gem "hecks".
The gem installs a hecks command for a domain you supply: init
(writes the stub of a new one), interview (drafts one from a conversation
with someone who knows the business), run, docs, narrate, ir, stores, model_check, smoke_test,
project_diagrams, project_cli, and mcp (the MCP server, over stdio
only). hecks lists them and hecks <command> --help prints one's
usage. In a clone of this repository, the same launcher also answers the
maintainer commands (hecks publishing_run.publish, hecks regeneration_run.regenerate_corpus,
hecks conformance_run.measure_doc_coverage, and the rest of the Codebase bluebook).
The gem carries the pizzas example the quickstart uses, so once it is installed
hecks console opens it. A clone adds the rest of the repository: the guides, the
other examples and the sources.
About ten minutes, and no database server. You need Ruby 3.2 or newer. If
installing fails building the pg gem, install Postgres's client library
(libpq) and run it again; nothing here connects to a database.
gem install hecks
hecks console
Or from a clone, which is how you work on hecks itself:
git clone https://github.com/heckslabs/hecks
cd hecks
bundle install
bundle exec hecks console
console boots the examples/pizzas domain on the in-memory adapter
and drops you into IRB with its Ruby driving adapter installed. Nothing needs a
database, and git status stays clean. Type this at the prompt (exit
leaves it):
order = Order.create_pizza!(name: "Margherita", pizza: { price_cents: 1200, size: "large" })
order.purchase!(customer_name: "Chris", amount: 1200) # ~> GivenNotMet: a pizza needs at least one topping
order.add_topping!(topping: "Basil", amount: 3)
order.purchase!(customer_name: "Chris", amount: 1200)
order.status # => "sold"
order.events.last.name # => "PizzaPurchased"
A value object with a single attribute takes a bare scalar (name: "Margherita" wraps into its one field); one with several fields, like
pizza:, takes an object. The first purchase! is refused by a given
declared on the command, before anything changes.
That block runs on every push, claims and all — not an illustration.
So does every other ruby-fenced example in this README and in
the guides that is not tagged skip or
excerpt; spec/guides_spec.rb is the harness.
Next, Getting started walks
through the pizzas bluebook you just dispatched against, and
Your own domain has you write
a bluebook of your own (starting from hecks init), run it, and deploy it
to AWS Lambda. The
Glossary at the end of this page defines the project's own
words. bundle exec hecks console subject=<domain> boots any other domain
directory as that directory is wired.
Take one real rule — "a pizza may carry at most 10 toppings." In a conventional application that rule tends to end up in several places at once: a check in the request handler, maybe a mirror of it in a client-side form, an ORM validation or a database constraint, a line in a test fixture asserting the boundary. Each copy is correct on its own. None of them is the rule — the rule is whatever the union of all four happens to enforce on a given day, and it drifts the moment one of them is touched without the others.
In hecks that rule is written once, on the command that can violate it:
given("at most 10 toppings") { toppings.size < 10 }
There is exactly one place this can be checked, because there is
exactly one path a command can take to reach state — every dispatch
walks the same fixed order (refuse unknown/missing arguments, check
role, enforce every given, apply the mutation, enforce every
ensures, persist, emit), for every command, in every domain. A rule
that is checked once, in the one place a violation can occur, cannot
quietly stop being checked somewhere.
What this does not give you is a check that the rule is the right one. If the business meant 12 toppings and the bluebook says 10, hecks enforces 10 identically in every runtime. Verification here shows the specification agrees with itself and that the runtimes agree with each other; whether the specification says what the business wants is still a human judgment.
Generalize that from one rule to a whole domain and the shape of the bet becomes: the business specification should be the durable artifact, and the implementation running it should be the disposable one. A traditional stack reads roughly as
business requirement → developer/AI interpretation → application code → framework/runtime
— four lossy translations between the rule and the thing enforcing it, each one a place the two can diverge. hecks collapses the middle two:
business domain → bluebook (explicit, constrained specification) → validated semantics → runtime/adapter
The specification is what a reviewer reads to know what the business actually requires; it is also, unmodified, what runs. Swapping the runtime underneath it — a different persistence adapter, a different dispatch language entirely — does not touch the specification at all (see Projections).
This is not a hypothetical concern about hecks's own corpus. ADR
0025
in this repository's own decision log measured it directly: two
preconditions — "the customer is active" and "the customer is not
closed" — had been independently typed out, worded slightly
differently each time, across a large share of the given clauses in
the corpus, because nothing made the duplication visible until someone
counted. The fix wasn't a linter; it was a language feature
(given("customer is active"), declared once and referenced by name
elsewhere) that makes the duplication itself impossible to write by
accident.
Generating code has become cheap. Reviewing an ever-growing, arbitrary codebase for architectural drift, duplicated business rules, and quietly-diverging invariants has not gotten any cheaper, and an AI agent editing that codebase inherits the same problem a human maintainer has: it has to hold thousands of implementation details in mind to avoid breaking one while fixing another, and nothing stops it from re-deriving "at most 10 toppings" a fifth way in a fifth file.
hecks's bet is narrower and more mechanical than "AI will manage
complexity for you": constrain what gets modified — by a human or an
agent — to a small, closed, checked vocabulary (aggregate, command,
given, sets, emits, and the rest of a closed vocabulary), and let the
compiler and runtime carry the burden of architectural consistency that
would otherwise depend on whoever (or whatever) is editing the code
noticing it. A bluebook that violates an invariant refuses to boot or
refuses to dispatch, deterministically, regardless of whether a person
or a model wrote the line. See AI-native
development for what
that looks like as a concrete integration today, not just an argument.
A domain is three files, each with one job. .bluebook declares what
the domain is — aggregates, commands, rules — independent of how any
deployment runs it. .hecksagon wires it to real ports: which adapter
persists it, which framework it attaches to. .world holds the one
thing neither of those names — per-deployment values, like a database
URL. Wiring covers the last two in
full; here is the first, in full — examples/pizzas/bluebook/pizzas.bluebook,
trimmed to fit:
aggregate "Order" do
identified_by :name
attribute :name, PizzaName
attribute :toppings, list_of(Topping)
value_object "PizzaName" do
attribute :value, String
invariant("a pizza is named") { !value.to_s.empty? }
end
value_object "Topping" do
attribute :name, String
attribute :amount, Integer
end
lifecycle :status, default: "available" do
transition "Purchase" => "sold", from: "available"
end
command "CreatePizza" do
role "Chef"
attribute :name, PizzaName
attribute :pizza, Pizza
emits PizzaCreated
end
command "AddTopping" do
role "Chef"
reference_to Order
attribute :topping, ToppingName
attribute :amount, ToppingAmount
given("a sold pizza cannot be changed") { status == "available" }
given("at most 10 toppings") { toppings.size < 10 }
sets :toppings, append: { name: :topping, amount: :amount }
emits ToppingAdded
end
end
An aggregate is the thing with identity — two orders named
"Margherita" are the same order, which is exactly what
identified_by :name declares. A value object has none; a PizzaName
is only its value, and its invariant travels with the value everywhere
it goes. The lifecycle names the states an order may hold and the
transitions between them. A command says three things and no more:
what it needs, what it refuses (given), what it announces (emits).
Nothing here is hand-drawn either — the same declaration draws its own diagrams:
hecks project_diagrams reads a booted domain's own declaration and draws it as Mermaid — <Name>_lifecycle.mmd, relationships.mmd, dispatch.mmd, roles.mmd, ports.mmd, read_models.mmd, <Name>_surface.mmd (what a command does, and what it writes), <Name>_saga.mmd, and frameworks.mmd. Nothing hand-drawn — the same reason a domain is data at all. Order's own lifecycle, straight off the bluebook above:
%% GENERATED by hecks project_diagrams from Order's own declared lifecycle (field: status) — DO NOT EDIT BY HAND.
%% Re-run `hecks project_diagrams <domain-path> Pizzas` after any change.
stateDiagram-v2
[*] --> available
available --> sold: Purchase
The full set for every domain in this checkout — examples/pizzas, examples/banking — lives in docs/generated/diagrams/, held to the declaration by spec/diagrams_spec.rb the same drift-refusing way this page is held to its own source.
The expression inside given/ensures/invariant is Ruby, parsed by
Prism and reduced to canonical text stored alongside the rest of the
IR — the same reason the domain as a whole is data rather than code —
and its grammar is small and closed for the same reason: an operator
is admitted only once it earns its place rendering a real rule already
in the corpus. Full grammar and dispatch order in Running a
runtime.
Pizzas is deliberately small. This is examples/banking — trimmed
here to one aggregate, the untrimmed original is
examples/banking/bluebook/:
Hecks.bluebook "Banking" do
vision "Customers hold accounts, accounts move money, and every movement is a transfer that can fail halfway. The domain that has to get it right twice — once in the rules, once in the recovery."
core
aggregate "Account" do
identified_by :number
attribute :number, AccountNumber
attribute :balance, Money
value_object "AccountNumber" do
attribute :value, String
invariant("an account number is present") { !value.to_s.empty? }
end
value_object "Money" do
attribute :cents, Integer, default: 0
attribute :currency, String, default: "USD"
invariant("a currency is a three-letter code") { currency.to_s.size == 3 }
end
value_object "PositiveMoney" do
attribute :cents, Integer
attribute :currency, String, default: "USD"
invariant("an amount is positive") { cents.positive? }
invariant("a currency is a three-letter code") { currency.to_s.size == 3 }
end
lifecycle :status, default: "open" do
transition "FreezeAccount" => "frozen", from: "open"
end
command "Open" do
role "Branch clerk"
attribute :number, AccountNumber
sets :number
emits "AccountOpened"
end
command "Credit" do
role "Teller"
reference_to Account
attribute :amount, PositiveMoney
given("the account is open") { status == "open" }
sets :balance, increment: :amount
emits "AccountCredited"
end
end
end
Hecks.hecksagon("Banking") do
attaches "Governance"
Banking::Account.persisted_by("Memory")
end
Hecks.hecksagon("Governance") do
Governance::RoleAssignment.persisted_by("Memory")
Governance::RoleTransition.persisted_by("Memory")
end
An aggregate, a lifecycle, an invariant, a command, an event — executed:
account = Account.open!(number: "1001")
account.credit!(amount: { cents: 500, currency: "USD" })
account.balance.to_h # => { cents: 500, currency: "USD" }
account.credit!(amount: { cents: -1, currency: "USD" }) # ~> InvariantViolation: an amount is positive
The real Account — the one in examples/banking/bluebook/, not the
trimmed one above — is where the "one rule, checked once" argument from
Why stops being a toy example. Several different commands can move
its balance; "the balance never goes negative" used to be three
different given/ensures clauses, worded differently, with two
commands that could only increase the balance saying nothing at all
— correctness depended on someone noticing which commands could
decrease it. It is now one aggregate-level invariant, checked after
every one of those commands, stated once:
# examples/banking/bluebook/deposit_accounts.bluebook — the real Account
invariant("the balance never goes negative") { balance.cents >= 0 }
command "Debit", from: "open" do
role "Teller"
reference_to Account
attribute :amount, PositiveMoney
given("customer is active")
given("the balance covers it") { balance.cents >= amount.cents }
given("the daily limit allows it") { daily_limit.cents >= amount.cents }
sets :balance, decrement: :amount
sets :ledger, append: { amount: :amount, narrative: :narrative, direction: { value: "debit" } }
# "no debit leaves the balance negative" — GONE, not reworded: the
# aggregate's own invariant above says it now.
ensures("the balance fell by exactly the amount") { old.balance.cents == balance.cents + amount.cents }
emits AccountDebited
end
Account's own lifecycle, drawn from that same real declaration —
another aggregate, another set of states, nothing hand-drawn here either:
%% GENERATED by hecks project_diagrams from Account's own declared lifecycle (field: status) — DO NOT EDIT BY HAND.
%% Re-run `hecks project_diagrams <domain-path> Banking` after any change.
stateDiagram-v2
[*] --> open
open --> frozen: FreezeAccount
frozen --> open: Unfreeze
open --> closed: CloseAccount
frozen --> closed: CloseAccount
To drive the full domain by hand, bundle exec hecks console subject=examples/banking boots it
as wired. Banking is bound to Heki, which keeps its records in
the git-tracked examples/banking/data/, so a dispatch there shows up in
git status; git checkout -- examples/banking/data and
git clean -f examples/banking/data put the clone back.
customer = Customer.register!(reference: "CUST-1001", name: { given: "Chris", family: "Young" }, email: "[email protected]")
account = Account.open!(customer: "CUST-1001", number: "1001", kind: "current", daily_limit: 50_000)
account.credit!(amount: { cents: 500, currency: "USD" }, narrative: "Opening deposit")
account.balance.to_h # => { cents: 500, currency: "USD" }
account.status # => "open"
To run a scripted step list instead of a REPL, writing to the same files:
bundle exec hecks run examples/banking spec/corpus/banking.json
Only what this repository actually does today, checked, not aspired to:
sets, increment/decrement,
append); every refusal is a named given or invariant. There is no
code path that mutates state outside sets.hecks model_check runs structural analysis
over a domain's own IR — unreachable lifecycle states, transitions
nothing can fire, saga states no handler chain reaches — before
anything boots against real data.hecks fuzz_run.fuzz
generates random-but-valid command/query sequences from a domain's
own IR and checks a battery of properties — declared lifecycle values,
saga advances that follow declared handlers, query answers that match
a reference implementation, role checks that agree with grants,
aggregations that match a recompute, and more (the list is
PROPERTY_NAMES in lib/hecks/fuzzing/properties/catalog.rb) — and,
the one that actually matters for an event-sourced system, replaying the same
steps against a fresh boot produces byte-identical history. This
runs against the Memory adapter by default, can steer seeds by real
runtime line coverage (runtime_coverage_feedback, off by default), and against real Sqlite
and Postgres with hecks fuzz_run.fuzz adapter=sqlite (or adapter=postgres, adapter=postgres_era) (see
Project status).spec/corpus/*.json
scripts real command/query sequences — successes and refusals both —
replayed by hecks run and pinned by spec/corpus_spec.rb; a runtime
that accepts what the corpus says must be refused is the failure
that matters more than one that rejects a valid command.emits is the only way anything
outside a command's own aggregate learns what happened; every emitted
event is durably recorded, not just returned to the caller.persisted_by in a .hecksagon
file is the entire migration between an in-memory adapter and a real
database — the .bluebook file never names a backend, so it never
changes. Every persistence adapter under lib/hecks/adapters/driven/
(Memory, , , (adds
schema-evolution tracking — see ),
(an append-only journal, no server), and others) satisfies the same
persistence port.The same separation extends past persistence, to dispatch itself: Projections: Rust and WebAssembly covers the generated Rust runtime, its WASM build, and how both are held byte-for-byte to Ruby in CI. AI-native development covers the storehouse bus and the MCP server a coding agent works through.
Current release: 6.0.0. docs/1.0-readiness.md
states plainly what the stability promise made at 1.0.0 covers — the DSL
and runtime API in the DSL reference
won't change in a breaking way without a major-version bump — and what it
explicitly doesn't cover yet. Project
status lists what works today,
exercised in CI on every push, and what is experimental or partial.
Planned or research only — nothing below is built:
Project status is the current
list of what is experimental or partial.
docs/future-features.md is a snapshot of what was
unbuilt on 2026-08-18; items may have shipped since, so check a capability
against the code before assuming it is missing or present.
The DSL reference — generated from the aggregate-local tables under lib/hecks/language/ and held to them by spec/reference_golden_spec.rb.
The architecture map, the command table, the resolution rules, the decision log, the changelog and the design documents are listed under Beyond the guides.
The example domains this README draws from, plus one that consumes a vendored embryonaut bluebook:
attaches ... from: :vendor, exercising the same dispatch-table merge a gem attaches already proves for Governance/Identity in examples/banking — see docs/decisions/0058 for what this domain exists to prove and what it deliberately does not.The project's own words, in the order a newcomer usually meets them.
The language a domain is declared in, the way SQL is the language queries
are declared in. A .bluebook file is a program in it: one domain's
aggregates, value objects, commands, rules, events, queries and policies
(Hecks.bluebook "Pizzas" do … end). It never names a backend. A bluebook is
all of the files that declare one name, and the module the Ruby driving adapter
installs for it (Pizzas). The language's framework bluebooks (Governance,
Identity, Privacy and others) live in lib/hecks/framework/bluebook/ and
its grammar bluebooks under lib/hecks/language/ and lib/hecks/grammar/
(hecks lists them); a hecksagon attaches a framework bluebook with attaches.
A .hecksagon file: the wiring for a bluebook — which adapter persists
each aggregate (Pizzas::Order.persisted_by("Memory")), which framework
bluebooks it attaches (attaches "Governance"), and its ports. See
Wiring.
A .world file: per-deployment values neither the bluebook nor the
hecksagon names, such as a database URL (examples/pizzas/bluebook/pizzas.world).
An adapter that reaches in to a domain from outside. The Ruby one is the surface a boot installs: one top-level module per booted
bluebook and one per aggregate, so Order.create_pizza!(…) dispatches the
CreatePizza command. Each boot re-installs it. The MCP server
hecks mcp is a driving adapter of the same kind, for an agent over stdio. The CLI, JSON and webhook adapters are the others.
A persistence adapter with no server: an append-only journal file per
aggregate (data/*.heki, *.heki.journal). examples/banking is bound
to it.
The Postgres persistence adapter that also tracks schema evolution. It
holds the source text each era of a domain was born from, refuses
to boot on drift between that text and the booting text, and requires a
translations/*.bluebook file before a shape change reinterprets old
rows. It is a plugin loaded with
require "hecks/ports/persistence/plugins/era", and it needs a reachable
Postgres. See Schema evolution.
One version of a domain's shape as PostgresEra holds it, numbered in
order. A declared translation carries existing records from one era to
the next.
Every real domain in this repository that the tooling walks: the example
domains under examples/, the framework and grammar bluebooks, and the
other domain directories lib/hecks/corpus.rb names. spec/corpus/*.json
holds the scripted command and query sequences replayed against them.
Hecks::Storehouse (lib/hecks/storehouse.rb): one bus that dispatches,
queries and inspects every booted domain, requiring a summary on every
call and a caller role on a role-gated command. hecks mcp
serves it over MCP. See AI-native
development.
A bluebook package vendored into a consuming domain's own
vendor/embryonaut_bluebooks/<name>/ directory and attached from its
hecksagon with attaches "<name>", from: :vendor, the same way
attaches attaches a framework bluebook.
examples/embryonaut_vendoring_demo is the worked example.
Issues, examples, and runtime/adapter work are all welcome — the gaps
in Project status are real
starting points, not a formality.
Before sending a change: bundle exec rspec, hecks model_check, and
hecks fuzz_run.fuzz are what CI runs, and every ruby-fenced example in a guide
or this README not tagged skip or excerpt is expected to execute exactly as shown
(spec/guides_spec.rb). See CONTRIBUTING.md for
the full checklist. To check the whole claim, not just the demo:
bundle exec rspec # the whole suite
bundle exec hecks model_check # static analysis over a domain's IR
bundle exec hecks fuzz_run.fuzz # generated sequences, checked against declared properties
Apache License 2.0 — see LICENSE.
SqlitePersistencePostgresPostgresEraHeki