Compose production D&D combat pipeline

This commit is contained in:
2026-07-21 05:42:33 +00:00
parent 50aa60e0b8
commit a1f5dce405
19 changed files with 963 additions and 62 deletions

View File

@@ -104,6 +104,21 @@ go run ./cmd/notarius run dnd-spells \
--reference spells.extract.npcs=./npc-run/lanes/npcs.json
```
For the independent NPC-to-combat workflow, bind the same completed NPC lane
to both combat stages explicitly:
```sh
go run ./cmd/notarius run dnd-combat \
--config examples/dnd-npc-combat-sequential.config.yml \
--input examples/seriatim-minimal-transcript.json \
--reference combat.extract.npcs=./npc-run/lanes/npcs.json \
--reference combat.normalize.npcs=./npc-run/lanes/npcs.json
```
The two selectors are independent stage-local bindings. Binding extraction
does not implicitly bind normalization, and Notarius does not discover or
schedule the preceding NPC run.
The same grammar can target chunk, merge, and normalize slots when the configured
modules declare them:

View File

@@ -23,6 +23,8 @@ The explicit-path option is defined in the [CLI reference](cli.md).
- [Production-oriented D&D spell configuration](../examples/dnd-spells-production.config.yml)
- [D&D NPC configuration](../examples/dnd-npcs.config.yml)
- [Sequential D&D NPC and spell configuration](../examples/dnd-npc-spell-sequential.config.yml)
- [D&D combat-turn configuration](../examples/dnd-combat-turns.config.yml)
- [Sequential D&D NPC and combat-turn configuration](../examples/dnd-npc-combat-sequential.config.yml)
All are complete version 3 files. The fragments below illustrate individual
fields and are not alternate complete configurations.
@@ -286,10 +288,12 @@ production validators do not call the LLM and must not set `llm_profile`.
| chunk | `dnd/scenes` | Uses an LLM to split transcript source units into D&D scenes. |
| extract | `dnd/spells` | Extracts typed D&D spell-list artifacts. |
| extract | `dnd/npcs` | Extracts typed D&D NPC-list artifacts. |
| extract | `dnd/combat-turns` | Extracts typed D&D combat-turn-list artifacts. |
| merge | `appendorder` | Combines typed artifacts in chunk order. |
| normalize | `noop` | Passes merged typed artifacts through unchanged. |
| normalize | `dnd/spells` | Deterministically canonicalizes and de-duplicates typed D&D spell-list artifacts. |
| normalize | `dnd/npcs` | Deterministically consolidates typed D&D NPC-list artifacts by canonical identity and aliases. |
| normalize | `dnd/combat-turns` | Deterministically canonicalizes, orders, and de-duplicates typed D&D combat-turn artifacts. |
| output | `json` | Produces JSON output files for normalized `application/json` lanes. |
## Implemented Production Validators
@@ -308,6 +312,10 @@ production validators do not call the LLM and must not set `llm_profile`.
| `extract/dnd/npcs/source_refs` | deterministic | Rejects missing or invalid D&D NPC source references. |
| `extract/dnd/npcs/source_relatedness` | deterministic | Emits warnings when an NPC name or alias is not found near its cited source text. |
| `normalize/dnd/npcs/identity` | deterministic | Rejects invalid canonical IDs, aliases, and cross-record identity collisions. |
| `extract/dnd/combat-turns/shape` | deterministic | Rejects malformed D&D combat-turn artifacts. |
| `extract/dnd/combat-turns/source_refs` | deterministic | Rejects missing or invalid D&D combat-turn source references. |
| `extract/dnd/combat-turns/source_relatedness` | deterministic | Emits warnings when an actor or declared action is not found near cited source text. |
| `normalize/dnd/combat-turns/invariants` | deterministic | Rejects normalized combat-turn identity, target, evidence-order, and chronology violations. |
The production default chain for `dnd/spells` is used for both its extract and
normalize stages:
@@ -342,6 +350,26 @@ normalize:
- extract/dnd/npcs/source_relatedness
```
The production default chains for `dnd/combat-turns` are:
```yaml
extract:
validators:
- generic/valid_json
- generic/valid_json_schema
- extract/dnd/combat-turns/shape
- extract/dnd/combat-turns/source_refs
- extract/dnd/combat-turns/source_relatedness
normalize:
validators:
- generic/valid_json
- generic/valid_json_schema
- extract/dnd/combat-turns/shape
- normalize/dnd/combat-turns/invariants
- extract/dnd/combat-turns/source_refs
- extract/dnd/combat-turns/source_relatedness
```
Empty chains approve output by default.
The `generic` chunker accepts:
@@ -395,6 +423,26 @@ accepts no references. To pass an NPC result to a later spell run, bind the
normalized payload explicitly at runtime; the maintained sequential example
documents that operator workflow.
The `dnd/combat-turns` extractor and normalizer declare the same optional
campaign slots and the structured `npcs` slot. The combat extractor uses the
NPC payload only to ground actor and target identity; the normalizer uses its
prepared immutable registry for the same canonicalization. Both slots accept
exactly one UTF-8 `application/json` file no larger than 1 MiB. The registry's
source ranges remain provenance for the reference and never become combat
evidence. Binding `npcs` to extraction and normalization is stage-local, so an
operator-driven combat run uses two explicit selectors:
```text
combat.extract.npcs=<npc-run>/lanes/npcs.json
combat.normalize.npcs=<npc-run>/lanes/npcs.json
```
When bound, the combat extractor and normalizer record only the registry's
semantic digest and count in their metadata and checkpoint fingerprints; names,
aliases, content, and paths are not recorded there. When absent, the combat
prompt receives the exact empty registry value `{"npcs":[]}` and no registry
provenance or fingerprint is recorded.
## State Surfaces
The `output`, `cache`, and `debug` top-level fields select independent physical

