# Canonical D&D Entity Registries And Occurrences Implementation Plan

## Objective

Implement the target state defined in
[Canonical D&D Entity Registries And Occurrences](entity-registries.md). NPCs,
locations, and items will each have one canonical registry lane and one
registry-backed occurrence lane, with consistent module, artifact, schema,
prompt, reference, validation, package, asset, example, and documentation
vocabulary.

This is a breaking pre-release migration. The completed implementation must not
retain compatibility aliases for old module keys, artifact kinds, types,
schemas, prompts, validators, reference slots, or asset paths. The stages below
deliberately change durable contracts before moving the corresponding module
namespace so that each prompt remains bounded for gpt-5.6-terra and the
repository can compile and pass focused tests after every stage.

Complete the stages in numerical order. Each stage is sized for one
implementation prompt. Do not combine stages unless a later review explicitly
revises this plan.

## Final Contract Matrix

Use these exact final identities. Do not invent variants during implementation.

| Entity role | Module key | Go artifact type | Artifact kind | Durable schema ID | Durable root |
| --- | --- | --- | --- | --- | --- |
| NPC registry | `dnd/npc-registry` | `dnd.NPCRegistry` | `dnd/npc-registry` | `notarius.dnd.npc_registry` | `npcs` |
| NPC occurrences | `dnd/npc-occurrences` | `dnd.NPCOccurrenceList` | `dnd/npc-occurrence-list` | `notarius.dnd.npc_occurrences` | `occurrences` |
| Location registry | `dnd/location-registry` | `dnd.LocationRegistry` | `dnd/location-registry` | `notarius.dnd.location_registry` | `locations` |
| Location occurrences | `dnd/location-occurrences` | `dnd.LocationOccurrenceList` | `dnd/location-occurrence-list` | `notarius.dnd.location_occurrences` | `occurrences` |
| Item registry | `dnd/item-registry` | `dnd.ItemRegistry` | `dnd/item-registry` | `notarius.dnd.item_registry` | `items` |
| Item occurrences | `dnd/item-occurrences` | `dnd.ItemOccurrenceList` | `dnd/item-occurrence-list` | `notarius.dnd.item_occurrences` | `occurrences` |

All durable contracts use schema version `v1` and media type
`application/json`. Registry records have required `id`, `name`, and
`source_refs`. NPC, location, and item occurrences have required `npc_id`,
`location_id`, or `item_id` respectively, plus required `name`, `kind`, and
`source_refs`. Item occurrence `quantity`, `from`, and `to` remain semantically
conditional durable fields.

Use these exact identity policies and digest inputs:

- NPC: `dnd.npc_registry.identity.v1`, SHA-256 of compact JSON
  `[policy, comparison_name]`, prefix `npc:sha256:`;
- location: `dnd.location_registry.identity.v1`, SHA-256 of compact JSON
  `[policy, comparison_name, source_id, start_unit_id, end_unit_id]` using the
  earliest canonical evidence anchor, prefix `location:sha256:`; and
- item: `dnd.item_registry.identity.v1`, SHA-256 of compact JSON
  `[policy, comparison_name]`, prefix `item:sha256:`.

Registry reference slots and corresponding PromptKit inputs are exactly
`npc_registry`, `location_registry`, and `item_registry`. Enemy-event grounding
uses `npc_occurrences` for the renamed NPC occurrence artifact.

## Rules For Every Stage

- Read and follow `docs/policy/architecture.md`,
  `docs/policy/documentation.md`, and `docs/policy/testing.md` before editing.
- Preserve the fixed pipeline architecture, typed artifact zone, ordered-step
  barrier, generated-reference provenance, whole-run failure behavior,
  scheduler, checkpoint, and output boundaries. This feature requires no new
  framework workflow primitive and no new ADR.
- Keep `assets/assets.go` as the root assets package's only Go file. It remains
  a read-only content boundary with no business logic, PromptKit dependency,
  registry behavior, or module registration.
- Keep D&D semantics inside `internal/modules/dnd` and `assets/dnd`. Do not
  implement the future generic LLM-assisted deduplicator in this work set.
- Do not create compatibility type aliases, duplicate module registrations,
  legacy schema decoders, fallback reference-slot names, copied prompt assets,
  or deprecated-key shims. Intermediate stages may retain a current module key
  until its explicit namespace-migration stage, but must not register both old
  and new keys.
- Preserve minimal evidence-grounded contracts. Registry references are
  source-free grounding and never occurrence evidence. Every occurrence must
  cite current-transcript ranges independently.
- Private structured-response schemas must reject unknown fields and include
  every declared property in `required`. Use nullable private fields for
  semantically absent item values and map them deterministically to omitted
  durable fields.
- Preserve prompt-cache construction: extraction prompts share the byte-identical
  system/identity/reference/chunk prefix; evidence and registry projections
  follow that prefix; module instructions remain final. Registry normalizers
  retain system, instructions, shared reconciliation policy, candidates, and
  transcript-window order.
- Update current-behavior documentation when a stage changes an implemented
  contract. Do not describe a not-yet-registered item module outside
  `docs/roadmap/` before its production-registration stage.
