Keep generated prose out of Markdown structure

This commit is contained in:
2026-08-13 02:17:11 +00:00
parent 2bd921f247
commit a18d5134c7
12 changed files with 233 additions and 13 deletions

View File

@@ -70,5 +70,7 @@ go test ./internal/generatedtext
```
Generated text supplies prose slots only; deterministic weather facts remain in
module and fact values. Every report definition must resolve to exactly one
supported catalog pair.
module and fact values. The renderer applies its plain-text policy to every
generated prose insertion, preserving ordinary text and paragraph breaks while
preventing provider text from creating Markdown or HTML structure. Every report
definition must resolve to exactly one supported catalog pair.

View File

@@ -30,7 +30,11 @@ partials cover daypart forecast variants, alert digest, and precipitation
timing. Template code receives curated typed contexts rather than raw data
packages or complete fact bundles, and it must not reimplement weather
selection or generated-text validation. Context construction rejects
report-identity disagreements before template execution.
report-identity disagreements before template execution. Every generated-prose
insertion uses the `plainText` helper. It retains ordinary prose and paragraph
breaks but renders Markdown/HTML syntax, code indentation, and control
characters as safe text, so the repository templates remain the sole owners of
report structure.
## Boundaries and verification

View File

@@ -47,6 +47,11 @@ from rendering.
calculations, source selection, or prompt-input shaping to a template.
- Keep generated prose in `.GeneratedText`; do not restate deterministic facts
in generated prose merely to compensate for a template change.
- Render every `.GeneratedText` value through `plainText`. It preserves prose
and paragraph breaks while escaping Markdown and HTML syntax, removing code
indentation, and replacing control characters. Never interpolate generated
prose directly: repository templates alone own headings, lists, links, and
other Markdown structure.
- When changing the generated-prose contract, update the matching prompt,
schema, validator, render context, and template together. The validation and
catalog rules are owned by [Generated Text internals](internal/generatedtext.md).
@@ -69,7 +74,7 @@ Minimal list pattern:
```gotemplate
{{ range .GeneratedText.ForecastDiscussion }}
{{ . }}
{{ plainText . }}
{{ end }}
```
@@ -82,6 +87,7 @@ Templates have these helpers in addition to Go template built-ins:
| `hasRelevantAlerts` | an alert-digest value or pointer | its `Relevant` slice is nonempty |
| `hasEnhancedOrHigherSPCRisk` | an SPC outlook value or pointer | its `RiskDigest` contains an Enhanced, Moderate, or High Risk entry |
| `isEnhancedOrHigherSPCRisk` | one SPC risk-digest entry | its `LabelText`, or fallback `RiskLabel`, is Enhanced, Moderate, or High Risk |
| `plainText` | a generated prose string | a readable plain-text rendering that preserves paragraph breaks without allowing dynamic Markdown or HTML structure |
For example, the alert partial uses the first two functions to decide whether
to render the section: