Files
promptkit/docs/roadmap/implementation.md

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:** Pending
### 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:** Pending
### 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.