Retire unselected collection configuration proposals

This commit is contained in:
2026-10-16 11:59:34 +00:00
parent b5866dd749
commit 190ec69345
4 changed files with 88 additions and 939 deletions

View File

@@ -1,372 +0,0 @@
# Proposal 1: Namespaced Collections With Current ConfigKit
Status: proposed PromptKit configuration standard; not implemented or accepted.
This proposal addresses the pending configuration decision in the
[namespace and collection roadmap](namespaces.md). It requires PromptKit changes
but no ConfigKit extension. Proposal 2, which may assume upstream changes, is
outside this document.
## Recommendation
Keep the existing top-level `backends`, `profiles`, and `prompts` mappings, and
use fully qualified resource IDs as their actual YAML keys. Add a `collections`
mapping keyed by namespace for release metadata. The application's main input
owns `version: 1`; reusable collection contributions omit that top-level field.
Compose independently maintained peer collections in one ConfigKit underlay
group. Put intentional application/operator overrides in later groups or the
main input. This uses ConfigKit's distinction between strict composition and
explicit precedence to protect independent contributions while retaining
ordinary configuration customization.
Namespace identity exists in the configuration tree before composition. A
separate `id` field inside an otherwise unqualified prompt definition would be
too late: ConfigKit merges mapping keys before PromptKit interprets definitions.
## Reviewed Baseline And Evidence
Reviewed the local `../ConfigKit` checkout at commit
`6fffde4af8f31f99d3ee295c426eb3234cb0fb77` (v0.7.1), the version selected by
PromptKit. The governing upstream sources are its
[configuration contract](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/v0.7.1/docs/config.md)
and [composition/overlay decision](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/v0.7.1/docs/adr/0001-separate-composition-and-overlay.md).
Implementation evidence includes `internal/engine/compose_test.go`,
`internal/engine/load_request_test.go`, `internal/engine/imports_test.go`, and
the public `VersionRule` and `LoadLayered` declarations in that checkout.
The relevant current rules are:
| ConfigKit behavior | Consequence for this proposal |
| --- | --- |
| Within a composition group, mappings combine recursively; every other overlapping pair conflicts, even equal scalars | Distinct qualified keys coexist; repeated metadata versions or message sequences conflict within the group |
| Between underlay groups, later values overlay earlier ones; main has higher precedence | Directory order must not accidentally become collection precedence; intentional overrides are separate layers |
| Overlay mappings combine recursively; scalars, sequences, and null replace lower values | An override can replace a setting or message sequence without replacing the entire prompt |
| Syntax and strict-composition failures happen before final domain validation | A higher layer cannot repair a conflicting peer group |
| The configured specification-version path belongs to main; underlays and imported fragments cannot claim it | There is one assembled-configuration specification declaration, not independently checked collection headers |
| Sibling `extends` resolves after composition and precedence | Fully qualified profile keys work as exact cross-collection inheritance references |
| Defaults, typed decoding, resolution, and semantic validation operate on final assembled values | PromptKit need not validate each collection separately |
| Source origins retain resource context and inspection history | File-backed content and explicit provenance inspection can retain existing behavior |
PromptKit already provides directory and embedded-source helpers, and its
configuration adapter calls `configkit.LoadLayered` once. Its current document
schema only exposes `backends`, `profiles`, and `prompts`; this proposal adds
version and metadata fields rather than claiming they work today. The
[current format reference](../formats.md) remains authoritative until adoption.
## Proposed Document Shape
The effective configuration has these application fields. Source documents may
also use supported ConfigKit directives, which ConfigKit consumes before the
application handoff:
| Field | Proposed meaning |
| --- | --- |
| `version` | PromptKit configuration/collection specification identifier, owned by the main input |
| `collections` | Optional mapping from namespace to collection release metadata |
| `backends` | Existing backend definitions, keyed by exact resource ID |
| `profiles` | Existing profile definitions, keyed by exact resource ID |
| `prompts` | Existing prompt definitions, keyed first by exact resource ID and then by prompt version |
The namespace roadmap's accepted grammar applies to resource keys: either an
unqualified ID or exactly `namespace/local-name`. Neither qualified component
contains `/`; dots remain available within local names. Published collections
use qualified keys. Unqualified application definitions remain supported.
Keep IDs in mapping keys, including the prompt's outer key; do not introduce
another embedded `id` or `namespace` field in each definition. Collection
metadata does not prefix or rewrite keys. Filenames and directory names have
no effect on identity. Namespace comparison and references remain exact.
The following compact contribution could be supplied by a provider collection.
The endpoint and model are synthetic configuration examples, not live targets:
```yaml
collections:
provider:
version: "2026.10.14"
backends:
provider/default:
endpoint: https://inference.example.invalid/v1
profiles:
provider/brief:
backend: provider/default
model: vendor/example-model
temperature: 0.2
```
Two independently maintained application collections can then supply the same
local prompt name without colliding:
```yaml
collections:
weatherreporter:
version: "2.4.0"
prompts:
weatherreporter/alerts.summarize:
"1":
default_profile: provider/brief
messages:
- role: user
content: Summarize the weather alerts.
output:
format: text
validation_mode: none
```
```yaml
collections:
homewatch:
version: "3.1.0"
prompts:
homewatch/alerts.summarize:
"1":
default_profile: provider/brief
messages:
- role: user
content: Summarize the household alerts.
output:
format: text
validation_mode: none
```
ConfigKit sees separate keys under `prompts`. It never needs to understand
PromptKit namespaces to preserve both definitions. The quoted `"1"` keys are
individual prompt versions, unrelated to the main specification version or the
collection release strings.
## Specification And Collection Versions
Configure ConfigKit's existing `VersionRule` at `configkit.Keys("version")`.
Recommend requiring an explicit supported value on the selected main input for
this new standard: canonical YAML is `version: 1`. ConfigKit exposes both integer
`1` and string `"1"` as version text `1`; accept both, while documenting the
integer spelling. Reject absent, null, and unsupported versions through the
configured checker. These are proposed PromptKit format choices, implemented
through ConfigKit's supported callback, not a separate input-scanning pass.
A minimal main input is:
```yaml
version: 1
```
Programmatic applications can supply the same main input with `configkit.Mapping`.
Do not infer the specification from a directory name or collection release, strip
version fields from fragments, or silently inject a version to hide a missing
declaration. Requiring main instead of accepting an omitted main is an additional
explicit migration cost of this proposal.
Reusable collection files, including a collection's root metadata file, omit
top-level `version`. So do operator underlays and imported fragments. ConfigKit
protects the configured version path even if a repeated value would equal `1`.
The version checker is invoked by ConfigKit once, at its normal point before
underlay assembly; it selects the contract for the complete configuration.
For collection metadata, propose an ordinary optional `collections` mapping:
- Its keys are exact nonblank namespace names without `/`.
- Each effective entry is a non-null mapping with a required nonblank string
`version`. Quote collection release identifiers; this nested field does not
receive ConfigKit's special integer-to-string handling for top-level `version`.
- Release strings are descriptive publisher identifiers, not executable
dependency constraints or implicit precedence. No SemVer ordering is needed.
- An omitted, null, or empty top-level `collections` mapping means no declared
collection metadata. Null individual entries are invalid final descriptors;
null is not a new per-entry deletion operator.
Validate this metadata only in final effective values. Do not require a metadata
entry for every resource namespace or require every metadata entry to have
resources. This lets ordinary application configuration add definitions or
overrides without fabricating a collection release. Collection publishing
guidance recommends declaring its own namespace and version, but PromptKit
does not audit which source file originally contributed a resource.
## Source Selection And Layering
Recommend this caller-selected assembly:
| Tier | Contents | Relationship |
| --- | --- | --- |
| First underlay group | All selected peer collection inputs | Strict composition across the entire group |
| Later underlay groups, if selected | Application defaults or operator override documents, in caller-chosen priority order | Strict within each group; later groups overlay earlier groups |
| Main input | Specification declaration and optional highest-priority local settings | Overlays the underlays |
These are integration guidance, not a new enforced source policy. The application
still selects ConfigKit groups and precedence explicitly. In particular, separate
directories do not each require their own underlay tier. Append their discovered
inputs into one group when they are independent peer libraries. Putting each
directory into a successive group intentionally enables later overrides.
An illustrative caller arrangement using existing source APIs is:
```go
var peers []configkit.Input
for _, directory := range collectionDirectories {
inputs, err := promptkit.DirectoryInputs(directory)
if err != nil { return err }
peers = append(peers, inputs...)
}
main := configkit.Input{Source: configkit.File(applicationConfigFile)}
sources := promptkit.ConfigurationSources{
Underlays: []configkit.Underlay{{Inputs: peers}},
Main: &main,
}
// Pass sources to NewEngine after the proposed document schema is implemented.
```
The helpers already supply filesystem context, recursively select YAML, and
leave imports disabled. An operator may install a collection with an ordinary
`collection.yml` metadata contribution and definition files in subdirectories.
That filename is a recommended packaging convention, not a special loader
instruction. All selected YAML files participate normally; resource files should
be outside that scan or use suitable non-configuration formats.
For explicit imports instead, select only collection entry-point files, enable
imports on those inputs, and let ConfigKit traverse their fragments. Do not both
recursively select all files and import those same files in the same group:
ConfigKit rejects repeated sources. Imports remain root-contained, relative,
opt-in paths/globs; they neither fetch repositories nor establish precedence.
Each directory input can have its own filesystem root, so peers need not share
an installation directory.
Importing all peers from main is also possible under ConfigKit's rules, but
main's own definitions and its imports then compose strictly. A main document
cannot override something in its own import graph. The recommended underlay
arrangement keeps main available for ordinary application overrides without
requiring a selected-overlay API addition to PromptKit.
## Collisions, Partial Contributions, And Overrides
| Contributions | Current ConfigKit outcome |
| --- | --- |
| `weatherreporter/alerts.summarize` and `homewatch/alerts.summarize` | Both definitions remain |
| Same qualified prompt ID, different prompt-version keys | Both versions remain; PromptKit's existing version selection applies |
| Same ID/version, disjoint definition fields in one group | Fields compose into one definition |
| Same ID/version, overlapping message sequences in one group | `compose.conflict`, even if the sequences are identical |
| Same `collections.<namespace>.version` in two peer inputs | `compose.conflict`, even if release strings are equal |
| Same namespace with disjoint metadata/resource contributions | Contributions can compose; there is no namespace-ownership error |
| Same qualified ID or collection metadata in a later layer | Ordinary recursive overlay semantics apply |
This does not promise that every repeated resource ID or collection namespace
is fatal. ConfigKit permits disjoint mapping contributions by design. Namespace
qualification avoids accidental *cross-namespace* collisions; strict grouping
detects conflicting leaves among peers. Deliberately sharing an exact key in
different precedence layers still allows overrides.
For example, main can override only the selected profile's temperature:
```yaml
version: 1
profiles:
provider/brief:
temperature: 0.1
```
The lower backend and model remain. Replacing `messages` would replace the
entire sequence, not append messages. An empty mapping does not clear inherited
children. Explicit null retains existing PromptKit field-specific meanings;
this proposal adds no generic delete or replace-whole-resource directive.
Do not inspect provenance to reject a higher-priority metadata edit, or declare
that a collection namespace is locked to one input. Provenance is for explicit
operator explanation. Final domain validation can reject an invalid resulting
profile or descriptor, but cannot resurrect or reject displaced source values.
## References, Inheritance, And Resources
Stored references remain exact IDs. A collection prompt can select
`provider/brief`; a profile can inherit `provider/brief` by writing that exact
key as `extends`. Profile mappings remain siblings under `profiles`, so this
uses ConfigKit's existing inheritance without moving definitions into nested
collection containers. A consumer's selected namespace never rebinds those
stored references. Provider `model` strings remain unchanged and may contain `/`.
Prompt versions remain an inner mapping. ConfigKit's sibling inheritance is
generic, but it does not make another prompt's outer ID a sibling of an inner
version definition. This proposal introduces no cross-prompt reference resolver.
Keep existing resource semantics: ConfigKit records the effective field's origin
and source context, and PromptKit uses that context for file-backed prompt
content. Do not derive content paths from namespaces or a collection metadata
filename. Schema roots and runtime artifacts retain their existing separately
configured behavior; a metadata file does not automatically mount schemas or
change their resource resolution. Moving to configuration collections must not
claim broader resource portability than those contracts provide.
## Collection Inventory
Propose a public `ListCollections` operation returning detached descriptors from
the engine's captured effective metadata. Exact Go names and signatures remain
to be chosen. Each descriptor contains namespace and declared collection release
version. Expose the effective specification version once at engine/configuration
level, not as a falsely independently checked version on each collection.
An absent metadata map returns no collection descriptors. Sort by namespace for
stable inspection. Report resources separately through the accepted namespace
listing/filter APIs; unregistered namespaces can exist there without fabricated
collection versions. A metadata-only declaration can appear in collection
inventory even when no resources remain under that namespace.
This API means "effective declared collections," not "every manifest ever
loaded" or "verified installed package versions." Metadata and resources can
be changed by later layers. A declared release does not certify that all effective
definitions are identical to a publisher's release. ConfigKit inspection explains
the sources and edits; the domain API does not replay history or reread files.
## PromptKit Changes And Deliberate Limits
Adoption would add the version field/checker and collection metadata schema,
final-value validation, captured inventory, and the public collection API. It
would also implement the accepted scoped lookup and discovery behavior and
remove automatically bundled catalogs and built-in profiles, as tracked in the
parent roadmap. Update canonical contracts and migration guidance only when
those changes land. Existing documents with no main specification declaration
would need an explicit migration under the proposed version policy.
There is no ConfigKit change, per-collection loading/validation pass, source-level
namespace registry, key-rewriting stage, or automatic configuration installation.
Definitions retain their qualified keys throughout assembly. Invalid effective
domain values are PromptKit's responsibility; composition, imports, inheritance,
and version-path protection remain ConfigKit's responsibility under
[ADR 0001](../adr/0001-configuration-boundary.md).
The main limitation is independent specification compatibility: a reusable
collection cannot carry its own protected top-level `version: 1` when imported
or used as an underlay. Publishers/operators must select configuration compatible
with the main standard. Strict decoding catches unsupported surviving fields,
but it is not an independent compatibility check for each collection release.
This is the principal issue to compare with proposal 2.
## Validation And Review Criteria
Review verification on 2026-10-14: the focused ConfigKit composition, grouped
loading, and import-version tests passed in the local checkout. A temporary
offline consumer using that checkout also loaded the five YAML examples above
through a prospective document schema and verified distinct identities, equal
and unequal peer conflicts, disjoint contributions, main overrides, atomic
sequence replacement, main/underlay/import version ownership, and qualified
inheritance. This establishes feasibility with current ConfigKit; it does not
implement the proposed PromptKit schema or inventory API. No temporary harness
or module replacement is part of either repository.
Review this proposal against these observable cases:
- Two collections using the same local prompt name survive composition under
distinct qualified keys.
- Equal and unequal overlapping metadata versions fail in one peer group;
disjoint contributions compose, and later metadata overrides remain allowed.
- Main overrides one profile field while preserving other fields; sequence
overrides replace whole sequences.
- A strict peer conflict cannot be hidden by a higher layer.
- Main `version: 1` succeeds, while omitted/null/unsupported versions fail the
proposed checker and imported/underlay declarations fail ConfigKit's existing
version ownership checks.
- Fully qualified inheritance resolves through the assembled sibling mappings;
file-backed content retains its declaring source's context.
- Inventory reflects final metadata and remains captured when source files
change; resource namespace discovery does not require a metadata declaration.
The configuration schema, explicit-version requirement, metadata null rules,
inventory shape, and recommended peer-group layout are proposal choices to
review. They do not resolve the parent roadmap's decision 5 merely by being
written here.

