colregs 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/data/applicability.json +143 -18
- package/data/corpora.json +5 -5
- package/data/editions.json +7 -2
- package/data/i18n/en.json +71 -5
- package/data/i18n/fi.json +2 -2
- package/data/images.json +12 -4
- package/data/lights.json +10 -20
- package/data/rules.json +216 -1
- package/data/text/intl/1977/fi.finlex.json +881 -0
- package/data/version.json +1 -1
- package/docs/adr/0010-text-withheld-jurisdictions.md +2 -0
- package/docs/adr/0013-corpus-files-with-editions.md +19 -0
- package/docs/adr/0018-jurisdiction-delta-is-a-merge-patch.md +102 -0
- package/docs/adr/0019-relation-reach-and-import-reads.md +106 -0
- package/docs/adr/0020-skeleton-is-a-delta.md +80 -0
- package/docs/budgets.json +4 -1
- package/docs/decisions.md +4 -0
- package/docs/gates.json +41 -0
- package/docs/identifiers.md +4 -1
- package/docs/maritime-sources.md +17 -0
- package/docs/part-b-invariants.md +26 -22
- package/docs/requirements.md +87 -88
- package/fixtures/applicability-fixtures.json +90 -0
- package/package.json +4 -1
- package/schema/applicability.schema.json +15 -0
- package/schema/display-evaluation.schema.json +0 -4
- package/schema/i18n-catalog.schema.json +25 -4
- package/schema/lights.schema.json +2 -3
- package/schema/rules.schema.json +48 -0
- package/data/text/intl/2016/fi.finlex.json +0 -22
package/data/version.json
CHANGED
|
@@ -102,6 +102,8 @@ data one.
|
|
|
102
102
|
international law where a national body deliberately has none. That is
|
|
103
103
|
about deltas, not licences; it survives this ADR untouched and is the live
|
|
104
104
|
blocker on CEVNI. Read the two together or the wrong one gets blamed.
|
|
105
|
+
*(ADR 0018 has since supplied that mechanism; the bar is now "land with
|
|
106
|
+
your tombstones", not "wait".)*
|
|
105
107
|
- `schema/rules.schema.json` carries the conditional: `text` required unless
|
|
106
108
|
`text_status` is `withheld`, in which case it is forbidden and
|
|
107
109
|
`withheld_reason` is required. Only `withheld_reason` is refused on a
|
|
@@ -53,6 +53,25 @@ anyone has checked, and the `us/inland@2014` edition is recalled, not
|
|
|
53
53
|
verified, and says so. That is the honest cost of this option and the reason
|
|
54
54
|
to look at both.
|
|
55
55
|
|
|
56
|
+
## Considered and declined (amendment, 2026-09-16)
|
|
57
|
+
|
|
58
|
+
Four designs additive on option B, declined for now rather than on
|
|
59
|
+
principle — each recorded in requirements.md §10 as a timed gate, closing
|
|
60
|
+
event the first national text whose edition cannot be determined:
|
|
61
|
+
|
|
62
|
+
- **Validity intervals on editions (`in_force_until`).** (GATE-7)
|
|
63
|
+
`superseded_by` plus `in_force` already infers validity for every
|
|
64
|
+
edition on file; a dedicated field would duplicate that inference.
|
|
65
|
+
- **A per-source publication record separate from the edition, FRBR
|
|
66
|
+
manifestation.** (GATE-8) `source` lives on the corpus and one edition
|
|
67
|
+
has one source per jurisdiction today; nothing distinguishes yet.
|
|
68
|
+
- **A content digest per corpus for verification.** (GATE-9)
|
|
69
|
+
`edition_status` already flags an unchecked corpus; a digest adds
|
|
70
|
+
tamper/drift detection nothing on file has needed.
|
|
71
|
+
- **A skeleton per edition, instead of one shared skeleton.** (GATE-10)
|
|
72
|
+
Low risk today: no amendment has ever renumbered a rule (GATE-1's
|
|
73
|
+
verified history). The trigger is the first one that does.
|
|
74
|
+
|
|
56
75
|
## Consequences
|
|
57
76
|
|
|
58
77
|
- Breaking file layout (REQ-PKG-4): `data/rules.json` no longer carries
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# ADR 0018 — A jurisdiction delta is an RFC 7396 merge patch, and a tombstone is its `null`
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-16
|
|
4
|
+
Status: proposed — merging this PR is the ruling; a revert undoes it
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
REQ-SCOPE-3 says a jurisdiction is a delta over `intl`: entries it does not
|
|
9
|
+
mention are inherited. Q-11 found that unsafe as stated. 33 CFR 83 leaves
|
|
10
|
+
Rule 28 "[Reserved]" and has no counterpart to 23(d)(ii), so a `us/inland`
|
|
11
|
+
delta that was merely silent there would inherit `rule:28` and assert an
|
|
12
|
+
international obligation the Inland Rules deliberately do not carry. Both
|
|
13
|
+
absences are verified against the primary source (Cornell LII's copy of
|
|
14
|
+
33 CFR 83: "§ 83.28 [Reserved] (Rule 28)"; 83.23(d) is a single paragraph
|
|
15
|
+
with no subparagraphs). Q-11 asked for tombstones — "this entry deliberately
|
|
16
|
+
does not exist here", distinguishable from "not yet transcribed" — and held
|
|
17
|
+
every non-`intl` jurisdiction until the mechanism existed. Issue #45 asked,
|
|
18
|
+
separately, which standard encoding the delta should follow.
|
|
19
|
+
|
|
20
|
+
Both questions have been waiting on a second jurisdiction to force them.
|
|
21
|
+
Issue #137 records that nothing was proposing one. `us/inland` already exists
|
|
22
|
+
with two add-only entries under ADR 0008's carve-out, and the two absences
|
|
23
|
+
above are the counterexample that carve-out was left open for: a delta that
|
|
24
|
+
must suppress, not only add.
|
|
25
|
+
|
|
26
|
+
## Decision
|
|
27
|
+
|
|
28
|
+
1. **The delta semantics are RFC 7396, JSON Merge Patch.** A jurisdiction's
|
|
29
|
+
delta over `intl` is, in meaning, a merge-patch document keyed by entry
|
|
30
|
+
id: present keys are that jurisdiction's own entries, `null` keys are its
|
|
31
|
+
tombstones, and every key the patch does not mention is inherited. Any
|
|
32
|
+
conformant merge-patch implementation applied to the `intl` entries by
|
|
33
|
+
id reproduces the jurisdiction's resolved rule set. RFC 6902 is not
|
|
34
|
+
adopted: an operation script with JSON Pointer paths would duplicate the
|
|
35
|
+
id addressing `docs/identifiers.md` already provides.
|
|
36
|
+
|
|
37
|
+
2. **Storage stays columnar; the patch is derived.** Entries keep their
|
|
38
|
+
`jurisdiction` field in the one `entries[]` table, as ADR 0008 laid them
|
|
39
|
+
down. Tombstones are a second top-level table, `suppressions[]`, one
|
|
40
|
+
record per (jurisdiction, entry): `jurisdiction`, `suppresses` (the
|
|
41
|
+
`intl` entry id), `cite` (the paragraph that has no counterpart) and
|
|
42
|
+
`why` (one sentence and the source). `test/data.test.mjs` builds the
|
|
43
|
+
merge patch from those two tables, applies it with a literal RFC 7396
|
|
44
|
+
implementation, and asserts the result equals the evaluator's
|
|
45
|
+
jurisdiction filter. That test is the statement that storage and
|
|
46
|
+
semantics agree; if they ever diverge, the test names which.
|
|
47
|
+
|
|
48
|
+
3. **Replace is suppress plus add.** A jurisdiction that reads a paragraph
|
|
49
|
+
differently tombstones the `intl` entry and adds its own, under its own
|
|
50
|
+
id (ADR 0015 — the ids are distinct because the norms are). There is no
|
|
51
|
+
third operation and no in-place edit of an `intl` entry: REQ-SCOPE-4
|
|
52
|
+
holds.
|
|
53
|
+
|
|
54
|
+
4. **The array caveat is accepted and irrelevant here.** Merge Patch
|
|
55
|
+
replaces an array wholesale. The patch is keyed at entry granularity, so
|
|
56
|
+
the only arrays it ever touches are inside a whole entry being added, and
|
|
57
|
+
an entry is small. A jurisdiction never patches one element of an
|
|
58
|
+
inherited entry's `lights`; it replaces the entry (point 3).
|
|
59
|
+
|
|
60
|
+
5. **Only an inherited entry can be tombstoned.** A tombstone naming a
|
|
61
|
+
jurisdiction's own entry, or another jurisdiction's, is rejected by test:
|
|
62
|
+
the first is a deletion, the second was never in force.
|
|
63
|
+
|
|
64
|
+
6. **A tombstone is verified the way an entry is.** Each needs one fixture
|
|
65
|
+
under its jurisdiction whose facts match the suppressed entry's predicate
|
|
66
|
+
and whose expectation omits it, and one `intl` fixture showing the entry
|
|
67
|
+
in force at the base (REQ-VERIFY-3, both sides).
|
|
68
|
+
|
|
69
|
+
7. **Two tombstones land with this ADR**, both verified: `us/inland`
|
|
70
|
+
suppresses `rule:28` (Rule 28 "[Reserved]") and `rule:23d_ii` (no
|
|
71
|
+
counterpart to the under-7 m, 7 kn exception). They are enough to
|
|
72
|
+
exercise inherit and delete; no verified replace case exists in Part C
|
|
73
|
+
yet, so that branch of point 3 is stated, schema-ready and unexercised.
|
|
74
|
+
|
|
75
|
+
## Consequences
|
|
76
|
+
|
|
77
|
+
- **Q-11 is answered and its hold is lifted.** REQ-SCOPE-3's "no non-`intl`
|
|
78
|
+
jurisdiction lands before an explicit suppression mechanism exists" is
|
|
79
|
+
discharged by this mechanism; a jurisdiction lands with its tombstones or
|
|
80
|
+
it does not land. ADR 0008's add-only carve-out survives as the trivial
|
|
81
|
+
case: a delta with no tombstones is a merge patch with no `null`s.
|
|
82
|
+
- **Issue #45 is closed by this ADR.** README names the RFC beside the
|
|
83
|
+
jurisdiction paragraph so a consumer knows which library semantics
|
|
84
|
+
reproduce the inheritance.
|
|
85
|
+
- **A suppressed entry can leave a record out of vocabulary.** Under
|
|
86
|
+
`us/inland` a record carrying `activity:cbd` now selects nothing:
|
|
87
|
+
`rule:28` is gone and the Rule 23(a) entries it imported read
|
|
88
|
+
`activity:none`. That is the honest answer — "constrained by her draft" is
|
|
89
|
+
not an Inland status — and the package does not pick a reading. It is
|
|
90
|
+
recorded in `known_omissions` and pinned by a fixture. Whether the fact
|
|
91
|
+
vocabulary should carry per-jurisdiction membership so a consumer's
|
|
92
|
+
decode can be checked is a new question, not decided here.
|
|
93
|
+
- **`rule:23d_i` cites a path Inland does not spell.** 33 CFR 83.23(d) has
|
|
94
|
+
no `(i)`; the content sits at bare `83.23(d)`. The entry is inherited
|
|
95
|
+
correctly — the norm is the same — and the citation-spelling question is
|
|
96
|
+
GATE-1's, untouched here.
|
|
97
|
+
- **GATE-1, Q-10 and Q-8 are not taken.** Q-11 named a "second-jurisdiction
|
|
98
|
+
bundle"; this ADR takes only the item that gated data. The others stay
|
|
99
|
+
open on their own triggers.
|
|
100
|
+
- **Cost to reverse.** Delete `suppressions[]` from data, schema and the
|
|
101
|
+
three tests; the two fixtures fail and are deleted with them. No consumer
|
|
102
|
+
reads the table yet. Pre-1.0, a revert.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# ADR 0019 — What a relation reaches, and what an import reads
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-16
|
|
4
|
+
Status: proposed — merging this PR is the ruling; a revert undoes it
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
REQ-MODEL-7 and the README define the six relation verbs by meaning and
|
|
9
|
+
leave composition to the consumer (REQ-CONS-3). colregs-engine#33 checked
|
|
10
|
+
the seven composition decisions its evaluator makes where the data is
|
|
11
|
+
silent. ADR 0007 settled the largest, direction: a directed "this one
|
|
12
|
+
prevails" is `rel:overrides`, and `rel:excludes` is pick-one between
|
|
13
|
+
alternatives. Three readings of the verbs remain stated only in
|
|
14
|
+
colregs-engine's `docs/engine-notes.md` (items 2, 3 and 5) and pinned only
|
|
15
|
+
by its tests: how far a relation reaches, what a `one_of` yields, and which
|
|
16
|
+
part of a referenced entry's predicate an import consults. Each is a
|
|
17
|
+
reading of the Rules, and the package that carries the Rules should say it.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
1. **Displacing relations reach entries in force; composing relations
|
|
22
|
+
reach what is exhibited.** An entry is *in force* when its own predicate
|
|
23
|
+
holds. `rel:overrides` and `rel:exempts` act on entries in force and on
|
|
24
|
+
nothing else: an import is not in force, it is a light set its carrier
|
|
25
|
+
exhibits, and the carrier is what a displacing relation must name.
|
|
26
|
+
`rel:in_lieu_of`, `rel:excludes`, `rel:includes` and
|
|
27
|
+
`rel:conditional_includes` act on what one display may contain, imports
|
|
28
|
+
included, and remove nothing from the set of entries in force. Direction
|
|
29
|
+
is carried by the verb, never by modality: an override fires from a
|
|
30
|
+
forceful entry (ADR 0007), an exemption from a `modality:exempt` one,
|
|
31
|
+
and `rel:excludes` fires from nowhere — it is symmetric and already
|
|
32
|
+
barred to forceful entries by CI (`test/data.test.mjs`, "rel:excludes is
|
|
33
|
+
reciprocated"). A consumer that infers a veto from an excluder's modality
|
|
34
|
+
is reading a relation the data does not carry.
|
|
35
|
+
|
|
36
|
+
2. **A `one_of` yields exactly one option per display, or none when the
|
|
37
|
+
carrier is `modality:may`.** Under a `may` carrier the chosen option is
|
|
38
|
+
exhibited in lieu of the carrier's own lights, and the none choice
|
|
39
|
+
exhibits them (25(d)(ii): the sailing lights, or failing them the torch).
|
|
40
|
+
An option already in force by its own predicate discharges the set: the
|
|
41
|
+
carrier imports nothing, and the option composes as the entry it is. A
|
|
42
|
+
vessel restricted in her ability to manoeuvre at anchor shows 30(a) or
|
|
43
|
+
30(b) because 30(a) and 30(b) apply to her, not because 27(b)(iv)
|
|
44
|
+
redirects her to them. Every choice is returned (REQ-MODEL-8).
|
|
45
|
+
|
|
46
|
+
3. **An import reads the referenced entry's lights, their modality, and
|
|
47
|
+
its scalar gates — never its axes.** An import is a `rel:includes`, the
|
|
48
|
+
`rel:includes` of a `rel:conditional_includes` branch, or a `one_of`
|
|
49
|
+
option. `facts.json`'s `axes` and `modifiers` are the field: a key
|
|
50
|
+
declared there is the vessel's situation, and the carrier's redirect has
|
|
51
|
+
already placed her in it — 30(d) sends a vessel aground to "the lights
|
|
52
|
+
prescribed in paragraph (a) or (b)", and that she is not at anchor is the
|
|
53
|
+
premise of the redirect, not a gate on it. A key not declared there is a
|
|
54
|
+
fact the redirect does not alter, and the referenced entry's condition
|
|
55
|
+
on it binds: 30(b)'s "less than 50 metres" binds a vessel aground as it
|
|
56
|
+
binds one at anchor, and 25(b)'s "less than 20 metres" binds a vessel
|
|
57
|
+
under oars. Modality is the referenced entry's, resolved against the
|
|
58
|
+
vessel (Rule 28's three reds are `may`; the Rule 23 lights it imports
|
|
59
|
+
stay `shall`). A gate the carrier needs that the source does not carry is
|
|
60
|
+
written on the carrier's branch `when`, as 27(f) and 29(a) do; nothing
|
|
61
|
+
is inferred from a source's axes in either direction. REQ-MODEL-7's
|
|
62
|
+
"lights only, never its predicate" is amended to say this.
|
|
63
|
+
|
|
64
|
+
4. **Two entries in force whose `rel:in_lieu_of` targets intersect are
|
|
65
|
+
alternatives to each other** and never share a display. This is an
|
|
66
|
+
invariant of the verb, not a declared pair list. The two pairs the data
|
|
67
|
+
can hold in force together read that way in the text: 23(d)(ii)'s
|
|
68
|
+
display is 23(d)(i)'s with the sidelights made practicable-only, and
|
|
69
|
+
25(d)(i)'s torch is what a vessel shows *if she does not* exhibit (a) or
|
|
70
|
+
(b). The Rule 24 entries that share targets — 24(a)(i) below and above
|
|
71
|
+
200 m, and 24(c) — are disjoint on their own predicates and never meet.
|
|
72
|
+
A future pair that lawfully combines is the evidence to reopen this
|
|
73
|
+
point; until one exists, the target sets are the declaration.
|
|
74
|
+
|
|
75
|
+
Three shapes were considered and not adopted. Restating each option's
|
|
76
|
+
scalar gate on the carrier's branch (30(d) split at 50 m) keeps "lights
|
|
77
|
+
only" literal at the cost of transcribing 30(b)'s condition into 27(b)(iv),
|
|
78
|
+
27(f), 29(a) and 30(d), and 25(b)'s into 25(d)(ii), plus a CI check to
|
|
79
|
+
police the copies; the text redirects, it does not restate, and REQ-MODEL-5
|
|
80
|
+
puts a gate where its fact lives. Declaring the point-4 pairs as a relation
|
|
81
|
+
duplicates what the target sets say. A per-verb "reaches imports" flag
|
|
82
|
+
would be set the same way on every entry, and a field nothing varies is
|
|
83
|
+
decoration.
|
|
84
|
+
|
|
85
|
+
## Consequences
|
|
86
|
+
|
|
87
|
+
- **No data changes.** The README relation table, REQ-MODEL-7 and the
|
|
88
|
+
`relations` notes in `data/applicability.json` say the four points in
|
|
89
|
+
this PR. `docs/identifiers.md` and the schema are untouched.
|
|
90
|
+
- **colregs-engine implements this in one issue, three deletions.** Its
|
|
91
|
+
modality-inferred exclusion path goes: `rel:excludes` is a co-occurrence
|
|
92
|
+
check on a display and nothing more. Its `includeApplies` goes: it
|
|
93
|
+
consulted one axis by name on `rel:includes` imports, the inverse of
|
|
94
|
+
point 3, and no import in the data reaches it on a coherent record —
|
|
95
|
+
every carrier either shares the source's position gate (23(b), 23(c),
|
|
96
|
+
25(c), 28), gates its branch on it (24, 27(f), 29(a)), or reads
|
|
97
|
+
`fact:making_way`, which refines `position:underway` (26(b)(iii),
|
|
98
|
+
26(c)(iii), 27(a)(iii), 27(b)(iii)). The availability test it already
|
|
99
|
+
applies to `one_of` options becomes the one rule for every import. Its
|
|
100
|
+
`docs/engine-notes.md` items 2–5 become pointers here. colregs-engine#33
|
|
101
|
+
items 1, 6 and 7 stay the engine's: presentation and output shape.
|
|
102
|
+
- **A `one_of` option's scalar gate is a normative read of the source
|
|
103
|
+
entry.** Editing 30(b)'s length gate changes what a vessel aground may
|
|
104
|
+
show. That is the intended coupling: one paragraph, one gate.
|
|
105
|
+
- **Cost to reverse.** Delete this file and restore two sentences; the
|
|
106
|
+
engine's deleted paths come back by revert. Pre-1.0, a revert.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# ADR 0020 — The skeleton is a delta too: a jurisdiction's paragraphs are a merge patch over `intl`
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-17
|
|
4
|
+
Status: proposed — merging this PR is the ruling; a revert undoes it
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
ADR 0018 made the entries table a delta over `intl`. The skeleton was not
|
|
9
|
+
one. `data/rules.json` keyed every paragraph by bare path, one record per
|
|
10
|
+
key, and every one of them was `intl`; the corpus test asserted that a
|
|
11
|
+
corpus paragraph's path belongs to the corpus's own jurisdiction. So a
|
|
12
|
+
`us/inland` corpus could not carry `21(a)`: the key existed and was
|
|
13
|
+
`intl`'s. The 2026-08-30 diff of 33 CFR 83 against Part C found 18 paths
|
|
14
|
+
where Inland spells the same path with different text, three it does not
|
|
15
|
+
spell at all, and a dozen it adds. None of the 18 could be stated. That,
|
|
16
|
+
not a licence, is why the Inland corpus stub had no paragraphs (issue
|
|
17
|
+
#161), and why every planning session stalled on "the second jurisdiction".
|
|
18
|
+
|
|
19
|
+
Solace ruled on issue #137 on 2026-09-17: `us/inland`, Part C, before v1.
|
|
20
|
+
|
|
21
|
+
## Decision
|
|
22
|
+
|
|
23
|
+
1. **`rules.json` gains `deltas`, keyed by jurisdiction.** A delta has
|
|
24
|
+
`paragraphs` (own rows, keyed by path, each carrying its jurisdiction)
|
|
25
|
+
and `suppressions` (paths the source does not spell, each with a `why`).
|
|
26
|
+
`paragraphs` at the top level stays the `intl` base, untouched, so every
|
|
27
|
+
existing consumer reads what it read.
|
|
28
|
+
|
|
29
|
+
2. **A jurisdiction's resolved skeleton is the RFC 7396 merge patch of its
|
|
30
|
+
delta over the base**, the same statement ADR 0018 makes for entries: own
|
|
31
|
+
rows present, suppressed paths `null`, an unmentioned path inherited
|
|
32
|
+
whole. A restated path merges field by field, so an own row that says
|
|
33
|
+
nothing about `images` inherits the base figure. The test builds the
|
|
34
|
+
patch, applies a literal RFC 7396 implementation, and asserts the
|
|
35
|
+
resolved skeleton is what corpora, entries and fixtures key into.
|
|
36
|
+
|
|
37
|
+
3. **A path is stated in a delta when its text differs from `intl` or has no
|
|
38
|
+
`intl` counterpart; suppressed when the source does not spell it; silent
|
|
39
|
+
otherwise.** A `[Reserved]` section is a spelled path with reserved text,
|
|
40
|
+
so it is stated, not suppressed: Inland `28` and `26(d)` are rows whose
|
|
41
|
+
corpus text will be `[Reserved]`.
|
|
42
|
+
|
|
43
|
+
4. **An entry in force under a jurisdiction cites a path in that
|
|
44
|
+
jurisdiction's resolved skeleton.** An inherited `intl` entry whose path
|
|
45
|
+
the delta suppresses cannot stand by silence: it is tombstoned, and where
|
|
46
|
+
the norm survives under another path, replaced (ADR 0018 point 3).
|
|
47
|
+
|
|
48
|
+
5. **`us/inland` lands with its Part C skeleton delta**, every row from
|
|
49
|
+
`docs/verification/2026-08-30-q6-q8.md`: 19 restated paths (the 18
|
|
50
|
+
differing plus `28`), 18 Inland-only paths, and `23(d)(i)`, `23(d)(ii)`,
|
|
51
|
+
`23(d)(iii)` suppressed. A new sub-paragraph is stated when `intl`
|
|
52
|
+
already carries sibling sub-rows of that parent; where `intl`'s parent is
|
|
53
|
+
one row, the parent is restated and the sub-paragraph waits for the
|
|
54
|
+
corpus. So `24(f)`'s Inland `(iii)` and `22(d)`'s Inland `(ii)` wait
|
|
55
|
+
(`24(f)`, `22(d)` are one row each in `intl`), while `24(g)(v)` is stated
|
|
56
|
+
(`intl`'s `24(g)(i)` is a sibling row). `NRHB_23_e.png` and
|
|
57
|
+
`NRHB_24_j.png` are mapped to `23(e)` and `24(j)` and stop being
|
|
58
|
+
unmapped.
|
|
59
|
+
|
|
60
|
+
6. **The first replace lands with it.** Inland 83.23(d) is the under-12 m
|
|
61
|
+
provision as one paragraph; `23(d)(i)` is not a path there. Point 4
|
|
62
|
+
forces the branch ADR 0018 left unexercised: `rule:23d_i` is tombstoned
|
|
63
|
+
under `us/inland` and `rule:23d`, same predicate and lights, cites
|
|
64
|
+
`23(d)`. Fixtures show both sides.
|
|
65
|
+
|
|
66
|
+
## Consequences
|
|
67
|
+
|
|
68
|
+
- The Inland corpus can now be filled: every path it will carry resolves.
|
|
69
|
+
Text is 17 U.S.C. 105, so REQ-PROV-2 does not gate it. That is the next PR.
|
|
70
|
+
- `us/inland` is now six applicability records and a skeleton delta of 40
|
|
71
|
+
rows. README's coverage statement says so; it is still not a model of the
|
|
72
|
+
Inland Rules until the entries for the restated paths land.
|
|
73
|
+
- The skeleton edition `us/inland@2014` is declared and still recalled, not
|
|
74
|
+
verified against the Federal Register (REQ-LANG-10). The corpus fetch
|
|
75
|
+
verifies it or corrects it.
|
|
76
|
+
- GATE-1 is untouched: a path that differs across jurisdictions at one time
|
|
77
|
+
is REQ-MODEL-1 working as designed, and this ADR gives it a home.
|
|
78
|
+
- **Cost to reverse.** Delete `deltas` from data and schema, the resolver and
|
|
79
|
+
three tests, `rule:23d` and its tombstone, two fixtures; unmap two images.
|
|
80
|
+
No consumer reads `deltas` yet. Pre-1.0, a revert.
|
package/docs/budgets.json
CHANGED
|
@@ -23,9 +23,12 @@
|
|
|
23
23
|
"docs/adr/0015-rule-ids-are-paragraph-keys.md": 210,
|
|
24
24
|
"docs/adr/0016-encounter-roles-are-pooled-across-frames.md": 90,
|
|
25
25
|
"docs/adr/0017-closed-vocabularies-are-prefixed-identifiers.md": 120,
|
|
26
|
+
"docs/adr/0018-jurisdiction-delta-is-a-merge-patch.md": 120,
|
|
27
|
+
"docs/adr/0019-relation-reach-and-import-reads.md": 110,
|
|
26
28
|
"docs/timeline.md": 130,
|
|
27
29
|
"docs/maritime-sources.md": 130,
|
|
28
|
-
"docs/decisions.md": 40
|
|
30
|
+
"docs/decisions.md": 40,
|
|
31
|
+
"docs/adr/0020-skeleton-is-a-delta.md": 110
|
|
29
32
|
},
|
|
30
33
|
"json_prose": {
|
|
31
34
|
"targets": [
|
package/docs/decisions.md
CHANGED
|
@@ -3,4 +3,8 @@
|
|
|
3
3
|
Rulings from `/sequence` sessions and other closed judgment calls. One line
|
|
4
4
|
each: date, short name, the answer, a link to the argument.
|
|
5
5
|
|
|
6
|
+
- 2026-09-16 — docs ownership: colregs-engine's docs are sited wrong; colregs owns docs going forward, no new doc lands in colregs-engine. `normative-language.md` was already migrated the same day (commit fe0f8c2, #125); migration of the remaining three (`formal-methods-glossary.md`, `formal-methods-reading-list.md`, `engine-notes.md`) is not yet scheduled. Argued on kanban; no public writeup to link.
|
|
6
7
|
- 2026-09-16 — own vs self: rename the subject segment `own` to `self` across the situation record (fact keys, precedence effect keys, facts.json, fixtures, docs/identifiers.md). Work spawned as [colregs#138](https://github.com/mark-brannan/colregs/issues/138).
|
|
8
|
+
- 2026-09-16 — Q-54, Rule 17's phases are monotone: once 17(a)(ii) permission or the 17(b) duty has arisen for the stand-on vessel, it does not fall back on belated give-way compliance. Argued in [colregs#72](https://github.com/mark-brannan/colregs/issues/72); recorded in `docs/requirements.md`.
|
|
9
|
+
- 2026-09-16 — Q-53, 17(a)(ii) is an exception, not a suspension: the stand-on vessel's duty stands and a departure is lawful only as action to avoid collision; a monitor flags any other alteration. Argued in [colregs#72](https://github.com/mark-brannan/colregs/issues/72); recorded in `docs/requirements.md`.
|
|
10
|
+
- 2026-09-17 — second jurisdiction: `us/inland`, Part C, before v1. Ruled on [colregs#137](https://github.com/mark-brannan/colregs/issues/137); skeleton delta lands under ADR 0020, text and entries follow.
|
package/docs/gates.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"first-non-english-corpus": "the second corpus of any one jurisdiction, which in practice is the first non-English text",
|
|
12
12
|
"first-community-translation-of-national-corpus": "the first `community`-tier translation of a `national`-tier corpus",
|
|
13
13
|
"first-external-contribution": "the first merged contribution from someone other than the copyright holder",
|
|
14
|
+
"first-undated-national-edition": "the first national text whose edition cannot be determined",
|
|
14
15
|
"none": "the decline does not get cheaper or dearer with time; recorded so it is not re-read as merely deferred"
|
|
15
16
|
},
|
|
16
17
|
"statuses": {
|
|
@@ -79,6 +80,46 @@
|
|
|
79
80
|
"status": "open",
|
|
80
81
|
"settled_by": null,
|
|
81
82
|
"note": "ADR 0004 settles the code licence (MIT \u2192 Apache-2.0) and leaves the data licence open. Already-published npm versions stay under the licence they shipped with; the gate governs future releases only, and is held open deliberately by REQ-PROV-7's CONTRIBUTING.md terms (DCO-style certification plus a relicensing grant); a CLA-assistant bot is the upgrade path if contributors arrive."
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "GATE-7",
|
|
86
|
+
"title": "validity intervals on editions (`in_force_until`)",
|
|
87
|
+
"declined_in": "docs/adr/0013-corpus-files-with-editions.md",
|
|
88
|
+
"closing_event": "first-undated-national-edition",
|
|
89
|
+
"trigger": "an edition superseded with no successor edition registered to bound it \u2014 repeal without replacement, or two editions of one jurisdiction whose validity ranges cannot be ordered from `superseded_by` alone",
|
|
90
|
+
"status": "open",
|
|
91
|
+
"settled_by": null,
|
|
92
|
+
"note": "Declined alongside ADR 0013 (option B, GATE-2 adopted): `superseded_by` plus `in_force` infers validity for every edition on file today. An explicit `in_force_until` is additive, not a rewrite, so paying for it before a case needs it buys nothing."
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "GATE-8",
|
|
96
|
+
"title": "a per-source publication record separate from the edition (FRBR manifestation)",
|
|
97
|
+
"declined_in": "docs/adr/0013-corpus-files-with-editions.md",
|
|
98
|
+
"closing_event": "first-undated-national-edition",
|
|
99
|
+
"trigger": "two distinct published sources for the same edition that disagree \u2014 e.g. an official gazette printing and a codifier's republication of the same edition differing in errata \u2014 so \"the source\" stops being a property of the edition",
|
|
100
|
+
"status": "open",
|
|
101
|
+
"settled_by": null,
|
|
102
|
+
"note": "Declined alongside ADR 0013: today `source` lives on the corpus and one edition has one source per jurisdiction. A manifestation layer is additive when a second publication record is actually needed."
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"id": "GATE-9",
|
|
106
|
+
"title": "a content digest per corpus for verification",
|
|
107
|
+
"declined_in": "docs/adr/0013-corpus-files-with-editions.md",
|
|
108
|
+
"closing_event": "first-undated-national-edition",
|
|
109
|
+
"trigger": "a corpus found to differ from its cited source with no accompanying edition or source change \u2014 silent drift a hash would have caught and prose review did not",
|
|
110
|
+
"status": "open",
|
|
111
|
+
"settled_by": null,
|
|
112
|
+
"note": "Declined alongside ADR 0013: `edition_status` (verified/claimed/unknown, this ADR's before-merge item 1) already flags an unchecked corpus. A digest adds tamper/drift detection on top, which nothing on file today has needed."
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "GATE-10",
|
|
116
|
+
"title": "a skeleton per edition",
|
|
117
|
+
"declined_in": "docs/adr/0013-corpus-files-with-editions.md",
|
|
118
|
+
"closing_event": "first-undated-national-edition",
|
|
119
|
+
"trigger": "the first amendment that deletes or renumbers a path",
|
|
120
|
+
"status": "open",
|
|
121
|
+
"settled_by": null,
|
|
122
|
+
"note": "Declined alongside ADR 0013: one shared `data/rules.json` skeleton is low risk because no amendment has ever renumbered a rule (seven IMO amendments since 1972, none doing so). A per-edition skeleton is the fallback the day one does."
|
|
82
123
|
}
|
|
83
124
|
]
|
|
84
125
|
}
|
package/docs/identifiers.md
CHANGED
|
@@ -181,7 +181,7 @@ is an enumerated fact, so its values follow the bare-fact-name rule above:
|
|
|
181
181
|
Rule 13(d) is the reason history is a class and not a note. Once a vessel is
|
|
182
182
|
overtaking, a subsequent alteration of the bearing does not make her a
|
|
183
183
|
crossing vessel; the instantaneous geometry, read alone, says otherwise and
|
|
184
|
-
hands the duty to the wrong vessel. So
|
|
184
|
+
hands the duty to the wrong vessel. So overtaking history is a fact:
|
|
185
185
|
|
|
186
186
|
- `self:hist:was_overtaking` — this subject was, earlier in this encounter,
|
|
187
187
|
an overtaking vessel with respect to the other.
|
|
@@ -433,6 +433,9 @@ implemented in the reference evaluator and asserted by the fixtures.
|
|
|
433
433
|
words. It is deliberately not a light reference and does not resolve to
|
|
434
434
|
one.
|
|
435
435
|
|
|
436
|
+
Not being an identifier says nothing about whether a vocabulary gets a
|
|
437
|
+
catalog label; see REQ-LANG-6.
|
|
438
|
+
|
|
436
439
|
## This does not reverse GATE-5
|
|
437
440
|
|
|
438
441
|
GATE-5 declined a CI-enforced terminology glossary, permanently, for the
|
package/docs/maritime-sources.md
CHANGED
|
@@ -26,6 +26,12 @@ question the rule text leaves open.
|
|
|
26
26
|
- **Nautical Institute — *Action by the Stand-On Vessel*** (Seaways case study). The stand-on vessel's stages as taught; does not reach whether they are reversible.
|
|
27
27
|
<https://www.nautinst.org/resources-page/200115-action-by-the-stand-on-vessel.html>
|
|
28
28
|
- **USCG Navigation Rules (Amalgamated)**. <https://www.navcen.uscg.gov/navigation-rules-amalgamated>
|
|
29
|
+
- **Steamship Mutual — *Navigation in Restricted Visibility***. Confirms Section II reapplies once vessels are in sight of one another again; silent on whether status accumulated before the loss of sight (a `Q-51`/`Q-52` classification, a `Q-54` Rule 17 stage) carries forward.
|
|
30
|
+
<https://www.steamshipmutual.com/publications/articles/restrictedvisibility0109>
|
|
31
|
+
- **Britannia P&I — *Navigation in restricted visibility***. No stand-on vessel exists under Rule 19; also silent on status continuity across the transition.
|
|
32
|
+
<https://britanniapandi.com/en_gb/guidance/navigation-in-restricted-visibility/>
|
|
33
|
+
- **Professional Mariner — *Overtaking or crossing? Don't assume what other ship will do***. The Rule 13(d) bearing-shift overtaking history, discussed only within continuous visual contact.
|
|
34
|
+
<https://professionalmariner.com/overtaking-or-crossing-dont-assume-what-other-ship-will-do/>
|
|
29
35
|
|
|
30
36
|
## Marine incident analysis
|
|
31
37
|
|
|
@@ -47,6 +53,16 @@ ambiguous give-way/stand-on roles are what the Rules are written against.
|
|
|
47
53
|
<https://www.bahamasmaritime.com/wp-content/uploads/2026/02/2026-5-Polesie-Verity-ReportAndAnnexes.pdf>
|
|
48
54
|
- IMO GISIS Marine Casualties and Incidents module. Not a paper but a source class: the mandatory-reporting database of marine safety investigation reports. Ground truth for real COLREGS-relevant incidents.
|
|
49
55
|
<https://www.imo.org/en/OurWork/IIIS/Pages/Marine-Safety-Investigation-reports.aspx>
|
|
56
|
+
- NTSB/MAR-16/01 — *Conti Peridot*/*Carla Maersk* collision, Houston Ship Channel. Occurred entirely within restricted visibility; no in-sight phase, no Rule 13/17 discussion. Checked for `Q-55` and found not on point.
|
|
57
|
+
<https://www.ntsb.gov/investigations/AccidentReports/Reports/MAR1601.pdf>
|
|
58
|
+
- Dutch Safety Board — *Collision in Restricted Visibility* (*VB Seal*/*Gulholmen*, Maasmond, 2026). Rules appendix cites only Rules 6 and 19; the encounter never had an in-sight phase. Checked for `Q-55` and found not on point.
|
|
59
|
+
<https://onderzoeksraad.nl/wp-content/uploads/2026/07/collision-in-restricted-visibility.pdf>
|
|
60
|
+
- TSB Canada M23C0143 — ferry *Svanoy*. Fog/lookout casualty; no Section II status question arises.
|
|
61
|
+
<https://www.tsb.gc.ca/eng/rapports-reports/marine/2023/m23c0143/m23c0143.html>
|
|
62
|
+
- SWZ Maritime on Nautical Institute MARS 202601 (near-miss in fog). Operational-failure summary only; no rule-regime discussion.
|
|
63
|
+
<https://swzmaritime.nl/news/2026/02/13/near-vessel-collision-in-fog/> · MARS index: <https://www.nautinst.org/technical-resources/mars.html>
|
|
64
|
+
- Marine Insight — fog/TSS-turn collision write-up. Overtaking occurred entirely within fog; no visibility transition at issue.
|
|
65
|
+
<https://www.marineinsight.com/real-life-incident-vessels-collide-in-dense-fog-during-sudden-tss-turn/>
|
|
50
66
|
|
|
51
67
|
## Known gaps
|
|
52
68
|
|
|
@@ -55,4 +71,5 @@ ambiguous give-way/stand-on roles are what the Rules are written against.
|
|
|
55
71
|
manoeuvrability, shoal water, a lee shore, set, visibility or sea state.
|
|
56
72
|
- Two standard texts — Cockcroft & Lameijer, *A Guide to the Collision Avoidance Rules*, and Farwell's *Rules of the Nautical Road* — are not online. Either may settle how the stages of a close-quarters encounter are divided, and whether they are treated as irreversible.
|
|
57
73
|
- Several of the questions in `docs/requirements.md` §11 appear unlitigated: overtaking geometry with no risk of collision, an overtaking situation becoming a head-on, resumption of course by a stand-on vessel that has acted, and the fate of accumulated Section II state across a visibility transition.
|
|
74
|
+
- `Q-55` (visibility transition): checked casualty reports, P&I guidance and trade-press commentary beyond the sources above — no authority found in either direction on whether a `Q-51`/`Q-52` classification or a `Q-54` Rule 17 stage survives a restricted-visibility interruption or resets on regaining sight. Deferred indefinitely, 2026-09-16; Cockcroft & Lameijer and Farwell (both already listed above as unreachable) remain the most plausible sources to settle it.
|
|
58
75
|
- BAILII refuses automated access. Use the National Archives Find Case Law service: <https://caselaw.nationalarchives.gov.uk/>
|
|
@@ -67,7 +67,7 @@ is Rules 11–18, III is Rule 19; which governs a pair is `INV-19a-scope`.
|
|
|
67
67
|
# Rule 13 — Overtaking
|
|
68
68
|
|
|
69
69
|
13(a) assigns a role, 13(b) deems the encounter, 13(c) resolves doubt, 13(d)
|
|
70
|
-
|
|
70
|
+
persists the result against the geometry that follows.
|
|
71
71
|
|
|
72
72
|
### INV-13b — the overtaking deeming test
|
|
73
73
|
|
|
@@ -122,7 +122,7 @@ well as Rules 14 and 15.
|
|
|
122
122
|
Settled by P4.2's role assignment: the override is or is not needed to keep
|
|
123
123
|
"never both give-way" true.
|
|
124
124
|
|
|
125
|
-
### INV-13d —
|
|
125
|
+
### INV-13d — overtaking history: the classification survives the geometry
|
|
126
126
|
|
|
127
127
|
`13(d)` · classification · temporal, a state to every later state of the
|
|
128
128
|
encounter · `✎`
|
|
@@ -140,10 +140,11 @@ until self is finally past and clear, the encounter type of the pair is
|
|
|
140
140
|
forbids: an overtaking drawn out on the bow reads *crossing*, and hands
|
|
141
141
|
give-way to the wrong vessel.
|
|
142
142
|
- **Undetermined term.** "Finally past and clear" has no fact and no threshold;
|
|
143
|
-
the data's
|
|
144
|
-
model's is its segmentation, and no bearing or range is
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
the data's overtaking history is consumer-cleared and never self-clears
|
|
144
|
+
(`Q-47`), a trace model's is its segmentation, and no bearing or range is
|
|
145
|
+
invented here.
|
|
146
|
+
- **Readings in doubt.** Two: what arms the overtaking history, `Q-51`; what it
|
|
147
|
+
forbids, `Q-52`.
|
|
147
148
|
- Entries: `rule:13d`, reading `self`/`other:hist:was_overtaking` and no geometry; the
|
|
148
149
|
`was_overtaking: false` gates on `rule:14b`, `rule:15a:crossing`, `rule:15a:keep_out_of_the_way`
|
|
149
150
|
implement `Q-52`'s *broad* reading. Settled by `Q-51`, `Q-52`, then TLC on a
|
|
@@ -162,8 +163,8 @@ type alone cannot distinguish `Q-52`'s readings.
|
|
|
162
163
|
|
|
163
164
|
- **State remembered.** As `INV-13d`.
|
|
164
165
|
- Entries: `rule:13a`'s `any_of` second limb, `self:hist:was_overtaking: true` — role
|
|
165
|
-
asserted from the
|
|
166
|
-
`Q-52`.
|
|
166
|
+
asserted from the overtaking history directly, keeping the limbs separable.
|
|
167
|
+
Settled by `Q-52`.
|
|
167
168
|
|
|
168
169
|
---
|
|
169
170
|
|
|
@@ -174,10 +175,10 @@ type alone cannot distinguish `Q-52`'s readings.
|
|
|
174
175
|
`14(b)` reading 14(a)'s conditions · classification · single-state · `✎`
|
|
175
176
|
|
|
176
177
|
**Invariant.** At any state at which two power-driven vessels are in sight,
|
|
177
|
-
both underway, with risk of collision, and neither
|
|
178
|
-
head-on situation is deemed to exist exactly when each sees
|
|
179
|
-
nearly ahead: `self:geo:rel_bearing_deg` and
|
|
180
|
-
in [0°, 11.25°] ∪ [348.75°, 360°).
|
|
178
|
+
both underway, with risk of collision, and neither carrying overtaking history
|
|
179
|
+
under `INV-13d`, a head-on situation is deemed to exist exactly when each sees
|
|
180
|
+
the other ahead or nearly ahead: `self:geo:rel_bearing_deg` and
|
|
181
|
+
`other:geo:rel_bearing_deg` each in [0°, 11.25°] ∪ [348.75°, 360°).
|
|
181
182
|
|
|
182
183
|
- **Both bearings, not one.** 14(b)'s lights limb is a statement about the
|
|
183
184
|
*other* vessel's aspect; one bearing alone swallows part of the crossing
|
|
@@ -219,9 +220,9 @@ way; both are directed to act, and the duty is symmetric.
|
|
|
219
220
|
`15(a)` · classification · single-state · `✎`
|
|
220
221
|
|
|
221
222
|
**Invariant.** At any state at which two power-driven vessels are in sight,
|
|
222
|
-
both underway, with risk of collision, and neither
|
|
223
|
-
encounter type is `crossing` exactly when it is neither
|
|
224
|
-
`INV-13b` nor `head-on` under `INV-14b`.
|
|
223
|
+
both underway, with risk of collision, and neither carrying overtaking history
|
|
224
|
+
under `INV-13d`, the encounter type is `crossing` exactly when it is neither
|
|
225
|
+
`overtaking` under `INV-13b` nor `head-on` under `INV-14b`.
|
|
225
226
|
|
|
226
227
|
- **Derived, not enumerated.** Enumerating the sector gives two statements of
|
|
227
228
|
one boundary, and the partition's only failure mode is editing one;
|
|
@@ -395,7 +396,9 @@ without further action by the give-way vessel.
|
|
|
395
396
|
- **Formally.** Reachability over the stand-on vessel's manoeuvring envelope:
|
|
396
397
|
`Safe(s, σ_own, σ_other)` of ADR 0005 §5 with the give-way strategy set
|
|
397
398
|
unconstrained — the first Part B invariant needing `kin:dynamics`.
|
|
398
|
-
- **Reading
|
|
399
|
+
- **Reading ruled.** Exception, not suspension (`Q-53`, 2026-09-16): the
|
|
400
|
+
17(a)(i) duty stands and the only lawful departure is action to avoid
|
|
401
|
+
collision; a checker flags any other alteration.
|
|
399
402
|
|
|
400
403
|
### INV-17b — phase 3: the stand-on vessel shall act
|
|
401
404
|
|
|
@@ -462,11 +465,12 @@ is at each state in exactly one of three phases:
|
|
|
462
465
|
|
|
463
466
|
Phase 3 dominates phase 2: an obligation is not qualified by a permission.
|
|
464
467
|
|
|
465
|
-
- **Reading
|
|
466
|
-
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
vessel still leaves stand-on
|
|
468
|
+
- **Reading ruled.** Monotone (`Q-54`): once phase 2 or 3 is reached for the
|
|
469
|
+
stand-on vessel in an encounter, it does not fall back on belated give-way
|
|
470
|
+
compliance.
|
|
471
|
+
- **State remembered.** One three-valued marker per stand-on vessel per
|
|
472
|
+
encounter, monotone: a late-complying give-way vessel still leaves stand-on
|
|
473
|
+
action permitted.
|
|
470
474
|
|
|
471
475
|
---
|
|
472
476
|
|
|
@@ -601,7 +605,7 @@ not jointly exhaustive (`INV-19a-third-state`); Rule 19 supplements Section I
|
|
|
601
605
|
|
|
602
606
|
- **Temporal note.** The switch is a function of the current state; a pair may
|
|
603
607
|
cross it and back inside one encounter, and no paragraph says what becomes of
|
|
604
|
-
a 13(d)
|
|
608
|
+
a 13(d) overtaking history or a Rule 17 phase: `Q-55`.
|
|
605
609
|
- **Undetermined term.** "In or near an area of restricted visibility" has no
|
|
606
610
|
fact (3(l)'s atmospheric condition is nowhere in `data/facts.json`), so `rule:19a`
|
|
607
611
|
selects Section III for any pair not in sight — wider — and records a gap.
|