Skip to content

Naming discipline drift audit — Phase C input spec

Naming discipline drift audit — Phase C input spec

1. Summary

D-017 (canonised 2026-05-20, commit 7a1d31a) established the three-name discipline for all WalkRPG entities: every entity carries a bare EN handle (its primary key), a canonical PL name, and a canonical EN name, all registered in data/src/content/_glossary.ts. Phase A wave 1 (commit b03798e) shipped pnpm lint:naming in warn mode with three rules — filename-matches-handle, handle-in-glossary, and en-body-no-untracked-pl. Phase A wave 2 (commit 5dcac07) populated the glossary with 17 bootstrap entries, ~30 mechanical entries, and 11 Phase C pending entries, reducing the handle-in-glossary rule from 70 warnings to 0. This audit catalogues the 30 warnings still open after wave 2: 6 under filename-matches-handle (quest filename prefix divergence), 0 under handle-in-glossary (clean), and 24 under en-body-no-untracked-pl (PL tokens in EN body strings). It also enumerates the 11 glossary entries marked PENDING_PHASE_C_PICK that require a four-phase name pick from narrative-designer. Together, these form the complete input specification for Phase C.

2. Lint warn-list snapshot (current state)

Captured 2026-05-20. Run: pnpm lint:naming.

$ node scripts/lint-naming.mjs
lint:naming — WARN mode, exit 0
-- filename-matches-handle: 6 warning(s) --
data/src/content/quests/quest-001-first-road.ts:0 — filename-matches-handle — filename slug "quest-001-first-road" != id handle "001-first-road" (expected file "001-first-road.ts")
data/src/content/quests/quest-002-pieczetarnia-milczy.ts:0 — filename-matches-handle — filename slug "quest-002-pieczetarnia-milczy" != id handle "002-pieczetarnia-milczy" (expected file "002-pieczetarnia-milczy.ts")
data/src/content/quests/quest-003-dzwoniacy-magazyn.ts:0 — filename-matches-handle — filename slug "quest-003-dzwoniacy-magazyn" != id handle "003-dzwoniacy-magazyn" (expected file "003-dzwoniacy-magazyn.ts")
data/src/content/quests/quest-004-polowa-rejestru.ts:0 — filename-matches-handle — filename slug "quest-004-polowa-rejestru" != id handle "004-polowa-rejestru" (expected file "004-polowa-rejestru.ts")
data/src/content/quests/quest-005-niemilknacy-kurier.ts:0 — filename-matches-handle — filename slug "quest-005-niemilknacy-kurier" != id handle "005-niemilknacy-kurier" (expected file "005-niemilknacy-kurier.ts")
data/src/content/quests/quest-006-najstarsze-biuro.ts:0 — filename-matches-handle — filename slug "quest-006-najstarsze-biuro" != id handle "006-najstarsze-biuro" (expected file "006-najstarsze-biuro.ts")
-- handle-in-glossary: 0 warning(s) --
-- en-body-no-untracked-pl: 24 warning(s) --
data/src/content/npcs/kepka-witness-bookkeeper.ts:12 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...Pan Kępka..."
data/src/content/npcs/kepka-witness-bookkeeper.ts:22 — en-body-no-untracked-pl — en literal contains PL diacritic "ł" near: "...per to Janisław „Three-R..."
data/src/content/npcs/pine-half-century.ts:11 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...Wnęt Pine-Half..."
data/src/content/npcs/pine-half-century.ts:22 — en-body-no-untracked-pl — en literal contains PL diacritic "ó" near: "...e surname „Półwiek" was ..."
data/src/content/npcs/stary-marno-niedoczas.ts:23 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...He says „idę i z tego ż..."
data/src/content/quests/quest-003-dzwoniacy-magazyn.ts:58 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...sement pieczętarka on du..."
data/src/content/quests/quest-003-dzwoniacy-magazyn.ts:66 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...), the pieczętarka's one..."
data/src/content/quests/quest-004-polowa-rejestru.ts:29 — en-body-no-untracked-pl — en literal contains PL diacritic "ó" near: "... of one — „pójdziesz, po..."
data/src/content/quests/quest-004-polowa-rejestru.ts:68 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...le gives. Wnęt Sosnowy-P..."
data/src/content/quests/quest-004-polowa-rejestru.ts:85 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...ouse mark Wnęt copied on..."
data/src/content/quests/quest-004-polowa-rejestru.ts:106 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...trip with Wnęt's copied ..."
data/src/content/quests/quest-005-niemilknacy-kurier.ts:73 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...ing. The urzędnik at the..."
data/src/content/quests/quest-005-niemilknacy-kurier.ts:90 — en-body-no-untracked-pl — en literal contains PL diacritic "ń" near: "...g — „znam pańską obrączk..."
data/src/content/quests/quest-005-niemilknacy-kurier.ts:112 — en-body-no-untracked-pl — en literal contains PL diacritic "ó" near: "...for two kroków on either..."
data/src/content/quests/quest-005-niemilknacy-kurier.ts:138 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...e day — „dziękuję panu, ..."
data/src/content/quests/quest-006-najstarsze-biuro.ts:18 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "..., do kogo idę. Pytaj mni..."
data/src/content/quests/quest-006-najstarsze-biuro.ts:47 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...ahead. Pan Kępka — Janis..."
data/src/content/quests/quest-006-najstarsze-biuro.ts:68 — en-body-no-untracked-pl — en literal contains PL diacritic "ł" near: "...3 r. po Pomyłce". The pl..."
data/src/content/quests/quest-006-najstarsze-biuro.ts:84 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...Pan Kępka receive..."
data/src/content/quests/quest-006-najstarsze-biuro.ts:108 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "...Pan Kępka turns t..."
data/src/content/quests/quest-006-najstarsze-biuro.ts:130 — en-body-no-untracked-pl — en literal contains PL diacritic "ą" near: "...i Pod Wczesną Pieczęcią ..."
data/src/content/materials/bound-soot.ts:15 — en-body-no-untracked-pl — en literal contains PL diacritic "ó" near: "... the Drobnogórze pilgrim..."
data/src/content/materials/frostlands-honey.ts:15 — en-body-no-untracked-pl — en literal contains PL diacritic "ó" near: "...ower-Drobnogórze tunnels..."
data/src/content/opponents/frost-thing-slow-vent.ts:16 — en-body-no-untracked-pl — en literal contains PL diacritic "ę" near: "... below Pieczętarnia Mroz..."
Summary: 30 warning(s). Phase A acceptance: lint runs cleanly without crashing.
Files scanned: 70. Glossary entries known: 64.

