Files
promptkit/docs/roadmap/implementation.md

45 KiB
Raw Blame History

Configuration Collections Implementation Plan

Status: ready for implementation; no feature changes are implemented by this plan.

Implement the accepted feature roadmap. That document owns intent, scope, and selected policy; this document owns API delivery choices and the ordered implementation steps. Each numbered stage is sized as one prompt for a Luna coding agent. Complete stages in order. Foundation and migration stages are parts of one refactor, not separately publishable releases.

Baseline And Working Rules

Reviewed PromptKit baseline: 683689a4f89d0fea6a8e0c6faf5830414adf9c7b. It selects ConfigKit v0.7.1, both /v3 provider catalogs at v3.20261013.0, and the completed smoke-test implementation and remediations. The existing implementation plan described that completed work; Git retains it. This plan replaces it without reopening the smoke-test design.

ConfigKit v0.8.0 resolves to commit 137769f3f18a370c68444242fb5d88e620e307e5 (the annotated tag object is distinct). Its released public contracts in docs/config.md, release notes, and ADRs 0011–0014 supply the required placement and shared version-check behavior. The adjacent checkout is a reference, not a committed build dependency.

Before each stage, inspect status, preserve unrelated work, read the owning implementation/tests, and follow development orientation. Apply the agent, architecture, testing, documentation, OSS, and relevant release policies. Use the repository's code-discovery instructions. Reuse unchanged readings. Update affected GoDoc and canonical contracts as each behavior lands; Stage 17 is a consistency pass, not permission to leave current documentation stale.

Record completion and validation beneath the stage, then commit each completed, nonempty unit with a short plain-English message. These stages change PromptKit; external collection publishing is separate work. Do not push, tag, release, or introduce committed workspace/replacement files. The sprint requires neither publication of a new catalog nor a live provider. Dependency acquisition is setup, separate from offline behavioral validation.

Maintain ADR 0001: ConfigKit owns source processing, composition, inheritance, and header checking through its configured version callback. PromptKit interprets final effective values and their resource contexts. Do not inspect contributions or source history to enforce domain validity, including in tests. Explicit operator inspection remains available.

Preserve execution, retry/repair, credentials, appended messages, capacity, observations, cancellation, and prepared-handle semantics except for the accepted identity/version changes. Introduce no installer, implicit discovery, new provider protocol, cross-collection inheritance resolver, compatibility alias bundle, live reload, or additional release gate.

Delivery Decisions

These names and signatures settle delivery details left by the feature roadmap. Private helper names below are implementation guidance, not public invariants.

Final Configuration And Runtime Identity

Use a typed root document with optional version, root backends, profiles, and prompts, and collections. Each collection body has optional release_version and the same three resource mappings. Root and collection resource maps are keyed by local ID; prompts maps directly to definitions, not another version map. Keep ordinary strict ConfigKit decoding and existing definition rules. Collection bodies are mappings; an empty body is useful.

Represent source-root version, collection release_version, and prompt version with *string in the typed document. Configure Definition.Version.Path = configkit.Keys("version") and SourceVersionModeIfPresent. The checker accepts absence, accepts exact text 1 from a string or strict decimal integer, and rejects explicit null or other texts with a safe ConfigKit diagnostic. ConfigKit owns type checking and non-main header consumption. Do not synthesize a main input or declaration. Collection release and prompt labels are opaque strings: nil means absent/null, and a non-nil empty string remains explicitly declared. Do not trim, order, interpret as SemVer, or require these labels.

Keep the retained ConfigKit result in its authored effective shape. Refactor configuration helpers to receive the effective resource base path rather than reconstructing it from a runtime ID. Visit root and collection mappings with their actual paths; after handoff, index each resource under its full public ID. Do not flatten/re-key the ConfigKit result or make a second load. Presence, normalization records, content-file origins, secret classification, explanations, and diffs must keep the effective paths, including collections/<namespace>.

Create shared private identity helpers under internal/domain. Follow the roadmap's exact grammar: a nonblank unqualified ID has no slash; a qualified reference has exactly one slash and two nonempty components. A configured local key is nonblank and slash-free. A collection namespace is nonblank and slash-free, using the same syntax as a nonempty scope. Preserve spelling, including otherwise permitted whitespace, dots, case, and punctuation. Add no other character whitelist. Never apply this grammar to models, paths, URLs, artifact/input names, session IDs, or descriptive labels.

Validate final configured resource keys and effective PromptKit references. Retain existing existence/readiness timing: for example, profile backend existence is checked as today, but an unselected backend diagnostic default is not made eagerly resolvable. ConfigKit interprets extends as a sibling key, not a PromptKit qualified reference. PromptKit does not validate or reinterpret consumed inheritance directives.

