Audit spell and scene processing

This commit is contained in:
2026-08-08 22:42:49 +00:00
parent 5d6305f21a
commit 0fc2f9ee01

View File

@@ -20,13 +20,12 @@ change only roadmap audit documents do not change that production target.
Pending final synthesis. The initial baseline is healthy. The architecture,
configuration/CLI, pipeline composition, reference/handoff, runtime, state,
LLM, generic/Seriatim, shared D&D, registry-family, and occurrence-family
reviews have found three High findings, five Medium findings, and twenty-two
Low findings, with no production dependency inversion, unbounded framework
worker pool, completion-order-dependent result
assembly, debug-to-cache coupling, model-visible credential material in the
embedded LLM assets, or domain leakage across the Seriatim and generic module
boundaries.
LLM, generic/Seriatim, shared D&D, registry-family, occurrence-family, and
spell/scene-family reviews have found three High findings, six Medium findings,
and twenty-three Low findings, with no production dependency inversion,
unbounded framework worker pool, completion-order-dependent result assembly,
debug-to-cache coupling, model-visible credential material in the embedded LLM
assets, or domain leakage across the Seriatim and generic module boundaries.
## Finding Index
@@ -58,12 +57,14 @@ Final cross-area ordering is pending synthesis.
| MOD-003 | Low | Simplicity | Remove unreachable JSON metadata clone helpers |
| DND-CORE-001 | Low | Simplicity | Remove the unused lossy unit-reference constructor |
| DND-REG-001 | Low | Correctness | Reject reversed evidence ranges at durable D&D codec boundaries |
| DND-REG-002 | Low | Correctness | Keep NPC extraction evidence inside the current chunk |
| DND-REG-002 | Low | Correctness | Keep NPC and spell extraction evidence inside the current chunk |
| DND-REG-003 | Low | Efficiency | Index item and location duplicate groups |
| DND-REG-004 | Low | Efficiency | Select location identity anchors without sorting |
| DND-OCC-001 | Medium | Correctness | Align item occurrence evidence fields with the shared prompt |
| DND-OCC-002 | Low | Simplicity | Remove unused campaign references from NPC occurrence normalization |
| DND-OCC-003 | Low | Efficiency | Reuse canonical item occurrence evidence |
| DND-SCENE-001 | Medium | Correctness | Expose catalog aliases to spell extraction |
| DND-SCENE-002 | Low | Correctness | Make scene and spell normalization diagnostics complete and bounded |
## Findings
@@ -201,7 +202,14 @@ Final cross-area ordering is pending synthesis.
`cloneBuildRequest` (`prepare.go:272``299` and 325327). Each request clone
deep-copies materialized reference content as well as options, so typed
builders receive two reference copies and as many as three option copies;
untyped stage and validator builders use fewer copies.
untyped stage and validator builders use fewer copies. Spell extractor,
normalizer, and catalog-validator construction then call
`spells/catalog.ResolveEffectiveCatalog`; that helper clones every slot and
every item byte slice in the already owned `ReferenceSet`
(`internal/modules/dnd/spells/catalog/effective.go:55``62` and 411426), even
though it reads only the `spell_catalog` slot and decodes its content into a
new overlay value. On extractor construction this needlessly recopies the
optional NPC registry and campaign-context references as well.
- **Impact:** Every preparation repeats allocation and byte copying for bounded
external references and nested options, with the highest cost and a
different ownership path specifically for typed lanes and validators. The
@@ -210,7 +218,9 @@ Final cross-area ordering is pending synthesis.
- **Recommendation:** Designate one private construction invocation as the
ownership boundary and clone the complete `BuildRequest` exactly there.
Store raw builders or remove the caller-side clone consistently so all stage
and validator registry variants follow the same single-copy rule.
and validator registry variants follow the same single-copy rule. Let spell
catalog resolution read the owned catalog slot directly instead of cloning
unrelated slots.
- **Preserve:** Builders must continue to receive independently owned options,
reference maps, slot slices, metadata, and content bytes; preparation must
retain its own immutable resolved/reference state; nil, key/name, execution
@@ -218,8 +228,11 @@ Final cross-area ordering is pending synthesis.
- **Validation:** Extend construction hooks to mutate nested options and
reference bytes for typed and untyped modules/validators, assert no aliasing
with resolved or sibling requests, and use allocation/byte-copy observations
or a focused benchmark to confirm a single defensive copy; run
`go test ./internal/framework/contracts ./internal/framework/pipeline`.
or a focused benchmark to confirm a single defensive copy. Add a spell
catalog construction case with large unrelated slots and verify that
resolution neither copies nor mutates them; run
`go test ./internal/framework/contracts ./internal/framework/pipeline
./internal/modules/dnd/spells/catalog`.
- **Grouping:** Independent.
### References And Ordered Handoffs
@@ -863,21 +876,26 @@ Final cross-area ordering is pending synthesis.
- **Severity:** Low
- **Category:** Correctness
- **Evidence:** The three registry and three occurrence integration contracts
require that a source range's start not follow its end
- **Evidence:** The three registry, three occurrence, spell, and scene-
description integration contracts require that a source range's start not
follow its end
(`docs/integrations/dnd-npc-registry-artifacts.md:32``34`,
`dnd-item-registry-artifacts.md:33``35`, and
`dnd-location-registry-artifacts.md:34``36`; and
`dnd-npc-occurrence-artifacts.md:33``36`,
`dnd-item-occurrence-artifacts.md:41``43`, and
`dnd-location-occurrence-artifacts.md:33``35`). All six durable codec
validators check only that both endpoints are positive
`dnd-item-occurrence-artifacts.md:41``43`,
`dnd-location-occurrence-artifacts.md:33``35`,
`dnd-spell-artifacts.md:31``35`, and
`dnd-scene-description-artifacts.md:33``35`). All eight reviewed durable
codec validators check only that both endpoints are positive
(`internal/modules/dnd/codec/npcregistry/codec.go:96``106`,
`codec/itemregistry/codec.go:97``107`, and
`codec/locationregistry/codec.go:97``107`; and
`codec/npcoccurrences/codec.go:99``109`,
`codec/itemoccurrences/codec.go:117``127`, and
`codec/locationoccurrences/codec.go:100``110`). Direct registry references are
`codec/itemoccurrences/codec.go:117``127`,
`codec/locationoccurrences/codec.go:100``110`,
`codec/spells/codec.go:89``99`, and
`codec/scenedescriptions/codec.go:101``109`). Direct registry references are
decoded and identity-checked by `npcs/registry.loadRegistry`,
`items/registry.loadRegistry`, and `locations/registry.loadRegistry`, but do
not pass through the generated-output source-reference validators. A durable
@@ -885,17 +903,21 @@ Final cross-area ordering is pending synthesis.
`{start_unit_id: 2, end_unit_id: 1}` and otherwise valid fields is therefore
accepted; for locations, `validIdentityReference` at
`internal/modules/dnd/locations/identity/identity.go:170``171` also treats
that reversed range as a valid identity anchor.
- **Impact:** An externally supplied registry or occurrence list can be
accepted as approved even though its evidence cannot denote the documented
forward source interval.
NPC and item prompt projections then hide the malformed provenance, while a
location can derive and retain a durable ID from it; only later consumers
that happen to construct contextual grounding against the same source may
reject it. Generated pipeline outputs remain protected by their source-
reference validator chains, which limits current exposure.
that reversed range as a valid identity anchor. The immutable scene-
eligibility registry also decodes approved scene artifacts directly before
projecting ID, range, and kind
(`internal/modules/dnd/scenedescriptions/registry/registry.go:174``223`).
- **Impact:** An externally supplied D&D artifact can be accepted as approved
even though its evidence cannot denote the documented forward source
interval.
NPC and item prompt projections then hide the malformed provenance, a
location can derive and retain a durable ID from it, and scene gating can
retain the impossible interval as eligibility metadata; only later consumers
with source context may reject it. Generated pipeline outputs remain
protected by their source-reference validator chains, which limits current
exposure.
- **Recommendation:** Add the order-independent structural condition
`start_unit_id <= end_unit_id` to all six reviewed codecs' durable
`start_unit_id <= end_unit_id` to all eight reviewed codecs' durable
validation, and make location identity reject reversed anchors as a defense
in depth.
Keep document membership and current-chunk coverage in the existing
@@ -904,46 +926,49 @@ Final cross-area ordering is pending synthesis.
field/trailing-value rejection, location identity's earliest canonical
anchor policy, and the separation between source-independent durable shape
validation and source-dependent evidence validation.
- **Validation:** Add codec encode/decode cases for reversed ranges in all six
- **Validation:** Add codec encode/decode cases for reversed ranges in all eight
reviewed families, a location identity case that refuses a reversed-only
anchor, and direct registry and occurrence reference cases proving malformed
referenced JSON is rejected; run the codec, identity, registry, occurrence,
and assembled CLI suites.
anchor, and direct registry, occurrence, spell, and scene-eligibility
reference cases proving malformed referenced JSON is rejected; run the
codec, identity, registry, spell, scene, occurrence, and assembled CLI suites.
- **Grouping:** Independent.
### DND-REG-002 — Keep NPC extraction evidence inside the current chunk
### DND-REG-002 — Keep NPC and spell extraction evidence inside the current chunk
- **Severity:** Low
- **Category:** Correctness
- **Evidence:** The NPC, item, and location extractors receive only the current
chunk and all three default extract chains run their family source-reference
validator. Item and location validators require a non-nil extraction chunk
and reject references whose endpoints are not in it
- **Evidence:** The NPC, item, location, and spell extractors receive only the
current chunk and all four default extract chains run their family source-
reference validator. Item and location validators require a non-nil
extraction chunk and reject references whose endpoints are not in it
(`internal/modules/dnd/validate/itemregistry/source_refs/validator.go:37``55`
and `validate/locationregistry/source_refs/validator.go:37``55`). The NPC
validator at `validate/npcregistry/source_refs/validator.go:36``53` checks
only that a reference is valid somewhere in the complete source document; it
neither requires `req.Chunk` during extraction nor checks chunk membership.
Its focused tests include reversed, foreign, and out-of-document ranges but
no off-chunk range or missing-chunk case.
- **Impact:** If an NPC extraction response supplies a valid unit ID from
another chunk, the candidate can pass evidence validation despite the model
never receiving that passage. A name that also appears at the off-chunk range
can pass advisory relatedness without proving the current candidate, causing
duplicated or misattributed registry provenance across chunk results. The
model usually copies visible unit IDs, which limits the likelihood.
The spell validator has the same document-only behavior
(`validate/spells/source_refs/validator.go:35``51`). Its tests exercise
valid, out-of-document, missing-document, malformed-shape, and bounded-
aggregate cases, but supply no stage or chunk and do not cover an existing
off-chunk range (`validator_test.go:15``73`).
- **Impact:** If an NPC or spell extraction response supplies a valid unit ID
from another chunk, the candidate can pass evidence validation despite the
model never receiving that passage. Relatedness can also succeed when the NPC
name or spell appears only at the cited off-chunk range, causing duplicated
or misattributed provenance across chunk results. The model usually copies
visible unit IDs, which limits the likelihood.
- **Recommendation:** Match the item/location extraction contract: require the
current chunk when `req.Stage` is extract and reject NPC references outside
that chunk. Keep whole-document validation for normalize and other non-
extraction validation calls.
current chunk when `req.Stage` is extract and reject NPC and spell references
outside that chunk. Keep whole-document validation for normalize and other
non-extraction validation calls.
- **Preserve:** Retain full-document source-ID/range validation, shape deferral,
bounded aggregate diagnostics, normalization without a chunk, direct factual
third-party mentions, and source-relatedness as an advisory check rather than
an identity gate.
- **Validation:** Add NPC source-reference cases for an existing off-chunk
range, a missing extraction chunk, an accepted in-chunk range, and normalize-
stage validation without a chunk; run the NPC registry validator, extractor,
and assembled pipeline tests.
- **Validation:** Add NPC and spell source-reference cases for an existing off-
chunk range, a missing extraction chunk, an accepted in-chunk range, and
normalize-stage validation without a chunk; run both source-reference
validators, their extractors, and assembled pipeline tests.
- **Grouping:** Independent.
### DND-REG-003 — Index item and location duplicate groups
@@ -1113,6 +1138,86 @@ Final cross-area ordering is pending synthesis.
per normalized record; run item occurrence normalize/validator tests.
- **Grouping:** Independent.
### Spells, Scene Chunking, And Scene Descriptions
### DND-SCENE-001 — Expose catalog aliases to spell extraction
- **Severity:** Medium
- **Category:** Correctness
- **Evidence:** The overlay contract says optional aliases contribute to spell
recognition (`docs/integrations/dnd-spell-catalog-overlays.md:3``6` and
5768), and the effective catalog correctly maps each accepted alias to its
canonical spell (`internal/modules/dnd/spells/catalog/effective.go:215``247`).
The extractor's model-visible projection serializes only
`EffectiveCatalog.CanonicalNames()` as `spell_names`
(`internal/modules/dnd/extract/spells/catalog_prompt_input.go:13``28`), while
its prompt explicitly says aliases are absent and requires exact canonical
spelling (`assets/dnd/spells/prompts/spell-catalog.md:1``6`). Focused and
assembled tests codify the omission by rejecting `Emberfall Aegis` from the
prompt even though the configured overlay maps it to `Aegis of Emberfall`
(`internal/modules/dnd/extract/spells/extractor_test.go:88``103` and
`internal/cli/spell_catalog_identity_contract_test.go:412``414`). The
downstream catalog validator and normalizer can resolve an alias only if the
model already returns it; they cannot teach the model that alias-only
transcript language denotes the configured spell.
- **Impact:** A real cast expressed only with a campaign alias can be omitted or
rejected because the extraction model receives neither the alias nor its
canonical mapping. This silently defeats a documented purpose of overlay
configuration and can lose durable spell-cast occurrences; canonical names
and base-catalog casts remain unaffected.
- **Recommendation:** Project a deterministic source-free list of canonical
spell names with their recognized aliases into `spell_catalog`, and instruct
the model to return the associated canonical spelling. Continue to omit
catalog source metadata, paths, licenses, rules text, and provenance. Treat
the projection shape and prompt change as semantic fingerprint changes.
- **Preserve:** Keep overlays recognition-only rather than evidence, retain
strict collision rejection and canonical display names, require current-
transcript cast evidence and an in-world caster, preserve NPC provenance
separation, and keep the existing prompt order and cache boundaries.
- **Validation:** Add base and overlay projection tests for stable canonical-
alias mappings, repeated aliases, and metadata exclusion; update the
assembled prompt contract to require `Emberfall Aegis` only as an alias of
`Aegis of Emberfall`; retain catalog rejection, normalization, checkpoint-
invalidation, and retry tests; run the spell extractor, catalog, normalizer,
validator, and CLI suites.
- **Grouping:** Independent.
### DND-SCENE-002 — Make scene and spell normalization diagnostics complete and bounded
- **Severity:** Low
- **Category:** Correctness
- **Evidence:** The maintained D&D convention says deterministic normalizers
issue bounded warnings for changes and collapsed duplicates
(`docs/internal/dnd.md:129``132`). Spell normalization emits a warning for
every canonicalized or unresolved name and changed reference list, then one
for every duplicate group, and returns the concatenated slice directly
(`internal/modules/dnd/normalize/spells/normalizer.go:89``105`, 108149,
and 177226); unlike the occurrence and registry normalizers, it never calls
`diagnostics.LimitWarnings`. Scene-description normalization has the inverse
gap: it trims title/summary, reorders records, and removes exact duplicates
(`normalize/scenedescriptions/normalizer.go:70``124`) but always returns the
value without any warnings (lines 5368). Its primary test performs all three
mutations without asserting diagnostic provenance
(`normalizer_test.go:15``43`).
- **Impact:** A large spell list can publish an unbounded number of warnings,
inflating manifests, debug artifacts, output files, and checkpoint payloads.
Scene normalization silently changes accepted durable content, so operators
cannot distinguish an unchanged scene list from repaired whitespace/order or
collapsed duplicates. Artifact values remain deterministic and valid.
- **Recommendation:** Pass spell warnings through the shared deterministic
limiter with a stable omission reason. Add bounded scene warnings that
distinguish prose trimming, canonical reordering, and exact duplicate
collapse while retaining stable input scopes and order. Emit no warning for
an already canonical value.
- **Preserve:** Keep current normalized values, source-position ordering,
conflict rejection, duplicate identities, input immutability, per-record
diagnostic order below the limit, and idempotent warning-free second passes.
- **Validation:** Add spell cases beyond `diagnostics.MaxWarnings` that assert
the stable omission summary, and scene cases for each mutation, combined
overflow, canonical input, and idempotence; run both normalizer suites and
assembled output/checkpoint warning tests.
- **Grouping:** Independent.
<!--
Finding template for later audit stages:
@@ -1146,13 +1251,13 @@ evidence projector.
| Family | Durable kind / Go type | Modules and execution class | Default validator chains (E / N) | Reference dependencies (E / N) | Codec, prompt, and schema ownership | Documented exception and later confirmation |
| --- | --- | --- | --- | --- | --- | --- |
| Spells | `dnd/spell-list` / `dnd.SpellList` | `dnd/spells` (LLM) → typed `appendorder` (deterministic) → `dnd/spells` (deterministic) | `B + catalog` / `B + catalog` | `C` plus optional `spell_catalog` and `npc_registry` / optional `spell_catalog` | `codec/spells`; extractor owns its prompt and private response schema; normalizer has no prompt | Catalog overlay and NPC caster grounding are optional and never evidence; confirm spell/catalog and scene-family behavior in the spells/scenes review. |
| Spells | `dnd/spell-list` / `dnd.SpellList` | `dnd/spells` (LLM) → typed `appendorder` (deterministic) → `dnd/spells` (deterministic) | `B + catalog` / `B + catalog` | `C` plus optional `spell_catalog` and `npc_registry` / optional `spell_catalog` | `codec/spells`; extractor owns its prompt and private response schema; normalizer has no prompt | Confirmed: catalog and NPC grounding remain source-free recognition context; canonicalization, retry, and checkpoint identities are deterministic. DND-REG-001, DND-REG-002, DND-SCENE-001, and DND-SCENE-002 record codec, chunk-evidence, alias-projection, and warning gaps. |
| NPC registry | `dnd/npc-registry` / `dnd.NPCRegistry` | `dnd/npc-registry` (LLM) → typed `appendorder` (deterministic) → `dnd/npc-registry` (LLM) | `B` / `B + identity` | `C` / none | `codec/npcregistry`; extractor and normalizer own prompts; extractor owns its response schema, while normalize uses the shared private entity-reconciliation schema | Confirmed: normalized comparison-name identity, deterministic exact duplicate consolidation, proposal-only semantic groups, collision-safe application, retry/fallback, and immutable name/identity projections; DND-REG-001 and DND-REG-002 record evidence-boundary gaps. |
| Combat turns | `dnd/combat-turn-list` / `dnd.CombatTurnList` | `dnd/combat-turns` (LLM) → typed `appendorder` (deterministic) → `dnd/combat-turns` (deterministic) | `B` / `B + invariants` | `C`, optional `npc_registry`, required `scene_descriptions` / optional `npc_registry` | `codec/combatturns`; extractor owns its prompt and private response schema; normalizer has no prompt | Scene descriptions gate LLM execution and NPC grounding is not evidence; confirm gate and empty-result semantics in the combat/enemy review. |
| Item occurrences | `dnd/item-occurrence-list` / `dnd.ItemOccurrenceList` | `dnd/item-occurrences` (LLM) → typed `appendorder` (deterministic) → `dnd/item-occurrences` (deterministic) | `B + registry` / `B + registry + invariants` | `C` plus required `item_registry` / required `item_registry` | `codec/itemoccurrences`; extractor owns its prompt and private response schema; normalizer has no prompt | Confirmed: exact names-only registry resolution is all-or-nothing, registry provenance never becomes evidence, and quantity/holder rules survive canonical ordering and duplicate collapse. DND-REG-001, DND-OCC-001, and DND-OCC-003 record bounded codec, prompt/schema, and repeated-work gaps. |
| Item registry | `dnd/item-registry` / `dnd.ItemRegistry` | `dnd/item-registry` (LLM) → typed `appendorder` (deterministic) → `dnd/item-registry` (LLM) | `B` / `B + identity` | `C` / none | `codec/itemregistry`; extractor and normalizer own prompts; extractor owns its response schema, while normalize uses the shared private entity-reconciliation schema | Confirmed: name identity, exact duplicate evidence union, proposal-only aliases, collision safety, and denomination/type-preserving currency gate; DND-REG-001 and DND-REG-003 record boundary/grouping gaps. |
| NPC occurrences | `dnd/npc-occurrence-list` / `dnd.NPCOccurrenceList` | `dnd/npc-occurrences` (LLM) → typed `appendorder` (deterministic) → `dnd/npc-occurrences` (deterministic) | `B + registry` / `B + registry + invariants` | `C` plus required `npc_registry` / `C` plus required `npc_registry` | `codec/npcoccurrences`; extractor owns its prompt and private response schema; normalizer has no prompt | Confirmed: names-only selection resolves to exact registry pairs, registry provenance cannot become occurrence evidence, `mentioned` remains factual, and current-source evidence is canonicalized before exact duplicate collapse. DND-REG-001 and DND-OCC-002 record the codec and unused-normalizer-reference gaps. |
| Scene descriptions | `dnd/scene-description-list` / `dnd.SceneDescriptionList` | `dnd/scene-descriptions` (LLM) → typed `appendorder` (deterministic) → `dnd/scene-descriptions` (deterministic) | `B` / `B + invariants` | optional `glossary`, `party`, and `players` / none | `codec/scenedescriptions`; extractor owns its prompt and private response schema; normalizer has no prompt | Each record has one `source_ref` rather than a slice; the separate `dnd/scenes` LLM chunker owns the full-transcript scene prompt. Confirm scene IDs, ordering, and classification in the spells/scenes review. |
| Scene descriptions | `dnd/scene-description-list` / `dnd.SceneDescriptionList` | `dnd/scene-descriptions` (LLM) → typed `appendorder` (deterministic) → `dnd/scene-descriptions` (deterministic) | `B` / `B + invariants` | optional `glossary`, `party`, and `players` / none | `codec/scenedescriptions`; extractor owns its prompt and private response schema; normalizer has no prompt | Confirmed: one model classification is mapped onto the current chunk's local ID and exact range, while an immutable source-free ID/range/kind projection alone controls later eligibility. DND-REG-001 and DND-SCENE-002 record codec and warning gaps. |
| Enemy events | `dnd/enemy-event-list` / `dnd.EnemyEventList` | `dnd/enemy-events` (LLM) → typed `appendorder` (deterministic) → `dnd/enemy-events` (deterministic) | `B + engagements` / `B + invariants` | `C` plus required `npc_registry`, `scene_descriptions`, `combat_turns`, and `npc_occurrences` / required `npc_registry` | `codec/enemyevents`; extractor owns its prompt and private response schema; normalizer has no prompt | Four generated artifacts ground extraction without becoming event evidence; durable decode separately proves required JSON-field presence. Confirm combat gating, engagement uniqueness, and observation ordering in the combat/enemy review. |
| Location registry | `dnd/location-registry` / `dnd.LocationRegistry` | `dnd/location-registry` (LLM) → typed `appendorder` (deterministic) → `dnd/location-registry` (LLM) | `B` / `B + identity` | `C` / none | `codec/locationregistry`; extractor and normalizer own prompts; extractor owns its response schema, while normalize uses the shared private entity-reconciliation schema | Confirmed: comparison name plus earliest canonical evidence identity, same-name/different-anchor preservation, proposal-only aliases, and immutable context-qualified selectors without durable IDs; DND-REG-001, DND-REG-003, and DND-REG-004 record bounded gaps. |
| Location occurrences | `dnd/location-occurrence-list` / `dnd.LocationOccurrenceList` | `dnd/location-occurrences` (LLM) → typed `appendorder` (deterministic) → `dnd/location-occurrences` (deterministic) | `B + registry` / `B + registry + invariants` | `C` plus required `location_registry` / required `location_registry` | `codec/locationoccurrences`; extractor owns its prompt and private response schema; normalizer has no prompt | Confirmed: source-free contextual selectors disambiguate same-name locations without exposing durable IDs, unresolved selections reject the whole response, registry evidence remains separate, and explicit kind precedence preserves speculation/mention distinctions. DND-REG-001 records the codec gap. |
@@ -1365,6 +1470,22 @@ mechanics that recur without erasing those distinctions.
its exhaustion through a provider-neutral sentinel. LLM-002 closes an
admission/cancellation race without combining these limits or moving provider
capacity policy into the pipeline worker pools.
- `internal/modules/dnd/chunk/scenes.planFromResponse` intentionally keeps the
whole-source coverage policy explicit in one pass: it resolves opaque unit
IDs through the shared document index, compares document positions rather
than numeric IDs, rejects reversed ranges, gaps, and overlaps, and requires
exact first-to-last coverage before the framework independently canonicalizes,
validates, and materializes the plan. Its repeated-looking resolve/position
calls are constant-time index lookups that preserve contextual diagnostics;
a generic range collector would hide scene-specific full-coverage policy.
- `internal/modules/dnd/spells/catalog.composeEffectiveCatalog` intentionally
uses separate canonical-name, per-spell alias, and complete lookup maps. The
function validates base and overlay collisions while applying overlays in
sorted ID order, then produces stable names and a semantic digest. Its nested
loops traverse catalog entries and each entry's aliases without a linear scan
inside the loop. DND-SCENE-001 concerns the later model-visible projection,
not combining catalog composition with prompting or weakening collision
checks.
## Areas Reviewed Without Findings
@@ -1895,14 +2016,13 @@ mechanics that recur without erasing those distinctions.
comparison, diagnostics, resolution, and reconciliation. No additional
callback- or reflection-driven helper reduced demonstrated drift.
- **Deferred lane questions:** Registry identity, reconciliation, immutable
projections, and occurrence-category/grounding policy are now confirmed,
projections, occurrence-category/grounding policy, spell catalogs, scene
chunking, scene identity, and scene classification are now confirmed,
with DND-REG-001 through DND-REG-004 and DND-OCC-001 through DND-OCC-003
recording the bounded gaps. Spell catalog, scene chunking, scene IDs, and
scene classification remain assigned to the spells/scenes review; combat
gates, engagement uniqueness, collective labels, and enemy observation
ordering remain assigned to the combat/enemy review. This area therefore
remains `Revisit` until those reviews confirm the remaining matrix
exceptions.
plus DND-SCENE-001 and DND-SCENE-002 recording the bounded gaps. Combat gates,
engagement uniqueness, collective labels, and enemy observation ordering
remain assigned to the combat/enemy review. This area therefore remains
`Revisit` until that review confirms the remaining matrix exceptions.
### NPC, Item, And Location Registries
@@ -2012,6 +2132,59 @@ mechanics that recur without erasing those distinctions.
order, and ownership; validator tests own pair, source, relatedness, and
invariant defenses. The prescribed occurrence and CLI suites pass.
### Spells, Scene Chunking, And Scene Descriptions
- **Accepted scene-plan path:** The scene chunker validates the complete source,
supplies the whole transcript after optional cached campaign context, and
decodes only ordered start/end unit IDs. `planFromResponse` resolves those
IDs against document positions and enforces non-empty, contiguous,
non-overlapping first-to-last coverage. The framework then canonicalizes,
validates, and materializes the plan before storing it. Only an accepted plan
is serialized into the distinct chunk-map artifact with exact source,
requested-chunker, producer, plan-digest, chunk-ID/range/count, and cloned
annotation metadata; lane rejection does not erase that accepted structural
artifact, while plan rejection publishes no chunk map. The D&D scene planner
deliberately emits empty annotations rather than duplicating classification
or source text, and the production contract verifies that separation.
- **Scene-description identity and eligibility:** Extraction maps exactly one
private kind/title/summary response onto the local current chunk ID and exact
range, so the model cannot select or invent durable identity. Extract
validation rechecks that exact chunk match; normalization uses source-document
positions, rejects ID/range conflicts, and removes only exact duplicates.
The immutable registry strictly decodes one approved artifact and projects
only ID, complete range, and kind into a semantic eligibility digest. Its
exact/missing/mismatched match state prevents title, summary, source prose,
or registry provenance from becoming later combat evidence. DND-REG-001 and
DND-SCENE-002 record the bounded codec and diagnostic exceptions.
- **Spell catalog, grounding, and evidence:** Construction strictly decodes the
optional single JSON overlay, composes it with the embedded 2014 SRD catalog
in sorted overlay-ID order, rejects cross-spell canonical/alias collisions,
and binds a semantic catalog digest to extractor, catalog-validator, and
normalizer checkpoints. Optional NPC grounding is resolved independently and
projected as source-free names; neither NPC nor catalog provenance becomes
cast evidence. The extractor injects the current source ID, canonicalizes
references and cast order, and the normalizer canonicalizes catalog names and
reference order before collapsing only exact valid-evidence duplicates.
DND-SCENE-001 records the missing model-visible alias mapping, DND-REG-002 the
extraction chunk gate, and PIPE-002 the redundant construction copy.
- **Validation, retries, and cache identity:** Default spell and scene chains
preserve shape deferral, strict private and durable schemas, source-aware
rejection, advisory relatedness, and normalization invariants. Catalog
rejection participates in the framework's bounded retry path, terminal
accepted-attempt warnings are retained, and rejected-attempt warnings are
suppressed as intended. Raw reference dependencies bind provenance/content,
while semantic catalog and NPC/scene projections bind behavior; focused CLI
tests prove catalog, mapping-policy, prompt/profile, and generated-reference
changes prevent stale checkpoint reuse.
- **Prompt and test ownership:** Scene planning follows the intentional D&D
prompt order of system, cacheable campaign references, uncached task
instructions/schema, and final ephemeral whole transcript. Per-chunk scene
descriptions and spells use the shared current-chunk evidence fragments;
local response schemas expose no source IDs or chunk IDs. Focused assets,
codecs, extractors, normalizers, validators, registries, catalog composition,
checkpoint/retry, and production chunk-map tests cover the reviewed contracts,
and the prescribed package suite passes.
## Validation Record
| Date | Scope | Command or check | Result |
@@ -2065,6 +2238,9 @@ mechanics that recur without erasing those distinctions.
| 2026-08-08 | Occurrence-family graph/code review | Exact symbol reads, complexity queries, and call traces across NPC, item, and location semantic registry projections/resolution, response adaptation, current-source evidence attachment, canonical ordering/deduplication, normalization, validation, codecs, prompt assets, private schemas, and generated-reference tests | DND-OCC-001 through DND-OCC-003 recorded and DND-REG-001 broadened; all-or-nothing identity attachment, evidence separation, category/holder/quantity/same-name policy, nil/empty ownership, and handoff behavior otherwise confirmed |
| 2026-08-08 | Required NPC, item, and location occurrence tests | `go test ./internal/modules/dnd/extract/npcoccurrences ./internal/modules/dnd/extract/itemoccurrences ./internal/modules/dnd/extract/locationoccurrences ./internal/modules/dnd/normalize/npcoccurrences ./internal/modules/dnd/normalize/itemoccurrences ./internal/modules/dnd/normalize/locationoccurrences ./internal/modules/dnd/validate/npcoccurrences/... ./internal/modules/dnd/validate/itemoccurrences/... ./internal/modules/dnd/validate/locationoccurrences/...` | Pass |
| 2026-08-08 | Generated occurrence handoff tests | `go test ./internal/cli` | Pass |
| 2026-08-08 | Audit target integrity before spell/scene-family review | `git diff --quiet 92e89076a268089e703978fb9d7176200e93344c..HEAD -- . ':(exclude)docs/roadmap/**'` | Pass; production target unchanged |
| 2026-08-08 | Spell and scene graph/code review | Scoped architecture, exact symbol reads, complexity inspection, and call traces across whole-source scene planning, framework plan canonicalization/materialization, chunk-map serialization/publication, per-chunk scene extraction, normalization, validation and eligibility projection, effective spell catalog composition, NPC grounding, response canonicalization, normalization, validation, checkpoint identity, retry, codecs, prompt assets, schemas, and focused tests | DND-SCENE-001 and DND-SCENE-002 recorded; DND-REG-001, DND-REG-002, and PIPE-002 broadened; structural/evidence separation, exact scene eligibility, and catalog/checkpoint behavior otherwise confirmed |
| 2026-08-08 | Required spell, scene chunking, and scene-description tests | `go test ./internal/modules/dnd/chunk/scenes ./internal/modules/dnd/extract/scenedescriptions ./internal/modules/dnd/normalize/scenedescriptions ./internal/modules/dnd/validate/scenedescriptions/... ./internal/modules/dnd/scenedescriptions/... ./internal/modules/dnd/extract/spells ./internal/modules/dnd/normalize/spells ./internal/modules/dnd/validate/spells/... ./internal/modules/dnd/spells/...` | Pass |
## Coverage Matrix
@@ -2081,6 +2257,6 @@ mechanics that recur without erasing those distinctions.
| Shared D&D types, codecs, and family mechanics | Revisit | D&D internal/module docs and all D&D integration contracts; root durable types; all ten codec packages and candidate JSON; shared references, ordering, citations, inputs, comparison, diagnostics, registry resolver, entity reconciliation, and assets; typed merger/evidence/default-chain/fallback/family registration; representative module asset declarations and focused tests | Target-integrity check, scoped graph architecture/search/traces, ten-family convention matrix, required codec/shared/register tests | DND-CORE-001 |
| NPC, item, and location registries | Reviewed | NPC/item/location registry integration contracts and relevant identity/evidence ADRs; extractors, candidate models and response schemas; identity, immutable registry, prompt/identity projection, location grounding, reconciliation, normalizer, validators, codecs, prompt assets, and focused tests for all three families | Target-integrity check, scoped graph architecture/complexity/search/traces, full family comparison, required extractor/identity/registry/normalizer/validator tests | DND-REG-001, DND-REG-002, DND-REG-003, DND-REG-004 |
| NPC, item, and location occurrences | Reviewed | NPC/item/location occurrence integration contracts and deterministic opaque-ID ADR; extractors, private response models/schemas, semantic registry projections and resolvers, canonicalizers, normalizers, validator chains, codecs, prompt assets, and generated/external registry handoff tests for all three families | Target-integrity check, scoped graph architecture/complexity/search/traces, full family comparison, required extractor/normalizer/validator tests, generated CLI handoff tests | DND-REG-001, DND-OCC-001, DND-OCC-002, DND-OCC-003 |
| Spells, scene chunking, and scene descriptions | Pending | — | — | — |
| Spells, scene chunking, and scene descriptions | Reviewed | Spell, overlay, scene-description, and accepted chunk-map integration contracts; D&D scene chunker and prompt/schema assets; framework plan canonicalization, validation, materialization, accepted chunk-map serialization/publication and production tests; scene-description extractor, normalizer, validators, immutable eligibility registry, codec, prompts, schemas, and tests; spell extractor, effective/base/overlay catalogs, NPC grounding, canonicalizer, normalizer, validators, codec, prompts, schemas, checkpoint/retry/CLI tests | Target-integrity check, scoped graph architecture/complexity/symbol/call review, full family comparison, required chunker/extractor/normalizer/validator/registry/catalog tests | PIPE-002, DND-REG-001, DND-REG-002, DND-SCENE-001, DND-SCENE-002 |
| Combat turns and enemy events | Pending | — | — | — |
| Test ownership, comments, and final synthesis | Pending | — | — | — |