Roles and goals
Roles carry authority. Goals carry intent. Keeping them separate is what makes behaviour reusable.
Roles
A role is an authority envelope. It grants capabilities, imposes constraints and allocates resource budgets, and it is resolved at attach time into a concrete manifest the validator works against.
return {
id = "domain/port-operations",
roles = {
["port.crane_operator"] = {
capabilities = { "machine.slew", "machine.hoist", "machine.grip", "cargo.transfer" },
constraints = {
max_payload_tonnes = 40,
forbid_when = { "storm_warning", "quay_closed" },
require_clearance = "port.deck_clear",
},
resources = {
power = { budget_per_minute = 240 },
},
},
},
goals = {
["port.clear_inbound_queue"] = {
satisfied_when = "inbound_queue.depth == 0",
decompose_to = { "cargo.select_next", "cargo.transfer", "cargo.stow" },
abandon_when = { "storm_warning" },
},
},
}Roles are declared inside domain packs rather than inline, so the same port.crane_operator can be used in every place that imports the pack, and so a change to what a crane operator may do is made in one location.
Goals
A goal names an objective, a predicate that determines whether it is satisfied, and optionally how it decomposes. It carries no authority of its own — submitting an ambitious goal to a narrowly scoped role produces a plan that is rejected, not a plan that escalates.
Decomposition
Decomposition turns a goal into sub-goals, which eventually bottom out in declared actions. It is declared data rather than code, so a plan can be inspected before it runs and replayed afterwards.
Sub-goals inherit the parent’s authority envelope. A decomposition cannot widen what an actor may do — only narrow the question it is currently answering.
Abandonment
Every goal should declare when to stop pursuing it. A storm warning abandons an approach; a cancelled order abandons a delivery; a lost assignment abandons a response. Without abandonment conditions, actors pursue stale objectives long after the situation has changed — the single most common source of behaviour that reads as “broken”.
Submitting goals
Goals come from your systems. A dispatcher assigns them; a quest system issues them; a scheduler queues them. Player-originated requests are legitimate too, provided they have already passed your own checks — the runtime will validate them again regardless.