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

44 KiB
Raw Blame History

Configuration Collections, Namespaces, And Scoped Lookup

Status: implementation complete; roadmap retirement remains for normal release preparation. This document consolidates the namespace roadmap and selected collection proposal 3. The configuration and namespace design decisions are settled. The companion implementation plan records concrete API delivery choices and the completed work.

Purpose

Simplify PromptKit configuration by loading independently maintained prompt, profile, and backend libraries as YAML collections instead of mandatory Go modules. Remove the unused prompt-version selection scheme. Specification headers are optional: maintainers can declare compatibility in a collection entry without adding a declaration to every file.

Consumers can select a namespace once and use local resource names while an engine contains several collections with overlapping names. For example, WeatherReporter and Homewatch can each define summarize.alerts, while provider namespaces distinguish profiles targeting the same upstream model.

Conventions and small convenience APIs should reduce authoring and maintenance work. Preserve consumer choice over sources, layouts, namespaces, and precedence without adding speculative options, mandatory workflows, or new compatibility and provenance guarantees.

Prerequisite: Adopt ConfigKit v0.8.0

The target selects released ConfigKit v0.8.0 for the collection schema and loading APIs. At planning time PromptKit selected v0.7.1; the implementation plan records adoption and verification of v0.8.0. Local filesystem replacements are not part of the distributed module.

The v0.8.0 tag was reviewed at 137769f on 2026-10-17. It supplies input placement, relative import destinations, and shared source-version checks needed by this design. No additional upstream extension is required. The implementation plan records adoption of this prerequisite.

ConfigKit's public docs/config.md, ADRs 0011–0014, and v0.8.0 release notes own the upstream contracts. The capabilities used here are:

  • Input.Destination for root-relative placement and import destination for placement relative to the immediate importing document.
  • Options.SourceVersionMode and LayeredOptions.SourceVersionMode, applying a shared Definition.Version policy in each document's source-local coordinates.
  • Consumption of accepted non-main headers before composition, with the main header retained and protected at its effective application path.
  • Detached SourceInfo.Destination and SourceInfo.VersionCheck evidence, together with existing source locations and resource contexts.

Keep Mapping-Based Collections

ConfigKit v0.8.0 also offers sequence-item placement and registered composition of distinct list records. These were reviewed and are not selected for this round. A higher-priority list replaces the entire lower-priority list, duplicate identities conflict within a strict group, and records cannot inherit from sibling list records. Switching formats would sacrifice ordinary per-resource partial overrides and local inheritance for flatter authoring.

Keep collection resources as mappings keyed by local ID. Do not add an alternative list schema or a PromptKit identity-based merge layer. The existing key supplies identity without an additional embedded id. This is a scope choice for this round, not a prohibition on future upstream capabilities.

Existing Foundation And Ownership

The format reference and consumer guide remain authoritative for current behavior until implementation lands. Directory and embedded configuration loading already exist through DirectoryInputs and FilesystemInputs. The current engine also supplies provider catalog modules and built-in profiles; the accepted removal is described below.

ADR 0001 assigns source loading, composition, and inheritance to ConfigKit. PromptKit interprets and validates only final effective values, including their resource contexts. Source history is available for explicit inspection, not additional validation of contributions. Do not add a manifest pre-parser, source-level namespace ownership checks, or a second composition pass. Two sources targeting the same namespace follow ordinary ConfigKit composition; a repeated namespace is not a separate error.

The architecture policy continues to assign CLI syntax, discovery policy, and application lifecycle to consuming applications.

Collection Ownership And Built-In Removal

Accepted: load collections as application/operator-supplied configuration, and remove PromptKit's built-in profiles as a breaking change. Remove the mandatory OpenRouter and RakestrawHome catalog Go-module dependencies and their automatic contribution to engine configuration. PromptKit owns the definition format and execution machinery; collection publishers own their configuration repositories, and applications/operators select and provision the collections they use. One or more explicitly selected directories can supply backends, profiles, and prompts through the existing ConfigKit integration.

Remove promptkit-openrouter-standard and promptkit-openrouter-auto, including their implicit catalog dependencies. Do not replace them with another mandatory starter bundle. Applications may embed their own defaults using the same configuration format and loader, then layer operator configuration as desired. Ordinary application overrides remain valid configuration and need not declare a collection release of their own.