Rule counts:

RuleWarnings
filename-matches-handle6
handle-in-glossary0
en-body-no-untracked-pl24
Total30

3. Drift categories — what Phase C must fix

3a. filename-matches-handle — quest prefix divergence (6 files)

All six quest files carry a quest-NNN- filename prefix. Their id-handles (the portion after the first dot in quest.<handle>) do not include that prefix. The lint rule expects the filename slug to equal the bare handle, so it fires for all six.

Eyes-on-file confirmation: every quest file verified. Each has id: QuestIdSchema.parse("quest.NNN-<slug>") where the bare handle is NNN-<slug> but the filename slug is quest-NNN-<slug>. The divergence is systematic and intentional — the prefix was added for file-tree readability, but the id-handle convention was set without it.

Quest id handleCurrent filenameWould rename to (option a)Keep as (option c)
001-first-roadquest-001-first-road.ts001-first-road.tsquest-001-first-road.ts (lint exempt)
002-pieczetarnia-milczyquest-002-pieczetarnia-milczy.ts002-pieczetarnia-milczy.tsquest-002-pieczetarnia-milczy.ts (lint exempt)
003-dzwoniacy-magazynquest-003-dzwoniacy-magazyn.ts003-dzwoniacy-magazyn.tsquest-003-dzwoniacy-magazyn.ts (lint exempt)
004-polowa-rejestruquest-004-polowa-rejestru.ts004-polowa-rejestru.tsquest-004-polowa-rejestru.ts (lint exempt)
005-niemilknacy-kurierquest-005-niemilknacy-kurier.ts005-niemilknacy-kurier.tsquest-005-niemilknacy-kurier.ts (lint exempt)
006-najstarsze-biuroquest-006-najstarsze-biuro.ts006-najstarsze-biuro.tsquest-006-najstarsze-biuro.ts (lint exempt)

Three resolution paths:

(a) Rename files to drop prefix. Filenames become 001-first-road.ts through 006-najstarsze-biuro.ts. Filename slug then matches id-handle exactly, rule passes with zero code changes. Cons: the quests/ directory loses the at-a-glance “this is a quest” visual cue; in a mixed content directory browsing is harder; any external references to the filename path (comments, wiki cross-refs, grep patterns) break. This is a real disruption for ~6 files now and compounds with every new quest.

(b) Strip the ordinal prefix from the id-handle. Handles become quest.first-road, quest.pieczetarnia-milczy, etc. Filename slug (quest-001-first-road) still does not match the new bare handle (first-road), so the rule would still fire unless files are also renamed. This option resolves nothing by itself and loses the ordinal anchor that canonically orders quests.

(c) Update the lint rule to recognise quest-NNN- as a canonical filename prefix for the quest schema kind. A one-line change in scripts/lint-naming.mjs: when the entity kind is quest, strip the leading quest- before comparing slug to handle. Filenames stay as they are; ids stay as they are; no content disruption; no cross-ref breaks. Cost: the rule becomes kind-aware rather than purely string-equal. If future schema kinds also need prefix handling, the pattern extends cleanly.

Recommendation: option (c). The quest-NNN- prefix is a meaningful ergonomic convention that makes the file tree self-documenting. Tooling should adapt to a well-established content convention, not the other way around. Option (a) causes immediate disruption with no semantic benefit; option (b) is strictly worse than (c) on every axis. Option (c) is a two-line script change. If CEO picks (a), the rename is mechanical and can be done by narrative-ops Sonnet; the qa-engineer would then verify all cross-refs in comments and wiki pages are updated (several files reference quest file paths by name in their JSDoc headers).

Note: Quest 001 (001-first-road) is the only quest with a fully completed transcreation note in the glossary (not PENDING_PHASE_C_PICK). Its filename-handle divergence is purely the quest- prefix issue — the handle itself is settled. Quests 002-006 have BOTH the filename divergence AND a PENDING_PHASE_C_PICK transcreation note (their handles are PL slugs awaiting rename). Section 3d covers those.

3b. handle-in-glossary — clean (0 warnings post-wave-2)

Pre-wave-2 this rule surfaced 70 warnings. Wave 2 (commit 5dcac07) populated the glossary with 64 unique bare handles covering all 70 entity files (6 kin-pair collisions where an item and its recipe share one bare handle, and a node and its mastery share another). Current state: 0 warnings. Glossary file: data/src/content/_glossary.ts. No Phase C action required for this rule — it is at zero.

3c. en-body-no-untracked-pl — loanword and proper-noun drift (24 warnings)

Each flagged token was verified against the entity file. Grouped by file, then by token, with category and Phase C handler.

