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

218 lines
11 KiB
Markdown

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