Prompt Selection And Optional Labels

Remove PromptVersion from public/internal RunRequest, RequirementInspectionRequest, and SmokeTestRequest. ValidateRequest, Prepare, and PrepareExecution continue to use RunRequest. Change InspectPrompt to:

func (e *Engine) InspectPrompt(ctx context.Context, promptID string) (*PromptInspection, error)

All prompt repository, compiled-template, and requirement-snapshot lookups use ID alone. Remove ambiguity tracking, version selectors, version ordering, and request assertions. Keep PromptIdentity.ID; change PromptIdentity.Version to *string. Keep output field names PromptVersion, changing their types to *string wherever they describe the selected label: prepared details, run results, prompt/requirement inspection, execution events, and smoke-test targets. The internal definition uses Version *string with equivalent semantics. Each returned mutable pointer is owned by its result, including events and prepared snapshots. JSON omitempty omits nil while preserving a declared empty string. Preserve other existing serialization choices.

The optional label remains part of captured definition metadata and its existing definition-hash projection. Copy the pointer; do not dereference it as a cache or selection key. No cross-release hash-stability promise is introduced. Derive automatic structured-output schema names from the selected prompt ID using the existing sanitization, not from a version-selection token. Keep explicit schema selection and structural validation behavior unchanged.

Immutable Scope On The Existing Engine

type Namespaces struct {
    Prompt  string
    Profile string
    Backend string
}

func (e *Engine) WithNamespaces(namespaces Namespaces) (*Engine, error)

Return another engine facade over the same assembled engine components with immutable scope values. An internal engine view may be a shallow copy containing the same runner, smoke tester, inspection, and inventory collaborators. It does not invoke NewEngine or build another capacity manager. WithNamespaces replaces all three scopes on the new view; empty fields clear those scopes. The receiver is unchanged. Validate syntax, not namespace existence. Use the existing invalid-configuration convention for an invalid engine or scope.

At the internal engine entry boundary, use shared qualification helpers for caller-supplied IDs. Validate supplied syntax; leave omitted optional IDs empty; qualify slashless nonempty IDs for the applicable scope; pass qualified IDs unchanged. The runner continues exact lookup. Do not requalify configured default profiles, backend references, sibling inheritance, or diagnostic defaults.

Apply the same policy to Run, Prepare, PrepareExecution, ValidateRequest, InspectPrompt, InspectProfile, InspectRequirements, and all three selection fields of SmokeTest. ValidateRequest already delegates to preparation; use that route rather than adding another validator. RunPrepared consumes its captured selection without qualification. Preserve its existing runner-ownership check; scope does not introduce another handle owner. InspectConfiguration continues to expose the complete retained configuration, not a filtered/re-keyed view. Results and observer metadata carry full selected identities.

Listing Filters And Inventory

Use one concrete optional filter argument for profile, prompt, and new backend listings:

type NamespaceFilterMode uint8

const (
    NamespaceDefault NamespaceFilterMode = iota
    NamespaceNamed
    NamespaceUnqualified
    NamespaceAll
)

type NamespaceFilter struct {
    Mode NamespaceFilterMode
    Name string // Used only by NamespaceNamed.
}

func (e *Engine) ListProfiles(ctx context.Context, filter ...NamespaceFilter) ([]string, error)
func (e *Engine) ListPrompts(ctx context.Context, filter ...NamespaceFilter) ([]PromptIdentity, error)
func (e *Engine) ListBackends(ctx context.Context, filter ...NamespaceFilter) ([]string, error)

Zero or one filter is accepted; multiple filters or an unknown mode return the ordinary invalid-request error rather than guessing precedence. Omission and a zero filter mean default: use that resource's scope, or all resources when it is unset. Named requires a nonempty valid namespace. Unqualified selects IDs without a slash; All includes every ID. A non-Named mode ignores Name. An explicit filter replaces, not intersects, the view's default for that call. Keep IDs full, sort lexically by full ID, and retain existing context, ownership, and empty-list behavior. Copy optional labels on returned prompt entries. Qualified results can be reused through any view. To select an unqualified ID returned by an explicit All/Unqualified filter, use the original engine or a view without that resource scope; a listing filter never changes execution scope.

Add the following small discovery surfaces:

type ResourceKind string

const (
    ResourcePrompt  ResourceKind = "prompt"
    ResourceProfile ResourceKind = "profile"
    ResourceBackend ResourceKind = "backend"
)

func (e *Engine) ListNamespaces(ctx context.Context, kinds ...ResourceKind) ([]string, error)