- Keep default tests offline, deterministic, and credential-free. Use package
  behavior tests for codecs, identity, registry resolution, normalization, and
  validators; integration tests for generated handoffs; and CLI tests for
  assembled examples. Do not add live-LLM tests, exact prompt snapshots, private
  asset inventories, or duplicate change-detector tests.
- Preserve unrelated user changes. Use `gofmt`, `git diff --check`, and focused
  tests in every stage. Do not retire this roadmap or the feature roadmap as
  part of implementation.

## Stage 1: Migrate The NPC Registry Durable Contract And Identity

### Goal

Adopt the final NPC registry type, artifact kind, schema identity, and identity
algorithm while the selectable module temporarily remains `dnd/npcs`.

### Required changes

1. In `internal/modules/dnd/types.go`, replace `NPCListKind` and `NPCList` with
   `NPCRegistryKind` and `NPCRegistry`. Retain `NPC` with required `ID`, `Name`,
   and `SourceRefs`, and retain the durable root field `npcs`.
2. Update every typed consumer, merger, evidence projection, validator,
   normalizer, extractor, test fixture, integration helper, and registry
   registration to use `dnd.NPCRegistry` and `dnd.NPCRegistryKind`. Do not leave
   a Go alias for the retired names.
3. In the existing NPC codec package, replace the durable schema with
   `dnd_npc_registry.v1.json`, schema ID `notarius.dnd.npc_registry`, schema name
   `notarius_dnd_npc_registry_v1`, and version `v1`. Keep the strict `npcs`
   envelope and `application/json` media type.
4. Change the NPC identity policy to `dnd.npc_registry.identity.v1`. Derive IDs
   from compact JSON `[policy, comparison_name]`, not the current bare comparison
   string. Preserve Unicode NFKC, apostrophe normalization, whitespace collapse,
   case folding, empty-input rejection, lowercase SHA-256, and the existing ID
   prefix.
5. Update registry validation and tests to prove the documented bytes, policy
   namespace, deterministic IDs, uniqueness, concurrency safety, malformed-ID
   rejection, and non-mutation. Update dependent fixtures to use newly derived
   IDs rather than copied old digests.
6. Update the current `docs/integrations/dnd-npc-artifacts.md` in place so its
   artifact kind, schema identity, Go-independent ID algorithm, and wire example
   match this stage. It must continue to name `dnd/npcs` as the current producer
   until Stage 2.

### Acceptance criteria

- No production Go code refers to `NPCList` or `NPCListKind`.
- The registered codec round-trips the strict `NPCRegistry` `v1` contract and
  reports the target kind and schema identity.
- All current NPC-grounded consumers compile against `NPCRegistry` without
  changing their reference-slot names yet.
- There is no compatibility alias or decoder for `dnd/npc-list` or
  `notarius.dnd.npcs`.

### Validation

Run `gofmt` on every Go file changed in this stage, then run:

```sh
go test ./internal/modules/dnd/... ./internal/modules/integration/...
git diff --check
```

## Stage 2: Move The NPC Registry Module Into Its Canonical Namespace

### Goal

Rename the NPC registry's selectable modules, packages, prompts, assets,
validators, capabilities, and reference slots without changing the durable
record shape completed in Stage 1.

### Required changes

1. Move the stage packages to Go-compatible canonical paths:
   `codec/npcregistry`, `extract/npcregistry`, `normalize/npcregistry`, and
   `validate/npcregistry/...`. Domain helpers under `internal/modules/dnd/npcs`
   may remain because they own NPC identity rather than a selectable module.
2. Set extract and normalize module keys to `dnd/npc-registry`. Rename all NPC
   validator keys from `.../dnd/npcs/...` to `.../dnd/npc-registry/...`, and
   rename the provided capability to `dnd.npc_registry`.
3. Move `assets/dnd/npcs/{extract,normalize}` to
   `assets/dnd/npc-registry/{extract,normalize}`. Use prompt IDs
   `dnd.npc_registry` and `dnd.npc_registry.normalize`; use private schema ID
   `notarius.dnd.npc_registry.llm`, schema name
   `notarius_dnd_npc_registry_llm_v1`, and filename
   `dnd_npc_registry_llm.v1.json`.
4. Rename `common-dnd-npcs.md` to `common-dnd-npc-registry.md`. Change the
   rendered PromptKit input and every module-facing reference slot from `npcs`
   to `npc_registry` in spells, combat turns, the still-current NPC-interaction
   modules, and enemy events. Preserve requiredness: optional for spell and
   combat grounding, required where the current consumer already requires an
   NPC registry.
5. Update immutable NPC registry projections to render ordered `{id, name}`
   pairs where identity is required and the narrowest source-free name
   projection where a consumer intentionally needs names only. Do not expose
   registry source references to prompts.
6. Update central module, codec, merger, evidence, validator, default-chain,
   prompt, and profile registration. Update the cross-lane prompt composition
   test for the new prompt ID and `npc_registry` input without duplicating its
   ordering assertions in module-local tests.
7. Rename the integration contract to
   `docs/integrations/dnd-npc-registry-artifacts.md`, update inbound links, and
   update the implemented module catalog, configuration reference, current
   internal D&D guide, and maintained examples for the new key and slot.
