Session metadata¶
Nerthus.Core (until cutover). This page describes the frozen system that runs today and is deleted at cutover. Replaced by: not yet written.
Exact syntax reference for the session header and every structured metadata key a session block may carry. For what the daemon does with this data — distribution, integrity hashing, the participation graph — see the session model.
Session header¶
### 2026-07-01, Eraster rozmawia z Tussalem, Anward
└── date ──┘└ title (may contain commas)┘└ narrator ┘
The header is the universal key: dedup, content hashing, participation, and distribution all key on it. Comparison is case-insensitive. The header is canonical and immutable — the tool never rewrites it.
| Field | Rule |
|---|---|
| Date | The first 10 characters, YYYY-MM-DD; multi-day suffixes below |
| Title | Everything between the date and the last comma — inner commas are preserved |
| Narrator | The text after the last comma, trimmed, with trailing *, , and ; removed |
Parsing notes:
- Any
###heading starting with aYYYY-MM-DD-shaped prefix is treated as a session header — even one with a malformed day part (2026-07-001). That tolerance enables the@Datarescue below. - A missing comma right after the date is tolerated.
- The narrator is never resolved to an entity — it stays a raw string.
Radadenotes a Rada session.
The narrator field is read as people¶
A header names one narrator, several, or none, and the field is read two ways at once: the raw string, and that string split into the people in it.
- Several narrators are joined with
+, spaces included. Each part is a name in its own right. Only+splits: a header writing the Polish conjunction —Rothe i Materios Dragonius— stays one string, becauseiis also an ordinary word and splitting on it would turn a fragment of a title into two narrators. - A field that is not a name yields no narrator. A name is capitalised, so a last
field whose first letter is lowercase is prose — a title that ran on past a comma —
and the evening is recorded as having no narrator rather than one called
zielarstwa i przywrócenia. Only the first letter decides:Lorenzo de ClementeandMoira von tepesare names, and a rule reading every word would refuse them. - An empty last field — a header ending in a comma — is no narrator either.
- A field with no letter at all, such as
???, gets no verdict and is kept.
An evening left without a narrator this way is not silently dropped: the
normalization worklist reports the refused fragment so the header
can be repaired. Use @Narrator below to state the narrators without touching the
header, which is immutable.
Multi-day suffixes¶
| Form | Example | Start | End |
|---|---|---|---|
Same-month /DD |
2022-12-21/22 |
2022-12-21 |
2022-12-22 |
Cross-month /MM-DD |
2023-01-31/02-01 |
2023-01-31 |
2023-02-01 |
The end date is composed textually from the start's year (and month, for /DD)
plus the suffix; it is never range-checked against the start.
Recognition rules¶
- Session-bearing roots — the daemon scans
*.mdunder exactly four directories:Wątki/,Organizacje/,Postaci/,Świat gry/. A session block anywhere else is invisible. That includes the generatedLokacje/registry at the repo root — its files hold session copies and are named what the scanner looks for, and they are still never read back (session registry). - Bare and
@forms are one format — the@prefix is the canonical spelling.- PU:parses identically to- @PU:. When the tool authors or edits metadata it writes the@-tag form; it never rewrites a hand-authored file to change its shape. -
Directive recognition is prefix-based. These stems map a label to a structured tag:
Stem Tag narrator-@Narratordata-@Datatytu-@Tytułlokacj-/lokalizacj-@Lokacjelog-@Logiexact PU@PUtransfer-@Transferintel-@Inteluczestnic-@UczestnicyA custom label sharing a stem (
- Logistyka:→@Logi) is read as structured — pick narrative labels off those stems. Parsing never alters the file text. -
What the parser accepts is wider than what the tool writes, and until 2026-09-01 this page said nothing about it at all. That silence had a cost: the word "spaces" appeared nowhere on 345 lines, and a narrator who copied the shape below and typed two spaces instead of four produced a session whose
@Lokacjeand@PUthe API silently could not read — 540 sessions, one in four of the last three years (project 116). An undocumented constraint that drops data without an error is not a constraint; the parser was widened rather than the corpus reflowed, and the accepted set is now written down so nobody has to discover it.accepted child indent any indent greater than its tag's — 2, 3, 4 spaces or a tab. A child at the tag's own indent is read too, but only where the field's grammar accepts the whole line: an award that is Name: 0.2end to end, a log line that IS a URL, a location with no colon in itbullets -,*,+,•. A dash needs no space after it (-PU:parses);*,+and•do, because*textis emphasis and* textis a list itemwhitespace tabs count as four; NBSP, figure space and narrow NBSP count as one; zero-width characters and the BOM are removed before the line is read tags @-prefixed or bare, colon optional (- PUopens the list), any casing (LOGI:)decimals Polish comma or dot. - @PU: A: 0,1, B: 0,2is two awards, not oneaward unit written out or omitted — 0,2 PUis0,2. Only when the rest of the value is a number:0,2 pktis not an awardawards the colon may be missing — - Aurelia Lesup 0.1is an awardGen2 italics the line must open with *; what it closes with is read loosely —*Lokacja: X*\,*Lokacja: X*., an unclosed*Lokacja: X, and*Logi:* https://…where only the label is emphasisedlog links a scheme is optional: pastebin.com/raw/xis read ashttps://pastebin.com/raw/xThe canonical shape below is still the one to write. Accepted is not the same as recommended, and the tool authors 4-space children and
@-tags. -
Rendering is fixed —
@Datais inline; every other tag's items are 4-space nested children on a single flat level (a nested interior is written as its fullParent/Interiorpath on one line). Empty tags are omitted. The authored tag order is the order of the key table below; the tool-managed@Plikisection is always rewritten at the end of the block. - Casing convention — session tags are capitalized, usually plural
(
@Lokacje,@Pliki,@PU); entity-index tags are lowercase singular (@lokacja,@plik). They are different tags in different files — see the tag schema.
Metadata keys¶
| Tag | Shape | Meaning |
|---|---|---|
@Narrator |
nested name(s) | Narrator override in metadata; the header stays untouched |
@Data |
inline YYYY-MM-DD |
Date override — rescues a malformed header date |
@Tytuł |
inline text | Title override — rescues a misspelled header title |
@Lokacje |
nested locations | Where it happened, as Parent/Interior paths |
@Logi |
nested URLs | Game-log links, fetched into the nerthus.logs/ archive |
@PU |
Postać: 0,3 |
Per-character PU grants — drive the monthly PU batch |
@Transfer |
[amount] denom, src -> dst |
Currency/item movements |
@Intel |
target: message |
Directed notifications, delivered via Discord when the session is closed |
@Uczestnicy |
nested names | Explicit participant roster without a PU grant |
@Pliki: (auto) |
nested paths | Distribution bookkeeping — machine-authored, overwritten on the next distribution |
@Narrator¶
Replaces the header-parsed narrator in parsed metadata. The header itself — the key — is never rewritten.
Repeat the tag for an evening several people adjudicated, one line each. The first
@Narrator line replaces whatever the header gave; the ones after it add to that answer.
This is the authored form of the + header field, and it is what repairs a header the
grammar refused. The name test the header path applies does not run here: writing the
field out by hand is a statement of what it is, and the tag exists to correct a header
rather than to be second-guessed.
@Data¶
Inline date override. Validation is shape-only (\d{4}-\d{2}-\d{2}): a value that
fails the shape is ignored, but a calendar-impossible value that fits it
(2024-13-40) still overrides the date — integrity later flags it as
MalformedDate.
### 2026-07-001, Eraster rozmawia z Tussalem, Anward ← broken header date
- @Data: 2026-07-01 ← override fixes the parsed date; header key stays
@Tytuł¶
Inline title override. Any non-empty value wins; there is no shape to validate.
Recognition is the tytu- stem, so the ASCII spelling @Tytul sets the title as well: a
header broken badly enough to need the override may have lost its diacritics too. The stem
is wide, and a narrative label picked off it (- Tytulatura:) overrides the parsed title
rather than staying prose. A nested list under the tag sets the title from its last
non-empty child.
### 2026-07-01, Eraster rowzmaria z Thuzalem, Anward ← misspelled header title
- @Tytuł: Eraster rozmawia z Tussalem ← override fixes the parsed title; header key stays
@Lokacje¶
Nested location references as Parent/Interior paths. An -> movement route always
normalizes into / path segments and feeds the map traversal graph
(location model). A legacy - separator is
ambiguous, since close to a quarter of the Margonem catalogue spells its own interiors
that way — Przeklęty Zamek - komnata is one map — so the location service resolves the
whole segment against the map registry first and splits on the separator only when that
fails. Author / today.
@Logi¶
Nested game-log links. The parser extracts http(s):// and file:// URLs into
metadata — file:// is admitted for local transcripts; a bare local path with no
scheme stays in the source but never reaches the parsed record. The log-fetch step
archives each URL under nerthus.logs/ (log model).
Note
@Logi names game transcripts; @Intel triggers Discord delivery. They are
entirely different systems and never overlap.
@PU¶
One Postać: value line per character. The Polish comma-decimal is accepted
(0,2 → 0.2), and the unit may be written out — 0,2 PU is the same award
as 0,2, in any casing, with or without the space. These grants drive the monthly
PU batch (PU model).
The unit is read only when the rest of the value is a number and nothing else.
0,2 pkt, ~0,2 and 0.5 + 0.2 (za dobre rozegranie) are not awards this grammar
defines — write the number.
@Transfer¶
[amount] denomination, source -> target — coin, stackable item, or unique item.
Endpoints are in-game entities only, never a Narrator. Syntax and application
rules are owned by the currency model.
@Intel¶
target: message lines — directed notifications the bot delivers to the target's
Discord channel (log model).
- @Intel:
- Thuzal: Mieszkańcy Thuzal widzieli, że Eraster odwiedził Lorda Tussal po południu w jego rezydencji.
@Uczestnicy¶
Explicit participant names — attendance without a PU grant. @PU and
@Uczestnicy are unioned for distribution; the names resolve through the
name resolver with fuzzy matching disabled.
@Pliki¶
The resolved distribution target set, recorded by the tool. It is not parsed as structured metadata — the live file set is discovered by scanning — and hand edits to it are overwritten wholesale on the next distribution.
It carries the (auto) marker on its section line, the same authorship marker entity
tags use (tag schema): the tool wrote this section and owns it. The
section has no value of its own, so the marker sits where a value would.
The section-matching regex is not end-anchored, so an unmarked @Pliki written before
the marker existed still matches and is replaced in place on the next distribution.
Nierozpoznane¶
What the parser still refuses, with the counts measured on the corpus pin
b46b0da0 (2026-09-01). Each is reported by GET /sessions/integrity, and none
can be repaired by the daemon: a session header is the deduplication key and the
PU ledger's key, so changing one is an operator's act.
| shape | count | issue | the repair |
|---|---|---|---|
| the header names no narrator | 92 | NarratorAbsent |
add - @Narrator: <name> to the body |
| the last comma field is not a name | 10 | NarratorDropped |
the same — the title contained a comma, so the field was the title's tail. The row carries the refused text |
@Logi holds something that is not a link |
7 | LogsUnparsed |
ask the narrator for the URL. A pasted transcript, an apology, or a repo-relative /Logi/... path, which has no URL scheme because nothing consumes that directory |
| the header's date is not a real date | 1 | MalformedDate |
fix the header — 2024-25-08 has no month 25, so the session sorts into a month nothing returns |
@Narrator is the repair for the first two by design: the tag path skips the
name test that the header path applies, precisely so an operator can overrule the
grammar. The grammar refuses a last field beginning with a lowercase letter
because that is how a title with a comma in it looks, and relaxing it costs 68
header fields across four narrators the index resolves — Lorenzo de Clemente,
Valdis la Douce, Kyria la Blachette, Moira von tepes. Voiding a name the
index reaches is the one thing that rule must never do.
Custom narrative tags¶
Any other - <Etykieta>: block — - Efekty: (session outcomes),
- Objaśnienia: (clarifications) — is preserved verbatim and carries no
structured weight: it moves no PU, no currency, and no participation. Zmiany is
not a structured directive; entity mutations are entity writes, never session
directives.
- PU:
- Eraster: 0,2
- Efekty: ← custom narrative tag (preserved, not parsed)
- Eraster wręczył Tussalowi łapówkę w postaci dziesięciu koron.
- Zjednał sobie przychylność Lorda Tussal.
Legacy generations (read-only)¶
The pre-tool repository holds three older shapes. The daemon auto-detects and
reads all of them as-is; the parser reports which shape a body used
(Gen1/Gen2/Gen3/Aktualny). No corpus-wide normalization pass exists.
| Shape | Era | Locations | Logs | Metadata |
|---|---|---|---|---|
| Gen1 | –2022 | none | Logi: <url> plain line |
none |
| Gen2 | 2022–2023 | italic label line, below | Logi: <url> plain line |
none |
| Gen3 | 2024–2025 | - Lokalizacje: list |
- Logi: list |
- PU:, - Efekty: (no @) |
| Aktualny | 2026+ | - @Lokacje: |
- @Logi: |
full @-tag set (the authored form) |
An example block (in Wątki/Intrygi w Thuzal.md) showing the Gen3 shape:
### 2025-01-26, Eraster spotyka się z Tussalem w Thuzal i wraz z nim planuje dalsze działania, Anward
*Lokalizacje: Thuzal, Thuzal/Rezydencja Tussal*
… prose …
- Logi: https://nerthus.pl/logizsesjierasteraztussalem
- Efekty:
- Eraster otrzymał zaproszenie do rezydencji Lorda Tussal …
- PU:
- Eraster: 0.2
The italic location line¶
A Gen2 or Gen3 body writes the evening's places on one italic line of its own. The label takes
either stem, in any case: Lokacje, Lokacja, Lokalizacje, Lokalizacja — the two stems
the lokacj- / lokalizacj- directive already takes.
*Lokacje: Karka-han/Karczma pod Złotą Wywerną*
*Lokalizacje: Przełęcz Dwóch Koron*
*Lokacje: Tuzmer; Płaskowyż Arpan*
*Lokalizacje Nithal, Podgrodzie Nithal*
The label goes together with whatever separates it from the first name — a colon, a semicolon,
a comma, or a run of spaces before a capitalised word. What remains splits on commas and
semicolons, and each name follows the @Lokacje rules above. A label with nothing after it
names no place.
Lokacja is an ordinary Polish noun as well as a field name, so the case of the following word
decides the line: *Lokacja jest piękna* is body prose and names nothing.
Two more italic shapes are read. A bare semicolon-separated list with no label at all is taken
as locations, and a line opening on Logi contributes its URLs to @Logi.
See also¶
- Session model — distribution, integrity tiers, the participation graph
- Tag schema — the entity-index tags (
@lokacja,@plik) - API — the session routes, cmdlet mapping, envelopes