hecks

Bluebook

Words available inside bluebook do ... end.

The tables on this page are generated from the language’s own aggregate-local syntax tables (lib/hecks/language/**/*.bluebook) by hecks language_run.project_reference — do not edit inside the markers. The prose between them is hand-written and survives regeneration.

Most of these run against the corpus — examples/banking is core and carries every structural word; examples/pizzas is supporting. generic and formerly_known_as are declared by nothing that ships, so they get a chapter each:

```ruby boot Hecks::Adapters::Folder.new.load_bluebooks(File.join(InMemoryDomain::ROOT, “examples/banking/bluebook”)) Kernel.load(File.join(InMemoryDomain::ROOT, “examples/pizzas/bluebook/pizzas.bluebook”))

Hecks.hecksagon(“Banking”) do attaches “Governance” Banking::Customer.persisted_by(“Memory”) Banking::Account.persisted_by(“Memory”) end Hecks.hecksagon(“Governance”) do Governance::RoleAssignment.persisted_by(“Memory”) Governance::RoleTransition.persisted_by(“Memory”) end

Hecks.hecksagon(“Pizzas”) do attaches “Governance” Pizzas::Order.persisted_by(“Memory”) end


```ruby bluebook
Hecks.bluebook "BluebookReference" do
  vision "Nothing anybody competes on — the kind of thing you would buy if you could."
  generic
  attaches_to "Query", "ReadModel"

  aggregate "Postcode" do
    attribute :code, Code

    identified_by :code
    value_object("Code") { attribute :value, String }

    command "Record" do
      sets :code
      emits "PostcodeRecorded"
    end
  end
end

```ruby bluebook Hecks.bluebook “Ledgering” do vision “The same domain, under the name it answers to now.” # THE OLD NAME, KEPT — a chapter that is renamed still has to be # findable by whatever wrote records under the name it used to have. formerly_known_as “Bookkeeping”

aggregate “Folio” do attribute :number, Number

identified_by :number
value_object("Number") { attribute :value, String }

command "OpenFolio" do
  sets :number
  emits "FolioOpened"
end   end end ```

```ruby boot Hecks.hecksagon(“BluebookReference”) { BluebookReference::Postcode.persisted_by(“Memory”) } Hecks.hecksagon(“Ledgering”) { Ledgering::Folio.persisted_by(“Memory”) }


## vision

<!-- generated:begin word=vision -->
`vision vision` — fills `vision`

| argument | kind | required | fills |
|---|---|---|---|
| positional 1 | text | true | vision |
<!-- generated:end -->

A one-line statement of what the domain is for. Optional, but not empty if given — the same value-object invariant fires here as on any string field, enforced by the meta-domain that judges the bluebook itself. Stored on the chapter and readable back after boot as `Chapter.vision`.

```ruby
runtime.registry.bluebook("Pizzas").vision  # => "Put toppings on a pizza and sell it to a customer."

formerly_known_as

formerly_known_as formerly_known_as — fills formerly_known_as

| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | formerly_known_as |

A domain’s own declared identity can change. Naming what it used to be lets the storage layer recognize its own history under the old name instead of minting a brand-new lineage from nothing — a rename, once applied, bridges the journal, the sequence, every era partition, and the domain-keyed rows in hecks_eras/hecks_era_texts/hecks_approvals/hecks_attestations onto the new name, in one transaction, the first time the domain boots under it. Optional, but not empty if given — the same value-object invariant fires here as on any string field. Stored on the chapter and readable back after boot as Chapter.formerly_known_as; safe to leave declared permanently, since every later boot after the bridge falls through to the ordinary already-held path.

The chapter answers to its current name and remembers the old one — the old name is a fact about this chapter, not a second chapter:

runtime.registry.bluebook("Ledgering").formerly_known_as  # => "Bookkeeping"
runtime.registry.bluebook("Bookkeeping")  # => nil

Nothing about the domain’s own shape depends on it:

Ledgering::Folio.open_folio!(number: { value: "f-1" }).number.value  # => "f-1"

Written exemption (ADR 0025 principle 4) — no bluebook in THIS repository’s own corpus renames itself, so the doctest above is necessarily synthetic. The word is real, and used for real, outside it: a downstream client project’s bluebook (a separate repository, outside this one) declares formerly_known_as with its previous domain name on its own chapter, bridging real production journal/ era/approval rows onto the renamed domain the day it deployed under the new name, zero data loss, real Member rows confirmed intact.

namespace

namespace namespace — fills namespace

| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | namespace |

Names the Ruby module the constants of a chapter install under, instead of its own name. Without it, a chapter called Pizzas installs as ::Pizzas and its aggregates as Pizzas::Order. With namespace "Tools::Workbench", the chapter installs as Tools::Workbench and its aggregates inside it, with no top-level shortcuts. The Hecks domain (ADR 0080) uses it: a chapter named Hecks would otherwise install into the gem module of the same name, where aggregates such as Release and Kernel collide with modules hecks already defines. A chapter whose constants would land in Hecks itself refuses at build.

