Files
promptkit/docs/roadmap/implementation.md

794 lines
45 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Configuration Collections Implementation Plan
Status: ready for implementation; no feature changes are implemented by this plan.
Implement the accepted [feature roadmap](configuration-collections.md). 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](../development.md).
Apply the [agent](../policy/agents.md), [architecture](../policy/architecture.md),
[testing](../policy/testing.md), [documentation](../policy/documentation.md),
[OSS](../policy/oss.md), and relevant [release](../policy/release.md) 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](../adr/0001-configuration-boundary.md): 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:
```go
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
```go
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:
```go
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:
```go
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
```go
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:
```sh
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.