colregs 0.3.0 → 0.3.1
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 +8 -6
- package/data/applicability.json +219 -244
- package/data/facts.json +4 -4
- 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 +1 -1
- 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/budgets.json +5 -15
- package/docs/identifiers.md +49 -51
- package/docs/maritime-sources.md +58 -0
- package/docs/normative-language.md +103 -0
- package/docs/part-b-invariants.md +31 -29
- package/docs/requirements.md +80 -71
- package/fixtures/applicability-fixtures.json +190 -190
- package/fixtures/situation-fixtures.json +287 -287
- package/package.json +1 -1
- package/schema/applicability-fixtures.schema.json +4 -13
- package/schema/applicability.schema.json +27 -27
- 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/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 +10 -146
- 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
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# ADR 0015 — Rule ids are paragraph keys in the `rule:` namespace
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-16
|
|
4
|
+
Status: accepted — Solace's ruling, 2026-09-16
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Issue #121 asked what an applicability entry should be called, and ruling C
|
|
9
|
+
was read as *names, not citations*. The first draft of this ADR wrote that
|
|
10
|
+
reading out: every id became a coined name in an `entry:` namespace —
|
|
11
|
+
`entry:anchored`, `entry:towing_mastheads_long_tow`, `entry:being_overtaken`.
|
|
12
|
+
It never landed and never shipped. Solace superseded that reading on
|
|
13
|
+
2026-09-16 and this file is the replacement.
|
|
14
|
+
|
|
15
|
+
What the rename was buying was insulation: `cite` is a data field, revised
|
|
16
|
+
whenever the package reads the Rules better, and `14a` had already become
|
|
17
|
+
`14b` when the reading of *which paragraph deems a head-on* moved. An
|
|
18
|
+
identifier containing a data field gets renamed every time the field is.
|
|
19
|
+
|
|
20
|
+
What it cost is the vocabulary. An entry is a norm out of the Convention,
|
|
21
|
+
and the people who read these ids — and the consumers that store them
|
|
22
|
+
alongside a `cite` anyway — already have a name for every norm in the
|
|
23
|
+
package: the Rule. `rule:13b` is *Rule 13(b)* in the mariner's mouth. A
|
|
24
|
+
coined name is a second vocabulary beside that one, invented here,
|
|
25
|
+
unguessable from the Rules, and needing a lookup in this file to be read
|
|
26
|
+
back as the paragraph it came from. The insulation is worth one rename in
|
|
27
|
+
a pre-1.0 package; the second vocabulary is permanent. Ruled by Solace,
|
|
28
|
+
2026-09-16.
|
|
29
|
+
|
|
30
|
+
## Decision
|
|
31
|
+
|
|
32
|
+
1. **The namespace noun is `rule:`.** It replaces `entry:` everywhere. The
|
|
33
|
+
overload with the Convention's Rules is the point, not an accident to
|
|
34
|
+
be designed around.
|
|
35
|
+
2. **An id is `rule:<paragraph-slug>`.** The slug is the cite with its
|
|
36
|
+
punctuation dropped: rule number, paragraph letter attached, each roman
|
|
37
|
+
subparagraph joined by `_`. `27(a)(i)` → `27a_i`; `13(b)` → `13b`;
|
|
38
|
+
`11` → `11`; `23(a)(iii)-(iv)` → `23a_iii_iv`.
|
|
39
|
+
3. **A bare id is the paragraph's principal norm.** Where the text yields
|
|
40
|
+
a further norm from the same paragraph, it takes a third segment after
|
|
41
|
+
a colon, named in the text's own words: `rule:24a_i` is 24(a)(i)'s two
|
|
42
|
+
masthead lights and `rule:24a_i:exceeds_200m` its three, the threshold
|
|
43
|
+
being the Convention's own, which is when a number may appear in a name.
|
|
44
|
+
4. **Where one sentence fuses a deeming test and a duty, both norms are
|
|
45
|
+
named.** 15(a) gives `rule:15a:crossing` — the classification, effect
|
|
46
|
+
`encounter: crossing` — and `rule:15a:keep_out_of_the_way` — the
|
|
47
|
+
precedence, own give-way. `avoid_crossing_ahead` is reserved for 15(a)'s
|
|
48
|
+
second duty when the `conduct` shape exists; it is not in use.
|
|
49
|
+
5. **A Convention subparagraph keeps its own cite.** 30(d)'s chapeau is
|
|
50
|
+
`rule:30d`, citing `30(d)`, importing the 30(a)/(b) anchor lights,
|
|
51
|
+
modality `shall`; the two all-round red lights are `rule:30d_i`, citing
|
|
52
|
+
`30(d)(i)`, modality `shall-if-practicable` because the paragraph says
|
|
53
|
+
"if practicable". `30(d)(i)` joins `rules.json` as a skeleton path with
|
|
54
|
+
this change. `rule:30d_ii` is reserved for the three balls, and lands
|
|
55
|
+
when day shapes do.
|
|
56
|
+
6. **A jurisdiction override is named by its difference**, and jurisdiction
|
|
57
|
+
stays a field: `rule:30a:mooring_buoy` and `rule:30b:mooring_buoy`, both
|
|
58
|
+
`us/inland`. The name says what 33 CFR 90.5 adds, not who adds it.
|
|
59
|
+
7. **13(b) is one symmetric entry.** The two entries that preceded it were
|
|
60
|
+
identical but for which subject's `geo:rel_bearing_deg` fell in
|
|
61
|
+
(112.5, 247.5), and both produced `encounter: overtaking`. `rule:13b`
|
|
62
|
+
reads the same sector object on either subject under `any_of` — the
|
|
63
|
+
pattern 13(d) already used over `hist:was_overtaking` — because the
|
|
64
|
+
encounter type belongs to the pair. One paragraph, one norm, one id.
|
|
65
|
+
8. **Every other entry with a unique cite takes the bare slug of it.**
|
|
66
|
+
9. **`represented_paragraphs` ids take the prefix too** — `2a` becomes
|
|
67
|
+
`rule:2a` — and keep their own list. They no longer need a schema
|
|
68
|
+
definition of their own: the id pattern is the same one.
|
|
69
|
+
10. **The schema pattern is**
|
|
70
|
+
`^rule:[0-9]+[a-z]?(_[ivx]+)*(:[a-z][a-z0-9_]*)?$`.
|
|
71
|
+
**An id is opaque to a consumer.** It looks like a citation and is not
|
|
72
|
+
one: a consumer that wants the paragraph reads `cite`, and never splits
|
|
73
|
+
the id to find it. The resemblance is for the human reading a trace;
|
|
74
|
+
the `cite` field is the machine-readable link to the Convention, and it
|
|
75
|
+
is the field that moves when the package's reading of a paragraph moves.
|
|
76
|
+
11. **The identifier-diff mechanism is removed, not left dormant.**
|
|
77
|
+
`retired_entry_ids`, `data/deprecated-identifiers.json`, its schema, the
|
|
78
|
+
dormant diff test and the retired-id half of the REQ-MODEL-10 collision
|
|
79
|
+
test all go. Nothing pre-1.0 is immutable, so there was nothing for the
|
|
80
|
+
registry to hold and no diff for the test to run; a mechanism that
|
|
81
|
+
cannot fire is a mechanism nobody maintains. It returns, if at all, with
|
|
82
|
+
the 1.0.0 tag that REQ-MODEL-10's baseline names. Ruled by Solace,
|
|
83
|
+
2026-09-16.
|
|
84
|
+
|
|
85
|
+
## The names
|
|
86
|
+
|
|
87
|
+
`was` is the `entry:` name from the superseded draft of this ADR, which
|
|
88
|
+
shipped in no release. The citation-derived ids that preceded *it* —
|
|
89
|
+
`24a-m2`, `23a1`, `30d-red` — were never names either and are not carried
|
|
90
|
+
forward.
|
|
91
|
+
|
|
92
|
+
| was | is | cite |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `entry:power_forward_masthead` | `rule:23a_i` | 23(a)(i) |
|
|
95
|
+
| `entry:power_second_masthead` | `rule:23a_ii` | 23(a)(ii) |
|
|
96
|
+
| `entry:power_sidelights_sternlight` | `rule:23a_iii_iv` | 23(a)(iii)-(iv) |
|
|
97
|
+
| `entry:air_cushion_non_displacement` | `rule:23b` | 23(b) |
|
|
98
|
+
| `entry:wig_near_surface` | `rule:23c` | 23(c) |
|
|
99
|
+
| `entry:power_under_12m` | `rule:23d_i` | 23(d)(i) |
|
|
100
|
+
| `entry:power_under_7m_slow` | `rule:23d_ii` | 23(d)(ii) |
|
|
101
|
+
| `entry:towing_mastheads` | `rule:24a_i` | 24(a)(i) |
|
|
102
|
+
| `entry:towing_mastheads_long_tow` | `rule:24a_i:exceeds_200m` | 24(a)(i) |
|
|
103
|
+
| `entry:towing_light` | `rule:24a_ii_iv` | 24(a)(ii)-(iv) |
|
|
104
|
+
| `entry:pushing_composite_unit` | `rule:24b` | 24(b) |
|
|
105
|
+
| `entry:pushing_ahead_or_alongside` | `rule:24c` | 24(c) |
|
|
106
|
+
| `entry:being_towed` | `rule:24e` | 24(e) |
|
|
107
|
+
| `entry:sail_sidelights_sternlight` | `rule:25a` | 25(a) |
|
|
108
|
+
| `entry:sail_combined_lantern` | `rule:25b` | 25(b) |
|
|
109
|
+
| `entry:sail_red_over_green` | `rule:25c` | 25(c) |
|
|
110
|
+
| `entry:sail_under_7m` | `rule:25d_i` | 25(d)(i) |
|
|
111
|
+
| `entry:under_oars` | `rule:25d_ii` | 25(d)(ii) |
|
|
112
|
+
| `entry:trawling_green_over_white` | `rule:26b_i` | 26(b)(i) |
|
|
113
|
+
| `entry:trawling_masthead` | `rule:26b_ii` | 26(b)(ii) |
|
|
114
|
+
| `entry:trawling_making_way` | `rule:26b_iii` | 26(b)(iii) |
|
|
115
|
+
| `entry:fishing_red_over_white` | `rule:26c_i` | 26(c)(i) |
|
|
116
|
+
| `entry:fishing_outlying_gear` | `rule:26c_ii` | 26(c)(ii) |
|
|
117
|
+
| `entry:fishing_making_way` | `rule:26c_iii` | 26(c)(iii) |
|
|
118
|
+
| `entry:nuc_red_over_red` | `rule:27a_i` | 27(a)(i) |
|
|
119
|
+
| `entry:nuc_making_way` | `rule:27a_iii` | 27(a)(iii) |
|
|
120
|
+
| `entry:ram_red_white_red` | `rule:27b_i` | 27(b)(i) |
|
|
121
|
+
| `entry:ram_making_way` | `rule:27b_iii` | 27(b)(iii) |
|
|
122
|
+
| `entry:ram_anchored` | `rule:27b_iv` | 27(b)(iv) |
|
|
123
|
+
| `entry:ram_underwater_obstruction` | `rule:27d` | 27(d) |
|
|
124
|
+
| `entry:diving` | `rule:27e_i` | 27(e)(i) |
|
|
125
|
+
| `entry:mine_clearance` | `rule:27f` | 27(f) |
|
|
126
|
+
| `entry:cbd` | `rule:28` | 28 |
|
|
127
|
+
| `entry:pilot` | `rule:29a` | 29(a) |
|
|
128
|
+
| `entry:anchored` | `rule:30a` | 30(a) |
|
|
129
|
+
| `entry:anchored_under_50m` | `rule:30b` | 30(b) |
|
|
130
|
+
| `entry:anchored_deck_lights` | `rule:30c` | 30(c) |
|
|
131
|
+
| `entry:aground_anchor_lights` | `rule:30d` | 30(d) |
|
|
132
|
+
| `entry:aground_red_over_red` | `rule:30d_i` | 30(d)(i) |
|
|
133
|
+
| `entry:anchored_under_7m_not_near_channel` | `rule:30e` | 30(e) |
|
|
134
|
+
| `entry:any_visibility` | `rule:4` | 4 |
|
|
135
|
+
| `entry:in_sight` | `rule:11` | 11 |
|
|
136
|
+
| `entry:restricted_visibility` | `rule:19a` | 19(a) |
|
|
137
|
+
| `entry:power_gives_way_to_nuc` | `rule:18a_i` | 18(a)(i) |
|
|
138
|
+
| `entry:power_gives_way_to_ram` | `rule:18a_ii` | 18(a)(ii) |
|
|
139
|
+
| `entry:power_gives_way_to_fishing` | `rule:18a_iii` | 18(a)(iii) |
|
|
140
|
+
| `entry:power_gives_way_to_sail` | `rule:18a_iv` | 18(a)(iv) |
|
|
141
|
+
| `entry:sail_gives_way_to_nuc` | `rule:18b_i` | 18(b)(i) |
|
|
142
|
+
| `entry:sail_gives_way_to_ram` | `rule:18b_ii` | 18(b)(ii) |
|
|
143
|
+
| `entry:sail_gives_way_to_fishing` | `rule:18b_iii` | 18(b)(iii) |
|
|
144
|
+
| `entry:fishing_gives_way_to_nuc` | `rule:18c_i` | 18(c)(i) |
|
|
145
|
+
| `entry:fishing_gives_way_to_ram` | `rule:18c_ii` | 18(c)(ii) |
|
|
146
|
+
| `entry:avoid_impeding_cbd` | `rule:18d_i` | 18(d)(i) |
|
|
147
|
+
| `entry:wig_keeps_well_clear` | `rule:18f_i` | 18(f)(i) |
|
|
148
|
+
| `entry:not_impeded_remains_obliged` | `rule:8f_iii` | 8(f)(iii) |
|
|
149
|
+
| `entry:overtaking_gives_way` | `rule:13a` | 13(a) |
|
|
150
|
+
| `entry:narrow_channel_small_or_sail` | `rule:9b` | 9(b) |
|
|
151
|
+
| `entry:narrow_channel_fishing` | `rule:9c` | 9(c) |
|
|
152
|
+
| `entry:traffic_lane_fishing` | `rule:10i` | 10(i) |
|
|
153
|
+
| `entry:traffic_lane_small_or_sail` | `rule:10j` | 10(j) |
|
|
154
|
+
| `entry:steady_bearing` | `rule:7d_i` | 7(d)(i) |
|
|
155
|
+
| `entry:sail_port_tack_gives_way` | `rule:12a_i` | 12(a)(i) |
|
|
156
|
+
| `entry:sail_windward_gives_way` | `rule:12a_ii` | 12(a)(ii) |
|
|
157
|
+
| `entry:sail_port_tack_uncertain_gives_way` | `rule:12a_iii` | 12(a)(iii) |
|
|
158
|
+
| `entry:overtaking` | `rule:13b` | 13(b) |
|
|
159
|
+
| `entry:being_overtaken` | `rule:13b` | 13(b) |
|
|
160
|
+
| `entry:overtaking_until_past_and_clear` | `rule:13d` | 13(d) |
|
|
161
|
+
| `entry:head_on` | `rule:14b` | 14(b) |
|
|
162
|
+
| `entry:crossing` | `rule:15a:crossing` | 15(a) |
|
|
163
|
+
| `entry:crossing_gives_way` | `rule:15a:keep_out_of_the_way` | 15(a) |
|
|
164
|
+
| `entry:mooring_buoy` | `rule:30a:mooring_buoy` | 30(a) |
|
|
165
|
+
| `entry:mooring_buoy_under_50m` | `rule:30b:mooring_buoy` | 30(b) |
|
|
166
|
+
|
|
167
|
+
## Consequences
|
|
168
|
+
|
|
169
|
+
- One pass, one commit: `data/applicability.json`, both fixture files,
|
|
170
|
+
`data/geometry.json`, `data/images.json`, `data/facts.json`,
|
|
171
|
+
`data/rules.json`, the schemas, the suite and the living docs. The
|
|
172
|
+
schema `$defs` that named the type follow the noun: `entryId` is
|
|
173
|
+
`ruleId`, `entryIdList` is `ruleIdList`.
|
|
174
|
+
- Two entries may still share a cite — `rule:24a_i` and
|
|
175
|
+
`rule:24a_i:exceeds_200m`, the two halves of 15(a), the two mooring-buoy
|
|
176
|
+
deltas. What they never share is an id, and the third segment says which
|
|
177
|
+
norm out of the paragraph the entry carries.
|
|
178
|
+
- Earlier ADRs and closed questions cite entries by the superseded names;
|
|
179
|
+
the table above is the map and stays in this file permanently. ADR 0006
|
|
180
|
+
carries a dated note that its identifier-diff half is gone.
|
|
181
|
+
- `represented_paragraphs` is now a second list of records that look like
|
|
182
|
+
entries, differing only in carrying no `when`. Folding it into `entries`
|
|
183
|
+
with a `care`/`meta` category is the obvious follow-up and is not done
|
|
184
|
+
here.
|
|
185
|
+
- Open, and not ruled: whether `fact:rule18_class` should be renamed. It
|
|
186
|
+
carries a rule number inside a fact name, which is the shape this ADR
|
|
187
|
+
moved *entry* ids towards and says nothing about for facts. Left visibly
|
|
188
|
+
open.
|
|
189
|
+
- An encounter with more than two vessels is issue #135; nothing in this
|
|
190
|
+
scheme is aimed at it, and a third subject would need a segment none of
|
|
191
|
+
these ids have.
|
package/docs/budgets.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"$comment": "Prose budgets enforced by prose-budget (the engine in dotfiles .local/bin), run locally by npm test and in CI by the shared workflow. Raising a number or adding an exception is a deliberate, reviewable diff.",
|
|
3
3
|
"lines": {
|
|
4
4
|
"README.md": 360,
|
|
5
|
+
"docs/normative-language.md": 130,
|
|
5
6
|
"AGENTS.md": 200,
|
|
6
7
|
"CLAUDE.md": 10,
|
|
7
8
|
"docs/requirements.md": 1500,
|
|
@@ -18,7 +19,10 @@
|
|
|
18
19
|
"required": true,
|
|
19
20
|
"pending": [],
|
|
20
21
|
"docs/adr/0013-corpus-files-with-editions.md": 95,
|
|
21
|
-
"docs/
|
|
22
|
+
"docs/adr/0014-engine-interface-owned-by-colregs.md": 110,
|
|
23
|
+
"docs/adr/0015-rule-ids-are-paragraph-keys.md": 210,
|
|
24
|
+
"docs/timeline.md": 130,
|
|
25
|
+
"docs/maritime-sources.md": 130
|
|
22
26
|
},
|
|
23
27
|
"json_prose": {
|
|
24
28
|
"targets": [
|
|
@@ -55,14 +59,10 @@
|
|
|
55
59
|
"data/applicability.json#/entries/62/note",
|
|
56
60
|
"data/applicability.json#/entries/63/note",
|
|
57
61
|
"data/applicability.json#/entries/64/note",
|
|
58
|
-
"data/applicability.json#/entries/64/gap",
|
|
59
62
|
"data/applicability.json#/entries/65/note",
|
|
60
|
-
"data/applicability.json#/entries/65/gap",
|
|
61
63
|
"data/applicability.json#/entries/66/note",
|
|
62
64
|
"data/applicability.json#/entries/67/note",
|
|
63
65
|
"data/applicability.json#/entries/68/note",
|
|
64
|
-
"data/applicability.json#/entries/68/gap",
|
|
65
|
-
"data/applicability.json#/entries/69/note",
|
|
66
66
|
"data/facts.json#/derived/note",
|
|
67
67
|
"data/facts.json#/derived/fact:rule18_class/note",
|
|
68
68
|
"data/facts.json#/derived/fact:rule18_class/decode/1/note",
|
|
@@ -134,16 +134,6 @@
|
|
|
134
134
|
]
|
|
135
135
|
},
|
|
136
136
|
"grandfathered": [
|
|
137
|
-
{
|
|
138
|
-
"file": "data/applicability.json",
|
|
139
|
-
"hash": "447b3984d57b",
|
|
140
|
-
"match": "PR #24"
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
"file": "data/applicability.json",
|
|
144
|
-
"hash": "5ff9d39319b8",
|
|
145
|
-
"match": "PR #24"
|
|
146
|
-
},
|
|
147
137
|
{
|
|
148
138
|
"file": "docs/conventions.md",
|
|
149
139
|
"hash": "d1f1ed997431",
|
package/docs/identifiers.md
CHANGED
|
@@ -17,10 +17,12 @@ 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.
|
|
24
26
|
|
|
25
27
|
**Vocabulary identifiers carry a type prefix.** These names are this
|
|
26
28
|
package's own — nothing in COLREGS calls anything `masthead` or `nuc`. They
|
|
@@ -35,6 +37,7 @@ resolves that collision by construction rather than by convention.
|
|
|
35
37
|
|
|
36
38
|
| form | class | examples |
|
|
37
39
|
|---|---|---|
|
|
40
|
+
| `rule:<paragraph-slug>` | applicability entries (`data/applicability.json`) | `rule:30a`, `rule:24a_i:exceeds_200m`, `rule:15a:keep_out_of_the_way` |
|
|
38
41
|
| `light:<id>` | light definitions (`data/lights.json`) | `light:masthead`, `light:sidelight_starboard`, `light:all_round` |
|
|
39
42
|
| `fact:<key>` | fact keys — the input vocabulary (`data/facts.json`) | `fact:activity`, `fact:length_m`, `fact:making_way`, `fact:on_mooring_buoy` |
|
|
40
43
|
| `<fact>:<value>` | values of an enumerated fact | `activity:nuc`, `position:anchored`, `propulsion:sail`, `obstruction_side:port` |
|
|
@@ -257,21 +260,21 @@ of them; the departure from the table is recorded in ADR 0005's pencil log and
|
|
|
257
260
|
in `Q-40`.
|
|
258
261
|
|
|
259
262
|
**Who governs over Rule 12.** 12(a)'s subjects are "two sailing vessels",
|
|
260
|
-
which is 3(c), so
|
|
263
|
+
which is 3(c), so the three Rule 12 entries gate on `fact:propulsion` and not on the Rule
|
|
261
264
|
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:
|
|
265
|
+
ranks the pair, its entry displaces Rule 12's: the three 18(b) entries and the two 18(c) entries
|
|
263
266
|
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
|
|
267
|
+
except Rules 9, 10 and 13 and nothing else. `rule:13a` overrides them for the same
|
|
265
268
|
reason it overrides Rule 18 — 13(a) is "notwithstanding" the rest of Sections
|
|
266
269
|
I and II. The test that pins the relation asserts both reasons from
|
|
267
270
|
`rules.json`, so the data cannot keep an override after losing the words.
|
|
268
271
|
|
|
269
272
|
**And over Rule 15, the same way.** 15(a)'s subjects are "two power-driven
|
|
270
|
-
vessels", which is 3(b), so `15a
|
|
273
|
+
vessels", which is 3(b), so `rule:15a:keep_out_of_the_way` gates on `fact:propulsion` and on
|
|
271
274
|
no Rule 18 rank either — a vessel engaged in fishing, or not under command,
|
|
272
275
|
whose machinery is in use is a power-driven vessel. It used to negate the four
|
|
273
276
|
ranks in its own predicate, which said the same thing in the one place a test
|
|
274
|
-
could not see the reason;
|
|
277
|
+
could not see the reason; the 18(a)(i)–(iii) entries, the two 18(c) entries and `rule:18f_i` now carry
|
|
275
278
|
`rel:overrides` against it instead. The derived half of the test is what makes
|
|
276
279
|
that checkable: a Rule 18 entry meets Rule 15 when it assigns a helm role and
|
|
277
280
|
neither subject is gated to a sailing vessel, and every such entry must carry
|
|
@@ -292,53 +295,48 @@ that is Rule 18's partial order rather than a gap in the table.
|
|
|
292
295
|
— keep well clear, and avoid impeding navigation. The vocabulary cannot
|
|
293
296
|
separate them and does not pretend to.
|
|
294
297
|
|
|
295
|
-
### Two-subject
|
|
298
|
+
### Two-subject rule ids
|
|
296
299
|
|
|
297
|
-
|
|
298
|
-
9(c), `
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
300
|
+
A two-subject entry is keyed on its paragraph like any other (below):
|
|
301
|
+
`rule:18a_i` is 18(a)(i), `rule:9c` is 9(c), `rule:8f_iii` is 8(f)(iii). No
|
|
302
|
+
subject segment appears in an id: every entry is evaluated from own's side,
|
|
303
|
+
and where the paragraph classifies the pair rather than one vessel the entry
|
|
304
|
+
reads both subjects inside one predicate — `rule:13b` is 13(b) whichever
|
|
305
|
+
vessel is coming up. Where a paragraph's subject is disjunctive — 9(b) is
|
|
306
|
+
"a vessel of less than 20 metres in length **or** a sailing vessel" —
|
|
307
|
+
`any_of` carries the disjunction inside one entry, `rule:9b`.
|
|
305
308
|
|
|
306
|
-
##
|
|
309
|
+
## Rule ids
|
|
307
310
|
|
|
308
|
-
An entry id is
|
|
309
|
-
with
|
|
310
|
-
|
|
311
|
+
An entry id is a paragraph key in the `rule:` namespace: `rule:` plus the
|
|
312
|
+
cite with its punctuation dropped. ADR 0015 (Solace, 2026-09-16) made the
|
|
313
|
+
change and carries the table from the ids it replaced; the rule for minting
|
|
314
|
+
a new one is here.
|
|
311
315
|
|
|
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 |
|
|
316
|
+
| paragraph | rule id | why that id |
|
|
324
317
|
|---|---|---|
|
|
325
|
-
|
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
|
|
|
329
|
-
|
|
|
330
|
-
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
318
|
+
| 30(a) | `rule:30a` | the bare slug of the cite: rule number, paragraph letter attached |
|
|
319
|
+
| 27(a)(i) | `rule:27a_i` | each roman subparagraph joined with `_` |
|
|
320
|
+
| 23(a)(iii)-(iv) | `rule:23a_iii_iv` | a span of subparagraphs, joined the same way |
|
|
321
|
+
| 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 |
|
|
322
|
+
| 15(a), the duty | `rule:15a:keep_out_of_the_way` | which half of a fused deeming-and-duty sentence this entry carries |
|
|
323
|
+
| 30(a), US inland | `rule:30a:mooring_buoy` | a jurisdiction delta named by its difference; the jurisdiction stays a field |
|
|
324
|
+
|
|
325
|
+
A bare id is the paragraph's principal norm. A third segment is added only
|
|
326
|
+
where the text yields a second norm from the same paragraph, and it is named
|
|
327
|
+
in the words of the text, not in what the entry produces. A number may appear
|
|
328
|
+
there only when the Convention states the threshold itself (`exceeds_200m` is
|
|
329
|
+
24(a)(i)'s).
|
|
330
|
+
|
|
331
|
+
**The id is opaque.** It looks like a citation and it is not one: a consumer
|
|
332
|
+
that wants "Rule 24(a)(i)" reads `cite`, and never splits an id to find a
|
|
333
|
+
paragraph. `cite` is the field that moves when the package reads the Rules
|
|
334
|
+
better — `14a` became `14b` once already — and the id resembling it is a
|
|
335
|
+
convenience for the human reading a trace, nothing the data promises. Two
|
|
336
|
+
entries may share a cite; they never share an id.
|
|
337
|
+
|
|
338
|
+
`represented_paragraphs` take the same prefix and the same shape
|
|
339
|
+
(`rule:2a` for 2(a)). They are not entries and nothing references them.
|
|
342
340
|
|
|
343
341
|
## Derived facts
|
|
344
342
|
|
|
@@ -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
|