8. Remove the old package trees, asset tree, prompt IDs, module/validator keys,
   capability, shared filename, and reference-slot name. The durable JSON field
   `npcs`, the record type `NPC`, and domain helper package name `npcs` are not
   legacy module identifiers and should remain.

### Acceptance criteria

- `dnd/npc-registry` is the only selectable NPC registry extract/normalize key.
- All NPC-registry consumers expose `npc_registry` and accept only
  `dnd/npc-registry` artifacts.
- Prompt and schema assets load only from `assets/dnd/npc-registry` and retain
  the documented cache order.
- Current docs and examples contain no claim that `dnd/npcs` is selectable.

### Validation

```sh
go test ./internal/modules/dnd/... ./internal/modules/integration/... ./internal/cli/...
go run ./cmd/notarius config validate --config examples/dnd-minimal.config.yml --pipeline dnd-session
git diff --check
```

## Stage 3: Redesign The NPC Occurrence Durable Contract

### Goal

Replace the NPC-interaction value contract with the final registry-ID-backed
NPC occurrence contract while the selectable module temporarily remains
`dnd/npc-interactions`.

### Required changes

1. Replace `NPCInteractionListKind`, `NPCInteractionList`,
   `NPCInteractionKind`, and `NPCInteraction` with `NPCOccurrenceListKind`,
   `NPCOccurrenceList`, `NPCOccurrenceKind`, and `NPCOccurrence`. The envelope
   field is `occurrences`; each record has required `npc_id`, `name`, `kind`,
   and `source_refs`.
2. Retain the six existing category values and their precedence and splitting
   semantics. Change only the contract vocabulary and registry identity link;
   do not add relationship, sentiment, biography, or persistent-state fields.
3. In the current codec package, adopt `dnd/npc-occurrence-list`, durable schema
   ID `notarius.dnd.npc_occurrences`, schema name
   `notarius_dnd_npc_occurrences_v1`, and strict file
   `dnd_npc_occurrences.v1.json`.
4. Change the current NPC-interaction private response contract to require
   `npc_id` as well as `name`, `kind`, and `source_refs`. Use private schema ID
   `notarius.dnd.npc_occurrences.llm`, schema name
   `notarius_dnd_npc_occurrences_llm_v1`, and a renamed schema file even though
   the prompt ID moves in Stage 4.
5. Project ordered `{id, name}` NPC registry entries into extraction. Require
   the model to return an exact supplied pair. Extraction, normalization, and
   registry validators must reject unknown IDs and mismatched ID/name pairs
   rather than repairing by name similarity.
6. Update deterministic ordering, exact-duplicate identity, clone/evidence
   helpers, merge functions, normalizers, validators, and diagnostics to include
   `npc_id`. Preserve source chronology and distinct kinds/evidence.
7. Update enemy-event grounding to consume `NPCOccurrenceList`, filtering
   `combat_opponent` occurrences while carrying only source-free grounding
   values. Do not add NPC IDs to the enemy-event durable contract.
8. Update `docs/integrations/dnd-npc-interaction-artifacts.md` in place to the
   new durable schema, root, and fields while accurately retaining the current
   producer key until Stage 4.

### Acceptance criteria

- No production Go code refers to an `NPCInteraction*` artifact type.
- The durable artifact uses `occurrences` and validates exact NPC ID/name pairs.
- Unknown IDs and mismatches are detected independently at extraction,
  normalization, and validation boundaries without mutation.
- Enemy-event grounding behavior remains source-free and category-filtered.

### Validation

```sh
go test ./internal/modules/dnd/codec/npcinteractions ./internal/modules/dnd/extract/npcinteractions ./internal/modules/dnd/normalize/npcinteractions ./internal/modules/dnd/validate/npcinteractions/... ./internal/modules/dnd/extract/enemyevents ./internal/modules/dnd/normalize/enemyevents
go test ./internal/modules/dnd/... ./internal/modules/integration/...
git diff --check
```

## Stage 4: Move NPC Occurrences Into Their Canonical Namespace

### Goal

Complete the NPC migration by replacing interaction module/package vocabulary
and updating the enemy-event dependency.

### Required changes

1. Move the codec, extract, normalize, validate, and D&D helper packages from
   `npcinteractions` to `npcoccurrences` where they own the occurrence artifact.
2. Set extract and normalize keys to `dnd/npc-occurrences`; rename validator
   keys and the provided capability to the `npc-occurrences`/
   `dnd.npc_occurrences` vocabulary.
3. Move `assets/dnd/npc-interactions` to `assets/dnd/npc-occurrences`. Change the
   extraction prompt ID to `dnd.npc_occurrences`, update instructions from
   “interactions” to “occurrences” without changing category semantics, and
   keep the target private schema identity established in Stage 3.
4. Rename the generated reference and PromptKit grounding input consumed by
   enemy events from `npc_interactions` to `npc_occurrences`. Update combat
   grounding assets, reference specs, fingerprint metadata, checkpoint inputs,
   and tests consistently.
5. Update central registration, default validator chains, artifact evidence and
   merge registration, prompt-cache tests, configuration keys, examples, and
   current documentation.
6. Rename the integration contract to
   `docs/integrations/dnd-npc-occurrence-artifacts.md` and update every link from
   NPC registry, enemy event, combat, internal D&D, configuration, and JSON
   output documentation.
