colregs 0.2.2 → 0.2.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.
@@ -0,0 +1,203 @@
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
187
+ reserved and nothing exported: a stub answering `inconclusive-in-model`
188
+ is a stub wearing a status.
189
+
190
+ ## Register
191
+
192
+ | item | level | what would settle it |
193
+ |---|---|---|
194
+ | Two more verbs, one per input, in this package | ✎ | Mark's confirmation; supersedes ADR 0011's "separate package" row |
195
+ | `Trace` an object over `TraceSample[]`, `t_s` on the caller's clock, pair identity the caller's | ✎ | the first trace fixture |
196
+ | `hist:was_overtaking` a caller-supplied snapshot fact, never engine-derived | ✎ | Q-47; the first conduct monitor |
197
+ | `ConductVerdict` alphabet `kept`/`breached`/`pending`; absent is absent | ✎ | the first STL monitor being written |
198
+ | Field names snake_case with unit suffixes; `EntryId`/`ParagraphCite` per ADR 0011 §4 | ✎ | that ADR's row; the compiler enforces neither |
199
+ | `ConductPhaseChange.phase` values are paragraph cites, not entry ids | ✎ | the programme phase-4 TLA+ or UPPAAL model |
200
+ | 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 |
201
+ | `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 |
202
+ | No *situation* input names a departure; the grid does, and is named in every finding | ✎ | — |
203
+ | Nothing exported until a fixture backs it; exports then carry TSDoc's `@beta` release tag | ✎ | — |
package/docs/budgets.json CHANGED
@@ -1,14 +1,19 @@
1
1
  {
2
2
  "$comment": "Prose budgets enforced by prose-budget (the engine in dotfiles .local/bin), run locally by npm test and in CI by the shared workflow. Raising a number or adding an exception is a deliberate, reviewable diff.",
3
3
  "lines": {
4
- "README.md": 360,
5
- "AGENTS.md": 120,
4
+ "README.md": 200,
5
+ "AGENTS.md": 122,
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
10
  "docs/adr/0007-rule26-overrides-and-aground.md": 90,
11
11
  "docs/adr/0008-mooring-buoy-modifier.md": 90,
12
+ "docs/adr/0009-data-version-stamp.md": 90,
13
+ "docs/adr/0010-text-withheld-jurisdictions.md": 125,
14
+ "docs/adr/0011-api-shape.md": 173,
15
+ "docs/adr/0012-trace-and-rule2-departure-api.md": 203,
16
+ "docs/verification/2026-09-09-text-slug-straw-man.md": 270,
12
17
  "required": true,
13
18
  "pending": []
14
19
  },
@@ -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. A delta that only *adds* entries is exempt: it suppresses nothing,
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)).
@@ -509,9 +516,10 @@ See ADR 0003.
509
516
 
510
517
  - **REQ-PROV-1** — Every text and image asset MUST record its source, the date
511
518
  retrieved, and its licence or public-domain basis.
512
- - **REQ-PROV-2** — A jurisdiction MUST NOT be added until its reproduction terms
513
- have been checked against the primary source and recorded. Recalled or assumed
514
- terms are not sufficient.
519
+ - **REQ-PROV-2** — A jurisdiction's *rule text* MUST NOT be published until its
520
+ reproduction terms have been checked against the primary source and recorded.
521
+ Recalled or assumed terms are not sufficient. This does not bar *modelling* a
522
+ jurisdiction: its structure may ship with the text withheld — ADR 0010.
515
523
  - **REQ-PROV-3** — Where a licence requires attribution (e.g. OGL, CC BY), the
516
524
  attribution text MUST ship in the package, not only in the repo.
517
525
  - **REQ-PROV-4** — Code licence and data licence MUST be stated separately. The
@@ -792,6 +800,9 @@ Tracked here until resolved; each becomes an ADR.
792
800
  unece.org was unreachable from the checking host, and the UN's default
793
801
  terms (personal, non-commercial use only) block it until written
794
802
  permission or a national transposition is chosen instead.
803
+ **Ruled 2026-09-09 (ADR 0010):** this blocks CEVNI's *text*, not CEVNI.
804
+ A jurisdiction may be modelled in full with its text withheld, so no
805
+ session should treat `eu/cevni` as unimplementable.
795
806
  - **Q-4** — Two upstream SignalK spec asks are outstanding and independent of
796
807
  this package: a making-way indicator, and `design.maxSpeed`.
797
808
  - **Q-5** — REQ-VERIFY-5 asks for boundary fixtures on every numeric gate.
@@ -918,7 +929,9 @@ Tracked here until resolved; each becomes an ADR.
918
929
  "this path/entry deliberately does not exist here", distinguishable from
919
930
  "not yet transcribed". Decide the mechanism in the second-jurisdiction
920
931
  bundle (GATE-1 re-take, GATE-2, Q-10); until then no non-`intl`
921
- jurisdiction lands.
932
+ jurisdiction lands. This is the live blocker on CEVNI, and it is *not* the
933
+ licence one: ADR 0010 removed the licence block by permitting structure with
934
+ the text withheld, and left this one standing.
922
935
  Also from the same verification pass, tracked on the global board rather
923
936
  than here: four transcription defects in `data/rules.json` itself
924
937
  (`21(a)`, `21(b)`, `23(b)`, `29(b)`) — a data fix, not a design
@@ -1328,6 +1341,16 @@ written up in `docs/identifiers.md` §"Effects"; what it could not is here.
1328
1341
  be a number invented rather than declared. Settled by whatever settles the
1329
1342
  `conduct` monitors, which are the things that watch a duty end rather than
1330
1343
  begin.
1344
+
1345
+ **Ruled 2026-09-08:** while one vessel holds the latch, 13(a)'s sector
1346
+ test is suppressed on the other, newly-gaining vessel — once overtaking,
1347
+ always overtaking, per 13(d)'s own text, so a geometry test that would
1348
+ newly name a second give-way vessel must not fire while the first still
1349
+ holds the role by history alone. `13a`'s sector branch now also reads
1350
+ `other:hist:was_overtaking: false`; the closing fixture in
1351
+ `fixtures/situation-fixtures.json` is the case this closes — a vessel
1352
+ drops back onto the latch-holder's own stern, and only the latch-holder
1353
+ gives way.
1331
1354
  - **Q-48** — **Nothing checks that a situation is geometrically possible.**
1332
1355
  `own:geo:rel_bearing_deg`, `other:geo:rel_bearing_deg`, the two
1333
1356
  `kin:heading_deg` and the two `kin:sog_kn` are six facts related by two
@@ -1447,3 +1470,10 @@ Two decisions taken in pencil, reversible in one edit: the invariants live in
1447
1470
  their own document under `REQ-INV-1`–`REQ-INV-7` (§4.2 says why), and the id
1448
1471
  scheme is `REQ-INV-2`'s — cheap to change until P4.2 cites an id from a TLA+
1449
1472
  module, expensive after.
1473
+
1474
+ A third, ruled rather than pencil: **`docs/part-b-invariants.md` stays
1475
+ hand-written Markdown; no derived JSON registry.** Ruled 2026-09-08. Revisit
1476
+ only if P4.2's TLA+ needs to cite `INV-` ids mechanically — the id scheme
1477
+ just above is already the cheap-to-change half of that trigger, so the
1478
+ revisit costs one file's worth of tooling, not a re-derivation of the ids
1479
+ themselves.
@@ -0,0 +1,264 @@
1
+ # The `text_slug` straw man, built on `intl` Rules 1-19
2
+
3
+ ADR 0010 leaves in pencil what a withheld paragraph carries in place of its
4
+ text, and names a slug as a straw man to be built and judged rather than
5
+ argued about. This is the build and the judgement. The research issue is
6
+ colregs#83; nothing here decides the pencil item.
7
+
8
+ Everything below is reproducible from `scripts/text-slug.mjs` over
9
+ `data/rules.json` at this commit. Nothing was written into `data/`.
10
+
11
+ ## What the pipeline does
12
+
13
+ Deterministic, no model in the loop, no dependencies:
14
+
15
+ 1. **Citations first.** `Rule 13`, `Rules 4-19`, `Rule 34(c)(i)` become
16
+ `rule-13`, `rules-4-19`, `rule-34ci`. A citation is a fact about which
17
+ provision is invoked, not expression, and it is the most navigable term a
18
+ slug can carry.
19
+ 2. **Terms of art as one term.** A fixed table of multi-word terms collapses
20
+ each to a hyphenated token: `traffic-separation-scheme`, `not-under-command`,
21
+ `restricted-ability-maneuver`, `keep-out-of-way`, `risk-of-collision`,
22
+ `in-sight`, `narrow-channel`. The target form is the applicability
23
+ vocabulary's wherever one exists.
24
+ 3. **Stop words out.** Function words, modals and the words that are in nearly
25
+ every paragraph and say nothing about which one this is: `vessel`, `rule`,
26
+ `shall`, `circumstances`, `practicable`.
27
+ 4. **Lemmatise.** Plurals by rule; every `-ed` and `-ing` form by a table,
28
+ because a suffix rule cannot tell `speed` from `stopped` without a lexicon.
29
+ Gerunds that name an activity in `facts.json` keep the gerund (`fishing`,
30
+ `towing`), so the slug's form is the vocabulary's form.
31
+ 5. **Sorted set.** No order, no syntax, no counts.
32
+
33
+ Numbers keep their unit (`20-meter`, `22-5-degree`).
34
+
35
+ ## Shape of the output
36
+
37
+ | measure | Rules 1-19 |
38
+ |---|---|
39
+ | paragraphs | 122 |
40
+ | terms per slug, min / median / max | 0 / 6 / 34 |
41
+ | empty slugs | 1: `6(a)`, whose text is "By all vessels:" |
42
+ | identical slugs | 3 groups, all in Rule 18: `18(a)(i)=18(b)(i)=18(c)(i)`, `18(a)(ii)=18(b)(ii)=18(c)(ii)`, `18(a)(iii)=18(b)(iii)` |
43
+ | near-duplicates, Jaccard ≥ 0.6 | 1 pair: `13(c)` and `14(c)`, the two "if in doubt, assume" paragraphs |
44
+
45
+ The identical slugs are identical source text: "a vessel not under command"
46
+ under three different chapeaux. No slug can separate them, and neither can
47
+ the text; the path does. That is the ceiling on question 1 for this corpus.
48
+
49
+ ## Question 1: can a reader navigate to the paragraph from the slug alone?
50
+
51
+ Method: the 122 slugs, shuffled by a fixed hash of the path and stripped of
52
+ their paths (`--blind`), were given to two readers with no access to the
53
+ repository or any other file, asked to name the paragraph for each line with
54
+ a confidence from 1 to 3. The readers were two language models chosen as
55
+ proxies for a strong and a weak human reader of the Rules: one that knows
56
+ COLREGS closely (Claude Opus) and one that knows it loosely (Claude Haiku).
57
+ Scored against the key.
58
+
59
+ | reader | exact paragraph | correct rule | wrong while "sure" |
60
+ |---|---|---|---|
61
+ | strong | 115 / 122 | 122 / 122 | 1 |
62
+ | weak | 30 / 122 | 79 / 122 | 38 |
63
+
64
+ The strong reader's seven misses: six are inside the three identical-slug
65
+ groups above, where the answer is one of the group and the slug cannot say
66
+ which; the seventh is `15` for `15(a)`, a citation-form slip on a rule with
67
+ one paragraph. Every miss that the slug could in principle have prevented,
68
+ it prevented.
69
+
70
+ The weak reader reached the right rule in two cases out of three and the
71
+ right paragraph in one out of four, and was confidently wrong 38 times. The
72
+ misses are almost all off-by-one within a rule (`8(b)` read as `8(a)`) or a
73
+ Rule 18 sub-item read as its Rule 3 definition, which is to say the reader
74
+ did not know the paragraph numbering, and the slug does not teach it.
75
+
76
+ Reading: **the slug is an index, not a substitute.** A reader who knows the
77
+ Rules lands on the paragraph from the slug alone, at the ceiling the corpus
78
+ allows. A reader who does not know the Rules lands on the rule, mostly, and
79
+ on the paragraph rarely. For the case ADR 0010 is for, that ordering
80
+ matters: a CEVNI reader will usually be the second kind, the source is not
81
+ English, and the terms would be translations, so the strong-reader number is
82
+ an upper bound and the weak-reader number is the honest expectation.
83
+
84
+ ## Question 2: do the terms overlap the applicability vocabulary?
85
+
86
+ Method: every fact key and enumerated value that `data/applicability.json`
87
+ predicates and `data/facts.json` axes use, 67 terms, mapped to slug space by
88
+ a table that is mostly the four abbreviations (`nuc`, `ram`, `cbd`, `wig`)
89
+ and the underscores.
90
+
91
+ - **59 of 67 terms appear in some slug of Rules 1-19.** The eight that do not
92
+ are terms from outside the range or not terms at all: `activity:diving`,
93
+ `activity:pushing`, `activity:none`, `position:moored`, `propulsion:oars`,
94
+ `fact:composite_unit`, `fact:on_mooring_buoy`, `wind_side:unknown`. Within
95
+ the range the overlap is complete.
96
+ - **Per entry, the cited paragraph's slug carries the term the entry turns
97
+ on.** All 31 entries citing a paragraph in Rules 1-19 were checked
98
+ (`--stats`). `13a` reads `hist:was_overtaking`; `13(a)`'s slug carries
99
+ `overtake`. `9b` reads `length_m`, `propulsion:sail` and
100
+ `confined_to_channel`; `9(b)`'s slug carries `20-meter`, `sailing-vessel`,
101
+ `narrow-channel`, `navigate`. `18d1` reads three Rule 18 classes;
102
+ `18(d)(i)`'s slug carries all three. What the slug does not carry are the
103
+ gates an entry inherits from a chapeau or a scope rule rather than from
104
+ its own paragraph: `pair:geo:in_sight` (Rule 11), `position:underway`
105
+ (`18(a)`'s chapeau), and the derived `fact:rule18_class` key. Those are in
106
+ the chapeau's slug, which is where the text puts them.
107
+
108
+ Reading: **the overlap is real and it runs the useful way.** The
109
+ applicability vocabulary was written from these paragraphs, so the slug of a
110
+ paragraph recovers the terms its entries gate on. A term-set built for a
111
+ withheld jurisdiction the same way would give a consumer a vocabulary-level
112
+ hook into each paragraph without a word of it.
113
+
114
+ ## What this does and does not settle
115
+
116
+ Settled by the build: the pipeline is deterministic, cheap, and produces a
117
+ navigable index for a reader who already knows the Rules, at the corpus's
118
+ own ceiling. The terms line up with the vocabulary.
119
+
120
+ Not settled, and not this document's to settle:
121
+
122
+ - Whether a slug ships for CEVNI at all, alone or beside `text_digest` and
123
+ `mirrors`. The weak-reader result says a slug does not replace the words
124
+ for the reader most likely to need them; `mirrors` does, for the majority
125
+ of CEVNI, and the slug's value is in the remainder.
126
+ - Where the derivative-work line falls. Out of scope on colregs#82; redaction
127
+ ships whatever the answer.
128
+ - The pipeline's tables are hand-built for this corpus. A CEVNI run needs a
129
+ translation step to English counterparts before the same tables apply,
130
+ and a lexicon for forms the tables do not know. That is engineering, not
131
+ a design question.
132
+
133
+ What would move the pencil item: a decision on the first bullet, taken with
134
+ the `mirrors` coverage of CEVNI in hand, since that number is what says how
135
+ much of CEVNI the slug would be the only thing standing in for.
136
+
137
+ ## The slugs
138
+
139
+ `node scripts/text-slug.mjs`, Rules 1-19, at this commit:
140
+
141
+ ```
142
+ 4 apply, rules-4-10, visibility
143
+ 5 appraisal, available, hearing, look-out, maintain, risk-of-collision, sight, situation
144
+ 6 action, avoid, collision, determine, distance, effective, factor, proceed, safe-speed, stop
145
+ 11 apply, in-sight, rules-11-18
146
+ 16 action, direct, early, keep-clear, keep-out-of-way, substantial
147
+ 18 except, rule-10, rule-13, rule-9
148
+ 1(a) apply, connect, high-seas, navigable, seagoing, water
149
+ 1(b) authority, conform, connect, harbor, high-seas, inland, interfere, lake, navigable, operation, river, roadstead, seagoing, waterway
150
+ 1(c) additional, authorize, convoy, elsewhere, engaged-in-fishing, fishing, fleet, government, interfere, light, mistake, operation, proceed, shape, ship, signal, state, station, war, whistle
151
+ 1(d) adopt, organization, traffic-separation-scheme
152
+ 1(e) appliance, arc, characteristic, comply, concern, construction, determine, disposition, government, light, number, position, range, shape, sound-signaling, visibility
153
+ 2(a) comply, consequence, crew, exonerate, master, neglect, owner, practice, precaution, seaman
154
+ 2(b) avoid, collision, comply, construe, danger, departure, immediate, limitation, navigation
155
+ 3(a) craft, non-displacement, seaplane, transportation, water, watercraft, wig-craft
156
+ 3(b) machinery, power-driven, propel
157
+ 3(c) fit, machinery, propel, sail, sailing-vessel
158
+ 3(d) apparatus, engaged-in-fishing, fishing, line, maneuver, net, restrict, trawl, trolling
159
+ 3(e) aircraft, design, maneuver, seaplane, water
160
+ 3(f) exceptional, keep-out-of-way, maneuver, not-under-command, unable
161
+ 3(g) ability, aircraft, cable, cargo, clear, course, deviate, dredging, engage, keep-out-of-way, launch, lay, limit, mark, mine, navigational, operation, person, pick-up, pipeline, recover, replenish, restrict, restricted-ability-maneuver, service, submarine, surveying, tow, towing, transfer, unable, underwater, underway, work
162
+ 3(h) available, constrained-by-draft, course, depth, draft, navigable, power-driven, restricted-ability-maneuver, water, width
163
+ 3(i) aground, anchor, fast, shore, underway
164
+ 3(j) breadth, length, overall
165
+ 3(k) in-sight, observe
166
+ 3(l) fall, fog, mist, rainstorm, restrict, restricted-visibility, sandstorm, snow, visibility
167
+ 3(m) action, close, craft, fly, mode, multimodal, operational, proximity, surface, surface-effect, utilize, wig, wing-in-ground
168
+ 6(a)
169
+ 6(a)(i) state, visibility
170
+ 6(a)(ii) concentration, density, fishing, traffic
171
+ 6(a)(iii) ability, distance, maneuver, stop, turn
172
+ 6(a)(iv) back, background, light, night, scatter, shore
173
+ 6(a)(v) current, hazard, navigational, proximity, sea, state, wind
174
+ 6(a)(vi) available, depth, draft, water
175
+ 6(b) operational, radar
176
+ 6(b)(i) characteristic, efficiency, equipment, limitation, radar
177
+ 6(b)(ii) constraint, impose, radar, range, scale
178
+ 6(b)(iii) detect, effect, interference, radar, sea, source, state, weather
179
+ 6(b)(iv) adequate, detect, float, ice, object, radar, range, small
180
+ 6(b)(v) detect, location, movement, number, radar
181
+ 6(b)(vi) assess, determine, object, radar, range, vicinity, visibility
182
+ 7(a) available, determine, doubt, risk, risk-of-collision
183
+ 7(b) detect, early, equipment, equivalent, fit, long-range, object, observe, operational, plot, radar, risk-of-collision, scan, systematic, warning
184
+ 7(c) assume, information, radar, scanty
185
+ 7(d) consideration, determine, risk-of-collision
186
+ 7(d)(i) approach, change, compass-bearing, risk
187
+ 7(d)(ii) appreciable, approach, bearing, change, close, large, range, risk, tow
188
+ 8(a) action, avoid, collision, observe, positive, rules-4-19, seamanship
189
+ 8(b) alter, avoid, collision, course, large, observe, radar, small, speed, succession
190
+ 8(c) action, alone, alter, avoid, close-quarters, course, effective, room, sea, substantial
191
+ 8(d) action, avoid, check, clear, collision, distance, effective, pass, past, safe
192
+ 8(e) assess, avoid, collision, propulsion, reverse, situation, slacken, speed, stop, way
193
+ 8(f)(i) action, early, impede, passage, room, safe, sea
194
+ 8(f)(ii) action, approach, impede, obligation, passage, relieve, risk-of-collision, rules-4-19, safe
195
+ 8(f)(iii) approach, comply, impede, oblige, passage, risk-of-collision, rules-4-19, two
196
+ 9(a) channel, course, fairway, keep, lie, limit, narrow-channel, near, outer, proceed, safe, side, starboard
197
+ 9(b) 20-meter, fairway, impede, length, narrow-channel, navigate, passage, sailing-vessel
198
+ 9(c) engaged-in-fishing, fairway, impede, narrow-channel, navigate, passage
199
+ 9(d) channel, cross, doubt, fairway, impede, intention, narrow-channel, navigate, passage, prescribe, rule-34d, signal
200
+ 9(e)(i) action, agreement, doubt, fairway, indicate, intend, intention, narrow-channel, overtake, pass, prescribe, rule-34ci, rule-34cii, rule-34d, safe, signal, sound, step
201
+ 9(e)(ii) obligation, overtake, relieve, rule-13
202
+ 9(f) alertness, area, bend, caution, fairway, intervene, narrow-channel, navigate, near, obscure, obstruction, prescribe, rule-34e, signal, sound
203
+ 9(g) anchor, avoid, narrow-channel
204
+ 10(a) adopt, apply, obligation, organization, relieve, traffic-separation-scheme
205
+ 10(b) traffic-separation-scheme
206
+ 10(b)(i) direction, flow, lane, proceed, traffic, traffic-lane
207
+ 10(b)(ii) keep-clear, separation-line, separation-zone, traffic
208
+ 10(b)(iii) angle, direction, flow, join, lane, leave, side, small, termination, traffic, traffic-lane
209
+ 10(c) angle, avoid, cross, direction, flow, heading, oblige, right, traffic, traffic-lane
210
+ 10(d)(i) 20-meter, adjacent, engaged-in-fishing, inshore-traffic-zone, length, sailing-vessel, traffic-lane, traffic-separation-scheme
211
+ 10(d)(ii) avoid, danger, immediate, inshore-traffic-zone, installation, offshore, pilot, port, route, rule-10di, situate, station, structure
212
+ 10(e) cross, enter, except, join, lane, leave, separation-line, separation-zone
213
+ 10(e)(i) avoid, danger, emergency, immediate
214
+ 10(e)(ii) engage, fishing, separation-zone
215
+ 10(f) area, caution, navigate, near, termination, traffic-separation-scheme
216
+ 10(g) anchor, area, avoid, near, termination, traffic-separation-scheme
217
+ 10(h) avoid, margin, traffic-separation-scheme, wide
218
+ 10(i) engaged-in-fishing, impede, passage, traffic-lane
219
+ 10(j) 20-meter, impede, length, passage, power-driven, safe, sailing-vessel, traffic-lane
220
+ 10(k) carry, comply, engage, exempt, maintenance, navigation, operation, restricted-ability-maneuver, safety, traffic-separation-scheme
221
+ 10(l) cable, carry, comply, engage, exempt, lay, operation, pick-up, restricted-ability-maneuver, service, submarine, traffic-separation-scheme
222
+ 12(a) approach, keep-out-of-way, risk-of-collision, sailing-vessel, two
223
+ 12(a)(i) keep-out-of-way, port, side, wind
224
+ 12(a)(ii) keep-out-of-way, leeward, side, wind, windward
225
+ 12(a)(iii) certainty, determine, keep-out-of-way, port, see, side, starboard, wind, windward
226
+ 12(b) carry, fore-and-aft, mainsail, opposite, sail, side, square-rigged, windward
227
+ 13(a) contain, keep-out-of-way, overtake, rules-4-18
228
+ 13(b) 22-5-degree, abaft, beam, come, direction, night, overtake, position, see, sidelight, sternlight
229
+ 13(c) act, assume, doubt, overtake
230
+ 13(d) alter, bearing, clear, cross, duty, keep, overtake, past, relieve, subsequent, two
231
+ 14(a) alter, course, meet, pass, port, power-driven, reciprocal, risk-of-collision, side, starboard, two
232
+ 14(b) ahead, aspect, correspond, day, line, masthead-light, night, observe, see, sidelight, situation
233
+ 14(c) act, assume, doubt, situation
234
+ 15(a) ahead, avoid, cross, keep-out-of-way, power-driven, risk-of-collision, side, starboard, two
235
+ 17(a)(i) course, keep, keep-out-of-way, speed, two
236
+ 17(a)(ii) action, alone, avoid, collision, comply, keep-out-of-way, maneuver
237
+ 17(b) action, aid, alone, avoid, close, collision, course, give-way, keep, speed
238
+ 17(c) action, alter, avoid, collision, course, cross, port, power-driven, rule-17aii, side, situation
239
+ 17(d) give-way, keep-out-of-way, obligation, relieve
240
+ 18(a) keep-out-of-way, power-driven, underway
241
+ 18(a)(i) not-under-command
242
+ 18(a)(ii) restricted-ability-maneuver
243
+ 18(a)(iii) engaged-in-fishing
244
+ 18(a)(iv) sailing-vessel
245
+ 18(b) keep-out-of-way, sailing-vessel, underway
246
+ 18(b)(i) not-under-command
247
+ 18(b)(ii) restricted-ability-maneuver
248
+ 18(b)(iii) engaged-in-fishing
249
+ 18(c) engaged-in-fishing, keep-out-of-way, underway
250
+ 18(c)(i) not-under-command
251
+ 18(c)(ii) restricted-ability-maneuver
252
+ 18(d)(i) avoid, constrained-by-draft, exhibit, impede, not-under-command, passage, restricted-ability-maneuver, rule-28, safe, signal
253
+ 18(d)(ii) caution, constrained-by-draft, navigate
254
+ 18(e) avoid, comply, impede, keep-clear, navigation, risk-of-collision, rules-4-19, seaplane, water
255
+ 18(f)(i) avoid, flight, impede, keep-clear, land, navigation, near, surface, wig-craft
256
+ 18(f)(ii) comply, operate, power-driven, rules-4-19, surface, water, wig-craft
257
+ 19(a) apply, area, in-sight, navigate, near, restricted-visibility
258
+ 19(b) adapt, engine, immediate, maneuver, power-driven, proceed, ready, restricted-visibility, safe-speed
259
+ 19(c) comply, restricted-visibility, rules-4-10
260
+ 19(d) action, alone, alter, avoid, close-quarters, course, detect, determine, develop, radar, risk-of-collision
261
+ 19(d)(i) alter, beam, course, forward, overtake, port
262
+ 19(d)(ii) abaft, abeam, alter, beam, course
263
+ 19(e) avoid, beam, caution, close-quarters, collision, course, danger, determine, except, extreme, fog-signal, forward, hear, keep, minimum, navigate, reduce, risk-of-collision, speed, way
264
+ ```
@@ -362,6 +362,23 @@
362
362
  "24a-rest"
363
363
  ]
364
364
  },
365
+ {
366
+ "name": "towing astern, at anchor (issue #44: Rule 23 running lights not imported)",
367
+ "facts": {
368
+ "fact:propulsion": "propulsion:power",
369
+ "fact:activity": "activity:towing",
370
+ "fact:position": "position:anchored",
371
+ "fact:length_m": 40,
372
+ "fact:tow_length_m": 150
373
+ },
374
+ "expect": [
375
+ "24a-m2",
376
+ "24a-rest",
377
+ "30a",
378
+ "30b",
379
+ "30c"
380
+ ]
381
+ },
365
382
  {
366
383
  "name": "pushing ahead, composite unit",
367
384
  "facts": {
@@ -375,6 +392,22 @@
375
392
  "24b"
376
393
  ]
377
394
  },
395
+ {
396
+ "name": "pushing ahead, composite unit, at anchor (issue #44: Rule 23 running lights not imported)",
397
+ "facts": {
398
+ "fact:propulsion": "propulsion:power",
399
+ "fact:activity": "activity:pushing",
400
+ "fact:position": "position:anchored",
401
+ "fact:length_m": 30,
402
+ "fact:composite_unit": true
403
+ },
404
+ "expect": [
405
+ "24b",
406
+ "30a",
407
+ "30b",
408
+ "30c"
409
+ ]
410
+ },
378
411
  {
379
412
  "name": "pushing ahead, not a composite unit",
380
413
  "facts": {
@@ -388,6 +421,22 @@
388
421
  "24c"
389
422
  ]
390
423
  },
424
+ {
425
+ "name": "pushing ahead, not a composite unit, at anchor (issue #44: Rule 23 running lights not imported)",
426
+ "facts": {
427
+ "fact:propulsion": "propulsion:power",
428
+ "fact:activity": "activity:pushing",
429
+ "fact:position": "position:anchored",
430
+ "fact:length_m": 30,
431
+ "fact:composite_unit": false
432
+ },
433
+ "expect": [
434
+ "24c",
435
+ "30a",
436
+ "30b",
437
+ "30c"
438
+ ]
439
+ },
391
440
  {
392
441
  "name": "constrained by draft",
393
442
  "facts": {