490 lines
26 KiB
Markdown
490 lines
26 KiB
Markdown
# PromptKit Feature Request: Mounted Collections And Specification Headers
|
|
|
|
Status:
|
|
|
|
- Input destinations are implemented on the ConfigKit development branch
|
|
(placement baseline reviewed at `315f464`).
|
|
- Relative import destinations have been accepted and implementation is in
|
|
progress; the [roadmap](relative-import-destinations.md) owns delivery scope.
|
|
- Source specification headers have been partially accepted in the shared
|
|
application-version-policy form described below. This does not accept
|
|
PromptKit's original independent collection-header semantics. Design and
|
|
implementation of this addition remain separate from relative import work.
|
|
|
|
## Requested Capability
|
|
|
|
Extend configuration loading with relative import destinations and opt-in
|
|
checks that contributing sources declare a version accepted by the consumer's
|
|
single configuration specification policy. 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 disposition
|
|
and revised source-version scope below supersede conflicting parts of PromptKit's
|
|
original proposal.
|
|
|
|
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. This describes the existing placement baseline, with no new source
|
|
checks enabled. The partially accepted extension will reuse the application's
|
|
[VersionRule](../config.md#application-schema-versions) for source declarations;
|
|
header lookup and its interaction with ordinary version payload remain design
|
|
questions, not a guarantee that every source-local `version` stays unchecked.
|
|
|
|
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 existing behavior when the new source checks are not enabled. The
|
|
accepted direction extends the same application version policy across source
|
|
files, rather than assigning separate policies to collections. Its interaction
|
|
with protected-path ownership and declaration retention must be designed
|
|
explicitly; it does not implicitly authorize version-field contributions.
|
|
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.
|
|
|
|
## Partially Accepted Addition: Shared Source Specification Checks
|
|
|
|
### Accepted Scope
|
|
|
|
A ConfigKit consumer defines one configuration specification, which may evolve
|
|
through versions. `Definition.Version` identifies the declaration path and the
|
|
consumer's checker determines which versions are acceptable. The accepted
|
|
extension lets the consumer apply that same policy across the source files
|
|
contributing to a configuration, rather than checking only the main declaration.
|
|
It does not introduce independent collection or subtree specification policies.
|
|
|
|
The consumer can opt in with increasing strictness:
|
|
|
|
1. **Current behavior:** retain existing loading and version-checking behavior,
|
|
including existing protected-path restrictions. No new source-header check.
|
|
2. **Check declarations when present:** a source that declares a version must
|
|
satisfy `Definition.Version`; omission is allowed.
|
|
3. **Require declarations:** every participating source must declare a version
|
|
that satisfies `Definition.Version`; omission is an error.
|
|
|
|
Acceptance is determined by the existing consumer-supplied checker, not by a
|
|
new ConfigKit-owned expected string or automatic equality with the main file's
|
|
declaration. A consumer may accept several compatible versions. A declaration
|
|
rejected under the selected mode fails the attempted load through structured
|
|
errors, with no usable partial result; a higher-precedence payload cannot turn
|
|
an incompatible source declaration into an acceptable one.
|
|
|
|
This establishes that checked sources declare an accepted specification version.
|
|
It does not prove that every fragment conforms to its declaration. Existing
|
|
final decoding and application validation still determine the validity of the
|
|
assembled configuration; fragments are not independently loaded as complete
|
|
applications.
|
|
|
|
### Difference From PromptKit's Original Proposal
|
|
|
|
PromptKit originally requested explicitly selected collection-entry rules,
|
|
separate from `Definition.Version`, with ordinary imports exempt from independent
|
|
checks. It also recommended consuming accepted entry headers and exposing them
|
|
as source evidence while leaving imported release metadata untouched.
|
|
|
|
That full set of semantics has not been accepted. The accepted direction uses
|
|
one shared `Definition.Version` policy and optional or required declarations
|
|
across participating sources. Separately versioned subsets or collections are
|
|
outside this addition's scope and may be considered independently in the future.
|
|
Header consumption and inspection from the original proposal remain design
|
|
suggestions, not accepted requirements.
|
|
|
|
### Remaining Design Work
|
|
|
|
The following details remain to be resolved before implementation:
|
|
|
|
- How the configured declaration path is interpreted for individual sources,
|
|
particularly before destination placement.
|
|
- Which source kinds and roles participate, including direct underlay inputs,
|
|
transitive imports, and other contributions.
|
|
- Whether checked declarations are consumed or retained, how that interacts
|
|
with composition and main-owned version protection, and what inspection
|
|
evidence is exposed.
|
|
- How null and malformed declarations behave in each mode, preserving the
|
|
distinction between absence and a present declaration and considering the
|
|
existing string/decimal-integer version vocabulary.
|
|
- The opt-in API, callback lifecycle, and source-local diagnostic attribution.
|
|
|
|
In particular, the original example of an imported `version: "2.4.0"` release
|
|
label is not guaranteed to remain outside checking in an enabled mode. That
|
|
depends on the chosen header lookup semantics. No automatic field consumption,
|
|
new coercion, or per-collection checker should be inferred from partial acceptance.
|
|
|
|
## Requested Addition: Relative Import Destinations
|
|
|
|
Disposition: accepted; implementation is in progress under the
|
|
[relative import destinations roadmap](relative-import-destinations.md).
|
|
The behavior below records the accepted capability; exact syntax and completion
|
|
status follow that work, rather than implying a released feature.
|
|
|
|
Extend the public YAML import directive so a `path` or `glob` import can specify
|
|
a relative destination as literal mapping-key segments. The roadmap selects
|
|
`destination` as a sequence of strings, for example `destination: [prompts]`.
|
|
|
|
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.
|
|
|
|
## Relative Import Collection Entry Example
|
|
|
|
An entry document selected by the application with destination
|
|
`[collections, weatherreporter]` could describe its layout as follows. This
|
|
example illustrates relative imports only and assumes no new source checks:
|
|
|
|
```yaml
|
|
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.
|
|
|
|
With current source-version behavior, `metadata.yml` can contain
|
|
`version: "2.4.0"` as an ordinary mounted collection release label. Prompt
|
|
fragments retain local mapping keys and optional prompt versions, and contribute
|
|
beneath the collection's `prompts` mapping. The main application can retain its
|
|
own version declaration. Enabling the proposed shared source checks may require
|
|
changes to these files once declaration lookup and retention are defined.
|
|
|
|
Publishers can describe collection layout while applications select collections
|
|
and destinations. The later shared-version feature will add opt-in declaration
|
|
checking; it does not establish package authenticity or release integrity.
|
|
|
|
## 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 implemented placement baseline and accepted relative-import
|
|
extension through supported public loading and inspection APIs. Source-version
|
|
rows below express the partially accepted direction; their unresolved details
|
|
must be settled before they become implementation acceptance tests.
|
|
|
|
| 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, with new checks disabled | Existing ordinary nested-field behavior |
|
|
| 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 |
|
|
| Source-version checks disabled | Existing version-checking and protected-path behavior |
|
|
| Check-when-present mode, declaration absent | Omission is allowed |
|
|
| Check-when-present mode, declaration present | Must satisfy the shared `Definition.Version` policy |
|
|
| Required mode, declaration absent | Structured load failure |
|
|
| Required mode, declaration present | Must satisfy the shared `Definition.Version` policy |
|
|
| Different declarations both accepted by the consumer checker | No additional exact-equality requirement |
|
|
| Rejected source declaration with payload superseded by a higher layer | Rejection still fails the load |
|
|
| 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 new source checks or relative import directives | Existing loading and main VersionRule semantics remain unchanged |
|
|
|
|
The relative-import outcome is a consumer that selects entry sources and
|
|
destinations, performs one ordinary layered load, and receives final typed
|
|
configuration with reliable diagnostics and inspection. Entry documents describe
|
|
their own relative import placements without consumer-side source rewriting,
|
|
generic merging, or provenance reconstruction.
|
|
|
|
The separately scoped, partially accepted source-version extension adds optional
|
|
or required declaration checking through the consumer's single
|
|
`Definition.Version` policy. It does not require or authorize independently
|
|
versioned collections.
|