413 lines
19 KiB
Markdown
413 lines
19 KiB
Markdown
# Scene-Aware Combat Extraction Implementation Plan
|
|
|
|
## Status
|
|
|
|
Ready for implementation. Execute each stage in order, with one implementation
|
|
prompt per stage.
|
|
|
|
## Feature Contract
|
|
|
|
The target behavior and policy choices are defined in
|
|
[Scene-Aware Combat Extraction](scene-aware-combat-extraction.md). That document
|
|
is authoritative when this plan and the feature contract appear to overlap.
|
|
|
|
The central rule is strict opt-in: `dnd/combat-turns` may call the LLM only for
|
|
an accepted chunk with one exact scene-description record whose `kind` is
|
|
`combat`. Exact non-combat matches and missing or mismatched coverage produce an
|
|
accepted empty combat-turn list without an LLM call.
|
|
|
|
## Required Reading
|
|
|
|
Before Stage 1, read:
|
|
|
|
- `docs/development.md`;
|
|
- every document under `docs/policy/`;
|
|
- `docs/roadmap/scene-aware-combat-extraction.md`;
|
|
- `docs/internal/pipeline.md`;
|
|
- `docs/internal/modules.md`;
|
|
- `docs/integrations/dnd-scene-description-artifacts.md`;
|
|
- `docs/integrations/dnd-combat-turn-artifacts.md`; and
|
|
- the focused code and tests named in the applicable stage.
|
|
|
|
Before changing a subsystem in a later stage, reread its focused current
|
|
documentation and tests if they have changed since the earlier stage.
|
|
|
|
## Global Implementation Constraints
|
|
|
|
- Keep scene interpretation under `internal/modules/dnd`; generic framework and
|
|
source packages must not import D&D types or interpret scene kinds.
|
|
- Use the existing ordered generated-reference mechanism. Do not add implicit
|
|
lane discovery, a new dependency syntax, a general DAG, or direct output-file
|
|
reads.
|
|
- Add `scene_descriptions` only to the combat extractor. The combat normalizer
|
|
continues to accept only the optional `npcs` registry.
|
|
- Keep both durable v1 schemas unchanged.
|
|
- Do not add the scene artifact or a derived classification to the combat
|
|
prompt. Do not change combat prompt assets, prompt ordering, prompt inputs,
|
|
response schema, or prompt fingerprint.
|
|
- Preserve existing combat extraction behavior for eligible combat chunks,
|
|
including NPC grounding, retries, validators, candidate mapping, and
|
|
normalization.
|
|
- Treat the scene artifact as control context, never as combat evidence. Never
|
|
copy its references, title, or summary into combat turns.
|
|
- Use deterministic, offline tests and existing LLM fakes. Do not call a live
|
|
provider or turn probabilistic model output into a correctness gate.
|
|
- Test stable behavior at the narrowest owning boundary. Do not add tests for
|
|
private helper shape, cache implementation details, or exact diagnostic prose.
|
|
- Do not add a framework abstraction unless implementation proves the existing
|
|
reference contracts cannot express this feature. No such framework gap is
|
|
currently expected.
|
|
|
|
## Stage 1 — Add the Domain Scene-Eligibility Reference Boundary
|
|
|
|
### Goal
|
|
|
|
Create one domain-owned, immutable boundary that decodes an approved
|
|
scene-description reference, retains only gating data, and supports both
|
|
construction-time external references and operation-time generated references.
|
|
This stage must not change combat extraction behavior.
|
|
|
|
### Implementation
|
|
|
|
1. Add `internal/modules/dnd/scenedescriptions/registry`, following the useful
|
|
external/generated resolution pattern in `internal/modules/dnd/npcs/registry`
|
|
without copying NPC-specific projection or identity policy.
|
|
2. Define these package contracts:
|
|
- reference slot: `scene_descriptions`;
|
|
- maximum item size: 1 MiB (`1048576` bytes);
|
|
- accepted representation: exactly one `application/json` item decoded by
|
|
`codec/scenedescriptions`;
|
|
- eligibility projection: scene ID, exact `source_ref`, and `kind` only.
|
|
3. Decode through the approved scene-description codec. Reject:
|
|
- more than one item;
|
|
- invalid or non-JSON media types;
|
|
- oversized content;
|
|
- invalid approved scene JSON; and
|
|
- duplicate scene IDs, even if duplicate records are otherwise identical.
|
|
4. Do not retain titles, summaries, original bytes, paths, or prompt material in
|
|
the resolved eligibility view. Framework reference provenance remains the
|
|
owner of raw reference identity.
|
|
5. Build an immutable lookup keyed by exact scene ID. Expose a chunk-match result
|
|
with three stable states:
|
|
- exact: ID, source ID, start unit ID, and end unit ID all match;
|
|
- missing: no record has the current chunk ID;
|
|
- mismatched: the ID exists but any source-range field differs.
|
|
Only the exact state exposes the scene kind.
|
|
6. Compute a semantic eligibility digest from a canonical projection sorted by
|
|
scene ID. The digest must:
|
|
- ignore title, summary, and input array order; and
|
|
- change when an ID, source-range field, or kind changes.
|
|
Represent an unbound view with the canonical empty projection
|
|
`{"scenes":[]}` and its `sha256:` digest so generated-reference construction
|
|
has a stable sentinel without pretending the required reference was bound.
|
|
7. Provide a resolver that:
|
|
- seeds and validates an external materialized item during construction;
|
|
- permits an unbound seed because a generated required reference is not
|
|
populated until the operation-time handoff;
|
|
- resolves an operation-time generated item when present;
|
|
- otherwise returns the seeded view; and
|
|
- safely reuses resolved immutable views across concurrent extract jobs.
|
|
8. Expose only the accessors required by combat extraction: bound state, record
|
|
count, eligibility digest, and exact chunk matching. Return defensive values
|
|
rather than mutable maps or scene records.
|
|
|
|
### Tests
|
|
|
|
Add focused package tests that own:
|
|
|
|
- approved decoding and exact match classification;
|
|
- missing and mismatched match states;
|
|
- strict item count, media type, size, codec, and duplicate-ID failures;
|
|
- semantic digest stability across prose and ordering changes;
|
|
- semantic digest changes for each gating field;
|
|
- construction-time seed versus operation-time generated override; and
|
|
- immutability and concurrent-safe reuse at the public package boundary where
|
|
practical.
|
|
|
|
Do not duplicate the scene codec's exhaustive JSON-shape tests.
|
|
|
|
### Validation
|
|
|
|
Run:
|
|
|
|
```sh
|
|
go test ./internal/modules/dnd/scenedescriptions/registry
|
|
go test ./internal/modules/dnd/codec/scenedescriptions
|
|
git diff --check
|
|
```
|
|
|
|
Stage 1 is complete when the new package is independently usable and tested but
|
|
no production module selects it yet.
|
|
|
|
## Stage 2 — Enforce the Required Combat Scene Gate
|
|
|
|
### Goal
|
|
|
|
Make `dnd/combat-turns` require the scene-description artifact and invoke its
|
|
existing LLM path only for exact combat scenes. Update directly affected
|
|
configuration fixtures and contract tests in the same stage so the repository
|
|
does not retain knowingly invalid combat configurations.
|
|
|
|
### Module Contract
|
|
|
|
1. In `internal/modules/dnd/extract/combatturns`, add aliases for the
|
|
scene-registry slot and size limit and declare a reference slot with:
|
|
- name `scene_descriptions`;
|
|
- `Required: true`;
|
|
- media type `application/json`;
|
|
- accepted artifact kind `dnd/scene-description-list`;
|
|
- maximum size 1 MiB; and
|
|
- a description stating that it gates combat eligibility and is not combat
|
|
evidence.
|
|
2. Preserve sorted reference-slot ordering and the existing agreement between
|
|
`ReferenceSlots()` and `ModuleSpec().ReferenceSlots`.
|
|
3. Add a scene resolver to the immutable extractor. Constructor behavior must:
|
|
- validate a materialized external scene artifact before execution;
|
|
- allow the construction reference set to lack an item for a configured
|
|
generated binding; and
|
|
- retain no scene prose.
|
|
4. Define a stable gating policy identity
|
|
`dnd.combat_turns.scene_gate.v1`.
|
|
5. Extend manifest metadata with `scene_gate_policy`. For a seeded external
|
|
reference only, also report `scene_eligibility_digest` and
|
|
`scene_description_count`. Do not publish those operation-time values for a
|
|
generated reference in singleton component metadata; framework provenance
|
|
owns that handoff.
|
|
6. Extend prepared-component checkpoint fingerprints with:
|
|
- `scene_gate_policy`; and
|
|
- `scene_eligibility`, using the seeded external eligibility digest or the
|
|
registry's stable unbound sentinel for a generated binding.
|
|
Keep the existing prompt, response-schema, mapping-policy, and NPC-registry
|
|
fingerprints unchanged.
|
|
|
|
### Extraction Behavior
|
|
|
|
In `Extractor.Extract`, retain common request validation through
|
|
`shared.PrepareChunkExtraction`, then resolve and apply the scene gate before
|
|
building prompt inputs or resolving NPC prompt grounding:
|
|
|
|
1. If the operation-time scene reference is absent, return a contextual module
|
|
error stating that the required reference is missing. Normal configured runs
|
|
should have failed earlier during resolution or required-producer handoff;
|
|
this check protects direct module use.
|
|
2. For an exact `combat` match, continue through the existing NPC resolution,
|
|
prompt construction, LLM request, response mapping, and validation path
|
|
without semantic changes.
|
|
3. For an exact `narrative`, `recap`, or `meta` match, return
|
|
`dnd.CombatTurnList{CombatTurns: []dnd.CombatTurn{}}`. Make no LLM call and
|
|
emit no warning.
|
|
4. For a missing or mismatched match, return the same non-nil empty list without
|
|
an LLM call and attach exactly one content-safe warning:
|
|
- scope: `scene_descriptions`;
|
|
- reason code: `scene_classification_unavailable`;
|
|
- message: explain that no exact classification was available and combat
|
|
extraction was skipped, without scene prose or transcript content.
|
|
5. The deterministic result is an accepted extract result, not an error or
|
|
rejection. It must pass the existing combat shape, source-reference, schema,
|
|
merge, and normalization boundaries and must not consume retry attempts.
|
|
6. Do not pass `scene_descriptions` into `shared.PromptInputs` or add it to the
|
|
structured completion request. The current shared helper already selects
|
|
only campaign and transcript inputs; preserve that boundary explicitly.
|
|
|
|
### Required Test And Fixture Migration
|
|
|
|
1. Update combat extractor test helpers so tests of the existing LLM behavior
|
|
supply an exact combat scene artifact for their current chunk. Keep
|
|
NPC-specific test intent unchanged.
|
|
2. Add a compact table-driven extractor test covering:
|
|
- exact combat;
|
|
- exact narrative, recap, and meta;
|
|
- missing scene ID;
|
|
- matching ID with each mismatched range identity; and
|
|
- an operation-time missing required reference.
|
|
Assert typed output, warning reason where applicable, and whether the fake LLM
|
|
was called. Do not assert private helper calls.
|
|
3. Update module specification, registration, metadata, and fingerprint tests
|
|
for the required slot and new policy identities. Assert that changing only
|
|
scene prose does not change the eligibility fingerprint, while changing
|
|
kind or range does.
|
|
4. Update every in-repository pipeline or programmatic test configuration that
|
|
selects `dnd/combat-turns`:
|
|
- the maintained complete example must bind the earlier
|
|
`scene-descriptions` lane to the later `scene_descriptions` step reference;
|
|
- CLI combat contract configurations must bind a compatible external scene
|
|
artifact when testing a standalone one-step profile;
|
|
- the ordered D&D integration fixture must produce scene descriptions in its
|
|
first step and bind them in its consumer step; and
|
|
- fake clients used by those integration tests must return an exact combat
|
|
scene where the test expects the combat LLM path.
|
|
5. Add or update a configuration-resolution assertion proving that a selected
|
|
combat extractor without `scene_descriptions` is rejected as an unbound
|
|
required slot.
|
|
6. Update existing expected lane counts, schemas, request sets, fingerprints,
|
|
and provenance counts only where the new scene producer or consumer
|
|
materially changes the contract. Preserve the original NPC-grounding
|
|
assertions.
|
|
|
|
### Validation
|
|
|
|
Run:
|
|
|
|
```sh
|
|
go run ./cmd/notarius config validate \
|
|
--config examples/dnd-complete.config.yml \
|
|
--pipeline dnd-session
|
|
go test ./internal/modules/dnd/...
|
|
go test ./internal/modules/integration
|
|
go test ./internal/cli
|
|
git diff --check
|
|
```
|
|
|
|
Stage 2 is complete when required-slot resolution is enforced, every combat
|
|
extract path obeys the eligibility rule, all affected configurations are valid,
|
|
and focused suites pass.
|
|
|
|
## Stage 3 — Prove Ordered Handoff, Provenance, And Retry Behavior
|
|
|
|
### Goal
|
|
|
|
Protect the cross-component behavior that cannot be established by the
|
|
scene-registry and extractor package tests alone. Keep this stage lean: reuse
|
|
the real codecs, resolver, pipeline, and validators, with a deterministic fake
|
|
only at the LLM boundary.
|
|
|
|
### Integration Coverage
|
|
|
|
1. Extend or refactor the focused ordered D&D integration test so it proves, in
|
|
one representative assembled workflow:
|
|
- scene descriptions and NPCs normalize in the first step;
|
|
- their accepted artifacts are supplied to the intended second-step targets;
|
|
- an exact combat scene causes exactly one combat completion request;
|
|
- the combat request contains transcript, campaign, and NPC inputs as
|
|
configured but no `scene_descriptions` input;
|
|
- the combat output and existing NPC canonicalization remain correct;
|
|
- the manifest records one generated scene-description reference for the
|
|
combat extract target with the expected kind, schema, media type, digest,
|
|
and producer identity; and
|
|
- manifest and component metadata contain no scene title, summary, transcript
|
|
text, or generated artifact payload.
|
|
2. Add one focused non-combat ordered-run case using an exact `narrative`
|
|
classification. Configure combat retries to a value greater than zero, then
|
|
prove:
|
|
- no combat completion request occurs;
|
|
- one accepted empty combat lane reaches normalized output;
|
|
- no retry or rejection is recorded for the deterministic skip; and
|
|
- no unavailable-classification warning is emitted for an exact non-combat
|
|
match.
|
|
3. Add one focused missing-or-mismatched case at the narrowest stable boundary
|
|
not already covered by Stage 2. Prove the accepted empty result and
|
|
`scene_classification_unavailable` warning reach the runner's durable warning
|
|
collection without a combat LLM call. Do not repeat every mismatch variant
|
|
at integration scope.
|
|
|
|
### Checkpoint And Identity Coverage
|
|
|
|
1. Confirm the existing framework-generated-reference dependency fingerprint
|
|
already includes the canonical generated scene artifact. Reuse that
|
|
mechanism; do not add a second framework fingerprint path.
|
|
2. Add focused D&D coverage proving:
|
|
- a seeded external reference's component fingerprint changes when eligibility
|
|
kind or exact range changes;
|
|
- title-only or summary-only changes leave that semantic component
|
|
fingerprint unchanged; and
|
|
- the generated reference appears in the combat extract checkpoint
|
|
dependencies, so changing the generated artifact prevents reuse.
|
|
3. Prefer an existing checkpoint identity or prepared-pipeline test boundary.
|
|
Do not add a full CLI resume test if the same dependency invalidation is
|
|
already credibly protected by the framework checkpoint tests plus the D&D
|
|
prepared-pipeline assertion.
|
|
|
|
### Validation
|
|
|
|
Run:
|
|
|
|
```sh
|
|
go test ./internal/modules/integration
|
|
go test ./internal/framework/pipeline
|
|
go test ./internal/cli
|
|
go test ./internal/modules/dnd/extract/combatturns
|
|
go test ./internal/modules/dnd/scenedescriptions/registry
|
|
git diff --check
|
|
```
|
|
|
|
Stage 3 is complete when the ordered generated handoff, no-call behavior,
|
|
warning propagation, provenance privacy, and checkpoint invalidation are
|
|
protected at their stable owning boundaries without redundant end-to-end cases.
|
|
|
|
## Stage 4 — Update Canonical Documentation And Finish Validation
|
|
|
|
### Goal
|
|
|
|
Move the implemented behavior out of future-only documentation and into each
|
|
canonical current-behavior owner, then perform repository-wide verification.
|
|
|
|
### Documentation
|
|
|
|
1. Update `docs/config.md` to own:
|
|
- the combat extractor's required `scene_descriptions` slot;
|
|
- accepted artifact kind, JSON media type, single-item and 1 MiB limits;
|
|
- configuration-time rejection when the slot is unbound; and
|
|
- the explicit generated binding used by the complete example.
|
|
Update illustrative combat configuration fragments so none show an invalid
|
|
unbound combat extractor.
|
|
2. Update `docs/operations.md` to explain that the first step produces NPC and
|
|
scene-description artifacts and the second step runs combat extraction only
|
|
for exact combat scenes.
|
|
3. Update `docs/integrations/dnd-combat-turn-artifacts.md` to own the externally
|
|
observable eligibility, empty-result, warning, retry, prompt-exclusion, and
|
|
provenance behavior.
|
|
4. Add a concise downstream-consumer link or summary to
|
|
`docs/integrations/dnd-scene-description-artifacts.md` without duplicating
|
|
the combat contract.
|
|
5. Update `docs/internal/modules.md` with the scene eligibility registry,
|
|
construction-time versus operation-time resolution, exact matching,
|
|
fingerprint, and content-retention boundaries. Keep generic ordered-handoff
|
|
mechanics canonical in `docs/internal/pipeline.md`; update that document only
|
|
if implementation changed or clarified a generic mechanism.
|
|
6. Update `docs/internal/overview.md` only if its component inventory requires
|
|
the new domain package.
|
|
7. Remove the completed combat-gating bullet from
|
|
`docs/roadmap/future.md`. Preserve the remaining scene-chunking evaluation
|
|
and ordered-pipeline guidance.
|
|
8. Review `docs/roadmap/scene-aware-combat-extraction.md` for consistency with
|
|
the final implementation. Keep it as feature history until a later explicit
|
|
retirement request; do not duplicate its implementation sequence there.
|
|
9. Verify that README and CLI documentation need no changes. Update them only
|
|
if an existing command or linked example became inaccurate.
|
|
|
|
### Final Verification
|
|
|
|
Run all repository checks:
|
|
|
|
```sh
|
|
go run ./cmd/notarius config validate \
|
|
--config examples/dnd-minimal.config.yml \
|
|
--pipeline dnd-session
|
|
go run ./cmd/notarius config validate \
|
|
--config examples/dnd-complete.config.yml \
|
|
--pipeline dnd-session
|
|
go test ./...
|
|
go vet ./...
|
|
go build ./cmd/notarius
|
|
git diff --check
|
|
```
|
|
|
|
Also verify:
|
|
|
|
- only the two maintained D&D configuration examples remain;
|
|
- every current documentation link points to an existing file;
|
|
- no current-behavior document describes combat extraction without the required
|
|
scene gate;
|
|
- no prompt asset or combat response schema changed; and
|
|
- no generated build artifact is left in the working tree.
|
|
|
|
Stage 4 is complete when documentation ownership is correct, future work no
|
|
longer lists the implemented item, both examples validate, and all repository
|
|
checks pass.
|
|
|
|
## Open Questions
|
|
|
|
None. The feature contract and this plan resolve the required behavior,
|
|
reference ownership, matching semantics, failure policy, warning policy,
|
|
checkpoint identity, documentation ownership, and test boundaries.
|