diff --git a/docs/releases/v0.7.0.md b/docs/releases/v0.7.0.md new file mode 100644 index 00000000..a23fd6a4 --- /dev/null +++ b/docs/releases/v0.7.0.md @@ -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 --pipeline ` 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. diff --git a/docs/roadmap/configkit.md b/docs/roadmap/configkit.md deleted file mode 100644 index 9685fd87..00000000 --- a/docs/roadmap/configkit.md +++ /dev/null @@ -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 ` 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 `/profiles/.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] -``` - -`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. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md deleted file mode 100644 index ceb2f814..00000000 --- a/docs/roadmap/implementation.md +++ /dev/null @@ -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. diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md deleted file mode 100644 index 596e263f..00000000 --- a/docs/roadmap/remediation.md +++ /dev/null @@ -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.