Skip to content

stopParamsObject

The stopParams object defines properties for stop types — fundamental to Spiral’s operation. Stop types can be modified during execution via update, enabling accurate modelling of stop states. Attribute names and types are links; sub-object fields and the Helix extension block are documented below.

{
"key": <string>,
"compulsory": <bool>,
"position": ANY | ONTIMEONLY | FIRST | LAST,
"deployMode": ALWAYS | NEVER | LATE | AUTO_HEAD | AUTO_ARV,
"reliable": <bool>,
"nonProductive": <bool>,
"exclusive": <bool>,
"areaExempt": <bool>,
"isBreak": <bool>,
"payUnits": <real>,
"failureFactor": <real>,
"quality": ANY | RELIABLE | IDLE | DARK_RELIABLE | DARK_IDLE,
"positionQuality": ANY | RELIABLE | IDLE,
"reassignQuality": ANY | RELIABLE | IDLE,
"arrivalIs": STOP | LOCATION,
"commitMode": SPIRAL | HOST,
"canBreak": <bool>,
"necessary": <bool>,
"noCall": <bool>,
"noOsrc": <bool>,
"optionsLimit": <integer> | null,
"contextMargin": <real>,
"idleSurgeLimit": <integer> | null,
"domain": { [domain]: { [alert]: { priority, ATA } } } | null,
"rebook": { "from": time, "until": time } | null,
}
key string
Unique identifier for stop-type reference.
clone stopParamsKey
Inherit values from another stop type.
minTravelTime timeInterval default 0 s
Minimum travel time allocated to the stop.
roundTimes timeInterval default 5 min
Rounding interval for proposed arrival / departure times.
compulsory bool default false
Whether a resource can refuse during voluntary periods.
position ANY | ONTIMEONLY | FIRST | LAST default ANY
Placement restriction: ANY (no restriction), ONTIMEONLY (arrival ≤ latest), FIRST (first stop of the working period), LAST (last stop before period end). FIRST and LAST also change the DISPLAY: see "Terminal duties" below.
defaultDuration timeInterval default 10 min
Default stop time when not supplied.
defaultContingency timeInterval default 0 s
Default contingency buffer.
locationContingency timeInterval default 5 min
Advanced — margin kept in hand when the optimiser CHOOSES a position for a dynamic location (a location given as [] — see location). The chosen position must leave at least this much slack before the work would turn voluntary, so a selection cannot land exactly on that boundary. See "Dynamic locations and the voluntary boundary" below.
deployMode ALWAYS | NEVER | LATE | AUTO_HEAD | AUTO_ARV default ALWAYS
Deployment messaging to the host: ALWAYS (standard), NEVER (host manages), LATE (only to prevent lateness), AUTO_HEAD / AUTO_ARV (transition straight to that state).
earlyDeploy timeInterval default 20 min
Early-deployment window for idle resources.
reliable bool default false
Whether the finish time is considered reliable.
nonProductive bool default false
Non-working-time activity.
exclusive bool default false
Requires no concurrent activities.
areaExempt bool default false
Exempt from working-area restrictions.
isBreak bool default false
Contributes to break-time allocation.
payUnits real default 1.0
Pay units for pay-per-job compensation.
failureFactor real default 0.0
Risk factor (0.0–1.0) for completion failure.
useStby useCondition
Accessibility condition under which the standby solution tier may be used.
useVoluntary useCondition
Accessibility condition under which the voluntary solution tier may be used.
useExtended useCondition
Accessibility condition under which the extended solution tier may be used.
useSurge useCondition
Accessibility condition under which surge de-prioritisation applies (e.g. NOSOLN, LATE, APPT, CLOSER).
useCloserMargin distance
Distance margin allowed when using a closer alternative.
useEarlierMargin timeInterval
Time margin allowed when using an earlier alternative.
useLateMargin timeInterval
Time margin allowed when using a later alternative.
useStbyTravelFactor real default 1.0
Travel-time multiplier applied when evaluating standby use.
quality ANY | RELIABLE | IDLE | DARK_RELIABLE | DARK_IDLE default ANY
Resource quality required to service this stop type. DARK_ variants are reliability sub-classes (signed-off but reachable).
positionQuality ANY | RELIABLE | IDLE default ANY
Controls where in the schedule this stop may be positioned.
reassignQuality ANY | RELIABLE | IDLE default ANY
Resource quality required before this stop may be reassigned.
arrivalIs STOP | LOCATION default STOP
Whether arrival timing references the stop or the location.
commitMode SPIRAL | HOST default SPIRAL
Whether Spiral commits the stop automatically (per commitWithinTime) or the host controls commit.
commitWithinTime timeInterval default 0 s
Spiral transitions to CMTD when predicted arrival is within this window.
uncommitWithinTime timeInterval
Window within which a committed stop may still be uncommitted.
headDelay timeInterval default 0 s
Time allowed for a resource to respond to deployment before heading.
clockResolution timeInterval default 60 s
Resolution of the estimated-arrival clock when heading to the stop.
sensitivity timeInterval default 5 min
Advanced — the smallest time interval treated as significant.
canBreak bool default false
A break may be taken within this stop type (though the stop is not itself a break).
necessary bool default true
Whether the stop is essential to its containing activity.
noCall bool default false
The stop should not trigger callout of resources in the STBY state.
noOsrc bool default false
Prevents the stop from entering the OSRC (outsource) state.
onlyRestrictionSubsidy real default 0.0
Subsidy applied when the stop is restricted to specific resources (an "only" restriction).
optionsLimit integer default null
Maximum number of skill options considered (null = unlimited).
contextMargin real
Cost / scoring margin applied to the stop’s surrounding scheduling context.
idleSurgeLimit integer default null
Count limit on the idle-surge effect for this stop type.
voluntaryConfirmWithin timeInterval default FOREVER
Timeframe within which voluntary deployment must be confirmed.
latest dynamicLatestObject
Controls generation of latest values for stops with dependencies.
noSoln noSolnObject
Cost and consequences of not scheduling (outsourcing) this stop type.
init initObject
How Spiral responds when a stop of this type enters the INIT state (immediate solutions / appointments).
delay delayObject
How predicted arrival times generate lateness costs.
handover handoverObject
Appointment / handover retrieval parameters.
parts partsObject
Parts-ordering lead-time configuration.
illegalActions illegalActionsObject
How rule violations for this stop type are handled.
domain { [domain]: { [alert]: { priority, ATA } } }
Spiral 2.4.0a surge factors for this stop type, keyed by surge domain then alert level (e.g. {"Road":{"AMBER":{"priority":1.0,"ATA":1.0},"RED":{"priority":0.1,"ATA":1.5}}}). Stop-side factors: priority (scheduling-priority weight) and ATA (arrival-time allowance). Activated for stops within a live surge zone naming this domain + alert — see surgeZoneObject.
rebook { "from": time, "until": time }
Rebooking window — the daily period into which a stop of this type may be re-initialised / rebooked (e.g. {"from":"16:00:00","until":"22:00:00"}).
helix helixObject
Helix-specific presentation and stop-building configuration (see below).

