Clean up completed roadmap documents
All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
# Current Conditions Condition-Code Selection
|
||||
|
||||
## Summary
|
||||
|
||||
Improve `/conditions/current` so numeric conditions continue to aggregate from recent observations, but `conditionCode` is selected by source-balanced WMO family consensus instead of numeric maximum. The goal is to prevent a single bad high WMO code, such as an erroneous thunderstorm code, from dominating current conditions while still returning a useful code when providers report semantically similar conditions.
|
||||
|
||||
## Target Behavior
|
||||
|
||||
Current conditions continue to use the application observation window, currently `app.ObservationWindowMinutesDefault`.
|
||||
|
||||
Numeric and directional fields remain aggregate values over recent `observations` rows:
|
||||
|
||||
- temperature, apparent temperature, dewpoint, relative humidity, and wind speed use averages;
|
||||
- wind direction uses circular averaging;
|
||||
- `isDay` comes from the latest row in the window.
|
||||
|
||||
`conditionCode` uses source-balanced consensus:
|
||||
|
||||
1. Select the latest observation per `event_source` within the current window.
|
||||
2. Each source contributes at most one WMO condition-code vote.
|
||||
3. Map each voted WMO code to a semantic family.
|
||||
4. Select the family with the highest source vote count.
|
||||
5. If the family vote is tied, return `model.WMOUnknown`.
|
||||
6. Within the winning family, select the most frequent exact WMO code.
|
||||
7. If exact-code vote is tied within the winning family, select the first code by family-specific representative ranking.
|
||||
8. If there are no recognized condition-code votes, return `model.WMOUnknown`.
|
||||
|
||||
Family mapping and tie ranking:
|
||||
|
||||
| Family | Codes / tie ranking |
|
||||
| --- | --- |
|
||||
| `clear_or_cloud` | `0`, `1`, `2`, `3` |
|
||||
| `fog` | `45`, `48` |
|
||||
| `drizzle` | `51`, `53`, `55`, `56`, `57` |
|
||||
| `rain` | `61`, `63`, `65`, `80`, `81`, `82`, `66`, `67` |
|
||||
| `snow` | `71`, `73`, `75`, `85`, `86`, `77` |
|
||||
| `thunderstorm` | `95`, `96`, `99` |
|
||||
|
||||
Examples:
|
||||
|
||||
| Source votes | Result |
|
||||
| --- | --- |
|
||||
| `0`, `1`, `2` | `0` |
|
||||
| `1`, `2`, `95` | `1` |
|
||||
| `0`, `95` | `model.WMOUnknown` |
|
||||
| `61`, `63`, `80` | `61` |
|
||||
| `61`, `95`, `0` | `model.WMOUnknown` |
|
||||
| only `95` | `95` |
|
||||
|
||||
## Policy Decisions
|
||||
|
||||
- No public response schema change is required.
|
||||
- No weatherfeeder change or database migration is required because `observations.event_source`, `condition_code`, `observed_at`, and `event_emitted_at` already exist.
|
||||
- Provider-specific blacklists, trust weights, and source priorities are intentionally out of scope for this change.
|
||||
- `WMOUnknown` is preferable to falsely choosing between tied precipitation, thunderstorm, and clear/cloud families.
|
||||
- Current conditions remain a latest-window read model, not a durable derived table.
|
||||
@@ -1,110 +0,0 @@
|
||||
# Implement Source-Balanced Current Conditions Condition Codes
|
||||
|
||||
## Summary
|
||||
|
||||
Implement the target behavior in `docs/roadmap/current.md`: keep `/conditions/current` response shape unchanged, continue aggregating numeric observations over the current window, and replace SQL `MAX(condition_code)` with Go-based source-balanced WMO family consensus.
|
||||
|
||||
This is a behavior change only. Do not add public fields, change query parameters, alter weatherfeeder tables, or add provider-specific blacklists.
|
||||
|
||||
## Stage 1: Add Condition-Code Consensus Helpers
|
||||
|
||||
Add package-local helpers in `internal/adapters/outbound/postgres` for current-conditions condition-code selection.
|
||||
|
||||
Required data shape:
|
||||
|
||||
- define a small candidate row/type containing `event_source` and `condition_code`;
|
||||
- the selector accepts latest-per-source candidates and returns `model.WMOCode`.
|
||||
|
||||
Required selector behavior:
|
||||
|
||||
- use the family mapping and tie ranking from `docs/roadmap/current.md` exactly;
|
||||
- ignore unrecognized WMO codes for family voting;
|
||||
- return `model.WMOUnknown` when no recognized candidates exist;
|
||||
- count each source at most once;
|
||||
- return `model.WMOUnknown` on tied family votes;
|
||||
- inside the winning family, choose the most frequent exact code;
|
||||
- when exact codes tie inside the winning family, choose by family ranking.
|
||||
|
||||
Add focused unit tests for the selector before wiring SQL changes.
|
||||
|
||||
## Stage 2: Split Current-Conditions Condition-Code Querying
|
||||
|
||||
Update the Postgres current-conditions read path so condition-code selection is no longer computed with `MAX(condition_code)`.
|
||||
|
||||
Required SQL behavior:
|
||||
|
||||
- keep the existing aggregate query for sample count, numeric averages, circular wind direction, and latest `is_day`;
|
||||
- remove `MAX(condition_code)` from the aggregate query result;
|
||||
- add a separate query that returns one latest condition-code candidate per `event_source` inside the same observation window;
|
||||
- latest per source is ordered by `observed_at DESC, event_emitted_at DESC`;
|
||||
- candidate columns should include `event_source` and `condition_code`; timestamp columns may remain SQL-only if used only for ordering.
|
||||
|
||||
Required repository flow:
|
||||
|
||||
1. query aggregate current conditions;
|
||||
2. return `nil, nil` when the aggregate sample count maps to no data, preserving current behavior;
|
||||
3. query condition-code candidates using the same observation window;
|
||||
4. run the Go selector;
|
||||
5. map numeric aggregate fields plus selected condition code into `app.CurrentConditions`.
|
||||
|
||||
Keep row DTOs and mappers local to the Postgres adapter. Do not move SQL or row types into `internal/app`.
|
||||
|
||||
## Stage 3: Preserve API And Presentation Behavior
|
||||
|
||||
Keep all existing `/conditions/current` API behavior except condition-code selection.
|
||||
|
||||
Required invariants:
|
||||
|
||||
- `conditionCode` remains present in JSON/XML/text responses through the existing presenter path;
|
||||
- `conditionText` continues to derive from selected `conditionCode` and `isDay`;
|
||||
- `format`, `units`, and `precision` behavior is unchanged;
|
||||
- no `tz` support is added;
|
||||
- no public response fields are added or removed.
|
||||
|
||||
Update existing endpoint or presenter tests only if expected condition codes need to change because of the new selector.
|
||||
|
||||
## Stage 4: Update Current-Behavior Documentation
|
||||
|
||||
After implementation is complete, update current-behavior docs outside roadmap:
|
||||
|
||||
- `docs/api.md`: current conditions aggregate numeric fields and choose `conditionCode` by source-balanced WMO family consensus;
|
||||
- `docs/internal/postgres-repository.md`: current conditions use an aggregate row plus a latest-per-source condition-code candidate query;
|
||||
- `docs/integrations/weatherfeeder-postgres.md`: current conditions use `observations.event_source` for source-balanced condition-code selection.
|
||||
|
||||
Do not document any unimplemented options such as provider blacklists, trust weights, or source priorities.
|
||||
|
||||
## Test Plan
|
||||
|
||||
Selector tests:
|
||||
|
||||
- `0`, `1`, `2` returns `0` by clear/cloud ranking;
|
||||
- `1`, `2`, `95` returns `1` because clear/cloud family wins;
|
||||
- `0`, `95` returns `model.WMOUnknown` because families tie;
|
||||
- `61`, `63`, `80` returns `61` by rain ranking;
|
||||
- `61`, `95`, `0` returns `model.WMOUnknown` because three families tie;
|
||||
- only `95` returns `95`;
|
||||
- unrecognized-only candidates return `model.WMOUnknown`;
|
||||
- duplicate candidates from the same source do not produce multiple votes if the selector receives them.
|
||||
|
||||
Repository tests:
|
||||
|
||||
- current conditions no longer chooses the highest numeric condition code;
|
||||
- latest condition-code candidate per source uses `observed_at DESC, event_emitted_at DESC`;
|
||||
- aggregate no-sample behavior still returns nil;
|
||||
- numeric aggregate fields, wind direction, and `isDay` mapping remain unchanged;
|
||||
- SQL/query errors from the candidate query include operation context.
|
||||
|
||||
Verification commands:
|
||||
|
||||
```sh
|
||||
go test ./internal/adapters/outbound/postgres ./internal/app
|
||||
go test ./internal/adapters/inbound/httpapi ./internal/adapters/inbound/httpapi/presenter
|
||||
go test ./...
|
||||
```
|
||||
|
||||
## Assumptions
|
||||
|
||||
- `observations.event_source` is non-null in the weatherfeeder-owned schema and is safe to use as the source identity.
|
||||
- The latest-per-source query can be implemented with existing Postgres features and does not require a migration.
|
||||
- Current conditions remain repository-owned because they are a Postgres aggregate read model; no new app service method or public interface is needed.
|
||||
- The implementation should remain standard-library and SQL based; do not add dependencies.
|
||||
Reference in New Issue
Block a user