471 lines
25 KiB
Markdown
471 lines
25 KiB
Markdown
# 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](../config.md#input-destinations). 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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```go
|
|
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](../config.md#strict-document-composition) and
|
|
[layering](../config.md#layered-loading) 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, `messages` does 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:
|
|
|
|
```yaml
|
|
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](../config.md#application-schema-versions).
|
|
|
|
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.Destination` reports 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:
|
|
|
|
```yaml
|
|
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](deferred.md). 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.
|