202 lines
9.3 KiB
Markdown
202 lines
9.3 KiB
Markdown
# Architecture Policy
|
|
|
|
**Base Policy Version:** 1.3
|
|
|
|
**Last Updated**: 2026-09-27
|
|
|
|
**Upstream Source:** [architecture.md](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/policy/architecture.md)
|
|
|
|
## Purpose And Ownership
|
|
|
|
The adopted policy defines Promptkit's purpose and the architectural
|
|
constraints contributors must follow. It owns system boundaries, responsibility
|
|
and dependency rules, established invariants, and explicit non-goals.
|
|
|
|
Describe the concrete package inventory and implementation mechanics in internal
|
|
documentation. Link to canonical public contracts for their exact behavior and
|
|
to ADRs for decision rationale.
|
|
|
|
## Architectural Approach
|
|
|
|
Hexagonal architecture is strongly recommended, including encapsulating external
|
|
integrations and APIs within internal adapters. Keep domain and application
|
|
logic independent of external integration details, and make dependency direction
|
|
clear in the adopted policy.
|
|
|
|
Implement that separation with proportionate, idiomatic code. Choose interfaces
|
|
and package layout to serve the application's actual boundaries. Use adapters
|
|
for collaborators with external integration responsibilities.
|
|
|
|
## Adopted Constraints And Implemented Structure
|
|
|
|
Architectural constraints govern current and future development. Record
|
|
implemented structure accurately, proposals and implementation status in the
|
|
roadmap, and significant decisions and rationale in ADRs. Describe a target
|
|
structure as implemented when its changes land.
|
|
|
|
Constraints must reflect deliberately selected requirements and decisions.
|
|
Do not promote incidental implementation choices into architectural invariants.
|
|
|
|
## System Shape
|
|
|
|
Promptkit is an importable Go library. It does not ship a command, an HTTP
|
|
service, or another application process. Repository examples demonstrate
|
|
library use but are not Promptkit applications or release artifacts.
|
|
|
|
The module root contains package `promptkit`, which is the public facade. It
|
|
provides the supported engine, configuration and source options, requests,
|
|
results, public values, extension interfaces, profiles, and error sentinels.
|
|
|
|
The [internal component overview](../internal/overview.md) owns the inventory
|
|
of implemented packages and their concrete responsibilities. This policy owns
|
|
the boundaries and dependency rules those components must preserve.
|
|
|
|
The internal engine composes selected catalog and caller ConfigKit inputs once,
|
|
then assembles the captured definitions, capacity manager, renderer, validator,
|
|
outbound client, and use-case runner. The configuration adapter interprets and
|
|
validates only the final effective values, including presence, null, and
|
|
resource context. It retains source history for explicit operator inspection,
|
|
not for validation or a second resolution pass. The root facade translates
|
|
public values and errors at the library boundary.
|
|
|
|
The engine constructs capacity policy from final backend definitions, wraps
|
|
the selected built-in or injected model client, and supplies bounded admission
|
|
to the runner. Runtime/domain components consume execution-owned values, not
|
|
ConfigKit types. Artifact reading and validation use the domain model and
|
|
application-neutral defaults; validation also uses file-catalog helpers and
|
|
JSON Schema. The model client uses the domain model, defaults, and an injected
|
|
or standard-library HTTP client. The runner coordinates narrow lookup and
|
|
collaborator interfaces without rereading configuration.
|
|
|
|
The current implementation follows this dependency direction:
|
|
|
|
```text
|
|
downstream consumers, including Scriptorium
|
|
|
|
|
v
|
|
root promptkit public facade
|
|
|
|
|
v
|
|
internal framework components
|
|
|
|
|
v
|
|
narrow injected abstractions
|
|
```
|
|
|
|
The backend index depends on the domain model and shared JSON-value ownership
|
|
and has no mutation API after construction. The configuration adapter applies
|
|
the OpenAI-compatible reserved request-field rule owned by the model client
|
|
when it validates final provider maps. The
|
|
capacity component depends on the domain model and the narrow internal
|
|
model-client boundary, not on provider transport implementation. The model
|
|
client does not depend on registry or capacity configuration. The facade
|
|
coordinates internal components and adapts the supported public extension
|
|
interfaces to narrow internal abstractions. Internal components must not depend
|
|
on consumers or on Scriptorium.
|
|
|
|
`internal/domain` owns source-neutral invariants for values shared across
|
|
multiple input and execution boundaries, including execution-setting bounds,
|
|
OpenAI-compatible base endpoints, session identifiers, and output-contract
|
|
legality. Callers retain source parsing, required-field rules, other
|
|
source-specific normalization, defaulting, error classification, and policy
|
|
specific to their own boundary.
|
|
|
|
## Repository And Consumer Boundary
|
|
|
|
Scriptorium is a downstream application that consumes Promptkit through
|
|
the supported public facade. It is not a Promptkit package and must not become
|
|
an internal dependency.
|
|
|
|
Promptkit owns reusable, application-neutral library behavior. It does not own:
|
|
|
|
- binaries or executable packaging;
|
|
- CLI commands, parsing, streams, or exit codes;
|
|
- HTTP routes, servers, request DTOs, status mapping, or deployment policy;
|
|
- application configuration discovery or precedence;
|
|
- process lifecycle, operational state, or application logging; or
|
|
- consumer-specific filesystem or security policy.
|
|
|
|
Those concerns remain with Scriptorium or another consuming application.
|
|
|
|
## Package Ownership
|
|
|
|
The module root is the supported public facade. Framework implementation
|
|
packages belong under Go's `internal/` boundary unless a demonstrated, stable
|
|
consumer contract requires a public package.
|
|
|
|
Each package must have one cohesive responsibility and a clear dependency
|
|
direction. Internal packages must not expose their types merely to simplify
|
|
wiring, and the public facade must not leak internal representations through
|
|
exported signatures. Production packages under `internal/` must not import the
|
|
module root; the root facade may depend on internal implementation packages.
|
|
New public packages require a durable consumer need that cannot be served
|
|
cleanly by the root facade.
|
|
|
|
The [internal component overview](../internal/overview.md) must be updated as
|
|
packages are implemented or their responsibilities change.
|
|
|
|
## Exported API Discipline
|
|
|
|
Export the smallest contract required by real consumers. Exported declarations
|
|
must have accurate GoDoc, stable semantics, and tests proportionate to their
|
|
compatibility risk. Avoid speculative extension points, aliases for internal
|
|
types, and public constructors that expose assembly details.
|
|
|
|
Once an exported API exists, its Go declaration and GoDoc own its exact public
|
|
contract. Architecture documentation owns boundary rules, not a duplicate API
|
|
reference.
|
|
|
|
## Error Boundaries
|
|
|
|
Internal failures must cross the public facade as errors meaningful to a Go
|
|
consumer without exposing private package types or transport-specific policy.
|
|
Wrapping should add useful context while preserving any public error identity
|
|
needed with `errors.Is` or `errors.As`.
|
|
|
|
Promptkit must not assign CLI exit codes or HTTP status codes. Consumers map
|
|
public library outcomes into their own transport behavior.
|
|
|
|
## Dependency Injection
|
|
|
|
External effects and consumer-selected policy must enter through narrow
|
|
interfaces or functions at the boundary that uses them. Dependencies should be
|
|
explicitly supplied during construction or invocation rather than read from
|
|
consumer configuration or hidden process-global state.
|
|
|
|
Interfaces should be owned by the code that consumes the behavior and should
|
|
contain only the operations that code requires. Provide defaults only for
|
|
application-neutral behavior; consumer-specific restrictions and adapters
|
|
remain injected from the consuming project.
|
|
|
|
## Repository Independence
|
|
|
|
Promptkit must build, test, and validate independently of Scriptorium. Do not
|
|
commit `go.work`, `go.work.sum`, or a local filesystem `replace` directive.
|
|
Temporary workspace or replacement configuration may support coordinated local
|
|
development, but it is not part of either repository's architecture or release
|
|
state.
|
|
|
|
## Current-State Maintenance
|
|
|
|
Do not list planned packages as implemented components. When implementation
|
|
introduces a package, update the internal inventory and the owning contract or
|
|
subsystem document in the same change.
|
|
|
|
## Trust Boundaries And Threat Scope
|
|
|
|
Where useful, state the trust boundaries and threat scenarios relevant to the
|
|
application's adopted requirements, along with scenarios it explicitly excludes.
|
|
For example, distinguish handling untrusted application input from defending
|
|
against a privileged actor concurrently modifying the host filesystem.
|
|
|
|
Threat exclusions define the scope of the adopted requirements. Base defenses
|
|
and guarantees on those requirements and raise unsettled scope for a deliberate
|
|
decision under the [agent policy](agents.md).
|
|
|
|
Keep this discussion proportionate to the application. A separate threat
|
|
assessment or security document is optional when adopting this template.
|
|
For this library, the framework format and integration documents own credential
|
|
supply and external contracts; internal documents own implementation mechanisms.
|
|
Consumer-specific filesystem and security policy remains outside Promptkit's
|
|
boundary as described above.
|