Path conventions
rule-line uses two different kinds of path, and they are not the same thing. One addresses a position inside a ruleset document; the other addresses a step's position inside the step tree. Keeping them distinct is deliberate.
Diagnostic location dot-path
A diagnostic location points at a position inside the ruleset document — it
is what a diagnostic's path field carries so an editor can jump to the exact
field that produced the problem.
It is built from dots and bracketed indices:
- A field name that looks like a plain identifier is joined with a dot:
steps[2].formulalocates theformulafield of the third step. - An array position is a bracketed integer:
steps[2]is the third step. - A segment containing a dot, a bracket, or a quote is bracket-quoted using
JSON string escaping, so it round-trips unambiguously:
input["odd.key"]locates the field literally namedodd.key, and a field name that itself contains a quote is escaped the way JSON escapes it, for exampleinput["he said \"hi\""].
Worked examples
| Location | Reads as |
|---|---|
steps[2].formula | the formula field of the third step |
input["odd.key"] | the input field literally named odd.key |
input["he said \"hi\""] | the input field literally named he said "hi" |
Why the escape set exists
Fixed DSL keys — steps, formula, type, and the rest — can never contain a
dot, so for them a plain dotted path is always unambiguous. But a tenant's own
input-schema field names are arbitrary strings and can contain a dot, a
bracket or a quote. Bracket-quoting exactly those segments is what keeps
input["odd.key"] from being misread as an input object with an odd field
and a key sub-field. The forward slash / is deliberately not part of the
escape set — see below.
Step path
A step path addresses a step's position in the tree. Unlike a location, it is derived from tree position rather than authored, and it is never written back into the ruleset document — it exists only in the trace, where it must stay fixed after the fact.
Step-path segments are slugs joined with a forward slash:
checkout/items/validate-total— thevalidate-totalstep, nested inside theitemsstep, inside thecheckoutstep.- Inside a
foreach, a runtime occurrence gains a bracketed iteration index on the iterating segment:invoice/positions[3]/compute-vataddresses thecompute-vatstep as it ran on the fourth ([3]) iteration of thepositionsloop.
Worked examples
| Step path | Reads as |
|---|---|
checkout/items/validate-total | a structural path — the step's fixed position, no iteration |
invoice/positions[3]/compute-vat | a runtime occurrence — the step as it ran on iteration [3] of the positions foreach |
Why / was chosen
The slash was chosen precisely so a step reference can never be mistaken for a
field traversal: a dot-path never uses / as a separator, so a step path
embeds cleanly inside a dot-path location without any escaping. JSON string
escaping does not escape / either, so this falls out of the encoding rather
than needing a special case.
Derived step paths are never stored in the ruleset document. A structural path is recomputed from tree position whenever it is needed; a runtime occurrence path is recorded only in the trace of an actual run.