type CollectionInfo struct {
    Namespace      string
    ReleaseVersion *string
}

func (e *Engine) ListCollections(ctx context.Context) ([]CollectionInfo, error)

No kinds means the union of all three resource types. Supplied kinds select their union; repeats are harmless and unknown kinds are invalid requests. Namespace enumeration describes resource membership across the captured engine, independent of view scopes. Return distinct named namespaces sorted lexically; exclude the unqualified group. Collection inventory likewise covers the entire captured collections mapping, sorted by namespace, including metadata-only and resource-only entries. It is not filtered by a view and does not include an invented root collection. Both surfaces use the existing listing-style nil-engine and context error conventions and return detached values. They do not inspect historical sources, reopen files, or assert package authenticity.

Capture sorted inventories once at construction. Namespace buckets or sorted ranges may avoid repeated full scans for filtered listings. Prefer a single resource traversal plus sorting and ordinary indexed lookups; no repeated ConfigKit queries, definition deep copies for identity-only listings, global caches, or performance/complexity promises in the public API are needed.

Collection Input Convenience

func CollectionInput(namespace string, source configkit.Source) (configkit.Input, error)

Validate the required namespace syntax and return an input with that source, Destination: configkit.Keys("collections", namespace), and Imports: true. Invalid namespace syntax returns an error matching ErrInvalidConfig. Do not open/read the source or infer a namespace/filename. ConfigKit still owns source validity and loading errors. The caller may change the returned input's imports, filesystem context, or destination, or construct an input directly. This supports host, embedded, and mapping sources with one helper, without introducing another loader or options framework. Directory/Filesystem helpers retain their existing explicit discovery and imports-disabled semantics. ProfileSource and BackendSource retain their root-document shapes and can be mounted with this helper. Direct local-key fragments use explicit ConfigKit destinations that include the resource category.

Migration Choices

Do not mass-copy the provider inventories into PromptKit or create a mandatory replacement bundle. Provide synthetic, complete collection examples and guidance for adapting selected existing catalog configuration. Adjacent repositories and their publication are outside this sprint. The reviewed pinned catalog assets have slash-free profile keys, so these explicit mappings are collision-free within those assets:

Previous identity Documented migration identity
Backend openrouter openrouter/default
Backend rakestrawhome rakestrawhome/default
Each OpenRouter catalog profile p openrouter/p, retaining the exact local key p
Profile rakestrawhome-gemma-4-31b rakestrawhome/rakestrawhome-gemma-4-31b
Built-in promptkit-openrouter-standard No automatic replacement; optional caller profile openrouter/standard
Built-in promptkit-openrouter-auto No automatic replacement; optional caller profile openrouter/auto

These are documented conversions, not installed aliases. Simply mounting old /v3 assets does not update their stored backend/profile references; those references and diagnostic defaults need the explicit edits shown here.

For an optional caller standard profile, extends: gemini-flash-latest is valid only within the same openrouter profiles mapping containing that base. The caller auto profile may explicitly set backend openrouter/default and model openrouter/auto. Neither name becomes a PromptKit promise. Move backend diagnostic defaults to exact new profile IDs. Preserve endpoint/settings and provider model strings, including the pinned Gemini model's leading ~.

Application-specific IDs require a deliberate collision review, not automatic slash-to-dot replacement. For a formerly multi-version prompt, its maintainer chooses the installed definition; examples use the single definition already present. Put its old version string into optional metadata where useful. Do not select a winner from a consumer's multi-version history by guessed ordering. Document exact references, paths, backend capacity identity changes, source-root header reservations, label null/empty semantics, and the loss of cross-mapping inheritance. Migration documentation is not a runtime migration tool.

Stage 1: Adopt The Released ConfigKit Prerequisite

Scope: dependency selection and existing integration compatibility only.

  1. Select ConfigKit v0.8.0 with normal Go module tooling; update go.mod and go.sum without filesystem replacements. Verify the selected release commit and notices; update its attribution entry with the full commit above.
  2. Check affected keyed literals and source-inspection handling for upstream additions. SourceInfo is no longer comparable; compare relevant fields if PromptKit currently relies on comparability. Do not enable new schema behavior.
  3. Leave Go's consumer minimum at 1.26.0 unless the selected dependency actually requires a higher minimum. Leave unrelated dependencies/tooling unchanged.

Validation: GOWORK=off go test ./internal/configuration . and the three offline examples, including configuration inspection/diff coverage. This stage establishes the unchanged PromptKit baseline on the new dependency.

