662 lines
32 KiB
Markdown
662 lines
32 KiB
Markdown
# External Backend Catalogs Implementation Plan
|
|
|
|
## Purpose
|
|
|
|
This document is the ordered implementation plan for the target state in
|
|
[external-backend-catalogs.md](external-backend-catalogs.md). It is written for
|
|
a coding agent implementing one stage per prompt, in order. The feature
|
|
roadmap owns the intended end state, compatibility policy, migration safety
|
|
requirements, and non-goals; this document owns sequencing and concrete
|
|
implementation decisions.
|
|
|
|
The work spans these three sibling repositories:
|
|
|
|
- `/home/eric/Workspace/promptkit` with module path
|
|
`gitea.maximumdirect.net/eric/promptkit`;
|
|
- `/home/eric/Workspace/promptkit-backend-openrouter` with module path
|
|
`gitea.maximumdirect.net/eric/promptkit-backend-openrouter`; and
|
|
- `/home/eric/Workspace/promptkit-backend-rakestrawhome` with module path
|
|
`gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome`.
|
|
|
|
## Cross-Stage Constraints
|
|
|
|
Apply these constraints throughout every stage:
|
|
|
|
- Read and follow each repository's `AGENTS.md` and contributor documentation
|
|
before changing it. In Promptkit, always follow
|
|
[docs/development.md](../development.md),
|
|
[docs/policy/architecture.md](../policy/architecture.md),
|
|
[docs/policy/documentation.md](../policy/documentation.md), and
|
|
[docs/policy/testing.md](../policy/testing.md).
|
|
- Implement exactly one stage per prompt. Start a stage only after every prior
|
|
stage's completion criteria are satisfied and committed. Preserve unrelated
|
|
work in all three worktrees and commit only the stage's intended files.
|
|
- Keep Promptkit an importable Go library. Do not add a command, service,
|
|
runtime catalog download, global registration, mutable registry, or public
|
|
Promptkit catalog API.
|
|
- Keep `BackendOpenRouter` and `BackendRakestrawHome`, their exact values, and
|
|
all current consumer construction and source-precedence behavior unchanged.
|
|
- Keep the external modules data-only. Their non-test package code may import
|
|
only `embed` and `io/fs`; it must not import Promptkit, read environment
|
|
variables, perform network calls, or expose mutable assets.
|
|
- Use no committed `go.work`, `go.work.sum`, vendor tree, or `replace`
|
|
directive. Temporary local workspaces may be used only for exploratory
|
|
development and must be disabled for acceptance. Every dependency and
|
|
release check must succeed with `GOWORK=off` through ordinary module
|
|
resolution.
|
|
- Keep all default tests deterministic, offline, and credential-free. Remote
|
|
publication and module-resolution checks are release gates, not test-suite
|
|
behavior.
|
|
- Treat copying the Promptkit-maintained catalog assets into the LGPL-3.0
|
|
external repositories as an intentional copyright-holder relicensing
|
|
decision. Preserve source provenance and record that decision in both
|
|
catalog READMEs; do not imply that an ordinary dependency extraction alone
|
|
changes an asset's license.
|
|
- Update the canonical current-state documentation in the same commit that
|
|
introduces or changes the behavior it describes. In particular, do not
|
|
defer an implemented-package inventory or source-boundary update to a later
|
|
stage merely because the feature has not reached runtime cutover.
|
|
- Treat catalog content and compatibility as high-risk data-integrity work.
|
|
Test strict decoding, duplicates, source ownership, immutable copies,
|
|
precedence, and exact pre-extraction parity at their narrowest owners; do
|
|
not repeat the same malformed-input matrix through the public facade.
|
|
- Never include catalog file contents, extra-parameter values, environment
|
|
values, or candidate secret values in new diagnostics. Errors may identify
|
|
the catalog display name and repository-relative asset path.
|
|
- Do not publish or move a tag that already exists. If a planned external
|
|
module tag exists locally or remotely, verify that it identifies the exact
|
|
intended release commit; otherwise stop and report the conflict.
|
|
- Do not create a Promptkit release tag as part of these stages. The external
|
|
modules must be released because Promptkit needs resolvable versions, but
|
|
Promptkit release publication remains governed by
|
|
[docs/release.md](../release.md) after the feature is accepted.
|
|
|
|
## Shared Catalog Asset Contract
|
|
|
|
Stages 2 through 6 must use this exact contract in both external modules and
|
|
Promptkit's private adapter:
|
|
|
|
- The module root package is named `openrouter` or `rakestrawhome`, matching
|
|
the backend. It exports only:
|
|
|
|
```go
|
|
const Root = "catalog"
|
|
func FS() fs.FS
|
|
```
|
|
|
|
`FS` returns the package's embedded filesystem as an `fs.FS`; callers cannot
|
|
replace or mutate the embedded value. Both declarations require accurate
|
|
GoDoc.
|
|
- The embedded tree contains `catalog/backend.json` and one or more profile
|
|
files below `catalog/profiles/`. Directories are allowed below `profiles`;
|
|
every nondirectory entry there must be a regular `.yml` file. No other file,
|
|
symlink, or special entry is part of the embedded tree.
|
|
- `backend.json` is one strict JSON object with these required fields and no
|
|
others:
|
|
|
|
```json
|
|
{
|
|
"schema_version": 1,
|
|
"id": "openrouter",
|
|
"endpoint": "https://openrouter.ai/api/v1",
|
|
"api_key_env": "OPENROUTER_API_KEY",
|
|
"concurrency_limit": 16,
|
|
"queue_capacity": 1024,
|
|
"extra_params": null
|
|
}
|
|
```
|
|
|
|
The Rakestrawhome manifest changes `id` to `rakestrawhome`, `endpoint` to
|
|
`https://inference.ai.rakestrawhome.com/v1`, `api_key_env` to
|
|
`RAKESTRAWHOME_INFERENCE_API_KEY`, and `concurrency_limit` to `4`; it keeps
|
|
`schema_version: 1`, `queue_capacity: 1024`, and `extra_params: null`.
|
|
`extra_params` may be a JSON object in later compatible catalog releases,
|
|
but it remains `null` for the compatibility baseline.
|
|
- Schema version `1` requires an integer version, a nonblank ID, endpoint, and
|
|
environment-variable name, a positive integer concurrency limit, a
|
|
nonnegative integer queue capacity whose sum with the concurrency limit fits
|
|
in `int`, and `extra_params` equal to `null` or an object. Promptkit also
|
|
applies all existing endpoint, environment-name, reserved-field, and bounded
|
|
JSON-value validation.
|
|
- OpenRouter owns every existing built-in profile except
|
|
`rakestrawhome-gemma-4-31b`; Rakestrawhome owns exactly that profile at the
|
|
initial release. Copy the YAML bytes without changing IDs or behavior.
|
|
- Each catalog is self-contained: every `base_profile` must resolve inside the
|
|
same module. A locally resolved profile must select the manifest's backend
|
|
ID. Raw catalog profiles must not provide `endpoint`, `api_key_env`, or a raw
|
|
API key; connection and credential-source metadata comes from the manifest.
|
|
- Strict source decoding and the bounded JSON-value rules are the primary
|
|
secret-safety controls. In addition, recursively reject case-insensitive
|
|
catalog `extra_params` keys named `api_key`, `apikey`, `authorization`,
|
|
`credential`, `credentials`, `password`, `secret`, `token`, or
|
|
`access_token`. `api_key_env` is the sole permitted credential-related
|
|
catalog field. Do not use value-pattern heuristics as a substitute for
|
|
structural validation; retain Promptkit's repository credential scan as a
|
|
separate acceptance check.
|
|
- Both external modules start at `v1.0.0`. The two-function/constant asset
|
|
surface and manifest schema are intentionally stable; normal profile-data
|
|
changes use later semantic versions according to each module's compatibility
|
|
policy.
|
|
|
|
## Stage 1: Freeze The Pre-Extraction Compatibility Baseline
|
|
|
|
**Status:** Complete
|
|
|
|
### Repository
|
|
|
|
`/home/eric/Workspace/promptkit`
|
|
|
|
### Objective
|
|
|
|
Record the complete current built-in catalog before any data is copied or any
|
|
runtime assembly changes. This stage must leave Promptkit using its existing
|
|
hard-coded backend definitions and embedded profile repository.
|
|
|
|
### Implementation
|
|
|
|
1. Add `testdata/builtin-catalog-v1.json` as a reviewable, permanently frozen
|
|
compatibility fixture. Represent both normalized backend definitions and
|
|
every raw built-in profile. Include all fields whose zero, empty, nil, or
|
|
explicit state affects behavior, including `queue_capacity_set`,
|
|
`base_profile`, backend, endpoint, model, execution settings,
|
|
`api_key_env`, `api_key_required`, and `extra_params`. Sort backends by ID
|
|
and profiles by ID so diffs are deterministic.
|
|
2. Add a focused compatibility test under `internal/profile/builtin` that
|
|
reads the fixture, obtains normalized built-ins from
|
|
`internal/backend.Registry`, discovers every current embedded profile, and
|
|
compares complete semantic values. Compare both directions so a missing or
|
|
extra backend/profile fails. Reuse the package's existing asset discovery
|
|
and profile repository rather than adding production enumeration APIs in
|
|
this stage.
|
|
3. Do not provide an automatic golden-file update path. The snapshot describes
|
|
the migration baseline and must change only by deliberate review of the
|
|
fixture itself.
|
|
|
|
### Tests And Validation
|
|
|
|
1. Run the focused `internal/backend` and `internal/profile/builtin` tests.
|
|
2. Run `go test ./...` and `go test -race ./...`.
|
|
3. Inspect the fixture diff against the current YAML and backend constants,
|
|
confirm that it contains no credential value, and run the Promptkit
|
|
credential scan from the development guide.
|
|
|
|
### Completion Criteria
|
|
|
|
- One deterministic fixture accounts for both current backends and every
|
|
current raw built-in profile.
|
|
- The fixture test fails for added, removed, or semantically changed catalog
|
|
data.
|
|
- Promptkit runtime construction and production code are unchanged.
|
|
|
|
## Stage 2: Build And Release The OpenRouter Catalog Module
|
|
|
|
**Status:** Complete
|
|
|
|
### Repository
|
|
|
|
`/home/eric/Workspace/promptkit-backend-openrouter`
|
|
|
|
### Objective
|
|
|
|
Create the independently testable OpenRouter data module, publish its source
|
|
commit, and release immutable tag `v1.0.0` before Promptkit depends on it.
|
|
|
|
### Implementation
|
|
|
|
1. Initialize `go.mod` with module path
|
|
`gitea.maximumdirect.net/eric/promptkit-backend-openrouter` and Go version
|
|
`1.25.5`. Name the root package `openrouter`.
|
|
2. Add `catalog/backend.json` using the exact OpenRouter manifest from the
|
|
shared contract. Copy every current OpenRouter-owned YAML asset from
|
|
`promptkit/internal/profile/builtin/assets/` into
|
|
`catalog/profiles/<provider>/` without editing its bytes. Record in the
|
|
README that the Promptkit-maintained source assets are intentionally being
|
|
distributed under this repository's LGPL-3.0 terms with authorization from
|
|
their copyright holder, and identify Promptkit as their source provenance.
|
|
3. Add the private embedded filesystem and the exact `Root`/`FS` public
|
|
surface. Embed only `catalog`, return the embedded filesystem by value
|
|
behind `fs.FS`, and add package GoDoc explaining that the module supplies
|
|
immutable Promptkit catalog assets rather than runtime provider behavior.
|
|
4. Replace the placeholder README with concise ownership, consumption,
|
|
compatibility, and validation guidance. State that Promptkit owns parsing,
|
|
runtime behavior, credentials, and consumer documentation; catalog releases
|
|
own OpenRouter manifest/profile data. Document the roadmap's ID stability,
|
|
additive profile, correction, deprecation/removal, and manifest-schema
|
|
compatibility policy.
|
|
5. Add `docs/release.md` with a source-only semantic-tag procedure. Require a
|
|
clean synchronized `main`, no workspace/replacement/vendor tree, complete
|
|
validation, an annotated tag, publication of only the selected tag, remote
|
|
tag verification, and resolution from a temporary module with `GOWORK=off`.
|
|
6. Add focused tests that use the exported `FS` and `Root` and verify the exact
|
|
embedded layout, strict manifest shape and owner ID, at least one profile,
|
|
unique trimmed profile IDs, backend selection equal to `openrouter`, no
|
|
connection fields or raw API key in profiles, and no forbidden secret key
|
|
in nested extra parameters. A test-only `gopkg.in/yaml.v3` dependency is
|
|
permitted for robust YAML-node inspection; confirm that the non-test root
|
|
package's dependency graph remains standard-library-only.
|
|
7. Run `go mod tidy`, commit the module with a short plain-English message,
|
|
push `main`, create annotated tag `v1.0.0`, push only that tag, and perform
|
|
the documented remote and temporary-module resolution checks. Never use a
|
|
local replacement to satisfy the resolution check.
|
|
|
|
### Tests And Validation
|
|
|
|
- Run `go test ./...`, `go test -race ./...`, `go vet ./...`, and
|
|
`go build ./...` with `GOWORK=off`.
|
|
- Require `gofmt -l` to report no tracked Go files and run `git diff --check`
|
|
before committing.
|
|
- Confirm `GOWORK=off go list -deps .` contains no non-standard-library
|
|
runtime package and scan tracked content for credentials.
|
|
- After publication, resolve
|
|
`gitea.maximumdirect.net/eric/promptkit-backend-openrouter@v1.0.0` from a
|
|
temporary module and verify the returned version.
|
|
|
|
### Completion Criteria
|
|
|
|
- The OpenRouter module contains its manifest and the complete copied profile
|
|
set behind the exact immutable asset API.
|
|
- Its validation is offline and its runtime package is standard-library-only.
|
|
- `main` and annotated tag `v1.0.0` are published and independently resolvable.
|
|
|
|
## Stage 3: Build And Release The Rakestrawhome Catalog Module
|
|
|
|
**Status:** Complete
|
|
|
|
### Repository
|
|
|
|
`/home/eric/Workspace/promptkit-backend-rakestrawhome`
|
|
|
|
### Objective
|
|
|
|
Create and publish the matching Rakestrawhome data module without coupling its
|
|
release history or package implementation to the OpenRouter module.
|
|
|
|
### Implementation
|
|
|
|
1. Repeat the module, package, immutable asset API, package documentation,
|
|
README policy, and `docs/release.md` structure from Stage 2, using module
|
|
path `gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome`, package
|
|
name `rakestrawhome`, and Go version `1.25.5`.
|
|
2. Add the exact Rakestrawhome manifest from the shared contract. Copy only
|
|
`google/rakestrawhome-gemma-4-31b.yml` into
|
|
`catalog/profiles/google/`, without editing its bytes. Apply the same
|
|
explicit LGPL-3.0 relicensing and Promptkit source-provenance statement as
|
|
Stage 2.
|
|
3. Apply the same focused asset tests, changing the expected backend to
|
|
`rakestrawhome` and the initial profile set to the single owned profile.
|
|
Keep the module independent: do not import or share code with either
|
|
Promptkit or the OpenRouter module.
|
|
4. Run the same validation and release sequence as Stage 2, commit and push
|
|
`main`, publish annotated tag `v1.0.0`, and verify ordinary module
|
|
resolution without a workspace or replacement.
|
|
|
|
### Tests And Validation
|
|
|
|
Use the complete Stage 2 validation list with the Rakestrawhome module path.
|
|
Inspect the copied profile against Promptkit's Stage 1 fixture before release.
|
|
|
|
### Completion Criteria
|
|
|
|
- The Rakestrawhome module contains exactly its owned backend and profile data
|
|
behind the same stable asset contract.
|
|
- Its tests, build, runtime dependency check, credential scan, and remote
|
|
resolution all pass.
|
|
- `main` and annotated tag `v1.0.0` are published and independently resolvable.
|
|
|
|
## Stage 4: Add Eager Immutable Profile Loading To Promptkit
|
|
|
|
**Status:** Pending
|
|
|
|
### Repository
|
|
|
|
`/home/eric/Workspace/promptkit`
|
|
|
|
### Objective
|
|
|
|
Add the reusable profile-package primitive needed to validate an immutable
|
|
external catalog completely at engine construction, without changing the lazy
|
|
point-in-time semantics of consumer-configured profile sources.
|
|
|
|
### Implementation
|
|
|
|
1. In `internal/profile`, add:
|
|
|
|
```go
|
|
type LoadedProfileMetadata struct {
|
|
ID string
|
|
Path string
|
|
ExplicitFields []string
|
|
}
|
|
|
|
func LoadFSRepository(
|
|
ctx context.Context,
|
|
fsys fs.FS,
|
|
root string,
|
|
) (Repository, []LoadedProfileMetadata, error)
|
|
```
|
|
|
|
It must discover sorted YAML paths through `internal/filecatalog`, read each
|
|
file once, require exactly one document, strictly decode the existing
|
|
profile schema, reject raw API keys, normalize and validate each raw
|
|
definition through the existing owners, and reject duplicate trimmed IDs.
|
|
Return an immutable in-memory raw repository plus a newly allocated metadata
|
|
slice sorted by normalized profile ID. Each metadata entry contains that ID,
|
|
the safe root-relative source path, and a newly allocated sorted list of the
|
|
exact top-level YAML field names present in the source document. An empty
|
|
source returns an empty repository and metadata slice; the catalog adapter,
|
|
not this generic primitive, decides whether emptiness is invalid. All three
|
|
fields, the type, and the function are internal to the Promptkit module but
|
|
require accurate GoDoc because they cross internal package boundaries.
|
|
2. Refactor existing private decode/metadata logic only as needed so eager and
|
|
point lookup share strict decoding, source-path context, raw-key rejection,
|
|
normalization, and defensive JSON-value copying. Do not change
|
|
`NewFSRepository`: configured and fallback consumer sources must retain
|
|
fresh point-in-time reads and their current error-preserving fallback
|
|
semantics. Derive `ExplicitFields` during the same source read and through
|
|
the existing YAML-node metadata path; do not make the later catalog adapter
|
|
reread or independently parse profile YAML.
|
|
3. The returned repository must honor context cancellation before lookup,
|
|
return `ErrProfileNotFound` for absence, and publish a fresh profile value
|
|
with a deeply copied `ExtraParams` tree on every successful lookup. Do not
|
|
pre-resolve `base_profile`; the raw repository must remain suitable for the
|
|
existing outer resolving repository and consumer shadowing rules.
|
|
4. Update `docs/internal/sources.md` in the same commit to describe the eager
|
|
immutable loader as an implemented internal profile boundary. Make clear
|
|
that configured consumer sources remain lazy and that engine assembly still
|
|
uses the existing embedded built-ins at this stage.
|
|
|
|
### Tests And Validation
|
|
|
|
1. Add focused profile-package tests for sorted metadata, exact explicit-field
|
|
presence including explicitly empty values, safe relative paths, strict
|
|
malformed-input rejection, duplicate IDs, raw API-key rejection, empty
|
|
input, cancellation, and defensive copies of metadata and profile values.
|
|
Reuse representative existing fixtures and avoid repeating the entire
|
|
profile rule matrix already owned by point lookup.
|
|
2. Add a parity test showing that eager and ordinary FS repositories publish
|
|
the same raw semantic value for representative standalone and derived
|
|
profiles.
|
|
3. Run `go test ./internal/profile`, `go test -race ./internal/profile`, and
|
|
`go test ./...`.
|
|
|
|
### Completion Criteria
|
|
|
|
- Promptkit can eagerly load and validate all raw profiles from an `fs.FS`
|
|
without creating a second YAML contract implementation.
|
|
- Returned metadata and profile values are caller-independent, and the
|
|
metadata preserves the distinction between an absent field and an explicitly
|
|
empty field.
|
|
- Existing configured, fallback, in-memory, and built-in runtime behavior is
|
|
unchanged.
|
|
- The internal source document accurately distinguishes eager immutable loads
|
|
from existing point-in-time consumer source lookup.
|
|
|
|
## Stage 5: Add And Verify The Private Catalog Adapter
|
|
|
|
**Status:** Pending
|
|
|
|
### Repository
|
|
|
|
`/home/eric/Workspace/promptkit`
|
|
|
|
### Objective
|
|
|
|
Import the two published module versions and validate them through one private
|
|
Promptkit adapter while retaining the existing embedded/hard-coded runtime
|
|
source as the active implementation.
|
|
|
|
### Implementation
|
|
|
|
1. Add direct requirements at `v1.0.0` for both external module paths and run
|
|
`go mod tidy` with `GOWORK=off`. Do not add a replacement or workspace.
|
|
2. Add `internal/catalog` with this internal contract:
|
|
|
|
```go
|
|
type Source struct {
|
|
Name string
|
|
ExpectedBackendID string
|
|
FS fs.FS
|
|
Root string
|
|
}
|
|
|
|
type Set struct {
|
|
Backends []domain.Backend
|
|
Profiles profile.Repository
|
|
}
|
|
|
|
func Load(sources ...Source) (Set, error)
|
|
```
|
|
|
|
`Name` is a safe maintainer-facing label used in errors; reject blank or
|
|
duplicate names. Require nonnil filesystems, valid non-root asset roots,
|
|
nonblank expected IDs, and at least one source. Return caller-independent
|
|
backend values and one raw immutable composite profile repository.
|
|
3. Strictly enforce the shared asset layout and manifest schema. Use
|
|
`json.Decoder.DisallowUnknownFields`, require exactly one JSON value, use
|
|
presence-aware raw fields so missing required fields differ from zero or
|
|
`null`, reject unsupported schema versions, and check the manifest ID
|
|
against `ExpectedBackendID`. When constructing `domain.Backend`, set
|
|
`QueueCapacitySet` to `true` because schema version 1 requires an explicit
|
|
`queue_capacity`; this ensures normalization preserves later compatible
|
|
releases that intentionally select a non-default capacity.
|
|
4. Rename the existing private backend normalizer to the internal exported
|
|
`backend.NormalizeDefinition` and have both `Registry` and the catalog
|
|
adapter call it. This remains inside Go's `internal` boundary and is not a
|
|
Promptkit public API. Do not duplicate endpoint, environment-name,
|
|
capacity, reserved-field, or bounded JSON-value policy in the adapter.
|
|
5. For each source, call `profile.LoadFSRepository` on `<root>/profiles`.
|
|
Reject an empty profile set. Use the returned explicit-field metadata to
|
|
reject `endpoint` or `api_key_env` whenever the key is present, including
|
|
when its YAML value is explicitly empty; do not infer source presence from
|
|
the decoded profile's zero values. Validate every raw profile's nested extra
|
|
parameters for forbidden secret keys. Resolve every metadata ID through a
|
|
source-local `profile.NewResolvingRepository`; this both proves inheritance
|
|
is self-contained and verifies that the final backend ID equals the manifest
|
|
owner.
|
|
6. Reject duplicate backend IDs and duplicate raw profile IDs across sources.
|
|
Compose the already validated raw repositories in source order only after
|
|
duplicate checks pass. Do not pre-resolve the returned composite: the root
|
|
engine must later place consumer sources above it and apply one outer
|
|
resolver to preserve base-profile shadowing semantics.
|
|
7. Keep errors bounded and redacted. Wrap failures with the safe source name
|
|
and relative path where available, but never include raw JSON/YAML values or
|
|
extra-parameter content.
|
|
8. Add an integration test that imports the released `openrouter` and
|
|
`rakestrawhome` packages, supplies their `FS()`/`Root` values with expected
|
|
backend IDs, loads the set, and compares it exactly with
|
|
`testdata/builtin-catalog-v1.json`. Keep the Stage 1 test against the old
|
|
source too; at this point both tests must pass while only the old source
|
|
participates in engine construction.
|
|
9. Update `docs/policy/architecture.md`, `docs/internal/overview.md`, and
|
|
`docs/internal/sources.md` in the same commit. Describe `internal/catalog`
|
|
as an implemented private validation/adapter boundary and the external
|
|
modules as imported immutable test-verified sources, while stating
|
|
accurately that root engine assembly still uses the original built-in
|
|
runtime source until cutover.
|
|
|
|
### Tests And Validation
|
|
|
|
1. At `internal/catalog`, use `testing/fstest.MapFS` tables for nil/invalid
|
|
sources, layout violations, strict/trailing/missing/unsupported manifests,
|
|
backend normalization failures, empty profiles, malformed profiles,
|
|
cross-source duplicates, missing/cyclic/cross-catalog bases, owner mismatch,
|
|
prohibited connection fields, secret-like nested keys, defensive copies,
|
|
and redacted source-aware errors.
|
|
2. Keep exhaustive YAML rules in `internal/profile` and backend invariants in
|
|
`internal/backend`; adapter tests must prove delegation and assembly, not
|
|
duplicate those packages' full matrices.
|
|
3. Run focused tests for `internal/profile`, `internal/backend`, and
|
|
`internal/catalog`, followed by `go test ./...`, `go test -race ./...`,
|
|
`go vet ./...`, and `go build ./...`.
|
|
4. With `GOWORK=off`, verify `go list -m all` selects exactly `v1.0.0` for both
|
|
catalogs and `go mod verify` succeeds.
|
|
|
|
### Completion Criteria
|
|
|
|
- Both released external catalogs load through one strict private adapter and
|
|
exactly match the frozen baseline.
|
|
- Every maintained profile is eagerly validated locally and across the
|
|
assembled set before publication.
|
|
- Promptkit still executes exclusively from its original built-in data source.
|
|
- The current-state architecture and internal inventory describe this
|
|
transitional implemented boundary without claiming that cutover is complete.
|
|
|
|
## Stage 6: Cut Promptkit Over To The External Catalogs
|
|
|
|
**Status:** Pending
|
|
|
|
### Repository
|
|
|
|
`/home/eric/Workspace/promptkit`
|
|
|
|
### Objective
|
|
|
|
Make the validated external modules Promptkit's sole runtime owners for the
|
|
maintained backends and profiles, then remove every duplicate production copy
|
|
from Promptkit in the same committed cutover.
|
|
|
|
### Implementation
|
|
|
|
1. In root engine assembly, import the two external packages with unambiguous
|
|
aliases and construct `catalog.Source` values in deterministic OpenRouter,
|
|
Rakestrawhome order. Use safe display names plus
|
|
`backend.OpenRouterID`/`backend.RakestrawHomeID` as the expected IDs.
|
|
2. Load the maintained `catalog.Set` during `NewEngine` after options and the
|
|
required prompt source have been validated, but before constructing the
|
|
backend registry, capacity manager, or runner. Map any load failure to
|
|
`ErrInvalidConfig` with the prefix `failed to load maintained catalogs` and
|
|
preserve redacted catalog/path context. Add no new public error identity.
|
|
3. Change `backend.NewRegistry` to accept maintained definitions and consumer
|
|
additions as separate slices. Normalize and copy both through the same
|
|
path, insert maintained definitions first, and reject every duplicate
|
|
across or within the two groups. This preserves the rule that a consumer
|
|
cannot replace a maintained ID without retaining hard-coded maintained
|
|
definitions inside the registry package.
|
|
4. Change `newProfileRepository` to accept the maintained raw repository as
|
|
its lowest-precedence source. Preserve this exact overlay order: in-memory,
|
|
ordinary configured file/FS/directory, application fallback, maintained
|
|
external catalog. Continue to wrap the complete raw overlay in exactly one
|
|
`profile.NewResolvingRepository`.
|
|
5. Delete `internal/profile/builtin`, including its embedded YAML assets, and
|
|
delete `builtInBackends` plus the OpenRouter/Rakestrawhome endpoint,
|
|
environment, and concurrency constants from `internal/backend`. Retain the
|
|
internal backend ID constants because the root public constants still
|
|
alias them, and retain the generic default queue-capacity policy used for
|
|
consumer registrations.
|
|
6. Move the Stage 1 compatibility assertion to `internal/catalog` and make it
|
|
compare only the external loaded set to the frozen fixture. Remove tests
|
|
whose sole purpose was the deleted duplicate source; retain or relocate
|
|
distinct compatibility, reserved-ID, profile completeness, and native
|
|
Rakestrawhome behavior coverage.
|
|
7. Update registry and engine tests for explicit maintained definitions. Add
|
|
only focused assembled-engine coverage needed to prove that ordinary
|
|
construction includes both catalogs, consumer backend IDs cannot replace
|
|
either maintained ID, consumer profiles still override catalog profiles,
|
|
inherited base lookup still observes the complete precedence chain, and
|
|
capacity/credential/endpoint behavior matches the compatibility fixture.
|
|
Reuse existing public contract tests wherever they already protect these
|
|
outcomes.
|
|
8. Confirm with repository search and `go list -deps` that no production Go
|
|
file embeds the old assets or hard-codes the extracted endpoints,
|
|
environment-variable names, concurrency values, or profile model catalog.
|
|
The frozen test fixture and canonical consumer documentation are the only
|
|
permitted Promptkit copies of compatibility data.
|
|
9. Update `docs/policy/architecture.md`, `docs/internal/overview.md`, and
|
|
`docs/internal/sources.md` in the cutover commit: remove the transitional
|
|
old-runtime description, remove `internal/profile/builtin`, and describe
|
|
explicit root assembly plus eager maintained-catalog validation and the
|
|
unchanged outer consumer overlay/resolution boundary.
|
|
10. Update `docs/formats.md` where its current wording says the catalog is
|
|
embedded or hard-coded. It remains the canonical consumer owner of the
|
|
built-ins supplied by a Promptkit release, so retain the exact backend and
|
|
profile tables after confirming them against the compatibility fixture.
|
|
Do not duplicate pinned module versions there; `go.mod` owns them.
|
|
|
|
### Tests And Validation
|
|
|
|
- Run focused backend, catalog, profile, root engine, capacity, and public
|
|
contract tests.
|
|
- Run the complete Promptkit maintainer workflow from
|
|
[docs/development.md](../development.md#maintainer-validation), including
|
|
ordinary/race tests, vet, build, both offline examples, Go formatting,
|
|
Markdown links, workspace/replacement/vendor guards, whitespace checks,
|
|
ignored-file review, credential scan, and full diff/status inspection.
|
|
- Run the complete workflow with `GOWORK=off` and no provider credentials.
|
|
|
|
### Completion Criteria
|
|
|
|
- Every new engine validates and uses both external catalogs automatically.
|
|
- Public constants, effective backend/profile values, source precedence,
|
|
inheritance, capacity, credentials, and consumer additions match the frozen
|
|
baseline and existing contracts.
|
|
- Promptkit has one runtime data owner: no embedded profile catalog or
|
|
hard-coded extracted backend definition remains.
|
|
|
|
## Stage 7: Update Current-State And Release Documentation
|
|
|
|
**Status:** Pending
|
|
|
|
### Repository
|
|
|
|
`/home/eric/Workspace/promptkit`
|
|
|
|
### Objective
|
|
|
|
Make durable documentation describe the implemented dependency boundary,
|
|
provide concise consumer-facing release guidance, and perform final acceptance
|
|
across all three clean repositories.
|
|
|
|
### Implementation
|
|
|
|
1. Review the current-state changes made in Stages 4 through 6 against the
|
|
final implementation. Correct any stale transitional language and ensure
|
|
`docs/policy/architecture.md`, `docs/internal/overview.md`,
|
|
`docs/internal/sources.md`, and `docs/formats.md` link to canonical owners
|
|
instead of duplicating the manifest or complete catalog.
|
|
2. Update `docs/development.md` with a task-specific reading-guide row for
|
|
external catalog or maintained built-in changes. Route contributors to the
|
|
internal source document, the format reference, testing policy, both module
|
|
repositories, and each module's release procedure.
|
|
3. Do not add a versionless supplemental release document during feature
|
|
implementation. Record for the later Promptkit release-preparation pass that
|
|
its versioned `docs/releases/vMAJOR.MINOR.PATCH.md` document should state
|
|
that the release adds two independently versioned data dependencies,
|
|
preserves the public API and configuration, requires no consumer migration,
|
|
and guarantees only the catalog versions selected and tested by that
|
|
Promptkit release. That document must link to canonical current-state
|
|
documentation rather than restating its contracts.
|
|
4. Update each external repository README only if the final implemented paths
|
|
or links changed during integration. Do not turn either README into a
|
|
parallel Promptkit consumer manual.
|
|
5. Mark every stage in this plan complete only after its committed state and
|
|
validation evidence exist. Leave Promptkit release tagging to the normal
|
|
release procedure; the future annotated Promptkit tag message must include
|
|
the dependency-boundary summary and the absence of consumer migration.
|
|
|
|
### Tests And Validation
|
|
|
|
1. Run the full Promptkit maintainer workflow, including the local Markdown
|
|
link validator, with `GOWORK=off`.
|
|
2. In each external module, run its full test/race/vet/build, formatting,
|
|
hygiene, credential, and ordinary module-resolution checks.
|
|
3. From a temporary module, download both external `v1.0.0` versions and the
|
|
current Promptkit commit's module dependencies without a workspace or
|
|
replacement. Promptkit itself need not be tagged in this stage.
|
|
4. Inspect all three worktrees and their committed diffs. Require each to be
|
|
clean and confirm that Promptkit's `go.mod`/`go.sum` identify the published
|
|
catalog versions.
|
|
|
|
### Completion Criteria
|
|
|
|
- Durable current-state documents accurately describe the external asset
|
|
boundary without duplicating implementation-plan detail.
|
|
- The later versioned release-document requirements are explicit without
|
|
creating a release note before a Promptkit version has been selected.
|
|
- All completion criteria in the feature roadmap hold, all three repositories
|
|
are clean, and the complete offline validation passes without credentials,
|
|
a workspace, a replacement, or provider network access.
|
|
|
|
## Open Questions
|
|
|
|
None. The implementation decisions required by this roadmap are fixed above.
|