View File

@@ -1,9 +1,8 @@
# D&D Combat-Turn Artifact Contract
This document defines the durable artifact, serialization, extraction,
candidate-validation, and standalone normalization boundaries for D&D combat
turns. Production composition and a selectable pipeline lane are not part of
this contract yet.
candidate-validation, normalization, and production lane boundaries for D&D
combat turns.
## Artifact identity
@@ -119,8 +118,9 @@ the shared Unicode-aware NPC identity policy, and checks declaration tokens of
at least four Unicode code points against complete cited-text tokens. Targets
are not checked deterministically.
The extractor and validators are package-complete but are not registered by the
production D&D family registrar yet.
The production D&D registrar exposes the extractor and these validators. Its
default extraction chain preserves this order: JSON syntax, private response
schema, combat shape, source references, then source relatedness.
## Normalization boundary
@@ -149,5 +149,19 @@ canonical evidence ordering, chronology, and duplicate identity. It rejects
with `invalid_combat_turn_normalization` under policy
`dnd.combat_turns.validator.normalized.v1`.
The normalizer and normalized-invariants validator are package-complete but are
not registered by the production D&D family registrar yet.
The production D&D registrar exposes the normalizer and normalized-invariants
validator. Its default normalization chain is JSON syntax, durable schema,
combat shape, normalized invariants, source references, then source
relatedness. The lane uses the framework's typed append-order merger and has no
merge validator chain.
## Production manifest and references
The selectable lane uses extractor and normalizer key `dnd/combat-turns`,
`appendorder` for the typed merger, and the durable codec above. A bound `npcs`
reference contributes raw-file provenance to the run manifest. Prepared combat
extractor and normalizer metadata and checkpoint fingerprints contain only the
NPC registry's semantic digest and count; the registry content, path, and NPC
source ranges are not copied into combat output. The normalized lane is emitted
as `lanes/<lane-id>.json` by the JSON output module, and warnings and rejection
summaries remain in their shared companion files.

View File

@@ -2,8 +2,9 @@
This document defines the durable D&D NPC-list artifact, its JSON codec, and
the selectable production NPC pipeline. The normalized JSON payload can be
passed explicitly to the spell extractor as an optional caster-name registry;
it remains a reference, not spell evidence.
passed explicitly to the spell extractor as an optional caster-name registry
or to the combat extractor and normalizer as an actor/target registry. It
remains a reference, not spell or combat evidence.
## Identity