Completion: ConfigKit v0.8.0 is selected at commit 137769f3f18a370c68444242fb5d88e620e307e5; its attribution links and module checksums are updated. The Go consumer minimum remains 1.26.0. No SourceInfo comparability-dependent code required adjustment. Validation passed: GOWORK=off go test ./internal/configuration . and GOWORK=off python3 scripts/check-examples.py.

Stage 2: Add Shared Resource Naming Primitives

Scope: small private domain helpers and focused behavior tests.

  1. Implement exact ID parsing/validation, namespace syntax validation, local-key validation, qualification, and namespace extraction as needed by later stages. Keep omission policy in operation callers, not a universal required-ID rule.
  2. Cover unqualified/dotted names, qualified bypass, missing/empty scope, malformed slash forms, and preserved spelling. Check that namespace matching is exact rather than a prefix match.
  3. Do not wire names into all public operations yet or export a splitting API.

Validation: GOWORK=off go test ./internal/domain. Test selected naming behavior, not a helper call sequence or invented character restrictions.

Completion: private resource-ID, namespace, local-key, qualification, and namespace-extraction helpers are implemented with focused grammar and spelling tests. Validation passed: GOWORK=off go test ./internal/domain and git diff --check.

Stage 3: Make Configuration Processing Path-Aware

Scope: preparation refactor inside internal/configuration; current format stays.

  1. Refactor captureFields, resolveBackends, resolveProfiles, validateEffective, prompt metadata capture, and prompt materialization to accept/use explicit effective base paths. Preserve current values and errors.
  2. Carry a resource's public ID separately from its ConfigKit path. Stop deriving an effective path by splitting a runtime ID. Use ConfigKit path segments for literal keys and preserve inherited content_file origins.
  3. Factor resource-map traversal only as needed to reuse the same processing for root and collection mappings later. Do not add a generic visitor framework.
  4. Prepare the existing provider-secret predicate to recognize actual resource paths without mistaking arbitrary nested extra_params data for resources. Keep its existing protected-key policy.

Validation: configuration load, target, prompt-origin, inheritance, and inspection tests; GOWORK=off go test ./internal/configuration .. A behavior-preserving refactor should not rewrite unrelated expectations.

Completion: configuration capture, normalization, effective validation, and prompt materialization now receive explicit ConfigKit base paths. Existing resource IDs remain independent from those paths; source-origin reads and the protected provider-key policy are preserved. Validation passed: GOWORK=off go test ./internal/configuration ..

Stage 4: Install One Prompt Definition Per ID

Scope: prompt storage format, materialization, and fixture-format migration.

  1. Change root prompts to map[string]promptConfig; add nullable Version *string to the raw prompt definition. Materialize one entry per ID, preserving the label text without normalization or a generated label.
  2. Remove nested version indexes, ambiguity tracking, and selection-by-version from the configuration adapter. Use the actual prompt path without the old version segment for metadata and message resources.
  3. To keep this stage bounded, existing repository method parameters and public selector fields may remain only until Stage 5. At this adapter boundary their temporary implementation delegates to ID-only lookup; do not compare them with labels or create an assertion mode. Existing string-valued output labels are converted to presence-aware values in Stage 6. These are migration seams, not new compatibility behavior to retain or publish.
  4. Mechanically migrate YAML fixtures, mapping builders, and example configuration files to the direct definition shape. Move retained version keys to quoted metadata. Replace tests of parallel versions/ambiguity with one-entry lookup and ordinary mapping-overlay cases. Keep unrelated validation coverage.

Validation: GOWORK=off go test ./..., including consumers of migrated fixtures. Test ID-only selection, source-relative content, and inherited/overlaid prompt settings. Do not add tests preserving temporary obsolete selector parameters.

Completion: prompts now map directly from each ID to one definition, with an optional descriptive version label. Configuration lookup and message/input metadata paths are ID-only; ambiguity handling and version-keyed indexes are removed. Fixtures, examples, and the canonical format reference use the direct shape, including layered overlays. Validation passed: GOWORK=off go test ./..., GOWORK=off python3 scripts/check-examples.py, and git diff --check.

Stage 5: Remove Request-Side Version Selection

Scope: compile-directed removal of obsolete selector plumbing; no new lookup rules.

  1. Remove request PromptVersion fields and the version parameter from public InspectPrompt and internal prompt repository/snapshot/template interfaces. Update runner preparation, inspection, requirements, and smoke selection.
  2. Update public-to-internal conversion, redacted formatting, fakes, tests, examples, and call sites. Remove version-without-prompt checks and the transitional signatures from Stage 4. Preserve ordinary not-found identities.
  3. Use the selected prompt ID for generated structured-schema names; retain the existing sanitization and explicit schema behavior. Do not remove output metadata fields merely because selectors are gone.
  4. Update affected GoDoc now so obsolete selectors are no longer advertised.