7. Delete every old package, key, schema, prompt, capability, asset, and current-
   behavior reference for `dnd/npc-interactions` and `npc_interactions`. Do not
   retain a module alias.

### Acceptance criteria

- `dnd/npc-occurrences` is the only selectable NPC occurrence key.
- Enemy events require `npc_occurrences` of kind
  `dnd/npc-occurrence-list`.
- Current catalogs, examples, and documentation use occurrence vocabulary.
- No production package or asset directory named `npcinteractions` or
  `npc-interactions` remains.

### Validation

```sh
go test ./internal/modules/dnd/... ./internal/modules/integration/... ./internal/cli/...
OPENROUTER_API_KEY=validation-placeholder go run ./cmd/notarius config validate --config examples/dnd-complete.config.yml --pipeline dnd-session
git diff --check
```

## Stage 5: Migrate The Location Registry Durable Contract And Identity

### Goal

Adopt the final location registry type, artifact kind, schema identity, and
identity-policy namespace while the selectable module remains `dnd/locations`.

### Required changes

1. Replace `LocationListKind` and `LocationList` with `LocationRegistryKind` and
   `LocationRegistry`. Retain `Location` and the durable `locations` root.
2. Update all typed location-registry consumers, especially location-occurrence
   extraction, normalization, validation, reference resolution, merge, evidence,
   registration, and tests. Do not leave aliases.
3. In the current location codec package, use kind `dnd/location-registry`,
   schema ID `notarius.dnd.location_registry`, schema name
   `notarius_dnd_location_registry_v1`, and strict file
   `dnd_location_registry.v1.json`.
4. Change the identity policy to `dnd.location_registry.identity.v1` and retain
   the exact compact JSON input `[policy, comparison_name, source_id,
   start_unit_id, end_unit_id]`. Continue to select the earliest canonical
   evidence anchor without mutating caller data. Accept the resulting pre-release
   ID invalidation.
5. Update identity, registry, codec, same-name-location, normalization, and
   dependent occurrence fixtures. Tests must prove same-name distinct anchors,
   deterministic ordering, invalid component rejection, and exact documented
   bytes.
6. Update `docs/integrations/dnd-location-artifacts.md` in place for the target
   kind, schema, and identity policy while retaining `dnd/locations` as the
   current module key until Stage 6.

### Acceptance criteria

- No production Go code refers to `LocationList` or `LocationListKind`.
- Same-name locations with distinct evidence anchors remain distinguishable.
- The codec and generated-reference compatibility use
  `dnd/location-registry`.
- No old kind, schema identity, or identity-policy compatibility path remains.

### Validation

```sh
go test ./internal/modules/dnd/locations/... ./internal/modules/dnd/codec/locations ./internal/modules/dnd/extract/locations ./internal/modules/dnd/normalize/locations ./internal/modules/dnd/extract/locationoccurrences ./internal/modules/dnd/normalize/locationoccurrences ./internal/modules/dnd/validate/locations/... ./internal/modules/dnd/validate/locationoccurrences/...
go test ./internal/modules/dnd/... ./internal/modules/integration/...
git diff --check
```

## Stage 6: Move And Tighten The Location Registry Module

### Goal

Move location registry code and assets into the canonical namespace and enforce
the roadmap's named-or-uniquely-designated eligibility policy.

### Required changes

1. Move the stage packages to `codec/locationregistry`,
   `extract/locationregistry`, `normalize/locationregistry`, and
   `validate/locationregistry/...`. Entity identity and immutable registry
   helpers may remain under `internal/modules/dnd/locations`.
2. Set extract and normalize keys to `dnd/location-registry`; rename validator
   keys and the provided capability to `dnd.location_registry`.
3. Move `assets/dnd/locations/{extract,normalize}` to
   `assets/dnd/location-registry/{extract,normalize}`. Use prompt IDs
   `dnd.location_registry` and `dnd.location_registry.normalize`; use private
   schema ID `notarius.dnd.location_registry.llm`, schema name
   `notarius_dnd_location_registry_llm_v1`, and filename
   `dnd_location_registry_llm.v1.json`.
4. Change the location-registry extraction instructions to accept only a stable
   proper name or unique in-world designation. Explicitly exclude generic,
   temporary, relative, and descriptive phrases such as `the room`, `the bar`,
   `the hallway`, `outside`, and `upstairs`. Do not use capitalization as the
   eligibility test and do not add a brittle deterministic word blacklist.
5. Rename the required location-occurrence reference slot and PromptKit input
   from `locations` to `location_registry`. Preserve the ordered source-free
   `{id, name}` projection and exact ID/name validation at extraction and
   normalization.
6. Update location-occurrence instructions so a generic current-chunk phrase
   may resolve to an existing named registry location only when context supports
   that coreference; it may not create a registry entity or substitute registry
   provenance for evidence.
7. Update central registration, prompt composition, validator chains,
   configuration, examples, and current docs. Rename the integration contract
   to `docs/integrations/dnd-location-registry-artifacts.md` and update links.
8. Remove old module/package/asset/prompt/validator/capability identities and
   old reference-slot names. The durable `locations` field and domain helper
   package remain valid.

