colregs 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -11
- package/data/applicability.json +478 -503
- package/data/facts.json +22 -22
- package/data/geometry.json +8 -8
- package/data/i18n/en.json +27 -0
- package/data/i18n/fi.json +25 -0
- package/data/images.json +32 -32
- package/data/operations.json +79 -0
- package/data/rules.json +5 -0
- package/data/version.json +1 -1
- package/docs/adr/0006-json-schema-and-identifier-diff.md +2 -0
- package/docs/adr/0011-api-shape.md +7 -7
- package/docs/adr/0012-trace-and-rule2-departure-api.md +2 -2
- package/docs/adr/0014-engine-interface-owned-by-colregs.md +103 -0
- package/docs/adr/0015-rule-ids-are-paragraph-keys.md +191 -0
- package/docs/adr/0016-encounter-roles-are-pooled-across-frames.md +82 -0
- package/docs/adr/0017-closed-vocabularies-are-prefixed-identifiers.md +105 -0
- package/docs/budgets.json +8 -15
- package/docs/decisions.md +6 -0
- package/docs/identifiers.md +121 -109
- package/docs/maritime-sources.md +58 -0
- package/docs/normative-language.md +103 -0
- package/docs/part-b-invariants.md +48 -45
- package/docs/requirements.md +147 -138
- package/fixtures/applicability-fixtures.json +190 -190
- package/fixtures/situation-fixtures.json +870 -599
- package/package.json +1 -1
- package/schema/applicability-fixtures.schema.json +4 -13
- package/schema/applicability.schema.json +77 -77
- package/schema/conduct-evaluation.schema.json +135 -0
- package/schema/display-evaluation.schema.json +146 -0
- package/schema/encounter-evaluation.schema.json +109 -0
- package/schema/evaluation.schema.json +149 -0
- package/schema/fact-record.schema.json +30 -0
- package/schema/facts.schema.json +4 -4
- package/schema/i18n-catalog.schema.json +47 -0
- package/schema/operations.schema.json +124 -0
- package/schema/rule2-departure-finding.schema.json +72 -0
- package/schema/rule2-departure-model.schema.json +159 -0
- package/schema/situation-fixtures.schema.json +30 -127
- package/schema/situation.schema.json +72 -0
- package/schema/trace.schema.json +33 -0
- package/data/deprecated-identifiers.json +0 -7
- package/schema/deprecated-identifiers.schema.json +0 -29
package/docs/identifiers.md
CHANGED
|
@@ -17,10 +17,23 @@ citation: `27(a)(i)` is what a mariner, a lawyer and a court all write, and
|
|
|
17
17
|
what a consumer stores when it records why a light was shown. Prefixing it
|
|
18
18
|
would put a package-local token in front of a reference that belongs to the
|
|
19
19
|
Convention rather than to this repository, and would make a stored citation
|
|
20
|
-
unreadable outside the tool that stored it.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
unreadable outside the tool that stored it. Rule ids sit beside this class
|
|
21
|
+
rather than in it: `rule:13b` is *shaped* like the cite it was minted from,
|
|
22
|
+
but it is a name in a namespace and a consumer reads the paragraph out of
|
|
23
|
+
`cite`, never out of the id (ADR 0015).
|
|
24
|
+
Paragraph-keying is argued in ADR 0001 and required by REQ-MODEL-4; nothing
|
|
25
|
+
here reopens either.
|
|
26
|
+
|
|
27
|
+
**Jurisdiction values sit beside paragraph paths in this class, and for the
|
|
28
|
+
same reason.** `intl` and `us/inland` are not names this package coined:
|
|
29
|
+
jurisdiction is a coordinate with REQ-SCOPE-2's own `<body>/<waters>`
|
|
30
|
+
grammar, its left segment borrowed from ISO 3166, the whole value doubling
|
|
31
|
+
as a corpus key and a `data/text/` filesystem path — its sibling axis,
|
|
32
|
+
`language`, is a bare BCP 47 tag for the same reason. A jurisdiction value
|
|
33
|
+
is immutable under REQ-MODEL-10 like any identifier here — renaming
|
|
34
|
+
`us/inland` would break every stored provenance and corpus path — it just
|
|
35
|
+
carries no prefix, because the grammar that owns it already keeps it stable
|
|
36
|
+
and collision-free (ADR 0017).
|
|
24
37
|
|
|
25
38
|
**Vocabulary identifiers carry a type prefix.** These names are this
|
|
26
39
|
package's own — nothing in COLREGS calls anything `masthead` or `nuc`. They
|
|
@@ -29,16 +42,25 @@ collided in it: `towing` was simultaneously a light id (Rule 21(d)) and an
|
|
|
29
42
|
`activity` value (Rule 24(a)), so a consumer holding the string `towing`
|
|
30
43
|
could not say what it was a name *for* without knowing which field it came
|
|
31
44
|
out of. The prefix makes the namespace part of the identifier, which
|
|
32
|
-
resolves that collision by construction rather than by convention.
|
|
45
|
+
resolves that collision by construction rather than by convention. The same
|
|
46
|
+
shape recurs inside the closed vocabularies themselves: `shall-not-impede`
|
|
47
|
+
names both a modality and a role, and `none` names both a role and an
|
|
48
|
+
encounter — resolved the identical way, `modality:shall-not-impede` and
|
|
49
|
+
`role:shall-not-impede` being two names rather than one (ADR 0017).
|
|
33
50
|
|
|
34
51
|
## The scheme
|
|
35
52
|
|
|
36
53
|
| form | class | examples |
|
|
37
54
|
|---|---|---|
|
|
55
|
+
| `rule:<paragraph-slug>` | applicability entries (`data/applicability.json`) | `rule:30a`, `rule:24a_i:exceeds_200m`, `rule:15a:keep_out_of_the_way` |
|
|
38
56
|
| `light:<id>` | light definitions (`data/lights.json`) | `light:masthead`, `light:sidelight_starboard`, `light:all_round` |
|
|
39
57
|
| `fact:<key>` | fact keys — the input vocabulary (`data/facts.json`) | `fact:activity`, `fact:length_m`, `fact:making_way`, `fact:on_mooring_buoy` |
|
|
40
58
|
| `<fact>:<value>` | values of an enumerated fact | `activity:nuc`, `position:anchored`, `propulsion:sail`, `obstruction_side:port` |
|
|
41
59
|
| `rel:<name>` | the five relation verbs (`data/applicability.json`) | `rel:includes`, `rel:in_lieu_of`, `rel:exempts` |
|
|
60
|
+
| `modality:<value>` | modality values (`data/applicability.json` `modalities`) | `modality:shall`, `modality:may` |
|
|
61
|
+
| `role:<value>` | effect role values (`data/applicability.json` `effects.roles`) | `role:give-way`, `role:none` |
|
|
62
|
+
| `encounter:<value>` | effect encounter values (`data/applicability.json` `effects.encounters`) | `encounter:head-on`, `encounter:none` |
|
|
63
|
+
| `category:<value>` | entry category values (`data/applicability.json` `categories`) | `category:precedence`, `category:display` |
|
|
42
64
|
|
|
43
65
|
The prefix names the namespace the identifier lives in. For a fact *value*
|
|
44
66
|
that namespace is the fact itself, written bare: `activity:nuc`, not
|
|
@@ -69,9 +91,9 @@ for a better idea, logging the change. What would settle it: the first
|
|
|
69
91
|
two-subject entry — Rule 18 — actually being written against it. This
|
|
70
92
|
section answers `Q-28`.
|
|
71
93
|
|
|
72
|
-
A `display` entry reads one vessel. A `classification` or
|
|
73
|
-
entry reads two, and needs to say *whose*
|
|
74
|
-
is three segments:
|
|
94
|
+
A `category:display` entry reads one vessel. A `category:classification` or
|
|
95
|
+
`category:precedence` entry reads two, and needs to say *whose*
|
|
96
|
+
`fact:activity` it means. The form is three segments:
|
|
75
97
|
|
|
76
98
|
```
|
|
77
99
|
<subject>:<class>:<key>
|
|
@@ -79,14 +101,14 @@ is three segments:
|
|
|
79
101
|
|
|
80
102
|
| segment | values |
|
|
81
103
|
|---|---|
|
|
82
|
-
| subject | `
|
|
104
|
+
| subject | `self`, `other`, `pair` |
|
|
83
105
|
| class | `fact`, `kin`, `geo`, `hist` |
|
|
84
106
|
| key | the identifier as it already exists, or a new one in a new class |
|
|
85
107
|
|
|
86
|
-
`
|
|
87
|
-
`
|
|
108
|
+
`self:fact:activity`, `other:kin:heading_deg`, `pair:geo:in_sight`,
|
|
109
|
+
`self:hist:was_overtaking`.
|
|
88
110
|
|
|
89
|
-
**A key with no subject segment means `
|
|
111
|
+
**A key with no subject segment means `self:`.** This is the whole of the
|
|
90
112
|
backward-compatibility story and it is why the subject is a *prefix* rather
|
|
91
113
|
than a change to the fact keys. `fact:activity` still spells `fact:activity`
|
|
92
114
|
and still denotes what it always denoted, so every predicate in
|
|
@@ -94,14 +116,14 @@ and still denotes what it always denoted, so every predicate in
|
|
|
94
116
|
`fixtures/applicability-fixtures.json` and every stored citation a consumer
|
|
95
117
|
holds stays correct unedited — `REQ-MODEL-10` is satisfied by construction
|
|
96
118
|
rather than by a migration. The alternative shapes were a suffix
|
|
97
|
-
(`fact:activity:
|
|
119
|
+
(`fact:activity:self`), which buries the thing you are scanning for at the
|
|
98
120
|
end of a variable-length name, and per-subject fact keys
|
|
99
121
|
(`fact:own_activity`), which would double the fact vocabulary and repoint
|
|
100
122
|
nothing but would leave two names for one concept forever. Prefixing is the
|
|
101
123
|
only one of the three where the existing vocabulary is a strict subset of
|
|
102
124
|
the new one.
|
|
103
125
|
|
|
104
|
-
The cost, stated so nobody rediscovers it: `
|
|
126
|
+
The cost, stated so nobody rediscovers it: `self`, `other` and `pair` are now
|
|
105
127
|
reserved at the head of the identifier space, and no fact, light or relation
|
|
106
128
|
may ever be named one of them. That is the price of a subject segment that
|
|
107
129
|
is not itself prefixed, and it is cheap — the three words are not candidate
|
|
@@ -109,9 +131,9 @@ names for anything this package models.
|
|
|
109
131
|
|
|
110
132
|
### The three subjects
|
|
111
133
|
|
|
112
|
-
`
|
|
134
|
+
`self` is the vessel the rule addresses; `other` is the vessel it is in an
|
|
113
135
|
encounter with. **`pair` is the encounter itself**, and it exists because
|
|
114
|
-
some facts belong to neither vessel: range is one number, not
|
|
136
|
+
some facts belong to neither vessel: range is one number, not self's number
|
|
115
137
|
and the other's. Putting `geo:range_m` under both subjects would create two
|
|
116
138
|
identifiers for one quantity and a class of bug — the two disagreeing —
|
|
117
139
|
that has no meaning.
|
|
@@ -122,21 +144,21 @@ Geometry splits on whether the quantity is symmetric between the vessels:
|
|
|
122
144
|
|
|
123
145
|
| fact | subject | |
|
|
124
146
|
|---|---|---|
|
|
125
|
-
| `geo:rel_bearing_deg` | `
|
|
147
|
+
| `geo:rel_bearing_deg` | `self` / `other` | bearing of the *other* subject, clockwise from this subject's heading |
|
|
126
148
|
| `geo:range_m` | `pair` | |
|
|
127
149
|
| `geo:bearing_change_deg_min` | `pair` | Rule 7(d)(i)'s steady bearing |
|
|
128
150
|
| `geo:cpa_m`, `geo:tcpa_s` | `pair` | |
|
|
129
151
|
| `geo:in_sight` | `pair` | Rule 3(k), symmetric because the rule defines it that way |
|
|
130
152
|
|
|
131
153
|
The directional row is where the namespace earns its keep.
|
|
132
|
-
`
|
|
133
|
-
off
|
|
154
|
+
`self:geo:rel_bearing_deg` is relative bearing — where the other vessel is
|
|
155
|
+
off self's bow. `other:geo:rel_bearing_deg` is the same fact read from the
|
|
134
156
|
other side, which is **aspect**. So aspect gets no identifier of its own: it
|
|
135
157
|
is a subject swap, not a second fact. Rule 13(b)'s overtaking sector is then
|
|
136
|
-
`other:geo:rel_bearing_deg` in (112.5, 247.5) —
|
|
158
|
+
`other:geo:rel_bearing_deg` in (112.5, 247.5) — self more than 22.5° abaft
|
|
137
159
|
the other vessel's beam — written once, in the units the rule itself uses.
|
|
138
|
-
Swapping `
|
|
139
|
-
which is exactly the operation a `precedence` rule needs and the reason to
|
|
160
|
+
Swapping `self` and `other` throughout a predicate reverses the encounter,
|
|
161
|
+
which is exactly the operation a `category:precedence` rule needs and the reason to
|
|
140
162
|
prefer a subject namespace over two parallel vocabularies.
|
|
141
163
|
|
|
142
164
|
The directional and the pair geometry are redundant with `kin:` wherever both
|
|
@@ -150,7 +172,7 @@ and a record that omits the kinematics is unchecked rather than wrong.
|
|
|
150
172
|
|
|
151
173
|
`kin:` is the kinematic class ADR 0005 introduces — `kin:position`,
|
|
152
174
|
`kin:heading_deg`, `kin:sog_kn`, `kin:rot_deg_min`, `kin:dynamics`. It takes
|
|
153
|
-
`
|
|
175
|
+
`self`/`other` only; there is no kinematic state of a pair. `kin:dynamics`
|
|
154
176
|
is an enumerated fact, so its values follow the bare-fact-name rule above:
|
|
155
177
|
`dynamics:tanker`, not `kin:dynamics:tanker`.
|
|
156
178
|
|
|
@@ -161,12 +183,12 @@ overtaking, a subsequent alteration of the bearing does not make her a
|
|
|
161
183
|
crossing vessel; the instantaneous geometry, read alone, says otherwise and
|
|
162
184
|
hands the duty to the wrong vessel. So the latch is a fact:
|
|
163
185
|
|
|
164
|
-
- `
|
|
186
|
+
- `self:hist:was_overtaking` — this subject was, earlier in this encounter,
|
|
165
187
|
an overtaking vessel with respect to the other.
|
|
166
|
-
- `
|
|
188
|
+
- `self:hist:latched_at_s` — how long ago that attached, for a `category:conduct`
|
|
167
189
|
monitor. A predicate at a point does not read it.
|
|
168
190
|
|
|
169
|
-
History is directional — it is *
|
|
191
|
+
History is directional — it is *self* that was overtaking — so it takes a
|
|
170
192
|
subject segment like the fact record does, and never `pair`.
|
|
171
193
|
|
|
172
194
|
### What this does not do
|
|
@@ -180,41 +202,41 @@ keys would have broken.
|
|
|
180
202
|
## Effects `✎`
|
|
181
203
|
|
|
182
204
|
**Pencil** (`docs/conventions.md`): ADR 0005 puts the whole two-subject shape
|
|
183
|
-
there. What would settle it: a second family of `precedence` paragraphs —
|
|
205
|
+
there. What would settle it: a second family of `category:precedence` paragraphs —
|
|
184
206
|
Rules 12, 14 and 15 — written against it. **Written, and it held with one
|
|
185
|
-
addition**: Rules 7(d) and 13–15 are the first `classification` entries, and a
|
|
207
|
+
addition**: Rules 7(d) and 13–15 are the first `category:classification` entries, and a
|
|
186
208
|
classification produces neither a role nor a section, so the table below grows
|
|
187
|
-
a third row. Rule 12 turned out to be `precedence` and not `classification`
|
|
209
|
+
a third row. Rule 12 turned out to be `category:precedence` and not `category:classification`
|
|
188
210
|
(below). This section answers the data half of `Q-27` and is required by
|
|
189
211
|
`REQ-CAT-8`.
|
|
190
212
|
|
|
191
|
-
A `display` entry produces `lights`. A `scope` or `precedence` entry produces
|
|
213
|
+
A `category:display` entry produces `lights`. A `category:scope` or `category:precedence` entry produces
|
|
192
214
|
an **effect**, and the shape of the effect is fixed by the category:
|
|
193
215
|
|
|
194
216
|
| category | effect |
|
|
195
217
|
|---|---|
|
|
196
|
-
| `scope` | `{"part", "section", "applies_rules"}` — which section of which Part governs, and the rules it contains |
|
|
197
|
-
| `precedence` | `{"
|
|
198
|
-
| `classification` | `{"encounter": <encounter>}` **or** `{"risk_of_collision": true}` — exactly one key |
|
|
218
|
+
| `category:scope` | `{"part", "section", "applies_rules"}` — which section of which Part governs, and the rules it contains |
|
|
219
|
+
| `category:precedence` | `{"self": <role>, "other": <role>}` — one role per subject |
|
|
220
|
+
| `category:classification` | `{"encounter": <encounter>}` **or** `{"risk_of_collision": true}` — exactly one key |
|
|
199
221
|
|
|
200
|
-
Five roles, a closed set: `give-way`, `stand-on`,
|
|
201
|
-
`keep-clear`, `none`. They are declared
|
|
202
|
-
`effects`, and
|
|
203
|
-
|
|
204
|
-
binds.
|
|
222
|
+
Five roles, a closed set: `role:give-way`, `role:stand-on`,
|
|
223
|
+
`role:shall-not-impede`, `role:keep-clear`, `role:none`. They are declared
|
|
224
|
+
in `data/applicability.json` under `effects`, and, like modality and
|
|
225
|
+
category, they are identifiers: prefixed closed vocabularies `REQ-MODEL-10`
|
|
226
|
+
binds (ADR 0017).
|
|
205
227
|
|
|
206
228
|
### Encounters, and why a classification effect has two shapes
|
|
207
229
|
|
|
208
|
-
Four encounters, a closed set like the roles: `head-on` (Rule 14),
|
|
209
|
-
(Rule 15), `overtaking` (Rule 13) and
|
|
210
|
-
`data/applicability.json` under
|
|
211
|
-
are
|
|
230
|
+
Four encounters, a closed set like the roles: `encounter:head-on` (Rule 14),
|
|
231
|
+
`encounter:crossing` (Rule 15), `encounter:overtaking` (Rule 13) and
|
|
232
|
+
`encounter:none`. They are declared in `data/applicability.json` under
|
|
233
|
+
`effects.encounters` and, like the roles, they are identifiers.
|
|
212
234
|
|
|
213
|
-
A `classification` effect carries **exactly one key**, and which key it is
|
|
235
|
+
A `category:classification` effect carries **exactly one key**, and which key it is
|
|
214
236
|
depends on which question the paragraph answers. Rule 7(d)(i) answers *does
|
|
215
237
|
risk of collision exist* and produces `{"risk_of_collision": true}`; Rules 13,
|
|
216
238
|
14 and 15 answer *what kind of encounter is this* and produce an `encounter`.
|
|
217
|
-
ADR 0005 gives both questions to `classification` — "relative geometry,
|
|
239
|
+
ADR 0005 gives both questions to `category:classification` — "relative geometry,
|
|
218
240
|
history → encounter type, risk of collision" — and the two do not merge. A
|
|
219
241
|
single shape would have made every encounter entry state a risk it does not
|
|
220
242
|
decide, and 15(a)'s crossing test reads `pair:geo:risk_of_collision` as an
|
|
@@ -223,7 +245,7 @@ input rather than producing it.
|
|
|
223
245
|
There is no `{"risk_of_collision": false}` and there never will be. 7(a) makes
|
|
224
246
|
risk a judgement on all available means and deems it to exist in any doubt, so
|
|
225
247
|
an entry can add a ground for risk and nothing in this package can deny one.
|
|
226
|
-
`none` is declared
|
|
248
|
+
`encounter:none` is declared for the completeness of the vocabulary and
|
|
227
249
|
no entry produces it: an encounter type is asserted by a paragraph, and the
|
|
228
250
|
absence of one is the absence of an entry rather than an entry with a null
|
|
229
251
|
value.
|
|
@@ -239,17 +261,17 @@ subjects' bearings in half-degree steps and asserts exactly one encounter at
|
|
|
239
261
|
each of the 518 400 points; the Alloy version of the same property lives in
|
|
240
262
|
`colregs-engine`.
|
|
241
263
|
|
|
242
|
-
### Rule 12 is `precedence`, not `classification`
|
|
264
|
+
### Rule 12 is `category:precedence`, not `category:classification`
|
|
243
265
|
|
|
244
266
|
ADR 0005 §1 and the proposal's first-cut table file Rule 12 under
|
|
245
|
-
`classification`. It is `precedence` here, for the reason `Q-37` gives for
|
|
267
|
+
`category:classification`. It is `category:precedence` here, for the reason `Q-37` gives for
|
|
246
268
|
13(a): **12(a) produces a role, and a classification effect has nowhere to put
|
|
247
269
|
one.** "One of them shall keep out of the way of the other" is give-way and
|
|
248
270
|
stand-on in the effect vocabulary that already exists, and it is not an
|
|
249
271
|
encounter type — two sailing vessels meeting are still in a head-on, a
|
|
250
272
|
crossing or an overtaking, and Rule 12 says which of them gives way rather
|
|
251
273
|
than which kind of meeting it is. Rule 12 has no deeming paragraph at all:
|
|
252
|
-
12(b) defines the windward side and is a `definition`, so it is the cite on the
|
|
274
|
+
12(b) defines the windward side and is a `category:definition`, so it is the cite on the
|
|
253
275
|
`kin:wind_side` fact rather than an entry.
|
|
254
276
|
|
|
255
277
|
The category is `Q-14`'s to settle paragraph by paragraph and this is two more
|
|
@@ -257,88 +279,83 @@ of them; the departure from the table is recorded in ADR 0005's pencil log and
|
|
|
257
279
|
in `Q-40`.
|
|
258
280
|
|
|
259
281
|
**Who governs over Rule 12.** 12(a)'s subjects are "two sailing vessels",
|
|
260
|
-
which is 3(c), so
|
|
282
|
+
which is 3(c), so the three Rule 12 entries gate on `fact:propulsion` and not on the Rule
|
|
261
283
|
18 rank — a fishing vessel under sail is a sailing vessel. Where Rule 18 also
|
|
262
|
-
ranks the pair, its entry displaces Rule 12's:
|
|
284
|
+
ranks the pair, its entry displaces Rule 12's: the three 18(b) entries and the two 18(c) entries
|
|
263
285
|
carry `rel:overrides` against all three, because Rule 18's opening words
|
|
264
|
-
except Rules 9, 10 and 13 and nothing else. `13a` overrides them for the same
|
|
286
|
+
except Rules 9, 10 and 13 and nothing else. `rule:13a` overrides them for the same
|
|
265
287
|
reason it overrides Rule 18 — 13(a) is "notwithstanding" the rest of Sections
|
|
266
288
|
I and II. The test that pins the relation asserts both reasons from
|
|
267
289
|
`rules.json`, so the data cannot keep an override after losing the words.
|
|
268
290
|
|
|
269
291
|
**And over Rule 15, the same way.** 15(a)'s subjects are "two power-driven
|
|
270
|
-
vessels", which is 3(b), so `15a
|
|
292
|
+
vessels", which is 3(b), so `rule:15a:keep_out_of_the_way` gates on `fact:propulsion` and on
|
|
271
293
|
no Rule 18 rank either — a vessel engaged in fishing, or not under command,
|
|
272
294
|
whose machinery is in use is a power-driven vessel. It used to negate the four
|
|
273
295
|
ranks in its own predicate, which said the same thing in the one place a test
|
|
274
|
-
could not see the reason;
|
|
296
|
+
could not see the reason; the 18(a)(i)–(iii) entries, the two 18(c) entries and `rule:18f_i` now carry
|
|
275
297
|
`rel:overrides` against it instead. The derived half of the test is what makes
|
|
276
298
|
that checkable: a Rule 18 entry meets Rule 15 when it assigns a helm role and
|
|
277
299
|
neither subject is gated to a sailing vessel, and every such entry must carry
|
|
278
300
|
the override, so a Rule 18 paragraph added later cannot join Rule 15 silently.
|
|
279
301
|
|
|
280
|
-
**The effect names both subjects, and that is the point.** A `precedence`
|
|
281
|
-
entry is evaluated from
|
|
282
|
-
other vessel stands on. Writing only
|
|
302
|
+
**The effect names both subjects, and that is the point.** A `category:precedence`
|
|
303
|
+
entry is evaluated from self's side, so 18(a)(i) says self gives way *and* the
|
|
304
|
+
other vessel stands on. Writing only self's half would lose Rule 17, which
|
|
283
305
|
attaches to the counterpart of a give-way duty and to nothing else. So
|
|
284
|
-
`stand-on` appears only opposite `give-way`, and the counterpart of
|
|
285
|
-
`shall-not-impede` is always `none` — that is 8(f)(iii) in the data: a vessel
|
|
286
|
-
whose passage is not to be impeded acquires no privilege by it. `none` is
|
|
306
|
+
`role:stand-on` appears only opposite `role:give-way`, and the counterpart of
|
|
307
|
+
`role:shall-not-impede` is always `role:none` — that is 8(f)(iii) in the data: a vessel
|
|
308
|
+
whose passage is not to be impeded acquires no privilege by it. `role:none` is
|
|
287
309
|
written rather than omitted, because a norm that confers nothing on a subject
|
|
288
|
-
is a finding and not an absence: NUC against RAM is `none` on both sides, and
|
|
310
|
+
is a finding and not an absence: NUC against RAM is `role:none` on both sides, and
|
|
289
311
|
that is Rule 18's partial order rather than a gap in the table.
|
|
290
312
|
|
|
291
|
-
`keep-clear` is one role for the two duties 18(e) and 18(f)(i) impose together
|
|
313
|
+
`role:keep-clear` is one role for the two duties 18(e) and 18(f)(i) impose together
|
|
292
314
|
— keep well clear, and avoid impeding navigation. The vocabulary cannot
|
|
293
315
|
separate them and does not pretend to.
|
|
294
316
|
|
|
295
|
-
### Two-subject
|
|
317
|
+
### Two-subject rule ids
|
|
296
318
|
|
|
297
|
-
|
|
298
|
-
9(c), `
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
319
|
+
A two-subject entry is keyed on its paragraph like any other (below):
|
|
320
|
+
`rule:18a_i` is 18(a)(i), `rule:9c` is 9(c), `rule:8f_iii` is 8(f)(iii). No
|
|
321
|
+
subject segment appears in an id: every entry is evaluated from self's side,
|
|
322
|
+
and where the paragraph classifies the pair rather than one vessel the entry
|
|
323
|
+
reads both subjects inside one predicate — `rule:13b` is 13(b) whichever
|
|
324
|
+
vessel is coming up. Where a paragraph's subject is disjunctive — 9(b) is
|
|
325
|
+
"a vessel of less than 20 metres in length **or** a sailing vessel" —
|
|
326
|
+
`any_of` carries the disjunction inside one entry, `rule:9b`.
|
|
305
327
|
|
|
306
|
-
##
|
|
328
|
+
## Rule ids
|
|
307
329
|
|
|
308
|
-
An entry id is
|
|
309
|
-
with
|
|
310
|
-
|
|
330
|
+
An entry id is a paragraph key in the `rule:` namespace: `rule:` plus the
|
|
331
|
+
cite with its punctuation dropped. ADR 0015 (Solace, 2026-09-16) made the
|
|
332
|
+
change and carries the table from the ids it replaced; the rule for minting
|
|
333
|
+
a new one is here.
|
|
311
334
|
|
|
312
|
-
| paragraph
|
|
313
|
-
|---|---|
|
|
314
|
-
| Rule 28 (one paragraph) | `28` |
|
|
315
|
-
| `23(b)` | `23b` |
|
|
316
|
-
| `25(d)(ii)` | `25d2` |
|
|
317
|
-
| `23(a)(iii)`–`(iv)`, one entry | `23a34` |
|
|
318
|
-
|
|
319
|
-
Where one paragraph produces more than one entry, a hyphenated suffix names
|
|
320
|
-
what distinguishes them. The suffixes are **not** drawn from a single
|
|
321
|
-
scheme, because the paragraphs they split do not divide on a single axis:
|
|
322
|
-
|
|
323
|
-
| suffix | means | example |
|
|
335
|
+
| paragraph | rule id | why that id |
|
|
324
336
|
|---|---|---|
|
|
325
|
-
|
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
|
|
|
329
|
-
|
|
|
330
|
-
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
337
|
+
| 30(a) | `rule:30a` | the bare slug of the cite: rule number, paragraph letter attached |
|
|
338
|
+
| 27(a)(i) | `rule:27a_i` | each roman subparagraph joined with `_` |
|
|
339
|
+
| 23(a)(iii)-(iv) | `rule:23a_iii_iv` | a span of subparagraphs, joined the same way |
|
|
340
|
+
| 24(a)(i), tow over 200 m | `rule:24a_i:exceeds_200m` | a further norm out of the same paragraph, named in the text's own words |
|
|
341
|
+
| 15(a), the duty | `rule:15a:keep_out_of_the_way` | which half of a fused deeming-and-duty sentence this entry carries |
|
|
342
|
+
| 30(a), US inland | `rule:30a:mooring_buoy` | a jurisdiction delta named by its difference; the jurisdiction stays a field |
|
|
343
|
+
|
|
344
|
+
A bare id is the paragraph's principal norm. A third segment is added only
|
|
345
|
+
where the text yields a second norm from the same paragraph, and it is named
|
|
346
|
+
in the words of the text, not in what the entry produces. A number may appear
|
|
347
|
+
there only when the Convention states the threshold itself (`exceeds_200m` is
|
|
348
|
+
24(a)(i)'s).
|
|
349
|
+
|
|
350
|
+
**The id is opaque.** It looks like a citation and it is not one: a consumer
|
|
351
|
+
that wants "Rule 24(a)(i)" reads `cite`, and never splits an id to find a
|
|
352
|
+
paragraph. `cite` is the field that moves when the package reads the Rules
|
|
353
|
+
better — `14a` became `14b` once already — and the id resembling it is a
|
|
354
|
+
convenience for the human reading a trace, nothing the data promises. Two
|
|
355
|
+
entries may share a cite; they never share an id.
|
|
356
|
+
|
|
357
|
+
`represented_paragraphs` take the same prefix and the same shape
|
|
358
|
+
(`rule:2a` for 2(a)). They are not entries and nothing references them.
|
|
342
359
|
|
|
343
360
|
## Derived facts
|
|
344
361
|
|
|
@@ -393,11 +410,6 @@ implemented in the reference evaluator and asserted by the fixtures.
|
|
|
393
410
|
|
|
394
411
|
## What is not an identifier
|
|
395
412
|
|
|
396
|
-
- **Modality values** (`shall`, `may`, `shall-if-practicable`,
|
|
397
|
-
`conditional`, `exempt`) and **jurisdiction values** (`intl`,
|
|
398
|
-
`us/inland`) are their own closed vocabularies, defined in §2 of the
|
|
399
|
-
requirements and not part of the identifier space REQ-MODEL-10 binds. So are
|
|
400
|
-
the **role** and **encounter** values of an effect.
|
|
401
413
|
- **Constants** — `situation.constants` in `data/facts.json`: the numbers a
|
|
402
414
|
Part B predicate needs and the Rules do not always give
|
|
403
415
|
(`appreciable_bearing_change_deg_min`, `head_on_half_angle_deg`, the two
|
|
@@ -412,7 +424,7 @@ implemented in the reference evaluator and asserted by the fixtures.
|
|
|
412
424
|
data is addressed by. Only the values inside them can be identifiers, and
|
|
413
425
|
where they are (`also_activity` holds an activity value) they are prefixed.
|
|
414
426
|
`not` and `any_of` are the second pair of words reserved at the head of an
|
|
415
|
-
identifier space, after `
|
|
427
|
+
identifier space, after `self`/`other`/`pair`: they appear where a fact key
|
|
416
428
|
appears, so no fact may ever be named either. The cost is the same and as
|
|
417
429
|
cheap — every fact key carries a class prefix (`fact:`, `geo:`, `kin:`,
|
|
418
430
|
`hist:`, `env:`) and neither word could be one.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Maritime sources — case law and incident analysis
|
|
2
|
+
|
|
3
|
+
Decisions and commentary on how the Rules are conventionally read, and
|
|
4
|
+
collision forensics showing how they fail in the water.
|
|
5
|
+
|
|
6
|
+
A case is evidence that a reading is or is not conventional. It never becomes
|
|
7
|
+
a proposition in `docs/part-b-invariants.md`, which holds no maritime doctrine
|
|
8
|
+
by design. Consult these where a `Q-` in `docs/requirements.md` §11 turns on a
|
|
9
|
+
question the rule text leaves open.
|
|
10
|
+
|
|
11
|
+
## Case law
|
|
12
|
+
|
|
13
|
+
- **Monford Management Ltd v Afina Navigation Ltd ("KIVELI" c/w "AFINA I") [2025] EWHC 1185 (Admlty)**. Bryan J, Admiralty Court; permission to appeal refused, [2025] EWHC 1210. When a Section II classification arms and how long it persists — `Q-51` and `Q-52`. Held: Rule 14 applies once risk of collision arises, not on geometry alone, and risk of collision was found on Rule 7(d)(i) steady bearing plus an unsafe CPA; once armed, the classification persists until the risk has passed, unaffected by later course changes. The court rejected the submission that a head-on at C-22 had become a crossing by C-6 as the bearing opened. Reaches Rule 13 by analogy only; silent on overtaking.
|
|
14
|
+
<https://caselaw.nationalarchives.gov.uk/ewhc/admlty/2025/1185> · case note by Nigel Cooper KC, counsel for AFINA I: <https://www.quadrantchambers.com/sites/default/files/2025-05/avoiding_a_head-on_collision_-_it_is_not_just_about_the_side_lights.pdf>
|
|
15
|
+
- **Evergreen Marine (UK) Ltd v Nautical Challenge Ltd ("Ever Smart" / "Alexandra 1") [2021] UKSC 6**. Frames Section II as a scheme about steady-bearing *collision* situations, with Rule 13 inside that taxonomy ([56]–[57]); leans against treating an engaged rule as inapplicable ([68]); describes Rule 17's obligations as qualified stages, predicates on the current state, with keep-course-and-speed accommodating manoeuvres such as slowing to pick up a pilot ([61]–[62]). Not asked when Rule 13 arms, and did not decide it. Separately, at [60] and [66]–[67], settles `Q-23`'s asymmetry: Rule 2(a) is a standing responsibility clause that authorises nothing, while Rule 2(b) is a conjunctive test — special circumstance *and* immediate danger — and rejects Rule 2 as a gap-filler for the steering rules.
|
|
16
|
+
<https://caselaw.nationalarchives.gov.uk/uksc/2021/6>
|
|
17
|
+
- **Crowley Marine Services Inc. v. Maritrans Inc., 447 F.3d 719 (9th Cir. 2006)**. `Q-23`: the burden of justifying a Rule 2(b) departure falls on the departing vessel, and the departure must respond to an immediate danger already created — a pre-emptive departure does not qualify (n.6).
|
|
18
|
+
<https://cdn.ca9.uscourts.gov/datastore/opinions/2006/05/08/0435724.pdf>
|
|
19
|
+
|
|
20
|
+
## Commentary and guidance
|
|
21
|
+
|
|
22
|
+
- **Kemp — *When Do Collision Regulations Begin to Apply?* (Journal of Navigation)**. A judicial split on the antecedent question: some decisions hold the steering and sailing rules begin at risk of collision, others that they apply just before it, risk of collision being the thing to be avoided.
|
|
23
|
+
<https://www.cambridge.org/core/journals/journal-of-navigation/article/abs/when-do-collision-regulations-begin-to-apply/E6DBCD8A6ABC43FA88B5E6CB3ABF807C>
|
|
24
|
+
- **eCOLREGs — overtaking and crossing on the high seas**. States the broad reading of Rule 13(d) as conventional: an overtaking vessel "maintains overtaking status and cannot transition into a crossing or head-on situation until completely past and clear".
|
|
25
|
+
<https://advanced.ecolregs.com/index.php?option=com_k2&view=item&id=172>
|
|
26
|
+
- **Nautical Institute — *Action by the Stand-On Vessel*** (Seaways case study). The stand-on vessel's stages as taught; does not reach whether they are reversible.
|
|
27
|
+
<https://www.nautinst.org/resources-page/200115-action-by-the-stand-on-vessel.html>
|
|
28
|
+
- **USCG Navigation Rules (Amalgamated)**. <https://www.navcen.uscg.gov/navigation-rules-amalgamated>
|
|
29
|
+
|
|
30
|
+
## Marine incident analysis
|
|
31
|
+
|
|
32
|
+
Collision forensics. Radar misinterpretation, mismatched turn decisions and
|
|
33
|
+
ambiguous give-way/stand-on roles are what the Rules are written against.
|
|
34
|
+
|
|
35
|
+
- Garzke, Simpson — *The Loss of Andrea Doria: A Marine Forensic Analysis* (Marine Technology Society Journal 46(6), 2012). Reconstructs the 1956 Andrea Doria–Stockholm collision from radar, navigation and rules-of-the-road evidence.
|
|
36
|
+
<https://www.ingentaconnect.com/content/mts/mtsj/2012/00000046/00000006/art00008> · <https://onepetro.org/JSPD/article/26/02/98/172277/The-Loss-of-Andrea-Doria-A-Marine-Forensic>
|
|
37
|
+
- British Wreck Commissioner (Lord Mersey) — *Report on the Loss of the Titanic* (1912). Excessive speed through a known ice field despite wireless ice warnings — a Rule 6 case, not give-way/stand-on.
|
|
38
|
+
<https://www.titanicinquiry.org/BOTInq/BOTReport/botRep01.php>
|
|
39
|
+
- Halpern — *Strangers on the Horizon: Titanic and Californian – A Forensic Approach* (2019). Reconstruction of the Titanic–Californian near-encounter: lookout, distress-signal and stand-on/give-way failures. Book only.
|
|
40
|
+
<https://www.amazon.com/STRANGERS-HORIZON-Californian-Forensic-Approach/dp/1702121984>
|
|
41
|
+
- MAIB (for the Isle of Man Ship Registry) — *Report on the investigation of
|
|
42
|
+
the collision between the bulk carrier Polesie and the general cargo ship
|
|
43
|
+
Verity* (Report No 5/2026, February 2026). German Bight TSS, 24 October
|
|
44
|
+
2023; *Verity* sank with five fatalities. Analysis covers Rules 5, 6, 7, 8,
|
|
45
|
+
15, 16 and 17(a)(ii)/(b) only — it does not reach Rule 2(b), correcting an
|
|
46
|
+
earlier claim that it paired 17(b) with 2(b) at closest quarters.
|
|
47
|
+
<https://www.bahamasmaritime.com/wp-content/uploads/2026/02/2026-5-Polesie-Verity-ReportAndAnnexes.pdf>
|
|
48
|
+
- IMO GISIS Marine Casualties and Incidents module. Not a paper but a source class: the mandatory-reporting database of marine safety investigation reports. Ground truth for real COLREGS-relevant incidents.
|
|
49
|
+
<https://www.imo.org/en/OurWork/IIIS/Pages/Marine-Safety-Investigation-reports.aspx>
|
|
50
|
+
|
|
51
|
+
## Known gaps
|
|
52
|
+
|
|
53
|
+
- `Q-23`: *The Bywell Castle* and *Boy Andrew v St Rognvald* are unread, and no
|
|
54
|
+
case was found holding a Rule 2(b) departure justified on draught,
|
|
55
|
+
manoeuvrability, shoal water, a lee shore, set, visibility or sea state.
|
|
56
|
+
- Two standard texts — Cockcroft & Lameijer, *A Guide to the Collision Avoidance Rules*, and Farwell's *Rules of the Nautical Road* — are not online. Either may settle how the stages of a close-quarters encounter are divided, and whether they are treated as irreversible.
|
|
57
|
+
- Several of the questions in `docs/requirements.md` §11 appear unlitigated: overtaking geometry with no risk of collision, an overtaking situation becoming a head-on, resumption of course by a stand-on vessel that has acted, and the fate of accumulated Section II state across a visibility transition.
|
|
58
|
+
- BAILII refuses automated access. Use the National Archives Find Case Law service: <https://caselaw.nationalarchives.gov.uk/>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Normative language — how "shall", "may" and friends are used here
|
|
2
|
+
|
|
3
|
+
Status: **ink**, 2026-09-05. Reviewed and accepted by the maintainer; this is the
|
|
4
|
+
standing decision until an ADR supersedes it.
|
|
5
|
+
|
|
6
|
+
## The decision
|
|
7
|
+
|
|
8
|
+
Two vocabularies, kept apart on purpose:
|
|
9
|
+
|
|
10
|
+
1. **Our own requirements** (`docs/requirements.md` here, and any spec in
|
|
11
|
+
this repo or in colregs-engine) use **MUST / MUST NOT / SHOULD / SHOULD NOT / MAY** in capitals,
|
|
12
|
+
with the meaning given by [RFC 2119] as clarified by [RFC 8174]: only
|
|
13
|
+
the capitalised words carry that meaning. Lower-case "must" or "should" in our prose is
|
|
14
|
+
ordinary English.
|
|
15
|
+
2. **The data follows the Convention.** The `modality` field in
|
|
16
|
+
`data/applicability.json` holds a lower-case token derived from the
|
|
17
|
+
Convention's own verb for that paragraph: `shall`, `may`, `shall-not`,
|
|
18
|
+
`shall-if-practicable`, `shall-not-impede`, `conditional`, `exempt`.
|
|
19
|
+
These are *not* RFC 2119 keywords. They are normalised from treaty text,
|
|
20
|
+
and the verbatim paragraph sits next to them in `data/rules.json` so a
|
|
21
|
+
reader can check the token against the words. Two tokens are not verbs:
|
|
22
|
+
`conditional` means the verb itself turns on a fact, and the entry's
|
|
23
|
+
`modality_by` table says which verb applies when (Rule 23(a)(ii) is
|
|
24
|
+
`shall` at 50 m and above, `may` below); `exempt` means the paragraph
|
|
25
|
+
lifts a duty another paragraph imposes, named by `rel:exempts` (Rule
|
|
26
|
+
30(e) for small vessels at anchor). Use `conditional` only when the
|
|
27
|
+
Convention states the threshold in the paragraph; use `exempt` only when
|
|
28
|
+
the paragraph's verb is "shall not be required" or equivalent.
|
|
29
|
+
|
|
30
|
+
So a capitalised MUST is a claim about the package. A lower-case `shall` in
|
|
31
|
+
the data is a claim about what COLREGS says. Nothing in the repo maps one
|
|
32
|
+
onto the other.
|
|
33
|
+
|
|
34
|
+
## Where COLREGS will surprise an RFC reader
|
|
35
|
+
|
|
36
|
+
If you learned obligation words from RFCs, three things about the
|
|
37
|
+
Convention are counterintuitive. The data model follows the Convention,
|
|
38
|
+
not the RFC, on each.
|
|
39
|
+
|
|
40
|
+
- **There is no SHOULD tier.** RFC 2119 gives you a recommended-but-waivable
|
|
41
|
+
level. COLREGS uses "should" once in the Rules (8(b); the Annexes are not checked) and
|
|
42
|
+
nowhere defines it as a weaker rank of duty. Instead the Convention
|
|
43
|
+
**softens a duty with a condition on it, not with a weaker verb**:
|
|
44
|
+
"so far as possible", "if the circumstances of the case admit", "if
|
|
45
|
+
practicable". The data carries that as `shall-if-practicable`, a
|
|
46
|
+
qualified obligation, rather than inventing a `should`. One token covers
|
|
47
|
+
several phrasings ("so far as possible", "if the circumstances of the
|
|
48
|
+
case admit"); whether the exact qualifier deserves its own field beside
|
|
49
|
+
`modality` is question Q-31, still open.
|
|
50
|
+
- **`may` is a lawful alternative, not an optional extra.** In RFC 2119 a
|
|
51
|
+
MAY is something nobody may depend on. In COLREGS a `may` display is one
|
|
52
|
+
of several complete, lawful options, and the paragraph says how it
|
|
53
|
+
relates to the others. Rule 25(b) lets a small sailing vessel *combine*
|
|
54
|
+
the 25(a) lights into one masthead lantern, so it replaces 25(a) and the
|
|
55
|
+
data records that with `rel:in_lieu_of`. Rule 25(c) lets any sailing
|
|
56
|
+
vessel show red-over-green *in addition to* 25(a), so it is
|
|
57
|
+
`rel:includes`, and it may not be shown with the 25(b) lantern, so the
|
|
58
|
+
two `rel:excludes` each other. The data keeps every lawful option with
|
|
59
|
+
its own modality and never picks one.
|
|
60
|
+
- **`shall not impede` is its own kind of duty.** It has no RFC analogue.
|
|
61
|
+
It is weaker than "shall keep out of the way", and Rule 8(f) says the
|
|
62
|
+
other vessel keeps all her own duties too. Where a paragraph's verb is
|
|
63
|
+
"shall not impede" (Rules 9(b), 9(c), 10(i), 10(j)) that is its
|
|
64
|
+
`modality`; the same duty is also recorded as an `effect` on the vessel,
|
|
65
|
+
so a paragraph with a different verb can still impose it (Rule 18(d)(i)).
|
|
66
|
+
|
|
67
|
+
`shall not` (a prohibition) and `shall` (an obligation) mean what an RFC
|
|
68
|
+
reader expects.
|
|
69
|
+
|
|
70
|
+
## What we defer to, and for what
|
|
71
|
+
|
|
72
|
+
| Question | Defer to |
|
|
73
|
+
|---|---|
|
|
74
|
+
| What MUST / SHOULD / MAY mean in our own specs | [RFC 2119], [RFC 8174]; the W3C's [RfcKeywords] page shows how other standards cite them |
|
|
75
|
+
| What `shall` / `may` / `shall not` mean in the data | The paragraph text in `data/rules.json`; no external standard redefines it |
|
|
76
|
+
| Which edition of COLREGS, and which amendments, the data encodes | `docs/adr/0001` and the provenance requirements (`REQ-PROV-*`); this note says nothing about editions |
|
|
77
|
+
| How standards bodies read `shall`/`should`/`may`/`can` in their own documents | [ISO/IEC Directives, Part 2], Clause 7. Not adopted here; listed because marine-standards readers will assume it |
|
|
78
|
+
|
|
79
|
+
The gap between ISO's `should` (a recommendation) and COLREGS's conditional
|
|
80
|
+
`shall` is exactly the first surprise above. None of this is legal advice:
|
|
81
|
+
the data records what the text says, not how a court would read it.
|
|
82
|
+
|
|
83
|
+
## For reviewers
|
|
84
|
+
|
|
85
|
+
A pull request that adds or changes an entry is held to this note. Check:
|
|
86
|
+
|
|
87
|
+
- the `modality` token matches the paragraph's own verb, and a
|
|
88
|
+
practicability phrase ("so far as possible", "if the circumstances of the
|
|
89
|
+
case admit", "if practicable") becomes `shall-if-practicable`, never a
|
|
90
|
+
new `should`;
|
|
91
|
+
- a `may` entry says how it relates to the displays it is an alternative
|
|
92
|
+
to: `rel:in_lieu_of` only where the paragraph replaces another display,
|
|
93
|
+
`rel:includes` where it adds to one, `rel:excludes` where the two may not
|
|
94
|
+
be shown together;
|
|
95
|
+
- a "shall not impede" paragraph carries the `shall-not-impede` effect,
|
|
96
|
+
whatever its `modality`;
|
|
97
|
+
- new prose in `requirements.md` capitalises the RFC keywords it means and
|
|
98
|
+
leaves ordinary "must" and "should" in lower case.
|
|
99
|
+
|
|
100
|
+
[RFC 2119]: https://www.rfc-editor.org/rfc/rfc2119
|
|
101
|
+
[RFC 8174]: https://www.rfc-editor.org/rfc/rfc8174
|
|
102
|
+
[RfcKeywords]: https://www.w3.org/wiki/RfcKeywords
|
|
103
|
+
[ISO/IEC Directives, Part 2]: https://www.iso.org/sites/directives/current/part2/index.xhtml
|