847 lines
44 KiB
Markdown
847 lines
44 KiB
Markdown
# 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](implementation.md) 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](implementation.md) 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](../formats.md) and
|
||
[consumer guide](../consumers/pkg-promptkit.md) 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](../adr/0001-configuration-boundary.md) 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](../policy/architecture.md) 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](implementation.md):
|
||
|
||
```go
|
||
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](implementation.md). 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).
|
||
|
||
### 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](#inspection-and-effective-collection-inventory).
|
||
|
||
## Authoring And Assembly
|
||
|
||
A file such as `weatherreporter/prompts/alerts.yml` would contain:
|
||
|
||
```yaml
|
||
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`:
|
||
|
||
```yaml
|
||
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:
|
||
|
||
```yaml
|
||
brief:
|
||
backend: provider/default
|
||
model: vendor/example-model
|
||
temperature: 0.2
|
||
```
|
||
|
||
Its `backends/default.yml` could contain this synthetic, non-live endpoint:
|
||
|
||
```yaml
|
||
default:
|
||
endpoint: https://inference.example.invalid/v1
|
||
```
|
||
|
||
A conventional `weatherreporter/collection.yml` entry can declare the shared
|
||
specification, optional collection release, and its layout:
|
||
|
||
```yaml
|
||
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:
|
||
|
||
```go
|
||
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:
|
||
|
||
```yaml
|
||
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](#accepted-consumer-scope) 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](../policy/release.md); 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](../policy/release.md). 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](implementation.md) owns sequencing, concrete public shapes,
|
||
documentation tasks, and focused offline validation under the
|
||
[testing policy](../policy/testing.md).
|
||
|
||
### 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](implementation.md) 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`:
|
||
|
||
```sh
|
||
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`:
|
||
|
||
```sh
|
||
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.
|