### Acceptance criteria

- `dnd/location-registry` is the only selectable location registry key.
- Registry prompts exclude generic locations, while occurrence prompts permit
  supported coreference to an existing registry entity.
- Location occurrences require `location_registry` of kind
  `dnd/location-registry` at extract and normalize stages.
- No production compatibility alias for `dnd/locations` remains.

### Validation

```sh
go test ./internal/modules/dnd/... ./internal/modules/integration/... ./internal/cli/...
OPENROUTER_API_KEY=validation-placeholder go run ./cmd/notarius config validate --config examples/dnd-complete.config.yml --pipeline dnd-session
git diff --check
```

## Stage 7: Add The Item Registry Domain Foundation

### Goal

Implement the item registry's typed durable contract, deterministic identity,
codec, and immutable reference projection without registering a selectable
pipeline module yet.

### Required changes

1. Add `ItemRegistryKind`, `ItemRegistry`, and `Item` to the D&D domain types.
   The strict envelope is `{"items":[...]}`; each item has required `id`,
   `name`, and `source_refs`.
2. Add an item identity package under `internal/modules/dnd/items/identity`.
   Implement comparison normalization consistent with the NPC identity text
   family, policy `dnd.item_registry.identity.v1`, compact JSON
   `[policy, comparison_name]`, prefix `item:sha256:`, syntax checks, registry
   uniqueness validation, and deterministic errors.
3. Add immutable item registry resolution under
   `internal/modules/dnd/items/registry`. It must decode through the canonical
   codec, validate IDs, preserve same-name/type policy, return defensive copies,
   and produce an ordered source-free `{id, name}` projection suitable for a
   later reference slot.
4. Add `internal/modules/dnd/codec/itemregistry` with strict durable schema
   `dnd_item_registry.v1.json`, ID `notarius.dnd.item_registry`, name
   `notarius_dnd_item_registry_v1`, version `v1`, and media type
   `application/json`.
5. Test codec round trips, strict unknown-field rejection, candidate encoding,
   metadata, identity bytes, empty/malformed identities, duplicate IDs,
   defensive copies, projection order, and non-mutation.
6. Do not register the codec or describe the item registry as selectable in
   current configuration or integration docs yet. The feature roadmap remains
   the canonical description until Stage 9.

### Acceptance criteria

- The new type, identity, codec, and registry packages pass independently.
- Item IDs are name/type identities rather than per-instance identities.
- The projection exposes IDs and names but no source references.
- No generic framework package depends on D&D item types.

### Validation

Run `gofmt` on every Go file changed in this stage, then run:

```sh
go test ./internal/modules/dnd/items/... ./internal/modules/dnd/codec/itemregistry
go test ./internal/modules/dnd/...
git diff --check
```

## Stage 8: Implement Item Registry Extraction And Validation

### Goal

Add the item-registry extraction module, private response contract, prompt
assets, canonical mapping, and extraction validators without production
registration.

### Required changes

1. Add `internal/modules/dnd/extract/itemregistry` with key
   `dnd/item-registry`, artifact kind `dnd/item-registry`, LLM-backed execution,
   the ordinary campaign reference slots, and prompt ID `dnd.item_registry`.
2. Add `assets/dnd/item-registry/extract` containing `prompt.yaml`, one coherent
   `instructions.md`, and strict private schema `dnd_item_registry_llm.v1.json`
   with ID `notarius.dnd.item_registry.llm` and name
   `notarius_dnd_item_registry_llm_v1`. The private response contains required
   item `name` and `source_refs`; deterministic code supplies source identity
   and derives the durable item ID.
3. Encode the roadmap eligibility policy precisely: accept named unique items,
   concrete reusable item types, stable unique designations, and separate
   currency denominations; reject vague `loot`, `treasure`, `some gear`,
   generic weapons, inferred properties, quantities, and uniqueness.
4. Reuse the shared extraction prompt prefix and evidence policy. Keep the item
   registry instructions final and ephemeral. Do not add item occurrence,
   holder, inventory, or ledger rules to this prompt.
5. Canonicalize display whitespace and evidence, derive IDs through the item
   identity package, preserve candidate evidence for validation, and order
   candidates deterministically. Do not semantically collapse aliases in the
   extractor.
6. Add `validate/itemregistry/shape`, `source_refs`, and
   `source_relatedness` packages and keys using the final module vocabulary.
   Keep relatedness advisory according to existing D&D conventions.
7. Add focused behavior, schema, prompt preparation, metadata-redaction,
   cancellation/error, and validator tests. Do not call a live model and do not
   snapshot full prompt text.
8. Do not add the module to the production D&D registrar until its normalizer
   and complete default chains exist in Stage 9.

### Acceptance criteria

- Direct module construction prepares and maps a strict item-registry response.
- Currency denominations and reusable item types are eligible; vague categories
  are excluded by prompt policy without a capitalization heuristic.
- Extracted records contain deterministic IDs and owned evidence slices.
- No current module catalog advertises an incomplete lane.

### Validation

```sh
go test ./internal/modules/dnd/extract/itemregistry ./internal/modules/dnd/validate/itemregistry/...
go test ./internal/modules/dnd/...
git diff --check
```

