feat(rai): disclose AI-generated content (#207)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Juan Manuel Servera committed May 25, 2026 at 23:23 UTC a819e1aecd6064de1ac65b8d3531d8e045f7ff36
9 files changed +141 -4
.squad/decisions/inbox/amy-ai-disclosure-pattern.md new
+7
@@ -0,0 +1,7 @@
1 +# AI Disclosure Pattern
2 +
3 +**Date:** 2026-05-25
4 +**Author:** Amy
5 +**Status:** Proposed
6 +
7 +Every page renders an AI-disclosure footer partial; article pages additionally show a prominent AI-generated badge in the meta block. Single partial = single source of truth.
.squad/skills/ai-content-disclosure/SKILL.md new
+32
@@ -0,0 +1,32 @@
1 +---
2 +name: "ai-content-disclosure"
3 +description: "Reusable pattern for visibly disclosing AI-generated editorial content across the Hugo site."
4 +domain: "responsible-ai, frontend, hugo, accessibility"
5 +confidence: "medium"
6 +source: "Amy AI disclosure implementation — 2026-05-25"
7 +owner: "Amy"
8 +---
9 +
10 +## Pattern
11 +
12 +Use one global Hugo partial for baseline disclosure on every page, then add a stronger badge in article metadata for generated analysis pages.
13 +
14 +## Requirements
15 +
16 +- Keep visible disclosure text in the normal reading flow; do not rely only on metadata, tooltips, or ARIA.
17 +- Use generic language such as "AI models" rather than vendor-specific names.
18 +- Name accountability: editorial structure and source curation are owned by the SquadScope maintainer.
19 +- Provide an error-reporting path to GitHub issues.
20 +- Keep the footer partial as the single source of truth so new pages inherit disclosure automatically.
21 +- Ensure print styles preserve the disclosure.
22 +
23 +## Hugo Implementation
24 +
25 +1. Render `layouts/partials/ai-disclosure.html` from `layouts/partials/footer.html`.
26 +2. Add `<meta name="ai-generated" content="true">` in `layouts/partials/head.html`.
27 +3. Add `data-ai-generated="true"` to the `<body>` in `layouts/_default/baseof.html`.
28 +4. Add an `AI-generated analysis` badge in `layouts/partials/article-meta.html` for weekly/topic analysis pages.
29 +
30 +## Verification
31 +
32 +Build the site, inspect rendered HTML for the meta and body attributes, and visually check the footer disclosure plus article badge in light/dark and mobile/desktop viewports.
assets/css/common/footer.css
+60
@@ -36,6 +36,66 @@
36 gap: var(--space-3);
37 }
38
39 +.ai-disclosure {
40 + display: grid;
41 + grid-template-columns: auto 1fr;
42 + gap: var(--space-3);
43 + max-width: var(--prose-measure);
44 + margin: 0 auto;
45 + padding: var(--space-3);
46 + border: var(--size-border-thin) solid var(--color-border);
47 + border-radius: var(--radius-lg);
48 + background: color-mix(in srgb, var(--color-accent-muted) 55%, var(--color-surface));
49 + text-align: left;
50 +}
51 +
52 +.ai-disclosure__icon {
53 + display: inline-flex;
54 + align-items: center;
55 + justify-content: center;
56 + width: var(--space-5);
57 + height: var(--space-5);
58 + border-radius: var(--radius-full);
59 + background: var(--color-surface);
60 + box-shadow: var(--shadow-sm);
61 +}
62 +
63 +.ai-disclosure__title,
64 +.ai-disclosure p {
65 + margin: 0;
66 +}
67 +
68 +.ai-disclosure__body {
69 + display: grid;
70 + gap: var(--space-2);
71 +}
72 +
73 +.ai-disclosure__title {
74 + color: var(--color-text);
75 + font-size: var(--text-sm);
76 + line-height: var(--leading-tight);
77 +}
78 +
79 +@media (max-width: 640px) {
80 + .ai-disclosure {
81 + grid-template-columns: 1fr;
82 + text-align: center;
83 + }
84 +
85 + .ai-disclosure__icon {
86 + margin: 0 auto;
87 + }
88 +}
89 +
90 +@media print {
91 + .ai-disclosure {
92 + break-inside: avoid;
93 + border-color: currentColor;
94 + background: transparent;
95 + color: #000;
96 + }
97 +}
98 +
99 .site-footer a,
100 .site-footer__link {
101 color: var(--color-text-muted);
assets/css/extended/squadscope.css
+13
@@ -516,6 +516,19 @@ summary:focus-visible,
516 line-height: var(--leading-normal);
517 }
518
519 +.article-ai-badge {
520 + display: inline-flex;
521 + align-items: center;
522 + gap: var(--space-1);
523 + padding: 0.15rem var(--space-2);
524 + border: var(--size-border-thin) solid var(--color-border);
525 + border-radius: var(--radius-full);
526 + background: var(--color-accent-muted);
527 + color: var(--color-text);
528 + font-weight: 600;
529 + white-space: nowrap;
530 +}
531 +
532 .squad-article .report-metrics,
533 .article-jump-nav,
534 .article-featured-repos,
layouts/_default/baseof.html
+2 -2
@@ -17,9 +17,9 @@
17 </head>
18
19 {{- if (or (ne .Kind `page` ) (eq .Layout `archives`) (eq .Layout `search`)) }}
20 -<body class="list" id="top">
20 +<body class="list" id="top" data-ai-generated="true">
21 {{- else }}
22 -<body id="top">
22 +<body id="top" data-ai-generated="true">
23 {{- end }}
24 <a class="skip-link" href="#main-content">Skip to content</a>
25 {{ partialCached "header.html" . .Page -}}
layouts/partials/ai-disclosure.html new
+14
@@ -0,0 +1,14 @@
1 +<section class="ai-disclosure" aria-labelledby="ai-disclosure-title">
2 + <span class="ai-disclosure__icon" aria-hidden="true">✨</span>
3 + <div class="ai-disclosure__body">
4 + <h2 id="ai-disclosure-title" class="ai-disclosure__title">AI-generated content disclosure</h2>
5 + <p>
6 + Editorial content on SquadScope is generated by AI models, with human curation of sources and editorial structure by the
7 + <a href="https://github.com/jmservera/SquadScope" target="_blank" rel="noopener">SquadScope maintainer</a>.
8 + Read more about <a href="{{ "about/" | absLangURL }}">how we work</a>.
9 + </p>
10 + <p>
11 + Spot an error? <a href="https://github.com/jmservera/SquadScope/issues/new" target="_blank" rel="noopener">Open an issue on GitHub</a>.
12 + </p>
13 + </div>
14 +</section>
layouts/partials/article-meta.html
+11 -2
@@ -15,6 +15,15 @@
15 {{- with $page.Params.top_repo -}}
16 {{- $meta = $meta | append (printf "Top repo: %s" .) -}}
17 {{- end -}}
18 -{{- if gt (len $meta) 0 -}}
19 -<p class="article-meta" aria-label="Report metadata">{{ delimit $meta " <span aria-hidden=\"true\">·</span> " | safeHTML }}</p>
18 +{{- $showAIBadge := or (eq $page.Section "weekly") (eq $page.Section "topics") -}}
19 +{{- if or (gt (len $meta) 0) $showAIBadge -}}
20 +<p class="article-meta" aria-label="Report metadata">
21 + {{- if gt (len $meta) 0 -}}
22 + {{ delimit $meta " <span aria-hidden=\"true\">·</span> " | safeHTML }}
23 + {{- end -}}
24 + {{- if and (gt (len $meta) 0) $showAIBadge }} <span aria-hidden="true">·</span> {{ end -}}
25 + {{- if $showAIBadge -}}
26 + <span class="article-ai-badge" title="This analysis was written by an AI model using public GitHub + press data. Editorial structure and source curation by the SquadScope maintainer." aria-label="AI-generated analysis. This analysis was written by an AI model using public GitHub and press data. Editorial structure and source curation by the SquadScope maintainer.">✨ AI-generated analysis</span>
27 + {{- end -}}
28 +</p>
29 {{- end -}}
layouts/partials/footer.html
+1
@@ -2,6 +2,7 @@
2 <footer class="footer site-footer" aria-label="Site footer">
3 <div class="site-footer__inner">
4 <p class="site-footer__tagline"><strong>SquadScope</strong> — Weekly tech signal from GitHub.</p>
5 + {{ partial "ai-disclosure.html" . }}
6 <nav class="site-footer__nav" aria-label="Footer">
7 <a href="{{ "privacy/" | absLangURL }}">Privacy</a>
8 <button type="button" class="site-footer__link" onclick="CookieConsent.showPreferences()">Manage cookies</button>
layouts/partials/head.html
+1
@@ -6,6 +6,7 @@
6 {{- else }}
7 <meta name="robots" content="noindex, nofollow">
8 {{- end }}
9 +<meta name="ai-generated" content="true">
10
11 {{- partial "seo.html" . -}}
12