View File

@@ -151,8 +151,9 @@ accepts only artifacts whose codec media type is `application/json`. The file
contains the codec-owned JSON bytes pretty-printed.
The schema of each lane payload is owned by that artifact contract. For the
current D&D lanes, see [D&D Spell Artifact](dnd-spell-artifacts.md) and
[D&D NPC Artifact](dnd-npc-artifacts.md).
current D&D lanes, see [D&D Spell Artifact](dnd-spell-artifacts.md),
[D&D NPC Artifact](dnd-npc-artifacts.md), and
[D&D Combat-Turn Artifact](dnd-combat-turn-artifacts.md).
## `rejected.json`

View File

@@ -39,9 +39,9 @@ without exposing Scriptorium types through stage contracts.
7. injecting that one shared client into complete pipeline preparation before
the source file is read or the runner is invoked.
The D&D scene chunker and spell and NPC extractors retain this injected client
and use it for every structured completion. Operation requests do not carry an
LLM client.
The D&D scene chunker and spell, NPC, and combat extractors retain this
injected client and use it for every structured completion. Operation requests
do not carry an LLM client.
The CLI separately gathers explicit profile IDs from resolved LLM-capable stage
and validator bindings. It prepares a small internal check prompt for each ID so
@@ -103,18 +103,22 @@ return defensive copies, and expose a diagnostics map that omits schema bytes.
The small framework registry contains only generic test schemas; production
schemas remain package-owned.
The spell extractor's package-owned prompt declares a required
The spell and combat extractors' package-owned prompts declare their structured
JSON inputs and private response schemas. The spell extractor's prompt declares a required
`application/json` `spell_catalog` input and an optional `application/json`
`npcs` input. The extractor generates the catalog input from its prepared
effective catalog as `{"spell_names":[...]}` using sorted canonical names only.
The shared D&D prompt assets include a generic NPC grounding fragment directly
after the campaign reference message. When an NPC registry is bound, the
after the campaign reference message for both extractors. When an NPC registry
is bound, the
domain registry boundary strictly decodes and identity-validates one durable
artifact, re-encodes canonical JSON, and generates a semantic digest over
those bytes. The unbound input is exactly `{"npcs":[]}`. Input digests cover
the generated bytes; manifests record catalog identity and optional NPC
registry digest/count rather than names, aliases, overlay bytes, registry
paths, or source metadata.
paths, or source metadata. Combat prompt, response-schema, mapping,
normalization, identity, and bound-registry fingerprints remain separate
semantic inputs to checkpoint identity.
## Debug And Redaction Boundaries

View File