## Stage 9: Implement Item Registry Normalization And Production Registration

### Goal

Complete and register the item registry as the third production registry
family.

### Required changes

1. Add `internal/modules/dnd/normalize/itemregistry` with key
   `dnd/item-registry`, LLM-backed execution, prompt ID
   `dnd.item_registry.normalize`, and the same retry/fallback contract used by
   NPC and location registry normalization.
2. Deterministically preprocess candidate names, evidence, IDs, and exact
   duplicates. Then use the existing shared D&D entity-reconciliation proposal
   mechanics with item-specific candidates and selected transcript windows.
   Validated groups may choose one supplied canonical display name and union
   evidence; unsafe, overlapping, unknown, or uncertain groups preserve the
   deterministic result with bounded diagnostics.
3. Add `assets/dnd/item-registry/normalize` with `prompt.yaml`,
   `instructions.md`, and `candidates.md`. Item instructions own only same-item-
   kind/designation judgment and canonical display preference. The shared
   reconciliation fragment alone owns opaque keys and safe group mechanics.
4. Explicitly prevent reconciliation across currency denominations, materially
   different item types, or merely nearby objects. Do not create per-instance
   identities or infer properties.
5. Add `validate/itemregistry/identity` and its default normalize-chain position.
   Test retry, invalid structured output, operational error, warning bounds,
   idempotence, order, non-mutation, and content-safe diagnostics.
6. Register the item registry codec, evidence projection, append-order merger,
   extractor, normalizer, validators, default chains, prompt assets, and module
   metadata in the D&D registrar. Add it to the centralized extraction prompt
   composition test.
7. Create the current durable contract at
   `docs/integrations/dnd-item-registry-artifacts.md`. Update configuration and
   internal module catalogs enough to describe the now-selectable registry,
   but defer the complete multi-step example until item occurrences consume it.

### Acceptance criteria

- `dnd/item-registry` is selectable for extraction and normalization with
  complete production validator chains.
- The normalizer uses proposal-only LLM semantics and deterministic application.
- Codec, merge, evidence, prompt, metadata, and registry registrations are all
  type-compatible.
- The current docs accurately describe the implemented item registry without
  claiming that item occurrences consume it yet.

### Validation

```sh
go test ./internal/modules/dnd/items/... ./internal/modules/dnd/codec/itemregistry ./internal/modules/dnd/extract/itemregistry ./internal/modules/dnd/normalize/itemregistry ./internal/modules/dnd/validate/itemregistry/... ./internal/modules/dnd/register
go test ./internal/modules/dnd/... ./internal/cli/...
git diff --check
```

## Stage 10: Redesign Item Events As Registry-Backed Item Occurrences

### Goal

Adopt the final item occurrence durable contract and required item-registry
grounding while the selectable module temporarily remains `dnd/item-events`.

### Required changes

1. Replace `ItemEventListKind`, `ItemEventList`, `ItemEventKind`, and `ItemEvent`
   with `ItemOccurrenceListKind`, `ItemOccurrenceList`, `ItemOccurrenceKind`,
   and `ItemOccurrence`. Use durable root `occurrences`; each record has required
   `item_id`, `name`, `kind`, and `source_refs`, plus conditional `quantity`,
   `from`, and `to`.
2. Preserve the five existing kinds and all holder, quantity, currency,
   ordering, and exact-duplicate semantics. Rename the D&D helper behavior to
   occurrence vocabulary in code, but defer physical package moves to Stage 11.
3. In the current codec package, adopt kind `dnd/item-occurrence-list`, schema
   ID `notarius.dnd.item_occurrences`, schema name
   `notarius_dnd_item_occurrences_v1`, and strict file
   `dnd_item_occurrences.v1.json`.
4. Change the private response schema to
   `notarius.dnd.item_occurrences.llm`/
   `notarius_dnd_item_occurrences_llm_v1` in
   `dnd_item_occurrences_llm.v1.json`. Require every property. Represent absent
   `quantity`, `from`, and `to` as nullable private fields and map them to the
   durable conditional fields without fabricating values.
5. Add required `item_registry` reference slots to both extraction and
   normalization, accepting only `dnd/item-registry`, `application/json`, and an
   explicit size bound. Resolve one immutable registry per operation and render
   ordered source-free `{id, name}` entries after the shared evidence message.
6. Require the model to return an exact registry ID/name pair. Extraction must
   reject or omit unknown/mismatched pairs without creating an item. The
   deterministic normalizer must canonicalize recognized names by ID and retain
   unknown values for the registry validator to reject according to the current
   validation architecture.
7. Add a registry membership validator to the item occurrence extract and
   normalize chains. Update shape, invariant, source-reference, relatedness,
   merge, evidence, clone, metadata, warning, and ordering behavior for
   `item_id` and the new envelope.
8. Update the maintained complete example immediately: produce
   `dnd/item-registry` in the earlier registry step and bind its generated
   artifact as `item_registry` to the still-current `dnd/item-events` extract
   and normalize stages. Keep the example valid at the end of this stage.
9. Update `docs/integrations/dnd-item-event-artifacts.md` in place to the target
   durable shape and required registry behavior while retaining the current
   producer key until Stage 11.

### Acceptance criteria

