Harmonize D&D artifact contracts

This commit is contained in:
2026-07-26 13:43:45 +00:00
parent 2f506f4985
commit f6981e2264
6 changed files with 281 additions and 800 deletions

View File

@@ -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.