colregs 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -11
- package/data/applicability.json +478 -503
- package/data/facts.json +22 -22
- package/data/geometry.json +8 -8
- package/data/i18n/en.json +27 -0
- package/data/i18n/fi.json +25 -0
- package/data/images.json +32 -32
- package/data/operations.json +79 -0
- package/data/rules.json +5 -0
- package/data/version.json +1 -1
- package/docs/adr/0006-json-schema-and-identifier-diff.md +2 -0
- package/docs/adr/0011-api-shape.md +7 -7
- package/docs/adr/0012-trace-and-rule2-departure-api.md +2 -2
- package/docs/adr/0014-engine-interface-owned-by-colregs.md +103 -0
- package/docs/adr/0015-rule-ids-are-paragraph-keys.md +191 -0
- package/docs/adr/0016-encounter-roles-are-pooled-across-frames.md +82 -0
- package/docs/adr/0017-closed-vocabularies-are-prefixed-identifiers.md +105 -0
- package/docs/budgets.json +8 -15
- package/docs/decisions.md +6 -0
- package/docs/identifiers.md +121 -109
- package/docs/maritime-sources.md +58 -0
- package/docs/normative-language.md +103 -0
- package/docs/part-b-invariants.md +48 -45
- package/docs/requirements.md +147 -138
- package/fixtures/applicability-fixtures.json +190 -190
- package/fixtures/situation-fixtures.json +870 -599
- package/package.json +1 -1
- package/schema/applicability-fixtures.schema.json +4 -13
- package/schema/applicability.schema.json +77 -77
- package/schema/conduct-evaluation.schema.json +135 -0
- package/schema/display-evaluation.schema.json +146 -0
- package/schema/encounter-evaluation.schema.json +109 -0
- package/schema/evaluation.schema.json +149 -0
- package/schema/fact-record.schema.json +30 -0
- package/schema/facts.schema.json +4 -4
- package/schema/i18n-catalog.schema.json +47 -0
- package/schema/operations.schema.json +124 -0
- package/schema/rule2-departure-finding.schema.json +72 -0
- package/schema/rule2-departure-model.schema.json +159 -0
- package/schema/situation-fixtures.schema.json +30 -127
- package/schema/situation.schema.json +72 -0
- package/schema/trace.schema.json +33 -0
- package/data/deprecated-identifiers.json +0 -7
- package/schema/deprecated-identifiers.schema.json +0 -29
|
@@ -49,7 +49,7 @@ that is the fixture contract: both fixture files `expect` entry ids.
|
|
|
49
49
|
|
|
50
50
|
### 2. `FactRecord` keeps its name
|
|
51
51
|
|
|
52
|
-
It is colregs' name for the per-vessel record (`
|
|
52
|
+
It is colregs' name for the per-vessel record (`self.fact` is "exactly the
|
|
53
53
|
record above, key for key"). ADR 0005 §2 has the situation wrap two fact
|
|
54
54
|
records; the fact record itself does not widen, and a display consumer never
|
|
55
55
|
sees a situation. A name that hinted at two vessels would describe the wrapper,
|
|
@@ -59,13 +59,13 @@ not the thing.
|
|
|
59
59
|
|
|
60
60
|
colregs states the situation twice: nested by subject and class in
|
|
61
61
|
`facts.json` §`situation.record` and in every fixture case, and flat as
|
|
62
|
-
`
|
|
62
|
+
`self:fact:activity` inside predicates. The engine's public type is the
|
|
63
63
|
**nested** form. The flat form is the predicate namespace and stays internal
|
|
64
64
|
to the walker.
|
|
65
65
|
|
|
66
66
|
```ts
|
|
67
67
|
interface Situation {
|
|
68
|
-
|
|
68
|
+
self: Subject;
|
|
69
69
|
other?: Subject;
|
|
70
70
|
pair?: Pair;
|
|
71
71
|
}
|
|
@@ -73,7 +73,7 @@ interface Subject { fact: FactRecord; kin?: Kinematics; geo?: DirectionalGeometr
|
|
|
73
73
|
interface Pair { geo?: PairGeometry; env?: Environment; }
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
- `
|
|
76
|
+
- `self` is required; `other`/`Subject.fact` follow colregs' own fixture schema
|
|
77
77
|
(`situation-fixtures.schema.json`, `0.2.0`) — `other` optional (Rule 19's
|
|
78
78
|
single-vessel scope needs no synthesized one), `fact` required. Every other
|
|
79
79
|
class, and every key inside a class, is optional — absent is absent.
|
|
@@ -98,7 +98,7 @@ interface EncounterEvaluation {
|
|
|
98
98
|
scope: EntryId[];
|
|
99
99
|
encounter?: 'head-on' | 'crossing' | 'overtaking' | 'none';
|
|
100
100
|
risk_of_collision: { asserted: boolean; by: EntryId[] };
|
|
101
|
-
roles: {
|
|
101
|
+
roles: { self: SubjectRole[]; other: SubjectRole[] };
|
|
102
102
|
overridden: { id: EntryId; by: EntryId }[];
|
|
103
103
|
modalities: Record<EntryId, Modality>;
|
|
104
104
|
}
|
|
@@ -164,10 +164,10 @@ resolution, and validation of the situation record.
|
|
|
164
164
|
| `FactRecord` keeps its name | ink | — |
|
|
165
165
|
| `Situation` nested by subject and class, generated from `facts.json` | ink | — |
|
|
166
166
|
| Verb name `evaluateEncounter`; result name `EncounterEvaluation` | ✎ | colregs renaming the `pair` subject or the `encounter` effect |
|
|
167
|
-
| `
|
|
167
|
+
| `self` required, `other`/`Subject.fact` per colregs 0.2.0's fixture schema | ✎ | revised 2026-09-07 from "own/other both required"; Mark to confirm before ink |
|
|
168
168
|
| `appliedEncounterEntries` as the fixture-replay companion | ✎ | the situation-fixture replay being written |
|
|
169
169
|
| Field names snake_case with unit suffixes across both ADRs; `EntryId`/`ParagraphCite` alias `string` for ids and cites | ✎ | the rename's alias window closing; a consumer arguing the compiler should enforce the two apart |
|
|
170
|
-
| `EncounterEvaluation` field set (§4) | ✎ | building it; Q-35, Q-36, Q-43 in colregs |
|
|
170
|
+
| `EncounterEvaluation` field set (§4); `categories` and `provenance` added 2026-09-16 beyond the block above, as `DisplayEvaluation` carries them — colregs-engine 0.1.5 built them and ADR 0014's `encounter-evaluation.schema.json` is now the shape; `roles` is the pooled two-frame read, ADR 0016 | ✎ | building it; Q-35, Q-36, Q-43 in colregs |
|
|
171
171
|
| `encounter` absent vs the ADR 0005 §5 status alphabet | ✎ | Q-43 |
|
|
172
172
|
| `conduct` is a separate package, not a third verb | ✎ | superseded by ADR 0012: a third and fourth verb, in this package |
|
|
173
173
|
| Geometry-consistency validation (REQ-VERIFY-8) in the engine's validator | ? | deciding whether it is data-suite-only |
|
|
@@ -81,12 +81,12 @@ interface ConductEvaluation {
|
|
|
81
81
|
phases: ConductPhaseChange[];
|
|
82
82
|
}
|
|
83
83
|
interface ConductVerdict {
|
|
84
|
-
id: EntryId; subject: '
|
|
84
|
+
id: EntryId; subject: 'self' | 'other';
|
|
85
85
|
verdict: 'kept' | 'breached' | 'pending';
|
|
86
86
|
attached_at_s?: number; decided_at_s?: number;
|
|
87
87
|
robustness?: { value: number; unit: string };
|
|
88
88
|
}
|
|
89
|
-
interface ConductPhaseChange { subject: '
|
|
89
|
+
interface ConductPhaseChange { subject: 'self' | 'other'; phase: ParagraphCite; at_s: number; }
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
- One **verdict** per applied conduct entry per subject it attached to; an
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# ADR 0014 — The engine interface is colregs' to own: an operations manifest and result schemas
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-16
|
|
4
|
+
Status: proposed. Solace ordered options 2 and 3 built on 2026-09-16; the
|
|
5
|
+
register marks what settles each row.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
ADR 0011 and ADR 0012 fix the engine's verbs and result envelopes, in prose
|
|
10
|
+
and TypeScript blocks, in this repository. The only machine-readable copy
|
|
11
|
+
lives in colregs-engine's `src/types.ts`, so the interface is de facto
|
|
12
|
+
colregs-owned and de jure the engine's: a second engine, in any language,
|
|
13
|
+
would transcribe the ADRs by hand and drift the way the engine's own
|
|
14
|
+
generator was written to stop.
|
|
15
|
+
|
|
16
|
+
colregs already owns *shapes* in production: the engine compiles every
|
|
17
|
+
`schema/*.schema.json` into generated types and derives `FactRecord` and
|
|
18
|
+
`Situation` from `data/facts.json`. What it does not own is the *operations*
|
|
19
|
+
— which verb reads which input and answers which envelope — and the fixture
|
|
20
|
+
files bind to verbs only by convention.
|
|
21
|
+
|
|
22
|
+
Ten routes were surveyed on 2026-09-16 for making the interface a colregs
|
|
23
|
+
artefact, the Java sense of interface against implementation:
|
|
24
|
+
|
|
25
|
+
| # | route | what it gives, what it costs |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| 1 | result schemas only, verbs stay prose | shapes checkable, operations still hand-read |
|
|
28
|
+
| 2 | **operations manifest**: verb → inputs → result → companion, in JSON | language-neutral; any engine derives its own binding |
|
|
29
|
+
| 3 | **fixtures bound to verbs** in the manifest | the conformance replay becomes the behavioural contract |
|
|
30
|
+
| 4 | hand-written `.d.ts` in colregs | TypeScript-only; the transcription the engine's generator exists to avoid |
|
|
31
|
+
| 5 | `x-operation` keywords inside the existing schemas | option 2 spread over eleven files |
|
|
32
|
+
| 6 | OpenAPI / AsyncAPI | a projection generable *from* 2, not a source |
|
|
33
|
+
| 7 | Protobuf / Smithy | model operations natively; a toolchain a data-only package does not have |
|
|
34
|
+
| 8 | TypeSpec | one source projecting JSON Schema and OpenAPI; same toolchain cost |
|
|
35
|
+
| 9 | a separate `colregs-api` package | a third release train for two files |
|
|
36
|
+
| 10 | status quo | the drift above |
|
|
37
|
+
|
|
38
|
+
## Decision
|
|
39
|
+
|
|
40
|
+
1. **`data/operations.json` is the interface.** One operation per verb ADR
|
|
41
|
+
0011 and ADR 0012 name, each with positional `inputs` (name and schema),
|
|
42
|
+
an `output` schema, the entry-id `companion` verb where one exists, and
|
|
43
|
+
the `fixtures` that exercise it. `schema/operations.schema.json` checks
|
|
44
|
+
its shape; `test/data.test.mjs` checks that every reference resolves.
|
|
45
|
+
2. **Result envelopes are schemas under `schema/`:** `display-evaluation`,
|
|
46
|
+
`encounter-evaluation`, `conduct-evaluation`, `rule2-departure-finding`,
|
|
47
|
+
transcribed from the engine's `src/types.ts` at colregs-engine 0.1.5.
|
|
48
|
+
JSON-Schema-expressible only: `Record<EntryId, Modality>` becomes
|
|
49
|
+
`patternProperties`, the `EntryId` and `ParagraphCite` aliases become
|
|
50
|
+
`$defs`, the two deprecated camelCase aliases are marked `deprecated` and
|
|
51
|
+
optional. From here the schema is the normative statement of each
|
|
52
|
+
envelope; the TypeScript blocks in ADR 0011 §4 and ADR 0012 §2–4 are
|
|
53
|
+
illustrations of record, and an envelope changes here first, the engine
|
|
54
|
+
second.
|
|
55
|
+
3. **Inputs get schemas too:** `fact-record`, `situation`, `trace`,
|
|
56
|
+
`rule2-departure-model`. The two fixture schemas `$ref` the first two
|
|
57
|
+
for a case's `facts` and `situation` instead of carrying a copy; a fixture
|
|
58
|
+
case is the verb's input, and the tests validate every bound case against
|
|
59
|
+
it.
|
|
60
|
+
4. **Schema files compose by `$ref`,** relative to their `$id`
|
|
61
|
+
(`applicability.schema.json#/$defs/ruleId`). ADR 0006's "no cross-file
|
|
62
|
+
references" is about data references — cite to `rules.json` — which stay in
|
|
63
|
+
the tests; a `$ref` between two schema files is one shape reused, not a
|
|
64
|
+
data reference. The suite registers every schema by `$id` before compiling.
|
|
65
|
+
What every envelope shares — the `colregs` stamp, `provenance`, the
|
|
66
|
+
`ruleId` and `paragraphCite` vocabularies — is `schema/evaluation.schema.json`,
|
|
67
|
+
`$defs` only, so moving one later is never a two-repository change.
|
|
68
|
+
5. **A fixture file is bound to a verb** by `fixtures[].file` and
|
|
69
|
+
`case_inputs`, one case key per positional input, with the answer under
|
|
70
|
+
`expect`: the companion's entry ids unless the binding names another
|
|
71
|
+
schema. A case's `status` and `jurisdiction` are the fixture file's own.
|
|
72
|
+
Every file under `fixtures/` must be bound; `evaluateConduct` and
|
|
73
|
+
`evaluateRule2Departure` bind nothing yet, and say so with an empty list.
|
|
74
|
+
6. **Not in the manifest:** trailing options a binding accepts (`opts.data`),
|
|
75
|
+
which verbs are built, and what a verb throws. Build status is ADR 0011's
|
|
76
|
+
and ADR 0012's tables; errors are the binding's.
|
|
77
|
+
|
|
78
|
+
## Consequences
|
|
79
|
+
|
|
80
|
+
- Ten new files under `schema/`. The engine's `generate-schema-types.ts`
|
|
81
|
+
throws on any schema its `ROOT_NAMES` does not list, so the next colregs
|
|
82
|
+
bump fails its build until colregs-engine#86 lands: emit
|
|
83
|
+
`interface ColregsEngine` from `operations.json`, `satisfies ColregsEngine`
|
|
84
|
+
at the engine's root, settle the `fact-record.ts` / `situation.ts`
|
|
85
|
+
collision with the types it derives from `facts.json` today, and validate
|
|
86
|
+
real output against these schemas for every fixture case.
|
|
87
|
+
- A YAML rendering of the manifest is lossless; XML is a generated view (JSON
|
|
88
|
+
Schema to XSD). Authoring the interface in TypeScript first is the one route
|
|
89
|
+
that forecloses both.
|
|
90
|
+
- The 2026-09-16 hand-off named the finding's schema `rule2-departure`; it
|
|
91
|
+
lands as `rule2-departure-finding` beside `rule2-departure-model`, since
|
|
92
|
+
the verb has two shapes to name.
|
|
93
|
+
|
|
94
|
+
## Register
|
|
95
|
+
|
|
96
|
+
| item | level | what would settle it |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| Options 2 and 3, not 1 or 4–10 | ✎ | this ADR accepted |
|
|
99
|
+
| Manifest shape: positional `inputs`, `output`, `companion`, `fixtures[].case_inputs` and `expect` | ✎ | the engine generator consuming it; the first conduct or Rule 2 fixture |
|
|
100
|
+
| Envelope schemas transcribed from `types.ts`, envelope changes land here first | ✎ | the first envelope change after the follow-up |
|
|
101
|
+
| Cross-file `$ref` between schema files | ✎ | a consumer whose validator cannot register a schema set |
|
|
102
|
+
| Deprecated aliases optional in the schema, required in the engine | ✎ | the engine dropping them |
|
|
103
|
+
| `opts` and thrown errors stay out of the manifest | ✎ | a second binding needing either |
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# ADR 0015 — Rule ids are paragraph keys in the `rule:` namespace
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-16
|
|
4
|
+
Status: accepted — Solace's ruling, 2026-09-16
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Issue #121 asked what an applicability entry should be called, and ruling C
|
|
9
|
+
was read as *names, not citations*. The first draft of this ADR wrote that
|
|
10
|
+
reading out: every id became a coined name in an `entry:` namespace —
|
|
11
|
+
`entry:anchored`, `entry:towing_mastheads_long_tow`, `entry:being_overtaken`.
|
|
12
|
+
It never landed and never shipped. Solace superseded that reading on
|
|
13
|
+
2026-09-16 and this file is the replacement.
|
|
14
|
+
|
|
15
|
+
What the rename was buying was insulation: `cite` is a data field, revised
|
|
16
|
+
whenever the package reads the Rules better, and `14a` had already become
|
|
17
|
+
`14b` when the reading of *which paragraph deems a head-on* moved. An
|
|
18
|
+
identifier containing a data field gets renamed every time the field is.
|
|
19
|
+
|
|
20
|
+
What it cost is the vocabulary. An entry is a norm out of the Convention,
|
|
21
|
+
and the people who read these ids — and the consumers that store them
|
|
22
|
+
alongside a `cite` anyway — already have a name for every norm in the
|
|
23
|
+
package: the Rule. `rule:13b` is *Rule 13(b)* in the mariner's mouth. A
|
|
24
|
+
coined name is a second vocabulary beside that one, invented here,
|
|
25
|
+
unguessable from the Rules, and needing a lookup in this file to be read
|
|
26
|
+
back as the paragraph it came from. The insulation is worth one rename in
|
|
27
|
+
a pre-1.0 package; the second vocabulary is permanent. Ruled by Solace,
|
|
28
|
+
2026-09-16.
|
|
29
|
+
|
|
30
|
+
## Decision
|
|
31
|
+
|
|
32
|
+
1. **The namespace noun is `rule:`.** It replaces `entry:` everywhere. The
|
|
33
|
+
overload with the Convention's Rules is the point, not an accident to
|
|
34
|
+
be designed around.
|
|
35
|
+
2. **An id is `rule:<paragraph-slug>`.** The slug is the cite with its
|
|
36
|
+
punctuation dropped: rule number, paragraph letter attached, each roman
|
|
37
|
+
subparagraph joined by `_`. `27(a)(i)` → `27a_i`; `13(b)` → `13b`;
|
|
38
|
+
`11` → `11`; `23(a)(iii)-(iv)` → `23a_iii_iv`.
|
|
39
|
+
3. **A bare id is the paragraph's principal norm.** Where the text yields
|
|
40
|
+
a further norm from the same paragraph, it takes a third segment after
|
|
41
|
+
a colon, named in the text's own words: `rule:24a_i` is 24(a)(i)'s two
|
|
42
|
+
masthead lights and `rule:24a_i:exceeds_200m` its three, the threshold
|
|
43
|
+
being the Convention's own, which is when a number may appear in a name.
|
|
44
|
+
4. **Where one sentence fuses a deeming test and a duty, both norms are
|
|
45
|
+
named.** 15(a) gives `rule:15a:crossing` — the classification, effect
|
|
46
|
+
`encounter: crossing` — and `rule:15a:keep_out_of_the_way` — the
|
|
47
|
+
precedence, own give-way. `avoid_crossing_ahead` is reserved for 15(a)'s
|
|
48
|
+
second duty when the `conduct` shape exists; it is not in use.
|
|
49
|
+
5. **A Convention subparagraph keeps its own cite.** 30(d)'s chapeau is
|
|
50
|
+
`rule:30d`, citing `30(d)`, importing the 30(a)/(b) anchor lights,
|
|
51
|
+
modality `shall`; the two all-round red lights are `rule:30d_i`, citing
|
|
52
|
+
`30(d)(i)`, modality `shall-if-practicable` because the paragraph says
|
|
53
|
+
"if practicable". `30(d)(i)` joins `rules.json` as a skeleton path with
|
|
54
|
+
this change. `rule:30d_ii` is reserved for the three balls, and lands
|
|
55
|
+
when day shapes do.
|
|
56
|
+
6. **A jurisdiction override is named by its difference**, and jurisdiction
|
|
57
|
+
stays a field: `rule:30a:mooring_buoy` and `rule:30b:mooring_buoy`, both
|
|
58
|
+
`us/inland`. The name says what 33 CFR 90.5 adds, not who adds it.
|
|
59
|
+
7. **13(b) is one symmetric entry.** The two entries that preceded it were
|
|
60
|
+
identical but for which subject's `geo:rel_bearing_deg` fell in
|
|
61
|
+
(112.5, 247.5), and both produced `encounter: overtaking`. `rule:13b`
|
|
62
|
+
reads the same sector object on either subject under `any_of` — the
|
|
63
|
+
pattern 13(d) already used over `hist:was_overtaking` — because the
|
|
64
|
+
encounter type belongs to the pair. One paragraph, one norm, one id.
|
|
65
|
+
8. **Every other entry with a unique cite takes the bare slug of it.**
|
|
66
|
+
9. **`represented_paragraphs` ids take the prefix too** — `2a` becomes
|
|
67
|
+
`rule:2a` — and keep their own list. They no longer need a schema
|
|
68
|
+
definition of their own: the id pattern is the same one.
|
|
69
|
+
10. **The schema pattern is**
|
|
70
|
+
`^rule:[0-9]+[a-z]?(_[ivx]+)*(:[a-z][a-z0-9_]*)?$`.
|
|
71
|
+
**An id is opaque to a consumer.** It looks like a citation and is not
|
|
72
|
+
one: a consumer that wants the paragraph reads `cite`, and never splits
|
|
73
|
+
the id to find it. The resemblance is for the human reading a trace;
|
|
74
|
+
the `cite` field is the machine-readable link to the Convention, and it
|
|
75
|
+
is the field that moves when the package's reading of a paragraph moves.
|
|
76
|
+
11. **The identifier-diff mechanism is removed, not left dormant.**
|
|
77
|
+
`retired_entry_ids`, `data/deprecated-identifiers.json`, its schema, the
|
|
78
|
+
dormant diff test and the retired-id half of the REQ-MODEL-10 collision
|
|
79
|
+
test all go. Nothing pre-1.0 is immutable, so there was nothing for the
|
|
80
|
+
registry to hold and no diff for the test to run; a mechanism that
|
|
81
|
+
cannot fire is a mechanism nobody maintains. It returns, if at all, with
|
|
82
|
+
the 1.0.0 tag that REQ-MODEL-10's baseline names. Ruled by Solace,
|
|
83
|
+
2026-09-16.
|
|
84
|
+
|
|
85
|
+
## The names
|
|
86
|
+
|
|
87
|
+
`was` is the `entry:` name from the superseded draft of this ADR, which
|
|
88
|
+
shipped in no release. The citation-derived ids that preceded *it* —
|
|
89
|
+
`24a-m2`, `23a1`, `30d-red` — were never names either and are not carried
|
|
90
|
+
forward.
|
|
91
|
+
|
|
92
|
+
| was | is | cite |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `entry:power_forward_masthead` | `rule:23a_i` | 23(a)(i) |
|
|
95
|
+
| `entry:power_second_masthead` | `rule:23a_ii` | 23(a)(ii) |
|
|
96
|
+
| `entry:power_sidelights_sternlight` | `rule:23a_iii_iv` | 23(a)(iii)-(iv) |
|
|
97
|
+
| `entry:air_cushion_non_displacement` | `rule:23b` | 23(b) |
|
|
98
|
+
| `entry:wig_near_surface` | `rule:23c` | 23(c) |
|
|
99
|
+
| `entry:power_under_12m` | `rule:23d_i` | 23(d)(i) |
|
|
100
|
+
| `entry:power_under_7m_slow` | `rule:23d_ii` | 23(d)(ii) |
|
|
101
|
+
| `entry:towing_mastheads` | `rule:24a_i` | 24(a)(i) |
|
|
102
|
+
| `entry:towing_mastheads_long_tow` | `rule:24a_i:exceeds_200m` | 24(a)(i) |
|
|
103
|
+
| `entry:towing_light` | `rule:24a_ii_iv` | 24(a)(ii)-(iv) |
|
|
104
|
+
| `entry:pushing_composite_unit` | `rule:24b` | 24(b) |
|
|
105
|
+
| `entry:pushing_ahead_or_alongside` | `rule:24c` | 24(c) |
|
|
106
|
+
| `entry:being_towed` | `rule:24e` | 24(e) |
|
|
107
|
+
| `entry:sail_sidelights_sternlight` | `rule:25a` | 25(a) |
|
|
108
|
+
| `entry:sail_combined_lantern` | `rule:25b` | 25(b) |
|
|
109
|
+
| `entry:sail_red_over_green` | `rule:25c` | 25(c) |
|
|
110
|
+
| `entry:sail_under_7m` | `rule:25d_i` | 25(d)(i) |
|
|
111
|
+
| `entry:under_oars` | `rule:25d_ii` | 25(d)(ii) |
|
|
112
|
+
| `entry:trawling_green_over_white` | `rule:26b_i` | 26(b)(i) |
|
|
113
|
+
| `entry:trawling_masthead` | `rule:26b_ii` | 26(b)(ii) |
|
|
114
|
+
| `entry:trawling_making_way` | `rule:26b_iii` | 26(b)(iii) |
|
|
115
|
+
| `entry:fishing_red_over_white` | `rule:26c_i` | 26(c)(i) |
|
|
116
|
+
| `entry:fishing_outlying_gear` | `rule:26c_ii` | 26(c)(ii) |
|
|
117
|
+
| `entry:fishing_making_way` | `rule:26c_iii` | 26(c)(iii) |
|
|
118
|
+
| `entry:nuc_red_over_red` | `rule:27a_i` | 27(a)(i) |
|
|
119
|
+
| `entry:nuc_making_way` | `rule:27a_iii` | 27(a)(iii) |
|
|
120
|
+
| `entry:ram_red_white_red` | `rule:27b_i` | 27(b)(i) |
|
|
121
|
+
| `entry:ram_making_way` | `rule:27b_iii` | 27(b)(iii) |
|
|
122
|
+
| `entry:ram_anchored` | `rule:27b_iv` | 27(b)(iv) |
|
|
123
|
+
| `entry:ram_underwater_obstruction` | `rule:27d` | 27(d) |
|
|
124
|
+
| `entry:diving` | `rule:27e_i` | 27(e)(i) |
|
|
125
|
+
| `entry:mine_clearance` | `rule:27f` | 27(f) |
|
|
126
|
+
| `entry:cbd` | `rule:28` | 28 |
|
|
127
|
+
| `entry:pilot` | `rule:29a` | 29(a) |
|
|
128
|
+
| `entry:anchored` | `rule:30a` | 30(a) |
|
|
129
|
+
| `entry:anchored_under_50m` | `rule:30b` | 30(b) |
|
|
130
|
+
| `entry:anchored_deck_lights` | `rule:30c` | 30(c) |
|
|
131
|
+
| `entry:aground_anchor_lights` | `rule:30d` | 30(d) |
|
|
132
|
+
| `entry:aground_red_over_red` | `rule:30d_i` | 30(d)(i) |
|
|
133
|
+
| `entry:anchored_under_7m_not_near_channel` | `rule:30e` | 30(e) |
|
|
134
|
+
| `entry:any_visibility` | `rule:4` | 4 |
|
|
135
|
+
| `entry:in_sight` | `rule:11` | 11 |
|
|
136
|
+
| `entry:restricted_visibility` | `rule:19a` | 19(a) |
|
|
137
|
+
| `entry:power_gives_way_to_nuc` | `rule:18a_i` | 18(a)(i) |
|
|
138
|
+
| `entry:power_gives_way_to_ram` | `rule:18a_ii` | 18(a)(ii) |
|
|
139
|
+
| `entry:power_gives_way_to_fishing` | `rule:18a_iii` | 18(a)(iii) |
|
|
140
|
+
| `entry:power_gives_way_to_sail` | `rule:18a_iv` | 18(a)(iv) |
|
|
141
|
+
| `entry:sail_gives_way_to_nuc` | `rule:18b_i` | 18(b)(i) |
|
|
142
|
+
| `entry:sail_gives_way_to_ram` | `rule:18b_ii` | 18(b)(ii) |
|
|
143
|
+
| `entry:sail_gives_way_to_fishing` | `rule:18b_iii` | 18(b)(iii) |
|
|
144
|
+
| `entry:fishing_gives_way_to_nuc` | `rule:18c_i` | 18(c)(i) |
|
|
145
|
+
| `entry:fishing_gives_way_to_ram` | `rule:18c_ii` | 18(c)(ii) |
|
|
146
|
+
| `entry:avoid_impeding_cbd` | `rule:18d_i` | 18(d)(i) |
|
|
147
|
+
| `entry:wig_keeps_well_clear` | `rule:18f_i` | 18(f)(i) |
|
|
148
|
+
| `entry:not_impeded_remains_obliged` | `rule:8f_iii` | 8(f)(iii) |
|
|
149
|
+
| `entry:overtaking_gives_way` | `rule:13a` | 13(a) |
|
|
150
|
+
| `entry:narrow_channel_small_or_sail` | `rule:9b` | 9(b) |
|
|
151
|
+
| `entry:narrow_channel_fishing` | `rule:9c` | 9(c) |
|
|
152
|
+
| `entry:traffic_lane_fishing` | `rule:10i` | 10(i) |
|
|
153
|
+
| `entry:traffic_lane_small_or_sail` | `rule:10j` | 10(j) |
|
|
154
|
+
| `entry:steady_bearing` | `rule:7d_i` | 7(d)(i) |
|
|
155
|
+
| `entry:sail_port_tack_gives_way` | `rule:12a_i` | 12(a)(i) |
|
|
156
|
+
| `entry:sail_windward_gives_way` | `rule:12a_ii` | 12(a)(ii) |
|
|
157
|
+
| `entry:sail_port_tack_uncertain_gives_way` | `rule:12a_iii` | 12(a)(iii) |
|
|
158
|
+
| `entry:overtaking` | `rule:13b` | 13(b) |
|
|
159
|
+
| `entry:being_overtaken` | `rule:13b` | 13(b) |
|
|
160
|
+
| `entry:overtaking_until_past_and_clear` | `rule:13d` | 13(d) |
|
|
161
|
+
| `entry:head_on` | `rule:14b` | 14(b) |
|
|
162
|
+
| `entry:crossing` | `rule:15a:crossing` | 15(a) |
|
|
163
|
+
| `entry:crossing_gives_way` | `rule:15a:keep_out_of_the_way` | 15(a) |
|
|
164
|
+
| `entry:mooring_buoy` | `rule:30a:mooring_buoy` | 30(a) |
|
|
165
|
+
| `entry:mooring_buoy_under_50m` | `rule:30b:mooring_buoy` | 30(b) |
|
|
166
|
+
|
|
167
|
+
## Consequences
|
|
168
|
+
|
|
169
|
+
- One pass, one commit: `data/applicability.json`, both fixture files,
|
|
170
|
+
`data/geometry.json`, `data/images.json`, `data/facts.json`,
|
|
171
|
+
`data/rules.json`, the schemas, the suite and the living docs. The
|
|
172
|
+
schema `$defs` that named the type follow the noun: `entryId` is
|
|
173
|
+
`ruleId`, `entryIdList` is `ruleIdList`.
|
|
174
|
+
- Two entries may still share a cite — `rule:24a_i` and
|
|
175
|
+
`rule:24a_i:exceeds_200m`, the two halves of 15(a), the two mooring-buoy
|
|
176
|
+
deltas. What they never share is an id, and the third segment says which
|
|
177
|
+
norm out of the paragraph the entry carries.
|
|
178
|
+
- Earlier ADRs and closed questions cite entries by the superseded names;
|
|
179
|
+
the table above is the map and stays in this file permanently. ADR 0006
|
|
180
|
+
carries a dated note that its identifier-diff half is gone.
|
|
181
|
+
- `represented_paragraphs` is now a second list of records that look like
|
|
182
|
+
entries, differing only in carrying no `when`. Folding it into `entries`
|
|
183
|
+
with a `care`/`meta` category is the obvious follow-up and is not done
|
|
184
|
+
here.
|
|
185
|
+
- Open, and not ruled: whether `fact:rule18_class` should be renamed. It
|
|
186
|
+
carries a rule number inside a fact name, which is the shape this ADR
|
|
187
|
+
moved *entry* ids towards and says nothing about for facts. Left visibly
|
|
188
|
+
open.
|
|
189
|
+
- An encounter with more than two vessels is issue #135; nothing in this
|
|
190
|
+
scheme is aimed at it, and a third subject would need a segment none of
|
|
191
|
+
these ids have.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# ADR 0016 — An encounter's roles are read from both frames, pooled, then resolved
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-16
|
|
4
|
+
Status: accepted — Solace's ruling on #141, 2026-09-16.
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Every `precedence` entry in `data/applicability.json` is written from the
|
|
9
|
+
duty-holder's seat: `effect.self` is one of `give-way`, `shall-not-impede`,
|
|
10
|
+
`keep-clear`, `none`, and `effect.other` is `stand-on` or `none`. No entry
|
|
11
|
+
names self stand-on. That is faithful to the text — a paragraph addresses the
|
|
12
|
+
vessel it binds, and the other vessel's stand-on is Rule 17's inference —
|
|
13
|
+
and it is why entry `rule:15a:keep_out_of_the_way` gates self's bearing on the
|
|
14
|
+
starboard half only.
|
|
15
|
+
|
|
16
|
+
ADR 0011 §4 defines `EncounterEvaluation.roles` as a role set for *both*
|
|
17
|
+
subjects: what everyone is to do. Nothing between the two said how an engine
|
|
18
|
+
gets from a one-seat table to a two-seat answer. colregs-engine read the
|
|
19
|
+
situation once, from self's seat, and passed every fixture doing it, because
|
|
20
|
+
`fixtures/situation-fixtures.json` expects one-seat entry ids.
|
|
21
|
+
|
|
22
|
+
Measured on 2026-09-16 (#141), with colregs' own matcher, one-seat against
|
|
23
|
+
pooled:
|
|
24
|
+
|
|
25
|
+
| encounter | one seat tells self | pooled says self is |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| crossing, other on self's port bow | nothing | stand-on |
|
|
28
|
+
| self not under command, ordinary power vessel to starboard | give-way (15(a)) | stand-on (18(a)(i)) |
|
|
29
|
+
| self power-driven, a sailing vessel overtaking her | give-way (18(a)(iv)) | stand-on (13(a)) |
|
|
30
|
+
|
|
31
|
+
The second and third are not gaps but wrong answers: both vessels give-way
|
|
32
|
+
at once. The cause is that every `rel:overrides` edge of the Q-40 family —
|
|
33
|
+
`rule:18a_i` → `rule:15a:keep_out_of_the_way`, `rule:13a` → `rule:18a_iv`,
|
|
34
|
+
`rule:9c` → `rule:18a_iii` and the rest — has its source in one vessel's
|
|
35
|
+
seat and its target in the other's. A one-seat reader never sees the source,
|
|
36
|
+
so the target stands. The suite has pooled both seats since ADR 0005 §4
|
|
37
|
+
(`pooledRoles` in `test/data.test.mjs`); with the swap removed it fails.
|
|
38
|
+
|
|
39
|
+
Two repairs were on the table: reciprocal entries (self stand-on, other
|
|
40
|
+
give-way, gates mirrored), or the pooled read stated as the contract.
|
|
41
|
+
|
|
42
|
+
## Decision
|
|
43
|
+
|
|
44
|
+
1. **`evaluateEncounter` reads two frames.** The situation as given, and its
|
|
45
|
+
swap (`self` and `other` exchanged, `pair` unchanged), are both matched
|
|
46
|
+
against every non-`display` entry. Derived facts are computed per frame.
|
|
47
|
+
2. **Precedence entries are pooled, then resolved.** The precedence entries
|
|
48
|
+
that apply in either frame form one pool, keyed by which vessel each fired
|
|
49
|
+
for. `rel:overrides` is resolved over the pool, so an override may reach
|
|
50
|
+
an entry that fired in the other frame. Only then are roles read.
|
|
51
|
+
3. **`roles.self` and `roles.other` come from the pool.** A vessel's roles are
|
|
52
|
+
every non-`none` role a surviving forceful entry lays on her, from
|
|
53
|
+
`effect.self` where she was the frame's self and from `effect.other` where
|
|
54
|
+
she was the frame's other; `by` names the entry either way.
|
|
55
|
+
4. **`applied`, `scope`, `encounter`, `risk_of_collision`, `modalities` and
|
|
56
|
+
`categories` stay self-frame.** They answer the fixture's entry-id `expect`
|
|
57
|
+
as they do today, and a classification is the pair's already.
|
|
58
|
+
5. **A situation fixture may state `roles`.** `roles: {self: [{role, by}],
|
|
59
|
+
other: [{role, by}]}` is the pooled, resolved answer for both subjects and
|
|
60
|
+
binds an engine to it; `expect` stays the self-frame entry ids.
|
|
61
|
+
6. **No reciprocal entries.** They would double the table and every override
|
|
62
|
+
edge, make `effect.other` redundant, and still need the override to reach
|
|
63
|
+
across the pair.
|
|
64
|
+
|
|
65
|
+
## Consequences
|
|
66
|
+
|
|
67
|
+
- Four binding cases in `fixtures/situation-fixtures.json` carry `roles`:
|
|
68
|
+
the crossing as written and the three encounters in the table above.
|
|
69
|
+
- `INV-PB-roles-exclusive` and `INV-PB-one-role-source` are stated over the
|
|
70
|
+
pooled read, as the suite already asserts them.
|
|
71
|
+
- colregs-engine's `evaluateEncounter` changes to match; colregs-engine#85
|
|
72
|
+
(the 13(d) latch ordering) is independent of this.
|
|
73
|
+
- 8(f)(iii)'s `none`/`none` and Q-35 are untouched: pooling adds no role,
|
|
74
|
+
it only lets every entry see the entries it was written to override.
|
|
75
|
+
|
|
76
|
+
## Register
|
|
77
|
+
|
|
78
|
+
| item | level | what would settle it |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| Two frames, pooled, resolved, then roles — not reciprocal entries | ink | Solace, 2026-09-16 |
|
|
81
|
+
| Self-frame `applied`; pooled `roles` only | ✎ | a consumer needing the other frame's applied ids |
|
|
82
|
+
| Fixture `roles` as `{role, by}` per subject | ✎ | the engine's conformance replay consuming it |
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# ADR 0017 — Closed vocabularies are prefixed identifiers
|
|
2
|
+
|
|
3
|
+
Date: 2026-09-16
|
|
4
|
+
Status: accepted — Solace's ruling, 2026-09-16
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Issue #120 built a display catalog for the light, modality, role, encounter
|
|
9
|
+
and jurisdiction vocabularies and, in doing so, named a fiction in
|
|
10
|
+
`docs/identifiers.md`: "What is not an identifier" declared modality, role,
|
|
11
|
+
jurisdiction and encounter values outside `REQ-MODEL-10`'s identifier space,
|
|
12
|
+
on the theory that a closed vocabulary is a different kind of thing from a
|
|
13
|
+
name. The carve-out never reduced what a rename costs. Every one of these
|
|
14
|
+
values is emitted by the engine (`DisplayEvaluation.modality`,
|
|
15
|
+
`EncounterEvaluation.roles[].role`, `.encounter`, `provenance.jurisdictions`,
|
|
16
|
+
`evaluated_categories`) and compared by a consumer the same way an entry id
|
|
17
|
+
or a fact value is — `if (role === 'give-way')` breaks on a rename exactly
|
|
18
|
+
as `if (activity === 'nuc')` would. Declining to promise stability does not
|
|
19
|
+
make the rename cheaper; it only leaves the promise unwritten.
|
|
20
|
+
|
|
21
|
+
The collision that follows from treating them as a separate kind is not
|
|
22
|
+
hypothetical. It has already happened, in the data on `main`:
|
|
23
|
+
|
|
24
|
+
| string | is a … | and also a … |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `shall-not-impede` | modality (`modalities`) | role (`effects.roles`) |
|
|
27
|
+
| `none` | role (`effects.roles`) | encounter (`effects.encounters`) |
|
|
28
|
+
|
|
29
|
+
This is the identical shape as `towing`, the collision ADR 0001's `light:`
|
|
30
|
+
prefix and `activity:` axis already resolved: a consumer holding the bare
|
|
31
|
+
string cannot say which field it came out of. The rule already on file —
|
|
32
|
+
"the prefix makes the namespace part of the identifier, which resolves that
|
|
33
|
+
collision by construction rather than by convention" — already applies here;
|
|
34
|
+
it was only not applied.
|
|
35
|
+
|
|
36
|
+
Issue #120's own draft, before this ruling, reached the opposite
|
|
37
|
+
recommendation on its Q2: that prefixing sections into identifiers "later"
|
|
38
|
+
costs the same as doing it now. That held only if the bare strings were not
|
|
39
|
+
identifiers. They are — held and compared by consumers — so the option runs
|
|
40
|
+
one way: pre-1.0, prefixing is a data-layer, one-PR change; post-1.0 it is
|
|
41
|
+
an identifier-layer change REQ-MODEL-10 forbids outright, leaving only the
|
|
42
|
+
two-names-forever shape `docs/identifiers.md` already rejected for
|
|
43
|
+
`fact:own_activity`. Ruled by Solace, 2026-09-16.
|
|
44
|
+
|
|
45
|
+
## Decision
|
|
46
|
+
|
|
47
|
+
1. **Four closed vocabularies take a type prefix**, the same mechanism as
|
|
48
|
+
`light:`, `fact:`, `rel:`: `modality:` (`data/applicability.json`
|
|
49
|
+
`modalities`, every entry's `modality`, `modality_by` branches),
|
|
50
|
+
`role:` (`effects.roles`, `effect.own`/`effect.other`), `encounter:`
|
|
51
|
+
(`effects.encounters`, `effect.encounter`), and `category:` (`categories`,
|
|
52
|
+
every entry's and `represented_paragraphs` record's `category`) — new to
|
|
53
|
+
this list because it is the same class (package-coined, closed, emitted,
|
|
54
|
+
compared) and leaving it bare while prefixing the other three would
|
|
55
|
+
recreate the inconsistency this ADR closes.
|
|
56
|
+
2. **Jurisdiction stays bare.** `intl` and `us/inland` are not names this
|
|
57
|
+
package coined. Jurisdiction is a coordinate with REQ-SCOPE-2's own
|
|
58
|
+
`<body>/<waters>` grammar, its left segment borrowed from ISO 3166, the
|
|
59
|
+
whole value doubling as a corpus key (REQ-LANG-3) and a `data/text/`
|
|
60
|
+
filesystem path — its sibling axis, `language`, is a bare BCP 47 tag for
|
|
61
|
+
the same reason. `jurisdiction:us/inland` would put a colon namespace in
|
|
62
|
+
front of a slash path and claim the package minted `us`, which it did
|
|
63
|
+
not. Its stability is a property of the grammar (REQ-SCOPE-4 makes
|
|
64
|
+
adding a jurisdiction additive; no fact, light or role value can spell
|
|
65
|
+
`us/inland`), not of a prefix — the same reason a paragraph path carries
|
|
66
|
+
none. Jurisdiction moves from "What is not an identifier" to the bare
|
|
67
|
+
class beside paragraph paths in `docs/identifiers.md`: it *is* an
|
|
68
|
+
identifier, immutable under REQ-MODEL-10 exactly like `27(a)(i)`, and it
|
|
69
|
+
just carries no prefix.
|
|
70
|
+
3. **The two live collisions are recorded, not just resolved.**
|
|
71
|
+
`modality:shall-not-impede` and `role:shall-not-impede` are two names;
|
|
72
|
+
`role:none` and `encounter:none` are two names. Each pair collided under
|
|
73
|
+
the old bare scheme and does not under this one.
|
|
74
|
+
|
|
75
|
+
## Consequences
|
|
76
|
+
|
|
77
|
+
- **One-pass rename, one commit.** `data/applicability.json`'s four
|
|
78
|
+
vocabulary maps and every entry field that names a value; both fixture
|
|
79
|
+
files (`applicability-fixtures.json` has no modality field to touch;
|
|
80
|
+
`situation-fixtures.json`'s `expect[].modality`); every schema enum/pattern
|
|
81
|
+
naming one of the four (`applicability.schema.json`,
|
|
82
|
+
`evaluation.schema.json`, `encounter-evaluation.schema.json`,
|
|
83
|
+
`i18n-catalog.schema.json`); the two i18n catalogs' `modality` section
|
|
84
|
+
keys; the test suite's literal comparisons; `docs/identifiers.md`,
|
|
85
|
+
`docs/requirements.md`, `README.md`. Jurisdiction values, patterns and
|
|
86
|
+
every corpus path are untouched.
|
|
87
|
+
- **colregs-engine follow-up.** Its generated types (`DisplayEvaluation`,
|
|
88
|
+
`EncounterEvaluation`, and any hand-written literal comparing against a
|
|
89
|
+
modality, role, encounter or category string) regenerate from this
|
|
90
|
+
package's schemas and wait on a colregs release carrying this ADR. Until
|
|
91
|
+
that release, colregs-engine's own types name the pre-ADR bare values;
|
|
92
|
+
this is a breaking change for it in the ordinary pre-1.0 sense (no
|
|
93
|
+
deprecation window, no shim — `AGENTS.md` "Stage: pre-consumer"), tracked
|
|
94
|
+
as its own follow-up, not blocking this PR.
|
|
95
|
+
- **The i18n catalog keys change shape**, not content: `modality:shall`
|
|
96
|
+
replaces `shall` as the key into `data/i18n/*.json`'s `modality` section;
|
|
97
|
+
the label strings themselves are untouched. A catalog section for role,
|
|
98
|
+
encounter or category is not added by this ADR — only the vocabulary
|
|
99
|
+
values those sections would key against.
|
|
100
|
+
- Rule ids (`rule:15a:crossing`) and shape keys (`when`, `effect`, a
|
|
101
|
+
`category:scope` effect's `part`/`section`/`applies_rules`) are untouched: only
|
|
102
|
+
the four vocabularies' *values* take the prefix, never a key or an id
|
|
103
|
+
that happens to share a word with one.
|
|
104
|
+
|
|
105
|
+
Ruled by Solace, 2026-09-16.
|
package/docs/budgets.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
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
|
+
"docs/normative-language.md": 130,
|
|
5
6
|
"AGENTS.md": 200,
|
|
6
7
|
"CLAUDE.md": 10,
|
|
7
8
|
"docs/requirements.md": 1500,
|
|
@@ -18,7 +19,13 @@
|
|
|
18
19
|
"required": true,
|
|
19
20
|
"pending": [],
|
|
20
21
|
"docs/adr/0013-corpus-files-with-editions.md": 95,
|
|
21
|
-
"docs/
|
|
22
|
+
"docs/adr/0014-engine-interface-owned-by-colregs.md": 110,
|
|
23
|
+
"docs/adr/0015-rule-ids-are-paragraph-keys.md": 210,
|
|
24
|
+
"docs/adr/0016-encounter-roles-are-pooled-across-frames.md": 90,
|
|
25
|
+
"docs/adr/0017-closed-vocabularies-are-prefixed-identifiers.md": 120,
|
|
26
|
+
"docs/timeline.md": 130,
|
|
27
|
+
"docs/maritime-sources.md": 130,
|
|
28
|
+
"docs/decisions.md": 40
|
|
22
29
|
},
|
|
23
30
|
"json_prose": {
|
|
24
31
|
"targets": [
|
|
@@ -55,14 +62,10 @@
|
|
|
55
62
|
"data/applicability.json#/entries/62/note",
|
|
56
63
|
"data/applicability.json#/entries/63/note",
|
|
57
64
|
"data/applicability.json#/entries/64/note",
|
|
58
|
-
"data/applicability.json#/entries/64/gap",
|
|
59
65
|
"data/applicability.json#/entries/65/note",
|
|
60
|
-
"data/applicability.json#/entries/65/gap",
|
|
61
66
|
"data/applicability.json#/entries/66/note",
|
|
62
67
|
"data/applicability.json#/entries/67/note",
|
|
63
68
|
"data/applicability.json#/entries/68/note",
|
|
64
|
-
"data/applicability.json#/entries/68/gap",
|
|
65
|
-
"data/applicability.json#/entries/69/note",
|
|
66
69
|
"data/facts.json#/derived/note",
|
|
67
70
|
"data/facts.json#/derived/fact:rule18_class/note",
|
|
68
71
|
"data/facts.json#/derived/fact:rule18_class/decode/1/note",
|
|
@@ -134,16 +137,6 @@
|
|
|
134
137
|
]
|
|
135
138
|
},
|
|
136
139
|
"grandfathered": [
|
|
137
|
-
{
|
|
138
|
-
"file": "data/applicability.json",
|
|
139
|
-
"hash": "447b3984d57b",
|
|
140
|
-
"match": "PR #24"
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
"file": "data/applicability.json",
|
|
144
|
-
"hash": "5ff9d39319b8",
|
|
145
|
-
"match": "PR #24"
|
|
146
|
-
},
|
|
147
140
|
{
|
|
148
141
|
"file": "docs/conventions.md",
|
|
149
142
|
"hash": "d1f1ed997431",
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Decisions
|
|
2
|
+
|
|
3
|
+
Rulings from `/sequence` sessions and other closed judgment calls. One line
|
|
4
|
+
each: date, short name, the answer, a link to the argument.
|
|
5
|
+
|
|
6
|
+
- 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).
|