Validation: GOWORK=off go test ./... and focused vet of modified packages. Search declarations/call sites for remaining selection/ambiguity logic; descriptive label references are valid and should not be blanket-deleted.

Completion: request-side version selectors and version parameters have been removed from prompt lookup, inspection, requirement analysis, and smoke-test selection. Generated structured-schema names now derive from the prompt ID; result-side descriptive label fields remain. Updated the consumer guide and GoDoc to reflect ID-only selection. Validation passed: GOWORK=off go test ./..., focused GOWORK=off go vet for modified packages, GOWORK=off python3 scripts/check-examples.py, and git diff --check.

Stage 6: Carry Optional Prompt Labels Through Results

Scope: presence-aware metadata across internal values and the facade.

  1. Change internal definition Version and all selected-label output fields identified above to *string. Read the raw optional field captured by Stage 4. Update conversion and ownership copies, hashing projection, requirement snapshots, observation selection/events, prepared details/results, and smoke target projection. Optional labels never become lookup keys.
  2. Distinguish nil from a declared empty string in inspection/listings and JSON. Both omitted and null mean no label; a higher-layer or inherited null clears the label through ordinary ConfigKit composition.
  3. Update the example summary types and test expectations. Add focused boundary coverage for absence, null clearing, empty/exact text, and pointer ownership; reuse existing report/prepared/event detachment tests where practical.

Validation: domain, promptdef, configuration, observation, usecase, and root tests; GOWORK=off go test ./... and the offline examples. Verify the existing selected definition hash still agrees between inspection and preparation.

Completion: prompt labels are presence-aware from configuration capture through inspection, listing, requirements, prepared and completed runs, observation events, and smoke targets. Nil and declared-empty labels remain distinct in JSON; null overlays clear inherited labels. Returned label pointers are independently owned, and inspection/preparation retain matching definition hashes. Validation passed: GOWORK=off go test ./..., GOWORK=off go vet ./..., GOWORK=off python3 scripts/check-examples.py, and git diff --check.

Stage 7: Enable Shared Specification Checking

Scope: root declaration policy through ConfigKit's released version API.

  1. Add optional root version to the typed document. Configure the version rule and SourceVersionModeIfPresent with the exact checker specified above. Keep no-main and headerless sources valid.
  2. Use ConfigKit's header consumption and source evidence without a source pre-parser, validation loop, or PromptKit mode option. Nested prompt labels remain ordinary metadata.
  3. Cover accepted string/integer 1, omitted main/header, rejected null or unsupported declarations, consumed non-main headers, and an explicit main declaration retained in inspection. A headerless imported prompt fragment still undergoes ordinary final-value validation.

Validation: focused real-ConfigKit tests in internal/configuration and public construction/inspection tests. Do not replicate upstream's full version-type or import matrix. Run GOWORK=off go test ./internal/configuration ..

Completion: the optional root declaration is checked through ConfigKit's shared source-version API, accepting only exact string or integer 1 while allowing absence and preserving the main declaration in inspection. Accepted underlay declarations are consumed; null and unsupported values produce safe diagnostics, and headerless payloads still receive final-value validation. Validation passed: GOWORK=off go test ./internal/configuration ..

Stage 8: Assemble And Index Final Collection Resources

Scope: namespaced schema, final resource interpretation, and runtime indexes.

  1. Add collections and collection release_version/resource mappings to the typed document. Visit root and collection maps using Stage 3's effective paths. Validate namespaces/local keys; build full IDs only in runtime indexes.
  2. Include backend/profile snapshots and prompt metadata/templates in that traversal. Keep the retained ConfigKit result structurally unchanged. Preserve the distinction between root unqualified and collection-local keys. Migrate maintained qualified-key fixtures into collection/local-key mappings, updating exact references and inspection paths together. Use ordinary explicit destinations until the convenience helper lands; do not keep a second root spelling for qualified identities.
  3. Validate effective references with the naming helper and existing existence policy. Local extends stays with ConfigKit. Do not reject repeated namespace contributions, require collection releases, or qualify stored references.
  4. Extend secret-path matching to backend/profile extras beneath collections, preserving the existing protected-key list. Use exact effective paths for field presence, normalization, content_file, explanations, and diagnostics.
  5. Preserve backend-ID capacity policy wiring. The same final backend is one capacity identity regardless of the sources that contributed its settings.

Validation: focused real-ConfigKit tests for two collections with duplicate local names, root resources, partial overrides, inherited relative content, explicit zero/null settings, and redacted namespaced provider data. Include retained release metadata, sibling inheritance, headerless collection fragments, resources named version in nested mappings, and header/declaration distinctions. Run GOWORK=off go test ./internal/configuration ..

