Clarify and streamline D&D item events
This commit is contained in:
@@ -1,213 +0,0 @@
|
||||
# D&D Item Events
|
||||
|
||||
## Status
|
||||
|
||||
Implemented. This document retains the original intent record; current
|
||||
behavior is defined by the
|
||||
[D&D item-event artifact contract](../integrations/dnd-item-event-artifacts.md)
|
||||
and [Configuration](../config.md#implemented-production-modules).
|
||||
|
||||
## Purpose
|
||||
|
||||
Add a source-grounded D&D item-event lane that records meaningful changes in
|
||||
the party's knowledge or possession of items. The lane should answer what item
|
||||
was discovered, acquired, lost, consumed, or transferred, who held it when that
|
||||
is known, how many were involved when the transcript states a quantity, and
|
||||
where the event appears in the source.
|
||||
|
||||
The feature should follow the application's minimal-extraction policy. It
|
||||
records structured occurrences supported by transcript ranges and does not ask
|
||||
the model to maintain an inventory, infer unstated ownership, resolve semantic
|
||||
item identity, summarize an item's significance, or analyze how it was used.
|
||||
|
||||
## One Event Extractor
|
||||
|
||||
The initial production lane should use one LLM extractor and one deterministic
|
||||
normalizer under the selectable key `dnd/item-events`. Its durable artifact
|
||||
should be an ordered `dnd/item-event-list`.
|
||||
|
||||
Unlike NPC identification, item identification is not initially a separate
|
||||
artifact. An item matters to this feature because a tracked event happened to
|
||||
it, so a preliminary item-list extractor would duplicate evidence and add an
|
||||
ordered dependency without establishing a distinct useful contract.
|
||||
|
||||
An item registry may be reconsidered after real transcripts demonstrate a need
|
||||
for canonical aliases, stable cross-event identity, or downstream consumers
|
||||
that need items independently of events. If introduced later, it should be an
|
||||
explicit earlier artifact consumed through the existing ordered generated-
|
||||
reference mechanism. It should not be anticipated through placeholder IDs or
|
||||
implicit coupling in the v1 event schema.
|
||||
|
||||
## Durable Event Contract
|
||||
|
||||
The durable payload should contain one required `events` array, which may be
|
||||
empty. Each event should contain:
|
||||
|
||||
- `name`: a concise item name supported by the cited transcript;
|
||||
- `kind`: one of the five event categories below;
|
||||
- `quantity`: an optional positive integer, present only when the transcript
|
||||
establishes a count;
|
||||
- `from`: an optional holder losing possession;
|
||||
- `to`: an optional holder gaining possession; and
|
||||
- `source_refs`: one or more supporting current-source ranges.
|
||||
|
||||
`from` and `to` should use a transcript-supported party-member name when the
|
||||
holder is known. The reserved display value `party` should represent collective
|
||||
party possession when the event is clear but no individual holder is
|
||||
established. The extractor must not guess an individual holder.
|
||||
|
||||
Every extraction-time source range must be wholly contained in the current
|
||||
accepted chunk. Campaign references may disambiguate item or holder names but
|
||||
must never become item-event evidence.
|
||||
|
||||
The initial contract should not assign item IDs. Name-derived IDs would conflate
|
||||
distinct generic objects, while model-assigned instance IDs would require
|
||||
unreliable entity resolution. Separately evidenced events involving identically
|
||||
named items must remain representable.
|
||||
|
||||
## Event Categories
|
||||
|
||||
| Kind | Meaning |
|
||||
| --- | --- |
|
||||
| `discovered` | The party learns of or directly encounters an item without establishing possession. |
|
||||
| `acquired` | The party or a party member gains possession or control of an item. |
|
||||
| `lost` | The party or a party member ceases to possess or control an item without consuming it. |
|
||||
| `consumed` | Use depletes, expends, or destroys the tracked item. |
|
||||
| `transferred` | Possession moves from one party member to another without changing party-level ownership. |
|
||||
|
||||
Category and holder rules should be:
|
||||
|
||||
- `discovered` has neither `from` nor `to`.
|
||||
- `acquired` requires `to` and has no party-holder `from`. The item's external
|
||||
source may be described by the transcript but is not modeled as a holder.
|
||||
- `lost` requires `from` and has no party-holder `to`.
|
||||
- `consumed` requires `from` and has no `to`.
|
||||
- `transferred` requires distinct `from` and `to` party members.
|
||||
- Giving an item to an NPC, spending currency, selling an item, or otherwise
|
||||
moving it outside party possession is `lost`, not `transferred`.
|
||||
- An item destroyed through its intended or immediate use is `consumed`; an
|
||||
item destroyed, abandoned, stolen, or otherwise removed from control is
|
||||
`lost`.
|
||||
- Ordinary non-depleting use, such as swinging a sword, wearing armor, reading
|
||||
a reusable book, or activating a reusable item, is not an event.
|
||||
|
||||
When discovery and acquisition are separately established, each is a separate
|
||||
ordered event. A statement that establishes only acquisition must not cause the
|
||||
extractor to invent an additional discovery event.
|
||||
|
||||
## Quantities And Currency
|
||||
|
||||
Currency is an item for this contract. For example, recovering 20 silver pieces
|
||||
should produce an `acquired` event with the transcript-supported denomination,
|
||||
quantity `20`, the gaining holder or `party`, and source evidence.
|
||||
|
||||
Each denomination should remain a separate item name and event. The feature
|
||||
must not convert values between denominations, infer omitted quantities, or
|
||||
calculate balances. An absent quantity means the source did not establish a
|
||||
reliable count; it does not mean one.
|
||||
|
||||
Distinct explicit quantities remain distinct events. The normalizer must not
|
||||
sum nearby acquisitions, subtract losses, or reconcile contradictory counts.
|
||||
|
||||
## Event Boundaries And Normalization
|
||||
|
||||
One event represents one item, one category, one holder transition, and one
|
||||
locally coherent passage within one accepted chunk. Events never span chunks.
|
||||
A category change, holder change, distinct possession transition, intervening
|
||||
scene, or meaningful absence begins a new event.
|
||||
|
||||
The deterministic normalizer should:
|
||||
|
||||
- trim item and holder display values without semantic rewriting;
|
||||
- canonicalize and validate source-reference ordering;
|
||||
- order events by earliest source-document position, followed by stable
|
||||
deterministic tie-breakers over item name, kind, holders, quantity, and the
|
||||
complete evidence sequence; and
|
||||
- remove only exact duplicates after deterministic formatting.
|
||||
|
||||
It must not merge nearby events, reconcile aliases, singularize or rename
|
||||
items, infer missing holders or quantities, calculate party inventory, or use
|
||||
an LLM. Case-folded comparison may support deterministic ordering, but it must
|
||||
not silently make differently displayed item names the same durable identity.
|
||||
|
||||
## Prompt And Reference Policy
|
||||
|
||||
The extractor should follow the established shared D&D prompt organization and
|
||||
prompt-caching conventions. Stable shared instructions and stable campaign
|
||||
references should precede the variable transcript input.
|
||||
|
||||
It may declare the existing optional `players`, `party`, and `glossary`
|
||||
campaign-reference slots for name disambiguation. It should not require the NPC
|
||||
registry, scene-description artifact, or a new item registry. Item events are
|
||||
independent of those lanes and should not add an implicit cross-lane
|
||||
dependency.
|
||||
|
||||
The prompt should emphasize literal extraction, category and holder rules,
|
||||
current-session evidence, and omission when the event or quantity is uncertain.
|
||||
It should not request descriptions, item properties, rarity, value, narrative
|
||||
importance, inferred ownership history, or inventory summaries.
|
||||
|
||||
## Validation, Provenance, And Quality
|
||||
|
||||
The lane should follow the established typed D&D module pattern:
|
||||
|
||||
- a strict durable codec and a separate private structured-response schema;
|
||||
- deterministic shape, source-reference, and normalized-invariant validators;
|
||||
- warning-only source relatedness where it provides useful signal;
|
||||
- exact source identity and chunk containment during extraction;
|
||||
- deterministic candidate mapping that adds the current source ID rather than
|
||||
asking the model to emit it;
|
||||
- bounded, content-safe diagnostics and metadata; and
|
||||
- prompt, schema, mapping, and normalization policy fingerprints for checkpoint
|
||||
correctness.
|
||||
|
||||
Validation should reject unsupported kinds, invalid category/holder
|
||||
combinations, non-positive quantities, malformed or out-of-chunk references,
|
||||
and normalized ordering or duplicate invariants. Relatedness checks should
|
||||
remain advisory because pronouns, currency notation, and contextual item names
|
||||
may make exact lexical matching incomplete.
|
||||
|
||||
Tests should protect the durable schema, category and holder rules, explicit
|
||||
currency quantities, source containment, deterministic ordering, exact
|
||||
duplicate removal, prompt/reference boundaries, registration, and one
|
||||
representative assembled pipeline. They should remain deterministic and
|
||||
offline, use a fake only at the LLM boundary, and avoid assertions over private
|
||||
helper structure or probabilistic model quality.
|
||||
|
||||
## Configuration And Documentation
|
||||
|
||||
The feature should become a selectable D&D artifact lane in production module
|
||||
registration and configuration documentation. The maintained complete D&D
|
||||
example should include the item-event lane in the pipeline step appropriate for
|
||||
independent extraction; it must not add a generated-reference dependency merely
|
||||
to group item events with other event artifacts. The minimal example should
|
||||
remain unchanged.
|
||||
|
||||
When implemented, the durable schema and consumer semantics should have a
|
||||
canonical integration contract. Internal documentation should describe package
|
||||
ownership and module mechanics without duplicating that external contract.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
This scope does not:
|
||||
|
||||
- produce a complete current inventory or starting inventory;
|
||||
- calculate running balances, net currency, encumbrance, or item value;
|
||||
- identify every mentioned item when no tracked event occurs;
|
||||
- record ordinary non-consuming item use;
|
||||
- model containers, equipment slots, attunement, charges, or item properties;
|
||||
- assign stable instance IDs or semantically reconcile aliases;
|
||||
- derive item ownership from NPC, combat-turn, or scene artifacts;
|
||||
- introduce a second LLM extractor, an LLM-backed normalizer, or an item
|
||||
registry; or
|
||||
- introduce new pipeline orchestration or reference syntax.
|
||||
|
||||
## Completion Criteria
|
||||
|
||||
The feature is complete when a configured D&D lane can produce and normalize a
|
||||
strict ordered item-event artifact for discoveries, acquisitions, losses,
|
||||
consumption, intra-party transfers, and currency; every event is grounded in
|
||||
current transcript ranges; deterministic validation and normalization enforce
|
||||
the category, holder, quantity, ordering, and exact-duplicate policies; the
|
||||
complete maintained example selects the lane; and canonical current-behavior
|
||||
documentation replaces this roadmap as the source of implemented contracts.
|
||||
@@ -1,661 +0,0 @@
|
||||
# D&D Item Events Implementation Plan
|
||||
|
||||
## Purpose
|
||||
|
||||
This document is the decision-complete implementation plan for
|
||||
[D&D Item Events](dnd-item-events.md). Implement the stages below in order, with
|
||||
one stage per coding-agent prompt. The feature roadmap is authoritative for
|
||||
product intent, artifact semantics, and non-goals; this document defines how to
|
||||
reach that target state in the current architecture.
|
||||
|
||||
The work adds one LLM-backed `dnd/item-events` extractor. It records a
|
||||
source-grounded, ordered history of item and currency events without attempting
|
||||
to maintain inventory state. A deterministic normalizer prepares the extractor
|
||||
output for downstream use.
|
||||
|
||||
## Cross-Stage Rules
|
||||
|
||||
Apply these rules in every stage:
|
||||
|
||||
- Follow [Architecture Policy](../policy/architecture.md),
|
||||
[Documentation Policy](../policy/documentation.md), and
|
||||
[Testing Policy](../policy/testing.md).
|
||||
- Preserve the durable boundary: private provider response types belong to the
|
||||
extractor package; public item-event artifacts belong to `internal/modules/dnd`;
|
||||
codecs own strict wire validation.
|
||||
- Keep LLM extraction and deterministic processing separate. The extractor may
|
||||
map provider fields and attach the current source ID, but it must not repair,
|
||||
infer, merge, or semantically reinterpret events.
|
||||
- Reuse the existing shared D&D prompt assets and prompt assembly helpers. Do not
|
||||
copy their text into item-specific assets. Stable shared references must
|
||||
precede item-specific instructions and the changing transcript message must
|
||||
remain last.
|
||||
- Use strict option decoding and reject unknown module options.
|
||||
- Use policy identities with a `v1` suffix because this is the first item-event
|
||||
contract. Do not introduce a `v2` schema or backward-compatibility layer; the
|
||||
application is pre-release.
|
||||
- Keep validation diagnostics bounded and content-safe, and keep capability and
|
||||
identity metadata free of transcript, prompt, reference, and credential
|
||||
content.
|
||||
- Keep all tests offline and deterministic. Do not add live-provider tests,
|
||||
wall-clock-dependent assertions, exact prompt-prefix-length tests, or tests
|
||||
coupled only to private helper structure.
|
||||
- Do not make unrelated cleanup changes. Preserve user changes already present
|
||||
in the worktree.
|
||||
- At the end of every stage, run the narrowest relevant package tests and
|
||||
`go test ./...`. Run the repository's documented formatting and static-analysis
|
||||
commands before completing the final stage.
|
||||
|
||||
## Fixed Contract Decisions
|
||||
|
||||
The implementation must use the following names and shapes consistently.
|
||||
|
||||
### Module and artifact identities
|
||||
|
||||
- Extractor key: `dnd/item-events`
|
||||
- Extractor capability: `dnd.item_events`
|
||||
- Durable artifact kind: `dnd/item-event-list`
|
||||
- Durable schema ID: `notarius.dnd.item_events`
|
||||
- Durable schema name: `notarius_dnd_item_events_v1`
|
||||
- Durable schema version: `v1`
|
||||
- Private LLM schema ID: `notarius.dnd.item_events.llm`
|
||||
- Private LLM schema name: `notarius_dnd_item_events_llm_v1`
|
||||
- Private response schema key: `dnd_item_events_llm`
|
||||
- Prompt ID: `dnd.item_events`
|
||||
- Extraction mapping policy: `dnd.item_events.extract_mapping.v1`
|
||||
- Normalization policy: `dnd.item_events.normalize.v1`
|
||||
|
||||
Use the same identity formats, MIME type, metadata keys, and fingerprint
|
||||
construction conventions as the other current D&D extractors.
|
||||
|
||||
### Durable Go shape
|
||||
|
||||
Add public D&D domain types equivalent to:
|
||||
|
||||
```go
|
||||
type ItemEventKind string
|
||||
|
||||
const (
|
||||
ItemEventKindDiscovered ItemEventKind = "discovered"
|
||||
ItemEventKindAcquired ItemEventKind = "acquired"
|
||||
ItemEventKindLost ItemEventKind = "lost"
|
||||
ItemEventKindConsumed ItemEventKind = "consumed"
|
||||
ItemEventKindTransferred ItemEventKind = "transferred"
|
||||
)
|
||||
|
||||
type ItemEventList struct {
|
||||
Events []ItemEvent `json:"events"`
|
||||
}
|
||||
|
||||
type ItemEvent struct {
|
||||
Name string `json:"name"`
|
||||
Kind ItemEventKind `json:"kind"`
|
||||
Quantity *int `json:"quantity,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
To string `json:"to,omitempty"`
|
||||
SourceRefs []source.SourceRef `json:"source_refs"`
|
||||
}
|
||||
```
|
||||
|
||||
The exact source import follows the existing D&D types file. `Quantity` is a
|
||||
pointer so omission is distinguishable from an explicitly invalid zero. Empty
|
||||
`From` and `To` values mean the optional property is absent.
|
||||
|
||||
### Event semantics
|
||||
|
||||
The allowed combinations are:
|
||||
|
||||
| Kind | `from` | `to` | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `discovered` | absent | absent | The party learned that the item exists or encountered it without establishing possession. |
|
||||
| `acquired` | absent | required | A party member or the collective party gained possession. |
|
||||
| `lost` | required | absent | A party member or the collective party ceased to possess the item for a reason other than intended consumption. |
|
||||
| `consumed` | required | absent | Intended use depleted the item or currency. |
|
||||
| `transferred` | required | required | Possession moved between two distinct named party members. |
|
||||
|
||||
The reserved holder value `party` represents collective or unresolved party
|
||||
possession. It is allowed for `acquired`, `lost`, and `consumed`, but not for
|
||||
either side of `transferred`. A transfer must name two distinct party members.
|
||||
Compare holders case-insensitively and Unicode-normalized when determining
|
||||
whether they are distinct, but preserve their display spelling.
|
||||
|
||||
`Quantity`, when present, must be a positive integer grounded explicitly in the
|
||||
source. Currency is represented as an item whose `name` preserves the stated
|
||||
denomination; do not convert denominations or calculate balances.
|
||||
|
||||
Every event must contain at least one source reference. Ordinary non-consuming
|
||||
use is not an event. Gifts, sales, or payments to someone outside the party are
|
||||
`lost`; destruction, abandonment, and theft are also `lost`. Emit both
|
||||
`discovered` and `acquired` only when the source independently establishes both
|
||||
events.
|
||||
|
||||
### Deterministic order and equality
|
||||
|
||||
Create one canonical item-event helper package and make both normalization and
|
||||
invariant validation use it. The canonical order is:
|
||||
|
||||
1. earliest valid source position, using the pipeline source-document order;
|
||||
2. normalized comparison form of `name`, then exact trimmed `name`;
|
||||
3. `kind`;
|
||||
4. `from` presence, normalized comparison form, then exact trimmed value;
|
||||
5. `to` presence, normalized comparison form, then exact trimmed value;
|
||||
6. `quantity` presence, then numeric value;
|
||||
7. the complete canonical source-reference sequence.
|
||||
|
||||
Absent optional fields sort before present fields. Valid source positions sort
|
||||
before malformed positions; comparison must remain total and panic-free for
|
||||
invalid candidates so validators can report them.
|
||||
|
||||
An exact duplicate has the same trimmed name, kind, quantity presence and value,
|
||||
trimmed holders, and complete canonical source-reference sequence. Do not merge
|
||||
records that differ in any of those fields. Use collision-safe equality or key
|
||||
construction rather than delimiter concatenation.
|
||||
|
||||
## Stage 1: Domain Contract, Canonical Rules, and Durable Codec
|
||||
|
||||
### Goal
|
||||
|
||||
Establish the public artifact contract and strict durable boundary without
|
||||
registering an incomplete production module.
|
||||
|
||||
### Changes
|
||||
|
||||
1. Extend `internal/modules/dnd/types.go` with:
|
||||
- `ItemEventListKind`;
|
||||
- `ItemEventKind` and the five constants above;
|
||||
- `ItemEventList` and `ItemEvent` using the fixed durable shape.
|
||||
2. Add `internal/modules/dnd/itemevents` as the canonical domain helper package.
|
||||
It must own:
|
||||
- supported-kind checks;
|
||||
- validation of the kind-specific holder combination;
|
||||
- holder comparison sufficient to reject `party` transfers and
|
||||
self-transfers;
|
||||
- canonical ordering;
|
||||
- exact-duplicate comparison;
|
||||
- source-reference equality and validity helpers needed by normalization and
|
||||
invariant validation.
|
||||
3. Reuse the existing normalized comparison-key implementation used by D&D NPC
|
||||
identity code for Unicode- and case-insensitive comparison. Use
|
||||
`strings.TrimSpace` for item and holder display normalization. Do not broaden
|
||||
this stage into a generic identity-package refactor.
|
||||
4. Add `internal/modules/dnd/codec/itemevents` following the current D&D typed
|
||||
codec pattern:
|
||||
- embed a strict JSON Schema;
|
||||
- distinguish candidate decoding from approved encode/decode validation;
|
||||
- reject unknown fields;
|
||||
- expose the fixed artifact and schema identities;
|
||||
- report only bounded structural metadata such as `event_count`;
|
||||
- deep-clone source-reference slices and quantity pointers where ownership
|
||||
crosses a boundary.
|
||||
5. Define the durable JSON Schema with:
|
||||
- a required top-level `events` array, which may be empty;
|
||||
- required event properties `name`, `kind`, and non-empty `source_refs`;
|
||||
- the five-value `kind` enum;
|
||||
- non-empty strings for `name` and for holders when present;
|
||||
- an integer `quantity` with minimum `1`;
|
||||
- strict item, source-reference, and top-level objects;
|
||||
- conditional requirements/prohibitions matching the event-semantics table.
|
||||
6. Enforce transfer holders being distinct and not equal to `party` in approved
|
||||
codec validation, because those comparisons are not cleanly expressed by the
|
||||
JSON Schema. Candidate decoding must still preserve semantically invalid
|
||||
values for the validation/retry pipeline.
|
||||
|
||||
### Tests
|
||||
|
||||
Add package tests covering:
|
||||
|
||||
- all valid event kinds and holder combinations;
|
||||
- collective-party acquisition, loss, and consumption;
|
||||
- rejection of `party` transfers and case/Unicode-equivalent self-transfers;
|
||||
- omitted versus present quantity and rejection of zero or negative quantity;
|
||||
- strict unknown-field rejection;
|
||||
- empty event lists;
|
||||
- missing or empty source references;
|
||||
- candidate decoding preserving values that approved decoding rejects;
|
||||
- canonical order, including all tie-breakers and malformed references;
|
||||
- exact equality distinguishing optional-field presence/value and complete
|
||||
evidence;
|
||||
- deep-copy behavior for source references and quantity pointers;
|
||||
- stable schema identity and metadata without payload leakage.
|
||||
|
||||
### Completion Criteria
|
||||
|
||||
- Public item-event values round-trip through the approved codec only when they
|
||||
satisfy the durable contract.
|
||||
- Candidate decoding can carry invalid semantic values to validators.
|
||||
- Canonical comparison and equality have a single tested implementation.
|
||||
- No item-event module, validator chain, or prompt is globally registered yet.
|
||||
|
||||
## Stage 2: Extractor, Private Response Schema, and Prompt Assets
|
||||
|
||||
### Goal
|
||||
|
||||
Implement source-grounded item-event extraction while preserving the boundary
|
||||
between provider output and the durable artifact.
|
||||
|
||||
### Changes
|
||||
|
||||
1. Add `internal/modules/dnd/extract/itemevents` following the common D&D
|
||||
extractor structure:
|
||||
- key `dnd/item-events`;
|
||||
- requires `chunks` and `source.transcript`;
|
||||
- provides `dnd.item_events`;
|
||||
- strict empty options;
|
||||
- nil-client guard;
|
||||
- shared chunk preflight and source-reference ordering;
|
||||
- the current D&D default model profile and repair-attempt convention.
|
||||
2. Define private provider response structs inside the extractor package. The
|
||||
response contains a required `events` array whose entries have:
|
||||
- required `name`, `kind`, and `source_refs`;
|
||||
- optional pointer `quantity`;
|
||||
- optional `from` and `to`;
|
||||
- private source ranges containing only `start_segment` and `end_segment`.
|
||||
3. Add the embedded private structured-output schema using the fixed private
|
||||
identities. Keep it structurally strict, but do not encode the durable
|
||||
semantic enum, positive-quantity, holder-combination, or transcript-range
|
||||
rules there. Those rules belong to deterministic application validators so
|
||||
invalid model output participates in the normal retry/diagnostic flow.
|
||||
4. Map the private response to `dnd.ItemEventList` by:
|
||||
- copying all provider fields without semantic rewriting;
|
||||
- attaching the current source ID to every private source range;
|
||||
- canonicalizing and deduplicating source references using the shared source
|
||||
order;
|
||||
- deterministically ordering candidates by earliest evidence as the existing
|
||||
D&D extractors do;
|
||||
- preserving quantity presence with a fresh pointer.
|
||||
5. Add item-specific prompt assets for the task and instructions only. The
|
||||
instructions must state all event semantics from the feature roadmap,
|
||||
including currency, holder rules, non-consuming use, and the distinction
|
||||
between discovery and acquisition.
|
||||
6. Assemble the prompt from existing shared assets in this order:
|
||||
- shared D&D system message;
|
||||
- shared extraction-evidence user message;
|
||||
- shared identity/reference context messages;
|
||||
- item-event task message;
|
||||
- rendered item-event instructions;
|
||||
- shared transcript message last.
|
||||
7. Accept the same prompt inputs as the NPC extractor:
|
||||
- required transcript;
|
||||
- optional players, party, and glossary references.
|
||||
Do not accept or inject NPC registries, scene descriptions, item registries,
|
||||
or output from another generated lane.
|
||||
8. Use the shared prompt manifest and caching metadata conventions. Reuse shared
|
||||
asset descriptors rather than creating item-specific copies. Publish the
|
||||
prompt, response-schema, and extraction-mapping policy identities and their
|
||||
fingerprints in module metadata.
|
||||
|
||||
### Tests
|
||||
|
||||
Add extractor and prompt tests covering:
|
||||
|
||||
- key, capabilities, options, nil-client behavior, and preflight errors;
|
||||
- private-schema identity and strict structural decoding;
|
||||
- all five kinds, currency quantity, collective `party`, and omitted optional
|
||||
fields mapping into durable candidates;
|
||||
- source-ID attachment, source-reference canonicalization, deterministic
|
||||
ordering, and quantity-pointer independence;
|
||||
- preservation of invalid semantic candidates for validators;
|
||||
- empty provider results producing a valid empty candidate list;
|
||||
- exact prompt asset sequence and roles, with transcript last;
|
||||
- optional reference rendering and absence behavior;
|
||||
- reuse of shared asset descriptors and no module-specific duplicate of shared
|
||||
prompt text;
|
||||
- stable fingerprint metadata that changes for item-specific prompt/schema/
|
||||
mapping changes without exposing rendered content.
|
||||
|
||||
Do not add a test that asserts the numeric length of a shared prompt prefix.
|
||||
|
||||
### Completion Criteria
|
||||
|
||||
- A fake structured-completion client can drive the extractor from a transcript
|
||||
chunk to a typed candidate artifact.
|
||||
- Prompt ordering supports backend prompt caching and reuses shared assets
|
||||
exactly.
|
||||
- The extractor is package-complete but is not yet added to production
|
||||
registration.
|
||||
|
||||
## Stage 3: Shape and Source-Reference Validators
|
||||
|
||||
### Goal
|
||||
|
||||
Add deterministic blocking validation for event semantics and evidence.
|
||||
|
||||
### Changes
|
||||
|
||||
1. Add `internal/modules/dnd/validate/itemevents/shape`:
|
||||
- key `extract/dnd/item-events/shape`;
|
||||
- policy identity `dnd.item_events.shape.v1`;
|
||||
- blocking reason code `invalid_item_event_shape`.
|
||||
2. The shape validator must reject:
|
||||
- absent or incorrectly typed item-event artifacts;
|
||||
- blank item names after trimming;
|
||||
- unsupported kinds;
|
||||
- invalid kind-specific `from`/`to` combinations;
|
||||
- `party` on either side of a transfer;
|
||||
- transfers whose holders have the same normalized comparison key;
|
||||
- present quantities less than one;
|
||||
- missing source-reference lists.
|
||||
3. Permit leading/trailing whitespace in nonblank display fields at the
|
||||
extraction boundary so the normalizer can perform deterministic trimming.
|
||||
Treat an empty optional holder string as absence.
|
||||
4. Add `internal/modules/dnd/validate/itemevents/source_refs`:
|
||||
- key `extract/dnd/item-events/source_refs`;
|
||||
- policy identity `dnd.item_events.source_refs.v1`;
|
||||
- blocking reason code `invalid_item_event_source_references`.
|
||||
5. Mirror the current D&D source-reference validator behavior:
|
||||
- validate source IDs and inclusive segment ranges against the available
|
||||
source document;
|
||||
- when validating extraction for a specific chunk, require each range to be
|
||||
wholly contained in that chunk;
|
||||
- when validating a merged or normalized artifact without a chunk, validate
|
||||
against the source document without imposing a single-chunk condition;
|
||||
- emit bounded, indexed diagnostics.
|
||||
6. Share the canonical domain helpers from Stage 1 rather than independently
|
||||
encoding holder semantics or source-reference validity.
|
||||
|
||||
### Tests
|
||||
|
||||
Cover every accepted and rejected row of the event-semantics table, plus:
|
||||
|
||||
- whitespace that is normalizable versus whitespace-only required fields;
|
||||
- quantities `nil`, `1`, `0`, and negative;
|
||||
- transfer holder comparison across case and Unicode normalization;
|
||||
- `party` in each applicable and inapplicable position;
|
||||
- empty lists and multiple independently diagnosed invalid records;
|
||||
- unknown source IDs, reversed ranges, out-of-bounds ranges, and extraction
|
||||
ranges crossing the current chunk;
|
||||
- valid multi-range and multi-chunk post-merge evidence;
|
||||
- diagnostic caps and stable policy metadata.
|
||||
|
||||
### Completion Criteria
|
||||
|
||||
- Invalid event semantics and invalid evidence fail with distinct owning
|
||||
validators and reason codes.
|
||||
- Valid candidates, including empty lists and currency events, pass.
|
||||
- The validators remain package-complete but are not yet in production chains.
|
||||
|
||||
## Stage 4: Deterministic Normalizer and Normalized Invariants
|
||||
|
||||
### Goal
|
||||
|
||||
Normalize presentation and ordering without inventing inventory semantics, then
|
||||
verify the normalized contract independently.
|
||||
|
||||
### Changes
|
||||
|
||||
1. Add `internal/modules/dnd/normalize/itemevents`:
|
||||
- key `dnd/item-events`;
|
||||
- requires `merged`;
|
||||
- provides `normalized`;
|
||||
- strict empty options;
|
||||
- policy identity `dnd.item_events.normalize.v1`;
|
||||
- no generated-reference dependency.
|
||||
2. For each event, the normalizer must:
|
||||
- trim leading/trailing whitespace from `name`, `from`, and `to`;
|
||||
- preserve case and internal spelling;
|
||||
- clone the optional quantity;
|
||||
- canonicalize and deduplicate source references using the source-document
|
||||
order;
|
||||
- avoid changing kind, quantity, holders, or event meaning.
|
||||
3. Sort the list with the Stage 1 canonical comparator.
|
||||
4. Collapse exact duplicates only after display trimming and source-reference
|
||||
canonicalization. Preserve invalid records when safe to do so; do not allow
|
||||
malformed evidence to panic or cause unrelated records to disappear.
|
||||
5. Emit bounded warning diagnostics, following existing D&D normalizer
|
||||
conventions, for:
|
||||
- normalized display fields;
|
||||
- canonicalized source references;
|
||||
- reordered events;
|
||||
- collapsed exact duplicates;
|
||||
- omitted warnings after the cap.
|
||||
6. Publish normalization policy metadata and fingerprint using the common
|
||||
normalizer conventions.
|
||||
7. Add `internal/modules/dnd/validate/itemevents/invariants`:
|
||||
- key `normalize/dnd/item-events/invariants`;
|
||||
- policy identity `dnd.item_events.normalize_invariants.v1`;
|
||||
- blocking reason code `invalid_normalized_item_event_invariants`.
|
||||
8. The invariant validator must require:
|
||||
- trimmed display fields;
|
||||
- canonical source-reference sequences;
|
||||
- canonical list order;
|
||||
- no exact duplicates.
|
||||
It must use the same Stage 1 helpers as the normalizer. It should defer
|
||||
malformed shape and range reporting to their owning validators instead of
|
||||
emitting competing diagnoses.
|
||||
|
||||
### Tests
|
||||
|
||||
Cover:
|
||||
|
||||
- each individual trim and source-reference normalization;
|
||||
- canonical ordering through every tie-breaker;
|
||||
- exact-duplicate collapse and preservation of near-duplicates;
|
||||
- quantity-pointer cloning;
|
||||
- empty and singleton lists;
|
||||
- deterministic behavior under repeated normalization;
|
||||
- malformed candidates remaining diagnosable without panics;
|
||||
- every invariant failure independently;
|
||||
- agreement between normalizer output and invariant validation;
|
||||
- warning caps and non-sensitive metadata/fingerprints.
|
||||
|
||||
Include at least one test demonstrating that no aliasing, singularization,
|
||||
denomination conversion, discovery inference, acquisition inference, transfer
|
||||
inference, or ledger calculation occurs.
|
||||
|
||||
### Completion Criteria
|
||||
|
||||
- Normalizing the same merged artifact repeatedly is idempotent.
|
||||
- Every valid normalizer output passes the invariant validator.
|
||||
- Semantically distinct events remain distinct.
|
||||
- The normalizer and invariant validator are not yet globally registered.
|
||||
|
||||
## Stage 5: Relatedness Validator and Production Registration
|
||||
|
||||
### Goal
|
||||
|
||||
Add advisory grounding checks, then register the complete module stack and its
|
||||
default validation chains atomically.
|
||||
|
||||
### Changes
|
||||
|
||||
1. Add `internal/modules/dnd/validate/itemevents/source_relatedness`:
|
||||
- key `extract/dnd/item-events/source_relatedness`;
|
||||
- policy identity `dnd.item_events.source_relatedness.v1`;
|
||||
- warning reason code `item_event_source_unrelated`.
|
||||
2. For every event with otherwise readable evidence, aggregate the cited
|
||||
transcript text and look for a normalized token sequence derived from the
|
||||
item `name`, using the existing shared D&D token helpers. Do not require
|
||||
holder names or quantities to appear in every cited range. Relatedness is
|
||||
advisory only, so aliases, currency abbreviations, and transcript variation
|
||||
may warn but must not block.
|
||||
3. Bound warnings consistently with the other D&D relatedness validators and
|
||||
avoid duplicating range-validity diagnostics owned by the source-reference
|
||||
validator.
|
||||
4. Update D&D production registration to add:
|
||||
- the typed item-event codec;
|
||||
- the standard always-accept and always-reject typed validators;
|
||||
- an append-order merger that deep-clones item events, quantity pointers, and
|
||||
source references while preserving nil-versus-empty list behavior;
|
||||
- the item-event extractor and its prompt/schema assets;
|
||||
- the custom item-event normalizer and standard no-op normalizer;
|
||||
- shape, source-reference, source-relatedness, and invariant validators.
|
||||
5. Add default chains with these exact members and ordering:
|
||||
|
||||
Extraction:
|
||||
|
||||
1. generic valid JSON;
|
||||
2. item-event shape;
|
||||
3. item-event source references;
|
||||
4. generic valid JSON Schema;
|
||||
5. item-event source relatedness.
|
||||
|
||||
Normalization:
|
||||
|
||||
1. generic valid JSON;
|
||||
2. item-event shape;
|
||||
3. normalized item-event invariants;
|
||||
4. item-event source references;
|
||||
5. generic valid JSON Schema;
|
||||
6. item-event source relatedness.
|
||||
|
||||
6. Do not add a registry validator or a generated-reference capability. Item
|
||||
events intentionally operate independently of NPC, scene, and other
|
||||
extraction outputs.
|
||||
7. Extend registration and chain contract tests so omissions, duplicate
|
||||
registration, incorrect order, policy-identity collisions, and capability
|
||||
drift fail clearly.
|
||||
|
||||
### Tests
|
||||
|
||||
Add tests for:
|
||||
|
||||
- related and unrelated item names, punctuation/case variation, multiple source
|
||||
ranges, invalid ranges, and capped warnings;
|
||||
- registration of every item-event component and asset;
|
||||
- exact default-chain order and blocking/advisory behavior;
|
||||
- append-order merge semantics, deep cloning, and nil/empty preservation;
|
||||
- duplicate-key and identity uniqueness across the full production registry;
|
||||
- module metadata and capabilities containing identities only, not rendered
|
||||
content.
|
||||
|
||||
### Completion Criteria
|
||||
|
||||
- The fully assembled registry exposes the item-event extractor, codec, merger,
|
||||
normalizers, validators, prompt assets, and both default chains.
|
||||
- The default extraction and normalization chains accept valid item events,
|
||||
block invalid shape/evidence, and only warn on relatedness.
|
||||
- Existing registered modules and chains remain unchanged.
|
||||
|
||||
## Stage 6: End-to-End Integration and Maintained Configuration
|
||||
|
||||
### Goal
|
||||
|
||||
Prove the production path and make the complete supported configuration exercise
|
||||
the new independent lane.
|
||||
|
||||
### Changes
|
||||
|
||||
1. Add a focused production-registry integration test that runs an item-events
|
||||
lane from a transcript chunk through:
|
||||
- extraction;
|
||||
- candidate validation;
|
||||
- merge;
|
||||
- deterministic normalization;
|
||||
- normalized validation;
|
||||
- durable encode/decode.
|
||||
2. Use a deterministic fake structured-completion client. Exercise at least:
|
||||
- one currency event with explicit quantity;
|
||||
- one event using collective `party`;
|
||||
- one transfer between named party members;
|
||||
- multiple events returned out of source order;
|
||||
- an exact duplicate that normalization removes;
|
||||
- a relatedness warning that does not fail the run.
|
||||
3. Add focused negative integration coverage proving a blocking shape or
|
||||
source-reference failure follows the configured retry/failure path rather
|
||||
than bypassing validation. Do not use a live LLM.
|
||||
4. Add `dnd/item-events` to the complete D&D configuration example. Place it in
|
||||
the first pipeline step alongside other independent extraction lanes; do not
|
||||
give it a dependency on generated NPC or scene artifacts. Leave the minimal
|
||||
example unchanged.
|
||||
5. Update maintained example contract tests and deterministic CLI/provider test
|
||||
doubles so they recognize `dnd.item_events`, return a structurally valid
|
||||
private response, and account for the additional output artifact. Avoid
|
||||
brittle whole-file snapshots where focused semantic assertions suffice.
|
||||
6. Verify the example still demonstrates the architecture already selected for
|
||||
other lanes: independent work in the first step and only genuinely dependent
|
||||
lanes in later steps.
|
||||
|
||||
### Tests and Verification
|
||||
|
||||
Run:
|
||||
|
||||
- the focused new integration tests;
|
||||
- example/configuration contract tests;
|
||||
- CLI tests that execute or inspect the complete example;
|
||||
- `go test ./...`.
|
||||
|
||||
Also perform a local configuration validation or dry run using the repository's
|
||||
documented offline-safe mechanism. Do not require credentials or a provider
|
||||
network call.
|
||||
|
||||
### Completion Criteria
|
||||
|
||||
- A production-registry pipeline produces a durable, normalized
|
||||
`dnd/item-event-list`.
|
||||
- The complete example configures the lane in the correct independent step and
|
||||
remains accepted by configuration validation.
|
||||
- The minimal example and all existing module behavior remain unchanged.
|
||||
|
||||
## Stage 7: Canonical Documentation and Final Verification
|
||||
|
||||
### Goal
|
||||
|
||||
Document the implemented contract in its owning locations, retire the future
|
||||
work entry, and perform repository-wide verification.
|
||||
|
||||
### Changes
|
||||
|
||||
1. Add a canonical item-event artifact guide under `docs/integrations/`,
|
||||
following the current D&D artifact documentation pattern. Document:
|
||||
- module, capability, artifact, and schema identities;
|
||||
- every field and event-kind rule;
|
||||
- the reserved `party` holder;
|
||||
- currency representation;
|
||||
- source-reference requirements;
|
||||
- deterministic order and exact-duplicate behavior;
|
||||
- representative JSON for every kind;
|
||||
- the distinction between event extraction and inventory/ledger state;
|
||||
- the absence of dependencies on generated NPC, scene, or item registries.
|
||||
2. Update the canonical configuration/module documentation to list:
|
||||
- the extractor;
|
||||
- supported prompt inputs;
|
||||
- the merger and normalizers;
|
||||
- validator keys and default chains;
|
||||
- the artifact kind and codec.
|
||||
3. Update internal architecture/module documentation and operational workflow
|
||||
documentation wherever their exhaustive lists or complete-example walkthrough
|
||||
would otherwise become inaccurate.
|
||||
4. Update `docs/roadmap/future.md` to remove the implemented item-tracking entry
|
||||
rather than leaving duplicate current and future documentation. Preserve
|
||||
unrelated future ideas.
|
||||
5. Mark `docs/roadmap/dnd-item-events.md` as implemented. Keep it as the feature
|
||||
intent record for now; roadmap retirement is a separate cleanup decision.
|
||||
6. Check links, terminology, and identity strings across code, schemas, examples,
|
||||
and docs. There must be no accidental use of `items`, `item-tracking`,
|
||||
`inventory-events`, or a `v2` identity where the fixed names require
|
||||
`item-events` and `v1`.
|
||||
|
||||
### Final Verification
|
||||
|
||||
Run the repository-documented:
|
||||
|
||||
- formatter;
|
||||
- full test suite;
|
||||
- static analysis;
|
||||
- documentation/link checks, if available;
|
||||
- configuration/example validation.
|
||||
|
||||
Inspect `git diff --check` and the final diff. Confirm that:
|
||||
|
||||
- no live-provider or credential-bearing test was introduced;
|
||||
- no shared prompt content was copied into item-specific assets;
|
||||
- no exact shared-prefix-length change-detector test was added;
|
||||
- no raw transcript, prompt, reference, or credential data appears in metadata,
|
||||
diagnostics, or fingerprints;
|
||||
- no item ledger, balance, item identity registry, alias resolution, ordinary-use
|
||||
tracking, or other roadmap non-goal slipped into scope;
|
||||
- unrelated roadmap and user changes are preserved.
|
||||
|
||||
### Completion Criteria
|
||||
|
||||
- Code, examples, and canonical documentation describe the same item-event
|
||||
contract and identities.
|
||||
- The future roadmap no longer presents this implemented feature as future work.
|
||||
- All required repository checks pass.
|
||||
|
||||
## Open Questions
|
||||
|
||||
None. The feature roadmap and the fixed decisions above define the artifact,
|
||||
event semantics, pipeline placement, validation ownership, normalization rules,
|
||||
and documentation boundary needed to implement each stage without further
|
||||
product decisions.
|
||||
Reference in New Issue
Block a user