The chapter keeps its own name; only where Ruby installs it changes:

Hecks::Bluebook::Chapter.new(name: "Workbench", namespace: "Tools::Workbench").namespace  # => "Tools::Workbench"

attaches_to

attaches_to attaches_to — fills attaches_to

| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | attaches_to |

ADR 0026’s own seam for a sub-language chapter: the core grammar does not name its extension points, so a chapter that extends one names ITSELF onto it instead. attaches_to "Query", "ReadModel" names the core contexts this chapter’s own Syntax aggregate contributes Keyword/Argument rows for — read at boot by SyntaxBoot’s own generic discovery (MetaValidator::ATTACHED_GRAMMAR_DIR), which merges every attached chapter’s rows into the same grammar table the core’s own rows populate, tagged by nothing more than having been found. Variadic — accumulates across calls the same reason identified_by/ group_by do. Stored on the chapter and readable back after boot as Chapter.attaches_to. Most chapters attach to nothing, and ABSENT IS NOT EMPTY, the same reading version/formerly_known_as give.

runtime.registry.bluebook("BluebookReference").attaches_to  # => ["Query", "ReadModel"]
runtime.registry.bluebook("Ledgering").attaches_to  # => []

The real, load-bearing use (not synthetic, unlike the fixture above): lib/hecks/language/bluebook/attaches/paging.bluebook declares attaches_to "Query", "ReadModel" for real — the Paging sub-language, whose own Syntax aggregate holds the seed rows for limit/offset/cursor/nulls (see docs/implemented/reference/query.md/ read_model.md’s own sections on each). Every boot merges them in; ParserTable/this doc generator/every conformance spec reads the merged table and cannot tell a core row from an attached one.

provides

provides provides, assignments:, grant:, transitions:, admit:, people:, register:, link:, resolve:, subscribe:, add_name:, confirm:, unsubscribe:, awaiting_confirmation:, receives_issues:, left:, confirm_window:, unsubscribe_window:, webhook_tolerance:, session_hold:, initiate:, succeeded:, failed:, lapse_reason:, holds_seat:, send_issue:, record_delivery:, schedule:, request:, registered_at:, frees_seat:, connect:, reconnect:, disconnect:, suspend:, resume:, enable:, disable:, mark_sensitive:, markings_for:, key_for:, shred: — fills provides

| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | provides | | assignments: | text | false | provides | | grant: | text | false | provides | | transitions: | text | false | provides | | admit: | text | false | provides | | people: | text | false | provides | | register: | text | false | provides | | link: | text | false | provides | | resolve: | text | false | provides | | subscribe: | text | false | provides | | add_name: | text | false | provides | | confirm: | text | false | provides | | unsubscribe: | text | false | provides | | awaiting_confirmation: | text | false | provides | | receives_issues: | text | false | provides | | left: | text | false | provides | | confirm_window: | text | false | provides | | unsubscribe_window: | text | false | provides | | webhook_tolerance: | text | false | provides | | session_hold: | text | false | provides | | initiate: | text | false | provides | | succeeded: | text | false | provides | | failed: | text | false | provides | | lapse_reason: | text | false | provides | | holds_seat: | text | false | provides | | send_issue: | text | false | provides | | record_delivery: | text | false | provides | | schedule: | text | false | provides | | request: | text | false | provides | | registered_at: | text | false | provides | | frees_seat: | text | false | provides | | connect: | text | false | provides | | reconnect: | text | false | provides | | disconnect: | text | false | provides | | suspend: | text | false | provides | | resume: | text | false | provides | | enable: | text | false | provides | | disable: | text | false | provides | | mark_sensitive: | text | false | provides | | markings_for: | text | false | provides | | key_for: | text | false | provides | | shred: | text | false | provides |

Declares a capability this chapter answers for every domain that attaches it, so nothing downstream has to recognise the chapter by its name. The one capability the language knows today is authorization, and it needs exactly three verbs, each one of this chapter’s own:

The Governance framework member is the real use (lib/hecks/framework/bluebook/governance.bluebook). The role check at dispatch, boot’s refusal of a role in a domain with no provider, the authorization adapter and the fuzzer all read these verbs. None of them looks for the name “Governance”. A provides with an unknown capability, a missing or extra key, or a verb that names no command or query of the right kind is refused when the chapter is built. Each key becomes one row, in the order written:

runtime.registry.bluebook("Governance").provision("authorization")[:grant]  # => "RoleAssignment.Assign"
runtime.registry.bluebook("Governance").provided_verb("authorization", :assignments)  # => "Governance::RoleAssignment.AssignmentsForActor"
runtime.registry.authorization_provider_for("Banking").name  # => "Governance"
runtime.registry.bluebook("Ledgering").provides  # => []

A capability can also name a quantity the host needs (ADR 0098). A :duration entry is spelled Aggregate.attribute and resolves to the whole-seconds default: of that attribute, so the number is declared once, in the bluebook. provides "newsletter" takes optional confirm_window: and unsubscribe_window: (the lifetimes of the emailed links), and provides "checkout" takes optional webhook_tolerance: and session_hold::

