Improve article intelligence projection and generated article UX#1994
Improve article intelligence projection and generated article UX#1994
Conversation
Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/8f235d03-a6f3-452d-ba7f-9142163c1167 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/8f235d03-a6f3-452d-ba7f-9142163c1167 Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🏷️ Automatic Labeling SummaryThis PR has been automatically labeled based on the files changed and PR metadata. Applied Labels: documentation,html-css,translation,testing,refactor,size-xl,news Label Categories
For more information, see |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
…nts roadmap Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/19038edc-3385-422b-ac60-96e9a8643afc Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
There was a problem hiding this comment.
Pull request overview
This PR updates the article aggregation + rendering pipeline to better project “reader-intelligence-first” narratives and improve the generated article UX, while keeping provenance traceable.
Changes:
- Reorders
AGGREGATION_ORDERand injects a deterministic Reader Intelligence Guide into aggregatedarticle.md. - Enhances rendered HTML article headers with a dek (description) and trust/provenance badges, plus CSS updates for the new layout.
- Tightens provenance/source enumeration by excluding generated
article*.mdandpass1/snapshots from artifact lists; updates docs/prompts accordingly.
Reviewed changes
Copilot reviewed 21 out of 97 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/render-lib.test.ts | Updates aggregation-order expectations and adds coverage for reader guide text, link-stripping in metadata extraction, and dek rendering. |
| tests/render-lib-architecture.test.ts | Adjusts architecture assertions to match new aggregation ordering and new header CSS hooks. |
| styles.css | Adds styling for article dek + trust badges, refines article container, and light-scheme overrides. |
| scripts/render-lib/article.ts | Renders the dek and trust/provenance badges inside the article header. |
| scripts/render-lib/aggregator.ts | Re-documents and changes aggregation order; adds Reader Intelligence Guide generation and improved inline-markdown→text cleaning. |
| scripts/render-articles.ts | Excludes pass1/ and generated article*.md from artifact enumeration used for source footers / JSON-LD isBasedOn. |
| political-intelligence.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_sv.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_da.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_no.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_fi.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_de.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_fr.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_es.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_nl.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_ar.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_he.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_ja.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_ko.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| political-intelligence_zh.html | Regenerates collection page (counts + new stream listing + updated modified timestamps). |
| analysis/daily/2026-04-21/realtime-1353/article.md | Shows the new Reader Intelligence Guide insertion and reordered sections in a real generated article. |
| Article-Generation.md | Updates narrative-order documentation, fixes description example, and adds the “Future Improvements Roadmap” section + ToC entry. |
| .github/prompts/06-article-generation.md | Updates workflow prompt contract to include reader guide insertion, new ordering, and refined source enumeration rules. |
| '## Reader Intelligence Guide', | ||
| '', | ||
| 'Use this guide to read the article as a political-intelligence product rather than a raw artifact dump. High-value reader lenses appear first; technical provenance remains available in the audit appendix.', | ||
| '', | ||
| '| Reader need | Where to go | Source artifact |', | ||
| '|---|---|---|', | ||
| ...entries, |
There was a problem hiding this comment.
The Reader Intelligence Guide table header doesn’t match the row content: the second column currently contains the “readerValue” (what the reader gets), not “Where to go”. This is confusing in the rendered article—either rename the header to reflect the content (e.g. “What you’ll get”) or change the row layout so the “Where to go” column is the anchor link and the first column is the reader need label.
| function anchorForTitle(title: string): string { | ||
| return title | ||
| .toLowerCase() | ||
| .normalize('NFKD') | ||
| .replace(/[^\p{L}\p{N}\s-]/gu, '') | ||
| .trim() | ||
| .replace(/\s+/g, '-'); | ||
| } |
There was a problem hiding this comment.
anchorForTitle re-implements heading slug generation, but the actual HTML IDs are produced by rehype-slug (GitHub slugger). If these algorithms diverge (punctuation/Unicode/duplicate-heading suffixes), the Reader Intelligence Guide links will be broken. Consider using the same slugger library as rehype-slug, or emit explicit stable IDs for section headings (e.g. based on the artifact filename) and link to those.
| <ul class="rm-article-trust-badges" aria-label="Article provenance and quality controls"> | ||
| <li>Public sources</li> | ||
| <li>AI-FIRST review</li> | ||
| <li>Traceable artifacts</li> | ||
| </ul> |
There was a problem hiding this comment.
The new trust-badge copy is hard-coded in English (and so is the aria-label). Since the site renders 14 languages and already has a LANGUAGE_META[lang].translations mechanism in chrome, these strings should be sourced from translations as well; otherwise non-English article pages will show English badges in the header.
| display: block; | ||
| overflow-x: auto; | ||
| -webkit-overflow-scrolling: touch; | ||
| border: 1px solid color-mix(in srgb, var(--primary-cyan, #00d9ff) 12%, transparent); | ||
| border-radius: 8px; |
There was a problem hiding this comment.
Setting .rm-article-body table { display: block; } can break table layout/column sizing and may degrade table semantics for assistive tech. A more robust pattern is to keep the table’s native display and wrap it in a scroll container (or apply overflow to a wrapper element) so responsiveness doesn’t sacrifice table behavior.
|
@copilot apply changes based on the comments in this thread |
…le headers Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/a3b09f19-a6f7-4c22-acd8-1507e8b70dde Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Applied all four review points in 36414d7:
All 25 aggregations and 50 EN/SV HTML articles regenerated; 2132 tests pass. |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
| // Disable rehype-sanitize's default `user-content-` ID prefix so heading | ||
| // anchors generated by `rehype-slug` (and matched by the aggregator's | ||
| // Reader Intelligence Guide via `github-slugger`) resolve as written. | ||
| // The aggregator owns the entire markdown body — there is no untrusted | ||
| // user content here that could weaponise DOM-clobbering on element IDs. | ||
| clobberPrefix: '', | ||
| attributes: { |
There was a problem hiding this comment.
Setting clobberPrefix: '' disables the DOM-clobbering mitigation in rehype-sanitize. Even if content is “repo-owned”, headings/IDs still come from AI-generated artifacts and can be influenced by external inputs; keeping the prefix (or another non-empty prefix) is the safer default. Consider restoring a non-empty clobberPrefix and updating the Reader Intelligence Guide anchor generation to include that prefix so links still resolve.
| // Skip if already wrapped (idempotent). | ||
| if ( | ||
| parent.type === 'element' && | ||
| (parent as Element).tagName === 'div' && | ||
| Array.isArray((parent as Element).properties?.className) && | ||
| ((parent as Element).properties!.className as string[]).includes('rm-table-wrap') | ||
| ) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
rehypeWrapTables() checks properties.className only when it is an array. If a table is already wrapped via raw HTML (<div class="rm-table-wrap">), className can be a string in HAST, and this will fail the idempotence check and double-wrap tables. Consider accepting both string and string[] forms when detecting an existing wrapper.
| @media (prefers-color-scheme: light) { | ||
| html:not([data-theme="dark"]) .rm-article-body, | ||
| html:not([data-theme="dark"]) body.rm-article-body { | ||
| color: var(--text-color, #1a1a1a); | ||
| background: var(--bg-color, #f8f9fa); | ||
| } | ||
|
|
||
| html:not([data-theme="dark"]) .rm-article { | ||
| background: var(--card-bg, #ffffff); | ||
| border-color: var(--card-border, #dee2e6); | ||
| box-shadow: 0 16px 48px rgba(0, 0, 0, 0.08); | ||
| } | ||
|
|
||
| html:not([data-theme="dark"]) .rm-article-body p, | ||
| html:not([data-theme="dark"]) .rm-article-body li, | ||
| html:not([data-theme="dark"]) .rm-article-dek { | ||
| color: var(--text-color, #1a1a1a); | ||
| } | ||
|
|
||
| html:not([data-theme="dark"]) .rm-article-body strong { color: #000000; } | ||
| html:not([data-theme="dark"]) .rm-article-body a { color: var(--link-color, #007744); } | ||
| html:not([data-theme="dark"]) .rm-article-body a:hover { color: var(--primary-color, #006633); } | ||
|
|
||
| html:not([data-theme="dark"]) .rm-site-header, | ||
| html:not([data-theme="dark"]) .rm-site-footer { | ||
| background: #ffffff; | ||
| color: var(--text-secondary, #4a4a4a); | ||
| } | ||
|
|
||
| html:not([data-theme="dark"]) .rm-site-header { | ||
| border-bottom-color: var(--card-border, #dee2e6); | ||
| } |
There was a problem hiding this comment.
The new @media (prefers-color-scheme: light) rules target html:not([data-theme="dark"]) ..., which also matches html[data-theme="light"]. Because these rules come after the existing html[data-theme="light"] block, they override explicit light-theme styling (e.g. .rm-site-header background gradient becomes plain white) whenever the OS prefers light. If the intent is only an OS-level fallback, scope these selectors to html:not([data-theme]) (so manual theme choice always wins), or align the overridden values with the existing html[data-theme="light"] rules.
|
@copilot apply changes based on the comments in this thread |
…string className, scope OS dark/light to no-explicit-theme Agent-Logs-Url: https://github.com/Hack23/riksdagsmonitor/sessions/6e4d9e98-5993-43a2-8aaa-a8fca0d66ffb Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Applied all three points in d467d92:
25 aggregations + 50 EN/SV articles regenerated; build, lint and 2132 tests pass. |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
anchorForTitleslugger withgithub-slugger(the same libraryrehype-sluguses) so guide anchors stay in sync with rendered heading IDs across punctuation/Unicode/duplicate casesrehype-slugwith a customrehypeSlugWithPrefixplugin that pre-prefixes heading IDs withrm-, keeprehype-sanitize's DOM-clobbering mitigation enabled (clobberPrefix: 'rm-',iddropped fromclobberto avoid double-prefix), and alignrehype-autolink-headingshrefs and the aggregator's Reader Intelligence Guide anchors to the same prefixLANGUAGE_META[lang].translations.articleTrust*<table>elements in<div class="rm-table-wrap">via a custom rehype plugin and removedisplay: blockfrom.rm-article-body tableto preserve native table layout/semantics for assistive tech; idempotence guard accepts bothstringandstring[]HASTclassNameshapes so raw-HTML wrappers are not double-wrapped@media (prefers-color-scheme: light)rules tohtml:not([data-theme])so an explicitdata-theme="light"/data-theme="dark"always wins over the OS preferencegithub-slugger@^2.0.0andhast-util-to-stringto direct dependencies (no advisory issues)analysis/daily/*/article.mdaggregations and 50 EN/SV HTML articles to reflect the new prefixed slugs, table wrappers and translated badges