@@ -17,17 +17,17 @@ validator registry. Package-family registrars compose those leaf registrations
into the production catalog and own family-level policy such as default
validator chains and prompt asset collection.
Production input, chunk, output, and D&D spell-extract packages register strict
option decoders and run-local builders. Preparation decodes their options into
Production input, chunk, output, and D&D spell- and combat-extract packages
register strict option decoders and run-local builders. Preparation decodes their options into
implementation-owned values and injects dependencies plus the materialized
reference set for the selected target. Each builder receives an isolated clone
of that set; input and output builders receive no references. The spell
extractor is typed over the canonical D&D model. D&D validators, merge, and
normalize use typed variants; JSON representation validators use serialized
of that set; input and output builders receive no references. The spell and
combat extractors are typed over the canonical D&D model. D&D validators, merge,
and normalize use typed variants; JSON representation validators use serialized
requests; and unconditional validators expose separate chunk and typed
variants. The D&D production registrar registers the canonical typed spell and
NPC implementations, including their kind-specific merge and normalize
behavior.
variants. The D&D production registrar registers the canonical typed spell,
NPC, and combat implementations, including their kind-specific merge and
normalize behavior.
Prepared extractors, extract validators, and codecs may be reused concurrently
by the run-wide extract pool. Production implementations are immutable after
@@ -98,8 +98,8 @@ candidate versus approved encode/decode operations.
The `internal/modules/dnd/codec/combatturns` package owns the durable
`dnd/combat-turn-list` schema and candidate versus approved JSON boundary. It
is available as a package contract but is not registered by the production D&D
family registrar yet.
is registered by the production D&D family registrar for the selectable combat
lane.
## Input Adapter
@@ -235,8 +235,8 @@ source-document position while preserving malformed candidate fields for
deterministic validators. Its prompt and private response schema are
package-owned, and its prepared metadata and checkpoint fingerprints contain
only prompt/schema/mapping identities plus an optional NPC registry digest.
The package exposes typed registration for focused composition but is not in
the production D&D registrar yet.
The package exposes typed registration and is included in the production D&D
registrar with the default combat extraction chain.
### `internal/modules/dnd/normalize/npcs`
@@ -294,8 +294,8 @@ position, and collapses only exact duplicate identities with fully valid
evidence. It deep-clones output storage and emits bounded warnings scoped to
merged input indexes. Its metadata and fingerprints identify the normalization
and NPC identity policies, with registry digest/count only when bound. The
normalizer is package-complete but is not registered in the production D&D
registrar.
normalizer is included in the production D&D registrar with the default combat
normalization chain.
## Output Encoder
@@ -365,8 +365,8 @@ turn for unrelated actor or declaration text. The normalized-invariants
validator owns display normalization, comparison-unique targets, canonical
source-reference order, chronology, and exact duplicate identity; it defers
shape and source-reference failures. All four validators are deterministic and
expose local policy fingerprints; they are package-complete but not yet in a
production validator chain.
expose local policy fingerprints. The D&D registrar orders them after generic
JSON and response-schema validation at extraction and normalization.
## Production Registration
@@ -376,8 +376,9 @@ complete framework registry set and one LLM asset registry. It invokes
`internal/modules/seriatim/register`, and `internal/modules/dnd/register` in
that order, then exposes the matching catalog for resolution. The generic and
Seriatim registrars own their production leaf registrations. The D&D registrar
owns D&D leaf registrations, typed spell and NPC default-validator chains,
typed append-order specializations, and D&D prompt/schema asset collection.
owns D&D leaf registrations, typed spell, NPC, and combat default-validator
chains, typed append-order specializations, and D&D prompt/schema asset
collection.
Concrete implementation packages do not import generic implementation
packages directly. A concrete family's `register` package is its composition

View File

@@ -65,9 +65,10 @@ run-local construction closures. Preparation injects shared dependencies and
constructs input, chunk, validators, ordered lanes, and output before source
parsing. Production modules use strict construction-time option decoding, and
LLM-backed modules retain the injected shared client. The D&D family registers
the canonical `dnd/spell-list` and `dnd/npc-list` codecs, typed spell and NPC
extractors and normalizers, validators, plus kind-specific generic merge
strategies; generic JSON validators use the serialized-validation contract. The runner executes lanes through
the canonical `dnd/spell-list`, `dnd/npc-list`, and `dnd/combat-turn-list`
codecs, typed spell, NPC, and combat extractors and normalizers, validators,
plus kind-specific generic merge strategies; generic JSON validators use the
serialized-validation contract. The runner executes lanes through
private exact-type-checked closures, coordinates extract results independently
of completion timing, and serializes artifacts only through their codec at
checkpoint, debug, and output boundaries.
@@ -91,8 +92,8 @@ Configuration. The implemented module packages are:
| `internal/modules/dnd/extract/spells` | Maps private structured model output to canonical source-grounded D&D spell lists. |
| `internal/modules/dnd/extract/npcs` | Maps private structured model output to canonical source-grounded D&D NPC lists. |
| `internal/modules/dnd/extract/combatturns` | Maps private structured model output to source-grounded D&D combat-turn candidates and preserves chronology and invalid candidate values for validators. |
| `internal/modules/dnd/normalize/combatturns` | Canonicalizes and orders merged combat turns, applies exact NPC identity matches, and collapses only exact valid-evidence duplicates without production composition. |
| `internal/modules/dnd/validate/combatturns` | Provides deterministic shape, source-reference, source-relatedness, and normalized-invariant validation for combat turns without production composition. |
| `internal/modules/dnd/normalize/combatturns` | Canonicalizes and orders merged combat turns, applies exact NPC identity matches, and collapses only exact valid-evidence duplicates. |
| `internal/modules/dnd/validate/combatturns` | Provides deterministic shape, source-reference, source-relatedness, and normalized-invariant validation for the production combat chains. |
| `internal/modules/dnd/npcs/registry` | Resolves validated normalized NPC references into immutable grounding data and exact identity lookup. |
| `internal/modules/dnd/npcs/identity` | Owns Unicode-aware NPC identity, ID derivation, and registry collision validation. |
| `internal/modules/dnd/spells/catalog` | Embeds and validates the versioned D&D 5e 2014 SRD catalog, composes optional overlays, and provides immutable effective lookup. |

