400 lines
20 KiB
Markdown
400 lines
20 KiB
Markdown
# Implementation Plan: Ordered Pipeline Follow-Up
|
|
|
|
## Status
|
|
|
|
Completed on 2026-07-22. This document retains the implementation sequence for
|
|
historical context; current contracts are maintained in the canonical CLI,
|
|
configuration, operations, integration, and internal documentation linked from
|
|
the [development guide](../development.md).
|
|
|
|
## Purpose
|
|
|
|
Record the work that closed the correctness, observability, test, and
|
|
maintainability gaps in the implemented
|
|
[Ordered Pipeline Steps](ordered-pipeline-steps.md) feature. That feature
|
|
roadmap remains the authority for product intent, policy choices, acceptance
|
|
criteria, and exclusions. This document preserves the ordered,
|
|
decision-complete implementation sequence for the follow-up work.
|
|
|
|
## Background
|
|
|
|
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 completed follow-up addressed these narrower gaps:
|
|
|
|
- selective recomputation now hydrates an unselected predecessor from its
|
|
accepted normalized artifact without requiring its extract and merge state;
|
|
- the failed checkpoint decision is recorded before a required-predecessor
|
|
error returns;
|
|
- checkpoint reason codes are assigned explicitly rather than inferred from
|
|
human-readable prose;
|
|
- runner lane and checkpoint orchestration has explicit responsibility seams;
|
|
and
|
|
- CLI and resumed-producer acceptance coverage exercises the complete recovery
|
|
contract.
|
|
|
|
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
|
|
|
|
Before changing code, read `docs/development.md`, all documents under
|
|
`docs/policy/`, and the task-specific internal documents named there. Use the
|
|
repository's code knowledge graph for code discovery before falling back to
|
|
text search.
|
|
|
|
Implement the stages in order. Each stage must leave the repository formatted,
|
|
building, and passing its focused tests. Preserve these invariants throughout:
|
|
|
|
- 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.
|
|
|
|
## Target Checkpoint Semantics
|
|
|
|
Use these definitions consistently in all stages:
|
|
|
|
- A **stage checkpoint** is internal resumable state for extract, merge, or
|
|
normalize. Ordinary resume may continue to reuse this state progressively.
|
|
- 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.
|
|
|
|
## Stage 1: Decompose Runner And Checkpoint Orchestration
|
|
|
|
### Objective
|
|
|
|
Create explicit, testable ownership seams for ordered-step coordination,
|
|
per-lane execution, and per-stage checkpoint handling without changing
|
|
observable behavior.
|
|
|
|
### Changes
|
|
|
|
1. Keep `Runner.Run` as the pipeline-wide coordinator. Extract a small
|
|
step-coordination helper responsible only for iterating prepared steps,
|
|
building generated reference sets at each barrier, invoking the lane engine,
|
|
and merging deterministic outcomes.
|
|
2. Split `runLanes` in `internal/framework/pipeline/runner_concurrent.go` into
|
|
helpers with these responsibilities:
|
|
- initialize lane state and resolve extract checkpoint state in lane order;
|
|
- run the bounded extract worker/continuation engine;
|
|
- collect terminal lane results and choose failures deterministically; and
|
|
- merge lane-local output into step output.
|
|
Preserve the existing bounded channels, cancellation, drain behavior,
|
|
chunk-first dispatch, lane ordering, and step barrier.
|
|
3. Split `continueTypedLane` in
|
|
`internal/framework/pipeline/runner_typed.go` into stage-specific merge and
|
|
normalize helpers. Each helper should own dependency construction, checkpoint
|
|
loading and canonical validation, execution/retry/validation when needed,
|
|
checkpoint recording, debug envelopes, and its typed result. Use small
|
|
result structs rather than long parallel return lists.
|
|
4. Centralize repeated checkpoint-decision flow in one pipeline helper that can
|
|
apply forced-execution policy, validate canonical stored artifacts, record
|
|
the final observable decision, and return a contextual error. Do not yet
|
|
change categories, reason codes, or required-predecessor semantics; Stages 2
|
|
and 3 will change those deliberately.
|
|
5. Keep stage-specific code where the data shapes genuinely differ. Do not
|
|
introduce reflection, a generic stage state machine, or callbacks that hide
|
|
the fixed extract/merge/normalize lifecycle.
|
|
|
|
### Tests
|
|
|
|
- Existing runner, checkpoint, barrier, ordering, cancellation, retry, debug,
|
|
and D&D integration tests must pass without expectation changes except moves
|
|
required by renamed private test fixtures.
|
|
- Add no tests for helper boundaries or collaborator call counts. Add a narrow
|
|
regression assertion only if the refactor exposes an observable behavior not
|
|
already protected.
|
|
|
|
### Completion Gate
|
|
|
|
Run `go test ./internal/framework/pipeline ./internal/framework/checkpoint
|
|
./internal/modules/integration` and the pipeline race tests. Review the diff to
|
|
confirm this stage changes structure only: serialized output, checkpoint state,
|
|
decision values, failure selection, and module invocation behavior must remain
|
|
unchanged.
|
|
|
|
## Stage 2: Make Checkpoint Decisions Typed And Observable
|
|
|
|
### Objective
|
|
|
|
Assign stable decision categories and reason codes explicitly, and retain the
|
|
decision that causes a required-dependency failure.
|
|
|
|
### Changes
|
|
|
|
1. In `internal/framework/pipeline/checkpoint.go`, introduce string-backed
|
|
internal types and constants for checkpoint decision categories and reason
|
|
codes. Keep the current JSON strings and public artifact fields compatible.
|
|
Categories remain exactly `executed`, `reused`, `forced_recompute`, and
|
|
`dependency_invalidated`.
|
|
2. Replace prose inspection in `internal/framework/checkpoint/loader.go` with an
|
|
explicit decision constructor accepting category, reason code, and optional
|
|
detail. Remove every `strings.Contains` classification branch. Assign a code
|
|
at the validation site using this bounded vocabulary:
|
|
- `loading_disabled`, `checkpoint_missing`, `checkpoint_path_invalid`,
|
|
`checkpoint_read_failed`, and `checkpoint_decode_failed`;
|
|
- `workspace_schema_incompatible` and `identity_mismatch`;
|
|
- `stage_mismatch`, `step_mismatch`, `lane_mismatch`, `module_mismatch`, and
|
|
`status_not_reusable`;
|
|
- `dependency_mismatch` for the `dependency_invalidated` category;
|
|
- `artifact_payload_invalid`, `artifact_digest_mismatch`,
|
|
`artifact_codec_incompatible`, and `artifact_not_canonical`; and
|
|
- `checkpoint_reused` and `accepted_artifact_reused` for successful reuse,
|
|
and `recompute_step` for forced execution.
|
|
Retain an existing code not listed here only when a current external or
|
|
documented contract already depends on it.
|
|
3. Keep detail human-readable, UTF-8, bounded, and sanitized through one helper.
|
|
Detail may identify stage, step, lane, module, expected status, or schema
|
|
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
|
|
|
|
- Add a table in `internal/framework/checkpoint` that exercises one
|
|
representative input per reason-code family and asserts category, code,
|
|
bounded valid UTF-8 detail, and absence of supplied secret/path sentinels.
|
|
- Add pipeline behavior tests proving a required-predecessor failure records the
|
|
underlying missing, corrupt, incompatible, or dependency-invalidated
|
|
decision before returning.
|
|
- Retain focused manifest/debug serialization assertions for category and code;
|
|
do not add exact-detail or full-manifest snapshots.
|
|
|
|
### Completion Gate
|
|
|
|
Run `go test ./internal/framework/checkpoint ./internal/framework/pipeline
|
|
./internal/core/artifacts ./internal/core/debugbundle ./internal/cli`. Search the
|
|
production checkpoint package to confirm no decision category or reason code is
|
|
derived from diagnostic prose.
|
|
|
|
## Stage 3: Hydrate Required Predecessors From Accepted Normalized Artifacts
|
|
|
|
### Objective
|
|
|
|
Make selective recomputation enforce the lane-level accepted-artifact contract:
|
|
a valid normalized producer artifact is sufficient even when its extract or
|
|
merge stage cache is unavailable.
|
|
|
|
### Changes
|
|
|
|
1. Extend the checkpoint loader contract with a dedicated accepted-normalized-
|
|
artifact lookup. Implement it in the filesystem loader and every no-op or
|
|
test implementation. The lookup receives step ID, lane ID, and normalizer
|
|
module key and returns the normalized artifact, its warnings, and an explicit
|
|
checkpoint decision. It must not require caller-supplied extract or merge
|
|
dependency fingerprints.
|
|
2. The filesystem lookup may reuse the existing normalize manifest and payload;
|
|
do not add a second persistent copy. It is reusable only when all of the
|
|
following hold:
|
|
- workspace schema is v3 and the non-empty stored checkpoint identity matches
|
|
the current invocation identity;
|
|
- stage, step, lane, normalizer module, and successful status match;
|
|
- the manifest output digest matches the payload; and
|
|
- the payload can subsequently be validated through the registered artifact
|
|
codec.
|
|
Skipping caller-supplied merge dependencies is safe only because the matched
|
|
non-empty checkpoint identity already binds the current input, resolved
|
|
topology and configuration, external references, runtime overrides, LLM
|
|
profiles, and component semantic fingerprints. Do not weaken or omit that
|
|
identity check.
|
|
A loader lacking a verifiable current identity must return an unavailable
|
|
decision rather than perform accepted-artifact reuse.
|
|
3. Add a pipeline hydration helper that decodes the returned serialized
|
|
artifact through the producer's registered codec, re-encodes it canonically,
|
|
and requires exact artifact kind, schema ID/name/version/digest, media type,
|
|
content bytes, and content digest. Return a runner-owned clone with producer
|
|
step, lane, module, and source identity. Any mismatch is an explicit bounded
|
|
decision and the bytes never reach a consumer.
|
|
4. Before normal execution of a lane marked in `RequireReusableLanes`, use the
|
|
accepted-artifact lookup:
|
|
- on success, mark the lane terminal without invoking extract, merge,
|
|
normalize, or their validators;
|
|
- append the accepted normalized output in the normal deterministic location
|
|
and restore only the warnings stored with that normalized checkpoint;
|
|
- record one `reused` normalize decision with a stable accepted-output reuse
|
|
reason code; do not synthesize extract/merge decisions, warnings, or
|
|
rejections that were not loaded; and
|
|
- 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
|
|
|
|
- At the pipeline/checkpoint boundary, create a valid producer normalize
|
|
checkpoint while omitting or corrupting its extract and merge checkpoints.
|
|
Select a later step for recomputation and prove the producer hydrates, no
|
|
producer operation or validator runs, and the dependent receives the exact
|
|
canonical artifact.
|
|
- 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
|
|
|
|
Run `go test ./internal/framework/checkpoint ./internal/framework/pipeline
|
|
./internal/modules/integration` and the corresponding race tests. Manually
|
|
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 4: Complete Recompute CLI And Recovery Acceptance Coverage
|
|
|
|
### Objective
|
|
|
|
Exercise the operator-visible recomputation contract through stable boundaries
|
|
and close the acceptance-test omissions from the original plan.
|
|
|
|
### Changes
|
|
|
|
1. Add one table-driven CLI contract test for `--recompute-step` covering:
|
|
- a valid explicit step and the implicit `default` step;
|
|
- repeated flags and an empty or unknown step ID;
|
|
- use without `--resume`;
|
|
- use when checkpoint recording is disabled; and
|
|
- combination with `--only`.
|
|
Assert exit classification and stable identifying fragments or reason codes,
|
|
not complete prose.
|
|
2. Add one filesystem-backed CLI execution test using deterministic fake
|
|
modules and a three-step generated dependency chain plus one unrelated lane:
|
|
- perform a fresh checkpointed run;
|
|
- remove or corrupt only the required producer's extract and merge state
|
|
inside `t.TempDir()`, leaving its normalize artifact valid;
|
|
- resume with the middle step selected;
|
|
- assert the selected lane and transitive dependents execute, the predecessor
|
|
hydrates without module calls, the unrelated lane reuses, and output and
|
|
decision ordering are deterministic; and
|
|
- invalidate the producer normalize state in a subcase and assert the command
|
|
fails before dependent execution with the bounded decision preserved.
|
|
3. Add or extend one generic two-step pipeline test proving handoff succeeds
|
|
both from fresh producer execution and from accepted normalized-artifact
|
|
hydration. Keep this at the pipeline boundary if the CLI execution test
|
|
already proves flag wiring; do not duplicate every CLI case end to end.
|
|
4. Review existing recomputation policy tests. Retain the pure closure test
|
|
because it protects transitive selection, but remove or consolidate any new
|
|
test that merely repeats the CLI or pipeline behavior above.
|
|
5. Make only production changes revealed as necessary by these contract tests;
|
|
do not add new flag semantics or broaden the feature roadmap.
|
|
|
|
### Completion Gate
|
|
|
|
Run `go test ./internal/cli ./internal/framework/pipeline
|
|
./internal/framework/checkpoint`, then run the CLI, pipeline, and checkpoint
|
|
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
|
|
|
|
None. The feature roadmap and this plan fix the required product and
|
|
architecture choices. If implementation reveals that accepted normalized
|
|
artifacts cannot be validated safely without a persistent format change, stop
|
|
and amend this plan rather than weakening identity, codec, or content
|
|
validation.
|