View File

@@ -1,434 +0,0 @@
# Proposal 2: Namespaced Collections As Identified Records
Status: proposed alternative; not implemented or accepted. This is a middle
ground between [proposal 1](configuration-collections-current-configkit.md),
which uses current ConfigKit, and
[proposal 3](configuration-collections-mounted-fragments.md), which changes
source placement and document organization. Proposal 3 also adopts the later
requirement for one installed definition per prompt and an optional version;
this proposal retains versioned record identities for comparison. The parent
[namespace and collection roadmap](namespaces.md) retains the accepted product
decisions and the pending configuration decision.
## Recommendation And Scope
Represent prompts, profiles, and backends as sequences of records with explicit
identity fields. Prefer separate `namespace` and local `id` fields. Ask ConfigKit
to support opt-in, application-declared identity rules at selected configuration
paths, so those records compose and overlay by identity rather than position.
The extension is one cohesive capability: identified record sequences, including
their strict composition, overlay, inheritance, and provenance behavior. It is
not a general user-defined merge framework. ConfigKit continues to own the
complete assembly process and hands PromptKit one final resolved configuration.
Keep the root `prompts:`, `profiles:`, and `backends:` keys. Keep collection
metadata as the ordinary namespace-keyed mapping from proposal 1. Keep the main
input's specification declaration and the existing source/layer model. This
proposal does not add bare single-record files, source mounting, implicit
namespace injection, or per-import specification checking. Those are separate
questions for proposal 3.
## Current Baseline And Required Extension
The reviewed ConfigKit baseline remains v0.7.1, local checkout commit
`6fffde4af8f31f99d3ee295c426eb3234cb0fb77`. Its
[configuration contract](https://gitea.maximumdirect.net/eric/ConfigKit/src/tag/v0.7.1/docs/config.md)
and the implementation/tests reviewed for proposal 1 establish that:
- Sequences are atomic: overlapping sequences conflict during strict composition
and replace one another across precedence layers.
- ConfigKit can already decode a single sequence of mappings into application
records. It does not currently use their fields as merge identities.
- `extends` currently identifies immediate mapping siblings. A sequence-element
mapping cannot establish its own sibling relationship, and mapping-valued
`extends` is not a supported reference form.
- Final decoding, application resolution, and semantic validation happen after
configuration assembly and inheritance.
Consequently, changing PromptKit's document structs alone would not implement
this proposal. Concatenating sequences in PromptKit or converting each source
to keyed mappings before calling ConfigKit would violate the chosen boundary.
## Proposed Authoring Format
Each prompt record describes one prompt version, without a nested version-key
mapping. Use explicit strings for identity fields:
```yaml
collections:
weatherreporter:
version: "2.4.0"
prompts:
- namespace: weatherreporter
id: summarize.alerts
version: "1"
default_profile: openrouter/gemini-flash-latest
messages:
- role: user
content: Summarize the weather alerts.
output:
format: text
validation_mode: none
```
A second collection can use the same local ID without colliding:
```yaml
collections:
homewatch:
version: "3.1.0"
prompts:
- namespace: homewatch
id: summarize.alerts
version: "1"
default_profile: openrouter/gemini-flash-latest
messages:
- role: user
content: Summarize the household alerts.
output:
format: text
validation_mode: none
```
The profile name is an illustrative configured reference, not a statement about
current provider inventory. A synthetic provider contribution shows the same
record shape for backends and profiles:
```yaml
collections:
provider:
version: "2026.10.14"
backends:
- namespace: provider
id: default
endpoint: https://inference.example.invalid/v1
profiles:
- namespace: provider
id: brief
backend: provider/default
model: vendor/example-model
temperature: 0.2
```
These are independent format examples; the synthetic provider does not satisfy
the earlier examples' OpenRouter reference. An assembled configuration must
supply its selected references through ordinary configuration and existing
PromptKit validation/selection rules.
Retain the existing `default_profile` field instead of also renaming it to
`profile`. Reducing nesting and making identity explicit are the changes under
evaluation; unrelated field renames are unnecessary.
### Identity And Public References
Register these record identities with ConfigKit:
| Configuration path | Identity fields, in order |
| --- | --- |
| `backends` | `namespace`, `id` |
| `profiles` | `namespace`, `id` |
| `prompts` | `namespace`, `id`, `version` |
PromptKit turns each final `(namespace, id)` pair into its public
`namespace/id` identifier after handoff; the prompt version remains a separate
selection dimension. The conversion creates runtime indexes, not another merge
or source-processing pass. It must reject invalid final domain identities rather
than silently re-keying them.
Require identity fields to be explicitly present strings in every record,
including partial override records. A prompt's nested `version` is a prompt
identity component, not the protected top-level specification declaration.
Do not derive any identity component from a typed default, parent, filename,
collection metadata entry, or caller execution scope: matching happens before
those application-level interpretations.
For unqualified application definitions, use `namespace: ""` explicitly; their
public identifier is the local `id` alone, without a leading slash.
Nonempty namespaces and local IDs follow the accepted namespace grammar;
local IDs cannot contain `/`. Preserve exact spelling. PromptKit owns these
domain checks on the final values; ConfigKit only requires usable structural
identity fields and compares exact string tuples. Distinct tuples must never
be conflated by concatenating fields with an unescaped delimiter internally.
Stored `default_profile` and `backend` references remain fully qualified or
exact unqualified strings, as already selected in the namespace roadmap.
Provider model values retain their existing syntax, including slashes.
## Proposed ConfigKit Contract
### Explicit Registration
The application's ConfigKit definition declares record-sequence rules at exact
semantic paths, with ordered identity field names. The table above is a
conceptual registration, not a proposed Go API declaration. ConfigKit does not
guess from field names such as `id`, and configuration files cannot silently
switch their own merge policy. No arbitrary merge callbacks are required.
An initial implementation only needs direct string identity fields at explicitly
registered paths. Wildcard registration, computed keys, key normalization,
implicit identity defaults, and nested identity expressions are not required
by this proposal.
At a registered path, a non-null contribution must be a sequence of mappings.
Every record must supply all structural identity fields with the declared
string type. Missing/null/non-string identity fields or non-mapping records
are ConfigKit structural errors: the loader cannot assign them a record identity.
These errors cannot be hidden by a later layer. Nonidentity body fields retain
the existing final-value decoding/validation lifecycle.
For consistency with duplicate mapping-key handling, recommend rejecting two
records with the same tuple within a single source sequence. Across separate
sources, matching tuples participate in normal composition as described below.
This is a proposed explicit upstream rule, not a claim about current ConfigKit
or a PromptKit source-level duplicate scan.
### Strict Composition And Overlay
Treat identity fields as the record's structural key. Their repeated equal
values are not overlapping body fields that cause composition conflicts.
Retain the fields in the final record and preserve their origin information.
All remaining fields form the body to which existing mapping rules apply.
| Contributions at a registered path | Proposed result |
| --- | --- |
| Different identity tuples in one group or different layers | Retain both records |
| Same tuple across peer sources, disjoint body fields | Recursively compose one body |
| Same tuple across peer sources, overlapping non-mapping body values | Composition conflict, even if equal |
| Same tuple in a higher-priority layer | Recursively overlay the higher body on the lower body |
| Same prompt namespace/local ID but different prompt version | Distinct records |
| Identity field changed in a later contribution | A different record; never an implicit rename of the old record |
Once a tuple is selected for matching, identity fields cannot themselves be
overlaid into a different tuple. This avoids record identity changing during
composition. A higher layer need only repeat the tuple and changed body fields:
```yaml
version: 1
profiles:
- namespace: provider
id: brief
temperature: 0.1
```
The provider/backend/model fields inherited through layering remain. A new
record introduced by an override still undergoes ordinary final validation;
matching by identity does not guarantee it is a complete runnable profile.
For registered sequences, recommend that `[]` contributes no records and does
not clear lower records, like an empty keyed mapping. This differs deliberately
from ordinary sequence replacement. Explicit null at the whole registered path
uses ordinary cross-layer replacement and can clear that whole collection when
the application's final schema permits null. Within strict composition, null
conflicts with another present value as usual. No per-record deletion marker,
rename operation, or general patch language is added.
Process tiers in precedence-assembly order, lowest first, with deterministic
source order within a tier. Preserve first-contribution order for distinct
records, retain a matched record's position, and append newly encountered
tuples. This makes output deterministic without giving position precedence or
identity meaning; acquisition order need not equal assembly order. PromptKit
can still sort its public inventory independently.
**Unregistered sequences keep all current semantics.** In particular, `messages`
remains atomic: it conflicts across strict peer definitions and is replaced
as a whole by a later override. Identified record support must not turn every
sequence into a union or keyed merge.
### Inheritance Within A Registered Sequence
The extension must give identified records a sibling identity space. For
composite identities, recommend a structural `extends` mapping containing the
complete parent tuple. A profile example is:
```yaml
profiles:
- namespace: weatherreporter
id: brief
extends:
namespace: provider
id: brief
temperature: 0.4
```
ConfigKit resolves this relationship within the same registered `profiles`
sequence after all composition and precedence. It overlays the child's body
over the parent's resolved body, retains the child's identity fields, and
consumes `extends` before typed decoding. Parent identity fields are never
inherited into the child. References for a three-field identity, such as a
prompt record, must include all three fields; no version guessing or namespace
fallback is introduced. References cannot cross resource paths.
Preserve existing behavior for omitted relationships, `extends: null`, missing
parents, cycles, and overridden relationships. A partial child contribution
can retain a lower-layer relationship, and only final active relationships are
resolved. Inherited file-backed content keeps the declaring field's source
context, not the child's source directory.
This structural reference form is only enabled for records at registered paths.
Existing mapping-sibling string inheritance remains unchanged elsewhere. A
future single-field record rule could use that same field in the reference
mapping; no PromptKit-specific interpretation of slash-separated parent strings
is needed inside ConfigKit.
### Provenance, Inspection, And Typed Handoff
ConfigKit owns record matching and provenance throughout assembly. A conflict
must identify the record and conflicting source locations, not merely report
that two sequences occupy `prompts`. Preserve ConfigKit's safe diagnostic
contract when representing that identity; do not indiscriminately echo input
field values in error messages. Identity-field declarations retain their
contributing locations even when those equal structural fields do not conflict.
Keep the final application-facing shape as a sequence of records, suitable for
ordinary slice decoding and effective-config rendering. ConfigKit can use a
private identity index, but should not expose private normalized nodes to
PromptKit. Final body presence/null state, field origins, inheritance history,
redaction, and passive source resource contexts must survive matching.
No new public identity-selector path language is required for this proposal.
Inspection may address final records by sequence index, with ConfigKit mapping
effective field paths to the appropriate historical source locations internally.
Indexes are snapshot-specific; consumers locate a record by its identity before
constructing an indexed path. Histories must not become attached to another
record merely because its source position differed. Existing positional diff
behavior need not become an identity-aware cross-snapshot diff in this work.
Rules must apply consistently to all enabled source kinds and composition
entry points, including imports and layered input. Existing explicit CLI
overrides must not leave duplicate or malformed structural identities in the
final registered sequences; ConfigKit owns their integration and consistency
checks. Ordinary application resolution must not trigger another merge or
inheritance pass. Detailed public option declarations and implementation
mechanics belong in the upstream design if this proposal is selected.
## Collection Metadata, Versions, And Assembly
Retain proposal 1's ordinary `collections.<namespace>.version` metadata and
effective-descriptor inventory. It is distinct from the generic ConfigKit term
"record sequence." Collection declarations still follow mapping composition;
they are not a source registry or exclusive ownership claim. Repeated metadata
release strings conflict in a strict peer group and can be overridden in a
later layer. PromptKit does not add an independent duplicate-namespace scan.
Also retain proposal 1's main-owned `version: 1` declaration and its proposed
explicit-version requirement. The value denotes the specification for the
assembled configuration, not each imported package. Prompt record versions and
collection release strings do not change that ownership rule. These proposals
are alternative candidates for a future standard; they are not two simultaneously
supported meanings for a released specification version.
Combine peer collection inputs in one strict underlay group; use later groups
or main for intentional overrides. Callers continue to choose grouping and
precedence. Directory inputs and explicit imports use existing ConfigKit
selection and resource-context rules. There is no PromptKit pre-parser that
reformats records, removes version fields, or replays the original sources.
**Independent collection specification checks remain unresolved.** This bounded
extension does not permit imported or underlay roots to declare their own
protected top-level `version`. A publisher must still supply configuration
compatible with the selected main standard. Proposal 3 may consider a broader
source/version contract; proposal 2 must not imply that record identity solves
that separate issue.
## Ergonomic Alternatives And Tradeoffs
The first syntax discussed is also compatible with the generic extension:
```yaml
prompts:
- id: weatherreporter/summarize.alerts
version: "1"
default_profile: openrouter/gemini-flash-latest
messages:
- role: user
content: Summarize the weather alerts.
output:
format: text
validation_mode: none
```
That alternative registers prompt identity as `(id, version)` and profile/backend
identity as `(id)`. It is simpler to align with public reference strings and
requires fewer identity fields in partial overrides. It repeats the namespace
inside each full ID and gives authors no separate namespace field.
Prefer the composite form for explicit author-facing fields and easier editing
of local names. Do not support both forms opportunistically in the same standard:
that would require aliasing or normalization before identity matching. Select
one representation for the adopted specification. The first form remains a
reasonable alternative if the extra namespace line proves more cumbersome than
the qualified string.
Retaining keyed mappings only for profiles could avoid extending inheritance
to records, but would give authors different identity conventions by resource
type. The recommendation favors a consistent record model and includes the
necessary generic inheritance work explicitly.
| Concern | Proposal 1 | Proposal 2 |
| --- | --- | --- |
| Author identity | Qualified YAML mapping key | Explicit namespace/local-ID fields |
| Prompt version | Nested mapping key | Field in each prompt record |
| Cross-source composition | Current ConfigKit | Opt-in record identity extension |
| Body conflicts and precedence | Mapping rules | Same mapping rules after tuple matching |
| Root `prompts:` wrapper | Required | Required |
| Provider model strings and execution references | Unchanged | Unchanged |
| Independent imported specification checks | Unavailable | Still unavailable |
| Main maintenance cost | PromptKit schema/API work | PromptKit work plus ConfigKit record lifecycle support |
This is more than a syntax-only change, but remains bounded: it introduces one
generic representation for identified entries while preserving configuration
source ownership and ordinary merge policy. It does not introduce collection
installation, dependency resolution, namespace ownership, mounted sources,
arbitrary transforms, or a general-purpose merge strategy system.
## Delivery And Validation
If selected, agree the upstream ConfigKit record contract first, implement and
test it there, then adopt that capability in PromptKit's prospective schema.
PromptKit would decode final records, validate domain identities/settings, build
its ordinary execution indexes, and implement the already accepted scope and
resource-discovery behavior plus the proposed collection inventory. Bundled
catalog/profile removal remains the accepted direction.
Migration must convert each old keyed resource into its chosen record identity,
flatten prompt-version entries, update any inheritance reference syntax, and
preserve provider model strings. Choose specification and software release
versions deliberately; do not silently reinterpret deployed configuration.
The upstream acceptance cases should include:
- Different tuples coexist; same local prompt names in different namespaces
never collide; different versions of one prompt remain separate.
- Matching tuples compose disjoint bodies but reject overlapping scalar/null/
ordinary-sequence body fields, including equal values.
- Matching tuples overlay across tiers without deleting unrelated records.
Changing a tuple adds another record; `[]`, whole-path null, and ordinary
message sequences follow their distinct documented semantics.
- Missing or malformed identity fields and repeated tuples in one source fail
structurally; identity comparisons cannot collide through delimiter encoding.
- Composite inheritance resolves final relationships, preserves child identity,
rejects missing/cyclic parents, and retains declaring resource contexts.
- Matching, effective indexes, source positions, sensitivity, and provenance
remain consistent through imports, overrides, inheritance, and rendering.
- Unregistered configuration loads and ordinary sequence behavior are unchanged.
- Existing version-path protections remain intact; record `version` fields are
not mistaken for the protected root field.
PromptKit acceptance should exercise the authoring examples, final identity
conversion, scoped selection, metadata inventory, and migrated references with
offline collaborators. The record composition and inheritance examples require
the proposed upstream extension; they are not executable supported workflows
on ConfigKit v0.7.1 today. Syntax checks and current-behavior probes can establish
the starting point, but cannot validate an unimplemented extension.
Review verification on 2026-10-14: a temporary offline consumer of the local
ConfigKit checkout decoded the five examples without the new inheritance form,
confirmed that the composite `extends` example is rejected today, and confirmed
current sequence conflict/replacement behavior across peer and precedence layers.
Local documentation links and whitespace checks passed. The temporary consumer
and its module replacement are outside both repositories.
This proposal remains for evaluation alongside proposal 1. The parent roadmap's
configuration decision remains open. Proposal 3 develops the alternative based
on source placement; independent collection specification checking remains open.

View File

@@ -1,15 +1,15 @@
# Proposal 3: Collections With Mounted Configuration Fragments
Status: proposed PromptKit standard; not implemented in PromptKit. Required
Status: selected configuration direction; not implemented in PromptKit. Required
ConfigKit capabilities are implemented on its development branch, reviewed at
`e66c371` on 2026-10-16, but are not in PromptKit's selected v0.7.1 dependency.
The recommended direction uses collection-local mapping keys, one installed
definition per prompt, and an optional flat prompt `version` field. The latest
review agrees on shared source checks using `IfPresent` and a distinct collection
`release_version` field; remaining PromptKit decisions are listed below. Compare
[proposal 1](configuration-collections-current-configkit.md) and
[proposal 2](configuration-collections-identified-records.md); the parent
[namespace roadmap](namespaces.md) owns the shared decisions.
`release_version` field; remaining PromptKit decisions are listed below. The
parent [namespace roadmap](namespaces.md) owns the shared decisions. Unselected
proposals 1 and 2 have been retired; their history and the separately captured
upstream request are recorded below.
## Recommendation
@@ -25,8 +25,8 @@ keys after ConfigKit hands off the resolved configuration.
There is one installed definition per qualified prompt ID. Its optional
`version` field describes that definition; it does not create another identity
or select a merge strategy. This removes the need for proposal 2's identified
record sequences and for the current nested prompt-version mapping.
or select a merge strategy. This avoids identified record sequences and the
current nested prompt-version mapping.
Placement belongs to ConfigKit. PromptKit must not parse, wrap, or rewrite each
source itself, nor impose duplicate-namespace checks before composition. This
@@ -241,8 +241,8 @@ To retain unqualified application resources, recommend optional root-level
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 is a recommendation
for proposal 3, not a change to today's accepted format.
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
@@ -272,10 +272,9 @@ is a simpler alternative but requires more consumer changes; settle the public
API shape before implementation. Listings should identify one prompt per ID
and expose the optional declared version separately from identity.
This intentionally supersedes multi-version preservation for this alternative.
Proposals 1 and 2 retain their earlier assumptions for comparison. The parent
roadmap's version-selection and listing language needs corresponding revision
if proposal 3 is adopted.
This deliberately replaces the earlier multi-version preservation assumption.
The selected direction identifies prompts by ID and exposes an optional version
label; the request-version and listing API details remain open below.
## ConfigKit Composition Boundary
@@ -346,8 +345,8 @@ unsupported declarations through the ConfigKit checker. The handling of an
absent main declaration remains an explicit decision below. 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 three proposals are alternative candidate formats, not
interchangeable released meanings of specification version 1.
complete schema. The retired proposals were alternatives, not released formats
sharing specification version 1.
The selected initial direction is `SourceVersionModeIfPresent`:
@@ -424,25 +423,33 @@ 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.
## Comparison And Migration
## Retired Alternatives And Upstream Request
| Concern | Proposal 1 | Proposal 2 | Proposal 3 |
| --- | --- | --- | --- |
| Author identity | Qualified mapping key | Namespace/local-ID fields | Local mapping key beneath a mounted collection |
| Prompt version | Required nested key | Required identity field | Optional descriptive field |
| Multiple installed versions per prompt | Supported | Supported | Not supported |
| Repeated resource wrapper in fragments | Required | Required | Omitted through placement |
| Composition machinery | Existing mappings | Identified record sequences | Existing mappings after placement |
| Upstream prerequisite | None beyond its original baseline | Proposed record composition and inheritance | Placement, relative imports, and shared source checks implemented in development |
| Cross-collection inheritance | Existing qualified sibling keys | Proposed tuple references | Requires a separate extension or authored flattening |
| Source specification checks as described | Main only | Main only | Shared policy; non-main declarations checked when present |
The unselected proposals were retired after the identified-record design was
captured for separate upstream consideration. Both original documents remain
available in PromptKit Git history at `b5866dd`:
Proposals 1 and 2 retain their original assumptions for historical comparison.
Proposal 3 is the recommended direction for the stated one-installed-prompt
requirement. Its authoring economy follows from the configuration hierarchy,
and it retains ordinary map composition. The tradeoffs are explicit assembly
destinations, changed inheritance scope, and a deliberate reduction of prompt
version selection to one installed definition.
```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 and the then-current ConfigKit
composition behavior. Proposal 2 used explicit identity fields in record
sequences and requested identity-based composition and inheritance. Neither is
an active PromptKit implementation option. The selected mounted-mapping design
meets the one-installed-prompt requirement with compact local keys and ordinary
mapping composition.
Proposal 2's generic extension is preserved in the ConfigKit repository as
`docs/roadmap/promptkit-identified-records-feature-request.md`. It requests
explicit record-identity registration, composition and overlay by identity,
record inheritance, deterministic ordering, and coherent inspection. That
request is for upstream evaluation, not an accepted ConfigKit capability or a
prerequisite of this roadmap. Retirement does not resolve the six remaining
PromptKit decisions below.
## Migration
Migration must choose one installed definition for each former multi-version
prompt, move any retained version into its optional field, and relocate
@@ -524,7 +531,6 @@ remain before the PromptKit contract is complete:
and migration examples. Direct mounted fragments remain a useful alternative
for applications that already assemble sources themselves.
The parent roadmap still owns the overall configuration decision. Its older
version-selection and metadata examples describe earlier alternatives; adopting
this standard requires reconciling those details and updating current contracts
when implementation lands.
The parent roadmap records this selected direction. Resolve the remaining
PromptKit choices here and update current contracts when implementation lands;
retiring the alternatives does not authorize feature implementation.

View File

@@ -3,9 +3,10 @@
Status: feature roadmap; not implemented. The reserved-slash naming convention,
reference-resolution rules, decisions 1–3 (scope shape, validation, and integrated
discovery filtering), and decision 4's move to application/operator-supplied
configuration are accepted target behavior. Collection metadata, inventory, and
specification-version integration remain pending in decision 5, subject to the
ConfigKit boundary. Exact Go names and option types remain delivery details.
configuration are accepted target behavior. Decision 5 selects mounted collections
and shared source-version checking; its remaining policy and API choices are
tracked in the collection roadmap, subject to the ConfigKit boundary. Exact Go
names and option types remain delivery details.
## Purpose
@@ -82,8 +83,9 @@ 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 versions also remain separate from namespaces and
continue to use the existing selection rules.
different settings. Prompt version labels remain separate from namespaces. The
selected collection format installs one definition per prompt ID; optional
request-version handling remains a decision in the collection roadmap.
Retain unqualified and dotted IDs. Existing slash-containing IDs need review:
those with one slash and nonempty components become qualified identities, while
@@ -135,8 +137,8 @@ The accepted resolution rules, independent of the final scope API, are:
Keep qualification consistent across execution, preparation, request validation,
prompt/profile inspection, and applicable smoke-test selections. Namespace
selection must not change request override precedence, prompt version selection,
or provider model strings.
selection must not change request override precedence, the chosen request-version
semantics, or provider model strings.
### Local And Qualified References In One Operation
@@ -288,11 +290,11 @@ follow the eventual decision 5:
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.
- Existing exact-ID calls using valid IDs, intentional overlays, and prompt
versions retain their behavior. Migrated catalog references resolve to their
intended resources without collisions.
- Listings preserve full IDs and separate prompt versions. Qualified results
round-trip through either engine or view; unqualified results round-trip
- Exact-ID calls using valid IDs and intentional overlays retain their behavior.
Migrated catalog references resolve without collisions; the collection roadmap
owns migration to one installed prompt and optional version labels.
- 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.
@@ -410,9 +412,10 @@ 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, retaining prompt versions as separate identity fields. Do not
invent a named namespace such as `default` for unqualified resources. Namespace
enumeration excludes that unnamed group. Use deterministic ordering consistent
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
@@ -485,89 +488,35 @@ dependencies instead.
### 5. Collection Metadata And Specification Versioning (Open)
[Proposal 1: current ConfigKit](configuration-collections-current-configkit.md)
develops a concrete configuration standard for the existing composition rules.
[Proposal 2: identified records](configuration-collections-identified-records.md)
uses an opt-in upstream extension for identity-based record composition and
inheritance. [Proposal 3: mounted fragments](configuration-collections-mounted-fragments.md)
uses explicit upstream source placement and collection-local mapping keys,
with one installed definition per prompt and an optional flat version field.
Proposal 3 now targets implemented ConfigKit development-branch capabilities,
including relative imports and shared source-version checks with `IfPresent`. It
uses `release_version` for collection metadata; proposals 1 and 2 preserve their
earlier baselines for comparison. This overall decision remains open. Proposal 3
deliberately revises the version-selection, metadata, and listing assumptions
elsewhere in this roadmap if that alternative is adopted. Its remaining questions
are PromptKit policy/API choices, not pending requests for those ConfigKit features.
**Selected direction: [mounted collections](configuration-collections-mounted-fragments.md).**
Proposal 3 is the active configuration roadmap. It uses collection-local mapping
keys, one installed definition per prompt, and an optional flat prompt version.
Input destinations, relative imports, and shared source-version checks are
implemented in ConfigKit's reviewed development branch. PromptKit still needs
to adopt a suitable dependency release and implement its new schema.
**Desired capability:** describe collection namespace and release version in
configuration and expose an API through which downstream applications can list
effective collection declarations captured by an engine. A collection release
version is distinct from an individual prompt version and from the PromptKit
software version. Use top-level `version: 1` for PromptKit specification version
1, following ConfigKit's version mechanism, rather than introducing a
`promptkit_spec` field. The ownership and placement of that version declaration
across multiple sources remain part of this open decision.
Use shared specification checking with `IfPresent` and distinct collection
`release_version` metadata. Collection entries can describe their layout through
relative imports; applications select their sources and namespaces. PromptKit
interprets the final collection/resource keys as qualified IDs after ConfigKit's
handoff. It adds no manifest pre-parser, source-level namespace ownership rule,
or history-based validation pass. Stored references remain exact public IDs.
The initially discussed root manifest contained `collection.namespace` and
`collection.version`. That shape and a standard manifest filename are not
settled. In particular, the proposal to load/check each manifest separately in
PromptKit before composing definitions is withdrawn. PromptKit must respect the
ConfigKit boundary described above, including for version checks and duplicate
declarations.
Proposals 1 and 2 were not selected and have been retired. Their original
contents and the separately preserved ConfigKit identified-record request are
recorded in the collection roadmap's
[retirement note](configuration-collections-mounted-fragments.md#retired-alternatives-and-upstream-request).
The record-sequence request is independent future upstream work and does not
block the selected mapping design.
ConfigKit v0.7.1's layered-loading contract checks the configured application
version path on the main input only. Underlay roots cannot claim that path,
and imported fragments cannot independently declare it. ConfigKit supplies the
version mechanism while the application defines supported specification values;
this does not give PromptKit permission to implement an independent collection
assembly or compatibility-checking pass.
The collection roadmap owns the candidate schema, examples, version policies,
metadata inventory, and migration requirements. Inventory describes effective
collection declarations captured by the engine, not historical manifests or
proof of unchanged published releases. Source explanation remains ConfigKit's
inspection concern.
**Recommended starting point, pending decision: ordinary ConfigKit composition
with collection metadata in final effective values.** For example, the main
configuration could own `version: 1`, with a namespace-keyed collection mapping:
```yaml
version: 1
collections:
openrouter:
version: "2026.10.13"
weatherreporter:
version: "2.4.0"
```
This is a candidate shape, not an implemented or accepted schema. Other sources
could contribute to `collections` without claiming the main version field.
ConfigKit would decide whether contributions conflict or combine; PromptKit
would interpret and validate only the final metadata. A namespace key would
identify one effective entry without requiring a source-level uniqueness rule.
It would not automatically qualify definition keys or stored references.
This approach reuses the existing composition contract and avoids a parallel
manifest loader. Its limitation is that it does not independently check a
specification declaration for each imported collection. An effective declared
release version also does not prove that every resource still matches that
release after operator overrides.
**Alternative: add collection/import compatibility support within ConfigKit
before adopting independently versioned collection roots.** If every collection
must declare its own specification version, design that capability in ConfigKit,
including how versions and metadata interact with imports and layers. PromptKit
would consume its resulting handoff and diagnostics. This could provide useful
per-collection compatibility checks without violating ownership, but it expands
the work into ConfigKit and requires a supported contract there before PromptKit
can depend on it. No ConfigKit change is selected by this roadmap yet.
Under either approach, the inventory should describe effective collection
metadata captured at engine construction, not reconstruct a historical list of
loaded manifests. Source contributions and explanations remain ConfigKit
inspection concerns. Inventory should not reread installed files or claim that
declared versions prove unmodified published contents. Namespace enumeration
from resource IDs remains independent of this metadata API.
**Pending question:** is ordinary composed collection metadata with one main
specification declaration sufficient, or are independently version-checked
collection roots essential enough to warrant ConfigKit work? Settle that before
finalizing metadata schema, root-file conventions, inventory API values, or
collection compatibility rules. Removal of bundled catalogs and built-in
profiles remains accepted in either case.
**Still open:** the [six remaining PromptKit decisions](configuration-collections-mounted-fragments.md#remaining-promptkit-decisions)
cover main declaration requirements, exposing stricter checking, resources named
`version`, request-version assertions, cross-collection inheritance, and consumer
APIs/packaging. Selecting proposal 3 and retiring its alternatives does not settle
those choices. Removal of bundled catalogs and built-in profiles remains accepted.