# 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.