Completion: final root and collection resources are interpreted from their effective ConfigKit paths and indexed under full runtime IDs without re-keying the retained result. Collection namespaces and local keys are validated; collection release labels, sibling inheritance, inherited relative content, presence/null handling, and namespaced secret redaction are covered. Capacity and profile snapshots use full backend identities. Validation passed: GOWORK=off go test ./..., GOWORK=off go vet ./..., and git diff --check.

Stage 9: Make Application Tests Independent Of Bundled Catalogs

Scope: owned fixtures and examples that currently assume automatic provider data.

  1. Locate tests/examples using bundled backend/profile IDs, inherited catalog settings, default smoke profiles, or baseline listing counts. Supply the explicit synthetic definitions each behavior needs through existing sources.
  2. Keep generic test helpers small and local; no full catalog snapshot or shared test fixture framework. HTTP adapter tests may retain provider adapter names but use controlled transports and synthetic configuration.
  3. Keep catalog-specific assembly tests until Stage 10 removes the integration. Do not preserve provider inventory assertions as application contracts.

Validation: the affected root, engine, and usecase tests plus examples. Ordinary coverage should now express its own backend, profile, and prompt requirements.

Completion: ordinary application tests now provide the backend, profile, and smoke metadata they exercise instead of relying on bundled provider entries. The listing and inheritance contracts use synthetic resources; catalog-specific assembly tests remain isolated for the later integration removal. Maintained examples already supplied their own definitions and needed no changes. Validation passed: GOWORK=off go test ./..., GOWORK=off python3 scripts/check-examples.py, and git diff --check.

Stage 10: Remove Automatic Catalogs And Built-In Profiles

Scope: engine composition, dependency/notices cleanup, and the retired promise.

  1. Remove catalog.Underlays() from internal/engine.New; load only caller underlays and optional main. Preserve their order and strict-group semantics. Empty configuration constructs an engine with empty resource inventories.
  2. Delete internal/catalog, its owned profiles, and integration-specific tests after checking references. Do not delete generic file-discovery helpers.
  3. Remove both catalog module requirements and unused checksums through module tooling. Do not update/publish those repositories or copy their inventories into PromptKit as a replacement.
  4. Update attribution for actual retained material; remove catalog dependency entries when nothing incorporated remains. Keep a notice if other copied material still requires it. Do not strip a shared license text still in use.
  5. Retire the release policy's catalog-update exception and literal built-in-name promise. Apply ordinary compatibility rules to PromptKit format/API changes; independently selected configuration is no longer a bundled release inventory. Update architecture/internal inventory and current format documentation for the removed component. Preserve historical release notes.

Validation: GOWORK=off go test ./...; verify empty inventories, explicit former backend names still work when configured, and inference smoke tests retain their selection/capability behavior with caller-supplied metadata. Review go.mod, go.sum, imports, and notices for residual automatic catalog coupling.

Completion: engine construction now composes only caller-selected underlays and the optional main input; an empty configuration has empty profile and prompt inventories. Removed the catalog adapter and its tests, provider catalog module requirements and checksums, catalog attribution entries, and the built-in profile compatibility exception. Current architecture, source, format, and consumer documentation now describe caller-supplied definitions; historical release notes remain unchanged. Explicit OpenRouter and Rakestrawhome backend IDs continue to work when configured. Validation passed: GOWORK=off go test ./..., GOWORK=off go vet ./..., GOWORK=off python3 scripts/check-examples.py, and git diff --check.

Stage 11: Add The Explicit Collection Input Helper

Scope: the single public convenience function and its GoDoc.

  1. Implement CollectionInput exactly as specified above, reusing namespace validation and existing facade error conventions. It creates an input, not an engine and not a configuration pre-parser.
  2. Keep raw ConfigKit inputs and existing directory/filesystem/programmatic helpers usable. Do not reinterpret their IDs or introduce root discovery.
  3. Add one native-file and one embedded/mapping assembled case with entry imports and a later deliberate override. Verify resource contexts survive.

Validation: source-helper contract tests and focused configuration/root tests. Verify helper construction itself performs no source read using an existing controlled source seam where needed, rather than adding a production interface.

Completion: added CollectionInput with shared namespace validation, a collections/<namespace> destination, and imports enabled without reading the source. Contract tests cover invalid namespaces and deferred source loading; native-file and embedded-filesystem imports assemble successfully, preserve their source context, and accept a later override. Validation passed: GOWORK=off go test ./..., GOWORK=off go vet ., and git diff --check.

