hecks

PortOperation

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

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

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 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 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 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.