From 8998ef935b0f71874a73e93255ee42bc57a3c4b6 Mon Sep 17 00:00:00 2001 From: Shaan Satsangi Date: Thu, 28 May 2026 19:48:53 +0530 Subject: [PATCH 1/7] docs(v0.9.7): spec privacy policy + terms (lightweight, India, 13+) --- .../2026-05-28-v0.9.7-legal-docs-design.md | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-28-v0.9.7-legal-docs-design.md diff --git a/docs/superpowers/specs/2026-05-28-v0.9.7-legal-docs-design.md b/docs/superpowers/specs/2026-05-28-v0.9.7-legal-docs-design.md new file mode 100644 index 0000000..6edb5be --- /dev/null +++ b/docs/superpowers/specs/2026-05-28-v0.9.7-legal-docs-design.md @@ -0,0 +1,130 @@ +# v0.9.7 — Privacy policy + terms design spec + +**Status:** Designed. Implementation plan to follow under `docs/superpowers/plans/`. +**Date:** 2026-05-28. +**Author:** Claude (Opus 4.7) with Shaan. + +> **Not legal advice.** This spec defines lightweight, honest, plain-language legal pages grounded in the app's actual data practices. A professional should review them before relying on them for a public launch. + +--- + +## 1. Goal + +Ship a Privacy Policy and Terms of Service as styled, indexable pages on the frontend, linked from a new global footer. Content is tailored to what Skill Issue actually does — no boilerplate that overpromises. This is the final pre-1.0 slice. + +## 2. Locked facts (2026-05-28) + +| Field | Value | +| --- | --- | +| Operator | Shaan Satsangi (individual) | +| Contact | shaansatsangi.cse@gmail.com | +| Governing law | India | +| Minimum age | 13+ (matches GitHub) | +| Tone | Lightweight, honest, plain-language | +| Effective / "Last updated" | 2026-05-28 | + +## 3. Locked scope decisions (2026-05-28) + +| Decision | Choice | Why | +| --- | --- | --- | +| Format | **Static TSX pages** (`/privacy`, `/terms`), not markdown | No new dependency, full design control, single source of truth, SEO/PPR-friendly. | +| Shared styling | A small **`LegalProse`** wrapper component | DRY the prose/heading styling across both pages. | +| Footer | New global **`SiteFooter`** in the root layout | "Link from frontend footer" — none exists today. Minimal, subtle, responsive. | +| `docs/legal/` | A short **`README.md` pointer** to the rendered pages | Satisfies PLAN's `docs/legal/` mention without a prose duplicate that would drift. | +| Depth | Lightweight (no cookie-consent banner, no DSAR tooling, no GDPR/CCPA machinery) | The user's "lightweight, honest" choice; matches a solo pre-launch. | + +## 4. Operating context — what the app actually does with data + +Grounded in the codebase (so the Privacy content is truthful, not generic): +- **GitHub OAuth `read:user`** (v0.9.5): on sign-in we read the user's public profile (login, name, avatar). The access token is **AES-GCM encrypted at rest** in the `sessions` table. +- **Saved analyses + history** (`/me`): when signed in, analyses you run are persisted (Neon Postgres) until you delete them; deletion cascades runs + narratives. +- **IP address** for rate limiting (v0.9.2): hashed/keyed per UTC-hour bucket in Upstash; not stored long-term. +- **Caches** (Upstash): Report cache (~6 h TTL), GitHub-API cache, narrative cache — transient. +- **AI narrative**: report data is sent to **Groq** (OpenAI-compatible) to generate the Roast/Mentor text. +- **Analytics**: **PostHog** (events + web vitals); **Sentry** (error telemetry). +- **Hosting**: **Vercel**; **database**: **Neon**. +- **Cookies**: a session cookie (`si_session`, httpOnly, Secure, SameSite=Lax, ~30-day TTL) and a short-lived OAuth `state` cookie. No third-party advertising cookies. + +These map directly to the Privacy "what we collect / how we use it / subprocessors / retention" sections. + +## 5. Content outline + +### 5.1 Privacy Policy (`/privacy`) +1. **Intro** — who operates the service (Shaan Satsangi), effective date, plain-language promise. +2. **What we collect** — GitHub public profile (`read:user`); analyses you run + saved history (signed-in); IP address (rate limiting); product analytics + error telemetry; session cookie. +3. **How we use it** — produce + display reports, persist your history, prevent abuse, improve reliability. +4. **Third parties / subprocessors** — GitHub, Vercel, Neon, Upstash, Groq, Sentry, PostHog (each with a one-line purpose). +5. **Retention** — sessions ~30 days; saved analyses until you delete them; caches transient. +6. **Your choices** — delete saved analyses, sign out, revoke GitHub access (link to GitHub settings). +7. **Cookies** — session + OAuth-state only; no ad cookies. +8. **Children** — 13+. +9. **Changes** — we may update; "last updated" reflects the date. +10. **Contact** — shaansatsangi.cse@gmail.com. + +### 5.2 Terms of Service (`/terms`) +1. **Acceptance** — using the service = agreeing. +2. **What the service is** — opinionated, automated GitHub analysis for insight + entertainment; **explicitly not** an authoritative hiring, credit, or employment-decision tool; scores are subjective/automated and may be wrong. +3. **Accounts** — optional GitHub OAuth sign-in; you're responsible for your GitHub account. +4. **Acceptable use** — no abuse, automated scraping, circumventing rate limits, or attempting to disrupt the service. +5. **Intellectual property** — the service + its scoring are ours; GitHub data belongs to its owners/GitHub. +6. **Disclaimers** — provided "as is," no warranty, scores are subjective and automated. +7. **Limitation of liability** — to the extent permitted by law, not liable for damages arising from use. +8. **Termination** — we may suspend abusive use; you may stop + delete your data anytime. +9. **Governing law** — India. +10. **Changes** + **Contact** — shaansatsangi.cse@gmail.com. + +## 6. Surface area + +### 6.1 New files +| File | Responsibility | +| --- | --- | +| `frontend/src/components/legal-prose.tsx` | `LegalProse` — presentational wrapper: page title, "Last updated" line, consistent prose/heading/link styling. Takes `title`, `lastUpdated`, `children`. | +| `frontend/src/app/privacy/page.tsx` | Static server component; Privacy content (§5.1) inside `LegalProse`; page `metadata`. | +| `frontend/src/app/terms/page.tsx` | Static server component; Terms content (§5.2) inside `LegalProse`; page `metadata`. | +| `frontend/src/components/site-footer.tsx` | `SiteFooter` — global footer: `Privacy · Terms` + GitHub repo link; subtle, responsive. | +| `frontend/src/components/__tests__/legal-pages.test.tsx` | Smoke tests: each page renders, shows its heading + the contact email; footer renders both links. | +| `docs/legal/README.md` | Pointer to the rendered `/privacy` + `/terms` pages + last-updated note. | + +### 6.2 Modified files +| File | Change | +| --- | --- | +| `frontend/src/app/layout.tsx` | Render `` inside `` after the children/providers so it sits at the bottom (body is already `min-h-full flex flex-col`). | +| Version literals + CHANGELOG + PLAN + PROGRESS_LOG | Release ritual (0.9.6 → 0.9.7). | + +### 6.3 Untouched (intentionally) +- Backend — no API or data-handling change; this slice is documentation + frontend presentation. +- No new dependency (TSX, not markdown rendering). + +## 7. Footer + landing-layout consideration + +`layout.tsx` body is `min-h-full flex flex-col` with `` then content. Adding `` at the end keeps the footer at the bottom; the landing page's `flex-1` hero still centers in the remaining space. **Must verify** the full-height landing hero isn't visually disrupted and the footer reads well at mobile breakpoints (per the non-negotiable responsive rule). The footer is unobtrusive: small muted text, a thin top border, links to `/privacy`, `/terms`, and the GitHub repo. + +## 8. Tests + +Per the project norm (UI doesn't need 100% coverage — visual verification is fine), keep tests minimal: +- `legal-pages.test.tsx`: render `Privacy` page → asserts the "Privacy" heading + contact email present; render `Terms` page → asserts "Terms" heading + governing-law mention; render `SiteFooter` → asserts links to `/privacy` and `/terms`. +- Plus `next build` + a visual check of `/privacy`, `/terms`, and the footer on desktop + mobile. + +## 9. Exit criteria + +- [ ] `/privacy` and `/terms` render the §5 content via `LegalProse`, with page metadata and a "Last updated 2026-05-28" line. +- [ ] Global `SiteFooter` links to both pages (+ GitHub) and is in the root layout; landing hero intact; mobile verified. +- [ ] `docs/legal/README.md` points to the rendered pages. +- [ ] `legal-pages.test.tsx` passes; frontend `lint` + `tsc` + `test:run` + `build` clean. +- [ ] Docs ritual + version bump to 0.9.7; PLAN v0.9.7 row flipped ✅; tag `v0.9.7` + release. + +## 10. Out of scope +- Cookie-consent banner / preference center. +- DSAR (data-subject-access-request) tooling or a self-serve "download my data." +- GDPR/CCPA-specific rights sections and a formal subprocessor agreement. +- Backend changes (no new data collection or deletion endpoints — `/me` delete already exists and is referenced under "your choices"). +- A markdown-rendering pipeline. + +## 11. Implementation ordering +1. `LegalProse` wrapper + `SiteFooter` component (+ wire footer into `layout.tsx`); verify landing hero + mobile. +2. `/privacy` page (content + metadata). +3. `/terms` page (content + metadata). +4. `legal-pages.test.tsx` smoke tests + `docs/legal/README.md`. +5. Docs ritual + version bump + ship (PR → CI → merge → prod smoke → confirm before tag). + +**Reversibility:** additive (new components/pages/footer); reverting removes the pages + footer with no data or backend impact. From 1fb1ee13445a6e458bf6ed9aef8d598f556b4482 Mon Sep 17 00:00:00 2001 From: Shaan Satsangi Date: Thu, 28 May 2026 20:27:46 +0530 Subject: [PATCH 2/7] docs(v0.9.7): implementation plan for privacy + terms --- .../plans/2026-05-28-v0.9.7-legal-docs.md | 562 ++++++++++++++++++ 1 file changed, 562 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-28-v0.9.7-legal-docs.md diff --git a/docs/superpowers/plans/2026-05-28-v0.9.7-legal-docs.md b/docs/superpowers/plans/2026-05-28-v0.9.7-legal-docs.md new file mode 100644 index 0000000..dd526d5 --- /dev/null +++ b/docs/superpowers/plans/2026-05-28-v0.9.7-legal-docs.md @@ -0,0 +1,562 @@ +# v0.9.7 — Privacy + Terms Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Ship styled, indexable `/privacy` and `/terms` pages (lightweight, honest, grounded in the app's real data practices) linked from a new global footer. + +**Architecture:** Static TSX server-component pages using a shared `LegalProse`/`LegalSection` presentational wrapper; a new global `SiteFooter` in the root layout. No new dependency, no backend change. + +**Tech Stack:** Next.js 16 App Router, React 19, Tailwind v4, TypeScript, vitest + Testing Library. + +**Spec:** [`docs/superpowers/specs/2026-05-28-v0.9.7-legal-docs-design.md`](../specs/2026-05-28-v0.9.7-legal-docs-design.md). + +--- + +## File structure + +| File | Responsibility | Action | +| --- | --- | --- | +| `frontend/src/components/legal-prose.tsx` | `LegalProse` (page shell) + `LegalSection` (styled section) | Create | +| `frontend/src/components/site-footer.tsx` | Global footer with Privacy · Terms · GitHub links | Create | +| `frontend/src/app/layout.tsx` | Render `` at the bottom of `` | Modify | +| `frontend/src/app/privacy/page.tsx` | Privacy Policy page + metadata | Create | +| `frontend/src/app/terms/page.tsx` | Terms of Service page + metadata | Create | +| `frontend/src/components/__tests__/legal-pages.test.tsx` | Smoke tests (pages render; footer links) | Create | +| `docs/legal/README.md` | Pointer to the rendered pages | Create | +| Version literals + CHANGELOG + PLAN + PROGRESS_LOG | Release ritual (0.9.6 → 0.9.7) | Modify | + +All frontend commands run from `frontend/`. + +--- + +### Task 1: Shared prose wrapper + global footer + +**Files:** +- Create: `frontend/src/components/legal-prose.tsx`, `frontend/src/components/site-footer.tsx` +- Modify: `frontend/src/app/layout.tsx` + +- [ ] **Step 1: Create `legal-prose.tsx`** + +```tsx +import type { ReactNode } from "react"; + +export function LegalProse({ + title, + lastUpdated, + children, +}: { + title: string; + lastUpdated: string; + children: ReactNode; +}) { + return ( +
+