View File

@@ -70,6 +70,34 @@ campaign data; protect both output roots and any checkpoint or debug roots that
retain derived application data. A registry from another session is allowed,
but its source references are never copied into spell output evidence.
## Sequential NPC And Combat Runs
The maintained [sequential NPC and combat configuration](../examples/dnd-npc-combat-sequential.config.yml)
also represents two independent runs. Run `dnd-npcs` first, then bind its
normalized `lanes/npcs.json` payload to both combat stages:
```sh
go run ./cmd/notarius run dnd-npcs \
--config examples/dnd-npc-combat-sequential.config.yml \
--input examples/seriatim-minimal-transcript.json \
--output-dir ./npc-output
go run ./cmd/notarius run dnd-combat \
--config examples/dnd-npc-combat-sequential.config.yml \
--input examples/seriatim-minimal-transcript.json \
--reference combat.extract.npcs=./npc-output/<run-id>/lanes/npcs.json \
--reference combat.normalize.npcs=./npc-output/<run-id>/lanes/npcs.json
```
Extraction and normalization bindings are stage-local and are intentionally
specified separately. Notarius does not discover the NPC run, copy its source
ranges into combat evidence, or compose the two runs into one workflow. The
combat manifest records both reference bindings and the prepared registry's
semantic digest/count. Changing the referenced NPC payload, prompt or schema,
normalization policy, or registry digest makes affected checkpoint state
incompatible; output, checkpoint, and debug roots remain independent sensitive
state surfaces.
## Chunk-Plan Cache
Chunk plans are stored at:

View File

@@ -1,6 +1,6 @@
# D&D Combat-Turn Extraction
Status: Accepted.
Status: Complete.
## Purpose

View File

@@ -16,18 +16,13 @@ not as committed release dates.
validator, and normalizer development. Treat model-quality review as an
iterative human evaluation aid, not a deterministic correctness gate.
### Add Sequential D&D Artifacts
### Expand Sequential D&D Artifacts
- The next proposed increment is
[D&D combat-turn extraction](dnd-combat-turn-extraction.md), using earlier NPC
output as an explicit identity reference while preserving independent runs.
- Add narrative extraction for scene summaries, party actions, and NPCs
encountered when that output proves useful beyond the dedicated NPC artifact.
- Define the preferred operational sequence for independent pipelines on the
same transcript. The initial direction is NPCs first, followed by spells and
combat turns as appropriate, with earlier JSON artifacts supplied to later
runs as references.
- Keep this sequencing operator- or script-driven initially. Do not require a
- Continue refining the preferred operational sequence for independent
pipelines on the same transcript as additional artifacts are introduced.
- Keep sequencing operator- or script-driven initially. Do not require a
general DAG or concurrent cross-lane reconciliation model.
### Improve D&D Scene Classification

View File

@@ -1,6 +1,6 @@
# D&D Combat-Turn Extraction Implementation Plan
Status: Ready for implementation.
Status: Complete.
Implement this plan in order. The feature policy and target state are defined
in [D&D Combat-Turn Extraction](dnd-combat-turn-extraction.md); this document
@@ -257,7 +257,7 @@ consumer, while preserving spell behavior.
existing content-safe error behavior.
- Update the internal overview, module, and LLM documentation in this stage so
the implemented registry owner and shared prompt-fragment ownership remain
accurate; do not claim combat extraction exists yet.
accurate while the later combat work remains independently scoped.
### Tests