The helix block is a Helix addition to the Spiral stopParams (it is ignored by Spiral itself). It controls how a stop type is presented and built in the Helix client, plus server-side validation and state-change hooks. check and the onState cells are "fnName()" specs resolved against the client crud module — see Backend hooks.

Inheritance & role roots. Stop types inherit via clone, bottoming out at a context-agnostic default that holds the shared Spiral fields (delay, noSoln, init, …) and no helix. The convention is three role roots — projectStop, responsePointStop, unprodStop — that each clone:"default" and carry only their own helix block, mirroring the frontend’s per-role rendering foundations. The helix override (context-sensitive) thus lives one level down, per role; a Spiral value must never appear on a role root — it would silently override Spiral’s own default by context. See Stop-type structure: the role roots.

{
"icon": <string>,
"color": <string> | null,
"domain": <string> | null,
"locationType": [string] | null,
"lineColor": <string> | null,
"mapVisibility": spec | null,
"listText": spec | null,
"planText": spec | null,
"planTitle": spec | null,
"highlight": spec | null,
"warning": { [code]: string } | null,
"roster": <bool> | null,
"messageIn": { actionee, urgency, text } | null,
"messageOut": { actionee, urgency, text } | null,
"delayColors": [object] | null,
"nextStateColors": [object] | null,
"mandatoryStop": { "to": stopParamsKey },
"templates": <object> | null,
"tag": <string> | null,
"check": spec | null,
"onState": { [from]: { [to]: spec } } | null,
}
icon string
Name of the map-marker icon rendered for this stop type (e.g. "Car"). Resolves to a canvas-rendered icon in the client. A null icon draws a small dot rather than a full marker. On an UNPROD (duty) stop an ABSENT icon falls through to the assigned resource's, so a duty draws as its vehicle.
color string
Type-level colour, used for every state that does not name its own — one colour for a whole family, inherited down the clone chain. The unprod floor sets mid grey this way. A state's own `color` wins, and an explicit `"color":null` on a state means no colour (it is not re-read as "inherit").
domain string
Helix-only — this stop type’s default skill domain (one of the names in helix.domains); the group the skills editor opens on. Not sent to Spiral. Distinct from the top-level surge `domain` block above.
locationType string
Which location modes the stop editor offers, any of "any" | "derived" | "area" | "precise" (see the location object). Controls the location-mode rotation UI. Defaults to ["any","derived","area"] when unset; destination / incident stops typically use ["derived","precise"].
lineColor string
CSS colour of the schedule line drawn into this stop on the map. When unset the line takes the stop's own (state) colour; set null to override a colour inherited from a cloned type.
mapVisibility spec
Expression (with `this` = the stop) deciding whether the marker is drawn. Defaults to helixMapVisibility(): drawn if the stop is in the current plan, on a watched/active desk, a child of a plan stop, or shares the current selection; "anywhere" (no location) stops are never drawn, and a FIRST/LAST terminal duty is not drawn until it is ARVD. Set your own expression here to override any of that per stop type.
listText spec
Expression producing the stop’s row text in the entity lists (default helixListText()). Returning null hides the stop from the lists.
planText spec
Expression producing the stop’s row text in the plan window (default helixPlanText()). Returning null hides it from the plan.
planTitle spec
Expression producing the plan-window title when this stop heads the plan (default helixPlanTitle(): the resource summary, falling back to the owning desk’s name).
highlight spec
Expression producing the row / marker highlight badge. helixHighlight("Lock") shows a lock while the stop is locked; helixHighlights("Lock","rendezvous") adds a rendezvous glyph; helixBaseHighlight("Lock","duty") — the default on the base (REST) stop — adds a glyph when an adjacent terminal duty is hidden. Several glyphs stack.
warning { [code]: string }
Map of Spiral illegal-action code → operator-facing message template shown when a mutation is refused. Templates may interpolate stop attributes with {attr|format} placeholders.
roster bool
Marks a roster stop type (NOW / REST / STBY). Roster stops are presentation shells for the resource’s shift rather than jobs, and are skipped when deriving the resource’s active work.
messageIn { actionee, urgency, text }
Resource → dispatch messaging facility for this stop type (action format). `text` is the enable gate: true = always available, an expression = per-stop, absent = hidden. See the Actions guide.
messageOut { actionee, urgency, text }
Dispatch → resource messaging facility (the "Message resource" button in the stop editor). Same shape and gating as messageIn. See the Actions guide.
actions actionObject
Per-stop-type action definitions raised into the dispatch Actions queue (messages to acknowledge, questions to resolve, automatic classed actions), keyed by action name. Fields in actionObject; the role and worked examples in the Actions guide.
delayColors object
Colour bands keyed by lateness threshold (seconds), used by helixColorDelay() to colour the stop by how far ahead of / behind schedule it is.
nextStateColors object
Colour bands by time-to-next-state, used to colour the stop as its next transition approaches.
[STATE] stateObject
Per-state presentation keyed by stop state (PLAN, DPLY, HEAD, …) — colour, status text and icon. See below.
addStop addStopObject
Default child-stop types offered when an operator adds a stop of this type (see below).
mandatoryStop { "to": stopParamsKey }
Child stops automatically attached to a stop of this type.
morphStop stopParamsKey
Stop types this stop may be morphed into.
templates object
Predefined stop templates available for this type.
tag string
The code shown at the top-right of a marker — and the ENTIRE content of a small (icon-less) one. Two meanings, by what it is attached to: on a project stop it is the CUSTOMER's code, from that customer's `customerParams.helix.tag`, and absent means ordinary work; on a resource it is the vehicle VARIANT, from its resourceParams. A stop type that authors its own `tag` keeps it — the customer only fills the gap. Renamed from `lineLetter`, which described neither a line nor always a letter.
check spec
Server-side validation run on the crud add/update path with `this` = the stop. Return a non-empty string to reject the mutation, or null/undefined to pass.
onState { [from]: { [to]: spec } }
Stop state-transition matrix. The cell [fromState][toState] is called (with `this` = the stop) on that transition; the string "null" marks the pre-create (null→first) and post-delete (last→null) edges. Cells are independent.