- No production Go code uses an `ItemEvent*` artifact type.
- Every item occurrence carries an exact item-registry ID/name pair.
- Extraction and normalization both declare the identical required registry
  slot; validation detects unknown and mismatched identities.
- The complete example resolves with an earlier item registry and no same-step
  dependency.

### Validation

```sh
go test ./internal/modules/dnd/itemevents ./internal/modules/dnd/codec/itemevents ./internal/modules/dnd/extract/itemevents ./internal/modules/dnd/normalize/itemevents ./internal/modules/dnd/validate/itemevents/... ./internal/modules/dnd/register ./internal/modules/integration/...
OPENROUTER_API_KEY=validation-placeholder go run ./cmd/notarius config validate --config examples/dnd-complete.config.yml --pipeline dnd-session
git diff --check
```

## Stage 11: Move Item Occurrences Into Their Canonical Namespace

### Goal

Complete the item migration by replacing item-event module, package, prompt,
validator, asset, and documentation vocabulary.

### Required changes

1. Move `internal/modules/dnd/itemevents` to `itemoccurrences` and move codec,
   extract, normalize, and validate packages to the corresponding
   `itemoccurrences` paths.
2. Set extract and normalize keys to `dnd/item-occurrences`; rename validator
   keys and the provided capability to `dnd.item_occurrences`.
3. Move `assets/dnd/item-events` to `assets/dnd/item-occurrences`. Use prompt ID
   `dnd.item_occurrences`, the target private schema identity from Stage 10, and
   occurrence terminology throughout module instructions.
4. Update all imports, central registrations, default chains, metadata,
   fingerprints, prompt composition cases, configuration keys, example lanes,
   generated references, output expectations, and tests.
5. Rename the integration contract to
   `docs/integrations/dnd-item-occurrence-artifacts.md` and update links from
   item registry, configuration, internal D&D, and JSON output documentation.
6. Delete every old `itemevents`, `item-events`, `dnd/item-events`,
   `dnd.item_events`, `notarius.dnd.item_events`, `events`-envelope fixture, and
   old schema/asset path that represented the retired artifact. Do not remove
   unrelated uses of the ordinary word “event” in other D&D lanes.

### Acceptance criteria

- `dnd/item-occurrences` is the only selectable item occurrence key.
- All item occurrence packages, prompts, validators, schemas, and docs use the
  canonical vocabulary.
- The complete example binds `item_registry` into both stages of the renamed
  lane.
- No compatibility alias for item events remains.

### Validation

```sh
go test ./internal/modules/dnd/... ./internal/modules/integration/... ./internal/cli/...
OPENROUTER_API_KEY=validation-placeholder go run ./cmd/notarius config validate --config examples/dnd-complete.config.yml --pipeline dnd-session
git diff --check
```

## Stage 12: Verify The Complete Ordered Entity Pipeline

### Goal

Make the maintained complete configuration and representative integration tests
prove the final three-registry/three-occurrence architecture.

### Required changes

1. Finalize `examples/dnd-complete.config.yml` with an earlier registry step
   containing `npc-registry`, `location-registry`, and `item-registry` lanes
   alongside scene descriptions. The next step contains NPC, location, and item
   occurrences plus spell and combat lanes. The derived enemy step consumes
   `npc_registry`, `npc_occurrences`, combat turns, and scene descriptions.
2. Use generated-reference aliases `npc_registry`, `location_registry`,
   `item_registry`, and `npc_occurrences`. Preserve explicit step-and-lane
   selectors and stage-local spell-catalog bindings. Do not infer dependencies
   from matching names.
3. Update scene-plan eligibility configuration for all renamed lane labels while
   preserving the shared chunk plan and exact combat-scene routing semantics.
4. Add or revise one representative integration workflow that executes the
   ordered handoffs with deterministic fake LLM responses and proves exact
   artifact-kind compatibility, registry projection, independent occurrence
   evidence, and final publication for all three entity families.
5. Test compatible external path references for each registry through existing
   reference preparation. Do not duplicate the framework's exhaustive topology
   and failure-propagation cases; add only D&D-specific compatibility coverage.
6. Test rejection of a wrong registry artifact kind and invalid same-step or
   forward binding at the existing resolver/integration boundary if those risks
   are not already credibly covered with the new kinds.
7. Update CLI maintained-example contract tests and production catalog tests so
   both D&D examples load, validate, resolve, and list only implemented module
   and validator identities.

### Acceptance criteria

- The complete example validates offline and resolves all generated handoffs.
- A representative assembled run proves all three registry-to-occurrence paths.
- External compatible registry artifacts remain supported.
- Cross-family integration tests protect compatibility without repeating
  package-level identity and validator cases.

### Validation

```sh
go test ./internal/modules/integration/... ./internal/cli/... ./internal/modules/dnd/register
go run ./cmd/notarius config validate --config examples/dnd-minimal.config.yml --pipeline dnd-session
OPENROUTER_API_KEY=validation-placeholder go run ./cmd/notarius config validate --config examples/dnd-complete.config.yml --pipeline dnd-session
git diff --check
```

## Stage 13: Consolidate Current Documentation And Examples

### Goal

Bring every canonical current-behavior document into alignment with the final
implemented entity family without duplicating contracts across documents.

