Words available inside operation do ... end / tells do ... end / asks 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.
A port operation is the only place these five words appear together, and
answers/refuses appear nowhere in the corpus at all, so the page
declares a domain of its own — a licence that is told when an inspection
happened, and asks a registry whether it is still valid:
```ruby bluebook Hecks.bluebook “PortOperationReference” do vision “One licence, told things by the outside and asking things of it.”
aggregate “Licence” do attribute :serial, Serial
identified_by :serial
attribute :holder, Holder
value_object("Serial") { attribute :value, String }
value_object("Holder") { attribute :value, String }
command "Grant" do
sets :serial
sets :holder
emits "LicenceGranted"
end end end ```
```ruby boot class RefRegistry def check(**args) raise “registry has no record of that licence” if args[:serial].to_s.include?(“unknown”)
{ "standing" => { "value" => "valid" } } end
# A PORT THAT HAS NOTHING TO SPREAD — one unnamed value, not a record. def locate(**) = “https://registry.example/lic-1” end
Hecks::Adapters.const_set(:RefRegistry, RefRegistry) unless Hecks::Adapters.const_defined?(:RefRegistry, false) Hecks.adapter(“RefRegistry”) { port “Registry” }
Hecks.hecksagon(“PortOperationReference”) do PortOperationReference::Licence.persisted_by(“Memory”)
PortOperationReference::Licence.port “Registry” do tells “Inspected” do attribute :serial, Hecks::Bluebook::Reference.new(“Licence”) attribute :inspector, Holder emits “InspectionRecorded” end
asks "Check" do
attribute :serial, Hecks::Bluebook::Reference.new("Licence")
answers "StandingReturned"
refuses "StandingUnavailable"
end
asks "Locate" do
attribute :serial, Hecks::Bluebook::Reference.new("Licence")
answers "LocationReturned"
refuses "LocationUnavailable"
end end end ```
runtime.dispatch("PortOperationReference::Licence.Grant", with: { serial: { value: "lic-1" }, holder: { value: "Ada" } })
runtime.dispatch("PortOperationReference::Licence.Grant", with: { serial: { value: "lic-unknown" }, holder: { value: "Bee" } })
reference_to type, as: — fills attributes
| argument | kind | required | fills |
|—|—|—|—|
| positional 1 | constant | true | type |
| as: | symbol | false | name |
The record this operation’s external fact is about; as: picks the
argument name, and this is the one attribute a bare, non-to: call
promotes straight into routing (Dispatcher#port_invocation) rather
than leaving it in the payload — an operation is refused if it names
none, the same way a creating command is refused with no identity to
mint one from.
as: :serial is what makes the operation addressable by the licence’s
own identity — the external caller names the thing in the domain’s own
terms, not with an id the domain minted. Promoted to routing, serial
never reaches the payload at all; the emitted event carries it as the
event’s own id instead:
told = runtime.dispatch("PortOperationReference::Licence.Registry.Inspected", to: "lic-1", with: { serial: "lic-1", inspector: { value: "Grace" } })
told.events.first.id # => "lic-1"
attribute name, type, default:, optional:, pattern:, matches:, admits:, one_of: — fills attributes
| argument | kind | required | fills |
|—|—|—|—|
| positional 1 | symbol | true | name |
| positional 2 | constant | true | type |
| default: | literal | false | default |
| optional: | flag | false | optional |
| pattern: | text | false | pattern |
| matches: | symbol | false | matches |
| admits: | text | false | admits |
| one_of: | list | false | one_of |
An extra field the external fact carries, declared the same way a
command’s own attribute is.
inspector is the fact’s own detail, and it rides through onto the
event unchanged — no sets anywhere, because an operation stores
nothing:
told.events.first.payload[:inspector][:value] # => "Grace"
The same gate a command’s arguments meet applies here — a field the operation never declared is refused rather than carried along:
runtime.dispatch_flat("PortOperationReference::Licence.Registry.Inspected", serial: "lic-1", weather: "fine") # ~> UnknownArgument: weather
emits emits — fills emits
| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | emits |
The event this operation announces once called. reference_to,
attribute, and emits are the whole of what a driving port’s
operation may declare — no given, no sets; it translates an
external fact into the domain’s own event vocabulary and stops there
(see wiring.md).
One event, and the record itself is untouched — Inspected announced
something without changing the licence at all:
told.events.map(&:name) # => ["InspectionRecorded"]
PortOperationReference::Licence.find("lic-1").holder.value # => "Ada"
answers answers — fills answers
| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | answers |
The event an asks becomes when the adapter came back — carrying what it
returned alongside the arguments the ask was made with.
A Hash answer is SPREAD, not nested: the adapter’s own keys become
top-level fields on the event. That is what closes the loop, because a
policy re-enters its target with the event payload verbatim and cannot
reach inside a key — an answer tucked under one could be read by a human
and by nothing else. It is a real contract on the adapter, which must
return what the reacting command takes, in the shape the runtime coerces
({ number: { value: 43 } }, not 43).
Singular, where emits is a list: an ask has exactly one success. A call
that could succeed two different ways is two calls.
Refused on a tells, which has no channel back to its caller.
What the adapter returned arrives under answered, beside the
arguments the ask was made with:
asked = runtime.dispatch("PortOperationReference::Licence.Registry.Check", to: "lic-1", with: { serial: "lic-1" })
asked.events.map(&:name) # => ["StandingReturned"]
asked.events.first.payload[:standing][:value] # => "valid"
The record this answer is about is still there beside it — not in the
payload (serial promoted straight to routing, same as Inspected
above), but as the event’s own identity:
asked.events.first.id # => "lic-1"
Only an answer with no names of its own keeps the nested shape — a port
returning a bare string has nothing to spread, and answered: is the
honest word for one unnamed value:
located = runtime.dispatch("PortOperationReference::Licence.Registry.Locate", to: "lic-1", with: { serial: "lic-1" })
located.events.first.payload[:answered] # => "https://registry.example/lic-1"
refuses refuses — fills refuses
| argument | kind | required | fills | |—|—|—|—| | positional 1 | text | true | refuses |
The event an asks becomes when the outside said no — carrying what it said
under refusal.
EVERY failure lands here, deliberately: a timeout, a bad credential, a
missing adapter, a nil where a number was wanted. A raise from the far side
of a boundary is not an exception in this domain’s terms, it is the outside
refusing, and the chapter has already named the word for that. So an ask
never propagates — the command that triggered it stands, and a policy
reacting to this event is where a retry or a give-up lives (see banking’s
ScheduledPayment, whose attempts counter and Retry command are the
worked example of exactly that shape).
Required on every asks. An ask that cannot fail is a call into a system
you do not control, pretending otherwise.
The adapter raises, and the caller gets an event rather than an exception — the dispatch returns normally:
refused = runtime.dispatch("PortOperationReference::Licence.Registry.Check", to: "lic-unknown", with: { serial: "lic-unknown" })
refused.events.map(&:name) # => ["StandingUnavailable"]
refused.events.first.payload[:refusal][:value] # => "RuntimeError: registry has no record of that licence"
“EVERY failure lands here” includes failures that are not the outside world’s fault at all — a broken adapter refuses exactly the same way a reachable one saying no does, which is the point: the domain has one word for “this did not happen”, and it is in the record.