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