731 lines
41 KiB
Markdown
731 lines
41 KiB
Markdown
# 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 .`.
|
||
|
||
## 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 .`.
|
||
|
||
## 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.
|
||
|
||
## 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.
|
||
|
||
## 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.
|
||
|
||
## 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.
|
||
|
||
## 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.
|
||
|
||
## 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.
|