Audit the implementation and add a roadmap with cleanup items and bugfixes

This commit is contained in:
2026-07-22 01:30:17 +00:00
parent 7071102ab7
commit 64ea23c21f

View File

@@ -1,500 +1,392 @@
# Implementation Plan: Ordered Pipeline Steps # Implementation Plan: Ordered Pipeline Follow-Up
## Purpose ## Purpose
Implement the target state in [Ordered Pipeline Steps](ordered-pipeline-steps.md). Close the remaining correctness, observability, test, and maintainability gaps
That feature roadmap is the authority for product intent, policy choices, in the implemented [Ordered Pipeline Steps](ordered-pipeline-steps.md) feature.
acceptance criteria, and exclusions. This document turns it into an ordered, That feature roadmap remains the authority for product intent, policy choices,
decision-complete implementation sequence for a coding agent. acceptance criteria, and exclusions. This document is the ordered,
decision-complete implementation sequence for the follow-up work.
This is a platform refactor followed by one D&D proving workflow. It does not ## Background
include item extraction, a general DAG scheduler, schema migrations for D&D
artifacts, or any other work excluded by the feature roadmap. The original implementation is complete in broad architecture. Pipelines now
resolve and prepare one canonical ordered-step model, execute hard barriers
between steps, hand accepted normalized artifacts to later operations as
canonical generated references, include generated identity in checkpoint and
output provenance, support `--recompute-step`, and use generated NPC output at
operation time in the D&D spell and combat consumers. Current documentation and
maintained examples describe that model.
The remaining work is narrower:
- selective recomputation currently treats an unselected predecessor as
reusable only when all of its extract, merge, and normalize stage checkpoints
are reusable, although the architectural dependency is its accepted
normalized artifact;
- the failed stage decision is not always recorded before a required-predecessor
error returns;
- checkpoint reason codes are inferred from human-readable prose instead of
being assigned explicitly;
- the runner's lane and checkpoint orchestration has become too concentrated in
large functions; and
- the roadmap-required CLI and resumed-producer acceptance coverage is absent.
No new product feature is introduced by this plan. Preserve configuration file
version 3 and checkpoint workspace schema `notarius.workspace.v3`; the fixes do
not require a new persistent format.
## Instructions For Every Stage ## Instructions For Every Stage
Before changing code, read `docs/development.md` and all documents under Before changing code, read `docs/development.md`, all documents under
`docs/policy/`. Preserve the fixed pipeline lifecycle and the two-zone data `docs/policy/`, and the task-specific internal documents named there. Use the
model described there. Use the repository's code knowledge graph for code repository's code knowledge graph for code discovery before falling back to
discovery before falling back to text search. text search.
Implement the stages in order. Each stage must leave the repository formatted, Implement the stages in order. Each stage must leave the repository formatted,
building, and passing the focused tests it changes. Do not maintain two building, and passing its focused tests. Preserve these invariants throughout:
competing internal representations merely to reduce a refactor: legacy
top-level `artifacts` is a configuration compatibility form, while ordered
steps are the single resolved, prepared, and runtime representation.
Follow these cross-stage rules: - The pipeline retains one input, chunk plan, output encoder, run identity,
checkpoint identity, failure boundary, worker budget, and provider scheduler.
- Every selected module and validator is constructed before source parsing.
- Steps schedule fixed extract, merge, and normalize lanes; this work must not
introduce module-to-module calls or a general DAG scheduler.
- Generated artifacts remain cloned operation-time context, never source
evidence. Never emit their content, source material, credentials, or local
paths in decisions, manifests, logs, or summaries.
- Public ordering remains step order, lane ID, and source/chunk order as
applicable. Refactoring must not expose completion order.
- Tests must follow `docs/policy/testing.md`: protect observable recovery,
orchestration, and CLI contracts without asserting private helper calls,
exact prose, goroutine choreography, or full-document snapshots.
- Update current-behavior documentation in the same stage that changes the
corresponding behavior. Keep detailed implementation sequencing only here.
- Keep input parsing, chunk planning, output encoding, run identity, worker ## Target Checkpoint Semantics
budgets, provider scheduling, and the failure boundary pipeline-wide.
- Preserve the lane lifecycle and existing typed module interfaces. Steps
schedule lanes; they do not permit modules to call other modules.
- Construct every selected module and validator before source parsing. Supply
generated bytes only through cloned operation-request `References`.
- Resolve and report deterministic order as step order, then lane ID, then
source/chunk order. Never expose completion order.
- Treat generated references as context, not source evidence. Never put their
content in manifests, checkpoint decisions, logs, or debug summaries.
- Add narrow, behavior-oriented, offline tests at the stable owner of each new
invariant. Do not add scheduler choreography, exact goroutine counts, full
manifest snapshots, exact error-string snapshots, or live-provider tests.
- Keep configuration file version 3. The new fields are a backward-compatible
extension; checkpoint wire state receives its own explicit compatibility
change in Stage 4.
- Update current-behavior documentation only in Stage 6, when the complete
behavior exists. The ADR may be added in Stage 1 because it records the
architecture decision rather than claiming released behavior.
## Canonical Internal Model Use these definitions consistently in all stages:
Use the following model throughout the implementation. Exact unexported helper - A **stage checkpoint** is internal resumable state for extract, merge, or
names may follow local conventions, but their responsibilities and invariants normalize. Ordinary resume may continue to reuse this state progressively.
are fixed here. - An **accepted lane artifact** is the one successful normalized artifact for a
`(step ID, lane ID, normalizer module)` under the current checkpoint identity.
It is the dependency exposed to a later step.
- An unselected required predecessor satisfies selective recomputation when its
accepted lane artifact can be validated and hydrated. Its extract and merge
stage checkpoints are not prerequisites for that handoff.
- A selected lane and its transitive dependents execute. They must never use
accepted-artifact hydration to bypass forced execution.
- If a required predecessor's accepted lane artifact is missing, rejected,
corrupt, non-canonical, or incompatible, fail the run before any dependent
lane starts. Do not implicitly rerun that predecessor.
- Ordinary resume without `--recompute-step` keeps its existing progressive
stage-reuse and cold-miss behavior.
### Configuration and profile types ## Stage 1: Decompose Runner And Checkpoint Orchestration
- Add `PipelineStepProfile` with `ID`, `Artifacts`, and `References` fields.
- Add `Steps []PipelineStepProfile` to `PipelineProfile`; retain top-level
`Artifacts` only as the legacy input form.
- Replace reference-map values in profile and file-config types with a
discriminated `ReferenceSource`. It has exactly one of:
- an external path, represented by the existing scalar YAML form; or
- `ArtifactReference{Step, Lane}`, represented by
`artifact: {step: ..., lane: ...}`.
- Keep CLI `--reference` values external-path overrides. They do not create or
replace generated bindings. `--reference-unbind` may remove an effective
external binding, but must not silently remove a generated dependency.
- Step-level `References` use the same source type as pipeline and target-local
references. Pipeline-level generated references are invalid because there is
no well-defined consumer step and they could imply a forward dependency.
### Resolved and prepared types
- Make `ResolvedPipeline.Steps []ResolvedPipelineStep` the sole lane container.
Each resolved step has a stable `ID`, ordered `ArtifactLanes`, and expanded
generated consumer bindings. Remove the flat `ArtifactLanes` field after all
callers migrate; provide read-only iteration/lookup helpers where callers
need all lanes.
- Resolve legacy top-level `artifacts` as one step named `default`. Do not add a
synthetic step back into effective user configuration output.
- Keep lane IDs globally unique. A producer and consumer are identified by
pipeline ID, step ID, and lane ID; module targets additionally include stage,
module key, and slot name.
- Represent external and generated bindings as distinct resolved variants.
Never encode a producer selector into a path string.
- Mirror resolved steps in `PreparedPipeline`. A prepared step contains its
already-constructed lanes and static external reference sets. Generated
content is held only in run-local state and overlaid onto request references
before a consumer step begins.
### Generated compatibility and handoff
- Extend `contracts.ReferenceSlot` with an optional cloned list of accepted
generated `ArtifactKind` values. An empty list means external references only.
Do not infer compatibility by decoding external bytes.
- A generated producer supplies exactly one accepted normalized artifact per
binding in this feature. Zero artifacts is a missing dependency; more than
one is a cardinality error. A collection such as an NPC list is one typed
artifact, not multiple reference items. Supporting aggregation or several
generated items in one slot remains out of scope.
- Use the producer kind's registered `ArtifactCodecSpec` as the canonical schema
identity and media contract. Kind compatibility plus the single registered
codec makes schema compatibility exact; also validate the consumer's media,
size, and cardinality constraints.
- Extend `contracts.ReferenceItem` with optional generated-artifact identity and
producer provenance rather than overloading file origin fields. Include kind,
schema ID/name/version/digest, producer pipeline/step/lane/module, content
digest, media type, and size. Clone byte slices and nested metadata at every
ownership boundary.
- The runtime dependency fingerprint is the canonical tuple of producer
identity, artifact kind, complete schema identity, media type, and content
digest. It must be added to every extract, merge, normalize, and validator
checkpoint dependency that receives the generated slot.
## Stage 1: Configuration, Contracts, And Resolution
### Objective ### Objective
Accept and fully validate ordered steps and structured artifact reference Create explicit, testable ownership seams for ordered-step coordination,
sources while preserving legacy single-step configuration. No runner behavior per-lane execution, and per-stage checkpoint handling without changing
changes belong in this stage. observable behavior.
### Changes ### Changes
1. Add an ADR under `docs/adr/` following the existing numbering and template. 1. Keep `Runner.Run` as the pipeline-wide coordinator. Extract a small
Record the bounded ordered-step extension, the single pipeline-wide step-coordination helper responsible only for iterating prepared steps,
input/chunk/output boundary, explicit generated bindings, barriers between building generated reference sets at each barrier, invoking the lane engine,
steps, and the rejection of a general DAG. and merging deterministic outcomes.
2. In `internal/core/config/file_config.go` and its validation/application 2. Split `runLanes` in `internal/framework/pipeline/runner_concurrent.go` into
helpers: helpers with these responsibilities:
- add file forms for ordered steps and discriminated reference sources; - initialize lane state and resolve extract checkpoint state in lane order;
- use strict YAML decoding for the mapping form and reject unknown fields, - run the bounded extract worker/continuation engine;
empty selectors, ambiguous scalar-plus-artifact values, and non-string - collect terminal lane results and choose failures deterministically; and
scalar paths; - merge lane-local output into step output.
- reject pipelines containing both `artifacts` and `steps`, empty explicit Preserve the existing bounded channels, cancellation, drain behavior,
steps, duplicate trimmed step IDs, and duplicate lane IDs across steps; chunk-first dispatch, lane ordering, and step barrier.
- preserve list order for steps and validator chains and deterministic key 3. Split `continueTypedLane` in
ordering for lane maps; and `internal/framework/pipeline/runner_typed.go` into stage-specific merge and
- update cloning, defaults application, effective-config rendering, and normalize helpers. Each helper should own dependency construction, checkpoint
redaction so selectors remain visible but external paths follow the current loading and canonical validation, execution/retry/validation when needed,
path redaction policy. checkpoint recording, debug envelopes, and its typed result. Use small
3. In `internal/framework/contracts/contracts.go`, add generated artifact-kind result structs rather than long parallel return lists.
compatibility to `ReferenceSlot` and update `CloneReferenceSlots` to deep 4. Centralize repeated checkpoint-decision flow in one pipeline helper that can
copy it. Keep existing external-slot behavior unchanged. apply forced-execution policy, validate canonical stored artifacts, record
4. In `internal/framework/pipeline/profile.go` and reference-resolution helpers: the final observable decision, and return a contextual error. Do not yet
- introduce the canonical step and reference-source types described above; change categories, reason codes, or required-predecessor semantics; Stages 2
- normalize legacy `artifacts` to resolved step `default`; and 3 will change those deliberately.
- resolve all step lanes and module/validator contracts before resolving 5. Keep stage-specific code where the data shapes genuinely differ. Do not
generated bindings, so producer codec metadata and consumer slots are introduce reflection, a generic stage state machine, or callbacks that hide
available for compatibility checks; the fixed extract/merge/normalize lifecycle.
- expand a step-scoped binding to every selected target in that step that
declares the slot, for both external and generated sources; do not treat
targets without that slot as errors;
- retain current pipeline, target-local, and CLI precedence for external
bindings, inserting step-local external bindings between pipeline defaults
and target-local bindings;
- reject every generated/external collision on an effective target slot and
every step-scoped/target-local generated collision;
- reject missing producers, same-step or forward producers, undeclared
target-local slots, non-normalized producer lanes, incompatible kinds,
schemas or media types, and selectors made ambiguous by duplicate IDs; and
- include step IDs/order, lane membership, producer selectors, expanded
consumers, and module/validator policy in canonical cloning and the
pipeline digest.
5. Migrate catalog, config validation, `--only` lane selection, checkpoint
identity construction, reference provenance discovery, and debug/effective
configuration code to iterate the resolved step model. Reject `--only` when
the source profile has explicit steps; retain its current behavior for the
implicit `default` step.
### Tests ### Tests
- Extend `internal/core/config/*_test.go` with table-driven contracts for legacy - Existing runner, checkpoint, barrier, ordering, cancellation, retry, debug,
shorthand, explicit step order, strict source-form parsing, mutual exclusion, and D&D integration tests must pass without expectation changes except moves
identity collisions, cloning, effective output, and redaction. required by renamed private test fixtures.
- Extend `internal/framework/pipeline/profile_test.go`, - Add no tests for helper boundaries or collaborator call counts. Add a narrow
`typed_resolution_test.go`, and reference tests for deterministic expansion, regression assertion only if the refactor exposes an observable behavior not
precedence/conflicts, backward/forward rules, global lane uniqueness, already protected.
generated-kind/media compatibility, canonical cloning, and digest changes.
- Add a contract test showing a legacy profile resolves to `default` with an
otherwise equivalent lane lifecycle and ordering.
- Prefer one representative case per validation owner; do not duplicate every
parser failure at the resolver layer.
### Completion Gate ### Completion Gate
Run focused config, contracts, and pipeline resolution tests, then `go test Run `go test ./internal/framework/pipeline ./internal/framework/checkpoint
./internal/core/config ./internal/framework/contracts ./internal/framework/pipeline`. ./internal/modules/integration` and the pipeline race tests. Review the diff to
The runner may still reject or lack execution support for more than one confirm this stage changes structure only: serialized output, checkpoint state,
resolved step, but all configurations and topology must resolve without a flat decision values, failure selection, and module invocation behavior must remain
parallel lane model. unchanged.
## Stage 2: Step-Aware Preparation And Execution ## Stage 2: Make Checkpoint Decisions Typed And Observable
### Objective ### Objective
Execute independent ordered steps with hard barriers, even when no generated Assign stable decision categories and reason codes explicitly, and retain the
references are configured. Preserve the existing typed lane lifecycle and one decision that causes a required-dependency failure.
bounded run-wide concurrency budget.
### Changes ### Changes
1. Refactor `internal/framework/pipeline/prepare.go` so `Prepare` walks resolved 1. In `internal/framework/pipeline/checkpoint.go`, introduce string-backed
steps in order and constructs every lane module and validator before it internal types and constants for checkpoint decision categories and reason
returns. A failure in any later step must occur before source parsing. codes. Keep the current JSON strings and public artifact fields compatible.
Materialize only external reference sources during this phase; generated Categories remain exactly `executed`, `reused`, `forced_recompute`, and
selectors carry no bytes yet. `dependency_invalidated`.
2. Refactor `runner.go` and `runner_concurrent.go` into a small ordered-step 2. Replace prose inspection in `internal/framework/checkpoint/loader.go` with an
coordinator plus the existing bounded lane engine: explicit decision constructor accepting category, reason code, and optional
- parse input and compute/reuse the chunk plan once; detail. Remove every `strings.Contains` classification branch. Assign a code
- invoke the lane engine once per step, passing only that step's prepared at the validation site using this bounded vocabulary:
lanes and immutable request-reference view; - `loading_disabled`, `checkpoint_missing`, `checkpoint_path_invalid`,
- wait for every lane in the step to become terminal before advancing; `checkpoint_read_failed`, and `checkpoint_decode_failed`;
- reuse the same worker/provider limits without permitting tasks from - `workspace_schema_incompatible` and `identity_mismatch`;
adjacent steps to overlap; and - `stage_mismatch`, `step_mismatch`, `lane_mismatch`, `module_mismatch`, and
- encode output once after all steps succeed. `status_not_reusable`;
3. Accumulate accepted, rejected, debug, and checkpoint outcomes across steps. - `dependency_mismatch` for the `dependency_invalidated` category;
Sort public results by step index, lane ID, source ID, and chunk index/ref as - `artifact_payload_invalid`, `artifact_digest_mismatch`,
applicable. Add step identity to internal errors and debug events where lane `artifact_codec_incompatible`, and `artifact_not_canonical`; and
identity alone no longer explains execution context. - `checkpoint_reused` and `accepted_artifact_reused` for successful reuse,
4. Preserve current fail-fast cancellation and bounded drain behavior. A and `recompute_step` for forced execution.
framework error in a step cancels its started work, prevents all later steps, Retain an existing code not listed here only when a current external or
and prevents output encoding while retaining completed upstream outcomes. documented contract already depends on it.
5. Until Stage 3 lands, reject execution of any pipeline with a generated 3. Keep detail human-readable, UTF-8, bounded, and sanitized through one helper.
binding before source parsing. This temporary guard prevents an accepted Detail may identify stage, step, lane, module, expected status, or schema
configuration from running a consumer without its declared dependency. version, but must not include artifact/reference content, source content,
credentials, environment values, or local paths. Tests must assert codes and
safety properties, not exact detail prose.
4. Change the centralized runner decision flow from Stage 1 so the final loader
or canonical-validation decision is recorded before returning a
required-predecessor error. The contextual error must identify the step and
lane and include the stable reason code; it must not interpolate unsafe
loader detail.
5. Propagate typed values without lossy conversion through checkpoint events,
run manifests, debug summaries, and CLI diagnostics. Convert to strings only
at existing serialized boundaries unless changing an internal field type is
simpler and wire-compatible.
6. Update the canonical current-behavior owners in the same change:
`docs/internal/state.md` owns the internal decision flow, while
`docs/operations.md` owns operator diagnosis and any operator-visible reason
code table. Link rather than duplicate the table elsewhere.
### Tests ### Tests
- Add runner tests with deterministic fake modules proving strict barriers, - Add a table in `internal/framework/checkpoint` that exercises one
concurrency within one step, reuse of the same global budget, stable output representative input per reason-code family and asserts category, code,
and failure order despite inverted completion timing, and no output encoding bounded valid UTF-8 detail, and absence of supplied secret/path sentinels.
after failure. - Add pipeline behavior tests proving a required-predecessor failure records the
- Add a preparation test proving every module and validator in every step is underlying missing, corrupt, incompatible, or dependency-invalidated
constructed before the input adapter is invoked. decision before returning.
- Retain existing single-step concurrency and cancellation tests unchanged - Retain focused manifest/debug serialization assertions for category and code;
where possible; migrate fixtures to the implicit `default` resolved step. do not add exact-detail or full-manifest snapshots.
### Completion Gate
Run `go test ./internal/framework/pipeline ./internal/cli` plus the repository's
race-test target for the framework if one is defined in `docs/development.md`.
Both legacy pipelines and explicit pipelines without generated references must
run successfully.
## Stage 3: Canonical Generated-Artifact Handoff And Provenance
### Objective
Make accepted normalized output from an earlier lane available as an immutable
operation-time reference to later consumers, with safe checkpoint dependencies
and bounded provenance.
### Changes
1. Add a domain-neutral handoff component in `internal/framework/pipeline` that:
- locates the producer's accepted normalized output after its step barrier;
- requires exactly one output and rejects missing, rejected-only, or multiple
outputs deterministically;
- canonicalizes it through `ArtifactCodecRegistry.Encode` (decoding a reused
serialized checkpoint through the registered codec first when necessary);
- verifies kind, exact codec schema identity, accepted media type, maximum
size, and one-item cardinality for every expanded consumer slot; and
- creates independently cloned `ReferenceItem` values for fan-out targets.
2. Build all generated bindings required by a consumer step before starting any
lane in that step. If any handoff fails, return one contextual dependency
error and start none of the step's consumers. An accepted typed empty list is
valid; absence of an accepted artifact is not.
3. Merge generated items with each target's already-materialized external
`ReferenceSet` only after resolution has proven there is no collision. Pass
the resulting cloned set through existing extraction, merge, normalize, and
validation request structs. Never mutate prepared static reference sets.
4. Add the canonical generated dependency fingerprint to checkpoint loader and
recorder inputs for every receiving operation immediately in this stage.
This is required before generated pipelines can safely use resume; do not
defer it to selective recomputation work.
5. Extend artifacts and runtime provenance:
- add step ID to artifact-lane, normalized-output, rejected-output, and
reference provenance records where needed for unambiguous context;
- record external and generated origins distinctly;
- for generated references record producer identities, codec schema
identity, media type, digest, and size, but never content or a fabricated
filesystem URI; and
- update generic JSON output and debug summaries to serialize these bounded
fields deterministically.
6. Ensure checkpoint, debug, and output clones own their byte slices and maps.
A consumer or test mutation must not affect another fan-out consumer or the
producer artifact.
### Tests
- Add handoff tests for one producer fan-out, empty typed collections, missing
output, rejected-only output, multiple outputs, type/schema/media/size
mismatch, target immutability, and failure before consumer start.
- Add a resume-oriented runner test proving changed canonical producer content
changes the consumer dependency fingerprint and prevents stale reuse.
- Extend artifacts/JSON/debug contract tests with field assertions for bounded
generated provenance and explicit assertions that content and local paths are
absent. Avoid whole-document snapshots.
### Completion Gate
Run focused pipeline, artifacts, JSON output, and debug tests, followed by `go
test ./internal/framework/... ./internal/core/artifacts
./internal/modules/generic/output/json`. A generic fake-codec pipeline must
complete a two-step handoff both from fresh execution and a compatible reused
producer checkpoint.
## Stage 4: Dependency-Aware Checkpoints And Selective Recompute
### Objective
Complete safe reuse, transitive invalidation, observable decisions, and the
`--recompute-step` operator control.
### Changes
1. Update checkpoint identity and manifests in
`internal/framework/checkpoint`:
- introduce workspace schema `notarius.workspace.v3` and retain explicit
recognition of v1/v2 as incompatible cold misses;
- include step ID in lane-stage manifests and lookup/recording context;
- include ordered topology in the persistent pipeline identity; and
- preserve the Stage 3 generated fingerprint tuple exactly, without content
or secrets in decision reasons.
2. When loading a normalized producer checkpoint, validate its manifest,
deserialize through the registered codec, re-encode canonically, and compare
schema/media/content fingerprints before exposing it to handoff. Corrupt,
missing, rejected, or incompatible state is a cold miss for ordinary resume
and must never reach a consumer.
3. Build a lane-level dependency index from resolved generated bindings. Use it
to invalidate only transitive consumer lanes when producer identity or bytes
change; unrelated lanes, including unrelated lanes in a later step, remain
eligible for reuse. The step barrier still applies when reused and executed
lanes coexist.
4. Add a single-value `--recompute-step <step-id>` flag in
`internal/cli/run.go`. Reject repeated occurrences, unknown steps, use
without checkpoint recording, use without `--resume`, and combination with
`--only`. Accept the stable implicit step ID `default`; selecting it forces
every lane in that single step. One selected step is sufficient for this
scope; do not add multi-selection semantics.
5. Convert the selected step to a force-execution set containing every lane in
that step plus the lane-level transitive dependency closure. Loader policy,
not persistent checkpoint identity, applies the force set. Required
predecessors and unrelated lanes remain reusable. If a required predecessor
was not selected and has no reusable accepted artifact, fail before starting
a dependent lane rather than implicitly recomputing it.
6. Replace the boolean-only checkpoint reporting model as needed with a bounded
decision category: `executed`, `reused`, `forced_recompute`, or
`dependency_invalidated`, plus a stable reason code and optional safe detail.
Propagate it to checkpoint events, manifests, debug summaries, and CLI
diagnostics. Do not make exact prose part of a test contract.
### Tests
- In checkpoint loader/recorder tests, cover v1/v2 cold misses, v3 step
identity, compatible producer decode, corruption, exact dependency match,
content/schema changes, and bounded reason fields.
- In pipeline tests, cover transitive invalidation and reuse of unrelated work.
- Add one CLI contract table for valid recomputation and every invalid flag
combination, plus one execution test proving the selected closure is forced
while a predecessor and unrelated lane are reused.
- Do not assert internal loader call counts when observable decision records
and outputs establish the behavior.
### Completion Gate ### Completion Gate
Run `go test ./internal/framework/checkpoint ./internal/framework/pipeline Run `go test ./internal/framework/checkpoint ./internal/framework/pipeline
./internal/cli`, then checkpoint/debug state tests under the race detector as ./internal/core/artifacts ./internal/core/debugbundle ./internal/cli`. Search the
directed by `docs/development.md`. Manually inspect one test fixture's decision production checkpoint package to confirm no decision category or reason code is
records to confirm they contain no reference bytes, secret values, or local derived from diagnostic prose.
paths.
## Stage 5: D&D NPC-First Production Adoption ## Stage 3: Hydrate Required Predecessors From Accepted Normalized Artifacts
### Objective ### Objective
Adopt the platform in the production D&D composition: NPC normalization runs Make selective recomputation enforce the lane-level accepted-artifact contract:
first, and its canonical artifact grounds spell extraction, combat-turn a valid normalized producer artifact is sufficient even when its extract or
extraction, and combat-turn normalization at operation time. merge stage cache is unavailable.
### Changes ### Changes
1. Declare the normalized NPC-list artifact kind on every `npcs` consumer slot: 1. Extend the checkpoint loader contract with a dedicated accepted-normalized-
- `internal/modules/dnd/extract/spells`; artifact lookup. Implement it in the filesystem loader and every no-op or
- `internal/modules/dnd/extract/combatturns`; and test implementation. The lookup receives step ID, lane ID, and normalizer
- `internal/modules/dnd/normalize/combatturns`. module key and returns the normalized artifact, its warnings, and an explicit
Keep the slot optional for external standalone use; a configured generated checkpoint decision. It must not require caller-supplied extract or merge
binding becomes required through the framework dependency. dependency fingerprints.
2. Refactor `internal/modules/dnd/npcs/registry` to provide a package-owned, 2. The filesystem lookup may reuse the existing normalize manifest and payload;
concurrency-safe operation-time resolver/cache: do not add a second persistent copy. It is reusable only when all of the
- validate and seed construction-time external references so bad static following hold:
configuration still fails before source parsing; - workspace schema is v3 and the non-empty stored checkpoint identity matches
- resolve the effective `npcs` item from each operation request; the current invocation identity;
- reuse the seeded immutable registry when its digest matches and cache a - stage, step, lane, normalizer module, and successful status match;
generated registry by canonical digest so concurrent chunk operations do - the manifest output digest matches the payload; and
not repeatedly decode it; and - the payload can subsequently be validated through the registered artifact
- return cloned or immutable views and never retain caller-owned content. codec.
3. Change the spell extractor, combat-turn extractor, and combat-turn Skipping caller-supplied merge dependencies is safe only because the matched
normalizer to obtain the NPC registry from `req.References` for each non-empty checkpoint identity already binds the current input, resolved
operation through that resolver. Do not reconstruct modules at a step topology and configuration, external references, runtime overrides, LLM
boundary. Retain current behavior when the request has no NPC item. profiles, and component semantic fingerprints. Do not weaken or omit that
4. Keep static prompt/schema metadata and construction-time external reference identity check.
fingerprints intact. Record generated NPC identity through framework A loader lacking a verifiable current identity must return an unavailable
reference provenance and dependency fingerprints; do not place decision rather than perform accepted-artifact reuse.
operation-varying generated digests into singleton module metadata. 3. Add a pipeline hydration helper that decodes the returned serialized
5. Preserve prompt ordering and evidence policy. The generated NPC listing is artifact through the producer's registered codec, re-encodes it canonically,
stable contextual input before the variable transcript, but it cannot prove and requires exact artifact kind, schema ID/name/version/digest, media type,
that a spell or combat event occurred. Source-unit citations remain the only content bytes, and content digest. Return a runner-owned clone with producer
event evidence. step, lane, module, and source identity. Any mismatch is an explicit bounded
6. Add a maintained explicit two-step D&D configuration under `examples/` and a decision and the bytes never reach a consumer.
matching production-catalog fixture under 4. Before normal execution of a lane marked in `RequireReusableLanes`, use the
`internal/modules/integration/testdata/`. Replace or supplement the two accepted-artifact lookup:
manual NPC-to-spell and NPC-to-combat configurations with this single - on success, mark the lane terminal without invoking extract, merge,
NPC-first workflow; retain standalone examples that demonstrate external normalize, or their validators;
NPC references where they remain useful. - append the accepted normalized output in the normal deterministic location
7. Add one offline integration test using scripted/fake LLM responses. Assert and restore only the warnings stored with that normalized checkpoint;
NPC normalization completes first, the exact canonical NPC digest reaches - record one `reused` normalize decision with a stable accepted-output reuse
all three consumers, spell and combat lanes may both succeed, combat reason code; do not synthesize extract/merge decisions, warnings, or
normalization uses the registry, and generated NPC context is not accepted rejections that were not loaded; and
as source evidence by itself. - allow the ordinary step barrier and generated handoff code to consume that
output exactly as it consumes a freshly executed output.
5. On missing, rejected, corrupt, non-canonical, or incompatible accepted state,
record the decision and fail the run before the dependent step begins. Do
not fall back to stage execution. Forced lanes must bypass this hydration
path and execute normally.
6. Leave ordinary resume unchanged when no lane is marked
`RequireReusableLanes`: it may reuse or recompute extract, merge, and
normalize progressively under the existing cold-miss rules.
7. Keep generated-reference fingerprints and provenance unchanged. Hydrating a
byte-identical producer must yield the same canonical handoff digest and
downstream dependency fingerprint as fresh execution.
8. Update `docs/internal/pipeline.md`, `docs/internal/state.md`, and
`docs/operations.md` in this stage to distinguish progressive stage reuse
from accepted normalized-artifact hydration and to document the fail-rather-
than-rerun rule for invalid required predecessors.
### Tests ### Tests
- Add focused registry and module tests for static fallback, generated override, - At the pipeline/checkpoint boundary, create a valid producer normalize
concurrent cache safety, malformed runtime content, and ownership isolation. checkpoint while omitting or corrupting its extract and merge checkpoints.
- Extend prompt-input tests only for semantic placement and content. Do not add Select a later step for recomputation and prove the producer hydrates, no
exact prompt-length or message-count change detectors. producer operation or validator runs, and the dependent receives the exact
- Run the single end-to-end offline D&D handoff test rather than duplicating it canonical artifact.
separately for every consumer. - Cover missing, rejected-status, corrupt, non-canonical, wrong-codec-identity,
and wrong-content-digest normalize state. Assert failure and the recorded
stable decision before any consumer invocation.
- Prove forced producers execute instead of hydrating, while a reusable
predecessor and an unrelated lane remain reusable.
- Prove fresh and hydrated producer outputs create identical generated
provenance and downstream checkpoint fingerprints without exposing content
or paths.
### Completion Gate ### Completion Gate
Run all D&D package tests and integration tests, including the race detector for Run `go test ./internal/framework/checkpoint ./internal/framework/pipeline
the registry cache, then parse and resolve every maintained example through the ./internal/modules/integration` and the corresponding race tests. Manually
production catalog. No test may require credentials or a live LLM provider. inspect one failed test fixture to confirm the accepted artifact remains on
disk but its bytes do not appear in the manifest, decision detail, debug
summary, or error.
## Stage 6: Current Documentation And Release Verification ## Stage 4: Complete Recompute CLI And Recovery Acceptance Coverage
### Objective ### Objective
Document the now-implemented behavior in its canonical current owners, retire Exercise the operator-visible recomputation contract through stable boundaries
obsolete manual workflow guidance, and perform repository-wide verification. and close the acceptance-test omissions from the original plan.
### Changes ### Changes
1. Update `docs/config.md` with version-3 `steps`, legacy `artifacts` 1. Add one table-driven CLI contract test for `--recompute-step` covering:
compatibility, reference source forms, precedence/conflict rules, global - a valid explicit step and the implicit `default` step;
identity rules, and the exact D&D two-step example. - repeated flags and an empty or unknown step ID;
2. Update `docs/cli.md` with `--recompute-step`, prerequisites, closure - use without `--resume`;
semantics, invalid combinations, and checkpoint decision categories. - use when checkpoint recording is disabled; and
3. Update `docs/internal/pipeline.md`, `docs/internal/state.md`, and - combination with `--only`.
`docs/internal/overview.md` with the fixed ordered-step model, preparation Assert exit classification and stable identifying fragments or reason codes,
timing, handoff boundary, codec use, provenance, and checkpoint dependency not complete prose.
behavior. Keep the architecture description linear and explicitly state that 2. Add one filesystem-backed CLI execution test using deterministic fake
this is not a general DAG. modules and a three-step generated dependency chain plus one unrelated lane:
4. Update `docs/operations.md` with resume/recompute procedures and safe failure - perform a fresh checkpointed run;
diagnosis. Update the D&D integration documents for generated NPC context, - remove or corrupt only the required producer's extract and merge state
evidence limitations, and the maintained workflow. Update inside `t.TempDir()`, leaving its normalize artifact valid;
`docs/policy/testing.md` only if the implementation exposes a genuinely new - resume with the middle step selected;
durable testing policy; do not restate feature-specific tests there. - assert the selected lane and transitive dependents execute, the predecessor
5. Update maintained example checks and any README/index links that point to hydrates without module calls, the unrelated lane reuses, and output and
replaced sequential examples. Keep the feature roadmap as historical target decision ordering are deterministic; and
state until the project applies its normal roadmap-completion process; do not - invalidate the producer normalize state in a subcase and assert the command
turn it into a duplicate current-behavior manual. fails before dependent execution with the bounded decision preserved.
6. Review changed exported identifiers and package comments, run formatting and 3. Add or extend one generic two-step pipeline test proving handoff succeeds
static analysis, and remove obsolete flat-lane compatibility helpers, both from fresh producer execution and from accepted normalized-artifact
temporary test adapters, dead sequential-workflow code, and TODOs introduced hydration. Keep this at the pipeline boundary if the CLI execution test
during earlier stages. already proves flag wiring; do not duplicate every CLI case end to end.
4. Review existing recomputation policy tests. Retain the pure closure test
### Verification because it protects transitive selection, but remove or consolidate any new
test that merely repeats the CLI or pipeline behavior above.
Run the repository-prescribed commands from `docs/development.md`, including: 5. Make only production changes revealed as necessary by these contract tests;
do not add new flag semantics or broaden the feature roadmap.
1. all unit and integration tests;
2. race tests for concurrency-sensitive framework, checkpoint, and D&D registry
packages;
3. `go vet` and the normal build;
4. maintained-example/config validation; and
5. documentation checks.
Perform one final acceptance review against every bullet in
[Ordered Pipeline Steps](ordered-pipeline-steps.md). Inspect a successful fresh
run, a resumed run, a forced-recompute run, and a missing-producer failure for
deterministic ordering, correct dependency decisions, bounded provenance, and
absence of generated content or secrets in manifests/debug state.
### Completion Gate ### Completion Gate
The feature is complete only when every acceptance criterion in the feature Run `go test ./internal/cli ./internal/framework/pipeline
roadmap is demonstrated by a stable test or maintained example and the full ./internal/framework/checkpoint`, then run the CLI, pipeline, and checkpoint
repository verification suite passes. packages under the race detector. Confirm no test asserts internal loader call
counts, exact decision detail, filesystem layout outside a temp workspace, or
the exact length of any prompt or prefix.
## Stage 5: Current Documentation And Release Verification
### Objective
Reconcile all canonical documentation after the staged changes and verify the
complete feature against policy and roadmap.
### Changes
1. Re-read `docs/internal/pipeline.md`, `docs/internal/state.md`,
`docs/operations.md`, and `docs/cli.md` against the final code. Correct any
stale statements left by Stages 1 through 4 without duplicating their
canonical contracts.
2. Ensure internal documentation describes the coordinator, lane engine, stage
checkpoint flow, and accepted-artifact validation at the responsibility
level without listing volatile private helper names.
3. Confirm the operator-visible reason-code table has one canonical owner and
other documents link to it rather than maintaining parallel copies.
4. Remove stale implementation claims exposed by this work and ensure the
feature roadmap continues to describe target policy rather than task
sequencing.
### Verification
Run the repository-prescribed commands from `docs/development.md`:
```sh
go test ./...
go vet ./...
go build ./cmd/notarius
```
Also run race tests for the pipeline, checkpoint, CLI state, D&D NPC registry,
and D&D integration packages. Validate maintained examples/configurations
through the production catalog. Review one fresh run, ordinary resumed run,
forced-recompute run, hydrated-predecessor run, and invalid-predecessor failure
for deterministic ordering, correct decisions, bounded provenance, and absence
of generated content, secrets, or local paths in manifests and diagnostics.
### Completion Gate
The follow-up is complete only when every finding summarized above is protected
by a stable behavioral test, the current documentation matches the corrected
implementation, the full verification suite passes, and the worktree contains
no temporary adapters or TODOs introduced by these stages.
## Open Questions ## Open Questions
None. The feature roadmap and this plan fix all product and architecture choices None. The feature roadmap and this plan fix the required product and
required for implementation. If an implementation detail conflicts with a architecture choices. If implementation reveals that accepted normalized
policy document, the policy document takes precedence; if it would change the artifacts cannot be validated safely without a persistent format change, stop
product semantics above, stop and amend the roadmap rather than deciding it in and amend this plan rather than weakening identity, codec, or content
code. validation.