# 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 ```