Each relation maps to the stop type(s) offered when the operator extends a stop of this type along that relation (an array of stopParamsKey, or null). The relations mirror the stopObject links of the same name.

to stopParamsKey
Stop types offered for a sequential delivery (`to`) child.
and stopParamsKey
Stop types offered for an unrelated concurrent (`and`) child.
with stopParamsKey
Stop types offered for a concurrent (`with`) child.
then stopParamsKey
Stop types offered for a sequential follow-up (`then`) child.
asset stopParamsKey
Stop types offered for an asset (`asset`) child.

Keys named after a stop state (PLAN, OSRC, CALL, DPLY, HEAD, CMTD, ARVD, DONE) carry the look and label for the stop while it is in that state:

{
"color": <string>,
"status": <string> | null,
"resColor": <true | string | null> | null,
"icon": <string> | null,
}
color string
Colour name (resolved via the client colour map) or expression (e.g. helixColorDelay()) for the marker in this state; "transparent" hides the dot.
status string
Domain text shown for this state in the lists, the stop editor and the map — e.g. an UNLOAD stop showing "RECY" for HEAD/CMTD. Falls back to the raw state code when unset.
resColor true | string | null
The colour a RESOURCE takes while it is in this state travelling to this stop — a different question from `color`, which is the stop's own pin. `true` matches this stop's colour; a string is a colour name or expression; `null` is off and overrides an inherited value; absent inherits down the clone chain and means no influence. See "The resource's colour" below.
icon string
Override the marker icon while the stop is in this state.

