colregs 0.2.4 → 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.
Files changed (57) hide show
  1. package/PROVENANCE.md +3 -3
  2. package/README.md +19 -10
  3. package/data/applicability.json +219 -244
  4. package/data/corpora.json +41 -0
  5. package/data/editions.json +27 -0
  6. package/data/facts.json +4 -4
  7. package/data/geometry.json +8 -8
  8. package/data/i18n/en.json +27 -0
  9. package/data/i18n/fi.json +25 -0
  10. package/data/images.json +32 -32
  11. package/data/operations.json +79 -0
  12. package/data/rules.json +176 -599
  13. package/data/text/intl/2016/en-US.uscg.json +877 -0
  14. package/data/text/intl/2016/es.boe.json +23 -0
  15. package/data/text/intl/2016/fi.finlex.json +22 -0
  16. package/data/text/us/inland/2014/en-US.ecfr.json +22 -0
  17. package/data/version.json +1 -1
  18. package/docs/adr/0001-name-and-jurisdiction-model.md +42 -2
  19. package/docs/adr/0003-language-as-a-dimension.md +8 -7
  20. package/docs/adr/0006-json-schema-and-identifier-diff.md +2 -0
  21. package/docs/adr/0011-api-shape.md +1 -1
  22. package/docs/adr/0012-trace-and-rule2-departure-api.md +3 -4
  23. package/docs/adr/0013-corpus-files-with-editions.md +70 -0
  24. package/docs/adr/0014-engine-interface-owned-by-colregs.md +103 -0
  25. package/docs/adr/0015-rule-ids-are-paragraph-keys.md +191 -0
  26. package/docs/budgets.json +17 -17
  27. package/docs/gates.json +3 -3
  28. package/docs/identifiers.md +49 -51
  29. package/docs/maritime-sources.md +58 -0
  30. package/docs/normative-language.md +103 -0
  31. package/docs/part-b-invariants.md +31 -29
  32. package/docs/requirements.md +118 -97
  33. package/docs/timeline.md +102 -0
  34. package/docs/verification/2026-09-09-text-slug-straw-man.md +3 -2
  35. package/fixtures/applicability-fixtures.json +190 -190
  36. package/fixtures/situation-fixtures.json +287 -287
  37. package/package.json +1 -1
  38. package/schema/applicability-fixtures.schema.json +4 -13
  39. package/schema/applicability.schema.json +27 -27
  40. package/schema/conduct-evaluation.schema.json +135 -0
  41. package/schema/corpora.schema.json +68 -0
  42. package/schema/corpus.schema.json +329 -0
  43. package/schema/display-evaluation.schema.json +146 -0
  44. package/schema/editions.schema.json +47 -0
  45. package/schema/encounter-evaluation.schema.json +109 -0
  46. package/schema/evaluation.schema.json +149 -0
  47. package/schema/fact-record.schema.json +30 -0
  48. package/schema/i18n-catalog.schema.json +47 -0
  49. package/schema/operations.schema.json +124 -0
  50. package/schema/rule2-departure-finding.schema.json +72 -0
  51. package/schema/rule2-departure-model.schema.json +159 -0
  52. package/schema/rules.schema.json +20 -90
  53. package/schema/situation-fixtures.schema.json +10 -146
  54. package/schema/situation.schema.json +72 -0
  55. package/schema/trace.schema.json +33 -0
  56. package/data/deprecated-identifiers.json +0 -7
  57. package/schema/deprecated-identifiers.schema.json +0 -29
package/docs/budgets.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
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
- "README.md": 200,
5
- "AGENTS.md": 122,
4
+ "README.md": 360,
5
+ "docs/normative-language.md": 130,
6
+ "AGENTS.md": 200,
6
7
  "CLAUDE.md": 10,
7
8
  "docs/requirements.md": 1500,
8
9
  "docs/identifiers.md": 450,
9
10
  "docs/part-b-invariants.md": 950,
11
+ "docs/adr/0001-name-and-jurisdiction-model.md": 280,
10
12
  "docs/adr/0007-rule26-overrides-and-aground.md": 90,
11
13
  "docs/adr/0008-mooring-buoy-modifier.md": 90,
12
14
  "docs/adr/0009-data-version-stamp.md": 90,
