Skip to content

The Session Registry (Lokacje/)

Nerthus.Core (until cutover). This page describes the frozen system that runs today and is deleted at cutover. Replaced by: not yet written.

Lokacje/ is a generated directory tree at the lore-repository root: one directory per known place, one generated Sesje.md in each, holding the sessions that placed there in chronological order. It is a derived projection of the session model — the hand-authored sources stay exactly where they are, and the registry is rebuilt from them. This page owns the registry's contracts: the ownership boundary, the tree shape, root resolution, the unplaced pile, pruning, and idempotency. The plain-language statement for operators is in Record a session.

The ownership boundary

The daemon owns exactly one filename in the tree — Sesje.md — plus the directory skeleton. Every Sesje.md is generated and regenerated wholesale; an edit to one is overwritten without warning, by design. Every other file in those directories is foreign, first-class, and never created, modified, or deleted by the daemon. That boundary is what makes the tree usable for future hand-authored content — an NPC roster or notes can sit beside a Sesje.md and the generator will never touch them.

Two marks carry the contract in-band:

  • the machine sentinel <!-- nerthus:generated:session-registry --> — a stable token on every generated file. The pruner acts only on Sesje.md files that carry it; a Sesje.md without it was written by something else and is never modified or deleted, only reported.
  • the banner — two Polish lines pointing at the operator article. The banner is documentation and may be reworded; the sentinel is contract and may not.

Each session block also carries a provenance comment, <!-- nerthus:sesja <hash> -->, holding the source session's content hash — the registry sits outside the integrity scan (below), so the hash is what lets a cheap check answer "is this mirror current" without scanning the tree.

The tree shape

The tree is driven by the location model, not by which sessions placed:

  • First level — one directory per outerior root: every distinct value the @outerior tags reference (tag schema), plus the reserved Nieprzypisane/ root.
  • Second level — one directory per active Lokacja carrying that @outerior. A location whose @outerior history names several roots keeps a directory under each, so pre-move sessions keep a home.
  • One Sesje.md per directory, empty ones included. A place with no sessions gets a banner-only file — a positive statement that the place is known and nothing has been recorded there yet. Empty and stale are different states (see Pruning).

Directory names are the entity names in canonical Polish — never transliterated, never slugged. Sanitization replaces / and NUL, strips trailing dots and spaces, and fails loudly if two distinct names would collapse into one segment.

Root resolution

Every session location token resolves through one total function — an answer or an explicit reason, never silence — in this order:

  1. Explicit Parent/Interior in the token: the parent, canonicalized through the alias fold table (KHKarka-han), must name a root in the universe. An unknown parent falls through to the interior's own @outerior before refusing (UnknownRoot).
  2. @outerior on the resolved Lokacja, as of the session's own date — the tag is temporal, and the registry respects a location that moves between roots.
  3. The from-date clamp: a session dated before its location's (YYYY-MM:) from-date still places under that root. The from-date records when the fact was first written down, not when it became true, so dropping those sessions would punish late bookkeeping. The report counts them separately (OuteriorClamped).
  4. Unplaced, with a closed reason: UnknownLocation, AmbiguousLocation, NoOuterior, or UnknownRoot.

A token that names a root itself places at the root's own Sesje.md. A root need not be an entity: a value like Pełne morze that real locations reference materializes as a root regardless, classified Tabela in the report; entity-backed roots classify as Lokacja, Mapa, or Region.

A session naming several locations places under the first token that resolves; the other roots it touched are recorded on the placement as SecondaryRoots. Registering the block under every root would multiply the duplication the registry exists to manage, and the tree would stop being a partition.

The unplaced pile

  • A session whose location line resolves to no root registers as a full block under Lokacje/Nieprzypisane/ — visible and actionable (fix the tag, or give the location an @outerior).
  • A session with no location line at all is counted in the report (NoLocation) and materializes nothing — a registry of places has nowhere honest to file it.

The accounting reconciles by contract: placed + registered-unplaced + no-location = sessions considered. The counts, split by source and by reason, are the regeneration response body — see the API reference.

Pruning — the one scoped physical delete

Everywhere else in the system removal is a @status: Usunięty transition, never a physical delete (architecture). The registry is the one scoped exception, because a derived tree must be able to shed directories its model no longer maps. The rule, exactly:

  1. Only files named Sesje.md, only under Lokacje/, are ever deleted — and only when they carry the machine sentinel.
  2. Empty is not stale. A live location with no sessions keeps its banner-only Sesje.md. Stale means the directory no longer maps to a location: its sentineled Sesje.md is deleted and the empty directory removed bottom-up.
  3. A directory holding any foreign file is never removed. The owned Sesje.md goes if the rule says so; the foreign content and the directory stay, and the case is reported (KeptForeign).
  4. Pruning runs only after the full intended tree wrote successfully — a failed write pass aborts with the written and pending sets in the error body, and the next regeneration repairs the tree.

The deletions are recoverable by construction: everything under Lokacje/ is a copy of a hand-authored original, so a pruning mistake costs a regeneration, not data.

Idempotency and determinism

Regenerating twice is byte-identical, and the guarantee is constructed, not observed:

  • every collection is sorted with an ordinal comparer — Polish diacritics collate differently per culture, and the same tree must come out of any host;
  • source-file enumeration order is never relied on: the session scanner sorts files ordinally within each root, and root order is intentional — Świat gry first, so a location's own chronicle outranks the character-diary copies in dedup (session model);
  • the rendered block is the winning source block verbatim minus the tool-managed @Pliki section — the registry copy is not a distribution target, and a mirrored @Pliki would read as a close-state signal;
  • no timestamps, counters, or host names appear in the output, and a file whose bytes already match is not rewritten.

The tree is materialized by adoption: POST /import generates it in the same pass that emits the index and stamps the schema version, and reports it back as SessionRegistry (API reference). From then on every model rebuild refreshes it through the same seam every write path already funnels through — gated like any lore write, and logged rather than raised, so a rebuild still completes. An explicit POST /session-registry/regenerate stays available as the repair: it answers with the same accounting and rewrites whatever drifted, on a repository whose tree was deleted, hand-mangled, or generated by a daemon older than the model it now serves.

What the tree is not

  • Not an input. The session scanner reads exactly four roots — Wątki/, Organizacje/, Postaci/, Świat gry/ (session metadata) — and Lokacje/ must never join them, though its files are named exactly what the scanner looks for. Adding it would give every session a second Files entry, and Test-NerthusSessionDistributed counts entries: the entire backlog would silently read as distributed-and-closed, reminders would stop, and the generated copy could win dedup and invert the direction of derivation. A test locks the four roots.
  • Not scanned, so not integrity-checked. The content-hash machinery never sees the tree; the per-block provenance hash is the staleness signal instead.
  • Not hand-editable in its Sesje.md files — though the directories accept any other file. This is the one place in the repository where overwriting a hand edit is the correct behavior.
  • Never an @ref target. A generated Sesje.md is overwritten wholesale, so a reference reading properties from one would be reading from a file the generator rewrites at will (entity model).
  • Not written by anything but the generator. Session authoring and distribution refuse any target resolving under Lokacje/ — a distributed copy inside the registry would be a foreign file the pruner must leave forever.

Cmdlet surface

Invoke-NerthusSessionRegistryRegenerate — routes, parameters, and the response shape are in the API reference.

See also