Files
promptkit/docs/roadmap/public-facade.md

11 KiB

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 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 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. 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, 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 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 to describe the implemented facade and assembly boundary, and the component overview 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.

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.