@@ -15,11 +17,17 @@
15
17
  "docs/adr/0012-trace-and-rule2-departure-api.md": 203,
16
18
  "docs/verification/2026-09-09-text-slug-straw-man.md": 270,
17
19
  "required": true,
18
- "pending": []
20
+ "pending": [],
21
+ "docs/adr/0013-corpus-files-with-editions.md": 95,
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
19
26
  },
20
27
  "json_prose": {
21
28
  "targets": [
22
29
  "data/*.json",
30
+ "data/text/**/*.json",
23
31
  "fixtures/*.json"
24
32
  ],
25
33
  "keys": [
@@ -51,14 +59,10 @@
51
59
  "data/applicability.json#/entries/62/note",
52
60
  "data/applicability.json#/entries/63/note",
53
61
  "data/applicability.json#/entries/64/note",
54
- "data/applicability.json#/entries/64/gap",
55
62
  "data/applicability.json#/entries/65/note",
56
- "data/applicability.json#/entries/65/gap",
57
63
  "data/applicability.json#/entries/66/note",
58
64
  "data/applicability.json#/entries/67/note",
59
65
  "data/applicability.json#/entries/68/note",
60
- "data/applicability.json#/entries/68/gap",
61
- "data/applicability.json#/entries/69/note",
62
66
  "data/facts.json#/derived/note",
63
67
  "data/facts.json#/derived/fact:rule18_class/note",
64
68
  "data/facts.json#/derived/fact:rule18_class/decode/1/note",
@@ -86,6 +90,7 @@
86
90
  "AGENTS.md",
87
91
  "CLAUDE.md",
88
92
  "data/*.json",
93
+ "data/text/**/*.json",
89
94
  "fixtures/*.json",
90
95
  "test/*.mjs",
91
96
  "docs/**/*.md"
@@ -96,6 +101,7 @@
96
101
  "AGENTS.md",
97
102
  "CLAUDE.md",
98
103
  "data/*.json",
104
+ "data/text/**/*.json",
99
105
  "fixtures/*.json",
100
106
  "test/*.mjs"
101
107
  ],
@@ -104,6 +110,7 @@
104
110
  "AGENTS.md",
105
111
  "CLAUDE.md",
106
112
  "data/*.json",
113
+ "data/text/**/*.json",
107
114
  "fixtures/*.json",
108
115
  "test/*.mjs"
109
116
  ],
@@ -112,6 +119,7 @@
112
119
  "AGENTS.md",
113
120
  "CLAUDE.md",
114
121
  "data/*.json",
122
+ "data/text/**/*.json",
115
123
  "fixtures/*.json",
116
124
  "test/*.mjs"
117
125
  ],
@@ -120,21 +128,12 @@
120
128
  "AGENTS.md",
121
129
  "CLAUDE.md",
122
130
  "data/*.json",
131
+ "data/text/**/*.json",
123
132
  "fixtures/*.json",
124
133
  "test/*.mjs"
125
134
  ]
126
135
  },
127
136
  "grandfathered": [
128
- {
129
- "file": "data/applicability.json",
130
- "hash": "447b3984d57b",
131
- "match": "PR #24"
132
- },
133
- {
134
- "file": "data/applicability.json",
135
- "hash": "5ff9d39319b8",
136
- "match": "PR #24"
137
- },
138
137
  {
139
138
  "file": "docs/conventions.md",
140
139
  "hash": "d1f1ed997431",
@@ -155,6 +154,7 @@
155
154
  "CLAUDE.md",
156
155
  "docs/**/*.md",
157
156
  "data/*.json",
157
+ "data/text/**/*.json",
158
158
  "fixtures/*.json"
159
159
  ]
160
160
  },
package/docs/gates.json CHANGED
@@ -36,9 +36,9 @@
36
36
  "declined_in": "docs/adr/0003-language-as-a-dimension.md",
37
37
  "closing_event": "first-non-english-corpus",
38
38
  "trigger": "a jurisdiction publishing two editions in force concurrently",
39
- "status": "open",
40
- "settled_by": null,
41
- "note": "Due at translation #1, not at 1.0."
39
+ "status": "adopted",
40
+ "settled_by": "docs/adr/0013-corpus-files-with-editions.md",
41
+ "note": "Re-taken and adopted ahead of its due date (translation #1) — ADR 0013 introduces the edition registry directly."
42
42
  },
