Prepare the v0.9.0 release
This commit is contained in:
@@ -1,661 +0,0 @@
|
||||
# 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:** Complete
|
||||
|
||||
### 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:** Complete
|
||||
|
||||
### 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:** Complete
|
||||
|
||||
### 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:** Complete
|
||||
|
||||
### 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.
|
||||
Reference in New Issue
Block a user