Add a new feature roadmap and implementation plan for D&D prompt ordering to improve LLM provider caching

This commit is contained in:
2026-07-27 15:04:32 -05:00
parent 7c94b5eeed
commit 53490cdb59
5 changed files with 350 additions and 899 deletions

View File

@@ -1,486 +1,257 @@
# Published Evidence Context Implementation Plan
# D&D Prompt Cache Ordering Implementation Plan
## Status
## Summary
Completed.
Implement the target state in
[D&D Prompt Cache Ordering](dnd-prompt-cache-ordering.md): give all six D&D
extraction prompts a byte-identical rendered prefix through the transcript
chunk, retain purpose-specific ordering for scene chunking and NPC
normalization, consolidate tests around durable prompt-order invariants, and
replace the current transcript-last documentation.
## Objective
This is a prompt-asset and documentation change. Do not change Go module APIs,
LLM request contracts, prompt IDs or versions, response schemas, artifact
schemas, reference declarations, extraction behavior, retry behavior, or
durable output. Existing prompt fingerprints must continue to provide
checkpoint invalidation; no checkpoint migration or cleanup is required.
Implement the accepted [Published Evidence Context](evidence.md) roadmap as an
optional, deterministic extension of the production JSON output. The completed
work must publish one deduplicated source-context artifact for an explicit set
of successful normalized lanes while leaving existing lane payloads,
normalization, checkpointing, subprocess results, and disabled output bundles
unchanged.
## Stage 1: Reorder The Six Extraction Prompts
Complete the stages below in order. Each stage must leave its affected packages
passing before the next begins. Do not implement per-record hydration, implicit
all-lane collection, a full-transcript mode, LLM processing, or any other
roadmap item marked out of scope.
Update these manifests:
## Decisions And Invariants
- `internal/modules/dnd/extract/npcs/assets/prompts/dnd.npcs.yaml`
- `internal/modules/dnd/extract/itemevents/assets/prompts/dnd.item_events.yaml`
- `internal/modules/dnd/extract/scenedescriptions/assets/prompts/dnd.scene_descriptions.yaml`
- `internal/modules/dnd/extract/combatturns/assets/prompts/dnd.combat_turns.yaml`
- `internal/modules/dnd/extract/npcinteractions/assets/prompts/dnd.npc_interactions.yaml`
- `internal/modules/dnd/extract/spells/assets/prompts/dnd.spells.yaml`
- Evidence publication is output policy. Normalizers continue to return
semantic artifacts with precise source references and do not receive
hydration responsibilities.
- The framework operates on the accepted generic `source.SourceDocument` and
typed artifact projections. It must not inspect serialized JSON for
`source_ref` or `source_refs`, and generic packages must not depend on D&D
types.
- The selected lane allowlist is explicit, non-empty, and globally addressed by
resolved lane ID. Pipeline resolution already guarantees lane IDs are unique
across ordered steps.
- The framework decodes accepted serialized normalize outputs through their
registered artifact codecs before invoking typed evidence projectors. This
supports both fresh and checkpoint-reused normalize outputs without retaining
a second typed result channel.
- Expansion uses source-document positions. Numeric unit IDs are identities,
not sequence numbers.
- Direct source references are never widened or rewritten. Expanded ranges are
context bounds only.
- Rejected, failed, and absent normalized lane outputs contribute no evidence.
- Evidence output is sensitive durable source content, not cache or debug
state. It contains accepted source units only and never raw input bytes,
prompts, model responses, auxiliary references, paths, or credentials.
- The optional `evidence_context` index field is an additive v1 JSON-bundle
change. Existing D&D artifact schemas and the subprocess receipt do not
change.
Give every manifest this exact leading message sequence:
## Stage 1: Add Typed Evidence Capability And Resolve Output Policy
1. `common-dnd-system.md`, with role `system` and no cache control;
2. `common-dnd-identity.md`, with role `user` and no cache control;
3. `common-dnd-references.md`, with role `user` and ephemeral cache control;
4. `common-dnd-transcript.md`, with role `user` and ephemeral cache control.
Add a dedicated artifact-evidence registry under the pipeline framework:
The common prefix must contain no extraction-evidence policy, NPC registry,
spell catalog, task, or module instructions. Those messages vary across lanes
and would prevent the transcript from participating in a shared cache prefix.
- `pipeline.ArtifactEvidenceRegistry` stores one typed projector per artifact
kind.
- `pipeline.ArtifactEvidenceProjector[T]` is
`func(T) []source.SourceRef`.
- `pipeline.RegisterArtifactEvidence[T](registry, kind, projector)` accepts a
non-empty kind and non-nil projector, records the exact Go type for `T`, and
rejects duplicate kinds.
- The erased projection boundary checks the exact registered type, invokes the
projector, and returns a defensive copy of its references.
- The registry exposes only the discovery and projection operations required by
resolution, preparation, and execution; do not expose its mutable entries.
Append the following lane-specific sequences after the transcript:
Add the registry to `pipeline.Registries` and `pipeline.ModuleCatalog`, including
CLI catalog conversion, production construction, empty-set detection, and
test registry helpers. A nil evidence registry remains valid when evidence
publication is disabled. Production construction and the D&D registrar require
and populate it.
| Prompt | Post-transcript sequence |
| --- | --- |
| `dnd.npcs` | `common-dnd-extraction-evidence.md`, `task.md`, `instructions.md` |
| `dnd.item_events` | `common-dnd-extraction-evidence.md`, `task.md`, `instructions.md` |
| `dnd.scene_descriptions` | `task.md`, `instructions.md` |
| `dnd.combat_turns` | `common-dnd-extraction-evidence.md`, `common-dnd-npcs.md`, `task.md`, `instructions.md` |
| `dnd.npc_interactions` | `common-dnd-extraction-evidence.md`, `common-dnd-npcs.md`, `task.md`, `instructions.md` |
| `dnd.spells` | `common-dnd-extraction-evidence.md`, `common-dnd-npcs.md`, `catalog.md`, `task.md`, `instructions.md` |
Define these framework-level output-policy contracts:
For every extraction prompt:
```go
type EvidenceContextPolicy struct {
Enabled bool
WindowUnits int
LaneIDs []string
}
- apply ephemeral cache control to the final `instructions.md` message;
- do not apply cache control to the extraction-evidence, NPC-registry, catalog,
or task messages;
- remove the existing cache control from `common-dnd-identity.md` and
`common-dnd-npcs.md`; and
- retain the existing inputs, requiredness, media types, output contract,
profile, repair setting, prompt identity, and schema path.
type EvidenceContextPolicyProvider interface {
EvidenceContextPolicy() EvidenceContextPolicy
}
Do not add the extraction-evidence asset to the scene-description prompt merely
to make it resemble the other lanes. It is not universal shared context and
therefore belongs outside the common-prefix contract.
### Stage 1 completion criteria
- Equivalent transcript, player, party, and glossary inputs render the same
first four messages for all six extraction prompts, including identical
roles, content, and cache metadata.
- Each transcript input is rendered exactly once.
- Every lane-specific message follows the transcript.
- Prompt preparation and schema wiring remain successful for every lane.
## Stage 2: Apply Purpose-Specific Ordering To Other D&D LLM Prompts
Update
`internal/modules/dnd/chunk/scenes/assets/prompts/dnd.scenes.yaml` to use:
1. `common-dnd-system.md`, without cache control;
2. `common-dnd-references.md`, with ephemeral cache control;
3. `task.md`, without cache control;
4. `instructions.md`, without cache control; and
5. `common-dnd-transcript.md`, with ephemeral cache control.
The scene chunker consumes the full transcript once and has no sibling lanes
that can share a transcript prefix. Stable task instructions therefore remain
before the changing full transcript.
Update
`internal/modules/dnd/normalize/npcs/assets/prompts/dnd.npcs.normalize.yaml` to
use:
1. `common-dnd-system.md`, without cache control;
2. `task.md`, without cache control;
3. `instructions.md`, with ephemeral cache control;
4. `candidates.md`, without cache control; and
5. `common-dnd-transcript.md`, with ephemeral cache control.
Remove the normalizer system message's current cache control. Preserve the
candidate-before-evidence ordering: the transcript windows support evaluation
of the candidate collection and are not a cross-lane transcript prefix.
Do not change input material construction, transcript windowing, prompt
fragments, module implementations, or response handling in either package.
### Stage 2 completion criteria
- Scene chunking renders references separately before the task and the full
transcript exactly once at the end.
- NPC normalization renders candidates and transcript windows in separate
messages, in that order, with the agreed cache boundaries.
- Both prompts retain their current identities, schemas, inputs, and runtime
behavior.
## Stage 3: Consolidate Prompt-Ordering Verification
Read and follow `docs/policy/testing.md` before modifying tests. The meaningful
risk is loss of the shared rendered prefix or incorrect placement of
request-specific material, not edits to prompt prose.
Add one family-level extraction invariant test in
`internal/modules/dnd/register/prompt_cache_test.go` (or an equivalently scoped
new register-package test file):
- register the complete D&D prompt asset family in a real in-memory
`llm.AssetRegistry`;
- construct a Scriptorium engine with a local, non-networked test profile;
- prepare all six extraction prompts using their exported prompt IDs and
versions;
- supply the same unique transcript, players, party, and glossary sentinels to
every prompt, plus only the lane-specific required inputs such as the NPC
registry and spell catalog;
- locate the one rendered message containing the transcript sentinel rather
than locating it through prompt prose or a hard-coded phrase;
- assert that the transcript sentinel occurs exactly once in each prepared
prompt;
- compare the rendered messages from the start through the transcript message
across all six prompts, including role, full rendered content, and complete
cache-control metadata;
- assert that the shared prefix contains exactly the four agreed messages and
that at least one lane-specific message follows it in every extraction
prompt; and
- assert that NPC-registry and spell-catalog sentinels, where supplied, occur
only after the transcript.
Use this family-level test as the single owner of the cross-lane prefix policy.
Do not duplicate the same prefix assertion in every extractor package.
Revise the existing prompt-asset tests in:
- `internal/modules/dnd/extract/npcs/scriptorium_assets_test.go`
- `internal/modules/dnd/extract/itemevents/scriptorium_assets_test.go`
- `internal/modules/dnd/extract/scenedescriptions/scriptorium_assets_test.go`
- `internal/modules/dnd/extract/combatturns/scriptorium_assets_test.go`
- `internal/modules/dnd/extract/npcinteractions/scriptorium_assets_test.go`
- `internal/modules/dnd/extract/spells/scriptorium_assets_test.go`
- `internal/modules/dnd/chunk/scenes/scriptorium_assets_test.go`
- `internal/modules/dnd/normalize/npcs/scriptorium_assets_test.go`
Remove transcript-last expectations that contradict the new extraction policy.
Keep package-local coverage only where it protects package-owned behavior, such
as prompt/schema registration, declared shared assets, isolation and placement
of package-specific input material, cache boundaries unique to the scene
chunker or NPC normalizer, and non-disclosure properties. Consolidate or remove
positional and prose-marker checks already covered by the family-level
invariant.
Tests may search for unique input sentinels created by the test. They must not
require the presence of specific words, phrases, sentences, or other prose in a
prompt asset. Do not introduce golden snapshots of complete prompts or a test
that treats harmless wording changes as failures.
### Stage 3 completion criteria
- One durable family-level test fails if any extraction lane moves
lane-specific material ahead of the transcript or changes the common
rendered prefix.
- Local asset tests protect only nonredundant package behavior and the two
purpose-specific prompt sequences.
- The test suite contains no prompt-language change detector.
- All prompt tests remain deterministic, offline, and independent of
credentials.
## Stage 4: Update Canonical Internal Documentation
Update `docs/internal/llm.md` as the generic owner of prompt-cache mechanics:
- explain that backend reuse depends on identical preceding roles, rendered
bytes, and cache metadata, not merely equivalent semantics;
- state the general sibling-prompt ordering rule: universal shared context,
request source material, then module-specific suffixes;
- explain that a cache boundary should be placed at a useful reusable prefix
and that redundant intermediate boundaries add no value;
- retain the rule that prompt-family owners may choose a different sequence
when their inputs and reuse pattern differ; and
- replace the current statement that D&D extraction puts transcripts last with
a link to the D&D-specific policy.
Update `docs/internal/dnd.md` as the concrete owner of maintained D&D prompt
composition:
- document the four-message extraction prefix and its cache boundaries;
- state that extraction-evidence policy, generated NPC registries, catalogs,
tasks, and instructions follow the transcript because they are not universal
across all extraction lanes;
- summarize the scene-chunking and NPC-normalization exceptions and their
rationale; and
- retain existing rules about shared-asset reuse, canonical reference input,
prompt fingerprints, and prompt behavior.
Do not update the README, configuration reference, architecture policy, CLI
documentation, or integration contracts. Prompt message order is an internal
runtime policy and has no user-visible configuration or wire-contract change.
### Stage 4 completion criteria
- No current-behavior document still says that D&D extraction transcripts are
final messages.
- Generic cache mechanics and D&D-specific ordering each have one canonical
owner.
- Documentation describes implemented behavior once the prompt changes land
and does not duplicate volatile manifest inventories unnecessarily.
## Stage 5: Verification
Inspect representative prepared prompts to confirm that the test inputs produce
the intended shared prefix and lane-specific suffixes. Then run:
```sh
git diff --check
go test ./internal/modules/dnd/chunk/scenes
go test ./internal/modules/dnd/extract/...
go test ./internal/modules/dnd/normalize/npcs
go test ./internal/modules/dnd/register
go test ./internal/modules/dnd/...
go test ./internal/modules/integration/...
go test ./...
go vet ./...
go build ./cmd/notarius
```
Provider and prepared-pipeline boundaries defensively copy `LaneIDs`.
The focused commands should be used while implementing their respective
stages; the repository-wide commands are the final acceptance gate.
Add an optional output-profile option-validation callback to
`OutputEncoderRegistry`:
## Expected Compatibility Effects
```go
type OutputProfileOptionContext struct {
LaneIDs []string
}
type OutputProfileOptionValidator func(
OutputProfileOptionContext,
map[string]any,
) error
```
Add `RegisterBuilderWithProfileValidation(spec, validateOptions,
validateProfile, builder)` and make existing output registration methods
delegate to it with no profile callback. The registry passes defensive copies
to validation. The callback receives the complete configured lane-ID set before
invocation-level `--only` filtering. The production JSON output uses it only to
prove that every configured evidence lane exists. Keep the extension generic:
the pipeline supplies lane identities, while the output module interprets its
own options. Build that lane set from the normalized legacy-or-steps profile
before selection and reject duplicate configured lane IDs through the existing
pipeline identity rules.
Extend the production JSON output options with the nested
`evidence_context` object:
- omission disables the feature;
- `enabled` is required when the object is present;
- `enabled: false` permits no `lanes` or `window_units` fields;
- `enabled: true` requires a non-empty `lanes` array;
- lane values are strings, trimmed, non-empty, unique after trimming, and
normalized to lexical order;
- `window_units` is an optional non-negative integer with default `3`; and
- outer and nested unknown fields and incompatible YAML value types remain
strict configuration errors.
The JSON encoder implements the policy provider from its decoded immutable
options. Pipeline resolution invokes its profile validator against all
configured steps, so an unknown evidence lane fails even when another lane is
selected with `--only`.
During `pipeline.Prepare`, after constructing the output encoder:
1. obtain and defensively normalize an enabled policy;
2. intersect its configured IDs with the effective prepared lanes, treating
allowlisted lanes removed by invocation-level filtering as inactive;
3. require an artifact-evidence registration for each active lane kind;
4. prove that its projector Go type exactly matches the active lane's registered
artifact codec type; and
5. retain an immutable private evidence plan on `PreparedPipeline`.
Duplicate or empty provider values, a missing evidence registry for an active
lane, unsupported active artifact kinds, and type mismatches fail preparation
with pipeline/output/lane context. A disabled or non-participating output
encoder creates no evidence plan and preserves existing preparation behavior.
The private plan retains both the full configured allowlist for publication and
the active lane/projector intersection for execution.
Register D&D evidence projectors for all six current artifact kinds. Each
projector returns copies of the artifact's direct references in record order:
spells, NPCs, combat turns, item events, NPC interactions, and the singular
reference from each scene description. Scene descriptions gain capability but
remain excluded unless their configured lane ID is allowlisted.
Stage tests:
- Registry tests cover nil, blank, duplicate, exact-type, defensive-copy, and
deterministic discovery behavior.
- JSON option tests cover disabled, enabled/default-window, explicit zero
window, normalization, duplicates, unknown fields, and invalid types.
- Preparation tests cover selected lanes across steps, unknown lanes,
unsupported kinds, projector/codec type mismatch, disabled behavior, and
defensive policy ownership.
- Resolution/preparation tests prove a valid allowlist survives `--only`, an
excluded lane contributes no active projector, and a genuinely unknown
configured lane still fails profile resolution.
- D&D registration tests prove every production D&D artifact kind has the
expected evidence capability without testing individual field loops
redundantly.
- One table-driven D&D projector test supplies representative values for all
six artifact kinds and proves plural and singular references are copied
without aliasing or semantic rewriting.
Stage completion:
- `go test ./internal/framework/pipeline`
- `go test ./internal/modules/generic/output/json`
- `go test ./internal/modules/dnd/register`
- `go test ./internal/cli`
## Stage 2: Define And Build The Evidence-Context Artifact
Add a domain-neutral `internal/framework/evidencecontext` package that owns the
durable model, JSON Schema, strict codec, projection algorithm, and these exact
identities:
- artifact kind `source/evidence-context`;
- media type `application/json`;
- schema ID `notarius.source.evidence_context`;
- schema name `notarius_source_evidence_context_v1`; and
- schema version `v1`.
Use these package-level model and build contracts:
```go
type Document struct {
SourceID string
SourceDigest string
WindowUnits int
SelectedLanes []string
Contexts []Context
}
type Context struct {
ContextRef source.SourceRef
EvidenceRefs []EvidenceRef
Units []source.SourceUnit
}
type EvidenceRef struct {
LaneID string
SourceRef source.SourceRef
}
type LaneEvidence struct {
LaneID string
SourceRefs []source.SourceRef
}
type BuildRequest struct {
Source *source.SourceDocument
WindowUnits int
SelectedLanes []string
LaneEvidence []LaneEvidence
}
```
Apply the JSON field names shown below. Provide `Build(BuildRequest)`,
`Serialize(BuildRequest)`, and a `Codec` with the same identity/encode/decode
responsibilities as the chunk-map codec.
The v1 payload has this exact shape:
```json
{
"source_id": "session-alpha",
"source_digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
"window_units": 0,
"selected_lanes": ["npcs"],
"contexts": [
{
"context_ref": {
"source_id": "session-alpha",
"start_unit_id": 13,
"end_unit_id": 13
},
"evidence_refs": [
{
"lane_id": "npcs",
"source_ref": {
"source_id": "session-alpha",
"start_unit_id": 13,
"end_unit_id": 13
}
}
],
"units": [
{
"id": 13,
"kind": "transcript_segment",
"text": "The party meets Rowan.",
"ref": {
"source_id": "session-alpha",
"start_unit_id": 13,
"end_unit_id": 13
}
}
]
}
]
}
```
All displayed fields are required. `selected_lanes`, `contexts`,
`evidence_refs`, and `units` encode as arrays rather than `null`; `contexts`
may be empty. Each unit uses the existing `source.SourceUnit` JSON shape with
required `id`, `kind`, `text`, and `ref`, plus optional JSON-shaped `metadata`.
Fixed objects reject unknown fields; metadata remains an arbitrary JSON object.
The builder accepts the validated source document, selected lane IDs, effective
window, and lane-attributed direct references, then:
1. requires a non-negative window and a non-empty, trimmed, unique selected
lane set, then stores that set in lexical order;
2. requires every `LaneEvidence.LaneID` to belong to the selected set;
3. validates the source document, recomputes its semantic digest, and requires
it to equal `SourceDocument.Digest`;
4. validates every direct reference against one `source.DocumentIndex`;
5. deduplicates exact `(lane_id, source_ref)` contributions;
6. resolves endpoints to document positions;
7. expands each side without integer overflow and clips at document bounds;
8. sorts by expanded document position with deterministic lane/reference
tie-breakers;
9. merges overlapping or position-contiguous expanded intervals;
10. unions and deterministically sorts each merged context's direct
contributions; and
11. deep-clones the corresponding source units and JSON-shaped metadata.
Contexts are disjoint and ordered by document position, so a source unit occurs
at most once in the document. `context_ref` identifies the first and last
included units; `evidence_refs` retains only original citations. An empty
reference collection produces the same source identity, window, sorted
allowlist, and an explicit empty contexts array.
Projection failures identify only structural scope such as lane and reference
position. They must not include source text, metadata values, raw serialized
artifacts, or unrelated paths.
The codec must validate its model before encoding, produce deterministic JSON,
strictly decode the checked-in schema, and return independently owned values.
The JSON output encoder remains responsible for pretty-printing the logical
file with its standard trailing newline. Follow the existing chunk-map
package's separation between model, builder, codec, schema asset, and contract
tests where useful, without coupling the two artifact formats.
Stage tests:
- A table-driven builder suite covers zero and nonzero windows, boundary
clipping, non-monotonic unit IDs, separate gaps, overlapping and contiguous
windows, duplicate contributions, multiple lanes, stable ordering, empty
contexts, invalid selected/contributing lanes, source-digest mismatch, and
invalid references.
- Ownership tests prove output mutation cannot affect the source document or
projector inputs, including nested metadata.
- Codec tests cover round trip, required arrays, schema identity, malformed and
trailing JSON, unknown fixed fields, invalid ordering/ranges, mismatched
source identities, and independently owned decoded metadata.
- Use structured assertions and a compact valid fixture; do not add a large
transcript golden file.
Stage completion:
- `go test ./internal/framework/evidencecontext`
## Stage 3: Integrate Projection With Runner Output
Extend `contracts.OutputRequest` with an optional
`EvidenceContext *SerializedArtifact` field and clone it at every ownership
handoff, following the existing chunk-map pointer pattern.
After lane execution and final manifest population, but before invoking the
output encoder, the runner must:
1. skip all work when the prepared evidence plan is absent;
2. index accepted `NormalizeOutputs` by their globally unique lane IDs and fail
on an internal duplicate rather than silently overwrite it;
3. for each selected lane with an output, verify its source and artifact kind,
decode it through the prepared artifact codec registry, and invoke the
prepared typed projector;
4. build and serialize the evidence document through the evidence-context
package; and
5. pass a defensive serialized-artifact copy to the output encoder.
Selected lanes without normalized output contribute nothing. Normalize
rejections remain successful pipeline outcomes; evidence projection does not
inspect rejected candidates. An invalid accepted reference, incompatible
serialized artifact, projection type failure, or evidence serialization failure
is an output-stage framework error before logical files are returned or
physically published.
At this external-content consumption boundary, do not propagate artifact-codec
or metadata-cloning errors with `%w` when their text could contain artifact
fields or source metadata. Return fixed, actionable categories scoped by lane
and operation; detailed codec errors remain available to direct trusted
callers and their focused tests.
Add an allowlisted debug summary containing only evidence artifact identity,
selected lanes, window, context count, unit count, and source digest. Do not
duplicate transcript text or source-unit metadata into a new evidence-specific
debug envelope. Existing normalized-output debug behavior remains unchanged.
The output artifact is not a normalized lane, generated reference, checkpoint,
or manifest normalized-output entry. It does not alter normalized-output,
rejection, or warning counts. Resume continues to reuse normalized checkpoints;
evidence is deterministically rebuilt during the always-executed output stage.
Stage tests:
- Runner tests use a real codec, evidence projector, and small capturing output
encoder to prove selected-lane union, absent/rejected lane omission, invalid
accepted-reference failure, output-request defensive ownership, and no work
when disabled.
- Include one checkpoint-reused normalized-output case to prove evidence is
reconstructed identically without retaining typed normalize values.
- Confirm projection failures prevent output encoding and return a failed
manifest without changing rejection semantics.
Stage completion:
- `go test ./internal/framework/pipeline`
## Stage 4: Publish Through The JSON Bundle
Teach the production JSON encoder to recognize the optional evidence-context
artifact, verify its exact kind, media type, schema identity, schema digest, and
payload validity through the evidence-context codec, and emit:
- logical file `evidence-context.json`; and
- optional `index.json` descriptor field `evidence_context`.
The descriptor uses the same six fields as `chunk_map`:
`artifact_kind`, `file`, `media_type`, `schema_id`, `schema_name`, and
`schema_version`. Refactor the encoder's private descriptor representation only
as needed to share that shape; do not change the existing `chunk_map` wire
contract. Evidence output is ordered with the encoder's other fixed logical
files, remains a non-lane artifact, and is present with an empty contexts array
when enabled but no selected lane produces references.
The JSON encoder's validation boundary returns a fixed content-safe evidence
artifact error rather than propagating decoder or schema diagnostics that could
echo transcript text or metadata. Direct evidence-context codec tests retain
detailed structural errors.
Update the maintained complete D&D configuration to enable evidence context
with window `3` for `item-events`, `npcs`, `spells`, `combat-turns`, and
`npc-interactions`. Deliberately omit `scene-descriptions`. Keep the minimal
configuration disabled by omission.
Stage tests:
- JSON encoder tests own descriptor shape, exact logical filename, identity
checking, empty evidence publication, and disabled bundle stability.
- One assembled production D&D test uses multiple selected lanes with
overlapping references and non-monotonic unit IDs, decodes the published
artifact through its production codec, and proves union/deduplication and
scene-description exclusion.
- A second narrow case explicitly allowlists a scene-description lane to prove
capability is opt-in rather than hard-coded exclusion.
- Existing index, chunk-map, lane, manifest, warning, and rejection tests remain
the owners of their current formats; do not repeat their full matrices.
Stage completion:
- `go test ./internal/modules/generic/output/json`
- `go test ./internal/modules/dnd/...`
- `go test ./internal/modules/integration`
- `go test ./internal/cli`
## Stage 5: Publish Current-Behavior Documentation
After implementation and behavioral tests pass, update canonical documentation:
- `docs/config.md` owns the nested JSON output options, defaults, strict
validation, required lane allowlist, and a small configuration snippet.
- A new `docs/integrations/evidence-context.md` owns the complete v1 payload,
identities, direct-evidence versus context semantics, ordering,
compatibility, and a compact valid example.
- `docs/integrations/json-output.md` owns the optional logical file and
`index.json` descriptor; link to the evidence contract rather than repeating
its payload.
- `docs/operations.md` owns durable source-content sensitivity, permissions,
retention, and the possibility that selected lanes cover most of a
transcript.
- `docs/consumers/subprocess.md` explains discovery through the optional index
descriptor and requires consumers to treat `evidence_refs`, not expanded
context bounds, as citations.
- `docs/policy/architecture.md` records the generic typed evidence-projection
boundary and output ownership without adding D&D or wire-format detail.
- `docs/internal/pipeline.md` and `docs/internal/modules.md` describe the typed
evidence registry, preparation checks, reconstruction from serialized
normalize outputs, and output-stage ownership without restating public wire
fields.
Update only the smallest orientation links needed for discoverability. Do not
add a CLI flag, configuration environment override, or duplicate the complete
configuration outside `examples/`.
After all current-behavior documentation is accurate:
- set [the feature roadmap](evidence.md) status to `Implemented`;
- set this plan's status to `Completed`; and
- leave the integration and configuration documents, not either roadmap, as
the canonical implemented contract.
Final verification:
- `git diff --check`
- `go test ./...`
- `go vet ./...`
- `go build ./cmd/notarius`
- `go test -race ./internal/framework/evidencecontext ./internal/framework/pipeline ./internal/modules/generic/output/json ./internal/modules/dnd/... ./internal/modules/integration ./internal/cli`
- Reordered prompt content changes prompt fingerprints and may intentionally
cause one-time cold misses for affected development checkpoints.
- Prompt IDs, prompt versions, schema identities, checkpoint format, durable
artifacts, and public behavior do not change.
- Backend cache reuse remains provider-dependent, but Notarius supplies a
longer common extraction prefix that includes the transcript chunk.
## Open Questions
None. The plan fixes the configuration shape and defaults, typed projection
boundary, preparation timing, durable schema and identities, range-union
algorithm, failure semantics, JSON discovery, D&D coverage, documentation
ownership, and test boundaries.
None. The message sequences, cache boundaries, test ownership, documentation
owners, and compatibility policy are specified above.