Harmonize D&D artifact contracts
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
# D&D Combat-Turn Artifact Contract
|
||||
# D&D Combat-Turn Artifact
|
||||
|
||||
This document defines the durable artifact, serialization, extraction,
|
||||
candidate-validation, normalization, and production lane boundaries for D&D
|
||||
combat turns.
|
||||
This contract defines the durable combat-action occurrence list produced by
|
||||
`dnd/combat-turns`. It records source-grounded turns and actions; it is not a
|
||||
complete initiative tracker, combat summary, or state model.
|
||||
|
||||
## Artifact identity
|
||||
## Identity and compatibility
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
@@ -14,167 +14,56 @@ combat turns.
|
||||
| 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.
|
||||
`v1` is a strict JSON object with required `combat_turns`; the array may be
|
||||
empty. Turn and source-reference objects reject unknown fields. A future
|
||||
incompatible shape requires a new schema version.
|
||||
|
||||
## JSON shape
|
||||
## Wire shape
|
||||
|
||||
Each combat turn contains these required fields:
|
||||
Each combat turn has these required fields:
|
||||
|
||||
| Field | Shape |
|
||||
| Field | Contract |
|
||||
| --- | --- |
|
||||
| `actor` | Non-empty string. |
|
||||
| `turn_kind` | One of `turn`, `reaction`, `legendary_action`, `lair_action`, or `other`. |
|
||||
| `source_refs` | Required array with at least one source reference. |
|
||||
| `actor` | Non-empty acting character or creature name. |
|
||||
| `turn_kind` | `turn`, `reaction`, `legendary_action`, `lair_action`, or `other`. |
|
||||
| `source_refs` | One or more transcript evidence ranges. |
|
||||
|
||||
Source references use the shared source-reference shape:
|
||||
Each source reference has exactly `source_id`, `start_unit_id`, and
|
||||
`end_unit_id`. It identifies an inclusive current-transcript range; unit IDs
|
||||
are positive and the start may not follow the end.
|
||||
|
||||
```json
|
||||
{
|
||||
"source_id": "session-alpha",
|
||||
"start_unit_id": 1,
|
||||
"end_unit_id": 2
|
||||
"combat_turns": [
|
||||
{
|
||||
"actor": "Mira Thorn",
|
||||
"turn_kind": "turn",
|
||||
"source_refs": [
|
||||
{"source_id": "session-7", "start_unit_id": 31, "end_unit_id": 32}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`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.
|
||||
## Eligibility, evidence, and normalized form
|
||||
|
||||
## Codec behavior
|
||||
The extractor requires an approved [scene-description artifact](dnd-scene-description-artifacts.md).
|
||||
It emits combat turns only for a chunk with an exact matching scene classified
|
||||
`combat`; an exact non-combat scene produces an accepted empty list. The scene
|
||||
record controls eligibility only: its title, summary, and reference do not
|
||||
become turn evidence. No exact matching scene also produces an empty list and
|
||||
the `scene_classification_unavailable` warning.
|
||||
|
||||
The codec exposes two representations of the same typed artifact:
|
||||
An optional normalized [NPC artifact](dnd-npc-artifacts.md) can ground an
|
||||
actor name. Its registry references are provenance, never combat evidence.
|
||||
Normalization trims and, where possible, canonicalizes actor names; orders and
|
||||
deduplicates exact source references; orders valid-evidence turns by source
|
||||
chronology; and collapses only duplicates with the same actor identity, turn
|
||||
kind, and complete valid evidence. It does not infer turns, initiative, or
|
||||
actions from registry or scene data.
|
||||
|
||||
- Candidate encode/decode preserves invalid actor and turn-kind values,
|
||||
collection presence, and source references so later validators can report
|
||||
them. Candidate decoding still requires valid JSON, one JSON value, known
|
||||
fields, and compatible JSON types.
|
||||
- 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 requires
|
||||
one `scene_descriptions` reference. That reference must be exactly one approved
|
||||
`dnd/scene-description-list` artifact with media type `application/json` and a
|
||||
maximum size of 1 MiB (1048576 bytes). It may be an external file validated
|
||||
during preparation or a canonical generated artifact supplied at an ordered
|
||||
step handoff. An unbound slot is a configuration error.
|
||||
|
||||
The scene artifact controls eligibility, not evidence. The extractor calls the
|
||||
LLM only when exactly one record has the current chunk's ID, source ID, start
|
||||
unit ID, and end unit ID, and that record has `kind: combat`. An exact
|
||||
`narrative`, `recap`, or `meta` record returns an accepted empty
|
||||
`combat_turns` array without an LLM call, warning, or retry attempt. A missing
|
||||
or mismatched exact record returns the same accepted empty result without an
|
||||
LLM call or retry and emits one content-safe
|
||||
`scene_classification_unavailable` warning. The scene artifact, its title and
|
||||
summary, and its source references are never copied into combat turns.
|
||||
|
||||
For an eligible combat 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. `scene_descriptions` is never a
|
||||
combat prompt input.
|
||||
|
||||
The private response envelope has the same turn fields and JSON types as the
|
||||
durable shape except that source references contain only `start_unit_id`
|
||||
and `end_unit_id`. It enforces required 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 list, actor, turn kind, and source references, plus supported turn-kind 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 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 and compares actors
|
||||
with the shared Unicode-aware NPC identity policy.
|
||||
|
||||
The production D&D registrar exposes the extractor and these validators. Its
|
||||
default extraction chain preserves this order: JSON syntax, combat shape,
|
||||
source references, private response schema, 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. For an external file, the NPC registry is resolved
|
||||
during preparation; for a generated binding, it is resolved at the operation-
|
||||
time handoff. Runtime normalization uses that immutable prepared or handed-off
|
||||
view.
|
||||
|
||||
Normalization policy is `dnd.combat_turns.normalize.v1`. It display-normalizes
|
||||
the actor, canonicalizes exact registry actor 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,
|
||||
and complete valid evidence set. The first normalized record is retained.
|
||||
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. An external registry may additionally contribute
|
||||
`npc_registry_digest` and `npc_count`; generated registry identity is retained
|
||||
in framework handoff provenance and dependency fingerprints. The
|
||||
normalized-invariants validator is
|
||||
`normalize/dnd/combat-turns/invariants`; it defers shape and source-reference
|
||||
failures, then checks actor display normalization, 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, combat shape,
|
||||
normalized invariants, source references, durable schema, 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. Bound external
|
||||
references contribute raw-file provenance to the run manifest. Generated
|
||||
bindings contribute artifact kind, schema identity, media type, canonical
|
||||
digest, size, and bounded producer provenance. This includes the generated
|
||||
scene-description artifact for the combat extractor. Consumer metadata and
|
||||
checkpoint fingerprints contain no reference names, content, paths, source
|
||||
ranges, scene titles, or scene summaries. For an external scene artifact,
|
||||
component metadata records the gate policy plus a semantic eligibility digest
|
||||
and record count; generated scene identity remains in framework handoff
|
||||
provenance and dependencies. The eligibility digest changes with scene ID,
|
||||
exact source range, or kind, but not with title or summary. 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.
|
||||
The [NPC-interaction artifact](dnd-npc-interaction-artifacts.md) records
|
||||
broader NPC occurrences. The [JSON output contract](json-output.md) defines
|
||||
publication, and [D&D module internals](../internal/dnd.md) describes routing
|
||||
and validation mechanics.
|
||||
|
||||
@@ -1,133 +1,78 @@
|
||||
# D&D Item-Event Artifact Contract
|
||||
# D&D Item-Event Artifact
|
||||
|
||||
This document defines the durable D&D item-event artifact and its production
|
||||
boundaries. It records source-grounded discoveries and possession changes; it
|
||||
does not maintain an inventory or ledger.
|
||||
This contract defines the durable item and currency occurrence list produced by
|
||||
`dnd/item-events`. It records source-grounded discoveries and possession
|
||||
changes; it does not maintain an inventory, balance, or ledger.
|
||||
|
||||
## Artifact Identity
|
||||
## Identity and compatibility
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Extractor key | `dnd/item-events` |
|
||||
| Extract capability | `dnd.item_events` |
|
||||
| Artifact kind | `dnd/item-event-list` |
|
||||
| Schema ID | `notarius.dnd.item_events` |
|
||||
| Schema name | `notarius_dnd_item_events_v1` |
|
||||
| Schema version | `v1` |
|
||||
| Media type | `application/json` |
|
||||
| Normalizer key | `dnd/item-events` |
|
||||
|
||||
The payload is one strict JSON object containing a required `events` array,
|
||||
which may be empty. Objects reject unknown fields.
|
||||
`v1` is a strict JSON object with required `events`; the array may be empty.
|
||||
Event and source-reference objects reject unknown fields. A future incompatible
|
||||
shape requires a new schema version.
|
||||
|
||||
## Event Shape And Categories
|
||||
## Wire shape
|
||||
|
||||
Every event has `name`, `kind`, and a non-empty `source_refs` array. `quantity`
|
||||
is optional and, when present, is a positive integer. `from` and `to` are
|
||||
optional display values whose presence depends on `kind`.
|
||||
Every event has required `name`, `kind`, and `source_refs`. `quantity`, `from`,
|
||||
and `to` are optional where the event kind permits them.
|
||||
|
||||
| Field | Rule |
|
||||
| Field | Contract |
|
||||
| --- | --- |
|
||||
| `name` | Non-empty, transcript-supported display value. |
|
||||
| `kind` | One of the categories below. |
|
||||
| `quantity` | Optional positive integer; omitted when the source does not establish a count. |
|
||||
| `from` | Holder that loses possession when the category permits it. |
|
||||
| `to` | Holder that gains possession when the category permits it. |
|
||||
| `source_refs` | One or more current-source references. |
|
||||
| `name` | Non-empty item or currency display name. |
|
||||
| `kind` | `discovered`, `acquired`, `lost`, `consumed`, or `transferred`. |
|
||||
| `quantity` | Optional positive integer; omit it when no count is established. |
|
||||
| `from` | Optional non-empty losing holder, when allowed by `kind`. |
|
||||
| `to` | Optional non-empty gaining holder, when allowed by `kind`. |
|
||||
| `source_refs` | One or more transcript evidence ranges. |
|
||||
|
||||
| Kind | Meaning and holder rule |
|
||||
| --- | --- |
|
||||
| `discovered` | The party learns of or encounters an item without established possession; neither holder is present. |
|
||||
| `acquired` | A party member or `party` gains possession; `to` is required and `from` is absent. |
|
||||
| `lost` | A party member or `party` ceases to possess an item without consuming it; `from` is required and `to` is absent. |
|
||||
| `consumed` | Use depletes, expends, or destroys an item; `from` is required and `to` is absent. |
|
||||
| `transferred` | Possession moves between distinct party members; both holders are required and neither may be `party`. |
|
||||
|
||||
`party` is the reserved display holder for collective party possession when an
|
||||
individual holder is not established. Its comparison is case- and
|
||||
Unicode-insensitive. Transfers require distinct normalized holder values.
|
||||
Giving an item to an NPC, spending currency, selling an item, or another move
|
||||
outside party possession is `lost`, not `transferred` or `consumed`. Monetary
|
||||
spending, purchases, and payments are always `lost`. Currency is `consumed` only
|
||||
when the source explicitly describes its physical destruction or expenditure as
|
||||
a non-payment component. Ordinary non-depleting use is not an event.
|
||||
|
||||
Currency is represented as an ordinary event name plus an explicit quantity
|
||||
when the transcript supplies one. Each denomination remains separate. The
|
||||
artifact never converts denominations, infers a missing count, calculates a
|
||||
balance, or sums nearby events.
|
||||
|
||||
## Source Evidence And Normalization
|
||||
|
||||
Each source reference contains `source_id`, `start_unit_id`, and `end_unit_id`.
|
||||
It must identify an ordered range in the current source document. During
|
||||
extraction, every cited range must also be wholly contained in the current
|
||||
accepted chunk. Campaign references may disambiguate names, but never provide
|
||||
event evidence.
|
||||
|
||||
The deterministic normalizer trims only display-edge whitespace in names and
|
||||
holders, canonicalizes source-reference order and exact duplicate references,
|
||||
then orders events by earliest valid source position and stable tie-breakers
|
||||
over name, kind, holders, quantity, and complete evidence. It removes only
|
||||
events with the same normalized fields and complete valid evidence sequence.
|
||||
Invalid evidence is never collapsed. It does not rename, singularize, resolve
|
||||
aliases, infer holders or quantities, or merge nearby events.
|
||||
|
||||
The default extraction validation chain is JSON syntax, item-event shape,
|
||||
source references, durable JSON Schema, then advisory source relatedness. The
|
||||
normalization chain adds normalized invariants after shape validation and before
|
||||
source references and schema validation. Relatedness warnings are advisory so
|
||||
contextual names and currency notation do not reject otherwise valid evidence.
|
||||
|
||||
## Representative JSON
|
||||
Each source reference has exactly `source_id`, `start_unit_id`, and
|
||||
`end_unit_id`. It identifies an inclusive current-transcript range; unit IDs
|
||||
are positive and the start may not follow the end.
|
||||
|
||||
```json
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"name": "Hidden Cache",
|
||||
"kind": "discovered",
|
||||
"source_refs": [{"source_id": "session-7", "start_unit_id": 1, "end_unit_id": 1}]
|
||||
},
|
||||
{
|
||||
"name": "Silver Pieces",
|
||||
"kind": "acquired",
|
||||
"quantity": 20,
|
||||
"to": "party",
|
||||
"source_refs": [{"source_id": "session-7", "start_unit_id": 2, "end_unit_id": 2}]
|
||||
},
|
||||
{
|
||||
"name": "Torch",
|
||||
"kind": "lost",
|
||||
"from": "party",
|
||||
"source_refs": [{"source_id": "session-7", "start_unit_id": 3, "end_unit_id": 3}]
|
||||
},
|
||||
{
|
||||
"name": "Healing Potion",
|
||||
"kind": "consumed",
|
||||
"from": "Aria",
|
||||
"source_refs": [{"source_id": "session-7", "start_unit_id": 4, "end_unit_id": 4}]
|
||||
},
|
||||
{
|
||||
"name": "Moonblade",
|
||||
"kind": "transferred",
|
||||
"from": "Aria",
|
||||
"to": "Borin",
|
||||
"source_refs": [{"source_id": "session-7", "start_unit_id": 5, "end_unit_id": 5}]
|
||||
"source_refs": [
|
||||
{"source_id": "session-7", "start_unit_id": 2, "end_unit_id": 2}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Production Boundary
|
||||
## Holder rules and minimal extraction
|
||||
|
||||
The extractor requires `chunks` and `source.transcript`, and accepts optional
|
||||
`glossary`, `party`, `players`, and deprecated `roster` campaign references for
|
||||
disambiguation only. It has no generated NPC, scene-description, or item-registry
|
||||
dependency. The append-order merger preserves chunk order, and the normalizer
|
||||
has no reference slots.
|
||||
`discovered` has neither holder; `acquired` requires `to` and forbids `from`;
|
||||
`lost` and `consumed` require `from` and forbid `to`; `transferred` requires
|
||||
both holders. `party` denotes collective possession. A transfer cannot use
|
||||
`party` for either holder and its two normalized holders must differ.
|
||||
|
||||
The normalized lane is emitted as `lanes/<lane-id>.json` by the JSON output
|
||||
module. See [Configuration](../config.md#implemented-production-modules) for
|
||||
the selectable keys and default chains, and the
|
||||
[JSON output contract](json-output.md) for bundle paths.
|
||||
Only an evidenced discovery or possession change belongs in this artifact.
|
||||
It does not infer quantities or holders, convert currency denominations,
|
||||
calculate balances, or merge nearby events. Campaign references may
|
||||
disambiguate names but are never event evidence. Currency uses the ordinary
|
||||
`name` field and an explicit `quantity` only when the transcript establishes
|
||||
one; each denomination remains a separate event.
|
||||
|
||||
Normalization trims display whitespace, orders and removes exact duplicate
|
||||
source references, then orders events by valid source chronology, name identity
|
||||
and display value, kind, holders, quantity, and reference sequence. It
|
||||
collapses only entries with the same normalized durable fields and complete
|
||||
valid evidence.
|
||||
|
||||
The [JSON output contract](json-output.md) defines publication. See
|
||||
[D&D module internals](../internal/dnd.md) for implementation details and the
|
||||
[NPC-interaction artifact](dnd-npc-interaction-artifacts.md) for a distinct
|
||||
kind of occurrence.
|
||||
|
||||
@@ -1,160 +1,69 @@
|
||||
# D&D NPC Artifact
|
||||
|
||||
This document defines the durable D&D NPC-list artifact, its JSON codec, and
|
||||
the selectable production NPC pipeline. The normalized JSON payload can be
|
||||
passed explicitly to the spell extractor as an optional caster-name registry
|
||||
or to the combat extractor and normalizer as an actor registry. It
|
||||
remains a reference, not spell or combat evidence.
|
||||
The NPC interaction extractor and normalizer also consume this registry for
|
||||
canonical identity; registry source references remain provenance and never
|
||||
become interaction evidence. Their occurrence contract is defined in the
|
||||
[D&D NPC interaction artifact](dnd-npc-interaction-artifacts.md).
|
||||
This contract defines the durable NPC registry produced by `dnd/npcs`. It is a
|
||||
minimal, source-grounded identity registry for other D&D artifacts, not a
|
||||
character sheet or a relationship summary.
|
||||
|
||||
## Identity
|
||||
## Identity and compatibility
|
||||
|
||||
- Artifact kind: `dnd/npc-list`
|
||||
- Durable schema ID: `notarius.dnd.npcs`
|
||||
- Durable schema name: `notarius_dnd_npcs_v1`
|
||||
- Durable schema version: `v1`
|
||||
- Media type: `application/json`
|
||||
- Identity policy: `dnd.npcs.identity.v1`
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Artifact kind | `dnd/npc-list` |
|
||||
| Schema ID | `notarius.dnd.npcs` |
|
||||
| Schema name | `notarius_dnd_npcs_v1` |
|
||||
| Schema version | `v1` |
|
||||
| Media type | `application/json` |
|
||||
| Identity policy | `dnd.npcs.identity.v1` |
|
||||
|
||||
The durable JSON Schema is owned by the D&D NPC codec. NPC IDs are derived from
|
||||
the Unicode-normalized, case-folded canonical name using the identity policy.
|
||||
The durable codec enforces the artifact shape and ID syntax; registry identity
|
||||
validation remains a separate deterministic concern.
|
||||
`v1` accepts one strict JSON object with required `npcs`; the array may be
|
||||
empty. NPC and source-reference objects reject unknown fields. A future
|
||||
incompatible artifact shape or identity policy uses a new version or policy.
|
||||
|
||||
The extractor's private LLM response schema is a separate structural transport
|
||||
contract. It omits framework-assigned NPC and source IDs and admits semantic
|
||||
candidates for the deterministic shape and source-reference validators; it is
|
||||
not part of this durable contract.
|
||||
## Wire shape and identity
|
||||
|
||||
## Output Shape
|
||||
Each NPC has these required fields:
|
||||
|
||||
The payload is one object with a required top-level `npcs` array:
|
||||
| Field | Contract |
|
||||
| --- | --- |
|
||||
| `id` | `npc:sha256:` followed by 64 lowercase hexadecimal characters. |
|
||||
| `name` | Non-empty canonical display name. |
|
||||
| `source_refs` | One or more transcript evidence ranges for the identity. |
|
||||
|
||||
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
|
||||
{"npcs": []}
|
||||
{
|
||||
"npcs": [
|
||||
{
|
||||
"id": "npc:sha256:99a16589618a04f535a7d21fdcc71a0b1c05d22f752cd492065b1086d97bc3d7",
|
||||
"name": "Mira Thorn",
|
||||
"source_refs": [
|
||||
{"source_id": "session-7", "start_unit_id": 4, "end_unit_id": 5}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The array may be empty. Every object and nested object rejects unknown fields.
|
||||
The ID is deterministic: normalize the name to Unicode NFKC, normalize the
|
||||
supported apostrophe forms, collapse whitespace, case-fold it, SHA-256 the
|
||||
result, then prefix the lowercase hexadecimal digest with `npc:sha256:`. Each
|
||||
canonical identity and ID appears at most once. Normalization collapses records
|
||||
with the same canonical identity, retains their earliest position, and merges
|
||||
their canonicalized evidence; it does not add aliases, roles, descriptions, or
|
||||
relationship fields.
|
||||
|
||||
## NPC Fields
|
||||
## Scope and consumers
|
||||
|
||||
Each NPC contains exactly these required fields:
|
||||
Only individually identifiable NPC names with transcript evidence belong in
|
||||
this artifact. Groups, generic roles, invented labels, and descriptive
|
||||
enrichment are excluded. Its source references prove registry provenance; they
|
||||
do not become evidence for a spell, interaction, or combat occurrence.
|
||||
|
||||
- `id`: `npc:sha256:` followed by 64 lowercase hexadecimal characters;
|
||||
- `name`: the canonical display name;
|
||||
- `source_refs`: at least one source reference supporting the NPC record.
|
||||
|
||||
Each source reference contains required `source_id`, `start_unit_id`, and
|
||||
`end_unit_id`; unit IDs are positive integers. Source document identity, unit
|
||||
existence, and range ordering are validated by the source-reference validator
|
||||
when the artifact is used by a pipeline.
|
||||
|
||||
## Codec Boundary
|
||||
|
||||
`EncodeCandidate` and `DecodeCandidate` provide strict single-value JSON
|
||||
serialization while preserving typed values that still need semantic
|
||||
validation. `Encode` and `Decode` are the approved-artifact boundary and
|
||||
require all durable structural fields, non-empty required strings, valid source
|
||||
reference shapes, and the NPC ID pattern.
|
||||
|
||||
Codec metadata contains only `npc_count`. Schema bytes and returned metadata
|
||||
are independent values so callers cannot mutate codec-owned state.
|
||||
|
||||
## Production Pipeline
|
||||
|
||||
The production identities are:
|
||||
|
||||
- extractor: `dnd/npcs`;
|
||||
- artifact kind: `dnd/npc-list`;
|
||||
- normalizer: `dnd/npcs`; and
|
||||
- durable schema: `notarius.dnd.npcs`, version `v1`, media type
|
||||
`application/json`.
|
||||
|
||||
The extractor maps private model records to the current source identity and
|
||||
assigns deterministic IDs. Extraction validation checks shape, source
|
||||
references, and source relatedness. The normalizer first consolidates equal
|
||||
canonical-name matches, then may make one document-level LLM-assisted identity
|
||||
decision per configured normalize attempt for eligible distinctly named
|
||||
records. Consolidation is name-based: it retains a supplied canonical display
|
||||
name, keeps the earliest affected output position, derives its ID again, and
|
||||
unions exact evidence in canonical order. Ambiguous, invalid, or conflicting
|
||||
proposals are not applied; independently safe matches may still be retained.
|
||||
After the retry budget is exhausted, the safe result is accepted with bounded
|
||||
normalization warnings and the usual validation. The durable v1 artifact shape
|
||||
does not add aliases, proposal fields, or any other semantic-normalization
|
||||
representation.
|
||||
|
||||
The extraction prompt asks only for individually identifiable NPC names backed
|
||||
by source evidence. Groups, generic roles, invented labels, and descriptive or
|
||||
relationship enrichment are outside the contract.
|
||||
|
||||
The default extraction chain is `generic/valid_json`,
|
||||
`extract/dnd/npcs/shape`, `extract/dnd/npcs/source_refs`,
|
||||
`generic/valid_json_schema`, and `extract/dnd/npcs/source_relatedness`. The
|
||||
default normalize chain is `generic/valid_json`, `extract/dnd/npcs/shape`,
|
||||
`normalize/dnd/npcs/identity`, `extract/dnd/npcs/source_refs`,
|
||||
`generic/valid_json_schema`, and `extract/dnd/npcs/source_relatedness`.
|
||||
Relatedness emits bounded warnings when an NPC canonical name is not present
|
||||
near its cited transcript text; opaque campaign
|
||||
references may explain such a warning but do not become evidence.
|
||||
|
||||
## Manifest And Artifact Handoff
|
||||
|
||||
The NPC extractor records prompt and response-schema identities. The durable
|
||||
codec records only `npc_count`; raw names, source references, and payload bytes
|
||||
stay in the lane file rather than manifest
|
||||
metadata. The normalized lane can be consumed by a later ordered step through
|
||||
the registered canonical codec:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- id: identify-npcs
|
||||
artifacts:
|
||||
npcs:
|
||||
extract: dnd/npcs
|
||||
normalize: dnd/npcs
|
||||
scene-descriptions:
|
||||
extract: dnd/scene-descriptions
|
||||
normalize: dnd/scene-descriptions
|
||||
- id: grounded-events
|
||||
references:
|
||||
npcs:
|
||||
artifact:
|
||||
step: identify-npcs
|
||||
lane: npcs
|
||||
scene_descriptions:
|
||||
artifact:
|
||||
step: identify-npcs
|
||||
lane: scene-descriptions
|
||||
artifacts:
|
||||
spells:
|
||||
extract: dnd/spells
|
||||
normalize: dnd/spells
|
||||
combat:
|
||||
extract: dnd/combat-turns
|
||||
normalize: dnd/combat-turns
|
||||
```
|
||||
|
||||
The framework hands only accepted normalized artifacts across the barrier. It
|
||||
validates the canonical bytes against each consumer slot and clones the NPC
|
||||
operation-time reference for spell and combat consumers. The accompanying
|
||||
scene-description reference is required by the combat extractor for eligibility
|
||||
only; its consumer contract is defined in the
|
||||
[D&D combat-turn artifact contract](dnd-combat-turn-artifacts.md). Generated
|
||||
provenance records the artifact kind, schema identity, media type, canonical
|
||||
digest, size, and producer step/lane/module, but not names, source ranges, or
|
||||
payload bytes. External normalized files remain supported as explicit references
|
||||
and retain their file provenance.
|
||||
|
||||
NPC source references are registry provenance and are never accepted as spell
|
||||
or combat evidence. Current transcript units remain the only event evidence.
|
||||
|
||||
Consumers receive a separate names-only projection in normalized registry
|
||||
order, for example `{"npcs":[{"name":"Mira Thorn"}]}`. The projection omits
|
||||
IDs and evidence. Its digest covers the exact projected bytes and is used for
|
||||
consumer-local checkpoint identity, while the full durable artifact digest
|
||||
remains the manifest and generated-reference provenance identity. The unbound
|
||||
projection is exactly `{"npcs":[]}` and also has a projection digest.
|
||||
This registry can ground actor or caster names in the [spell](dnd-spell-artifacts.md)
|
||||
and [combat-turn](dnd-combat-turn-artifacts.md) artifacts. It is required to
|
||||
resolve the canonical `name` in an [NPC interaction](dnd-npc-interaction-artifacts.md).
|
||||
The [JSON output contract](json-output.md) defines publication, and
|
||||
[D&D module internals](../internal/dnd.md) owns pipeline mechanics.
|
||||
|
||||
@@ -1,20 +1,38 @@
|
||||
# D&D NPC Interaction Artifact
|
||||
|
||||
This document defines the durable D&D NPC-interaction-list artifact and its
|
||||
two-step production pipeline. It records discrete, source-grounded occurrences
|
||||
for NPCs already accepted into a normalized NPC registry; it does not expand
|
||||
the registry or summarize events.
|
||||
This contract defines the durable occurrence list produced by
|
||||
`dnd/npc-interactions`. It records discrete, source-grounded interactions with
|
||||
NPCs already present in a normalized registry; it does not extend that registry
|
||||
or summarize the session.
|
||||
|
||||
## Identity And JSON
|
||||
## Identity and compatibility
|
||||
|
||||
- Artifact kind: `dnd/npc-interaction-list`
|
||||
- Durable schema ID: `notarius.dnd.npc_interactions`
|
||||
- Durable schema name: `notarius_dnd_npc_interactions_v1`
|
||||
- Durable schema version: `v1`
|
||||
- Media type: `application/json`
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Artifact kind | `dnd/npc-interaction-list` |
|
||||
| Schema ID | `notarius.dnd.npc_interactions` |
|
||||
| Schema name | `notarius_dnd_npc_interactions_v1` |
|
||||
| Schema version | `v1` |
|
||||
| Media type | `application/json` |
|
||||
|
||||
The payload is one strict JSON object with only an `interactions` array. The
|
||||
array may be empty. Each item has exactly `name`, `kind`, and `source_refs`:
|
||||
`v1` is a strict JSON object with required `interactions`; the array may be
|
||||
empty. Interaction and source-reference objects reject unknown fields. A future
|
||||
incompatible shape requires a new schema version.
|
||||
|
||||
## Wire shape
|
||||
|
||||
Each interaction has these required fields:
|
||||
|
||||
| Field | Contract |
|
||||
| --- | --- |
|
||||
| `name` | Non-empty canonical display name from the required NPC registry. |
|
||||
| `kind` | One of the interaction categories below. |
|
||||
| `source_refs` | One or more transcript evidence ranges. |
|
||||
|
||||
Each 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. Extraction evidence
|
||||
for an interaction is confined to its accepted chunk.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -30,119 +48,31 @@ array may be empty. Each item has exactly `name`, `kind`, and `source_refs`:
|
||||
}
|
||||
```
|
||||
|
||||
`name` is the canonical display name from the required NPC registry.
|
||||
`source_refs` contains one or more current-source ranges with required
|
||||
`source_id`, `start_unit_id`, and `end_unit_id`; unit IDs are positive integers.
|
||||
During extraction, every range must be wholly contained in the current accepted
|
||||
chunk. This prevents a candidate from citing valid units that were not presented
|
||||
to that extraction call.
|
||||
Unknown fields are rejected.
|
||||
|
||||
## Interaction Categories
|
||||
|
||||
`kind` is exactly one of:
|
||||
## Interaction categories
|
||||
|
||||
| Kind | Meaning |
|
||||
| --- | --- |
|
||||
| `mentioned` | The NPC is referred to, but is not established as present or communicating in the evidenced passage. |
|
||||
| `noncombat_presence` | The NPC is present and relevant to the passage but does not meaningfully participate in dialogue or combat. |
|
||||
| `dialogue` | The NPC speaks, responds, or is directly engaged in a meaningful non-combat exchange. |
|
||||
| `mentioned` | The NPC is referred to but is not established as present or communicating. |
|
||||
| `noncombat_presence` | The NPC is present and relevant without meaningful dialogue or combat participation. |
|
||||
| `dialogue` | The NPC speaks, responds, or meaningfully participates in a non-combat exchange. |
|
||||
| `combat_ally` | The NPC actively participates in combat on the party's side. |
|
||||
| `combat_opponent` | The NPC actively participates in combat against the party. |
|
||||
| `other` | The transcript clearly establishes a direct NPC occurrence that fits none of the preceding kinds. |
|
||||
| `other` | A clearly evidenced direct occurrence not covered by another category. |
|
||||
|
||||
`other` is a residual category for positively evidenced activity, not a fallback
|
||||
for uncertain classification. When activities overlap, active combat
|
||||
participation outranks dialogue, presence, and mention; dialogue outranks
|
||||
non-combat presence and mention; and non-combat presence outranks mention.
|
||||
Combat alignment is not resolved by precedence: a meaningful change between
|
||||
ally and opponent creates separate occurrences.
|
||||
The categories do not represent motives, relationships, state, or events that
|
||||
the cited transcript does not establish. An `other` entry is not a substitute
|
||||
for uncertain classification.
|
||||
|
||||
These categories do not encode summaries, relationships, state, motives, or
|
||||
unobserved events.
|
||||
## Identity, evidence, and order
|
||||
|
||||
## Occurrence Boundaries And Ordering
|
||||
The required normalized [NPC artifact](dnd-npc-artifacts.md) resolves `name`.
|
||||
Registry references are provenance only and never replace an interaction's own
|
||||
evidence. Normalization canonicalizes recognized registry names, orders and
|
||||
deduplicates exact source references, then orders interactions by valid source
|
||||
chronology, NPC comparison identity, display name, kind, and reference sequence.
|
||||
Only entries with the same canonical name, kind, and complete valid evidence
|
||||
sequence are collapsed; distinct categories or evidence remain separate.
|
||||
|
||||
One occurrence represents one NPC, one kind, and one locally coherent passage
|
||||
within one accepted chunk. Repeated evidence belongs to the same occurrence
|
||||
only while it supports the same uninterrupted activity. A kind change, combat
|
||||
alignment change, intervening scene or meaningful absence, or transition from
|
||||
mention to presence starts a new occurrence. Occurrences never span chunks, and
|
||||
merge or normalization never semantically combines nearby, overlapping, or
|
||||
cross-chunk records.
|
||||
|
||||
Normalization orders records by:
|
||||
|
||||
1. earliest valid source-document position;
|
||||
2. the NPC identity comparison key;
|
||||
3. the exact canonical NPC display name;
|
||||
4. interaction kind in lexical order; and
|
||||
5. the complete canonical source-reference sequence, ordered by source ID and
|
||||
the source-document positions of each range's start and end.
|
||||
|
||||
Only records with identical canonical names, kinds, and complete valid evidence
|
||||
sequences are duplicates. Different categories, ranges, or separately grounded
|
||||
occurrences remain separate.
|
||||
|
||||
## Evidence, Registry, And Normalization
|
||||
|
||||
The registry proves only the canonical NPC identity. Its source references are
|
||||
registry provenance and are never interaction evidence. Every durable
|
||||
interaction must cite current transcript units supporting both the name and its
|
||||
classification.
|
||||
|
||||
The extractor receives a names-only registry projection such as
|
||||
`{"npcs":[{"name":"Mira Thorn"}]}`. The normalizer uses the full immutable
|
||||
registry for exact canonical-name lookup. It canonicalizes source references
|
||||
and applies the ordering and exact-duplicate rules above.
|
||||
|
||||
## Production Pipeline
|
||||
|
||||
The extractor and normalizer key is `dnd/npc-interactions`. Both require the
|
||||
structured `npcs` slot, so an accepted normalized registry must come from an
|
||||
earlier step:
|
||||
|
||||
```yaml
|
||||
pipelines:
|
||||
dnd-npc-interactions:
|
||||
input: seriatim
|
||||
steps:
|
||||
- id: identify-npcs
|
||||
artifacts:
|
||||
npcs:
|
||||
extract: dnd/npcs
|
||||
normalize: dnd/npcs
|
||||
- id: extract-interactions
|
||||
references:
|
||||
npcs:
|
||||
artifact:
|
||||
step: identify-npcs
|
||||
lane: npcs
|
||||
artifacts:
|
||||
interactions:
|
||||
extract: dnd/npc-interactions
|
||||
normalize: dnd/npc-interactions
|
||||
```
|
||||
|
||||
The framework passes only the accepted normalized producer. A missing, rejected,
|
||||
or incompatible NPC artifact prevents the consumer step from executing. It
|
||||
records generated artifact identity and bounded producer provenance without
|
||||
copying registry names, source ranges, or payload content into the manifest.
|
||||
|
||||
## Validation And Metadata
|
||||
|
||||
The default extract chain is `generic/valid_json`, interaction shape, registry,
|
||||
and source-reference validation, `generic/valid_json_schema`, then warning-only
|
||||
source relatedness. The normalize chain runs normalized invariants after
|
||||
registry validation and before source-reference and schema validation, followed
|
||||
by relatedness. Normalizer and relatedness warnings are bounded and end with an
|
||||
omission summary when necessary. The codec metadata contains only
|
||||
`interaction_count`. Extractor metadata identifies its prompt and private
|
||||
response schema; component-local checkpoint identities include the names-only
|
||||
registry projection where relevant. Generated registry identity stays in
|
||||
framework provenance and dependency fingerprints.
|
||||
|
||||
See [Configuration](../config.md#implemented-production-modules) for selectable
|
||||
keys and chains, [the NPC artifact contract](dnd-npc-artifacts.md) for the
|
||||
registry boundary, and the copyable
|
||||
[complete D&D example](../../examples/dnd-complete.config.yml).
|
||||
See the [combat-turn artifact](dnd-combat-turn-artifacts.md) for combat-action
|
||||
occurrences and the [JSON output contract](json-output.md) for publication.
|
||||
Pipeline mechanics are described in [D&D module internals](../internal/dnd.md).
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# D&D Scene Description Artifact Contract
|
||||
# D&D Scene-Description Artifact
|
||||
|
||||
This document defines the durable `dnd/scene-description-list` artifact
|
||||
emitted by the D&D scene-description lane.
|
||||
This contract defines the durable output of `dnd/scene-descriptions`. Each
|
||||
record classifies one accepted transcript chunk and gives it a minimal
|
||||
source-grounded title and summary.
|
||||
|
||||
## Artifact identity
|
||||
## Identity and compatibility
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
@@ -13,38 +14,32 @@ emitted by the D&D scene-description lane.
|
||||
| Schema version | `v1` |
|
||||
| Media type | `application/json` |
|
||||
|
||||
The normalized payload is written by the JSON output module to
|
||||
`lanes/<lane-id>.json`. See [JSON output](json-output.md) for the surrounding
|
||||
output bundle.
|
||||
`v1` is a strict JSON object with required non-empty `scenes`. Scene and
|
||||
source-reference objects reject unknown fields. A future incompatible shape
|
||||
requires a new schema version.
|
||||
|
||||
## JSON shape
|
||||
## Wire shape
|
||||
|
||||
The payload is a JSON object containing exactly one required field, `scenes`.
|
||||
Each scene object contains exactly these required fields:
|
||||
Each scene has exactly these required fields:
|
||||
|
||||
| Field | Shape and ownership |
|
||||
| Field | Contract |
|
||||
| --- | --- |
|
||||
| `id` | Non-empty accepted chunk ID, assigned by Notarius. |
|
||||
| `source_ref` | Exact inclusive accepted chunk range, assigned by Notarius. |
|
||||
| `kind` | One of `combat`, `narrative`, `recap`, or `meta`. |
|
||||
| `source_ref` | The assigned inclusive source range for that chunk. |
|
||||
| `kind` | `combat`, `narrative`, `recap`, or `meta`. |
|
||||
| `title` | Non-empty, trimmed, source-grounded title. |
|
||||
| `summary` | Non-empty, trimmed, source-grounded summary. |
|
||||
|
||||
All object layers reject unknown fields. The `scenes` array must be present and
|
||||
non-empty. `source_ref` has exactly `source_id`, `start_unit_id`, and
|
||||
`end_unit_id`; its source ID is non-empty and its unit IDs are positive
|
||||
integers.
|
||||
`source_ref` has exactly `source_id`, `start_unit_id`, and `end_unit_id`.
|
||||
Its source ID identifies the input transcript; its positive unit IDs identify
|
||||
the chunk's inclusive range, with the start no later than the end.
|
||||
|
||||
```json
|
||||
{
|
||||
"scenes": [
|
||||
{
|
||||
"id": "chunk-000001",
|
||||
"source_ref": {
|
||||
"source_id": "session-alpha",
|
||||
"start_unit_id": 1,
|
||||
"end_unit_id": 3
|
||||
},
|
||||
"source_ref": {"source_id": "session-7", "start_unit_id": 1, "end_unit_id": 3},
|
||||
"kind": "narrative",
|
||||
"title": "Arrival at the watchtower",
|
||||
"summary": "The party reaches the ruined watchtower and begins to investigate it."
|
||||
@@ -53,78 +48,22 @@ integers.
|
||||
}
|
||||
```
|
||||
|
||||
`id` and `source_ref` are application-owned identity and evidence. The model
|
||||
provides only `kind`, `title`, and `summary`; it is not asked for chunk IDs,
|
||||
source IDs, unit IDs, ranges, participants, or confidence.
|
||||
## Meaning and normalized form
|
||||
|
||||
## Scene kinds
|
||||
`combat` identifies a chunk where active combat is the central activity.
|
||||
`narrative` is current in-world play that is not principally combat, recap, or
|
||||
meta discussion. `recap` is primarily a recounting of an earlier session, and
|
||||
`meta` is primarily out-of-character discussion. The artifact does not add
|
||||
participants, confidence, events, or information absent from the chunk.
|
||||
|
||||
| Kind | Meaning |
|
||||
| --- | --- |
|
||||
| `combat` | Active combat is a substantive central activity. |
|
||||
| `narrative` | Current-session in-world play that is not principally combat, recap, or meta discussion. |
|
||||
| `recap` | The table is primarily recounting a previous session. |
|
||||
| `meta` | Sustained out-of-character discussion is the scene's primary purpose. |
|
||||
Normalization trims title and summary, orders scenes by source position and
|
||||
then ID, and removes exact duplicate records. A reused ID with different
|
||||
durable fields, or the same source range with different kind, title, or
|
||||
summary, is invalid. It does not merge adjacent ranges, alter prose, or infer
|
||||
missing scenes.
|
||||
|
||||
For a mixed accepted chunk, classification prefers `combat`, then `recap`,
|
||||
then `meta`, then `narrative`. Brief table talk, rules clarification, or a
|
||||
short recollection does not replace the main current-session activity.
|
||||
|
||||
## Extraction and evidence
|
||||
|
||||
The extractor runs once for each accepted chunk and maps one successful model
|
||||
response to one scene record. It copies the current chunk ID and exact chunk
|
||||
range, preserves the model kind without repair, and trims only surrounding
|
||||
whitespace from title and summary. A model response cannot represent an empty
|
||||
result; extraction failure follows the configured retry and rejection policy.
|
||||
|
||||
Optional `players`, `party`, and `glossary` campaign references can help
|
||||
disambiguate names or setting terms. They never supply scene evidence or add
|
||||
events absent from the accepted chunk. The lane requires no NPC registry or
|
||||
other generated artifact reference.
|
||||
|
||||
## Merge and normalization
|
||||
|
||||
Accepted per-chunk lists are appended in chunk order. Normalization then:
|
||||
|
||||
1. validates the current-source range, non-empty ID and prose, and closed kind;
|
||||
2. trims only title and summary whitespace;
|
||||
3. sorts records by source-document start position, then ID;
|
||||
4. removes records only when all five durable fields are identical;
|
||||
5. rejects a reused ID when any remaining durable field differs; and
|
||||
6. rejects the same exact range when `kind`, `title`, or `summary` differs.
|
||||
|
||||
Two different IDs with the same range and identical model-owned content remain
|
||||
separate records. Normalization does not join adjacent ranges, rewrite prose,
|
||||
repair kinds, infer missing scenes, or use chunk annotations.
|
||||
|
||||
## Validation and warnings
|
||||
|
||||
Extraction validation requires exactly one record with an ID and range exactly
|
||||
equal to its current chunk. Later validation checks source membership without a
|
||||
current chunk. Shape, source-range, kind, ID, and normalized-invariant failures
|
||||
reject the artifact.
|
||||
|
||||
Relatedness checks are advisory. They separately warn when a scene title or
|
||||
summary has no significant lexical token in its cited transcript range. The
|
||||
check ignores short tokens and common function words, uses transcript text only,
|
||||
and does not treat campaign references as evidence. Warning diagnostics are
|
||||
bounded and do not copy transcript or campaign-reference content.
|
||||
|
||||
## Configuration
|
||||
|
||||
Use `dnd/scenes` to form accepted scene chunks, then select
|
||||
`dnd/scene-descriptions` for extraction and normalization. The maintained
|
||||
[complete D&D configuration](../../examples/dnd-complete.config.yml) includes
|
||||
this lane alongside the other D&D artifacts. Selectable keys and default
|
||||
validator chains are defined in [Configuration](../config.md).
|
||||
|
||||
## Downstream combat use
|
||||
|
||||
The combat-turn extractor consumes this approved artifact as required
|
||||
eligibility control context through an explicit ordered reference binding. It
|
||||
uses only an exact chunk ID and source-range match with `kind: combat` to permit
|
||||
combat extraction; titles, summaries, and scene references do not become combat
|
||||
prompt material or evidence. The complete downstream behavior, including empty
|
||||
results and warnings for unavailable coverage, is defined in the
|
||||
[D&D combat-turn artifact contract](dnd-combat-turn-artifacts.md).
|
||||
The [combat-turn artifact](dnd-combat-turn-artifacts.md) uses an exact matching
|
||||
`combat` scene only as eligibility control; scene title, summary, and source
|
||||
reference never become combat evidence. Publication is defined by the
|
||||
[JSON output contract](json-output.md); implementation details live in
|
||||
[D&D module internals](../internal/dnd.md).
|
||||
|
||||
@@ -1,204 +1,73 @@
|
||||
# D&D Spell Artifact
|
||||
|
||||
This document is the durable serialized artifact contract for the production
|
||||
D&D spell extractor. Selectable extractor keys are cataloged in
|
||||
[Configuration](../config.md#implemented-production-modules).
|
||||
This contract defines the durable output of the `dnd/spells` extractor and
|
||||
normalizer. It records source-grounded spell-casting occurrences; it is not a
|
||||
spellbook, a rules lookup result, or a record of hypothetical casts.
|
||||
|
||||
## Identity
|
||||
## Identity and compatibility
|
||||
|
||||
- Artifact kind: `dnd/spell-list`
|
||||
- Prompt ID: `dnd.spells`
|
||||
- Response schema key: `dnd_spells`
|
||||
- Response schema ID: `notarius.dnd.spells`
|
||||
- Response schema name: `notarius_dnd_spells_v1`
|
||||
- Response schema version: `v1`
|
||||
- Media type: `application/json`
|
||||
|
||||
The durable JSON Schema is owned by the D&D spell artifact codec. The
|
||||
extractor's private LLM response schema is a separate transport contract: its
|
||||
source-reference objects omit `source_id`, which the extractor assigns while
|
||||
mapping the response to the canonical artifact. The LLM DTO and transport
|
||||
schema are not part of this durable contract. The private schema owns required
|
||||
fields, JSON types, object and array shapes, and unknown-field rejection;
|
||||
deterministic validators own the durable artifact's semantic constraints.
|
||||
|
||||
The output contains canonical spell casts derived from transcript evidence.
|
||||
Source IDs are assigned from the input identity; source-unit ranges identify
|
||||
the evidence location.
|
||||
|
||||
## Output Shape
|
||||
|
||||
The extractor payload is a JSON object with one required top-level array. Its
|
||||
structure is:
|
||||
|
||||
```text
|
||||
{"spell_casts": [<spell-cast object>, ...]}
|
||||
```
|
||||
|
||||
`spell_casts` must be present. It may be empty when no spell casts are found.
|
||||
When multiple chunk results are combined, spell casts remain in chunk order.
|
||||
When the payload is written as durable output, its logical path is derived from
|
||||
the configured artifact lane ID as defined by the
|
||||
[JSON output contract](json-output.md#output-payload-files).
|
||||
|
||||
## Spell-Cast Fields
|
||||
|
||||
Each spell cast contains exactly these required fields:
|
||||
|
||||
- `caster`: in-world character or creature casting the spell;
|
||||
- `spell`: spell name;
|
||||
- `source_refs`: transcript source references with extractor-assigned source
|
||||
IDs and evidence unit ranges. It must contain at least one entry.
|
||||
|
||||
Both string fields must be non-empty. `caster` is the canonical in-world
|
||||
caster, not the human player, transcript speaker, or GM when the associated
|
||||
character or creature can be identified. Player and party references may
|
||||
disambiguate that identity, but do not independently establish that a cast
|
||||
occurred. The `spell` value must resolve through the effective SRD-plus-overlay
|
||||
catalog as either a canonical name or alias. Catalog validation accepts aliases
|
||||
but does not rewrite them; unknown fields are rejected.
|
||||
|
||||
The artifact includes an actual casting event or an unambiguous declared
|
||||
casting attempt. Spell mentions, hypothetical plans, rules discussion, and
|
||||
catalog matches without a casting event are excluded. The spell catalog is a
|
||||
name-recognition policy and never evidence that a cast occurred.
|
||||
|
||||
## Source References
|
||||
|
||||
Each source reference contains exactly three required fields: `source_id`,
|
||||
`start_unit_id`, and `end_unit_id`. The source ID must match the input identity.
|
||||
The unit IDs must be positive integers present in the input, and the start unit
|
||||
must not appear after the end unit. Unknown fields are rejected.
|
||||
|
||||
For each cast, the complete `source_refs` collection identifies the transcript
|
||||
evidence for the caster, spell name, and occurrence of the cast or declared
|
||||
attempt. The deterministic validators establish that ranges are structurally
|
||||
valid and that the spell name is related to cited text. Semantic evidence
|
||||
sufficiency is an extraction policy and remains subject to evaluation rather
|
||||
than deterministic proof.
|
||||
|
||||
Reference slot keys and accepted file types are defined in
|
||||
[Configuration](../config.md#implemented-production-modules). References are
|
||||
supporting disambiguation material, not source evidence, and are not
|
||||
addressable through `source_refs`.
|
||||
|
||||
## Optional NPC Grounding
|
||||
|
||||
The `dnd/spells` extractor accepts an optional `npcs` reference containing one
|
||||
normalized NPC artifact as `application/json`, up to 1 MiB. An external file is
|
||||
validated during preparation; an ordered generated binding is validated at the
|
||||
step handoff. Both paths use the approved NPC codec and identity policy,
|
||||
re-encode canonical durable JSON for registry provenance, and supply only the
|
||||
registry's ordered names as the operation-time spell prompt input. It helps the
|
||||
model prefer canonical caster names; it does not establish that a spell was
|
||||
cast.
|
||||
|
||||
NPC source references may identify the run that produced the registry or any
|
||||
other session. They remain registry provenance and are never copied into a
|
||||
spell cast's `source_refs`; every spell evidence range must still identify the
|
||||
current transcript. Generated provenance records producer and canonical
|
||||
artifact identity without payload content or a path. When the slot is absent,
|
||||
the prompt receives exactly `{"npcs":[]}` with its projection digest, and the
|
||||
run has no NPC reference provenance.
|
||||
|
||||
## Normalization Behavior
|
||||
|
||||
When the `dnd/spells` normalizer is selected, each recognized spell name is
|
||||
rewritten to the effective catalog's canonical display name. Lookup uses the
|
||||
catalog's case-insensitive, whitespace-normalizing, apostrophe-normalizing, and
|
||||
alias rules. Unknown names are preserved exactly for the normalize validators;
|
||||
the normalizer does not guess or apply fuzzy matching.
|
||||
|
||||
Each cast's `source_refs` is copied, sorted by exact `source_id`,
|
||||
`start_unit_id`, and `end_unit_id`, and stripped of exact structural
|
||||
duplicates. Adjacent or overlapping ranges are not merged, and the normalizer
|
||||
does not synthesize references or change their boundaries.
|
||||
|
||||
After those per-cast changes, duplicate identity requires the same canonical
|
||||
spell name, the same caster after case folding and whitespace normalization,
|
||||
and the same complete, non-empty set of source references valid for the source
|
||||
document. Only the first occurrence is retained, in stable order. Its caster
|
||||
and canonical references are preserved. Unknown names, empty or invalid
|
||||
evidence, and casts with different evidence remain separate.
|
||||
|
||||
Mutation and duplicate decisions are returned through the normal warnings
|
||||
surface. Warning scopes use the merged input index, such as `spell_casts[0]`,
|
||||
so they remain meaningful even when a later duplicate is removed. The
|
||||
normalizer uses these reason codes:
|
||||
|
||||
| Reason code | Meaning |
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| `spell_name_canonicalized` | A catalog lookup replaced an input name with its canonical display name. |
|
||||
| `spell_name_unresolved` | A name was not found in the effective catalog and was retained unchanged. |
|
||||
| `source_references_normalized` | Reference order changed or exact duplicate references were removed. |
|
||||
| `duplicate_spell_cast_collapsed` | A later cast matched the retained cast's complete duplicate identity. |
|
||||
| Artifact kind | `dnd/spell-list` |
|
||||
| Schema ID | `notarius.dnd.spells` |
|
||||
| Schema name | `notarius_dnd_spells_v1` |
|
||||
| Schema version | `v1` |
|
||||
| Media type | `application/json` |
|
||||
|
||||
Only warnings from an accepted normalize attempt are promoted to
|
||||
`warnings.json`. If an unresolved name reaches the default normalize validator
|
||||
chain, the catalog validator rejects the candidate with `unknown_spell`; the
|
||||
`spell_name_unresolved` warning remains in the attempt's debug artifact. An
|
||||
explicit validator override that accepts the candidate promotes the unresolved
|
||||
warning normally.
|
||||
`v1` is a single strict JSON object. It requires `spell_casts`; the array may
|
||||
be empty. Each spell-cast object and source-reference object rejects unknown
|
||||
fields. A future incompatible shape requires a new schema version.
|
||||
|
||||
The default extraction and normalization chains both preserve this registered
|
||||
order: JSON syntax, spell shape, catalog membership, source references, JSON
|
||||
Schema, then source relatedness. Extraction validates the private response
|
||||
schema; normalization validates the durable artifact schema.
|
||||
## Wire shape
|
||||
|
||||
## Manifest Metadata
|
||||
Each `spell_casts` entry has these required fields:
|
||||
|
||||
The extractor adds prompt and response-schema provenance under the artifact lane
|
||||
manifest metadata:
|
||||
| Field | Contract |
|
||||
| --- | --- |
|
||||
| `caster` | Non-empty in-world character or creature name. |
|
||||
| `spell` | Non-empty spell name. |
|
||||
| `source_refs` | One or more transcript evidence ranges. |
|
||||
|
||||
Every source reference has exactly `source_id`, `start_unit_id`, and
|
||||
`end_unit_id`. The source ID identifies the input transcript; the unit IDs are
|
||||
positive inclusive unit identifiers, and the start may not follow the end in
|
||||
that source. References are evidence for the cast, not campaign-reference or
|
||||
NPC-registry provenance.
|
||||
|
||||
```json
|
||||
{
|
||||
"metadata": {
|
||||
"extractor": {
|
||||
"prompt_id": "dnd.spells",
|
||||
"prompt_version": "v1",
|
||||
"prompt_sha256": "sha256:...",
|
||||
"response_schema_key": "dnd_spells",
|
||||
"response_schema_id": "notarius.dnd.spells",
|
||||
"response_schema_name": "notarius_dnd_spells_v1",
|
||||
"response_schema_version": "v1",
|
||||
"response_schema_sha256": "sha256:...",
|
||||
"catalog_base_id": "dnd-5e-2014-srd-spells",
|
||||
"catalog_digest": "sha256:...",
|
||||
"catalog_overlay_ids": ["campaign.example"],
|
||||
"npc_registry_digest": "sha256:...",
|
||||
"npc_count": 3
|
||||
},
|
||||
"normalizer": {
|
||||
"catalog_base_id": "dnd-5e-2014-srd-spells",
|
||||
"catalog_digest": "sha256:...",
|
||||
"catalog_overlay_ids": ["campaign.example"]
|
||||
"spell_casts": [
|
||||
{
|
||||
"caster": "Mira Thorn",
|
||||
"spell": "Fireball",
|
||||
"source_refs": [
|
||||
{"source_id": "session-7", "start_unit_id": 12, "end_unit_id": 13}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`catalog_digest` identifies the effective semantic catalog, while
|
||||
`catalog_overlay_ids` is sorted and empty for a base-only configuration. Raw
|
||||
prompt, schema, catalog, alias, and local overlay-file content are not
|
||||
included in manifest metadata. The `normalizer` metadata uses the same catalog
|
||||
identity fields when that module is selected. Overlay origin, media type, byte
|
||||
size, and raw digest are recorded separately in the manifest's reference
|
||||
provenance; see the [JSON output contract](json-output.md#manifestjson).
|
||||
## Evidence and normalized form
|
||||
|
||||
The `npc_registry_digest` and `npc_count` fields in the example are present for
|
||||
an external NPC registry when the extractor publishes its prepared module
|
||||
metadata. They contain no NPC names, source references, paths, or raw
|
||||
bytes. A generated registry's identity is instead represented by the framework
|
||||
handoff provenance and dependency fingerprint, so the consumer module metadata
|
||||
does not duplicate it.
|
||||
An entry represents an actual cast or an unambiguous declared attempt. A spell
|
||||
mention, rules discussion, plan, or catalog match alone is not an occurrence.
|
||||
The configured catalog checks the name; it does not establish evidence.
|
||||
|
||||
The extractor's prompt hash, private response-schema hash, and effective catalog
|
||||
digest also contribute independently scoped semantic checkpoint fingerprints.
|
||||
Changing any of those prepared contracts intentionally produces a cold
|
||||
checkpoint miss. Fingerprints contain only digests, never prompt, schema,
|
||||
catalog, or reference content. When an NPC registry is bound, its semantic
|
||||
digest contributes an additional local `npc_registry` fingerprint for an
|
||||
external binding; the manifest metadata contains only that digest and
|
||||
`npc_count`. Raw NPC file provenance remains independently recorded in the
|
||||
manifest's `references` list. Generated bindings contribute the canonical
|
||||
artifact dependency fingerprint and bounded producer provenance instead.
|
||||
When normalization is selected, recognized spell names use the effective
|
||||
catalog's canonical display name. Source references are put in canonical source
|
||||
order and exact duplicate references are removed. A later entry is collapsed
|
||||
only when it has the same canonical spell, the same case- and
|
||||
whitespace-insensitive caster identity, and the same complete valid reference
|
||||
sequence. Remaining entries retain their merged order.
|
||||
|
||||
The optional normalized [NPC artifact](dnd-npc-artifacts.md) can ground a
|
||||
caster name. Its own references remain registry provenance and are never copied
|
||||
into `source_refs`.
|
||||
|
||||
## Related contracts
|
||||
|
||||
The [spell-catalog overlay contract](dnd-spell-catalog-overlays.md) defines
|
||||
the configured catalog additions. The [JSON output contract](json-output.md)
|
||||
defines where this logical artifact is published; [D&D module internals](../internal/dnd.md)
|
||||
describes extraction and validation mechanics.
|
||||
|
||||
Reference in New Issue
Block a user