Add a feature roadmap and implementation plan for a context evidence artifact

This commit is contained in:
2026-07-27 12:09:14 -05:00
parent fb1134e591
commit 64d461fc18
2 changed files with 670 additions and 242 deletions

View File

@@ -1,284 +1,486 @@
# Subprocess Integration Implementation Plan
# Published Evidence Context Implementation Plan
## Status
Completed.
Ready for implementation.
## Objective
Implement the accepted [Subprocess Integration Contract](subprocess.md) as a
small, generic extension of the existing CLI boundary. The completed work must
let a subprocess caller discover a successful Notarius output bundle through a
versioned JSON receipt without parsing human prose, while preserving current
pipeline, output, rejection, warning, configuration, and interactive CLI
behavior.
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.
Complete the stages below in order. Each stage must leave its affected package
passing before the next begins. Do not add Narratio-specific production code,
a public Go client, a new output encoder, or any feature listed as out of scope
in the feature roadmap.
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.
## Decisions And Invariants
- `--json` is a boolean flag on `notarius run`; it does not affect pipeline
resolution, execution, checkpoint identity, output encoding, or publication.
- The machine result is a private CLI Go type implementing the public
`notarius.run-result.v1` wire contract. Do not expose framework or CLI Go
packages for external import.
- The finalized run manifest is authoritative for `run_id`, `pipeline_id`, and
`validation_status`. Verify that its pipeline ID matches the effective
resolved pipeline rather than emitting conflicting provenance.
- Human-oriented stdout remains byte-for-byte governed by its current path when
`--json` is absent. Machine mode emits no human status or debug-path line on
stdout.
- Warnings and errors remain on stderr. Exit statuses remain 0 for success, 1
for runtime failure, and 2 for syntax failure.
- Validation rejection remains a successful pipeline outcome. The receipt
reports counts but does not decide which lanes an orchestrator requires.
- The receipt points to the existing bundle; it does not duplicate output
descriptors, lane payloads, warnings, rejections, or manifest content.
- Reported filesystem paths are lexical absolute paths produced with
`filepath.Abs`. Do not resolve symlinks or change the physical directories
used for publication.
- For the production `json` output module, `index_file` is exactly
`index.json` and is included only after confirming that the runner returned
exactly one logical file with that clean name. For any injected or future
non-`json` output module, omit `index_file`; the machine receipt remains
generic.
- Machine-result construction and JSON encoding occur before debug success
terminalization, but stdout writing occurs only after output publication and
successful requested debug terminalization.
- Standard output is not transactional. A final writer failure returns exit 1
and may leave partial bytes. Consumers must ignore stdout unless the process
exits 0. Because debug terminalization must precede receipt emission, a
result-delivery failure does not rewrite an already persisted successful run
report; it is additionally reported on stderr and the process exits 1.
- Do not add a stable JSON error envelope or promote diagnostic wording into a
compatibility contract.
- 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.
## Stage 1: Define The Run-Result Wire Model
## Stage 1: Add Typed Evidence Capability And Resolve Output Policy
Add a focused private implementation under `internal/cli`, preferably
`run_result.go`, with:
Add a dedicated artifact-evidence registry under the pipeline framework:
- a constant for `notarius.run-result.v1`;
- a private result struct whose JSON fields and requiredness match the feature
roadmap;
- a constructor that accepts the resolved pipeline, completed runner output,
run output directory, and optional debug directory; and
- serialization into an owned byte slice containing exactly one compact JSON
object followed by one newline; and
- a complete-write helper for delivering those prepared bytes.
- `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.
The result fields are:
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.
- required `schema_version`, `run_id`, `pipeline_id`, `output_directory`,
`normalized_output_count`, `rejected_output_count`, and `warning_count`;
- required non-empty `validation_status`, copied without reinterpretation from
the final run manifest;
- optional `index_file`, set to `index.json` for the resolved production JSON
output module; and
- optional `debug_directory`, present only when debug capture was requested and
its path is non-empty.
Define these framework-level output-policy contracts:
The constructor must:
```go
type EvidenceContextPolicy struct {
Enabled bool
WindowUnits int
LaneIDs []string
}
1. validate the required run ID, pipeline ID, final validation status, and
non-empty run output directory rather than emit a malformed receipt;
2. verify that the final manifest pipeline ID matches the effective resolved
pipeline ID;
3. convert the output bundle and optional debug bundle paths to lexical absolute
paths;
4. derive all counts from the completed `pipeline.RunOutput`;
5. identify the output module through the resolved module key, not concrete
encoder types or D&D knowledge; and
6. for module key `pipeline.DefaultOutputModule`, verify exactly one returned
logical output file is named `index.json` before setting `index_file`.
type EvidenceContextPolicyProvider interface {
EvidenceContextPolicy() EvidenceContextPolicy
}
```
Treat a missing or duplicate `index.json` from the production JSON output as a
runtime contract error. Do not inspect or decode the index contents here; the
output encoder and its existing tests own that format.
Provider and prepared-pipeline boundaries defensively copy `LaneIDs`.
Implement the writer with `encoding/json` and complete-write handling. It must
surface encoding errors, zero-progress writes, short writes, and underlying
writer errors to the command boundary. The command must map a delivery failure
to a fixed, bounded result-write error without wrapping caller-supplied writer
text into user or debug diagnostics. Do not add an injected serializer or a
generic serialization framework merely to manufacture unreachable error cases
for tests.
Add an optional output-profile option-validation callback to
`OutputEncoderRegistry`:
Add lean focused tests for the wire-model boundary:
```go
type OutputProfileOptionContext struct {
LaneIDs []string
}
- required fields, counts, schema identity, and compact newline-terminated JSON;
- blank required manifest identities, blank validation status, and a mismatch
between final-manifest and effective pipeline IDs;
- lexical absolute conversion for relative output and debug paths;
- production JSON entry-point discovery;
- omission of `index_file` for a non-JSON output module;
- missing and duplicate production entry points; and
- a representative failing or zero-progress writer.
type OutputProfileOptionValidator func(
OutputProfileOptionContext,
map[string]any,
) error
```
Decode structured output in tests rather than snapshotting an entire JSON
string. Assert literal field names and the schema identity because they are the
public compatibility contract.
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/cli`
## Stage 2: Integrate Machine Mode With Run Finalization
Update the run command in `internal/cli/run.go`:
- register `--json` with the existing `flag.FlagSet`;
- include it in root usage without adding it to `runFlagTakesValue`;
- leave all existing flag combinations valid; and
- do not add configuration or environment equivalents.
After the runner succeeds and requested debug summaries have been written,
apply this exact finalization order:
1. when `--json` is selected, construct and encode the complete receipt into
owned memory; fail before physical output publication if its required fields
or production entry point are invalid;
2. publish the runner's logical output files through the existing confined
output writer;
3. terminalize requested debug reporting as successful and stop without
writing stdout if terminalization fails;
4. in machine mode, write the prepared receipt to stdout and return a runtime
failure if that write does not complete;
5. otherwise, use the unchanged human-oriented success reporting path; and
6. report successful-run warning counts to stderr as today.
The receipt's `output_directory` is the absolute path to the run-specific
bundle, not the configured output root. `debug_directory` is the absolute path
to the allocated run-specific debug bundle.
On a machine-result stdout failure after terminalization:
- report a fixed, bounded, code-owned result-write error on stderr through the
existing CLI failure reporting surface without including the underlying
writer error;
- include the existing debug-path discovery line when applicable;
- return exit 1;
- do not attempt terminal reporting a second time; and
- leave the successfully published output and debug bundle intact.
Do not change `pipelineCommandState`, the debug run-report wire shape, the
output encoder contract, or `writeOutputFiles` solely to support this feature.
The machine receipt belongs to CLI reporting after the runner and output module
have completed their existing responsibilities.
Stage completion:
- `go test ./internal/cli`
## Stage 3: Protect The Public CLI Contract
Add or extend behavior-level CLI tests at the narrowest stable boundaries.
Reuse existing production components, fakes, state harnesses, and maintained
examples rather than creating a second subprocess fixture framework.
Cover:
- a representative maintained production invocation with `--json`, decoding
exactly one stdout document and verifying the schema identity, run and
pipeline IDs, absolute output directory, `index_file`, counts, validation
status, and the existence of the referenced bundle entry point;
- a warning-bearing debug run, proving a correct warning count, warning
reporting on stderr, an absolute optional debug path, and no human prose in
stdout;
- a rejection-bearing successful run using an explicit deterministic rejecting
validator, proving zero or partial normalized outputs and the correct
rejection count without changing exit status;
- one representative syntax failure and one representative runtime failure
with `--json`, proving the established exit class, stderr ownership, and no
completed success document;
- an injected stdout writer failure after successful publication, proving exit
1, retained output files, fixed diagnostic reporting, and omission of a
recognizable writer-error sentinel from stderr and debug diagnostics; and
- the existing no-`--json` tests continuing to protect interactive output.
For the writer-failure case, use a writer that fails before accepting bytes when
asserting empty stdout. The public contract nevertheless remains that arbitrary
writers may leave partial bytes and consumers must ignore stdout on nonzero
exit.
Do not:
- duplicate the JSON output encoder's lane, manifest, warning, or rejection
serialization matrix;
- assert complete human error strings;
- add a golden file for the small receipt;
- test private helper call order; or
- add a prompt-language or unrelated end-to-end test.
Stage completion:
- `go test ./internal/cli`
- `go test ./internal/framework/pipeline`
- `go test ./internal/modules/generic/output/json`
- `go test ./internal/modules/integration`
- `go test ./internal/modules/dnd/register`
- `go test ./internal/cli`
## Stage 4: Publish The Consumer-Facing Documentation
## Stage 2: Define And Build The Evidence-Context Artifact
After the code and behavior tests pass, update the canonical current-behavior
documentation:
Add a domain-neutral `internal/framework/evidencecontext` package that owns the
durable model, JSON Schema, strict codec, projection algorithm, and these exact
identities:
- `docs/cli.md`
- add `--json` to run syntax and its flag table;
- define stdout/stderr behavior and the requirement to parse machine stdout
only after exit 0;
- preserve the existing exit-status table and link to the run-result
contract.
- `docs/integrations/run-result.md`
- own the complete `notarius.run-result.v1` field table, requiredness,
example, path semantics, production `index_file` rule, partial-write rule,
and additive compatibility policy;
- link to the published JSON bundle contract rather than repeating its
descriptors or payload schemas.
- `docs/integrations/json-output.md`
- add only a narrow cross-link explaining that a subprocess caller obtains
the physical bundle root from the run-result receipt before using
`index.json` for logical discovery;
- do not duplicate run-result fields or stream semantics.
- `docs/consumers/subprocess.md`
- provide a concise task workflow for preflight, invocation, separate stream
capture, exit checking, receipt decoding, confined index resolution, lane
lookup by ID, media-type and schema-identity checks, required-versus-optional
consumer policy, provenance retention, and sensitive-data handling;
- use a generic orchestrator example with placeholders, not private Narratio
paths, credentials, or a duplicate complete configuration.
- `docs/internal/cli.md`
- document receipt construction before publication, emission after
publication and debug terminalization, and the stdout failure behavior;
- preserve the CLI composition-root boundary.
- `README.md` and `docs/development.md`
- add only the smallest links needed to make subprocess consumer guidance and
its implementation owner discoverable.
- 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`.
Do not otherwise change `docs/config.md`, `docs/operations.md`, or the JSON
output bundle contract unless implementation reveals an actual change to
behavior they own. The machine receipt adds no configuration field, filesystem
surface, retention policy, or lane-output shape.
Use these package-level model and build contracts:
After all code, tests, and current-behavior documentation are complete:
```go
type Document struct {
SourceID string
SourceDigest string
WindowUnits int
SelectedLanes []string
Contexts []Context
}
- change [the feature roadmap](subprocess.md) status to `Implemented`;
- change this plan's status to `Completed`; and
- ensure neither roadmap is used as the canonical description of current
behavior.
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:
- validate documentation links and command examples against the implementation;
- `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/cli`
- `go test -race ./internal/framework/evidencecontext ./internal/framework/pipeline ./internal/modules/generic/output/json ./internal/modules/dnd/... ./internal/modules/integration ./internal/cli`
## Open Questions
None. The decisions above resolve stdout partial-write behavior, output-module
generality, path normalization, partial pipeline success, terminal reporting,
documentation ownership, and test boundaries.
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.