StaticapplyStaticcanStaticmergeFold absorbed into survivor. Validates both are Globbable and
that survivor.canMergeWith(absorbed) returns true. Increments
the survivor's quantity, destructs the absorbed Stuff (which
fires its own onDestruct chain — that's where "this Stuff is
going away" subscribers belong), then fires
survivor.onMerged(absorbed).
merge itself emits no movement events. Used by:
ContainmentApi.move (after
the arrival's onContainableAdded has fired, so subscribers
see the arrival before the destruct).applyQuantity when an action
returns { ok: false } after a split.StaticsplitSplit n units off source into a new Stuff. Semantics:
n is a positive integer ≤ source.getQuantity().
Programmatic-contract violation throws.source.canSplit(n) (shadow seam). Veto throws.n === source.getQuantity()
returns source itself. The caller is going to move the
whole stack; a no-op split avoids the clone churn and
matches the slate's "destruct-on-zero" rule (no orphan
splitoff to clean up).source.getTemplatePath(),
copies every globIdentityFields value over, sets quantities
(splitoff = n, source = M - n), placeDirects the splitoff
into source's environment (no arrival witnesses), and fires
source.onSplit(splitoff).placeDirect is what makes split silent on movement events —
subdividing matter already in the room is not the same as
matter arriving there. See
docs/subsystems/glob.md § GlobbableApi.split.
Walk
candidatesin scored order, distributingquantityacross matches. Non-globbable matches contribute 1 unit each; globbable matches contribute up to their fullgetQuantity(). The action callback runs per operand with the contribution applied.See
docs/subsystems/glob.md § GlobbableApi.applyQuantityfor the full contract.Behavior:
{ ok: false, status: 'declined' }with anempty-resultnote. No actions run.mode: 'strict',kind: 'count'):sum(units(c) for c in candidates) < n→ immediate decline withquantity-clamped-rejected. No actions run.contribution = min(units(c), remaining). Split when the candidate is globbable andcontribution < c.getQuantity(), else operand = c.target-declinednote (target = the candidate, not the operand); if a split occurred, merge the operand back into c (reglob); continue the walk; remaining is unchanged.mode: 'lenient', count kind, remaining > 0 after walk): emitquantity-clamped; status'partial'.'partial'when any progress was made (any successful action) AND something diverged (target-declined or clamp);'declined'whenapplied === 0; absent on a clean run.Throw propagation (G5): if the action throws, the helper does NOT catch — the throw propagates. Partial state may be left in place (a successful split with no reglob). The controller's outer error handler is responsible.