diff --git a/docs/cli.md b/docs/cli.md index 932b953..568cada 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -1,7 +1,7 @@ # Weatherreporter CLI -`weatherreporter` generates Markdown weather reports, runs scheduled report -batches, and inspects stored artifacts. +`weatherreporter` generates weather reports, runs report batches, and inspects +artifacts already stored in its workspace. ## Shortest Useful Command @@ -9,14 +9,11 @@ batches, and inspects stored artifacts. weatherreporter generate today --out ./today.md ``` -This loads configuration, collects weather data, writes managed workspace -artifacts, runs `scriptorium render` as a preflight check, runs structured -`scriptorium run`, validates generated text, renders the embedded Today -template, and writes an extra Markdown copy to `./today.md`. If distributor -notification is enabled in configuration, the command also uploads the managed -Markdown report after final metadata is saved. +The command uses the configured Weather API and writes an extra Markdown copy +at `./today.md`. See the [configuration reference](config.md) to supply the +required Weather API endpoint. -## Commands +## Commands And Usage ```text weatherreporter --help @@ -37,57 +34,38 @@ weatherreporter inspect prior [--config PATH] RUN_ID weatherreporter inspect sources [--config PATH] RUN_ID ``` -Implemented `generate` commands emit a compact JSON summary to stdout on -success. The summary includes command identity, report identity, RunID, status, -valid period, and managed artifact paths. They also write a JSON module -snapshot, YAML data package, preflight artifact, managed Markdown report, and -metadata under the configured workspace. `--out` writes an extra Markdown copy -for the operator; distributor notification uses the managed report path, not -the extra copy. `generate daily`, -`generate today`, `generate tomorrow`, and `generate hourly` write managed -generated-text artifacts, validate structured text from Scriptorium, and render -the managed Markdown report from embedded templates. `generate daily` requires -`--date YYYY-MM-DD` for the selected local civil day; omitting `--date` is a -command error and stops before weather data is collected. `generate hourly` -covers the next six hours in the effective report timezone and does not accept -date or event window flags. `generate storm` requires explicit event-window -bounds with `--start` and `--end`. +| Command | Contract | +| --- | --- | +| `generate daily` | Requires `--date YYYY-MM-DD`; the date is interpreted in the effective report timezone. | +| `generate today` | Accepts an optional `--date YYYY-MM-DD`; without it, the current local date in the effective report timezone is used. | +| `generate tomorrow`, `three-day`, `weekend` | Use their report-defined valid period and accept the common generate flags. | +| `generate hourly` | Covers the next six hours in the effective report timezone. It does not accept `--date`, `--start`, `--end`, `--hours`, or `--duration`. | +| `generate storm` | Requires both `--start TIME` and `--end TIME`. Each time may be `YYYY-MM-DDTHH:MM` in the effective timezone or an RFC3339 timestamp with an explicit offset. | +| `run morning` and `run evening` | Run their defined report batches. `--out-dir` writes extra Markdown copies; `--out` is not accepted. | -`run morning` generates Today Report, Tomorrow Report, and a dated Daily Report -for each later future local civil day with complete hourly forecast coverage. -`run evening` generates Tomorrow Report and the same eligible future Daily -reports. Future Daily expansion starts with the day after tomorrow and skips -days that do not have every hourly forecast period for the local civil day. -Batch commands collect weather data once before planning; a collection failure -stops the batch before any report is generated. Batch runs continue independent -reports after a later report failure, print a JSON summary to stdout, write -compact status lines to stderr, and return nonzero when any report failed. -`--out-dir` writes extra Markdown copies for the operator; distributor -notification uses managed report paths, not the extra copies. Today and -Tomorrow use their report default copy names, and dynamic Daily copies use -`daily-YYYY-MM-DD.md`. When distributor and batch notification are enabled, a -fully successful batch uploads one distributor bundle after report generation -finishes. The JSON summary exposes that upload as a top-level `notification` -object, and stderr includes one `batchNotification` status line. If any planned -report fails, the batch notification is skipped for the whole batch. +`generate` accepts all seven report command names shown above. `run` accepts +only `morning` and `evening`. Batch membership, workspace artifacts, and +notification sequencing are described in the [operations guide](operations.md). -Hourly Report, 3-Day Outlook, and Weekend Outlook are explicit only; they are -not included in `run morning` or `run evening`. +## Output, Errors, And Quiet Mode -`inspect` commands read existing workspace artifacts and emit the requested -JSON data to stdout. They do not collect weather data or invoke `scriptorium`. -Inspection commands do not accept `--quiet`. +Action commands (`generate` and `run`) write a JSON summary to stdout unless +`--quiet` is set. `run` also writes compact per-report and batch status lines +to stderr. A pre-run error, such as an invalid flag, missing required argument, +or configuration-load failure, produces no partial JSON summary. When an action +fails after it has produced a result, its summary has `"status": "failed"` and +an `error` field. -## Output +`--quiet` is supported by action commands only. It suppresses action summaries +and routine batch status output; it does not suppress command errors. -Action commands, meaning `generate` and `run`, emit JSON summaries to stdout by -default. Pre-run errors, such as invalid flags, missing required arguments, or -configuration load failures, return an error without emitting partial JSON. -`--quiet` suppresses successful action-command stdout and routine stderr. It -does not hide returned errors. Inspection commands are data-output commands; -they always write the requested JSON to stdout and are not quietable. +Inspection commands always write their requested JSON value to stdout and do +not accept `--quiet`. -Generate summaries have this shape: +### Generate Summary + +A generate summary always identifies the command, report, run, generation +time, valid period, and status: ```json { @@ -101,102 +79,60 @@ Generate summaries have this shape: "validPeriod": { "start": "2026-05-29T00:00:00-05:00", "end": "2026-05-30T00:00:00-05:00" - }, - "reportPath": "workspace/reports/today/2026-05-29/report.20260529T120000.000000000Z_today.md", - "metadataPath": "workspace/snapshots/today/2026-05-29/metadata.20260529T120000.000000000Z_today.json", - "dataPackagePath": "workspace/data-packages/today/2026-05-29/data_package.20260529T120000.000000000Z_today.yaml", - "preflightPath": "workspace/preflight/today/2026-05-29/render.20260529T120000.000000000Z_today.json", - "generatedTextRawPath": "workspace/snapshots/today/2026-05-29/generated_text_raw.20260529T120000.000000000Z_today.json", - "generatedTextResultPath": "workspace/snapshots/today/2026-05-29/generated_text_result.20260529T120000.000000000Z_today.json", - "generatedTextPath": "workspace/snapshots/today/2026-05-29/generated_text.20260529T120000.000000000Z_today.json", - "renderContextPath": "workspace/snapshots/today/2026-05-29/render_context.20260529T120000.000000000Z_today.json" + } } ``` -Markdown-path reports omit the generated-text fields. If distributor -notification is attempted, summaries include `notificationPath`; successful -notification also includes a compact `notification` object. If notification -fails after report artifacts exist, the summary has `"status": "failed"` and an -`error` string while retaining inspectable artifact paths. +When available, the summary also includes `reportPath`, `metadataPath`, +`dataPackagePath`, and `preflightPath`. Generated-text reports additionally +include `generatedTextRawPath`, `generatedTextResultPath`, +`generatedTextPath`, and `renderContextPath`. `outputPath` is included only +when `--out` wrote an extra copy. Distributor notification, when attempted, +adds `notificationPath` and may add a compact `notification` object. -Run summaries have this shape: +### Run Summary And Stderr -```json -{ - "command": "run", - "batch": "morning", - "status": "succeeded", - "startedAt": "2026-05-29T12:00:00Z", - "finishedAt": "2026-05-29T12:01:00Z", - "total": 1, - "succeeded": 1, - "failed": 0, - "reports": [ - { - "reportId": "today", - "reportName": "Today Report", - "promptId": "weather.today_generated_text", - "runId": "20260529T120000.000000000Z_today", - "status": "succeeded", - "generatedAt": "2026-05-29T12:00:00Z", - "validPeriod": { - "start": "2026-05-29T00:00:00-05:00", - "end": "2026-05-30T00:00:00-05:00" - }, - "reportPath": "workspace/reports/today/2026-05-29/report.20260529T120000.000000000Z_today.md", - "metadataPath": "workspace/snapshots/today/2026-05-29/metadata.20260529T120000.000000000Z_today.json", - "dataPackagePath": "workspace/data-packages/today/2026-05-29/data_package.20260529T120000.000000000Z_today.yaml", - "preflightPath": "workspace/preflight/today/2026-05-29/render.20260529T120000.000000000Z_today.json" - } - ] -} -``` +A run summary contains `command`, `batch`, `status`, `startedAt`, `finishedAt`, +`total`, `succeeded`, `failed`, and a `reports` array. It may also contain a +top-level `notification` object and `error`. Batch status is `failed` if any +report or the batch notification fails. -`run` status is `failed` when any report failed or the top-level batch -notification failed. Batch stderr uses compact status lines, for example: +Without `--quiet`, batch status lines use this form: ```text report=today status=succeeded output="reports/today.md" batch=morning total=2 succeeded=2 failed=0 ``` -## Flags +## Flag Reference -- `-h`, `--help`: show help. -- `--config PATH`: load configuration from `PATH` instead of `/usr/local/etc/weatherreporter/config.yml`. -- `--units VALUE`: override configured Weather API units for `generate` and `run`. -- `--tz NAME`: override configured Weather API timezone for `generate` and `run`. -- `--out PATH`: write an extra Markdown report copy where supported by the `generate` command. -- `--out-dir PATH`: write extra Markdown report copies for `run morning` and `run evening`. -- `--quiet`: suppress successful stdout and routine stderr for `generate` and `run`. -- `--date YYYY-MM-DD`: required date for `generate daily`; optional date for `generate today`, defaulting to the current local date in the configured timezone. -- `--start TIME`: required start time for `generate storm`. -- `--end TIME`: required end time for `generate storm`. -- `--limit N`: maximum records for `inspect reports`; defaults to `20`, and `0` means no limit. +| Flag | Accepted by | Meaning | +| --- | --- | --- | +| `-h`, `--help` | top level | Show help. | +| `--config PATH` | all commands | Load `PATH` instead of `/usr/local/etc/weatherreporter/config.yml`. | +| `--units VALUE` | `generate`, `run` | Override `weather_api.units` for this command. | +| `--tz NAME` | `generate`, `run` | Override `weather_api.timezone` for this command. | +| `--out PATH` | every `generate` command | Write an extra Markdown report copy. | +| `--out-dir PATH` | `run morning`, `run evening` | Write extra Markdown report copies in `PATH`. | +| `--quiet` | `generate`, `run` | Suppress action summaries and routine batch status output. | +| `--date YYYY-MM-DD` | `generate daily`, `generate today` | Required for Daily; optional for Today. | +| `--start TIME`, `--end TIME` | `generate storm` | Required storm-event bounds. | +| `--limit N` | `inspect reports` | Maximum runs to list. Defaults to `20`; `0` means no limit. | -Storm times accept `YYYY-MM-DDTHH:MM` in the configured timezone or RFC3339 -timestamps with explicit offsets. +Distributor notification is configured through `notify.distributor`; there are +no Distributor-specific CLI flags. See the [configuration reference](config.md). -Distributor notification is configured only through `notify.distributor`; there -are no distributor-specific CLI flags. - -## Common Workflows +## Invocation Examples ```sh -weatherreporter generate today --out ./today.md weatherreporter generate daily --date 2026-05-29 --out ./daily.md -weatherreporter generate tomorrow --out ./tomorrow.md -weatherreporter generate hourly -weatherreporter generate three-day --out ./three-day.md -weatherreporter generate weekend --out ./weekend.md +weatherreporter generate today --date 2026-05-29 --out ./today.md +weatherreporter generate hourly --out ./hourly.md weatherreporter generate storm --start 2026-05-29T18:00 --end 2026-05-30T06:00 --out ./storm.md weatherreporter run morning --out-dir ./reports -weatherreporter run evening --out-dir ./reports -weatherreporter generate today --quiet -weatherreporter run morning --quiet ``` -## Inspection +## Inspection Commands ```sh weatherreporter inspect reports --limit 10 @@ -205,12 +141,17 @@ weatherreporter inspect modules 20260529T100000.000000000Z_today weatherreporter inspect data-package 20260529T100000.000000000Z_today weatherreporter inspect prior 20260529T100000.000000000Z_today weatherreporter inspect sources 20260529T100000.000000000Z_today -weatherreporter inspect metadata 20260529T100000.000000000Z_daily ``` -`inspect reports` lists recent generated runs with artifact paths and source -warning counts. The other inspect commands require a RunID. `inspect modules` -returns the persisted ordered module snapshot for a run. `inspect prior` -returns the prior comparable snapshot metadata selected from stored metadata, or -`null` when none exists. `inspect sources` shows source provenance and source -warnings without dumping full weather payloads. +| Command | JSON returned | +| --- | --- | +| `inspect reports` | Recent generated runs, including artifact paths and source-warning counts. | +| `inspect metadata RUN_ID` | Persisted metadata for the run. | +| `inspect modules RUN_ID` | The run's persisted ordered module snapshot. | +| `inspect data-package RUN_ID` | The run's persisted prompt data package. | +| `inspect prior RUN_ID` | Prior comparable snapshot metadata, or `null` when none exists. | +| `inspect sources RUN_ID` | Source provenance and source warnings without full weather payloads. | + +Inspection is read-only: it does not collect weather data or invoke +`scriptorium`. See the [operations guide](operations.md) for artifact lifecycle +and recovery. diff --git a/docs/config.md b/docs/config.md index 47805c5..53d60d3 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,319 +1,206 @@ # Weatherreporter Configuration -Configuration is YAML. By default, `weatherreporter` reads: +Weatherreporter reads YAML configuration. The default path is: ```text /usr/local/etc/weatherreporter/config.yml ``` -Use `--config PATH` to load a different file. If the default file is absent, -built-in defaults are used. If `--config PATH` points to a missing file, loading -fails. +If the default file is absent, Weatherreporter uses built-in defaults. An +explicit `--config PATH` must exist. Values are applied in this order: -Precedence is: +1. built-in defaults; +2. the configuration file, when present; and +3. the `--units` and `--tz` command-line overrides. -1. CLI flags -2. configuration file -3. built-in defaults +Environment variables do not override configuration fields. Output flags write +extra report copies for a command and do not change configuration. -The CLI configuration overrides are `--units` and `--tz`. Output flags control -report copies for the current command but do not change configuration files. -Environment variables do not override configuration fields. +## Maintained Examples -## Minimal Config +- [minimal-config.yml](../examples/minimal-config.yml) is the smallest useful + collection and generation configuration. +- [config.yml](../examples/config.yml) is a representative production-oriented + configuration using synthetic endpoints and no credentials. -See [examples/minimal-config.yml](../examples/minimal-config.yml). +Both files are loaded by the configuration test suite. + +## Minimal Configuration ```yaml weather_api: base_url: https://weather.api.example.com/ ``` -`weather_api.base_url` is required for commands that collect weather data. -Other fields fall back to defaults. - -## Production-Oriented Config - -See [examples/config.yml](../examples/config.yml). The example is loaded by the -config test suite. +`weather_api.base_url` is required for workflows that collect weather data. +All omitted fields use their built-in defaults. ## Field Reference ### `weather_api` -- `base_url`: absolute base URL for the Weather API. Required for generation and collection workflows. -- `timeout`: HTTP timeout duration. Default: `10s`. -- `precision`: numeric precision query value. Default: `0`, which requests integer values where supported. -- `units`: Weather API units query value. Default: `us`. -- `timezone`: report timezone and Weather API timezone query value where supported. Default: `America/Chicago`. -- `format`: Weather API response format. Must be `json`. Default: `json`. +| Field | Default | Rules | +| --- | --- | --- | +| `base_url` | empty | Absolute Weather API URL. Required for collection and generation. | +| `timeout` | `10s` | Must be greater than zero. | +| `precision` | `0` | Must be zero or greater. Sent as the Weather API precision query value. | +| `units` | `us` | Required Weather API units query value; `--units` overrides it for one command. | +| `timezone` | `America/Chicago` | Required report and Weather API timezone; `--tz` overrides it for one command. | +| `format` | `json` | Required and must be `json`. | Timezone values may be IANA names, configured aliases such as `Chicago` and `Stl`, US timezone abbreviations, or UTC offsets such as `-5` and `+09:30`. ### `location` -`location` is descriptive prompt context included in module metadata and -Scriptorium data packages. It does not select a Weather API endpoint or enable -multiple configured forecast locations. +`location` supplies descriptive prompt context; it does not choose a Weather +API endpoint or configure multiple forecast locations. -- `id`: short local identifier. Default: `home`. -- `name`: human-readable location name. Default: `Brentwood`. -- `region`: broader forecast area context. Default: `St. Louis Metro`. +| Field | Default | +| --- | --- | +| `id` | `home` | +| `name` | `Brentwood` | +| `region` | `St. Louis Metro` | -The prompt-facing location object also includes `timezone`, derived from the -effective `weather_api.timezone` after CLI overrides such as `--tz`. +The prompt-facing location timezone is derived from the effective +`weather_api.timezone` after command-line overrides. ### `secrets` -- `directory`: optional directory of file-backed environment secrets. Default: - empty, which disables secret loading. +`secrets.directory` defaults to empty, which disables secret loading. When it +is set, every regular file directly in that directory is loaded after the file +and command-line overrides. A file basename must match +`[A-Za-z_][A-Za-z0-9_]*`; it becomes an environment variable name, and the +file contents replace any existing value. One trailing LF or CRLF is removed. -When configured, each regular file directly under `secrets.directory` is loaded -after config file parsing and CLI overrides. The file basename must be a valid -environment variable name matching `[A-Za-z_][A-Za-z0-9_]*`; the file contents -become the environment variable value and overwrite any existing value. One -trailing LF or CRLF is stripped. Subdirectories, symlinks, invalid filenames, -missing directories, and unreadable files fail config loading. +Missing directories, unreadable files, subdirectories, symlinks, non-regular +files, and invalid names fail configuration loading. Put only secret values in +this directory, never in the YAML file. -### `notify` +### `notify.distributor` -`notify.distributor` controls distributor uploads after successful report -rendering. It is disabled by default and does not add CLI flags. When enabled, -`generate ` uploads one distributor bundle for the generated report -after final metadata is saved. `run morning` and `run evening` use -`notify.distributor.batch`: when batch notification is enabled and every -planned report succeeds, weatherreporter uploads one distributor bundle that -contains all managed Markdown reports from that batch. +Distributor notification is disabled by default. Its fields are: -- `enabled`: whether distributor notification config is active. Default: - `false`. -- `endpoint`: absolute distributor endpoint URL. Required when enabled. - Default: `https://distributor.example.com`. -- `token_env`: environment variable name that will contain the distributor - upload token. Required when enabled. Default: `DISTRIBUTOR_UPLOAD_TOKEN`. -- `timeout`: distributor operation timeout. Must be greater than zero when - enabled. Default: `30s`. -- `failure_policy`: must be `error` when enabled. Default: `error`. -- `pipeline_id_template`: template for single-report distributor pipeline IDs. - Required when enabled. Default: empty. -- `bundle_id_template`: template for single-report distributor bundle IDs. - Default: `weatherreporter.{location_id}.{report_id}`. -- `idempotency_key_template`: template for single-report distributor - idempotency keys. Default: `{bundle_id}.{run_id}`. -- `batch.enabled`: whether batch distributor notification config is active - when distributor notification is enabled. Default: `true`. -- `batch.pipeline_id_template`: template for batch distributor pipeline IDs. - Required when distributor notification and batch notification are enabled. - Default: `weatherreporter`. -- `batch.bundle_id_template`: template for batch distributor bundle IDs. - Required when distributor notification and batch notification are enabled. - Default: `weatherreporter.{location_id}.{batch}`. -- `batch.idempotency_key_template`: template for batch distributor idempotency - keys. Required when distributor notification and batch notification are - enabled. Default: `{bundle_id}.{batch_run_id}`. +| Field | Default | Rules when notification is enabled | +| --- | --- | --- | +| `enabled` | `false` | Activates Distributor notification validation. | +| `endpoint` | `https://distributor.example.com` | Must be an absolute URL. | +| `token_env` | `DISTRIBUTOR_UPLOAD_TOKEN` | Must name a valid environment variable. | +| `timeout` | `30s` | Must be greater than zero. | +| `failure_policy` | `error` | Must be `error`. | +| `pipeline_id_template` | empty | Required single-report pipeline ID template. | +| `bundle_id_template` | `weatherreporter.{location_id}.{report_id}` | Required single-report bundle ID template. | +| `idempotency_key_template` | `{bundle_id}.{run_id}` | Required single-report idempotency-key template. | +| `batch.enabled` | `true` | Activates batch notification validation when Distributor notification is enabled. | +| `batch.pipeline_id_template` | `weatherreporter` | Required when batch notification is enabled. | +| `batch.bundle_id_template` | `weatherreporter.{location_id}.{batch}` | Required when batch notification is enabled. | +| `batch.idempotency_key_template` | `{bundle_id}.{batch_run_id}` | Required when batch notification is enabled. | -Single-report templates support `location_id`, `report_id`, `run_id`, +The upload token is read from the environment variable named by `token_env`. +Use `secrets.directory` when a file-backed secret is appropriate. + +Single-report bundle templates accept `location_id`, `report_id`, `run_id`, `artifact_group`, `batch_output_name`, `valid_start_date`, `valid_end_date`, `valid_start_time`, `valid_end_time`, `valid_start_stamp`, `valid_end_stamp`, -and `storm_id`. Date values use `YYYY-MM-DD`, time values use `HHMM`, and -stamp values use `YYYY-MM-DDTHHMM` in the effective report timezone. -`storm_id` is derived from the storm report valid period as -`{valid_start_stamp}-{valid_end_stamp}`; it renders empty for non-storm -reports. `pipeline_id_template` and `idempotency_key_template` may also use -`bundle_id`. +and `storm_id`. Pipeline and idempotency-key templates may also use +`bundle_id`. Dates use `YYYY-MM-DD`; times use `HHMM`; and stamps use +`YYYY-MM-DDTHHMM` in the effective report timezone. `storm_id` is +`{valid_start_stamp}-{valid_end_stamp}` for Storm Report and empty otherwise. -The rendered pipeline ID selects the configured distributor `http_upload` -workflow. The rendered bundle ID is the stable logical source identity for the -report stream. The rendered idempotency key is the per-run retry identity. +Batch bundle and pipeline templates accept `location_id`, `batch`, +`batch_run_id`, and `batch_started_date`; batch idempotency-key templates may +also use `bundle_id`. `batch_started_date` is the batch start date in the +effective report timezone. -Batch templates support `location_id`, `batch`, `batch_run_id`, and -`batch_started_date`. Batch idempotency templates may also use `bundle_id`. -`batch_started_date` is the batch start date in the effective report timezone. -Batch bundle IDs identify a logical batch stream; batch idempotency keys -identify a specific retryable batch attempt. +`reports..distributor.path_templates` overrides the default ordered +Distributor paths for that report. Each rendered path must be a unique relative +path with `/` separators. Backslashes, empty segments, `.` and `..` segments, +`manifest.json`, and the reserved Distributor sidecar basename are rejected. +The default paths are: -Rendered report paths must be unique relative paths with `/` separators. They -must not contain backslashes, empty path segments, `.`, `..`, `manifest.json`, -or the reserved distributor sidecar basename, formed from a leading dot plus -`distributor.json`. In a batch upload, uniqueness is checked across every -rendered bundle path for every included report before distributor is called. -Managed Markdown report paths are the only upload source files; copies written -with `--out` or `--out-dir` are never uploaded. +| Report | Paths | +| --- | --- | +| `hourly` | `hourly/index.md` | +| `daily` | `daily/{valid_start_date}/{run_id}.md`, `daily/{valid_start_date}/index.md` | +| `today` | `daily/{valid_start_date}/{run_id}.md`, `daily/{valid_start_date}/index.md`, `today/index.md` | +| `tomorrow` | `daily/{valid_start_date}/{run_id}.md`, `daily/{valid_start_date}/index.md`, `tomorrow/index.md` | +| `three_day` | `three-day/{valid_start_date}/{run_id}.md`, `three-day/{valid_start_date}/index.md` | +| `weekend` | `weekend/{valid_start_date}/{run_id}.md`, `weekend/{valid_start_date}/index.md` | +| `storm` | `storm/{storm_id}/{run_id}.md`, `storm/{storm_id}/index.md` | -Distributor bundle paths are report-specific. Weatherreporter uses -`reports..distributor.path_templates` when that override is configured; -otherwise it uses the report definition defaults: - -- `hourly`: `hourly/index.md` -- `daily`: `daily/{valid_start_date}/{run_id}.md`, `daily/{valid_start_date}/index.md` -- `today`: `daily/{valid_start_date}/{run_id}.md`, `daily/{valid_start_date}/index.md`, `today/index.md` -- `tomorrow`: `daily/{valid_start_date}/{run_id}.md`, `daily/{valid_start_date}/index.md`, `tomorrow/index.md` -- `three_day`: `three-day/{valid_start_date}/{run_id}.md`, `three-day/{valid_start_date}/index.md` -- `weekend`: `weekend/{valid_start_date}/{run_id}.md`, `weekend/{valid_start_date}/index.md` -- `storm`: `storm/{storm_id}/{run_id}.md`, `storm/{storm_id}/index.md` - -The upload token is read from the environment variable named by `token_env` -after config loading and `secrets.directory` processing. Config files should -name the variable only; they should not contain the token value. +See the [operations guide](operations.md) for notification timing, uploaded +artifact selection, and failure handling. ### `missing_source` -- `default`: missing-source behavior for optional sources. One of `error`, `warn`, or `none`. Default: `warn`. -- `sources`: optional map of source-specific overrides, using the same policy values. - -Hourly forecast data is required for generated reports. Optional sources use -the missing-source policy. Source override keys include `observations`, -`current`, `narrative`, `alerts`, `discussion`, `weather_story`, and -`spc_convective_outlooks`. +`missing_source.default` defaults to `warn` and accepts `error`, `warn`, or +`none`. `missing_source.sources` optionally overrides that policy by source. +Hourly forecast data is required for generated reports. Supported optional +source keys are `observations`, `current`, `narrative`, `alerts`, `discussion`, +`weather_story`, and `spc_convective_outlooks`. ### `scriptorium` -- `binary`: `scriptorium` executable name or path. Default: `scriptorium`. -- `config_path`: optional Scriptorium config path passed to the adapter. -- `profile`: optional Scriptorium profile passed to the adapter. -- `timeout`: subprocess timeout. Default: `2m`. -- `extra_args`: optional additional arguments passed to Scriptorium commands. +| Field | Default | Rules | +| --- | --- | --- | +| `binary` | `scriptorium` | Required executable name or path. | +| `config_path` | empty | Optional Scriptorium configuration path. | +| `profile` | empty | Optional Scriptorium profile. | +| `timeout` | `2m` | Must be greater than zero. | +| `extra_args` | empty | Optional extra arguments passed to Scriptorium commands. | ### `workspace` -- `root`: workspace root for managed artifacts. Default: `workspace`. -- `snapshots_dir`: module snapshot and metadata directory under `workspace.root`. Default: `snapshots`. -- `reports_dir`: managed Markdown report directory under `workspace.root`. Default: `reports`. -- `data_packages_dir`: prompt input package directory under `workspace.root`. Default: `data-packages`. -- `preflight_dir`: Scriptorium render output directory under `workspace.root`. Default: `preflight`. -- `notifications_dir`: distributor notification debug artifact directory under `workspace.root`. Default: `notifications`. +| Field | Default | +| --- | --- | +| `root` | `workspace` | +| `snapshots_dir` | `snapshots` | +| `reports_dir` | `reports` | +| `data_packages_dir` | `data-packages` | +| `preflight_dir` | `preflight` | +| `notifications_dir` | `notifications` | -Workspace subdirectories must be relative paths that stay inside -`workspace.root`. Managed artifact paths below those directories are grouped by -artifact group and valid-period start date; the path template is not -configurable. +`workspace.root` is required. Each workspace subdirectory must be a relative +path that stays within the root. See the [operations guide](operations.md) for +the managed workspace layout and lifecycle. ### `dayparts` -`dayparts` is a list of named local-time windows used by forecast derivation. -Each entry has: - -- `name` -- `start` -- `end` - -`start` and `end` use `HH:MM`. The default entries are overnight, morning, -midday, afternoon, and evening. +`dayparts` is a non-empty list of named local-time windows used in forecast +derivation. Every item needs `name`, `start`, and `end`; start and end use +`HH:MM`. Defaults are `overnight` (`00:00`–`06:00`), `morning` +(`06:00`–`10:00`), `midday` (`10:00`–`15:00`), `afternoon` +(`15:00`–`17:00`), and `evening` (`17:00`–`24:00`). ### `recent_change` -- `temperature_degrees`: temperature change threshold. Default: `5`. -- `precip_probability_points`: precipitation probability threshold. Default: `20`. -- `wind_gust_miles_per_hour`: wind gust change threshold. Default: `10`. -- `precip_timing_shift_minutes`: precipitation timing shift threshold. Default: `120`. +| Field | Default | +| --- | --- | +| `temperature_degrees` | `5` | +| `precip_probability_points` | `20` | +| `wind_gust_miles_per_hour` | `10` | +| `precip_timing_shift_minutes` | `120` | -Recent Changes are added to prompt input when a prior comparable module -snapshot exists and a threshold is crossed. +These thresholds control when Recent Changes are included in prompt input for a +prior comparable module snapshot. ### `reports` -`reports` optionally overrides the ordered deterministic modules declared by -report definitions. Omit a report entry to use its default module order. +`reports` optionally overrides a report's ordered deterministic modules and +Distributor path templates. Omit a report entry to retain its defaults. -Supported report keys are `daily`, `today`, `tomorrow`, `hourly`, -`three_day`, `weekend`, and `storm`. Canonical report IDs and accepted aliases -are also valid, including `three_day_outlook`, `weekend_outlook`, and -`storm_report`. Hyphens and underscores are treated equivalently in report -keys. Retired report keys are not supported. +Supported report keys are `daily`, `today`, `tomorrow`, `hourly`, `three_day`, +`weekend`, and `storm`. Configuration also accepts `three_day_outlook`, +`weekend_outlook`, and `storm_report`; hyphens and underscores are equivalent. -`reports.today` applies only to the Today Report. `reports.daily` applies only -to the dated Daily Report. +Each report entry can contain: -Each report entry supports: +- `deterministic_modules`: an ordered list of module IDs, or objects with `id` + and optional `options`. +- `distributor.path_templates`: an optional, non-empty ordered list of + Distributor paths for that report. -- `deterministic_modules`: ordered module list. Entries may be string module - IDs or objects with `id` and optional `options`. -- `distributor.path_templates`: optional ordered distributor bundle path - templates for this report. If omitted, the report definition defaults are - used. If present, the list must contain at least one template. - -Example: - -```yaml -reports: - daily: - distributor: - path_templates: - - "daily/{valid_start_date}/{run_id}.md" - - "daily/{valid_start_date}/index.md" - deterministic_modules: - - metadata - - current_conditions - - narrative_forecast - - alert_digest - - spc_convective_outlooks - - id: area_forecast_discussion - options: - sections: - - long_term - - spc_convective_discussion - - daily_planning - - hourly_forecast - today: - deterministic_modules: - - metadata - - current_conditions - - narrative_forecast - - derived_daily_summary - - derived_daypart_summaries - - precip_timing - - alert_digest - - spc_convective_outlooks - - area_forecast_discussion - - spc_convective_discussion - - weather_story - - outdoor_windows - - hourly_forecast - - today_planning - hourly: - deterministic_modules: - - metadata - - current_conditions - - hourly_forecast - - precip_timing - - alert_digest - - spc_convective_outlooks - - id: area_forecast_discussion - options: - sections: - - key_messages - - short_term - - spc_convective_discussion - - weather_story -``` - -Unknown reports, unknown modules, duplicate modules, incompatible report/module -combinations, duplicate stanza names, and invalid options fail config loading. -`area_forecast_discussion.options.sections` may contain `product`, -`key_messages`, `short_term`, and `long_term`. Empty or omitted `sections` -includes all available AFD sections. Default report definitions may choose a -smaller report-specific subset, such as daily reports using only `long_term`. - -The module registry accepts all module IDs documented in -[Module Contract Internals](internal/module.md). Unknown or unimplemented -module IDs fail validation instead of being skipped. - -## Secrets - -Configuration files should not contain raw secrets. Use `secrets.directory` to -load secret values from files into environment variables for integrations that -read credentials from the environment. Secret file names become environment -variable names, and secret file contents become values. For distributor -notification, this allows a file such as -`/DISTRIBUTOR_UPLOAD_TOKEN` to supply the token referenced by -`notify.distributor.token_env`. - -## Maintained Examples - -- [examples/minimal-config.yml](../examples/minimal-config.yml): smallest - useful config for generation and fetching. -- [examples/config.yml](../examples/config.yml): production-oriented config - covering maintained fields. - -Both example files are loaded by the config test suite. +Unknown reports and modules, duplicate modules, incompatible report-module +combinations, duplicate stanza names, invalid path templates, and invalid +module options fail configuration loading. The accepted module IDs and module +option contracts are documented in the [module contract internals](internal/module.md).