File: data/src/content/npcs/kepka-witness-bookkeeper.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:12Kepka (PL: Kępka)"Pan Kępka" in role fieldHybrid name (PL surname)Glossary entry for handle kepka-witness-bookkeeper with four-phase EN pick; EN role field updated to use EN handle form once Phase C pick resolves the name. Also overlaps with 3d pending.
:22Janislaw (PL: Janislaw with ogonek l)"Janislaw \"Three-Receipts\" Mrzec" — lint fires on the l-with-stroke in the EN description fieldProper-noun personal name (Cholewa first name)The EN field uses Janislaw (diacritic-free) in the role string at line 79, but the description field at line 83 contains the PL-diacritic form. Phase C: replace the diacritic form with Janislaw (diacritic-stripped transliteration, matching the glossary entry for three-receipts). No new glossary entry needed — mechanical EN body edit.

File: data/src/content/npcs/pine-half-century.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:11Wnet (PL: Wn + ogonek-e + t)"Wnet Pine-Half-Century" in name.en (with PL diacritic)Hybrid name (PL first name particle in EN field)Pending Phase C pick for pine-half-century glossary entry. The EN field currently carries the PL-diacritic first name — the four-phase pick must decide whether to keep it as an exotic transliteration or replace with an EN equivalent (e.g. Knoll). Phase C task: complete four-phase pick, update name.en and glossary en field accordingly.
:22Polwiek (PL: P + o-acute + lwiek)"the surname [PL-form] was awarded" in description.enQuoted PL diegetic term (surname used in EN narration)The EN description explains the character’s surname by quoting it in PL. After the four-phase pick resolves the EN form of the surname, Phase C replaces the PL quote with the EN equivalent (e.g. "Half-Century") and a parenthetical. Category is quoted PL dialog by appearance but is more precisely a diegetic proper-noun that needs translation.

File: data/src/content/npcs/stary-marno-niedoczas.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:23idę (PL: i walk)"He says „idę i z tego żyję"" in description.enQuoted PL dialog — canonical character voiceThis is Marno’s defining line, his only reliably finished sentence. Translating it mechanically loses the flat delivery that makes it land. Recommend: append an EN gloss in the same string — "He says [PL: ide i z tego zyje] — I walk and this is what I live by". CEO flag in Open Questions (§5) is required before Phase C acts on this pattern.

File: data/src/content/quests/quest-003-dzwoniacy-magazyn.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:58pieczętarka"the basement pieczętarka on duty" in beat-4 step ENDiegetic loanword (Akademia institutional role)Per CEO ratification 2026-05-20: anglicise diegetic loanwords. pieczetarka (PL diacritic form flagged by lint) = seal-maker / sealing-clerk. EN translation: seal-clerk or seal-keeper (narrative-designer to confirm). Phase C: add glossary entry pieczetarkakind: loanword, en: "seal-clerk" (or chosen form); update all EN body occurrences.
:66pieczętarka"the pieczętarka's one-paragraph note" in beat-4 step ENSame as aboveSame handler — covered by the same glossary entry and EN body sweep.

File: data/src/content/quests/quest-004-polowa-rejestru.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:29pojdziesz (PL diacritic form)"„pójdziesz, popatrzysz, wrócisz, opowiesz"" in beat-1 step EN (Bertranda’s instruction)Quoted PL dialog — canonical character voiceBertranda’s half-finished sentence, a recurring structural motif. Like Marno’s line, translating removes diegetic texture. Recommend glossed-EN pattern with EN translation following the PL quote. CEO flag in Open Questions (§5).
:68Wnet (PL diacritic form Wnet with ogonek-e)"Wnet Sosnowy-Polwiek" in beat-3 step EN (PL diacritics in source)Hybrid name (same token as pine-half-century.ts)After Phase C pick for pine-half-century, this EN body occurrence is updated identically. Mechanical sweep once pick is made.
:85Wnet (PL diacritic form)"house mark Wnet copied onto" in beat-4 step ENHybrid nameSame — mechanical sweep after pick.
:106Wnet (PL diacritic form)"trip with Wnet's copied" in beat-5 step ENHybrid nameSame — mechanical sweep after pick.

File: data/src/content/quests/quest-005-niemilknacy-kurier.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:73urzednik (PL diacritic form urzedni+ogonek)"The urzędnik at the gate" in beat-2 step ENDiegetic loanword (municipal gate official)urzednik (diacritic form flagged) = official / gate clerk. EN: gate official or gate clerk. Phase C: add glossary entry urzednikkind: loanword, en: "gate official"; update EN body globally (token also appears in quest-001 steps).
:90panska (PL diacritic form)"„znam pańską obrączkę. Drewniana. Nowsza niż moja koperta."" in beat-3 step ENQuoted PL dialog — canonical character voiceMarno’s first line to the Walker. Formal-polite register (the honorific form) is load-bearing — establishes that Marno uses full formal address despite walking alone for 112 years. Recommend glossed-EN pattern. CEO flag (§5 Q2).
:112krokow (PL diacritic form)"for two kroków on either side" in beat-4 step ENDiegetic loanword (unit of measure)krok (genitive plural krokow with diacritic) = step/pace, the canonical WalkRPG unit. EN: strides or paces. Phase C: add glossary entry krokkind: loanword, en: "pace"; update EN body.
:138dziekuje (PL diacritic form)"„dziękuję panu, że nie zwolnił."" in beat-5 step ENQuoted PL dialog — canonical character voiceMarno’s final sentence to the Walker. Formal-polite register. Recommend glossed-EN pattern. CEO flag (§5 Q2).

