colregs 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROVENANCE.md +7 -3
- package/README.md +116 -286
- package/data/applicability.json +13 -10
- package/data/corpora.json +41 -0
- package/data/editions.json +27 -0
- package/data/images.json +570 -33
- package/data/rules.json +202 -610
- package/data/text/intl/2016/en-US.uscg.json +877 -0
- package/data/text/intl/2016/es.boe.json +23 -0
- package/data/text/intl/2016/fi.finlex.json +22 -0
- package/data/text/us/inland/2014/en-US.ecfr.json +22 -0
- package/data/version.json +1 -1
- package/docs/adr/0001-name-and-jurisdiction-model.md +75 -2
- package/docs/adr/0003-language-as-a-dimension.md +9 -8
- package/docs/adr/0010-text-withheld-jurisdictions.md +123 -0
- package/docs/adr/0011-api-shape.md +173 -0
- package/docs/adr/0012-trace-and-rule2-departure-api.md +202 -0
- package/docs/adr/0013-corpus-files-with-editions.md +70 -0
- package/docs/budgets.json +16 -2
- package/docs/gates.json +3 -3
- package/docs/requirements.md +77 -35
- package/docs/timeline.md +102 -0
- package/docs/verification/2026-09-09-text-slug-straw-man.md +265 -0
- package/fixtures/situation-fixtures.json +78 -0
- package/package.json +1 -1
- package/schema/corpora.schema.json +68 -0
- package/schema/corpus.schema.json +329 -0
- package/schema/editions.schema.json +47 -0
- package/schema/images.schema.json +26 -1
- package/schema/rules.schema.json +27 -24
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# ADR 0012 — The trace and Rule 2 departure verbs: kinematic and temporal evaluation
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-07
|
|
4
|
+
Origin: colregs-engine, where it was ADR 0002 until 2026-09-09. The
|
|
5
|
+
decision is about colregs-engine's public API; "this package" below is
|
|
6
|
+
colregs-engine. ADRs for the whole family live here (AGENTS.md).
|
|
7
|
+
Status: draft, full stop. It names the verbs and frames their inputs and
|
|
8
|
+
results so the programme has a target to build toward; it does not build
|
|
9
|
+
them, and it expects to be broken while the package is 0.x.
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
ADR 0011 fixed two verbs, one per input, and put kinematic and temporal
|
|
14
|
+
evaluation out of *that* shape: a different input and a different tool. It did
|
|
15
|
+
not say what the verbs would be, which reads as "never" — the wrong lesson,
|
|
16
|
+
since the formal-methods programme (colregs-engine#1, phases 3–5) exists to do
|
|
17
|
+
this evaluation and colregs' ADR 0005 has fixed most of the input.
|
|
18
|
+
|
|
19
|
+
What the data settles today:
|
|
20
|
+
|
|
21
|
+
- The **situation record** carries `kin:`, `geo:` and `hist:` per subject,
|
|
22
|
+
every threshold a paragraph reads declared once under `situation.constants`.
|
|
23
|
+
- `conduct` is "monitored over a trace, not evaluated at a point";
|
|
24
|
+
`kin:rot_deg_min` and `hist:latched_at_s` are read by no point predicate.
|
|
25
|
+
- Rule 2 is a region of situation space a game solver finds, with a closed
|
|
26
|
+
status alphabet and a fixed output list (ADR 0005 §5, proposal v4 §4).
|
|
27
|
+
R0/R1/R2 are research-ontology labels — colregs' vocabulary, not this API.
|
|
28
|
+
|
|
29
|
+
Not settled: the dynamics model (Q-18), the game's parameters (Q-19, Q-20),
|
|
30
|
+
offline or runtime (Q-22), the conduct effect shape, a trace fixture schema.
|
|
31
|
+
|
|
32
|
+
## Decision
|
|
33
|
+
|
|
34
|
+
### 1. Four inputs, four verbs
|
|
35
|
+
|
|
36
|
+
| input | verb | result | status |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| `FactRecord` — one vessel | `evaluateDisplay` | `DisplayEvaluation` | built |
|
|
39
|
+
| `Situation` — two vessels, one instant | `evaluateEncounter` | `EncounterEvaluation` | target |
|
|
40
|
+
| `Trace` — the situation over time | `evaluateConduct` | `ConductEvaluation` | named here |
|
|
41
|
+
| `Situation` under a `Rule2DepartureModel` | `evaluateRule2Departure` | `Rule2DepartureFinding` | named here |
|
|
42
|
+
|
|
43
|
+
The two new verbs live **in this package**, superseding ADR 0011's pencilled
|
|
44
|
+
"separate package": a function of a trace and a lookup in a precomputed grid are
|
|
45
|
+
both pure and total, under the same validator, `opts.data` and `colregs.version`
|
|
46
|
+
provenance. What is *not* pure — the solver, the model checker — stays in
|
|
47
|
+
`research/`.
|
|
48
|
+
|
|
49
|
+
### 2. `Trace` — the input `conduct` reads
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
interface TraceSample { t_s: number; situation: Situation; }
|
|
53
|
+
/** Non-empty, strictly increasing `t_s`, the same two vessels throughout. */
|
|
54
|
+
interface Trace { samples: TraceSample[]; }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`Trace` is runtime-verification vocabulary — the object an STL monitor reads, a
|
|
58
|
+
checker's counterexample — not the Rules'. `t_s` is seconds on the caller's
|
|
59
|
+
clock; the engine reads differences only. A trace is a window, not a session:
|
|
60
|
+
the caller (searoom, the simulator, a fixture) decides how much history to hand
|
|
61
|
+
over, and the result says what window it saw. An object, so a field can be added
|
|
62
|
+
without breaking a caller. The validator that rejects an unknown fact key
|
|
63
|
+
rejects what it can see — an empty trace, non-increasing `t_s`, `other` in some
|
|
64
|
+
samples and not others. It cannot see identity (a `Situation` names no vessel):
|
|
65
|
+
the pair staying the same is the caller's, like the latch; purity is over
|
|
66
|
+
well-formed input.
|
|
67
|
+
|
|
68
|
+
`hist:was_overtaking` is a snapshot fact the caller supplies and clears, not
|
|
69
|
+
history the engine derives: set when 13(b)'s sector held at an earlier sample,
|
|
70
|
+
because "finally past and clear" is a judgement colregs declines to threshold
|
|
71
|
+
(Q-47). A windowed trace cannot see a latch set before it; time is the caller's.
|
|
72
|
+
|
|
73
|
+
### 3. `ConductEvaluation` — verdicts over the window
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
interface ConductEvaluation {
|
|
77
|
+
colregs: { version: string; source: 'resolved' | 'caller' };
|
|
78
|
+
window: { from_s: number; to_s: number; samples: number };
|
|
79
|
+
applied: EntryId[];
|
|
80
|
+
verdicts: ConductVerdict[];
|
|
81
|
+
phases: ConductPhaseChange[];
|
|
82
|
+
}
|
|
83
|
+
interface ConductVerdict {
|
|
84
|
+
id: EntryId; subject: 'own' | 'other';
|
|
85
|
+
verdict: 'kept' | 'breached' | 'pending';
|
|
86
|
+
attached_at_s?: number; decided_at_s?: number;
|
|
87
|
+
robustness?: { value: number; unit: string };
|
|
88
|
+
}
|
|
89
|
+
interface ConductPhaseChange { subject: 'own' | 'other'; phase: ParagraphCite; at_s: number; }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- One **verdict** per applied conduct entry per subject it attached to; an
|
|
93
|
+
entry that never attached is absent, as it is from `applied`.
|
|
94
|
+
`kept`/`breached` are decided; `pending` means the window ended before the
|
|
95
|
+
duty could be judged (17(a)(ii)'s "as soon as it becomes apparent" hasn't
|
|
96
|
+
run out). `attached_at_s` is when the entry attached the role judged;
|
|
97
|
+
`decided_at_s` when a breach began or a duty was met. `robustness`, like
|
|
98
|
+
`Trace`, is runtime-verification vocabulary: the STL margin the monitor
|
|
99
|
+
computes, value and unit, so a near miss and a wide pass do not look alike.
|
|
100
|
+
- A **phase** is the Rule 13(d)/17 protocol state: the latch, the stand-on
|
|
101
|
+
vessel passing from 17(a)(i) to 17(a)(ii) to 17(b). `phase` is a
|
|
102
|
+
`ParagraphCite`, never an `EntryId` — ADR 0011 §4's aliases, used throughout
|
|
103
|
+
here, and several phases have no entry — naming the state machine
|
|
104
|
+
programme phase 4's TLA+ or UPPAAL model checks; the monitor refines it.
|
|
105
|
+
- `applied` and the companion `appliedConductEntries(trace)` keep the fixture
|
|
106
|
+
contract; per-sample encounter evaluations are not returned — call `evaluateEncounter`.
|
|
107
|
+
|
|
108
|
+
Vague quantities a conduct paragraph reads — "readily apparent" (8(b)), "ample
|
|
109
|
+
time" (16), "as soon as it becomes apparent" (17(a)(ii)) — are declared once in
|
|
110
|
+
colregs' `situation.constants`, as `appreciable_bearing_change_deg_min` is in
|
|
111
|
+
`facts.json` at 0.2.0. A number a paragraph reads belongs in colregs; one only
|
|
112
|
+
the solver reads, inside the grid.
|
|
113
|
+
|
|
114
|
+
### 4. `Rule2DepartureModel` and `Rule2DepartureFinding` — the Rule 2 departure finding
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
interface SolverParameters {
|
|
118
|
+
dynamics: string[]; horizon_s: number; cadence_s: number; separation_m: number;
|
|
119
|
+
information: 'full' | 'partial'; adversary: 'compliant' | 'physics';
|
|
120
|
+
}
|
|
121
|
+
interface Rule2DepartureModel extends SolverParameters {
|
|
122
|
+
version: string; colregs_version: string;
|
|
123
|
+
}
|
|
124
|
+
interface Rule2DepartureFinding {
|
|
125
|
+
status: 'not-flagged' | 'model-rule-conflict'
|
|
126
|
+
| 'no-robust-policy-in-model' | 'inconclusive-in-model';
|
|
127
|
+
rules: EncounterEvaluation; advisories: Rule2DepartureAdvisory[];
|
|
128
|
+
model: { version: string; colregs_version: string; parameters: SolverParameters;
|
|
129
|
+
assumptions_violated: string[] };
|
|
130
|
+
}
|
|
131
|
+
interface Rule2DepartureAdvisory {
|
|
132
|
+
action: { alter_deg?: number; sog_kn?: number }; margin_m: number;
|
|
133
|
+
breaches: ParagraphCite[]; envelope: { holds_until_s: number };
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The model is required and positional, not an `opts` field: `opts.data` defaults
|
|
138
|
+
to the colregs release this package resolves and a grid has no default. Any
|
|
139
|
+
field beyond `version`, `colregs_version` and the parameters is the artefact's
|
|
140
|
+
own, not API. `SolverParameters` are the axes the sensitivity matrix will vary
|
|
141
|
+
(Q-17 to Q-22): the field set is a claim about what that matrix is. `version`
|
|
142
|
+
names one grid, immutably; `colregs_version` the release it was solved against,
|
|
143
|
+
carried beside `rules.colregs.version` (a mismatch is reported, not refused);
|
|
144
|
+
the finding echoes the parameters, so a consumer can say under which ones it
|
|
145
|
+
holds. `action` and `envelope` take the shapes the worked scenarios used.
|
|
146
|
+
Otherwise the shape follows proposal v4 §4. `not-flagged`, `model-rule-conflict`
|
|
147
|
+
and `no-robust-policy-in-model` name R0, R1 and R2, `inconclusive-in-model`
|
|
148
|
+
none, so a `region` field could only repeat the status or invent one. The
|
|
149
|
+
rule-derived obligations sit in `rules` unchanged, so the reader sees what the
|
|
150
|
+
Rules said; R1's advisories are ranked best margin first, each with the
|
|
151
|
+
paragraphs it breaks; R2's list is empty; `assumptions_violated` is display
|
|
152
|
+
text, never matched on. `not-flagged` means not flagged by this model, never
|
|
153
|
+
"the rules suffice". `breaches` carries `ParagraphCite`s (`17(c)`), as
|
|
154
|
+
`phase` does, never entry ids: a `breaches` entry against a shall-if-practicable
|
|
155
|
+
paragraph is the model's verdict and not the Rules' — the compliance predicate's
|
|
156
|
+
rule for those, and for 17(a)(ii)'s `may`, is open and carded.
|
|
157
|
+
|
|
158
|
+
The guard rail is narrower than "no danger input": the grid asserts departures,
|
|
159
|
+
and a caller who swaps it changes every finding. What the signature buys is that
|
|
160
|
+
no *situation* input names a departure — correcting facts moves the outputs;
|
|
161
|
+
asserting danger is not an input — and that `model.version` rides on every
|
|
162
|
+
finding, so a claim is attributable to a named grid, never to the Rules.
|
|
163
|
+
Disclaimers live in the README and licence, not here.
|
|
164
|
+
|
|
165
|
+
### 5. How each layer is checked
|
|
166
|
+
|
|
167
|
+
| layer | verb | what discharges it |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| point | `evaluateDisplay`, `evaluateEncounter` | exhaustive enumeration; Z3; Alloy for the sectors |
|
|
170
|
+
| trace | `evaluateConduct` | STL monitors *are* the verdict function; TLA+/UPPAAL for the 13(d)/17 phase machine |
|
|
171
|
+
| Rule 2 departure | `evaluateRule2Departure` | the game solver, offline; the artefact checked in, its parameters on the model |
|
|
172
|
+
|
|
173
|
+
The tools do not move into `src/`. What moves is their *output*: a counterexample
|
|
174
|
+
trace becomes a fixture; a certified grid, a `Rule2DepartureModel`.
|
|
175
|
+
|
|
176
|
+
## Consequences
|
|
177
|
+
|
|
178
|
+
- README's roadmap paragraph names all four verbs and their status; ADR 0011
|
|
179
|
+
§5's `conduct` and Rule 2 bullets and its register row point here.
|
|
180
|
+
- Order of work, each step shipping something a consumer can call:
|
|
181
|
+
1. colregs: a trace fixture schema and the first `conduct` entries (16,
|
|
182
|
+
17, 8(b)) with an effect shape, as `situation-fixtures.json` did.
|
|
183
|
+
2. `Trace`, `appliedConductEntries` against those fixtures — phase 3
|
|
184
|
+
starts here.
|
|
185
|
+
3. `evaluateConduct` with verdicts and phases; the constants above.
|
|
186
|
+
4. `evaluateRule2Departure` once a grid exists. ~~Until then the name is reserved and nothing exported: a stub answering `inconclusive-in-model` is a stub wearing a status.~~
|
|
187
|
+
**Struck. Reserving the name was never the policy here: a name gets an export the day it is written down, throwing or answering `inconclusive-in-model`. Withholding an export to look careful is the failure mode, not the stub. — Solace, 2026-09-13**
|
|
188
|
+
|
|
189
|
+
## Register
|
|
190
|
+
|
|
191
|
+
| item | level | what would settle it |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| Two more verbs, one per input, in this package | ✎ | Mark's confirmation; supersedes ADR 0011's "separate package" row |
|
|
194
|
+
| `Trace` an object over `TraceSample[]`, `t_s` on the caller's clock, pair identity the caller's | ✎ | the first trace fixture |
|
|
195
|
+
| `hist:was_overtaking` a caller-supplied snapshot fact, never engine-derived | ✎ | Q-47; the first conduct monitor |
|
|
196
|
+
| `ConductVerdict` alphabet `kept`/`breached`/`pending`; absent is absent | ✎ | the first STL monitor being written |
|
|
197
|
+
| Field names snake_case with unit suffixes; `EntryId`/`ParagraphCite` per ADR 0011 §4 | ✎ | that ADR's row; the compiler enforces neither |
|
|
198
|
+
| `ConductPhaseChange.phase` values are paragraph cites, not entry ids | ✎ | the programme phase-4 TLA+ or UPPAAL model |
|
|
199
|
+
| Vague-quantity constants live in colregs; `SolverParameters` on the model and echoed on the finding, `colregs_version` naming the release solved against; nothing else on the model is API | ✎ | the first constant a conduct entry reads; Q-19's sensitivity matrix |
|
|
200
|
+
| `Rule2DepartureFinding` field set — `rules`, `Rule2DepartureAdvisory[]`, no banner cite (it is a function of `status`); the status alphabet is colregs' (ADR 0005 §5), not this package's to rename | ✎ | proposal v4 §4's sensitivity matrix; Q-19, Q-20 |
|
|
201
|
+
| No *situation* input names a departure; the grid does, and is named in every finding | ✎ | — |
|
|
202
|
+
| A name is exported the day it is written down, throwing or answering `inconclusive-in-model`; exports carry TSDoc's `@beta` release tag until a fixture backs them | ✎ | — |
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# ADR 0013 — Rule text as corpus files under an edition registry (GATE-2 adopted)
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-12
|
|
4
|
+
Status: accepted (2026-09-13)
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
ADR 0003 made language a dimension and sketched its layout, but nothing
|
|
9
|
+
landed, and its GATE-2 (an edition layer between instrument and corpus) is
|
|
10
|
+
due for re-take *before* the first non-English corpus. Finlex (`fi`) and BOE
|
|
11
|
+
(`es`) are both licence-clear (ADR 0001, 2026-09-12), so the next text to
|
|
12
|
+
land forces the key shape. This ADR fixes the keys and leaves the words for
|
|
13
|
+
issue #98. A sibling proposal (branch `multi-jurisdiction-schema`, option A)
|
|
14
|
+
confirms GATE-2 declined instead; the two are meant to be read side by side.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
1. **`data/rules.json` is the skeleton.** Per paragraph: `path`, `rule`,
|
|
19
|
+
`jurisdiction`, `images`. No `text`, no `rule_title`, no source, and no
|
|
20
|
+
amendment state of its own. It gains `24(g)(i)`: the Convention has the
|
|
21
|
+
path; the USCG page lacks the words, which is the corpus's gap.
|
|
22
|
+
2. **`data/editions.json` is the registry: jurisdiction → instrument →
|
|
23
|
+
editions.** An edition id is `<jurisdiction>@<tag>` (`intl@2016`,
|
|
24
|
+
`us/inland@2014`), carrying `amended_through`, `in_force` and optionally
|
|
25
|
+
`superseded_by`. **The tag is the `in_force` year, nothing else** — the
|
|
26
|
+
amending instrument (an IMO resolution, a Federal Register cite) belongs
|
|
27
|
+
in `amended_through`, never in the tag. Each jurisdiction names the
|
|
28
|
+
edition the skeleton consolidates. Two editions of
|
|
29
|
+
one jurisdiction may be registered at once, which is GATE-2's trigger
|
|
30
|
+
case expressed as data rather than as a diff between strings.
|
|
31
|
+
3. **One file per corpus**,
|
|
32
|
+
`data/text/<jurisdiction>/<tag>/<language>.<source_id>.json`, identity
|
|
33
|
+
`<edition>.<language>.<source_id>`. A corpus names its `edition` and
|
|
34
|
+
nothing about jurisdiction or amendment state: both are the edition's.
|
|
35
|
+
CI checks the edition is registered and the filename agrees.
|
|
36
|
+
4. **A corpus paragraph is `rule_title` + `text`** (or ADR 0010's withheld
|
|
37
|
+
fields). Corpus metadata otherwise as in option A: `tier`,
|
|
38
|
+
`normalization`, `source` (REQ-PROV-6), `rights` (three statements),
|
|
39
|
+
`gaps`, optional `translation_of`.
|
|
40
|
+
5. **`data/corpora.json` indexes the files**, derived and drift-checked.
|
|
41
|
+
6. **GATE-2 is re-taken and adopted.** The cost is one registry file and
|
|
42
|
+
one extra path segment; the gain is that "which consolidated state" is an
|
|
43
|
+
identifier compared by equality, not free text compared by eye, and a
|
|
44
|
+
stub must name an edition before it names anything else.
|
|
45
|
+
|
|
46
|
+
## What the stubs show
|
|
47
|
+
|
|
48
|
+
Four corpora ship: `intl@2016.en-US.uscg` (today's text, moved verbatim),
|
|
49
|
+
`intl@2016.fi.finlex`, `intl@2016.es.boe` and `us/inland@2014.en-US.ecfr`,
|
|
50
|
+
the last three empty. The edition layer forces a claim option A lets a stub
|
|
51
|
+
defer: the Finnish and Spanish stubs assert they reflect `intl@2016` before
|
|
52
|
+
anyone has checked, and the `us/inland@2014` edition is recalled, not
|
|
53
|
+
verified, and says so. That is the honest cost of this option and the reason
|
|
54
|
+
to look at both.
|
|
55
|
+
|
|
56
|
+
## Consequences
|
|
57
|
+
|
|
58
|
+
- Breaking file layout (REQ-PKG-4): `data/rules.json` no longer carries
|
|
59
|
+
text. colregs-engine's conformance research reads only `paragraphs` keys
|
|
60
|
+
and is unaffected; anything reading `.text` moves to the en-US corpus.
|
|
61
|
+
- ADR 0010's withheld mechanics move to the corpus paragraph, where the
|
|
62
|
+
text is; the skeleton has nothing to withhold.
|
|
63
|
+
- A new IMO amendment is a new registered edition, a new skeleton pointer,
|
|
64
|
+
and new corpus files under a new directory; the old ones may stay until
|
|
65
|
+
the last consumer moves, marked `superseded_by`. Under option A the same
|
|
66
|
+
event is an in-place edit of every corpus's `amendment_state`.
|
|
67
|
+
- REQ-LANG-1, -3, -5, -9, -10 and REQ-PROV-6 move from unimplemented to
|
|
68
|
+
implemented for the data on file; catalogs (REQ-LANG-6) are untouched.
|
|
69
|
+
- Landing Finnish or Spanish words is an edit to one file and one number in
|
|
70
|
+
the index, and still waits on this ADR being accepted (REQ-GATE-2).
|
package/docs/budgets.json
CHANGED
|
@@ -2,20 +2,28 @@
|
|
|
2
2
|
"$comment": "Prose budgets enforced by prose-budget (the engine in dotfiles .local/bin), run locally by npm test and in CI by the shared workflow. Raising a number or adding an exception is a deliberate, reviewable diff.",
|
|
3
3
|
"lines": {
|
|
4
4
|
"README.md": 360,
|
|
5
|
-
"AGENTS.md":
|
|
5
|
+
"AGENTS.md": 200,
|
|
6
6
|
"CLAUDE.md": 10,
|
|
7
7
|
"docs/requirements.md": 1500,
|
|
8
8
|
"docs/identifiers.md": 450,
|
|
9
9
|
"docs/part-b-invariants.md": 950,
|
|
10
|
+
"docs/adr/0001-name-and-jurisdiction-model.md": 280,
|
|
10
11
|
"docs/adr/0007-rule26-overrides-and-aground.md": 90,
|
|
11
12
|
"docs/adr/0008-mooring-buoy-modifier.md": 90,
|
|
12
13
|
"docs/adr/0009-data-version-stamp.md": 90,
|
|
14
|
+
"docs/adr/0010-text-withheld-jurisdictions.md": 125,
|
|
15
|
+
"docs/adr/0011-api-shape.md": 173,
|
|
16
|
+
"docs/adr/0012-trace-and-rule2-departure-api.md": 203,
|
|
17
|
+
"docs/verification/2026-09-09-text-slug-straw-man.md": 270,
|
|
13
18
|
"required": true,
|
|
14
|
-
"pending": []
|
|
19
|
+
"pending": [],
|
|
20
|
+
"docs/adr/0013-corpus-files-with-editions.md": 95,
|
|
21
|
+
"docs/timeline.md": 130
|
|
15
22
|
},
|
|
16
23
|
"json_prose": {
|
|
17
24
|
"targets": [
|
|
18
25
|
"data/*.json",
|
|
26
|
+
"data/text/**/*.json",
|
|
19
27
|
"fixtures/*.json"
|
|
20
28
|
],
|
|
21
29
|
"keys": [
|
|
@@ -82,6 +90,7 @@
|
|
|
82
90
|
"AGENTS.md",
|
|
83
91
|
"CLAUDE.md",
|
|
84
92
|
"data/*.json",
|
|
93
|
+
"data/text/**/*.json",
|
|
85
94
|
"fixtures/*.json",
|
|
86
95
|
"test/*.mjs",
|
|
87
96
|
"docs/**/*.md"
|
|
@@ -92,6 +101,7 @@
|
|
|
92
101
|
"AGENTS.md",
|
|
93
102
|
"CLAUDE.md",
|
|
94
103
|
"data/*.json",
|
|
104
|
+
"data/text/**/*.json",
|
|
95
105
|
"fixtures/*.json",
|
|
96
106
|
"test/*.mjs"
|
|
97
107
|
],
|
|
@@ -100,6 +110,7 @@
|
|
|
100
110
|
"AGENTS.md",
|
|
101
111
|
"CLAUDE.md",
|
|
102
112
|
"data/*.json",
|
|
113
|
+
"data/text/**/*.json",
|
|
103
114
|
"fixtures/*.json",
|
|
104
115
|
"test/*.mjs"
|
|
105
116
|
],
|
|
@@ -108,6 +119,7 @@
|
|
|
108
119
|
"AGENTS.md",
|
|
109
120
|
"CLAUDE.md",
|
|
110
121
|
"data/*.json",
|
|
122
|
+
"data/text/**/*.json",
|
|
111
123
|
"fixtures/*.json",
|
|
112
124
|
"test/*.mjs"
|
|
113
125
|
],
|
|
@@ -116,6 +128,7 @@
|
|
|
116
128
|
"AGENTS.md",
|
|
117
129
|
"CLAUDE.md",
|
|
118
130
|
"data/*.json",
|
|
131
|
+
"data/text/**/*.json",
|
|
119
132
|
"fixtures/*.json",
|
|
120
133
|
"test/*.mjs"
|
|
121
134
|
]
|
|
@@ -151,6 +164,7 @@
|
|
|
151
164
|
"CLAUDE.md",
|
|
152
165
|
"docs/**/*.md",
|
|
153
166
|
"data/*.json",
|
|
167
|
+
"data/text/**/*.json",
|
|
154
168
|
"fixtures/*.json"
|
|
155
169
|
]
|
|
156
170
|
},
|
package/docs/gates.json
CHANGED
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"declined_in": "docs/adr/0003-language-as-a-dimension.md",
|
|
37
37
|
"closing_event": "first-non-english-corpus",
|
|
38
38
|
"trigger": "a jurisdiction publishing two editions in force concurrently",
|
|
39
|
-
"status": "
|
|
40
|
-
"settled_by":
|
|
41
|
-
"note": "
|
|
39
|
+
"status": "adopted",
|
|
40
|
+
"settled_by": "docs/adr/0013-corpus-files-with-editions.md",
|
|
41
|
+
"note": "Re-taken and adopted ahead of its due date (translation #1) — ADR 0013 introduces the edition registry directly."
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
"id": "GATE-3",
|
package/docs/requirements.md
CHANGED
|
@@ -76,7 +76,9 @@ Neither consumer lives in this repo.
|
|
|
76
76
|
(Rule 28 "[Reserved]") means silence-means-inherit would apply
|
|
77
77
|
international law where the national body deliberately has none, so no
|
|
78
78
|
non-`intl` jurisdiction lands before an explicit suppression mechanism
|
|
79
|
-
exists.
|
|
79
|
+
exists. That bar is about suppression, not licensing: it is independent of
|
|
80
|
+
REQ-PROV-2 and ADR 0010, and it binds whether a jurisdiction's text ships or
|
|
81
|
+
is withheld. A delta that only *adds* entries is exempt: it suppresses nothing,
|
|
80
82
|
so the Q-11 hazard cannot arise from it (ADR 0008, `30a-buoy`/`30b-buoy`).
|
|
81
83
|
A delta that suppresses or replaces an `intl` entry still waits.
|
|
82
84
|
- **REQ-SCOPE-4** — Adding a jurisdiction MUST be additive. It MUST NOT require
|
|
@@ -123,7 +125,12 @@ Four layers, each independently addressable.
|
|
|
123
125
|
|
|
124
126
|
- **REQ-MODEL-1** — **Rule text**, verbatim, keyed by paragraph path. Text MUST
|
|
125
127
|
NOT be paraphrased, summarised or reflowed. Where a jurisdiction's text
|
|
126
|
-
differs, both MUST be stored, keyed by jurisdiction
|
|
128
|
+
differs, both MUST be stored, keyed by jurisdiction — unless the paragraph is
|
|
129
|
+
`text_status: withheld` (ADR 0010), which stores no rule text. What a
|
|
130
|
+
withheld paragraph carries instead — citation alone, a digest, a
|
|
131
|
+
deterministic non-prose reduction, or the `intl` equivalent — is pencil in
|
|
132
|
+
ADR 0010, deliberately unsettled. This requirement bars paraphrasing text
|
|
133
|
+
the package *ships*; it does not by itself decide the withheld case.
|
|
127
134
|
- **REQ-MODEL-2** — **Light definitions** (Rule 21) MUST carry colour, arc of
|
|
128
135
|
visibility in degrees, and range by length band (Rule 22). Jurisdictions MAY
|
|
129
136
|
add definitions (e.g. the US special flashing light, Inland 21(g)).
|
|
@@ -431,8 +438,7 @@ official languages, and many states gazette their own legally binding
|
|
|
431
438
|
translation. (Recalled, not yet verified against the primary sources — Q-6.)
|
|
432
439
|
See ADR 0003.
|
|
433
440
|
|
|
434
|
-
- **REQ-LANG-1**
|
|
435
|
-
data)** — Language MUST be a dimension orthogonal to jurisdiction,
|
|
441
|
+
- **REQ-LANG-1** — Language MUST be a dimension orthogonal to jurisdiction,
|
|
436
442
|
identified by BCP 47 tags. Which body of rules applies and which text of
|
|
437
443
|
them is displayed are independent questions; neither MUST ever be inferred
|
|
438
444
|
from the other, and no property beyond the language of the text — not
|
|
@@ -445,8 +451,7 @@ See ADR 0003.
|
|
|
445
451
|
and renaming an identifier is a breaking change (REQ-PKG-4). Immutability
|
|
446
452
|
itself is REQ-MODEL-10; this requirement adds only that identifiers are
|
|
447
453
|
never localized.
|
|
448
|
-
- **REQ-LANG-3**
|
|
449
|
-
corpus per jurisdiction × language × source)** — Rule text MUST be storable
|
|
454
|
+
- **REQ-LANG-3** — Rule text MUST be storable
|
|
450
455
|
as a **corpus** per (jurisdiction × language × source), keyed by paragraph
|
|
451
456
|
path, holding at most one text per path, with corpus-level provenance and
|
|
452
457
|
one declared status tier:
|
|
@@ -461,8 +466,7 @@ See ADR 0003.
|
|
|
461
466
|
rule applies per corpus, against that corpus's own source.
|
|
462
467
|
- **REQ-LANG-4** — Adding a language MUST be additive: no schema change, no
|
|
463
468
|
edits to existing corpora or catalogs (the language mirror of REQ-SCOPE-4).
|
|
464
|
-
- **REQ-LANG-5**
|
|
465
|
-
are unwritten)** — Corpora MAY be partial. Coverage MUST be declared in
|
|
469
|
+
- **REQ-LANG-5** — Corpora MAY be partial. Coverage MUST be declared in
|
|
466
470
|
machine-readable form, and CI MUST fail on a corpus key that does not
|
|
467
471
|
resolve to a known paragraph path, and on a corpus filename that disagrees
|
|
468
472
|
with the file's internal metadata. Silence MUST NOT imply coverage (the
|
|
@@ -488,15 +492,13 @@ See ADR 0003.
|
|
|
488
492
|
- **REQ-LANG-8** — A `community`-tier corpus MUST record who produced and
|
|
489
493
|
who reviewed it. Machine translation without named human review MUST NOT
|
|
490
494
|
be accepted.
|
|
491
|
-
- **REQ-LANG-9**
|
|
492
|
-
form)** — Verbatim (REQ-MODEL-1) is defined at the Unicode level: each
|
|
495
|
+
- **REQ-LANG-9** — Verbatim (REQ-MODEL-1) is defined at the Unicode level: each
|
|
493
496
|
corpus MUST declare the normalization form applied to its text (NFC unless
|
|
494
497
|
declared otherwise) and MUST NOT insert or strip bidi control characters,
|
|
495
498
|
localize numerals, punctuation, units or quotation marks, or otherwise "fix"
|
|
496
499
|
the source text. Rendering direction is a consumer concern and MUST stay out
|
|
497
500
|
of the data.
|
|
498
|
-
- **REQ-LANG-10**
|
|
499
|
-
declares an amendment state)** — The structural skeleton MUST declare, as
|
|
501
|
+
- **REQ-LANG-10** — The structural skeleton MUST declare, as
|
|
500
502
|
data, the amendment state it consolidates (e.g. "COLREGS 72 as amended
|
|
501
503
|
through …"). Every corpus MUST declare the amendment state its source
|
|
502
504
|
reflects. The two MAY differ — a corpus transcribed from an older
|
|
@@ -509,9 +511,10 @@ See ADR 0003.
|
|
|
509
511
|
|
|
510
512
|
- **REQ-PROV-1** — Every text and image asset MUST record its source, the date
|
|
511
513
|
retrieved, and its licence or public-domain basis.
|
|
512
|
-
- **REQ-PROV-2** — A jurisdiction MUST NOT be
|
|
513
|
-
have been checked against the primary source and recorded.
|
|
514
|
-
terms are not sufficient.
|
|
514
|
+
- **REQ-PROV-2** — A jurisdiction's *rule text* MUST NOT be published until its
|
|
515
|
+
reproduction terms have been checked against the primary source and recorded.
|
|
516
|
+
Recalled or assumed terms are not sufficient. This does not bar *modelling* a
|
|
517
|
+
jurisdiction: its structure may ship with the text withheld — ADR 0010.
|
|
515
518
|
- **REQ-PROV-3** — Where a licence requires attribution (e.g. OGL, CC BY), the
|
|
516
519
|
attribution text MUST ship in the package, not only in the repo.
|
|
517
520
|
- **REQ-PROV-4** — Code licence and data licence MUST be stated separately. The
|
|
@@ -519,8 +522,7 @@ See ADR 0003.
|
|
|
519
522
|
- **REQ-PROV-5** — Images MUST be addressable as data: an image record per file,
|
|
520
523
|
naming what it illustrates by entry id or paragraph path. Unexplained filename
|
|
521
524
|
prefixes are a provenance defect.
|
|
522
|
-
- **REQ-PROV-6**
|
|
523
|
-
rights are one flat field)** — Source identity MUST be structured data —
|
|
525
|
+
- **REQ-PROV-6** — Source identity MUST be structured data —
|
|
524
526
|
publisher, title, edition, publication and effective dates, URL, retrieval
|
|
525
527
|
date — not a prose string. Rights MUST be recorded separately for the source
|
|
526
528
|
text, for the basis on which this package redistributes it, and for the
|
|
@@ -691,20 +693,21 @@ Each gate names the declined design, the closing event, and the trigger.
|
|
|
691
693
|
second-jurisdiction bundle — justified by Q-11 and GATE-2, no longer by
|
|
692
694
|
a cross-jurisdiction trigger route.
|
|
693
695
|
|
|
694
|
-
-
|
|
695
|
-
(ADR 0003, declined; the adopted 80% is REQ-LANG-10).
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
696
|
+
- ~~**GATE-2 — instrument → edition → corpus as first-class layers**~~
|
|
697
|
+
(ADR 0003, declined; the adopted 80% is REQ-LANG-10). **Re-taken and
|
|
698
|
+
adopted, ADR 0013** — ahead of its due date (translation #1): the
|
|
699
|
+
edition registry lands directly, rather than waiting for the trigger
|
|
700
|
+
below to fire.
|
|
701
|
+
*Closing event, as declined*: the second corpus of any one jurisdiction
|
|
702
|
+
— which, read against ADR 0003's sequencing, means **the first
|
|
703
|
+
non-English corpus**, not a distant milestone. A French or Finnish text
|
|
704
|
+
of `intl` is a second corpus of `intl`. With one corpus, re-homing it
|
|
705
|
+
under an edition parent is a single file move; the cost scales with
|
|
701
706
|
corpora × languages immediately thereafter.
|
|
702
|
-
*Trigger*: a jurisdiction publishing two editions in force
|
|
707
|
+
*Trigger, as declined*: a jurisdiction publishing two editions in force
|
|
703
708
|
concurrently — an old and a new text running in parallel through a
|
|
704
709
|
transition period. REQ-LANG-10's declared amendment state makes such a
|
|
705
|
-
pair machine-visible, which is what
|
|
706
|
-
*Re-take required before translation #1 lands* — the edition-layer
|
|
707
|
-
decision is due at the first added translation, not at 1.0.
|
|
710
|
+
pair machine-visible, which is what gave this trigger its foothold.
|
|
708
711
|
|
|
709
712
|
- **GATE-3 — legal-status × translation-status as two enums**
|
|
710
713
|
(ADR 0003, half-adopted: one tier for legal authority in REQ-LANG-3,
|
|
@@ -792,6 +795,9 @@ Tracked here until resolved; each becomes an ADR.
|
|
|
792
795
|
unece.org was unreachable from the checking host, and the UN's default
|
|
793
796
|
terms (personal, non-commercial use only) block it until written
|
|
794
797
|
permission or a national transposition is chosen instead.
|
|
798
|
+
**Ruled 2026-09-09 (ADR 0010):** this blocks CEVNI's *text*, not CEVNI.
|
|
799
|
+
A jurisdiction may be modelled in full with its text withheld, so no
|
|
800
|
+
session should treat `eu/cevni` as unimplementable.
|
|
795
801
|
- **Q-4** — Two upstream SignalK spec asks are outstanding and independent of
|
|
796
802
|
this package: a making-way indicator, and `design.maxSpeed`.
|
|
797
803
|
- **Q-5** — REQ-VERIFY-5 asks for boundary fixtures on every numeric gate.
|
|
@@ -831,6 +837,14 @@ Tracked here until resolved; each becomes an ADR.
|
|
|
831
837
|
not only as a whole: clearing one candidate source unblocks that corpus
|
|
832
838
|
alone, which is the cheap path when a demo needs a specific language
|
|
833
839
|
early.
|
|
840
|
+
**BOE (`es`) and Finlex (`fi`) verified clean, 2026-09-09 and 2026-09-12**
|
|
841
|
+
(ADR 0001 amendments) — both permit the reuse REQ-PROV-2 needs. The UNTS
|
|
842
|
+
deposit (`en`/`fr`) is confirmed **blocked**, not merely unverified: no
|
|
843
|
+
UNTS-specific rights statement exists, and the reachable terms are the
|
|
844
|
+
same personal/non-commercial, no-derivative-works terms that block CEVNI.
|
|
845
|
+
`es` is the cheapest language to ship the first non-`intl` corpus; UNTS
|
|
846
|
+
needs written UN permission or a national republication before `en`/`fr`
|
|
847
|
+
can use the deposit route.
|
|
834
848
|
- **Q-8** — Does the paragraph path survive the first national amalgamation?
|
|
835
849
|
GATE-1's accepted risk rests on paragraph paths being immutable
|
|
836
850
|
(REQ-MODEL-10) — adding and deprecating are fine, but a path that keeps its
|
|
@@ -918,7 +932,9 @@ Tracked here until resolved; each becomes an ADR.
|
|
|
918
932
|
"this path/entry deliberately does not exist here", distinguishable from
|
|
919
933
|
"not yet transcribed". Decide the mechanism in the second-jurisdiction
|
|
920
934
|
bundle (GATE-1 re-take, GATE-2, Q-10); until then no non-`intl`
|
|
921
|
-
jurisdiction lands.
|
|
935
|
+
jurisdiction lands. This is the live blocker on CEVNI, and it is *not* the
|
|
936
|
+
licence one: ADR 0010 removed the licence block by permitting structure with
|
|
937
|
+
the text withheld, and left this one standing.
|
|
922
938
|
Also from the same verification pass, tracked on the global board rather
|
|
923
939
|
than here: four transcription defects in `data/rules.json` itself
|
|
924
940
|
(`21(a)`, `21(b)`, `23(b)`, `29(b)`) — a data fix, not a design
|
|
@@ -1328,6 +1344,16 @@ written up in `docs/identifiers.md` §"Effects"; what it could not is here.
|
|
|
1328
1344
|
be a number invented rather than declared. Settled by whatever settles the
|
|
1329
1345
|
`conduct` monitors, which are the things that watch a duty end rather than
|
|
1330
1346
|
begin.
|
|
1347
|
+
|
|
1348
|
+
**Ruled 2026-09-08:** while one vessel holds the latch, 13(a)'s sector
|
|
1349
|
+
test is suppressed on the other, newly-gaining vessel — once overtaking,
|
|
1350
|
+
always overtaking, per 13(d)'s own text, so a geometry test that would
|
|
1351
|
+
newly name a second give-way vessel must not fire while the first still
|
|
1352
|
+
holds the role by history alone. `13a`'s sector branch now also reads
|
|
1353
|
+
`other:hist:was_overtaking: false`; the closing fixture in
|
|
1354
|
+
`fixtures/situation-fixtures.json` is the case this closes — a vessel
|
|
1355
|
+
drops back onto the latch-holder's own stern, and only the latch-holder
|
|
1356
|
+
gives way.
|
|
1331
1357
|
- **Q-48** — **Nothing checks that a situation is geometrically possible.**
|
|
1332
1358
|
`own:geo:rel_bearing_deg`, `other:geo:rel_bearing_deg`, the two
|
|
1333
1359
|
`kin:heading_deg` and the two `kin:sog_kn` are six facts related by two
|
|
@@ -1398,16 +1424,25 @@ nothing is blocked while open.
|
|
|
1398
1424
|
- **Q-51** — **What arms 13(d)'s latch?** *A:* 13(b)'s deeming, at the first
|
|
1399
1425
|
state the geometry holds. *B:* 13(a)'s duty actually attaching. They differ
|
|
1400
1426
|
where the geometry holds but a condition on 13(a) does not — `Q-50`'s
|
|
1401
|
-
surface.
|
|
1402
|
-
|
|
1403
|
-
|
|
1427
|
+
surface. Pencilled **B** ✎ (Solace, 2026-09-14): the latch arms when the
|
|
1428
|
+
duty attaches. KIVELI [2025] EWHC 1185 (Admlty) holds the analogous Rule 14
|
|
1429
|
+
latch arms on risk of collision and then persists; no authority was found on
|
|
1430
|
+
overtaking geometry with no risk of collision. Free to reverse: four entries
|
|
1431
|
+
(`13a`, `13b-*`, `13d`) and their fixtures, and nothing consumes the one
|
|
1432
|
+
state this changes the answer for. Settled by: a decision on Rule 13 itself,
|
|
1433
|
+
or Cockcroft & Lameijer on whether overtaking status needs risk of
|
|
1434
|
+
collision.
|
|
1404
1435
|
- **Q-52** — **What does 13(d)'s latch forbid?** *Narrow:* reclassification to
|
|
1405
1436
|
*crossing* only, as the paragraph says, leaving head-on to Rule 14 on the
|
|
1406
1437
|
geometry of the moment. *Broad:* the encounter stays an overtaking and no
|
|
1407
1438
|
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.
|
|
1409
|
-
**broad
|
|
1410
|
-
`
|
|
1439
|
+
on encounter type, which Rule 17's phases and 14(a) hang off.
|
|
1440
|
+
Pencilled **broad** ✎ (Solace, 2026-09-14), which is also the data's
|
|
1441
|
+
default: `13d` yields `encounter: overtaking` from history alone, `14b` and
|
|
1442
|
+
`15a-crossing` gate on `was_overtaking: false`. 13(a)'s "notwithstanding"
|
|
1443
|
+
already displaces Rule 14; eCOLREGs states the broad reading as conventional.
|
|
1444
|
+
Settled by: a case or commentary on an overtaking becoming a head-on; none
|
|
1445
|
+
found.
|
|
1411
1446
|
- **Q-53** — **Does 17(a)(ii) suspend 17(a)(i)'s duty, or add an exception?**
|
|
1412
1447
|
*Suspension:* "may, however" lifts the duty once non-compliance is apparent;
|
|
1413
1448
|
a monitor then flags nothing. *Exception:* the duty stands and a departure is
|
|
@@ -1447,3 +1482,10 @@ Two decisions taken in pencil, reversible in one edit: the invariants live in
|
|
|
1447
1482
|
their own document under `REQ-INV-1`–`REQ-INV-7` (§4.2 says why), and the id
|
|
1448
1483
|
scheme is `REQ-INV-2`'s — cheap to change until P4.2 cites an id from a TLA+
|
|
1449
1484
|
module, expensive after.
|
|
1485
|
+
|
|
1486
|
+
A third, ruled rather than pencil: **`docs/part-b-invariants.md` stays
|
|
1487
|
+
hand-written Markdown; no derived JSON registry.** Ruled 2026-09-08. Revisit
|
|
1488
|
+
only if P4.2's TLA+ needs to cite `INV-` ids mechanically — the id scheme
|
|
1489
|
+
just above is already the cheap-to-change half of that trigger, so the
|
|
1490
|
+
revisit costs one file's worth of tooling, not a re-derivation of the ids
|
|
1491
|
+
themselves.
|