diff --git a/docs/integrations/scriptorium.md b/docs/integrations/scriptorium.md index e4f7416..0cf04bc 100644 --- a/docs/integrations/scriptorium.md +++ b/docs/integrations/scriptorium.md @@ -1,34 +1,17 @@ -# weatherreporter Subprocess Integration +# Scriptorium Integration + +This document describes the external Scriptorium CLI contract used by +`weatherreporter`. ## Purpose -This document defines the supported subprocess contract for weatherreporter invoking Scriptorium through the public CLI. +`weatherreporter` invokes Scriptorium as a subprocess to preflight prompt input +and generate Markdown reports. This page documents the CLI surface the adapter +uses, not the full Scriptorium product. -This is a CLI contract, not an internal Go package integration. +## Commands Used -## Supported Commands - -weatherreporter should invoke: - -- `scriptorium run` -- `scriptorium render` - -Use `run` for generation. - -Use `render` for preflight/debug output without LLM execution. - -## Recommended Invocation Shapes - -Run: - -```bash -scriptorium run \ - --prompt \ - --input data_package= \ - --out -``` - -Render: +Render preflight: ```bash scriptorium render \ @@ -37,78 +20,78 @@ scriptorium render \ --format json ``` -weatherreporter may add: +Report generation: -- `--config ` -- `--profile ` -- repeatable `--input name=path` -- repeatable `--var name=value` -- runtime overrides when explicitly needed (`--model`, `--llm-base-url`, `--timeout`, etc.) +```bash +scriptorium run \ + --prompt \ + --input data_package= \ + --out +``` -## Config And Directory Behavior +`weatherreporter` always passes prompt input as +`--input data_package=`. The data package is structured JSON created by +`internal/promptinput`. -weatherreporter can rely on resolved app config or pass explicit paths. +## Configured Arguments -- default config search order: - 1. `/usr/local/etc/scriptorium/config.yml` - 2. `/etc/scriptorium/config.yml` -- explicit `--config` requires file existence and valid syntax -- CLI flags override config values +The adapter can prepend configured flags before prompt-specific arguments: -## Profile Selection +- `--config ` from `scriptorium.config_path` +- `--profile ` from `scriptorium.profile` -Profile selection follows runner behavior: +It appends `scriptorium.extra_args` after the built-in arguments. Extra +arguments are passed directly as argv items. -1. explicit `--profile` -2. prompt `default_profile` -3. error if neither is available +`scriptorium.binary` selects the executable name or path. If unset inside the +adapter, it falls back to `scriptorium`. -weatherreporter should treat prompt/profile IDs as deployment configuration, not hardcoded logic. +## Execution Behavior -## Input And Variable Contract +The adapter runs Scriptorium without shell interpolation. Arguments are passed +through `exec.CommandContext`. -- Inputs use repeated `--input name=path`. -- Input names must match prompt definition input names. -- Variables use repeated `--var name=value` for small metadata values. -- Prefer file inputs for large content. +`scriptorium.timeout` limits each subprocess call when configured. Context +cancellation or timeout returns an execution error. -## Environment Contract +Stdout and stderr are captured separately. Each stream is capped at 1 MiB and +the result records whether truncation occurred. -- Pass through required API-key environment variables referenced by `api_key_env`. -- Never pass raw API keys via CLI arguments. -- Keep subprocess environment scoped to required variables. +## Results -## Output And Error Handling +Render results include: -`run`: +- full argv recorded as `command` +- stdout +- stderr +- exit code +- truncation flags when applicable -- stdout: artifact body unless `--out` is used -- `--out`: writes artifact to file -- stderr: success summary and errors +Run results include the same fields plus the requested output path. -`render`: +`weatherreporter` persists render preflight JSON when orchestration reaches the +preflight save point. The final Markdown artifact is written by Scriptorium to +the `--out` path. -- stdout: prepared-run output unless `--out` is used -- stderr: errors +## Failure Behavior -weatherreporter should capture stdout and stderr separately. +The adapter validates required request fields before starting Scriptorium: -## Exit Status Contract +- prompt ID +- data package path +- output path for `run` -- `0`: success -- `1`: parse/config/load/render/generation/IO/runtime error -- `2`: run completed but validation failed +Nonzero exits return both the captured result and an error containing the exit +code and stderr. A `run` exit code such as `2` is still treated as an error by +the adapter, even if Scriptorium wrote output to the requested artifact path. -A `run` exit code `2` can still produce output (stdout or `--out`). +Subprocess start failures, context cancellation, and timeouts return errors +without fabricating a successful result. ## Security Notes -- Treat generated artifacts and stderr logs as potentially sensitive. -- Avoid logging full rendered prompts by default in production contexts. -- Use controlled output paths and access controls for persisted artifacts. - -## Canonical References - -- CLI behavior: [CLI reference](https://gitea.maximumdirect.net/eric/scriptorium/docs/cli.md) -- Config behavior: [Configuration reference](https://gitea.maximumdirect.net/eric/scriptorium/docs/config.md) -- Operations and failure handling: [Operations guide](https://gitea.maximumdirect.net/eric/scriptorium/docs/operations.md), [Troubleshooting](https://gitea.maximumdirect.net/eric/scriptorium/docs/troubleshooting.md) +- The adapter does not invoke a shell. +- Generated artifacts, rendered prompt context, stdout, and stderr can contain + operationally sensitive data. +- API keys should be provided through the Scriptorium environment or + Scriptorium configuration, not through `weatherreporter` CLI arguments. diff --git a/docs/integrations/weatherapi.md b/docs/integrations/weatherapi.md index 07f6f5b..4f27ab3 100644 --- a/docs/integrations/weatherapi.md +++ b/docs/integrations/weatherapi.md @@ -1,354 +1,122 @@ -# weatherapi External API +# Weather API Integration -This document describes the public HTTP API exposed by `weatherapi` for external consumers. +This document describes the external Weather API contract used by +`weatherreporter`. + +## Purpose + +`weatherreporter` uses a configured Weather API base URL to fetch normalized +weather source data and assemble a `forecast.Bundle`. This is an integration +contract for the project adapter, not a complete public API reference for the +upstream service. ## Base URL -The service is typically served at your deployment host, for example: +`weather_api.base_url` must be an absolute URL. Adapter requests join this base +URL with the endpoint paths listed below. Generation and explicit bundle fetches +fail before any HTTP request when the base URL is empty or not absolute. -- `https://weather.api.rakestrawhome.com` +The HTTP client uses `weather_api.timeout`. -All paths below are relative to the service root. +## Response Envelope -## Common Conventions - -### Response envelope - -All endpoints return a top-level envelope: - -- `data`: endpoint payload or `null` when no current/latest resource is available. - -JSON example: +Every response used by the adapter must be JSON with a top-level `data` field: ```json { - "data": {"...": "..."} + "data": {} } ``` -### Output format +`data: null` is treated as a missing source. Missing optional sources follow the +configured missing-source policy. Missing hourly forecast data fails bundle +fetching because hourly periods are required for report generation. -Supported via `format` query parameter (case-insensitive): +Malformed JSON envelopes, non-2xx statuses, and response read failures include +endpoint context in returned errors. Decode errors include source context when +they fail the fetch; optional malformed sources follow the missing-source policy. -- `json` (default) -- `xml` -- `text` +## Query Parameters -### Units +The adapter sends these query parameters: -Supported via `units` query parameter (case-insensitive): +- `format`: from `weather_api.format`; the implemented configuration requires + `json` +- `units`: from `weather_api.units` +- `precision`: from `weather_api.precision` on observations, current + conditions, hourly forecast, and narrative forecast requests +- `tz`: from `weather_api.timezone` on hourly forecast, narrative forecast, and + discussion requests -- `metric` (default) -- `us` +Alerts do not receive `precision` or `tz`. -Endpoints that include unit-based numeric fields return either metric or US field variants depending on this value. +## Endpoints Used -### Precision +The adapter fetches these endpoints once per bundle: -Supported where documented via `precision` query parameter: +- `/observations` +- `/conditions/current` +- `/forecast/hourly` +- `/forecast/narrative` +- `/alerts/active` +- `/discussion` -- integer range: `0` to `2` -- controls decimal rounding of numeric output fields +`weatherreporter` does not call day-slice forecast endpoints or discussion +subsection endpoints. Report-period selection and daypart summarization happen +inside Go after the full hourly and narrative products are fetched. -### Timezone (`tz` / `TZ`) +## Required And Optional Sources -Supported where documented: +Hourly forecast is required: -- accepted values include: - - IANA timezone names (example: `America/Chicago`) - - common US abbreviations (example: `CDT`, `EST`) - - UTC offsets in `±H`, `±HH`, or `±HH:MM` (example: `-5`, `+09:30`) - - aliases including `Chicago` and `Stl` -- `tz` and `TZ` are treated equivalently -- if both are provided, they must match exactly or the request fails +- `data: null` for `/forecast/hourly` fails the fetch. +- an hourly forecast with no `periods` fails the fetch. +- malformed hourly data fails the fetch. -Timezone affects datetime rendering and day-slice filtering for `/today` and `/tomorrow` forecast routes. +Other fetched sources are optional and follow `missing_source.default` or a +source-specific `missing_source.sources` policy: -### Query validation +- `observations` for `/observations` +- `current` for `/conditions/current` +- `narrative` for `/forecast/narrative` +- `alerts` for `/alerts/active` +- `discussion` for `/discussion` -- Unknown query parameters are rejected with `400 Bad Request`. -- Invalid parameter values are rejected with `400 Bad Request`. +The adapter also creates missing stub source records for `daily` and +`weather_story` because those source slots exist in the internal bundle but are +not fetched from the Weather API. -Error response body follows the service error envelope; exact fields may vary by error type. +Policy behavior: -## Endpoints +- `error`: fail the fetch for that source +- `warn`: omit the source data, add a warning, and continue +- `none`: omit the source data and continue without a warning -## `GET /observations` +## Source Identity -Returns the latest weather observation. +For non-null source payloads, the adapter records: -Query parameters: +- source name +- endpoint path +- query parameters sent +- fetch time +- source issue and update timestamps when present in the payload +- SHA-256 hash of the compact raw `data` JSON -- `units`: `metric` | `us` -- `format`: `json` | `xml` | `text` -- `precision`: `0..2` +Warnings are recorded both on the affected source and on the bundle-level +warnings list. -Response `data` fields: +## Compatibility Assumptions -- `stationId` (string, optional) -- `stationName` (string, optional) -- `timestamp` (RFC3339 datetime, required) -- `conditionCode` (integer WMO code, required) -- `isDay` (boolean, optional) -- `textDescription` (string, optional) -- Metric mode fields: - - `temperatureC`, `dewpointC`, `windSpeedKmh`, `windGustKmh`, `barometricPressurePa`, `visibilityMeters`, `relativeHumidityPercent`, `apparentTemperatureC` (number, optional) - - `windDirectionDegrees` (number, optional) -- US mode fields: - - `temperatureF`, `dewpointF`, `windSpeedMph`, `windGustMph`, `barometricPressureInHg`, `visibilityMiles`, `relativeHumidityPercent`, `apparentTemperatureF` (number, optional) - - `windDirectionDegrees` (number, optional) -- `presentWeather` (array, optional) +The adapter expects payload fields compatible with the internal forecast bundle +types in `internal/forecast/bundle.go`, including: -## `GET /alerts/active` +- observation timestamps and observation values +- current condition values +- forecast run metadata and `periods` +- active alert run data +- discussion metadata, key messages, and short/long-term sections -Returns the latest active alert run. - -Query parameters: - -- `units`: `metric` | `us` (accepted; does not materially alter alert payload) -- `format`: `json` | `xml` | `text` - -Response `data` fields: - -- Weather alert run object from canonical model (includes run metadata and active alerts list). - -## `GET /conditions/current` - -Returns current conditions synthesized from latest observation/forecast data. - -Query parameters: - -- `units`: `metric` | `us` -- `format`: `json` | `xml` | `text` -- `precision`: `0..2` - -Response `data` fields: - -- Common: - - `conditionText` (string, optional) - - `isDay` (boolean, optional) - - `relativeHumidityPercent` (number, optional) - - `windDirectionDegrees` (number, optional) -- Metric mode: - - `temperatureC`, `apparentTemperatureC`, `dewpointC`, `windSpeedKmh` (number, optional) -- US mode: - - `temperatureF`, `apparentTemperatureF`, `dewpointF`, `windSpeedMph` (number, optional) - -## Forecast endpoints - -- `GET /forecast/hourly` -- `GET /forecast/hourly/today` -- `GET /forecast/hourly/tomorrow` -- `GET /forecast/narrative` -- `GET /forecast/narrative/today` -- `GET /forecast/narrative/tomorrow` - -Query parameters: - -- `units`: `metric` | `us` -- `format`: `json` | `xml` | `text` -- `precision`: `0..2` -- `tz` or `TZ`: timezone selector - -Day-slice routes: - -- `/today` returns periods with `period.startTime` in the current calendar day for the resolved timezone. -- `/tomorrow` returns periods with `period.startTime` in the next calendar day for the resolved timezone. - -Response `data` fields: - -- Run-level: - - `locationId` (string, optional) - - `locationName` (string, optional) - - `issuedAt` (RFC3339 datetime, required) - - `updatedAt` (RFC3339 datetime, optional) - - `product` (string, required; e.g. `hourly`, `narrative`) - - `latitude`, `longitude` (number, optional) - - Metric mode: `elevationMeters` (number, optional) - - US mode: `elevationFeet` (number, optional) - - `periods` (array, required) -- Period fields: - - `startTime`, `endTime` (RFC3339 datetime, required) - - `name` (string, optional) - - `isDay` (boolean, optional) - - `conditionCode` (integer WMO code, optional) - - `textDescription` (string, optional) - - Metric mode (optional): - - `temperatureC`, `temperatureCMin`, `temperatureCMax`, `dewpointC`, `windSpeedKmh`, `windGustKmh`, `barometricPressurePa`, `visibilityMeters`, `apparentTemperatureC`, `cloudCoverPercent`, `probabilityOfPrecipitationPercent`, `precipitationAmountMm`, `snowfallDepthMM`, `uvIndex`, `relativeHumidityPercent`, `windDirectionDegrees` - - US mode (optional): - - `temperatureF`, `temperatureFMin`, `temperatureFMax`, `dewpointF`, `windSpeedMph`, `windGustMph`, `barometricPressureInHg`, `visibilityMiles`, `apparentTemperatureF`, `cloudCoverPercent`, `probabilityOfPrecipitationPercent`, `precipitationAmountIn`, `snowfallDepthIn`, `uvIndex`, `relativeHumidityPercent`, `windDirectionDegrees` - -Notes: - -- Narrative periods may omit `conditionCode`. -- Text format uses forecast-specific templates (`hourly` and `narrative`). - -## Discussion endpoints - -- `GET /discussion` -- `GET /discussion/key-messages` -- `GET /discussion/short-term` -- `GET /discussion/long-term` - -Query parameters: - -- `units`: `metric` | `us` (accepted; does not materially alter discussion payload) -- `format`: `json` | `xml` | `text` -- `tz` or `TZ`: timezone selector - -Response `data` fields: - -- `/discussion`: - - `officeId` (string, optional) - - `officeName` (string, optional) - - `product` (string, required) - - `issuedAt` (RFC3339 datetime, required) - - `updatedAt` (RFC3339 datetime, optional) - - `keyMessages` (array of string) - - `shortTerm` (object, optional) - - `longTerm` (object, optional) -- `/discussion/key-messages`: - - `officeId`, `officeName`, `product`, `issuedAt`, `updatedAt` - - `keyMessages` (array of string) -- `/discussion/short-term`: - - `officeId`, `officeName`, `product`, `issuedAt`, `updatedAt` - - `shortTerm` (object, optional) -- `/discussion/long-term`: - - `officeId`, `officeName`, `product`, `issuedAt`, `updatedAt` - - `longTerm` (object, optional) - -Discussion section object fields: - -- `title` (string, optional) -- `narrative` (string, optional) -- `issuedAt` (RFC3339 datetime, optional) - -## Examples - -### Observation (JSON, metric) - -```http -GET /observations?format=json&units=metric&precision=1 -``` - -```json -{ - "data": { - "stationId": "KSTL", - "timestamp": "2026-05-29T14:00:00Z", - "conditionCode": 3, - "isDay": true, - "textDescription": "Partly cloudy", - "temperatureC": 24.4, - "windSpeedKmh": 17.2, - "relativeHumidityPercent": 56.0 - } -} -``` - -### Alerts (JSON) - -```http -GET /alerts/active?format=json -``` - -```json -{ - "data": { - "asOf": "2026-05-29T14:00:00Z", - "alerts": [] - } -} -``` - -### Current conditions (JSON, US) - -```http -GET /conditions/current?format=json&units=us&precision=1 -``` - -```json -{ - "data": { - "conditionText": "Partly cloudy", - "isDay": true, - "temperatureF": 75.9, - "apparentTemperatureF": 76.1, - "windSpeedMph": 10.7, - "relativeHumidityPercent": 56.0 - } -} -``` - -### Forecast narrative (JSON, optional `conditionCode`) - -```http -GET /forecast/narrative?format=json&units=metric&precision=1&tz=America/Chicago -``` - -```json -{ - "data": { - "locationId": "nws-lsx-grid-90-74", - "issuedAt": "2026-05-29T10:30:00-05:00", - "product": "narrative", - "periods": [ - { - "startTime": "2026-05-29T13:00:00-05:00", - "endTime": "2026-05-29T19:00:00-05:00", - "name": "Today", - "isDay": true, - "textDescription": "Partly sunny, with a high near 81.", - "temperatureC": 27.2, - "windSpeedKmh": 18.0, - "probabilityOfPrecipitationPercent": 10.0 - } - ] - } -} -``` - -### Forecast hourly today (text) - -```http -GET /forecast/hourly/today?format=text&units=us&precision=1&tz=CDT -``` - -```text - -``` - -### Discussion key messages (JSON) - -```http -GET /discussion/key-messages?format=json&tz=Chicago -``` - -```json -{ - "data": { - "officeId": "LSX", - "product": "discussion", - "issuedAt": "2026-05-29T09:25:00-05:00", - "keyMessages": [ - "Scattered showers possible this evening.", - "Warmer temperatures this weekend." - ] - } -} -``` - -### Invalid timezone error example - -```http -GET /forecast/narrative?tz=not-a-timezone -``` - -```json -{ - "error": { - "code": "invalid_parameter", - "message": "tz must be a valid timezone" - } -} -``` +The adapter intentionally keeps upstream transport and envelope details inside +`internal/adapters/weatherapi`; downstream packages consume the normalized +bundle.