{title}

+

Last updated {lastUpdated}

+
+ {children} +
+
+ ); +} + +export function LegalSection({ heading, children }: { heading: string; children: ReactNode }) { + return ( +
+

{heading}

+ {children} +
+ ); +} +``` + +- [ ] **Step 2: Create `site-footer.tsx`** + +```tsx +import Link from "next/link"; + +export function SiteFooter() { + return ( +
+
+

© {new Date().getFullYear()} Skill Issue

+ +
+
+ ); +} +``` + +- [ ] **Step 3: Wire the footer into the layout** + +In `frontend/src/app/layout.tsx`: add the import `import { SiteFooter } from "@/components/site-footer";` alongside the other component imports, and render `` as the LAST child of ``, after the `` closing tag. The `` should read: + +```tsx + + + + {children} + + + +``` + +- [ ] **Step 4: Typecheck + build (verify the footer doesn't break the layout)** + +Run: `npx tsc --noEmit` +Expected: clean. + +Run: `npm run build` +Expected: build succeeds; `/` still listed (static), no new errors. + +- [ ] **Step 5: Visual check (manual)** + +Start `npm run dev`, open `/` — confirm the landing hero is still centered and the footer sits unobtrusively at the bottom; check at a mobile width (~375px) that the footer stacks (column) and reads cleanly. (If the dev server can't run in this environment, note it — the build + later test cover structure; visual is the operator's quick confirm.) + +- [ ] **Step 6: Commit** + +```bash +git add frontend/src/components/legal-prose.tsx frontend/src/components/site-footer.tsx frontend/src/app/layout.tsx +git commit -m "feat(v0.9.7): global site footer + legal-prose wrapper" +``` + +--- + +### Task 2: Privacy Policy page + +**Files:** +- Create: `frontend/src/app/privacy/page.tsx` + +- [ ] **Step 1: Create the page** + +```tsx +import type { Metadata } from "next"; +import { LegalProse, LegalSection } from "@/components/legal-prose"; + +export const metadata: Metadata = { + title: "Privacy Policy — Skill Issue", + description: "What Skill Issue collects, how it's used, and your choices.", +}; + +const EMAIL = "shaansatsangi.cse@gmail.com"; + +export default function PrivacyPage() { + return ( + +

+ Skill Issue is operated by Shaan Satsangi (“we”). This policy explains, in plain + language, what we collect, why, and the choices you have. We collect as little as we need to + run the service. +

+ + +
    +
  • + GitHub profile (if you sign in). We use + GitHub OAuth with read-only read:user scope to read your public profile + (login, name, avatar). Your GitHub access token is encrypted at rest. +
  • +
  • + Analyses and history. When signed in, the + analyses you run are saved to your history until you delete them. +
  • +
  • + IP address. Used transiently to rate-limit + requests and prevent abuse. +
  • +
  • + Usage and error data. Aggregate product + analytics and error reports help us keep the service working. +
  • +
  • + A session cookie. Keeps you signed in. +
  • +
+
+ + +

+ To generate and display your report, save your history when you’re signed in, prevent + abuse, and improve reliability. We do not sell your data or use it for advertising. +

+
+ + +

We rely on a few service providers to run Skill Issue:

+
    +
  • GitHub — the source of the public data we analyze.
  • +
  • Vercel — hosting.
  • +
  • Neon — database (accounts, saved analyses).
  • +
  • Upstash — short-lived caching and rate-limit counters.
  • +
  • Groq — generates the AI narrative from your report.
  • +
  • Sentry — error monitoring.
  • +
  • PostHog — product analytics.
  • +
+
+ + +

+ Sign-in sessions expire after about 30 days. Saved analyses stay until you delete them. + Caches are short-lived and expire automatically. +

+
+ + +

+ You can delete saved analyses from your history, sign out at any time, and revoke + Skill Issue’s access from your{" "} + + GitHub application settings + + . +

+
+ + +

+ We use a session cookie to keep you signed in and a short-lived cookie during the GitHub + sign-in flow. We do not use advertising or cross-site tracking cookies. +

+
+ + +

Skill Issue is intended for users aged 13 and older, consistent with GitHub.

+
+ + +

+ We may update this policy; the “last updated” date above reflects the latest + version. +

+
+ + +

+ Questions? Email{" "} + + {EMAIL} + + . +

+
+
+ ); +} +``` + +- [ ] **Step 2: Typecheck** + +Run: `npx tsc --noEmit` +Expected: clean. + +- [ ] **Step 3: Commit** + +```bash +git add frontend/src/app/privacy/page.tsx +git commit -m "feat(v0.9.7): privacy policy page" +``` + +--- + +### Task 3: Terms of Service page + +**Files:** +- Create: `frontend/src/app/terms/page.tsx` + +- [ ] **Step 1: Create the page** + +```tsx +import type { Metadata } from "next"; +import { LegalProse, LegalSection } from "@/components/legal-prose"; + +export const metadata: Metadata = { + title: "Terms of Service — Skill Issue", + description: "The terms for using Skill Issue.", +}; + +const EMAIL = "shaansatsangi.cse@gmail.com"; + +export default function TermsPage() { + return ( + +

+ By using Skill Issue (operated by Shaan Satsangi), you agree to these terms. If you + don’t agree, please don’t use the service. +

+ + +

+ Skill Issue produces an opinionated, automated analysis of a public GitHub profile for + insight and entertainment. The scores and commentary are generated by software and may be + incomplete or wrong. Skill Issue is not an + authoritative hiring, credit, employment, or background-check tool, and should not be used + to make decisions about people. +

+
+ + +

+ Signing in is optional and uses GitHub OAuth. You are responsible for activity under your + GitHub account and for keeping it secure. +

+
+ + +

You agree not to:

+
    +
  • abuse, overload, or attempt to disrupt the service;
  • +
  • scrape it or access it through automated means outside normal use;
  • +
  • circumvent rate limits or security measures;
  • +
  • use it to harass others or for unlawful purposes.
  • +
+
+ + +

+ The service, its design, and its scoring system belong to us. GitHub data shown in a report + belongs to its respective owners and GitHub; we present it for analysis. +

+
+ + +

+ The service is provided “as is” and “as available,” without + warranties of any kind. Scores are subjective, automated estimates and are not guarantees + of skill, quality, or fitness for any purpose. +

+
+ + +

+ To the maximum extent permitted by law, we are not liable for any indirect, incidental, or + consequential damages arising from your use of the service. +

+
+ + +

+ We may suspend or limit access that abuses the service. You can stop using it and delete + your saved data at any time. +

+
+ + +

These terms are governed by the laws of India.

+
+ + +

+ We may update these terms; the “last updated” date above reflects the latest + version. Continued use means you accept the changes. +

+
+ + +

+ Questions? Email{" "} + + {EMAIL} + + . +

+
+
+ ); +} +``` + +- [ ] **Step 2: Typecheck** + +Run: `npx tsc --noEmit` +Expected: clean. + +- [ ] **Step 3: Commit** + +```bash +git add frontend/src/app/terms/page.tsx +git commit -m "feat(v0.9.7): terms of service page" +``` + +--- + +### Task 4: Smoke tests + docs/legal pointer + +**Files:** +- Create: `frontend/src/components/__tests__/legal-pages.test.tsx`, `docs/legal/README.md` + +- [ ] **Step 1: Write the tests** + +```tsx +import { render, screen } from "@testing-library/react"; +import { describe, it, expect } from "vitest"; +import PrivacyPage from "@/app/privacy/page"; +import TermsPage from "@/app/terms/page"; +import { SiteFooter } from "@/components/site-footer"; + +describe("legal pages", () => { + it("privacy page shows its heading and the contact email", () => { + render(); + expect( + screen.getByRole("heading", { name: /privacy policy/i, level: 1 }), + ).toBeInTheDocument(); + expect(screen.getByText("shaansatsangi.cse@gmail.com")).toBeInTheDocument(); + }); + + it("terms page shows its heading and the governing law", () => { + render(); + expect( + screen.getByRole("heading", { name: /terms of service/i, level: 1 }), + ).toBeInTheDocument(); + expect(screen.getByText(/laws of India/i)).toBeInTheDocument(); + }); + + it("footer links to privacy and terms", () => { + render(); + expect(screen.getByRole("link", { name: /privacy/i })).toHaveAttribute("href", "/privacy"); + expect(screen.getByRole("link", { name: /terms/i })).toHaveAttribute("href", "/terms"); + }); +}); +``` + +- [ ] **Step 2: Run the tests** + +Run: `npm run test:run -- legal-pages` +Expected: 3 passed. (If `next/link` needs no special handling, this works under the existing happy-dom setup; the pages are sync server components with no async or server-only APIs, so RTL renders them directly.) + +- [ ] **Step 3: Create `docs/legal/README.md`** + +```markdown +# Legal + +The Privacy Policy and Terms of Service are maintained as rendered pages (single +source of truth — no markdown duplicate to drift): + +- Privacy Policy: [`frontend/src/app/privacy/page.tsx`](../../frontend/src/app/privacy/page.tsx) → `/privacy` +- Terms of Service: [`frontend/src/app/terms/page.tsx`](../../frontend/src/app/terms/page.tsx) → `/terms` + +Last updated: 2026-05-28. Lightweight, plain-language drafts grounded in the +app's actual data practices — **not legal advice**; have a professional review +before relying on them. +``` + +- [ ] **Step 4: Commit** + +```bash +git add frontend/src/components/__tests__/legal-pages.test.tsx docs/legal/README.md +git commit -m "test(v0.9.7): legal page smoke tests + docs/legal pointer" +``` + +--- + +### Task 5: Docs ritual + version bump + ship + +**Files:** +- Modify: `backend/pyproject.toml:3`, `backend/app/settings.py:5`, `frontend/package.json:3`, `frontend/src/app/page.tsx:26`, `frontend/src/components/results-view.tsx:355`, `README.md` (status lead + running list + health curl), `CHANGELOG.md`, `PLAN.md` (v0.9.7 row + section), `docs/PROGRESS_LOG.md`, `backend/uv.lock` + +- [ ] **Step 1: Bump version literals** + +`backend/pyproject.toml` line 3 `version = "0.9.6"` → `"0.9.7"`; `backend/app/settings.py` line 5 `VERSION = "0.9.6"` → `"0.9.7"`; `frontend/package.json` line 3 `"version": "0.9.6",` → `"0.9.7",`; `frontend/src/app/page.tsx` line 26 `... · v0.9.6` → `v0.9.7`; `frontend/src/components/results-view.tsx` line 355 `... Protocol v0.9.6` → `v0.9.7`. + +- [ ] **Step 2: Update README** + +`README.md` line 46: change the lead `Latest shipped release is **v0.9.6** (...)` to name **v0.9.7** (privacy policy + terms of service pages with a global footer), demote v0.9.6 to "before it," and change the trailing pointer from `**v0.9.7 — privacy policy + terms** is next.` to `v0.9.7 adds a Privacy Policy and Terms of Service (linked from a new global footer). **v1.0.0 — public launch** is next.` Bump the health-curl `"version":"0.9.6"` → `"0.9.7"` (line ~79). + +- [ ] **Step 3: Add the CHANGELOG entry** + +In `CHANGELOG.md`, insert below the preamble `---` and above `## [0.9.6]`: + +```markdown +## [0.9.7] — 2026-05-28 + +### Added +- **Privacy Policy and Terms of Service.** Plain-language legal pages at `/privacy` and `/terms`, linked from a new site-wide footer. + +--- +``` + +- [ ] **Step 4: Update PLAN.md** + +Version-map row `| **v0.9.7** | Privacy policy + terms (legal docs) | pending |` → `| **v0.9.7** | Privacy policy + terms + global footer | ✅ shipped |`. + +Replace the `## v0.9.7 — Legal docs (deferred)` section body (Goal + "Exit criteria: TBD") with: + +```markdown +## v0.9.7 — Privacy + Terms (shipped 2026-05-28) + +**Goal:** Plain-language Privacy Policy + Terms of Service pages, linked from a new global footer. + +**Delivered:** Static TSX pages `/privacy` + `/terms` (shared `LegalProse` wrapper), a global `SiteFooter`, content grounded in the app's real data practices (GitHub `read:user`, Neon, Upstash, Groq, Sentry, PostHog), India governing law, 13+, contact shaansatsangi.cse@gmail.com. Not legal advice — flagged for professional review before launch. + +**Design spec:** [`docs/superpowers/specs/2026-05-28-v0.9.7-legal-docs-design.md`](./docs/superpowers/specs/2026-05-28-v0.9.7-legal-docs-design.md). +**Sub-plan:** [`docs/superpowers/plans/2026-05-28-v0.9.7-legal-docs.md`](./docs/superpowers/plans/2026-05-28-v0.9.7-legal-docs.md). + +**Exit criteria:** +- [x] `/privacy` + `/terms` render via `LegalProse`; metadata + "Last updated 2026-05-28". +- [x] Global `SiteFooter` links both (+ GitHub); landing hero intact; mobile verified. +- [x] Smoke tests pass; frontend lint/tsc/test/build clean. +- [x] Docs ritual + version bump to 0.9.7; tag + release. +``` + +- [ ] **Step 5: Re-sync `uv.lock`** + +Run: `cd backend && uv lock` +Expected: only `skill-issue-backend 0.9.6 → 0.9.7` changes. + +- [ ] **Step 6: Add the PROGRESS_LOG entry** + +In `docs/PROGRESS_LOG.md`, add a new top entry per the file's format: header `## 2026-05-28 — Claude (Opus 4.7) — v0.9.7 shipped (privacy + terms)`; Slice v0.9.7; Done (the two pages + footer + LegalProse + tests + docs/legal pointer); Decisions (static TSX over markdown — no dep, single source; lightweight India/13+ per user; global footer added since none existed; not-legal-advice flagged); Verified (lint/tsc/test/build + visual); Blocked/open (professional legal review recommended pre-launch); Next (v1.0.0 public launch). + +- [ ] **Step 7: Full verification** + +Run: `cd frontend && npm run lint && npx tsc --noEmit && npm run test:run && npm run build` +Expected: lint/tsc clean; vitest 54 + 3 new = 57 passed; build succeeds with `/privacy` and `/terms` listed (static). + +Run: `cd backend && uv run pytest -q --no-header` +Expected: 290 passed (backend untouched; confirms the version bump didn't break anything). + +- [ ] **Step 8: Commit** + +```bash +git add backend/pyproject.toml backend/app/settings.py frontend/package.json frontend/src/app/page.tsx frontend/src/components/results-view.tsx README.md CHANGELOG.md PLAN.md docs/PROGRESS_LOG.md backend/uv.lock +git commit -m "chore(v0.9.7): bump version + docs ritual (legal docs)" +``` + +- [ ] **Step 9: Push, PR, CI, merge, smoke (confirm before tag)** + +Push the branch, open a PR, wait for CI green, merge to `main`. Prod smoke: `curl …/health` → `version: 0.9.7`; load `/privacy` + `/terms` on prod. Then **pause for user confirmation** before `git tag v0.9.7 && git push origin v0.9.7`. + +--- + +## Self-review notes + +- **Spec coverage:** content §5.1/§5.2 → Tasks 2–3; rendering §3 (TSX + LegalProse) → Tasks 1–3; footer §6.2/§7 → Task 1; docs/legal §3 → Task 4; tests §8 → Task 4; exit criteria §9 → Task 5. All covered. +- **Placeholder scan:** none — full page prose and component code included. +- **Type consistency:** `LegalProse({title,lastUpdated,children})` and `LegalSection({heading,children})` signatures are used identically in Tasks 2–3; `SiteFooter` exported (named) and imported as named in layout + test; pages default-exported and imported as default in the test. +- **No backend/data change:** "your choices" references the existing `/me` delete; no new endpoint. +- **Test count:** frontend 54 → 57 (+3). Backend unchanged at 290. From ab420126c3bd3536b003e9d067b901fa67b82f0c Mon Sep 17 00:00:00 2001 From: Shaan Satsangi Date: Thu, 28 May 2026 20:36:53 +0530 Subject: [PATCH 3/7] feat(v0.9.7): global site footer + legal-prose wrapper --- frontend/src/app/layout.tsx | 2 ++ frontend/src/components/copyright-year.tsx | 5 ++++ frontend/src/components/legal-prose.tsx | 30 +++++++++++++++++++ frontend/src/components/site-footer.tsx | 35 ++++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 frontend/src/components/copyright-year.tsx create mode 100644 frontend/src/components/legal-prose.tsx create mode 100644 frontend/src/components/site-footer.tsx diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 55d4385..08f3dd3 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata, Viewport } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import { FramerProvider } from "@/components/framer-provider"; +import { SiteFooter } from "@/components/site-footer"; import { SiteHeader } from "@/components/site-header"; import { ObservabilityProvider } from "@/observability/provider"; import "./globals.css"; @@ -44,6 +45,7 @@ export default function RootLayout({ {children} + ); diff --git a/frontend/src/components/copyright-year.tsx b/frontend/src/components/copyright-year.tsx new file mode 100644 index 0000000..21431e9 --- /dev/null +++ b/frontend/src/components/copyright-year.tsx @@ -0,0 +1,5 @@ +"use client"; + +export function CopyrightYear() { + return <>{new Date().getFullYear()}; +} diff --git a/frontend/src/components/legal-prose.tsx b/frontend/src/components/legal-prose.tsx new file mode 100644 index 0000000..b248608 --- /dev/null +++ b/frontend/src/components/legal-prose.tsx @@ -0,0 +1,30 @@ +import type { ReactNode } from "react"; + +export function LegalProse({ + title, + lastUpdated, + children, +}: { + title: string; + lastUpdated: string; + children: ReactNode; +}) { + return ( +
+

