25 KiB
PromptKit Feature Request: Mounted Collections And Specification Headers
Status: input destinations are implemented on the ConfigKit development branch
(reviewed at 315f464). Independent collection specification headers and relative
import destinations are requested additions; not yet accepted or implemented.
Requested Capability
Extend the public loading API so independently maintained collections can declare and check their specification compatibility and describe relative import destinations. Preserve the implemented capability to select a destination mapping path for each configuration input. The input's application content contributes beneath that path while retaining ConfigKit's normal composition, precedence, inheritance, validation, diagnostics, and inspection behavior.
PromptKit needs this capability for its proposal 3, Collections With Mounted
Configuration Fragments, recorded in PromptKit's
docs/roadmap/configuration-collections-mounted-fragments.md. This request
describes the public API needs and observable acceptance criteria. It does not
prescribe ConfigKit's internal design or an implementation plan.
The original request targeted ConfigKit v0.7.1. The development branch now
supplies Input.Destination and SourceInfo.Destination, as documented in the
input destination contract. The placement
requirements below remain the baseline for the two additions. Current released
behavior and development-branch behavior must not be confused with the newly
proposed header and import syntax.
Consumer Problem
PromptKit applications assemble independently maintained collections of prompts,
inference profiles, and backends. Collections can contain identical local names,
such as summarize.alerts, without identifying the same resource. PromptKit wants
an effective configuration organized by collection, while letting authors omit
repeated collection and resource-container wrappers from individual files.
A file selected from a collection's prompts directory would contain:
summarize.alerts:
version: "1"
default_profile: provider/brief
messages:
- role: user
content: Summarize the weather alerts.
output:
format: text
validation_mode: none
Given destination [collections, weatherreporter, prompts], its contribution
would appear in the final application configuration as:
collections:
weatherreporter:
prompts:
summarize.alerts:
version: "1"
default_profile: provider/brief
messages:
- role: user
content: Summarize the weather alerts.
output:
format: text
validation_mode: none
Another collection can contribute summarize.alerts beneath
[collections, homewatch, prompts] without conflicting. After final handoff,
PromptKit interprets these as weatherreporter/summarize.alerts and
homewatch/summarize.alerts. The profile reference is illustrative and must be
supplied separately by the application configuration.
PromptKit will support one installed definition per prompt. Its optional flat
version is ordinary domain metadata, not a merge identity. This request does
not need identified-record sequences, sequence-root documents, or a custom
merge policy.
Authors could write the complete wrappers today, but that repeats assembly details in every fragment. Consumer-side parsing and wrapping would put generic source processing back into PromptKit and complicate source attribution. PromptKit instead wants to select sources and destinations, then receive one final configuration through ConfigKit's supported loading lifecycle.
Implemented Placement API And Required Baseline
Per-Input Destination
The development branch provides Destination Path on each Input passed to
LoadLayered, matching the original request. The following partial consumer
example uses that implemented field, which was not available in v0.7.1:
promptInput := configkit.Input{
Source: configkit.File("/usr/local/share/collections/weatherreporter/prompts/alerts.yml"),
Destination: configkit.Keys("collections", "weatherreporter", "prompts"),
}
peers := configkit.Underlay{
Inputs: []configkit.Input{promptInput},
}
The consumer would include peers in its existing LayeredOptions and invoke
LoadLayered with its normal root main input and application Definition.
This is a partial API sketch, not an executable example or a proposed new
loading entry point. A single public layered load is sufficient for PromptKit;
it does not require a separate mounting operation on completed results.
The API must let the consumer:
- Express a destination relative to the application configuration root using literal mapping-key segments. Dots, slashes, and numeric-looking text inside a key are not implicit path separators or array indexes. PromptKit's namespace grammar remains its own domain rule.
- Omit the destination to retain existing root placement. An explicitly empty destination should have the same meaning. Mapping destinations are sufficient; sequence-index destinations are not needed by this request.
- Assign different destinations to inputs in the same strict composition group, and assign the same destination to inputs in separate precedence groups.
- Use the destination with existing content-bearing source kinds: host files, native filesystem files, bytes, readers, and mapping sources, subject to their existing acquisition and ownership contracts.
- Retain the existing import opt-in and filesystem context independently of the destination. A configuration destination is not a filesystem root or base directory.
PromptKit can continue to enumerate an explicitly selected directory and attach the same destination to its returned inputs. ConfigKit does not need a new directory scanner, namespace registry, filename convention, or PromptKit schema. The namespace comes from the application's assembly declaration, not from an implicit interpretation of a directory basename.
Representative Assembly
These are application choices, not ConfigKit-specific reserved paths:
| Selected inputs | Destination key segments |
|---|---|
| Application main document | Root |
weatherreporter/collection.yml |
collections, weatherreporter |
YAML files under weatherreporter/prompts |
collections, weatherreporter, prompts |
YAML files under homewatch/prompts |
collections, homewatch, prompts |
YAML files under provider/profiles |
collections, provider, profiles |
YAML files under provider/backends |
collections, provider, backends |
Peer collection contributions belong to a single strict underlay group when the application wants conflict detection among them. Intentional operator overrides use later groups or main. Destination selection must not establish precedence or make source traversal order authorize replacement.
Required Observable Behavior
Composition And Precedence
Mounted and explicitly wrapped contributions must have equivalent application values and conflict behavior at their effective destinations. Actual source locations naturally differ and must remain accurate.
Retain the strict composition and layering rules:
- Disjoint mapping entries at the same destination combine.
- Overlapping non-mapping values within a strict group conflict, including equal scalars, nulls, and sequences. Different destinations do not collide merely because the source documents share a local key.
- Later precedence layers recursively override mappings and replace other values. A strict conflict cannot be repaired by an upper layer.
- Empty mappings do not clear lower definitions; ordinary arrays remain atomic.
In particular,
messagesdoes not acquire sequence-merging behavior. - Overlapping destination paths and contributions at their ancestors follow those same rules. No namespace-level uniqueness restriction is requested.
Changing only a prompt's version in a later layer changes that field while
retaining omitted lower-layer fields. ConfigKit must not treat a domain version
label as a request to replace an entire definition.
Imports And Existing Structural Directives
An input with imports enabled must be able to use its existing source-root
configkit.imports directive while contributing application data at a nested
destination. The directive remains structural configuration, not an application
field below that destination.
Without an explicit relative destination, imports inherit their importing input's destination, including transitive imports. Import paths continue to resolve relative to their declaring source's filesystem context. A mounted document must not send its imported application fields back to the configuration root accidentally.
Existing opt-in, containment, missing-input, cycle, and repeated-source behavior must remain applicable. A different destination is not a request to bypass the current rejection of a repeated physical source within a strict group.
Sibling extends must resolve against mappings at their effective destinations.
For example, two independently supplied profile fragments mounted beneath the
same collection's profiles mapping can establish extends: brief once they
compose. Inheritance remains ConfigKit-owned; PromptKit does not perform another
relationship-resolution pass.
Protected Specification Paths
The distinction between source-local field names and effective application
paths is essential. PromptKit's main document will declare version: 1 at the
configuration root. A separate collection metadata file can contain:
version: "2.4.0"
Mounted at [collections, weatherreporter], that field is an ordinary collection
release label. It must not be rejected as an attempt to contribute the protected
root specification field. The same applies to a prompt's nested optional
version. Only the main-owned specification declaration uses the application's
configured VersionRule.
Conversely, placement must not permit an underlay or imported fragment to claim
or replace the actual protected specification path or an ancestor that would
replace it. The public contract must define protection in terms of effective
application paths, rather than reserving the spelling version in every source.
Protection must remain valid with imports, inheritance, overlays, and explicit
CLI overrides.
Retain the main-owned specification check with its existing lifecycle. The new
collection-header checks below are explicit, separate source compatibility
checks; they do not authorize contributions to the main-owned version path.
PromptKit does not need main mounted away from the root or integer-to-string
conversion for every nested ordinary version field.
Typed Loading, Diagnostics, And Inspection
The destination must be visible consistently through the ordinary public loading and inspection surface:
- Final typed configuration and application callbacks use effective destination paths. Defaults, resolution, and validation retain the existing single-load lifecycle; fragments are not independently validated as whole applications.
- Metadata and presence queries distinguish omission, explicit null, and other explicit values at the mounted paths.
- Diagnostics identify the effective conflicting or invalid path and the real contributing source locations. Consumers can determine which source and placement produced a value without recreating that relationship themselves.
- Effective rendering, explanations, history, and configuration comparison agree with the final configuration. Existing sensitivity and disclosure rules apply to mounted fields, including application path-based secret classification.
- Unsupported destination forms and destination-related loading failures use the normal structured error boundary. Exact new diagnostic codes, if needed, are for ConfigKit to define.
PromptKit does not require a separate mount registry or new inspection facade. Existing inspection APIs are sufficient if they expose accurate effective paths, source attribution, and placement information. ConfigKit should document how source-local locations relate to effective paths.
Source-Relative Resources And Lifecycle
For a mounted field such as content_file: ./summary.md, obtaining its effective
origin and calling Result.SourceContext or Resolution.SourceContext must
still identify the declaring source's filesystem and base directory. The same
holds when that field survives an override or is inherited into another mapping.
Its configuration destination must not change the meaning of the relative file
reference.
Retain existing source ownership, load completion, result lifetime, containment, and resource-access behavior. Destinations must participate in documented load limits without creating a way to bypass them; this request does not prescribe new limit values or their accounting machinery.
Requested Addition: Independent Collection Specification Headers
Consumer Need And Public API
Allow an application to attach an opt-in source specification rule to a selected
collection entry Input. PromptKit would configure the source-local header path
as version and supply its supported-specification policy. ConfigKit owns
checking the declaration and delivering structured diagnostics within the same
LoadLayered operation. This must not require PromptKit to open, parse, or load
collection manifests separately.
Reuse the public version vocabulary where appropriate: the checker should be
able to distinguish absent, null, string, and strict decimal-integer declarations
and receive their text without imposing SemVer. For PromptKit specification 1,
version: 1 and version: "1" can express the same supported identifier.
PromptKit decides which declarations it accepts, including whether a missing
header is an error. Exact API names and how the source rule relates to the
existing VersionRule type are ConfigKit design decisions.
This is a separate rule from Definition.Version, which continues to describe
the main-owned application specification. It checks a source's declared format,
not its complete domain schema. Domain validation still runs on final effective
configuration, not once per collection or fragment.
The minimum API must support several collection entry inputs in one load, each
with a destination and an explicitly assigned source-header policy. Source
selection determines which documents are entries. Neither a filename nor the
mere presence of a field named version automatically enables header checking.
No YAML-selected executable checker or general source-validation callback is
requested.
Header Scope And Payload
Recommend one required specification declaration per selected collection entry,
with its ordinary imported fragments authored under that declared specification.
Do not require a repeated header in every prompt/profile fragment. Importing a
fragment does not give it an independent header rule; in particular, its ordinary
release or prompt version must not be consumed as a specification header.
For an explicitly configured entry, check its own declaration independently of other inputs and precedence layers. Another source cannot supply a missing header, and a higher-priority override cannot conceal an unsupported declaration. Any rejected entry fails the whole load with no usable partial result, even if its payload would subsequently be superseded. Reusing a physical source across allowed separate tiers retains existing source-reuse semantics; this request adds no source-uniqueness policy.
After an entry header is accepted, recommend consuming it as structural source
metadata: it does not contribute at the mounted application path. For example,
entry version: 1 must not become collections.weatherreporter.version. The
collection release label can be contributed separately as ordinary payload.
This distinction also prevents unrelated collection specification declarations
from conflicting during strict composition.
This consumption applies only to the explicitly selected source header. It
must not remove nested versions, imported metadata, or the main application's
existing version field. The API should make the distinction between a consumed
source header and a retained main declaration explicit; it must not silently
change existing Definition.Version behavior.
Diagnostics And Inspection
Expose the checked declaration and its source-local location through supported source inspection, or equivalent public metadata. Consumers should be able to identify the source, header path, and accepted declaration without rereading files. Preserve the normal distinction between authored input evidence and rendered effective application configuration: a consumed header is evidence of source compatibility, not an effective domain field.
Header failures and warnings should use ConfigKit's structured diagnostics and normal disclosure rules. Report the actual entry source and header location, or a source-location fallback when the header is absent; do not attribute them to the unrelated protected root field or invent an effective payload field. The source's destination remains inspectable. Exact diagnostic codes and inspection types are left to ConfigKit.
Requested Addition: Relative Import Destinations
Extend the public YAML import directive so a path or glob import can specify
a relative destination as literal mapping-key segments. An illustrative spelling
is destination: [prompts]; the exact directive field name remains a proposal.
The public behavior should be:
- The importing document has an effective destination established by its input or its own import. An explicit import destination appends key segments to that destination. Omission or an empty list retains the importing destination.
- Every file matched by a glob receives the same declared relative destination. Transitive imports are relative to their immediate importing document's resulting destination, not always to the original entry destination.
- Filesystem paths still resolve relative to the declaring file, with existing
containment rules. Logical placement and physical file resolution are
independent; there is no root-reset or parent-traversal meaning in destination
key segments. Literal keys follow the same rules as
Input.Destination. - Imports retain their existing strict-composition tier. Import order and destination declarations do not create precedence or permit replacement.
- Existing optionality, repeated-source, cycle, source-kind, and ownership rules remain intact. A destination does not authorize importing one physical file twice in a group. Existing source-root directive and reserved-key rules apply.
SourceInfo.Destinationreports each import's resulting effective destination. Effective paths, original locations, resource contexts, protected version paths, sensitivity, and error behavior retain the placement baseline above.- Invalid directive shapes fail through the normal structured error boundary; destination fields must not be silently ignored.
This is declarative access to the existing placement capability. It does not request recursive glob syntax, implicit directory scanning, namespace inference, or a new composition strategy.
Combined Collection Entry Example
With both additions, an entry document selected by the application with a source
header rule and destination [collections, weatherreporter] could contain:
version: 1
configkit:
imports:
- path: metadata.yml
- glob: prompts/*.yml
destination: [prompts]
- glob: profiles/*.yml
destination: [profiles]
- glob: backends/*.yml
destination: [backends]
This is proposed syntax, not currently executable configuration. Each required glob assumes matching files; collections can omit unused categories or use existing optional-import behavior. The application still selects and provisions the collection and assigns its namespace. ConfigKit does not discover it.
The entry's version: 1 is checked and consumed. Its metadata.yml can contain
version: "2.4.0", retained as the collection's ordinary release label because
that import has no independent header rule and inherits the collection
destination. Prompt fragments retain local mapping keys and optional prompt
versions, and are placed beneath the collection's prompts mapping. The main
application can independently retain its own version: 1 declaration.
Thus publishers can describe their collection layout, authors can keep compact fragments, and operators can compose several independently checked collections without application-side source rewriting. These checks establish declared specification compatibility, not package authenticity, release integrity, or proof that every fragment was authored correctly.
Deferred And Excluded Work
Identity-based list composition. Keep this deferred. Proposal 3's local mapping keys already identify each installed resource; the two requested additions do not require a different representation or sequence merge policy.
Cross-collection inheritance. Nested collection profiles are not immediate
siblings across collections. Current sibling inheritance remains sufficient for
the initial proposal, with authored definitions flattened where necessary.
Generic cross-path inheritance could be considered separately if consumer needs
justify revisiting the deferred scope. Mounting must not silently
reinterpret extends: provider/brief as a cross-collection reference.
PromptKit also does not request collection installation, automatic directory discovery, remote fetching, namespace ownership enforcement, dependency solving, live reload, filename-derived identity, or arbitrary document transformations. Its qualified resource IDs, optional prompt version semantics, collection inventory, and execution APIs remain PromptKit responsibilities.
Public Acceptance Scenarios
Evaluate the extension through supported public loading and inspection APIs:
| Scenario | Expected consumer-visible result |
|---|---|
| Destination omitted or empty | Existing root-placement behavior |
| Same fragment shape mounted under two distinct collection keys from independent sources | Two separate effective resources |
| Disjoint inputs at the same destination in one group | Mapping contributions combine |
| Equal or unequal overlapping scalar fields in one group | Structured composition conflict with both source locations |
| Same destination across deliberate precedence tiers | Existing recursive overlay behavior |
| Version-only prompt override | Label changes; omitted prompt body fields survive |
| Nested mounted import without a destination directive | Imported fields contribute at the inherited destination |
| Local profile inheritance across mounted peer fragments | Parent resolves at the effective sibling scope |
Collection metadata containing version mounted below root |
Ordinary nested field, independent of root VersionRule |
| Mounted/imported contribution actually targeting the protected version path | Rejected under version ownership rules |
| Mounted field using source-relative content, including inheritance | Original declaring resource context remains available |
| Mounted explicit null and omitted field | Distinct effective presence information |
| Mounted validation error or secret field | Effective paths, physical locations, and existing disclosure rules agree |
| File, native filesystem, byte, reader, or mapping input with equivalent content | Equivalent application values, subject to existing source contracts |
| Unsupported destination or failed load | Normal structured failure, no usable partial result |
| Several collection entries with supported specification headers | Each declaration is independently accepted; payloads compose normally |
| Missing, null, malformed, or unsupported entry header | ConfigKit reports structural/type errors or invokes the configured version policy as appropriate; rejection fails the whole load |
| Unsupported entry superseded by a higher layer | Compatibility failure remains visible and fails loading |
| Consumed entry header plus imported release metadata | Header is inspectable as source evidence; only the release label becomes the collection's ordinary version field |
Ordinary imported fragment containing version without a header rule |
Field remains ordinary payload and is not independently consumed or checked |
| Relative import destination omitted or empty | Existing inherited-destination behavior |
Entry at [collections, weather], import at [prompts], nested import at [extras] |
Nested contribution appears beneath [collections, weather, prompts, extras] |
| Glob import with a relative destination | Every matched contribution uses that destination and composes strictly |
| Relative import destination with source-relative resource | Effective configuration path changes; resource base remains the declaring source |
| Load without source-header rules or relative import directives | Existing loading and main VersionRule semantics remain unchanged |
The requested outcome is a consumer that selects collection entry sources, destinations, and specification policies, performs one ordinary layered load, and receives final typed configuration with reliable diagnostics and inspection. Collection entries describe their own relative import placements, and unsupported source specifications fail independently of payload precedence. It should need no consumer-side source rewriting, generic merging, or provenance reconstruction.