This decouples collection updates from PromptKit rebuilds and releases and avoids imposing provider inventories on every consumer. The tradeoff is that applications and deployments must supply their required configuration and manage compatible collection revisions. PromptKit does not gain a collection installer, automatic downloader, or implicit directory search policy.

Retain the captured-engine lifecycle: configuration changes take effect when the application constructs and validates a replacement engine. A change to an installed directory does not mutate an existing engine's captured definitions. Distribution as configuration does not make new provider protocols or diagnostic adapters data-only features; those can still require implementation work.

Prepare explicit identifier mappings, replacement configuration examples, and consumer migration instructions. Update the built-in-profile promise in the release policy and its canonical format documentation when removal lands. Use a release permitted to break configuration and API contracts under the current release policy: a minor release before v1, or a major release at/after v1. Actual catalog mappings, packaging arrangements, and release versions remain delivery work; independent collection releases need not track future PromptKit software releases.

Accepted Identity Convention

Reserve / exclusively as the namespace separator in PromptKit prompt, profile, and backend identifiers and references. A qualified identifier has exactly two nonempty components: namespace/local-name. Neither component can contain /. Dots remain available within local names. An unqualified identifier has no slash and remains valid under the existing nonblank-ID rule.

Reject resource identifiers or supplied references with multiple slashes or an empty qualified component, such as a/b/c, /name, and namespace/. These rules do not apply to provider model values, endpoints, resource paths, or prompt versions. Preserve exact spelling; do not silently trim or rewrite IDs.

Resource Namespace Local name Qualified ID
Prompt weatherreporter summarize.alerts weatherreporter/summarize.alerts
Prompt homewatch summarize.alerts homewatch/summarize.alerts
Profile openrouter google.gemma-4-31b-it openrouter/google.gemma-4-31b-it
Profile rakestrawhome google.gemma-4-31b-it rakestrawhome/google.gemma-4-31b-it

Prompt, profile, and backend identities remain separate resource types. The same namespace can contain all three, and resources in it may come from multiple configuration sources. A namespace does not imply a directory, package, provider model identifier, or security boundary.

A profile ID identifies execution settings. Its configured model value is still the identifier passed to the provider; qualifying the profile ID does not modify that value. Both example profiles may retain the configured model google/gemma-4-31b-it. Different profiles may select the same model with different settings. Prompt version labels remain separate from namespaces. The selected collection format installs one definition per prompt ID and removes request-side version selection and assertions. Optional labels are descriptive.

Retain unqualified and dotted IDs. Existing slash-containing IDs need review: those with one slash and nonempty components become qualified identities, while those outside the new grammar require migration. This intentional format break is accepted in favor of unambiguous resolution; the migration work is described below.

Accepted Consumer Scope

Provide a lightweight, immutable view over an existing engine with independent prompt, profile, and backend scopes. The following sketch illustrates the accepted shape; concrete declarations are specified in the implementation plan:

weather, err := engine.WithNamespaces(Namespaces{
    Prompt:  "weatherreporter",
    Profile: "openrouter",
})
// Handle err, then use local prompt and profile names through weather.

The concrete declarations are recorded in the implementation plan. Empty scope fields mean no scope for that resource type. Keep name qualification shared across the view and engine; do not duplicate selection logic in each operation. Omit a one-string shorthand that implicitly scopes all resource types.

The implemented views share the engine's captured configuration, clients, and execution machinery. Selecting a namespace does not rebuild the engine or duplicate its backend capacity accounting. Consumers can use different views concurrently without changing a shared current-namespace setting.

The accepted resolution rules, independent of the final scope API, are:

  1. A supplied name without a slash is qualified using the selected scope for its resource type, then looked up exactly. Without that scope, it is looked up as an exact unqualified ID.
  2. A missing scoped resource does not trigger a search in other namespaces or among unqualified IDs. Loading another collection cannot redirect lookup.
  3. A valid supplied name containing one slash is fully qualified and is looked up exactly, regardless of scope. Malformed resource references are rejected.
  4. Scope applies to caller-supplied resource names. Stored references, including prompt default profiles, profile backends, inheritance references, and backend smoke-test default profiles, retain their existing exact meaning. They are not rebound to the invoking consumer's namespace.
  5. Omitted optional IDs retain their existing selection/default behavior; an omission is not converted into a namespace prefix or a lookup for an empty local name.
  6. Where outputs, inspection, errors, or diagnostic reports identify a selected resource, preserve its resolved full identity. Any exposed local name should be distinguishable from that identity.

