44 KiB
Configuration Collections, Namespaces, And Scoped Lookup
Status: accepted feature roadmap; not implemented in PromptKit. This document consolidates the namespace roadmap and selected collection proposal 3. The configuration and namespace design decisions are settled. The companion implementation plan defines concrete API delivery choices and the staged work needed to reach this target state.
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. PromptKit currently selects v0.7.1. Dependency adoption and its verification belong to the implementation plan; 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. Recording this
prerequisite does not itself upgrade PromptKit's dependency.
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.Destinationfor root-relative placement and importdestinationfor placement relative to the immediate importing document.Options.SourceVersionModeandLayeredOptions.SourceVersionMode, applying a sharedDefinition.Versionpolicy 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.DestinationandSourceInfo.VersionCheckevidence, 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.
These declarations do not exist yet. 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. Initially omit a one-string shorthand that implicitly scopes all resource types.
The view would share the engine's captured configuration, clients, and execution machinery. Selecting a namespace would not rebuild the engine or duplicate its backend capacity accounting. Consumers could use different views concurrently without changing a shared current-namespace setting.
The accepted resolution rules, independent of the final scope API, are:
- 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.
- A missing scoped resource does not trigger a search in other namespaces or among unqualified IDs. Loading another collection cannot redirect lookup.
- A valid supplied name containing one slash is fully qualified and is looked up exactly, regardless of scope. Malformed resource references are rejected.
- 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.
- 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.
- 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 is a breaking configuration/API change under the release policy; implementation must include consumer migration guidance and update current contracts when it lands. The proposal does not change today's supported configuration or API.
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 lands. Current policy and format documents remain unchanged until implementation.
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 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 when it lands. 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 valuegoogle/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 upstream behavior, not an implemented PromptKit feature.
Delivery Reference
No substantive design question remains. ConfigKit v0.8.0 is the selected dependency. The implementation plan specifies helper signatures, inventory and optional-label representations, identifier migration mappings, and 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 does
not change those decisions or implement the feature.