File: data/src/content/quests/quest-006-najstarsze-biuro.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:18ide (PL diacritic form)"nie pytaj mnie, do kogo idę" in description.en (PL diacritic flagged)Quoted PL dialog — canonical character voiceMarno’s central forward-bearing sentence; the thematic hinge of quests 005-006. Recommend same glossed-EN pattern. CEO flag (§5 Q2).
:47Kepka (PL diacritic form)"Pan Kępka — Janislaw" in beat-1 step ENHybrid nameAfter Phase C pick for kepka-witness-bookkeeper, all occurrences of the PL-diacritic form in EN body are updated to the resolved EN form. Six occurrences across quest-006. Mechanical sweep after pick.
:68Pomylce (PL diacritic form, genitive of Pomylka)"33 r. po Pomyłce" — the brass plate text in beat-2 step ENProper-noun place/era namepomylka (diacritic: Pomylka with o-acute) = the canonical era-marking event, literally “the Mistake”. A proper-noun era name used on dated plaques. Phase C: add glossary entry pomylkakind: place, authorise the PL form as a proper-noun loan (like “Anno Domini”) OR anglicise to “the Mistake” — CEO call (§5 Q3).
:84Kepka (PL diacritic form)"Pan Kępka receive" in beat-3 step ENHybrid nameSame sweep as :47.
:108Kepka (PL diacritic form)"Pan Kępka turns" in beat-4 step ENHybrid nameSame sweep as :47.
:130Piecz + ecia with PL diacritics"i Pod Wczesna Pieczecia" in beat-3 step EN (PL diacritics flagged)Diegetic proper-noun — dissolved courier-house namedom-pod-wczesna-pieczecia (diacritic form: Dom Kurierski Pod Wczesna Pieczecia) is the canonical name of the dissolved courier-house revealed in quest-006. A proper-noun institutional name; the Cholewa double-reading (sealed too soon / early sign) is cited as canonical. Phase C: add glossary entry dom-pod-wczesna-pieczeciakind: place, en: "The Courier House of the Early Seal" (or narrative-designer’s pick). Register so lint authorises it as a proper-noun in EN body.

File: data/src/content/materials/bound-soot.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:15Drobnogórze"the Drobnogórze pilgrim route" in description.enProper-noun place name (toponym)Drobnogórze (diacritic-form; handle: drobnogórze) is a named geographic area in the Frostlands (drobne gory = small mountains / smallcrags). Per toponym-preservation rule (2026-05-18), proper-noun place names are preserved in PL with pl == en. Phase C: add glossary entry drobnogórzekind: place, pl == en, note covers toponym-preservation rule.

File: data/src/content/materials/frostlands-honey.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:15Drobnogórze"lower-Drobnogórze tunnels" in description.enProper-noun place name (toponym)Same token and same handler as bound-soot. One glossary entry covers both occurrences.

File: data/src/content/opponents/frost-thing-slow-vent.ts

Lint refToken in EN bodyExact EN contextCategoryPhase C handler
:16Pieczetarnia (PL diacritic form)"below Pieczętarnia Mroza" in description.enProper-noun place name (toponym)pieczetarnia-mroza (handle; diacritic form flagged by lint) = the Seal-House of the Frost, a named Frostlands location introduced in quest-002. Proper-noun institutional toponym. Phase C: add glossary entry pieczetarnia-mrozakind: place, pl == en (toponym preservation).

Unique PL tokens summary by category

CategoryUnique tokensFiles affectedPhase C action type
Proper-noun place name (toponym)drobnogórze, pieczetarnia-mroza, pomylka (handles; PL diacritic forms in EN body)3 filesGlossary entry (kind: place, pl == en), lint passes
Proper-noun institutional namedom-pod-wczesna-pieczecia (handle; PL diacritic form in EN body)1 file (quest-006)Glossary entry (kind: place), EN body may keep PL form with glossary authorisation
Diegetic loanwordpieczetarka, urzednik, krok / krokow (handles; PL diacritic forms in EN body)3 filesGlossary entry (kind: loanword) + EN body mechanical sweep
Quoted PL dialogFive Marno/Bertranda character-voice lines (PL diacritic forms in EN body)4 filesCEO call first (§5 Q2); then glossed-EN pattern
Hybrid name particleWnet / Kepka / Janislaw (handles; PL diacritic forms in EN body)3 filesFour-phase pick resolves first; mechanical sweep after

Total unique-token handlers: 12 distinct token types driving 24 warnings (some tokens appear multiple times across files/lines).

3d. Phase C pending registry (cross-check with glossary)

STATUS — PICKED 2026-05-20: all 11 four-phase picks landed on narrative-designer side. Decision log: ops/decisions/2026-05-20-phase-c-four-phase-picks.md. Glossary updated: PENDING_PHASE_C_PICK markers cleared, new handles registered, supersedes field populated for audit trail. The mechanical Phase C wave (entity file renames + EN body sweeps + loanword/toponym entries) is the follow-up.

NPCs (6 picked):

Old bare handleNew bare handleNew constAnchor typeStatus
stary-marno-niedoczasundelivered-courierundeliveredCourierquirk (cosmic)PICKED
brodek-spoznionyhalf-sentence-sealerhalfSentenceSealerquirk (diction)PICKED
pan-bezelecunopened-door-wardenunopenedDoorWardenquirk (Pratchett-paradox)PICKED
kepka-witness-bookkeeperhouse-mark-readerhouseMarkReaderquirk (action)PICKED
pine-half-centuryhalf-century-smolarzhalfCenturySmolarzsurname-honour + hybrid loanwordPICKED
full-sentence-elderfull-sentence-elderfullSentenceElderquirk (diction)VERIFIED (no rename)

Quests (5 picked):

