Files
notarius/docs/integrations/dnd-combat-turn-artifacts.md

171 lines
7.8 KiB
Markdown

# D&D Combat-Turn Artifact Contract
This document defines the durable artifact, serialization, extraction,
candidate-validation, normalization, and production lane boundaries for D&D
combat turns.
## Artifact identity
| Property | Value |
| --- | --- |
| Artifact kind | `dnd/combat-turn-list` |
| Schema ID | `notarius.dnd.combat_turns` |
| Schema name | `notarius_dnd_combat_turns_v1` |
| Schema version | `v1` |
| Media type | `application/json` |
The top-level JSON object contains the required `combat_turns` array, which
may be empty. Every object rejects unknown fields.
## JSON shape
Each combat turn contains these required fields:
| Field | Shape |
| --- | --- |
| `actor` | Non-empty string. |
| `turn_kind` | One of `turn`, `reaction`, `legendary_action`, `lair_action`, or `other`. |
| `round` | Required JSON field containing a positive integer or `null`. |
| `actions` | Required array with at least one action. |
| `summary` | Non-empty string. |
| `source_refs` | Required array with at least one source reference. |
Each action contains these required fields:
| Field | Shape |
| --- | --- |
| `category` | One of `attack`, `spell`, `movement`, `item`, `ability_check`, `saving_throw`, `condition`, or `other`. |
| `declaration` | Non-empty string describing what was declared. |
| `targets` | Required array of strings; the array may be empty, but entries may not be empty. |
| `resolution` | Required JSON field containing a non-empty string or `null`. |
Source references use the shared source-reference shape:
```json
{
"source_id": "session-alpha",
"start_unit_id": 1,
"end_unit_id": 2
}
```
`source_id` must be non-empty and both unit IDs must be positive integers. The
codec does not resolve references against a source document or enforce source
range ordering; those checks belong to the later source-reference validation
boundary.
## Codec behavior
The codec exposes two representations of the same typed artifact:
- Candidate encode/decode preserves invalid enum values, nullable values,
required-array presence, required strings, targets, and source references so
later validators can report them. Candidate decoding still requires valid
JSON, one JSON value, known fields, and the explicitly present `round` and
`resolution` keys; `null` is distinct from a missing key.
- Approved encode/decode enforces the structural rules in this contract.
The codec owns the durable JSON Schema, whose object layers all set
`additionalProperties` to `false`. Codec metadata contains only
`combat_turn_count`.
The maintained compact fixture is
`internal/modules/dnd/codec/combatturns/testdata/dnd_combat_turns.v1.json`.
## Extraction boundary
The standalone extractor uses these identities:
| Property | Value |
| --- | --- |
| Extractor key | `dnd/combat-turns` |
| Capability | `dnd.combat_turns` |
| Prompt ID | `dnd.combat_turns` |
| Prompt version | `v1` |
| Private response-schema key | `dnd_combat_turns_llm` |
| Private response-schema ID | `notarius.dnd.combat_turns.llm` |
| Default profile | `gemini-2-flash` |
It requires `chunks` and `source.transcript`, accepts no options, and makes one
structured completion for each supplied chunk. The prompt receives the
chunk-scoped transcript plus the existing `players`, `party`, and `glossary`
inputs, and optionally the deprecated `roster` reference through the shared
party mapping. The optional `npcs` reference is an approved normalized NPC
artifact used only for identity grounding; it never supplies combat evidence.
The private response envelope has the same fields and JSON types as the durable
turn/action shape except that source references contain only `start_unit_id`
and `end_unit_id`. It enforces required and nullable field presence, types, and
unknown-field rejection, while deterministic validators own enum membership,
non-empty values and collections, and positive-number requirements. The
extractor assigns the current source ID, removes exact duplicate ranges, and
stable-sorts turns by the earliest valid source-document position. Numeric unit
IDs are identifiers; source-document slice position determines chronology.
Semantically malformed candidate fields remain in the typed result for the
configured validation and retry boundary.
## Deterministic candidate validation
The standalone validator keys are:
| Validator | Responsibility |
| --- | --- |
| `extract/dnd/combat-turns/shape` | Required arrays, strings, nullable fields, positive rounds, and supported enum values. |
| `extract/dnd/combat-turns/source_refs` | Source identity, source-unit existence, and range order through the source document. |
| `extract/dnd/combat-turns/source_relatedness` | At most one advisory warning per turn when the actor or declared action is not related to cited transcript text. |
Source-reference and relatedness validators defer malformed shape to the shape
validator. Relatedness also defers when any cited source range is invalid. It
combines overlapping cited ranges once in document order, compares actors with
the shared Unicode-aware NPC identity policy, and checks declaration tokens of
at least four Unicode code points against complete cited-text tokens. Targets
are not checked deterministically.
The production D&D registrar exposes the extractor and these validators. Its
default extraction chain preserves this order: JSON syntax, private response
schema, combat shape, source references, then source relatedness.
## Normalization boundary
The standalone normalizer uses key `dnd/combat-turns`, requires `merged`,
provides `normalized`, accepts no options, and accepts only the optional
structured `npcs` reference. Campaign references are LLM extraction context and
are not normalizer inputs. The NPC registry is resolved during preparation;
runtime normalization uses that immutable prepared view.
Normalization policy is `dnd.combat_turns.normalize.v1`. It display-normalizes
actor, summary, declarations, targets, and non-null resolutions; canonicalizes
exact registry actor and target matches; orders and deduplicates exact source
references; stable-sorts records by earliest valid source-document position; and
collapses only records with the same actor identity, turn kind, round value, and
complete valid evidence set. The first normalized record is retained without
merging its actions or prose. Invalid evidence is never eligible for duplicate
collapse. Every mutation and collapse emits a bounded warning using the merged
input index in its scope.
The normalizer reports `normalization_policy` and `identity_policy` metadata and
fingerprints, plus `npc_registry_digest`, `npc_count`, and `npc_registry` only
when a registry is bound. The normalized-invariants validator is
`normalize/dnd/combat-turns/invariants`; it defers shape and source-reference
failures, then checks display normalization, target identity uniqueness,
canonical evidence ordering, chronology, and duplicate identity. It rejects
with `invalid_combat_turn_normalization` under policy
`dnd.combat_turns.validator.normalized.v1`.
The production D&D registrar exposes the normalizer and normalized-invariants
validator. Its default normalization chain is JSON syntax, durable schema,
combat shape, normalized invariants, source references, then source
relatedness. The lane uses the framework's typed append-order merger and has no
merge validator chain.
## Production manifest and references
The selectable lane uses extractor and normalizer key `dnd/combat-turns`,
`appendorder` for the typed merger, and the durable codec above. A bound `npcs`
reference contributes raw-file provenance to the run manifest. Prepared combat
extractor and normalizer metadata and checkpoint fingerprints contain only the
NPC registry's semantic digest and count; the registry content, path, and NPC
source ranges are not copied into combat output. The normalized lane is emitted
as `lanes/<lane-id>.json` by the JSON output module, and warnings and rejection
summaries remain in their shared companion files.