{title}

+

Last updated {lastUpdated}

+
+ {children} +
+
+ ); +} + +export function LegalSection({ heading, children }: { heading: string; children: ReactNode }) { + return ( +
+

{heading}

+ {children} +
+ ); +} diff --git a/frontend/src/components/site-footer.tsx b/frontend/src/components/site-footer.tsx new file mode 100644 index 0000000..eb14347 --- /dev/null +++ b/frontend/src/components/site-footer.tsx @@ -0,0 +1,35 @@ +import Link from "next/link"; +import { Suspense } from "react"; +import { CopyrightYear } from "@/components/copyright-year"; + +export function SiteFooter() { + return ( +
+
+

+ ©{" "} + + + {" "} + Skill Issue +

+ +
+
+ ); +} From aa75c82e0bb52bc5114733958bfad188de3358cd Mon Sep 17 00:00:00 2001 From: Shaan Satsangi Date: Thu, 28 May 2026 20:38:09 +0530 Subject: [PATCH 4/7] refactor(v0.9.7): static footer year (drop client component + Suspense) --- frontend/src/components/copyright-year.tsx | 5 ----- frontend/src/components/site-footer.tsx | 10 +--------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 frontend/src/components/copyright-year.tsx diff --git a/frontend/src/components/copyright-year.tsx b/frontend/src/components/copyright-year.tsx deleted file mode 100644 index 21431e9..0000000 --- a/frontend/src/components/copyright-year.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client"; - -export function CopyrightYear() { - return <>{new Date().getFullYear()}; -} diff --git a/frontend/src/components/site-footer.tsx b/frontend/src/components/site-footer.tsx index eb14347..f032df6 100644 --- a/frontend/src/components/site-footer.tsx +++ b/frontend/src/components/site-footer.tsx @@ -1,18 +1,10 @@ import Link from "next/link"; -import { Suspense } from "react"; -import { CopyrightYear } from "@/components/copyright-year"; export function SiteFooter() { return (