Keep qualification consistent across execution, preparation, request validation, prompt/profile inspection, and applicable smoke-test selections. Namespace selection must not change request override precedence or provider model strings. Prompts resolve by ID alone.

Local And Qualified References In One Operation

Syntax alone distinguishes local and qualified references. Do not decide whether to qualify a name by checking which candidates exist; that would make resolution depend on loaded content.

Within a weatherreporter prompt scope, summarize.alerts resolves only to weatherreporter/summarize.alerts, while homewatch/summarize.alerts explicitly selects that other library's prompt. A qualified profile override similarly bypasses the profile scope. Scoped operations accept both forms without a separate reference type or requiring a return to the original engine for cross-namespace access.

The original engine remains a way to select an exact unqualified ID without applying a scope. No escape syntax for unqualified IDs is introduced here.

Separate Resource Scopes

A consumer can select prompt shorthand under weatherreporter and profile shorthand under openrouter in the same request. Each resource type has an independent scope. A prompt's configured default profile needs no caller profile scope because the stored reference is already exact. Scope syntax and existence handling are defined in scope validation.

Scope Validation

Accepted: validate syntax when creating the view; resolve existence when an operation selects a resource. In the scope value, the empty string means no scope. Reject a nonempty namespace containing / or consisting only of whitespace. Otherwise preserve its exact spelling, with no trimming, case folding, or additional character whitelist. This distinguishes omission from an unusable namespace without inventing normalization rules. Report invalid scope construction through PromptKit's existing public error conventions.

Allow a well-formed namespace even when it has no current members. Its scoped listing would be empty, and selection of a missing local resource would produce the ordinary lookup error. Qualified selections would remain usable regardless of the view's default scope. An empty optional request ID continues to mean omission under that operation's existing contract; it is never qualified.

This keeps scope as a naming context and supports uniform application setup across installations with different library contents. It also avoids treating successful scope construction as validation of a prompt or execution profile. The tradeoff is that a misspelled but syntactically valid namespace is detected only when listed or used, rather than during view construction.

Discovery And Namespace Filtering

Accepted: integrated namespace filtering and full-identity results. Enumerate the distinct named namespaces present in final effective prompts, profiles, and backends. Allow filtering that enumeration by resource type so a prompt picker need not show profile-only namespaces. Provide scoped listings for the supported resource types, adding a backend inventory surface as part of this work if needed. Namespace filtering is an explicit option in the library listing API, not a consumer-side filtering helper. Preserve unfiltered engine listing behavior, with the accepted removal of prompt-version identity and selection. The companion implementation plan defines the Go filter shape.

Selection Listing behavior
No explicit filter on the engine All namespaces, including unqualified resources
No explicit filter on a scoped view The selected scope for that resource type; all resources of that type if no scope is selected
Explicit named namespace Resources in exactly that namespace
Explicit unqualified-only filter Resources whose IDs have no namespace
Explicit all-namespaces filter All resources of that type, including unqualified resources

An explicit filter replaces the view's default for the current listing; it does not intersect with that default or mutate the view. A named namespace filter uses the namespace syntax rules above. A well-formed namespace with no matching resources returns an empty listing. Represent default, named, unqualified-only, and all-namespaces selections distinctly in the API; do not overload an empty string to mean several of them.

Return full IDs with optional prompt version labels separately; the full ID identifies the installed prompt. Do not invent a named namespace such as default for unqualified resources. Namespace enumeration excludes that unnamed group. Use deterministic ordering consistent with existing listing contracts.

Qualified result IDs can be passed unchanged to either scoped or unscoped operations. Unqualified IDs remain literal unqualified identities: when an explicit all-namespaces or unqualified-only filter exposes one through a scoped view, use the original engine or a view without that resource scope to select it exactly. A listing filter does not change subsequent execution scope. This preserves the accepted lookup rules without introducing an escape syntax.

