Files
notarius/docs/internal/dnd.md

210 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# D&D Module Internals
This guide records the conventions shared by the production D&D module family.
It complements [Module Internals](modules.md), which owns generic registration
and extension mechanics, and [Configuration](../config.md), which owns the
selectable keys, bindings, reference syntax, and default validator chains.
## Durable Artifact Contracts
The ten lanes have separate durable wire contracts. This guide deliberately
does not repeat their JSON shapes or schemas.
| Lane | Durable contract |
| --- | --- |
| Spells | [spell artifacts](../integrations/dnd-spell-artifacts.md) |
| NPC registry | [NPC registry artifacts](../integrations/dnd-npc-registry-artifacts.md) |
| Combat turns | [combat-turn artifacts](../integrations/dnd-combat-turn-artifacts.md) |
| Item occurrences | [item-occurrence artifacts](../integrations/dnd-item-occurrence-artifacts.md) |
| Item registry | [item-registry artifacts](../integrations/dnd-item-registry-artifacts.md) |
| NPC occurrences | [NPC-occurrence artifacts](../integrations/dnd-npc-occurrence-artifacts.md) |
| Scene descriptions | [scene-description artifacts](../integrations/dnd-scene-description-artifacts.md) |
| Enemy events | [enemy-event artifacts](../integrations/dnd-enemy-event-artifacts.md) |
| Location registry | [location-registry artifacts](../integrations/dnd-location-registry-artifacts.md) |
| Location occurrences | [location-occurrence artifacts](../integrations/dnd-location-occurrence-artifacts.md) |
## Family Composition
The D&D registrar registers the familys 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, location, and item-registry
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.
Private structured-LLM response schemas are deliberately minimal. They reject
invalid JSON structure, missing required fields, incompatible types, and
unknown fields, while preserving semantic candidates for deterministic
validation. Do not promote a private response envelope into a durable schema;
the contracts above define durable data.
## Prompt Construction
D&D LLM-facing content lives beneath `assets/dnd/`. Each module contributes a
local `prompt.yaml` declaration and `instructions.md`; input-specific files
such as a catalog, registry, grounding projection, or candidate collection are
local only when that module needs them. New extractor content uses its feature
subtree, while families with both extraction and normalization content use their
`extract` and `normalize` subtrees. Shared visual-provenance fragments use
the `common-dnd-` prefix. Production lane code belongs with its D&D codec,
extractor, normalizer, and validator packages; registry projections and
identity helpers remain in their owning entity packages rather than in a
consumer lane.
The owning modules manifest is the source of truth for which local and shared
assets are selected, their mount paths, their message order, cache controls,
and the files included in its prompt fingerprint. Shared fragments belong to
the D&D shared implementation and are selected by name rather than copied into
module directories. The root `assets` package is a content-only boundary; its
physical ownership and rationale are defined by
[ADR-0011](../adr/0011-centralize-llm-assets.md).
Put each rule at its narrowest owner:
- universal behavior belongs in the shared system asset;
- D&D-family behavior belongs in a selected `common-dnd-` asset;
- rules for an input projection belong with that input asset;
- lane-specific policy belongs in the modules `instructions.md`; and
- transport-envelope shape belongs in the private response schema.
A rule is eligible for the system prompt only when every D&D LLM prompt needs
it regardless of lane, inputs, or response shape. Module instructions must not
repeat rules selected from shared assets or schemas. Reintroduce such repetition
only after observational evaluation with representative transcripts shows that
it improves results at the intended target models and cost; structural prompt
tests alone are not that evidence.
Every maintained D&D LLM prompt selects `dnd-extraction` as its default
profile. The D&D registrar registers the fallback, while an operator can
replace it with a complete profile of the same ID from the configured PromptKit
source. Deployment profile selection is documented in
[Configuration](../config.md#promptkit-profiles).
The transcript assets have distinct consumers. Scene chunking consumes the
complete-session `common-dnd-transcript-full.md`; extraction prompts consume
the current-chunk `common-dnd-transcript-chunk.md`; and NPC, location, and item
normalization consume `common-dnd-transcript-windows.md` alongside their
candidate collections. Player, party, glossary, and compatible campaign
references provide disambiguating context, not evidence. Reference material is
canonically ordered before rendering so equivalent inputs remain stable.
Extraction prompts render the common system and identity messages first, then
cached campaign references and the cached chunk transcript. Evidence policy and
any lane-specific registry, catalog, or grounding projection follow that
prefix. The final module instructions message is ephemeral. This keeps the
reusable extraction prefix identical while preserving the lane-specific suffix.
Scene chunking intentionally uses a different order: system, cached campaign
references, uncached module instructions, then the final ephemeral full
transcript. Entity normalization also has its own order: system, uncached
module instructions, ephemeral reconciliation policy, uncached candidates, and
final ephemeral transcript windows. These orders and cache controls are prompt
behavior; change them only through the owning manifest and prompt declaration.
## Evidence, Candidates, And Normalization
The current transcript is the only durable evidence source. Extractors assign
the current source identity, preserve candidate evidence ranges for validators,
and canonically order or remove exact duplicate ranges without asking the
model to repair semantic errors. Campaign context and generated artifacts may
ground names or control routing, but they never establish evidence for a D&D
result.
Default chains keep responsibilities separate: structural validators assess the
candidate, source-reference validators resolve cited ranges against the current
source, durable-schema validation checks an approved representation, and
relatedness validators report advisory evidence concerns. The configured order
is documented in
[Configuration](../config.md#production-validator-keys-and-default-chains).
Enemy-event extraction additionally rejects a second `engaged` observation for
the same comparison identity within one scene-scoped result. Normalization may
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 occurrences, NPC
occurrences, 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. NPC,
item, and location registry normalizers are intentional exceptions: each first
produces a deterministic candidate set, then may use a bounded structured-LLM
proposal to reconcile identity groups. The proposal selects supplied
descriptors—names with their candidate source references—not durable IDs.
Request-local candidate keys may support resolution internally, but are never
included in model input or output. Colliding descriptors are ineligible, and
invalid or unusable proposals retain the deterministic result with retry or
fallback diagnostics; the model does not directly replace durable records.
## Generated References And Grounding
Normalized D&D artifacts can be handed to a later step through a generated
reference binding. The framework verifies artifact compatibility and retains
producer provenance; consumers resolve the handed-off artifact into an
immutable, validated projection for each operation. External files are checked
during preparation, while generated artifacts are resolved at the handoff.
NPC and item registry consumers receive names-only grounding. Location
consumers receive a contextual selector containing the canonical name and the
registry references needed to distinguish same-name places. The calling module
resolves those supplied selections locally and maps them into the unchanged
durable ID/name pair; an unknown or ambiguous selection rejects the complete
occurrence result rather than accepting a partial mapping. The NPC registry
additionally supplies names-only actor grounding to spells, combat turns, and
enemy events.
Registry references establish a registry identity and may disambiguate a
selection, but never become occurrence evidence. Each occurrence keeps its own
current-transcript source references, even when it was grounded through the
same registry record.
Scene descriptions are eligibility-only projections: they retain current-chunk
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 occurrences to `combat_opponent` names and kinds.
These projections are guidance only and never event evidence.
## Lane-Specific Rules
The following differences are intentional and should remain explicit when a
shared helper changes.
Shared D&D text comparison is identified by `dnd.text_comparison.v1`. Any
semantic change requires an explicit policy-version review for every affected
identity, mapping, normalization, and validator policy; helper source is not a
checkpoint fingerprint.
| Lane | Intentional behavior |
| --- | --- |
| Spells | May use a spell-catalog overlay and optional NPC grounding; the catalog validator supplies domain-specific semantic checks. |
| NPC registry | Establishes transcript-grounded NPC identities, including factual third-party mentions, without assigning occurrence categories. It does not consume an NPC registry, and its normalizer is the LLM-assisted reconciliation exception described above. |
| Combat turns | Requires a scene-description artifact. It calls the LLM only for an exact `combat` classification; exact non-combat classifications return an accepted empty result, while missing or mismatched classifications return an empty result with a bounded warning. Optional NPC grounding never becomes evidence. |
| Item occurrences | Requires the normalized item registry for exact deterministic grounding at extraction and normalization. Campaign context may disambiguate, but the registry never becomes occurrence evidence. |
| Item registry | Produces source-grounded item types and unique designations. Its LLM-assisted reconciliation is proposal-only, preserves distinct currency denominations and item types, and does not create per-instance identities. |
| NPC occurrences | Requires the normalized NPC registry at extraction and normalization, using it for canonical actor grounding only. It separately emits cited current-transcript occurrence facts, including `mentioned`, rather than deriving them from registry provenance. |
| 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-occurrence 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. |
| Location registry | Produces a source-anchored, session-scoped registry from stable proper names or unique in-world designations. 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. Its [durable occurrence categories](../integrations/dnd-location-occurrence-artifacts.md#occurrence-categories) distinguish explicit speculation from unsupported inference; 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:
[combat turns](../integrations/dnd-combat-turn-artifacts.md) and
[scene descriptions](../integrations/dnd-scene-description-artifacts.md).
The [enemy-event contract](../integrations/dnd-enemy-event-artifacts.md)
defines its durable semantics; [Configuration](../config.md) owns its
selectable bindings and validation chains.
## Focused Verification
When changing D&D behavior, test the affected codec, extractor, normalizer,
validator, prompt-asset manifest, and registry projection. Also test generated
handoffs at the integration boundary and run the full D&D module suite:
~~~sh
go test ./internal/modules/dnd/...
go test ./internal/modules/integration/...
~~~