A stop pinned to the first or last slot of the working period is a sign-on / sign-off duty, and it sits at the resource’s base. Helix therefore does not draw it on the map until it is ARVD — before that it would only stack a pin on top of the base pin. So the duty is not invisible in the meantime, the adjacent base stop carries a highlight glyph instead: for a FIRST duty the base before it, for a LAST duty the base after it. The hint clears the moment the duty arrives and draws its own pin.

Nothing needs configuring for this — the behaviour comes from the core helixMapVisibility() and the base stop’s default helixBaseHighlight("Lock","duty"). Both are ordinary defaults, so a stop type that should behave differently overrides helix.mapVisibility (or the base stop’s helix.highlight) as usual. The glyph name is the second argument, so a different icon is a one-word change.

Note this is driven by position, not by the stop being unproductive: any stop type carrying FIRST or LAST behaves this way.

Dynamic locations and the voluntary boundary (locationContingency)

Section titled “Dynamic locations and the voluntary boundary (locationContingency)”

A location supplied as [] is dynamic: the position is not given, and Spiral selects one while optimising (see location). The choice is a trade — a position that suits the schedule best is often the one that uses the working period most completely.

That is exactly where it can go wrong. A resource’s duty turns voluntary past a threshold it carries itself (stopOnMandatory, or voluntaryDeployStarts for a new deployment — see dutyObject), and voluntary work is work the resource may refuse unless the stop type is compulsory. A dynamic location chosen right at that threshold produces a plan that is legal at the instant it is made and fragile immediately afterwards: a few minutes of traffic, or an earlier stop running over, tips the work across the line and puts it back in front of the operator for agreement.

locationContingency is the margin held back from that boundary. The optimiser only selects a position that leaves at least this much slack before the work would become voluntary — 5 minutes by default, which is enough to absorb ordinary variance. Raise it where the consequence of crossing is expensive (a resource likely to decline, or an estate where voluntary work needs authorising); lower it, or set 0 s, to let the optimiser use the whole period and accept the churn.

It applies only to positions the optimiser chooses. A stop with real coordinates is unaffected — nothing is being selected, so there is no boundary to stand back from.

A resource’s own marker is coloured by the stop it is working or travelling to — so a van deploying to a break should look like a break, not like a van deploying to a job. The stop decides that, per state:

"DPLY": { "color":"yellow", "status":"BRK?", "resColor":true } // resource matches this stop's colour
"DPLY": { "color":"yellow", "status":"BRK?", "resColor":"lightGrey" } // a colour name, or an expression
"DPLY": { "color":"yellow", "status":"BRK?", "resColor":null } // off — overrides an inherited value
"DPLY": { "color":"yellow", "status":"BRK?" } // absent — inherit; unset = no influence

true avoids repeating the colour, and keeps the two from drifting apart when one is edited. null matters because of the clone chain: once a parent type sets resColor, only an explicit null on a child turns it off again — absence means “not specified”, which inherits. The same true / expression / absent shape is used by messageIn.text and messageOut.text.

Where no stop offers a colour, the resource falls back to the arguments of helixNowColor on its roster NOW stop (idle, deploying, heading, committed), so a domain that authors no resColor anywhere behaves exactly as it did before the key existed.

One rule for every stop kind. A project, responsePoint and unprod (duty) stop all resolve their own stopParams entry through the same clone chain, differing only in the core floor beneath them. An unprod stop additionally takes the assigned resource’s icon when it declares no icon of its own, so a duty on a vehicle’s tree draws as that vehicle unless the duty says otherwise ("icon":null gives the small marker).