32 KiB
External Backend Catalogs Implementation Plan
Purpose
This document is the ordered implementation plan for the target state in 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/promptkitwith module pathgitea.maximumdirect.net/eric/promptkit;/home/eric/Workspace/promptkit-backend-openrouterwith module pathgitea.maximumdirect.net/eric/promptkit-backend-openrouter; and/home/eric/Workspace/promptkit-backend-rakestrawhomewith module pathgitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome.
Cross-Stage Constraints
Apply these constraints throughout every stage:
- Read and follow each repository's
AGENTS.mdand contributor documentation before changing it. In Promptkit, always follow docs/development.md, docs/policy/architecture.md, docs/policy/documentation.md, and docs/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
BackendOpenRouterandBackendRakestrawHome, 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
embedandio/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, orreplacedirective. Temporary local workspaces may be used only for exploratory development and must be disabled for acceptance. Every dependency and release check must succeed withGOWORK=offthrough 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 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
openrouterorrakestrawhome, matching the backend. It exports only:const Root = "catalog" func FS() fs.FSFSreturns the package's embedded filesystem as anfs.FS; callers cannot replace or mutate the embedded value. Both declarations require accurate GoDoc. -
The embedded tree contains
catalog/backend.jsonand one or more profile files belowcatalog/profiles/. Directories are allowed belowprofiles; every nondirectory entry there must be a regular.ymlfile. No other file, symlink, or special entry is part of the embedded tree. -
backend.jsonis one strict JSON object with these required fields and no others:{ "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
idtorakestrawhome,endpointtohttps://inference.ai.rakestrawhome.com/v1,api_key_envtoRAKESTRAWHOME_INFERENCE_API_KEY, andconcurrency_limitto4; it keepsschema_version: 1,queue_capacity: 1024, andextra_params: null.extra_paramsmay be a JSON object in later compatible catalog releases, but it remainsnullfor the compatibility baseline. -
Schema version
1requires 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 inint, andextra_paramsequal tonullor 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_profilemust resolve inside the same module. A locally resolved profile must select the manifest's backend ID. Raw catalog profiles must not provideendpoint,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_paramskeys namedapi_key,apikey,authorization,credential,credentials,password,secret,token, oraccess_token.api_key_envis 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
- Add
testdata/builtin-catalog-v1.jsonas 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, includingqueue_capacity_set,base_profile, backend, endpoint, model, execution settings,api_key_env,api_key_required, andextra_params. Sort backends by ID and profiles by ID so diffs are deterministic. - Add a focused compatibility test under
internal/profile/builtinthat reads the fixture, obtains normalized built-ins frominternal/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. - 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
- Run the focused
internal/backendandinternal/profile/builtintests. - Run
go test ./...andgo test -race ./.... - 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
- Initialize
go.modwith module pathgitea.maximumdirect.net/eric/promptkit-backend-openrouterand Go version1.25.5. Name the root packageopenrouter. - Add
catalog/backend.jsonusing the exact OpenRouter manifest from the shared contract. Copy every current OpenRouter-owned YAML asset frompromptkit/internal/profile/builtin/assets/intocatalog/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. - Add the private embedded filesystem and the exact
Root/FSpublic surface. Embed onlycatalog, return the embedded filesystem by value behindfs.FS, and add package GoDoc explaining that the module supplies immutable Promptkit catalog assets rather than runtime provider behavior. - 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.
- Add
docs/release.mdwith a source-only semantic-tag procedure. Require a clean synchronizedmain, 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 withGOWORK=off. - Add focused tests that use the exported
FSandRootand verify the exact embedded layout, strict manifest shape and owner ID, at least one profile, unique trimmed profile IDs, backend selection equal toopenrouter, no connection fields or raw API key in profiles, and no forbidden secret key in nested extra parameters. A test-onlygopkg.in/yaml.v3dependency is permitted for robust YAML-node inspection; confirm that the non-test root package's dependency graph remains standard-library-only. - Run
go mod tidy, commit the module with a short plain-English message, pushmain, create annotated tagv1.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 ./..., andgo build ./...withGOWORK=off. - Require
gofmt -lto report no tracked Go files and rungit diff --checkbefore 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.0from 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.
mainand annotated tagv1.0.0are 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
- Repeat the module, package, immutable asset API, package documentation,
README policy, and
docs/release.mdstructure from Stage 2, using module pathgitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome, package namerakestrawhome, and Go version1.25.5. - Add the exact Rakestrawhome manifest from the shared contract. Copy only
google/rakestrawhome-gemma-4-31b.ymlintocatalog/profiles/google/, without editing its bytes. Apply the same explicit LGPL-3.0 relicensing and Promptkit source-provenance statement as Stage 2. - Apply the same focused asset tests, changing the expected backend to
rakestrawhomeand 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. - Run the same validation and release sequence as Stage 2, commit and push
main, publish annotated tagv1.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.
mainand annotated tagv1.0.0are 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
-
In
internal/profile, add: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. -
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. DeriveExplicitFieldsduring 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. -
The returned repository must honor context cancellation before lookup, return
ErrProfileNotFoundfor absence, and publish a fresh profile value with a deeply copiedExtraParamstree on every successful lookup. Do not pre-resolvebase_profile; the raw repository must remain suitable for the existing outer resolving repository and consumer shadowing rules. -
Update
docs/internal/sources.mdin 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
- 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.
- Add a parity test showing that eager and ordinary FS repositories publish the same raw semantic value for representative standalone and derived profiles.
- Run
go test ./internal/profile,go test -race ./internal/profile, andgo test ./....
Completion Criteria
- Promptkit can eagerly load and validate all raw profiles from an
fs.FSwithout 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
-
Add direct requirements at
v1.0.0for both external module paths and rungo mod tidywithGOWORK=off. Do not add a replacement or workspace. -
Add
internal/catalogwith this internal contract: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)Nameis 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. -
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 ornull, reject unsupported schema versions, and check the manifest ID againstExpectedBackendID. When constructingdomain.Backend, setQueueCapacitySettotruebecause schema version 1 requires an explicitqueue_capacity; this ensures normalization preserves later compatible releases that intentionally select a non-default capacity. -
Rename the existing private backend normalizer to the internal exported
backend.NormalizeDefinitionand have bothRegistryand the catalog adapter call it. This remains inside Go'sinternalboundary and is not a Promptkit public API. Do not duplicate endpoint, environment-name, capacity, reserved-field, or bounded JSON-value policy in the adapter. -
For each source, call
profile.LoadFSRepositoryon<root>/profiles. Reject an empty profile set. Use the returned explicit-field metadata to rejectendpointorapi_key_envwhenever 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-localprofile.NewResolvingRepository; this both proves inheritance is self-contained and verifies that the final backend ID equals the manifest owner. -
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.
-
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.
-
Add an integration test that imports the released
openrouterandrakestrawhomepackages, supplies theirFS()/Rootvalues with expected backend IDs, loads the set, and compares it exactly withtestdata/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. -
Update
docs/policy/architecture.md,docs/internal/overview.md, anddocs/internal/sources.mdin the same commit. Describeinternal/catalogas 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
- At
internal/catalog, usetesting/fstest.MapFStables 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. - Keep exhaustive YAML rules in
internal/profileand backend invariants ininternal/backend; adapter tests must prove delegation and assembly, not duplicate those packages' full matrices. - Run focused tests for
internal/profile,internal/backend, andinternal/catalog, followed bygo test ./...,go test -race ./...,go vet ./..., andgo build ./.... - With
GOWORK=off, verifygo list -m allselects exactlyv1.0.0for both catalogs andgo mod verifysucceeds.
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
- In root engine assembly, import the two external packages with unambiguous
aliases and construct
catalog.Sourcevalues in deterministic OpenRouter, Rakestrawhome order. Use safe display names plusbackend.OpenRouterID/backend.RakestrawHomeIDas the expected IDs. - Load the maintained
catalog.SetduringNewEngineafter options and the required prompt source have been validated, but before constructing the backend registry, capacity manager, or runner. Map any load failure toErrInvalidConfigwith the prefixfailed to load maintained catalogsand preserve redacted catalog/path context. Add no new public error identity. - Change
backend.NewRegistryto 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. - Change
newProfileRepositoryto 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 oneprofile.NewResolvingRepository. - Delete
internal/profile/builtin, including its embedded YAML assets, and deletebuiltInBackendsplus the OpenRouter/Rakestrawhome endpoint, environment, and concurrency constants frominternal/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. - Move the Stage 1 compatibility assertion to
internal/catalogand 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. - 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.
- Confirm with repository search and
go list -depsthat 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. - Update
docs/policy/architecture.md,docs/internal/overview.md, anddocs/internal/sources.mdin the cutover commit: remove the transitional old-runtime description, removeinternal/profile/builtin, and describe explicit root assembly plus eager maintained-catalog validation and the unchanged outer consumer overlay/resolution boundary. - Update
docs/formats.mdwhere 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.modowns 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, 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=offand 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
- 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, anddocs/formats.mdlink to canonical owners instead of duplicating the manifest or complete catalog. - Update
docs/development.mdwith 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. - 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.mddocument 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. - 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.
- 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
- Run the full Promptkit maintainer workflow, including the local Markdown
link validator, with
GOWORK=off. - In each external module, run its full test/race/vet/build, formatting, hygiene, credential, and ordinary module-resolution checks.
- From a temporary module, download both external
v1.0.0versions and the current Promptkit commit's module dependencies without a workspace or replacement. Promptkit itself need not be tagged in this stage. - Inspect all three worktrees and their committed diffs. Require each to be
clean and confirm that Promptkit's
go.mod/go.sumidentify 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.