diff --git a/docs/roadmap/configkit.md b/docs/roadmap/configkit.md new file mode 100644 index 0000000..6995648 --- /dev/null +++ b/docs/roadmap/configkit.md @@ -0,0 +1,319 @@ +# ConfigKit v1 Adoption And Identified Resource Collections + +Status: accepted direction; implementation pending. The source-builder API +choice at the end remains open. This roadmap describes future behavior, not +the currently supported configuration format. + +## Purpose + +Adopt `go.maximumdirect.net/configkit` v1.0.0 and simplify authoring by using +identified lists consistently for prompts, profiles, and backends. An individual +resource file should begin with its own `id`, without a resource-name wrapper. +Collection entries can import those files beneath the appropriate resource list. +ConfigKit's sibling inheritance supplies concise variants, especially for +profiles and backends. + +Prefer separately named variants when changing an existing definition. This is +authoring guidance, not a prohibition on deliberate same-ID replacement across +configuration layers. Keep ConfigKit responsible for composition and inheritance; +PromptKit interprets the final effective configuration and owns resource meaning. + +## Current Baseline + +The repository currently declares Go 1.26.0 and depends on +`gitea.maximumdirect.net/eric/ConfigKit` v0.8.0. The +[format reference](../formats.md) and [source adapter overview](../internal/sources.md) +describe the implemented behavior: + +- Root `prompts`, `profiles`, and `backends` are mappings keyed by local ID. + `collections` is a namespace-keyed mapping whose entries contain the same + resource mappings and an optional `release_version` label. +- `CollectionInput` mounts a selected source at `collections/` and + enables its imports. Namespace views qualify convenient runtime names. +- ConfigKit composes inputs and resolves mapping-sibling `extends` before + PromptKit validates, captures content, and constructs runtime indexes. +- Source-root `version: 1` or `version: "1"` is optional. The prompt `version` + field is a separate descriptive label still carried through public values. +- Public configuration and inspection APIs expose ConfigKit types. The module + identity change therefore affects downstream Go callers as well as internals. + +A prior exploratory migration in a temporary copy passed the existing test +suite and maintained consumer examples against the tagged ConfigKit v1.0.0 +checkout after mechanical API and control-block updates. It retained resource +mappings; it does not validate the list migration planned here. + +## Targeted End State + +### Dependency And Public API Migration + +Use `go.maximumdirect.net/configkit` v1.0.0 throughout production code, tests, +examples, and current consumer guidance, and raise the Go consumer minimum to +1.27.0. Update build and release-toolchain guidance accordingly. PromptKit's own +module identity is outside this migration's scope. + +Update PromptKit's existing uses of: + +| Current use | Replacement | +| --- | --- | +| `Definition.Secret` | `Definition.Sensitive` | +| `Resolution.InputInfo` | `Resolution.EntryInfo` | +| `Resolution.Record(..., ChangeNormalization, ...)` | `Resolution.RecordNormalization(...)` | + +Audit the remaining exposed inspection types and diagnostic consumers against +the complete [ConfigKit v1.0.0 migration notes](https://forge.maximumdirect.net/base-layer/configkit/src/tag/v1.0.0/docs/releases/v1.0.0.md). +For example, version evidence fields were renamed, the main provenance layer is +now `main` rather than `base`, and the history-limit code changed. Do not require +identical historical rendering or source ordering where ConfigKit deliberately +changed those contracts or corrected attribution. + +Coordinate consumers such as Scriptorium that pass `Source`, `Input`, `Underlay`, +`Path`, or inspection values across PromptKit's boundary. Old-path and new-path +ConfigKit types are different Go types; changing only a dependency requirement +does not bridge them. The released dependency must use the new module directly, +without a local replacement or an old-path compatibility adapter. + +### Source Controls And Optional Headers + +Migrate genuine source-root `configkit:` controls to `config:` in main files, +collection entries, fragments, and programmatic inputs. Controls are extracted +from each acquired document before placement. Nested `config` values are ordinary +application data subject to the existing application schema; placement does not +trigger another control interpretation pass. Do not globally rename arbitrary +data or the unchanged `configkit:"secret"` Go tag. + +Also account for the intervening v0.9 import changes: imports are optional by +default, `optional` has been removed, and authors use `required: true` when a +missing import should fail. Preserve those upstream choices rather than imposing +a new PromptKit-wide requiredness policy. Directory imports can select filename +patterns and recurse; filesystem globs do not acquire recursive `**` semantics. + +Advance the PromptKit configuration specification to version 2 for the new +resource shape. Accept the exact string `"2"` or strict decimal integer `2` when +a source declares a header; keep the existing if-present checking approach and +permit headerless files. Do not require a declaration in every resource file or +infer a missing header from an enclosing source. The header describes the +PromptKit specification, independently of ConfigKit's software version. + +Remove the unused descriptive prompt `version` field and its propagation through +definitions, inventories, inspection, results, and observations. Remove the +corresponding `prompt_version` output fields and stale documentation. Retain the +collection `release_version` label and optional source specification headers; +these have distinct purposes. In a standalone prompt item file, a root `version` +is now solely a specification header; do not carry the former prompt label into +that position. Audit hashes and prepared snapshots affected by +the removed field without promising hash equality across this format migration. + +### Resource Shape And Identity + +Keep `collections` as a mapping keyed by namespace. Change each of `prompts`, +`profiles`, and `backends` to a list of mappings, both at the root and within a +collection. Each record supplies its local `id` explicitly. Filenames remain +organizational choices, not identities. + +Preserve the existing nonblank, slash-free namespace and local-ID rules, exact +spelling, qualified `namespace/id` runtime identity, namespace views, and exact +resource references. A provider's `model` string remains provider data and can +contain `/`. Do not derive or rewrite stored backend/profile references from +the source's mount point. The same local ID can occur in different namespaces +or resource kinds. + +Register `id` through `Definition.SequenceIdentities` for each root resource list +and each `collections` / any mapping key / resource-list destination. Use +`SequenceIdentityAt` and `SequenceIdentityMatching` with +`SequenceAnyMappingKey`; no source pre-scan or namespace enumeration is needed. +Authors do not need to repeat YAML identity declarations. ConfigKit owns +structural and final identity checks; PromptKit's schema and validation retain +responsibility for ID syntax, allowed value shapes, and domain semantics. + +Build runtime indexes from the final validated records. Keep effective inspection +in the assembled list shape, including its index-based paths and provenance. +Do not impose a new execution-order meaning on list position or change the +existing inventory sorting solely because source resources become lists. + +### Inheritance And Layering + +Identified siblings inherit using `extends: {id: parent-id}`. This applies to +backends and profiles as well as prompts; no additional PromptKit inheritance +engine or prompt-specific restriction is needed. Lookup stays within the same +resource list. Ordinary qualified references to other resources are separate +from this sibling relationship. + +ConfigKit resolves surviving parents before children, retains the child's ID, +and applies its normal mapping and presence rules to inherited settings. +Missing parents and cycles follow upstream diagnostics. Parent records remain +ordinary definitions subject to normal validation, not automatically hidden or +abstract templates. Preserve PromptKit's existing allowance for partial profiles +that requests can complete; inheritance does not add a readiness requirement. + +Within one strict tier, duplicate IDs fail. Across precedence tiers, the default +identified-list operation is upsert: a matching record is replaced in full. +Authors can select ConfigKit's append or replace modes. Under upsert or append, +`[]` contributes no records; explicit replacement with `[]` clears a list. +An ID cannot inherit its displaced lower-layer version by extending itself. + +Recommend a distinct variant ID with `extends` for customization. Preserve +deliberate same-ID whole-record replacement and upstream collection operations. +Do not add a partial-record merge, ban inheritance for prompts, require new IDs +for every modification, or add constraints on source documents sharing a final +namespace. Shared prompt text can continue using `content_file`. + +## Illustrative Authoring Workflow + +These snippets describe the target format. They are not complete deployment +files and do not assert provider model availability. + +A caller mounts an entry document under `weatherreporter` using `CollectionInput`. +The entry can declare the specification once and import headerless resources: + +```yaml +version: 2 +config: + imports: + - dir: profiles + pattern: ["*.yaml", "*.yml"] + recursive: true + destination: [profiles] + placement: sequence-item + - dir: prompts + pattern: ["*.yaml", "*.yml"] + recursive: true + destination: [prompts] + placement: sequence-item +``` + +One profile file can contain a base definition: + +```yaml +id: sonnet +backend: openrouter/api +model: example-model +temperature: 0.7 +``` + +Another profile file contains its variant: + +```yaml +id: sonnet-precise +extends: {id: sonnet} +temperature: 0.2 +``` + +A prompt file can select it by qualified ID: + +```yaml +id: foo.bar +default_profile: weatherreporter/sonnet-precise +messages: + - role: user + content: Describe the weather. +output: + format: text + validation_mode: none +``` + +The caller separately supplies `openrouter/api`. A backend collection uses the +same item-import pattern with `destination: [backends]` and can define backend +variants using sibling `extends`. Inline resource lists are equally valid; +one-file-per-item and directory layout are conveniences, not requirements. + +## Work And Sequencing + +### 1. Establish The ConfigKit v1 Baseline + +Migrate the module, toolchain, affected APIs, and source controls while temporarily +retaining resource mappings. Update fixtures and examples and validate that +configuration loading, presence, inheritance, inspection, and execution retain +their intended behavior. This isolates dependency migration failures from the +later shape change; the intermediate state need not be separately released. + +Review module sums and the resolved dependency graph, and update +[attribution](../oss/attribution.md) to the selected upstream revision and notices. +ConfigKit's own release-tooling migration does not by itself require changing +PromptKit's release tooling or publication workflow. + +### 2. Convert The Configuration Adapter And Public Values + +Change root and collection resource decoding to identified records, register all +six root/dynamic identity rules, and advance the optional specification checker. +Adapt validation and runtime indexing to read IDs from records. Remove prompt +version metadata throughout the relevant public and internal values. + +Review every path-dependent operation as mapping keys become list indexes: +presence capture, profile/backend defaults, prompt input metadata, sensitive +provider fields, errors, and effective resource lookup. Resolve `content_file` +against the effective field's contributing source after inheritance and upsert. +Retain one ConfigKit load and the existing effective-value handoff; source history +must not become a second resolver. + +Update `BackendSource`, `ProfileSource`, and profile inheritance encoding to emit +the list format, following the open API decision below. Update namespace views, +listing, inspection, prepared execution, and smoke-test selection as needed to +consume the same captured runtime indexes. Do not change their selection or +execution semantics merely to mirror the new configuration representation. + +### 3. Exercise The Integrated Target + +Use focused behavioral coverage for the changed boundaries: + +- Root and arbitrarily named collections load inline lists and imported items; + equal local IDs in separate namespaces remain independent. +- Duplicate IDs within a resource list fail, including final values introduced + through supported application resolution. Exercise the registrations through + PromptKit without duplicating ConfigKit's complete generic test suite. +- Profile and backend variants inherit settings while preserving child identity, + explicit zero/false/null behavior, and ordinary domain validation. +- Layered upsert replaces a whole record; explicit list replacement and empty + contributions follow ConfigKit semantics. +- Inherited and replaced prompt resources retain the correct source context; + provider credentials remain redacted at the new effective paths. +- Optional version-2 headers and headerless fragments load; unsupported declared + versions and obsolete resource shapes fail through their owning validators. +- Public source builders, namespace lookup, inspection, preparation, validation, + smoke tests, and execution still work with the converted catalogs. + +Run the existing suite, relevant vet checks, and maintained offline consumer +examples under Go 1.27. Tests require no live provider calls. Reuse existing +coverage where it already proves the behavior; do not turn incidental history +formatting or list positions into stronger guarantees. + +### 4. Publish One Coherent Migration + +Update the [format reference](../formats.md), [consumer migration guide](../consumers/configuration-migration.md), +[Go consumer guide](../consumers/pkg-promptkit.md), source-adapter documentation, +GoDoc, and maintained examples as implementation lands. Explain the module/type +change, Go minimum, `config:` controls, import requiredness, resource lists, +mapping-to-record `extends` conversion, removed prompt labels, specification +headers, and index-based inspection paths together. + +Plan a breaking pre-v1 minor release under the existing +[release policy](../policy/release.md). Prefer delivering both implementation +stages together so downstream applications and collection authors migrate once. +Keep historical release notes intact. Coordinate Scriptorium's dependency and +configuration updates without making its release or internal implementation a +PromptKit dependency. + +The target format replaces the old resource mappings; this scope does not add +dual-format decoding, source rewriting, catalog discovery conventions, or a +compatibility module. Preserve operator choices already supplied by ConfigKit. +Once implementation is complete, move durable behavior into its canonical +owners and retire this temporary roadmap under repository practice. + +## Open Decisions + +### Go Source-Builder Inputs + +**Recommendation:** retain the keyed-map inputs to `BackendSource` and +`ProfileSource`, translating each key into the emitted record's `id`. Retain +`ProfileConfig.Extends` as a convenient parent-ID string and encode it as +`extends: {id: ...}`. Emit records in deterministic key order. These helpers +already express resource identity and can produce the new configuration shape +without forcing another caller migration. This concerns Go convenience inputs, +not continued support for the old YAML mapping format. Raw ConfigKit sources +remain available for authored order, explicit nulls, and other supported forms. + +**Alternative:** replace those helper inputs with slices of explicit-ID resource +values. This mirrors YAML, makes order caller-controlled, and allows duplicate +IDs to reach normal configuration validation. It also changes public signatures, +requires new ID-bearing values or fields, and makes existing keyed Go catalogs +convert unnecessarily. Choose it if downstream callers need ordered records or +uniform Go/YAML authoring enough to justify that additional API migration.