### Required changes

1. Update `docs/config.md` as the canonical owner of selectable module keys,
   validator keys, default chains, reference slots, requiredness, size limits,
   execution classes, profile behavior, and ordered binding examples.
2. Update `docs/internal/dnd.md` for ten final D&D artifact lanes, the three
   registry normalizers, source-free ID/name projections, occurrence grounding,
   package/asset ownership, and lane-specific differences. Link to integration
   contracts instead of repeating their schemas or categories.
3. Update `docs/internal/modules.md`, `docs/internal/overview.md`, README, CLI,
   operations, JSON output, and other current documents only where they actually
   name affected modules, artifacts, examples, or links. Preserve each
   document's canonical scope under the documentation policy.
4. Ensure the six target integration contracts are the only canonical durable
   entity contracts:
   `dnd-npc-registry-artifacts.md`, `dnd-npc-occurrence-artifacts.md`,
   `dnd-location-registry-artifacts.md`,
   `dnd-location-occurrence-artifacts.md`,
   `dnd-item-registry-artifacts.md`, and
   `dnd-item-occurrence-artifacts.md`.
5. Each integration contract must define its exact kind, schema ID/name/version,
   media type, strict wire shape, identity or occurrence policy, evidence
   boundary, normalization behavior, consumers, and compatibility statement.
   Cross-link rather than copying common reference or output semantics.
6. Verify that only `examples/dnd-minimal.config.yml` and
   `examples/dnd-complete.config.yml` remain as maintained D&D configurations,
   that both are secret-free, and that documentation links to rather than
   reproduces them.
7. Keep future or historical discussion in `docs/roadmap/` and ADRs. Do not
   rewrite accepted ADRs as current reference documentation, add a changelog,
   or delete the active roadmap files.

### Acceptance criteria

- Current docs contain only implemented final names and contracts.
- Each volatile fact has one canonical owner and all links resolve.
- Maintained examples are valid, copyable, and tested.
- No documentation claims compatibility with the retired pre-release names.

### Validation

```sh
go test ./internal/cli/... ./internal/modules/integration/...
go run ./cmd/notarius config validate --config examples/dnd-minimal.config.yml --pipeline dnd-session
OPENROUTER_API_KEY=validation-placeholder go run ./cmd/notarius config validate --config examples/dnd-complete.config.yml --pipeline dnd-session
git diff --check
```

## Stage 14: Final Contract Audit And Repository Verification

### Goal

Perform a final gap audit, remove stale implementation remnants, and prove the
completed feature at repository scope.

### Required changes

1. Search production code, assets, tests, examples, and current documentation
   for retired exact identities, including:
   `dnd/npcs`, `dnd/npc-interactions`, `dnd/locations`, `dnd/item-events`,
   `dnd/npc-list`, `dnd/npc-interaction-list`, `dnd/location-list`,
   `dnd/item-event-list`, old schema IDs/names, old prompt IDs, old capability
   names, `common-dnd-npcs.md`, and old reference-slot names. Exclude
   `docs/roadmap/`, where migration context intentionally names the old values.
2. Search for retired Go artifact identifiers `NPCList`, `NPCInteraction`,
   `LocationList`, and `ItemEvent`. Distinguish legitimate words or unrelated
   concepts from compatibility remnants; remove every actual old contract or
   alias.
3. Verify the final package and asset trees contain only canonical registry and
   occurrence stage packages. Confirm every D&D prompt subtree has
   `prompt.yaml` and `instructions.md`, manifests select only existing assets,
   and all prompt/schema fingerprints are content-safe.
4. Verify every private D&D response schema has `additionalProperties: false`,
   includes every declared property in `required`, and uses the exact final
   private identity. Verify durable schemas independently through their codecs;
   do not conflate private and durable contracts.
5. Review default validator chains for all six entity modules. Each registry
   must have shape, identity, source-reference, schema, and relatedness coverage;
   each occurrence must additionally enforce exact registry membership and
   domain invariants at the appropriate stages.
6. Review tests against `docs/policy/testing.md`. Consolidate redundant
   cross-layer assertions, retain regression coverage for identities and
   handoffs, and do not add asset inventories or exact prefix-length detectors.
7. Run repository-wide tests, vet, build, example validation, and whitespace
   checks. Fix any defect discovered; do not merely document it as follow-up if
   it is within this roadmap's scope.
8. Report any optional live-model observations separately. Live model quality
   is useful human evaluation but is not a credentialed automated acceptance
   gate and must not block deterministic completion.

### Acceptance criteria

- The target end state in `entity-registries.md` is fully implemented.
- No production compatibility alias or stale current-behavior name remains.
- All six entity modules register with exact typed contracts and default chains.
- Both maintained configurations validate offline.
- The full repository test, vet, and build checks pass with a clean diff check.

### Validation

```sh
go test -count=1 ./...
go vet ./...
go build ./cmd/notarius
go run ./cmd/notarius config validate --config examples/dnd-minimal.config.yml --pipeline dnd-session
OPENROUTER_API_KEY=validation-placeholder go run ./cmd/notarius config validate --config examples/dnd-complete.config.yml --pipeline dnd-session
git diff --check
```
