diff --git a/docs/roadmap/configkit.md b/docs/roadmap/configkit.md index de92d71..1443238 100644 --- a/docs/roadmap/configkit.md +++ b/docs/roadmap/configkit.md @@ -1,8 +1,9 @@ # ConfigKit Adoption -Status: agreed feature roadmap. ConfigKit v0.5.0 supplies the -required upstream capabilities. A separate implementation plan will define stages, -signatures, and execution details. No implementation is authorized by this document. +Status: agreed feature roadmap. ConfigKit v0.5.0 supplies the required upstream +capabilities. The [implementation plan](implementation.md) owns execution steps +and identifies one remaining catalog-validation placement decision. This document +owns the intended end state, not implementation authorization. ## Purpose @@ -171,8 +172,8 @@ provider setting deliberately suppresses it; it must not restore the same settin from backend defaults. A null provider map suppresses the configured lower map, while null entries inside a provider map remain literal JSON null values. This supports reusable bases and deliberate removal of stale settings without changing -ConfigKit's merge rules. Framework-required settings such as timeouts need explicit -field-by-field rules in the implementation plan rather than blanket nil defaulting. +ConfigKit's merge rules. Framework-required settings such as timeouts have explicit +field-specific null semantics rather than blanket nil defaulting. Preserve ConfigKit public loading errors through wrapping for `errors.As`, with a PromptKit configuration category. Use source-aware domain/resource failures where @@ -231,9 +232,8 @@ Expose a read-only inspection view backed by retained ConfigKit state, with targeted path queries, source/history information, and explicit redacted effective and explanation output. Reuse suitable public ConfigKit inspection values rather than mirroring them. Do not expose mutable runtime definitions, resource capabilities -merely for diagnostics, or request credentials. Exact Go signatures belong in the -implementation plan; the scope is generic targeted inspection, not just narrow -profile/prompt origin summaries. +merely for diagnostics, or request credentials. The scope is generic targeted +inspection, not just narrow profile/prompt origin summaries. PromptKit ships no CLI and emits no automatic debug reports. Applications own presentation, access control, and disclosure; redaction does not make all prompt @@ -311,6 +311,5 @@ does not discard lower endpoint, reasoning, provider, or inheritance settings. The end state is one shared configuration model with the old loading/resolution machinery removed, useful operator inspection, and all substantive execution workflows still available. The six previously open design choices have been -approved and incorporated into the owning sections above. Field-level details and signatures -belong in the decision-complete implementation plan, not a compatibility-preserving -reinterpretation of this roadmap. +approved and incorporated into the owning sections above. This is not a +compatibility-preserving reinterpretation of the existing configuration engine. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md new file mode 100644 index 0000000..6c0f178 --- /dev/null +++ b/docs/roadmap/implementation.md @@ -0,0 +1,567 @@ +# ConfigKit Adoption Implementation Plan + +Status: ready for staged implementation, except for the explicitly identified +catalog-validation decision under Open Questions. This is a planning document; +writing it does not implement the feature or authorize publishing dependencies. + +## Authority And Working Method + +The [ConfigKit feature roadmap](configkit.md) owns the intended end state and +approved policy. Its intentional-breaking-change direction is authoritative: +preserve substantive prompting capabilities, not the old loader, signatures, +source wrappers, incidental error ordering, or configuration syntax. + +Implement the stages below in order, one stage per coding-agent prompt. Each +stage includes its tests and affected current-state documentation. Read +[development](../development.md) and the applicable policies before starting; +in particular, follow [agents](../policy/agents.md), +[architecture](../policy/architecture.md), [testing](../policy/testing.md), +[documentation](../policy/documentation.md), and [OSS](../policy/oss.md). +Catalog work also follows each catalog repository's own instructions and release +procedure. Do not turn this plan's internal organization into new public promises. + +Begin each stage by inspecting the worktree and earlier stages. Commit completed +units under the agent policy. Keep stages buildable; temporary private wiring +needed to connect successive stages must be removed by Stage 11. Do not ship a +legacy mode, dual loader, or old-format conversion layer. Do not add prototype +applications or a generic configuration framework of our own. + +Planning baselines: + +- PromptKit runtime v0.11.2, commit + `12d6d80024e80c552d122a076c22429bf6f2f6c2`; approved roadmap at `521291d`. +- ConfigKit v0.5.0, commit + `dc09959cca4e105f49ec316c6d4ba585bd2340bd`, module + `gitea.maximumdirect.net/eric/ConfigKit`. +- Both catalog dependencies currently select `/v2` at `v2.20260926.0`. + +Use ConfigKit v0.5.0 for this work. Do not silently upgrade to a different +contract. The catalog asset migration needs coordinated releases, not a committed +workspace or local replacement. Exact catalog release versions are selected at +publication under their existing date-based policy; record the versions and full +commits actually consumed. Publishing any repository requires separate authority. + +## Implementation Decisions + +### Construction Surface + +Keep `NewEngine(Config, ...Option)` and the execution/injection surface where it +still serves its substantive purpose. Replace profile/prompt source options with +these fields and helpers; this is an intentional API break: + +```go +type ConfigurationSources struct { + Underlays []configkit.Underlay + Main *configkit.Input +} + +// Add Sources; remove PromptDir and ProfileDir. +// Retain SchemaDir, Timeout, HTTPClient, and TransientRetries. +type Config struct { /* existing non-definition settings; Sources ConfigurationSources */ } + +func DirectoryInputs(directory string) ([]configkit.Input, error) +func FilesystemInputs(label string, filesystem fs.FS, root string) ([]configkit.Input, error) +``` + +The pointer distinguishes omitted main input without inspecting ConfigKit's +opaque Source. Nil main becomes `configkit.NoSource()`; a supplied invalid Input +is not silently ignored. Prepend two private underlays: external catalogs, then +PromptKit-owned definitions. Append caller underlays in their supplied order; +main is last and keeps its ConfigKit meaning. Imports follow each Input's flag. +Do not expose named overlays, CLI overrides, or VersionRule through another +PromptKit abstraction in this feature. + +Show the usual application categories as caller underlays ordered fallback, +operator, programmatic, with main omitted. For a single highest-priority document, +main is also useful. These are explicit selections, not directory-name heuristics +or a second automatic precedence algorithm. Each directory helper returns inputs +for one strict-composition group, recursively finding `.yaml` and `.yml` files; +sorted discovery makes diagnostics reproducible, not one file higher priority. +An explicitly selected missing/unreadable root fails; an empty directory supplies +no contributions. Helpers set imports false; callers may enable them explicitly. +Use one ConfigKit filesystem namespace per helper invocation and root resource +contexts at the supplied directory. Single files use ConfigKit directly. + +Remove `WithPromptFS`, `WithPromptFile`, `WithProfileFS`, `WithProfileFile`, +`WithFallbackProfileFS`, and `WithProfiles` at public cutover. Retain schema source +options and collaborator/backend injection. Do not require any prompts to exist +merely to construct an engine for profile inspection. + +Programmatic configuration uses labeled `configkit.Mapping` inputs. Replace the +old zero-sentinel `Profile`/`OpenAICompatibleProfileConfig` pair with one small +`ProfileConfig` convenience and a source builder: + +```go +type ProfileConfig struct { + Extends, Backend, Endpoint, Model *string + Temperature, TopP *float64 + MaxTokens, TimeoutSeconds *int + ServiceTier, ReasoningEffort, APIKeyEnv *string + APIKeyRequired *bool + ExtraParams map[string]any +} +func ProfileSource(label string, profiles map[string]ProfileConfig) configkit.Source +``` + +Nil convenience fields mean omitted; nonnil pointers preserve zero/false/empty. +Nil ExtraParams omits the map; a nonnil empty map supplies an empty mapping, not a +clear operation. Use raw Mapping for explicit null, prompt definitions, or richer +configuration. Do not introduce a public generic optional-value type just for +this helper. Convert fields explicitly to ConfigKit-supported built-in values, +then call Mapping for snapshot ownership and load-time errors. Do not marshal +through YAML or infer absence from a value. Keep the existing local-backend +constructor, which configures runtime backend registration rather than profiles. + +### Typed Configuration And Execution Values + +Add `internal/configuration` as the cohesive ConfigKit adapter. Its private +document DTO has `profiles: map[string]profileConfig` and +`prompts: map[string]map[string]promptConfig`. Collection entries are mappings, +not nullable definition pointers. No duplicate identity fields; ConfigKit consumes +`extends`, so no DTO field or second resolver handles it. Use `configkit.Keys`, +`Key`, and `Index` for semantic paths, never split IDs on dots. + +Profile DTO fields match ProfileConfig's YAML spellings (`backend`, +`reasoning_effort`, etc.). Nullable pointers plus ConfigKit presence metadata +distinguish absent, value, and explicit null. Store that distinction in small +private execution-owned fields or presence bits, not retained metadata queries. +Runtime/domain packages must not import ConfigKit. Do not duplicate its tree, +merge engine, inheritance graph, diagnostics, or presence framework. + +Use the existing prompt fields under each keyed version, without `id`/`version`. +Use pointers for `content`, `content_file`, and optional clearable strings; +preserve existing input, cache-control, output-contract, and role rules. +`messages` must be nonempty; each message has exactly one nonnull content source. +An explicitly supplied empty inline string is a valid template. Null content +removes that selection; null content_file removes its reference. Output remains a +required mapping with valid format and validation mode. Optional description, +session template, default profile, cache control, inputs, and schema path can be +cleared where their domain permits absence. Null required role/format/mode or a +null whole definition is invalid. Scalar `required` and `repair_attempts` retain +their false/zero defaults and are not nullable. Null inputs means no declarations. +Unknown fields are ConfigKit errors, including old `id`, `version`, and +`base_profile`. Prompt versions must be string mapping keys. + +Literal definition keys remain exact; require nonblank identity but do not +normalize/rekey maps after ConfigKit has resolved relationships. Update selection +to use exact nonblank IDs/versions rather than silently trimming them into a +different identity. Existing value normalization (for example role lowercase or +endpoint trimming) is recorded through ConfigKit's resolution callbacks. Field +references to definition IDs remain exact as well. + +Profile field rules after configuration overlay/inheritance: + +| Fields | Absent | Explicit value | Explicit null | +| --- | --- | --- | --- | +| `backend` | No selected backend | Exact registered ID; blank means none | No backend | +| `endpoint` | Selected backend endpoint, if any | Normalize/validate nonblank URL; empty clears | Clear, do not recover backend endpoint | +| `model` | Unspecified | Nonblank supplied model; empty clears | Unspecified | +| `temperature`, `top_p`, `max_tokens` | Unspecified | Validate existing bounds; zero is explicit and sent | Suppress outbound control | +| `reasoning_effort`, `service_tier` | Unspecified | Existing normalization; blank suppresses | Suppress outbound control | +| `timeout_seconds` | Framework 600-second baseline | Nonnegative; zero disables generation deadline | Reset to framework baseline, not lower profile timeout | +| `api_key_env` | Selected backend's optional lookup name | Trimmed value; blank disables lookup | Disable lookup, do not recover backend name | +| `api_key_required` | False | Explicit boolean, including false overriding true | False | +| `extra_params` | Backend defaults | ConfigKit-composed profile map, recursively overlaid on backend map | Suppress entire configured provider map | + +Backend registry defaults are applied once during snapshot construction, after +effective profile inheritance. Keep them out of the inheritance input: a parent's +derived endpoint/key/defaults must not follow a child that changes backend. +Provider map null entries are literal JSON null; empty mappings retain children. +For backend-to-profile provider-map overlay, use a narrow JSON-value merge helper +with the same recursive-map/value-replacement semantics; it is not a configuration +loader and must not interpret `extends` in backend runtime values. + +Record any defaults/normalizations written into the typed configuration via +ConfigKit Resolve, with dependencies on relevant configuration paths. Do not +project runtime collaborators or credentials into the configuration tree just to +report them. Inspection describes resolved configuration, while InspectProfile +and preparation describe backend-completed execution settings. Make this boundary +explicit rather than inventing synthetic provenance for registry defaults. + +Retain APIKeyRequired's direct-key workflow: true suppresses a backend-derived +environment source. An explicitly configured profile environment source, or a +request environment override, remains usable; false restores ordinary optional +credential semantics. No credential values belong in the DTO, inspection, or +construction-time environment resolution. Raw profile `api_key` remains invalid. + +Validate all effective supplied values and referenced backend IDs at construction. +Do not require a complete model/endpoint there or reject unused partial profiles. +Do not require a prompt default_profile to be runnable before a request can +override it. Final target completeness is checked after runtime overrides. +InspectProfile can report a partial target without credential or provider access. +Keep request override contracts otherwise unchanged, including complete nonempty +runtime ExtraParams replacement and reasoning clearing. A runtime literal +`extra_params["extends"]` remains ordinary provider data. Do not expand this work +into a redesign of every request override. + +### Snapshot And Inspection Design + +The loader returns indexed, execution-owned definitions plus a private retained +ConfigKit result. An adapter may implement the runner's narrow lookup interfaces +over maps during migration; no repository chain, fallback-on-error, or per-call +configuration load survives. Preserve defensive ownership across injected clients +and public results. Cache prompt definition hashes at construction; hash effective +captured content, not a stale resource path. Hash bytes need not match the old +configuration format. Preserve rendered-prompt hashing semantics. + +Resolve each effective content_file using its exact field origin and ConfigKit +SourceContext. Join its relative name to BaseDir inside the returned rooted FS; +reject absolute/outside-root paths and close every opened handle. Preserve path +text, including meaningful whitespace, after separately checking nonblankness. +Do not choose the child's source for an inherited reference. If there is no unique +contributing source with context, fail with an actionable path/source error; the +caller can supply an explicit Input filesystem context or override the field. +Retain no resource capability for the purpose of public diagnostics. + +Capture message text and parse message/session template syntax at construction +with the same function names/options used for runtime rendering. Do not invoke +input helpers or require Vars/artifacts while parsing. Reuse compiled templates +where practical, binding request-specific input functions on safe per-call clones; +never mutate a shared template function closure with request state. Runtime +missing-variable/input failures remain runtime errors. + +Expose a concrete read-only facade value: + +```go +func (e *Engine) InspectConfiguration() (ConfigurationInspection, error) +func (v ConfigurationInspection) Metadata() configkit.Metadata +func (v ConfigurationInspection) Sources() []configkit.SourceInfo +func (v ConfigurationInspection) Diagnostics() []configkit.Diagnostic +func (v ConfigurationInspection) Explain(path configkit.Path) (configkit.Explanation, bool) +func (v ConfigurationInspection) WriteEffective(w io.Writer) error +func (v ConfigurationInspection) WriteExplanation(w io.Writer, path configkit.Path) error +``` + +Store an unexported narrow implementation reference; do not expose Result.Config, +ConfigKit SourceContext, mutable runtime values, or internal types. Delegate +queries and redaction to the retained result. Nil Engine returns ErrInvalidConfig; +the zero inspection view has empty query results and writer operations return +ErrInvalidConfig. Writer errors remain inspectable. Mark credential-shaped dynamic +keys sensitive using Definition.Secret, reusing the existing catalog secret-key +vocabulary; do not redact `api_key_env` names as credential values. This is not a +promise that arbitrary prompt/provider content is safe to disclose. + +Keep the runner independent of this view. Inspection calls perform no discovery, +resource reads, environment credential lookup, or provider operations. Schemas, +artifacts, preparation ownership, generation admission, retry/repair, observations, +and cancellation remain at their established runtime boundaries. + +## Stage 1 — Add The Dependency And Typed Loading Core + +Scope: module metadata, `internal/configuration` DTOs, real ConfigKit loading, +and its focused tests. Leave engine assembly on the old path for this stage. + +1. Add ConfigKit v0.5.0; update go.sum and factual OSS notices for it and its + selected transitive dependencies. Preserve project licenses and the Go floor. +2. Add the keyed DTOs and a private load entry point accepting ConfigKit Inputs + and underlays. Call LoadLayered once, with no VersionRule or secret resolution + bindings for API credentials. Use callback-scoped Resolve/Validate correctly. +3. Implement presence extraction and the field rules above. Validate supplied + domain values through existing domain helpers; no final target requirement. + Record normalization/default mutations, not arbitrary DTO bookkeeping fields. +4. Preserve `*configkit.LoadError` through wrapping. Domain callback messages + identify paths/problems without interpolating raw values or credentials. +5. Add representative real-loader tests: keyed literal IDs, strict group conflict, + recursive cross-tier overlay, a shared base with explicit zero/null/false, an + unused invalid effective definition, and a malformed lower source not hidden + by an overlay. Do not reproduce ConfigKit's parser/inheritance matrix. + +Exit: focused tests and `go test ./...` pass; old engine behavior is not presented +as migrated. Internal inventory documents only the loader now actually present. + +## Stage 2 — Add Source Discovery And Prompt Resource Capture + +Scope: source selection helpers and construction-time prompt materialization. + +1. Implement private directory/FS discovery behind the chosen helper signatures; + wrap discovered paths as ConfigKit sources, retaining explicit root context. + Reuse useful filecatalog path handling without carrying profile metadata scans. +2. Materialize effective prompt message text using field origins and passive + contexts. Move reusable prompt/input/cache/output validation into the loader + or shared domain owner; do not copy entire legacy parsers. +3. Build immutable prompt indexes by exact ID/version and an ambiguity indicator + for omitted versions. Capture declared-input inspection data and prompt hashes. +4. Validate static template syntax using the renderer's grammar/function setup. + Refactor that setup into one private owner if needed; do not render at load. +5. Test native and embedded sources, imported/inherited relative content from a + different directory, explicit mapping context, contextless failure, handle + closure, malformed static syntax, and resource mutation after capture. Use + minimal fixtures, sharing scenarios where the same adapter is exercised. + +Exit: loader creates usable frozen prompt definitions without request data. +`go test ./internal/configuration ./internal/prompt ./internal/promptdef` passes; +existing execution tests still work through the old assembly until cutover. + +## Stage 3 — Make Execution Settings Presence-Aware + +Scope: domain settings, target resolution, wire-presence propagation, profile +inspection. No new source discovery in execution packages. + +1. Add execution-owned presence/suppression state and construct indexed profiles + from loader output plus the immutable backend registry. Apply registry-derived + values after inheritance, using the field table above. +2. Replace scalar-zero/blank merge assumptions in target resolution. Configuration + explicit zero reaches injected clients and the built-in request body; null does + not resurrect a provider setting. Extend existing TargetPresence plumbing + through preparation/results as necessary rather than creating a parallel path. +3. Preserve request-level override precedence, API-key availability behavior, + endpoint-only profiles, backend identity/capacity, and partial-profile runtime + completion. Move final completeness checks after overrides. InspectProfile + returns partial settings without treating missing model/endpoint as load errors. +4. Test explicit numeric zero versus omission, map overlay and null, backend + change without derived defaults leaking from parent, timeout null/zero, + credential-source suppression, and completion through request overrides. + Include one built-in-client wire test, not just an injected-client assertion. + +Exit: focused domain/usecase/client tests pass. Any temporary adaptation for the +old assembly is private, clearly limited to reaching Stage 7, and does not become +a compatibility promise. Update affected target/presence GoDoc with this change. + +## Stage 4 — Migrate The OpenRouter Catalog Assets + +Scope: `../promptkit-backend-openrouter` only. Resolve the catalog-validation open +question before implementing its consumption strategy in Stage 6. + +1. Read that repository's instructions; preserve unrelated work. Convert each + profile asset to `profiles: {literal-id: {settings...}}`, preserving inventory + and values, changing `base_profile` to `extends`, and removing embedded ID. + Keep per-profile files and the exported FS/Root package; no dependency on + PromptKit and no second legacy asset tree. +2. Audit zero/empty values that previously meant unspecified. Remove those when + omission was intended; retain intentional explicit values. Review any dynamic + provider `extends` keys rather than silently changing outbound payloads. +3. Leave backend.json and its schema unchanged. Update catalog format notes and + applicable tests to the keyed format, with a prominent incompatible-format + migration note. Model availability is not probed. +4. Validate under its procedure and commit. If publication is authorized, publish + the next unused `v2.YYYYMMDD.N`; otherwise report the exact commit awaiting + publication. The date/revision is selected then, not guessed in this plan. + +Exit: one reviewed migrated catalog commit; existing immutable tags still serve +old consumers. The profile-data change does not change the Go module path/API or +the JSON backend manifest. Warn old loaders not to consume the new assets. + +## Stage 5 — Migrate The Rakestrawhome Catalog Assets + +Scope: `../promptkit-backend-rakestrawhome` only. Apply the same keyed-format, +presence, notice, validation, and publication treatment as Stage 4 to its smaller +inventory. Preserve backend endpoint, credentials metadata, and capacity policy. +Do not couple its date/revision to OpenRouter's release. + +Exit: both compatible catalog versions are published and ordinarily resolvable +before Stage 6 pins them. If permission or publication is unavailable, stop at +this external dependency checkpoint; do not commit a local replace, guessed +version, or legacy-to-new runtime converter to bypass it. + +## Stage 6 — Integrate Catalog Sources And Maintained Profiles + +Scope: `internal/catalog`, maintained definitions, catalog integration tests, +dependency pins/notices. Keep this separate from public API migration. + +1. Pin the published compatible catalog versions and exact attribution commits. + Keep strict backend.json loading/validation and registry construction. +2. Replace catalog profile repositories with ConfigKit file Inputs for the first + underlay. Supply PromptKit-owned keyed Mapping definitions as the next group: + standard extends the selected catalog's current maintained base; auto retains + its current direct model/backend definition. Preserve both literal names. +3. Implement the selected catalog source-policy strategy from Open Questions. + Preserve reserved-name, ownership, connection-metadata, and secret checks; + consumers may still override catalog profiles and their bases. Do not apply + catalog-only namespace restrictions to application sources. +4. Test real imported assets offline with the new loader, maintained-name + resolution, consumer override of a maintained base, and synthetic malformed + catalog fixtures for the source-policy risks. No upstream endpoint probes. + +Exit: catalog and owned definitions participate in the new keyed configuration +model without a legacy profile parser on that path. Catalog integration tests +pass against released modules with `GOWORK=off` and no replacements. + +## Stage 7 — Cut Over Engine Assembly And Public Construction + +Scope: `engine.go`, configuration facade/helpers, `internal/engine`, facade +adapters, and migration of their direct test setup. + +1. Introduce the Construction Surface exactly as specified above. Remove the + superseded source/profile options and convenience types rather than keeping + deprecated pass-through wrappers. Keep intentional runtime injection APIs. +2. Assemble registry, combined configuration snapshot, renderer, validator, + capacity manager, and runner. Return no Engine on construction failure. + Translate construction errors to ErrInvalidConfig while preserving LoadError + via errors.As. Resource errors include semantic path and safe source context. +3. Connect indexed prompt/profile lookups to preparation and inspection. Missing + selections keep meaningful not-found errors; prompt version ambiguity stays a + selection error. Old parse/load error categories need not be manufactured for + errors now detected at construction. +4. Move root test fixtures/setup and maintained example construction to ConfigKit + sources, keyed definitions, and ProfileSource so all consumers still compile. + Update expected timing only for the deliberate eager-validation change. Keep + behavioral assertions for prompting, overrides, and errors. +5. Add public tests for a minimal custom local backend and programmatic profile, + plus embedded defaults/operator/programmatic tiers. No elaborate consumer + setup should be needed merely to send one prompt. + +Exit: the public Engine uses only the new loading path. Root/package tests pass; +all package/example sources compile. Update GoDoc and consumer construction +guidance in this stage so removed options are not presented as usable. + +## Stage 8 — Complete Frozen Rendering And Lifecycle Integration + +Scope: renderer/runtime consumption of the snapshot, ownership, and lifecycle +regressions. Stage 2 already owns acquisition and static syntax validation. + +1. Use captured text and parsed templates without per-run configuration reads, + parsing, or inheritance. Bind input helpers per invocation safely; keep missing + request data errors and direct-session template bypass behavior. +2. Verify preparation and execution still handle artifacts and schema compilation + at invocation time, honor output overrides, freeze prepared resources, and + keep prepared handles bound to their engine. Credentials remain at runtime. +3. Test an old engine after configuration/resource edits and a newly constructed + engine observing the edits. Test concurrent renders with different Vars/input + helpers and no data leakage. Use an FS that rejects reads after construction + to demonstrate no configuration/content-file reopening during ordinary runs; + do not incorrectly prohibit request artifact/schema reads. +4. Preserve appended-message ordering, structural repair, retries, admission, + cancellation, observers, and result metadata through existing regression + tests; add only distinct missing cross-boundary cases. + +Exit: focused race tests pass for assembled rendering/preparation and lifecycle. +No retained callbacks capture ConfigKit resolution contexts or request state. + +## Stage 9 — Expose Explicit Configuration Inspection + +Scope: the read-only facade and retained ConfigKit inspection adapter. + +1. Implement ConfigurationInspection and its specified methods without exposing + Result.Config or filesystem capabilities. Retain a private narrow view of the + ConfigKit result; no regeneration of origins from runtime values. +2. Ensure effective/explanation reports use ConfigKit redaction, including the + selected sensitivity predicate. Preserve safe value-free domain diagnostics. +3. Test a targeted literal-key path with layered/inherited history, detached + returned slices/records, writer failures, zero/nil behavior, and redacted secret + shaped provider values. Demonstrate inspection still works after source removal + and makes no filesystem/provider calls. +4. Document construction inspection versus InspectProfile and prepared details. + Consumers choose report disclosure; do not add CLI commands or automatic logs. + +Exit: public inspection tests pass, including concurrent inspection/execution. +The runner has no reference to the inspector or ConfigKit metadata types. + +## Stage 10 — Migrate Examples And Canonical Format Guidance + +Scope: maintained examples and user-facing documentation. + +1. Complete the preparation, execution, and observation example migration begun + at public cutover. Preserve their distinct existing semantic assertions; extend + the preparation example with a small explicit inspection query if useful. +2. Update `docs/formats.md` to own the new keyed syntax, source-group semantics, + inheritance/presence/null table, eager validation, snapshot lifetime, and + resource-context rules. Link ConfigKit for generic grammar; do not copy its + complete parser/inheritance contract. +3. Update README and `docs/consumers/pkg-promptkit.md` with minimal local-backend, + file/embedded source, programmatic Mapping/ProfileSource, explicit inspection, + and engine-replacement workflows. Explain main versus ordered underlays. +4. Add concise old-to-new migration guidance in the consumer guide: removed API + options, exact IDs, `base_profile` to `extends`, zero/false/null, recursive maps, + no clear via `{}`, reserved `extends` runtime escape, failure timing, and the + fact that changing a model/backend does not clear unrelated explicit fields. + Remove old automatic reload claims. Release notes later link to this owner. + +Exit: all three offline examples run with their semantic checks; documentation +links and snippets match the implemented API. No unreleased version is tagged or +published, and no speculative release-note version is invented. + +## Stage 11 — Remove Superseded Machinery And Consolidate Tests + +Scope: dead loading/resolution code and its obsolete tests, module cleanup, and +internal architectural documentation. + +1. Delete lazy prompt/profile metadata prepasses, filesystem repositories, + fallback-on-error overlays, custom base-chain resolver/depth cap, zero-sentinel + profile merging, and temporary stage adapters once no caller needs them. + Retain narrow lookup interfaces only where they still simplify real consumers. +2. Retain useful filecatalog/schema utilities and JSON-value ownership/validation. + Remove direct yaml.v3 use and dependency if no remaining owner requires it; + do not claim all YAML dependencies disappear when ConfigKit still uses YAML. +3. Move meaningful regressions from deleted package tests to their new owning + boundary. Retire tests only about old internals, repeated parser coverage, or + superseded semantics. Do not replace lost runtime protection with loader-only + tests. Keep private assembly checks separate from external facade tests. +4. Update `docs/internal/overview.md`, `docs/internal/sources.md`, relevant renderer + and engine documents, and the implemented-structure paragraphs in architecture + policy. Describe the new package/dependency boundary without turning names or + chosen adapters into permanent invariants. Update OSS notices with module tidy. + +Exit: no legacy source path remains, no internal package imports the public root, +runtime/domain packages do not import ConfigKit, and `go test ./...` passes with +the reduced implementation/test inventory. + +## Stage 12 — Validate Scale And The Complete Consumer Workflow + +Scope: final integration evidence and high-confidence simplification, not another +feature expansion or release gate. + +1. Add focused benchmarks for construction with many independent definitions, + shared inherited bases, and several tiers; add an already-constructed selection + benchmark. Report allocations and input/output shape; inherited materialized + output/history may dominate. Do not impose timing thresholds or new caps. +2. Inspect for repeated full-map copies, per-run provenance calls, rescans, + repeated inheritance, and unnecessary double parsing. Fix demonstrated local + inefficiencies with straightforward ownership/indexing; no global cache. +3. Run the repository's existing complete local check script once on the final + candidate, including race tests and offline examples. Investigate failures; + do not weaken meaningful tests to get a green result. Stage intended new + files before the script's tracked-file checks. +4. Review the feature-roadmap capability list against actual public workflows: + independent inspection, partial profile completion, optional credentials, + source contexts, artifacts/schemas, prepared ownership, backend capacity, + overrides, appended messages, repair/retries, observations, and hashes. +5. Report completed stages, selected catalog versions, validation evidence, and + remaining issues. Minor-release documentation/version selection and roadmap + retirement belong to the separately authorized release preparation workflow. + +Exit: the feature roadmap's end state is demonstrably implemented, with one +configuration lifecycle, explicit operator inspection, no legacy loader, and +no newly invented provider checks or human release gates. + +## Open Questions + +### Catalog Source-Policy Validation Versus Single-Pass Loading + +ConfigKit v0.5.0 performs typed callbacks only after effective overlay/inheritance; +it does not expose a raw-source policy callback. PromptKit currently checks each +catalog's resolved backend ownership and prohibited fields before consumer +overlays. Checking only the effective merged configuration would allow consumers +to mask a broken catalog contribution. Conversely, independently loading every +catalog and then loading the original sources again duplicates parsing and +inheritance work. Feeding the first load back as Mapping loses original source +provenance and changes when inheritance occurs. Neither workaround should be +silently described as the agreed single-pass design. + +**Recommendation:** validate immutable catalog-only backend ownership and +source-policy rules in PromptKit's offline dependency contract tests, using real +ConfigKit and the selected released assets. At engine construction, retain +backend-manifest validation, strict source composition, effective domain +validation, and a history-based structural audit for reserved profile names, +forbidden connection fields, and prohibited secret-shaped keys contributed by +catalog sources (including masked paths). Use ConfigKit source IDs/history to +distinguish catalog contributions from permitted consumer overrides. Do not +parse Explanation YAML or rebuild an inheritance graph to recover historical +backend values. This keeps ordinary construction single-pass and treats pinned, +immutable dependency ownership as a tested distribution property. It changes the +location of part of existing catalog validation, so approval is needed before +Stage 6 rather than assuming it is merely an implementation detail. + +**Viable alternative:** keep independent construction-time catalog preflight +loads through ConfigKit, followed by the combined load using original inputs. +This retains the current fail-closed runtime catalog checks and original +combined-load provenance, with simpler validation code. It deliberately relaxes +the acquire/parse-once efficiency objective for the bounded maintained catalogs +and adds repeated inheritance work; do not add a global cache to hide that cost. +If selected, document this narrow exception in the feature roadmap and benchmark +it. It need not affect single-pass loading of application sources. + +An upstream raw-source policy hook could avoid the tradeoff, but it is a separate +ConfigKit feature request, not an assumed v0.5.0 capability or a reason to build +a replacement loader in PromptKit. All other decisions above are selected for +implementation; catalog publication permission is an operational checkpoint, +not an unresolved configuration-design choice.