Integrated filtering gives consumers a direct way to discover the desired resources, while full identities keep logs and subsequent selections unambiguous. Namespace/local-name display decomposition is separate from filtering; a public splitting helper is not required by this decision.

Namespace membership is exact: weather does not include weatherreporter. Collection inventory describes final collection declarations and optional release labels; it is distinct from enumeration of namespaces that contain resources. See collection inventory.

Authoring And Assembly

A file such as weatherreporter/prompts/alerts.yml would contain:

summarize.alerts:
  version: "1"
  default_profile: provider/brief
  messages:
    - role: user
      content: Summarize the weather alerts.
  output:
    format: text
    validation_mode: none

The version may simply be omitted. Another collection can contribute the same local prompt name, for example homewatch/prompts/alerts.yml:

summarize.alerts:
  default_profile: provider/brief
  messages:
    - role: user
      content: Summarize the household alerts.
  output:
    format: text
    validation_mode: none

A provider collection's profiles/brief.yml uses the same local-key convention:

brief:
  backend: provider/default
  model: vendor/example-model
  temperature: 0.2

Its backends/default.yml could contain this synthetic, non-live endpoint:

default:
  endpoint: https://inference.example.invalid/v1

A conventional weatherreporter/collection.yml entry can declare the shared specification, optional collection release, and its layout:

