diff --git a/README.md b/README.md index ce75c18..51906af 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,9 @@ Notarius is a Go CLI for turning source material into structured artifacts with configured extraction pipelines. The implemented D&D workflow reads Seriatim -transcript JSON and can produce scene descriptions, item and currency events, -NPC identities, combat turns, NPC interactions, enemy events, and spell casts. +transcript JSON and can produce location registries and occurrences, scene +descriptions, item and currency events, NPC identities, combat turns, NPC +interactions, enemy events, and spell casts. ## Quickstart diff --git a/docs/config.md b/docs/config.md index 833e2ba..15ef272 100644 --- a/docs/config.md +++ b/docs/config.md @@ -370,14 +370,16 @@ selected target declares them: | **players** | Optional text player context. | | **glossary** | Optional text campaign glossary. | | **spell_catalog** | Optional JSON spell-catalog overlay for spell extraction and normalization. See [spell-catalog overlays](integrations/dnd-spell-catalog-overlays.md). | +| **locations** | Required normalized location registry for location-occurrence extraction and normalization. | | **npcs** | Normalized NPC registry. Optional for spells and combat turns; required for NPC interactions and enemy-event extraction and normalization. | | **scene_descriptions** | Required normalized scene-description artifact for combat-turn and enemy-event extraction. | | **combat_turns** | Required normalized combat-turn artifact for enemy-event extraction. | | **npc_interactions** | Required normalized NPC-interaction artifact for enemy-event extraction. | -Enemy-event artifact slots have the following exact binding contract. Durable -event semantics and wire shape remain in the -[enemy-event artifact contract](integrations/dnd-enemy-event-artifacts.md). +Location-occurrence and enemy-event artifact slots have the following exact +binding contracts. Durable semantics and wire shapes remain in their +[location-occurrence](integrations/dnd-location-occurrence-artifacts.md) and +[enemy-event](integrations/dnd-enemy-event-artifacts.md) contracts. | Slot | Accepted artifact kind | Media type | Maximum size | Required stage | | --- | --- | --- | --- | --- | @@ -385,13 +387,16 @@ event semantics and wire shape remain in the | `scene_descriptions` | `dnd/scene-description-list` | `application/json` | 1,048,576 bytes | extract only | | `combat_turns` | `dnd/combat-turn-list` | `application/json` | 1,048,576 bytes | extract only | | `npc_interactions` | `dnd/npc-interaction-list` | `application/json` | 1,048,576 bytes | extract only | +| `locations` | `dnd/location-list` | `application/json` | 1,048,576 bytes | location-occurrence extract and normalize | Scene descriptions accept **party**, **players**, and **glossary**, but not **roster**. NPC interactions require **npcs** for both extraction and normalization. Combat turns require **scene_descriptions** for extraction; the -normalized combat-turn module may use optional **npcs**. Enemy-event extraction -requires all four JSON artifact slots; its normalizer requires **npcs**. The -complete example shows the ordered generated bindings. +normalized combat-turn module may use optional **npcs**. Location occurrences +require **locations** for extraction and normalization. Enemy-event extraction +requires all four of its JSON artifact slots; its normalizer requires **npcs**. +The [complete example](../examples/dnd-complete.config.yml) shows the ordered +generated bindings. ## Production Module Keys @@ -399,11 +404,17 @@ complete example shows the ordered generated bindings. | --- | --- | | Input | **seriatim** | | Chunk | **generic**, **dnd/scenes** | -| Extract | **dnd/spells**, **dnd/npcs**, **dnd/combat-turns**, **dnd/item-events**, **dnd/npc-interactions**, **dnd/scene-descriptions**, **dnd/enemy-events** | +| Extract | **dnd/spells**, **dnd/npcs**, **dnd/combat-turns**, **dnd/item-events**, **dnd/npc-interactions**, **dnd/scene-descriptions**, **dnd/enemy-events**, **dnd/locations**, **dnd/location-occurrences** | | Merge | **appendorder** | -| Normalize | **noop**, **dnd/spells**, **dnd/npcs**, **dnd/combat-turns**, **dnd/item-events**, **dnd/npc-interactions**, **dnd/scene-descriptions**, **dnd/enemy-events** | +| Normalize | **noop**, **dnd/spells**, **dnd/npcs**, **dnd/combat-turns**, **dnd/item-events**, **dnd/npc-interactions**, **dnd/scene-descriptions**, **dnd/enemy-events**, **dnd/locations**, **dnd/location-occurrences** | | Output | **json** | +`dnd/locations` extraction and normalization are `llm_backed`; location +normalization may use the pipeline's selected LLM profile for bounded duplicate +proposals. `dnd/location-occurrences` extraction is `llm_backed`, while its +normalizer is `deterministic`. The complete example binds the registry in one +step and the occurrence lane in the next. + The D&D artifact contracts define each emitted schema: [spells](integrations/dnd-spell-artifacts.md), [NPCs](integrations/dnd-npc-artifacts.md), @@ -411,7 +422,9 @@ The D&D artifact contracts define each emitted schema: [combat turns](integrations/dnd-combat-turn-artifacts.md), [item events](integrations/dnd-item-event-artifacts.md), [scene descriptions](integrations/dnd-scene-description-artifacts.md), and -[enemy events](integrations/dnd-enemy-event-artifacts.md). +[enemy events](integrations/dnd-enemy-event-artifacts.md), +[locations](integrations/dnd-location-artifacts.md), and +[location occurrences](integrations/dnd-location-occurrence-artifacts.md). ## Production Validator Keys And Default Chains @@ -427,6 +440,8 @@ Available validator keys are: | NPC interactions | **extract/dnd/npc-interactions/shape**, **extract/dnd/npc-interactions/registry**, **extract/dnd/npc-interactions/source_refs**, **extract/dnd/npc-interactions/source_relatedness**, **normalize/dnd/npc-interactions/invariants** | | Scene descriptions | **extract/dnd/scene-descriptions/shape**, **extract/dnd/scene-descriptions/source_refs**, **extract/dnd/scene-descriptions/source_relatedness**, **normalize/dnd/scene-descriptions/invariants** | | Enemy events | **extract/dnd/enemy-events/shape**, **extract/dnd/enemy-events/engagements**, **extract/dnd/enemy-events/source_refs**, **extract/dnd/enemy-events/source_relatedness**, **normalize/dnd/enemy-events/invariants** | +| Locations | **extract/dnd/locations/shape**, **extract/dnd/locations/source_refs**, **extract/dnd/locations/source_relatedness**, **normalize/dnd/locations/identity** | +| Location occurrences | **extract/dnd/location-occurrences/shape**, **extract/dnd/location-occurrences/registry**, **extract/dnd/location-occurrences/source_refs**, **extract/dnd/location-occurrences/source_relatedness**, **normalize/dnd/location-occurrences/invariants** | When no override is configured, production D&D bindings use the following ordered chains. Each row lists extract then normalize; spell chains are the @@ -441,6 +456,8 @@ same at both stages. | NPC interactions | generic/valid_json, extract/dnd/npc-interactions/shape, extract/dnd/npc-interactions/registry, extract/dnd/npc-interactions/source_refs, generic/valid_json_schema, extract/dnd/npc-interactions/source_relatedness | generic/valid_json, extract/dnd/npc-interactions/shape, extract/dnd/npc-interactions/registry, normalize/dnd/npc-interactions/invariants, extract/dnd/npc-interactions/source_refs, generic/valid_json_schema, extract/dnd/npc-interactions/source_relatedness | | Scene descriptions | generic/valid_json, extract/dnd/scene-descriptions/shape, extract/dnd/scene-descriptions/source_refs, generic/valid_json_schema, extract/dnd/scene-descriptions/source_relatedness | generic/valid_json, extract/dnd/scene-descriptions/shape, normalize/dnd/scene-descriptions/invariants, extract/dnd/scene-descriptions/source_refs, generic/valid_json_schema, extract/dnd/scene-descriptions/source_relatedness | | Enemy events | generic/valid_json, extract/dnd/enemy-events/shape, extract/dnd/enemy-events/engagements, extract/dnd/enemy-events/source_refs, generic/valid_json_schema, extract/dnd/enemy-events/source_relatedness | generic/valid_json, extract/dnd/enemy-events/shape, normalize/dnd/enemy-events/invariants, extract/dnd/enemy-events/source_refs, generic/valid_json_schema, extract/dnd/enemy-events/source_relatedness | +| Locations | generic/valid_json, extract/dnd/locations/shape, extract/dnd/locations/source_refs, generic/valid_json_schema, extract/dnd/locations/source_relatedness | generic/valid_json, extract/dnd/locations/shape, normalize/dnd/locations/identity, extract/dnd/locations/source_refs, generic/valid_json_schema, extract/dnd/locations/source_relatedness | +| Location occurrences | generic/valid_json, extract/dnd/location-occurrences/shape, extract/dnd/location-occurrences/registry, extract/dnd/location-occurrences/source_refs, generic/valid_json_schema, extract/dnd/location-occurrences/source_relatedness | generic/valid_json, extract/dnd/location-occurrences/shape, extract/dnd/location-occurrences/registry, normalize/dnd/location-occurrences/invariants, extract/dnd/location-occurrences/source_refs, generic/valid_json_schema, extract/dnd/location-occurrences/source_relatedness | Chains are only registered for the D&D extract and normalize modules shown above; select an explicit override when a different compatible chain is diff --git a/docs/integrations/dnd-location-artifacts.md b/docs/integrations/dnd-location-artifacts.md new file mode 100644 index 0000000..d3298b3 --- /dev/null +++ b/docs/integrations/dnd-location-artifacts.md @@ -0,0 +1,89 @@ +# D&D Location Artifact + +This contract defines the durable, source-grounded location registry produced +by `dnd/locations`. It records transcript-established physical places for one +source document; it is not a map, location hierarchy, campaign-wide world +registry, or location description. + +## Identity and compatibility + +| Property | Value | +| --- | --- | +| Artifact kind | `dnd/location-list` | +| Schema ID | `notarius.dnd.locations` | +| Schema name | `notarius_dnd_locations_v1` | +| Schema version | `v1` | +| Media type | `application/json` | +| Identity policy | `dnd.locations.identity.v1` | + +`v1` accepts one strict JSON object with required `locations`; the array may be +empty. Location and source-reference objects reject unknown fields. An +incompatible artifact shape or identity-policy change uses a new version or +policy. + +## Wire shape and identity + +Each location has these required fields: + +| Field | Contract | +| --- | --- | +| `id` | `location:sha256:` followed by 64 lowercase hexadecimal characters. | +| `name` | Non-empty transcript-established display name. | +| `source_refs` | One or more transcript evidence ranges that identify the place. | + +A source reference has exactly `source_id`, `start_unit_id`, and `end_unit_id`. +The source ID identifies the transcript, unit IDs are positive inclusive unit +identifiers, and the start may not follow the end. + +```json +{ + "locations": [ + { + "id": "location:sha256:5c1a91f15729df0b8c257093865fdf2452b43c215375e8cf2341aa9c37bb99aa", + "name": "Moon Gate", + "source_refs": [ + {"source_id": "session-7", "start_unit_id": 4, "end_unit_id": 5} + ] + } + ] +} +``` + +The ID is deterministic and scoped to the source document. Notarius normalizes +the display name for comparison with Unicode NFKC, supported apostrophe +normalization, collapsed whitespace, and case folding. It hashes compact JSON +for this array, using the earliest canonical source reference as the anchor: + +```text +["dnd.locations.identity.v1", comparison_name, source_id, start_unit_id, end_unit_id] +``` + +The canonical ID is the lowercase SHA-256 digest of those bytes with the +`location:sha256:` prefix. Equal display names are allowed when their evidence +anchors differ, so a generic name does not force distinct places to collapse. + +## Scope, reconciliation, and evidence + +Locations are physical or spatial places established by the transcript, such +as planes, regions, settlements, districts, buildings, rooms, landmarks, +routes, and geographic features. A generic label is permitted only when it +identifies a specific place in the transcript. Notarius does not infer an +unstated place or add hierarchy, coordinates, descriptions, participants, or +ownership. + +Normalization first applies deterministic display, evidence, and ID rules. It +then may use a bounded LLM-assisted proposal to reconcile semantically duplicate +records. The proposal is validated and applied conservatively; invalid or +unusable proposals retain the deterministic result with retry or fallback +diagnostics. The registry's source references establish registry provenance, +not evidence for later artifacts. + +## Consumers and publication + +`dnd/location-occurrences` requires one approved location registry through its +`locations` reference slot. Its prompt receives an ordered source-free `{id, +name}` projection and must not treat registry references as occurrence +evidence. See the [location-occurrence artifact](dnd-location-occurrence-artifacts.md) +for that contract, [Configuration](../config.md#references-and-ordered-handoffs) +for binding rules, and the [JSON output contract](json-output.md) for +publication. diff --git a/docs/integrations/dnd-location-occurrence-artifacts.md b/docs/integrations/dnd-location-occurrence-artifacts.md new file mode 100644 index 0000000..6d1da68 --- /dev/null +++ b/docs/integrations/dnd-location-occurrence-artifacts.md @@ -0,0 +1,80 @@ +# D&D Location-Occurrence Artifact + +This contract defines the durable occurrence list produced by +`dnd/location-occurrences`. It records source-grounded ways the party relates +to locations in a required normalized location registry; it does not extend +that registry or infer a place absent from it. + +## Identity and compatibility + +| Property | Value | +| --- | --- | +| Artifact kind | `dnd/location-occurrence-list` | +| Schema ID | `notarius.dnd.location_occurrences` | +| Schema name | `notarius_dnd_location_occurrences_v1` | +| Schema version | `v1` | +| Media type | `application/json` | + +`v1` accepts one strict JSON object with required `occurrences`; the array may +be empty. Occurrence and source-reference objects reject unknown fields. An +incompatible shape change requires a new schema version. + +## Wire shape + +Each occurrence has these required fields: + +| Field | Contract | +| --- | --- | +| `location_id` | Exact ID from the required normalized [location registry](dnd-location-artifacts.md). | +| `name` | Exact canonical display name for `location_id` in that registry. | +| `kind` | One of `visited`, `planned`, `recalled`, or `mentioned`. | +| `source_refs` | One or more current-transcript evidence ranges for this occurrence. | + +A source reference has exactly `source_id`, `start_unit_id`, and `end_unit_id`. +It identifies an inclusive range in the current transcript; unit IDs are +positive and the start may not follow the end. + +```json +{ + "occurrences": [ + { + "location_id": "location:sha256:5c1a91f15729df0b8c257093865fdf2452b43c215375e8cf2341aa9c37bb99aa", + "name": "Moon Gate", + "kind": "visited", + "source_refs": [ + {"source_id": "session-7", "start_unit_id": 12, "end_unit_id": 13} + ] + } + ] +} +``` + +## Occurrence categories + +| Kind | Meaning | +| --- | --- | +| `visited` | The transcript establishes physical party presence, including arrival, continuing presence, or departure. | +| `planned` | The party explicitly proposes, intends, or agrees to future travel; speculation alone is not enough. | +| `recalled` | The transcript explicitly recounts prior party presence before the current live events. | +| `mentioned` | The location is explicit but no stronger category applies, including lore, directions, third-party activity, or out-of-character discussion. | + +For overlapping evidence, precedence is `visited`, then `planned`, then +`recalled`, then `mentioned`. Inferred locations are omitted. Normalization +canonicalizes the registry name, orders and deduplicates source references, and +orders occurrences by source chronology, location ID, name, kind, and reference +sequence. It collapses only exact duplicates with the same ID, kind, and +complete canonical evidence sequence. + +## Required grounding and evidence + +Both extraction and normalization require exactly one `locations` reference of +kind `dnd/location-list`, media type `application/json`, and at most 1 MiB. The +registry provides identity grounding only: unknown IDs and mismatched ID/name +pairs are rejected rather than guessed or reassigned. The current transcript is +the only evidence source for an occurrence; registry evidence and provenance +never become occurrence evidence. + +See [Configuration](../config.md#d-d-reference-slots) for the selectable slot +and generated-handoff compatibility, [D&D module internals](../internal/dnd.md) +for implementation behavior, and the [JSON output contract](json-output.md) +for publication. diff --git a/docs/integrations/json-output.md b/docs/integrations/json-output.md index 627f698..b10cc32 100644 --- a/docs/integrations/json-output.md +++ b/docs/integrations/json-output.md @@ -76,7 +76,9 @@ than infer a lane schema from its name. The current D&D payload contracts are [combat turns](dnd-combat-turn-artifacts.md), [item events](dnd-item-event-artifacts.md), [scene descriptions](dnd-scene-description-artifacts.md), and -[enemy events](dnd-enemy-event-artifacts.md). +[enemy events](dnd-enemy-event-artifacts.md), +[locations](dnd-location-artifacts.md), and +[location occurrences](dnd-location-occurrence-artifacts.md). ## `manifest.json` diff --git a/docs/internal/dnd.md b/docs/internal/dnd.md index 896c487..73401c2 100644 --- a/docs/internal/dnd.md +++ b/docs/internal/dnd.md @@ -7,7 +7,7 @@ selectable keys, bindings, reference syntax, and default validator chains. ## Durable Artifact Contracts -The seven lanes have separate durable wire contracts. This guide deliberately +The nine lanes have separate durable wire contracts. This guide deliberately does not repeat their JSON shapes or schemas. | Lane | Durable contract | @@ -19,6 +19,8 @@ does not repeat their JSON shapes or schemas. | NPC interactions | [NPC-interaction artifacts](../integrations/dnd-npc-interaction-artifacts.md) | | Scene descriptions | [scene-description artifacts](../integrations/dnd-scene-description-artifacts.md) | | Enemy events | [enemy-event artifacts](../integrations/dnd-enemy-event-artifacts.md) | +| Locations | [location artifacts](../integrations/dnd-location-artifacts.md) | +| Location occurrences | [location-occurrence artifacts](../integrations/dnd-location-occurrence-artifacts.md) | ## Family Composition @@ -26,9 +28,9 @@ The D&D registrar registers the family’s artifact codecs, extractors, typed append-order mergers, normalizers, validators, prompt assets, fallback LLM profile asset, and default validator chains. Each extractor and normalizer has a stable module spec, explicit execution class, strict option decoding, and a -typed builder. Scene chunking, every extractor, and NPC normalization are -registered as `llm_backed`; the remaining current D&D mergers and normalizers -are `deterministic`. The metadata is available to catalog inspection and +typed builder. Scene chunking, every extractor, NPC normalization, and location +normalization are registered as `llm_backed`; the remaining current D&D mergers +and normalizers are `deterministic`. The metadata is available to catalog inspection and resolved-pipeline debug data and determines which selected bindings inherit the pipeline profile. Configuration remains the canonical owner of the exact keys, profile precedence, and validator order. @@ -42,10 +44,10 @@ the contracts above define durable data. ## Prompt Construction D&D extractors assemble prompts from an ordered manifest of shared and -module-owned assets. Reuse the shared D&D system, evidence, identity, -reference, and transcript assets instead of copying their text into individual -modules. A manifest’s declared sequence, including cache-control placement, is -part of the prompt behavior. +module-owned assets. The location extractor and occurrence extractor reuse the +shared D&D system, evidence, identity, reference, and transcript assets instead +of copying their text into individual modules. A manifest’s declared sequence, +including cache-control placement, is part of the prompt behavior. Every maintained D&D LLM prompt selects `dnd-extraction` as its default profile. The D&D registrar embeds that fallback profile with the maintained @@ -72,7 +74,9 @@ remains stable. The other D&D LLM prompts intentionally follow different patterns. Scene chunking has no sibling extraction lane with which to share its full transcript, so it renders campaign references before its task and instructions, then places -the cacheable full transcript last. NPC normalization keeps its task and +the cacheable full transcript last. NPC and location normalization share the +entity-reconciliation response schema and safety boundary while retaining their +own task and identity rules. NPC normalization keeps its task and cacheable instructions before the candidate collection, followed by the cacheable transcript windows: candidates must be available before their supporting evidence is evaluated, and those windows are not a cross-lane @@ -108,13 +112,14 @@ combine results from distinct scenes, so it intentionally does not apply that rule. Configuration owns the exact validator key and chain position. Normalizers are deterministic for spells, combat turns, item events, NPC -interactions, scene descriptions, and enemy events. They canonicalize display +interactions, scene descriptions, enemy events, and location occurrences. They canonicalize display values and evidence, use source-document order for stable output, and issue -bounded warnings for changes or collapsed duplicates. The NPC normalizer is -the intentional exception: it first produces a deterministic candidate set, -then uses a bounded structured-LLM proposal to reconcile identity groups. -Invalid or unusable proposals retain the deterministic result and surface retry -or fallback diagnostics; the model does not directly replace durable records. +bounded warnings for changes or collapsed duplicates. The NPC and location +normalizers are intentional exceptions: each first produces a deterministic +candidate set, then may use a bounded structured-LLM proposal to reconcile +identity groups. Invalid or unusable proposals retain the deterministic result +and surface retry or fallback diagnostics; the model does not directly replace +durable records. ## Generated References And Grounding @@ -130,8 +135,11 @@ they do not supply evidence. Scene-description registries are eligibility-only projections: they retain the current chunk’s classification data, not scene prose or evidence, and exist to route combat extraction. Enemy-event extraction also projects combat turns to `actor` and `turn_kind` and filters NPC -interactions to `combat_opponent` names and kinds. These compact projections, -like NPC grounding, are source-free guidance and never event evidence. +interactions to `combat_opponent` names and kinds. Location registries project +ordered `{id, name}` pairs to location-occurrence extraction and normalization; +exact ID/name matching keeps same-name locations distinguishable. These compact +projections, like NPC grounding, are source-free guidance and never event +evidence. ## Lane-Specific Rules @@ -147,6 +155,8 @@ shared helper changes. | NPC interactions | Requires the normalized NPC registry at extraction and normalization, using it for canonical actor grounding only. | | Scene descriptions | Produces the classifications consumed by combat routing; it does not consume an NPC registry or provide evidence for combat artifacts. | | Enemy events | Requires NPC, scene-description, combat-turn, and NPC-interaction artifacts. It calls the LLM only for an exact `combat` classification, records ordered observations rather than terminal state, and normalizes recognized names through the NPC registry while preserving grounded collective labels. | +| Locations | Produces a source-anchored, session-scoped registry. Its LLM-assisted reconciliation is proposal-only and never collapses same-name places without validated identity and evidence rules. | +| Location occurrences | Requires the normalized location registry for both extraction and normalization. It classifies only transcript-established occurrences as visited, planned, recalled, or mentioned; the deterministic normalizer enforces exact registry grounding and never turns registry provenance into occurrence evidence. | The combat and scene-description contracts describe their exact handoff and empty-result behavior in more detail: diff --git a/docs/roadmap/future.md b/docs/roadmap/future.md index d308902..c830d35 100644 --- a/docs/roadmap/future.md +++ b/docs/roadmap/future.md @@ -7,19 +7,6 @@ not as committed release dates. ## Near-Term D&D Pipeline -### Location Extraction - -- Add a D&D artifact for locations visited by the party or otherwise mentioned - in the transcript. -- Distinguish observed visits from references, plans, recalled places, and - uncertain or inferred locations so a mention alone is not reported as a - visit. -- Preserve transcript evidence for each visit or mention and reconcile aliases, - nested places, and repeated appearances without collapsing distinct - locations that share a generic name. -- Define how the location artifact should ground later narrative reports and - whether future event artifacts should retain canonical location identities. - ### Evaluate Spell Extraction And Normalization - Evaluate ordinary extraction retries and the completed normalization path