9.3 KiB
Architecture Policy
Purpose
This policy defines Weatherreporter's system shape, normative ownership, dependency direction, architectural invariants, safety properties, and non-goals. Developers and coding agents should use it to preserve the application's boundaries as the implementation evolves.
The development guide owns the current package inventory
and contributor workflow. Focused documents under docs/internal/ own
implemented subsystem mechanics. This policy owns the rules those packages and
mechanics must preserve.
System Shape
Weatherreporter is a deterministic weather briefing and report-preparation CLI. It consumes normalized weather data, derives report facts and module snapshots, builds curated prompt packages, compares structured snapshots with prior runs, invokes Scriptorium for bounded generated text, renders managed Markdown reports, persists inspectable artifacts, and can upload completed reports through Distributor.
The application is intentionally a small, explicit, dependency-light Go program. Add abstraction only when it protects a real boundary, makes an important invariant testable, or supports an implemented extension point.
The primary flow is:
- CLI parsing and configuration resolution;
- report or batch resolution;
- normalized weather collection;
- deterministic fact derivation and module construction;
- structured prior-snapshot comparison;
- curated prompt input and generated-text processing;
- managed Markdown rendering and metadata persistence; and
- optional notification using managed report artifacts.
Inspection is a separate read-only flow over persisted state. It must not collect weather data, invoke Scriptorium, or upload reports.
Ownership And Dependency Direction
Entry Point And CLI
The binary entry point should do no business work beyond constructing and running the CLI. CLI code owns commands, arguments, flags, help, output formatting, and conversion into application requests.
CLI packages must not own meteorological decisions, report composition, artifact layout, Recent Changes comparison, external transport, or subprocess construction.
Configuration
Configuration loading, built-in defaults, overrides, secret loading, and
validation belong to internal/config. Operational values shared across
packages must be explicit configuration or constants owned by the responsible
package, not hidden in CLI or adapter code.
The exact configuration contract belongs in the configuration reference. Other architecture documents should state ownership and safety rules rather than repeat fields, defaults, or precedence.
Application Orchestration
internal/app owns top-level use cases and workflow order. It composes report
resolution, collection, domain transformations, state, rendering, and optional
notification through narrow project-owned contracts.
The application layer may coordinate components and convert between their contracts. It must not absorb CLI parsing, HTTP transport, subprocess argument construction, filesystem layout, weather derivation algorithms, template execution, or adapter-specific dependency types.
Domain And Report Logic
Meteorological selection, forecast-period resolution, daypart grouping, threshold detection, fact derivation, report composition, module construction, generated-text validation, and Recent Changes comparison belong in deterministic Go domain packages.
Domain packages must not depend on CLI parsing, process execution, remote transport, or concrete external-library types. Given the same normalized inputs, configuration, valid period, prior snapshot, and clock, domain behavior should be reproducible.
Report selection must go through the report registry or an equivalent centralized mechanism. A report definition owns its identity, prompt and rendering mode, valid-period resolver, module composition, comparison strategy, artifact grouping, and output naming. Do not scatter report-ID conditionals through CLI, orchestration, or adapters.
External Adapters
External integrations use adapter boundaries under internal/adapters.
Adapters own transport and protocol mechanics; application and domain packages
own decisions.
- The Weather API adapter owns HTTP request construction, timeouts, retries, response-envelope handling, decoding, and endpoint compatibility.
- The Scriptorium adapter owns argument construction, context-aware subprocess execution, stdout and stderr capture, exit interpretation, and result decoding. It must avoid shell interpolation.
- The Distributor adapter owns dependency-specific bundle and upload types, client construction, request execution, status handling, and redaction.
External dependency types must not leak beyond the adapter that integrates them. Adapters should expose narrow project-owned inputs and outputs so an integration can be tested or replaced without changing domain logic.
State And Embedded Assets
internal/state owns managed workspace paths, durable metadata, atomic
artifact persistence, prior lookup, and inspection reads. Other packages should
request state operations rather than reconstruct managed paths independently.
Schemas, prompts, Markdown templates, and partials should live as separate repository assets and be embedded by the package that owns their execution or lookup. Keep weather derivation and path construction out of templates.
Architectural Invariants
Weather Truth And Generated Text
- Normalized source data and deterministic Go derivation are authoritative for weather facts.
- LLM prompts receive curated module-based packages rather than raw, unbounded source payloads.
- Generated text is limited to defined prose slots, validated before use, and rendered through typed or otherwise explicit contexts.
- Templates arrange validated prose and deterministic facts; they do not perform meteorological derivation.
Reports And Comparison
- Report behavior is resolved through centralized definitions.
- Recent Changes is computed from structured module snapshots, never by comparing rendered Markdown.
- Batch workflows collect normalized weather data once and reuse that collection for planning and report generation.
- Report metadata links identity, generation time, valid period, source provenance, and the managed artifacts produced for the run.
Managed State And Notification
- Durable structured writes are atomic where practical.
- Managed paths remain beneath the configured workspace root.
- Operations that delete, move, overwrite, or copy files use narrow, explicit paths; destructive cleanup is opt-in.
- Intermediate artifacts reached before a later failure remain inspectable where practical.
- Distributor uploads use managed Markdown reports, never optional output copies or broad workspace scans.
- Notification occurs only after the managed report and required metadata have been successfully produced.
Security, Errors, And Cancellation
- Secrets must not appear in logs, errors, persisted artifacts, examples, or user-facing output.
- Errors preserve actionable operation, report, RunID, path, endpoint, or subprocess context without exposing secrets or unnecessarily large payloads.
- External calls, subprocesses, storage operations, and multi-step workflows
accept or propagate
context.Contextwhere cancellation or timeout is meaningful. - Adapter failures preserve useful status, stderr, or response context at the boundary and are translated into project-owned errors before crossing into unrelated packages.
Dependency Policy
Prefer the Go standard library. Add an external dependency only when it materially improves correctness, security, interoperability, or maintainability. A dependency used for a small convenience does not justify its lifetime upgrade and compatibility cost.
Keep dependency-specific types inside the package that intentionally adopts the dependency. The application should remain understandable and testable without requiring framework-wide abstractions or live external services.
Verification And Documentation
Core behavior must be testable without live Weather API, Scriptorium, or Distributor services. The testing policy owns test philosophy, sufficiency, boundaries, and test-double guidance.
Documentation must follow the
documentation policy. Update the canonical user,
operator, integration, internal, and example documentation in the same change
as the behavior it describes. Future or proposed behavior belongs under
docs/roadmap/; significant durable decisions may be recorded as ADRs.
Non-Goals
Weatherreporter is not:
- a source weather-data ingestion or normalization service;
- a general-purpose LLM orchestration framework;
- an application in which an LLM selects authoritative weather facts or report policy;
- a plugin framework with dynamically discovered report or module behavior;
- an HTTP service or multi-user distributed job system;
- a replacement for Scriptorium or Distributor protocol ownership; or
- a system that hides operational state exclusively inside opaque logs or remote services.
New requirements may justify revisiting a non-goal. A change that alters system shape, dependency direction, a safety property, or another architectural invariant should be recorded deliberately in this policy or an ADR rather than introduced implicitly.