Complete the Stage 9 briefing audit

This commit is contained in:
2026-08-12 16:03:43 +00:00
parent ae52606772
commit edde330390

View File

@@ -1,6 +1,6 @@
# Repository Audit Ledger
Status: In progress; Stages 1-8 complete.
Status: In progress; Stages 1-9 complete.
This temporary roadmap document is the evidence ledger for the staged audit
defined by [the audit plan](audit-plan.md). It records audit evidence and
@@ -56,7 +56,16 @@ empty weather-story object is published as available. It also found one
low-severity snapshot compatibility defect because validation accepts any
nonempty schema version. Default composition, supported-report rules,
source-module omission, checked-empty alerts, ordered snapshots, and curated
current/hourly prompt exports otherwise match their contracts. Subsystem
current/hourly prompt exports otherwise match their contracts. Stage 9 found
six medium-severity briefing defects: apparent temperature is
published as heat index, normalized daypart keys can overwrite one another,
planning applies configurable daypart names inconsistently, outdoor-window
ranking ignores snow, ice, and fog, subzero temperatures are described as
positive zero-degree bands, and embedded descriptions labeled as official SPC
definitions have drifted from the current SPC category table. Derived
precipitation timing, deterministic display ordering, missing-data behavior,
SPC overlap and threshold selection, and the intended Today versus
Daily/Tomorrow planning variation otherwise match their contracts. Subsystem
conclusions and final disposition remain pending the later stages.
## Baseline Metadata
@@ -192,7 +201,7 @@ inventory commands, graph index refresh, and graph architecture inspection.
| 6 | Audit weather data acquisition and collection | Complete |
| 7 | Audit forecast and fact derivation | Complete |
| 8 | Audit module contracts, registry, and source-facing briefing modules | Complete |
| 9 | Audit derived, planning, formatting, and SPC briefing modules | Pending |
| 9 | Audit derived, planning, formatting, and SPC briefing modules | Complete |
| 10 | Audit prompt inputs, assets, and neutral execution contracts | Pending |
| 11 | Audit Promptkit adaptation and secure prompt debugging | Pending |
| 12 | Audit generated-text validation and catalog contracts | Pending |
@@ -226,7 +235,7 @@ with evidence about meaningful risks, test ownership, gaps, and duplication.
| CLI parsing, output, and exit behavior | `cmd/weatherreporter`, `internal/cli`, representative app-facing CLI tests | 5 | Partly insufficient. Command/flag ownership, paths, one-executor mapping, stream separation, quiet mode, comparison safe errors, and visible result/exit rules are coherent. `AUD-008` records missing signal cancellation, `AUD-009` records late generate date validation, and `AUD-010` records missing assembled non-comparison CLI protection. |
| Weather transport and normalized collection | `internal/adapters/weatherapi`, `internal/collect`, `internal/weatherdata` | 6 | Partly insufficient. All eight source requests, required/optional policy, checked-empty products, retryable source statuses, cancellation, normalized provenance, and collection error ownership have focused offline coverage. `AUD-011` through `AUD-015` record unsupported schemes, unsafe response-body diagnostics, an unenforced body-size limit, structurally invalid required hourly periods, and overbroad warmup retries. |
| Forecast and fact derivation | `internal/forecast`, `internal/facts` | 7 | Partly insufficient. Half-open selection, stable ordering, alert parsing and clipping, precipitation-window grouping, report-family selection, and SPC checked-empty behavior have focused owners. `AUD-016` through `AUD-019` record unit collapse, overnight alert omission, invalid percentage acceptance, and incomplete ice-text classification. |
| Module and briefing contracts | `internal/module`, `internal/briefing` | 8-9 | Stage 8 is partly insufficient; Stage 9 remains pending. Registry/default composition, report support, missing optional sources, checked-empty alerts, output identity, ordering, and current/hourly prompt curation have focused owners. `AUD-020` through `AUD-022` record inconsistent pointer options, false weather-story availability, and permissive snapshot schema validation. Derived, planning, formatting, and SPC behavior remains assigned to Stage 9. |
| Module and briefing contracts | `internal/module`, `internal/briefing` | 8-9 | Partly insufficient. Registry/default composition, report support, missing optional sources, checked-empty alerts, output identity, ordering, current/hourly prompt curation, precipitation timing, and SPC selection have focused owners. `AUD-020` through `AUD-028` record inconsistent pointer options, false weather-story availability, permissive snapshot schema validation, apparent-temperature mislabeling, daypart-key collisions, inconsistent planning identities, incomplete outdoor hazard scoring, incorrect subzero phrases, and stale official SPC descriptions. |
| Prompt inputs, embedded assets, and execution contracts | `internal/promptinput`, `internal/promptassets`, `internal/promptexec` | 10 | Pending |
| Promptkit boundary and sensitive debug output | `internal/adapters/promptkit`, `internal/promptdebug` | 11 | Pending |
| Generated-text validation | `internal/generatedtext` | 12 | Pending |
@@ -1045,6 +1054,219 @@ with evidence about meaningful risks, test ownership, gaps, and duplication.
- Related findings: none
- Remediation reference: pending
### AUD-023: Apparent temperature is published as heat index
- Stage: 9
- Status: candidate
- Severity: medium
- Confidence: high
- Category: correctness
- Area: `internal/briefing.derivedDailySummaryValue` and
`DerivedDailySummaryModule.HeatIndexMaxF`
- Evidence: Daypart derivation aggregates the source's generic
`ApparentTemperatureF` values into `DaypartSummary.ApparentTemperature`.
The daily briefing builder takes the maximum of that range unconditionally
and publishes it as `heat_index_max_f`. There is no heat-indicator,
temperature, humidity, or source-field check that establishes the value as a
heat index. The ordinary focused test uses a hot apparent value and therefore
confirms only the coincidentally valid case; no cold, mild, or wind-chill
case exists.
- Contract at risk: User-facing derived fields must retain the meteorological
meaning of their source values rather than relabel a generic feels-like
temperature as a specific heat hazard measurement.
- Impact: Cold and ordinary days can carry a purported heat-index maximum that
is actually wind chill or another apparent-temperature estimate. Prompt and
render consumers can then make a false heat claim from a correctly collected
source value.
- Recommendation: Preserve the field as apparent temperature with unit-aware
naming, or populate a heat-index field only from an explicit heat-index
source or a documented, validated derivation under heat-appropriate
conditions.
- Test implications: Add hot, mild, and below-freezing apparent-temperature
cases. Assert that only a semantically established heat index receives that
label, while generic apparent temperature remains available under its own
identity.
- Validation: Cold and mild inputs never emit a heat-index claim; hot inputs
retain an accurately named value; focused briefing and downstream schema or
render tests pass.
- Related findings: `AUD-016`
- Remediation reference: pending
### AUD-024: Normalized daypart keys can silently overwrite summaries
- Stage: 9
- Status: candidate
- Severity: medium
- Confidence: high
- Category: correctness
- Area: `buildDerivedDaypartSummariesModule`, `daypartKey`, `normalizedKey`,
and daypart configuration validation
- Evidence: The builder stores every derived daypart in a map keyed by a
lowercase name whose non-alphanumeric runs become underscores. It assigns
without checking whether the key already exists. Configuration requires only
a nonblank name and parseable clocks; it does not require unique names or
unique normalized names. Consequently duplicate names, or distinct names
such as `early morning` and `early-morning`, produce the same key and the
later summary silently replaces the earlier one. Existing tests exercise
ordinary unique default names only.
- Contract at risk: Every configured local-time window selected into derived
facts must remain represented in the briefing module and prompt package.
- Impact: A valid configuration can silently lose a complete daypart's
temperature, precipitation, hazard, and alert facts, causing generated
reports to reason from an incomplete day.
- Recommendation: Define one canonical daypart identity policy and reject
duplicate canonical identities during configuration validation. Retain a
defensive collision error in the module builder so programmatic facts cannot
overwrite data silently.
- Test implications: Add exact-duplicate, case-only, punctuation-normalized,
and non-colliding Unicode/name cases at configuration and builder boundaries.
Assert an explicit error rather than map length alone.
- Validation: Every accepted daypart maps to one distinct output entry, and
every collision is rejected before prompt construction.
- Related findings: `AUD-025`
- Remediation reference: pending
### AUD-025: Planning applies configurable daypart identities inconsistently
- Stage: 9
- Status: candidate
- Severity: medium
- Confidence: high
- Category: correctness
- Area: `buildTodayPlanning`, `buildMorningCommuteOvernightPlanning`, and
`daypartNamed`
- Evidence: Morning, overnight, afternoon, and evening role lookup uses
case-insensitive `daypartNamed`, while the commute/workday loop excludes only
names exactly equal to lowercase `overnight` or `evening`. Daypart names are
configurable nonblank strings, and neither configuration nor derivation
canonicalizes them into semantic roles. A configured `Overnight` window is
therefore used for overnight-watch guidance and also treated as a daytime
commute/workday window; renamed windows silently lose the special planning
role altogether. Focused planning tests use only lowercase default names.
- Contract at risk: Equivalent accepted configuration identities must produce
equivalent planning behavior, and display labels must not double as
inconsistently compared semantic keys.
- Impact: Harmless casing changes can duplicate overnight/evening hazards into
workday concerns, while customized labels can replace real readiness or
late-day analysis with generic fallback text.
- Recommendation: Separate stable semantic daypart roles from display names,
or explicitly constrain and canonicalize the names accepted by
configuration. Apply that policy once across every planning lookup and
exclusion.
- Test implications: Exercise default, mixed-case, and renamed configurations
through Today, Tomorrow, and Daily planning, asserting which windows feed
morning, workday, late-day, and overnight sections.
- Validation: Equivalent daypart configurations yield identical planning
classification, and unsupported role configurations fail explicitly rather
than degrading to fallback prose.
- Related findings: `AUD-024`
- Remediation reference: pending
### AUD-026: Outdoor-window ranking ignores snow, ice, and fog
- Stage: 9
- Status: candidate
- Severity: medium
- Confidence: high
- Category: correctness
- Area: `scoreOutdoorWindow` and Today outdoor planning
- Evidence: Outdoor scoring considers precipitation probability, gust speed,
alert count, heat, and cold. It does not inspect the already-derived snow,
ice, or fog indicators. When one of those indicators occurs without a high
precipitation probability, gust, alert, heat, or cold signal, the daypart
receives score zero and the reason `quiet weather`; it can be selected as the
best outdoor window. The same planning helper file correctly treats snow and
ice as travel concerns and fog as a morning visibility concern, proving the
facts are available. Existing outdoor tests cover precipitation, wind,
alerts, heat, and cold but no indicator-only winter or fog case.
- Contract at risk: Best/worst outdoor guidance and its reasons must account
for material weather constraints already present in the selected daypart.
- Impact: A snowy, icy, or foggy period can be recommended as the day's best
outdoor window and explicitly called quiet, creating misleading planning
advice even when upstream classification is correct.
- Recommendation: Give each supported outdoor hazard an explicit scoring and
reason policy, sharing the indicator vocabulary with planning notes where
appropriate. Document tie behavior and avoid implying safety merely from a
zero score.
- Test implications: Add isolated snow, ice, and fog cases plus mixed hazards
and deterministic ties. Assert both ranking and user-facing reasons.
- Validation: None of the supported hazard-only periods is described as quiet,
and best/worst selection reflects the documented relative policy.
- Related findings: `AUD-019`
- Remediation reference: pending
### AUD-027: Subzero temperatures are rendered as positive zero-degree bands
- Stage: 9
- Status: candidate
- Severity: medium
- Confidence: high
- Category: correctness
- Area: `temperatureBandPhrase`, `temperatureBandIndex`, and derived daypart
temperature trend prose
- Evidence: Both helpers compute a decade with Go integer division toward zero.
Values from `-9` through `-1` therefore receive decade `0`; the absolute
remainder then produces phrases such as `mid 0s` or `upper 0s`, which denote
positive temperatures. The same band index can also distort trend ordering
around zero. Focused phrase tests cover only positive values in the upper
60s through lower 80s, so realistic cold-weather values are unprotected.
- Contract at risk: Temperature phrases and trend summaries must preserve the
sign and ordering of the underlying Fahrenheit values.
- Impact: A dangerous below-zero forecast can be understated by roughly ten
degrees and described on the wrong side of zero in prompt-facing daypart
summaries.
- Recommendation: Define explicit signed temperature-band semantics, including
conventional `below zero` wording and monotonic band ordering across zero,
rather than applying positive-decade arithmetic to negative values.
- Test implications: Add table-driven values on both sides of every zero-area
boundary, including `-11`, `-10`, `-9`, `-5`, `-1`, `0`, `1`, and `9`, plus
rising/falling trends that cross zero.
- Validation: Every negative input produces unambiguously below-zero wording,
and band indices remain monotonic from colder to warmer values.
- Related findings: `AUD-016`
- Remediation reference: pending
### AUD-028: Embedded official SPC category descriptions have drifted
- Stage: 9
- Status: candidate
- Severity: medium
- Confidence: high
- Category: correctness
- Area: `assets/spc_convective_outlook_definitions.json` and
`spcOutlookBackgroundDefinition`
- Evidence: The embedded asset labels one field `official_description` but
carries no source URL, source date, or revision metadata. The
[current NOAA Storm Prediction Center outlook reference](https://www.spc.noaa.gov/about/outlooks/),
updated March 3, 2026, defines
Thunderstorms as an area with a 10% or higher thunderstorm probability and
describes Marginal through High using current organization, coverage, and
intensity language. Multiple embedded strings differ materially, including
Thunderstorms as only `No severe thunderstorms expected` and Slight as
isolated intense storms that are short-lived or not widespread. Tests lock
the key set and nonblank grammar but do not compare externally owned meaning
or provenance. The CIG vocabulary and conditional-versus-probability warning
otherwise align with the current NOAA explanation.
- Contract at risk: Content presented as an official external definition must
identify and track its authoritative source closely enough to avoid
misrepresenting current risk terminology.
- Impact: Every matching SPC outlook can carry stale or inaccurately attributed
background text into prompts and reports, where users may treat it as NOAA's
current official description.
- Recommendation: Refresh the categorical descriptions from the current SPC
reference, record source URL and revision date beside the asset, and define a
periodic review/update owner. If repository prose is intentionally
interpretive, rename the field so it is not represented as verbatim official
guidance.
- Test implications: Retain schema/key/lookup tests, add required provenance,
and protect stable domain identifiers rather than freezing unsourced prose.
A maintainer review should compare externally owned wording at each update.
- Validation: Every `official_description` is traceable to the recorded
authoritative revision, and repository-authored summaries are labeled as
such; asset and briefing tests pass.
- Related findings: none
- Remediation reference: pending
## Retained Decisions
### RET-001: Keep the application package as the explicit composition owner
@@ -1251,6 +1473,29 @@ distinction is useful prompt and template information and has a focused
behavioral test. Reconsider only if the normalized source availability model
changes or all downstream consumers gain an equivalent status channel.
### RET-019: Keep Today planning richer than Daily and Tomorrow planning
Daily and Tomorrow intentionally share one morning, commute/workday, and
overnight planning shape, while Today adds outdoor-window guidance and a
late-day change watch. The distinction follows the report horizon: Today can
act on the remainder of the current day, whereas Daily and Tomorrow retain a
compact next-day readiness structure. Builders reuse the common planning
helper and focused tests protect both shapes. This is meaningful product
variation rather than drift. Reconsider only if report schemas intentionally
converge or a new horizon requires the same additional planning sections.
### RET-020: Keep SPC outlook records separate from selected briefing digests
The SPC outlook module preserves all selected overlapping outlook records and
adds background definitions only for known type/label pairs. Its risk digest
then applies a location-specific categorical severity threshold, while the
discussion module separately selects discussion days from qualifying
categorical outlooks. Focused tests cover type, rank, location, day, overlap,
unknown definitions, and checked-empty behavior. Keeping the complete selected
record set distinct from narrower editorial digests preserves evidence without
forcing every consumer through one filter. Reconsider only if SPC product
semantics or the report contract adopts a single shared selection policy.
## Open Questions
No Stage 1 open questions or unexplained baseline failures remain.
@@ -1387,6 +1632,23 @@ Stage 8 routed these investigation leads to their assigned later stages:
should verify the complete serialized prompt boundary and intended curation;
Stage 8 accounts only for their source-to-module transformations.
Stage 9 routed these investigation leads to their assigned later stages:
- The derived and planning modules amplify the unit collapse in `AUD-016`, the
overnight-alert omission in `AUD-017`, invalid percentages in `AUD-018`, and
incomplete ice classification in `AUD-019`. Stage 9 did not duplicate those
upstream causes; remediation and later end-to-end tests should protect the
downstream wording and thresholds as well.
- `titleWord` uppercases the first byte rather than the first rune, and several
daypart-oriented helpers repeat display and semantic-name handling. The
consequential identity behavior is captured by `AUD-024` and `AUD-025`;
Stage 23 should assess Unicode-safe shared formatting and removal of repeated
day-style policy without turning display labels into domain identities.
- Prompt serialization and rendering of the newly audited fields remain with
Stages 10 and 13. Stage 24 should decide where the externally maintained SPC
definition source and review date belong in canonical documentation after
`AUD-028` is remediated.
## Stage Log
### Stage 1: Establish The Baseline And Audit Ledger
@@ -1784,3 +2046,58 @@ Stage 8 routed these investigation leads to their assigned later stages:
- Retained decisions: `RET-016`, `RET-017`, and `RET-018`.
- Open questions: the four leads recorded above are routed to their assigned
later stages.
### Stage 9: Audit Derived, Planning, Formatting, And SPC Briefing Modules
- Status: Complete.
- Scope reviewed: the remaining production code and focused tests in
`internal/briefing`: daily and daypart summaries, precipitation timing,
outdoor windows, Daily/Today/Tomorrow planning, shared summary and formatting
helpers, SPC outlooks and discussions, and the embedded SPC definition
asset; plus relevant briefing, facts, configuration, and authoritative SPC
references needed to judge the domain rules.
- Exclusions: Complete prompt serialization and embedded prompt assets remain
Stage 10; generated-text schemas and render contexts remain Stages 12-13;
application preparation remains Stage 14; test-portfolio and cross-cutting
cleanup remain Stages 19-23; canonical documentation reconciliation remains
Stage 24; and adversarial dynamic checks remain Stage 25.
#### Derived And Planning Rule Accounting
| Module or risk | Threshold, ordering, missing-data, and test evidence | Disposition |
| --- | --- | --- |
| Daily summary | Selects narrative high/low and daily precipitation when present, falls back to daypart aggregates, preserves deterministic conditions/hazards, and emits timing/gust facts. | Ordinary and fallback selection are exercised. Apparent temperature is incorrectly relabeled as heat index in `AUD-023`; unit fallback remains upstream `AUD-016`. |
| Daypart summaries | Emit local periods, ranges, condition/hazard flags, precipitation and gust maxima, temperature phrases/trends, and configured normalized keys; date prefixes prevent ordinary multi-day collisions. | Field and positive-temperature behavior is exercised. Canonical key collisions are `AUD-024`, subzero wording is `AUD-027`, and unit interpretation remains `AUD-016`. |
| Precipitation timing | Maps dry and rainy windows, open-ended periods, expectation thresholds, type phrases, thunder mentions, and local labels without mutating derived order. | Matches the reviewed briefing contract with focused rainy, dry, threshold, phrase, and missing-daypart cases. Percentage-domain defects remain `AUD-018`. |
| Outdoor windows | Scores precipitation, gusts, alerts, heat, and cold; preserves deterministic input-order ties and exposes best/worst periods and reasons to Today planning. | Incomplete hazard policy: snow, ice, and fog can score as quiet; `AUD-026`. |
| Planning variants | Daily and Tomorrow share morning, workday, and overnight sections; Today intentionally adds outdoor and late-day sections. Empty daily summaries produce empty modules or defined fallback prose according to builder context. | Report variation is intentional and retained as `RET-019`. Configurable name classification is inconsistent in `AUD-025`; upstream unit/alert/text defects are not duplicated. |
| User-facing formatting | Time/date labels use the requested location with UTC fallback, wind compass boundaries are focused-tested, strings are deduplicated in stable order, and sentence casing is Unicode-aware. | Ordinary formatting is coherent. Signed temperature bands are incorrect in `AUD-027`; byte-oriented title casing is routed to Stage 23 after the identity defects. |
| SPC outlooks | Preserve selected overlapping records and checked/missing status, attach definitions only for recognized normalized keys, and compute a location-specific categorical risk digest at severity rank 3 or higher. | Selection, ordering, threshold equality, type, location, overlap, unknown definitions, and empty/missing states have focused tests; separation retained as `RET-020`. Official categorical descriptions have drifted in `AUD-028`. |
| SPC discussion | Includes selected discussions only for days with an overlapping categorical outlook meeting the rank threshold; ignores high-rank noncategorical products and nonoverlapping outlooks. | Matches the explicit regional discussion policy with focused threshold, day, type, overlap, missing-text, and missing-source cases. |
| Complexity and repetition | Scoped builders perform small linear maps over configured dayparts, precipitation windows, outlooks, and discussions. The highest production cognitive scores are in planning, precipitation type selection, and outdoor scoring, with no material service-sized cost. | No Stage 9 performance finding. Repeated daypart role/display logic is routed to Stage 23 with concrete evidence. |
| Test ownership | One derived-module suite owns daily/daypart/precipitation/planning behavior, format-helper tests own compass mapping, and dedicated SPC suites own risk/discussion/definition behavior. | Mostly behavioral. Missing semantic regressions are attached to `AUD-023` through `AUD-028`, not inferred from coverage alone. |
#### Commands And Evidence
- Used graph symbol and augmented-code search, source snippets, inbound traces,
and complexity queries for every remaining builder/helper, configured
daypart identities, apparent-temperature flow, outdoor hazard inputs, signed
temperature bands, SPC selection/definition lookup, and their focused tests.
Bounded text inspection was used for scoped documents, configuration
examples, and the embedded JSON asset.
- Compared SPC vocabulary and externally attributed definitions with NOAA/NWS
Storm Prediction Center's current convective-outlook reference and the
[2026 Conditional Intensity service-change notice](https://www.weather.gov/media/notification/pdf_2026/scn26-11_SPC_conditional-intensity.pdf).
Categorical definition drift is recorded as `AUD-028`; supported CIG
identifiers and the distinction between occurrence probability and
conditional intensity remain coherent.
- Ran
`go test -coverprofile=/tmp/weatherreporter-stage9-cover.out ./internal/briefing ./internal/config ./internal/forecast ./internal/facts`;
all focused packages passed. Statement coverage was 82.5% for briefing,
86.1% for config, 89.7% for forecast, and 73.4% for facts; coverage guided
missing-edge inspection but was not itself a finding.
- Findings: `AUD-023`, `AUD-024`, `AUD-025`, `AUD-026`, `AUD-027`, and
`AUD-028`.
- Retained decisions: `RET-019` and `RET-020`.
- Open questions: the three leads recorded above are routed to their assigned
later stages.