version: 1
release_version: "2.4.0"
configkit:
  imports:
    - glob: prompts/*.yml
      destination: [prompts]

A provider entry can import profiles/*.yml at [profiles] and backends/*.yml at [backends] in the same way. Collection publishers choose which categories to include. A required glob must match; unused categories can be omitted or use ConfigKit's existing optional-import behavior. Ordinary fragments may omit specification headers under the selected IfPresent mode. An entry header is the recommended publishing convention, not a load-time requirement selectively enforced on entries by this mode.

The application selects each entry, enables its imports, and supplies its namespace through the public input destination. For example, the following partial Go snippet uses released ConfigKit v0.8.0 APIs:

entry := configkit.Input{
    Source: configkit.File("/usr/local/share/collections/weatherreporter/collection.yml"),
    Imports: true,
    Destination: configkit.Keys("collections", "weatherreporter"),
}

For independent peer collections, one Underlay group's Inputs is a useful starting point, not a required grouping. Consumers choose precedence and groups. Configure LayeredOptions.SourceVersionMode as SourceVersionModeIfPresent and Definition.Version.Path as configkit.Keys("version"), with PromptKit's supported-specification checker. The application main input remains at the root and declares version: 1 in the examples below; both the main input and its declaration are optional. Namespace and source selection require no manifest parsing in PromptKit.

Selected source Effective destination
Application main document Configuration root
weatherreporter/collection.yml [collections, weatherreporter]
Its prompts/*.yml imports [collections, weatherreporter, prompts]
Homewatch entry and its prompt imports [collections, homewatch] and [collections, homewatch, prompts]
Provider entry and its profile/backend imports [collections, provider], then [profiles] or [backends] appended

Directly selected fragments with Input.Destination remain possible. Entry imports are the recommended packaging convention because publishers can describe their layout. Filenames and directory basenames do not define resource identity; no implicit directory discovery, namespace inference, or collection installer is introduced. A small helper should construct inputs from an explicitly selected source and namespace, without inspecting manifests to compute destinations. collection.yml is a convention, not a required filename. Direct fragments and programmatic sources remain supported through the source API; no parallel collection loader or mandatory filesystem layout is introduced.

Use later underlay groups or main for intentional application/operator overrides. Placement does not establish precedence. Do not also recursively scan files already selected by entry imports: ConfigKit's repeated-source rules still apply.

The entry's source-root version is checked and consumed before its payload composes. Its release_version is retained as ordinary collection metadata. A prompt's nested optional version is also ordinary payload. These are three distinct concepts; source-root release metadata must no longer use version.

The assembled application shape would be equivalent to:

version: 1
collections:
  weatherreporter:
    release_version: "2.4.0"
    prompts:
      summarize.alerts:
        version: "1"
        default_profile: provider/brief
        messages:
          - role: user
            content: Summarize the weather alerts.
        output:
          format: text
          validation_mode: none
  homewatch:
    prompts:
      summarize.alerts:
        default_profile: provider/brief
        messages:
          - role: user
            content: Summarize the household alerts.
        output:
          format: text
          validation_mode: none
  provider:
    backends:
      default:
        endpoint: https://inference.example.invalid/v1
    profiles:
      brief:
        backend: provider/default
        model: vendor/example-model
        temperature: 0.2

This yields weatherreporter/summarize.alerts and homewatch/summarize.alerts, plus profile provider/brief and backend provider/default. The provider's model string remains vendor/example-model; it is not a PromptKit resource identifier.

One fragment may contain several local definitions. A fragment starting directly with messages: would need a destination that also supplies the local prompt key. ConfigKit mounting permits such placement, but with source checks enabled its root version would be the specification header, not prompt metadata. Recommend the local-key mapping above as the standard authoring form: identity travels with the definition and files may be reorganized without changing it. Filename-derived IDs are not needed.

Final Schema And Identity

Use collections as a mapping of namespace to collection body, containing optional release_version and optional backends, profiles, and prompts mappings. Collection release labels are descriptive, with no SemVer ordering or dependency-solving semantics. Metadata is optional: an installed collection can contain resources without claiming a published release.

Each resource key inside a collection is local. The parent namespace and local key follow the accepted slash-free component grammar; their public spelling is exactly namespace/local-name. There are no duplicate embedded id or namespace fields to reconcile. Dots remain ordinary characters.

Retain unqualified application resources through optional root-level backends, profiles, and prompts mappings, also keyed by local name and using the same definition shapes. They produce unqualified public IDs. Under this proposal, namespaced definitions belong under collections, not a second qualified-key spelling in those root maps. This avoids two configuration paths producing the same public identity. This representation belongs to the selected future direction; it does not change today's implemented format.

Stored references such as default_profile, profile backend, and smoke-test profile defaults remain exact public references. Nesting does not automatically qualify a slashless reference or rebind it to caller scope. For example, a profile in provider still declares backend: provider/default. Caller-scoped lookup follows the scope rules above after final indexes are built.

One Installed Prompt And Optional Version

The resource's qualified ID is its sole configuration identity. There is no version-key mapping, implicit latest, version ordering, or parallel installed history. Updating the optional version changes metadata on the same prompt.

An optional prompt version is an opaque string with no SemVer, nonblank, or normalization requirement. Omission or explicit null means no declared label; null can clear a lower-layer or inherited label. Quote numeric-looking labels such as "1". Preserve supplied strings, including empty strings, and distinguish an absent label in inspection. Do not synthesize a version.

Remove request-side prompt-version fields, version selection, ordering, and version defaults as part of the breaking change. Do not reinterpret those fields as exact-match assertions. Execution and inspection resolve the installed prompt by ID alone. Listings expose one prompt per ID with its optional descriptive label separately. A label neither controls execution nor proves unchanged content.

ConfigKit Composition Boundary

Use the implemented upstream contracts without duplicating them in PromptKit. Input destinations use literal mapping-key segments, independent of filesystem roots and precedence. Relative import destinations are sequences of strings; omission and [] retain the importing destination. Null, scalar, and non-string destination members are invalid. No logical path segment means parent traversal or a reset to the application root.

Source-root import directives remain structural. Imported content contributes at its resulting destination, while physical import paths and content_file bases remain relative to their declaring sources. SourceInfo.Destination records placement; normal origins and diagnostics retain real source locations. Selected overlays and explicit CLI overrides remain rooted at the application root.

Mounting preserves source identity, cycle/repetition rules, and load limits. An absent NoSource contributes no payload; an actual empty mapping can materialize its destination containers without clearing lower contents. ConfigKit's reserved structural keys continue to apply. PromptKit must not strip headers, re-key source trees, merge fragments itself, or turn source inspection into an additional enforcement pass.

Composition And Overrides

Different collection keys prevent accidental collisions between equal local resource names. Two sources targeting the same namespace are not automatically invalid: disjoint mapping contributions combine. Overlapping scalar fields, message sequences, nulls, and equal release labels conflict within a strict group. Across deliberate layers they follow ordinary precedence instead.

A higher-layer prompt mapping is a partial override. Changing only its version does not replace its messages or other settings. Omitting a field retains its lower value, and an empty mapping does not clear a definition. Publishers and operators must treat such changes as configuration customization, not as an atomic installation of another prompt release. Do not introduce source audits or a version-triggered replacement rule to conceal this distinction.

Inheritance

Existing sibling inheritance remains useful within each collection's resource mapping: extends: brief can name the local profile brief. Prompt versions, now ordinary optional fields, would inherit and overlay like other fields; version: null explicitly clears an inherited label.

Cross-collection profiles are no longer immediate siblings. Consequently, extends: provider/brief cannot reach that collection under current ConfigKit inheritance. Use existing local sibling inheritance for this development round; applications can still select the qualified provider/brief profile directly. Authors needing a customized derivative may publish an expanded definition. Expansion is a migration option, not a mandatory authoring workflow.

Generic cross-path inheritance remains possible future ConfigKit work if a concrete consumer need justifies it. It is not a prerequisite of this round. PromptKit must not add its own inheritance resolver after ConfigKit handoff.

Specification Versions And Collection Inventory

Shared Specification Policy

Use one Definition.Version policy, with source-local path version, for the candidate PromptKit specification. Initially accept specification text 1 from string or strict decimal-integer declarations; reject explicit null and other unsupported declarations through the ConfigKit checker. Accept an absent main declaration, including an absent main input. Accept additional versions only when PromptKit actually supports their format semantics: accepting a header does not migrate its payload or independently validate a fragment's complete schema. The retired proposals were alternatives, not released formats sharing specification version 1.

Use SourceVersionModeIfPresent without adding a PromptKit mode-selection option in this round. A stricter option can be considered for a demonstrated consumer need. The upstream modes remain distinct:

Mode Implemented behavior Role in this proposal
Default Existing main-only effective-path check and protected-path behavior Not the proposed shared-source policy
IfPresent Checks present declarations; skips absent non-main declarations Initial choice for compact fragments
Required Requires declarations on every participating document Upstream capability; exposing a PromptKit option is deferred

Participating documents include actual main and underlay inputs, transitive and glob imports, and the selected overlay. Defaults, synthetic CLI overrides, resolution output, absent optional imports, and empty groups do not need headers. Explicit null is present and reaches the checker; malformed selected values fail ConfigKit's version-type validation. A rejected declaration fails loading even if a higher layer would supersede that source's payload.

In enabled modes, accepted non-main headers are consumed before composition. The main declaration remains and is protected at its destination-prefixed path. PromptKit's recommended main is unmounted, so that protected path is /version. Do not confuse header consumption with permission to override the remaining protected application field.

IfPresent does not require a collection entry header and does not infer a fragment's compatibility from its entry. A publisher can declare compatibility in the entry while leaving imported files headerless; the assembled configuration still undergoes normal validation. A declaration is not a guarantee of future compatibility or independent schema validation of every imported fragment. ConfigKit checks the declarations actually present. Requiring headers only on entry files is not supported by the load-wide mode. Do not emulate it using a preliminary manifest load or validation of source history. If that stronger guarantee becomes essential, seek an explicit upstream contract instead.

Naming And Optional Main Inputs

Reserve source-root version for specification declarations throughout the new format. Use release_version for optional collection release labels and retain nested prompt version for the optional installed-prompt label. An old release file containing only version: "2.4.0" can fail the specification check; if its text happens to be accepted, that field is consumed and lost as release metadata. Migration must rename it rather than simply enable the new mode.

This reservation affects resource fragments whose local key is literally version: a root mapping with that name is interpreted as a malformed header. Allow that resource name through a nested resource mapping, for example prompts: {version: ...} in a collection entry. Reserve the header position, not the public resource name. Existing configkit and extends structural reservations also remain applicable; destinations do not bypass them.

An absent header in an actual main document still reaches the checker under IfPresent; NoSource invokes the absent-main checker as well. PromptKit's checker accepts omission in both cases. Consumers may assemble only underlays, fragments, or programmatic inputs without manufacturing a main document or header. Do not inject a declaration or report undeclared sources as checked. Present unsupported declarations still fail; final schema validation still runs.

Inspection And Effective Collection Inventory

SourceInfo.VersionCheck is source evidence, not a generic success flag. A non-nil record with Present == false can describe an omission for which the checker did not run. Header paths and locations are source-local. Evidence is detached and does not reread inputs; text is conservatively redacted during callbacks and on failed loads, and successful final records respect completed sensitivity classification. These records belong in explicit configuration inspection, not in ordinary domain validation or collection-release identity.

Derive collection inventory from the final collections mapping: namespace plus optional effective release_version, sorted and detached from the captured engine. Metadata-only and resource-only entries can both appear. Root unqualified resources do not invent a named collection; resource-based namespace enumeration remains separate. The companion implementation plan defines exported inventory types.

An inventory describes effective configuration, not immutable package identity, historical source ownership, or proof of unchanged published contents. Operator overrides may change resources while retaining a release label. Engine construction captures definitions; edits require a replacement engine.

Migration

Migration must choose one installed definition for each former multi-version prompt, move any retained version into its optional field, and relocate qualified resource keys into collection/local-key mappings. Update destination declarations, stored references, inventories, and configuration-inspection paths. Rename collection release metadata to release_version, configure the shared checker and mode, and account for the source-root header reservation and optional main declarations. Remove obsolete request-version fields and version-selection logic from consumer examples and public APIs rather than retaining them as assertions. Preserve provider model strings and source resource bases. Namespace mounting does not automatically relocate schema or artifact stores or rewrite references when a collection is renamed.

Retain the accepted removal of mandatory catalog modules and built-in profiles. This was a breaking configuration/API change under the release policy; the implementation includes consumer migration guidance and updates current contracts. The completed work changes the supported configuration and API as described here.

Existing unscoped exact lookup remains available for IDs valid under the new grammar. Compatibility with arbitrary slash-containing resource IDs is intentionally broken. The migration must:

  • Audit maintained catalogs and representative consumer definitions for invalid IDs and for valid one-slash IDs whose namespace meaning needs review.
  • Choose explicit old-to-new mappings and check for collisions. Do not automatically replace slashes with dots: distinct existing IDs could collapse to the same new identity.
  • Update renamed keys and their references together, including inheritance, prompt default profiles, profile backend references, backend smoke-test defaults, and affected examples and consumer request IDs.
  • Keep provider model strings unchanged. Renaming a profile does not rename the upstream model.
  • Document consumer action and coordinate the PromptKit format change with catalog adoption under the release policy. Its catalog profile-data exception does not cover this general format/API behavior change; deliberately retire its built-in-profile preservation promise when the accepted removal landed. Current policy and format documents now reflect the implemented behavior.

Renaming a backend changes its identity; duplicating it under another ID can separate capacity accounting. Do not use blanket aliases or duplicate catalog entries as an automatic compatibility mechanism. Exact release versions and catalog mappings remain implementation-planning work.

Work Deferred Or Outside This Round

  • Collection installation, automatic downloading, implicit directory discovery, package registries, dependency solving, and live reload.
  • Automatic rewriting of stored references when mounting or renaming collections.
  • Implicit namespace search paths, fallback lookup, namespace-based access control, and filesystem isolation.
  • Changes to provider model names, inference behavior, or ConfigKit precedence.
  • Sequence-item resource catalogs, identity-based overlays, cross-path inheritance, and a PromptKit option requiring specification headers in every document.

Explicit source destinations, direct fragments, programmatic sources, and consumer-selected precedence remain supported in the design. Deferred features can be reconsidered for concrete needs; none blocks implementation.

Target Acceptance

The completed target includes the collection schema and final configuration adapter, catalog and built-in removal, ID-only prompt selection, immutable scoped views, integrated filtering, and collection inventory. Runtime indexes use full identities after ConfigKit handoff. Current contracts and consumer migration guidance describe the implemented behavior. The implementation plan owns sequencing, concrete public shapes, documentation tasks, and focused offline validation under the testing policy.

Namespace And Consumer Acceptance

  • Two prompt scopes resolve the same local name to their respective prompts; a missing name never resolves from the other scope or an unqualified entry.
  • Two provider profile scopes resolve the same local name, such as google.gemma-4-31b-it, to different profiles without changing the configured provider model value google/gemma-4-31b-it.
  • A qualified reference bypasses the selected scope without double qualification; adding another collection does not change local or qualified resolution.
  • Multiple slashes and empty qualified components are rejected in resource IDs and references, while provider model values retain their existing syntax.
  • Prompt shorthand and a profile override use their intended scopes, while stored default references and omitted selections retain their meaning.
  • Scoped views coexist concurrently and share backend capacity with ordinary calls on the underlying engine.
  • Scope creation rejects invalid syntax, accepts well-formed absent namespaces, and treats empty scope fields as no scope. Missing resources fail at lookup; qualified references remain usable even when the default namespace is absent.
  • Listing without an explicit filter uses the resource's view scope, or lists all resources when no scope is selected. Explicit named, unqualified-only, and all-namespaces filters override that default for the current listing only.
  • A valid namespace with no matches produces an empty listing. Namespace filtering respects resource type and exact namespace boundaries.
  • Exact-ID calls using valid IDs and intentional overlays retain their behavior. Migrated catalog references resolve without collisions; migration installs one prompt per ID with an optional descriptive label.
  • Listings preserve full IDs and separate optional prompt version labels. Qualified results round-trip through either engine or view; unqualified results round-trip through the engine without a corresponding scope.
  • Representative validation, inspection, execution, and smoke-test paths use the same qualification rules without duplicating every test at every layer.
  • Engines use caller-selected definitions without automatically contributing provider catalogs or PromptKit-owned profiles. Ordinary directory and embedded configuration sources retain ConfigKit composition semantics.
  • PromptKit does not add source-history-based duplicate-namespace rejection or independently pre-validate collection definitions before the final handoff.

Configuration Acceptance

Exercise typed assembly of multiple collections, relative/nested imports, source-local header checking and consumption, ordinary release and prompt metadata, unqualified resources, optional prompt labels, inventory without release metadata, source-relative content, and intentional partial overrides. Cover headerless entries/fragments, omitted main inputs and headers, and resources named version in nested mappings. Verify ID-only lookup without request-version selection or assertions. No automatic provider catalogs or built-in profiles should be contributed.

Do not duplicate ConfigKit's generic source validation or enforce domain rules through historical source inspection. Preserve local inheritance and ordinary mapping overrides while migrating catalog definitions.

Review Evidence

The 2026-10-16 review against ConfigKit development commit e66c371 verified mounted assembly, relative imports and resource contexts, shared header checks, and this document's YAML assembly through public consumer tests. The 2026-10-17 review of release v0.8.0 at 137769f passed maintained consumer examples and focused public API probes for sequence assembly, duplicate rejection, whole-list replacement, inheritance limits, header consumption, and final identity validation boundaries. The latter review informed the decision to retain mappings. These checks establish the upstream behavior relied on by the implemented PromptKit feature.

Delivery Reference

No substantive design question remains. ConfigKit v0.8.0 is the selected dependency. The implementation plan records helper signatures, inventory and optional-label representations, identifier migration mappings, and completed consumer migration work. Its delivery choices implement these accepted contracts without adding speculative options or reopening workflows.

Consolidation And Retired Alternatives

This document replaces the separate namespace roadmap and collection proposal 3. Their pre-consolidation versions, including considered namespace API alternatives, remain available at PromptKit commit ddda2f8:

git show ddda2f8:docs/roadmap/namespaces.md
git show ddda2f8:docs/roadmap/configuration-collections-mounted-fragments.md

The unselected collection proposals remain available at b5866dd:

git show b5866dd:docs/roadmap/configuration-collections-current-configkit.md
git show b5866dd:docs/roadmap/configuration-collections-identified-records.md

Proposal 1 used fully qualified mapping keys. Proposal 2 requested identified record sequences, identity-based overlays, and record inheritance. ConfigKit v0.8.0 implements a narrower sequence-composition capability; it does not supply those identity-based overlays or inheritance. Its sequence feature was evaluated and not selected for this roadmap. The original upstream feature-request lineage is recorded in ConfigKit history under docs/roadmap/promptkit-identified-records-feature-request.md; no outstanding part of that request is a prerequisite here.

The accepted configuration choices remain: optional main declarations and if-present checks; source-root version reserved without banning that resource name; removal of request-version machinery; current ConfigKit inheritance; and flexible source assembly with a small convenience API. This consolidation records those decisions and their implementation scope; current behavior is maintained in the canonical format and consumer documents.