Stage 12: Add Immutable Scopes To Ordinary Execution

Scope: public Namespaces/WithNamespaces and shared engine-boundary routing.

  1. Add the public facade and internal immutable view. Replace all scopes on each new view without mutating its receiver. Reuse assembled collaborators.
  2. Route caller prompt/profile selections in Run, Prepare, and PrepareExecution through shared qualification. ValidateRequest follows its current preparation route. Stored defaults and omitted IDs stay untouched.
  3. Preserve resolved full IDs through prepared details, results, events, errors, and selected backend identity. Leave RunPrepared captured and single-claim; do not add a scope mismatch check or rebuild shared state.
  4. Add public behavior tests for two independent prompt/profile scopes, qualified bypass, no fallback, absent well-formed namespaces, and invalid scope syntax. Assert the receiver retains its original behavior.

Validation: root/prepared/usecase tests and focused race-enabled scope tests. Use injected generation or a controlled HTTP boundary; never a live endpoint.

Completion: added immutable WithNamespaces views that share the assembled runner and captured configuration. Ordinary Run, Prepare, PrepareExecution, and the preparation-backed ValidateRequest now qualify slashless caller prompt/profile IDs while preserving qualified IDs, captured defaults, full result/event identities, and prepared-handle ownership behavior. Tests cover independent views, replacement and clearing, no fallback, absent well-formed namespaces, invalid syntax, and concurrent preparation. Validation passed: GOWORK=off go test ./..., focused GOWORK=off go test -race . -run 'TestWithNamespaces' -count=1, GOWORK=off go vet ./..., and git diff --check.

Stage 13: Apply Scopes To Inspection And Diagnostics

Scope: complete the existing operation inventory without a second resolver.

  1. Route InspectPrompt, InspectProfile, and InspectRequirements through the same helpers; resolve each request's explicitly supplied prompt/profile fields independently. Keep configured references exact.
  2. Route SmokeTest's supplied PromptID, ProfileID, and BackendID by their own scopes before ordinary diagnostic selection. Keep its precedence, optional omissions, backend defaults, factual reports, and errors unchanged.
  3. Keep InspectConfiguration/Diff global with effective ConfigKit paths. Do not qualify schema paths, provider models, input identifiers, or overrides.
  4. Review all public resource-selection entry points for omissions. Validate malformed supplied reference syntax through the existing invalid-argument boundary; retain ordinary not-found behavior for well-formed missing IDs.

Validation: representative public inspection/requirements/validation/smoke cases prove shared behavior and full identities. Reuse naming-helper tests instead of duplicating the full syntax matrix in every API test. Run relevant root/engine tests and focused race checks.

Completion: prompt, profile, and requirement inspections now qualify each explicit caller selection independently, and smoke diagnostics qualify supplied prompt, profile, and backend IDs before applying their unchanged selection precedence. Captured references and global configuration inspection remain unchanged. Contract tests verify full returned identities, exact configured prompt defaults and backend references, malformed caller IDs, and ordinary not-found behavior. Validation passed: GOWORK=off go test ./..., focused GOWORK=off go test -race . -run 'TestWithNamespaces' -count=1, GOWORK=off go vet ./..., GOWORK=off python3 scripts/check-examples.py, and git diff --check.

Stage 14: Add Backend Listing And Namespace Filters

Scope: the existing identity-inventory path, not the execution runner.

  1. Add the filter declarations and optional arguments to profile/prompt listing; add backend identity listing through configuration, internal engine, and facade. Existing no-filter engine calls still enumerate everything.
  2. Resolve default/explicit filters once per listing using the relevant view scope. Validate named filters and modes; filter exact namespace membership. Use captured sorted inventories, optionally indexed by namespace.
  3. Return full IDs and detached optional prompt labels, with existing cancellation and empty-result behavior. Do not materialize definitions for identity listing.

Validation: extend listing tests with scoped defaults, explicit named/All/ Unqualified overrides, no-scope defaults, absent namespaces, same-prefix namespace names, full-ID round trips (and unqualified selection through the original engine), invalid filters, ownership, and canceled contexts. Use a compact matrix across resource types, not three copies of all tests. Run configuration/root tests.

Stage 15: Expose Namespace And Effective Collection Inventories

Scope: the two discovery APIs and construction-time inventory capture.

  1. Capture namespace sets by resource kind from final indexed IDs; implement ListNamespaces with the chosen union, kind validation, and global semantics.
  2. Capture CollectionInfo values from final collections declarations. Preserve absent/null versus empty/exact release labels, including metadata-only and resource-only collections. Copy label pointers on output.
  3. Delegate through the internal engine's narrow inventory boundary and root facade. Do not derive collection releases from source header evidence or confuse resource namespace membership with collection declarations.