Old bare handleNew bare handleNew constAnchor typeStatus
002-pieczetarnia-milczy002-silent-seal-housequest002SilentSealHousedescriptor compressedPICKED
003-dzwoniacy-magazyn003-ringing-warehousequest003RingingWarehousedescriptor literalPICKED
004-polowa-rejestru004-half-registerquest004HalfRegisterdescriptor compressedPICKED
005-niemilknacy-kurier005-undelivered-loopquest005UndeliveredLoopquirk (structural)PICKED
006-najstarsze-biuro006-oldest-officequest006OldestOfficedescriptor literalPICKED

Phase C picks landed — summary

The 11 picks honour the bound parameters from auto-mode resolution of Phase A Q1/Q2/Q3:

  • Q1 (option c) — quest filenames will keep the quest-NNN- prefix; new bare handles are NNN-<eng-slug>; full IDs are quest.NNN-<eng-slug>. Lint will be updated by tech-architect to accept the prefix.
  • Q2 (option a STRICT) — Marno’s PL character-voice quotes get translated in EN body in the mechanical wave; the handle picks do NOT anchor on PL phrases that the EN reader will never see (e.g. Marno’s handle does NOT use „idę i z tego żyję" as anchor).
  • Q3 — toponyms get kind:place + pl==en (mechanical wave); diegetic loanwords (smolarz, pieczetarka, urzednik, krok) get kind:loanword + EN translation (mechanical wave). The house-mark-reader and half-century-smolarz picks integrate cleanly with these.

Three structural notes:

  1. Quirk-over-role consistency. Five of six NPC picks use the quirk-anchor pattern (D-013/D-006/canonical-fact anchors). Only full-sentence-elder was verified rather than renamed — its existing handle IS the quirk-anchor; the verification documents the rationale and lifts the PENDING marker.

  2. Pratchett-paradox compound shape. Three NPC handles (undelivered-courier, unopened-door-warden, half-sentence-sealer) use the same negated-compound shape as faction.unfinished-guild and faction.quiet-lights — the institution/character defined by what it does not do or cannot do.

  3. Chain-resonance. npc.undelivered-courierquest.005-undelivered-loop makes the Marno-Walker chain visible at the ID level; npc.half-sentence-sealernpc.full-sentence-elder makes the Frostlands diction-axis pairing visible. The 18.05 rule (deeper anchor over visible symptom) drove both pairings.

Alternatives considered (preserved in the decision log per 18.05 audit-trail discipline):

  • Marno: unstopping-courier (body-fact, lost on depth) / looped-courier (spatial-fact, lost on slot fit).
  • Brodek: belated-magister (Cholewa surname) / rotation-sealer (role).
  • Pan Bezelec: night-warden (role) / without-light-warden (Cholewa surname).
  • Pan Kępka: witness-bookkeeper (role) / unslept-witness (body-fact).
  • Wnęt: half-century-resin-burner (fully-EN) / flood-survivor-smolarz (plot-fact).
  • Trudka: wykuwisko-four-elder (role) / iron-walls-elder (Quest-002-outcome).
  • Quest 002: 002-seal-house-falls-silent (verbose) / 002-listening-post (threshold not destination).
  • Quest 003: 003-warehouse-seventeen (toponym) / 003-stamps-strike (verb-phrase).
  • Quest 004: 004-half-of-the-register (verbose) / 004-resin-trail (mechanism not fact).
  • Quest 005: 005-unhushed-courier (literal, no chain) / 005-companion-walk (mechanical).
  • Quest 006: 006-mokrych-stempli (toponym, lore-split) / 006-wczesna-pieczec (content not location).

Cross-reference: 3c warnings overlapping with 3d pending:

The following files appear in BOTH the en-body-no-untracked-pl list (§3c) AND the Phase C pending registry (§3d), meaning one Phase C handler can resolve both:

  • kepka-witness-bookkeeper.ts — the PL-diacritic Kepka token in EN body (§3c, 2 warnings) and the kepka-witness-bookkeeper pending four-phase pick (§3d) are resolved together. Once the four-phase pick settles the EN handle and EN name form, the EN body occurrences are updated in the same pass.
  • pine-half-century.ts — the PL-diacritic Wnet and Polwiek tokens in EN body (§3c, 2 warnings) are resolved when the four-phase pick for pine-half-century settles the EN first-name form and surname translation.
  • quest-004-polowa-rejestru.ts — three PL-diacritic Wnet EN body warnings (§3c) are resolved mechanically after the pine-half-century pick settles.
  • quest-005-niemilknacy-kurier.ts — the quoted-dialog tokens (PL diacritics in Bertranda / Marno character-voice lines) and the quest-005 pending handle rename (§3d) can be addressed in a single Phase C pass.
  • quest-006-najstarsze-biuro.ts — PL-diacritic Kepka EN body occurrences (§3c, 3 warnings) and the PL-diacritic era-name Pomylka (§3c, 1 warning) are both in this file, which is also a pending handle rename (§3d). One pass resolves all four issues plus the handle rename.

In general: for quests 002-006, the Phase C rename pass that converts the PL slug handle to an EN handle also creates the opportunity to sweep the EN body strings in the same commit, minimising file-churn.

4. Phase C work breakdown (ordered tasks for narrative-designer)

Tasks are ordered with dependency-first sequencing. Four-phase picks (narrative-designer Opus) must precede mechanical sweeps (narrative-ops Sonnet) that depend on the picked EN form.

handle: drobnogórze-glossary-entry
kind: glossary-entry
scope: data/src/content/_glossary.ts (1 entry added)
acceptance: glossary entry for bare handle "drobnogórze" with kind:place, pl==en, transcreationNote covers toponym-preservation rule; pnpm lint:naming passes for bound-soot.ts:15 and frostlands-honey.ts:15
source: §3c, bound-soot.ts + frostlands-honey.ts
status: todo
handle: pieczetarnia-mroza-glossary-entry
kind: glossary-entry
scope: data/src/content/_glossary.ts (1 entry added)
acceptance: glossary entry for "pieczetarnia-mroza" with kind:place, pl==en, transcreationNote; pnpm lint:naming passes for frost-thing-slow-vent.ts:16
source: §3c, frost-thing-slow-vent.ts
status: todo
handle: pomylka-era-name-glossary-entry
kind: glossary-entry
scope: data/src/content/_glossary.ts (1 entry) + data/src/content/quests/quest-006-najstarsze-biuro.ts (EN body edit or authorised-loanword exemption)
acceptance: CEO picks translation vs preservation; glossary entry created; quest-006:68 warning resolved
source: §3c, quest-006:68; §5 Q3
status: blocked-ceo (§5 Q3)
handle: pieczetarka-loanword-glossary-entry
kind: glossary-entry
scope: data/src/content/_glossary.ts (1 entry)
acceptance: glossary entry for "pieczetarka" with kind:loanword and an EN translation (e.g. "seal-clerk"); narrative-designer confirms EN term; entry passes lint
source: §3c, quest-003:58,66
status: needs-narrative-designer-en-pick
handle: urzednik-loanword-glossary-entry
kind: glossary-entry
scope: data/src/content/_glossary.ts (1 entry)
acceptance: glossary entry for "urzednik" with kind:loanword, EN translation (e.g. "gate official"); narrative-designer confirms
source: §3c, quest-005:73
status: needs-narrative-designer-en-pick
handle: krok-loanword-glossary-entry
kind: glossary-entry
scope: data/src/content/_glossary.ts (1 entry)
acceptance: glossary entry for "krok" with kind:loanword, EN translation (e.g. "pace" or "stride"); narrative-designer confirms canonical EN unit name; quest-005:112 passes
source: §3c, quest-005:112
status: needs-narrative-designer-en-pick
handle: dom-pod-wczesna-pieczecia-glossary-entry
kind: glossary-entry
scope: data/src/content/_glossary.ts (1 entry)
acceptance: glossary entry for "dom-pod-wczesna-pieczecia" with kind:place; EN form decided (preserve PL proper-noun or translate); quest-006:130 passes
source: §3c, quest-006:130
status: needs-narrative-designer-en-pick
handle: janislaw-en-body-sweep
kind: mechanical-edit
scope: data/src/content/npcs/kepka-witness-bookkeeper.ts (replace Janisław with Janislaw in description.en)
acceptance: kepka-witness-bookkeeper.ts:22 lint warning resolved; no other file affected (other EN body uses already use diacritic-free form)
source: §3c, kepka-witness-bookkeeper.ts:22
status: todo (no pick needed; diacritic-stripped form is canonical per three-receipts glossary entry)
handle: quoted-pl-dialog-ceo-decision
kind: ceo-call
scope: see §5 Q2
acceptance: CEO rules on glossed-EN-with-parenthetical vs strict-anglicise for all five quoted-dialog tokens; decision recorded
source: §3c (5 quoted-dialog warnings across stary-marno-niedoczas.ts, quest-004.ts, quest-005.ts x2, quest-006.ts)
status: blocked-ceo (§5 Q2)
handle: full-sentence-elder-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts (update transcreationNote, confirm or replace EN handle); data/src/content/npcs/full-sentence-elder.ts if EN handle changes
acceptance: PENDING_PHASE_C_PICK marker removed; four-phase provenance confirmed; pnpm lint:naming handle-in-glossary stays at 0
source: §3d, full-sentence-elder
status: todo
handle: stary-marno-niedoczas-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts (new EN handle, remove PENDING marker); data/src/content/npcs/stary-marno-niedoczas.ts (id rename); all files that reference npc.stary-marno-niedoczas
acceptance: PENDING_PHASE_C_PICK removed; EN handle chosen from candidates; id in entity file updated; cross-references updated; if quoted-dialog decision is made (§5 Q2), stary-marno-niedoczas.ts:23 also resolved
source: §3d; §3c (overlapping quoted-dialog warning)
status: blocked-ceo (§5 Q2 first for dialog sweep)
handle: brodek-spozniony-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts; data/src/content/npcs/brodek-spozniony.ts (id rename); cross-references
acceptance: PENDING_PHASE_C_PICK removed; EN handle chosen; file renamed if CEO picks option (a) or (c) for quest prefix (not applicable here — NPC files do not have the prefix issue, so rename is unconditional)
source: §3d
status: todo
handle: pan-bezelec-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts; data/src/content/npcs/pan-bezelec.ts (id rename); cross-references
acceptance: PENDING_PHASE_C_PICK removed; EN handle chosen from candidates
source: §3d
status: todo
handle: kepka-witness-bookkeeper-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts; data/src/content/npcs/kepka-witness-bookkeeper.ts (id, name.en); all EN body occurrences of Kępka in quest-006 (5 occurrences)
acceptance: PENDING_PHASE_C_PICK removed; EN handle fully EN; kepka-witness-bookkeeper.ts:12 warning resolved; quest-006 Kępka sweep complete
source: §3d + §3c (kepka-witness-bookkeeper.ts:12; quest-006:47,84,108)
status: todo
handle: pine-half-century-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts; data/src/content/npcs/pine-half-century.ts (name.en, id if handle changes); all EN body occurrences of Wnęt across quest-004 (3 occurrences) and pine-half-century.ts; Półwiek quoted reference in pine-half-century description.en
acceptance: PENDING_PHASE_C_PICK removed; pine-half-century.ts:11,22 resolved; quest-004:68,85,106 resolved
source: §3d + §3c (pine-half-century.ts:11,22; quest-004:68,85,106)
status: todo
handle: quest-002-handle-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts (update handle, remove PENDING marker); data/src/content/quests/quest-002-pieczetarnia-milczy.ts (id field; filename under CEO option a/c); all prerequisites referencing quest.002-pieczetarnia-milczy
acceptance: PENDING_PHASE_C_PICK removed; EN handle chosen; prerequisite references updated; filename resolved per CEO option
source: §3d; §3a
status: blocked-ceo (§5 Q1 for filename option)
handle: quest-003-handle-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts; quest-003 file (id + filename option); pieczętarka EN body sweep (2 warnings in same file, done in same pass); prerequisite references
acceptance: PENDING_PHASE_C_PICK removed; quest-003:58,66 resolved; filename resolved per CEO option
source: §3d + §3c (quest-003:58,66); §3a
status: blocked-ceo (§5 Q1) + needs pieczetarka loanword entry first
handle: quest-004-handle-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts; quest-004 file (id + filename option); Wnęt sweep in quest-004 (after pine-half-century pick); pójdziesz quoted-dialog (after CEO Q2); prerequisite references
acceptance: PENDING_PHASE_C_PICK removed; filename resolved; quest-004:68,85,106 resolved (after pine-half-century pick); quest-004:29 resolved (after CEO Q2)
source: §3d + §3c (quest-004:29,68,85,106); §3a
status: blocked-ceo (§5 Q1 + Q2) + depends on pine-half-century pick
handle: quest-005-handle-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts; quest-005 file (id + filename option); urzednik EN sweep; krok EN sweep; quoted-dialog sweep (after CEO Q2); prerequisite references
acceptance: PENDING_PHASE_C_PICK removed; quest-005:73,112 resolved; quest-005:90,138 resolved (after CEO Q2); filename resolved
source: §3d + §3c (quest-005:73,90,112,138); §3a
status: blocked-ceo (§5 Q1 + Q2) + depends on urzednik and krok loanword entries
handle: quest-006-handle-four-phase-pick
kind: four-phase-pick
scope: data/src/content/_glossary.ts; quest-006 file (id + filename option); Kępka sweep (after kepka pick); Pomyłka resolution (after CEO Q3); dom-pod-wczesna-pieczecia entry; idę quoted-dialog (after CEO Q2); prerequisite references
acceptance: PENDING_PHASE_C_PICK removed; quest-006:47,68,84,108,130 resolved; idę warning resolved; filename resolved
source: §3d + §3c (quest-006:18,47,68,84,108,130); §3a
status: blocked-ceo (§5 Q1 + Q2 + Q3) + depends on kepka pick + dom-pod-wczesna-pieczecia entry

Task count summary: 20 tasks total (7 glossary-entry tasks, 1 mechanical EN body edit, 1 CEO call, 11 four-phase picks). Of the 11 picks, 6 are NPC picks and 5 are quest handle picks. Dependency graph: CEO decisions in §5 unblock the quoted-dialog tasks and the filename option; glossary entries for loanwords unblock the quest handle sweeps; NPC four-phase picks unblock EN body sweeps in quest files that reference those NPCs.

5. Open questions for CEO

Q1 — Quest filename prefix divergence (§3a)

The quest-NNN- prefix in all six quest filenames causes the filename-matches-handle rule to fire for all six. Three options:

  • (a) Rename quest files to drop prefix. quest-001-first-road.ts001-first-road.ts. Zero lint code change. Real cost: file-tree readability lost; all JSDoc cross-references to quest file paths by name must be swept (at least 12 occurrences across data files referencing each other in headers); every future quest authored without the prefix loses the self-documenting convention.
  • (b) Strip ordinal from id-handle. quest.001-first-roadquest.first-road. Still does not fix the filename mismatch without also renaming; loses canonical quest ordering from the id itself. Strictly worse than (a).
  • (c) Update lint rule to recognise quest-NNN- prefix as canonical for the quest schema kind. Two-line script change in scripts/lint-naming.mjs. No content, no id, no file renamed. Tooling adapts to an established and useful convention.

Recommendation: (c). The prefix is ergonomic and correct; the lint rule should be kind-aware. If CEO prefers (a), qa-engineer will scope the cross-reference sweep before Phase C executes it.

Q2 — Quoted PL dialog in EN body strings

Five quoted PL lines appear in EN body fields: Bertranda’s go-look-return instruction, and four Marno lines (I-walk-and-live-by-it, I-know-your-ring, thank-you-for-not-slowing, and the central do-not-ask-who-I-am-going-to sentence). These are canonical character-voice lines whose register (formal Polish, flat delivery, incomplete sentences) is diegetically significant.

  • (a) Strict anglicise. Replace PL quoted forms with EN translation only. Lint passes. Flavour loss: the PL quoted form is what the player sees in the game EN build; removing it makes the character speak standard English rather than a character with a distinct diegetic voice.
  • (b) Glossed EN with parenthetical PL original. Append an EN gloss immediately after the PL quote in the same string. The PL form is preserved inline as flavour; the EN gloss follows. Lint rule may need a glossed-inline exemption pattern, or the quoted PL can be moved to a flavour field (which is separate from the description.en body the rule scans). This preserves the diegetic register at the cost of a small lint-rule extension.
  • (c) Move quoted PL to flavour field only. The en description body uses EN-only narration; the original PL quote lives in flavour.en as the canonical character-voice artefact. Lint rule does not scan flavour fields (confirm with tech-architect). Clean separation; no lint extension needed.

Recommendation: (b) if the lint rule can be extended to allow inline-glossed PL quotes. (c) if rule extension is too costly. This is a flavour-loss tradeoff that is the CEO’s call — the audit surfaces it, does not decide it.

Q3 — pomylka era-name treatment in EN body

The PL-diacritic era name Pomylka (handle: pomylka) appears in the quest-006 beat-2 brass-plate text. It is the canonical era-marking event name used on dated plaques throughout Plenny. Two options:

  • (a) Toponym-preservation. Register pomylka as a proper-noun era name in the glossary (kind: place, pl == en), making it an authorised loanword in EN body. The EN description keeps "Year 33 after Pomylka" (PL form as proper noun, analogous to how Plenny, Targosie, and Mglica are preserved).
  • (b) Anglicise. Register as loanword with EN form "the Mistake". EN body reads "Year 33 after the Mistake". Cleaner for EN players; loses the in-world linguistic texture of a city that dates years in its own language even on brass plaques.

Recommendation: (a), for internal consistency with the toponym-preservation rule. The PL era-name form functions as an era-name-as-proper-noun the same way “Anno Domini” functions in English — the PL form IS the canonical reference, not a translation of it. CEO to confirm.

6. Acceptance criteria for Phase C completion

Phase C is complete when ALL of the following pass:

  1. pnpm lint:naming total warnings: 0. Zero warnings under all three rules. The only permitted exception is a documented exemption registered in scripts/lint-naming.mjs comments (e.g. if CEO selects option (c) for quest prefix, the rule is updated to emit 0 rather than 6 warnings; that counts as 0).

  2. data/src/content/_glossary.ts has zero PENDING_PHASE_C_PICK markers. Grep: grep -r "PENDING_PHASE_C_PICK" data/src/content/_glossary.ts returns empty. All 11 pending entries have been resolved with a completed four-phase pick; each entry has a transcreationNote that documents the pick rationale without the pending token.

  3. All quest file slugs match their bare handles under whichever resolution CEO picks. If option (a): filenames 001-*.ts through 006-*.ts. If option (c): lint rule updated, filenames unchanged, rule passes at 0. Verified by running pnpm lint:naming with no filename-matches-handle warnings.

  4. All entity file IDs reference handles that exist in the glossary. pnpm lint:naming rule handle-in-glossary at 0. Already at 0; Phase C must not regress this by adding new entity files without glossary entries.

  5. All EN body strings either pass the diacritic check or reference a glossary entry that authorises the token. pnpm lint:naming rule en-body-no-untracked-pl at 0. This requires: (a) all loanword glossary entries added, (b) all EN body strings updated or authorised, (c) all quoted-dialog warnings resolved per CEO Q2 decision, (d) all hybrid-name EN body occurrences updated after four-phase picks complete.

  6. pnpm lint:language passes. The new glossary entries and EN body edits must not introduce PL text into ENG-only paths or vice versa. Run pnpm lint:language after each Phase C commit batch.

  7. pnpm lint:tags passes. No Phase C action is expected to touch wiki pages, but any wiki edits (e.g. lore pages referencing renamed quest handles) must carry valid frontmatter tags.

  8. pnpm --filter @walkrpg/data typecheck passes. Schema validation still passes after all entity file id-renames (id field must remain valid per QuestIdSchema, NpcIdSchema, etc.).

  9. Cross-reference integrity. Any entity file that lists another in its prerequisites array (quests) or JSDoc header must reference the current id/handle post-rename. Grep for the old PL handles in all data/src/content/ files and confirm 0 stale references after each rename batch.


Stop-flag-ask findings

The following were observed during the audit that are outside the scope of the three lint rules and D-017, and require routing before Phase C acts:

SF-1 — kepka-witness-bookkeeper.ts uses the PL-diacritic form of a first name in description.en (line 83) but the diacritic-free transliteration in role.en (line 79). The inconsistency is within a single EN body. The lint catches the diacritic occurrence in description.en. Phase C can fix this mechanically (replace the PL-diacritic form with the diacritic-stripped transliteration in description.en), but the presence of an inconsistency suggests the EN body sweep for NPC first names that appear in other NPC descriptions needs to be systematic, not piecemeal. Routing to: qa-engineer can handle the mechanical sweep; no CEO input needed, but narrative-ops Sonnet should run a grep for all first-name diacritics in EN body fields after the four-phase picks resolve.

SF-2 — quest-006-najstarsze-biuro.ts beat-2 uses a direct PL plaque quotation inside an EN body string. The plaque text includes WOLNOKUPIECTWO TARGOSIE — Biuro Najstarsze, 33 r. po Pomylce (PL with one diacritic flagged). The lint fires only on the diacritic character, missing the WOLNOKUPIECTWO TARGOSIE — Biuro Najstarsze portion (no diacritics in those words, but they are PL institutional names). This means the lint rule’s diacritic-detection heuristic does not catch PL text that happens to use only ASCII characters. This is a gap in the rule’s coverage — a broader approach (e.g. detecting known PL institutional names in EN body strings) would require a significant lint rule expansion. Flagged here for tech-architect or qa-engineer review of the lint rule scope; not a Phase C content task.

SF-3 — RESOLVED (false alarm; system-reviewer cross-check confirmed). The entity file data/src/content/npcs/full-sentence-elder.ts exists; line 9 carries id: NpcIdSchema.parse("npc.full-sentence-elder"). The handle is correctly registered in the glossary AND the entity file is on disk. The PENDING_PHASE_C_PICK marker stands for four-phase provenance verification only (the handle reads clean but its derivation is undocumented in the 18.05 decision) — Phase C still owes a verification pass, but there is no missing-file gap. No routing action required.