Prepare the v0.10.0 release

This commit is contained in:
2026-09-22 14:06:35 +00:00
parent c8e4abcdbd
commit d79f1e9562
11 changed files with 25 additions and 960 deletions

View File

@@ -33,7 +33,7 @@ boundary and constraints that framework work must preserve.
## Release Guidance
Consumers preparing for the unreleased `v0.10.0` profile-policy transition
Consumers upgrading to the `v0.10.0` profile-policy transition
should read the [v0.10.0 transition guide](docs/releases/v0.10.0.md).
Consumers upgrading from `v0.8.0` to `v0.9.0` should read the

View File

@@ -264,7 +264,7 @@ only the setting it owns:
```go
promptkit.WithProfiles(promptkit.Profile{
ID: "weather-light",
BaseProfileID: "deepseek-4-flash",
BaseProfileID: "promptkit-openrouter-standard",
ReasoningEffort: "high",
})
```

View File

@@ -346,7 +346,7 @@ cross-version guarantee for these names.
The external profile inventory is defined by the selected catalog modules, not
duplicated here. Browse the exact data selected by this Promptkit version:
- [OpenRouter catalog v2.20260922.0 profiles](https://gitea.maximumdirect.net/eric/promptkit-backend-openrouter/src/tag/v2.20260922.0/catalog/profiles)
- [OpenRouter catalog v2.20260922.1 profiles](https://gitea.maximumdirect.net/eric/promptkit-backend-openrouter/src/tag/v2.20260922.1/catalog/profiles)
- [Rakestrawhome catalog v2.20260922.0 profiles](https://gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/src/tag/v2.20260922.0/catalog/profiles)
External profile entries are volatile across catalog releases. The

View File

@@ -1,9 +1,7 @@
# Promptkit v0.10.0 (Unreleased)
# Promptkit v0.10.0
This supplemental transition guide describes the planned `v0.10.0` catalog
profile-policy change. It is not a release record and does not claim that the
version has been published. The annotated tag, when created, remains the
authoritative release record.
This supplemental guide summarizes the `v0.10.0` changes and migration needs.
The annotated tag remains the authoritative release record.
## Transition Summary
@@ -12,6 +10,13 @@ modules whose external profile data can change between catalog releases. It
also introduces the maintained literal names
`promptkit-openrouter-standard` and `promptkit-openrouter-auto`.
The public package now delegates assembly to an internal engine while retaining
the existing Go API. This refactor requires no consumer code changes.
This release selects OpenRouter `v2.20260922.1` and Rakestrawhome
`v2.20260922.0`; their exact inventories are linked from the
[format reference](../formats.md#external-profile-inventories).
The maintained names provide continuity only within the catalog versions
selected and tested by a Promptkit release and when consumers do not override
the name or its base. Their current selections may change. External catalog
@@ -26,6 +31,12 @@ catalog profile's volatility, or define a complete application-owned profile
when the application must control its target and settings. An alias that
inherits from an external base is not a frozen copy.
In particular, the selected OpenRouter catalog removes `deepseek-3-2`,
`deepseek-4-flash`, and `deepseek-4-pro`. Consumers using those IDs must choose
an available profile or supply their own complete definition; the new
`deepseek-flash-latest` and `deepseek-pro-latest` profiles are not automatic
aliases and need not preserve the previous settings.
See the [release policy](../release.md#catalog-profile-compatibility) for the
compatibility boundary, the [format reference](../formats.md#built-in-profile-catalog)
for current Promptkit-owned definitions and selected external inventories, and

View File

@@ -1,284 +0,0 @@
# Profile Policy Implementation Plan
## Scope And Starting Point
Implement the [profile-policy roadmap](profile-policy.md): volatile external
profile catalogs, dated `/v2` catalog modules, and two Promptkit-owned stable
profile names. This replaces the completed public-facade implementation plan.
The facade refactor and its review remediation are complete; composition work
belongs in `internal/engine`, not the public package.
The feature roadmap still labels its policy provisional. Before executing
Stage 1, confirm maintainer acceptance of this plan and update that roadmap's
status to accepted for implementation, linking here for sequencing. Remove
superseded procedural language there without changing its intended end state.
Until implementation lands, existing durable contracts remain authoritative.
Execute stages in numerical order, one coding-agent prompt per stage. Stage 2
is a release handoff requiring explicit publication authority. This plan does
not itself authorize releases or pushes. Do not carry forward completion marks
or API snapshots from the previous implementation plan.
Read `AGENTS.md`, [development](../development.md), all policies under
`docs/policy/`, [formats](../formats.md), [internal sources](../internal/sources.md),
and [release procedure](../release.md). Read each catalog repository's own
instructions and release procedure before editing it. Preserve unrelated
changes. For every implementation stage, run focused tests and the affected
repository's complete validation workflow before acceptance. Report results
and blockers; do not treat an unrun check as successful.
## Implementation Decisions
- Both catalog modules move to `/v2` paths and `v2.YYYYMMDD.N` releases. Use the
actual UTC publication date and the next unused revision starting at zero;
no placeholder tag or preliminary `v2.0.0` is needed. Modules release
independently. Preserve published tags and their historical contracts.
- Only external profile data becomes volatile. Go APIs, manifest/format
semantics, backend IDs and definitions, and Promptkit runtime behavior remain
outside the exception. Promptkit adds no live model or endpoint checks.
Catalog owners remain responsible for upstream mapping accuracy.
- The only profile-specific continuity guarantees are the literal names
`promptkit-openrouter-standard` and `promptkit-openrouter-auto`, within the
selected-and-tested dependency scope and absent consumer overrides. Models,
backends, routing, defaults, and other selection characteristics are not
permanent promises. Do not add notice or approval gates for later selections.
- Initially, standard inherits `gemini-flash-latest`; auto directly selects
backend `openrouter` and model `openrouter/auto`. Add no generation overrides,
connection metadata, implicit default selection, public API, or fallback loop.
- Construct the two raw definitions as internal Go values through
`profile.NewMemoryRepository`, owned by a focused `internal/engine` helper.
Two definitions do not justify another embedded asset tree or package.
- Compose raw sources in this order: consumer memory, ordinary consumer source,
consumer fallback source, Promptkit-owned profiles, external catalogs. Keep
one runtime resolver over the complete hierarchy. Separately validate the
maintained-only composition at construction without freezing its resolved
values into the runtime source.
- Reject the exact `promptkit-` prefix on normalized external catalog IDs in
`internal/catalog.Load`. Do not impose that reservation on consumer sources,
fold case beyond existing ID rules, or alter generic repositories.
- Replace the old external-catalog extraction snapshot with structural and
maintained-name tests. Do not create another full-profile inventory snapshot:
module versions/checksums identify selected data, while repeated snapshots
recreate catalog churn. Preserve backend-contract coverage separately.
- The transition targets a new Promptkit minor release, currently `v0.10.0`.
Later catalog-only updates and maintained-selection adjustments may ship as
patches if other contracts remain intact. Promptkit retains semantic versioning.
## Stage 1: Prepare Both Catalog Modules For The New Contract
**Completed:** 2026-09-22
Repositories: `../promptkit-backend-openrouter` and
`../promptkit-backend-rakestrawhome`. Their small data-only packages make this
one bounded prompt; keep changes in their respective repositories.
1. Inspect both worktrees, module files, tags, assets, tests, README files, and
`docs/release.md`. Preserve maintainer changes. The current OpenRouter
checkout already contains a profile refresh; retain it rather than reverting
to Promptkit's selected v1 catalog. Additional model curation is out of scope.
2. Change each module declaration to its existing path plus `/v2`, and update
self-imports and examples. Preserve package names, `FS()`, `Root`, embedded
layout, manifest schema, and backend definitions. Do not introduce a
dependency on Promptkit or duplicate its runtime validation.
3. Revise compatibility documentation: profiles may be added, removed, renamed,
or modified without deprecation or notice; backend definitions, public Go
API, and schema compatibility do not gain that exception. Describe dated
versions and explain that dates do not certify live availability. Do not
pretend a README change retroactively changes the v1 contract.
4. Update each release procedure for `/v2`, dated tags, immutable versions,
independent release dates, and normal module-resolution checks. Retain its
existing validation and annotated source-tag publication requirements.
5. Keep proportionate data-package tests for the exported filesystem and asset
shape. No network calls, credentials, provider SDKs, runtime dependencies,
or cross-version profile-continuity assertions. Run both full workflows.
Acceptance: both source trees are ready for dated v2 releases; their asset APIs
and backend manifests are unchanged apart from module identity. Report the
candidate commits and validation. Do not publish without authority.
## Stage 2: Publish And Verify The Catalog Releases
**Completed:** 2026-09-22
**Publication record:**
- OpenRouter: `gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v2`
`v2.20260922.0` from `ad0b16f77035d8acbe38a6ae1e278eaf7eeb0dc8`.
- Rakestrawhome:
`gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v2`
`v2.20260922.0` from `d664696142bf7c2d93db2a6b2e07f3deb55f2226`.
This is a maintainer-authorized publication stage, not a coding workaround for
unavailable dependencies. If authority or remote access is missing, report the
handoff and stop before adopting unpublished dependencies.
1. Follow each catalog's updated release procedure. Select an unused
`v2.YYYYMMDD.N` tag using the publication date, validate the clean candidate,
publish its source commit and only the selected annotated tag, and verify
the remote tag and peeled commit. Never move a published tag.
2. From a temporary module outside these repositories, resolve each exact
`/v2@version` with `GOWORK=off`, without replacements or vendoring. Verify
module identity and version. Downloads are not inference-provider probes
and are separate from offline test execution.
3. Record the two actual module versions and source commits in execution notes
in this plan for Stage 5. They need not share dates or revision numbers.
Acceptance: both published dependencies resolve normally without sibling
checkouts, workspaces, or replacements.
## Stage 3: Enforce The External Catalog Namespace
**Completed:** 2026-09-22
Repository: Promptkit. Keep current v1 dependencies during this stage;
dependency adoption remains atomic in Stage 5.
1. In `internal/catalog.Load`, reject normalized profile IDs starting with
`promptkit-` while inspecting eager-loaded catalog metadata. Use the existing
descriptive catalog-construction error style with source context. Public
construction continues to classify failures as `ErrInvalidConfig`.
2. Preserve catalog-local inheritance, owning-backend validation, duplicate
detection, secret/connection-field rules, strict decoding, and value checks.
Do not relax these because profiles are volatile or add upstream validation.
3. Extend synthetic catalog tests with a reserved maintained name and an
otherwise unused `promptkit-` name. Confirm ordinary IDs still load. Keep
the rule out of generic parsing so consumer definitions can use the prefix.
4. Update internal source documentation for the implemented catalog-only
reservation. Do not yet claim the maintained profiles exist.
Acceptance: catalogs cannot occupy the namespace; existing protections and
consumer source behavior are unchanged.
## Stage 4: Add And Compose Promptkit-Owned Profiles
**Completed:** 2026-09-22
1. Add a maintained-profile helper in `internal/engine` with the two raw
definitions specified above. Build its memory repository, overlay it above
the raw external catalog repository, and supply that raw composition to the
existing consumer-source assembly.
2. During construction, resolve each Promptkit-owned definition through a
maintained-only resolver and check that its effective backend exists in the
maintained backend set. Reuse existing resolver and backend lookup owners;
do not implement another inheritance walker or target-policy validator.
External profiles retain catalog-local validation in `catalog.Load`.
Together these checks cover the maintained set without consumer definitions
or network access.
3. If a maintained definition fails to resolve, fail construction through the
existing `ErrInvalidConfig` boundary. Consumer overrides must not mask broken
bundled definitions. Keep raw definitions for runtime resolution so consumer
overrides of a maintained profile or its base still work. No resolved-profile
cache or global mutable repository is needed.
4. Use minimal synthetic composition tests for inherited selection, direct
selection, missing-base failure, and invalid backend reference. Expected
selections should follow the supplied definitions, not become permanent
literal model promises.
5. Add representative public inspection/preparation coverage proving both
literal names resolve, consumers can replace maintained profiles or their
bases, and default-profile selection is unchanged. Reuse source-precedence
and prepared-execution coverage rather than duplicating full matrices.
6. Update formats with the two current definitions, opt-in selection, and
precedence. Update internal ownership and source-validation documentation.
Scope intermediate compatibility claims to policy actually adopted so far.
Acceptance: both names work through existing APIs, consumer precedence remains
effective, and no public exports or provider routing logic have been added.
## Stage 5: Adopt Published V2 Catalogs And Replace Snapshot Tests
**Completed:** 2026-09-22
1. Update catalog imports in production and tests to `/v2`. Select the exact
Stage 2 versions through normal Go tooling, tidy module metadata, and review
`go.mod`/`go.sum`. No `latest`, unpublished revisions, unrelated upgrades,
or local replacements. Remove unused v1 requirements.
2. Replace `TestLoadPublishedCatalogsMatchCompatibilityFixture` with an offline
integration test loading both selected catalogs for structural acceptance.
Remove snapshot-only helpers and `testdata/builtin-catalog-v1.json` once no
callers remain. Retain malformed-input tests and backend metadata contract
coverage; add focused backend assertions if deleting the snapshot removes
their only protection. Backend definitions are not volatile profile data.
3. Run maintained-profile tests against the new modules. Verify standard's base
exists locally; if absent, change the maintained selection in this same
dependency update and document the current definition. This is not a new
permanent selection-approval gate. The inspected catalog currently contains
`gemini-flash-latest`, so no replacement is planned.
4. Convert tests that use removed external profiles for unrelated runtime
behavior to consumer-owned fixtures. Keep small real-catalog integration
coverage without coupling every runtime test to external inventories.
5. Review the catalog diff for local compatibility consequences, not provider
health. Do not restore removed IDs or add migration aliases just to preserve
the old snapshot. Confirm backend manifests and public/runtime contracts
did not change as a side effect.
Acceptance: Promptkit passes its full workflow using published v2 dependencies;
tests protect structural validity and maintained names without promising an
external inventory across releases.
## Stage 6: Publish Durable Policy And Consumer Guidance
**Completed:** 2026-09-22
1. Update `docs/release.md`, the release-policy owner: narrow external-profile
exception, two-name continuity scope, initial minor transition, and later
patch eligibility. Replace its blanket catalog-update instruction to claim
no consumer migration is needed: removals may require consumer action.
Preserve ordinary compatibility and release records; do not require model
change announcements or additional selection gates.
2. Replace the exhaustive external profile table in `docs/formats.md` with links
to inventories at the selected catalog versions. Keep the small Promptkit
inventory here, label selections current rather than permanent, and link to
release policy for cross-version guarantees.
3. Explain consumer choices in the consumer guide: use a maintained name,
accept a volatile external profile, or define a complete application-owned
profile. Inheritance still follows base changes and can fail if the base
disappears; an application alias is not a frozen copy.
4. Explain selected-and-tested dependency scope and the resolved module graph:
independently upgrading a transitive catalog can break an inherited base.
Pinned data does not preserve upstream availability; running binaries neither
download updates nor automatically replace withdrawn models.
5. Update architecture, internal source/overview, and contributor documentation
only where ownership or workflow changed. Avoid duplicate inventories and
exact contracts. No live endpoint/model-list check belongs in adoption,
tests, or release validation.
6. Prepare `docs/releases/v0.10.0.md` as supplemental transition guidance linking
canonical policy/formats and describing potential profile migration. Add a
README link labeled unreleased until publication. Do not claim release
completion or require individual model-change notices.
Acceptance: durable documentation distinguishes stable literal names from
volatile selections and agrees across repositories without weakening unrelated
contracts.
## Stage 7: Final Integration Review And Release Handoff
**Completed:** 2026-09-22
1. Run the complete Promptkit maintainer workflow: ordinary/race tests, vet,
build, both offline examples, formatting, local links, and hygiene. Verify
normal module resolution independent of sibling checkouts.
2. Compare public declarations against the completed facade baseline: no new
API, internal type leak, default-selection change, or consumer restriction
is intended. Confirm existing error, injection, override, inheritance, repair,
prepared-execution, and capacity tests still pass.
3. Check recorded dependency versions, maintained-name resolution without
consumer help, consumer overrides, and absence of cross-version external
inventory promises in tests or documentation.
4. Review for stronger promises than authorized: permanent backend/routing
implied by a profile name, change announcements, deprecation periods, or
live-service checks. Remove accidental guarantees rather than adding gates.
5. Report completion and the minor-release candidate. Retire completed roadmaps
only when requested or approved during release preparation. Promptkit
publication follows its release procedure under separate authority; do not
tag or push it merely because this stage passes.
Acceptance: ready for independent review and subsequent release preparation,
with validation results and any blockers reported.
## Open Questions
No unresolved product or architecture decision is needed for this proposed
implementation. Maintainer acceptance of the still-provisional policy and
authority to publish catalog releases are execution prerequisites, not assumed
permissions. Exact version strings are selected at publication using the
agreed date convention and recorded before Promptkit adoption.

View File

@@ -1,359 +0,0 @@
# Built-In Profile Compatibility Policy
## Status
Accepted for implementation on 2026-09-22. The implementation sequence and
completion status are maintained in the
[profile-policy implementation plan](implementation.md). Current code, GoDoc,
durable documentation, published module contracts, and release tags remain
authoritative until each corresponding implementation change lands.
## Prerequisite And Sequencing
Implementation of this roadmap must begin only after the
[public facade refactor](public-facade.md) is complete and its compatibility
and validation criteria have passed. The refactor preserves existing behavior;
this subsequent work introduces the deliberate profile-policy changes.
Profile-source composition, maintained-profile construction, and catalog
dependency assembly must use the internal owners established by that refactor.
Both scopes may ship in `v0.10.0`, but they remain separately reviewable changes.
This prerequisite has been satisfied. The remaining work proceeds through the
[profile-policy implementation plan](implementation.md).
## Purpose
Define a truthful compatibility and release boundary for Promptkit's
maintained built-in profiles. The intended direction is to permit exceptionally
rapid catalog updates without weakening Promptkit's stability commitments for
its engine, public Go API, configuration semantics, backend registry, or other
non-profile behavior.
Model catalogs change faster than the Promptkit runtime. Providers control
their own model catalogs and service lifecycles. Promptkit cannot
reasonably guarantee that a maintained profile's upstream model or endpoint
will remain available, and retaining obsolete profiles can be actively
misleading when their targets no longer provide the expected service.
The release model should treat external catalog profiles as volatile data,
while providing a small set of Promptkit-owned aliases with stable IDs for
applications that prefer a maintained general-purpose selection.
## Proposed Compatibility Boundary
The provisional policy would exclude external catalog profile data from
Promptkit's cross-version compatibility surface. A catalog or Promptkit
release could, without deprecation or advance notice:
- add or remove an external catalog profile;
- rename a profile, represented as removing one ID and adding another;
- change the upstream model selected by a profile;
- change profile defaults such as reasoning effort, timeout, service tier,
sampling controls, token limits, or extra parameters; and
- correct or otherwise revise profile metadata in response to upstream
behavior.
Promptkit would make no cross-version promise about the continued presence,
identity, settings, suitability, or upstream availability of an external
catalog profile. The separately defined Promptkit-owned alias IDs would have
the continuity guarantee below. Promptkit would make no claim that an upstream
provider will continue to serve a cataloged model or endpoint for the lifetime
of a Promptkit or catalog release.
This volatility exception should apply only to external catalog profile
data. It should not silently weaken the ordinary compatibility rules for:
- Promptkit's exported Go API and GoDoc contracts;
- prompt, profile, and schema format semantics;
- request preparation, execution, validation, repair, cancellation, error,
credential, ownership, and concurrency behavior;
- public built-in backend IDs and registry behavior;
- the external catalog packages' exported Go API;
- the catalog manifest and asset schema understood by Promptkit; or
- consumer-defined profiles and backends.
Backend definitions, including endpoint, credential-environment, and capacity
metadata, remain outside the profile-volatility exception and subject to the
ordinary compatibility policy.
## Proposed Stable Promptkit Profiles
Promptkit would maintain a small, explicitly documented set of built-in
profiles using existing profile definitions and inheritance:
| Stable profile ID | Initial definition | Initial purpose |
| --- | --- | --- |
| `promptkit-openrouter-standard` | Inherits from external catalog profile `gemini-flash-latest`. | A maintained selection for general-purpose text generation. |
| `promptkit-openrouter-auto` | Selects backend `openrouter` and model `openrouter/auto`. | Delegates model selection to OpenRouter's automatic routing. |
These definitions and purposes describe the initial selections only and are
not continuing model-selection promises.
The auto profile can initially be a direct profile definition; it does not
require a second catalog alias or a new public API. It inherits connection and credential
metadata from the OpenRouter backend and need not add generation overrides.
Promptkit forwards `openrouter/auto` as the model identifier through its ordinary
client. OpenRouter owns the routing decision, eligible models, and resulting
provider behavior. Promptkit adds no model-selection algorithm or fallback
loop. Its ordinary local validation does not check the upstream availability
of `openrouter/auto`.
Both profiles receive the stable-ID guarantee below. References to aliases
describe inherited definitions where applicable; the same namespace,
precedence, and structural-validation requirements cover direct definitions.
Only these two profiles are in scope. This roadmap imposes no additional
approval process for future profiles.
The only profile-specific continuity promise is that the literal names
`promptkit-openrouter-standard` and `promptkit-openrouter-auto` remain
available, subject to the supported dependency and consumer-override scope
below. Their actual models may change at any time, for any reason, without
notice to anyone. No deprecation, approval, or change-announcement gate applies
to model selection. The names do not promise a permanent model, provider,
routing strategy, or set of defaults. Both initially use OpenRouter, but the
profile names themselves are the only continuity guarantee.
In particular, `promptkit-openrouter-auto` is not required to retain
`openrouter/auto` or automatic routing indefinitely. Neither profile guarantees
unchanged output, cost, latency, capabilities, or upstream availability. While
using upstream automatic routing, the selected model can vary between requests
even with unchanged Promptkit and catalog versions.
Catalog dependency updates must preserve local resolution of the two maintained
profile names. If a referenced catalog profile disappears or is renamed,
adjust the affected alias together with the dependency update and ship both in
the same Promptkit release. If the catalog retains the referenced ID but
changes its model, inheritance picks up the change automatically; no separate
model-selection approval is required. Ordinary code review and structural
validation apply. Upstream deprecation, retirement, and mapping accuracy remain
the catalog library's responsibility and do not create a Promptkit monitoring
or upstream-verification obligation.
Release validation must prove that every maintained alias resolves against
the selected catalog versions. This continuity guarantee applies to the
versions selected and tested by Promptkit and assumes consumers have not
overridden the alias or its base with their own definitions. Independently
upgrading a transitive catalog can remove an alias's base without updating
Promptkit; that untested combination is outside the guarantee.
Aliases would retain the ordinary profile precedence and inheritance
semantics. They would introduce no new public API or automatic model failover.
An already deployed application would retain its compiled alias and catalog
data until upgraded; an upstream withdrawal would not change its selection
automatically.
Adding these profiles does not change existing default-profile selection.
Consumers opt into them through the existing profile-selection mechanisms.
### Ownership, Precedence, And Composition
The maintained alias definitions belong to a small Promptkit-owned profile
source. Profile precedence is consumer sources in their existing order, then
Promptkit-owned aliases, then external catalog profiles. Consumers may override
an alias or its base using the ordinary profile mechanisms.
Reserve the `promptkit-` profile-ID prefix against definitions in external
catalogs. Reject such catalog definitions even if they do not yet collide with
an existing alias. This namespace rule does not prohibit consumer overrides.
Retain existing catalog-local inheritance and backend-ownership validation,
and verify that the combined maintained profiles resolve without relying on
consumer overrides. Do not assert a permanent model or routing selection based
on a profile name. Runtime inheritance must continue to observe the complete
source hierarchy, including consumer overrides of aliases and their bases.
Reuse the existing resolver; exact private assembly and validation sequencing
belongs in the implementation plan. Do not introduce a second inheritance
implementation or freeze alias values in a way that bypasses consumer overrides.
## Local Validation And Existing Safeguards
Profile volatility does not remove existing structural validation or library
safeguards. Preserve the following implemented responsibilities without making
them additional profile-availability or permanent implementation promises:
- catalog assets are immutable build inputs rather than runtime downloads;
- manifests and profiles satisfy the supported schemas;
- IDs are unique and profiles select their owning backend;
- external catalogs do not define IDs in the reserved `promptkit-` namespace;
- secret-bearing fields and prohibited connection metadata are rejected;
- profile values are normalized, copied, resolved, and overlaid through the
existing Promptkit architecture;
- the selected catalogs pass Promptkit's integration suite before a Promptkit
release adopts them; and
- the module graph and checksums provide a reproducible identity for the data
that was tested.
These checks establish local integrity for selected data, together with the
maintained-profile resolution requirement above. They do not establish external
catalog profile continuity across versions or live upstream availability.
Promptkit's checks cover local syntax and structural semantics: supported
fields, duplicate IDs, namespace conflicts, inheritance resolution, backend
references, endpoint URL format, and existing security and value constraints.
They expressly exclude attempts to verify that an endpoint exists, a model ID
is recognized upstream, or a provider serves that model. Catalog libraries own
the accuracy of upstream model and endpoint mappings. Promptkit must not add
network probes, model-list lookups, or paid inference checks to catalog adoption
or release validation. Ordinary execution continues to report upstream errors
through the existing generation-error contract.
## Provisional Release Model
The first release adopting this policy should be a new Promptkit minor release
because it changes a previously documented consumer expectation and may remove
profile IDs on which deployed configuration depends. `v0.10.0` is the current
candidate.
The OpenRouter catalog's published `v1.0.0` contract promised stable profile
IDs and advance handling before removal. A later README cannot retroactively
weaken that promise. The current recommendation is therefore to establish the
new boundary in `promptkit-backend-openrouter` major version 2, using the dated
release convention below, even if its exported Go declarations and manifest
schema do not change.
After that boundary is established, maintained catalog releases could publish
profile additions, deletions, and modifications rapidly under their documented
volatile-data policy. Promptkit could select a reviewed catalog update without
treating profile churn as a compatibility break in its otherwise stable core.
Later catalog updates and corresponding alias adjustments may ship in a
Promptkit patch release, such as `v0.10.1` after `v0.10.0`, provided unrelated
compatibility contracts remain intact. Profile churn alone does not require
`v0.11.0` or another minor release.
Promptkit's integration validation covers the catalog versions it selected
and tested. It does not guarantee upstream correctness for those versions.
Go module version selection may cause an advanced consumer's build
to select a newer compatible catalog module than Promptkit's own requirement;
the resolved module graph, rather than Promptkit's `go.mod` alone, determines
the actual embedded data.
Release notes may identify selected catalog updates and summarize changes for
convenience, but no notice of model-selection changes is required, in advance
or afterward. Ordinary release records and validation still apply; they must
not introduce a profile-change announcement gate.
### Proposed Date-Based Catalog Versions
The proposed catalog version convention is `vMAJOR.YYYYMMDD.N`. This is a
calendar-based convention encoded in Go-compatible semantic-version syntax:
- `MAJOR` identifies the compatibility generation of the exported Go package
and catalog asset format. Profile additions, deletions, renames, and setting
changes do not require a major-version increase under the proposed policy.
- `YYYYMMDD` is the release publication date in UTC. It occupies the numeric
minor-version component and communicates snapshot freshness, not the scale
or compatibility of a profile change.
- `N` starts at zero for each UTC date and increments for every additional
release that day, regardless of the kind of profile change. It occupies the
numeric patch-version component and carries no profile-continuity promise.
For example, `v2.20260922.0` and `v2.20260922.1` would be successive releases
on September 22, 2026, followed by `v2.20260923.0` on the next day. These are
illustrative dates, not scheduled releases. Use the actual publication date;
published versions remain immutable, and corrections receive new versions.
Within a major generation, these versions sort chronologically under Go's
ordinary module-version ordering.
Go requires a matching major-version suffix in module paths for version 2 and
later. The OpenRouter transition would therefore use
`gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v2` in the catalog's
module declaration and Promptkit's imports and dependency requirement. A first
release on the example date would be `v2.20260922.0`; a preliminary `v2.0.0`
release is not required. Promptkit consumers would continue importing
Promptkit through its existing module path. See the
[Go module version rules](https://go.dev/ref/mod#versions) and
[major-version suffix rules](https://go.dev/ref/mod#major-version-suffixes).
Both maintained catalog libraries, OpenRouter and Rakestrawhome, would
transition to this policy and dated major-version-2 convention as part of this
work, with matching `/v2` module paths. Thereafter each would release
independently; releases need not share dates or revision numbers.
Promptkit itself would retain its existing version-number
convention and explicitly select and test individual catalog versions.
This proposal replaces the earlier idea of classifying catalog refreshes as
ordinary minor releases and metadata corrections as patches. The date records
when a snapshot was published; release notes may summarize what changed.
Neither establishes live upstream availability or cross-version profile
continuity. The convention remains provisional along with this roadmap.
## Consumer Guidance
The eventual durable documentation should make the tradeoff explicit:
- external catalog profiles optimize for convenient access to a maintained
current model catalog;
- selecting or inheriting directly from an external catalog profile accepts
its volatility;
- selecting either `promptkit-openrouter-standard` or
`promptkit-openrouter-auto` guarantees only continued availability of that
literal profile name within the supported dependency scope; models and
settings may change for any reason without notice;
- `promptkit-openrouter-auto` initially delegates model selection to OpenRouter;
its initial routing behavior is not a permanent contract, and upstream
routing can change independently of a library upgrade;
- pinning the complete resolved Go module graph preserves the exact catalog
assets used by that build, but cannot preserve live upstream availability;
- applications that require their own fixed model selection or settings should
define a complete application-owned profile with an explicit backend, model,
and defaults; and
- an application-owned alias that inherits from an external catalog profile
isolates application configuration from the catalog ID only while that base
exists; it deliberately continues to inherit changes to the base profile.
Promptkit should not promise automatic migration or replacement aliases for
every removed external catalog ID, deprecation windows for catalog profiles,
or runtime fallback from a removed or unavailable model. Its small maintained
alias set provides continuity through coordinated library releases.
## Expected Documentation And Repository Changes
If accepted, implementation would likely require coordinated changes in
Promptkit and each maintained catalog repository:
- revise catalog compatibility and release documentation to distinguish the
stable Go/schema boundary from volatile profile data and document the
`vMAJOR.YYYYMMDD.N` convention;
- update Promptkit's release policy with a narrowly scoped built-in-profile
volatility exception for external catalogs and an explicit continuity
guarantee for Promptkit-owned alias IDs, while retaining its other pre-`v1`
compatibility rules;
- add `promptkit-openrouter-standard` through existing profile inheritance and
`promptkit-openrouter-auto` as a direct `openrouter/auto` profile; document
their distinct purposes and opt-in selection;
- require coordinated catalog and alias updates, with release checks proving
every maintained alias resolves against the selected catalog versions;
- update the architecture, format, source, development, and consumer
documentation at their existing ownership boundaries;
- keep exact external profile inventories in the catalog repositories, linking
to the selected versions from Promptkit rather than duplicating their tables;
keep Promptkit's small alias inventory and runtime semantics in its own
canonical documentation;
- revise tests and compatibility-fixture terminology so they protect local
structural validity and maintained-profile resolution, without asserting
cross-version stability of external profiles; retain an exact selected-version
snapshot only where its review value justifies its maintenance cost;
- publish both revised catalog contracts as dated major-version-2 releases
with matching `/v2` module paths;
- update Promptkit's catalog imports and requirement, current selected-profile
documentation, and release guidance; and
- publish the policy transition and its consumer impact in the eventual
Promptkit minor release note and annotated tag.
Implementation is sequenced and tracked in the
[profile-policy implementation plan](implementation.md).
## Non-Goals
This proposal does not introduce:
- runtime catalog downloads, discovery, or mutable global registration;
- any upstream endpoint or model-existence verification by Promptkit, or
guarantees based on current provider health;
- automatic replacement aliases or migration tables for every removed catalog
profile, or runtime fallback models;
- weakened validation of catalog structure or content;
- weaker compatibility for Promptkit's public engine or consumer-defined
configuration; or
- a general exception allowing arbitrary dependency updates to break stable
Promptkit behavior.

View File

@@ -1,217 +0,0 @@
# Public Facade Refactor
## Status
Accepted direction; implementation has not begun. This feature roadmap defines
the intended end state and compatibility requirements. The companion
[implementation plan](implementation.md) defines the work sequence. Current
GoDoc and durable documentation remain authoritative until implementation lands.
## Purpose
Make Promptkit's root package a deliberately maintained consumer facade while
placing engine implementation and assembly behind Go's `internal/` boundary.
Consumers should depend only on intentional public contracts, and maintainers
should be able to refactor internal behavior without changing those contracts.
The aim is a clearer ownership boundary, not the smallest possible root package
or a reduction in consumer capabilities. Private root functions are already
inaccessible to consumers; relocating them improves separation but does not by
itself control the exported API. The refactor must also protect public type
ownership, signatures, fields, and method sets against accidental exposure of
implementation details.
## Current Position
Promptkit already places execution, repair, capacity management, rendering,
source loading, transport, and validation in specialized internal packages.
Its [architecture policy](../policy/architecture.md) already identifies the
root package as the supported public facade.
The remaining root implementation includes engine construction and collaborator
assembly, source selection, profile overlay construction, filesystem source
setup, and an in-memory profile repository. These responsibilities can move
inward while the root retains public declarations and explicit adapters.
ConfigKit's completed facade refactor is a design reference: root-owned public
types wrap private engine state and expose explicitly selected methods. Its
implementation should inform this work without introducing a dependency on
ConfigKit or copying mechanisms that Promptkit does not need.
## Target End State
### Root-Owned Public Contract
The root `promptkit` package remains the consumer import path and owns:
- public named types, interfaces, constants, and error identities;
- public configuration, request, result, inspection, and profile values;
- constructors, options, and deliberate forwarding methods;
- opaque public handles backed by private internal state;
- public GoDoc and supported serialization and formatting behavior; and
- explicit value conversion, injected-collaborator adaptation, and public
error translation where they cross the facade boundary.
Public types must remain root-defined. Do not alias them to internal types,
anonymously embed internal implementations to promote methods, expose internal
types in public fields or signatures, or add public bridge methods for wiring.
Private fields may refer to internal implementations.
Straightforward convenience constructors and boundary code may remain in the
root. A forwarding layer or additional record type should have a concrete
ownership or compatibility purpose; moving every helper is not an objective.
### Internal Engine Assembly
A cohesive internal assembly owner constructs an operational engine from
internal configuration and supplied collaborators. `internal/engine` is the
proposed home; exact private package and helper layout belongs in the
implementation plan. Its responsibilities
include source selection and defaults, maintained catalog loading, profile
overlay assembly, backend registration assembly, capacity-manager construction,
default client and validator selection, and runner/repairer wiring.
The public `Engine` delegates through private state to internal implementation.
Root options express consumer choices and translate public values; internal
construction owns how those choices assemble components. Preserve established
contracts for option capture, application, validation, and source access.
Incidental private ordering is not a new compatibility requirement.
Build on the existing specialized internal responsibilities. Internal assembly
is the composition owner, not a replacement for the runner,
catalog adapter, profile repositories, transport, or domain packages. It should
not duplicate execution orchestration already owned by `internal/usecase`.
Move the in-memory profile repository behind the internal boundary;
`internal/profile` is its natural home. Reuse existing definition validation and
copying owners. Other root implementation
helpers should move to their cohesive existing owner or to internal assembly
as appropriate. Avoid generic registries, service locators, or a new extension
framework for this relocation.
Dependency direction remains root facade to internal components. Internal
production packages must not import the root package. The root can use internal
domain values and narrow collaborator interfaces for explicit conversions;
this proposal does not require all root imports to pass through a single
internal package or require duplicate forwarding APIs to achieve that shape.
## Compatibility Requirements
This is a behavior-preserving refactor covering the actual exported surface,
including methods and fields. An exported declaration is
accessible even when it is absent from the consumer guide. Do not infer that
an apparently low-level declaration is safe to remove: injected model clients
and other intentional extension contracts may rely on it.
Preserve existing public names, signatures, named-type identity, method sets,
interface satisfaction, exported struct fields, and supported value behavior.
Any discovered API removal or redesign requires a separate explicit decision
and consumer migration rationale; the minor release is not blanket permission
to remove accessible declarations.
The refactor must preserve, in particular:
- established option order, replacement and accumulation rules, nil behavior,
validation timing, and capture/copy contracts;
- profile-source precedence, lazy consumer source reads, inheritance, and
maintained-catalog validation;
- public error identities and typed errors through `errors.Is` and `errors.As`,
including cancellation propagation and sensitive-detail redaction;
- public JSON representations, formatting, and defensive-copy guarantees;
- injected client and artifact-reader contracts, including caller ownership
of collaborator values;
- preparation and inspection semantics, hash relationships, request overrides,
appended messages, output validation, and bounded repair;
- prepared-execution engine binding, shared lifecycle when handles are copied,
single-use execution, discard behavior, and credential retention; and
- engine-local concurrency, admission, cancellation, usage accounting, and
transport behavior.
Preserve intentional contracts without elevating incidental private layout,
helper names, operation ordering, diagnostic wording, or opaque hash encodings
into new promises. The baseline inventory identifies accessible API; it is
not authority to turn every observed implementation behavior into a contract.
## Verification And Test Ownership
Follow the [testing policy](../policy/testing.md). Existing external-package
contract tests and maintained offline examples should continue to exercise the
same consumer API. Tests coupled to private layout may be adapted or
consolidated under the testing policy without changing their contract-level
expectations.
Move implementation tests with their owning behavior when package relocation
requires it. Keep focused source, profile, runner, capacity, and transport
coverage at the existing internal owners. Root tests should focus on public
compatibility, boundary conversion, error adaptation, and representative
assembled workflows. Do not duplicate internal test matrices at the facade or
add one test per trivial forwarding method.
Add a focused public-boundary check that detects internal types escaping
through exported types, signatures, fields, or promoted methods, and rejects
public aliases to internal implementations. Use Go's type information where
needed to inspect the actual exported method sets. The check must permit
private internal state and the established sealed option mechanism. Protect
the no-internal-production-imports-of-root dependency rule without freezing an
incidental file layout or exact internal package inventory.
Compatibility evidence includes an unchanged exported surface and behavioral
coverage of consequential boundary risks: option timing, conversion ownership,
typed errors, and prepared-execution lifecycle. Acceptance requires the complete
[maintainer validation workflow](../development.md#maintainer-validation),
including race checks and both offline examples. No live provider calls or
sibling repository are required for validation.
## Relationship To The Profile Policy Work
This refactor and the [profile-policy proposal](profile-policy.md) are separate
scopes that may ship together in the planned `v0.10.0` release. Establish the
facade boundary with current behavior preserved before implementing the
catalog-policy and standard-profile changes. Their assembly logic can then
land in its intended internal owner.
This roadmap does not itself change catalog versions, introduce standard
profiles, or adopt the provisional profile compatibility policy. Release
guidance should distinguish the internal refactor from the deliberate consumer
policy changes accompanying it. A completed facade refactor should require no
consumer migration on its own.
## Documentation End State
Update the [architecture policy](../policy/architecture.md) to describe the
implemented facade and assembly boundary, and the
[component overview](../internal/overview.md) to identify the new owner and
relocated responsibilities. Update affected internal source and runner
documentation and links when implementation moves.
Go declarations and GoDoc remain the canonical public contract. Consumer and
format documentation should not acquire new requirements from a private
relocation. Record the durable facade decision in an ADR if needed to preserve
its rationale; ConfigKit is a reference, not an ongoing normative dependency.
Remove this temporary roadmap after completion and durable documentation
updates according to the [documentation policy](../policy/documentation.md).
## Completion Criteria
- The root exposes deliberate, root-owned consumer contracts and explicit
adapters without leaking internal types or promoted implementation methods.
- Internal assembly and repositories own the relocated implementation, with
one production path and no parallel legacy engine.
- Specialized internal components retain clear ownership and do not import
the public facade.
- Public API comparison and behavioral tests establish compatibility,
including ownership, errors, option timing, and prepared execution.
- Boundary checks protect accidental API exposure without preventing ordinary
internal refactoring or consumer extension.
- Required validation passes, documentation matches the implemented state,
and consumers require no migration for this refactor.
## Non-Goals
- Public API expansion, removal, or redesign.
- Changes to provider behavior, formats, defaults, or execution semantics.
- Replacing the existing specialized packages with one large engine package.
- A dependency on ConfigKit or a reusable facade-generation framework.
- A minimum root-package line count, exhaustive wrapper tests, or a blanket
prohibition on private boundary logic in the root.
- Implementing the separate profile-policy roadmap during this refactor.

View File

@@ -1,86 +0,0 @@
# Public Facade Review Remediation
## Scope
Review of implementation commits after baseline `7a858c2` through `06251e3`,
against the [feature roadmap](public-facade.md) and
[implementation plan](implementation.md).
The production ownership split is in place. No production behavior regression
or public API change was identified in the reviewed diff. One required
architecture-test gap was identified and has now been addressed; it did not
establish an existing public type leak in Promptkit.
## Finding 1: Complete Reachable Public Type Inspection (P2)
Status: addressed.
Location: [public boundary checker](../../public_boundary_test.go),
`publicSurfaceViolations`, in its named/alias and method-set traversal.
The original checker inspected method sets only for exported package-scope type
names. It did not inspect methods on an unexported root type reached through an
exported function or field. Such methods are still callable by consumers. It
also walked generic parameter constraints but not instantiated type arguments;
an internal argument can escape without appearing in the underlying fields.
Both examples below were type-checked through the existing fixture helper and
incorrectly produced zero violations:
```go
// Example 1: a caller can invoke New().Leak().
type result struct{}
func (result) Leak() hidden.Value { return hidden.Value{} }
func New() result { return result{} }
```
```go
// Example 2: an internal type appears in an exported result signature even
// though Box has no field using its type parameter.
type Box[T any] struct{}
func New() Box[hidden.Value] { return Box[hidden.Value]{} }
```
Here `hidden` is the fixture's own-module internal package. These are separate
fixture packages, not additions to Promptkit's API. Temporary diagnostic tests
confirmed both false negatives and were removed after review.
### Resolution
The recursive named-type traversal now checks reachable root types' value and
pointer method sets, using instantiated selections for their signatures. Named
types and aliases have their concrete type arguments inspected before their
underlying representation is considered. The separate package-scope method
loop was removed. Regression fixtures cover returned private types, private
field types with public pointer methods, generic arguments, aliases that erase
their arguments, and allowed public arguments with recursive method signatures.
### Acceptance Criteria
Extend the existing recursive traversal rather than adding a second checker:
- Inspect accessible methods of reachable root named types, including
unexported types, with the existing cycle protection and promoted-method
checks. Cover pointer and value method sets where applicable.
- Inspect concrete generic type arguments on named types and aliases before
losing them through unaliasing or skipping an external named type's internals.
Continue checking constraints. Do not recursively police unrelated dependency
implementation details.
- Add the two reproductions to the compact fixture table and exercise an
allowed public type argument as a control. Preserve acceptance of private
internal fields and sealed options. Avoid duplicated method traversal.
Acceptance: both examples are rejected, legitimate private state still passes,
and the full maintainer workflow succeeds. No production API changes are needed.
## Review Validation
The existing full maintainer workflow passed: ordinary and race tests, vet,
build, both offline examples, formatting, Markdown links, and repository
hygiene. The targeted diagnostic tests failed as expected, demonstrating the
checker gap rather than a runtime failure. No production or permanent test
files were changed during this review.
After remediation, the focused checker tests and full maintainer workflow also
passed. The changes are confined to the architecture test and this review log;
production code and public contracts remain unchanged.

2
go.mod
View File

@@ -3,7 +3,7 @@ module gitea.maximumdirect.net/eric/promptkit
go 1.25.5
require (
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v2 v2.20260922.0
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v2 v2.20260922.1
gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v2 v2.20260922.0
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2
gopkg.in/yaml.v3 v3.0.1

4
go.sum
View File

@@ -1,5 +1,5 @@
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v2 v2.20260922.0 h1:uxvVFOylxXrzcg0jJLXcRBIj73BGNjvnsRuMoOeoYKw=
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v2 v2.20260922.0/go.mod h1:a3VBbh9oaGXD6ruxhtwHj+AWW6DY/SSyby1QtMmYSUo=
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v2 v2.20260922.1 h1:S2/Q/JDE+m8Ca3pDhAjVbfYrdlEhs9blj0STgNopHo0=
gitea.maximumdirect.net/eric/promptkit-backend-openrouter/v2 v2.20260922.1/go.mod h1:a3VBbh9oaGXD6ruxhtwHj+AWW6DY/SSyby1QtMmYSUo=
gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v2 v2.20260922.0 h1:Y0EXc3eh12eKW2NrOviVqdk3oBo6UUjopt1YAa1B3Oc=
gitea.maximumdirect.net/eric/promptkit-backend-rakestrawhome/v2 v2.20260922.0/go.mod h1:mtgD1E0SGm0e/I8Ffhz+AYX7GN7RKUWJXehNFIKHs4Y=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=

View File

@@ -18,7 +18,7 @@ func TestProfileInheritanceBuiltInAliasWorkflow(t *testing.T) {
SchemaDir: frameworkSchemaDir,
}, promptkit.WithProfiles(promptkit.Profile{
ID: "weather-light",
BaseProfileID: "deepseek-4-flash",
BaseProfileID: "promptkit-openrouter-standard",
ReasoningEffort: "high",
TimeoutSeconds: 120,
}))
@@ -26,7 +26,7 @@ func TestProfileInheritanceBuiltInAliasWorkflow(t *testing.T) {
t.Fatalf("construct alias engine: %v", err)
}
base, err := engine.InspectProfile(context.Background(), "deepseek-4-flash")
base, err := engine.InspectProfile(context.Background(), "promptkit-openrouter-standard")
if err != nil {
t.Fatalf("inspect base: %v", err)
}
@@ -84,7 +84,7 @@ func TestProfileInheritanceYAMLAliasOfBuiltIn(t *testing.T) {
engine, err := promptkit.NewEngine(promptkit.Config{},
promptkit.WithPromptFS(fstest.MapFS{}, "."),
promptkit.WithProfileFS(fstest.MapFS{
"alias.yaml": &fstest.MapFile{Data: []byte("id: yaml-alias\nbase_profile: deepseek-4-flash\n")},
"alias.yaml": &fstest.MapFile{Data: []byte("id: yaml-alias\nbase_profile: promptkit-openrouter-standard\n")},
}, "."),
)
if err != nil {