Validation: one representative inventory fixture covers cross-kind membership, deduplication/sorting, unqualified exclusion, metadata-only collections, scope-independent enumeration, effective overrides, cancellation, and ownership. Run configuration/root tests; no file access or generation should be needed.

Stage 16: Maintain Complete Consumer And Migration Examples

Scope: runnable/synthetic examples and precise migration guidance.

  1. Keep the existing prepare/run/observe examples valid with ID-only selection and optional labels. Extend the preparation example, or its existing input assets, to demonstrate two collections, explicit input selection, a scoped view, filtering, and operator inventory without a provider call. Keep the run/observe examples focused on their distinct existing workflows.
  2. Add small complete collection entry/fragments under examples/, with optional headers/releases, source-relative message content, exact stored references, sibling inheritance, and an intentional later partial override. Use synthetic endpoints/models rather than another maintained provider catalog.
  3. Add docs/consumers/configuration-migration.md explaining the breaking format and signatures, the explicit mapping table above, optional caller standard/auto choices, and deployment selection responsibility. Do not promise arbitrary consumer migrations are automatic or validate upstream provider availability.
  4. Update semantic example checks only for the changed example outputs. If script logic changes, run its focused checks; do not rewrite release tooling.

Validation: GOWORK=off python3 scripts/check-examples.py. Assemble the maintained collection example through real ConfigKit and PromptKit. Keep copied material's actual attribution obligations aligned if any is introduced.

Stage 17: Finalize Canonical Contracts And Migration Documentation

Scope: documentation consistency after the APIs and examples exist.

  1. Update GoDoc for all changed/new public declarations, errors, optional labels, ownership, scopes, and listing behavior. Do not leave obsolete selectors or an implication that a descriptive label controls execution.
  2. Update docs/formats.md for the final schema, specification header policy, source-root reservations, one installed prompt, exact references, local inheritance, and explicit provider configuration. Keep current contracts here, not in the migration guide or roadmap.
  3. Update docs/consumers/pkg-promptkit.md, README quickstart/navigation, docs/internal/sources.md, internal inventory, and affected runner/rendering/ observation documents. Explain unchanged resource contexts and operator-only provenance use. Remove live claims about automatically bundled inventories.
  4. Review architecture, release policy/procedure, development reading guidance, and attribution for obsolete catalog assumptions. Revise only affected local statements; do not rewrite upstream policy language unnecessarily or weaken ordinary compatibility promises. Do not revise historical release notes or immutable ADR decisions.

Validation: review changed Markdown links and example references, check GoDoc against declarations, and run git diff --check. Release notes and version/tag selection belong to the later requested release preparation, not this stage.

Stage 18: Verify Cross-Boundary Behavior And Close The Sprint

Scope: focused integration gaps and final regression validation, not new features.

  1. Trace the feature roadmap's acceptance criteria to focused existing/new tests. Add only missing assembled-boundary coverage. One synthetic fixture can prove independent collection names, scoped execution/inspection, full-ID filtering, exact defaults, optional labels, and inventory without duplicating every assertion at each internal layer.

  2. Use a controlled concurrent model boundary to verify ordinary and differently scoped calls share the same configured backend's capacity. Reuse existing admission/cancellation tests for queue mechanics. Verify prepared selections remain captured when another view is used; no namespace is mutable global state.

  3. Confirm final-value-only validation, source-local headers versus ordinary metadata, retained inherited/overlaid content origins, and redaction under namespaced paths. Final inspection still describes ConfigKit's original effective shape rather than a rewritten runtime index.

  4. Remove all temporary selector signatures/adapters from the migration stages. Review source/tests/docs for old version-key maps, version selection/assertions, automatic catalog contributions, mandatory built-in names, and local module replacements. Historical notes and optional metadata references may remain.

  5. Run the final offline checks once for the resulting content:

    GOWORK=off go test -race ./...
    GOWORK=off go vet ./...
    GOWORK=off python3 scripts/check-examples.py
    git diff --check
    

    Format changed Go files and validate affected Markdown links. Review the full diff/status for unintended files and private data; commit the completed unit. Mark this plan and the feature roadmap complete, leaving retirement to normal release preparation. Do not publish, wait for CI, probe providers, or introduce a catalog-release dependency to finish implementation.

Open Questions

None required to begin implementation. The feature roadmap's policy decisions are settled; the delivery decisions above specify the public shapes and migration path. A newly discovered upstream incompatibility or genuine change of scope should be reported with a recommendation and viable alternative rather than silently changing the accepted configuration boundary or adding restrictions.