Propose collection configuration using current ConfigKit
This commit is contained in:
372
docs/roadmap/configuration-collections-current-configkit.md
Normal file
372
docs/roadmap/configuration-collections-current-configkit.md
Normal file
@@ -0,0 +1,372 @@
|
||||
# Proposal 1: Namespaced Collections With Current ConfigKit
|
||||
|
||||
Status: proposed PromptKit configuration standard; not implemented or accepted.
|
||||
This proposal addresses the pending configuration decision in the
|
||||
[namespace and collection roadmap](namespaces.md). It requires PromptKit changes
|
||||
but no ConfigKit extension. Proposal 2, which may assume upstream changes, is
|
||||
outside this document.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Keep the existing top-level `backends`, `profiles`, and `prompts` mappings, and
|
||||
use fully qualified resource IDs as their actual YAML keys. Add a `collections`
|
||||
mapping keyed by namespace for release metadata. The application's main input
|
||||
owns `version: 1`; reusable collection contributions omit that top-level field.
|
||||
|
||||
Compose independently maintained peer collections in one ConfigKit underlay
|
||||
group. Put intentional application/operator overrides in later groups or the
|
||||
main input. This uses ConfigKit's distinction between strict composition and
|
||||
explicit precedence to protect independent contributions while retaining
|
||||
ordinary configuration customization.
|
||||
|
||||
Namespace identity exists in the configuration tree before composition. A
|
||||
separate `id` field inside an otherwise unqualified prompt definition would be
|
||||
too late: ConfigKit merges mapping keys before PromptKit interprets definitions.
|
||||
|
||||
## Reviewed Baseline And Evidence
|
||||
|
||||
Reviewed the local `../ConfigKit` checkout at commit
|
||||
`6fffde4af8f31f99d3ee295c426eb3234cb0fb77` (v0.7.1), the version selected by
|
||||
PromptKit. The governing upstream sources are its
|
||||
[configuration contract](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/v0.7.1/docs/config.md)
|
||||
and [composition/overlay decision](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/v0.7.1/docs/adr/0001-separate-composition-and-overlay.md).
|
||||
Implementation evidence includes `internal/engine/compose_test.go`,
|
||||
`internal/engine/load_request_test.go`, `internal/engine/imports_test.go`, and
|
||||
the public `VersionRule` and `LoadLayered` declarations in that checkout.
|
||||
|
||||
The relevant current rules are:
|
||||
|
||||
| ConfigKit behavior | Consequence for this proposal |
|
||||
| --- | --- |
|
||||
| Within a composition group, mappings combine recursively; every other overlapping pair conflicts, even equal scalars | Distinct qualified keys coexist; repeated metadata versions or message sequences conflict within the group |
|
||||
| Between underlay groups, later values overlay earlier ones; main has higher precedence | Directory order must not accidentally become collection precedence; intentional overrides are separate layers |
|
||||
| Overlay mappings combine recursively; scalars, sequences, and null replace lower values | An override can replace a setting or message sequence without replacing the entire prompt |
|
||||
| Syntax and strict-composition failures happen before final domain validation | A higher layer cannot repair a conflicting peer group |
|
||||
| The configured specification-version path belongs to main; underlays and imported fragments cannot claim it | There is one assembled-configuration specification declaration, not independently checked collection headers |
|
||||
| Sibling `extends` resolves after composition and precedence | Fully qualified profile keys work as exact cross-collection inheritance references |
|
||||
| Defaults, typed decoding, resolution, and semantic validation operate on final assembled values | PromptKit need not validate each collection separately |
|
||||
| Source origins retain resource context and inspection history | File-backed content and explicit provenance inspection can retain existing behavior |
|
||||
|
||||
PromptKit already provides directory and embedded-source helpers, and its
|
||||
configuration adapter calls `configkit.LoadLayered` once. Its current document
|
||||
schema only exposes `backends`, `profiles`, and `prompts`; this proposal adds
|
||||
version and metadata fields rather than claiming they work today. The
|
||||
[current format reference](../formats.md) remains authoritative until adoption.
|
||||
|
||||
## Proposed Document Shape
|
||||
|
||||
The effective configuration has these application fields. Source documents may
|
||||
also use supported ConfigKit directives, which ConfigKit consumes before the
|
||||
application handoff:
|
||||
|
||||
| Field | Proposed meaning |
|
||||
| --- | --- |
|
||||
| `version` | PromptKit configuration/collection specification identifier, owned by the main input |
|
||||
| `collections` | Optional mapping from namespace to collection release metadata |
|
||||
| `backends` | Existing backend definitions, keyed by exact resource ID |
|
||||
| `profiles` | Existing profile definitions, keyed by exact resource ID |
|
||||
| `prompts` | Existing prompt definitions, keyed first by exact resource ID and then by prompt version |
|
||||
|
||||
The namespace roadmap's accepted grammar applies to resource keys: either an
|
||||
unqualified ID or exactly `namespace/local-name`. Neither qualified component
|
||||
contains `/`; dots remain available within local names. Published collections
|
||||
use qualified keys. Unqualified application definitions remain supported.
|
||||
|
||||
Keep IDs in mapping keys, including the prompt's outer key; do not introduce
|
||||
another embedded `id` or `namespace` field in each definition. Collection
|
||||
metadata does not prefix or rewrite keys. Filenames and directory names have
|
||||
no effect on identity. Namespace comparison and references remain exact.
|
||||
|
||||
The following compact contribution could be supplied by a provider collection.
|
||||
The endpoint and model are synthetic configuration examples, not live targets:
|
||||
|
||||
```yaml
|
||||
collections:
|
||||
provider:
|
||||
version: "2026.10.14"
|
||||
backends:
|
||||
provider/default:
|
||||
endpoint: https://inference.example.invalid/v1
|
||||
profiles:
|
||||
provider/brief:
|
||||
backend: provider/default
|
||||
model: vendor/example-model
|
||||
temperature: 0.2
|
||||
```
|
||||
|
||||
Two independently maintained application collections can then supply the same
|
||||
local prompt name without colliding:
|
||||
|
||||
```yaml
|
||||
collections:
|
||||
weatherreporter:
|
||||
version: "2.4.0"
|
||||
prompts:
|
||||
weatherreporter/alerts.summarize:
|
||||
"1":
|
||||
default_profile: provider/brief
|
||||
messages:
|
||||
- role: user
|
||||
content: Summarize the weather alerts.
|
||||
output:
|
||||
format: text
|
||||
validation_mode: none
|
||||
```
|
||||
|
||||
```yaml
|
||||
collections:
|
||||
homewatch:
|
||||
version: "3.1.0"
|
||||
prompts:
|
||||
homewatch/alerts.summarize:
|
||||
"1":
|
||||
default_profile: provider/brief
|
||||
messages:
|
||||
- role: user
|
||||
content: Summarize the household alerts.
|
||||
output:
|
||||
format: text
|
||||
validation_mode: none
|
||||
```
|
||||
|
||||
ConfigKit sees separate keys under `prompts`. It never needs to understand
|
||||
PromptKit namespaces to preserve both definitions. The quoted `"1"` keys are
|
||||
individual prompt versions, unrelated to the main specification version or the
|
||||
collection release strings.
|
||||
|
||||
## Specification And Collection Versions
|
||||
|
||||
Configure ConfigKit's existing `VersionRule` at `configkit.Keys("version")`.
|
||||
Recommend requiring an explicit supported value on the selected main input for
|
||||
this new standard: canonical YAML is `version: 1`. ConfigKit exposes both integer
|
||||
`1` and string `"1"` as version text `1`; accept both, while documenting the
|
||||
integer spelling. Reject absent, null, and unsupported versions through the
|
||||
configured checker. These are proposed PromptKit format choices, implemented
|
||||
through ConfigKit's supported callback, not a separate input-scanning pass.
|
||||
|
||||
A minimal main input is:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
```
|
||||
|
||||
Programmatic applications can supply the same main input with `configkit.Mapping`.
|
||||
Do not infer the specification from a directory name or collection release, strip
|
||||
version fields from fragments, or silently inject a version to hide a missing
|
||||
declaration. Requiring main instead of accepting an omitted main is an additional
|
||||
explicit migration cost of this proposal.
|
||||
|
||||
Reusable collection files, including a collection's root metadata file, omit
|
||||
top-level `version`. So do operator underlays and imported fragments. ConfigKit
|
||||
protects the configured version path even if a repeated value would equal `1`.
|
||||
The version checker is invoked by ConfigKit once, at its normal point before
|
||||
underlay assembly; it selects the contract for the complete configuration.
|
||||
|
||||
For collection metadata, propose an ordinary optional `collections` mapping:
|
||||
|
||||
- Its keys are exact nonblank namespace names without `/`.
|
||||
- Each effective entry is a non-null mapping with a required nonblank string
|
||||
`version`. Quote collection release identifiers; this nested field does not
|
||||
receive ConfigKit's special integer-to-string handling for top-level `version`.
|
||||
- Release strings are descriptive publisher identifiers, not executable
|
||||
dependency constraints or implicit precedence. No SemVer ordering is needed.
|
||||
- An omitted, null, or empty top-level `collections` mapping means no declared
|
||||
collection metadata. Null individual entries are invalid final descriptors;
|
||||
null is not a new per-entry deletion operator.
|
||||
|
||||
Validate this metadata only in final effective values. Do not require a metadata
|
||||
entry for every resource namespace or require every metadata entry to have
|
||||
resources. This lets ordinary application configuration add definitions or
|
||||
overrides without fabricating a collection release. Collection publishing
|
||||
guidance recommends declaring its own namespace and version, but PromptKit
|
||||
does not audit which source file originally contributed a resource.
|
||||
|
||||
## Source Selection And Layering
|
||||
|
||||
Recommend this caller-selected assembly:
|
||||
|
||||
| Tier | Contents | Relationship |
|
||||
| --- | --- | --- |
|
||||
| First underlay group | All selected peer collection inputs | Strict composition across the entire group |
|
||||
| Later underlay groups, if selected | Application defaults or operator override documents, in caller-chosen priority order | Strict within each group; later groups overlay earlier groups |
|
||||
| Main input | Specification declaration and optional highest-priority local settings | Overlays the underlays |
|
||||
|
||||
These are integration guidance, not a new enforced source policy. The application
|
||||
still selects ConfigKit groups and precedence explicitly. In particular, separate
|
||||
directories do not each require their own underlay tier. Append their discovered
|
||||
inputs into one group when they are independent peer libraries. Putting each
|
||||
directory into a successive group intentionally enables later overrides.
|
||||
|
||||
An illustrative caller arrangement using existing source APIs is:
|
||||
|
||||
```go
|
||||
var peers []configkit.Input
|
||||
for _, directory := range collectionDirectories {
|
||||
inputs, err := promptkit.DirectoryInputs(directory)
|
||||
if err != nil { return err }
|
||||
peers = append(peers, inputs...)
|
||||
}
|
||||
main := configkit.Input{Source: configkit.File(applicationConfigFile)}
|
||||
sources := promptkit.ConfigurationSources{
|
||||
Underlays: []configkit.Underlay{{Inputs: peers}},
|
||||
Main: &main,
|
||||
}
|
||||
// Pass sources to NewEngine after the proposed document schema is implemented.
|
||||
```
|
||||
|
||||
The helpers already supply filesystem context, recursively select YAML, and
|
||||
leave imports disabled. An operator may install a collection with an ordinary
|
||||
`collection.yml` metadata contribution and definition files in subdirectories.
|
||||
That filename is a recommended packaging convention, not a special loader
|
||||
instruction. All selected YAML files participate normally; resource files should
|
||||
be outside that scan or use suitable non-configuration formats.
|
||||
|
||||
For explicit imports instead, select only collection entry-point files, enable
|
||||
imports on those inputs, and let ConfigKit traverse their fragments. Do not both
|
||||
recursively select all files and import those same files in the same group:
|
||||
ConfigKit rejects repeated sources. Imports remain root-contained, relative,
|
||||
opt-in paths/globs; they neither fetch repositories nor establish precedence.
|
||||
Each directory input can have its own filesystem root, so peers need not share
|
||||
an installation directory.
|
||||
|
||||
Importing all peers from main is also possible under ConfigKit's rules, but
|
||||
main's own definitions and its imports then compose strictly. A main document
|
||||
cannot override something in its own import graph. The recommended underlay
|
||||
arrangement keeps main available for ordinary application overrides without
|
||||
requiring a selected-overlay API addition to PromptKit.
|
||||
|
||||
## Collisions, Partial Contributions, And Overrides
|
||||
|
||||
| Contributions | Current ConfigKit outcome |
|
||||
| --- | --- |
|
||||
| `weatherreporter/alerts.summarize` and `homewatch/alerts.summarize` | Both definitions remain |
|
||||
| Same qualified prompt ID, different prompt-version keys | Both versions remain; PromptKit's existing version selection applies |
|
||||
| Same ID/version, disjoint definition fields in one group | Fields compose into one definition |
|
||||
| Same ID/version, overlapping message sequences in one group | `compose.conflict`, even if the sequences are identical |
|
||||
| Same `collections.<namespace>.version` in two peer inputs | `compose.conflict`, even if release strings are equal |
|
||||
| Same namespace with disjoint metadata/resource contributions | Contributions can compose; there is no namespace-ownership error |
|
||||
| Same qualified ID or collection metadata in a later layer | Ordinary recursive overlay semantics apply |
|
||||
|
||||
This does not promise that every repeated resource ID or collection namespace
|
||||
is fatal. ConfigKit permits disjoint mapping contributions by design. Namespace
|
||||
qualification avoids accidental *cross-namespace* collisions; strict grouping
|
||||
detects conflicting leaves among peers. Deliberately sharing an exact key in
|
||||
different precedence layers still allows overrides.
|
||||
|
||||
For example, main can override only the selected profile's temperature:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
profiles:
|
||||
provider/brief:
|
||||
temperature: 0.1
|
||||
```
|
||||
|
||||
The lower backend and model remain. Replacing `messages` would replace the
|
||||
entire sequence, not append messages. An empty mapping does not clear inherited
|
||||
children. Explicit null retains existing PromptKit field-specific meanings;
|
||||
this proposal adds no generic delete or replace-whole-resource directive.
|
||||
|
||||
Do not inspect provenance to reject a higher-priority metadata edit, or declare
|
||||
that a collection namespace is locked to one input. Provenance is for explicit
|
||||
operator explanation. Final domain validation can reject an invalid resulting
|
||||
profile or descriptor, but cannot resurrect or reject displaced source values.
|
||||
|
||||
## References, Inheritance, And Resources
|
||||
|
||||
Stored references remain exact IDs. A collection prompt can select
|
||||
`provider/brief`; a profile can inherit `provider/brief` by writing that exact
|
||||
key as `extends`. Profile mappings remain siblings under `profiles`, so this
|
||||
uses ConfigKit's existing inheritance without moving definitions into nested
|
||||
collection containers. A consumer's selected namespace never rebinds those
|
||||
stored references. Provider `model` strings remain unchanged and may contain `/`.
|
||||
|
||||
Prompt versions remain an inner mapping. ConfigKit's sibling inheritance is
|
||||
generic, but it does not make another prompt's outer ID a sibling of an inner
|
||||
version definition. This proposal introduces no cross-prompt reference resolver.
|
||||
|
||||
Keep existing resource semantics: ConfigKit records the effective field's origin
|
||||
and source context, and PromptKit uses that context for file-backed prompt
|
||||
content. Do not derive content paths from namespaces or a collection metadata
|
||||
filename. Schema roots and runtime artifacts retain their existing separately
|
||||
configured behavior; a metadata file does not automatically mount schemas or
|
||||
change their resource resolution. Moving to configuration collections must not
|
||||
claim broader resource portability than those contracts provide.
|
||||
|
||||
## Collection Inventory
|
||||
|
||||
Propose a public `ListCollections` operation returning detached descriptors from
|
||||
the engine's captured effective metadata. Exact Go names and signatures remain
|
||||
to be chosen. Each descriptor contains namespace and declared collection release
|
||||
version. Expose the effective specification version once at engine/configuration
|
||||
level, not as a falsely independently checked version on each collection.
|
||||
|
||||
An absent metadata map returns no collection descriptors. Sort by namespace for
|
||||
stable inspection. Report resources separately through the accepted namespace
|
||||
listing/filter APIs; unregistered namespaces can exist there without fabricated
|
||||
collection versions. A metadata-only declaration can appear in collection
|
||||
inventory even when no resources remain under that namespace.
|
||||
|
||||
This API means "effective declared collections," not "every manifest ever
|
||||
loaded" or "verified installed package versions." Metadata and resources can
|
||||
be changed by later layers. A declared release does not certify that all effective
|
||||
definitions are identical to a publisher's release. ConfigKit inspection explains
|
||||
the sources and edits; the domain API does not replay history or reread files.
|
||||
|
||||
## PromptKit Changes And Deliberate Limits
|
||||
|
||||
Adoption would add the version field/checker and collection metadata schema,
|
||||
final-value validation, captured inventory, and the public collection API. It
|
||||
would also implement the accepted scoped lookup and discovery behavior and
|
||||
remove automatically bundled catalogs and built-in profiles, as tracked in the
|
||||
parent roadmap. Update canonical contracts and migration guidance only when
|
||||
those changes land. Existing documents with no main specification declaration
|
||||
would need an explicit migration under the proposed version policy.
|
||||
|
||||
There is no ConfigKit change, per-collection loading/validation pass, source-level
|
||||
namespace registry, key-rewriting stage, or automatic configuration installation.
|
||||
Definitions retain their qualified keys throughout assembly. Invalid effective
|
||||
domain values are PromptKit's responsibility; composition, imports, inheritance,
|
||||
and version-path protection remain ConfigKit's responsibility under
|
||||
[ADR 0001](../adr/0001-configuration-boundary.md).
|
||||
|
||||
The main limitation is independent specification compatibility: a reusable
|
||||
collection cannot carry its own protected top-level `version: 1` when imported
|
||||
or used as an underlay. Publishers/operators must select configuration compatible
|
||||
with the main standard. Strict decoding catches unsupported surviving fields,
|
||||
but it is not an independent compatibility check for each collection release.
|
||||
This is the principal issue to compare with proposal 2.
|
||||
|
||||
## Validation And Review Criteria
|
||||
|
||||
Review verification on 2026-10-14: the focused ConfigKit composition, grouped
|
||||
loading, and import-version tests passed in the local checkout. A temporary
|
||||
offline consumer using that checkout also loaded the five YAML examples above
|
||||
through a prospective document schema and verified distinct identities, equal
|
||||
and unequal peer conflicts, disjoint contributions, main overrides, atomic
|
||||
sequence replacement, main/underlay/import version ownership, and qualified
|
||||
inheritance. This establishes feasibility with current ConfigKit; it does not
|
||||
implement the proposed PromptKit schema or inventory API. No temporary harness
|
||||
or module replacement is part of either repository.
|
||||
|
||||
Review this proposal against these observable cases:
|
||||
|
||||
- Two collections using the same local prompt name survive composition under
|
||||
distinct qualified keys.
|
||||
- Equal and unequal overlapping metadata versions fail in one peer group;
|
||||
disjoint contributions compose, and later metadata overrides remain allowed.
|
||||
- Main overrides one profile field while preserving other fields; sequence
|
||||
overrides replace whole sequences.
|
||||
- A strict peer conflict cannot be hidden by a higher layer.
|
||||
- Main `version: 1` succeeds, while omitted/null/unsupported versions fail the
|
||||
proposed checker and imported/underlay declarations fail ConfigKit's existing
|
||||
version ownership checks.
|
||||
- Fully qualified inheritance resolves through the assembled sibling mappings;
|
||||
file-backed content retains its declaring source's context.
|
||||
- Inventory reflects final metadata and remains captured when source files
|
||||
change; resource namespace discovery does not require a metadata declaration.
|
||||
|
||||
The configuration schema, explicit-version requirement, metadata null rules,
|
||||
inventory shape, and recommended peer-group layout are proposal choices to
|
||||
review. They do not resolve the parent roadmap's decision 5 merely by being
|
||||
written here.
|
||||
@@ -485,6 +485,10 @@ dependencies instead.
|
||||
|
||||
### 5. Collection Metadata And Specification Versioning (Open)
|
||||
|
||||
[Proposal 1: current ConfigKit](configuration-collections-current-configkit.md)
|
||||
develops a concrete configuration standard for the existing composition rules.
|
||||
It is a proposal for comparison; this decision remains open.
|
||||
|
||||
**Desired capability:** describe collection namespace and release version in
|
||||
configuration and expose an API through which downstream applications can list
|
||||
effective collection declarations captured by an engine. A collection release
|
||||
|
||||
Reference in New Issue
Block a user