Tag schema¶
Lookup reference for the closed, validated, Polish-only @tag schema carried on entity blocks in nerthus.entities.md and its overflow files. It covers the recognized tag set, link-value format, the temporal range grammar and its evaluation rules, the historical write rule, and the projected entity record. The surrounding model — index files, merging, the claim ledger — is the entity model.
The closed tag table¶
Every recognized tag and its kind:
- multi — repeatable; evaluation returns every active entry.
- scalar — single current value; last-active-wins.
- scalar, historical — scalar whose updates close-and-append instead of replacing (see the historical write rule).
| Tag | Meaning | Kind |
|---|---|---|
@alias |
Alternative name, added to the resolver's name set — never consulted for mention routing | multi |
@lokacja |
The entity's place: its containing location, or on a Mapa the place the map depicts | scalar, historical |
@drzwi |
Physical access edge to another Lokacja/Mapa (a "door"); log-derived lines carry (auto) (Locations model) |
multi |
@typ |
Entity type (authoritative over the ## Category heading) |
scalar, historical |
@należy_do |
The entity's owner — commonly a Postać's owning Gracz | scalar, historical |
@grupa |
Group / faction membership | multi |
@status |
Aktywny / Nieaktywny / Usunięty; quarantined location buckets also use Niepewny |
scalar, historical |
@ilość |
Quantity for stackables (currency, items) | scalar, historical |
@plik |
Source file path — the distribution target for session writes (Sessions model) | scalar |
@generyczne_nazwy |
Comma-list of generic names added to the resolver (Korony Elanckie, Korony) |
multi |
@nazwa_nerthus |
Canonical Nerthus display name when the heading is a variant | scalar |
@koordynaty |
Position on a parent Mapa (grid x,y) |
scalar, historical |
@slug |
Margonem URL slug for a Lokacja/Mapa | scalar |
@margonemid |
Margonem profile ID (Gracz) or map ID (Mapa) — authoritative identity | scalar |
@url |
Primary image URL of any entity (Mapa render, token art, portrait); on a Mapa the CDN image is versioned, so updates keep history | scalar, historical |
@wymiary |
Mapa pixel/grid dimensions (W×H) |
scalar |
@pu_startowe |
PU STARTOWE — starting skill points for a Postać | scalar |
@pu_suma |
PU SUMA — total skill points (daemon-computed snapshot) | scalar |
@pu_zdobyte |
PU ZDOBYTE = SUMA − STARTOWE (derived) | scalar |
@pu_nadmiar |
PU NADMIAR — unbounded monthly-cap overflow pool | scalar |
@url_nerthus |
Nerthus-hosted mirror of the primary image | scalar |
@url_grafika |
Alternate/additional image URL (battle map, old portrait, variant) | multi |
@url_dokument |
Linked external document (Google Docs sheet, pastebin) | multi |
@url_strona |
Linked website/page (nerthus.pl article, forum thread) | multi |
@typ_lokacji |
Mapa exterior/interior marker (zewnętrzna/wewnętrzna) |
scalar, historical |
@outerior |
The nearest exterior (world root) a Lokacja resolves to — importer-computed | scalar, historical |
@inheritsfrom |
Reserved: an interior inheriting its world position from its Outerior | scalar |
@isexterior |
Reserved: the koordynaty/Mapa-evidence exterior classification (deferred) | scalar |
@first_session |
Reserved: first session date that mentioned the entity — not currently emitted; first-mention dates surface as the (YYYY-MM:) scopes on @lokacja/@outerior |
scalar |
@floor |
Recognized on read only: Mapa floor number parsed off a p.N name suffix — never machine-emitted (the importer derives the floor from the map name itself); a hand-authored line is honored on read and projected onto the entity record |
scalar |
@base_name |
Recognized on read only: Mapa base name with the floor/room suffix stripped — never machine-emitted (the importer derives it from the map name and emits the base name as @alias); a hand-authored line is honored on read, projected onto the entity record, and joins the resolver's name set |
scalar |
@forma_sesyjna |
A claim: routes one literal session form to this block; a (auto) suffix marks a machine-appended claim (the claim ledger) |
multi |
@duplikat |
Source files of duplicates dropped at import — machine-owned, re-derived at every merge | multi |
@prfwebhook |
Discord webhook — the Gracz block in the committed index is the one source (Players model) | scalar |
@tematy_zastrzeżone |
A Gracz's content triggers (Tematy zastrzeżone) as a typed field — the Gracz block holds no freeform prose (Players model) | scalar |
Note
The PU tags are a snapshot the daemon maintains; PU itself is computed monthly from session PU metadata and is authoritative — PU model. Currency @ilość and transfers: Currency model.
Containment has exactly one direction: the child's @lokacja. The former parent-side @zawiera convenience is retired from the closed schema; a leftover @zawiera line is an UnknownTag integrity error like any other unrecognized key.
On a Mapa, @lokacja is depiction, not containment. It names the place the map depicts — the importer links a map to the Lokacja carrying its base name, and the location tree and the containment graph read Lokacja blocks only, so a Mapa is never a node in the world hierarchy. One place may be depicted by several maps (floors, event copies); each map depicts exactly one place, and map-level @drzwi doors project onto locations through this link (Locations model).
@należy_do names the owner of any entity, not only a character's player. A Przedmiot — a coin holding included — names its holder this way, and that holder may be a Postać, an NPC, a Grupa, a Gracz, or a Lokacja; currency classes a holding by its holder's type (a Postać's coin is physical Margonem items, every other holder's is RP bookkeeping). A holding hangs off exactly one of @należy_do or @lokacja (Currency model).
Closed-schema validation¶
The recognized-tag set is a fixed list in code, enforced at two seams:
- On write: unknown tag ⇒ reject. A tag update naming
@faction(English) or a typo@grpais refused with a validation error (HTTP 422) before anything touches disk. There is no generic catch-all. Entity creation validates its seed tags against the same schema, additionally rejects an unknown@typ, and refuses a(name, type)pair a block already carries with409— a duplicate heading would merge into one entity at parse time and shadow the new block from every write path. - On read: unknown tags are reported, not dropped. The scanner keeps every
@keyit finds, so a hand-edit is never silently lost;Test-NerthusEntityIntegrityreports the full finding set:
| Finding | Severity | Meaning |
|---|---|---|
UnknownTag |
Error | A tag key outside the closed schema |
UnknownType |
Error | A @typ value outside the seven types |
InvalidRange |
Error | A (from:to) suffix whose date tokens do not parse |
InvertedRange |
Error | A range with from > to |
UnresolvedReference |
Warning | @należy_do/@lokacja/@drzwi naming no entity in the model (lore may legitimately lag) |
InvalidUrl |
Warning | A @url_* value whose first token is not an absolute http(s) URL |
LocationCycle |
Error | A @lokacja containment chain that loops instead of reaching a root |
Diacritics are canonical. On disk a tag is its full Polish form: @należy_do, @ilość, @status: Usunięty, type Postać. The daemon writes diacritics only. Exactly two legacy ASCII spellings — @nalezy_do and @ilosc — are recognized on read (model build and write bodies) and normalized to the diacritic key; no other ASCII tag variant is mapped. Legacy-read normalization of values at adoption belongs to the import model.
Link values (the @url_* family)¶
The URL tags split by role and by what the link points at, so a renderer knows an image from a plain link without fetching it:
| Tag | Role | Kind |
|---|---|---|
@url |
The entity's one primary image; the Margonem CDN versions map images, so a newer render closes the old line and appends — the map checkup writes the new value with (auto) |
scalar, historical |
@url_nerthus |
Nerthus-hosted mirror — replace in place | scalar |
@url_grafika |
Alternate images | multi |
@url_dokument |
Documents | multi |
@url_strona |
Websites | multi |
A link value is: an absolute http(s) URL, then an optional label in double quotes, then the optional (auto) marker, then an optional temporal range — in that order. The URL is the first whitespace-delimited token; the label is display text for the link. Temporal ranges work as on every tag, so retired art stays queryable:
### Rezydencja Tussal
- @typ: Lokacja
- @url_grafika: https://i.imgur.com/QVi9jT2.png "Mapa poglądowa Rezydencji Tussal"
- @url_grafika: https://i.imgur.com/1Lcwrm8.png "Mapa z ogrodem" (:2026-06)
- @url_dokument: https://docs.google.com/document/d/1AbC "Zamówienie na serwis do herbaty"
Integrity validates the shape: a value whose first token does not parse as an absolute http(s) URL is reported as InvalidUrl (Warning — legacy lore may hold odd links). A Gracz profile link needs no tag: it derives from @margonemid.
Temporal ranges¶
Any tag value may carry a parenthesized validity range after the value:
- @lokacja: Ithan (:2026-07) # active until Jul 2026 (open start)
- @lokacja: Thuzal (2026-07:) # active Jul 2026 onward (open-ended)
- @status: Aktywny (2022-01:2026-06) # active Jan 2022 – Jun 2026
- @grupa: Gildia Teologów (2021-01:) # joined Jan 2021, still a member
- Format:
(VALID_FROM:VALID_TO). Either bound may be omitted for open-ended ranges. A value with no suffix is always-active. - Partial dates resolve to bounds:
YYYY→ the full year,YYYY-MM→ the full month (end bound uses the real day count),YYYY-MM-DD→ an exact day. - Range parsing is uniform — the suffix is recognized on every tag; integrity checks its validity, and the historical write rule below applies only to the nine
scalar, historicaltags. - No seasons. The only temporal primitive is the dated range.
Evaluation rules¶
"Current" is a computed query, never a stored field. Given an -ActiveOn date (default: now), the daemon evaluates the history:
- Scalar tags: last-active-wins — among entries whose range covers the query date, the one with the latest start bound. Undated entries tie-break by load order: the later file wins, giving overflow files their primacy over the base index. So Opat Perrin's current
Locationafter a 2026-07 move isThuzal, notIthan. - Multi-valued tags (
@alias,@grupa,@drzwi,@generyczne_nazwy, …): all-active — every entry whose range covers the query date, as a deduplicated set.
The historical write rule¶
For the nine scalar, historical tags (@lokacja, @status, @należy_do, @ilość, @koordynaty, @typ, @typ_lokacji, @outerior, @url) an update never overwrites the active line. It closes that line's validity range at the write's effective month — the current month, unless the caller passes an explicit date — and appends the new value dated from that month. The block keeps the full timeline. Moving Opat Perrin to Thuzal in July 2026:
- @lokacja: Ithan (:2026-07) # was: Ithan — closed, not erased
- @lokacja: Thuzal (2026-07:) # appended
One sanctioned variant: session transfers append their @ilość deltas as new lines dated to the session month, leaving the prior line as is — last-active-wins still resolves the newest balance, and the history keeps every step (Currency model).
A first-ever value is written undated, exactly as the importer writes it; setting the already-active value is a no-op. All other scalar tags (@pu_*, @slug, @wymiary, @plik, …) are tool-refreshed snapshots and replace in place; multi-valued tags always append a new line.
Soft-delete¶
Removal is the historical write applied to @status: the active status line is range-closed and Usunięty is appended, keeping every bullet. Semantics and consequences are in the entity model; the operator recipe is Add, edit, and retire entities.
The (auto) marker¶
A value carrying (auto) is machine-written and re-derivable; an unmarked value is operator-authored and preserved verbatim by every merge — the full authorship contract is the entity model. Grammar facts:
- The marker sits directly after the value (and its label, on a link value), before any temporal range:
- @drzwi: Thuzal (auto),- @url: https://…/rezydencja_tussal.1.png (auto) (2026-07:). That order keeps a marked line range-closable. - Matching folds the marker off —
Thuzal/Rezydencja Tussal (auto)andThuzal/Rezydencja Tussalare the same claim with different authorship. - The daemon strips the marker from every projected value except
SessionForms, which keeps claim lines raw. - Three entity-tag families carry it today:
@forma_sesyjnaclaims, the log-derived@drzwidoors, and the checkup-refreshed Mapa@url(Locations model owns the latter two derivations). - The marker is not entity-only. A session's
@Plikisection — the distribution target list the tool writes and rewrites whole — carries it on the section line:- @Pliki: (auto)(Sessions model). The section has no value of its own, so the marker sits where a value would. It means what it means everywhere else: the tool authored this and owns it, so an operator edit is overwritten on the next pass.
The projected entity record¶
The daemon parses the Markdown into a structured object; cmdlets return this shape with temporal fields already resolved to the requested -ActiveOn date. All values are the raw tag strings — the daemon does no numeric coercion (Quantity is "50", not an integer).
| Field | Source tag(s) | Notes |
|---|---|---|
Name |
### heading text, overridden by @nazwa_nerthus |
Canonical name |
Heading |
### heading text |
The literal block heading |
Type |
@typ (fallback: the ## Category) |
One of the seven types |
Category |
the ## Category heading |
Grouping only |
Status |
@status (default Aktywny) |
See the tag table |
Aliases |
@alias + @generyczne_nazwy |
All-active alternative names |
Owner |
@należy_do |
The entity's owner |
Groups |
@grupa |
All-active memberships |
Location |
@lokacja |
Last-active place |
Doors |
@drzwi |
All-active access edges |
Quantity |
@ilość |
Stackables/currency |
FilePath |
@plik |
Distribution target |
Coordinates |
@koordynaty |
Grid position on parent Mapa |
RestrictedTopics |
@tematy_zastrzeżone |
A Gracz's content triggers |
SessionForms |
@forma_sesyjna |
Claim lines, raw — the (auto) marker included |
DuplicatesDropped |
@duplikat |
Duplicate sources dropped at import |
Tag-presence fields are added to the record of any entity carrying the tag — a Gracz has a MargonemId; a Mapa has MargonemId, Url, Slug, Dimensions. The set: PuStartowe, PuSuma, PuZdobyte, PuNadmiar (the Postać PU snapshot), plus MargonemId, Url, UrlNerthus, Slug, Dimensions, Outerior, InheritsFrom, IsExterior, Floor, BaseName, and the all-active link arrays ImageUrls (@url_grafika), DocumentUrls (@url_dokument), WebsiteUrls (@url_strona) — link values raw, the quoted label included.
Routes, envelopes, and the cmdlet ⇄ route mapping for entity reads and writes: API reference. How @alias and @generyczne_nazwy feed the resolver: Name resolution.