Prepare version 0.7.0 and retire completed configuration plans

This commit is contained in:
2026-09-14 00:54:22 +00:00
parent c02eec74dd
commit 4e77725b24
4 changed files with 88 additions and 1586 deletions

88
docs/releases/v0.7.0.md Normal file
View File

@@ -0,0 +1,88 @@
# Notarius v0.7.0
This release simplifies configuration management with ConfigKit and makes
cache access permissions a deployment responsibility.
## Summary
Notarius adopts ConfigKit v0.3.0 for strict configuration loading, imports,
configuration profiles, provenance, and redacted inspection. Operators can
compose whole pipeline definitions from separate files and select development
overlays without duplicating the base configuration.
Configured relative filesystem paths now resolve against their declaring
files, including imported definitions and profile replacements. The effective
configuration and its explanation reflect the paths used at runtime.
## Compatibility
- Configuration version 5 replaces version 4; there is no compatibility loader.
Numeric `version: 5` is the canonical spelling; quoted `"5"` also works.
- Module and validator bindings require objects with `module:`; external
references require objects with `path:`. Lane-level reference aliases are
removed; use binding-local references instead.
- Strict ConfigKit YAML parsing rejects aliases, merge keys, and unsupported
scalar forms. See the [configuration reference](../config.md) for the
complete contract and migration guidance.
- The seven operational environment overrides are removed:
`NOTARIUS_TOTAL_LLM_CONCURRENCY`, `NOTARIUS_STAGE_WORKERS_EXTRACT`,
`NOTARIUS_OUTPUT_DIR`, `NOTARIUS_CACHE_CHUNK_PLANS_MODE`,
`NOTARIUS_CACHE_CHUNK_PLANS_DIR`, `NOTARIUS_CACHE_CHECKPOINTS_DIR`, and
`NOTARIUS_DEBUG_DIR`. `NOTARIUS_CONFIG` still selects the configuration
file; PromptKit credential environment variables remain supported.
- Configured relative reference, PromptKit profile, output, cache, and debug
paths use the canonical declaring file's directory. Built-in relative
defaults use the main configuration directory. CLI-relative paths still use
the invocation directory; absolute paths are unchanged.
- Requested debug bundles now contain `effective-config.yml` instead of
`effective-config.json`. Inspection output is redacted and is not promised
to be reloadable configuration.
- Chunk-plan and checkpoint cache writers respect deployment permissions
through ordinary creation modes, umask, setgid inheritance, and default ACLs;
they no longer impose private permissions. Existing entries are not chmodded
or migrated. Output and debug access contracts are unchanged.
- Durable extraction artifact schemas and subprocess receipt formats are
unchanged by this release.
## Upgrade
1. Migrate deployed configurations to version 5 using the
[configuration migration guide](../config.md#version-5-migration). Expand
shorthand bindings and references, and move retired environment settings
into YAML or supported CLI overrides.
2. Review relative paths, particularly output/cache/debug directories and
PromptKit profile sources. Use absolute paths when preserving an existing
deployment location is important.
3. Review deployment access settings for cache directories. Provision umask,
groups, and ACLs as appropriate; Notarius does not choose the installation's
access policy. See [Operations](../operations.md).
4. Use `config validate --config <path> --pipeline <id>` for each pipeline,
including selected configuration profiles. Inspect resolved settings with
`config effective` and their provenance with `config explain`; see the
[CLI reference](../cli.md).
5. Update any debug-bundle tooling that consumes the former effective JSON
file. Subprocess callers may optionally select an overlay through
`--config-profile`; this is separate from PromptKit's `--llm-profile`.
The maintained [minimal](../../examples/dnd-minimal.config.yml) and
[complete](../../examples/dnd-complete.config.yml) D&D examples demonstrate
self-contained configuration and composed pipelines with a development profile.
Build from the source tag using the [source-release procedure](../release.md).
## Changes
- Replaced custom YAML parsing and environment-overlay machinery with a
ConfigKit-backed typed document boundary and detached runtime conversion.
- Added imports, configuration profiles, source-aware normalization, safe
effective/explanation commands, and source-file legends for load errors.
- Preserved explicit zeroes and empty validator overrides at supported stages,
rejected invalid reference branches before conversion, and retained
whitespace normalization for worker identifiers.
- Corrected normalization recording for ordered steps and validator sequences,
with regression coverage for runtime values and provenance.
- Accepted ConfigKit's integer representation at module-option boundaries
through checked shared conversion.
- Updated examples, configuration and subprocess guidance, and debug rendering.
- Reworked cache creation to respect deployment-selected permissions while
retaining confined paths, symlink rejection, atomic publication, validation,
and recovery.

View File

@@ -1,634 +0,0 @@
# ConfigKit Adoption
## Purpose
Replace Notarius's application-owned YAML parsing, presence bookkeeping,
default application, and configuration-inspection machinery with ConfigKit,
while preserving Notarius ownership of pipeline semantics, module catalogs,
runtime resolution, and invocation behavior.
This roadmap targets ConfigKit v0.3.0. Its released source-origin APIs support
source-aware normalization within one load, removing the prior upstream
dependency gate. Ordered implementation work belongs in
[the implementation plan](implementation.md).
### Released Source Metadata Support
Use `Resolution.InputInfo(path) (FieldInfo, bool)` to query the fixed decoded
entry state, and `Resolution.Source(id) (SourceInfo, bool)` to retrieve an
already-acquired source's canonical file path. These callback-scoped queries
return detached records and preserve original input attribution despite
consumer normalization or map-key moves. Notarius records resolved values
through the existing normalization API before ConfigKit freezes inspection
output; no post-load path conversion or second load is needed.
The v0.3.0 public-facade refactor preserves consumer signatures and introduces
no additional migration. Use only the root public package. Go 1.25 remains
supported; the private YAML v4 prerelease dependency is unchanged. ConfigKit's
macOS support is now best-effort, consistent with Notarius's platform policy.
No application platform expansion or new release gate is required.
## Motivation
The current configuration boundary under `internal/core/config` works, but it
reimplements a substantial general-purpose configuration system. In
particular, Notarius currently owns:
- strict YAML setup, duplicate-key and trailing-document checks;
- custom YAML decoders for module bindings, references, pipelines, steps, and
lanes;
- pointer and private-flag bookkeeping to distinguish omitted values from
explicit zero, false, empty, or null values;
- a large file-to-runtime application function that combines normalization,
validation, conversion, and default derivation;
- operational environment overlays and their separate validation path; and
- bespoke cloning and redaction for effective configuration summaries.
ConfigKit already provides strict single-document YAML parsing, typed decode,
input presence, typed defaults, recorded normalization and derived defaults,
semantic diagnostics with source locations, detached results, redacted
effective rendering, and value explanation. Adopting those facilities should
reduce application-specific code and make configuration failures easier for an
operator to understand without moving domain policy into the dependency.
This is a configuration-contract migration, not a mechanical parser swap.
ConfigKit deliberately rejects several conveniences supported by version 4,
and its dynamic integer representation differs from `gopkg.in/yaml.v3`.
## Target End State
After this work:
- every command uses one ConfigKit-backed loading path;
- Notarius configuration files declare schema version 5 using the existing
integer YAML convention;
- the file schema uses canonical object forms rather than YAML type-dependent
shorthand;
- operators can import whole pipeline definitions and select a configuration
profile for development, testing, or deployment;
- defaults, surviving-input presence, normalization, derived values, and
configuration-only validation are expressed through one ConfigKit
`Definition`;
- ConfigKit diagnostics are rendered safely with semantic paths and available
source locations at the CLI boundary;
- the successful load result retains ConfigKit metadata for safe effective
configuration rendering and explanation;
- dynamic module-option integers have one documented `int64` configuration
representation and are range-checked when converted to runtime `int` values;
- pipeline and module resolution remains in Notarius and still fails before
module preparation when a configured composition is invalid;
- configuration discovery and invocation-only CLI state remain application
responsibilities;
- `NOTARIUS_CONFIG` remains a configuration-discovery input, while the seven
field-level operational environment overlays are retired;
- read-only CLI commands expose ConfigKit's redacted effective configuration
and per-path provenance explanations; and
- the direct `gopkg.in/yaml.v3` dependency and the superseded custom parsing,
presence, environment-overlay, and configuration-redaction code are removed.
The migration must preserve the fixed pipeline architecture, deterministic
resolved-pipeline identity, detached ownership, fail-before-execution checks,
secret exclusion, source-relative reference behavior, and offline validation
requirements defined by the architecture and current integration contracts.
## Ownership Boundaries
### ConfigKit owns
- reading and bounding the selected YAML source, its declared imports, and the
selected configuration profile after Notarius chooses them;
- composing imported definitions and applying the selected profile overlay;
- enforcing a single strict YAML document, supported scalar forms, unique
mapping keys, and known typed fields;
- decoding into the application-defined configuration schema;
- distinguishing omitted input from explicit zero, false, empty, and null;
- applying typed application defaults;
- recording application normalization and derived defaults;
- attaching application validation diagnostics to semantic paths and source
locations;
- detaching the successful configuration and its metadata;
- rendering the redacted effective configuration; and
- explaining the provenance of an effective or historical configuration path.
### Notarius continues to own
- configuration-file discovery through `--config`, `NOTARIUS_CONFIG`, and the
installed default path;
- the version policy and the application configuration structs;
- the meaning and validation of every Notarius field;
- pipeline, module, validator, artifact, and reference catalogs;
- conversion from configuration-facing values to runtime pipeline types;
- source-relative interpretation of configured filesystem paths;
- run-time selectors and actions such as lane selection, input, resume,
recomputation, session identity, and CLI reference changes;
- PromptKit profile discovery and validation; and
- redaction of resolved runtime structures that are outside ConfigKit's typed
configuration snapshot, including exclusion of materialized reference
content.
ConfigKit configuration profiles must not be confused with PromptKit LLM
profiles or Notarius named pipeline definitions. ConfigKit does not become a
module registry, pipeline resolver, command framework, or secret manager for
PromptKit credentials.
## Configuration Contract Version 5
### Version spelling
Use the following root declaration:
```yaml
version: 5
```
ConfigKit v0.3.0 accepts string or strict decimal-integer input at the exact
path selected by its `VersionRule`. It passes the original spelling to the
application checker and can decode an accepted integer into a string-backed Go
field. This narrow exception lets Notarius retain its existing numeric YAML
convention without weakening strict decoding elsewhere. Quoted `version: "5"`
is also accepted by ConfigKit, but the maintained Notarius spelling is the
unquoted integer shown above.
The version checker must reject absent, null, and unsupported identifiers with
safe, value-free diagnostics at `/version`. Version 5 is a clean break: the
loader must not decode or rewrite a version 4 document and must not retain the
legacy loader as a fallback.
### Canonical module bindings
Every module and validator binding must use an object with a `module` field.
For example:
```yaml
input:
module: seriatim
extract:
module: dnd/spells
merge:
module: appendorder
normalize:
module: dnd/spells
```
Version 5 does not accept scalar binding shorthand. Preserving shorthand would
require a dynamic subtree or a compatibility pre-parser and would retain much
of the complexity this adoption is intended to remove.
### Canonical reference sources
Reference sources must also use explicit objects. An external file reference
uses a `path` field; a generated reference retains its typed `artifact`
selector. For example:
```yaml
references:
party:
path: ./dnd-party.txt
npc_registry:
artifact:
step: describe-session
lane: npc-registry
```
Pipeline-level references remain external-file-only. Step- and binding-local
references may select earlier artifacts according to the existing ordered
handoff rules. Relative external paths resolve from the file that declared the
surviving value, including imported fragments and configuration profiles. CLI
reference paths continue to resolve from the process working directory.
Remove the lane-level `references` compatibility alias in version 5. New files
must place a lane's extraction references under `extract.references`. This
leaves one canonical location and removes merge-precedence behavior from the
file converter.
### Strictness and dynamic options
Keep ConfigKit's strict YAML rules. Version 5 therefore rejects aliases, merge
keys, unsupported tags and scalar forms, duplicate mapping keys, unknown typed
fields, trailing documents, and incompatible YAML types. Do not recreate an
application pre-parser to weaken these rules.
Module `options` remain `map[string]any` where the selected module determines
the valid keys. ConfigKit represents YAML integers in dynamic values as
`int64`. Module option validators must accept that canonical representation,
check range before conversion, and hand runtime modules a safe `int` only where
their APIs require it. Do not marshal and decode dynamic subtrees through YAML
to recover native `int` values.
## ConfigKit-Backed Loading Model
Define a plain, typed configuration document for ConfigKit. It should mirror
the public file schema, use ordinary YAML tags, and contain no custom YAML
unmarshal hooks. Keep it separate from framework runtime types where doing so
avoids YAML concerns, presence flags, or configuration-only unions leaking into
`internal/framework/pipeline`.
The reusable ConfigKit definition should provide:
- fresh built-in defaults for each load;
- the `/version` rule;
- recorded whitespace normalization for identifiers, paths, profile IDs,
endpoints, and other fields whose current contract normalizes input;
- recorded derivation of `concurrency.stage_workers.extract` from the effective
`concurrency.total_llm` only when the worker value was omitted;
- collision checks before normalizing map keys or cross-step identifiers;
- configuration-only semantic validation with precise paths and related paths
where useful; and
- a dynamic secret predicate that covers credential-shaped keys below module
option maps, preserving the current defensive redaction boundary even though
ordinary Notarius configuration contains no credential values.
Use ConfigKit's resolver only for deterministic configuration normalization and
derived defaults. Use its read-only semantic validator for domain constraints.
Do not put module lookup, PromptKit profile lookup, filesystem resource checks,
or runtime pipeline construction in either callback.
After a successful load, perform one explicit, independently tested conversion
from the canonical configuration document to the Notarius runtime `Config` and
pipeline profile types. The converter should be a narrow ownership boundary,
not a second parser or validation framework. It may translate enums and checked
numeric values, but it must not silently change already validated meaning.
Retain the ConfigKit result alongside the runtime configuration in an
application-owned loaded-configuration value. Commands that need only the
runtime `Config` should not gain access to parser internals; commands and debug
writers that expose safe configuration provenance may use the retained result.
## Imports And Configuration Profiles
Enable ConfigKit's explicit imports using its existing `configkit.imports`
syntax, including path, glob, and optional declarations. Operators can keep
whole pipeline definitions in separate files and compose them through the
selected root file. The root file alone declares `version`.
Use ConfigKit's strict composition semantics. Imports combine disjoint
definitions; they do not provide ordered override layers or concatenate
sequences. In particular, one pipeline's ordered `steps` list remains together
in a single definition. Do not add Notarius-specific step merging or a second
import mechanism.
Expose `--config-profile <name>` on every command that loads configuration:
`run`, `config validate`, `pipelines list`, `config effective`, and
`config explain`. Pass the selection to ConfigKit. A missing selection uses
the composed base configuration. ConfigKit's reserved selection `default`
also means no overlay; it does not load a default profile file.
Use ConfigKit's profile location `<configuration-root>/profiles/<name>.yml`
and existing selection rules. A selected missing profile fails loading.
Profiles recursively overlay mappings and replace scalars, sequences, and
nulls as specified upstream. Development profiles can change pipeline-level
`llm_profile`, concurrency, and output settings without repeating the pipeline.
Changing an individual binding inside `steps` requires replacing the complete
steps sequence. Document this limitation prominently in examples.
Configuration profiles and PromptKit profiles are independent. The former
select a configuration overlay with `--config-profile`; the latter select LLM
settings through `llm_profile` and the existing `--llm-profile` override. Neither
selection implicitly chooses the other.
Use the selected file's canonical directory as ConfigKit's configuration root.
Document the upstream requirement that imports and configuration profiles stay
within that root, along with composition conflicts, cycle/repeated-import
errors, and profile sequence replacement. This containment rule does not apply
to Notarius input, reference, output, cache, debug, or PromptKit profile paths.
### Paths And Source Ownership
Resolve every nonempty relative application filesystem path authored in
configuration against the canonical directory of the file supplying the
surviving value. This includes external references, `promptkit.profile_file`,
`promptkit.profile_dir`, and output, cache, and debug directories. Absolute
paths remain unchanged. This deliberately changes configured PromptKit sources
and runtime destinations from working-directory-relative behavior in version 5.
File-authored profile replacements use the profile file's directory, including
equal-valued replacements. Inherited values keep their base-file origin.
Built-in relative defaults have no declaring file and use the canonical main
configuration directory. Explicit CLI-relative paths use the invocation's
working directory, including output/debug overrides submitted through ConfigKit.
CLI references remain invocation-owned. Empty optional cache paths keep their
existing disabled/automatic meaning; do not turn an empty path into a directory.
Use `InputInfo` at original scalar paths and `Source` for their source IDs,
not composite parent origins or diagnostic display labels. Preserve original
path mappings when identifiers are normalized. Record normalization so runtime,
effective output, and explanation agree; do not resolve overwritten values.
Canonical target-file directories, not symlink alias directories, supply bases.
Notarius's file-backed loader must supply the canonical main directory and the
invocation directory where needed. If a nonempty relative value unexpectedly
lacks the provenance needed to choose its base, report a safe field diagnostic
and fail loading rather than silently choose a different directory. This is a
correctness failure, not an added restriction on operators' valid paths. Do not
copy the upstream example's optional warning-and-leave-relative fallback.
Application paths may be absolute or contain `../`; do not add confinement,
target-existence checks, or new symlink restrictions during normalization.
ConfigKit's own import and profile-source rules remain unchanged. Module-owned
dynamic options are not automatically interpreted as filesystem paths.
## Discovery, Precedence, And Overrides
Keep the existing file-discovery order: explicit `--config`, then non-empty
`NOTARIUS_CONFIG`, then the installed default path. `NOTARIUS_CONFIG` selects a
source and is not a field-level configuration override, so it remains outside
ConfigKit.
Retire these seven field-level environment overlays in version 5:
- `NOTARIUS_TOTAL_LLM_CONCURRENCY`;
- `NOTARIUS_STAGE_WORKERS_EXTRACT`;
- `NOTARIUS_OUTPUT_DIR`;
- `NOTARIUS_CACHE_CHUNK_PLANS_MODE`;
- `NOTARIUS_CACHE_CHUNK_PLANS_DIR`;
- `NOTARIUS_CACHE_CHECKPOINTS_DIR`; and
- `NOTARIUS_DEBUG_DIR`.
ConfigKit deliberately does not model general environment overlays. Applying
them after a successful load would make the retained effective configuration,
origins, and explanations disagree with the values used at runtime. Operators
must put durable values in YAML and use the supported explicit CLI flags for
invocation-local changes.
Classify the current run flags by responsibility. A flag that directly
overrides a field in the typed configuration document should become a
ConfigKit `Override` record only when the operator explicitly supplied it;
parser defaults must not be submitted as overrides. Input selection, lane
selection, session identity, resume/recompute actions, command-line references,
and similar invocation state remain outside ConfigKit even when they affect
runtime resolution. The documented precedence becomes built-in defaults,
composed YAML sources, the selected configuration profile, explicit
configuration-valued CLI overrides, and then
invocation-only resolution actions at their existing application boundary.
## Validation And Diagnostics
Move configuration-only checks currently split among parsing, file
application, and `Config.Validate` into the ConfigKit
definition when they can be expressed against the typed file contract. Report
all independently discoverable semantic errors in one validation pass rather
than failing at the first field, while avoiding cascaded diagnostics whose only
cause is an earlier invalid parent.
Keep catalog-dependent checks in `Config.Resolve` and
`pipeline.ResolvePipeline`. These include registered module and validator
existence, stage capabilities, artifact compatibility, reference-slot
compatibility, PromptKit profile availability, and the concrete resolved
pipeline digest. Their existing error classification and fail-before-prepare
behavior must remain intact.
At the CLI boundary, recognize `*configkit.LoadError` and use ConfigKit's
canonical diagnostic renderer. Operators should receive the diagnostic code,
semantic path, safe message, and source coordinates when available. Do not
append raw YAML parser errors, source fragments, configuration values, or
secret values to the outward error chain. File discovery/read context should
remain clear without wrapping the same failure repeatedly.
Nonfatal ConfigKit diagnostics, if Notarius defines any, must not be silently
dropped. They should enter the existing process-warning model only when they
represent an actionable configuration degradation. Ordinary normalization,
defaulting, and provenance records are explanation data, not run warnings.
## Effective Configuration And Redaction
Use `Result.WriteEffective` as the canonical renderer for the loaded effective
configuration. The requested debug bundle should store this deterministic,
redacted YAML rather than a separately maintained JSON clone of the raw
configuration. Update the artifact name and documentation together if the
format changes.
Use ConfigKit's secret predicate to redact credential-shaped dynamic option
paths. PromptKit credentials remain environment references in PromptKit-owned
profile files and must not be copied into Notarius configuration or debug
state.
Do not remove Notarius's redaction of the resolved pipeline merely because the
loaded configuration is ConfigKit-backed. The resolved value contains
application-owned structures, selection results, and reference provenance that
ConfigKit does not own; materialized reference bytes must continue to be
excluded from ordinary summaries. Remove only manual configuration cloning and
redaction made redundant by the frozen ConfigKit result.
## Configuration Inspection Commands
Add these read-only commands as first-class ConfigKit-backed inspection
surfaces:
```text
notarius config effective [--config path/to/config.yml] [--config-profile name]
notarius config explain [--config path/to/config.yml] [--config-profile name] <json-pointer>
```
`config effective` loads the selected configuration through the production
path and writes only ConfigKit's deterministic redacted YAML to standard
output. `config explain` accepts one canonical JSON Pointer, loads through the
same path, and writes ConfigKit's deterministic redacted explanation for that
effective or historical path. Neither command resolves a pipeline, prepares a
module, requires provider credentials, or contacts a provider.
Both commands must use ordinary configuration discovery, ConfigKit diagnostic
rendering, the same import and profile selection as execution, and the existing
CLI exit-status conventions. An unknown or
noncanonical explanation path is a command failure, not an empty successful
result. Standard output contains only the requested inspection output;
diagnostics go to standard error. Effective output is YAML and explanation is
ConfigKit's human-readable text; neither is promised to be reloadable
configuration or a new durable JSON API. There is no unredacted mode.
## Required Code And Contract Changes
The implementation must address the following coherent work areas:
- pin `gitea.maximumdirect.net/eric/ConfigKit v0.3.0` and use its public
source-origin APIs;
- introduce the version 5 typed document and its ConfigKit definition;
- replace `LoadFileConfig`, custom `UnmarshalYAML` implementations, manual
strict-parser checks, presence flags, `ApplyFileConfig`, and redundant
configuration cloning with the ConfigKit load boundary;
- consolidate defaults, normalization, and configuration-only validation into
the definition while retaining runtime catalog validation;
- update CLI configuration loading and error rendering without changing file
discovery;
- enable ConfigKit imports and configuration profiles, wire `--config-profile`
through all configuration-loading commands, and retain declaration origins
for configured filesystem paths;
- retire field-level environment overlays while retaining `NOTARIUS_CONFIG`;
- classify current run flags so only true configuration overrides use
ConfigKit override records and invocation-only state remains outside the
configuration loader;
- update dynamic module-option decoding and range checks for `int64`;
- integrate ConfigKit effective rendering and metadata into requested debug
bundles and the new `config effective` and `config explain` commands;
- remove superseded code, tests, and the direct `gopkg.in/yaml.v3` dependency
after no production or test-only use remains; and
- update maintained examples and canonical documentation in the same change as
each externally visible contract adjustment.
## Documentation And Migration
Update `docs/config.md` as the canonical owner of version 5 syntax, defaults,
precedence, canonical bindings and references, strict YAML behavior, and the
retirement of field-level environment overrides. Replace the version 3
migration section with a concise version 4-to-5 migration guide covering:
- changing the numeric version value from 4 to 5;
- expanding scalar module bindings into `{module: ...}` objects;
- expanding scalar external references into `{path: ...}` objects;
- moving lane-level reference aliases to `extract.references`;
- resolving configured PromptKit profile-source and output/cache/debug paths
from their declaring file instead of the working directory, and built-in
relative defaults from the main configuration directory; CLI-relative paths
retain their invocation-directory base;
- using imports for whole pipeline definitions and configuration profiles for
overlays, including complete replacement of step sequences;
- replacing retired operational environment overrides with YAML or supported
explicit CLI flags; and
- any effective/debug artifact or inspection-command changes.
Update `docs/internal/configuration.md` to describe the ConfigKit boundary,
typed document-to-runtime conversion, application-owned validation, retained
metadata, and catalog-resolution handoff. Update `docs/cli.md` only for changed
commands or diagnostic behavior, `docs/operations.md` only for operator-facing
configuration and debug workflows, and the relevant architecture text if the
adopted precedence model changes.
Convert both maintained D&D configurations and every configuration snippet to
version 5 canonical syntax. Examples must remain secret-free and load through
the same production path exercised by the CLI. Do not keep a second set of
version 4 examples.
Keep the minimal D&D example self-contained. Extend the complete D&D example
with an imported whole pipeline definition and a selectable development
configuration profile. Keep configuration overlays physically distinct from
the maintained PromptKit profile files so readers can see which system owns
each file. Test the complete example both with and without the development
overlay, and update subprocess guidance to show optional `--config-profile`
selection. All configuration inspection commands must document what the
selected profile changes and that runtime pipeline resolution is separate.
Because this change establishes a durable dependency and reallocates ownership
at a core boundary, add an ADR recording the decision to use ConfigKit, the
responsibilities retained by Notarius, the deliberate version 5 break, and the
retirement of the application environment-overlay layer. The rejected
alternatives should include continuing the custom loader, retaining version 4
temporarily, or wrapping ConfigKit in a compatibility pre-parser. The ADR
should record rationale, not duplicate the current configuration reference or
implementation details.
## Testing And Verification
Follow the repository's behavior-focused testing policy. Reuse ConfigKit's own
tests for generic YAML and metadata mechanics; Notarius tests should protect
only its contract and integration responsibilities.
Required Notarius coverage includes:
- successful loading of omitted, explicit zero/false/empty, and derived values
that materially affect Notarius behavior;
- numeric and quoted version 5 acceptance and safe rejection of absent, null,
version 4, unsupported identifiers, and non-integer numeric forms;
- canonical binding and reference decoding plus rejection of former shorthand;
- identifier normalization and normalized-collision rejection;
- aggregation and source location of representative application validation
errors;
- unchanged configuration discovery and source-relative reference resolution;
- imported whole pipeline definitions, representative composition conflicts,
and profile precedence through the real ConfigKit loader;
- profile sequence replacement and explicit clearing, so Notarius does not
accidentally append or merge ordered steps;
- relative references, PromptKit sources, and destination paths from root files,
imported fragments, and overlays, including inherited and equal-valued replaced
values, normalized enclosing identifiers, default bases, CLI bases, and empty
optional cache settings;
- matching configuration-profile selection across execution, validation,
pipeline listing, effective output, and explanation, without provider calls;
- checked dynamic integer conversion at each production module option boundary;
- detached ownership of loaded configuration and resolved pipelines;
- safe CLI rendering of representative ConfigKit parse, decode, version,
resolution, and validation failures;
- successful redacted `config effective` output and effective, historical,
unknown, and noncanonical `config explain` path behavior;
- effective-configuration redaction, including nested credential-shaped dynamic
option keys, without exposing materialized references;
- maintained minimal and complete example validation through the production
loader and production module catalog; and
- representative end-to-end CLI validation and pipeline resolution without
network access or credentials.
Delete or rewrite legacy parser tests whose only purpose is to retest behavior
owned by ConfigKit. Retain a small integration sample for each strictness rule
that is itself part of the Notarius public contract, and avoid assertions on
incidental diagnostic prose.
The completed implementation must pass:
```sh
go test ./...
go vet ./...
go build ./cmd/notarius
```
It must also validate both maintained example files with the production CLI.
The complete example may use a placeholder credential for offline PromptKit
profile validation, as documented today; no live provider call is required.
## Non-Goals
- Moving pipeline resolution, module catalogs, or PromptKit profiles into
ConfigKit.
- Adding arbitrary environment-to-field binding.
- Retaining the retired field-level operational environment variables as a
parallel precedence layer.
- Loading credentials into the Notarius configuration document.
- Supporting remote configuration sources or formats other than YAML.
- Adding a generic dynamic-subtree conversion layer.
- Composing ordered steps across fragments or adding application-specific
sequence merge, profile inheritance, or profile stacking semantics.
- Preserving scalar shorthand with an application pre-parser.
- Changing the fixed pipeline shape or durable extraction artifacts.
- Changing run input, output publication, cache, checkpoint, retry, or warning
semantics except where configuration loading or debug rendering directly
exposes them.
## Acceptance Criteria
- All configuration-loading commands use ConfigKit v0.3.0 through its public
package, and no production code directly imports a YAML parser.
- Version 5 is the only accepted file contract, documents `version: 5` as its
canonical spelling, and has one canonical object representation for module
bindings and references.
- The ConfigKit definition is the sole owner of typed defaults, input presence,
recorded normalization, derived configuration defaults, and
configuration-only semantic diagnostics.
- Notarius remains the sole owner of catalog-dependent resolution and runtime
conversion.
- Dynamic option integers are handled without unchecked narrowing or YAML
round-trips.
- CLI diagnostics and effective configuration output are deterministic,
redacted, value-safe, and retain useful path/source context.
- `config effective` and `config explain` expose only ConfigKit's redacted
inspection views and perform no pipeline or provider work.
- Imports compose whole pipeline definitions and selected configuration
profiles apply ConfigKit's documented overlay semantics consistently across
all configuration-loading commands.
- Configured relative filesystem paths use the surviving declaration's canonical
directory; built-in defaults use the main configuration directory and CLI
values use the invocation directory. Safe inspection agrees with runtime.
- The resolved pipeline remains detached, reference content remains excluded
from ordinary summaries, and its identity changes under the same semantic
conditions as before.
- Maintained examples, current documentation, focused tests, and repository-wide
validation agree with the implemented version 5 contract.
- `NOTARIUS_CONFIG` still participates in discovery, while the seven
field-level operational environment variables no longer affect loaded
configuration.
- Superseded custom parser, presence, environment, clone, redaction, and test
code is removed rather than left as an alternate loading path.
## Deferred Composition Work
Step-level composition may be revisited after operators use imported whole
pipeline definitions. It is not part of this adoption: ConfigKit does not
concatenate sequences or merge their elements by application-defined IDs.
Any future extension must define ordering, duplicate step IDs, dependency
validation, profile overrides, and provenance before changing that contract.

View File

@@ -1,738 +0,0 @@
# ConfigKit Adoption Implementation Plan
## Purpose And Execution Contract
Implement the target in [ConfigKit Adoption](configkit.md). That document owns
the feature scope and operator choices; this document owns the implementation
sequence. Each numbered stage is one implementation prompt for a
gpt-5.6-terra coding agent. Execute stages in order and finish the named checks
before proceeding. Do not combine the configuration cutover with unrelated
pipeline or security refactoring.
Read `AGENTS.md`, `docs/development.md`, all three documents under
`docs/policy/`, and the relevant focused internal documentation before acting.
Apply the implementation-discipline policy: use the dependency's actual APIs,
preserve the requested flexibility, and do not add application restrictions
under the guise of this migration. Tests protect behavior, not exact helper
structure, incidental formatting, or a duplicated implementation of ConfigKit.
ConfigKit v0.3.0 supplies the required source-origin APIs. Pin that release from
Stage 1; there is no remaining upstream gate. Stage 7 uses the released
`Resolution.InputInfo` and `Resolution.Source` methods. Keep the fourteen
stages ordered and bounded as below.
Temporary coexistence of the old production loader and the new loader during
these stages is implementation scaffolding only. There is no supported dual
version loader in the final product and no release should be tagged midway
through the cutover. Each stage must compile and pass its scoped tests; the
final stage establishes repository-wide readiness.
## Repository Baseline
Planning examined Notarius at `946fa10` plus the outstanding policy and roadmap
changes. The dependency review now uses the sibling ConfigKit checkout tagged
`v0.3.0`, including its release guide and source-aware consumer example.
Relevant current owners are:
| Area | Repository evidence |
| --- | --- |
| Version 4 YAML and conversion | `internal/core/config/file_config.go`, including `FileConfig`, custom `UnmarshalYAML` methods, `ParseFileConfigYAML`, `LoadFileConfig`, and `ApplyFileConfig` |
| Defaults, runtime ownership, domain validation | `internal/core/config/config.go`, `validation.go`, `effective_config.go`, and their contract tests |
| Field-level environment overrides | `internal/core/config/env.go` and `env_contract_test.go` |
| Configuration discovery and command overrides | `internal/cli/run.go`: `loadConfig`, `runConfigValidate`, `runPipelinesList`, and `runPipelineCommand` |
| Catalog checks and configured/CLI reference behavior | `internal/framework/pipeline/profile.go`, `references.go`, and CLI reference contract tests |
| Dynamic integer consumers | `internal/modules/generic/chunk/units/chunker.go` and `internal/modules/generic/output/json/encoder.go` currently require native `int` |
| Debug configuration and resolved-pipeline output | `internal/core/config/redaction.go`, `internal/core/debugbundle/summary.go`, and the CLI debug handoff |
| Maintained examples | `examples/dnd-minimal.config.yml`, `examples/dnd-complete.config.yml`, `examples/pipelines/dnd-session.yml`, `examples/llm-profiles/dnd-extraction.yml`, and CLI production contract tests |
| Direct YAML test dependency | `internal/cli/production_contract_test.go` also parses embedded prompt definitions using `yaml.v3` |
| Upstream contract | ConfigKit `docs/config.md`, `docs/consumers/README.md`, public `load.go`, `lifecycle.go`, `metadata.go`, `records.go`, `path.go`, and consumer examples at v0.3.0 |
The sibling checkout is reference material, not a production dependency path.
Do not commit a local `replace`, modify upstream, or invent a future API as part
of these Notarius stages.
## Decisions Applied Throughout
### Configuration And Dependency Boundary
- Pin `gitea.maximumdirect.net/eric/ConfigKit v0.3.0`. Use the root public
package, never its private engine or an unreviewed `main` revision.
- Use a string-backed document version and a `/version` rule accepting exactly
text `5`; both numeric `version: 5` and quoted `version: "5"` are accepted.
Maintained examples use the numeric form. Reject missing/null/unsupported
versions. Numeric v4 fails the application checker with migration guidance.
- Use plain YAML-tagged document structs in `internal/core/config`, without
custom YAML hooks. Keep runtime framework types independent of ConfigKit.
- Keep the existing runtime `Config`, `ResolveInput`, and `EffectiveConfig`
boundary where it is useful. A new `LoadedConfig` owns the private ConfigKit
result and exposes runtime configuration plus safe writer methods. No
caller may mutate a returned `Result.Config` and expect its frozen inspection
output to change.
- ConfigKit owns composition, strict decoding, metadata, and safe rendering.
Notarius owns typed defaults, field semantics, recorded normalization,
conversion, catalog resolution, and physical run preparation.
- Application paths use typed ConfigKit key/index constructors; never build
JSON Pointers by concatenating unescaped pipeline IDs or reference keys.
### Document Shape And Presence
Use a distinct finite `ValidatorDocument` containing only `module`,
`llm_profile`, `structured_output_repair_attempts`, `retries`, and `options`.
The producer `BindingDocument` contains those fields plus references,
validation policy, and a validator list. This avoids recursive Go schema types
and makes forbidden nested validator fields strict unknown-field errors.
Use pointer scalars only when nil means inheritance or omission, and ordinary
structs/maps/slices elsewhere as appropriate to the contract. Use ConfigKit
presence before normalization changes paths. Remove custom parser-presence
flags. In particular:
- omitted validators use the production chain; explicit `[]` selects an empty
chain; present `null` is invalid, matching the existing array contract;
- omitted repair attempts inherit; explicit zero disables repair; present
null is invalid for this integer setting;
- omitted LLM-profile fields inherit; explicitly blank or null selections are
invalid;
- omitted optional module bindings permit existing catalog defaults; a present
binding must be a valid object with a nonempty module;
- reference objects have exactly one selected source: nonempty `path` or a
complete `artifact` object with step and lane; explicit invalid branches must
not disappear during pointer conversion;
- preserve the single-step `artifacts` form and the ordered `steps` form;
reject simultaneous declarations and explicitly empty/null `steps`;
- retain optional null behavior where the existing contract permits clearing
an optional object or collection; do not impose a universal no-null rule;
- remove the lane-level reference alias. Keep the existing treatment of the
deprecated lane-level validator list during migration (empty allowed,
nonempty rejected) rather than adding an unrelated contract removal.
Static defaults stay as currently documented. Derive omitted extraction workers
from the final configured total LLM limit. ConfigKit profile null replacement
is subject to the destination field's existing domain validity; overlay support
does not make null valid for every setting.
### CLI And Path Ownership
| Input | Owner / treatment |
| --- | --- |
| `--config`, `NOTARIUS_CONFIG`, installed fallback | Existing CLI discovery; ConfigKit receives the selected file |
| `--config-profile` | ConfigKit profile selection on all five configuration-loading command forms |
| Explicit `--output-dir` | ConfigKit override at `/output/directory` |
| Explicit `--debug-dir` | ConfigKit override at `/debug/directory`; preserve `--debug` requirement |
| Explicit `--chunk_cache` | ConfigKit override at `/cache/chunk_plans/mode` |
| `--llm-profile` | Existing runtime selection overriding all selected LLM bindings; not a replacement for a single YAML field |
| Input, lanes, session ID, reasoning effort, references, resume/recompute, debug enablement, JSON output | Existing invocation actions outside ConfigKit |
| Seven retired operational environment variables | No longer read or applied; no deprecation-warning scan of process environment |
| PromptKit credential environment variables | Existing PromptKit responsibility |
Only explicitly supplied flags create override records, with safe flag names
as provenance. Preserve current blank-value checks and invocation error classes.
For `--config-profile`, pass the supplied name through ConfigKit's selection
rules; empty and exact `default` select no overlay. Do not invent stricter name
validation or whitespace normalization than the upstream profile contract.
Enable imports without a separate Notarius enablement flag. The canonical
selected file's directory is the configuration root. Every nonempty configured
relative filesystem path (external references, PromptKit profile sources, and
output/cache/debug directories) uses the surviving declaration's canonical
directory. Built-in relative defaults use the main configuration directory;
explicit CLI values use the invocation directory. Absolute values remain
unchanged. Empty optional cache paths retain their existing meaning.
Resolve paths before runtime conversion without reading target contents.
CLI references retain their existing runtime handling and generated-reference
protection. Configuration-root confinement applies only to ConfigKit's imported
configuration and overlay files, not application paths. Do not infer path
semantics for arbitrary dynamic module options.
## Stage 1 — Add ConfigKit And The Version 5 Document Types
**Status:** Completed 2026-09-13.
**Goal:** Establish the new file contract without switching production commands.
**Work:**
1. Pin v0.3.0 in `go.mod`/`go.sum` and inspect its actual exported API.
2. Add `document.go` and `load.go` in `internal/core/config` (or equivalent
cohesive files) with the complete plain document types, finite validator
type, explicit reference union, and YAML-tagged validation-policy fields.
3. Provide a private reusable definition factory with fresh static defaults and
the exact version rule. Define a small application `LoadOptions` for profile
selection and explicit configuration overrides; avoid a generic loader
abstraction or exposing parser types.
4. Keep the new load entry point unconnected to production until its
normalization and validation stages are complete. Mark it internal
scaffolding; do not advertise partially implemented v5 support.
5. Add the next available ADR (expected `0018-adopt-configkit.md`) recording
the accepted ownership, v5 break, imports/profiles, and environment policy.
Record application path-base policy and the released source-origin APIs;
do not claim the Notarius migration is implemented merely because upstream is.
**Acceptance:** v5 numeric/quoted versions decode; unsupported versions fail
before document decoding; representative canonical bindings and references
decode while shorthand/unknown fields fail. Separate loads do not alias
defaults. The current CLI still works through its existing loader.
**Validation:** `go test ./internal/core/config`; `go test ./...` to confirm the
new dependency and schema types do not break the existing build. Keep tests
small; generic YAML exhaustiveness remains upstream.
**Prompt size:** Small to medium; types, dependency, and version boundary only.
## Stage 2 — Implement Operational Defaults And Normalization
**Status:** Completed 2026-09-13.
**Goal:** Make operational configuration behavior correct and inspectable.
**Work:**
1. Express existing concurrency, output, cache, debug, and PromptKit-local-backend
defaults and validations through the definition callbacks.
2. Use recorded `Default`/`Normalize` operations for derived extraction workers,
path whitespace, cache mode, and endpoint whitespace. Derived defaults must
follow profile/CLI overlays and preserve explicit zero for validation.
3. Use presence-aware checks for explicit empty/null PromptKit source selectors
and required fields in a present local-backend object. Preserve mutually
exclusive profile sources and the existing endpoint/range constraints.
4. Keep filesystem checks and PromptKit profile catalog lookup out of callbacks.
This stage trims filesystem paths but does not yet resolve their bases;
Stage 7 owns all source-aware path normalization.
5. Extract small reusable value checks where needed so runtime validation and
document validation do not separately implement the same rule. Collect
independent errors through ConfigKit with safe messages and field paths.
**Acceptance:** defaults and explicit values retain their meanings; worker
derivation follows effective total concurrency; multiple independent field
errors identify their source locations; normalization appears in explanation
without generating warnings.
**Validation:** `go test ./internal/core/config`. Add load-boundary cases for
profiles overriding concurrency, CLI overrides, invalid output versus empty
cache paths, and local backend presence. No live profile resolution.
**Prompt size:** Medium; operational fields only.
## Stage 3 — Implement Pipeline Document Rules
**Status:** Completed 2026-09-13.
**Goal:** Validate and normalize the complete pipeline file shape before
catalog resolution.
**Work:**
1. Implement one typed traversal of pipelines, steps, lanes, bindings,
validators, and reference sources. Prefer shared binding helpers and
explicit walks over reflective generic validation.
2. Perform all presence-sensitive checks against original input paths before
renaming map keys. Detect whitespace collisions deterministically before
writing normalized keys. Report both paths where useful.
3. Normalize identifiers, selected profiles, and generated selectors with
recorded changes. For map-key moves, record the parent operation and relevant
source dependencies; preserve a load-local original-path mapping for Stage
7. Do not guess origin from a normalized key after it has moved.
4. Enforce the field and shape contracts above, including global lane-ID
uniqueness across steps, valid retry values, validation-policy fields, and
allowed binding-local reference/validator placements.
5. Keep stage/module compatibility and actual option meaning in the existing
catalog resolver. Do not reject a module based on an embedded allowlist in
the document schema.
6. Factor shared semantic value checks with `Config.Validate` where necessary.
Keep file presence/shape checks in the document callback and direct runtime
construction checks at the runtime boundary; avoid parallel complete
validators with independently copied rules.
**Acceptance:** single-step and ordered pipelines behave as specified;
explicit empty validator chains and zero repair budgets survive; invalid
references, profile selectors, normalized collisions, and duplicate lane IDs
fail with useful paths. No mutation is hidden from ConfigKit's change recorder.
**Validation:** `go test ./internal/core/config ./internal/framework/pipeline`.
Migrate meaningful rule cases from the old parser tests to the new load
boundary as they gain an owner; retain old-loader coverage until cutover.
**Prompt size:** Medium to large, bounded to pipeline document semantics; do
not combine it with source-origin or CLI work.
## Stage 4 — Convert Documents Into Runtime Configuration
**Status:** Completed 2026-09-13.
**Goal:** Produce the existing typed runtime composition from the new document.
**Work:**
1. Add a narrow explicit conversion after successful ConfigKit loading. Build
independently owned `Config` and `pipeline.PipelineProfile` values, preserving
map identities, step order, inheritance pointers, empty chains, and reference
source forms. Convert the finite validator document to runtime bindings.
2. Wrap the result in `LoadedConfig`, retaining the private ConfigKit snapshot.
Provide safe effective/explanation writers and the runtime configuration;
avoid a public unredacted serialization method.
3. Stop re-deriving workers on the already finalized new-load path. Retain
programmatic `Config` validation and ownership checks. Any legacy
recomputation shim needed by the old loader is temporary and removed at
Stage 12. Runtime callers needing defaults should use the same default values
as the document definition, not a second literal default table.
4. Keep `Config.Resolve`/`pipeline.ResolvePipeline` responsible for selection,
capabilities, concrete validation-policy inheritance, options, references,
and digest generation. Do not add configuration profile name or source
history to semantic pipeline identity.
**Acceptance:** equivalent canonical documents produce equivalent runtime
pipelines; changing a semantic setting changes identity, while merely importing
an equivalent definition does not. Mutating caller-owned maps/slices cannot
alter the snapshot or resolved pipeline. ConfigKit changes do not force its
types into module interfaces.
**Validation:** `go test ./internal/core/config ./internal/framework/pipeline`.
Use a real test catalog and representative reference/validator chains; resolve
absolute reference fixtures until Stage 7 supplies relative path resolution.
**Prompt size:** Medium; conversion and runtime ownership only.
## Stage 5 — Support ConfigKit Dynamic Integers In Modules
**Status:** Completed 2026-09-13.
**Goal:** Ensure the module boundary accepts valid ConfigKit options.
**Work:**
1. Inspect every production numeric `map[string]any` option consumer. The known
sites are generic unit chunking and JSON evidence `window_units`.
2. Add one narrow shared option conversion helper in the existing pipeline
option-helper area. Accept `int64` from ConfigKit and native `int` from
programmatically constructed module bindings. Range-check `int64` before
conversion; keep domain positivity/nonnegativity at the module owner.
3. Preserve rejection of floats and strings. Do not recursively rewrite all
options, change their serialized representation, or add general numeric
coercion. Keep options in ConfigKit form through config conversion.
4. Update the old test that rejects `int64` to protect the newly accepted
boundary. Add representative real-loader-to-module tests, including nested
JSON evidence options.
**Acceptance:** integral options work from YAML and programmatic bindings;
fractional/string inputs and invalid ranges still fail before preparation.
No unchecked narrowing or YAML round-trip remains at these consumers.
**Validation:** `go test ./internal/modules/generic/... ./internal/framework/pipeline ./internal/core/config`.
**Prompt size:** Small; two known consumers and a narrow shared helper.
## Stage 6 — Prepare Debug Rendering For The ConfigKit Result
**Status:** Completed 2026-09-13.
**Goal:** Prepare the publication boundary before changing command loading.
**Work:**
1. Add a small writer interface such as `WriteEffective(io.Writer) error` to
the debug summary boundary. `LoadedConfig` satisfies it without exposing a
concrete ConfigKit result to debug storage.
2. Add publication of `effective-config.yml` through the existing confined,
atomic summary file writer. Buffer redacted output before publishing if the
current file API takes bytes; propagate rendering and write errors.
3. Preserve the existing redacted resolved-pipeline writer and all reference
content exclusion. The old `effective-config.json` writer remains only until
the run command switches in Stage 10.
4. Reuse `sensitiveConfigKey` for the definition's dynamic secret predicate at
nested options paths, including validator options, containers, and moved
map keys. Keep scalar classification policy in one owner. Do not log raw
configuration or metadata snapshots that contain values.
**Acceptance:** redacted effective YAML publishes atomically; failure leaves no
partial artifact; nested sensitive values never appear in effective output or
explanation. Resolved runtime summaries still omit materialized references.
**Validation:** `go test ./internal/core/config ./internal/core/debugbundle`.
Test rendering failure and caller mutation at these real local boundaries.
**Prompt size:** Small to medium; rendering interface and publication only.
## Stage 7 — Resolve Declaration-Relative Paths
**Status:** Completed 2026-09-13.
**Goal:** Normalize file paths using their effective source origins and retain
correct ConfigKit inspection history.
**Work:**
1. Query `resolution.InputInfo(originalPath)` for each path-bearing scalar.
It returns `(FieldInfo, bool)` from fixed entry-state metadata, unaffected by
earlier normalization, additions, removals, or moves. Use Stage 3's original
path mapping for renamed keys. Read `Origin.Layer` and the exact scalar's
`Origin.Locations`; use `resolution.Source(location.SourceID)` to obtain
`(SourceInfo, bool)` and its canonical `Path`. Do not select an arbitrary
origin of a composite parent.
2. Implement one narrow application path-base helper shared by typed traversals.
Base/profile file values use the declaring file directory; default relative
values use the canonical main directory; CLI relative values use the
invocation directory supplied by the CLI load boundary. Capture that
directory once for the load. Obtain canonical main-file identity consistently
with ConfigKit's acquired source records; no second parse or load.
3. Normalize nonempty relative external references, PromptKit profile sources,
and output/cache/debug directories to absolute paths. Preserve already
absolute values after existing whitespace handling. Empty optional cache
paths retain their automatic/disabled semantics. Do not inspect target
existence or read referenced content.
4. Record normalized paths and map moves so frozen inspection matches runtime
and traces the original declaration. Keep collision checks ahead of mutation
and process stable sorted keys. Use retained input metadata only as provenance,
not as a substitute for reading the current working value.
5. Fail with a safe field-level resolution diagnostic if a relative value
unexpectedly lacks an unambiguous required source/base. Defaults are a known
case, not missing provenance. Do not copy the upstream example's optional
warning-and-leave-relative fallback or impose confinement on application
paths. Queries are callback-scoped; detached records may be retained, but
never retain the `Resolution` context for post-load queries.
6. Enable imports and selected profiles on the public new loader, using
`File` and the selected file's canonical directory as the root. Keep CLI
references runtime-owned and use ConfigKit's composition semantics directly.
**Acceptance:** root, fragment, and overlay values resolve from their own files;
inherited values retain their base origin; equal-valued replacements identify
their new source; profile-replaced sequences and normalized enclosing IDs retain
attribution. Destinations follow the same rule as references. Defaults use the
main directory; CLI overrides use the invocation directory. Effective and
explanation output agree with runtime. No second load or new file-access policy.
**Validation:** `go test ./internal/core/config ./internal/framework/pipeline`.
Use temporary trees with same-named references in different directories,
canonical symlink targets accepted by ConfigKit, profile replacements,
normalized keys, default and CLI destinations, empty cache paths, and absolute
paths outside the config root. Change the working directory in a controlled
test to distinguish the bases. Materialize a real reference in one case to
prove the intended bytes are chosen. Protect Notarius policy at the load
boundary; generic fixed-view/record-detachment tests remain upstream.
**Prompt size:** Medium; one shared path-base helper and its typed consumers,
with no CLI activation or example migration.
## Stage 8 — Prepare Shared CLI Loading And Diagnostics
**Status:** Completed 2026-09-13.
**Goal:** Make one command-facing configuration entry point ready for cutover.
**Work:**
1. Add a CLI helper that discovers the file, passes profile selection and
explicit override records to the new loader, and returns `LoadedConfig`
plus the selected config path. Supply the invocation directory needed for
relative CLI overrides. Keep discovery order and injected test lookup.
2. Add a shared load-error reporter using `errors.As` for ConfigKit load errors
and `WriteDiagnostics`; supply safe source context using the dependency's
source records. Do not flatten multi-error output into repeated wrappers or
print raw parser/source contents. Preserve status 1 for valid invocations
whose configuration cannot load.
3. Add shared flag registration/parsing only where it reduces actual repeated
code. Use existing CLI parsing conventions, including flags surrounding
positional arguments. Preserve missing-value and unexpected-argument checks.
4. Route successful ConfigKit process diagnostics through one reporting owner.
Define no new application warnings for successful imports, defaulting,
normalization, or ignored retired environment names. Current application
rules should ordinarily return no nonfatal configuration diagnostics.
**Acceptance:** the helper returns a fully validated loaded value with safe
diagnostics and uses the requested source/profile. No production command has
switched accidentally; existing command tests remain green.
**Validation:** `go test ./internal/cli ./internal/core/config`. Add direct
helper tests for explicit flags versus parser defaults, discovery, safe
multi-error reporting, and profile selection. Use the real ConfigKit boundary.
**Prompt size:** Medium; helper and diagnostic wiring only.
## Stage 9 — Prepare Validation And Pipeline Listing Commands
**Status:** Completed 2026-09-13.
**Goal:** Finish the read-only command implementations before the shared
production cutover.
**Work:**
1. Implement v5 handlers for `config validate` and `pipelines list` using the
new helper and `--config-profile`. Preserve `--pipeline`, `--only`, JSON
listing, sorting, and exit/stream behavior. Keep production dispatch on the
old handlers until Stage 10 so all configuration commands switch together.
Use temporary explicit handler names, not a runtime version switch.
2. Plain validation is configuration-only. Selected-pipeline validation still
performs catalog and explicit PromptKit profile checks at their existing
owners, without provider calls or credential requirements beyond existing
offline profile validation.
3. Test these handlers with dedicated v5 fixtures and production registries.
Keep existing dispatch and maintained-example tests intact until Stage 10.
Do not weaken tests to accept both versions or advertise the unconnected
handlers as implemented CLI features.
**Acceptance:** the new handlers see imported pipelines and overlay changes,
reject v4, and retain useful module/profile failure context. Existing public
command behavior stays coherent until all three handlers switch in Stage 10.
**Validation:** `go test ./internal/cli ./internal/core/config`; run the focused
production validation cases using v5 fixtures, including unknown modules and
incompatible generated references.
**Prompt size:** Medium; two closely related read-only commands.
## Stage 10 — Switch Run And The Maintained V5 Configurations
**Status:** Completed 2026-09-13.
**Goal:** Complete production cutover and make all runs use one ConfigKit load.
**Work:**
1. Switch `runPipelineCommand` to the new loader. Translate only the three
config-valued flags in the decision table to ConfigKit overrides; remove
their direct post-load mutations. Add `--config-profile` consistently to
argument reordering and missing-value checks. Activate Stage 9's validation
and listing handlers in the same change and remove superseded handlers.
2. Preserve runtime LLM override, references, input, session identity,
resume/recompute, scheduler setup, and output receipt semantics. Config
failures must still occur before source parsing and physical run allocation.
3. Pass the retained loaded result to the effective YAML debug writer. Continue
to write the separately redacted resolved pipeline after reference
materialization. Add effective configuration profile selection to debug
invocation metadata (`config_profile`), with no overlay represented as empty.
Do not add profile-selection text to checkpoint or pipeline identity.
4. Convert both maintained D&D example configurations to v5 object syntax in
place, still as self-contained files in this stage. Change the complete
example's PromptKit source path for declaration-relative interpretation.
Review destination paths in both examples: retain their declared relative
strings unless an example's intended location requires a deliberate change;
explain their new configuration-file base.
5. Migrate run/reference/state tests and the maintained-example loader to v5.
Split mechanical fixture updates from behavioral assertions within the
diff so regressions remain reviewable. Retired env-based tests now supply
YAML or explicit flags; keep one test proving those variables have no effect.
6. Update current CLI, configuration, operations, and consumer references for
v5, profile selection, environment retirement, path bases (including defaults
and destinations), and `effective-config.yml`.
**Acceptance:** all production commands use the new helper; imported/profile
configuration and explicit overrides agree with execution and debug output;
the maintained minimal and full fake-provider workflows still complete with
the same artifact families and valid reference handoffs.
**Validation:** `go test ./internal/cli ./internal/core/config ./internal/core/debugbundle ./internal/framework/pipeline`.
Use the existing complete D&D fake LLM test instead of a live backend. Verify
JSON run receipt and stderr warning behavior remain unchanged.
**Prompt size:** Largest cutover prompt, limited to wiring, fixture migration,
and its current documentation. Schema, paths, numeric adapters, diagnostic
helpers, and debug writer must already be complete in earlier stages.
## Stage 11 — Add Effective And Explanation Commands
**Status:** Completed 2026-09-13.
**Goal:** Expose the accepted operator inspection workflow.
**Work:**
1. Add `config effective` and `config explain` with the syntax in the feature
roadmap, including `--config` and `--config-profile`.
2. Effective writes only `LoadedConfig.WriteEffective` output. Explain accepts
exactly one JSON Pointer positional argument, parses it with ConfigKit, and
calls the safe explanation writer. Accept the empty pointer for the root
when explicitly supplied; reject malformed escapes/argument shape with
status 2. A well-formed unknown path returns status 1 and no success output.
3. Use ConfigKit's contextual sequence-index rules. Signed numeric mapping
keys remain valid keys; `/steps/+0` cannot identify a sequence element.
Do not add a blanket numeric-token restriction that rejects legal map keys.
4. Preserve writer failure handling. Explanations are human-oriented text;
effective configuration is YAML. Neither output is promised to be a
reloadable config or a new durable JSON API. Do not print success banners.
5. Avoid LLM client construction, module preparation, reference materialization,
state allocation, or pipeline selection. Existing catalog registration at
CLI composition is acceptable; it must not trigger provider work.
6. Update `docs/cli.md`, the configuration inspection guidance, and help.
**Acceptance:** both commands work against imports/overlays, redaction applies
to history and effective values, removed keys remain explainable, malformed
and unknown paths fail appropriately, and stdout contains only requested data.
**Validation:** `go test ./internal/cli ./internal/core/config`. Use real
ConfigKit with temporary files and a provider factory that fails if called.
**Prompt size:** Medium; two commands sharing the completed load boundary.
## Stage 12 — Remove The Legacy Configuration Machinery
**Status:** Completed 2026-09-13.
**Goal:** Finish the simplification promised by the adoption.
**Work:**
1. Remove the unused v4 parser, YAML hooks, `FileConfig` application APIs,
environment-overlay code, parser-only presence flags, and old loading helper.
2. Remove `extractWorkersConfigured`/`defaultedExtractWorkers` inference once
file defaults are finalized by ConfigKit. Programmatically constructed
configs treat a supplied worker entry as explicit and derive only an absent
entry; update internal construction tests accordingly. Keep runtime checks
through `Config.Resolve` for direct callers, without mutating shared maps.
3. Delete the superseded `effective-config.json` publication method and raw
configuration summary/redaction functions. Retain runtime ownership cloning
and resolved-pipeline redaction that still serve real callers.
4. Remove obsolete parser/env tests; retain behavioral coverage at the new
owner. Remove `yaml.v3` from the embedded-prompt test in
`production_contract_test.go`: exercise the production PromptKit loader or
existing asset-loading seam, rather than adopting ConfigKit to parse
PromptKit assets or adding another YAML dependency for the test.
5. Run `go mod tidy`; remove the direct `gopkg.in/yaml.v3` requirement and verify
Notarius Go files no longer import a YAML parser. Dependencies may retain
their private YAML implementation.
**Acceptance:** no alternate loader, environment application layer, redundant
configuration redactor, or parser-only clone remains. Legitimate pipeline and
artifact serialization code is unaffected. Programmatic config behavior has
an explicit tested ownership/default contract.
**Validation:** `go test ./...`; `go vet ./...`; search for `UnmarshalYAML`,
`ApplyFileConfig`, `ApplyEnvOverrides`, `gopkg.in/yaml`, and old effective-config
artifact references. Inspect matches rather than treating historical docs or
negative migration tests as failures.
**Prompt size:** Medium; deletion and ownership consolidation after all callers
have moved.
## Stage 13 — Demonstrate Imports And Development Profiles
**Status:** Completed 2026-09-13.
**Goal:** Supply two maintained operator examples covering simple and composed
configuration, with a clear distinction between the two profile systems.
**Work:**
1. Keep `examples/dnd-minimal.config.yml` self-contained.
2. Keep `examples/dnd-complete.config.yml` as the complete root and move its
whole `dnd-session` pipeline into
`examples/pipelines/dnd-session.yml`, imported through `configkit.imports`.
Only the root declares version. Fix fragment-relative campaign/catalog
references to `../dnd-party.txt`, `../dnd-glossary.txt`, and
`../dnd-spell-catalog.json` where applicable.
3. Move the existing PromptKit example to
`examples/llm-profiles/dnd-extraction.yml` and point the root's
`promptkit.profile_file` to `./llm-profiles/dnd-extraction.yml`.
4. Add `examples/profiles/development.yml` as a ConfigKit overlay. Set
`concurrency.total_llm: 1` and `stage_workers.extract: 1`, plus a development
output directory. Include a short comment explaining how an operator can
change the pipeline's `llm_profile` independently; do not invent an
unavailable model profile just to demonstrate the field. The overlay has
no version or import directive.
5. Extend the existing maintained-example test to load base and development
configurations through production code, validate catalogs/profiles offline,
and exercise complete fake-provider reference materialization from a
working directory other than the example directory.
6. Update `docs/config.md`, `docs/operations.md`, `docs/consumers/subprocess.md`,
and `docs/consumers/dnd-pipeline.md` at their canonical scopes. Link the
examples; explain disjoint composition and whole-sequence replacement
instead of duplicating complete configs in prose.
**Acceptance:** all example paths resolve, the base and development selections
validate, the full pipeline still contains every implemented D&D lane, and
operators can distinguish `--config-profile` from `--llm-profile`.
**Validation:** `go test ./internal/cli`; production CLI validation for both
roots and the development overlay. No API key or live generation is needed;
use only the documented placeholder if offline PromptKit validation requires
one. Verify all moved example links.
**Prompt size:** Medium; examples and their operator-facing documentation.
## Stage 14 — Final Architecture And Contract Verification
**Status:** Completed 2026-09-13.
**Goal:** Verify the completed architecture and reconcile canonical docs with
the implemented boundaries.
**Work:**
1. Review the complete diff against `configkit.md` and this plan. Check that no
stage was bypassed, no source-origin workaround weakened the selected
contract, and no unsupported step-composition semantics slipped in.
2. Finalize `docs/internal/configuration.md`, `docs/internal/cli.md`,
`docs/internal/state.md`, and `docs/internal/overview.md`. Add
`docs/integrations/pkg-configkit.md` for the pinned API boundary and relevant
upstream semantics; link upstream for general library behavior.
3. Update architecture's configuration ownership/precedence text and finalize
the adoption ADR. Document public behavior in its canonical owner; do not
retroactively edit historical release notes or prior accepted ADR decisions.
4. Ensure ConfigKit warning/diagnostic handling, safe rendering, source-relative
paths, runtime option validation, direct construction, and digest semantics
all have sufficient focused coverage. Do not duplicate upstream's test
suite or create exact-output change detectors.
5. Verify current docs no longer recommend v4 syntax, retired environment
overlays, the former PromptKit example path, or `effective-config.json`.
Migration explanations and historical records may legitimately name them.
6. Complete the checks below and report any remaining issue explicitly. Do not
tag a release, push, or retire the active roadmap during implementation
without a separate request.
**Acceptance:** every feature acceptance criterion holds, current docs and
examples agree with runtime behavior, all required checks pass, and the
remaining configuration code expresses Notarius policy rather than rebuilding
ConfigKit machinery.
**Validation:**
```sh
go test ./...
go vet ./...
go build ./cmd/notarius
go run ./cmd/notarius config validate --config examples/dnd-minimal.config.yml --pipeline dnd-session
go run ./cmd/notarius config validate --config examples/dnd-complete.config.yml --pipeline dnd-session
go run ./cmd/notarius config validate --config examples/dnd-complete.config.yml --config-profile development --pipeline dnd-session
go run ./cmd/notarius pipelines list --config examples/dnd-complete.config.yml --config-profile development --json
go run ./cmd/notarius config effective --config examples/dnd-complete.config.yml --config-profile development
go run ./cmd/notarius config explain --config examples/dnd-complete.config.yml --config-profile development /concurrency/total_llm
git diff --check
```
Use the project's normal link-check mechanism if present; otherwise check
changed relative links and heading anchors manually. Live model evaluation is
unnecessary for a configuration migration.
**Prompt size:** Medium; verification and focused documentation reconciliation,
with no planned production redesign.
## Released API And Readiness
ConfigKit v0.3.0 provides both required query methods in its public
`lifecycle.go`:
- `(*Resolution).InputInfo(Path) (FieldInfo, bool)`: fixed decoded entry
metadata, including retained defaults and explicit null; missing or
shape-mismatched paths return false.
- `(*Resolution).Source(string) (SourceInfo, bool)`: detached acquired-source
records; empty/unknown IDs return false; non-file sources have no invented
filesystem path.
Both methods are callback-scoped and perform no new filesystem or credential
lookup. Their entry-state contract permits querying original paths even after
map-key normalization. Existing `Normalize`/`Record` and result renderers
complete the one-load workflow; no upstream extension remains outstanding.
Review `docs/releases/v0.3.0.md`, the recorded-resolution contract in
`docs/config.md`, and `examples/consumers/consumers.go` /
`consumers_test.go` in the pinned dependency. The example demonstrates
`LoadNotariusPaths`; adopt its public-API usage, not its optional fallback
policy. Its warning-and-leave-relative behavior is not the Notarius contract.
The public-facade refactor needs no consumer migration; do not reach into the
private engine. Go 1.25 compatibility and the private YAML v4 prerelease pin
are unchanged. Upstream macOS support is best-effort, matching Notarius policy;
do not add platform gates as part of this adoption.
No open product decisions or upstream prerequisites remain.

View File

@@ -1,214 +0,0 @@
# ConfigKit Adoption Review Findings
## Scope And Assessment
Reviewed the adoption commits from `c403a38` through `3010a91` against
[the feature roadmap](configkit.md) and [the implementation plan](implementation.md).
The main architecture is implemented: ConfigKit v0.3.0 owns composition,
decoding, provenance, and frozen inspection; Notarius owns document semantics,
source-aware path policy, detached runtime conversion, and catalog resolution.
The old YAML loader and operational environment-overlay layer are removed.
The following findings require remediation. No application code was changed
during this review. Reproductions used temporary configuration files and the
built production CLI, without provider calls.
## Finding 1 — Record Normalization At Sequence Boundaries
Status: Addressed. Scoped normalization records now cover steps and validators;
load-boundary regressions verify normalized runtime values and inspection.
Priority: P1. Valid configuration can fail loading.
Evidence: `internal/core/config/pipeline_document.go:194` records individual
step-field changes; validator normalization at the same file's binding
traversal similarly records individual validator fields. ConfigKit audits
these sequence changes at the enclosing sequence boundary. The special-case
steps record in `document_paths.go` covers relative-reference resolution,
but not other sequence normalization.
This otherwise valid pipeline fails plain `config validate`:
```yaml
version: 5
pipelines:
session:
input: {module: seriatim}
steps:
- id: " first "
artifacts:
spells:
extract: {module: dnd/spells}
```
Observed: `resolve.unrecorded_change` at `/pipelines/session/steps`.
Likewise, a single-step extractor with
`validators: [{module: " generic/valid_json "}]` fails at the validators
sequence. Accepted whitespace normalization must not depend on the presence
of an unrelated relative reference or enclosing map-key rename.
Required correction:
- Record legitimate sequence transformations at the covering boundary required
by ConfigKit, including nested validators and ordered steps.
- Consolidate the relative-reference-specific workaround into a coherent
normalization-recording approach. Do not disable the audit or record the
entire configuration indiscriminately.
- Preserve original-path lookup and useful normalization reasons.
Acceptance: the two reproductions load and normalize correctly; nested
module/profile/selector/key changes in ordered steps also work; effective
values match runtime values and explanation retains declaration provenance.
Add focused load-boundary regression tests without duplicating upstream tests.
## Finding 2 — Preserve Explicit Presence In Document Validation
Status: Addressed. Plain loading rejects unsupported empty validator overrides
and invalid reference branches; supported empty overrides retain their meaning.
Priority: P2. Plain validation and runtime conversion disagree about invalid
configuration.
Two concrete gaps in `internal/core/config/pipeline_document.go`:
- At line 405, disallowed validators are checked with `len > 0`.
`input: {module: seriatim, validators: []}` passes plain
`config validate`, but selected-pipeline validation rejects it because the
runtime override is explicitly set. Output bindings have the same gap.
- At line 428, reference source selection tests whether the path is nonblank,
rather than whether the branch was supplied. A binding-local reference
`{path: "", artifact: {step: previous, lane: registry}}` passes plain
validation. Conversion in `loaded_config.go` then silently drops the
explicitly invalid path branch and selects the artifact. Whitespace-only
paths have the same problem.
Required correction:
- Reject an explicitly configured validator override on input/output, including
an empty list, while retaining empty-list disabling on supported stages and
the separately agreed empty lane-level legacy-list behavior.
- Validate reference union branch presence separately from branch content.
Reject a supplied empty path and reject two supplied branches; retain
existing null rejection and valid external/generated forms.
- Keep presence-only rules in the document boundary and share semantic
predicates with runtime validation where practical. Do not add a second
complete validation pass simply to compensate for these omissions.
Acceptance: both invalid examples fail plain loading with field-specific
diagnostics before runtime conversion; valid empty validator overrides on
chunk/extract/merge/normalize and valid reference forms remain accepted.
## Finding 3 — Restore Worker-Key Normalization Without Losing Explicitness
Status: Addressed. Worker keys normalize before defaulting; original input
presence preserves explicit counts. Regression tests cover defaults and collisions.
Priority: P2. The migration introduced an unplanned restriction.
The former `normalizeStageWorkers` trimmed keys and rejected normalized
collisions. The new operational resolver does not normalize worker keys before
`deriveDocumentExtractWorkers`; `validateStageWorkerKey` requires the literal
`extract`. The following formerly supported input is now rejected:
```yaml
version: 5
concurrency:
stage_workers:
" extract ": 2
```
Observed: `validate.error` reports the padded key as unsupported. The target
retains whitespace-normalized worker identifiers.
Required correction:
- Normalize worker keys with deterministic collision checking and a recorded
map transformation before applying derived defaults.
- Preserve the original declaration's explicitness: ConfigKit presence queries
use entry-state paths, so checking only the newly canonical key would mistake
a moved explicit worker count for an omitted count and overwrite it.
- Reuse the existing map/path machinery where useful rather than adding a
parallel normalization framework.
Acceptance: the example retains a worker count of 2, omitted workers derive
from effective total concurrency, explicit invalid counts still fail, and
`extract` plus `" extract "` fails as a collision. Inspection explains the
normalization instead of reporting a synthetic default for the explicit value.
## Finding 4 — Include Source Filenames In Load Diagnostics
Status: Addressed. CLI load errors include a quoted source legend; command tests
cover imported/profile failures, source coordinates, and terminal-control escaping.
Priority: P2. Import/profile failures lack actionable file attribution.
`internal/cli/configkit_load.go:29` calls `WriteDiagnostics` with diagnostics
only and discards `LoadError.Sources()`. Errors display locations such as
`(source-1:6:7)` without any filename or source-ID legend. With composed
configurations, an operator cannot identify the file to edit from that output.
Stage 8 explicitly called for safe source context using source records.
Required correction:
- Continue using ConfigKit's canonical diagnostic renderer and add a small,
deterministic source-ID-to-file/label presentation using its public source
records. A source legend is sufficient; do not rebuild a diagnostic framework.
- Quote/escape source labels safely, and keep all diagnostics on stderr.
Do not print configuration contents or wrap the same failure repeatedly.
- Cover an error originating in an imported file or selected profile, not only
a main-file failure.
Acceptance: stderr identifies the actual declaring filename alongside the
diagnostic's source ID, line, and column; stdout remains empty on failure;
no credentials, raw YAML, or unescaped terminal controls are added.
## Finding 5 — Finish Current Documentation And Comment Migration
Status: Addressed. The validator example uses canonical objects and passes
production selected-pipeline validation; the loader comment describes current behavior.
Priority: P2 for the unusable operator example; P3 for the stale comment.
`docs/config.md:539` explains how to enable the combat semantic validator,
but five entries in its YAML snippet still use scalar validator shorthand.
Version 5 requires objects. Copying even its first scalar entry produces
`decode.type`: “configuration value must be a mapping.”
`internal/core/config/load.go:18` also still describes the production loader
as under construction and claims commands use the deleted version 4 loader.
Required correction:
- Convert every validator in that current example to the canonical
`module:` object form, preserving order and retry settings.
- Check other current snippets for the same migration omission, excluding
intentional historical/migration examples.
- Replace the obsolete loader comment with its implemented responsibility.
Acceptance: a minimal v5 configuration containing the documented validator
chain passes production selected-pipeline validation offline, and current
comments no longer describe a dual-loader transition.
## Verification
Already passed during review:
- `go test ./...`
- `go vet ./...`
- Production CLI build.
- Selected-pipeline validation of both maintained D&D examples, plus the complete
example with the development configuration profile.
- Effective/explanation inspection smoke checks.
The existing green suite does not cover the failing edge cases above. After
remediation, add the focused behavioral coverage described per finding, rerun
the full checks, and validate the corrected documentation example. No live
LLM tests are needed.
### Remediation Verification
All five findings are addressed. Added load-boundary regressions cover sequence
normalization, explicit branch validity, and worker-key presence; CLI tests cover
source legends. After these changes, the full Go tests, vet, and build passed,
as did production selected-pipeline validation of the corrected documentation
snippet and the minimal, complete, and development-profile examples.