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.
-
keystring - Unique identifier for stop-type reference.
-
clonestopParamsKey - Inherit values from another stop type.
-
minTravelTimetimeIntervaldefault0 s - Minimum travel time allocated to the stop.
-
roundTimestimeIntervaldefault5 min - Rounding interval for proposed arrival / departure times.
-
compulsorybooldefaultfalse - Whether a resource can refuse during voluntary periods.
-
positionANY | ONTIMEONLY | FIRST | LASTdefaultANY - 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.
-
defaultDurationtimeIntervaldefault10 min - Default stop time when not supplied.
-
defaultContingencytimeIntervaldefault0 s - Default contingency buffer.
-
locationContingencytimeIntervaldefault5 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.
-
deployModeALWAYS | NEVER | LATE | AUTO_HEAD | AUTO_ARVdefaultALWAYS - Deployment messaging to the host: ALWAYS (standard), NEVER (host manages), LATE (only to prevent lateness), AUTO_HEAD / AUTO_ARV (transition straight to that state).
-
earlyDeploytimeIntervaldefault20 min - Early-deployment window for idle resources.
-
reliablebooldefaultfalse - Whether the finish time is considered reliable.
-
nonProductivebooldefaultfalse - Non-working-time activity.
-
exclusivebooldefaultfalse - Requires no concurrent activities.
-
areaExemptbooldefaultfalse - Exempt from working-area restrictions.
-
isBreakbooldefaultfalse - Contributes to break-time allocation.
-
payUnitsrealdefault1.0 - Pay units for pay-per-job compensation.
-
failureFactorrealdefault0.0 - Risk factor (0.0–1.0) for completion failure.
-
useStbyuseObject - When the standby solution tier may be used — the accessibility rule plus the context-space thresholds.
-
useVoluntaryuseObject - When the voluntary solution tier may be used — the accessibility rule plus the context-space thresholds.
-
useExtendeduseObject - When the extended solution tier may be used — the accessibility rule plus the context-space thresholds.
-
useSurgeuseObject - When the surge solution tier may be used — the accessibility rule (e.g. NOSOLN, LATE, APPT, CLOSER) plus the context-space thresholds. Supersedes idleSurgeLimit, whose idle test is now useSurge.idleThreshold.
-
useCloserMargindistance - Distance margin allowed when using a closer alternative.
-
useEarlierMargintimeInterval - Time margin allowed when using an earlier alternative.
-
useLateMargintimeInterval - Time margin allowed when using a later alternative.
-
useStbyTravelFactorrealdefault1.0 - Travel-time multiplier applied when evaluating standby use.
-
qualityANY | RELIABLE | IDLE | DARK_RELIABLE | DARK_IDLEdefaultANY - Resource quality required to service this stop type. DARK_ variants are reliability sub-classes (signed-off but reachable).
-
positionQualityANY | RELIABLE | IDLEdefaultANY - Controls where in the schedule this stop may be positioned.
-
reassignQualityANY | RELIABLE | IDLEdefaultANY - Resource quality required before this stop may be reassigned.
-
arrivalIsSTOP | LOCATIONdefaultSTOP - Whether arrival timing references the stop or the location.
-
commitModeSPIRAL | HOSTdefaultSPIRAL - Whether Spiral commits the stop automatically (per commitWithinTime) or the host controls commit.
-
commitWithinTimetimeIntervaldefault0 s - Spiral transitions to CMTD when predicted arrival is within this window.
-
uncommitWithinTimetimeInterval - Window within which a committed stop may still be uncommitted.
-
headDelaytimeIntervaldefault0 s - Time allowed for a resource to respond to deployment before heading.
-
clockResolutiontimeIntervaldefault60 s - Resolution of the estimated-arrival clock while a resource heads to the stop: the reported arv moves in steps of this size, so a comparison against latest can change only at a step.
-
sensitivitytimeIntervaldefault5 min - Advanced — the smallest time interval treated as significant.
-
canBreakbooldefaultfalse - A break may be taken within this stop type (though the stop is not itself a break).
-
necessarybooldefaulttrue - Whether the stop is essential to its containing activity.
-
noCallbooldefaultfalse - The stop should not trigger callout of resources in the STBY state.
-
noOsrcbooldefaultfalse - Prevents the stop from entering the OSRC (outsource) state.
-
onlyRestrictionSubsidyrealdefault0.0 - Subsidy applied when the stop is restricted to specific resources (an "only" restriction).
-
optionsLimitintegerdefaultnull - Maximum number of skill options considered (null = unlimited).
-
contextMarginreal - Cost / scoring margin applied to the stop’s surrounding scheduling context.
-
voluntaryConfirmWithintimeIntervaldefaultFOREVER - Timeframe within which voluntary deployment must be confirmed.
-
latestdynamicLatestObject - Controls generation of latest values for stops with dependencies.
-
noSolnnoSolnObject - Cost and consequences of not scheduling (outsourcing) this stop type.
-
initinitObject - How Spiral responds when a stop of this type enters the INIT state (immediate solutions / appointments).
-
delaydelayObject - How predicted arrival times generate lateness costs.
-
handoverhandoverObject - Appointment / handover retrieval parameters.
-
partspartsObject - Parts-ordering lead-time configuration.
-
illegalActionsillegalActionsObject - 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"}).
-
helixhelixObject - Helix-specific presentation and stop-building configuration (see below).
Helix extensions
Section titled “Helix extensions”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.
-
iconstring - 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.
-
colorstring - 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").
-
domainstring - 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.
-
locationTypestring - 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"].
-
lineColorstring - 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.
-
mapVisibilityspec - 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.
-
listTextspec - Expression producing the stop’s row text in the entity lists (default helixListText()). Returning null hides the stop from the lists.
-
planTextspec - Expression producing the stop’s row text in the plan window (default helixPlanText()). Returning null hides it from the plan.
-
planTitlespec - 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).
-
highlightspec - 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.
-
rosterbool - 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.
-
comms{ devices, nocomms } - Device-presence badge, shipped by default on the roster stops (NOW / REST / STBY). `devices` maps a device type to its glyph; `nocomms` is the alert shown when a signed-on resource has no device connected. Set null to disable it for a roster type. See below.
-
info{ highlight, text } - Spiral’s alerts, shipped by default on every stop floor and roster block: `highlight` is a `{ icon, color }` (an amber `alert` glyph) drawn on the list row, the map pin and the editor title bar whenever the stop carries `info`; `text` is the wording, a map of illegalReason code → the line shown on hover, where a client sets the language and the business nuance of each reason. Set `info` to null to disable the glyph for a type.
-
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.
-
actionsactionObject - 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.
-
delayColorsobject - Colour bands on lateness, used by helixColorDelay() to colour the stop by how far ahead of / behind its latest time it will arrive. The value is arv − latest in seconds, negative meaning before latest; the first band whose threshold is above it wins, and a null threshold catches the rest. Default for project stops: [{green:-1800},{lightBlue:0},{orange:1800},{red:3600},{magenta:null}] — green until 30 min before latest, light blue for the last 30 min, then orange, red and magenta for each 30 min late. Resource stops: [{yellow:0},{orange:null}].
-
nextStateColorsobject - 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.
-
addStopaddStopObject - 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.
-
morphStopstopParamsKey - Stop types this stop may be morphed into.
-
templatesobject - Predefined stop templates available for this type.
-
tagstring - 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.
-
checkspec - 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.
addStop
Section titled “addStop”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.
-
tostopParamsKey - Stop types offered for a sequential delivery (`to`) child.
-
andstopParamsKey - Stop types offered for an unrelated concurrent (`and`) child.
-
withstopParamsKey - Stop types offered for a concurrent (`with`) child.
-
thenstopParamsKey - Stop types offered for a sequential follow-up (`then`) child.
-
assetstopParamsKey - Stop types offered for an asset (`asset`) child.
State presentation
Section titled “State presentation”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:
-
colorstring - Colour name (resolved via the client colour map) or expression (e.g. helixColorDelay()) for the marker in this state; "transparent" hides the dot.
-
statusstring - 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.
-
resColortrue | 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.
-
iconstring - Override the marker icon while the stop is in this state.
Terminal duties (position: FIRST / LAST)
Section titled “Terminal duties (position: FIRST / LAST)”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.
Device presence (comms)
Section titled “Device presence (comms)”Helix shows, on a resource’s current roster stop, which of the crew’s devices are reaching the
operation: each connected device draws its own glyph, and when nothing is connected the red not-connected
badge appears. It ships as a default, so a domain gets it with no authoring, and it is shown against the
resource once — on the head roster stop (the current one, with no after), never on its work stops or
the rest of the roster chain.
Not connected is the default. A resource with no device shows the badge straight away — you do not wait for it to have once connected and then dropped. It is an alert, on NOW and STBY only (on duty or on standby, the crew should be reachable). REST carries no badge — a resting resource’s app is expected to be off, so it is never flagged; a device that does connect while resting still draws its glyph. A device connecting clears the badge and draws its own glyph in its place.
Presence is a typed set of connections, not a flag: an engineer on a phone and a tablet shows both
glyphs, and only the empty set is “not connected”. It reads from resource.comms, a Helix-owned field the
platform maintains from the live dispatch connections (never sent to Spiral, and an incoming value is
silently dropped — so a client that reads a resource and writes it straight back is untouched by it);
nothing in the domain writes it.
-
devices{ [type]: { icon, color } } - Maps a device type ("phone", "tablet", "laptop", "vehicle", …) to the glyph it draws. The type is declared by the client app; one the map does not name draws the "unknown" glyph. Add your own types here, or re-icon the defaults — core never needs to know a type name.
-
nocomms{ after, highlight } - The not-connected badge. `highlight` is a `{ icon, color }`, red by default. `after` is a grace period in seconds (default 90 — one reconnect window) that applies ONLY to a device that connected and then dropped, so a brief blip does not flash; a resource that has never connected shows the badge at once. Present on NOW and STBY by default and ABSENT on REST (a resting resource is not flagged). Set nocomms to null to keep the device glyphs but drop the badge.
The default is authored per roster block — the same devices map. NOW and STBY carry the nocomms alert;
REST omits it (device glyphs only):
// NOW and STBY:"comms": { "devices": { "phone": { "icon": "dev-phone" }, "tablet": { "icon": "dev-tablet" }, "laptop": { "icon": "dev-laptop" }, "vehicle": { "icon": "dev-vehicle" }, "unknown": { "icon": "dev-unknown" } }, "nocomms": { "after": 90, "highlight": { "icon": "nocomms", "color": "red" } }}// REST: the same `devices` map, with no `nocomms` block.Override it per resource type at resourceParams.<type>.rosterStop.<NOW|STBY|REST>.helix.comms — null
there disables presence for that roster type; "nocomms": null keeps the device glyphs but not the badge.
A simulation or demo with no engineer apps will show every rostered resource as not connected, so disable
it there (comms: null, or nocomms: null) if that is noise.
Spiral’s alerts (info)
Section titled “Spiral’s alerts (info)”Spiral writes info on an output stop when something about its current placement is off — the stop is
outside the resource’s current area, an overtime offer would reach the crew too late, a skill is missing.
It is an array of the illegalReason codes, or null when
there is nothing to say, and it changes as the plan changes (see
stopObject). Helix shows it in three places:
- the stop editor: an amber triangle beside the stop in the hierarchy — hover it and the reasons appear
in amber, each with Spiral’s code after it; the Schedule section’s
inforow reads the same lines; - the list row and the map pin: the same triangle in the highlight badge, with the reasons as hover text;
- the editor title bar: the badge again, for the stop being edited.
The wording is the domain’s. helix.info.text maps each illegalReason code to the line shown, so a
client controls the language and the business nuance of every reason — the same home as the warning
templates. Helix ships a default line for each code; a type’s own entries merge over them one code at a
time, so authoring one line changes one reason and keeps the rest. A value may be a plain string or a
language map ({ "en": "…", "fr": "…" }), picked as the reply templates are. A code with no line is shown
as the code itself, so nothing Spiral says is lost. Every non-empty info raises the indicator — the
voluntary-overtime codes included, since they are the ones that say why a stop sits where it does.
-
highlight{ icon, color } - The glyph and its colour. The default is the code-drawn `alert` triangle in amber (#f59e0b); name another glyph to re-icon it, or a client may ship an `alert.png` of its own.
-
text{ [code]: string | { [lang]: string } } - The line shown for each illegalReason code — on the editor’s hover, in its Schedule row, and as the row and pin glyph’s hover text. Merged over Helix’s defaults per code; a language map picks the operator’s language, then `en`.
The default, shipped on every stop floor and every roster block (the text map holds one line per code;
a few shown):
"info": { "highlight": { "icon": "alert", "color": "#f59e0b" }, "text": { "CURRENT_AREA": "The stop is outside the resource's current working area", "VOLUNTARY_OFFER_TOO_LATE": "The overtime offer would reach the resource too late", "SKILL_MISSING": "The resource lacks a skill this stop needs", "EXCEEDS_SHIFT_PERIOD": "This stop would run past the end of the resource's shift" }}A client rewording one reason for its own operation, in its own words:
{ "clone":"projectStop", "key":"RCY", "helix": { "info": { "text": { "CURRENT_AREA": "Outside the patrol's patch — check the area before allocating" } } } }Override it per stop type at stopParams.<type>.helix.info, and for roster stops at
resourceParams.<type>.rosterStop.<NOW|STBY|REST>.helix.info; null disables the row and pin glyph for that
type (the editor’s mark always shows). The unprod floor ships highlight: null, so a dedicated-activity type
draws the row and pin glyph only once it authors a highlight expression, for example
"highlight": "helixHighlight(\"Lock\")". This is a different thing from helix.warning: those are the
templates for a refused mutation’s reply, and carry attributes; info carries codes alone.
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.
The resource’s colour (resColor)
Section titled “The resource’s colour (resColor)”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 influencetrue 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).