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

593 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/*.md``content/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):**
```bash
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.md``test_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`**
```bash
git checkout dev && git pull --ff-only
git checkout -b feature/duncarrow-purge
```
- [ ] **Step 2: Confirm a green baseline**
```bash
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:
````markdown
# 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**
```bash
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**
```bash
PYTHONPATH=tools .venv/bin/python -m pytest tools/content_build/tests -q
```
Expected: all pass (unchanged; specimen adds no test).
- [ ] **Step 4: Commit**
```bash
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`:
````markdown
# 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**
```bash
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:
```bash
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**
```bash
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:
```python
e = entry_from_raw(_raw(id="npc.mera-fenn", type="person",
```
to:
```python
e = entry_from_raw(_raw(id="npc.specimen-teague", type="person",
```
and change the following assertion:
```python
assert e.slug == "mera-fenn"
```
to:
```python
assert e.slug == "specimen-teague"
```
- [ ] **Step 6: Rebuild and verify (ladder moved, Duncarrow gone, no orphans)**
```bash
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**
```bash
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**
```bash
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:
```markdown
model. Match the voice and structure of `content/lore/duncarrow.md` — it is the
reference specimen.
```
with:
```markdown
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:
```markdown
Organize a file with `##` headings and `>` notes like `content/lore/duncarrow.md`.
```
with:
```markdown
Organize a file with `##` headings and `>` notes like `content/lore/specimen.md`.
```
- [ ] **Step 3: `schema.md` — where the ladder lives**
Replace:
```markdown
Don't duplicate or remove it; it already lives in `duncarrow.md`.
```
with:
```markdown
Don't duplicate or remove it; it lives in `mechanics.md`.
```
- [ ] **Step 4: `schema.md` — the ascending-secrecy chain example**
Replace:
```markdown
`never`-gated core — mirror the Crell chain in `duncarrow.md` (1→2→2→3→4).
```
with:
```markdown
`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:
```markdown
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:
```markdown
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:
```yaml
related: [town.duncarrow]
```
to:
```yaml
related: [place.specimen-wharf]
```
and change:
```yaml
related: [town.duncarrow, person.old-teague]
```
to:
```yaml
related: [place.specimen-wharf, person.old-teague]
```
- [ ] **Step 7: `tone.md` — the voice quote**
In `.claude/skills/world-building/references/tone.md`, replace:
```markdown
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:
```markdown
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:
```markdown
less (fear reads as prickliness — Mera Fenn is the model). `hostile` is a
```
with:
```markdown
less (fear reads as prickliness). `hostile` is a
```
- [ ] **Step 9: `tone.md` — the Old Teague reference**
Replace:
```markdown
heard, blunt loyalty. Old Teague's "dryness turns to something almost fond."
```
with:
```markdown
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:
```json
"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**
```bash
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:
```bash
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**
```bash
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:
```markdown
- **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:
```markdown
- **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**
```bash
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**
```bash
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. ✓
```