colregs 0.1.0 → 0.2.0

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 (34) hide show
  1. package/LICENSE +194 -26
  2. package/PROVENANCE.md +46 -13
  3. package/README.md +324 -20
  4. package/data/applicability.json +1247 -236
  5. package/data/deprecated-identifiers.json +7 -0
  6. package/data/facts.json +549 -112
  7. package/data/geometry.json +34 -34
  8. package/data/images.json +52 -26
  9. package/data/lights.json +36 -36
  10. package/data/rules.json +887 -104
  11. package/docs/adr/0001-name-and-jurisdiction-model.md +194 -0
  12. package/docs/adr/0002-wig-gate-and-jurisdiction-field.md +77 -0
  13. package/docs/adr/0003-language-as-a-dimension.md +220 -0
  14. package/docs/adr/0004-licence-layering.md +78 -0
  15. package/docs/adr/0005-rule-categories-and-the-situation-record.md +323 -0
  16. package/docs/adr/0006-json-schema-and-identifier-diff.md +95 -0
  17. package/docs/conventions.md +27 -0
  18. package/docs/gates.json +84 -0
  19. package/docs/identifiers.md +435 -0
  20. package/docs/requirements.md +1320 -0
  21. package/docs/verification/2026-08-30-q6-q8.md +278 -0
  22. package/docs/verification/2026-09-05-rule27f-rule28-anchor-scenarios.md +330 -0
  23. package/fixtures/applicability-fixtures.json +498 -138
  24. package/fixtures/situation-fixtures.json +3603 -0
  25. package/package.json +27 -5
  26. package/schema/applicability-fixtures.schema.json +47 -0
  27. package/schema/applicability.schema.json +401 -0
  28. package/schema/deprecated-identifiers.schema.json +29 -0
  29. package/schema/facts.schema.json +793 -0
  30. package/schema/geometry.schema.json +239 -0
  31. package/schema/images.schema.json +53 -0
  32. package/schema/lights.schema.json +131 -0
  33. package/schema/rules.schema.json +50 -0
  34. package/schema/situation-fixtures.schema.json +325 -0
