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,
|
This contract defines the durable combat-action occurrence list produced by
|
||||||
candidate-validation, normalization, and production lane boundaries for D&D
|
`dnd/combat-turns`. It records source-grounded turns and actions; it is not a
|
||||||
combat turns.
|
complete initiative tracker, combat summary, or state model.
|
||||||
|
|
||||||
## Artifact identity
|
## Identity and compatibility
|
||||||
|
|
||||||
| Property | Value |
|
| Property | Value |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -14,167 +14,56 @@ combat turns.
|
|||||||
| Schema version | `v1` |
|
| Schema version | `v1` |
|
||||||
| Media type | `application/json` |
|
| Media type | `application/json` |
|
||||||
|
|
||||||
The top-level JSON object contains the required `combat_turns` array, which
|
`v1` is a strict JSON object with required `combat_turns`; the array may be
|
||||||
may be empty. Every object rejects unknown fields.
|
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. |
|
| `actor` | Non-empty acting character or creature name. |
|
||||||
| `turn_kind` | One of `turn`, `reaction`, `legendary_action`, `lair_action`, or `other`. |
|
| `turn_kind` | `turn`, `reaction`, `legendary_action`, `lair_action`, or `other`. |
|
||||||
| `source_refs` | Required array with at least one source reference. |
|
| `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
|
```json
|
||||||
{
|
{
|
||||||
"source_id": "session-alpha",
|
"combat_turns": [
|
||||||
"start_unit_id": 1,
|
{
|
||||||
"end_unit_id": 2
|
"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
|
## Eligibility, evidence, and normalized form
|
||||||
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 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,
|
The [NPC-interaction artifact](dnd-npc-interaction-artifacts.md) records
|
||||||
collection presence, and source references so later validators can report
|
broader NPC occurrences. The [JSON output contract](json-output.md) defines
|
||||||
them. Candidate decoding still requires valid JSON, one JSON value, known
|
publication, and [D&D module internals](../internal/dnd.md) describes routing
|
||||||
fields, and compatible JSON types.
|
and validation mechanics.
|
||||||
- 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.
|
|
||||||
|
|||||||
@@ -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
|
This contract defines the durable item and currency occurrence list produced by
|
||||||
boundaries. It records source-grounded discoveries and possession changes; it
|
`dnd/item-events`. It records source-grounded discoveries and possession
|
||||||
does not maintain an inventory or ledger.
|
changes; it does not maintain an inventory, balance, or ledger.
|
||||||
|
|
||||||
## Artifact Identity
|
## Identity and compatibility
|
||||||
|
|
||||||
| Property | Value |
|
| Property | Value |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Extractor key | `dnd/item-events` |
|
|
||||||
| Extract capability | `dnd.item_events` |
|
|
||||||
| Artifact kind | `dnd/item-event-list` |
|
| Artifact kind | `dnd/item-event-list` |
|
||||||
| Schema ID | `notarius.dnd.item_events` |
|
| Schema ID | `notarius.dnd.item_events` |
|
||||||
| Schema name | `notarius_dnd_item_events_v1` |
|
| Schema name | `notarius_dnd_item_events_v1` |
|
||||||
| Schema version | `v1` |
|
| Schema version | `v1` |
|
||||||
| Media type | `application/json` |
|
| Media type | `application/json` |
|
||||||
| Normalizer key | `dnd/item-events` |
|
|
||||||
|
|
||||||
The payload is one strict JSON object containing a required `events` array,
|
`v1` is a strict JSON object with required `events`; the array may be empty.
|
||||||
which may be empty. Objects reject unknown fields.
|
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`
|
Every event has required `name`, `kind`, and `source_refs`. `quantity`, `from`,
|
||||||
is optional and, when present, is a positive integer. `from` and `to` are
|
and `to` are optional where the event kind permits them.
|
||||||
optional display values whose presence depends on `kind`.
|
|
||||||
|
|
||||||
| Field | Rule |
|
| Field | Contract |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `name` | Non-empty, transcript-supported display value. |
|
| `name` | Non-empty item or currency display name. |
|
||||||
| `kind` | One of the categories below. |
|
| `kind` | `discovered`, `acquired`, `lost`, `consumed`, or `transferred`. |
|
||||||
| `quantity` | Optional positive integer; omitted when the source does not establish a count. |
|
| `quantity` | Optional positive integer; omit it when no count is established. |
|
||||||
| `from` | Holder that loses possession when the category permits it. |
|
| `from` | Optional non-empty losing holder, when allowed by `kind`. |
|
||||||
| `to` | Holder that gains possession when the category permits it. |
|
| `to` | Optional non-empty gaining holder, when allowed by `kind`. |
|
||||||
| `source_refs` | One or more current-source references. |
|
| `source_refs` | One or more transcript evidence ranges. |
|
||||||
|
|
||||||
| Kind | Meaning and holder rule |
|
Each source reference has exactly `source_id`, `start_unit_id`, and
|
||||||
| --- | --- |
|
`end_unit_id`. It identifies an inclusive current-transcript range; unit IDs
|
||||||
| `discovered` | The party learns of or encounters an item without established possession; neither holder is present. |
|
are positive and the start may not follow the end.
|
||||||
| `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
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"events": [
|
"events": [
|
||||||
{
|
|
||||||
"name": "Hidden Cache",
|
|
||||||
"kind": "discovered",
|
|
||||||
"source_refs": [{"source_id": "session-7", "start_unit_id": 1, "end_unit_id": 1}]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Silver Pieces",
|
"name": "Silver Pieces",
|
||||||
"kind": "acquired",
|
"kind": "acquired",
|
||||||
"quantity": 20,
|
"quantity": 20,
|
||||||
"to": "party",
|
"to": "party",
|
||||||
"source_refs": [{"source_id": "session-7", "start_unit_id": 2, "end_unit_id": 2}]
|
"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}]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Production Boundary
|
## Holder rules and minimal extraction
|
||||||
|
|
||||||
The extractor requires `chunks` and `source.transcript`, and accepts optional
|
`discovered` has neither holder; `acquired` requires `to` and forbids `from`;
|
||||||
`glossary`, `party`, `players`, and deprecated `roster` campaign references for
|
`lost` and `consumed` require `from` and forbid `to`; `transferred` requires
|
||||||
disambiguation only. It has no generated NPC, scene-description, or item-registry
|
both holders. `party` denotes collective possession. A transfer cannot use
|
||||||
dependency. The append-order merger preserves chunk order, and the normalizer
|
`party` for either holder and its two normalized holders must differ.
|
||||||
has no reference slots.
|
|
||||||
|
|
||||||
The normalized lane is emitted as `lanes/<lane-id>.json` by the JSON output
|
Only an evidenced discovery or possession change belongs in this artifact.
|
||||||
module. See [Configuration](../config.md#implemented-production-modules) for
|
It does not infer quantities or holders, convert currency denominations,
|
||||||
the selectable keys and default chains, and the
|
calculate balances, or merge nearby events. Campaign references may
|
||||||
[JSON output contract](json-output.md) for bundle paths.
|
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
|
# D&D NPC Artifact
|
||||||
|
|
||||||
This document defines the durable D&D NPC-list artifact, its JSON codec, and
|
This contract defines the durable NPC registry produced by `dnd/npcs`. It is a
|
||||||
the selectable production NPC pipeline. The normalized JSON payload can be
|
minimal, source-grounded identity registry for other D&D artifacts, not a
|
||||||
passed explicitly to the spell extractor as an optional caster-name registry
|
character sheet or a relationship summary.
|
||||||
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).
|
|
||||||
|
|
||||||
## Identity
|
## Identity and compatibility
|
||||||
|
|
||||||
- Artifact kind: `dnd/npc-list`
|
| Property | Value |
|
||||||
- Durable schema ID: `notarius.dnd.npcs`
|
| --- | --- |
|
||||||
- Durable schema name: `notarius_dnd_npcs_v1`
|
| Artifact kind | `dnd/npc-list` |
|
||||||
- Durable schema version: `v1`
|
| Schema ID | `notarius.dnd.npcs` |
|
||||||
- Media type: `application/json`
|
| Schema name | `notarius_dnd_npcs_v1` |
|
||||||
- Identity policy: `dnd.npcs.identity.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
|
`v1` accepts one strict JSON object with required `npcs`; the array may be
|
||||||
the Unicode-normalized, case-folded canonical name using the identity policy.
|
empty. NPC and source-reference objects reject unknown fields. A future
|
||||||
The durable codec enforces the artifact shape and ID syntax; registry identity
|
incompatible artifact shape or identity policy uses a new version or policy.
|
||||||
validation remains a separate deterministic concern.
|
|
||||||
|
|
||||||
The extractor's private LLM response schema is a separate structural transport
|
## Wire shape and identity
|
||||||
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.
|
|
||||||
|
|
||||||
## 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
|
```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;
|
This registry can ground actor or caster names in the [spell](dnd-spell-artifacts.md)
|
||||||
- `name`: the canonical display name;
|
and [combat-turn](dnd-combat-turn-artifacts.md) artifacts. It is required to
|
||||||
- `source_refs`: at least one source reference supporting the NPC record.
|
resolve the canonical `name` in an [NPC interaction](dnd-npc-interaction-artifacts.md).
|
||||||
|
The [JSON output contract](json-output.md) defines publication, and
|
||||||
Each source reference contains required `source_id`, `start_unit_id`, and
|
[D&D module internals](../internal/dnd.md) owns pipeline mechanics.
|
||||||
`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.
|
|
||||||
|
|||||||
@@ -1,20 +1,38 @@
|
|||||||
# D&D NPC Interaction Artifact
|
# D&D NPC Interaction Artifact
|
||||||
|
|
||||||
This document defines the durable D&D NPC-interaction-list artifact and its
|
This contract defines the durable occurrence list produced by
|
||||||
two-step production pipeline. It records discrete, source-grounded occurrences
|
`dnd/npc-interactions`. It records discrete, source-grounded interactions with
|
||||||
for NPCs already accepted into a normalized NPC registry; it does not expand
|
NPCs already present in a normalized registry; it does not extend that registry
|
||||||
the registry or summarize events.
|
or summarize the session.
|
||||||
|
|
||||||
## Identity And JSON
|
## Identity and compatibility
|
||||||
|
|
||||||
- Artifact kind: `dnd/npc-interaction-list`
|
| Property | Value |
|
||||||
- Durable schema ID: `notarius.dnd.npc_interactions`
|
| --- | --- |
|
||||||
- Durable schema name: `notarius_dnd_npc_interactions_v1`
|
| Artifact kind | `dnd/npc-interaction-list` |
|
||||||
- Durable schema version: `v1`
|
| Schema ID | `notarius.dnd.npc_interactions` |
|
||||||
- Media type: `application/json`
|
| 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
|
`v1` is a strict JSON object with required `interactions`; the array may be
|
||||||
array may be empty. Each item has exactly `name`, `kind`, and `source_refs`:
|
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
|
```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.
|
## Interaction categories
|
||||||
`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:
|
|
||||||
|
|
||||||
| Kind | Meaning |
|
| Kind | Meaning |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `mentioned` | The NPC is referred to, but is not established as present or communicating in the evidenced passage. |
|
| `mentioned` | The NPC is referred to but is not established as present or communicating. |
|
||||||
| `noncombat_presence` | The NPC is present and relevant to the passage but does not meaningfully participate in dialogue or combat. |
|
| `noncombat_presence` | The NPC is present and relevant without meaningful dialogue or combat participation. |
|
||||||
| `dialogue` | The NPC speaks, responds, or is directly engaged in a meaningful non-combat exchange. |
|
| `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_ally` | The NPC actively participates in combat on the party's side. |
|
||||||
| `combat_opponent` | The NPC actively participates in combat against the party. |
|
| `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
|
The categories do not represent motives, relationships, state, or events that
|
||||||
for uncertain classification. When activities overlap, active combat
|
the cited transcript does not establish. An `other` entry is not a substitute
|
||||||
participation outranks dialogue, presence, and mention; dialogue outranks
|
for uncertain classification.
|
||||||
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.
|
|
||||||
|
|
||||||
These categories do not encode summaries, relationships, state, motives, or
|
## Identity, evidence, and order
|
||||||
unobserved events.
|
|
||||||
|
|
||||||
## 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
|
See the [combat-turn artifact](dnd-combat-turn-artifacts.md) for combat-action
|
||||||
within one accepted chunk. Repeated evidence belongs to the same occurrence
|
occurrences and the [JSON output contract](json-output.md) for publication.
|
||||||
only while it supports the same uninterrupted activity. A kind change, combat
|
Pipeline mechanics are described in [D&D module internals](../internal/dnd.md).
|
||||||
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).
|
|
||||||
|
|||||||
@@ -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
|
This contract defines the durable output of `dnd/scene-descriptions`. Each
|
||||||
emitted by the D&D scene-description lane.
|
record classifies one accepted transcript chunk and gives it a minimal
|
||||||
|
source-grounded title and summary.
|
||||||
|
|
||||||
## Artifact identity
|
## Identity and compatibility
|
||||||
|
|
||||||
| Property | Value |
|
| Property | Value |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
@@ -13,38 +14,32 @@ emitted by the D&D scene-description lane.
|
|||||||
| Schema version | `v1` |
|
| Schema version | `v1` |
|
||||||
| Media type | `application/json` |
|
| Media type | `application/json` |
|
||||||
|
|
||||||
The normalized payload is written by the JSON output module to
|
`v1` is a strict JSON object with required non-empty `scenes`. Scene and
|
||||||
`lanes/<lane-id>.json`. See [JSON output](json-output.md) for the surrounding
|
source-reference objects reject unknown fields. A future incompatible shape
|
||||||
output bundle.
|
requires a new schema version.
|
||||||
|
|
||||||
## JSON shape
|
## Wire shape
|
||||||
|
|
||||||
The payload is a JSON object containing exactly one required field, `scenes`.
|
Each scene has exactly these required fields:
|
||||||
Each scene object contains exactly these required fields:
|
|
||||||
|
|
||||||
| Field | Shape and ownership |
|
| Field | Contract |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `id` | Non-empty accepted chunk ID, assigned by Notarius. |
|
| `id` | Non-empty accepted chunk ID, assigned by Notarius. |
|
||||||
| `source_ref` | Exact inclusive accepted chunk range, assigned by Notarius. |
|
| `source_ref` | The assigned inclusive source range for that chunk. |
|
||||||
| `kind` | One of `combat`, `narrative`, `recap`, or `meta`. |
|
| `kind` | `combat`, `narrative`, `recap`, or `meta`. |
|
||||||
| `title` | Non-empty, trimmed, source-grounded title. |
|
| `title` | Non-empty, trimmed, source-grounded title. |
|
||||||
| `summary` | Non-empty, trimmed, source-grounded summary. |
|
| `summary` | Non-empty, trimmed, source-grounded summary. |
|
||||||
|
|
||||||
All object layers reject unknown fields. The `scenes` array must be present and
|
`source_ref` has exactly `source_id`, `start_unit_id`, and `end_unit_id`.
|
||||||
non-empty. `source_ref` has exactly `source_id`, `start_unit_id`, and
|
Its source ID identifies the input transcript; its positive unit IDs identify
|
||||||
`end_unit_id`; its source ID is non-empty and its unit IDs are positive
|
the chunk's inclusive range, with the start no later than the end.
|
||||||
integers.
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scenes": [
|
"scenes": [
|
||||||
{
|
{
|
||||||
"id": "chunk-000001",
|
"id": "chunk-000001",
|
||||||
"source_ref": {
|
"source_ref": {"source_id": "session-7", "start_unit_id": 1, "end_unit_id": 3},
|
||||||
"source_id": "session-alpha",
|
|
||||||
"start_unit_id": 1,
|
|
||||||
"end_unit_id": 3
|
|
||||||
},
|
|
||||||
"kind": "narrative",
|
"kind": "narrative",
|
||||||
"title": "Arrival at the watchtower",
|
"title": "Arrival at the watchtower",
|
||||||
"summary": "The party reaches the ruined watchtower and begins to investigate it."
|
"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
|
## Meaning and normalized form
|
||||||
provides only `kind`, `title`, and `summary`; it is not asked for chunk IDs,
|
|
||||||
source IDs, unit IDs, ranges, participants, or confidence.
|
|
||||||
|
|
||||||
## 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 |
|
Normalization trims title and summary, orders scenes by source position and
|
||||||
| --- | --- |
|
then ID, and removes exact duplicate records. A reused ID with different
|
||||||
| `combat` | Active combat is a substantive central activity. |
|
durable fields, or the same source range with different kind, title, or
|
||||||
| `narrative` | Current-session in-world play that is not principally combat, recap, or meta discussion. |
|
summary, is invalid. It does not merge adjacent ranges, alter prose, or infer
|
||||||
| `recap` | The table is primarily recounting a previous session. |
|
missing scenes.
|
||||||
| `meta` | Sustained out-of-character discussion is the scene's primary purpose. |
|
|
||||||
|
|
||||||
For a mixed accepted chunk, classification prefers `combat`, then `recap`,
|
The [combat-turn artifact](dnd-combat-turn-artifacts.md) uses an exact matching
|
||||||
then `meta`, then `narrative`. Brief table talk, rules clarification, or a
|
`combat` scene only as eligibility control; scene title, summary, and source
|
||||||
short recollection does not replace the main current-session activity.
|
reference never become combat evidence. Publication is defined by the
|
||||||
|
[JSON output contract](json-output.md); implementation details live in
|
||||||
## Extraction and evidence
|
[D&D module internals](../internal/dnd.md).
|
||||||
|
|
||||||
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).
|
|
||||||
|
|||||||
@@ -1,204 +1,73 @@
|
|||||||
# D&D Spell Artifact
|
# D&D Spell Artifact
|
||||||
|
|
||||||
This document is the durable serialized artifact contract for the production
|
This contract defines the durable output of the `dnd/spells` extractor and
|
||||||
D&D spell extractor. Selectable extractor keys are cataloged in
|
normalizer. It records source-grounded spell-casting occurrences; it is not a
|
||||||
[Configuration](../config.md#implemented-production-modules).
|
spellbook, a rules lookup result, or a record of hypothetical casts.
|
||||||
|
|
||||||
## Identity
|
## Identity and compatibility
|
||||||
|
|
||||||
- Artifact kind: `dnd/spell-list`
|
| Property | Value |
|
||||||
- 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 |
|
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `spell_name_canonicalized` | A catalog lookup replaced an input name with its canonical display name. |
|
| Artifact kind | `dnd/spell-list` |
|
||||||
| `spell_name_unresolved` | A name was not found in the effective catalog and was retained unchanged. |
|
| Schema ID | `notarius.dnd.spells` |
|
||||||
| `source_references_normalized` | Reference order changed or exact duplicate references were removed. |
|
| Schema name | `notarius_dnd_spells_v1` |
|
||||||
| `duplicate_spell_cast_collapsed` | A later cast matched the retained cast's complete duplicate identity. |
|
| Schema version | `v1` |
|
||||||
|
| Media type | `application/json` |
|
||||||
|
|
||||||
Only warnings from an accepted normalize attempt are promoted to
|
`v1` is a single strict JSON object. It requires `spell_casts`; the array may
|
||||||
`warnings.json`. If an unresolved name reaches the default normalize validator
|
be empty. Each spell-cast object and source-reference object rejects unknown
|
||||||
chain, the catalog validator rejects the candidate with `unknown_spell`; the
|
fields. A future incompatible shape requires a new schema version.
|
||||||
`spell_name_unresolved` warning remains in the attempt's debug artifact. An
|
|
||||||
explicit validator override that accepts the candidate promotes the unresolved
|
|
||||||
warning normally.
|
|
||||||
|
|
||||||
The default extraction and normalization chains both preserve this registered
|
## Wire shape
|
||||||
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.
|
|
||||||
|
|
||||||
## Manifest Metadata
|
Each `spell_casts` entry has these required fields:
|
||||||
|
|
||||||
The extractor adds prompt and response-schema provenance under the artifact lane
|
| Field | Contract |
|
||||||
manifest metadata:
|
| --- | --- |
|
||||||
|
| `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
|
```json
|
||||||
{
|
{
|
||||||
"metadata": {
|
"spell_casts": [
|
||||||
"extractor": {
|
{
|
||||||
"prompt_id": "dnd.spells",
|
"caster": "Mira Thorn",
|
||||||
"prompt_version": "v1",
|
"spell": "Fireball",
|
||||||
"prompt_sha256": "sha256:...",
|
"source_refs": [
|
||||||
"response_schema_key": "dnd_spells",
|
{"source_id": "session-7", "start_unit_id": 12, "end_unit_id": 13}
|
||||||
"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"]
|
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`catalog_digest` identifies the effective semantic catalog, while
|
## Evidence and normalized form
|
||||||
`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).
|
|
||||||
|
|
||||||
The `npc_registry_digest` and `npc_count` fields in the example are present for
|
An entry represents an actual cast or an unambiguous declared attempt. A spell
|
||||||
an external NPC registry when the extractor publishes its prepared module
|
mention, rules discussion, plan, or catalog match alone is not an occurrence.
|
||||||
metadata. They contain no NPC names, source references, paths, or raw
|
The configured catalog checks the name; it does not establish evidence.
|
||||||
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.
|
|
||||||
|
|
||||||
The extractor's prompt hash, private response-schema hash, and effective catalog
|
When normalization is selected, recognized spell names use the effective
|
||||||
digest also contribute independently scoped semantic checkpoint fingerprints.
|
catalog's canonical display name. Source references are put in canonical source
|
||||||
Changing any of those prepared contracts intentionally produces a cold
|
order and exact duplicate references are removed. A later entry is collapsed
|
||||||
checkpoint miss. Fingerprints contain only digests, never prompt, schema,
|
only when it has the same canonical spell, the same case- and
|
||||||
catalog, or reference content. When an NPC registry is bound, its semantic
|
whitespace-insensitive caster identity, and the same complete valid reference
|
||||||
digest contributes an additional local `npc_registry` fingerprint for an
|
sequence. Remaining entries retain their merged order.
|
||||||
external binding; the manifest metadata contains only that digest and
|
|
||||||
`npc_count`. Raw NPC file provenance remains independently recorded in the
|
The optional normalized [NPC artifact](dnd-npc-artifacts.md) can ground a
|
||||||
manifest's `references` list. Generated bindings contribute the canonical
|
caster name. Its own references remain registry provenance and are never copied
|
||||||
artifact dependency fingerprint and bounded producer provenance instead.
|
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