19 KiB
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. 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
and composition/overlay decision.
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 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:
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:
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
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:
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-levelversion. - 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
collectionsmapping 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:
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:
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.
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: 1succeeds, 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.