The regulation corpus¶
Nerthus.Core (until cutover). This page describes the frozen system that runs today and is deleted at cutover. Replaced by: not yet written.
The rules a chat sanction is issued under, as data: the documents, their versions, which version was in force on a given day, the citable points of each, and the sanction ranges.
Every READ in the corpus carries no capability. A player contesting a sanction has to be able to read the rule that was applied to them, in the version that was applied — a corpus readable only by the people who enforce it is not a corpus.
Routes¶
| Method | Path | Cmdlet | Cap | Write |
|---|---|---|---|---|
| GET | /regulations |
— | — | — |
| GET | /regulations/effective |
— | — | — |
| GET | /regulations/tariff |
— | — | — |
| GET | /regulations/{doc} |
— | — | — |
| GET | /regulations/{doc}/{version} |
— | — | — |
| GET | /regulations/{doc}/{version}/points |
Get-NerthusRegulationPoint |
— | — |
| GET | /regulations/{doc}/{version}/points/{point} |
Get-NerthusRegulationPoint -Point |
— | — |
| POST | /regulations/{doc}/versions |
— | regulation.write |
✓ |
Cap is the required capability (— = public, no token). Paths are relative to /v1/api; the
cross-cutting contract is on the API reference index.
Six of these rows carried a cmdlet name that named nothing. Get-NerthusRegulation,
Get-NerthusTariff and New-NerthusRegulationVersion exist in no client/*.ps1 file and are
exported by no manifest — the reference asserted a shell client this family never had. The cells
are empty now, following the editor's precedent for a browser-only family: this corpus
is read from the dashboard, and an empty cell is a true statement where a name was a false one.
Get-NerthusRegulationPoint is the exception and it was written for a reason. A citation has to
resolve from a shell as well as from a browser, and the narrator absence duty
(rozporzadzenia-rady § 7 pkt 6) is addressable only through the single-point read — an addressing
scheme whose read no command can call satisfies the letter of resolvable and nothing else.
The route-coverage guard cannot see this column. It compares paths, capabilities and write
flags, and says so about itself; whether a Cmdlet cell names a command that exists is checked by
nobody. That is why six of eight rows here were wrong for as long as they were.
Every GET row is public, and that is a deliberate property rather than an oversight. A player
contesting a sanction has to be able to fetch the exact rule that was applied to them, in the
version that was applied — a regulation readable only by the people who enforce it is not a
regulation. Every GET row is also replica-safe and poolable: committed files, read whole, no
principal consulted. The corpus lives at .nerthus/regulations in the moderators tree
(corpora.mc) when that directory exists, with the lore tree as a migration fallback only where
the moderators tree carries no corpus — so two hosts agree only when both moderators clones sit
on the same commit, the caveat the moderator roster states.
The one write is the SMC's. An amendment changes the text every future sanction is judged
against and every past one is contested against, which is the same class of act as publishing a
keyring epoch and not the same class as issuing a mute. admin.all satisfies it.
§-numbered documents address two levels¶
The rozporządzenia number paragraphs as ### § 7. headings and their points as 6) list items in
the body, and the corpus cites the two together — „§ 7, pkt. 1, 2 i 4", „§ 9, pkt. 2". Both
levels are citable, so both are returned:
| id | what it names |
|---|---|
§7 |
the paragraph |
§7.6 |
point 6 inside it — the narrator's duty to report an absence to the Rada |
The corpus is left as transcribed. Renumbering ### § 7. to ### 7. would make the transcription
disagree with the raw capture it is checked against, and would invent numbering across three
versions of a document that prints none.
One version file may transcribe several instruments, and each restarts its § numbering.
rozporzadzenia-rady/2026-06-15 carries the 2021 rozporządzenie plus the two 2026 blocking
instruments, so § 1 names three different provisions in one file. A ## heading beginning
Rozporządzenie opens a new scope, and a paragraph outside the first scope carries that scope's
printed ordinal — §1, 3§1, 4§1 in that version, the gap because the amendment heading between
them opens a scope carrying no paragraph of its own. The prefix is numbering the document does not
print, and it is the narrower of two evils: without it three provisions answer to one address and
the reader gets whichever comes first. Each row also carries scope, the instrument heading
verbatim.
Point counts for rozporzadzenia-rady, which returned 0 before this scheme: 56 for
2021-10-18, 54 for 2023-08-02, 62 for 2026-06-15. The decimal documents are untouched
— nerthus-fabularny/2021-12-06 still returns 110, nerthus-swiat/2021-05-11 53,
taryfikator/do-2026-02-17 183, and none of their rows carries a § id.
What the answers say about themselves¶
GET /regulations/{doc}/{version}/points/{point} returns ONE point — the regulation
justification a record cites. It answers RegulationPointNotFound when the version exists and the
point does not, which a caller must tell apart from RegulationVersionNotFound: the first means
the point was renumbered or removed and the citation is unresolved, the second means the version
was never captured. Collapsing them would leave a client unable to say which.
POST /regulations/{doc}/versions lands a new version and closes its predecessor in the same
change, setting the predecessor's effective_to to the day before the new effective_from.
The bounds are inclusive, so a shared day would make a citation from it resolve to two texts.
Closing used to be a separate manual edit, and forgetting it produced an overlapping corpus that
was only reported after the fact. A dry run reports which version it would close and with what
date; the route refuses to overwrite an existing version directory, because a version directory is
the evidence a past sanction is judged against.
GET /regulations lists the five documents and their versions. A document with no versions is
still a row, and version_count: 0 with has_text: false is the corpus telling the truth about
itself — known and empty is a different statement from does not exist. A citation into a
document with no text resolves to a version and renders nothing, which is the right failure: the
alternative is rendering the nearest text instead, and that silently justifies a sanction under a
rule that was not in force.
rozporzadzenia-rady carries three versions, and the three are one document's history rather than
four separate instruments. The 2021 narratorskie rozporządzenie takes effect on 2021-10-18, seven
days after publication — the notice_days: 7 this table declares, showing up as a real answer,
because ?at=2021-10-17 resolves to nothing. The 2023 Zmieniające lands as the next version, so
?diff= across the pair renders exactly what that amendment states: § 8 loses two points and the
list renumbers. The two 2026 blocks arrive in a third version. What a version cannot say about
them is on the ledger instead — that one is bounded to 2026-11-02 and expires by clock, that
each formalises a decision from an earlier date, and that each names one player. Those are facts
about instruments, and a version directory models texts. The governance ledger
holds them.
Point extraction does not reach this document, measured rather than assumed. A point is a
heading whose first token is a dotted id rooted in a roman numeral or a decimal, and
rozporzadzenia-rady numbers its provisions § 1. through § 18. — no such root. All three
versions answer count: 0 where the other four documents answer 183, 310, 53 and 112. Citations in
the corpus read § 9, pkt. 2 Rozporządzenia, so the numbering a record would cite exists; the
regex does not express it.
GET /regulations/effective?at=<yyyy-MM-dd> answers which version of each document was in force
on a day, defaulting to today. A document with nothing in force is a row with a null version and
not an omission — a large share of the historic mutelog predates the Nerthus regulation
entirely, and an omission reads as not asked about. An ?at= that does not parse answers
400 rather than falling back to today: a view that asked what applied on the day of the
offence and silently got what applies now renders the wrong rule beside a real sanction, and
nothing on the page would say so.
GET /regulations/{doc}/{version} renders one version with its transcription, and ?diff=<other
version> renders the amendment against another version of the same document.
GET /regulations/{doc}/{version}/points lists the citable points of one version, each joined to
its tariff row where one exists. A point with no tariff row carries nulls rather than being
omitted: the most frequently cited conduct carries no fixed duration, and dropping it would hide
the sanction the corpus prescribes most often.
GET /regulations/tariff is the sanction ranges as data, so a client can refuse an out-of-range
duration at compose time rather than leaving a discrepancy nobody ever measures. The Margonem
Taryfikator's doubling rule is deliberately absent: it excludes chat offences under its own
section, so a Nerthus chat sanction escalates by the Nerthus recydywa ladder alone and applying
both would double-punish.
Ingest is not a route¶
Change detection over the published regulations takes a fetcher and writes nothing on its own. The
one place a real outbound HTTP call belongs is a scheduled workload with a timeout and a rate
limit, keyed on the same schedules.<workload>.owner mechanism the rest of the estate uses.
Hiding one inside a public route is how an endpoint anyone may call acquires an outbound network
dependency nobody declared.