Files
weatherfeeder/docs/policy/development.md
Eric Rakestraw 8041f99782
All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
Clean and update documentation
2026-06-11 10:00:15 -05:00

231 lines
8.0 KiB
Markdown

# Development Policy
## Purpose
This document describes how to change `weatherfeeder` safely. It is for
maintainers and coding agents working in the repository.
Use this alongside the [architecture policy](architecture.md). User-facing CLI,
configuration, operations, and wire-contract details belong in their canonical
docs, not here.
## Repository Layout
- `cmd/weatherfeeder/`: executable wiring, sample `config.yml`, and runtime
composition tests.
- `model/`: canonical weather payload structs. JSON tags are part of the wire
contract.
- `standards/`: schema strings, versioning conventions, WMO constants, and
shared wire-format policy.
- `internal/sources/`: source adapters that poll upstream providers and emit raw
feed events.
- `internal/normalizers/`: raw-to-canonical event transforms.
- `internal/providers/`: pure provider helper code shared by sources and
normalizers.
- `internal/sinks/postgres/`: weatherfeeder-owned Postgres schema and canonical
event mapper.
- `docs/`: current behavior, consumer guides, integration contracts, policies,
and roadmap files.
- `examples/`: maintained, copyable configuration examples.
## Build And Test
Run the full test suite before committing behavior or documentation changes that
depend on code behavior:
```sh
go test ./...
```
Use narrower commands while iterating:
```sh
go test ./cmd/weatherfeeder
go test ./internal/sources/...
go test ./internal/normalizers/...
go test ./internal/sinks/postgres
```
Format Go code before committing:
```sh
gofmt -w <changed-go-files>
```
Do not require live upstream weather services, NATS, or Postgres for unit tests.
Use fixtures, local test servers, and package-level tests.
## Coding Conventions
- Keep `cmd/weatherfeeder` focused on composition: config load, registry setup,
scheduler jobs, processor chain, dispatch, signal handling, and logging.
- Keep source fetching separate from normalizer mapping.
- Match normalizers by schema constants from `standards`, not source names.
- Keep provider-specific helper code under `internal/providers/<provider>` when
both sources and normalizers use it.
- Keep cross-provider normalizer helpers pure and deterministic under
`internal/normalizers/common`.
- Keep sink persistence mapping isolated under `internal/sinks/<sink>`.
- Wrap errors with operation context, but do not include whole upstream payloads
in errors or logs by default.
- Prefer explicit registries and small package-level constructors over hidden
global behavior.
## Architecture-Preserving Changes
When changing `weatherfeeder`, preserve the split between feedkit
infrastructure and weather-domain behavior.
Do:
- keep generic scheduling, dispatch, processor, config, and sink mechanics in
feedkit;
- keep weather provider rules in source adapters, provider helpers, and
normalizers;
- keep canonical weather payloads in `model` and schema/wire identifiers in
`standards`;
- keep Postgres table and row mapping under `internal/sinks/postgres`;
- use explicit registries for built-in sources and normalizers.
Do not:
- move provider parsing, WMO mapping, or canonical weather policy into
`cmd/weatherfeeder`;
- move weather-specific constants, schemas, or validation rules into feedkit;
- put database column metadata or sink-specific tags on canonical model structs;
- replace explicit registries with dynamic plugin loading;
- introduce broad abstractions when a small provider-specific helper preserves
clarity.
## Dependency Policy
Prefer the Go standard library unless a dependency materially improves
maintainability or interoperability.
`feedkit` owns generic daemon infrastructure for config, HTTP source helpers,
scheduling, processors, dispatch, and sinks. Weatherfeeder code should contain
weather-domain behavior and narrow adapter logic rather than duplicating feedkit
infrastructure.
Do not add broad dependencies for small conveniences. Do not let
dependency-specific types leak across package boundaries unless that dependency
is the package contract.
## Adding Config Fields
Generic config shape is owned by feedkit. Weatherfeeder-specific config behavior
belongs in source or sink constructors, registry setup, and tests.
When adding config behavior:
- validate required params at the adapter boundary;
- keep secrets in environment variables or placeholders, not committed values;
- update [configuration docs](../config.md);
- update maintained examples when the change affects normal operation;
- add or update config-load tests for example files when practical.
## Adding CLI Flags
The executable currently reads `config.yml` from the current working directory.
If CLI flags are added:
- keep parsing in `cmd/weatherfeeder`;
- avoid moving config policy into domain packages;
- update [CLI docs](../cli.md);
- update tests that exercise command behavior.
## Adding A Source Driver
Source drivers should fetch upstream data and emit raw events with minimal
metadata decoding.
Checklist:
- implement the driver under `internal/sources/<provider>`;
- build from `config.SourceConfig`;
- validate required params in the constructor;
- use feedkit HTTP helpers for HTTP polling when applicable;
- emit raw schema constants from `standards`;
- advertise emitted kinds through `Kinds()`;
- decode only metadata needed for event ID and effective time;
- register the driver in `internal/sources/builtins.go`;
- add constructor, kind, and polling tests;
- update config docs and examples when operators need new configuration;
- add provider integration notes when the provider contract needs maintenance
context.
## Adding A Normalizer
Normalizers own provider-to-canonical mapping.
Checklist:
- add one normalizer type per normalizer file;
- match using `Event.Schema`;
- decode raw payloads into provider structs;
- map to canonical `model` payloads;
- use `internal/normalizers/common.Finalize` so envelope handling and float
rounding stay consistent;
- preserve input envelope fields except schema, payload, and effective time;
- register through the provider package and `internal/normalizers/builtins.go`;
- add tests for schema matching, key payload fields, effective time, malformed
required data, and output validation.
## Adding Canonical Models Or Schemas
Canonical event changes affect multiple contracts.
Checklist:
- update payload structs in `model`;
- add or update schema constants in `standards`;
- update [event wire contract docs](../integrations/events.md);
- update normalizers that produce the schema;
- update Postgres mapping if the schema is persisted;
- add tests for wire shape and mapper behavior.
## Adding Postgres Mapping
Weatherfeeder owns the canonical-event-to-table mapping. Feedkit owns the
generic Postgres sink mechanics.
Checklist:
- update `internal/sinks/postgres/schema.go`;
- update `internal/sinks/postgres/map.go`;
- preserve event envelope columns in parent rows when the table supports them;
- validate required canonical fields before writing;
- use positional indexes for child rows that represent arrays;
- update mapper and schema tests;
- update [Postgres integration docs](../integrations/postgres.md) when the table
contract changes.
## Examples And Documentation
Documentation must follow the [documentation policy](documentation.md).
When behavior changes, update the canonical docs in the same change:
- config shape: `docs/config.md`;
- CLI behavior: `docs/cli.md`;
- operations and recovery: `docs/operations.md`;
- troubleshooting: `docs/troubleshooting.md`;
- public Go package consumption: `docs/consumers/`;
- external contracts: `docs/integrations/`;
- internal component behavior: `docs/internal/`;
- copyable configs: `examples/`.
Keep roadmap content under `docs/roadmap/`. Current-behavior docs must describe
implemented behavior only.
## Review Checklist
Before committing:
- run focused tests for changed packages;
- run `go test ./...` for broad behavior or documentation changes tied to code;
- verify maintained examples still load when examples or config docs changed;
- check links in changed docs;
- search for stale paths, unsupported features, and secret-like values;
- keep unrelated refactors out of the change.