value_object "Seconds" do
  attribute :value, Integer
end
attribute :hold, Seconds, default: { value: 1800 }   # 30 minutes

provides "checkout", session_hold: "CheckoutSession.hold"

A chapter that names an attribute that does not exist, or whose default is not a positive whole number of seconds, is refused when it is built.

Two more optional kinds name a word and a field (ADR 0099). A :text entry (provides "payments", lapse_reason: "Payment.lapse_reason") resolves to the string default: of that attribute, and an :attribute entry (provides "registrations", registered_at: "Registration.requested_at") resolves to the attribute’s own name. Both are refused when the attribute is missing, and a :text entry when its default is not a non-empty string.

The Privacy chapter declares two more, so the runtime never looks for its name: provides "privacy" (mark_sensitive: the command that flags an attribute, markings_for: the query of one domain’s markings) is what boot’s seeding of mark_sensitive facts and a handle’s masking of a read dispatch through, and provides "subject_keys" (key_for: and shred:) is what Ports::KeyVault.shred! dispatches through.

core

core — fills classification

Marks this chapter a core subdomain, in the DDD sense (core/supporting/generic). core, supporting, and generic all just set the one classification field, so calling more than one leaves whichever ran last — and, verified against the runtime, nothing else currently reads that field back. It documents intent; it gates nothing.

runtime.registry.bluebook("Banking").classification  # => "core"

“Gates nothing” is checkable rather than promised — a core chapter dispatches exactly like any other:

Banking::Account.open!(customer: "bb-1", number: { value: "bb-a1" }, kind: { name: "current" }, daily_limit: { cents: 1 })  # ~> NotFound: bb-1

supporting

supporting — fills classification

Marks this chapter a supporting subdomain. Same field, same mutual exclusivity, as core.

runtime.registry.bluebook("Pizzas").classification  # => "supporting"

generic

generic — fills classification

Marks this chapter a generic subdomain — undifferentiated, off-the-shelf territory in the DDD sense. Same field as core.

runtime.registry.bluebook("BluebookReference").classification  # => "generic"

aggregate

aggregate name do ... end — opens a Aggregate body

| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | name |

Opens the thing with identity in this domain — identified_by, its attributes, commands, and lifecycle. See the Aggregate reference page for the full vocabulary inside.

Every aggregate a chapter opens becomes an entry point of its own, named after the chapter:

runtime.registry.bluebook("Banking").aggregates.map(&:hecks_name).first(3)  # => ["Customer", "Account", "OnboardingCase"]
BluebookReference::Postcode.record!(code: { value: "N1" }).code.value  # => "N1"

read_model

read_model name do ... end — opens a ReadModel body, was report

| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | name |

Opens a read that gathers heads from several aggregates around one spine — declared at the chapter’s own top level, not under any single aggregate, because no one aggregate owns it. See the ReadModel reference page for reference_to/include and the rest. report (ADR 0025 reverts it — the IR construct, the registry API, and the docs filename all said read_model the whole time) stays answered only for frozen era text still parsed by the legacy grammar; live source refuses it, naming this word.

Banking declares ten, and they sit on the chapter rather than under any aggregate — which is the whole reason the word exists at this level:

runtime.registry.bluebook("Banking").read_models.map(&:hecks_name)  # => ["CustomerPortfolio", "ComplianceDashboard", "DisputedPaymentCount", "DisputedPaymentMedian", "DisputedPaymentTotal", "DisputedPaymentAverage", "DisputedPaymentSmallest", "DisputedPaymentLargest", "DisputedPaymentP95", "AccountsByKind"]

policy

policy name do ... end — opens a Policy body

| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | name |

The same word as inside an aggregate — a reaction to an event, on/trigger — but written here at the chapter’s top level for a policy that isn’t one aggregate’s own business. Pizzas’ OnPizzaPaymentReceived (examples/pizzas/bluebook/pizzas.bluebook) is a real example: it triggers Order.Purchase but is declared beside the aggregate, not inside it. See the Policy reference page.

Banking declares four here and two inside aggregates, and they all land in the same place — where a policy was WRITTEN is a readability decision, not a structural one:

runtime.registry.bluebook("Banking").policies.map(&:name)  # => ["ReviewOnFreeze", "RetryOnPaymentFailure", "FreezeAccountsOnSuspension", "NotifyOnClosure", "ReviewOnBoxSurrender", "FlagKeyReturn"]

The first two of those are Account’s and ScheduledPayment’s own, declared inside them; the rest are the chapter’s.

process_manager

process_manager name do ... end — opens a ProcessManager body

| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | name |

Opens a stateful saga spanning several events and commands — correlates_by, starts_on/ends_on, its handlers. Chapter-level only, like report and top-level policy, since it belongs to no single aggregate. See the ProcessManager reference page.

runtime.registry.bluebook("Banking").process_managers.map(&:name)  # => ["Onboarding", "Settlement", "ExternalSettlement"]