diff --git a/docs/roadmap/configuration-collections-mounted-fragments.md b/docs/roadmap/configuration-collections-mounted-fragments.md deleted file mode 100644 index ea6fa79..0000000 --- a/docs/roadmap/configuration-collections-mounted-fragments.md +++ /dev/null @@ -1,541 +0,0 @@ -# Proposal 3: Collections With Mounted Configuration Fragments - -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. The six configuration decisions below are accepted; -exact public API shapes remain delivery details. 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. - -## Configuration Simplicity - -This development round simplifies configuration authoring and consumption: - -- Load prompt, profile, and backend libraries as YAML configuration, removing - mandatory catalog Go-module imports and bundled profiles. -- Remove the unused prompt-version selection scheme and its request API fields. -- Let publishers opt into specification checks by declaring a source-root - `version`, commonly in a collection entry. Neither entries nor individual - fragments must declare it; authors need not add headers to every file. - -Prefer conventions and small convenience APIs over mandatory workflows. -Preserve consumer choice over sources, layout, namespaces, and precedence without -adding speculative options or stronger compatibility/provenance guarantees. - -## Recommendation - -Use ConfigKit's implemented input destinations and relative import destinations. -For example, a collection entry can import its `prompts` directory beneath -`[collections, weatherreporter, prompts]`. Authors write local prompt names and -definitions without repeating either `prompts:` or the namespace in each file. - -Use a collection-shaped effective configuration. Each collection contains its -release metadata and ordinary mappings of backends, profiles, and prompts. -PromptKit derives qualified public IDs from the final collection and resource -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 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 -preserves the accepted [configuration boundary](../adr/0001-configuration-boundary.md). - -## Reviewed Upstream Capabilities And Adoption Status - -The reviewed ConfigKit development checkout is `e66c371`. Its public -configuration contract (`docs/config.md`, sections “Input destinations” and -“Shared source-version checks”) and ADRs 0011–0013 own the upstream behavior. -PromptKit still selects ConfigKit v0.7.1; choose a released dependency containing -these capabilities before implementation. This roadmap does not imply they are -already available through PromptKit or select an upstream release number. - -The implemented public surface provides: - -- `Input.Destination` for root-relative placement, and YAML import - `destination: [prompts]` for placement relative to the immediate importing - document. Glob matches share the declared destination; nested imports append - to their immediate parent's destination. -- `Options.SourceVersionMode` and `LayeredOptions.SourceVersionMode`, using one - shared `Definition.Version` rule. Enabled modes interpret the rule's path in - each document's source-local coordinates before placement and composition. -- Consumption of accepted non-main specification headers, with the main header - retained and protected at its effective application path. -- `SourceInfo.Destination` and `SourceInfo.VersionCheck` for detached inspection - evidence, alongside existing source locations and resource contexts. - -Strict mapping composition, explicit precedence, atomic sequences, and local -sibling inheritance retain their existing rules. ConfigKit owns the complete -load; PromptKit interprets and validates final effective values. No further -ConfigKit extension is required for the initial direction described here. - -The new PromptKit schema and runtime indexes remain future work. The existing -[format reference](../formats.md) remains authoritative until adoption. - -## Authoring And Assembly - -A file such as `weatherreporter/prompts/alerts.yml` would contain: - -```yaml -summarize.alerts: - version: "1" - default_profile: provider/brief - messages: - - role: user - content: Summarize the weather alerts. - output: - format: text - validation_mode: none -``` - -The version may simply be omitted. Another collection can contribute the same -local prompt name, for example `homewatch/prompts/alerts.yml`: - -```yaml -summarize.alerts: - default_profile: provider/brief - messages: - - role: user - content: Summarize the household alerts. - output: - format: text - validation_mode: none -``` - -A provider collection's `profiles/brief.yml` uses the same local-key convention: - -```yaml -brief: - backend: provider/default - model: vendor/example-model - temperature: 0.2 -``` - -Its `backends/default.yml` could contain this synthetic, non-live endpoint: - -```yaml -default: - endpoint: https://inference.example.invalid/v1 -``` - -A conventional `weatherreporter/collection.yml` entry can declare the shared -specification, optional collection release, and its layout: - -```yaml -version: 1 -release_version: "2.4.0" -configkit: - imports: - - glob: prompts/*.yml - destination: [prompts] -``` - -A provider entry can import `profiles/*.yml` at `[profiles]` and -`backends/*.yml` at `[backends]` in the same way. Collection publishers choose -which categories to include. A required glob must match; unused categories can -be omitted or use ConfigKit's existing optional-import behavior. Ordinary -fragments may omit specification headers under the selected `IfPresent` mode. -An entry header is the recommended publishing convention, not a load-time -requirement selectively enforced on entries by this mode. - -The application selects each entry, enables its imports, and supplies its -namespace through the public input destination. For example, the following -partial Go snippet uses implemented ConfigKit development-branch APIs: - -```go -entry := configkit.Input{ - Source: configkit.File("/usr/local/share/collections/weatherreporter/collection.yml"), - Imports: true, - Destination: configkit.Keys("collections", "weatherreporter"), -} -``` - -For independent peer collections, one `Underlay` group's `Inputs` is a useful -starting point, not a required grouping. Consumers choose precedence and groups. -Configure `LayeredOptions.SourceVersionMode` as `SourceVersionModeIfPresent` and -`Definition.Version.Path` as `configkit.Keys("version")`, with PromptKit's -supported-specification checker. The application main input remains at the -root and declares `version: 1` in the examples below; both the main input and -its declaration are optional. Namespace and source selection require no manifest parsing in PromptKit. - -| Selected source | Effective destination | -| --- | --- | -| Application main document | Configuration root | -| `weatherreporter/collection.yml` | `[collections, weatherreporter]` | -| Its `prompts/*.yml` imports | `[collections, weatherreporter, prompts]` | -| Homewatch entry and its prompt imports | `[collections, homewatch]` and `[collections, homewatch, prompts]` | -| Provider entry and its profile/backend imports | `[collections, provider]`, then `[profiles]` or `[backends]` appended | - -Directly selected fragments with `Input.Destination` remain possible. Entry -imports are the recommended packaging convention because publishers can describe -their layout. Filenames and directory basenames do not define resource identity; -no implicit directory discovery, namespace inference, or collection installer is -introduced. A small helper should construct inputs from an explicitly selected -source and namespace, without inspecting manifests to compute destinations. `collection.yml` -is a convention, not a required filename. Direct fragments and programmatic -sources remain supported through the source API; no parallel collection loader -or mandatory filesystem layout is introduced. - -Use later underlay groups or main for intentional application/operator overrides. -Placement does not establish precedence. Do not also recursively scan files -already selected by entry imports: ConfigKit's repeated-source rules still apply. - -The entry's source-root `version` is checked and consumed before its payload -composes. Its `release_version` is retained as ordinary collection metadata. -A prompt's nested optional `version` is also ordinary payload. These are three -distinct concepts; source-root release metadata must no longer use `version`. - -The assembled application shape would be equivalent to: - -```yaml -version: 1 -collections: - weatherreporter: - release_version: "2.4.0" - prompts: - summarize.alerts: - version: "1" - default_profile: provider/brief - messages: - - role: user - content: Summarize the weather alerts. - output: - format: text - validation_mode: none - homewatch: - prompts: - summarize.alerts: - default_profile: provider/brief - messages: - - role: user - content: Summarize the household alerts. - output: - format: text - validation_mode: none - provider: - backends: - default: - endpoint: https://inference.example.invalid/v1 - profiles: - brief: - backend: provider/default - model: vendor/example-model - temperature: 0.2 -``` - -This yields `weatherreporter/summarize.alerts` and -`homewatch/summarize.alerts`, plus profile `provider/brief` and backend -`provider/default`. The provider's model string remains `vendor/example-model`; -it is not a PromptKit resource identifier. - -One fragment may contain several local definitions. A fragment starting directly -with `messages:` would need a destination that also supplies the local prompt -key. ConfigKit mounting permits such placement, but with source checks enabled -its root `version` would be the specification header, not prompt metadata. Recommend -the local-key mapping above as the standard authoring form: identity travels -with the definition and files may be reorganized without changing it. Filename-derived IDs are not -needed. - -## Final Schema And Identity - -Recommend `collections` as a mapping of namespace to collection body, containing -optional `release_version` and optional `backends`, `profiles`, and `prompts` -mappings. Collection release labels are descriptive, with no SemVer ordering -or dependency-solving semantics. Metadata is optional: an installed collection -can contain resources without claiming a published release. - -Each resource key inside a collection is local. The parent namespace and local -key follow the accepted slash-free component grammar; their public spelling is -exactly `namespace/local-name`. There are no duplicate embedded `id` or -`namespace` fields to reconcile. Dots remain ordinary characters. - -To retain unqualified application resources, recommend optional root-level -`backends`, `profiles`, and `prompts` mappings, also keyed by local name and -using the same definition shapes. They produce unqualified public IDs. Under -this proposal, namespaced definitions belong under `collections`, not a second -qualified-key spelling in those root maps. This avoids two configuration paths -producing the same public identity. This representation belongs to the selected -future direction; it does not change today's implemented format. - -Stored references such as `default_profile`, profile `backend`, and smoke-test -profile defaults remain exact public references. Nesting does not automatically -qualify a slashless reference or rebind it to caller scope. For example, a -profile in `provider` still declares `backend: provider/default`. Caller-scoped -lookup follows the existing namespace roadmap after final indexes are built. - -### One Installed Prompt And Optional Version - -The resource's qualified ID is its sole configuration identity. There is no -version-key mapping, implicit `latest`, version ordering, or parallel installed -history. Updating the optional version changes metadata on the same prompt. - -An optional prompt version is an opaque string with no SemVer, nonblank, or -normalization requirement. Omission or explicit null means no declared label; -null can clear a lower-layer or inherited label. Quote numeric-looking labels -such as `"1"`. Preserve supplied strings, including empty strings, and distinguish -an absent label in inspection. Do not synthesize a version. - -Remove request-side prompt-version fields, version selection, ordering, and -version defaults as part of the breaking change. Do not reinterpret those fields -as exact-match assertions. Execution and inspection resolve the installed prompt -by ID alone. Listings expose one prompt per ID with its optional descriptive -label separately. A label neither controls execution nor proves unchanged content. - -## ConfigKit Composition Boundary - -Use the implemented upstream contracts without duplicating them in PromptKit. -Input destinations use literal mapping-key segments, independent of filesystem -roots and precedence. Relative import destinations are sequences of strings; -omission and `[]` retain the importing destination. Null, scalar, and non-string -destination members are invalid. No logical path segment means parent traversal -or a reset to the application root. - -Source-root import directives remain structural. Imported content contributes at -its resulting destination, while physical import paths and `content_file` bases -remain relative to their declaring sources. `SourceInfo.Destination` records -placement; normal origins and diagnostics retain real source locations. Selected -overlays and explicit CLI overrides remain rooted at the application root. - -Mounting preserves source identity, cycle/repetition rules, and load limits. -An absent `NoSource` contributes no payload; an actual empty mapping can -materialize its destination containers without clearing lower contents. -ConfigKit's reserved structural keys continue to apply. PromptKit must not -strip headers, re-key source trees, merge fragments itself, or turn source -inspection into an additional enforcement pass. - -### Composition And Overrides - -Different collection keys prevent accidental collisions between equal local -resource names. Two sources targeting the same namespace are not automatically -invalid: disjoint mapping contributions combine. Overlapping scalar fields, -message sequences, nulls, and equal release labels conflict within a strict -group. Across deliberate layers they follow ordinary precedence instead. - -A higher-layer prompt mapping is a partial override. Changing only its version -does not replace its messages or other settings. Omitting a field retains its -lower value, and an empty mapping does not clear a definition. Publishers and -operators must treat such changes as configuration customization, not as an -atomic installation of another prompt release. Do not introduce source audits -or a version-triggered replacement rule to conceal this distinction. - -### Inheritance - -Existing sibling inheritance remains useful within each collection's resource -mapping: `extends: brief` can name the local profile `brief`. Prompt versions, -now ordinary optional fields, would inherit and overlay like other fields; -`version: null` explicitly clears an inherited label. - -Cross-collection profiles are no longer immediate siblings. Consequently, -`extends: provider/brief` cannot reach that collection under current ConfigKit -inheritance. Use existing local sibling inheritance for this development round; -applications can still select the qualified `provider/brief` profile directly. -Authors needing a customized derivative may publish an expanded definition. -Expansion is a migration option, not a mandatory authoring workflow. - -Generic cross-path inheritance remains possible future ConfigKit work if a -concrete consumer need justifies it. It is not a prerequisite of this round. -PromptKit must not add its own inheritance resolver after ConfigKit handoff. - -## Specification Versions And Collection Inventory - -### Shared Specification Policy - -Use one `Definition.Version` policy, with source-local path `version`, for the -candidate PromptKit specification. Initially accept specification text `1` from -string or strict decimal-integer declarations; reject explicit null and other -unsupported declarations through the ConfigKit checker. Accept an absent main -declaration, including an absent main input. Accept additional versions only when PromptKit actually supports their format semantics: accepting -a header does not migrate its payload or independently validate a fragment's -complete schema. The retired proposals were alternatives, not released formats -sharing specification version 1. - -Use `SourceVersionModeIfPresent` without adding a PromptKit mode-selection -option in this round. A stricter option can be considered for a demonstrated -consumer need. The upstream modes remain distinct: - -| Mode | Implemented behavior | Role in this proposal | -| --- | --- | --- | -| `Default` | Existing main-only effective-path check and protected-path behavior | Not the proposed shared-source policy | -| `IfPresent` | Checks present declarations; skips absent non-main declarations | Initial choice for compact fragments | -| `Required` | Requires declarations on every participating document | Upstream capability; exposing a PromptKit option is deferred | - -Participating documents include actual main and underlay inputs, transitive and -glob imports, and the selected overlay. Defaults, synthetic CLI overrides, -resolution output, absent optional imports, and empty groups do not need headers. -Explicit null is present and reaches the checker; malformed selected values -fail ConfigKit's version-type validation. A rejected declaration fails loading -even if a higher layer would supersede that source's payload. - -In enabled modes, accepted non-main headers are consumed before composition. -The main declaration remains and is protected at its destination-prefixed path. -PromptKit's recommended main is unmounted, so that protected path is `/version`. -Do not confuse header consumption with permission to override the remaining -protected application field. - -`IfPresent` does not require a collection entry header and does not infer a -fragment's compatibility from its entry. A publisher can declare compatibility -in the entry while leaving imported files headerless; the assembled configuration -still undergoes normal validation. A declaration is not a guarantee of future -compatibility or independent schema validation of every imported fragment. -ConfigKit checks the declarations actually present. Requiring headers only on -entry files is not supported by the load-wide mode. Do not emulate it using a preliminary manifest load or validation of -source history. If that stronger guarantee becomes essential, seek an explicit -upstream contract instead. - -### Naming And Optional Main Inputs - -Reserve source-root `version` for specification declarations throughout the new -format. Use `release_version` for optional collection release labels and retain -nested prompt `version` for the optional installed-prompt label. An old release -file containing only `version: "2.4.0"` can fail the specification check; if its -text happens to be accepted, that field is consumed and lost as release metadata. -Migration must rename it rather than simply enable the new mode. - -This reservation affects resource fragments whose local key is literally -`version`: a root mapping with that name is interpreted as a malformed header. -Allow that resource name through a nested resource mapping, for example -`prompts: {version: ...}` in a collection entry. Reserve the header position, -not the public resource name. Existing `configkit` and `extends` structural -reservations also remain applicable; destinations do not bypass them. - -An absent header in an actual main document still reaches the checker under -`IfPresent`; `NoSource` invokes the absent-main checker as well. PromptKit's -checker accepts omission in both cases. Consumers may assemble only underlays, -fragments, or programmatic inputs without manufacturing a main document or -header. Do not inject a declaration or report undeclared sources as checked. -Present unsupported declarations still fail; final schema validation still runs. - -### Inspection And Effective Collection Inventory - -`SourceInfo.VersionCheck` is source evidence, not a generic success flag. A -non-nil record with `Present == false` can describe an omission for which the -checker did not run. Header paths and locations are source-local. Evidence is -detached and does not reread inputs; text is conservatively redacted during -callbacks and on failed loads, and successful final records respect completed -sensitivity classification. These records belong in explicit configuration -inspection, not in ordinary domain validation or collection-release identity. - -Derive collection inventory from the final `collections` mapping: namespace -plus optional effective `release_version`, sorted and detached from the captured -engine. Metadata-only and resource-only entries can both appear. Root unqualified -resources do not invent a named collection; resource-based namespace enumeration -remains separate. Exact exported inventory types remain API design work. - -An inventory describes effective configuration, not immutable package identity, -historical source ownership, or proof of unchanged published contents. Operator -overrides may change resources while retaining a release label. Engine -construction captures definitions; edits require a replacement engine. - -## Retired Alternatives And Upstream Request - -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`: - -```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. The subsequently accepted PromptKit decisions -are recorded below. - -## Migration - -Migration must choose one installed definition for each former multi-version -prompt, move any retained version into its optional field, and relocate -qualified resource keys into collection/local-key mappings. Update destination -declarations, stored references, inventories, and configuration-inspection paths. Rename collection release metadata to -`release_version`, configure the shared checker and mode, and account for the -source-root header reservation and optional main declarations. Remove obsolete -request-version fields and version-selection logic from consumer examples and -public APIs rather than retaining them as assertions. Preserve provider model -strings and source resource bases. Namespace mounting does not -automatically relocate schema or artifact stores or rewrite references when a -collection is renamed. - -Retain the accepted removal of mandatory catalog modules and built-in profiles. -This is a breaking configuration/API change under the -[release policy](../policy/release.md); implementation must include consumer -migration guidance and update current contracts when it lands. The proposal -does not change today's supported configuration or API. - -## Delivery And Validation - -Select a released ConfigKit dependency containing the reviewed capabilities, -then implement PromptKit's schema, source-selection convenience APIs, and final -configuration adapter. Do not use a committed filesystem replacement as the -release integration. Build qualified runtime indexes after handoff. Collection -installation, remote fetching, implicit discovery, live reload, identity-based -list composition, and dependency solving remain outside this work. - -PromptKit acceptance should exercise typed assembly of multiple collections, -relative/nested imports, source-local header checking and consumption, ordinary -release and prompt metadata, unqualified resources, scoped selection, optional -prompt versions, inventory without release metadata, source-relative content, -intentional partial overrides, headerless entries/fragments, omitted main inputs -and headers, and resources named `version` in nested mappings. Verify ID-only -lookup and descriptive labels without version selection or request assertions. -Do not duplicate ConfigKit's generic source validation or enforce domain rules through historical source inspection. - -Review verification on 2026-10-16 against ConfigKit `e66c371`: focused public API -and consumer-example tests passed. A temporary typed consumer also passed -combined assembly for two collections with equal local prompt names, nested -relative imports and content-file contexts, optional prompt versions, both -enabled header modes, rejected headers despite upper-layer overrides, and -migration behavior for old release fields. The temporary consumer lives outside -both repositories. An additional offline check assembled this document's YAML -fragments through entry imports and matched the illustrated effective tree; -local links and whitespace checks passed. These checks verify upstream behavior -and the candidate schema, not an implemented PromptKit engine feature. - -## Accepted Configuration Decisions - -The following decisions settle the six questions previously listed here: - -1. **Main declaration:** optional, including for programmatic or underlay-only - construction. Validate supplied declarations without injecting missing ones. -2. **Public mode selection:** use `IfPresent`; defer a stricter PromptKit option - until a consumer need justifies it. No per-file header requirement. -3. **Reserved header naming:** reserve source-root `version`, while allowing a - resource named `version` through a nested resource mapping. -4. **Request versions:** remove request-side version fields and selection - machinery. Retain optional descriptive prompt labels only. -5. **Cross-collection inheritance:** use current ConfigKit inheritance; direct - profile references and authored expansion remain available. Defer a generic - upstream extension without adding a PromptKit resolver. -6. **Consumer API and packaging:** provide a small source/namespace convenience - API; recommend entry files and relative imports without requiring a filename, - layout, main input, or single peer group. Preserve direct fragment loading, - programmatic sources, and consumer-controlled precedence. Inventory reports - effective collections with optional release metadata. - -## Remaining Delivery Details - -No unresolved configuration-policy decision remains from those six questions. -Implementation still needs to select a released ConfigKit dependency containing -the required capabilities, finalize helper signatures and inventory/optional-label -representations, and provide migration examples. These are delivery details, -not reasons to introduce additional workflows or speculative API options. - -Stricter header modes and cross-path inheritance are deferred capabilities, not -implementation prerequisites. Update current contracts when behavior lands; -this roadmap records future work and does not itself implement the feature. diff --git a/docs/roadmap/configuration-collections.md b/docs/roadmap/configuration-collections.md new file mode 100644 index 0000000..12da2b3 --- /dev/null +++ b/docs/roadmap/configuration-collections.md @@ -0,0 +1,845 @@ +# Configuration Collections, Namespaces, And Scoped Lookup + +Status: accepted feature roadmap; not implemented in PromptKit. This document +consolidates the namespace roadmap and selected collection proposal 3. The +configuration and namespace design decisions are settled; public API details +and staged implementation planning remain delivery work. + +## Purpose + +Simplify PromptKit configuration by loading independently maintained prompt, +profile, and backend libraries as YAML collections instead of mandatory Go +modules. Remove the unused prompt-version selection scheme. Specification +headers are optional: maintainers can declare compatibility in a collection +entry without adding a declaration to every file. + +Consumers can select a namespace once and use local resource names while an +engine contains several collections with overlapping names. For example, +WeatherReporter and Homewatch can each define `summarize.alerts`, while provider +namespaces distinguish profiles targeting the same upstream model. + +Conventions and small convenience APIs should reduce authoring and maintenance +work. Preserve consumer choice over sources, layouts, namespaces, and precedence +without adding speculative options, mandatory workflows, or new compatibility +and provenance guarantees. + +## Prerequisite: Adopt ConfigKit v0.8.0 + +Adopt released ConfigKit **v0.8.0** before implementing the new collection schema +and loading APIs. PromptKit currently selects v0.7.1. Update the module dependency +and checksums, review affected public API compatibility and attribution, and +verify the existing ConfigKit integration as the prerequisite implementation +unit. Do not use a committed local filesystem replacement for adoption. + +The v0.8.0 tag was reviewed at `137769f` on 2026-10-17. It supplies input +placement, relative import destinations, and shared source-version checks needed +by this design. No additional upstream extension is required. Recording this +prerequisite does not itself upgrade PromptKit's dependency. + +ConfigKit's public `docs/config.md`, ADRs 0011–0014, and v0.8.0 release notes own +the upstream contracts. The capabilities used here are: + +- `Input.Destination` for root-relative placement and import `destination` for + placement relative to the immediate importing document. +- `Options.SourceVersionMode` and `LayeredOptions.SourceVersionMode`, applying + a shared `Definition.Version` policy in each document's source-local coordinates. +- Consumption of accepted non-main headers before composition, with the main + header retained and protected at its effective application path. +- Detached `SourceInfo.Destination` and `SourceInfo.VersionCheck` evidence, + together with existing source locations and resource contexts. + +### Keep Mapping-Based Collections + +ConfigKit v0.8.0 also offers sequence-item placement and registered composition +of distinct list records. These were reviewed and are not selected for this +round. A higher-priority list replaces the entire lower-priority list, duplicate +identities conflict within a strict group, and records cannot inherit from +sibling list records. Switching formats would sacrifice ordinary per-resource +partial overrides and local inheritance for flatter authoring. + +Keep collection resources as mappings keyed by local ID. Do not add an +alternative list schema or a PromptKit identity-based merge layer. The existing +key supplies identity without an additional embedded `id`. This is a scope +choice for this round, not a prohibition on future upstream capabilities. + +## Existing Foundation And Ownership + +The [format reference](../formats.md) and +[consumer guide](../consumers/pkg-promptkit.md) remain authoritative for current +behavior until implementation lands. Directory and embedded configuration +loading already exist through `DirectoryInputs` and `FilesystemInputs`. +The current engine also supplies provider catalog modules and built-in profiles; +the accepted removal is described below. + +[ADR 0001](../adr/0001-configuration-boundary.md) assigns source loading, +composition, and inheritance to ConfigKit. PromptKit interprets and validates +only final effective values, including their resource contexts. Source history +is available for explicit inspection, not additional validation of contributions. +Do not add a manifest pre-parser, source-level namespace ownership checks, or a +second composition pass. Two sources targeting the same namespace follow +ordinary ConfigKit composition; a repeated namespace is not a separate error. + +The [architecture policy](../policy/architecture.md) continues to assign CLI +syntax, discovery policy, and application lifecycle to consuming applications. + +## Collection Ownership And Built-In Removal + +**Accepted: load collections as application/operator-supplied configuration, +and remove PromptKit's built-in profiles as a breaking change.** Remove the +mandatory OpenRouter and RakestrawHome catalog Go-module dependencies and their +automatic contribution to engine configuration. PromptKit owns the definition +format and execution machinery; collection publishers own their configuration +repositories, and applications/operators select and provision the collections +they use. One or more explicitly selected directories can supply backends, +profiles, and prompts through the existing ConfigKit integration. + +Remove `promptkit-openrouter-standard` and `promptkit-openrouter-auto`, including +their implicit catalog dependencies. Do not replace them with another mandatory +starter bundle. Applications may embed their own defaults using the same +configuration format and loader, then layer operator configuration as desired. +Ordinary application overrides remain valid configuration and need not declare +a collection release of their own. + +This decouples collection updates from PromptKit rebuilds and releases and +avoids imposing provider inventories on every consumer. The tradeoff is that +applications and deployments must supply their required configuration and +manage compatible collection revisions. PromptKit does not gain a collection +installer, automatic downloader, or implicit directory search policy. + +Retain the captured-engine lifecycle: configuration changes take effect when +the application constructs and validates a replacement engine. A change to an +installed directory does not mutate an existing engine's captured definitions. +Distribution as configuration does not make new provider protocols or diagnostic +adapters data-only features; those can still require implementation work. + +Prepare explicit identifier mappings, replacement configuration examples, and +consumer migration instructions. Update the built-in-profile promise in the +release policy and its canonical format documentation when removal lands. +Use a release permitted to break configuration and API contracts under the +current release policy: a minor release before v1, or a major release at/after +v1. Actual catalog mappings, packaging arrangements, and release versions remain +delivery work; independent collection releases need not track future PromptKit +software releases. + +## Accepted Identity Convention + +Reserve `/` exclusively as the namespace separator in PromptKit prompt, profile, +and backend identifiers and references. A qualified identifier has exactly two +nonempty components: `namespace/local-name`. Neither component can contain `/`. +Dots remain available within local names. An unqualified identifier has no slash +and remains valid under the existing nonblank-ID rule. + +Reject resource identifiers or supplied references with multiple slashes or an +empty qualified component, such as `a/b/c`, `/name`, and `namespace/`. These +rules do not apply to provider model values, endpoints, resource paths, or prompt +versions. Preserve exact spelling; do not silently trim or rewrite IDs. + +| Resource | Namespace | Local name | Qualified ID | +| --- | --- | --- | --- | +| Prompt | `weatherreporter` | `summarize.alerts` | `weatherreporter/summarize.alerts` | +| Prompt | `homewatch` | `summarize.alerts` | `homewatch/summarize.alerts` | +| Profile | `openrouter` | `google.gemma-4-31b-it` | `openrouter/google.gemma-4-31b-it` | +| Profile | `rakestrawhome` | `google.gemma-4-31b-it` | `rakestrawhome/google.gemma-4-31b-it` | + +Prompt, profile, and backend identities remain separate resource types. The +same namespace can contain all three, and resources in it may come from +multiple configuration sources. A namespace does not imply a directory, +package, provider model identifier, or security boundary. + +A profile ID identifies execution settings. Its configured model value is +still the identifier passed to the provider; qualifying the profile ID does +not modify that value. Both example profiles may retain the configured model +`google/gemma-4-31b-it`. Different profiles may select the same model with +different settings. Prompt version labels remain separate from namespaces. The +selected collection format installs one definition per prompt ID and removes +request-side version selection and assertions. Optional labels are descriptive. + +Retain unqualified and dotted IDs. Existing slash-containing IDs need review: +those with one slash and nonempty components become qualified identities, while +those outside the new grammar require migration. This intentional format break +is accepted in favor of unambiguous resolution; the migration work is described +below. + +## Accepted Consumer Scope + +Provide a lightweight, immutable view over an existing engine with independent +prompt, profile, and backend scopes. The following sketch illustrates the +accepted shape; method/type names and final signatures remain provisional: + +```go +weather, err := engine.WithNamespaces(Namespaces{ + Prompt: "weatherreporter", + Profile: "openrouter", +}) +// Handle err, then use local prompt and profile names through weather. +``` + +These declarations do not exist yet. Empty scope fields mean no scope for that +resource type. Keep name qualification shared across the view and engine; do not +duplicate selection logic in each operation. Initially omit a one-string shorthand +that implicitly scopes all resource types. + +The view would share the engine's captured configuration, clients, and execution +machinery. Selecting a namespace would not rebuild the engine or duplicate its +backend capacity accounting. Consumers could use different views concurrently +without changing a shared current-namespace setting. + +The accepted resolution rules, independent of the final scope API, are: + +1. A supplied name without a slash is qualified using the selected scope for its + resource type, then looked up exactly. Without that scope, it is looked up as + an exact unqualified ID. +2. A missing scoped resource does not trigger a search in other namespaces or + among unqualified IDs. Loading another collection cannot redirect lookup. +3. A valid supplied name containing one slash is fully qualified and is looked + up exactly, regardless of scope. Malformed resource references are rejected. +4. Scope applies to caller-supplied resource names. Stored references, including + prompt default profiles, profile backends, inheritance references, and backend + smoke-test default profiles, retain their existing exact meaning. They are + not rebound to the invoking consumer's namespace. +5. Omitted optional IDs retain their existing selection/default behavior; an + omission is not converted into a namespace prefix or a lookup for an empty + local name. +6. Where outputs, inspection, errors, or diagnostic reports identify a selected + resource, preserve its resolved full identity. Any exposed local name should + be distinguishable from that identity. + +Keep qualification consistent across execution, preparation, request validation, +prompt/profile inspection, and applicable smoke-test selections. Namespace +selection must not change request override precedence or provider model strings. +Prompts resolve by ID alone. + +### Local And Qualified References In One Operation + +Syntax alone distinguishes local and qualified references. Do not decide whether +to qualify a name by checking which candidates exist; that would make resolution +depend on loaded content. + +Within a `weatherreporter` prompt scope, `summarize.alerts` resolves only to +`weatherreporter/summarize.alerts`, while `homewatch/summarize.alerts` explicitly +selects that other library's prompt. A qualified profile override similarly +bypasses the profile scope. Scoped operations accept both forms without a +separate reference type or requiring a return to the original engine for +cross-namespace access. + +The original engine remains a way to select an exact unqualified ID without +applying a scope. No escape syntax for unqualified IDs is introduced here. + +### Separate Resource Scopes + +A consumer can select prompt shorthand under `weatherreporter` and profile +shorthand under `openrouter` in the same request. Each resource type has an +independent scope. +A prompt's configured default profile needs no caller profile scope because +the stored reference is already exact. Scope syntax and existence handling are +defined in [scope validation](#scope-validation). + +### Scope Validation + +**Accepted: validate syntax when creating the view; resolve existence when +an operation selects a resource.** In the scope value, the empty +string means no scope. Reject a nonempty namespace containing `/` or consisting +only of whitespace. Otherwise preserve its exact spelling, with no trimming, +case folding, or additional character whitelist. This distinguishes omission +from an unusable namespace without inventing normalization rules. Report invalid +scope construction through PromptKit's existing public error conventions. + +Allow a well-formed namespace even when it has no current members. Its scoped +listing would be empty, and selection of a missing local resource would produce +the ordinary lookup error. Qualified selections would remain usable regardless +of the view's default scope. An empty optional request ID continues to mean +omission under that operation's existing contract; it is never qualified. + +This keeps scope as a naming context and supports uniform application setup +across installations with different library contents. It also avoids treating +successful scope construction as validation of a prompt or execution profile. +The tradeoff is that a misspelled but syntactically valid namespace is detected +only when listed or used, rather than during view construction. + +## Discovery And Namespace Filtering + +**Accepted: integrated namespace filtering and full-identity results.** +Enumerate the distinct named namespaces present in final effective +prompts, profiles, and backends. Allow filtering that enumeration by resource +type so a prompt picker need not show profile-only namespaces. Provide scoped +listings for the supported resource types, adding a backend inventory surface +as part of this work if needed. Namespace filtering is an explicit option in +the library listing API, not a consumer-side filtering helper. Preserve unfiltered +engine listing behavior, with the accepted removal of prompt-version identity +and selection; the exact Go option shape remains to be specified. + +| Selection | Listing behavior | +| --- | --- | +| No explicit filter on the engine | All namespaces, including unqualified resources | +| No explicit filter on a scoped view | The selected scope for that resource type; all resources of that type if no scope is selected | +| Explicit named namespace | Resources in exactly that namespace | +| Explicit unqualified-only filter | Resources whose IDs have no namespace | +| Explicit all-namespaces filter | All resources of that type, including unqualified resources | + +An explicit filter replaces the view's default for the current listing; it +does not intersect with that default or mutate the view. A named namespace +filter uses the namespace syntax rules above. A well-formed namespace +with no matching resources returns an empty listing. Represent default, named, +unqualified-only, and all-namespaces selections distinctly in the API; do not +overload an empty string to mean several of them. + +Return full IDs with optional prompt version labels separately; the full ID +identifies the installed prompt. Do not invent a named namespace such as `default` +for unqualified resources. Namespace enumeration excludes that unnamed group. +Use deterministic ordering consistent with existing listing contracts. + +Qualified result IDs can be passed unchanged to either scoped or unscoped +operations. Unqualified IDs remain literal unqualified identities: when an +explicit all-namespaces or unqualified-only filter exposes one through a scoped +view, use the original engine or a view without that resource scope to select +it exactly. A listing filter does not change subsequent execution scope. This +preserves the accepted lookup rules without introducing an escape syntax. + +Integrated filtering gives consumers a direct way to discover the desired +resources, while full identities keep logs and subsequent selections +unambiguous. Namespace/local-name display decomposition is separate from +filtering; a public splitting helper is not required by this decision. + +Namespace membership is exact: `weather` does not include `weatherreporter`. +Collection inventory describes final collection declarations and optional release +labels; it is distinct from enumeration of namespaces that contain resources. +See [collection inventory](#inspection-and-effective-collection-inventory). + +## Authoring And Assembly + +A file such as `weatherreporter/prompts/alerts.yml` would contain: + +```yaml +summarize.alerts: + version: "1" + default_profile: provider/brief + messages: + - role: user + content: Summarize the weather alerts. + output: + format: text + validation_mode: none +``` + +The version may simply be omitted. Another collection can contribute the same +local prompt name, for example `homewatch/prompts/alerts.yml`: + +```yaml +summarize.alerts: + default_profile: provider/brief + messages: + - role: user + content: Summarize the household alerts. + output: + format: text + validation_mode: none +``` + +A provider collection's `profiles/brief.yml` uses the same local-key convention: + +```yaml +brief: + backend: provider/default + model: vendor/example-model + temperature: 0.2 +``` + +Its `backends/default.yml` could contain this synthetic, non-live endpoint: + +```yaml +default: + endpoint: https://inference.example.invalid/v1 +``` + +A conventional `weatherreporter/collection.yml` entry can declare the shared +specification, optional collection release, and its layout: + +```yaml +version: 1 +release_version: "2.4.0" +configkit: + imports: + - glob: prompts/*.yml + destination: [prompts] +``` + +A provider entry can import `profiles/*.yml` at `[profiles]` and +`backends/*.yml` at `[backends]` in the same way. Collection publishers choose +which categories to include. A required glob must match; unused categories can +be omitted or use ConfigKit's existing optional-import behavior. Ordinary +fragments may omit specification headers under the selected `IfPresent` mode. +An entry header is the recommended publishing convention, not a load-time +requirement selectively enforced on entries by this mode. + +The application selects each entry, enables its imports, and supplies its +namespace through the public input destination. For example, the following +partial Go snippet uses released ConfigKit v0.8.0 APIs: + +```go +entry := configkit.Input{ + Source: configkit.File("/usr/local/share/collections/weatherreporter/collection.yml"), + Imports: true, + Destination: configkit.Keys("collections", "weatherreporter"), +} +``` + +For independent peer collections, one `Underlay` group's `Inputs` is a useful +starting point, not a required grouping. Consumers choose precedence and groups. +Configure `LayeredOptions.SourceVersionMode` as `SourceVersionModeIfPresent` and +`Definition.Version.Path` as `configkit.Keys("version")`, with PromptKit's +supported-specification checker. The application main input remains at the +root and declares `version: 1` in the examples below; both the main input and +its declaration are optional. Namespace and source selection require no manifest parsing in PromptKit. + +| Selected source | Effective destination | +| --- | --- | +| Application main document | Configuration root | +| `weatherreporter/collection.yml` | `[collections, weatherreporter]` | +| Its `prompts/*.yml` imports | `[collections, weatherreporter, prompts]` | +| Homewatch entry and its prompt imports | `[collections, homewatch]` and `[collections, homewatch, prompts]` | +| Provider entry and its profile/backend imports | `[collections, provider]`, then `[profiles]` or `[backends]` appended | + +Directly selected fragments with `Input.Destination` remain possible. Entry +imports are the recommended packaging convention because publishers can describe +their layout. Filenames and directory basenames do not define resource identity; +no implicit directory discovery, namespace inference, or collection installer is +introduced. A small helper should construct inputs from an explicitly selected +source and namespace, without inspecting manifests to compute destinations. `collection.yml` +is a convention, not a required filename. Direct fragments and programmatic +sources remain supported through the source API; no parallel collection loader +or mandatory filesystem layout is introduced. + +Use later underlay groups or main for intentional application/operator overrides. +Placement does not establish precedence. Do not also recursively scan files +already selected by entry imports: ConfigKit's repeated-source rules still apply. + +The entry's source-root `version` is checked and consumed before its payload +composes. Its `release_version` is retained as ordinary collection metadata. +A prompt's nested optional `version` is also ordinary payload. These are three +distinct concepts; source-root release metadata must no longer use `version`. + +The assembled application shape would be equivalent to: + +```yaml +version: 1 +collections: + weatherreporter: + release_version: "2.4.0" + prompts: + summarize.alerts: + version: "1" + default_profile: provider/brief + messages: + - role: user + content: Summarize the weather alerts. + output: + format: text + validation_mode: none + homewatch: + prompts: + summarize.alerts: + default_profile: provider/brief + messages: + - role: user + content: Summarize the household alerts. + output: + format: text + validation_mode: none + provider: + backends: + default: + endpoint: https://inference.example.invalid/v1 + profiles: + brief: + backend: provider/default + model: vendor/example-model + temperature: 0.2 +``` + +This yields `weatherreporter/summarize.alerts` and +`homewatch/summarize.alerts`, plus profile `provider/brief` and backend +`provider/default`. The provider's model string remains `vendor/example-model`; +it is not a PromptKit resource identifier. + +One fragment may contain several local definitions. A fragment starting directly +with `messages:` would need a destination that also supplies the local prompt +key. ConfigKit mounting permits such placement, but with source checks enabled +its root `version` would be the specification header, not prompt metadata. Recommend +the local-key mapping above as the standard authoring form: identity travels +with the definition and files may be reorganized without changing it. Filename-derived IDs are not +needed. + +## Final Schema And Identity + +Use `collections` as a mapping of namespace to collection body, containing +optional `release_version` and optional `backends`, `profiles`, and `prompts` +mappings. Collection release labels are descriptive, with no SemVer ordering +or dependency-solving semantics. Metadata is optional: an installed collection +can contain resources without claiming a published release. + +Each resource key inside a collection is local. The parent namespace and local +key follow the accepted slash-free component grammar; their public spelling is +exactly `namespace/local-name`. There are no duplicate embedded `id` or +`namespace` fields to reconcile. Dots remain ordinary characters. + +Retain unqualified application resources through optional root-level +`backends`, `profiles`, and `prompts` mappings, also keyed by local name and +using the same definition shapes. They produce unqualified public IDs. Under +this proposal, namespaced definitions belong under `collections`, not a second +qualified-key spelling in those root maps. This avoids two configuration paths +producing the same public identity. This representation belongs to the selected +future direction; it does not change today's implemented format. + +Stored references such as `default_profile`, profile `backend`, and smoke-test +profile defaults remain exact public references. Nesting does not automatically +qualify a slashless reference or rebind it to caller scope. For example, a +profile in `provider` still declares `backend: provider/default`. Caller-scoped +lookup follows [the scope rules above](#accepted-consumer-scope) after final +indexes are built. + +### One Installed Prompt And Optional Version + +The resource's qualified ID is its sole configuration identity. There is no +version-key mapping, implicit `latest`, version ordering, or parallel installed +history. Updating the optional version changes metadata on the same prompt. + +An optional prompt version is an opaque string with no SemVer, nonblank, or +normalization requirement. Omission or explicit null means no declared label; +null can clear a lower-layer or inherited label. Quote numeric-looking labels +such as `"1"`. Preserve supplied strings, including empty strings, and distinguish +an absent label in inspection. Do not synthesize a version. + +Remove request-side prompt-version fields, version selection, ordering, and +version defaults as part of the breaking change. Do not reinterpret those fields +as exact-match assertions. Execution and inspection resolve the installed prompt +by ID alone. Listings expose one prompt per ID with its optional descriptive +label separately. A label neither controls execution nor proves unchanged content. + +## ConfigKit Composition Boundary + +Use the implemented upstream contracts without duplicating them in PromptKit. +Input destinations use literal mapping-key segments, independent of filesystem +roots and precedence. Relative import destinations are sequences of strings; +omission and `[]` retain the importing destination. Null, scalar, and non-string +destination members are invalid. No logical path segment means parent traversal +or a reset to the application root. + +Source-root import directives remain structural. Imported content contributes at +its resulting destination, while physical import paths and `content_file` bases +remain relative to their declaring sources. `SourceInfo.Destination` records +placement; normal origins and diagnostics retain real source locations. Selected +overlays and explicit CLI overrides remain rooted at the application root. + +Mounting preserves source identity, cycle/repetition rules, and load limits. +An absent `NoSource` contributes no payload; an actual empty mapping can +materialize its destination containers without clearing lower contents. +ConfigKit's reserved structural keys continue to apply. PromptKit must not +strip headers, re-key source trees, merge fragments itself, or turn source +inspection into an additional enforcement pass. + +### Composition And Overrides + +Different collection keys prevent accidental collisions between equal local +resource names. Two sources targeting the same namespace are not automatically +invalid: disjoint mapping contributions combine. Overlapping scalar fields, +message sequences, nulls, and equal release labels conflict within a strict +group. Across deliberate layers they follow ordinary precedence instead. + +A higher-layer prompt mapping is a partial override. Changing only its version +does not replace its messages or other settings. Omitting a field retains its +lower value, and an empty mapping does not clear a definition. Publishers and +operators must treat such changes as configuration customization, not as an +atomic installation of another prompt release. Do not introduce source audits +or a version-triggered replacement rule to conceal this distinction. + +### Inheritance + +Existing sibling inheritance remains useful within each collection's resource +mapping: `extends: brief` can name the local profile `brief`. Prompt versions, +now ordinary optional fields, would inherit and overlay like other fields; +`version: null` explicitly clears an inherited label. + +Cross-collection profiles are no longer immediate siblings. Consequently, +`extends: provider/brief` cannot reach that collection under current ConfigKit +inheritance. Use existing local sibling inheritance for this development round; +applications can still select the qualified `provider/brief` profile directly. +Authors needing a customized derivative may publish an expanded definition. +Expansion is a migration option, not a mandatory authoring workflow. + +Generic cross-path inheritance remains possible future ConfigKit work if a +concrete consumer need justifies it. It is not a prerequisite of this round. +PromptKit must not add its own inheritance resolver after ConfigKit handoff. + +## Specification Versions And Collection Inventory + +### Shared Specification Policy + +Use one `Definition.Version` policy, with source-local path `version`, for the +candidate PromptKit specification. Initially accept specification text `1` from +string or strict decimal-integer declarations; reject explicit null and other +unsupported declarations through the ConfigKit checker. Accept an absent main +declaration, including an absent main input. Accept additional versions only +when PromptKit actually supports their format semantics: accepting +a header does not migrate its payload or independently validate a fragment's +complete schema. The retired proposals were alternatives, not released formats +sharing specification version 1. + +Use `SourceVersionModeIfPresent` without adding a PromptKit mode-selection +option in this round. A stricter option can be considered for a demonstrated +consumer need. The upstream modes remain distinct: + +| Mode | Implemented behavior | Role in this proposal | +| --- | --- | --- | +| `Default` | Existing main-only effective-path check and protected-path behavior | Not the proposed shared-source policy | +| `IfPresent` | Checks present declarations; skips absent non-main declarations | Initial choice for compact fragments | +| `Required` | Requires declarations on every participating document | Upstream capability; exposing a PromptKit option is deferred | + +Participating documents include actual main and underlay inputs, transitive and +glob imports, and the selected overlay. Defaults, synthetic CLI overrides, +resolution output, absent optional imports, and empty groups do not need headers. +Explicit null is present and reaches the checker; malformed selected values +fail ConfigKit's version-type validation. A rejected declaration fails loading +even if a higher layer would supersede that source's payload. + +In enabled modes, accepted non-main headers are consumed before composition. +The main declaration remains and is protected at its destination-prefixed path. +PromptKit's recommended main is unmounted, so that protected path is `/version`. +Do not confuse header consumption with permission to override the remaining +protected application field. + +`IfPresent` does not require a collection entry header and does not infer a +fragment's compatibility from its entry. A publisher can declare compatibility +in the entry while leaving imported files headerless; the assembled configuration +still undergoes normal validation. A declaration is not a guarantee of future +compatibility or independent schema validation of every imported fragment. +ConfigKit checks the declarations actually present. Requiring headers only on +entry files is not supported by the load-wide mode. Do not emulate it using a +preliminary manifest load or validation of +source history. If that stronger guarantee becomes essential, seek an explicit +upstream contract instead. + +### Naming And Optional Main Inputs + +Reserve source-root `version` for specification declarations throughout the new +format. Use `release_version` for optional collection release labels and retain +nested prompt `version` for the optional installed-prompt label. An old release +file containing only `version: "2.4.0"` can fail the specification check; if its +text happens to be accepted, that field is consumed and lost as release metadata. +Migration must rename it rather than simply enable the new mode. + +This reservation affects resource fragments whose local key is literally +`version`: a root mapping with that name is interpreted as a malformed header. +Allow that resource name through a nested resource mapping, for example +`prompts: {version: ...}` in a collection entry. Reserve the header position, +not the public resource name. Existing `configkit` and `extends` structural +reservations also remain applicable; destinations do not bypass them. + +An absent header in an actual main document still reaches the checker under +`IfPresent`; `NoSource` invokes the absent-main checker as well. PromptKit's +checker accepts omission in both cases. Consumers may assemble only underlays, +fragments, or programmatic inputs without manufacturing a main document or +header. Do not inject a declaration or report undeclared sources as checked. +Present unsupported declarations still fail; final schema validation still runs. + +### Inspection And Effective Collection Inventory + +`SourceInfo.VersionCheck` is source evidence, not a generic success flag. A +non-nil record with `Present == false` can describe an omission for which the +checker did not run. Header paths and locations are source-local. Evidence is +detached and does not reread inputs; text is conservatively redacted during +callbacks and on failed loads, and successful final records respect completed +sensitivity classification. These records belong in explicit configuration +inspection, not in ordinary domain validation or collection-release identity. + +Derive collection inventory from the final `collections` mapping: namespace +plus optional effective `release_version`, sorted and detached from the captured +engine. Metadata-only and resource-only entries can both appear. Root unqualified +resources do not invent a named collection; resource-based namespace enumeration +remains separate. Exact exported inventory types remain API design work. + +An inventory describes effective configuration, not immutable package identity, +historical source ownership, or proof of unchanged published contents. Operator +overrides may change resources while retaining a release label. Engine +construction captures definitions; edits require a replacement engine. + +## Migration + +Migration must choose one installed definition for each former multi-version +prompt, move any retained version into its optional field, and relocate +qualified resource keys into collection/local-key mappings. Update destination +declarations, stored references, inventories, and configuration-inspection paths. +Rename collection release metadata to +`release_version`, configure the shared checker and mode, and account for the +source-root header reservation and optional main declarations. Remove obsolete +request-version fields and version-selection logic from consumer examples and +public APIs rather than retaining them as assertions. Preserve provider model +strings and source resource bases. Namespace mounting does not +automatically relocate schema or artifact stores or rewrite references when a +collection is renamed. + +Retain the accepted removal of mandatory catalog modules and built-in profiles. +This is a breaking configuration/API change under the +[release policy](../policy/release.md); implementation must include consumer +migration guidance and update current contracts when it lands. The proposal +does not change today's supported configuration or API. + +Existing unscoped exact lookup remains available for IDs valid under the new +grammar. Compatibility with arbitrary slash-containing resource IDs is +intentionally broken. The migration must: + +- Audit maintained catalogs and representative consumer definitions for invalid + IDs and for valid one-slash IDs whose namespace meaning needs review. +- Choose explicit old-to-new mappings and check for collisions. Do not + automatically replace slashes with dots: distinct existing IDs could collapse + to the same new identity. +- Update renamed keys and their references together, including inheritance, + prompt default profiles, profile backend references, backend smoke-test + defaults, and affected examples and consumer request IDs. +- Keep provider model strings unchanged. Renaming a profile does not rename + the upstream model. +- Document consumer action and coordinate the PromptKit format change with + catalog adoption under the [release policy](../policy/release.md). Its catalog + profile-data exception does not cover this general format/API behavior change; + deliberately retire its built-in-profile preservation promise when the + accepted removal lands. Current policy and format documents remain unchanged + until implementation. + +Renaming a backend changes its identity; duplicating it under another ID can +separate capacity accounting. Do not use blanket aliases or duplicate catalog +entries as an automatic compatibility mechanism. Exact release versions and +catalog mappings remain implementation-planning work. + +## Work Deferred Or Outside This Round + +- Collection installation, automatic downloading, implicit directory discovery, + package registries, dependency solving, and live reload. +- Automatic rewriting of stored references when mounting or renaming collections. +- Implicit namespace search paths, fallback lookup, namespace-based access control, + and filesystem isolation. +- Changes to provider model names, inference behavior, or ConfigKit precedence. +- Sequence-item resource catalogs, identity-based overlays, cross-path inheritance, + and a PromptKit option requiring specification headers in every document. + +Explicit source destinations, direct fragments, programmatic sources, and +consumer-selected precedence remain supported in the design. Deferred features +can be reconsidered for concrete needs; none blocks implementation. + +## Delivery And Validation + +The next work is a staged implementation plan grounded in these accepted +contracts. Its first prerequisite is [ConfigKit v0.8.0 adoption](#prerequisite-adopt-configkit-v080). +Then cover the collection schema and final configuration adapter, catalog and +built-in removal, prompt-version API removal, immutable scoped views, integrated +filtering, and collection inventory. Build qualified runtime indexes after +ConfigKit handoff. Select exact helper signatures, listing options, and metadata +representations using representative consumer examples. + +Plan focused offline validation under the [testing policy](../policy/testing.md). +Update GoDoc, the format reference, consumer guidance, affected release policy, +and internal documentation when behavior lands. Retire this roadmap after the +work is complete. This outline is not yet the staged implementation plan. + +### Namespace And Consumer Acceptance + +- Two prompt scopes resolve the same local name to their respective prompts; + a missing name never resolves from the other scope or an unqualified entry. +- Two provider profile scopes resolve the same local name, such as + `google.gemma-4-31b-it`, to different profiles without changing the configured + provider model value `google/gemma-4-31b-it`. +- A qualified reference bypasses the selected scope without double qualification; + adding another collection does not change local or qualified resolution. +- Multiple slashes and empty qualified components are rejected in resource IDs + and references, while provider model values retain their existing syntax. +- Prompt shorthand and a profile override use their intended scopes, while + stored default references and omitted selections retain their meaning. +- Scoped views coexist concurrently and share backend capacity with ordinary + calls on the underlying engine. +- Scope creation rejects invalid syntax, accepts well-formed absent namespaces, + and treats empty scope fields as no scope. Missing resources fail at lookup; + qualified references remain usable even when the default namespace is absent. +- Listing without an explicit filter uses the resource's view scope, or lists + all resources when no scope is selected. Explicit named, unqualified-only, + and all-namespaces filters override that default for the current listing only. +- A valid namespace with no matches produces an empty listing. Namespace + filtering respects resource type and exact namespace boundaries. +- Exact-ID calls using valid IDs and intentional overlays retain their behavior. + Migrated catalog references resolve without collisions; migration installs one + prompt per ID with an optional descriptive label. +- Listings preserve full IDs and separate optional prompt version labels. + Qualified results round-trip through either engine or view; unqualified results + round-trip through the engine without a corresponding scope. +- Representative validation, inspection, execution, and smoke-test paths use + the same qualification rules without duplicating every test at every layer. +- Engines use caller-selected definitions without automatically contributing + provider catalogs or PromptKit-owned profiles. Ordinary directory and embedded + configuration sources retain ConfigKit composition semantics. +- PromptKit does not add source-history-based duplicate-namespace rejection or + independently pre-validate collection definitions before the final handoff. + +### Configuration Acceptance + +Exercise typed assembly of multiple collections, relative/nested imports, +source-local header checking and consumption, ordinary release and prompt +metadata, unqualified resources, optional prompt labels, inventory without +release metadata, source-relative content, and intentional partial overrides. +Cover headerless entries/fragments, omitted main inputs and headers, and +resources named `version` in nested mappings. Verify ID-only lookup without +request-version selection or assertions. No automatic provider catalogs or +built-in profiles should be contributed. + +Do not duplicate ConfigKit's generic source validation or enforce domain rules +through historical source inspection. Preserve local inheritance and ordinary +mapping overrides while migrating catalog definitions. + +### Review Evidence + +The 2026-10-16 review against ConfigKit development commit `e66c371` verified +mounted assembly, relative imports and resource contexts, shared header checks, +and this document's YAML assembly through public consumer tests. The 2026-10-17 +review of release v0.8.0 at `137769f` passed maintained consumer examples and +focused public API probes for sequence assembly, duplicate rejection, whole-list +replacement, inheritance limits, header consumption, and final identity validation +boundaries. The latter review informed the decision to retain mappings. These +checks establish upstream behavior, not an implemented PromptKit feature. + +## Remaining Delivery Details + +No substantive design question remains before staged implementation planning. +ConfigKit v0.8.0 is the selected prerequisite, not an unresolved release choice. +Finalize public helper signatures, inventory and optional-label representations, +identifier migration mappings, and concrete consumer migration examples in the +plan and implementation. Do not reopen settled workflows or add speculative +options merely to complete API shapes. + +## Consolidation And Retired Alternatives + +This document replaces the separate namespace roadmap and collection proposal 3. +Their pre-consolidation versions, including considered namespace API alternatives, +remain available at PromptKit commit `ddda2f8`: + +```sh +git show ddda2f8:docs/roadmap/namespaces.md +git show ddda2f8:docs/roadmap/configuration-collections-mounted-fragments.md +``` + +The unselected collection proposals remain available at `b5866dd`: + +```sh +git show b5866dd:docs/roadmap/configuration-collections-current-configkit.md +git show b5866dd:docs/roadmap/configuration-collections-identified-records.md +``` + +Proposal 1 used fully qualified mapping keys. Proposal 2 requested identified +record sequences, identity-based overlays, and record inheritance. ConfigKit +v0.8.0 implements a narrower sequence-composition capability; it does not supply +those identity-based overlays or inheritance. Its sequence feature was evaluated +and not selected for this roadmap. The original upstream feature-request lineage +is recorded in ConfigKit history under +`docs/roadmap/promptkit-identified-records-feature-request.md`; no outstanding +part of that request is a prerequisite here. + +The accepted configuration choices remain: optional main declarations and +if-present checks; source-root `version` reserved without banning that resource +name; removal of request-version machinery; current ConfigKit inheritance; and +flexible source assembly with a small convenience API. This consolidation does +not change those decisions or implement the feature. diff --git a/docs/roadmap/namespaces.md b/docs/roadmap/namespaces.md deleted file mode 100644 index b4af763..0000000 --- a/docs/roadmap/namespaces.md +++ /dev/null @@ -1,538 +0,0 @@ -# Namespaces, Collections, And Scoped Resource Lookup - -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. Decision 5 selects mounted collections -and optional source-version checking. Its six configuration decisions are -accepted in the collection roadmap, subject to the ConfigKit boundary. Exact Go -names and option types remain delivery details. - -## Purpose - -Simplifying configuration is a primary goal of this round: YAML collections -replace mandatory catalog Go-module imports, unused prompt-version selection is -removed, and specification declarations are optional. Publishers may declare a -specification in an entry document without adding headers to every file. -Conventions should ease authoring without prescribing consumer layout or -precedence. The [collection roadmap](configuration-collections-mounted-fragments.md) -owns the configuration decisions and delivery details. - -Let consumers select a namespace once and use convenient local names within -that namespace across PromptKit operations. An assembled engine may contain -multiple application prompt libraries and provider profile catalogs with -overlapping local names. Resolution must remain unambiguous. - -The motivating cases are: - -- WeatherReporter and another application both define `summarize.alerts`. - Each consumer selects its application namespace and uses the local name. -- OpenRouter and RakestrawHome both provide the model identified in the - discussion as `google/gemma-4-31b-it`. Consumers distinguish the execution - profiles by provider namespace while retaining convenient local naming. - -Model names here illustrate identity and resolution, not provider availability -or a promise of maintained catalog entries. - -The primary need is organization and convenient lookup within assembled -libraries, with backend, profile, and prompt collections managed independently -of PromptKit software releases. Ansible collections are a conceptual reference -for qualified names; a package manager and relocatable collection imports are -not required for this feature. - -## Existing Foundation - -The [format reference](../formats.md) defines prompt, profile, and backend keys -as exact literal IDs. Punctuation is already allowed, and references are not -trimmed or re-keyed. Merely adopting prefixed IDs therefore does not provide -the requested scoped lookup behavior. - -Existing prompt and profile listings expose assembled identities. The -[consumer guide](../consumers/pkg-promptkit.md) describes current operations; -their Go declarations and GoDoc remain authoritative until this feature lands. - -Directory-based and embedded configuration loading already exist through -`DirectoryInputs` and `FilesystemInputs`. The current engine also prepends -configuration assets from the imported OpenRouter and RakestrawHome Go modules, -plus PromptKit-owned profiles. Decision 4 removes that automatic inclusion and -dependency coupling; it does not require a replacement for ConfigKit loading. - -[ADR 0001](../adr/0001-configuration-boundary.md) assigns source loading, -composition, and inheritance to ConfigKit. This proposal adds interpretation -of consumer-supplied resource names against the final effective configuration, -without a second source-composition pass. - -## Accepted Identity Convention - -Reserve `/` exclusively as the namespace separator in PromptKit prompt, profile, -and backend identifiers and references. A qualified identifier has exactly two -nonempty components: `namespace/local-name`. Neither component can contain `/`. -Dots remain available within local names. An unqualified identifier has no slash -and remains valid under the existing nonblank-ID rule. - -Reject resource identifiers or supplied references with multiple slashes or an -empty qualified component, such as `a/b/c`, `/name`, and `namespace/`. These -rules do not apply to provider model values, endpoints, resource paths, or prompt -versions. Preserve exact spelling; do not silently trim or rewrite IDs. - -| Resource | Namespace | Local name | Qualified ID | -| --- | --- | --- | --- | -| Prompt | `weatherreporter` | `summarize.alerts` | `weatherreporter/summarize.alerts` | -| Prompt | `homewatch` | `summarize.alerts` | `homewatch/summarize.alerts` | -| Profile | `openrouter` | `google.gemma-4-31b-it` | `openrouter/google.gemma-4-31b-it` | -| Profile | `rakestrawhome` | `google.gemma-4-31b-it` | `rakestrawhome/google.gemma-4-31b-it` | - -Prompt, profile, and backend identities remain separate resource types. The -same namespace can contain all three, and resources in it may come from -multiple configuration sources. A namespace does not imply a directory, -package, provider model identifier, or security boundary. - -A profile ID identifies execution settings. Its configured model value is -still the identifier passed to the provider; qualifying the profile ID does -not modify that value. Both example profiles may retain the configured model -`google/gemma-4-31b-it`. Different profiles may select the same model with -different settings. Prompt version labels remain separate from namespaces. The -selected collection format installs one definition per prompt ID and removes -request-side version selection and assertions. Optional labels are descriptive. - -Retain unqualified and dotted IDs. Existing slash-containing IDs need review: -those with one slash and nonempty components become qualified identities, while -those outside the new grammar require migration. This intentional format break -is accepted in favor of unambiguous resolution; the migration work is described -below. - -## Accepted Consumer Scope - -Provide a lightweight, immutable view over an existing engine with independent -prompt, profile, and backend scopes. The following sketch illustrates the -accepted shape; method/type names and final signatures remain provisional: - -```go -weather, err := engine.WithNamespaces(Namespaces{ - Prompt: "weatherreporter", - Profile: "openrouter", -}) -// Handle err, then use local prompt and profile names through weather. -``` - -This sketch follows [decision 1](#1-public-scope-shape-accepted); these -declarations do not exist yet. - -The view would share the engine's captured configuration, clients, and execution -machinery. Selecting a namespace would not rebuild the engine or duplicate its -backend capacity accounting. Consumers could use different views concurrently -without changing a shared current-namespace setting. - -The accepted resolution rules, independent of the final scope API, are: - -1. A supplied name without a slash is qualified using the selected scope for its - resource type, then looked up exactly. Without that scope, it is looked up as - an exact unqualified ID. -2. A missing scoped resource does not trigger a search in other namespaces or - among unqualified IDs. Loading another collection cannot redirect lookup. -3. A valid supplied name containing one slash is fully qualified and is looked - up exactly, regardless of scope. Malformed resource references are rejected. -4. Scope applies to caller-supplied resource names. Stored references, including - prompt default profiles, profile backends, inheritance references, and backend - smoke-test default profiles, retain their existing exact meaning. They are - not rebound to the invoking consumer's namespace. -5. Omitted optional IDs retain their existing selection/default behavior; an - omission is not converted into a namespace prefix or a lookup for an empty - local name. -6. Where outputs, inspection, errors, or diagnostic reports identify a selected - resource, preserve its resolved full identity. Any exposed local name should - be distinguishable from that identity. - -Keep qualification consistent across execution, preparation, request validation, -prompt/profile inspection, and applicable smoke-test selections. Namespace -selection must not change request override precedence or provider model strings. -Prompts resolve by ID alone. - -### Local And Qualified References In One Operation - -Syntax alone distinguishes local and qualified references. Do not decide whether -to qualify a name by checking which candidates exist; that would make resolution -depend on loaded content. - -Within a `weatherreporter` prompt scope, `summarize.alerts` resolves only to -`weatherreporter/summarize.alerts`, while `homewatch/summarize.alerts` explicitly -selects that other library's prompt. A qualified profile override similarly -bypasses the profile scope. Scoped operations accept both forms without a -separate reference type or requiring a return to the original engine for -cross-namespace access. - -The original engine remains the way to select an exact unqualified ID without -applying a scope. No escape syntax for unqualified IDs is introduced here. - -### Separate Resource Scopes - -A consumer can select prompt shorthand under `weatherreporter` and profile -shorthand under `openrouter` in the same request. Each resource type has an -independent scope, as defined in [decision 1](#1-public-scope-shape-accepted). -A prompt's configured default profile needs no caller profile scope because -the stored reference is already exact. Scope syntax and existence handling are -defined in [decision 2](#2-scope-validation-accepted). - -## Discovery And Collection Organization - -Namespace enumeration and filtering are integrated into PromptKit's listing -APIs. Consumers select a filter and receive matching full resource identities; -they do not need to fetch all resources and filter them themselves. Defaults, -explicit filter overrides, and result semantics are defined in -[decision 3](#3-discovery-accepted). - -Derive namespace membership from the qualified identifier's namespace component: -`weather` must not include `weatherreporter/...`. Nested slash-separated -namespaces are outside the accepted grammar. - -Collection namespace/release metadata and an API for reporting effective -collection declarations are desired additions. They are distinct from resource -namespace enumeration. Their configuration shape and specification-version -handling are accepted in [decision 5](#5-collection-metadata-and-specification-versioning-accepted). - -## Composition And Compatibility - -Qualified identities reduce accidental collisions between independently -authored libraries. Definitions with the same exact qualified identity still -follow the existing ConfigKit overlay and inheritance rules; namespaces do not -create a separate collision policy. - -Leave source composition and configuration inspection with ConfigKit. Do not -introduce implicit qualification of definition keys, mounting under caller-chosen -prefixes, or rewriting of stored references as part of consumer scope. Those -would be separate configuration-design work. - -ConfigKit owns all configuration processing before handing off the final, -fully resolved configuration. PromptKit must not add a collection-manifest -preflight, source-level uniqueness check, or independent version-checking pass -around that processing. Its domain interpretation and validation consume final -effective values. Source history remains available through explicit ConfigKit -inspection, not as a way to enforce extra assembly rules. - -In particular, two documents contributing metadata for the same collection -namespace are governed by ordinary ConfigKit composition semantics. ConfigKit -may reject conflicts or combine contributions according to their layers; a -repeated source declaration is not an additional PromptKit error. The earlier -proposal to reject duplicate declared namespaces before composition is withdrawn. - -Existing unscoped exact lookup remains available for IDs valid under the new -grammar. Compatibility with arbitrary slash-containing resource IDs is -intentionally broken. The migration must: - -- Audit maintained catalogs and representative consumer definitions for invalid - IDs and for valid one-slash IDs whose namespace meaning needs review. -- Choose explicit old-to-new mappings and check for collisions. Do not - automatically replace slashes with dots: distinct existing IDs could collapse - to the same new identity. -- Update renamed keys and their references together, including inheritance, - prompt default profiles, profile backend references, backend smoke-test - defaults, and affected examples and consumer request IDs. -- Keep provider model strings unchanged. Renaming a profile does not rename - the upstream model. -- Document consumer action and coordinate the PromptKit format change with - catalog adoption under the [release policy](../policy/release.md). Its catalog - profile-data exception does not cover this general format/API behavior change; - deliberately retire its built-in-profile preservation promise when the - accepted removal lands. Current policy and format documents remain unchanged - until implementation. - -Renaming a backend changes its identity; duplicating it under another ID can -separate capacity accounting. Do not use blanket aliases or duplicate catalog -entries as an automatic compatibility mechanism. Exact release versions and -catalog mappings remain implementation-planning work. - -The [architecture policy](../policy/architecture.md) continues to assign CLI -syntax, flags, and application policy to consumers such as Scriptorium. PromptKit -would supply reusable scope and resolution semantics. - -## Work Not Included In The Initial Proposal - -- Package installation, collection registries, and dependency resolution. -- Automatic rewriting of stored references when mounting or renaming collections. - Explicit source destinations and namespace selection are supported by proposal 3. -- Implicit namespace search paths or fallback to whichever resource exists. -- Namespace-based access control or filesystem isolation. -- Changes to model names, inference behavior, or configuration precedence. - -These boundaries limit the initial proposal; they do not prohibit future work -if a concrete consumer need emerges. - -## Delivery Outline - -1. Adopt the ConfigKit capabilities selected in decision 5 and audit existing - identifier usage against the accepted grammar. Record migration mappings and - application provisioning guidance. Finalize Go names and option types for - the accepted namespace and collection APIs. -2. Remove automatic catalog inclusion, catalog-module dependencies, and built-in - profiles under decision 4. Use caller-selected configuration sources and - update examples/tests to supply their own definitions. -3. Implement the smallest public scope surface with shared name qualification - and existing engine collaborators. Cover supported operations consistently. -4. Add integrated namespace filtering and representative consumer examples. - Implement collection metadata/inventory under the accepted decision 5. -5. Verify compatibility and scoped resolution through focused offline tests, - following the [testing policy](../policy/testing.md). Update GoDoc, the - format reference, consumer guide, and relevant internal documentation as - behavior lands; retire this roadmap when its work is complete. - -Acceptance cases for the settled rules; the collection roadmap owns additional -configuration and metadata acceptance cases: - -- Two prompt scopes resolve the same local name to their respective prompts; - a missing name never resolves from the other scope or an unqualified entry. -- Two provider profile scopes resolve the same local name, such as - `google.gemma-4-31b-it`, to different profiles without changing the configured - provider model value `google/gemma-4-31b-it`. -- A qualified reference bypasses the selected scope without double qualification; - adding another collection does not change local or qualified resolution. -- Multiple slashes and empty qualified components are rejected in resource IDs - and references, while provider model values retain their existing syntax. -- Prompt shorthand and a profile override use their intended scopes, while - stored default references and omitted selections retain their meaning. -- Scoped views coexist concurrently and share backend capacity with ordinary - calls on the underlying engine. -- Scope creation rejects invalid syntax, accepts well-formed absent namespaces, - and treats empty scope fields as no scope. Missing resources fail at lookup; - qualified references remain usable even when the default namespace is absent. -- Listing without an explicit filter uses the resource's view scope, or lists - all resources when no scope is selected. Explicit named, unqualified-only, - and all-namespaces filters override that default for the current listing only. -- A valid namespace with no matches produces an empty listing. Namespace - filtering respects resource type and exact namespace boundaries. -- Exact-ID calls using valid IDs and intentional overlays retain their behavior. - Migrated catalog references resolve without collisions; 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. -- Engines use caller-selected definitions without automatically contributing - provider catalogs or PromptKit-owned profiles. Ordinary directory and embedded - configuration sources retain ConfigKit composition semantics. -- PromptKit does not add source-history-based duplicate-namespace rejection or - independently pre-validate collection definitions before the final handoff. - -## Decisions And Pending Design - -Decisions 1–3 are accepted; their alternatives are retained as considered but -not selected. Decision 4 records the accepted change in collection ownership -and supersedes the earlier catalog-only migration proposal. Decision 5 records -the accepted collection configuration direction and links its delivery details. - -### 1. Public Scope Shape (Accepted) - -**Accepted: an immutable view with independent resource scopes.** Provide one -engine operation that accepts a small value with prompt, profile, and backend -namespace fields and returns a scoped view or an error. The illustrative -`WithNamespaces` and `Namespaces` names above are provisional. Empty fields mean -no scope for that resource type. The view captures the value and delegates to -the same engine; it does not expose a mutable namespace setting. Apply the -prompt field to caller-supplied prompt IDs, the profile field to profile IDs, -and the backend field to backend IDs wherever those selections are supported. -Qualified references bypass the corresponding field. - -This directly serves the select-once workflow and the common mixed case of an -application prompt namespace with a provider profile namespace. It also keeps -scope out of stored configuration and makes concurrent use independent of call -order. The cost is an additional facade surface whose supported operations must -remain consistent with the engine. Keep qualification shared rather than -duplicating selection logic in each wrapper. - -Initially omit an ambiguous one-string `InNamespace` shorthand. Repeating the -same namespace in several fields happens once when creating a view and states -exactly which resource types it affects. A convenience operation can follow if -that repetition is a demonstrated burden. - -**Considered alternative, not selected: one namespace per immutable view, -applied to all resource types.** A one-string operation would scope every -unqualified prompt, profile, and backend selection; callers would use qualified -references for resources in other namespaces. This is smaller and reads naturally for self-contained -application collections. It is less convenient when prompts and profiles -habitually come from different namespaces, because profile overrides would -repeatedly need qualification. Prefer it if consumers primarily use prompts' -configured defaults and rarely select profiles themselves. - -**Considered alternative, not selected: explicit scope values supplied per -operation.** Add scope to request values and appropriate inspection/listing -options instead of returning a view. This makes every invocation self-contained -and suits applications that -choose a different scope for every request. It spreads scope through more call -sites and API shapes, and consumers would need their own reusable wrapper to -achieve the requested select-once experience. The immutable view is therefore -the better default for the use case discussed so far. - -### 2. Scope Validation (Accepted) - -**Accepted: validate syntax when creating the view; resolve existence when -an operation selects a resource.** In the scope value, the empty -string means no scope. Reject a nonempty namespace containing `/` or consisting -only of whitespace. Otherwise preserve its exact spelling, with no trimming, -case folding, or additional character whitelist. This distinguishes omission -from an unusable namespace without inventing normalization rules. Report invalid -scope construction through PromptKit's existing public error conventions. - -Allow a well-formed namespace even when it has no current members. Its scoped -listing would be empty, and selection of a missing local resource would produce -the ordinary lookup error. Qualified selections would remain usable regardless -of the view's default scope. An empty optional request ID continues to mean -omission under that operation's existing contract; it is never qualified. - -This keeps scope as a naming context and supports uniform application setup -across installations with different library contents. It also avoids treating -successful scope construction as validation of a prompt or execution profile. -The tradeoff is that a misspelled but syntactically valid namespace is detected -only when listed or used, rather than during view construction. - -**Considered alternative, not selected: require each selected namespace to have -resources of its corresponding type when creating the view.** For an immutable -assembled engine, this can catch spelling mistakes immediately and gives startup checks useful -feedback without attempting execution. However, it rejects empty optional -collections and a view intended to use qualified references outside its default -namespace. A namespace having one member also does not establish that the -particular required prompt/profile exists. Prefer this approach only if eager -inventory validation is itself an intended consumer contract; it should not be -an accidental consequence of implementing scope creation. - -### 3. Discovery (Accepted) - -**Accepted: integrated namespace filtering and full-identity results.** -Enumerate the distinct named namespaces present in final effective -prompts, profiles, and backends. Allow filtering that enumeration by resource -type so a prompt picker need not show profile-only namespaces. Provide scoped -listings for the supported resource types, adding a backend inventory surface -as part of this work if needed. Namespace filtering is an explicit option in -the library listing API, not a consumer-side filtering helper. Preserve existing -unfiltered engine calls and their result contracts; the exact Go option shape -remains to be specified. - -| Selection | Listing behavior | -| --- | --- | -| No explicit filter on the engine | All namespaces, including unqualified resources | -| No explicit filter on a scoped view | The selected scope for that resource type; all resources of that type if no scope is selected | -| Explicit named namespace | Resources in exactly that namespace | -| Explicit unqualified-only filter | Resources whose IDs have no namespace | -| Explicit all-namespaces filter | All resources of that type, including unqualified resources | - -An explicit filter replaces the view's default for the current listing; it -does not intersect with that default or mutate the view. A named namespace -filter uses the namespace syntax rules in decision 2. A well-formed namespace -with no matching resources returns an empty listing. Represent default, named, -unqualified-only, and all-namespaces selections distinctly in the API; do not -overload an empty string to mean several of them. - -Return full IDs with optional prompt version labels separately; the full ID -identifies the installed prompt. Do not invent a named namespace such as `default` -for unqualified resources. Namespace enumeration excludes that unnamed group. -Use deterministic ordering consistent -with existing listing contracts. - -Qualified result IDs can be passed unchanged to either scoped or unscoped -operations. Unqualified IDs remain literal unqualified identities: when an -explicit all-namespaces or unqualified-only filter exposes one through a scoped -view, use the original engine or a view without that resource scope to select -it exactly. A listing filter does not change subsequent execution scope. This -preserves the accepted lookup rules without introducing an escape syntax. - -Integrated filtering gives consumers a direct way to discover the desired -resources, while full identities keep logs and subsequent selections -unambiguous. Namespace/local-name display decomposition is separate from -filtering; a public splitting helper is not required by this decision. - -**Considered alternative, not selected: add a richer inventory API returning -full ID, namespace, local name, resource type, and prompt version where -applicable.** Preserve existing list methods and expose the structured form -separately. This is attractive for -Scriptorium-style library browsers: grouping and display require no parsing, -and one inventory operation can cover all resource kinds. It creates another -public representation and overlaps existing listing APIs, so its value should -justify that maintenance. Prefer it if downstream clients routinely need a -combined catalog view rather than a resource picker or namespace filter. In -either approach, the full ID remains the authoritative selection value. - -### 4. Collection Ownership And Built-In Removal (Accepted) - -**Accepted: load collections as application/operator-supplied configuration, -and remove PromptKit's built-in profiles as a breaking change.** Remove the -mandatory OpenRouter and RakestrawHome catalog Go-module dependencies and their -automatic contribution to engine configuration. PromptKit owns the definition -format and execution machinery; collection publishers own their configuration -repositories, and applications/operators select and provision the collections -they use. One or more explicitly selected directories can supply backends, -profiles, and prompts through the existing ConfigKit integration. - -Remove `promptkit-openrouter-standard` and `promptkit-openrouter-auto`, including -their implicit catalog dependencies. Do not replace them with another mandatory -starter bundle. Applications may embed their own defaults using the same -configuration format and loader, then layer operator configuration as desired. -Ordinary application overrides remain valid configuration and need not declare -a collection release of their own. - -This decouples collection updates from PromptKit rebuilds and releases and -avoids imposing provider inventories on every consumer. The tradeoff is that -applications and deployments must supply their required configuration and -manage compatible collection revisions. PromptKit does not gain a collection -installer, automatic downloader, or implicit directory search policy. - -Retain the captured-engine lifecycle: configuration changes take effect when -the application constructs and validates a replacement engine. A change to an -installed directory does not mutate an existing engine's captured definitions. -Distribution as configuration does not make new provider protocols or diagnostic -adapters data-only features; those can still require implementation work. - -Prepare explicit identifier mappings, replacement configuration examples, and -consumer migration instructions. Update the built-in-profile promise in the -release policy and its canonical format documentation when removal lands. -Use a release permitted to break configuration and API contracts under the -current release policy: a minor release before v1, or a major release at/after -v1. Actual catalog mappings, packaging arrangements, and release versions remain -delivery work; independent collection releases need not track future PromptKit -software releases. - -**Superseded alternatives:** retaining catalog Go modules with renamed profiles, -or retaining a small PromptKit-owned starter bundle, would simplify initial -provisioning but preserve some provider-data coupling. The accepted direction -places optional defaults in consuming applications and removes those core -dependencies instead. - -### 5. Collection Metadata And Specification Versioning (Accepted) - -**Accepted 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. - -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. - -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. - -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. - -The [six configuration decisions](configuration-collections-mounted-fragments.md#accepted-configuration-decisions) -are now accepted: optional main declarations, if-present checking without a new -strict-mode option, no global ban on resources named `version`, removal of -request-version machinery, existing ConfigKit inheritance, and flexible source -assembly with a small convenience API. Entry names and layouts are conventions; -consumers retain source and precedence choices. Cross-path inheritance and -stricter header options are deferred, not prerequisites. - -No configuration-policy question remains open from that discussion. Exact API -signatures, metadata representations, dependency release selection, and migration -examples remain [delivery details](configuration-collections-mounted-fragments.md#remaining-delivery-details). -Removal of bundled catalogs and built-in profiles remains accepted.