43
43
  {
44
44
  "id": "GATE-3",
@@ -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. Entry ids are derived from
21
- paragraph paths (below) and inherit the same transparency for the same
22
- reason. Paragraph-keying is argued in ADR 0001 and required by
23
- REQ-MODEL-4; nothing here reopens either.
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 `12a1`–`12a3` gate on `fact:propulsion` and not on the Rule
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: `18b1`–`18b3` and `18c1`–`18c2`
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-give-way` gates on `fact:propulsion` and on
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; `18a1`–`18a3`, `18c1`–`18c2` and `18f1` now carry
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 entry ids
298
+ ### Two-subject rule ids
296
299
 
297
- Entry ids stay citation-derived, exactly as below: `18a1` is 18(a)(i), `9c` is
298
- 9(c), `8f3` is 8(f)(iii). Where a paragraph's subject is disjunctive — 9(b) is
299
- "a vessel of less than 20 metres in length **or** a sailing vessel", and a
300
- `when` is a conjunction — the paragraph takes two entries and the suffix names
301
- the half: `9b-small` and `9b-sail`, `10j-small` and `10j-sail`. That is the
302
- same rule the `-m2`/`-mw`/`-anc` suffixes below follow: name what
303
- distinguishes them, in terms a reader with the rule text in front of them can
304
- find.
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
- ## Entry ids
309
+ ## Rule ids
307
310
 
308
- An entry id is derived from the paragraph path its entry cites, lowercased
309
- with the parentheses dropped and roman sub-paragraph numerals written as
310
- arabic digits:
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 path | entry id |
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
- | `-m2` / `-m3` | two or three masthead lights | `24a-m2`, `24a-m3` |
326
- | `-rest` | the remainder of the rule's requirements once the split ones are taken out | `24a-rest` |
327
- | `-id` | the identity lights: the all-round group that says *what the vessel is* | `26b-id`, `27a-id`, `27b-id` |
328
- | `-mast` | the masthead light the paragraph adds on top of the identity lights | `26b-mast` |
329
- | `-mw` | the making-way half of a rule that lights differently when moving through the water | `26b-mw`, `27a-mw`, `27b-mw` |
330
- | `-gear` | the light indicating the direction of outlying gear | `26c-gear` |
331
- | `-anc` | the at-anchor branch | `27b-anc` |
332
- | `-anchor` / `-red` | 30(d)'s two halves: the anchor lights it requires, and the two red all-round lights of a vessel aground | `30d-anchor`, `30d-red` |
333
-
334
- This was reviewed and kept as it stands. The alternative — a uniform
335
- ordinal suffix, `27a-1` / `27a-2` — would be self-consistent and completely
336
- opaque: it tells a reader with the rule text in front of them nothing, in
337
- exchange for no gain to a machine, which only ever compares entry ids for
338
- equality. `24a-m2` / `24a-m3` are worth calling out in particular, because
339
- the two-or-three masthead split is stated in 24(a)(i) itself — the
340
- cardinality is in the law, not a modelling convenience of this package, and
341
- the suffix names something the reader can go and find.
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
@@ -34,9 +34,9 @@ suffix naming what distinguishes them.
34
34
  - Insertion appends a suffix and never renumbers; a suffix names something and
35
35
  is not an ordinal (`REQ-INV-2`).
36
36
  - The paragraph is the unit (ADR 0001): an invariant reading several takes the
37
- id of the one stating its operative content, as entry `14b` does.
37
+ id of the one stating its operative content, as entry `rule:14b` does.
38
38
  - `INV-` is a type prefix on a citation-derived name — the one departure from
39
- `docs/identifiers.md`, because `13a` is already an entry id. Jurisdiction is a
39
+ `docs/identifiers.md`, because `rule:13a` is already an entry id. Jurisdiction is a
40
40
  dimension (`REQ-SCOPE-2`, `Q-8`); only `intl` is populated.
41
41
 
42
42
  ---
@@ -82,10 +82,10 @@ not overtaking); its edges are the forward edges of Rule 21(c)'s sternlight
82
82
  arc, which 13(b)'s second limb states in light terms.
83
83
 
84
84
  - **Undetermined term.** "Coming up with" is a speed comparison the model cannot
85
- express (`Q-46`); `13b-overtaking` substitutes `pair:geo:tcpa_s > 0`, which
85
+ express (`Q-46`); `rule:13b` substitutes `pair:geo:tcpa_s > 0`, which
86
86
  admits a pair closing because the vessel ahead stopped. This states the rule.
87
87
  - **Not stated by 13(b).** Risk of collision: `Q-50`.
88
- - Entries: `13b-overtaking`, `13b-overtaken`;
88
+ - Entries: `rule:13b`;
89
89
  `situation.constants.overtaking_sector_from_deg`/`_to_deg` (ink). Settled by
90
90
  P2.2's Alloy sector model: a partition, or a bearing that falls in both.
91
91
 
@@ -100,9 +100,11 @@ overtaking vessel bears forward of her beam.
100
100
 
101
101
  - **Why separate.** A formalisation carrying encounter type per vessel
102
102
  classifies the overtaken side as a crossing, two types on one pair.
103
- - Entries: `13b-overtaking`, `13b-overtaken`, each other's `when` with subjects
104
- swapped. Settled by P4.2 carrying the type on the pair and TLC finding no
105
- state where the subjects disagree.
103
+ - **2026-09-16.** The two-entry form this section recorded is reversed by
104
+ ADR 0015 as rewritten: 13(b) is one symmetric entry, the same sector object
105
+ read on either subject under `any_of`. The invariant is unchanged.
106
+ - Entries: `rule:13b`. Settled by P4.2 carrying the type on the pair and TLC
107
+ finding no state where the subjects disagree.
106
108
 
107
109
  ### INV-13a — the overtaking vessel keeps out of the way
108
110
 
@@ -116,7 +118,7 @@ roles displace any role Rules 4–18 would otherwise assign to either vessel.
116
118
  well as Rules 14 and 15.
117
119
 
118
120
  - **Not stated by 13(a).** Risk of collision: `Q-50`.
119
- - Entries: `13a`, overriding all eleven Rule 18 and all three Rule 12 entries.
121
+ - Entries: `rule:13a`, overriding all eleven Rule 18 and all three Rule 12 entries.
120
122
  Settled by P4.2's role assignment: the override is or is not needed to keep
121
123
  "never both give-way" true.
122
124
 
@@ -142,8 +144,8 @@ until own is finally past and clear, the encounter type of the pair is
142
144
  model's is its segmentation, and no bearing or range is invented here.
143
145
  - **Readings in doubt.** Two: what arms the latch, `Q-51`; what it forbids,
144
146
  `Q-52`.
145
- - Entries: `13d`, reading `own`/`other:hist:was_overtaking` and no geometry; the
146
- `was_overtaking: false` gates on `14b`, `15a-crossing`, `15a-give-way`
147
+ - Entries: `rule:13d`, reading `own`/`other:hist:was_overtaking` and no geometry; the
148
+ `was_overtaking: false` gates on `rule:14b`, `rule:15a:crossing`, `rule:15a:keep_out_of_the_way`
147
149
  implement `Q-52`'s *broad* reading. Settled by `Q-51`, `Q-52`, then TLC on a
148
150
  three-state trace of an overtaking drawing out onto the bow.
149
151
 
@@ -159,7 +161,7 @@ the duty by its second clause, and a formalisation deriving role from encounter
159
161
  type alone cannot distinguish `Q-52`'s readings.
160
162
 
161
163
  - **State remembered.** As `INV-13d`.
162
- - Entries: `13a`'s `any_of` second limb, `own:hist:was_overtaking: true` — role
164
+ - Entries: `rule:13a`'s `any_of` second limb, `own:hist:was_overtaking: true` — role
163
165
  asserted from the latch directly, keeping the limbs separable. Settled by
164
166
  `Q-52`.
165
167
 
@@ -186,7 +188,7 @@ in [0°, 11.25°] ∪ [348.75°, 360°).
186
188
  that way.
187
189
  - **Not formalised.** The night and day observation limbs are stated as the
188
190
  geometry they encode; a vessel that cannot see the lights has 14(c).
189
- - Entries: `14b`. Settled by `INV-13b`'s partition sweep plus a decision on the
191
+ - Entries: `rule:14b`. Settled by `INV-13b`'s partition sweep plus a decision on the
190
192
  constant.
191
193
 
192
194
  ### INV-14a — both vessels alter to starboard
@@ -205,8 +207,8 @@ way; both are directed to act, and the duty is symmetric.
205
207
  - **Why it matters formally.** The one Section II encounter with no `stand-on`
206
208
  vessel, and why `INV-17a1-scope` has content.
207
209
  - Entries: none; `conduct`, in `known_omissions`, no conduct shape (`Q-45`);
208
- `14a` is retired in `data/deprecated-identifiers.json`. Settled by the first
209
- conduct monitor.
210
+ `14a` was never a name and is gone with the rest of the citation-derived ids
211
+ (ADR 0015). Settled by the first conduct monitor.
210
212
 
211
213
  ---
212
214
 
@@ -228,7 +230,7 @@ encounter type is `crossing` exactly when it is neither `overtaking` under
228
230
  - **Known incompleteness.** Two sailing vessels get no encounter type (Rules 14
229
231
  and 15 are gated on power, Rule 12 has no deeming paragraph): the Rules'. A
230
232
  pair with the history fact absent gets none: the model's (`Q-43`).
231
- - Entries: `15a-crossing`.
233
+ - Entries: `rule:15a:crossing`.
232
234
 
233
235
  ### INV-15a-give-way — the vessel with the other to starboard gives way
234
236
 
@@ -241,7 +243,7 @@ other holds `stand-on`.
241
243
  - **Undetermined term.** "On her own starboard side" has no sector in the
242
244
  paragraph; the data reads `own:geo:rel_bearing_deg` in (0°, 112.5°], its upper
243
245
  edge 13(b)'s constant, so it is checkable against the partition.
244
- - Entries: `15a-give-way`, plus six `rel:overrides` from Rule 18 entries, since
246
+ - Entries: `rule:15a:keep_out_of_the_way`, plus six `rel:overrides` from Rule 18 entries, since
245
247
  Rule 18's chapeau excepts Rules 9, 10 and 13 and no others.
246
248
 
247
249
  ### INV-15a-single — at most one give-way vessel in a crossing
@@ -277,7 +279,7 @@ advancing position.
277
279
  segment ("ahead of" a moving vessel), and the state the role attached.
278
280
  - **Undetermined term.** "If the circumstances of the case admit" has nowhere to
279
281
  live — `modality` is one closed value, an action has no `effect` — `Q-31`.
280
- - Entries: none; a `gap` on `15a-give-way`.
282
+ - Entries: none; a `gap` on `rule:15a:keep_out_of_the_way`.
281
283
 
282
284
  ---
283
285
 
@@ -443,7 +445,7 @@ discharge her obligation.
443
445
  - **Why separate.** The natural automaton takes the give-way duty as an input to
444
446
  its transitions; 17(d) says the arrow does not run back, so model two
445
447
  obligations holding concurrently, not one machine with one obligation.
446
- - Entries: none; nearest is 8(f)(ii)'s parallel for `shall-not-impede`, `8f3`.
448
+ - Entries: none; nearest is 8(f)(ii)'s parallel for `shall-not-impede`, `rule:8f_iii`.
447
449
 
448
450
  ### INV-17-phases — the phase structure
449
451
 
@@ -485,9 +487,9 @@ governs.
485
487
  - **Why both directions.** "To nothing else" is what runs the `rel:overrides`
486
488
  edges from Rule 18 *to* Rules 12 and 15; got wrong once, found by a sweep
487
489
  (`Q-40`).
488
- - Entries: overrides on `13a` against all Rule 18 and Rule 12 entries; on
489
- `18a1`–`18a3`, `18c1`–`18c2`, `18f1` against `15a-give-way`; on the Rule 18
490
- entries that can meet two sailing vessels against `12a1`–`12a3`; a derived
490
+ - Entries: overrides on `rule:13a` against all Rule 18 and Rule 12 entries; on
491
+ the 18(a)(i)–(iii) entries, the two 18(c) entries, `rule:18f_i` against `rule:15a:keep_out_of_the_way`; on the Rule 18
492
+ entries that can meet two sailing vessels against the three Rule 12 entries; a derived
491
493
  check asserts the hand-list of six against Rule 15 is complete.
492
494
 
493
495
  ### INV-18-order — the rank order
@@ -510,7 +512,7 @@ The subject holds `give-way`; the object `stand-on`.
510
512
  (`Q-32`) is the model's decode onto these ranks. A vessel constrained by her
511
513
  draught is a power-driven vessel for 18(a) and is ranked by it.
512
514
  - **Not stated by Rule 18.** Risk of collision and encounter type: `Q-50`.
513
- - Entries: `18a1`–`18a4`, `18b1`–`18b3`, `18c1`–`18c2`.
515
+ - Entries: the four 18(a) entries, the three 18(b) entries, the two 18(c) entries.
514
516
 
515
517
  ### INV-18-partial — the order is partial, and the gaps are the Rules'
516
518
 
@@ -542,7 +544,7 @@ discharging `INV-16` requires of her.
542
544
 
543
545
  - **Why stated.** Read as a condition on the antecedent, it gives a roleless
544
546
  pair exactly when compliance is hard and the roles matter.
545
- - Entries: `18c1`, `18c2`, modality `shall-if-practicable` with effect
547
+ - Entries: `rule:18c_i`, `rule:18c_ii`, modality `shall-if-practicable` with effect
546
548
  `give-way`/`stand-on` — `Q-31`'s shape, resolved only because the duty is a
547
549
  role.
548
550
 
@@ -563,8 +565,8 @@ is not relieved (8(f)(ii)).
563
565
  protected vessel a Rule 17 duty no paragraph confers — 8(f)(iii)'s content,
564
566
  and why `none` is explicit.
565
567
  - **Undetermined term.** "Exhibiting the signals in Rule 28" is a
566
- display-compliance fact `18d1` does not read; the entry is wider (`Q-34`).
567
- - Entries: `18d1`, `8f3`.
568
+ display-compliance fact `rule:18d_i` does not read; the entry is wider (`Q-34`).
569
+ - Entries: `rule:18d_i`, `rule:8f_iii`.
568
570
 
569
571
  ### INV-18f1 — the WIG craft keeps well clear
570
572
 
@@ -575,7 +577,7 @@ in flight near the surface, she holds `keep-clear` with respect to every other
575
577
  vessel — keeps well clear and avoids impeding their navigation — and the other
576
578
  holds `none`. `keep-clear` is outside the give-way/stand-on pairing and Rule 16.
577
579
 
578
- - Entries: `18f1`.
580
+ - Entries: `rule:18f_i`.
579
581
 
580
582
  ---
581
583
 
@@ -601,9 +603,9 @@ not jointly exhaustive (`INV-19a-third-state`); Rule 19 supplements Section I
601
603
  cross it and back inside one encounter, and no paragraph says what becomes of
602
604
  a 13(d) latch or a Rule 17 phase: `Q-55`.
603
605
  - **Undetermined term.** "In or near an area of restricted visibility" has no
604
- fact (3(l)'s atmospheric condition is nowhere in `data/facts.json`), so `19a`
606
+ fact (3(l)'s atmospheric condition is nowhere in `data/facts.json`), so `rule:19a`
605
607
  selects Section III for any pair not in sight — wider — and records a gap.
606
- - Entries: `11`, `19a`, complementary on `pair:geo:in_sight`.
608
+ - Entries: `rule:11`, `rule:19a`, complementary on `pair:geo:in_sight`.
607
609
 
608
610
  ### INV-19a-noroles — Section III has no give-way and no stand-on vessel
609
611
 
@@ -633,7 +635,7 @@ excludes Section II, 19(a) excludes Rule 19, and only Section I governs.
633
635
  - **Why written down.** A hole forced by both scope paragraphs, which "not
634
636
  Section II, so Section III" will not have. The model closes it by dropping
635
637
  19(a)'s second conjunct — a decision, not a reading: `Q-56`.
636
- - Entries: `19a`'s `gap`.
638
+ - Entries: `rule:19a`'s `gap`.
637
639
 
638
640
  ### INV-19b — safe speed, and engines ready
639
641