@@ -0,0 +1,1320 @@
1
+ # colregs — design requirements
2
+
3
+ Status: **draft**, seeded 2026-08-29. This is the source of truth for what the
4
+ package must do. Coding sessions work against these IDs; tests cite them.
5
+
6
+ Requirement IDs are stable and never reused. If a requirement is dropped it is
7
+ struck through and kept, not deleted — a spec whose IDs shift silently cannot
8
+ be cited by a test.
9
+
10
+ A requirement marked **(unimplemented)** is specified but not yet built, and
11
+ nothing in the repository satisfies it today; **(unimplemented in part)**
12
+ names the clause that is outstanding. Either way it is still binding; the
13
+ marker exists so the document cannot be read as a description of what ships.
14
+ Requirements that are pure prohibitions, or constraints on a future addition
15
+ that has not happened, carry no marker — there is nothing to
16
+ implement until something tries to violate them. Gate status is not marked in
17
+ prose at all: it is data, in `docs/gates.json`.
18
+
19
+ Language: **MUST** / **SHOULD** / **MAY** in the RFC 2119 sense.
20
+
21
+ ---
22
+
23
+ ## 1. Purpose
24
+
25
+ Publish the international collision regulations, and the national amalgamations
26
+ derived from them, as language-neutral data that more than one implementation
27
+ can consume and verify against.
28
+
29
+ Two named consumers shape the design:
30
+
31
+ - **an educational app** — wants every rule, including those with no switchable
32
+ output, plus imagery and prose;
33
+ - **a switching plugin** — wants only the subset a boat can actually act on,
34
+ evaluated against live vessel state.
35
+
36
+ Neither consumer lives in this repo.
37
+
38
+ ### Non-goals
39
+
40
+ - **No inference.** The package does not decide what a vessel *is doing*.
41
+ Deriving `making_way`, propulsion or activity from sensor data belongs to a
42
+ separate consumer. This package is a pure function of a fact record.
43
+ - **No runtime.** Data and fixtures only; no evaluator ships here.
44
+ - **No advice.** The package states what the rules require. It does not tell a
45
+ mariner what to do, and carries no claim of fitness for navigation.
46
+
47
+ ---
48
+
49
+ ## 2. Definitions
50
+
51
+ | Term | Meaning |
52
+ |---|---|
53
+ | **paragraph path** | The citation unit: `27(a)(i)`, `25(d)(ii)`. Not the rule number. |
54
+ | **fact record** | A set of facts about one vessel at one moment; the input. |
55
+ | **entry** | One applicability record: predicate → lights/refs → modality → citation. |
56
+ | **modality** | `shall` / `may` / `shall-if-practicable`. |
57
+ | **jurisdiction** | A body of rules: `intl`, `us/inland`, `ca/inland`, … |
58
+ | **delta** | A jurisdiction's departures from the international text. |
59
+ | **identifier** | Any name the data is addressed by: entry id, paragraph path, light id, fact key, fact value, relation name. |
60
+
61
+ ---
62
+
63
+ ## 3. Scope and jurisdictions
64
+
65
+ - **REQ-SCOPE-1** — The package MUST model the international regulations
66
+ (COLREGS 72) as its base body of rules.
67
+ - **REQ-SCOPE-2** — Jurisdiction MUST be a first-class dimension on every
68
+ applicability entry and every rule-text record, expressed as
69
+ `<country-or-body>/<waters>` with `intl` as the reserved base value.
70
+ Examples: `intl`, `us/inland`, `us/great-lakes`, `us/western-rivers`,
71
+ `ca/inland`, `de/binnen`, `eu/cevni`.
72
+ - **REQ-SCOPE-3** — A jurisdiction MUST be expressible as a *delta*: entries
73
+ absent from a jurisdiction's data inherit from `intl`. A jurisdiction MUST
74
+ NOT require restating the whole body of rules. Inheritance is "unless
75
+ suppressed", not unconditional — see Q-11: verified Inland structure
76
+ (Rule 28 "[Reserved]") means silence-means-inherit would apply
77
+ international law where the national body deliberately has none, so no
78
+ non-`intl` jurisdiction lands before an explicit suppression mechanism
79
+ exists.
80
+ - **REQ-SCOPE-4** — Adding a jurisdiction MUST be additive. It MUST NOT require
81
+ a schema change or edits to existing `intl` entries.
82
+ - **REQ-SCOPE-5** — Geography that gates a rule (Great Lakes, Western Rivers,
83
+ a designated special anchorage area) MUST be an ordinary fact read by a
84
+ predicate, NOT a jurisdiction value of its own where the rule is a
85
+ conditional inside a wider jurisdiction.
86
+ - **REQ-SCOPE-6** — Every release MUST state, in the README, exactly which
87
+ jurisdictions and which rule parts it contains. Silence MUST NOT imply
88
+ coverage.
89
+
90
+ ### 3.1 Rule parts
91
+
92
+ Part C (Rules 20–31, lights and shapes) is v1. The structure MUST accommodate
93
+ the rest without redesign.
94
+
95
+ - **REQ-PART-1** — Part C lights MUST be complete for `intl` before any other
96
+ part or jurisdiction is added.
97
+ - **REQ-PART-2** — Day shapes MUST use the same entry model as lights, differing
98
+ only in the fixture vocabulary they emit.
99
+ - **REQ-PART-3** — Sound and light signals (Part D, Rules 32–37) SHOULD be
100
+ representable by the same entry model. Where they are not — signals are
101
+ event-triggered rather than state-derived — the divergence MUST be recorded
102
+ as an ADR before any Part D data is written.
103
+ - **REQ-PART-4** — ~~Steering and sailing rules (Part B) are OUT of v1 scope and
104
+ MAY never be modelled; they govern conduct between two vessels, not the
105
+ appearance of one, and the fact record is single-vessel by construction.~~
106
+ **Superseded by ADR 0005** (2026-09-04), and kept here rather than deleted
107
+ per the preamble's ID-stability rule. Part B remains out of v1 scope —
108
+ REQ-PART-1 still orders Part C first, and no Part B data lands with the
109
+ ADR — but "MAY never be modelled" does not survive: the obstacle it named,
110
+ a single-vessel fact record, is met by the situation record of REQ-CAT-4
111
+ without changing that record. Replaced by REQ-CAT-1..5 (§4.1).
112
+
113
+ ---
114
+
115
+ ## 4. Data model
116
+
117
+ Four layers, each independently addressable.
118
+
119
+ - **REQ-MODEL-1** — **Rule text**, verbatim, keyed by paragraph path. Text MUST
120
+ NOT be paraphrased, summarised or reflowed. Where a jurisdiction's text
121
+ differs, both MUST be stored, keyed by jurisdiction.
122
+ - **REQ-MODEL-2** — **Light definitions** (Rule 21) MUST carry colour, arc of
123
+ visibility in degrees, and range by length band (Rule 22). Jurisdictions MAY
124
+ add definitions (e.g. the US special flashing light, Inland 21(g)).
125
+ - **REQ-MODEL-3** — **Facts**: the input vocabulary. Three orthogonal axes MUST
126
+ be used, never a single flattened status enum:
127
+ - `fact:propulsion` ∈ `propulsion:power` / `propulsion:sail` /
128
+ `propulsion:oars`
129
+ - `fact:activity` ∈ `activity:none` / `activity:fishing` /
130
+ `activity:trawling` / `activity:towing` / `activity:pushing` /
131
+ `activity:being_towed` / `activity:nuc` / `activity:ram` /
132
+ `activity:ram_underwater` / `activity:cbd` / `activity:mine` /
133
+ `activity:pilot` / `activity:diving`
134
+ - `fact:position` ∈ `position:underway` / `position:anchored` /
135
+ `position:aground` / `position:moored`
136
+ plus `fact:making_way` as a boolean refining `fact:position=position:underway`,
137
+ and numeric and boolean facts (`fact:length_m`, `fact:tow_length_m`,
138
+ `fact:max_speed_kn`, `fact:composite_unit`, and the education-only facts).
139
+ Fact keys, and the values of the enumerated facts, carry a type prefix;
140
+ `docs/identifiers.md` states the scheme and why citation-derived
141
+ identifiers do not.
142
+ - **REQ-MODEL-4** — **Applicability entries**: `when` (predicate over facts) →
143
+ lights or refs → modality → citation → jurisdiction. Every entry MUST have a
144
+ stable id derived from its paragraph path (`25b`, `25d1`).
145
+ - **REQ-MODEL-5** — Gates MUST be expressed as predicates over facts
146
+ (`fact:length_m < 7`), never as pre-enumerated tuples or configuration counts. Any
147
+ count of "configurations" is an output of evaluation, never an input to the
148
+ data.
149
+ - **REQ-MODEL-6** — Entries MUST compose. Multiple entries applying to one fact
150
+ record is the normal case, not an error (Rule 28 is "in addition to" Rule 23).
151
+ - **REQ-MODEL-7** — Five relations MUST be supported:
152
+ - `rel:includes` — import another entry's **lights only**, never its predicate;
153
+ - `rel:in_lieu_of` — legal alternatives for the same fact record;
154
+ - `rel:excludes` — mutual exclusion, including across rules;
155
+ - `rel:exempts` — one entry lifting another's obligation;
156
+ - `rel:conditional_includes` — import or alternatives, gated on a predicate.
157
+ The five are not interchangeable; README.md holds the working semantics.
158
+ - **REQ-MODEL-13** — Where a condition goes is not a style choice. A
159
+ condition on whether a paragraph *applies to this vessel at all* MUST be in
160
+ the entry's predicate; a condition on *which of two applicable paragraphs
161
+ prevails* MUST be a relation between them, never a negation of the other
162
+ entry's class folded into the predicate. The test: delete the other
163
+ paragraph, and if this one is still true of the vessel, it is a relation.
164
+ Rule 28 does not speak to a vessel constrained by her draught at anchor, so
165
+ `underway` is a predicate; Rule 15 does speak to a fishing vessel under
166
+ power, and Rule 18 only displaces the role it assigns, so that is
167
+ `rel:overrides`. A gate doing a relation's job silently drops every pair the
168
+ displacing paragraph is silent about (fishing against fishing under Rule 18).
169
+ ADR 0005 §4.
170
+ - **REQ-MODEL-12** — `rel:conditional_includes` currently carries three
171
+ distinct shapes under one relation name: a bare `one_of` alternative set
172
+ (`25d2`), a gated alternative set (`when` + `one_of`, `27f`), and a gated
173
+ import with its own citation (`when` + `rel:includes` + `cite`, `29a`). Which
174
+ behaviour applies is inferred from which keys are present. This is a
175
+ **soft** requirement — the data is correct today and the tests cover it,
176
+ so nothing is broken. It is recorded because a third jurisdiction adding
177
+ a fourth shape is how the inference stops being obvious, and because a
178
+ relation whose semantics depend on key presence cannot be validated by
179
+ schema. Before a non-`intl` jurisdiction lands, either split the relation
180
+ or add an explicit discriminant. Recorded as accepted risk until then
181
+ (Q-10).
182
+ - **REQ-MODEL-8** — Alternatives MUST be first-class. Where the rules permit a
183
+ choice, the data MUST express all lawful options with their differing
184
+ modalities and gates, and MUST NOT pick one.
185
+ - **REQ-MODEL-9** — A decode table from SignalK `navigation.state` to the three
186
+ axes MUST ship with the package. Its lossy cases MUST be enumerated in data,
187
+ not prose — at minimum, the flat enum cannot express fishing-at-anchor.
188
+ - **REQ-MODEL-10** — **Identifiers are immutable once published.** An
189
+ identifier (§2) that has shipped in a released version MUST NOT be
190
+ renamed, reused, or repointed. Specifically:
191
+ - **Adding** an identifier is always permitted, at any version.
192
+ - **Deprecating** one is permitted: it MUST keep denoting what it always
193
+ denoted, MUST be marked deprecated in data with the version that
194
+ deprecated it, and MUST NOT be removed in the same major version.
195
+ - **Mutating** one is forbidden. This covers the obvious case (renaming
196
+ `25b`) and the dangerous quiet one: an identifier keeping its spelling
197
+ while changing what it denotes — a paragraph path repointed at
198
+ different text, a fact value narrowed, a light id reassigned. A
199
+ consumer cannot detect this, and every stored citation becomes silently
200
+ wrong.
201
+ - **Reuse after removal** is forbidden outright. A retired identifier is
202
+ retired permanently; its spelling MUST NOT be reissued with a new
203
+ meaning in any later version.
204
+
205
+ Where a renumbering upstream forces a genuine collision, the resolution
206
+ is a **new identifier plus a deprecation**, never a repoint. Renaming or
207
+ removing an identifier is a major version (REQ-PKG-4); repointing one is
208
+ not a version event at all, because it is not permitted.
209
+
210
+ **Immutability baseline: `0.1.1`.** The prohibitions above bind every
211
+ identifier present in the first version released *after* `colregs@0.1.1`,
212
+ and every identifier introduced from then on. Identifiers as they stood
213
+ in `0.1.1` and earlier are outside the baseline. The reason, recorded so
214
+ it is not mistaken for convenience later: `0.1.1` was published
215
+ 2026-08-29, the day the package was seeded, before the identifier review
216
+ this requirement itself calls for had been done and before any consumer
217
+ existed. Read without a baseline, the requirement froze the vocabulary at
218
+ the moment of its first accidental publication and forbade the one review
219
+ it was written to make possible — including the vocabulary type-prefixing
220
+ that resolved a live namespace collision (`docs/identifiers.md`). That is
221
+ a defect in the requirement, not a licence to skip the review.
222
+
223
+ The baseline is **set exactly once**. It MUST NOT be moved, raised,
224
+ re-stated in a later version, or joined by a second baseline clause. This
225
+ clause is the whole of the exception; there is no mechanism for granting
226
+ another. Without that, "move the baseline" is a silent escape hatch from
227
+ REQ-MODEL-10 and the exception becomes the pattern — a specification that
228
+ can suspend its own prohibition by editing one number is advisory, not
229
+ normative. One recorded exception is a correction; a second is a policy.
230
+
231
+ `test/data.test.mjs` pins the baseline literal and asserts it is stated
232
+ exactly once. A build has no access to git history, so it cannot see the
233
+ number being *edited* in place; a test that reconstructed history to
234
+ check would cost more than it is worth and would still pass on a rewritten
235
+ history. What it can refuse is a **second** baseline clause, which is the
236
+ form the escape hatch actually takes — nobody deletes the recorded reason
237
+ for the first exception in order to grant themselves a second. Editing
238
+ the pinned literal is possible, but it is no longer silent: it fails the
239
+ suite and must be done deliberately, in a reviewable diff.
240
+
241
+ **Recorded review — identifier audit, 2026-08-30.** REQ-MODEL-10 binds a
242
+ vocabulary that had never been reviewed as a vocabulary. The audit that
243
+ the baseline authorises is that review; `docs/identifiers.md` states what
244
+ it changed. What it examined and deliberately did **not** change is
245
+ recorded here so it is not re-opened as an oversight:
246
+ - **The entry-id suffix taxonomy** — `23a1`, `24a-m2`, `24a-rest`,
247
+ `26b-mw`, `30d-red`. The suffixes are not drawn from one scheme
248
+ (ordinal, masthead count, fact abbreviation, colour) because the
249
+ paragraphs they disambiguate do not divide on one axis. A uniform
250
+ scheme would have to be ordinal, which would make every id opaque to
251
+ the reader who has the rule text in front of them, for no gain to a
252
+ machine that only ever compares them for equality. Kept as they are.
253
+ - **`24a-m2` / `24a-m3`** — the two-or-three masthead split. The
254
+ cardinality is stated in 24(a)(i) itself ("two masthead lights … three
255
+ such lights" by tow length), so the suffix names something the law
256
+ names, not a modelling convenience of this package.
257
+ - **`nuc`, `cbd`, `ram`, `ram_underwater`** — kept unspelled as terms of
258
+ art; see `docs/identifiers.md` for the reasoning and the trap in `ram`.
259
+ - **REQ-MODEL-11** — Deprecated identifiers MUST be recorded as data — a
260
+ registry naming each retired identifier, what it denoted, the version that
261
+ deprecated it, and its replacement where one exists. Prose in a changelog
262
+ MUST NOT stand in for it: a consumer pinned to an old version needs to
263
+ resolve a stale identifier mechanically. The registry is
264
+ `data/deprecated-identifiers.json`, schema-validated
265
+ (`schema/deprecated-identifiers.schema.json`, ADR 0006); it ships empty
266
+ until the first identifier is retired.
267
+
268
+ ### 4.1 Rule categories and the situation record
269
+
270
+ ADR 0005. Everything in this subsection is **pencil** (`docs/conventions.md`):
271
+ any session may change it for a better idea, logging the change. It is
272
+ recorded as a requirement because it is what the package has decided to build
273
+ towards, not because the shape is settled.
274
+
275
+ - **REQ-CAT-1** **(unimplemented in part — `category` is carried by the
276
+ two-subject entries and defaulted for the rest; no record in `rules.json`
277
+ carries one, so a paragraph with no entry is uncategorised)** —
278
+ Every rule paragraph record MUST carry exactly one `category` from the
279
+ closed set `scope`, `definition`, `standard`, `display`,
280
+ `classification`, `precedence`, `conduct`, `care`, `meta`. The field
281
+ defaults to `display`, so existing entries are correct unedited. CI MUST
282
+ fail on a value outside the set. Where a paragraph plays a second role,
283
+ that role MUST be expressed as a relation (REQ-CAT-3), never as a second
284
+ category.
285
+ - **REQ-CAT-2** — `care` and `meta` paragraphs (Rules 2(a) and 2(b)) MUST NOT
286
+ be applicability entries. They MUST be recorded in a registry sibling to
287
+ `known_omissions`, stating that the package represents them and evaluates
288
+ neither. CI MUST fail on a `care` or `meta` paragraph that appears as an
289
+ entry.
290
+ - **REQ-CAT-3** **(unimplemented in part — `shall-not` has no entry yet)** —
291
+ The modality vocabulary MUST admit `shall-not` and `shall-not-impede`
292
+ alongside `shall`, `may`, `shall-if-practicable`, `conditional` and
293
+ `exempt`, and the relation vocabulary MUST admit `rel:overrides` as a
294
+ sixth verb beside REQ-MODEL-7's five. Both remain closed sets; CI MUST
295
+ fail on a value outside them, and on a cycle in `rel:overrides`.
296
+ - **REQ-CAT-4** — A two-subject rule MUST read a **situation record**:
297
+ two per-vessel fact records, a kinematic state per vessel, relative
298
+ geometry, and history. The per-vessel fact record MUST NOT change to accommodate it, and kinematic
299
+ state MUST be a distinct fact class — a consumer that reads only `display`
300
+ entries MUST NOT be required to supply one. Adding the situation record
301
+ MUST leave every existing fixture valid unedited.
302
+ - **REQ-CAT-5** — A situation MUST NOT be expressible in the current
303
+ single-vessel fixture format. Before any two-subject entry lands, the fixture schema MUST be
304
+ extended to carry a situation and to name each subject unambiguously, and
305
+ the extension MUST be backward-compatible with the fixtures published
306
+ today (REQ-VERIFY-1).
307
+ - **REQ-CAT-6** — The situation record MUST be declared in
308
+ `data/facts.json` under `situation`, and MUST address each vessel's facts
309
+ through the subject namespace of `docs/identifiers.md`:
310
+ `<subject>:<class>:<key>`, subject from `own`/`other`/`pair`, class from
311
+ `fact`/`kin`/`geo`/`hist`/`env`. A key with no subject segment MUST mean `own:`,
312
+ so that every predicate and fixture published today is a valid situation
313
+ predicate unedited. `own:fact:*` and `other:fact:*` MUST resolve to the
314
+ per-vessel fact record key for key, with no key renamed or copied.
315
+ `pair` MUST carry only classes whose facts are symmetric between the two
316
+ vessels; `env` — where the encounter is happening — is `pair`-only for that
317
+ reason and MUST NOT appear under a vessel. Every fact in the `kin`, `geo` and `hist` classes MUST carry
318
+ `type`, `cite`, `actuable` and `signalk` like the existing scalars, and a
319
+ `null` `cite` MUST carry `cite_pending` naming the paragraph it awaits, or
320
+ an explicit `null` where no paragraph will ever justify it. CI MUST fail
321
+ on a subject or class outside the declared sets, on a `pair` class that is
322
+ not symmetric, and on a fact record key that does not survive the subject
323
+ prefix.
324
+ - **REQ-CAT-7** — Situation fixtures MUST live in a file separate from
325
+ `fixtures/applicability-fixtures.json`, which MUST remain byte-identical
326
+ (REQ-VERIFY-1). Each case MUST carry a `situation` whose every key
327
+ resolves in the namespace of REQ-CAT-6 to a fact declared in
328
+ `data/facts.json`, and a `status` from a closed set. An element of
329
+ `expect` MUST be either a bare entry id — the published one-subject form,
330
+ asserting nothing about modality — or `{entry, modality}` naming the
331
+ modality that entry is expected to carry, which is what Q-5 needs. A case
332
+ whose `status` is `illustrative` MUST assert no entries and MUST NOT join
333
+ the fixture replay; it fixes the shape and the namespace before the
334
+ entries exist. CI MUST fail on an undeclared fact, an unresolvable key, an
335
+ unknown entry id, an unknown modality, and on an `illustrative` case that
336
+ names an entry.
337
+
338
+ - **REQ-CAT-8** — A two-subject entry MUST state an `effect` and MUST NOT
339
+ state `lights`. For a `precedence` entry the effect MUST be a role per
340
+ subject, `{own, other}`, from the closed set `give-way`, `stand-on`,
341
+ `shall-not-impede`, `keep-clear`, `none`; for a `scope` entry it MUST name
342
+ the part, the section and the rules that section governs; for a
343
+ `classification` entry it MUST carry exactly one key, either `encounter`
344
+ from the closed set `head-on`, `crossing`, `overtaking`, `none`, or
345
+ `risk_of_collision`, whose only value is `true`. `stand-on` MUST
346
+ appear only as the counterpart of `give-way`, and the counterpart of
347
+ `shall-not-impede` MUST be `none` — Rule 8(f)(iii) is why. CI MUST fail on a
348
+ role or an encounter outside its set, on an effect whose shape does not match
349
+ its category, and on a `rel:overrides` that resolves to no entry, points at an
350
+ entry of a different category, or closes a cycle. `✎` pencil, with the rest of
351
+ §4.1.
352
+ - **REQ-CAT-9** — The `classification` entries for Rules 13, 14 and 15 MUST
353
+ **partition** the pair's relative geometry: for any situation those entries
354
+ all reach, exactly one encounter type MUST apply — never two, never none.
355
+ The crossing sector MUST NOT be enumerated; it MUST be derived, as the
356
+ negation of the other two, so that a single constraint object states each
357
+ sector and its complement. Any numeric threshold a Part B predicate needs
358
+ which the Rules do not state MUST be declared once in `data/facts.json`
359
+ under `situation.constants`, with its status under `docs/conventions.md`
360
+ and, where pencilled, what would settle it; an entry MUST read the declared
361
+ constant rather than repeat the literal. CI MUST sweep both subjects'
362
+ relative bearings and fail on any bearing in two encounter types or in none,
363
+ on either edge of 13(b)'s sector falling on the wrong side, and on any entry
364
+ whose threshold does not match the declared constant. `✎` pencil, with the
365
+ rest of §4.1.
366
+
367
+ ---
368
+
369
+ ## 5. Languages and localization
370
+
371
+ COLREGS 72 is multi-lingual at the source: English and French are equally
372
+ authentic treaty texts, Spanish and Russian official translations were
373
+ deposited with the original, Arabic and Chinese texts exist through IMO's
374
+ official languages, and many states gazette their own legally binding
375
+ translation. (Recalled, not yet verified against the primary sources — Q-6.)
376
+ See ADR 0003.
377
+
378
+ - **REQ-LANG-1** **(unimplemented — no language dimension exists in the
379
+ data)** — Language MUST be a dimension orthogonal to jurisdiction,
380
+ identified by BCP 47 tags. Which body of rules applies and which text of
381
+ them is displayed are independent questions; neither MUST ever be inferred
382
+ from the other, and no property beyond the language of the text — not
383
+ source, audience, nor legal applicability — MUST be inferred from a tag.
384
+ - **REQ-LANG-2** — Identifiers — entry ids, fact values, light ids,
385
+ paragraph paths, relation names — MUST be language-neutral and MUST NOT
386
+ be localized. Translations attach to identifiers; they never replace them.
387
+ Identifiers are schema keywords, not display strings: each vocabulary
388
+ distinguishes machine identifier, display label (catalog), and definition,
389
+ and renaming an identifier is a breaking change (REQ-PKG-4). Immutability
390
+ itself is REQ-MODEL-10; this requirement adds only that identifiers are
391
+ never localized.
392
+ - **REQ-LANG-3** **(unimplemented — rule text is one untagged ruleset, not a
393
+ corpus per jurisdiction × language × source)** — Rule text MUST be storable
394
+ as a **corpus** per (jurisdiction × language × source), keyed by paragraph
395
+ path, holding at most one text per path, with corpus-level provenance and
396
+ one declared status tier:
397
+ - `authentic` — identified by the governing instrument itself as an
398
+ equally authentic text (a claim of the instrument, never this repo's
399
+ assessment);
400
+ - `official` — an official translation published or deposited through the
401
+ instrument's depositary organization;
402
+ - `national` — a state's legally binding published text;
403
+ - `community` — informational, no legal standing.
404
+ Tier is a property of the source, not the language. REQ-MODEL-1's verbatim
405
+ rule applies per corpus, against that corpus's own source.
406
+ - **REQ-LANG-4** — Adding a language MUST be additive: no schema change, no
407
+ edits to existing corpora or catalogs (the language mirror of REQ-SCOPE-4).
408
+ - **REQ-LANG-5** **(unimplemented — no coverage declaration, and the CI checks
409
+ are unwritten)** — Corpora MAY be partial. Coverage MUST be declared in
410
+ machine-readable form, and CI MUST fail on a corpus key that does not
411
+ resolve to a known paragraph path, and on a corpus filename that disagrees
412
+ with the file's internal metadata. Silence MUST NOT imply coverage (the
413
+ language mirror of REQ-SCOPE-6).
414
+ - **REQ-LANG-6** **(unimplemented — no display catalogs exist)** — Display
415
+ strings for the identifier vocabularies (light names, fact-value labels,
416
+ modality labels, image captions) MUST be addressable via stable string keys
417
+ with per-language catalogs, separate from legal corpora. Catalog entries are
418
+ static strings: no interpolation, plural or gender grammar — message
419
+ composition belongs to the consumer's i18n system, and this package MUST NOT
420
+ grow one. Each catalog MUST carry lightweight provenance: contributors,
421
+ reviewers, review date, licence. Maintainer notes inside structural files
422
+ are working documentation, not display strings, not part of the localization
423
+ surface, and stay untranslated.
424
+ - **REQ-LANG-7** **(unimplemented in part — no fallback policy is encoded, but
425
+ the mixed-corpus statement is missing from the package documentation)** — The
426
+ package MUST NOT encode a language fallback policy, and MUST NOT silently
427
+ substitute one corpus for another. Text is only addressable inside a corpus,
428
+ so every textual unit a consumer retrieves is attributable to its corpus;
429
+ package documentation MUST state that a mixed-corpus rendering is never a
430
+ single authoritative edition. Choice and fallback beyond that are the
431
+ consumer's (the language mirror of REQ-CONS-3).
432
+ - **REQ-LANG-8** — A `community`-tier corpus MUST record who produced and
433
+ who reviewed it. Machine translation without named human review MUST NOT
434
+ be accepted.
435
+ - **REQ-LANG-9** **(unimplemented — the shipped text declares no normalization
436
+ form)** — Verbatim (REQ-MODEL-1) is defined at the Unicode level: each
437
+ corpus MUST declare the normalization form applied to its text (NFC unless
438
+ declared otherwise) and MUST NOT insert or strip bidi control characters,
439
+ localize numerals, punctuation, units or quotation marks, or otherwise "fix"
440
+ the source text. Rendering direction is a consumer concern and MUST stay out
441
+ of the data.
442
+ - **REQ-LANG-10** **(unimplemented — neither the skeleton nor the ruleset
443
+ declares an amendment state)** — The structural skeleton MUST declare, as
444
+ data, the amendment state it consolidates (e.g. "COLREGS 72 as amended
445
+ through …"). Every corpus MUST declare the amendment state its source
446
+ reflects. The two MAY differ — a corpus transcribed from an older
447
+ consolidation is legitimate — but the difference MUST be machine-visible,
448
+ never silent.
449
+
450
+ ---
451
+
452
+ ## 6. Provenance and licensing
453
+
454
+ - **REQ-PROV-1** — Every text and image asset MUST record its source, the date
455
+ retrieved, and its licence or public-domain basis.
456
+ - **REQ-PROV-2** — A jurisdiction MUST NOT be added until its reproduction terms
457
+ have been checked against the primary source and recorded. Recalled or assumed
458
+ terms are not sufficient.
459
+ - **REQ-PROV-3** — Where a licence requires attribution (e.g. OGL, CC BY), the
460
+ attribution text MUST ship in the package, not only in the repo.
461
+ - **REQ-PROV-4** — Code licence and data licence MUST be stated separately. The
462
+ code licence does not cover third-party scans.
463
+ - **REQ-PROV-5** — Images MUST be addressable as data: an image record per file,
464
+ naming what it illustrates by entry id or paragraph path. Unexplained filename
465
+ prefixes are a provenance defect.
466
+ - **REQ-PROV-6** **(unimplemented — source identity is a prose string and
467
+ rights are one flat field)** — Source identity MUST be structured data —
468
+ publisher, title, edition, publication and effective dates, URL, retrieval
469
+ date — not a prose string. Rights MUST be recorded separately for the source
470
+ text, for the basis on which this package redistributes it, and for the
471
+ licence the package distributes under; one flat `licence` field MUST NOT
472
+ stand in for all three. A translation MAY carry translator rights even where
473
+ the underlying instrument is public domain, and that check is part of
474
+ REQ-PROV-2.
475
+ - **REQ-PROV-7** — An external contribution MUST NOT be merged from a
476
+ contributor who has not agreed to the contribution terms in
477
+ `CONTRIBUTING.md`, which state that opening a pull request constitutes
478
+ agreement. Those terms MUST include both (a) a DCO-style certification
479
+ that the contributor has the right to submit the work, and (b) a licence
480
+ grant to the maintainer sufficient to relicense the compilation. (b) is
481
+ what holds GATE-6 open; a bare DCO certifies origin and grants nothing,
482
+ so it does not.
483
+
484
+ ---
485
+
486
+ ## 7. Verification
487
+
488
+ - **REQ-VERIFY-1** — Fixtures MUST pair fact records with the entries that apply,
489
+ and MUST be consumable by an implementation in any language.
490
+ - **REQ-VERIFY-2** — A **drift test** MUST cross-check the forward direction
491
+ (fact record → lights) against a reverse direction (observed lights →
492
+ candidate fact records), so the two cannot silently disagree.
493
+ - **REQ-VERIFY-3** — Every applicability entry MUST be covered by at least one
494
+ fixture that exercises it, and at least one that excludes it.
495
+ - **REQ-VERIFY-4** — Every `rel:in_lieu_of` and `rel:excludes` relation MUST have a
496
+ fixture demonstrating it.
497
+ - **REQ-VERIFY-5** — Predicates MUST be tested at their boundaries. Every numeric
498
+ gate MUST have fixtures immediately either side of the threshold.
499
+ - **REQ-VERIFY-6** — CI MUST fail on a fixture that references an entry id, a
500
+ light definition or a paragraph path that does not exist.
501
+ - **REQ-VERIFY-7** **(unimplemented — the check is unwritten; it depends on
502
+ REQ-MODEL-11)** — CI MUST fail on an identifier present in the deprecation
503
+ registry (REQ-MODEL-11) that has reappeared in the live data with a
504
+ different denotation, and on a registry entry whose replacement does not
505
+ resolve. Immutability (REQ-MODEL-10) is otherwise a cross-version property
506
+ no single build can check; the registry is what makes the checkable part
507
+ checkable.
508
+ - **REQ-VERIFY-8** — A situation record's relative geometry MUST be checkable
509
+ against its kinematic state. `data/facts.json` MUST declare, under
510
+ `situation.geometry.consistency`, the equations relating the two relative
511
+ bearings, the range, the CPA, the TCPA and the bearing rate to the two
512
+ positions, headings and speeds, with the tolerances a comparison uses. CI
513
+ MUST fail on a situation fixture that states quantities on both sides of one
514
+ of those equations and violates it, and every situation the suite constructs
515
+ for a sweep MUST satisfy the declaration. A property asserted over
516
+ constructed situations — REQ-CAT-9's partition, the precedence properties —
517
+ MUST be asserted over consistent situations only; where a property holds on
518
+ consistent situations and fails on an inconsistent one, the suite MUST pin
519
+ that situation as one the check rejects, so the exclusion is visible rather
520
+ than assumed. `✎` pencil, with `Q-48`.
521
+
522
+ ---
523
+
524
+ ## 8. Packaging
525
+
526
+ - **REQ-PKG-1** — Zero runtime dependencies.
527
+ - **REQ-PKG-2** — Data MUST be consumable without a JavaScript runtime: plain
528
+ JSON, no code-carrying formats.
529
+ - **REQ-PKG-3** — The published package MUST contain data, images, fixtures and
530
+ provenance, and MUST NOT contain build tooling or source scans.
531
+ - **REQ-PKG-4** — Breaking changes to entry ids, fact vocabulary or relation
532
+ semantics MUST be a major version.
533
+ - **REQ-PKG-5** — Package keywords MUST include the terms a searcher would
534
+ actually use for each covered jurisdiction (`colregs`, `navrules`,
535
+ `inland-rules`, `33-cfr-83`, `cevni`).
536
+
537
+ ---
538
+
539
+ ## 9. Consumer contracts
540
+
541
+ - **REQ-CONS-1** — The switching subset MUST be derivable from the data by
542
+ filtering, not by a separate hand-maintained list. A consumer MUST be able to
543
+ select actuable entries mechanically.
544
+ - **REQ-CONS-2** — Education-only facts MUST be marked as such in the fact
545
+ vocabulary, so a switching consumer can assert it never reads them.
546
+ - **REQ-CONS-3** — Final selection among lawful alternatives belongs to the
547
+ consumer (for switching, the vessel's fixture map). The package MUST NOT
548
+ encode a preference.
549
+ - **REQ-CONS-4** — The package MUST NOT assume a SignalK consumer beyond the
550
+ optional decode table of REQ-MODEL-9.
551
+
552
+ ---
553
+
554
+ ## 10. Reversibility gates
555
+
556
+ A pre-1.0 data package can decline a design and still adopt it later. That
557
+ stops being true at a specific, nameable event — a version tag, a second
558
+ corpus, a first consumer. A declined design whose cost rises over time is
559
+ therefore not "closed"; it is **timed**, and the timing is part of the
560
+ specification until 1.0.
561
+
562
+ - **REQ-GATE-1** — Every design declined on the grounds that it can be
563
+ adopted later MUST be recorded as a gate below, naming the **closing
564
+ event** after which adoption stops being cheap, and the **reopening
565
+ trigger** — the observable fact that forces reconsideration. A decline
566
+ recorded without both is incomplete.
567
+ - **REQ-GATE-2** — A gate MUST NOT be closed by the passage of time or by
568
+ a maintainer's judgement alone. It closes when its named event occurs, or
569
+ when its trigger fires and the decision is re-taken in a new ADR.
570
+ - **REQ-GATE-3** — Tagging 1.0 MUST be blocked until every gate whose
571
+ closing event is *the 1.0 tag* has been re-taken deliberately: confirmed
572
+ in a new ADR, or adopted. Inheriting one by default is the failure this
573
+ section exists to prevent.
574
+ - **REQ-GATE-4** — A gate that is adopted or confirmed MUST be struck
575
+ through here, not deleted, and MUST cite the ADR that settled it
576
+ (the gate mirror of the ID-stability rule in this document's preamble).
577
+
578
+ ### Open gates
579
+
580
+ Each gate names the declined design, the closing event, and the trigger.
581
+
582
+ - **GATE-1 — `paragraph_id` split from `citation_path`**
583
+ (ADR 0003, declined; REQ-LANG-3, REQ-PKG-4).
584
+ *Closing event*: the 1.0 tag. Before it, splitting the two is a
585
+ mechanical rewrite of cross-references in a package with no stable-API
586
+ promise. After it, every consumer's lookup path breaks.
587
+ *Trigger*: any paragraph path that keeps its spelling while changing what
588
+ text it denotes — the mutation REQ-MODEL-10 forbids outright, which is
589
+ why the collision has to be resolved in the schema rather than absorbed.
590
+ Two ways that happens, both now checked against primary sources
591
+ ([verification](verification/2026-08-30-q6-q8.md)):
592
+ - **Amendment renumbering — verified real, not hypothetical.** The
593
+ original "believed never since 1972" premise is refuted: A.910(22)
594
+ (2001) displaced the old `23(c)` to `23(d)` when WIG lighting took its
595
+ path, and A.464(XII) (1981) relettered `24(g)`→`24(h)` and
596
+ `27(d)(iv)`→`(iii)`. Two of seven amendments renumbered Part C.
597
+ - **Cross-jurisdiction divergence — verified present, and NOT this
598
+ gate's trigger.** The 15 same-path-different-text rows in the 33 CFR 83
599
+ diff are the *jurisdiction dimension working as designed* (REQ-MODEL-1
600
+ stores both texts, keyed by jurisdiction) — the effective identifier is
601
+ (jurisdiction, path), so they are not REQ-MODEL-10 mutations. This
602
+ route is retired, not merely dormant: divergence between two
603
+ jurisdictions at one point in time can never fire the trigger. The
604
+ residue that is genuinely structural — `23(d)(i)` having no Inland
605
+ counterpart path, Rule 28 "[Reserved]" — is a delta-model problem
606
+ (Q-11), not a repoint.
607
+ - **National renumbering across releases — live, and the second real
608
+ route.** A national body renumbering its own amalgamation is the same
609
+ within-jurisdiction repoint as an IMO amendment: if `us/inland 24(c)`
610
+ denotes different text in vN+1 than it did in vN, that is a mutation
611
+ REQ-MODEL-10 forbids, jurisdiction dimension or not. The trigger is
612
+ *within one jurisdiction across releases*, on either route — not
613
+ between jurisdictions at one moment.
614
+ *Ruling, 2026-08-30*: the gate does not flip on today's data — no
615
+ published path has changed denotation. But with the "rare, believed
616
+ never" premise gone (real base rate: twice in seven amendments), the
617
+ pre-1.0 re-take **leans adopt**, decided in the second-jurisdiction
618
+ bundle alongside GATE-2 and Q-10. Final call is the maintainer's at that
619
+ point.
620
+ *This gate is open on timing, not on outcome.* A real-world respelling —
621
+ a citation keeping its spelling while denoting different text —
622
+ **forces** the split: REQ-MODEL-10 forbids repointing and forbids reuse,
623
+ so no third option exists. The re-take, if the trigger fires, is
624
+ therefore predetermined:
625
+ - The split lands and it is a **major version**.
626
+ - Identifiers in the new major are **defined incompatible** with
627
+ identifiers of the prior major. A consumer MUST NOT assume a
628
+ same-spelled path denotes the same text across the boundary.
629
+ - An optional **prior → new identifier mapping** MAY ship alongside.
630
+ REQ-MODEL-11's deprecation registry, with its replacement pointers, is
631
+ the seed of that mapping and exists for this reason.
632
+ What remains open is only whether the trigger ever fires, and whether
633
+ the split lands pre-emptively or on demand.
634
+ *Re-take required before 1.0* (REQ-GATE-3), and re-checked in the
635
+ second-jurisdiction bundle — justified by Q-11 and GATE-2, no longer by
636
+ a cross-jurisdiction trigger route.
637
+
638
+ - **GATE-2 — instrument → edition → corpus as first-class layers**
639
+ (ADR 0003, declined; the adopted 80% is REQ-LANG-10).
640
+ *Closing event*: the second corpus of any one jurisdiction — which,
641
+ read against ADR 0003's sequencing, means **the first non-English
642
+ corpus**, not a distant milestone. A French or Finnish text of `intl`
643
+ is a second corpus of `intl`. With one corpus, re-homing it under an
644
+ edition parent is a single file move; the cost scales with
645
+ corpora × languages immediately thereafter.
646
+ *Trigger*: a jurisdiction publishing two editions in force
647
+ concurrently — an old and a new text running in parallel through a
648
+ transition period. REQ-LANG-10's declared amendment state makes such a
649
+ pair machine-visible, which is what gives this trigger a foothold.
650
+ *Re-take required before translation #1 lands* — the edition-layer
651
+ decision is due at the first added translation, not at 1.0.
652
+
653
+ - **GATE-3 — legal-status × translation-status as two enums**
654
+ (ADR 0003, half-adopted: one tier for legal authority in REQ-LANG-3,
655
+ translation provenance as structured metadata in REQ-LANG-8/REQ-PROV-6).
656
+ *Closing event*: the first `community`-tier translation of a `national`
657
+ corpus. Until one exists, the four-way combination the split exists to
658
+ express is hypothetical.
659
+ *Trigger*: a real corpus whose legal tier and translation tier disagree
660
+ in a way a consumer must filter on, and cannot from the metadata as
661
+ structured.
662
+
663
+ - **GATE-4 — a package-encoded language fallback chain**
664
+ (ADR 0003, declined; replaced by REQ-LANG-7's no-silent-substitution).
665
+ *Closing event*: none — this door opens outward. Loosening a strict rule
666
+ is additive; tightening one later breaks consumers. Recorded so the
667
+ asymmetry is not re-discovered as an argument for adopting it early.
668
+ *Trigger*: none anticipated. Consumer demand for a *documented,
669
+ non-normative* recommended chain, shipped outside the data, would not
670
+ reopen it.
671
+
672
+ - **GATE-5 — a CI-enforced terminology glossary**
673
+ (ADR 0003, declined for legal corpora; REQ-MODEL-1).
674
+ *Closing event*: none for legal corpora — the decline follows from
675
+ verbatimness and does not get cheaper or dearer with time. For display
676
+ catalogs it is contributor guidance, gated on the contribution docs
677
+ existing at all.
678
+ *Trigger*: none. A glossary contradicting a verbatim source is a defect
679
+ in the glossary.
680
+
681
+ - **GATE-6 — the compilation's outbound licence**
682
+ (ADR 0004; changed MIT → Apache-2.0 in `2669e2a` on `main`. REQ-PROV-4.)
683
+ *Closing event*: the **first merged external contribution**. Until then
684
+ the copyright holder is one person and the licence can be changed at
685
+ will. After it, relicensing needs every contributor's consent — and
686
+ soliciting translations is the express purpose of the language work, so
687
+ this door closes early and hard.
688
+ *Trigger*: deciding the data side wants CC0 or CC BY 4.0 separately from
689
+ the code (REQ-PROV-4). Tracked as Q-9; ADR 0004 settles the code licence
690
+ and deliberately leaves this open.
691
+ Two refinements the gate carries:
692
+ - Already-published npm versions are irreversibly under the licence they
693
+ shipped with. The gate governs future releases only.
694
+ - The gate is **held open deliberately**, by the mechanism adopted in
695
+ REQ-PROV-7: `CONTRIBUTING.md` terms, agreed by opening a pull request,
696
+ carrying a DCO-style certification *and* a licence grant sufficient to
697
+ relicense. The grant is the part that holds the gate open. Implied
698
+ assent is weaker than recorded assent; a CLA-assistant bot is the
699
+ upgrade path if contributors arrive (ADR 0004).
700
+ *Expected to stay open indefinitely* — external contributors are
701
+ unlikely short of major success. Recorded regardless: "unlikely" is not
702
+ a closing event (REQ-GATE-1).
703
+
704
+ Gates whose closing event is "none" are recorded because a future reader
705
+ will otherwise re-ask whether they were merely deferred. They were not.
706
+
707
+ ### Gate status is data
708
+
709
+ The gates above are mirrored in **`docs/gates.json`** — id, closing event,
710
+ trigger, status (`open` / `re-taken` / `adopted` / `declined-permanently`),
711
+ and the ADR that settled it where one exists. That file, not this prose, is
712
+ what REQ-GATE-3 is enforced against: `test/data.test.mjs` fails the build if
713
+ `package.json`'s major version is 1 or higher while any gate whose closing
714
+ event is the 1.0 tag is still `open` or cites no ADR. There is no environment
715
+ variable, skip flag, or warning-only path — undoing the block means editing
716
+ the registry in a pull request, which is the deliberate re-take REQ-GATE-3
717
+ asks for. The test also fails on a gate that appears in one place and not the
718
+ other, so the prose and the registry cannot drift apart.
719
+
720
+ ---
721
+
722
+ ## 11. Open questions
723
+
724
+ Tracked here until resolved; each becomes an ADR.
725
+
726
+ - **Q-1** — Do Part D sound signals fit the entry model, or do they need an
727
+ event dimension? Blocks REQ-PART-3.
728
+ - **Q-2** — Are the USCG scans the educational payload, or a stopgap until
729
+ light geometry is rendered from data? Affects how hard REQ-PROV-5 is pushed.
730
+ - **Q-3** — Jurisdiction licence terms are **unverified**: US public domain,
731
+ UK OGL v3.0, AU CC BY 4.0, DE §5 UrhG *amtliche Werke*, CA Reproduction of
732
+ Federal Law Order, EU/UNECE CEVNI unclear. REQ-PROV-2 blocks each until
733
+ checked against the primary source. CEVNI is the one most likely to fail.
734
+ **Verified 2026-09-05** against the primary sources for every jurisdiction
735
+ except CEVNI — see ADR 0001 Amendments (2026-09-05). CEVNI remains open:
736
+ unece.org was unreachable from the checking host, and the UN's default
737
+ terms (personal, non-commercial use only) block it until written
738
+ permission or a national transposition is chosen instead.
739
+ - **Q-4** — Two upstream SignalK spec asks are outstanding and independent of
740
+ this package: a making-way indicator, and `design.maxSpeed`.
741
+ - **Q-5** — REQ-VERIFY-5 asks for boundary fixtures on every numeric gate.
742
+ Three gates (`23a2`, `26b-mast`, `30c`'s `fact:length_m` thresholds) live only in
743
+ `modality_by`, not in the entry's `when` — they flip `shall` to `may`, not
744
+ which entries apply. The fixture format only asserts applying entry ids, not
745
+ expected modality, so there is no way to fixture these three without
746
+ extending the schema to carry expected modality per entry. Not done
747
+ speculatively; blocks a clean REQ-VERIFY-5 pass on these three gates until
748
+ decided.
749
+ **Decided in pencil 2026-09-04** (PR #22), as part of the situation
750
+ fixture schema: an element of `expect` is either a bare entry id, exactly as
751
+ today, or `{entry, modality}`. The two forms are interchangeable and a bare
752
+ id asserts nothing, so `applicability-fixtures.json` stays byte-identical
753
+ and needs no migration; REQ-CAT-7 states the rule and
754
+ `fixtures/situation-fixtures.json` carries the worked example. What is left
755
+ is not a decision but the work: writing the boundary fixtures for `23a2`,
756
+ `26b-mast` and `30c` in the new form. Pencil, so a session that finds a
757
+ better shape may change it, logging the change; settled for good by those
758
+ three fixtures actually landing.
759
+ - **Q-6** — The treaty-language facts behind §5 (en/fr authentic, es/ru
760
+ deposited translations, ar/zh via IMO official languages) are recalled, not
761
+ verified. Verify against the Convention's final clauses and IMO's current
762
+ practice before the first non-English corpus lands.
763
+ **Verified 2026-08-30** against the UNTS deposit (Vol. 1050, I-15824,
764
+ Article IX) — en/fr authentic and es/ru deposited translations both
765
+ confirmed verbatim; ar/zh confirmed as a mechanism (IMO's six official
766
+ languages) with a catalogued Chinese edition, Arabic edition indicated but
767
+ not independently re-fetched. See
768
+ [docs/verification/2026-08-30-q6-q8.md](verification/2026-08-30-q6-q8.md#claim-3-q-6--verified-arabic-component-partially).
769
+ - **Q-7** — Reproduction terms per language corpus are unverified, and this —
770
+ not translation effort — sequences the work. IMO's consolidated
771
+ six-language editions are sold publications and probably NOT reproducible;
772
+ the UNTS deposit (en/fr authentic texts) and national gazettes (Finlex,
773
+ BOE, …) are the likely lawful sources. REQ-PROV-2 blocks each corpus until
774
+ its source's terms are checked and recorded. Answerable **per language**,
775
+ not only as a whole: clearing one candidate source unblocks that corpus
776
+ alone, which is the cheap path when a demo needs a specific language
777
+ early.
778
+ - **Q-8** — Does the paragraph path survive the first national amalgamation?
779
+ GATE-1's accepted risk rests on paragraph paths being immutable
780
+ (REQ-MODEL-10) — adding and deprecating are fine, but a path that keeps its
781
+ spelling while changing what text it points at breaks every citation and
782
+ every `cite` in `applicability.json`. The threat is not primarily a future
783
+ amendment. It is the **second jurisdiction**: while `intl` is the only
784
+ populated one, path and citation are trivially identical, and the question
785
+ cannot fail. The US Inland rules deliberately parallel COLREGS rule
786
+ numbering but are known to diverge below rule level (Rules 9, 15, 24 and
787
+ 34 are the usual examples). Check 33 CFR 83 against `rules.json` paragraph
788
+ by paragraph **before `us/inland` lands**, not at 1.0.
789
+ - If paths survive, GATE-1's accepted risk is earned rather than
790
+ assumed, and the 1.0 re-take is a confirmation.
791
+ - If they do not, GATE-1 flips to *adopt*, and the split must land
792
+ before the second jurisdiction rather than after.
793
+ - Secondary, and much weaker: whether any COLREGS amendment has ever
794
+ renumbered a Part C paragraph. Recalled as never — the WIG amendment
795
+ *inserted* 23(c) rather than renumbering around it — but 50 quiet
796
+ years is not a guarantee about the next amendment. Worth a check
797
+ against the IMO amendment resolutions, not worth blocking on.
798
+
799
+ **Verified 2026-08-30**, both parts, against primary sources — see
800
+ [docs/verification/2026-08-30-q6-q8.md](verification/2026-08-30-q6-q8.md).
801
+ Primary claim (33 CFR 83 vs `data/rules.json`): of 90 Part C paths, 15
802
+ are same-spelling-different-text mutations (`22(a)/(b)/(c)`, `24(c)`,
803
+ `24(d)`, `24(f)`, `24(g)`, `24(i)`, `25(d)(i)`, `25(d)(ii)`, `25(e)`,
804
+ `26(d)`, `30(e)`), plus one structural path-mismatch (`23(d)(i)` — the
805
+ Inland equivalent sits at bare `23(d)`, not `23(d)(i)`), one whole-rule
806
+ clean absence (`28`, "[Reserved]" in Inland), and two clean intl-only
807
+ absences. Secondary claim (amendment history): also refuted — the WIG
808
+ amendment (A.910(22), 2001) explicitly renumbers, displacing the
809
+ pre-existing `23(c)` (small-vessel alternative lights) to `23(d)`; a 1981
810
+ amendment (A.464(XII)) separately relettered `24(g)`→`24(h)` and
811
+ `27(d)(iv)`→`27(d)(iii)`. Both findings feed the GATE-1 re-take, not
812
+ decided here.
813
+ - **Q-12** — REQ-MODEL-10 has a **baseline off-by-one**, recorded here
814
+ because the requirement as written forbids work already approved. It
815
+ binds any identifier that "has shipped in a released version";
816
+ `colregs@0.1.1` was published 2026-08-29, so every identifier in it is
817
+ already bound — including the ones the identifier audit reviewed and
818
+ cleared for change (type-prefixing the vocabulary class:
819
+ `light:masthead`, `activity:nuc`). Fix by naming the baseline:
820
+ immutability binds from a stated version forward, with 0.1.1 explicitly
821
+ outside it and the reason recorded. The baseline itself MUST be
822
+ immutable: stated exactly once, never moved. A movable baseline is the
823
+ quiet escape hatch from REQ-MODEL-10 — one recorded exception is a
824
+ correction; a second is a pattern. This is a defect in the requirement,
825
+ not a reason to skip the rename; it must land in the same pre-1.0 PR as
826
+ the rename it authorises, and before that PR renames anything. The audit
827
+ itself — six findings, all verified against `data/*.json`, three kept
828
+ with rationale (`nuc`/`cbd`/`ram` as terms of art, the entry-id suffix
829
+ taxonomy, `24a-m2`/`24a-m3` whose cardinality is stated in 24(a)(i)
830
+ itself) — is REQ-MODEL-10's recorded review of the identifiers it binds,
831
+ and is on PR #4.
832
+
833
+ **Resolved 2026-08-30.** REQ-MODEL-10 now states the baseline (`0.1.1`,
834
+ outside it, with the reason) and the hardening that makes the exception
835
+ safe: the baseline is set exactly once and MUST NOT be moved, raised,
836
+ re-stated, or joined by a second clause. `test/data.test.mjs` pins the
837
+ literal and asserts a single statement; the requirement records why the
838
+ cross-version half is not asserted. The three accepted findings are
839
+ written up as REQ-MODEL-10's recorded review rather than left on a pull
840
+ request, and the changed one is `docs/identifiers.md`.
841
+ - **Q-10** — Split `rel:conditional_includes`, or add a discriminant?
842
+ REQ-MODEL-12 records the overload; this is the unresolved half. Splitting
843
+ it names each behaviour and lets a schema validate the shape, at the cost
844
+ of a sixth and seventh relation verb in a vocabulary CLAUDE.md already
845
+ warns is easy to confuse. A discriminant key is cheaper and keeps the
846
+ verb count down. Neither is urgent: the trigger is the second
847
+ jurisdiction, the same as GATE-2 and Q-8, so the three should be decided
848
+ together rather than one at a time.
849
+ - **Q-9** — Is Apache-2.0 the right outbound licence for a *data*
850
+ compilation, or should the data carry CC0 / CC BY 4.0 separately from
851
+ the code (REQ-PROV-4)? ADR 0004 settles the code licence and leaves this
852
+ open deliberately — the data question was never put. GATE-6's trigger;
853
+ must be settled before the contribution path opens. The CLA/DCO half of
854
+ that question is now answered by REQ-PROV-7.
855
+ - **Q-11** — What is the delta suppression mechanism? REQ-SCOPE-3's
856
+ inheritance-by-absence is verified unsafe as stated: 33 CFR 83 leaves
857
+ Rule 28 "[Reserved]" and has no counterpart for `23(d)(ii)`/`(iii)`
858
+ ([verification](verification/2026-08-30-q6-q8.md), Claim 1), so a
859
+ `us/inland` delta that is merely silent there would inherit the `intl`
860
+ entries and assert international obligations on inland waters. A delta
861
+ needs explicit suppression records (tombstones) alongside overrides —
862
+ "this path/entry deliberately does not exist here", distinguishable from
863
+ "not yet transcribed". Decide the mechanism in the second-jurisdiction
864
+ bundle (GATE-1 re-take, GATE-2, Q-10); until then no non-`intl`
865
+ jurisdiction lands.
866
+ Also from the same verification pass, tracked on the global board rather
867
+ than here: four transcription defects in `data/rules.json` itself
868
+ (`21(a)`, `21(b)`, `23(b)`, `29(b)`) — a data fix, not a design
869
+ question.
870
+
871
+ ### From ADR 0005 (pencilled items)
872
+
873
+ Every item ADR 0005 records is pencil (`docs/conventions.md`), and each
874
+ pencilled item in the proposal behind it names what would settle it. They are
875
+ listed here, one line each, because the ADR is what makes them live. Most are
876
+ `colregs-engine`'s to settle rather than this package's; they are marked
877
+ *(engine)* where so, and this package's own are the ones that gate data.
878
+
879
+ - **Q-13** — Is `category` the right word for the field (against kind, type,
880
+ flavour, charge)? Settled by Mark, before REQ-CAT-1's field name ships.
881
+ - **Q-14** — Which category does each paragraph take? The proposal's table is
882
+ a first cut; settled paragraph by paragraph as Rules 1–19 are transcribed.
883
+ PR #24 settles the first sixteen and departs from the table once: 13(a) is
884
+ `precedence`, not `classification` (`Q-37`).
885
+ - **Q-15** — Which verification tool discharges each category (Alloy, Z3, TLA+,
886
+ STL, Rocq)? Settled by building one proof per category, not by argument.
887
+ *(engine)*
888
+ - **Q-16** — Are the invariant levels right as hard / safe / rule-level, and
889
+ are procedural and physical levels needed? Settled by the first level-3
890
+ invariant that does not fit. *(engine)*
891
+ - **Q-17** — What separation distance *d* defines the "safe" level? Fixed to
892
+ one value to start; settled by the sensitivity matrix of Q-22. *(engine)*
893
+ - **Q-18** — What is the dynamics model, and what is the list of dynamics
894
+ classes (tanker, ferry, yacht, …)? Settled by the first two-vessel
895
+ computation; the class list is a data question once it stabilises.
896
+ A first-cut class list now exists in data as `kin:dynamics`
897
+ (PR #22) so the situation record has something to carry; it is
898
+ pencil and the question is unchanged. `dynamics:unknown` stays in the set
899
+ whatever the list becomes.
900
+ - **Q-19** — What are the game's parameters — horizon *T*, terminal condition,
901
+ action cadence Δt, the admissible set *A*, the information assumption?
902
+ Settled by the sensitivity matrix, which is owed before the ontology moves
903
+ out of pencil. *(engine)*
904
+ - **Q-20** — Is the adversary rule-compliant, arbitrary within physics, or
905
+ both, and does the horizon hide the region behind it? Settled by computing
906
+ both and comparing. *(engine)*
907
+ - **Q-21** — Can the hybrid, partial-information game be encoded soundly in
908
+ UPPAAL TIGA/STRATEGO or KeYmaera X at all? Settled by a benchmark with
909
+ certified bounds, not by reading the tools' documentation. *(engine)*
910
+ - **Q-22** — Is the region computed offline per dynamics-class pair, or does
911
+ it have to be computed at runtime? Settled by the first two-vessel
912
+ computation's cost. *(engine)*
913
+ - **Q-23** — Is Rule 2(b) a duty where departure is necessary, and does the
914
+ departing vessel bear the burden? Settled by reading the cases (*The Bywell
915
+ Castle*, *Boy Andrew v St Rognvald*, both unverified), not by advocacy.
916
+ - **Q-24** — Are the four worked illustrations (R0, R1, R2,
917
+ `inconclusive-in-model`) actually in the regions they are said to be in?
918
+ They fix meanings, not numbers; settled by computing them. *(engine)*
919
+ - **Q-25** — Is the tractability tiering (findable under duress / with time /
920
+ likely missed) a real axis, and do its proxies measure it? Settled by
921
+ bridge-simulator or human-reliability evidence, not by the model. *(engine)*
922
+ - **Q-26** — How large is the relative-frame state space? The 10⁴–10⁶ figure
923
+ is a back-of-envelope guess; settled by a worksheet. *(engine)*
924
+ - **Q-27** — What are the field names for `category`, `subjects`, `when`,
925
+ `effect` and the widened `modality`? Settled when the first non-`display`
926
+ entry lands, and cheap to change until then.
927
+ Unchanged by PR #22, which names none of those five. The names it
928
+ does fix — `situation`, the four classes, the three subjects — are
929
+ REQ-CAT-6's and are equally pencil.
930
+ - **Q-28** — What namespace distinguishes the two subjects of a two-subject
931
+ entry (`own:` / `other:` is the working proposal)? No such segment exists in
932
+ `docs/identifiers.md`; settled before Rule 18 lands, and it is an
933
+ identifier decision under REQ-MODEL-10.
934
+ **Decided in pencil 2026-09-04** (PR #22): `<subject>:<class>:<key>`
935
+ with subject `own`/`other`/`pair` and class `fact`/`kin`/`geo`/`hist`, and a
936
+ bare key meaning `own:`. Written up in `docs/identifiers.md` §"Two
937
+ subjects", required by REQ-CAT-6, and exercised by
938
+ `fixtures/situation-fixtures.json`. Three things the working proposal did
939
+ not have: a third subject, because range and in-sight belong to the
940
+ encounter and not to either vessel; a class segment, so kinematics and
941
+ history are new classes rather than new fact keys; and the bare-key default,
942
+ which is what makes the whole thing additive under REQ-MODEL-10 — no
943
+ existing identifier is renamed or repointed, and `own`/`other`/`pair` become
944
+ reserved at the head of the identifier space, which is the only cost.
945
+ Settled for good by Rule 18 being written against it.
946
+ - **Q-29** — What are the file names and schemas for the invariants file and
947
+ the region grid, and how does a level-3 invariant carry `jurisdiction`?
948
+ Settled when the first invariant is written down.
949
+ - **Q-30** — What does the `care`/`meta` registry look like as a file — its
950
+ name, its schema, and its relationship to `known_omissions`? Settled by
951
+ REQ-CAT-2's implementation, which is the next data change after this ADR.
952
+
953
+ **Decided in pencil, PR #23.** The registry is `represented_paragraphs`,
954
+ a sibling array to `known_omissions` in `data/applicability.json`. Each
955
+ record: `id` (paragraph-derived, e.g. `2a`), `jurisdiction`, `cite`,
956
+ `category` (`care` or `meta`), and a one-sentence `note`; no `when`, no
957
+ `lights` — a registry record states that the paragraph is represented
958
+ and evaluated by nothing here, never a predicate. Holds the `2(a)` and
959
+ `2(b)` records this PR adds. `✎` under `docs/conventions.md`: the file
960
+ name and schema stay open to a better idea, logged when changed.
961
+
962
+ ### From the first two-subject data (PR #24)
963
+
964
+ Rules 4, 11, 19(a) and 18(a)–(f) are the first entries written against ADR
965
+ 0005's model. What the model expressed, and what it could not, is recorded
966
+ here rather than in a commit message. All pencil.
967
+
968
+ **Decided in pencil 2026-09-04 (PR #24)**, answering the data half of `Q-27`:
969
+ the field names are `category`, `subjects` and `effect`, and `effect` is
970
+ `{own, other}` roles for a `precedence` entry and `{part, section,
971
+ applies_rules}` for a `scope` one. Written up in `docs/identifiers.md`
972
+ §"Effects", required by `REQ-CAT-8`, and exercised by
973
+ `fixtures/situation-fixtures.json`. `Q-28`'s namespace is settled for good by
974
+ the same PR — Rule 18 is written against it and it held, with one addition:
975
+ a fifth class `env`, `pair`-only, because a narrow channel and a traffic
976
+ separation scheme are properties of the water and belong to neither vessel
977
+ nor to the pair's geometry. `fixtures/situation-fixtures.json` had already
978
+ named that gap before the class existed.
979
+
980
+ - **Q-31** — `modality` is a single closed value, and 18(c), 18(d)(i) and 9(a)
981
+ each carry two deontic qualifications at once: a practicability caveat
982
+ ("so far as possible", "if the circumstances of the case admit") *and* the
983
+ duty itself. 18(c) is expressible because `shall-if-practicable` already
984
+ exists; 18(d)(i) is not, and its caveat is dropped with a `gap` note on the
985
+ entry. Settled by deciding whether practicability is a second field beside
986
+ `modality` rather than a value inside it — which is the same question
987
+ `modality_by` answers for the light rules, and should probably be answered
988
+ the same way. **Narrowed, not settled, 2026-09-04 (PR #25):** 18(d)(i) is now
989
+ `shall-if-practicable` with `effect.own: shall-not-impede`, so the two
990
+ qualifications sit in two fields and neither is dropped. That works only
991
+ because this duty happens to be a *role*, which `effect` already carries;
992
+ 9(a)'s "if the circumstances of the case admit" has no second field to move
993
+ into, and the general question is untouched.
994
+ - **Q-32** — **decided in pencil 2026-09-04 (PR #25): a derived fact.**
995
+ `fact:activity` is a *display* axis: it says what lights a vessel shows, not
996
+ what her rank is under Rule 18, and the two disagree. `fact:rule18_class` is
997
+ the rank, declared in a new `derived` section of `data/facts.json` with a
998
+ decode table that is its definition, in the same style as the SignalK
999
+ `navigation.state` table. Seven values — `rule18_class:nuc`, `:ram`,
1000
+ `:fishing`, `:wig`, `:cbd`, `:sail`, `:power` — each cited to the Rule 3
1001
+ paragraph that defines it. The three cases this question named are decoded
1002
+ rather than hand-listed: `activity:mine` (27(f)) and `activity:diving`
1003
+ (27(e)) to `rule18_class:ram` by 3(g), `activity:trawling` to
1004
+ `rule18_class:fishing` by 3(d), and the 27(c) tow that severely restricts the
1005
+ pair — the one the old predicates could not catch at all — to
1006
+ `rule18_class:ram`, via a new boolean `fact:tow_restricts_deviation`. Every
1007
+ Rule 18, 9 and 10 entry reads the class; no `precedence` entry reads
1008
+ `fact:activity` any more, and a test enforces that. `fact:activity` itself is
1009
+ unchanged and no entry that reads it was touched. What is *not* settled: the
1010
+ eighth rank Rule 18 distinguishes is the seaplane of 3(e), and it has no
1011
+ value here because there is no fact for being a seaplane — 18(e) stays in
1012
+ `known_omissions`. A vessel under oars decodes to nothing, deliberately: Rule
1013
+ 18 does not rank her, and 25(d)(ii) is a lights permission rather than a
1014
+ rank. Both are recorded in the fact's own `undecodable` list. The
1015
+ alternatives — a second refinement table, or a per-value attribute on the
1016
+ activity axis — were declined because both would have put the rank back
1017
+ inside the display axis, which is the thing this question says is the
1018
+ mistake.
1019
+ - **Q-33** — **decided in pencil 2026-09-04 (PR #25): the predicate language
1020
+ grows `not` and `any_of`.** A `when` was a conjunction with no negation and
1021
+ no disjunction, and Rule 18 needed both. `{"not": C}` is a constraint on one
1022
+ fact; `any_of` is disjunction, holding sub-predicates as a key of a `when`
1023
+ and constraints as the value of a fact. Both are implemented in `satisfies`
1024
+ and one shared walker, so the one-subject and two-subject evaluators get them
1025
+ without a second copy. **`not` over an absent fact is unsatisfied**, like
1026
+ every constraint over an absent fact, so `{"not": C}` and `C` are both false
1027
+ where the fact is missing and the language is not classical there — the point
1028
+ being that a duty is never laid on a vessel because a consumer left a field
1029
+ out. That is also why `not` is a constraint and never a key of a `when`: a
1030
+ predicate-level negation would be satisfied by silence. 18(d)(i) and the
1031
+ 18(a) family are now written as the negations their paragraphs state, over
1032
+ `fact:rule18_class` rather than over the activity axis; 9(b) and 10(j) each
1033
+ collapse from two entries to one with `any_of`. The generated-complement
1034
+ alternative was declined: it would have produced a predicate no reader could
1035
+ check against the rule text, to avoid a language feature the rules themselves
1036
+ use in plain words.
1037
+ - **Q-34** — A `shall-not-impede` entry names a duty toward `other`, but the
1038
+ paragraphs identify the protected vessel by a property this package does not
1039
+ carry: "a vessel which can safely navigate only within a narrow channel"
1040
+ (9(b), 9(d)), "any vessel following a traffic lane" (10(i), 10(j)), and a
1041
+ CBD vessel "exhibiting the signals in Rule 28" (18(d)(i)). `pair:env:*` says
1042
+ where the encounter is, not which vessel is confined to it, so those entries
1043
+ are wider than their paragraphs and could assert a duty owed to a third
1044
+ vessel. Settled by deciding whether the protected-vessel property is a
1045
+ per-vessel fact, and whether it is a fact at all or a consumer's judgement.
1046
+
1047
+ **Decided in pencil 2026-09-05, for the 9(b)/9(d) and 10(i)/10(j) half: the
1048
+ protected-vessel property is a per-vessel fact, consumer-supplied like
1049
+ `pair:env:narrow_channel` and `pair:env:traffic_lane` are.** `fact:confined_to_channel`
1050
+ and `fact:following_traffic_lane` name it, in the same shape as the existing
1051
+ `fact:near_channel` and `fact:obstruction_exists`; entries `9b`, `10i` and
1052
+ `10j` now read `other:fact:confined_to_channel`/`other:fact:following_traffic_lane`
1053
+ and their `gap` fields are closed. `9(d)` has no entry yet, so it inherits
1054
+ the same fact when one is written. **18(d)(i) is deliberately left open**:
1055
+ "exhibiting the signals in Rule 28" is a display-compliance fact rather than
1056
+ a vessel-identity one, and this package models night lights only (see
1057
+ CLAUDE.md "Coverage") — settling it is a narrower, separate decision.
1058
+ - **Q-35** — 8(f)(iii)'s antecedent is "a vessel, the passage of which is not to
1059
+ be impeded" — the *output* of another norm, not a fact. A precedence
1060
+ predicate reads only facts, so entry `8f3` reads the risk-of-collision half
1061
+ alone and applies to every pair with risk of collision. Settled by deciding
1062
+ whether a norm may read another norm's effect, which is the same question a
1063
+ `conduct` monitor will ask about role and phase.
1064
+ - **Q-36** — Rule 18(a)(iv) and 18(d)(i) are both in force between a sailing
1065
+ vessel and a vessel constrained by her draught: the CBD vessel gives way, and
1066
+ the sailing vessel must not impede her. Neither overrides the other and the
1067
+ model records both, so one subject holds `stand-on` and `shall-not-impede`
1068
+ at once. The test suite pins it as a finding rather than asserting it away.
1069
+ Settled by reading 8(f)(ii) against the cases, not by picking a role.
1070
+ - **Q-37** — 13(a) is `classification` in ADR 0005 §1 and in the proposal's
1071
+ first-cut table, but it is the one paragraph of Rule 13 that assigns a role
1072
+ and a `classification` entry has nowhere to put one. Entry `13a` is
1073
+ `precedence` here, with 13(b)'s sector test left for the `classification`
1074
+ entry that would set the `hist:was_overtaking` latch. Settled with the rest
1075
+ of `Q-14`, paragraph by paragraph.
1076
+
1077
+ **Closed in pencil 2026-09-04 (PR #26).** 13(b) is now written, as *two*
1078
+ `classification` entries rather than one — `13b-overtaking` and
1079
+ `13b-overtaken` — because the encounter type belongs to the pair and reading
1080
+ aspect alone classified the overtaken vessel's side of the same encounter as
1081
+ a crossing. 13(a) stays `precedence`, and its predicate now carries both
1082
+ halves of "any vessel overtaking any other": the 13(b) sector and the 13(d)
1083
+ latch, as an `any_of`, which closes the gap the entry recorded. 13(d) is a
1084
+ third `classification` entry, reading history and no geometry at all. The
1085
+ split confirms the thing ADR 0005 §1 was really defending — one category per
1086
+ paragraph — and costs one paragraph two entries, which the entry-id suffix
1087
+ scheme was already built for.
1088
+ - **Q-38** — 9(d), 18(d)(ii), 18(e), 18(f)(ii), 1(a)–(e) and 20(b)–(c) are
1089
+ recorded in `known_omissions` rather than modelled: 9(d) needs the channel's
1090
+ axis, 18(e) needs a fact for being a seaplane, 20(b)–(c) need time of day,
1091
+ and 1(a)–(e) are addressed to an authority rather than to a vessel. Settled
1092
+ one at a time as the facts they need land; none blocks the rest of Part B.
1093
+ - **Q-39** — Rule 4's `scope` entry has an empty `when`, because "any condition
1094
+ of visibility" is the absence of a condition. That makes it unfalsifiable —
1095
+ `REQ-VERIFY-3`'s "excluded by at least one fixture" half cannot be satisfied
1096
+ and the test exempts it explicitly. Settled by deciding whether an ungated
1097
+ norm should be a registry record like `represented_paragraphs` rather than an
1098
+ entry with a vacuous predicate.
1099
+
1100
+ ### From the classification norms (PR #26)
1101
+
1102
+ Rules 7(d), 12, 13(b)–(d), 14 and 15 are the first `classification` entries and
1103
+ the second family of `precedence` ones. All pencil. What the model expressed is
1104
+ written up in `docs/identifiers.md` §"Effects"; what it could not is here.
1105
+
1106
+ - **Q-40** — **Rule 12 is `precedence`, not `classification`.** ADR 0005 §1 and
1107
+ the proposal's table both file it under `classification`; it produces a role
1108
+ — "one of them shall keep out of the way of the other" — and a
1109
+ classification effect has nowhere to put one, which is `Q-37`'s argument
1110
+ applied a second time. Two sailing vessels are still in a head-on, a crossing
1111
+ or an overtaking; Rule 12 says which of them gives way, not what kind of
1112
+ meeting it is. 12(b) is a `definition` and is the cite on `kin:wind_side`
1113
+ rather than an entry. The second half of the question is narrower and worse:
1114
+ 12(a) says "two sailing vessels", which is 3(c), but entries `12a1`–`12a3`
1115
+ read `rule18_class:sail` instead, so a vessel engaged in fishing under sail
1116
+ falls out of Rule 12 — because letting her in would have given her a give-way
1117
+ duty under 12(a) and a stand-on role under 18(b)(iii) with no override
1118
+ between them. Narrowed deliberately and recorded on each entry. Settled with
1119
+ the rest of `Q-14`, and by deciding whether Rule 18 overrides Rules 12–15 the
1120
+ way Rules 9, 10 and 13 override Rule 18 — a `rel:overrides` nobody has yet
1121
+ written down. *Which mechanism* is settled: REQ-MODEL-13 makes it a
1122
+ relation, not a gate, because Rule 12 and Rule 15 do apply to the vessel and
1123
+ Rule 18 only displaces the role.
1124
+
1125
+ **Decided in pencil 2026-09-05 (PR #35): Rule 12 reads 3(c), and Rules 13
1126
+ and 18 override it.** The three Rule 12 entries gate both subjects on
1127
+ `fact:propulsion: propulsion:sail`, which is 3(c)'s sailing vessel, and no
1128
+ longer on `rule18_class:sail`; a vessel engaged in fishing under sail, or
1129
+ not under command under sail, is inside Rule 12 as the paragraph says. The
1130
+ conflict the narrowing was avoiding is resolved the way the Rules resolve
1131
+ it: Rule 18's chapeau excepts Rules 9, 10 and 13 and no others, so every
1132
+ Rule 18 norm that can be in force between two sailing vessels — 18(b)(i)–(iii)
1133
+ from the ordinary sailing vessel's side, 18(c)(i)–(ii) from the fishing
1134
+ vessel's — carries `rel:overrides` against `12a1`–`12a3`. 13(a), being
1135
+ "notwithstanding" the whole of Sections I and II, overrides them too, which
1136
+ it had to: two sailing vessels in an overtaking held 13(a)'s and 12(a)(ii)'s
1137
+ roles at once and nothing had noticed. Two fixtures and a sweep over sailing
1138
+ fleets — every tack and windward combination, plain and with one vessel
1139
+ ranked by Rule 18 — assert never both give-way, never both stand-on, and no
1140
+ two helm roles after resolution. What it leaves: between two sailing vessels
1141
+ Rule 18 does not order — one not under command, one restricted in her
1142
+ ability to manoeuvre — Rule 12 is now the only norm in force, and it lays a
1143
+ helm duty on a vessel that may be unable to discharge it; that is Rule 2's
1144
+ region (ADR 0005 §5) and is recorded, not gated.
1145
+
1146
+ **Decided in pencil 2026-09-05 (PR #46): Rule 15 reads 3(b), and Rule 18
1147
+ overrides it too.** The same question answered the other way for Rule 15.
1148
+ `15a-give-way` kept the four Rule 18 ranks — NUC, RAM, fishing and WIG —
1149
+ out of Rule 15 by negating them on both subjects in its own predicate; the
1150
+ negation is gone, both subjects gate on `fact:propulsion: propulsion:power`
1151
+ which is 3(b), and `18a1`–`18a3`, `18c1`–`18c2` and `18f1` carry
1152
+ `rel:overrides` against it. The gate was not merely the relation said in
1153
+ the wrong place. It produced the right roles wherever Rule 18 speaks and
1154
+ *no* role at all where Rule 18 is silent: two vessels engaged in fishing
1155
+ under power, and a vessel not under command crossing one restricted in her
1156
+ ability to manoeuvre under power, are pairs Rule 18 does not order, and
1157
+ neither vessel took a helm role from any entry. Both are fixtures now, and
1158
+ both failed on the data before the change — `15a-crossing` classified the
1159
+ encounter and nothing assigned a role. What it leaves is Rule 12's residue
1160
+ again: on those unordered pairs Rule 15 lays a give-way duty on a vessel
1161
+ that may be unable to discharge it, which is Rule 2's region and is
1162
+ recorded, not gated. `13a` needs no override against `15a-give-way` — the
1163
+ entry excludes every overtaking by the `hist:was_overtaking` latch and by
1164
+ 13(b)'s sector — and a test pins that absence so it stays a reason rather
1165
+ than an oversight. The hand-list of six is asserted complete by a derived
1166
+ check: every Rule 18 entry that assigns a helm role and does not gate a
1167
+ subject to a sailing vessel must carry the override, so a Rule 18 paragraph
1168
+ written later cannot join Rule 15 silently. The steady-bearing sweep gained
1169
+ power fleets — each rank on one side, on both sides, and against each other
1170
+ rank — and every one of the six overrides fails the sweep when removed. The
1171
+ fixtures file's `override_note` stays as it is: it says `expect` names every
1172
+ entry a situation selects, including one a `rel:overrides` then displaces,
1173
+ which is still how the fixtures are written and is what makes these six
1174
+ overrides observable rather than invisible.
1175
+
1176
+ The category half — Rule 12 as `precedence` — is unchanged and stays with
1177
+ `Q-14`.
1178
+ - **Q-41** — **13(c) and 14(c) invert the absent-fact rule.** "When a vessel is
1179
+ in any doubt as to whether she is overtaking, she shall assume that this is
1180
+ the case" is a duty that fires on the *absence* of knowledge, and the
1181
+ predicate language's one firm commitment is that an absent fact satisfies
1182
+ nothing (`Q-33`). Modelling either paragraph means a `doubt` boolean, which
1183
+ asks a consumer to report a mental state, or reading absence as assertion,
1184
+ which reverses the rule the language is built on. Both are in
1185
+ `known_omissions`. 12(a)(iii) is the same shape and *is* modelled, only
1186
+ because 12(b) makes the uncertainty a declared value of a fact that is
1187
+ present — `wind_side:unknown`. Settled by deciding whether "in doubt" is a
1188
+ fact of the situation at all; if it is, it is one fact and it closes three
1189
+ paragraphs.
1190
+ - **Q-42** — **7(d)(ii) is recorded, not modelled.** "Risk may sometimes exist
1191
+ even when an appreciable bearing change is evident, particularly when
1192
+ approaching a very large vessel or a tow or when approaching a vessel at
1193
+ close range." Two of the three limbs have no fact behind them — the Rules
1194
+ give no length for "very large" and no distance for "close range", so both
1195
+ would be numbers this package invented rather than thresholds on a quantity a
1196
+ paragraph names, which is what saves 7(d)(i)'s constant. The third limb, a
1197
+ tow, is expressible. It is not written alone because the modality is the real
1198
+ obstacle: "may sometimes exist" is neither a deeming rule nor a permission,
1199
+ and the vocabulary has no value for an instruction to the mariner's
1200
+ judgement. Settled by deciding whether the vocabulary gains one — `Q-31`'s
1201
+ neighbour, not its duplicate.
1202
+ - **Q-43** — **The partition needs history to be present, and says nothing when
1203
+ it is absent.** `14b` and `15a-crossing` are gated on `hist:was_overtaking`
1204
+ being `false` on both subjects, which is 13(d)'s requirement and the only way
1205
+ the three encounter types stay disjoint once the latch is set. Because absent
1206
+ is absent, a situation that omits the fact is classified as *no encounter at
1207
+ all* rather than as a head-on or a crossing. That is conservative in the
1208
+ right direction and completely silent, which is the wrong way to be
1209
+ conservative: a consumer who forgets one boolean gets an empty answer that
1210
+ looks like a lawful one. The neighbouring finding: two sailing vessels get no
1211
+ encounter type either, because Rules 14 and 15 are gated on two power-driven
1212
+ vessels and Rule 12 has no deeming paragraph — that one is a property of the
1213
+ Rules rather than of the model. Settled by deciding whether an engine may
1214
+ distinguish "no encounter" from "cannot say", which is the status alphabet of
1215
+ ADR 0005 §5 and belongs to `colregs-engine`.
1216
+ - **Q-44** — **`kin:wind_side` is not kinematics.** Rule 12 needs to know which
1217
+ side each vessel has the wind on. It is a per-vessel Part B fact, so it
1218
+ cannot go in the fact record (`REQ-CAT-4`: the record does not change, and a
1219
+ display consumer must never be asked for it) and it cannot go in `pair` (it
1220
+ is not symmetric), which leaves `kin` — a class whose declared meaning is
1221
+ where a vessel is, where she is pointing, how fast she is going and turning,
1222
+ and what she handles like. A sailing vessel's tack is none of those. Filed
1223
+ there rather than fixed by widening the class note, because widening it would
1224
+ make `kin` mean "per-vessel and not in the fact record", which is a
1225
+ description of the leftovers and not of a class. Settled by the second
1226
+ per-vessel Part B fact that is not kinematics: one is an awkwardness, two is
1227
+ a missing class.
1228
+ - **Q-45** — **One entry, two paragraphs.** `14a` cites 14(a), which states the
1229
+ situation, and reads 14(b), which fixes the geometry that deems it to exist.
1230
+ The paragraph is supposed to be the unit (ADR 0001), and Rule 13 splits the
1231
+ same way into two entries only because 13(a) also produces a role. Here there
1232
+ is nothing for a second entry to produce. Settled by deciding whether a
1233
+ deeming paragraph that produces no effect of its own gets an entry anyway —
1234
+ the same question `Q-39` asks about an ungated norm, from the other end.
1235
+
1236
+ **Decided in pencil 2026-09-05 (PR #36): the head-on classification cites
1237
+ 14(b), and 14(a) is `conduct`.** The question assumed 14(b) produced no
1238
+ effect of its own. It produces the only effect the entry has: "such a
1239
+ situation shall be deemed to exist when…" is the deeming test, and
1240
+ `{"encounter": "head-on"}` is what a deeming test yields — exactly as
1241
+ `13b-overtaking` and `13b-overtaken` cite 13(b), the deeming paragraph,
1242
+ and not 13(a). 14(a) states the situation in words ("reciprocal or nearly
1243
+ reciprocal courses") and imposes the duty ("each shall alter her course to
1244
+ starboard"); the duty is `conduct`, and the words are what 14(b) makes
1245
+ checkable. So the entry is `14b`, cites 14(b), and reads nothing 14(b) does
1246
+ not fix; 14(a) joins `known_omissions` as conduct beside 18(d)(ii), and
1247
+ `14a` is retired in `retired_entry_ids` and, because v0.1.3 shipped it,
1248
+ recorded in `data/deprecated-identifiers.json` with `14b` as its
1249
+ replacement — the registry's first record (REQ-MODEL-11). The
1250
+ general question — whether a paragraph with no effect of its own gets an
1251
+ entry — is `Q-39`'s and is untouched; this paragraph turned out not to be an
1252
+ instance of it.
1253
+ - **Q-46** — **"Coming up with" is a comparison of two facts.** 13(b) deems a
1254
+ vessel to be overtaking when *coming up with* another from more than 22.5°
1255
+ abaft her beam. That is `own:kin:sog_kn` against `other:kin:sog_kn`, and the
1256
+ predicate language compares a fact to a constant and never one fact to
1257
+ another — the same wall `12a2` hits on "both have the wind on the same side",
1258
+ which it gets over only because that comparison has two values to enumerate
1259
+ and this one has infinitely many. `pair:geo:tcpa_s > 0` stands in: the pair
1260
+ is closing, and closing from abaft the beam is coming up. It excludes the
1261
+ vessel drawing away astern, which is the case that mattered, and admits a
1262
+ pair closing because the vessel ahead has stopped, which is not an overtaking
1263
+ in seamanship and is one here. Settled by deciding whether the language gains
1264
+ fact-to-fact comparison — a bigger change than `not` and `any_of` were, and
1265
+ not one to make for a single paragraph.
1266
+ - **Q-47** — **13(d)'s latch never clears.** The paragraph runs "until she is
1267
+ finally past and clear", nothing in the fact vocabulary carries that, and so
1268
+ in this model `hist:was_overtaking` is set by a consumer and cleared by a
1269
+ consumer while entry `13d` classifies an overtaking for as long as it stands.
1270
+ Not approximated with a range or a bearing: past and clear is a seamanship
1271
+ judgement of the same kind as risk of collision, and a threshold for it would
1272
+ be a number invented rather than declared. Settled by whatever settles the
1273
+ `conduct` monitors, which are the things that watch a duty end rather than
1274
+ begin.
1275
+ - **Q-48** — **Nothing checks that a situation is geometrically possible.**
1276
+ `own:geo:rel_bearing_deg`, `other:geo:rel_bearing_deg`, the two
1277
+ `kin:heading_deg` and the two `kin:sog_kn` are six facts related by two
1278
+ equations, and the situation record enforces neither. A consumer — or a
1279
+ fixture — can state a pair of bearings that no two headings produce, and the
1280
+ entries will classify it without complaint. It bites in one visible place:
1281
+ where both vessels have the other on the starboard side, which cannot happen
1282
+ on a collision course, `15a-give-way` applies to both of them and the "never
1283
+ both give-way" property fails. The fixtures added here are built from a
1284
+ heading and a bearing so that they are consistent by construction, and the
1285
+ file says so; the model is not. Settled by deciding whether the record gains
1286
+ a consistency check, whether the relative quantities become derived facts
1287
+ like `fact:rule18_class` — computed from the two kinematic states rather than
1288
+ supplied beside them — or whether it is the consumer's problem and says so.
1289
+
1290
+ **Decided in pencil 2026-09-05: the record gains a consistency check,
1291
+ declared in data and enforced in the suite.** `data/facts.json` declares the
1292
+ equations under `situation.geometry.consistency` — the two relative bearings
1293
+ are two readings of one line of sight (`own + own heading + 180 ≡ aspect +
1294
+ other heading`), positions reproduce range and both bearings, and the two
1295
+ headings and speeds reproduce CPA, TCPA and bearing rate — with tolerances,
1296
+ and `REQ-VERIFY-8` requires the suite to apply them. A quantity a record does
1297
+ not state constrains nothing, so the check says "not inconsistent" rather
1298
+ than "possible" where a record is sparse. What it found: all sixteen fixtures
1299
+ that state kinematics satisfied the heading and position equations, and every
1300
+ one of them failed the motion equations, because their CPA, TCPA and
1301
+ bearing-rate values were placeholders. They were re-derived from the headings
1302
+ and speeds. Two cases could not keep their story: the R1 head-on had both
1303
+ vessels with the other to starboard, which no speeds make a steady bearing,
1304
+ and is recast as one; the 13(d) latch case is 400 m abeam with the CPA
1305
+ already past, and lost `7d1`. The partition sweep now constructs positions
1306
+ and headings for every bearing pair and is consistent at every point. The
1307
+ "never both give-way" property is asserted over a sweep of steady-bearing
1308
+ geometries — every relative bearing, several speed pairs, both intercept
1309
+ solutions — where it is a theorem rather than an observation:
1310
+ `u·sin(own bearing) = −v·sin(aspect)`, so the two bearings lie on opposite
1311
+ sides and 15(a) can name only one vessel. The both-starboard counterexample
1312
+ is pinned as a record the check rejects. What is *not* settled, and is
1313
+ pinned too: the theorem is exact only at a bearing rate of zero, and
1314
+ 7(d)(i)'s pencilled 1°/min admits a slow, close, starboard-to-starboard
1315
+ passing just outside the head-on cone on which risk is deemed and both
1316
+ vessels are give-way. That is 14(c)'s doubt case and belongs with `Q-41`,
1317
+ not here. Of the three options this question named, "the consumer's problem"
1318
+ is closed — the definition a consumer would need is now in the data — and
1319
+ the relative quantities as derived facts stays open in the block's
1320
+ `settled_by`.