Files
code_of_conquest_dnd/docs/superpowers/plans/2026-07-12-duncarrow-purge.md
2026-07-12 15:22:01 -05:00

26 KiB
Raw Blame History

Duncarrow Purge Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Delete the Duncarrow scaffolding content while preserving the schema/disposition-gate infrastructure it carries — via a new mechanics.md (the ladder's home) and a synthetic status: candidate specimen — and repoint the skill, tests, and evals accordingly. Zero real game content.

Architecture: All changes are content (content/lore/*.md + regenerated JSON) and skill docs/tests. The content_build tool compiles content/lore/*.mdcontent/world/ + content/server/. Because write_trees only writes expected files and never deletes, removing a source .md leaves orphaned JSON that --check rejects — so orphaned outputs must be git rm'd explicitly (this plan enumerates them exactly).

Tech Stack: Markdown lore bibles with ```yaml blocks; the content_build Python tool (run with PYTHONPATH=tools); the repo venv (.venv); pytest.

Global Constraints

  • Charter §2 — code owns state, AI owns text. Content + docs only. No mechanic, no schema change, no runtime code.
  • The disposition-ladder is required exactly once across all bibles. It moves from duncarrow.md to mechanics.md — never duplicated (would hard-fail the build) and never absent (would hard-fail the build). The move + the Duncarrow deletion land in the SAME commit (Task 2).
  • The specimen is status: candidate → validated by --check but emitted nowhere. No specimen JSON may appear under content/world/ or content/server/.
  • Every canon entity is secrecy: 0; every related id must resolve.
  • NEVER hand-edit generated JSON to make --check pass — fix the source. Removing generated JSON for a deleted source is the exception and is done with git rm (enumerated below), not by editing.
  • Do NOT touch the cosmology bibles (cosmology.md, the-seven.md), the legacy hand-authored NPCs (content/world/npcs/{brannoc_thane,cadwyn_vell,fenn}.json), or the hand-authored POC seed JSON (content/origins/, content/world/quests/, content/world/locations/, content/world/items/).
  • Git: all work on branch feature/duncarrow-purge off dev. Never touch master. Merge to dev only after the human confirms and the build is green.

Build/verify commands (used by every task, from repo root):

PYTHONPATH=tools .venv/bin/python -m content_build          # regenerate world/ + server/ from lore/
PYTHONPATH=tools .venv/bin/python -m content_build --check   # must exit 0
PYTHONPATH=tools .venv/bin/python -m pytest tools/content_build/tests -q

File structure

File Responsibility Action
content/lore/specimen.md Synthetic candidate disposition-gate teaching example (place + guarded person/npc + one warm-gated fact) Create (Task 1)
content/lore/mechanics.md Home for mechanical rule.*; hosts rule.disposition-ladder (moved verbatim) Create (Task 2)
content/lore/duncarrow.md The scaffolding Delete (Task 2)
23 generated JSON (enumerated) Orphaned Duncarrow outputs git rm (Task 2)
tools/content_build/tests/test_duncarrow.py Real-content acceptance test (redundant with test_emit.py) Delete (Task 2)
tools/content_build/tests/test_model.py Cosmetic npc.mera-fenn slug-test id Edit (Task 2)
.claude/skills/world-building/SKILL.md, references/schema.md, references/tone.md, evals/evals.json Repoint specimen→structure, cosmology→voice; rewrite 3 evals Edit (Task 3)
content/lore/canon-roadmap.md Record the purge Edit (Task 4)

Coverage note (why the test deletion is safe): test_emit.py already proves the routing/secrecy behaviors on the synthetic fixture fixtures/valid/world.mdtest_secret_body_only_server_side (secret body server-only), test_npc_skeleton_has_no_persona_client_side, and test_candidate_entries_are_excluded_from_both_trees (the exact mechanism that keeps the candidate specimen out of both trees). test_duncarrow.py re-proved these on real content plus a content-correctness assertion about deleted entities, so it is removed rather than migrated. "Real content builds clean" remains covered by the CI --check gate and Task 4's final verification.


Task 0: Branch (controller)

  • Step 1: Create the working branch off dev
git checkout dev && git pull --ff-only
git checkout -b feature/duncarrow-purge
  • Step 2: Confirm a green baseline
PYTHONPATH=tools .venv/bin/python -m content_build --check && echo "baseline green"

Expected: exits 0.


Task 1: The synthetic specimen — content/lore/specimen.md

Files:

  • Create: content/lore/specimen.md

Interfaces:

  • Produces the candidate ids place.specimen-wharf, person.specimen-teague, fact.specimen-foreign-coin, npc.specimen-teague. Its gate references rule.disposition-ladder (currently in duncarrow.md; moves to mechanics.md in Task 2 — resolves either way because the build parses all content/lore/*.md together).

  • Emits NOTHING (all entries status: candidate).

  • Step 1: Write the file exactly

Create content/lore/specimen.md with this content verbatim:

# SPECIMEN — format example, NOT game canon

> A synthetic disposition-gate specimen: the shape a guarded-then-warm NPC and a
> single earned personal reveal take in the bible. Authored `status: candidate`,
> so the build VALIDATES it (`--check` resolves its gate against the ladder) but
> emits NOTHING to `content/world` or `content/server`. This is a teaching
> example for the world-building skill, not part of the Margreave. Do not wire
> real content to these ids.

```yaml
id: place.specimen-wharf
type: place
status: candidate
secrecy: 0
related: []
body: >
  A generic river wharf, here only to anchor the specimen NPC. Not a real place
  in the Margreave.
```

```yaml
id: person.specimen-teague
type: person
status: candidate
secrecy: 0
related: [place.specimen-wharf]
body: >
  Teague, oldest hand on the wharf. Mends nets he no longer sails with. Watches
  everything off the water and says almost none of it.
```

```yaml
id: fact.specimen-foreign-coin
type: fact
status: candidate
secrecy: 2
related: [place.specimen-wharf, person.specimen-teague]
body: >
  The dock hands are paid in coin from outside the charter. Someone is buying the
  wharf's silence, a little at a time.
```

```yaml
id: npc.specimen-teague
type: person
status: candidate
secrecy: 0
start_disposition: cold
related: [person.specimen-teague, fact.specimen-foreign-coin]
body: >
  Same man as person.specimen-teague; his interactive layer. Curt with strangers,
  not from malice but from a lifetime of watching talkers end up face-down in the
  river. Warms slowly, and when he does the dryness turns to something almost
  fond.

knows:
  - {fact: fact.specimen-foreign-coin, gate: warm}

disposition_notes: >
  Starts cold. Buying his catch, not his story, is what moves him. At warm he
  names the coin; he never speculates aloud about who mints it.
```
  • Step 2: Build and verify the specimen validates but emits nothing
PYTHONPATH=tools .venv/bin/python -m content_build
PYTHONPATH=tools .venv/bin/python -m content_build --check && echo "check 0"
ls content/world/canon/specimen-wharf.json 2>/dev/null && echo "LEAK: specimen emitted" || echo "OK: specimen not emitted"
ls content/world/npcs/specimen-teague.json 2>/dev/null && echo "LEAK: specimen npc emitted" || echo "OK: no specimen npc"

Expected: --check exits 0; both ls report "OK" (candidate entries emit nothing). If a specimen JSON exists, an entry is not candidate — fix the source.

  • Step 3: Run the build tests
PYTHONPATH=tools .venv/bin/python -m pytest tools/content_build/tests -q

Expected: all pass (unchanged; specimen adds no test).

  • Step 4: Commit
git add content/lore/specimen.md
git commit -m "feat(content): synthetic candidate disposition-gate specimen

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ"

(No generated JSON is staged — the specimen is candidate and emits nothing.)


Task 2: Reparent the ladder + purge Duncarrow

This is the atomic heart: the ladder moves to mechanics.md and duncarrow.md is deleted in the SAME commit, so the required-once ladder is never duplicated or missing. Orphaned generated JSON is removed, and the now-invalid test_duncarrow.py is deleted in the same breath so the suite stays green.

Files:

  • Create: content/lore/mechanics.md

  • Delete: content/lore/duncarrow.md

  • git rm (23 generated JSON, enumerated in Step 3)

  • Delete: tools/content_build/tests/test_duncarrow.py

  • Edit: tools/content_build/tests/test_model.py

  • Step 1: Create content/lore/mechanics.md with the ladder moved verbatim

Create content/lore/mechanics.md:

# MECHANICS — canonical world rules (mechanical, not lore)

> Home for mechanical `rule.*` entries the dialogue/engine layer depends on.
> `rule.disposition-ladder` lives here (moved from the retired `duncarrow.md`
> scaffolding); it is the required-exactly-once gate vocabulary every NPC
> knowledge-gate references.

```yaml
id: rule.disposition-ladder
type: rule
status: canon
secrecy: 0
related: []
rungs: [hostile, cold, neutral, warm, trusted]
body: >
  NPC trust toward the player is one of five ordered states:
  hostile < cold < neutral < warm < trusted. Where a given player sits with a
  given NPC is runtime state owned by code. The ladder itself is canon. Every
  knowledge-link gate references one of these five names, plus the special
  gate `never` (fact is known but never revealed through dialogue at any rung).
```
  • Step 2: Delete the Duncarrow source
git rm content/lore/duncarrow.md
  • Step 3: Remove the orphaned generated JSON (exact list)

content/world/canon/disposition-ladder.json is NOT removed — it regenerates from mechanics.md (same content). Remove exactly these 23 Duncarrow-generated files:

git rm \
  content/world/canon/duncarrow.json \
  content/world/canon/elves.json \
  content/world/canon/mayor-oswin-crell.json \
  content/world/canon/the-tallow-reach.json \
  content/world/canon/the-white-antlers.json \
  content/world/topics/crell-runs-slave-trade.json \
  content/world/topics/crells-guard-acts-alone.json \
  content/world/topics/elves-avoid-the-shrine.json \
  content/world/topics/harn-daughter-left.json \
  content/world/topics/militia-never-investigates.json \
  content/world/topics/travelers-go-missing.json \
  content/server/topics/crell-runs-slave-trade.json \
  content/server/topics/crells-guard-acts-alone.json \
  content/server/topics/elves-avoid-the-shrine.json \
  content/server/topics/harn-daughter-left.json \
  content/server/topics/militia-never-investigates.json \
  content/server/topics/travelers-go-missing.json \
  content/world/npcs/harn-blackwood.json \
  content/world/npcs/mayor-oswin-crell.json \
  content/world/npcs/mera-fenn.json \
  content/server/npcs/harn-blackwood.json \
  content/server/npcs/mayor-oswin-crell.json \
  content/server/npcs/mera-fenn.json
  • Step 4: Delete the redundant real-content test
git rm tools/content_build/tests/test_duncarrow.py
  • Step 5: Fix the cosmetic Duncarrow id in test_model.py

In tools/content_build/tests/test_model.py, the slug test uses a Duncarrow name as a throwaway string. Replace it with the specimen's id. Change:

    e = entry_from_raw(_raw(id="npc.mera-fenn", type="person",

to:

    e = entry_from_raw(_raw(id="npc.specimen-teague", type="person",

and change the following assertion:

    assert e.slug == "mera-fenn"

to:

    assert e.slug == "specimen-teague"
  • Step 6: Rebuild and verify (ladder moved, Duncarrow gone, no orphans)
PYTHONPATH=tools .venv/bin/python -m content_build
PYTHONPATH=tools .venv/bin/python -m content_build --check && echo "check 0"
git status --short

Expected: build succeeds (ladder resolves from mechanics.md; no "exactly one" error); --check exits 0 with NO "orphan" lines. git status shows the new mechanics.md, the deletions, and possibly a regenerated content/world/canon/disposition-ladder.json if its serialization differs (it should be byte-identical → no change). If --check still names an orphan, git rm that exact file and re-check.

  • Step 7: Run the build tests
PYTHONPATH=tools .venv/bin/python -m pytest tools/content_build/tests -q

Expected: all pass — test_duncarrow.py's 3 tests are gone; test_emit.py still covers routing/secrecy/candidate-exclusion; test_model.py slug test passes with the new id.

  • Step 8: Commit
git add content/lore/mechanics.md tools/content_build/tests/test_model.py content/world content/server
git commit -m "feat(content): reparent disposition-ladder to mechanics.md; purge Duncarrow

Move rule.disposition-ladder into content/lore/mechanics.md and delete the
Duncarrow scaffolding (source + 23 generated JSON). Remove the redundant
test_duncarrow.py (routing/secrecy already covered by test_emit.py synthetic
fixtures); repoint a cosmetic slug-test id.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ"

Task 3: Repoint the world-building skill + rewrite evals

Structure examples → the specimen; narrator-voice examples → the cosmology bodies; the 3 Duncarrow-based evals → cosmology-aware. Skill files aren't built, so the "test" is that the build stays green and the edits read correctly.

Files:

  • Edit: .claude/skills/world-building/SKILL.md

  • Edit: .claude/skills/world-building/references/schema.md

  • Edit: .claude/skills/world-building/references/tone.md

  • Edit: .claude/skills/world-building/evals/evals.json

  • Step 1: SKILL.md — specimen pointer

In .claude/skills/world-building/SKILL.md, replace:

  model. Match the voice and structure of `content/lore/duncarrow.md` — it is the
  reference specimen.

with:

  model. Match the **structure** of `content/lore/specimen.md` (the synthetic
  disposition-gate specimen — a guarded-then-warm NPC with one earned reveal) and
  the **voice** of the canon bodies in `content/lore/cosmology.md`.
  • Step 2: schema.md — organize-like pointer

In .claude/skills/world-building/references/schema.md, replace:

Organize a file with `##` headings and `>` notes like `content/lore/duncarrow.md`.

with:

Organize a file with `##` headings and `>` notes like `content/lore/specimen.md`.
  • Step 3: schema.md — where the ladder lives

Replace:

  Don't duplicate or remove it; it already lives in `duncarrow.md`.

with:

  Don't duplicate or remove it; it lives in `mechanics.md`.
  • Step 4: schema.md — the ascending-secrecy chain example

Replace:

`never`-gated core — mirror the Crell chain in `duncarrow.md` (1→2→2→3→4).

with:

`never`-gated core — a chain climbs by secrecy, e.g. 1→2→2→3→4 from an
observable rumor to the core secret.
  • Step 5: schema.md — the reachable-ceiling paragraph

Replace:

secret without holding the secret entry itself (Mera Fenn tops out at
`crells-guard-acts-alone`, never `crell-runs-slave-trade` — the player infers the
rest). Understanding earned beats understanding handed over.

with:

secret without holding the secret entry itself: its top rung names what it
witnessed, not the whole truth, and the player infers the rest. Understanding
earned beats understanding handed over.
  • Step 6: schema.md — worked example, repoint the dangling town.duncarrow

The worked example references a now-deleted town. Replace both occurrences of town.duncarrow in the worked-example YAML with the specimen's place. Change:

related: [town.duncarrow]

to:

related: [place.specimen-wharf]

and change:

related: [town.duncarrow, person.old-teague]

to:

related: [place.specimen-wharf, person.old-teague]
  • Step 7: tone.md — the voice quote

In .claude/skills/world-building/references/tone.md, replace:

Look at the `duncarrow.md` bodies: "prosperous and orderly place, which is exactly
the reputation its mayor has spent fifteen years building and depends on." The
menace is in the plain statement, not in adjectives.

with:

Look at the cosmology bodies: "Most of the Margreave believes the Seven are real.
Most of the Margreave is right." The weight is in the plain statement, not in
adjectives.
  • Step 8: tone.md — the fear-as-prickliness model

Replace:

  less (fear reads as prickliness — Mera Fenn is the model). `hostile` is a

with:

  less (fear reads as prickliness). `hostile` is a
  • Step 9: tone.md — the Old Teague reference

Replace:

   heard, blunt loyalty. Old Teague's "dryness turns to something almost fond."

with:

   heard, blunt loyalty — the specimen NPC's "dryness turns to something almost
   fond."
  • Step 10: evals/evals.json — rewrite the 3 Duncarrow-based evals

Replace the entire "evals" array in .claude/skills/world-building/evals/evals.json with:

  "evals": [
    {
      "id": 1,
      "name": "new-npc-reconciled",
      "prompt": "Add a new NPC on the road: a caravan guard who's cagey with strangers but opens up once you've earned it. Give them something personal they only share when they trust you. Put it in a new lore file.",
      "expected_output": "New npc.* dialogue-layer entry (+ its person/place/knowledge entries) authored in a NEW content/lore file; start_disposition cold or neutral; at least one fact gated at warm/trusted; every related resolves; no existing lore file edited; PYTHONPATH=tools content_build --check exits 0; tone dry and guarded-then-warm; nothing contradicts the cosmology backdrop.",
      "assertions": [
        "Created at least one new npc.* entry in content/lore with start_disposition of cold or neutral",
        "The NPC's knows list gates a personal fact at warm or trusted (earned reveal)",
        "Every related id resolves to a real entry",
        "No pre-existing content/lore/*.md file was modified (or any edit was surfaced for approval, not silent)",
        "python -m content_build --check exits 0 after the change",
        "Persona/body prose is dry and in-tone; no narrator winking or 'hilarious' register",
        "Nothing authored contradicts the cosmology canon (the Warden / the Seven)"
      ],
      "files": []
    },
    {
      "id": 2,
      "name": "contradiction-approval-gate",
      "prompt": "Write an NPC priest who tells the player the Warden's true name and proves the god is real and good.",
      "expected_output": "Skill recognizes this contradicts locked cosmology (the Warden is nameless BY DESIGN; his existence and goodness are 'distant & contested', never confirmed by the narrator), STOPS instead of authoring it, and either explains the conflict or proposes an in-tone alternative (an NPC who CLAIMS a name or believes, with the game confirming nothing) and asks before writing.",
      "assertions": [
        "Did NOT author content that states the Warden's true name or confirms the god is real/good",
        "Surfaced the contradiction explicitly, naming the cosmology rule (nameless by design; distant & contested)",
        "Proposed an in-tone alternative and/or asked for approval before writing"
      ],
      "files": []
    },
    {
      "id": 3,
      "name": "new-storyline-file",
      "prompt": "Start a new storyline about a roadside cult bleeding travelers to feed one of the Seven: who they are, and a rumor the player might hear. Make it its own file.",
      "expected_output": "A new content/lore/*.md file with canon entities and at least one knowledge entry; related wired to a real cosmology id (faction.the-seven or a specific lord); knowledge with correct ascending secrecy; build + --check green; canon-roadmap updated. The demon tie is handled in-tone: a gated secret (hidden engine), not stated in the open.",
      "assertions": [
        "Authored a new content/lore/*.md file (its own file)",
        "Wired at least one new entry's related to a real cosmology id (faction.the-seven or a lord)",
        "Includes at least one knowledge entry (rumor/fact/secret) with correct ascending secrecy",
        "python -m content_build --check exits 0 after the change",
        "content/lore/canon-roadmap.md updated to reflect the new content",
        "The demonic connection is a gated secret, not open surface text"
      ],
      "files": []
    }
  ]
  • Step 11: Verify build still green + evals.json is valid JSON
PYTHONPATH=tools .venv/bin/python -m content_build --check && echo "check 0"
PYTHONPATH=tools .venv/bin/python -m pytest tools/content_build/tests -q
.venv/bin/python -c "import json; json.load(open('.claude/skills/world-building/evals/evals.json')); print('evals.json valid')"

Expected: --check exits 0; tests pass; evals.json parses.

  • Step 12: Sanity-read the four files

Confirm no remaining reference to duncarrow, Crell, Mera Fenn, or town.duncarrow in the skill:

grep -rn "duncarrow\|Duncarrow\|Crell\|Mera Fenn\|town.duncarrow" .claude/skills/world-building/ || echo "clean — no Duncarrow refs left"

Expected: "clean". (grep returns nonzero when nothing matches, so the || echo prints the confirmation.)

  • Step 13: Commit
git add .claude/skills/world-building/
git commit -m "docs(skills): repoint world-building off Duncarrow (specimen + cosmology); rewrite evals

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ"

Task 4: Roadmap + final verification

Files:

  • Edit: content/lore/canon-roadmap.md

  • Step 1: Update the roadmap

In content/lore/canon-roadmap.md, replace this exact Authored bullet:

- **duncarrow.md** — the bounded-dialogue / disposition-gate specimen (Duncarrow,
  the Crell chain, Mera Fenn, etc.). **Scaffolding, not story:** it seeded the
  schema + skill and hosts the required-once `rule.disposition-ladder`. Slated for
  removal in a follow-up that reparents the ladder into `cosmology.md` and stands
  up a cosmology-native specimen town.

with:

- **mechanics.md** — hosts `rule.disposition-ladder` (the required-once gate
  vocabulary), reparented out of the retired Duncarrow scaffolding.
- **specimen.md** — a synthetic `status: candidate` disposition-gate specimen
  (validated by `--check`, emitted nowhere); the world-building skill's structure
  example. Not game canon. The Duncarrow scaffolding (Crell chain, Mera Fenn) is
  deleted.

(The "Pending" section needs no change — it does not reference the deleted faction.elves / Tallow Reach stubs.)

  • Step 2: Full final verification
PYTHONPATH=tools .venv/bin/python -m content_build
PYTHONPATH=tools .venv/bin/python -m content_build --check && echo "check 0"
PYTHONPATH=tools .venv/bin/python -m pytest tools/content_build/tests -q
echo "--- no Duncarrow-generated artifacts remain ---"
ls content/world/canon/duncarrow.json content/world/npcs/mera-fenn.json content/server/topics/crell-runs-slave-trade.json 2>/dev/null && echo "STILL PRESENT (bug)" || echo "OK: gone"
echo "--- ladder present exactly once, regenerated from mechanics ---"
ls content/world/canon/disposition-ladder.json
echo "--- legacy npcs intact ---"
ls content/world/npcs/brannoc_thane.json content/world/npcs/cadwyn_vell.json content/world/npcs/fenn.json
git status --short

Expected: build clean; --check exits 0; tests pass; Duncarrow artifacts gone; disposition-ladder.json present; legacy NPCs intact; git status shows only the roadmap change uncommitted.

  • Step 3: Commit
git add content/lore/canon-roadmap.md
git commit -m "docs(content): roadmap — Duncarrow purged, ladder in mechanics.md, specimen added

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuHRPE7VfppUJEaoGBEUqZ"
  • Step 4: Report

Summarize for the human: ladder reparented; Duncarrow source + 23 generated JSON + test_duncarrow.py removed; specimen added (candidate, emits nothing); skill/evals repointed; --check + pytest green; legacy NPCs and POC seed JSON untouched. Stop for the human to smoke-test and confirm before any merge to dev (charter §18).


Self-review notes (author's check against the spec)

  • Spec §3 (reparent ladder): Task 2 Steps 1/6/8 move it to mechanics.md verbatim, atomically with the Duncarrow deletion (never duplicated/missing). ✓
  • Spec §4 (synthetic specimen): Task 1 — status: candidate, synthetic non-colliding ids, one place + guarded person/npc + one warm-gated fact; Step 2 asserts it emits nothing. ✓
  • Spec §2/§5 (self-contained deletion + orphan cleanup): Task 2 Step 3 enumerates all 23 orphaned JSON (verified against the live tree); disposition-ladder.json intentionally retained (regenerated from mechanics). ✓
  • Spec §5 (repoint skill; structure→specimen, voice→cosmology): Task 3 Steps 19; Step 12 greps for residual Duncarrow refs. ✓
  • Spec §6 (tests + evals): test_duncarrow.py deleted (redundant with test_emit.py), test_model.py cosmetic id fixed (Task 2); 3 evals rewritten cosmology-aware (Task 3 Step 10). ✓
  • Spec §7 (out of scope untouched): Global Constraints + Task 4 Step 2 verify legacy NPCs intact; no task touches POC seed JSON or cosmology bibles. ✓
  • Spec §8 (acceptance): Task 4 Step 2 checks build/check/pytest green, Duncarrow artifacts gone, ladder present, legacy NPCs intact. ✓
  • Type/id consistency: specimen ids (place.specimen-wharf, person.specimen-teague, fact.specimen-foreign-coin, npc.specimen-teague) are used identically in Task 1, Task 2 Step 5, and Task 3 Step 6. ✓