Files
promptkit/docs/roadmap/configuration-collections-current-configkit.md

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-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:

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: 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.