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