Skip to content

Reason tags

Leon Starr edited this page Aug 4, 2026 · 2 revisions

Both non-transition responses — Ignore and Can't Happen — take an explanation. Most reasons are used once and are simply written where they apply:

can't happen
    Door closed
        Door is opening, so it should not detect ‘closed’.

The explanation is free text indented one level below the event name. It may run to as many lines as it needs; the parser joins them into a single paragraph, so the line breaks are yours to place wherever the source reads best.

Reusing a reason

Some reasons apply in several states, and repeating the prose invites the copies to drift apart. Label the reason once with a tag in angle brackets, at the start of its explanation:

state OPENING
...
can't happen
    Unlock
        <Cabin not moving> Cabin is not able to progress to a state where it can send this event while we are
        opening the door.

Every other state then names the tag alone:

state CLOSING
...
can't happen
    Unlock
        <Cabin not moving>

Since a reference carries no text of its own, it may also ride on the event line:

state COMPLETED
...
can't happen
    Cabin arrived <Transfer synch>
    Cancel <Transfer synch>

Both forms mean the same thing. The compact one earns its keep in a state whose responses are all references, where the indented lines would otherwise be a column of lone tags.

The grammar:

response = INDENT event_name (SP tag)? EOL* explanation?
explanation = expl_line+
expl_line = INDENT INDENT tag? expl_text EOL
expl_text = r'[^\n]*'
tag = '<' r'[^>\n]+' '>' SP*

Note that only the indented form can define a tag, because a definition needs somewhere to put its text.

A tag is any text between angle brackets, so spaces are fine and reasons can be named in plain English. Tags are scoped to the file. The occurrence carrying explanatory text defines the tag; an occurrence with a tag and nothing after it is a reference. Definition and reference may appear in either order and in any state, and a tag may cross between the ignore and can't happen sections when the same reason justifies both.

Conventions worth following

  • Tag only what you reuse. A reason used once reads better as plain prose with no tag at all. In the Door lifecycle, eleven reasons are tagged and nine are one-offs left untagged.
  • Avoid apostrophes in tag names. Tags match as literal text, so a straight ' typed against a curly silently fails to resolve. <Closed door cannot be blocked> sidesteps the problem that <Can't block a closed door> invites.
  • One reason, one tag. If two states need subtly different justifications, write two reasons. Sharing a tag across situations that are only roughly similar is how a reason ends up too vague to be worth reading.

Why tags rather than numbered footnotes

The state transition tables these files replaced used numbered comments — CH-14, IGN-4 — collected in a separate table. That works on paper but inserting a comment renumbers everything after it, and the numbers mean nothing at the point of use. A tag reads as its own explanation at every reference, and a generated table can still number the reasons however it likes at render time.

What the parser gives you

Each Response_a carries tag and explanation. A definition has both; a reference has a tag and an empty explanation; an untagged one-off has an explanation and tag set to None. Resolving a reference means looking up the tag among the definitions:

reason = r.explanation if r.explanation else tags[r.tag]

Clone this wiki locally