diff --git a/.beads/.gitignore b/.beads/.gitignore deleted file mode 100644 index 74fdbd2b..00000000 --- a/.beads/.gitignore +++ /dev/null @@ -1,47 +0,0 @@ -# SQLite databases -*.db -*.db?* -*.db-journal -*.db-wal -*.db-shm - -# Daemon runtime files -daemon.lock -daemon.log -daemon.pid -daemon-error -bd.sock -sync-state.json -last-touched - -# Local version tracking (prevents upgrade notification spam after git ops) -.local_version - -# Legacy database files -db.sqlite -bd.db - -# Worktree redirect file (contains relative path to main repo's .beads/) -# Must not be committed as paths would be wrong in other clones -redirect - -# Merge artifacts (temporary files from 3-way merge) -beads.base.jsonl -beads.base.meta.json -beads.left.jsonl -beads.left.meta.json -beads.right.jsonl -beads.right.meta.json - -# Sync state (local-only, per-machine) -# These files are machine-specific and should not be shared across clones -.sync.lock -.jsonl.lock -sync_base.jsonl -export-state/ - -# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here. -# They would override fork protection in .git/info/exclude, allowing -# contributors to accidentally commit upstream issue databases. -# The JSONL files (issues.jsonl, interactions.jsonl) and config files -# are tracked by git by default since no pattern above ignores them. diff --git a/.beads/.jsonl.lock b/.beads/.jsonl.lock deleted file mode 100644 index e69de29b..00000000 diff --git a/.beads/README.md b/.beads/README.md deleted file mode 100644 index 50f281f0..00000000 --- a/.beads/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Beads - AI-Native Issue Tracking - -Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code. - -## What is Beads? - -Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git. - -**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads) - -## Quick Start - -### Essential Commands - -```bash -# Create new issues -bd create "Add user authentication" - -# View all issues -bd list - -# View issue details -bd show - -# Update issue status -bd update --status in_progress -bd update --status done - -# Sync with git remote -bd sync -``` - -### Working with Issues - -Issues in Beads are: -- **Git-native**: Stored in `.beads/issues.jsonl` and synced like code -- **AI-friendly**: CLI-first design works perfectly with AI coding agents -- **Branch-aware**: Issues can follow your branch workflow -- **Always in sync**: Auto-syncs with your commits - -## Why Beads? - -✨ **AI-Native Design** -- Built specifically for AI-assisted development workflows -- CLI-first interface works seamlessly with AI coding agents -- No context switching to web UIs - -🚀 **Developer Focused** -- Issues live in your repo, right next to your code -- Works offline, syncs when you push -- Fast, lightweight, and stays out of your way - -🔧 **Git Integration** -- Automatic sync with git commits -- Branch-aware issue tracking -- Intelligent JSONL merge resolution - -## Get Started with Beads - -Try Beads in your own projects: - -```bash -# Install Beads -curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash - -# Initialize in your repo -bd init - -# Create your first issue -bd create "Try out Beads" -``` - -## Learn More - -- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs) -- **Quick Start Guide**: Run `bd quickstart` -- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples) - ---- - -*Beads: Issue tracking that moves at the speed of thought* ⚡ diff --git a/.beads/config.yaml b/.beads/config.yaml deleted file mode 100644 index ff8bc921..00000000 --- a/.beads/config.yaml +++ /dev/null @@ -1,67 +0,0 @@ -# Beads Configuration File -# This file configures default behavior for all bd commands in this repository -# All settings can also be set via environment variables (BD_* prefix) -# or overridden with command-line flags - -# Issue prefix for this repository (used by bd init) -# If not set, bd init will auto-detect from directory name -# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc. -# issue-prefix: "" - -# Use no-db mode: load from JSONL, no SQLite, write back after each command -# When true, bd will use .beads/issues.jsonl as the source of truth -# instead of SQLite database -# no-db: false - -# Disable daemon for RPC communication (forces direct database access) -# no-daemon: false - -# Disable auto-flush of database to JSONL after mutations -# no-auto-flush: false - -# Disable auto-import from JSONL when it's newer than database -# no-auto-import: false - -# Enable JSON output by default -# json: false - -# Default actor for audit trails (overridden by BD_ACTOR or --actor) -# actor: "" - -# Path to database (overridden by BEADS_DB or --db) -# db: "" - -# Auto-start daemon if not running (can also use BEADS_AUTO_START_DAEMON) -# auto-start-daemon: true - -# Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE) -# flush-debounce: "5s" - -# Export events (audit trail) to .beads/events.jsonl on each flush/sync -# When enabled, new events are appended incrementally using a high-water mark. -# Use 'bd export --events' to trigger manually regardless of this setting. -# events-export: false - -# Git branch for beads commits (bd sync will commit to this branch) -# IMPORTANT: Set this for team projects so all clones use the same sync branch. -# This setting persists across clones (unlike database config which is gitignored). -# Can also use BEADS_SYNC_BRANCH env var for local override. -# If not set, bd sync will require you to run 'bd config set sync.branch '. -# sync-branch: "beads-sync" - -# Multi-repo configuration (experimental - bd-307) -# Allows hydrating from multiple repositories and routing writes to the correct JSONL -# repos: -# primary: "." # Primary repo (where this database lives) -# additional: # Additional repos to hydrate from (read-only) -# - ~/beads-planning # Personal planning repo -# - ~/work-planning # Work planning repo - -# Integration settings (access with 'bd config get/set') -# These are stored in the database, not in this file: -# - jira.url -# - jira.project -# - linear.url -# - linear.api-key -# - github.org -# - github.repo diff --git a/.beads/interactions.jsonl b/.beads/interactions.jsonl deleted file mode 100644 index e69de29b..00000000 diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl deleted file mode 100644 index 2b91d72d..00000000 --- a/.beads/issues.jsonl +++ /dev/null @@ -1,179 +0,0 @@ -{"id":"docs-150","title":"Epic: Stripe Design Parity — 25 findings from review","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T22:23:16.261796+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T22:32:26.03+13:00","closed_at":"2026-02-14T22:32:26.03+13:00","close_reason":"All 3 tasks complete (TOC H2+H3, sidebar chevron, 36 CSS changes). 25/25 acceptance checks pass. Build succeeds."} -{"id":"docs-150.1","title":"TOC: support H2+H3 headings with indentation, hide if \u003c2 headings","description":"## Files\n- documentation/components/TableOfContents.js (modify)\n\n## What to do\nTwo changes to the TableOfContents component:\n\n### 1. Extract H2 AND H3 headings (not just H2)\nChange line 14 from `querySelectorAll(\"h2\")` to `querySelectorAll(\"h2, h3\")`. Add a `level` property to each item based on the tag name.\n\n### 2. Hide TOC when fewer than 2 headings\nChange the render guard from `headings.length === 0` to `headings.length \u003c 2`.\n\n### 3. Render H3 items with indentation\nAdd a `data-level` attribute or CSS class to distinguish H3 links from H2 links.\n\nReplace the entire file with:\n\n```js\nimport { useState, useEffect, useRef } from \"react\";\nimport { useRouter } from \"next/router\";\n\nexport function TableOfContents() {\n const [headings, setHeadings] = useState([]);\n const [activeId, setActiveId] = useState(\"\");\n const observerRef = useRef(null);\n const router = useRouter();\n const currentPath = router.asPath.split(\"#\")[0].split(\"?\")[0];\n\n // Extract H2 and H3 headings from the DOM after render\n useEffect(() =\u003e {\n if (typeof window === \"undefined\") return;\n const article = document.querySelector(\".layout-content article\");\n if (!article) {\n setHeadings([]);\n return;\n }\n\n const elements = article.querySelectorAll(\"h2, h3\");\n const items = Array.from(elements).map((el) =\u003e {\n if (!el.id) {\n el.id = el.textContent\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n }\n return {\n id: el.id,\n text: el.textContent,\n level: el.tagName === \"H3\" ? 3 : 2,\n };\n });\n setHeadings(items);\n setActiveId(\"\");\n }, [currentPath]);\n\n // Scroll spy using IntersectionObserver\n useEffect(() =\u003e {\n if (headings.length === 0) return;\n\n if (observerRef.current) {\n observerRef.current.disconnect();\n }\n\n const callback = (entries) =\u003e {\n const visibleEntries = entries.filter((e) =\u003e e.isIntersecting);\n if (visibleEntries.length \u003e 0) {\n setActiveId(visibleEntries[0].target.id);\n }\n };\n\n observerRef.current = new IntersectionObserver(callback, {\n rootMargin: \"-80px 0px -60% 0px\",\n threshold: 0,\n });\n\n headings.forEach(({ id }) =\u003e {\n const el = document.getElementById(id);\n if (el) observerRef.current.observe(el);\n });\n\n return () =\u003e {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n };\n }, [headings]);\n\n if (headings.length \u003c 2) return null;\n\n return (\n \u003cnav className=\"toc\" aria-label=\"Table of contents\"\u003e\n \u003ch4 className=\"toc-title\"\u003eOn this page\u003c/h4\u003e\n \u003cul className=\"toc-list\"\u003e\n {headings.map(({ id, text, level }) =\u003e (\n \u003cli key={id}\u003e\n \u003ca\n href={`#${id}`}\n className={`toc-link${level === 3 ? \" toc-link-h3\" : \"\"}${\n activeId === id ? \" toc-link-active\" : \"\"\n }`}\n onClick={(e) =\u003e {\n e.preventDefault();\n const target = document.getElementById(id);\n if (target) {\n target.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n setActiveId(id);\n history.pushState(null, \"\", `#${id}`);\n }\n }}\n \u003e\n {text}\n \u003c/a\u003e\n \u003c/li\u003e\n ))}\n \u003c/ul\u003e\n \u003c/nav\u003e\n );\n}\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst src = fs.readFileSync(\\\"components/TableOfContents.js\\\", \\\"utf8\\\");\nconst hasH3 = src.includes(\\\"h2, h3\\\");\nconst hasLevel = src.includes(\\\"level\\\");\nconst hasMinThreshold = src.includes(\\\"headings.length \u003c 2\\\");\nconst hasH3Class = src.includes(\\\"toc-link-h3\\\");\nif (!hasH3) { console.error(\\\"FAIL: not extracting h3\\\"); process.exit(1); }\nif (!hasLevel) { console.error(\\\"FAIL: no level property\\\"); process.exit(1); }\nif (!hasMinThreshold) { console.error(\\\"FAIL: no min heading threshold\\\"); process.exit(1); }\nif (!hasH3Class) { console.error(\\\"FAIL: no toc-link-h3 class\\\"); process.exit(1); }\nconsole.log(\\\"PASS\\\");\n\"\n```\n\n## Dont\n- Do not change the scroll spy logic or IntersectionObserver options\n- Do not change the router dependency\n- Do not touch globals.css (CSS changes will be handled by a separate task)","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T22:23:43.762066+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T22:26:12.857638+13:00","closed_at":"2026-02-14T22:26:12.857638+13:00","close_reason":"d3a98d1 TOC supports H2+H3 headings with indentation, hides if \u003c2 headings","dependencies":[{"issue_id":"docs-150.1","depends_on_id":"docs-150","type":"parent-child","created_at":"2026-02-14T22:23:43.763409+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-150.2","title":"Sidebar: move chevron to left side, make inline, reduce size to 12px","description":"## Files\n- documentation/components/Sidebar.js (modify)\n\n## What to do\nStripe places the expand/collapse chevron to the LEFT of the link text, inline within the link element. Ours is a separate button on the right side. Also reduce chevron from 16x16 to 12x12 and change nesting indent from 16px increments to 12px.\n\nReplace the NavItem component (lines 18-89) with this version that puts the chevron inline before the text:\n\n```js\nfunction NavItem({ item, currentPath, depth = 0 }) {\n const hasChildren = item.children \u0026\u0026 item.children.length \u003e 0;\n const active = item.path \u0026\u0026 isActive(item.path, currentPath);\n const childActive = hasChildren \u0026\u0026 isChildActive(item, currentPath);\n const [expanded, setExpanded] = useState(childActive || active);\n\n useEffect(() =\u003e {\n if (childActive || active) {\n setExpanded(true);\n }\n }, [childActive, active]);\n\n const handleToggle = (e) =\u003e {\n if (hasChildren) {\n e.preventDefault();\n setExpanded(!expanded);\n }\n };\n\n const paddingLeft = `${12 + depth * 12}px`;\n\n const chevron = hasChildren ? (\n \u003csvg\n className=\"sidebar-chevron\"\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n style={{\n transform: expanded ? \"rotate(90deg)\" : \"rotate(0deg)\",\n transition: \"transform 0.15s ease\",\n }}\n \u003e\n \u003cpath\n d=\"M6 4l4 4-4 4\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n /\u003e\n \u003c/svg\u003e\n ) : null;\n\n return (\n \u003cli className=\"sidebar-nav-item\"\u003e\n {item.path ? (\n \u003cLink\n href={item.path}\n className={`sidebar-link${active ? \" sidebar-link-active\" : \"\"}${\n childActive ? \" sidebar-link-parent-active\" : \"\"\n }`}\n style={{ paddingLeft }}\n onClick={hasChildren \u0026\u0026 !item.path ? handleToggle : undefined}\n \u003e\n {chevron}\n {item.title}\n \u003c/Link\u003e\n ) : (\n \u003cspan\n className={`sidebar-link sidebar-link-toggle${\n childActive ? \" sidebar-link-parent-active\" : \"\"\n }`}\n style={{ paddingLeft }}\n onClick={handleToggle}\n \u003e\n {chevron}\n {item.title}\n \u003c/span\u003e\n )}\n {hasChildren \u0026\u0026 expanded \u0026\u0026 (\n \u003cul className=\"sidebar-nav-children\"\u003e\n {item.children.map((child) =\u003e (\n \u003cNavItem\n key={child.path || child.title}\n item={child}\n currentPath={currentPath}\n depth={depth + 1}\n /\u003e\n ))}\n \u003c/ul\u003e\n )}\n \u003c/li\u003e\n );\n}\n```\n\nKey changes:\n1. Removed the separate `.sidebar-nav-link-row` div wrapper\n2. Removed the separate `.sidebar-expand-btn` button\n3. Chevron SVG is now inline BEFORE the text inside the link/span\n4. Chevron is 12x12 (was 16x16)\n5. Indent is `12 + depth * 12` (was `16 + depth * 16`)\n6. Non-link parents with children get class `sidebar-link-toggle` and onClick to toggle\n7. Added `sidebar-chevron` class to the SVG for CSS styling\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst src = fs.readFileSync(\\\"components/Sidebar.js\\\", \\\"utf8\\\");\nconst hasInlineChevron = src.includes(\\\"sidebar-chevron\\\");\nconst noExpandBtn = !src.includes(\\\"sidebar-expand-btn\\\");\nconst noLinkRow = !src.includes(\\\"sidebar-nav-link-row\\\");\nconst has12px = src.includes(\\\"12 + depth * 12\\\");\nconst has12x12 = src.includes(\\\"width=\\\\\\\"12\\\\\\\"\\\") \u0026\u0026 src.includes(\\\"height=\\\\\\\"12\\\\\\\"\\\");\nif (!hasInlineChevron) { console.error(\\\"FAIL: no sidebar-chevron class\\\"); process.exit(1); }\nif (!noExpandBtn) { console.error(\\\"FAIL: still has sidebar-expand-btn\\\"); process.exit(1); }\nif (!noLinkRow) { console.error(\\\"FAIL: still has sidebar-nav-link-row\\\"); process.exit(1); }\nif (!has12px) { console.error(\\\"FAIL: indent not 12px increments\\\"); process.exit(1); }\nif (!has12x12) { console.error(\\\"FAIL: chevron not 12x12\\\"); process.exit(1); }\nconsole.log(\\\"PASS\\\");\n\"\n```\n\n## Dont\n- Do not change NavSection or the Sidebar export function\n- Do not change the collapse button (sidebar-collapse-btn)\n- Do not touch globals.css (CSS handled separately)\n- Do not change the navigation data import","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T22:24:05.829993+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T22:27:45.00586+13:00","closed_at":"2026-02-14T22:27:45.00586+13:00","close_reason":"d3a98d1 - Sidebar chevron moved to left side, inline, 12px (implemented alongside docs-150.1)","dependencies":[{"issue_id":"docs-150.2","depends_on_id":"docs-150","type":"parent-child","created_at":"2026-02-14T22:24:05.831325+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-150.3","title":"Breadcrumbs: lighter separator color, wider spacing","description":"## Files\n- documentation/components/Breadcrumbs.js (modify)\n\n## What to do\nUpdate the breadcrumb separator color and spacing to match Stripe. Currently the separator uses a dark gray; Stripe uses a lighter gray with more spacing.\n\nRead the current Breadcrumbs.js file first. Find the separator span element and update:\n1. The separator character should remain \"/\" \n2. No changes needed in the JS — the color/spacing changes are CSS-only\n\nActually, this task requires NO JS changes. The separator styling is in globals.css. Cancel this task — the CSS mega-task will handle it.\n\n## Test\nN/A — this task should be cancelled\n\n## Dont\nN/A","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T22:24:15.360221+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T22:24:21.845024+13:00","closed_at":"2026-02-14T22:24:21.845027+13:00","dependencies":[{"issue_id":"docs-150.3","depends_on_id":"docs-150","type":"parent-child","created_at":"2026-02-14T22:24:15.361244+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-150.4","title":"Mega CSS: all 25 Stripe parity fixes in globals.css","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nApply ALL of the following CSS changes to globals.css. Each change is numbered and references the current line. Read the file first to confirm line numbers, then apply all changes.\n\n### Design Token Changes (in :root block, lines 72-136)\n\n1. **Unify heading colors** — Change line 87 `--color-text-heading: #353a44` to `--color-text-heading: #1a1b25` and line 88 `--color-text-title: #21252c` to `--color-text-title: #1a1b25`\n\n2. **Content max-width** — Change line 110 `--content-max-width: 800px` to `--content-max-width: 880px`\n\n3. **TOC width** — Change line 109 `--toc-width: 200px` to `--toc-width: 250px`\n\n### Add responsive sidebar width (after the :root closing brace, ~line 136)\n\n4. **Sidebar responsive** — Add after the `:root` block:\n```css\n@media (min-width: 1400px) {\n :root {\n --sidebar-width: 280px;\n }\n}\n```\n\n### Header Changes (lines 175-221)\n\n5. **Header background** — Change line 181 from `background: rgba(255, 255, 255, 0.95)` to `background: #f6f8fa`. Remove lines 182-183 (`backdrop-filter` and `-webkit-backdrop-filter`).\n\n6. **Logo font-size** — Change line 196 `font-size: 15px` to `font-size: 16px`\n\n### Breadcrumb Changes (lines 223-268)\n\n7. **Breadcrumb font-size** — Change line 235 `font-size: 13px` to `font-size: 14px`\n\n8. **Breadcrumb separator** — Change line 245 `margin: 0 6px` to `margin: 0 8px`. Change line 246 `color: var(--color-text-secondary)` to `color: #a3acba`\n\n### Sidebar Changes (lines 275-439)\n\n9. **Sidebar content padding** — Change line 304 `padding: var(--space-6) 0` to `padding: var(--space-3) 0`\n\n10. **Sidebar section spacing** — Change line 350 `margin-top: var(--space-5)` to `margin-top: var(--space-4)` and line 351 `padding-top: var(--space-5)` to `padding-top: var(--space-4)`\n\n11. **Sidebar section header** — Change the `.sidebar-section-header` rule (lines 362-369):\n```css\n.sidebar-section-header {\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--color-text-secondary);\n padding: var(--space-1) var(--space-4);\n margin-bottom: var(--space-2);\n}\n```\n\n12. **Sidebar link density** — In `.sidebar-link` (lines 384-395), change `padding: 6px var(--space-3)` to `padding: 4px var(--space-2)` and change `border-radius: var(--radius-md)` to `border-radius: var(--radius-sm)`\n\n13. **Remove old sidebar-nav-link-row and sidebar-expand-btn rules** — Delete the `.sidebar-nav-link-row` rule (lines 379-382) and the `.sidebar-expand-btn` rules (lines 420-435). These are no longer used since the chevron is now inline.\n\n14. **Add sidebar-chevron styling** — Add after the `.sidebar-link` rules:\n```css\n.sidebar-chevron {\n flex-shrink: 0;\n margin-right: 4px;\n color: var(--color-text-secondary);\n}\n```\n\n15. **Add sidebar-link-toggle** — Add:\n```css\n.sidebar-link-toggle {\n cursor: pointer;\n}\n```\n\n16. **Sidebar link needs flex for inline chevron** — Add `display: flex; align-items: center;` to `.sidebar-link` (it currently has `display: block`). Change `display: block` to `display: flex` and add `align-items: center;`\n\n17. **Sidebar active link** — Change `.sidebar-link-active` (lines 403-407):\n```css\n.sidebar-link-active {\n color: var(--color-link);\n font-weight: 500;\n background: none;\n border-left: 2px solid var(--color-link);\n margin-left: -2px;\n}\n```\n\n18. **Sidebar parent-of-active** — Change `.sidebar-link-parent-active` (lines 415-418):\n```css\n.sidebar-link-parent-active {\n font-weight: 500;\n color: var(--color-text-primary);\n}\n```\n\n### Content Area Changes (lines 441-451)\n\n19. **Content padding** — Change line 446 `padding: var(--space-10) var(--space-12)` to `padding: var(--space-8) var(--space-10)` (32px top, 40px sides)\n\n### TOC Changes (lines 453-502)\n\n20. **TOC link font-size** — Change line 484 `font-size: 13px` to `font-size: 14px`\n\n21. **TOC link spacing** — Change line 483 `padding: var(--space-1) 0` to `padding: var(--space-2) 0`\n\n22. **TOC title margin** — Change line 474 `margin-bottom: var(--space-2)` to `margin-bottom: var(--space-4)`\n\n23. **Add TOC H3 indentation** — Add after the `.toc-link-active` rule:\n```css\n.toc-link-h3 {\n padding-left: 22px;\n font-size: 13px;\n}\n```\n\n24. **TOC responsive breakpoint** — Change line 878 `@media (max-width: 1100px)` to `@media (max-width: 1200px)`\n\n### Typography Changes (lines 556-705)\n\n25. **H1** — Remove `letter-spacing: -0.02em` from `.layout-content h1` (line 564)\n\n26. **H2** — Change `.layout-content h2` (lines 567-574): `font-size: 24px`, `line-height: 32px` (was 20px / 28px)\n\n27. **H3** — Change `.layout-content h3` (lines 576-583): `font-size: 16px`, `font-weight: 700` (was 18px / 600)\n\n28. **Paragraph spacing** — Change line 633 `margin-bottom: var(--space-4)` to `margin-bottom: var(--space-3)` (12px)\n\n29. **Bold text** — Change line 598 `font-weight: 600` to `font-weight: 700`\n\n30. **Inline code border** — In the inline code rule (lines 689-696), add `border: 1px solid var(--color-border)` and change `padding: 2px 6px` to `padding: 1px 4px`\n\n31. **Code blocks dark theme** — Change `.layout-content pre` (lines 671-681):\n```css\n.layout-content pre {\n background: #0a2540;\n padding: var(--space-4);\n border-radius: var(--radius-sm);\n overflow-x: auto;\n margin-bottom: var(--space-4);\n font-family: var(--font-mono);\n font-size: 13px;\n line-height: 19px;\n color: #f5fbff;\n}\n```\n\n32. **Code block scrollbar** — Update the code block scrollbar thumb colors (lines 47-54) to work on dark background: change `rgba(0, 0, 0, 0.12)` to `rgba(255, 255, 255, 0.15)` and `rgba(0, 0, 0, 0.2)` to `rgba(255, 255, 255, 0.25)`. Also update the Firefox scrollbar-color on line 70 from `rgba(0, 0, 0, 0.12)` to `rgba(255, 255, 255, 0.15)`.\n\n33. **Blockquote** — Change `.layout-content blockquote` (lines 733-738): `border-left: 1px solid #c0c8d2` (was 4px solid var(--color-border)), `padding: 5px 0 5px 10px` (was 0 16px), add `font-size: 14px; line-height: 20px`\n\n34. **Table headers** — In `.layout-content th` (lines 714-720), add `text-transform: uppercase; font-size: 13px; letter-spacing: 0.03em`\n\n35. **Scrollbar gutter** — Add `scrollbar-gutter: stable;` to `.sidebar-content` (around line 303)\n\n### Link styling\n\n36. **Content links** — Change `.layout-content a` (lines 657-662): remove `border-bottom: 1px solid transparent` and add `text-decoration: underline; text-underline-offset: 2px; text-decoration-color: rgba(5, 112, 222, 0.4)`. Change `.layout-content a:hover` (lines 664-668): remove `border-bottom-color` line, change to `text-decoration-color: var(--color-link-hover)`\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\nconst checks = [\n [css.includes(\\\"--color-text-heading: #1a1b25\\\"), \\\"heading color unified\\\"],\n [css.includes(\\\"--color-text-title: #1a1b25\\\"), \\\"title color unified\\\"],\n [css.includes(\\\"--content-max-width: 880px\\\"), \\\"content max-width 880\\\"],\n [css.includes(\\\"--toc-width: 250px\\\"), \\\"toc width 250\\\"],\n [css.includes(\\\"min-width: 1400px\\\"), \\\"responsive sidebar\\\"],\n [css.includes(\\\"background: #f6f8fa\\\") || css.includes(\\\"background:#f6f8fa\\\"), \\\"header solid bg\\\"],\n [css.includes(\\\"background: #0a2540\\\"), \\\"dark code blocks\\\"],\n [css.includes(\\\"color: #f5fbff\\\"), \\\"light code text\\\"],\n [css.includes(\\\"font-size: 13px\\\") \u0026\u0026 css.includes(\\\"line-height: 19px\\\"), \\\"code block sizing\\\"],\n [css.includes(\\\"toc-link-h3\\\"), \\\"toc h3 class\\\"],\n [css.includes(\\\"sidebar-chevron\\\"), \\\"sidebar chevron class\\\"],\n [css.includes(\\\"font-weight: 500\\\") \u0026\u0026 css.includes(\\\"sidebar-link-active\\\"), \\\"active link weight 500\\\"],\n [css.includes(\\\"border-left: 2px solid var(--color-link)\\\"), \\\"active left border\\\"],\n [css.includes(\\\"text-underline-offset\\\"), \\\"link underline style\\\"],\n [css.includes(\\\"scrollbar-gutter: stable\\\"), \\\"scrollbar gutter\\\"],\n [css.includes(\\\"max-width: 1200px\\\"), \\\"toc breakpoint 1200\\\"],\n [css.match(/\\\\.layout-content h2[^}]*font-size: 24px/s), \\\"h2 24px\\\"],\n [css.match(/\\\\.layout-content h3[^}]*font-size: 16px/s), \\\"h3 16px\\\"],\n [css.match(/\\\\.layout-content h3[^}]*font-weight: 700/s), \\\"h3 weight 700\\\"],\n];\nlet pass = true;\nfor (const [ok, name] of checks) {\n if (!ok) { console.error(\\\"FAIL: \\\" + name); pass = false; }\n else { console.log(\\\"PASS: \\\" + name); }\n}\nif (!pass) process.exit(1);\nconsole.log(\\\"\\\\nAll CSS checks passed.\\\");\n\"\n```\n\n## Dont\n- Do not modify any component JS files\n- Do not change the mobile responsive rules (keep mobile padding as-is)\n- Do not remove any CSS rules that are still referenced by components (check before deleting)\n- Do not change the design token variable names, only their values","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T22:25:14.346526+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T22:31:23.234686+13:00","closed_at":"2026-02-14T22:31:23.234686+13:00","close_reason":"2fda6b5 Applied all 36 CSS changes for Stripe design parity: unified heading colors, updated layout dimensions, improved sidebar/TOC styling, dark code blocks, enhanced typography, and responsive breakpoints","dependencies":[{"issue_id":"docs-150.4","depends_on_id":"docs-150","type":"parent-child","created_at":"2026-02-14T22:25:14.348237+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-1ho","title":"Epic: Adopt scaffold visual design language for docs site","description":"Migrate the documentation site's visual design (fonts, colors, radii, typography, header) to match the hypercerts-scaffold-atproto design language. The scaffold uses Syne for display/headings, Outfit for body text, Geist Mono for code, an OKLCH indigo-tinted neutral palette, 10px base radius, and a refined modern aesthetic. The docs site currently uses system fonts, hex colors (#0570de accent), plain CSS with 4-8px radii, and a light-grey header. This epic updates the CSS custom properties and font loading — it does NOT introduce Tailwind or shadcn. The docs site stays plain CSS. Success: the docs site visually reads as the same brand as the scaffold app.","status":"closed","priority":1,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T18:19:37.842151+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:36:09.646131+08:00","closed_at":"2026-02-20T18:36:09.646135+08:00","labels":["needs-integration-review","scope:medium"]} -{"id":"docs-1ho.1","title":"Load Syne, Outfit, and Geist Mono fonts","description":"## Files\n- documentation/components/Layout.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\n\n### 1. Add Google Fonts in Layout.js \u003cHead\u003e\nAdd a `\u003clink\u003e` tag inside the existing `\u003cHead\u003e` block to load Syne (400;500;600;700;800), Outfit (300;400;500;600;700), and Geist Mono (400;500) from Google Fonts.\n\nUse this exact URL:\n```\nhttps://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800\u0026family=Outfit:ital,wght@0,300;0,400;0,500;0,600;0,700\u0026family=Geist+Mono:wght@400;500\u0026display=swap\n```\n\nAdd both a `\u003clink rel=\"preconnect\"\u003e` for `fonts.googleapis.com` and `fonts.gstatic.com`, plus the stylesheet link. Place them BEFORE the existing `\u003cmeta\u003e` tags inside `\u003cHead\u003e`.\n\n### 2. Update CSS custom properties in globals.css\nReplace the existing `--font-sans` and `--font-mono` custom properties in the `:root` block:\n\n```css\n--font-sans: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,\n Helvetica, Arial, sans-serif;\n--font-display: 'Syne', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n--font-mono: 'Geist Mono', 'Source Code Pro', SFMono-Regular, Menlo, Monaco,\n Consolas, monospace;\n```\n\nNote: `--font-display` is a NEW custom property. Keep the system font fallbacks.\n\n### 3. Apply the display font to headings\nAdd these rules right after the existing `body { ... }` rule:\n\n```css\nh1, h2, h3, h4, h5, h6,\n.sidebar-section-header {\n font-family: var(--font-display);\n}\n```\n\nThis single rule applies Syne to all headings and sidebar section headers.\n\n## Don't\n- Do NOT remove any existing CSS rules\n- Do NOT change any font sizes, weights, or colors in this task (that is a separate task)\n- Do NOT add next/font — this project uses the Pages Router, use \u003clink\u003e tags\n- Do NOT add fonts to _app.js — they go in Layout.js \u003cHead\u003e\n- Do NOT touch any files other than Layout.js and globals.css","acceptance_criteria":"1. Visiting the docs site shows Outfit for body text (inspect body element, computed font-family starts with Outfit)\n2. All headings (h1-h6) render in Syne (inspect any h2, computed font-family starts with Syne)\n3. Code blocks render in Geist Mono (inspect a \u003ccode\u003e element, computed font-family starts with Geist Mono)\n4. Sidebar section headers render in Syne\n5. The Google Fonts link tag exists in the rendered HTML \u003chead\u003e\n6. Preconnect links exist for fonts.googleapis.com and fonts.gstatic.com\n7. globals.css defines three font custom properties: --font-sans, --font-display, --font-mono\n8. `npm run build` in documentation/documentation/ succeeds without errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":20,"created_at":"2026-02-20T18:19:58.309568+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:24:39.028803+08:00","closed_at":"2026-02-20T18:24:39.028803+08:00","close_reason":"db0ab3a Load Syne, Outfit, and Geist Mono fonts","labels":["scope:small"],"dependencies":[{"issue_id":"docs-1ho.1","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:19:58.310671+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-1ho.2","title":"Migrate color palette to OKLCH indigo-tinted neutrals","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nReplace the hex color values in the `:root` CSS custom properties with OKLCH equivalents that match the scaffold's indigo-tinted neutral palette (hue ~260). This aligns the docs site with the scaffold's visual identity.\n\n### Replace these custom properties in `:root`:\n\n**Neutrals:**\n```css\n/* OLD → NEW */\n--color-bg: #ffffff; → --color-bg: oklch(0.995 0.001 260);\n--color-bg-subtle: #f6f8fa; → --color-bg-subtle: oklch(0.96 0.005 260);\n--color-border: #ebeef1; → --color-border: oklch(0.91 0.005 260);\n--color-border-strong: #d8dee4; → --color-border-strong: oklch(0.86 0.008 260);\n--color-text-primary: #414552; → --color-text-primary: oklch(0.40 0.015 260);\n--color-text-heading: #1a1b25; → --color-text-heading: oklch(0.20 0.01 260);\n--color-text-title: #1a1b25; → --color-text-title: oklch(0.16 0.005 260);\n--color-text-secondary: #687385;→ --color-text-secondary: oklch(0.55 0.01 260);\n--color-text-sidebar: #414552; → --color-text-sidebar: oklch(0.40 0.015 260);\n```\n\n**Accent / Links — keep the blue but express in OKLCH:**\n```css\n--color-link: #0570de; → --color-link: oklch(0.50 0.18 260);\n--color-link-hover: #0055bc; → --color-link-hover: oklch(0.43 0.19 260);\n--color-accent: #0570de; → --color-accent: oklch(0.50 0.18 260);\n```\n\n**Interaction colors (misc section):**\n```css\n--hover-bg: #f6f8fa; → --hover-bg: oklch(0.96 0.005 260);\n--active-bg: #f0f4ff; → --active-bg: oklch(0.95 0.015 260);\n--focus-ring: 0 0 0 4px rgba(5, 112, 222, 0.36); → --focus-ring: 0 0 0 4px oklch(0.50 0.18 260 / 0.36);\n```\n\n**Callout colors — keep the hues but express in OKLCH:**\n```css\n--color-info: #0570de; → --color-info: oklch(0.50 0.18 260);\n--color-info-bg: #f0f7ff; → --color-info-bg: oklch(0.96 0.02 260);\n--color-warning: #c84801; → --color-warning: oklch(0.55 0.17 55);\n--color-warning-bg: #fef9f0; → --color-warning-bg: oklch(0.97 0.02 80);\n--color-danger: #df1b41; → --color-danger: oklch(0.52 0.22 25);\n--color-danger-bg: #fef0f4; → --color-danger-bg: oklch(0.97 0.02 15);\n--color-success: #228403; → --color-success: oklch(0.52 0.17 145);\n--color-success-bg: #f0fef0; → --color-success-bg: oklch(0.97 0.03 145);\n```\n\n### Also update these hardcoded colors in other selectors:\n\n1. `.layout-header` background: change `#f6f8fa` → `oklch(0.985 0.002 260)` (scaffold's background tone for the header — near-white, matching the page body but with a faint warmth)\n\n2. `.breadcrumbs-separator` color: change `#a3acba` → `oklch(0.70 0.01 260)`\n\n3. `.layout-content blockquote` border-left color: change `#c0c8d2` → `oklch(0.80 0.01 260)`\n\n### DO NOT change these hardcoded colors (they are code-block-specific and should stay):\n- `#011627` (Night Owl background)\n- `#0d2137` (code block header)\n- `#1e3a5f` (code block border)\n- `#8899aa`, `#556677`, `#aabbcc` (code block UI elements)\n- Any `rgba(255, 255, 255, ...)` scrollbar colors inside code blocks\n\n## Don't\n- Do NOT change font families, font sizes, font weights, or spacing\n- Do NOT change the code block color scheme (Night Owl)\n- Do NOT touch Layout.js or any other files\n- Do NOT add dark mode (separate concern)\n- Do NOT change the LANGUAGE_META colors in CodeBlock.js\n- Do NOT add new CSS rules — only modify existing property values","acceptance_criteria":"1. All `:root` color custom properties use oklch() syntax (no hex values remain for tokens listed above)\n2. The page background is near-white with a faint blue tint (visually similar to before but not pure white)\n3. Links are still blue and visually similar to before\n4. Callout boxes still show 4 distinct colors (blue info, orange warning, red danger, green success)\n5. Code blocks are unchanged (Night Owl dark blue background preserved)\n6. Header background is updated to oklch value\n7. Breadcrumb separator and blockquote border use oklch values\n8. `npm run build` in documentation/documentation/ succeeds without errors\n9. The site loads correctly in Chrome 111+ and Safari 15.4+ (oklch browser support baseline)","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":25,"created_at":"2026-02-20T18:20:31.538843+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:27:47.682078+08:00","closed_at":"2026-02-20T18:27:47.682078+08:00","close_reason":"6f5dd21 Migrate color palette to OKLCH indigo-tinted neutrals","labels":["scope:small"],"dependencies":[{"issue_id":"docs-1ho.2","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:20:31.54053+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-1ho.2","depends_on_id":"docs-1ho.1","type":"blocks","created_at":"2026-02-20T18:20:31.542591+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-1ho.3","title":"Update border radii and spacing to match scaffold","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nUpdate the border radius custom properties and a few spacing values to match the scaffold's more rounded, modern aesthetic.\n\n### 1. Update radius tokens in `:root`\n```css\n/* OLD → NEW */\n--radius-sm: 4px; → --radius-sm: 6px;\n--radius-md: 6px; → --radius-md: 8px;\n--radius-lg: 8px; → --radius-lg: 10px;\n```\n\nAlso add a new token after `--radius-lg`:\n```css\n--radius-xl: 14px;\n```\n\n### 2. Update the code block border-radius\nIn `.codeblock`, change:\n```css\nborder-radius: 8px; → border-radius: var(--radius-lg);\n```\n\nIn the fallback rule `.layout-content pre:not(.codeblock-pre)`, change:\n```css\nborder-radius: 8px; → border-radius: var(--radius-lg);\n```\n\n### 3. Update card-link border-radius\nIn `.card-link`, change:\n```css\nborder-radius: var(--radius-lg);\n```\nThis already uses the token — it will automatically pick up the new 10px value. No change needed.\n\n### 4. Update pagination-link border-radius\nIn `.pagination-link`, it already uses `var(--radius-lg)` — will pick up automatically.\n\n## Don't\n- Do NOT change any font, color, or typography properties\n- Do NOT change spacing tokens (--space-*) — they are fine as-is\n- Do NOT change the code block internal padding or header styling\n- Do NOT touch any files other than globals.css","acceptance_criteria":"1. --radius-sm is 6px, --radius-md is 8px, --radius-lg is 10px, --radius-xl is 14px in :root\n2. Code blocks have 10px border radius (inspect .codeblock element)\n3. Card links have 10px border radius\n4. Sidebar links have 8px border radius (--radius-md)\n5. Inline code still has rounded corners (uses --radius-sm = 6px now)\n6. `npm run build` succeeds without errors","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":15,"created_at":"2026-02-20T18:20:48.238131+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:25:12.922188+08:00","closed_at":"2026-02-20T18:25:12.922188+08:00","close_reason":"8d7ef9e Update border radii and spacing to match scaffold","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-1ho.3","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:20:48.239264+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-1ho.4","title":"Refine heading typography to match scaffold display style","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nUpdate heading styles to match the scaffold's Syne display typography conventions. The scaffold uses tighter letter-spacing and bolder heading hierarchy with Syne. These changes assume docs-1ho.1 (font loading) is already applied.\n\n### 1. Update `.layout-content h1`\nAdd `letter-spacing: -0.02em;` (tracking-tight in scaffold convention).\nKeep all other properties (font-size: 32px, font-weight: 700, etc.) unchanged.\n\n### 2. Update `.layout-content h2`\nAdd `letter-spacing: -0.01em;`.\nKeep all other properties unchanged.\n\n### 3. Update `.sidebar-section-header`\nChange from:\n```css\n.sidebar-section-header {\n font-size: 14px;\n font-weight: 600;\n text-transform: uppercase;\n color: var(--color-text-heading);\n padding: var(--space-1) var(--space-5);\n margin-bottom: var(--space-3);\n}\n```\nTo:\n```css\n.sidebar-section-header {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--color-text-secondary);\n padding: var(--space-1) var(--space-5);\n margin-bottom: var(--space-3);\n}\n```\n\nThis matches the scaffold's convention: sidebar section headers are smaller (11px), bolder (700), wider-tracked (0.08em), and use secondary color to feel like category labels rather than content headings.\n\n### 4. Update `.toc-title`\nChange `font-weight: 700` → `font-weight: 600` and add `font-family: var(--font-display);`\n\nThis gives the TOC \"On this page\" label the Syne display treatment.\n\n## Don't\n- Do NOT change font-size on h1, h2, h3, h4 (only add letter-spacing)\n- Do NOT change any colors (that is the colors task)\n- Do NOT change body text or paragraph styling\n- Do NOT touch any files other than globals.css","acceptance_criteria":"1. H1 has letter-spacing: -0.02em (inspect computed style)\n2. H2 has letter-spacing: -0.01em\n3. Sidebar section headers are 11px, font-weight 700, letter-spacing 0.08em, color is --color-text-secondary\n4. TOC title uses Syne (font-family: var(--font-display))\n5. No heading font sizes have changed from their current values\n6. `npm run build` succeeds without errors","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":15,"created_at":"2026-02-20T18:21:05.00645+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:29:09.519173+08:00","closed_at":"2026-02-20T18:29:09.519173+08:00","close_reason":"6f5dd21 Refine heading typography to match scaffold display style","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-1ho.4","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:21:05.008011+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-1ho.4","depends_on_id":"docs-1ho.1","type":"blocks","created_at":"2026-02-20T18:21:05.009956+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-1ho.5","title":"Add antialiased rendering and body font-weight","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe scaffold uses `antialiased` font rendering and Outfit at weight 400 for body text. The docs site already has `-webkit-font-smoothing: antialiased` and `-moz-osx-font-smoothing: grayscale` on body — but Outfit renders slightly differently than system fonts at the default weight. Add `font-weight: 400;` explicitly to the `body` rule to ensure consistent rendering with Outfit.\n\n### 1. In the `body { ... }` rule, add:\n```css\nfont-weight: 400;\n```\nPlace it after `font-size: 16px;`.\n\n### 2. Update the existing `body` line-height from `1.65` to `1.6`\nThe scaffold uses tighter line-height with Outfit. Change:\n```css\nline-height: 1.65; → line-height: 1.6;\n```\n\n### 3. Update list item line-height to match\nIn `.layout-content article li`, change:\n```css\nline-height: 1.65; → line-height: 1.6;\n```\n\n## Don't\n- Do NOT change any font-family properties\n- Do NOT change any colors\n- Do NOT change heading styles\n- Do NOT touch any files other than globals.css","acceptance_criteria":"1. Body element has font-weight: 400 in computed styles\n2. Body element has line-height: 1.6\n3. List items have line-height: 1.6\n4. Text renders with antialiased smoothing (already present, verify not removed)\n5. `npm run build` succeeds without errors","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":10,"created_at":"2026-02-20T18:21:19.242335+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:27:48.11089+08:00","closed_at":"2026-02-20T18:27:48.11089+08:00","close_reason":"6f5dd21 Add antialiased rendering and body font-weight","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-1ho.5","depends_on_id":"docs-1ho","type":"parent-child","created_at":"2026-02-20T18:21:19.243605+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-1ho.5","depends_on_id":"docs-1ho.1","type":"blocks","created_at":"2026-02-20T18:21:19.245486+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-2wk","title":"Epic: Typography \u0026 Content Readability","description":"Fix typography hierarchy and content readability: H3 at 16px collapses into body text, content max-width 920px is too wide (~95 chars/line), body text too light, asymmetric padding, insufficient H2 whitespace, understyled blockquotes. Polish: paragraph margin, inline code padding, scroll-margin-top, code block margins. Covers UX findings #5, 6, 16, 17, 18, 19, 31, 32, 33, 34.","status":"closed","priority":1,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T19:53:44.087105+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:13:06.979146+08:00","closed_at":"2026-02-20T20:13:06.979146+08:00","close_reason":"fe58266 all typography tasks complete","labels":["scope:medium"]} -{"id":"docs-2wk.1","title":"Heading scale + body color + content width + padding","description":"## Files\n- styles/globals.css (modify)\n\n## What to do\nAll changes are CSS-only in globals.css.\n\n### H3 size increase (finding #5)\nChange `.layout-content h3` font-size from `16px` to `18px`. This is the same as body text size currently, making sections indistinguishable. Also set `letter-spacing: -0.005em` on h3 for visual distinction.\n\nMobile h3 (`@media max-width: 768px`): change from `15px` to `17px`.\n\n### Content max-width (finding #6)\nChange `--content-max-width: 920px` to `--content-max-width: 768px` in `:root`. This brings line length from ~95 chars to ~72 chars (optimal).\n\n### Body text color (finding #16)\nChange `--color-text-primary: oklch(0.40 0.015 260)` to `--color-text-primary: oklch(0.30 0.015 260)` in `:root` (light mode only). This darkens body text for better readability.\n\nAlso update `--color-text-sidebar` from `oklch(0.40 0.015 260)` to `oklch(0.35 0.015 260)` to keep sidebar slightly lighter than body but still readable.\n\n### Content right padding (finding #17)\nThe current `.layout-content` has `padding: var(--space-6) 0 var(--space-8) 72px` — 72px left, 0px right. Add right padding:\nChange to: `padding: var(--space-6) var(--space-8) var(--space-8) 72px`\n\nThis adds 32px on the right to balance the 72px left margin.\n\n### H2 margin increase (finding #18)\nChange `.layout-content h2` margin-top from `var(--space-12)` (48px) to `56px`.\n\n## Dont\n- Do NOT change dark mode text colors — only light mode\n- Do NOT change H1 or H2 font sizes\n- Do NOT change font families\n- Do NOT touch the tablet/mobile responsive padding overrides — those are fine as-is","acceptance_criteria":"1. H3 headings render at 18px (was 16px), visually distinct from 16px body text\n2. Content area max-width is 768px (was 920px)\n3. Body text color is noticeably darker (oklch 0.30 vs 0.40) in light mode\n4. Dark mode body text color is unchanged\n5. Content area has visible right padding (~32px) instead of flush right edge\n6. H2 sections have more breathing room above (56px gap instead of 48px)\n7. Build passes: npm run build -- --webpack","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":30,"created_at":"2026-02-20T19:54:01.085494+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.804273+08:00","closed_at":"2026-02-20T20:06:32.804273+08:00","close_reason":"1094f5d typography fixes","labels":["scope:small"],"dependencies":[{"issue_id":"docs-2wk.1","depends_on_id":"docs-2wk","type":"parent-child","created_at":"2026-02-20T19:54:01.08663+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-2wk.2","title":"Blockquotes + inline code + scroll-margin + code margin + paragraph margin","description":"## Files\n- styles/globals.css (modify)\n\n## What to do\nAll changes are CSS-only in globals.css.\n\n### Blockquotes (finding #19)\nCurrent blockquote styles are understyled: 1px border, cramped padding, smaller font.\nReplace the `.layout-content blockquote` rule at ~line 1086:\n\n```css\n.layout-content blockquote {\n border-left: 3px solid var(--color-border-strong);\n padding: var(--space-4) var(--space-5);\n margin: var(--space-6) 0;\n color: var(--color-text-secondary);\n font-size: 16px;\n line-height: 1.6;\n background: var(--color-bg-subtle);\n border-radius: 0 var(--radius-md) var(--radius-md) 0;\n}\n```\n\nUpdate dark mode blockquote:\n```css\nhtml.dark .layout-content blockquote {\n border-left-color: oklch(0.40 0.01 260);\n background: oklch(0.16 0.005 260);\n}\n```\n\n### Paragraph margin (finding #31)\nChange `.layout-content p` margin-bottom from `var(--space-4)` (16px) to `var(--space-5)` (20px).\n\n### Inline code padding (finding #32)\nChange `.layout-content p code, .layout-content li code, .layout-content td code, .layout-content th code` padding from `1px 4px` to `2px 6px`.\n\n### Scroll-margin-top on headings (finding #33)\nAdd scroll-margin-top to all content headings so anchor links dont hide behind the sticky header:\n\n```css\n.layout-content h1,\n.layout-content h2,\n.layout-content h3,\n.layout-content h4 {\n scroll-margin-top: calc(var(--header-height) + var(--space-6));\n}\n```\n\nNote: `html` already has `scroll-padding-top` set, but individual headings need `scroll-margin-top` for anchor (#hash) navigation to work correctly.\n\n### Code block vertical margin (finding #34)\nChange `.codeblock` margin-bottom from `var(--space-4)` (16px) to `var(--space-6)` (24px).\nAlso change `.layout-content pre:not(.codeblock-pre)` margin-bottom from `var(--space-4)` to `var(--space-6)`.\n\n## Dont\n- Do NOT change code block colors or font sizes\n- Do NOT modify the .codeblock-pre styles\n- Do NOT touch the dark mode inline code styles (they were already set)","acceptance_criteria":"1. Blockquotes have 3px left border, 16px padding, subtle background, body-sized font (16px), and rounded right corners\n2. Dark mode blockquotes have adapted border and background colors\n3. Paragraphs have 20px bottom margin (was 16px)\n4. Inline code has 2px 6px padding (was 1px 4px), visibly more spacious\n5. Clicking an anchor link (#heading) scrolls the heading below the sticky header, not behind it\n6. Code blocks have 24px bottom margin (was 16px)\n7. Build passes: npm run build -- --webpack","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":30,"created_at":"2026-02-20T19:54:17.337357+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:13:06.923965+08:00","closed_at":"2026-02-20T20:13:06.923965+08:00","close_reason":"fe58266 blockquotes + inline code + scroll-margin + code margin","labels":["scope:small"],"dependencies":[{"issue_id":"docs-2wk.2","depends_on_id":"docs-2wk","type":"parent-child","created_at":"2026-02-20T19:54:17.338566+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-2wk.2","depends_on_id":"docs-2wk.1","type":"blocks","created_at":"2026-02-20T19:56:45.710114+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-390","title":"Epic: Replace speculative blockchain/tokenization language with freeze-then-fund model across all docs","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:19:57.096114+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T19:52:57.976277+13:00","closed_at":"2026-02-16T19:52:57.976277+13:00","close_reason":"Closed"} -{"id":"docs-390.1","title":"Rewrite architecture/overview.md: replace blockchain ownership layer with freeze-then-fund model","description":"## Files\n- documentation/pages/architecture/overview.md (modify)\n\n## What to do\n\nUpdate all references to blockchain-based tokenization/ownership to reflect that:\n1. The tokenization layer has NOT been developed yet — it is TBD\n2. The theory and architecture are correct — freeze records, anchor on-chain, then fund\n3. The docs should present this as the intended design, not as something that exists today\n\nThe freeze-then-fund model:\n1. A hypercert starts as mutable ATProto records (activity claim + evidence + evaluations etc.)\n2. Before a hypercert can be funded, its ATProto records must be **frozen** — a snapshot of the current state is taken and anchored on-chain. This creates an immutable reference point.\n3. The reason: a funder must know exactly what they are funding. If the cert contents can still change after funding, the funder might end up paying for something different than what they signed up for.\n4. Once frozen and anchored on-chain, the hypercert can be listed for funding.\n5. Evaluations and evidence can still accumulate AFTER freezing — they are separate records that reference the frozen claim. But the core claim itself is immutable once frozen.\n\n### IMPORTANT FRAMING GUIDANCE\n- Do NOT strip out the blockchain/tokenization concept — the theory is right and the architecture is sound\n- DO make clear that the tokenization layer hasn't been built yet — it's TBD\n- Frame it as: \"the intended design is X\" or \"the planned approach is X\" — not as if it's live\n- Use phrases like \"the tokenization layer is not yet implemented\", \"this is the planned design\", \"the on-chain mechanisms are being designed\"\n- Keep the two-layer architecture (data layer + ownership/funding layer) — it's the right design\n- When describing how tokenization/funding WILL work, use future tense or \"planned\" language\n- The freeze-then-fund concept is the KEY insight to convey: you must freeze the cert before allowing funding\n\n### Specific changes needed:\n\n**Line 8:** Change \"combines AT Protocol for data portability with blockchain for ownership guarantees\" → something like \"combines AT Protocol for data portability with planned on-chain anchoring for ownership and funding guarantees.\" Make clear this is the design, not yet implemented.\n\n**Line 18 (Ownership Layer description):** Rewrite. The ownership layer is planned but not yet built. The intended design: frozen hypercert snapshots will be anchored on-chain so they can be funded. A hypercert cannot be funded while its contents are still changing — freezing ensures funders know exactly what they are paying for. Make clear this layer is TBD.\n\n**Lines 20-31 (stack diagram):** Update the bottom layer label. Add \"(planned)\" or similar to indicate it's not yet built.\n\n**Lines 63-81 (Ownership Layer Deep Dive):** Rewrite this section to describe the PLANNED design:\n- \"Anchoring\": Describe the planned approach — freezing the ATProto record state and anchoring the snapshot on-chain.\n- \"Tokenization\": Explain the planned approach — the specific on-chain mechanism (token standard, chain choice) is being designed. The concept: freeze the cert, anchor it, then enable funding.\n- \"Funding Mechanisms\": Keep the general concepts (direct purchase, retroactive funding, etc.) but frame as planned/intended, not implemented.\n- \"Multi-Chain Support\": Frame as intended design, not yet determined.\n\n**Lines 83-112 (How the Layers Connect):** Rewrite to describe the planned design:\n- The bridge will be: ATProto records are frozen, the snapshot CID is anchored on-chain, and funding operates against that frozen state.\n- Keep the cross-layer example but frame as \"how this will work\" not \"how this works.\"\n\n**Lines 114-136 (Key Design Decisions):**\n- \"Why Not Fully On-Chain?\" — Keep as-is, still valid.\n- \"Why Not Fully Off-Chain?\" — Rewrite to emphasize: without on-chain anchoring, there's no way to guarantee that what a funder pays for won't change. Freezing and anchoring provides the immutability guarantee.\n- \"Why This Separation?\" — Update to mention freeze-then-fund as the key planned mechanism.\n\n**Lines 138-148 (What This Enables):**\n- Frame enabled use cases as what the design WILL enable once the tokenization layer is built.\n\n### Key terminology:\n- Keep \"tokenization\" as a concept but mark as \"planned\" / \"not yet implemented\" / \"TBD\"\n- Use \"freeze\" / \"frozen\" / \"freezing\" to describe the new concept\n- Use future tense or \"planned\" language for anything on-chain\n- Keep \"funding\" language — the concept is real\n\n### Tone:\n- The architecture is sound, the theory is right\n- The tokenization layer just hasn't been built yet\n- Be clear about what exists (ATProto data layer) vs what's planned (on-chain ownership/funding layer)\n\n## Test\ngrep -q 'not yet\\|TBD\\|being designed\\|planned\\|will be' documentation/pages/architecture/overview.md \u0026\u0026 \\\ngrep -q 'freez' documentation/pages/architecture/overview.md \u0026\u0026 \\\ngrep -q 'funder.*know\\|know.*fund\\|change.*after\\|cannot.*funded.*changing\\|can.t.*funded.*chang\\|exactly what' documentation/pages/architecture/overview.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the blockchain/tokenization concept entirely — the theory is right, keep it as the planned design\n- Present tokenization as if it's already implemented/live\n- Invent specific smart contract details or token standards that don't exist\n- Change the Data Layer section (lines 33-61) — that's accurate as-is\n- Change the \"Why ATProto Over IPFS\" section (126-130) — that's accurate","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:20:42.512677+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T12:32:03.352974+13:00","closed_at":"2026-02-16T12:32:03.352974+13:00","close_reason":"9055496 Rewrite architecture/overview.md to reflect freeze-then-fund model and planned tokenization layer","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-390.1","depends_on_id":"docs-390","type":"parent-child","created_at":"2026-02-16T12:20:42.514231+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-390.2","title":"Rewrite architecture/data-flow-and-lifecycle.md: replace Stage 5 blockchain funding with freeze-then-fund","description":"## Files\n- documentation/pages/architecture/data-flow-and-lifecycle.md (modify)\n\n## What to do\n\nUpdate blockchain-centric language in the lifecycle doc. Key framing:\n1. The tokenization layer has NOT been developed yet — it is TBD\n2. The theory and architecture are correct — freeze records, anchor on-chain, then fund\n3. The docs should present this as the intended design, not as something that exists today\n\nThe freeze-then-fund model: before a hypercert can be funded, its ATProto records must be frozen (snapshotted and anchored on-chain). This protects funders — they need to know exactly what they're funding, and the cert contents must not change after funding.\n\n### IMPORTANT FRAMING GUIDANCE\n- Do NOT strip out the blockchain/tokenization concept — the theory is right and the architecture is sound\n- DO make clear that the tokenization layer hasn't been built yet — it's TBD\n- Frame it as: \"the intended design is X\" or \"the planned approach is X\" — not as if it's live\n- Use phrases like \"the tokenization layer is not yet implemented\", \"this is the planned design\", \"the on-chain mechanisms are being designed\"\n- When describing how tokenization/funding WILL work, use future tense or \"planned\" language\n- The freeze-then-fund concept is the KEY insight to convey\n\n### Specific changes:\n\n**Line 16 (Enrichment):** Change \"Rights records define what token holders receive\" → \"Rights records define what funders or stakeholders receive.\" (The concept of rights is correct, just don't assume tokens exist yet.)\n\n**Line 22 (Funding stage summary):** Rewrite. Current text presents tokenization as live. New text should describe the planned design: Before funding, the hypercert's ATProto records will be frozen — a cryptographic snapshot taken and anchored on-chain. This ensures funders know exactly what they are paying for. The cert's core content cannot change after freezing. The specific on-chain funding mechanisms are being designed.\n\n**Line 24 (Accumulation):** Update to reflect planned design. Evaluations and evidence continue accumulating around the frozen claim.\n\n**Line 29 (diagram):** Change \"Blockchain\" under Funding to \"On-chain (planned)\"\n\n**Line 69 (Rights records):** Change \"define what token holders receive\" → \"define what funders or stakeholders receive\"\n\n**Lines 146-181 (Stage 5: Funding \u0026 Ownership):** Rewrite this section to describe the PLANNED design:\n\n- **Anchoring:** Describe the planned approach — when a hypercert is ready for funding, its current ATProto state will be frozen. The snapshot CID is anchored on-chain. The reason: a funder must know exactly what they are funding.\n\n- **Tokenization → rename to \"Freezing and Immutability\":** Explain the planned concept. Once frozen, the core activity claim cannot be modified. Evidence and evaluations can still accumulate. The specific on-chain representation (token standard, contract design) is being designed. Note: the tokenization layer is not yet implemented, but the theory is sound.\n\n- **Funding Mechanisms:** Keep the general concepts but frame as planned. Various funding models are intended, including direct funding, retroactive funding, and impact certificates. The specific mechanisms are being designed.\n\n- **Multi-Chain Support:** Frame as intended design. The protocol plans to be chain-agnostic but specifics are TBD.\n\n- **Diagram:** Update to reflect planned state. Replace \"Token Contract\" / \"Token ID\" with planned equivalents like \"Frozen Snapshot\" / \"CID: bafyrei...\"\n\n**Lines 199-205 (Stage 6 - Ownership Transfers / Long-Term Value):**\n- Reframe around the planned design. The frozen on-chain anchor will persist independently. Remove language that presents token transfers as live. Frame as intended future behavior.\n\n**Line 212 (diagram):** Change \"Blockchain\" labels to \"On-chain (planned)\"\n\n### Key terminology:\n- Keep \"tokenization\" as a concept but mark as \"planned\" / \"not yet implemented\" / \"TBD\"\n- Use \"freeze\" / \"frozen\" / \"freezing\" to describe the new concept\n- \"funders\" instead of \"token holders\" where referring to people who fund\n- Use future tense or \"planned\" language for anything on-chain\n- Keep \"on-chain\" — blockchain IS the plan\n\n## Test\ngrep -q 'not yet\\|TBD\\|being designed\\|planned\\|will be' documentation/pages/architecture/data-flow-and-lifecycle.md \u0026\u0026 \\\ngrep -q 'freez' documentation/pages/architecture/data-flow-and-lifecycle.md \u0026\u0026 \\\ngrep -q 'cannot.*change\\|must not.*change\\|exactly what.*fund\\|know.*what.*pay\\|frozen' documentation/pages/architecture/data-flow-and-lifecycle.md \u0026\u0026 \\\n! grep -q 'token holder' documentation/pages/architecture/data-flow-and-lifecycle.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the blockchain/tokenization concept entirely — the theory is right, keep it as the planned design\n- Present tokenization as if it's already implemented/live\n- Change Stages 1-4 (Creation, Enrichment, Evaluation, Discovery) unless they contain token holder language\n- Invent smart contract details\n- Change the Cross-PDS References section (lines 215-234) — that's accurate\n- Change the \"What This Flow Enables\" section (lines 236-246) unless it has token language","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:21:20.091221+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T12:31:55.410818+13:00","closed_at":"2026-02-16T12:31:55.410818+13:00","close_reason":"5d8ba48 Replace Stage 5 blockchain funding with freeze-then-fund model","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-390.2","depends_on_id":"docs-390","type":"parent-child","created_at":"2026-02-16T12:21:20.092158+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-390.3","title":"Rewrite getting-started/the-hypercerts-infrastructure.md: replace blockchain ownership layer and integration patterns with freeze-then-fund","description":"## Files\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify)\n\n## What to do\n\nThis is the heaviest file — it has an entire \"ownership layer: blockchain\" section and 4 blockchain integration patterns. Update to reflect that the tokenization layer hasn't been developed yet but the theory is right.\n\n### IMPORTANT FRAMING GUIDANCE\n- Do NOT strip out the blockchain/tokenization concept — the theory is right and the architecture is sound\n- DO make clear that the tokenization layer hasn't been built yet — it's TBD\n- Frame it as: \"the intended design is X\" or \"the planned approach is X\" — not as if it's live\n- Use phrases like \"the tokenization layer is not yet implemented\", \"this is the planned design\", \"the on-chain mechanisms are being designed\"\n- Keep the two-layer architecture — it's the right design\n- The freeze-then-fund concept is the KEY insight: you must freeze the cert before allowing funding, because funders must know exactly what they're paying for\n\n### Core concept:\nBefore a hypercert can be funded, its ATProto records must be frozen — a snapshot is taken and anchored on-chain. This protects funders: they need to know exactly what they're paying for. If the cert contents could change after funding, a funder might end up paying for a different cert than what they committed to. The specific on-chain mechanisms (token standards, smart contracts, chain choices) are being designed.\n\n### Specific changes:\n\n**Line 7:** Change \"AT Protocol for data and blockchain for ownership\" → \"AT Protocol for data and planned on-chain anchoring for ownership and funding\"\n\n**Lines 19-23 (The ownership layer: blockchain):** Rewrite. Rename to something like \"The funding layer: on-chain anchoring (planned)\". Describe the intended design: when a hypercert is ready for funding, its ATProto records will be frozen and anchored on-chain. Explain WHY: funders must know exactly what they are paying for. Make clear this layer is not yet implemented but the theory is sound.\n\n**Lines 25-29 (How the layers connect):** Rewrite to describe the planned design. Keep the AT-URI example. Explain that when ready for funding, the claim's state will be frozen and its CID anchored on-chain. Note that a claim can exist on ATProto without ever being frozen for funding.\n\n**Lines 83-87 (Step 4: Ownership is anchored on-chain):** Rename to \"4. The hypercert is frozen and funded (planned)\". Describe the intended flow: Carol's funding app will freeze Alice's hypercert, anchor the snapshot on-chain, and Carol funds the frozen cert. Make clear this is the planned design. Alice's claim continues on ATProto with new evaluations referencing the frozen claim.\n\n**Lines 123-139 (Blockchain Integration Patterns):** Rewrite as \"Planned Funding Patterns\" or similar. Present as intended design patterns, not implemented features:\n- Pattern 1: Freeze-on-create — freeze immediately upon creation\n- Pattern 2: Freeze-when-ready — accumulate evidence first, freeze when ready for funding (expected default)\n- Pattern 3: Batch freezing — freeze multiple claims together periodically\n- Pattern 4: Partial freezing — freeze core claim while keeping some records mutable\nFor all patterns, note the tokenization layer is not yet implemented.\n\n**Lines 170-172 (Blockchain scalability):** Rewrite. Keep the idea that on-chain operations are expensive. Frame as planned design consideration.\n\n**Lines 184-186 (Access control via smart contracts):** Mark as future/planned. \"Token-gated data\" is a potential future feature.\n\n**Line 192:** Change \"implementing novel blockchain integration patterns\" → \"implementing freeze-then-fund patterns\"\n\n### Key terminology:\n- Keep \"tokenization\" as a concept but mark as \"planned\" / \"not yet implemented\"\n- Use \"freeze\" / \"frozen\" / \"freezing\" for the new concept\n- \"anchor on-chain\" for the planned on-chain step\n- Keep \"on-chain\" and \"blockchain\" where appropriate — it IS the plan\n- Add \"(planned)\" markers where helpful\n\n## Test\ngrep -q 'not yet\\|TBD\\|being designed\\|planned\\|will be' documentation/pages/getting-started/the-hypercerts-infrastructure.md \u0026\u0026 \\\ngrep -q 'freez' documentation/pages/getting-started/the-hypercerts-infrastructure.md \u0026\u0026 \\\ngrep -q 'cannot.*change\\|must not.*change\\|exactly what.*fund\\|know.*what.*pay\\|frozen' documentation/pages/getting-started/the-hypercerts-infrastructure.md \u0026\u0026 \\\n! grep -q 'mints an NFT' documentation/pages/getting-started/the-hypercerts-infrastructure.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the blockchain/tokenization concept entirely — the theory is right, keep it as the planned design\n- Present tokenization as if it's already implemented/live\n- Change the Data Layer section (lines 13-17) — accurate as-is\n- Change DID, PDS, Lexicon, XRPC, Repository sections (lines 33-61) — accurate\n- Change Steps 1-3 of the data flow (lines 65-81) — accurate\n- Change Migration and Portability section (lines 141-158) — accurate\n- Change Privacy section (lines 174-183) except the token-gated line\n- Invent specific smart contract or token standard details","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:21:57.277367+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T12:31:26.421333+13:00","closed_at":"2026-02-16T12:31:26.421333+13:00","close_reason":"a51984a Rewrite infrastructure doc to use freeze-then-fund model","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-390.3","depends_on_id":"docs-390","type":"parent-child","created_at":"2026-02-16T12:21:57.279592+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-390.4","title":"Rewrite getting-started/why-atproto.md: update blockchain complement section with freeze-then-fund","description":"## Files\n- documentation/pages/getting-started/why-atproto.md (modify)\n\n## What to do\n\nUpdate the \"ATProto + Blockchain: Better Together\" section to reflect that the tokenization layer hasn't been developed yet but the theory is right.\n\n### IMPORTANT FRAMING GUIDANCE\n- Do NOT strip out the blockchain/tokenization concept — the theory is right\n- DO make clear that the tokenization layer hasn't been built yet — it's TBD\n- Frame it as the intended/planned design, not as something live\n- The freeze-then-fund concept is the key insight to add\n\n### Specific changes:\n\n**Lines 49-51 (ATProto + Blockchain section):** Rewrite. Current text presents tokenization as live (\"a smart contract mints a token pointing to that record, funders purchase the token on-chain\").\n\nNew text should describe the planned design: ATProto handles the data layer — claims, evidence, evaluations, trust signals. On-chain anchoring is planned to handle the funding layer — freezing hypercert state and enabling funding flows. The intended flow: a contributor creates an activity claim on ATProto, the claim accumulates evidence and evaluations, when ready for funding the claim will be frozen and its snapshot anchored on-chain, funders will commit resources against the frozen cert (knowing exactly what they're paying for), and evaluations continue accumulating on ATProto over time. The tokenization layer is not yet implemented, but the architecture is designed for it.\n\n**Line 42 (table):** Keep \"Pure blockchain | Too expensive for rich data...\" — still accurate.\n\n## Test\ngrep -q 'not yet\\|TBD\\|being designed\\|planned\\|will be' documentation/pages/getting-started/why-atproto.md \u0026\u0026 \\\ngrep -q 'freez' documentation/pages/getting-started/why-atproto.md \u0026\u0026 \\\n! grep -q 'mints a token' documentation/pages/getting-started/why-atproto.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Change the Philosophy section (lines 10-22) — accurate\n- Change the For Builders section (lines 24-36) — accurate\n- Change the comparison table except if it mentions tokenization as live\n- Remove blockchain from the section title — it's still the plan\n- Remove the concept of tokenization — just mark it as planned/TBD\n- Invent implementation details","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:22:14.281265+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T12:30:38.309051+13:00","closed_at":"2026-02-16T12:30:38.309051+13:00","close_reason":"98d35ed Updated blockchain section to reflect freeze-then-fund as planned design, not live implementation","labels":["scope:small"],"dependencies":[{"issue_id":"docs-390.4","depends_on_id":"docs-390","type":"parent-child","created_at":"2026-02-16T12:22:14.282802+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-390.5","title":"Rewrite getting-started/why-were-building-hypercerts.md: update onchain anchoring section with freeze-then-fund","description":"## Files\n- documentation/pages/getting-started/why-were-building-hypercerts.md (modify)\n\n## What to do\n\nUpdate the \"Ownership \u0026 Funding Layer: Onchain\" section (lines 182-194) to reflect that the tokenization layer hasn't been developed yet but the theory is right.\n\n### IMPORTANT FRAMING GUIDANCE\n- Do NOT strip out the blockchain/tokenization concept — the theory is right and the architecture is sound\n- DO make clear that the tokenization layer hasn't been built yet — it's TBD\n- Frame it as the intended/planned design, not as something live\n- The freeze-then-fund concept is the key insight to add\n\n### Specific changes:\n\n**Line 182:** Change \"That's where onchain anchoring and tokenization come in.\" → Keep the concept but note it's planned. Something like \"That's where onchain anchoring comes in — and eventually, tokenization.\"\n\n**Lines 184-194 (The Ownership \u0026 Funding Layer: Onchain):** Rewrite this section.\n\nThe key message: ATProto provides the data layer. But funding requires a stronger guarantee — funders need to know that what they're paying for won't change after the fact. The planned approach: before a hypercert can be funded, its ATProto records will be frozen (a cryptographic snapshot taken and anchored on-chain). A hypercert cannot be funded if its contents are still changing, because the funder might end up paying for a different cert than what they committed to.\n\nThe tokenization layer — including token standards, smart contract design, and chain selection — is not yet implemented. The theory and architecture are sound: once frozen and anchored, hypercerts can be funded through various mechanisms (milestone-based payouts, retroactive rewards, collective funding pools). The protocol intends to be chain-agnostic. But the specific on-chain implementation is being designed.\n\nKeep the spirit of the original text (ATProto for data, on-chain for guarantees) but make clear what exists vs what's planned.\n\n## Test\ngrep -q 'not yet\\|TBD\\|being designed\\|planned\\|will be' documentation/pages/getting-started/why-were-building-hypercerts.md \u0026\u0026 \\\ngrep -q 'freez\\|frozen' documentation/pages/getting-started/why-were-building-hypercerts.md \u0026\u0026 \\\ngrep -q 'cannot.*funded.*chang\\|can.t.*funded.*chang\\|won.t change\\|exactly what\\|know.*what.*pay' documentation/pages/getting-started/why-were-building-hypercerts.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Change anything before line 172 (The Problem, The Shift, Hypercerts sections) — all accurate\n- Change the \"Where We're Headed\" or \"Start Building\" sections (lines 196+) — accurate\n- Remove blockchain/on-chain entirely — it's the plan, the theory is right\n- Remove the concept of tokenization — just mark it as not yet implemented\n- Invent specific token or contract details","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:22:39.256967+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T12:30:45.642363+13:00","closed_at":"2026-02-16T12:30:45.642363+13:00","close_reason":"79a0ea0 Updated onchain anchoring section with freeze-then-fund model, clarified tokenization is planned but not yet implemented","labels":["scope:small"],"dependencies":[{"issue_id":"docs-390.5","depends_on_id":"docs-390","type":"parent-child","created_at":"2026-02-16T12:22:39.258763+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-390.6","title":"Update reference/faq.md: rewrite blockchain/wallet/tokenization Q\u0026As for freeze-then-fund","description":"## Files\n- documentation/pages/reference/faq.md (modify)\n\n## What to do\n\nUpdate FAQ entries that reference blockchain wallets, tokenization, and on-chain funding. The tokenization layer hasn't been developed yet but the theory is right.\n\n### IMPORTANT FRAMING GUIDANCE\n- Do NOT strip out the blockchain/tokenization concept — the theory is right\n- DO make clear that the tokenization layer hasn't been built yet — it's TBD\n- Frame answers as describing the planned design, not something live\n- The freeze-then-fund concept is the key insight to add\n\n### Specific changes:\n\n**Line 18 (How is this different):** Change \"while still using blockchain for ownership and funding\" → \"while planning to use on-chain anchoring to freeze hypercerts before funding. The tokenization layer is not yet implemented, but the architecture is designed for it.\"\n\n**Lines 20-22 (Do I need a blockchain wallet?):** Rewrite. New: \"Not to create or evaluate hypercerts — you only need an ATProto account (DID). A blockchain wallet will eventually be needed to fund hypercerts on-chain, but the tokenization layer is not yet implemented. The on-chain mechanisms are being designed.\"\n\n**Lines 37-38 (How do I fund a hypercert?):** Rewrite. New: \"The planned approach: before a hypercert can be funded, its ATProto records must be frozen — a cryptographic snapshot is taken and anchored on-chain. This ensures funders know exactly what they are paying for, since the cert's contents cannot change after freezing. The specific on-chain funding mechanisms are being designed. The hypercert data itself lives on ATProto, while the frozen snapshot and funding state will live on-chain. The tokenization layer is not yet implemented.\"\n\n**Lines 44-46 (What chains are supported?):** Rewrite. New: \"The protocol intends to be chain-agnostic for on-chain anchoring and funding. The tokenization layer is not yet implemented, so specific chain support has not been determined. The data layer (ATProto) is independent of any particular blockchain.\"\n\n## Test\ngrep -q 'not yet\\|TBD\\|being designed\\|planned\\|will be' documentation/pages/reference/faq.md \u0026\u0026 \\\ngrep -q 'freez\\|frozen' documentation/pages/reference/faq.md \u0026\u0026 \\\n! grep -q 'tokenized ownership' documentation/pages/reference/faq.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Change Q\u0026As that don't mention blockchain/tokens (What is a hypercert, Is my data public, Can I delete, Who can evaluate, Can I use Bluesky, How do I get help)\n- Remove the blockchain wallet question entirely — it's still relevant\n- Remove the concept of tokenization — just mark it as not yet implemented\n- Invent specific chain or contract details","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:22:59.153902+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T12:30:49.249953+13:00","closed_at":"2026-02-16T12:30:49.249953+13:00","close_reason":"ff1a74e Updated FAQ blockchain/wallet/tokenization Q\u0026As to reflect freeze-then-fund model and clarify that tokenization layer is not yet implemented","labels":["scope:small"],"dependencies":[{"issue_id":"docs-390.6","depends_on_id":"docs-390","type":"parent-child","created_at":"2026-02-16T12:22:59.155466+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-390.7","title":"Update 3 small files: introduction-to-impact-claims.md, building-on-hypercerts.md, working-with-evaluations.md","description":"## Files\n- documentation/pages/getting-started/introduction-to-impact-claims.md (modify)\n- documentation/pages/reference/building-on-hypercerts.md (modify)\n- documentation/pages/tutorials/working-with-evaluations.md (modify)\n\n## What to do\n\nThree small files each have 1 line that needs updating. The tokenization layer hasn't been developed yet but the theory is right.\n\n### IMPORTANT FRAMING GUIDANCE\n- Do NOT strip out the blockchain/tokenization concept — the theory is right\n- DO make clear that the tokenization layer hasn't been built yet where relevant\n- These are small, surgical changes — don't over-explain\n\n### introduction-to-impact-claims.md\n\n**Line 21:** Change \"Hypercerts can exist as a standalone activity claim, or it can be tokenized to then be listed up for sale in a marketplace.\" → \"Hypercerts can exist as a standalone activity claim. In the planned design, a hypercert's records will be frozen and anchored on-chain before it can be funded — this ensures funders know exactly what they are paying for. The tokenization layer is not yet implemented.\"\n\n### building-on-hypercerts.md\n\n**Line 24:** Change \"Create platforms that use hypercerts to tokenize contributions and distribute funding.\" → \"Create platforms that use hypercerts to structure contributions and distribute funding.\" (Simply remove \"tokenize\" — the sentence works without it and avoids implying the feature exists.)\n\n### working-with-evaluations.md\n\n**Line 70:** Change \"Scripts and bots can publish evaluations based on on-chain data, API metrics, or other programmatic checks.\" → \"Scripts and bots can publish evaluations based on API metrics, external data sources, or other programmatic checks.\" (Remove \"on-chain data\" since the on-chain layer is not yet implemented. Replace with something generic.)\n\n## Test\n! grep -q 'tokenize' documentation/pages/getting-started/introduction-to-impact-claims.md \u0026\u0026 \\\n! grep -q 'tokenize contributions' documentation/pages/reference/building-on-hypercerts.md \u0026\u0026 \\\n! grep -q 'on-chain data' documentation/pages/tutorials/working-with-evaluations.md \u0026\u0026 \\\ngrep -q 'not yet\\|planned' documentation/pages/getting-started/introduction-to-impact-claims.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Change anything else in these files\n- Add lengthy explanations — these are 1-line fixes\n- Remove the concept of funding from any of these files\n- Remove blockchain/tokenization as a concept — just mark as not yet implemented where relevant","status":"closed","priority":3,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T12:23:12.609071+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T12:30:41.07996+13:00","closed_at":"2026-02-16T12:30:41.07996+13:00","close_reason":"20fa098 Updated tokenization language across 3 docs files to reflect planned design and current implementation status","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-390.7","depends_on_id":"docs-390","type":"parent-child","created_at":"2026-02-16T12:23:12.610177+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-7b4","title":"Epic 7: Fix Broken Links and Content Issues","description":"## Summary\nResolve all broken links and content issues identified during the documentation audit. This includes GitBook orphan page references, absolute GitBook editor URLs, and anchor ID mismatches.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory. The content was migrated from GitBook. By the time this epic runs, Epic 3 (Content Migration) has moved all files into `documentation/pages/` and done initial cleanup. However, some broken links may remain that require human judgment to resolve.\n\nAll content files are now in `documentation/pages/` (not the old root-level locations). Internal links should use paths without `.md` extensions.\n\n## Broken Links Inventory\n\n### Category 1: GitBook Orphan Page References (3 instances)\nWhen GitBook exports to Git, internal cross-references that fail to resolve become `/broken/pages/XXXXX` links. These need to be resolved or removed.\n\n| File (post-migration path) | Broken Link | Context |\n|---|---|---|\n| `pages/index.md` | `/broken/pages/gNQzZ9R1b3NKrJoQ0Iqa` | Read the surrounding text to determine what this was supposed to link to. It's on the landing/welcome page. |\n| `pages/lexicons/introduction-to-lexicons.md` | `/broken/pages/3UwgsMpD20ErXurhzmTh` | Read the surrounding text -- likely links to one of the lexicon pages |\n| `pages/lexicons/introduction-to-lexicons.md` | `/broken/pages/FdVczwFhTBj7n5nszh0P` | Read the surrounding text -- likely links to one of the lexicon pages |\n\n**Resolution approach:**\n1. Open each file and read the surrounding text around the broken link\n2. Infer what page the link was supposed to point to based on the link text and context\n3. If the target can be determined (e.g., \"General Lexicons\" probably links to `/lexicons/general-lexicons`), replace with the correct path\n4. If the target cannot be determined, remove the `[text](/broken/pages/...)` link syntax but keep the text as plain text. Add a `\u003c!-- TODO: broken link from GitBook migration --\u003e` HTML comment\n\n### Category 2: Absolute GitBook Editor URL (1 instance)\n| File | Broken Link |\n|---|---|\n| `pages/lexicons/hypercerts-lexicons/activity-claim.md` | `https://app.gitbook.com/o/uxU8o4s6mrp88yBisLn5/s/8dkOAon1uQbyqL8RomaJ/hypercert-claim-lexicons/the-impact-and-work-space` |\n\n**Resolution:** The URL path ends with `the-impact-and-work-space`. Replace with the relative internal link `/getting-started/the-impact-and-work-space`.\n\n### Category 3: Anchor ID Mismatches (2 instances)\n| File | Link | Problem | Fix |\n|---|---|---|---|\n| `pages/getting-started/the-impact-and-work-space.md` | Self-referencing `#the-impact-space` | No heading with this anchor exists in the file | Read the file to find the closest matching heading and fix the anchor. If no heading matches, remove the link. |\n| `pages/lexicons/general-lexicons/location.md` | Self-referencing `#locationtype` | The heading is \"### Location Type\" which generates anchor `#location-type` (hyphenated) | Change `#locationtype` to `#location-type` |\n\n## Verification Steps\nAfter fixing all links, run these checks:\n```bash\n# Check for any remaining GitBook artifacts\ngrep -r '/broken/pages/' documentation/pages/\ngrep -r 'app.gitbook.com' documentation/pages/\n\n# Should all return zero results\n```\n\nAlso manually verify:\n- Open each fixed link in the dev server to confirm it navigates correctly\n- For anchor links, click them and verify they scroll to the correct heading\n\n## Acceptance Criteria\n- All 3 `/broken/pages/...` links are either resolved to correct internal paths or removed (text preserved)\n- The absolute GitBook editor URL in activity-claim.md is replaced with `/getting-started/the-impact-and-work-space`\n- Both anchor mismatches are fixed to point to valid heading IDs\n- `grep -r '/broken/' documentation/pages/` returns zero results\n- `grep -r 'app.gitbook.com' documentation/pages/` returns zero results\n- All fixed links work when tested in the browser via `npm run dev`\n","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:45:06.195729+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:18:28.396574+13:00","closed_at":"2026-02-14T13:18:28.396576+13:00","dependencies":[{"issue_id":"docs-7b4","depends_on_id":"docs-qsc","type":"blocks","created_at":"2026-02-13T13:45:06.198001+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-7dv","title":"Epic 4: Navigation and Layout System","description":"## Summary\nBuild a three-column page layout and programmatic navigation system to replace GitBook's `SUMMARY.md`-based navigation. The layout should follow the design language used by Stripe's documentation (docs.stripe.com): a left sidebar for navigation, a centered content column, and a right-side table of contents with scroll spy.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory. GitBook uses a file called `SUMMARY.md` with nested Markdown lists to define navigation. We need to replace this with React components.\n\nBy the time this epic runs:\n- Epic 1 has scaffolded the Next.js project\n- `pages/_app.js` exists and wraps all pages\n- Content `.md` files are in `pages/` (from Epic 3)\n\n### Current SUMMARY.md navigation structure:\n```markdown\n# Table of contents\n* [Welcome to the Hypercerts Protocol](README.md)\n## Getting Started\n* [Why we're building Hypercerts](getting-started/why-were-building-hypercerts.md)\n* [Introduction to Impact Claims](getting-started/introduction-to-impact-claims.md)\n* [The Impact and Work Space](getting-started/the-impact-and-work-space.md)\n* [The Hypercerts Infrastructure](getting-started/the-hypercerts-infrastructure.md)\n* [Installing the SDK](getting-started/installing-the-sdk.md)\n## Lexicons\n* [Introduction to Lexicons](lexicons/introduction-to-lexicons.md)\n* [General Lexicons](lexicons/general-lexicons/README.md)\n * [Shared Defs](lexicons/general-lexicons/shared-defs.md)\n * [Location](lexicons/general-lexicons/location.md)\n* [Hypercerts Lexicons](lexicons/hypercerts-lexicons/README.md)\n * [Activity Claim](lexicons/hypercerts-lexicons/activity-claim.md)\n * [Contribution](lexicons/hypercerts-lexicons/contribution.md)\n * [Evaluation](lexicons/hypercerts-lexicons/evaluation.md)\n * [Measurement](lexicons/hypercerts-lexicons/measurement.md)\n * [Attachment](lexicons/hypercerts-lexicons/attachment.md)\n * [Rights](lexicons/hypercerts-lexicons/rights.md)\n * [Collection](lexicons/hypercerts-lexicons/collection.md)\n---\n* [Deep Dive: The Work Scope](deep-dive-the-work-scope.md)\n```\n\n## Design Language (Stripe-Inspired)\n\nThe layout should follow these Stripe documentation patterns:\n\n### Three-column layout:\n```\n┌──────────────────────────────────────────────────────────┐\n│ Header: Site title + optional search │\n├────────────┬──────────────────────────────┬───────────────┤\n│ │ │ │\n│ Left │ Main Content │ Right TOC │\n│ Sidebar │ (max-width ~720px) │ (~200px) │\n│ (~240px) │ │ │\n│ │ │ - Section 1 │\n│ Section │ # Page Title │ - Section 2 │\n│ Header │ │ - Section 3 │\n│ ├─ Link │ Body text... │ │\n│ ├─ Link │ │ │\n│ └─ Link │ ## Section 1 │ │\n│ │ More text... │ │\n│ Section │ │ │\n│ Header │ ## Section 2 │ │\n│ ├─ Link │ Even more text... │ │\n│ └─ Link │ │ │\n│ │ ┌─────────┬──────────┐ │ │\n│ │ │ ← Prev │ Next → │ │ │\n│ │ └─────────┴──────────┘ │ │\n├────────────┴──────────────────────────────┴───────────────┤\n```\n\n### Visual characteristics:\n- **Background**: Pure white (#ffffff) everywhere\n- **Sidebar**: 240px wide, sticky/fixed position, scrolls independently. Right border: 1px solid #ebeef1 (very subtle gray)\n- **Content area**: Max-width ~720px, centered in the remaining space with padding 32px on each side\n- **Right TOC**: ~200px wide, sticky, shows H2-level headings from current page, highlights current section as user scrolls (scroll spy)\n- **Section headers in sidebar**: Small text, semibold (font-weight 600), uppercase or regular case, color #687385 (secondary gray). NOT clickable -- they are category labels\n- **Nav items in sidebar**: Regular weight (400), color #414552 (dark gray). On hover: slight background tint. Active item: bold text (600) + light blue-gray background tint\n- **Nesting**: One level of indent for child pages (e.g., \"Shared Defs\" indented under \"General Lexicons\")\n- **Pagination**: Previous/Next links at the bottom of content area, styled as a flex row with left/right alignment\n\n## What You Need to Create\n\n### 1. Navigation config -- `lib/navigation.js`\n```js\nexport const navigation = [\n { title: 'Welcome', path: '/' },\n {\n section: 'Getting Started',\n children: [\n { title: \"Why We're Building Hypercerts\", path: '/getting-started/why-were-building-hypercerts' },\n { title: 'Introduction to Impact Claims', path: '/getting-started/introduction-to-impact-claims' },\n { title: 'The Impact and Work Space', path: '/getting-started/the-impact-and-work-space' },\n { title: 'The Hypercerts Infrastructure', path: '/getting-started/the-hypercerts-infrastructure' },\n { title: 'Installing the SDK', path: '/getting-started/installing-the-sdk' },\n ]\n },\n {\n section: 'Lexicons',\n children: [\n { title: 'Introduction to Lexicons', path: '/lexicons/introduction-to-lexicons' },\n {\n title: 'General Lexicons',\n path: '/lexicons/general-lexicons',\n children: [\n { title: 'Shared Defs', path: '/lexicons/general-lexicons/shared-defs' },\n { title: 'Location', path: '/lexicons/general-lexicons/location' },\n ]\n },\n {\n title: 'Hypercerts Lexicons',\n path: '/lexicons/hypercerts-lexicons',\n children: [\n { title: 'Activity Claim', path: '/lexicons/hypercerts-lexicons/activity-claim' },\n { title: 'Contribution', path: '/lexicons/hypercerts-lexicons/contribution' },\n { title: 'Evaluation', path: '/lexicons/hypercerts-lexicons/evaluation' },\n { title: 'Measurement', path: '/lexicons/hypercerts-lexicons/measurement' },\n { title: 'Evidence', path: '/lexicons/hypercerts-lexicons/attachment' },\n { title: 'Rights', path: '/lexicons/hypercerts-lexicons/rights' },\n { title: 'Collection', path: '/lexicons/hypercerts-lexicons/collection' },\n ]\n }\n ]\n },\n { title: 'Deep Dive: The Work Scope', path: '/deep-dive-the-work-scope' },\n];\n```\n\nItems with `section` are non-clickable category headers. Items with `path` are clickable nav links. Items with `children` can be expanded/collapsed.\n\n### 2. Sidebar component -- `components/Sidebar.js`\n- Renders the navigation tree recursively\n- Uses `next/router` to detect the current path and highlight the active item\n- Section headers rendered as non-clickable labels (semibold, secondary gray, small text)\n- Active item gets bold weight + subtle background highlight\n- Child items are indented ~16px from their parent\n- Collapsible groups: items with children auto-expand when any child is active\n- On mobile (\u003c 768px): sidebar is hidden, revealed via a hamburger toggle button\n\n### 3. Table of Contents component -- `components/TableOfContents.js`\n- Extracts H2 (and optionally H3) headings from the page content\n- Renders a list of anchor links on the right side\n- Implements scroll spy: highlights the heading currently in view as the user scrolls\n- Sticky positioning: stays in view as user scrolls the content\n- Heading text in small font (13px), secondary gray color, active heading in dark color\n\nTo extract headings, you can parse `pageProps.markdoc.content` (the Markdoc AST) or use a client-side approach that queries the DOM for `h2` elements after render.\n\n### 4. Layout component -- `components/Layout.js`\n- Wraps all pages with the three-column layout\n- Accepts `frontmatter` (from `pageProps.markdoc.frontmatter`) and `children`\n- Renders: Header, Sidebar, Main Content, TableOfContents\n- Header: simple bar with site title \"Hypercerts Protocol\" on the left, minimal height (~56px), bottom border 1px #ebeef1\n- Main content area: the `children` prop, wrapped in an `\u003carticle\u003e` tag\n- Pagination: prev/next links at the bottom, derived from the flat list of navigation items\n\n### 5. Update `pages/_app.js`\n```jsx\nimport '../styles/globals.css';\nimport Layout from '../components/Layout';\n// ... component imports for Markdoc tags\n\nexport default function App({ Component, pageProps }) {\n return (\n \u003cLayout frontmatter={pageProps.markdoc?.frontmatter}\u003e\n \u003cComponent {...pageProps} /\u003e\n \u003c/Layout\u003e\n );\n}\n```\n\n### 6. Helper: Flatten navigation for prev/next\nCreate a utility function that flattens the navigation tree into an ordered array of `{ title, path }` objects. This is used to compute previous/next page links based on the current path.\n\n## Acceptance Criteria\n- `lib/navigation.js` exports the full navigation tree matching the SUMMARY.md structure\n- `components/Sidebar.js` renders the tree with section headers, active highlighting, and nesting\n- `components/TableOfContents.js` renders right-side H2 anchors with scroll spy\n- `components/Layout.js` implements the three-column layout with header, sidebar, content, and TOC\n- `pages/_app.js` wraps all pages in the Layout component\n- Current page is highlighted in the sidebar\n- Prev/next pagination works at the bottom of each page\n- Layout is responsive: sidebar and TOC collapse/hide on mobile (\u003c 768px), hamburger menu reveals sidebar\n- Page title from frontmatter is displayed in the header or as the page `\u003ctitle\u003e`\n- `SUMMARY.md` is no longer needed for navigation (can be deleted in the cleanup epic)\n","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:43:02.062194+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:16:23.775047+13:00","closed_at":"2026-02-14T13:16:23.775053+13:00","dependencies":[{"issue_id":"docs-7dv","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:43:02.063743+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-brh","title":"Epic: API/SDK Reference \u0026 Code Examples","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:47:50.566515+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:08:19.057366+13:00","closed_at":"2026-02-15T01:08:19.057366+13:00","close_reason":"All children complete: brh.1 (lexicon code examples), brh.2 (error handling)"} -{"id":"docs-brh.1","title":"Add code examples to all 7 existing Hypercerts Lexicon pages","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`.\n\nThe 7 existing Hypercerts Lexicon pages document schemas (field names, types, required/optional) but do NOT include code examples showing how to create records. Stripe's API docs always show a code example alongside every schema. This task adds a \"Code Example\" section to each page.\n\n## Files (all modify)\n- documentation/pages/lexicons/hypercerts-lexicons/activity-claim.md\n- documentation/pages/lexicons/hypercerts-lexicons/contribution.md\n- documentation/pages/lexicons/hypercerts-lexicons/evaluation.md\n- documentation/pages/lexicons/hypercerts-lexicons/measurement.md\n- documentation/pages/lexicons/hypercerts-lexicons/attachment.md\n- documentation/pages/lexicons/hypercerts-lexicons/rights.md\n- documentation/pages/lexicons/hypercerts-lexicons/collection.md\n\n## What to do\n\nFor EACH of the 7 files listed above, append a new section at the end of the file (before any existing closing content, but after the existing \"Examples\" or \"Notes\" section). The new section should be:\n\n```markdown\n## Code Example\n\nCreate a [record type name] record:\n\n\\`\\`\\`typescript\nimport { BskyAgent } from '@atproto/api'\n\nconst agent = new BskyAgent({ service: 'https://pds.example.com' })\nawait agent.login({ identifier: 'your-handle', password: 'your-app-password' })\n\nconst response = await agent.api.com.atproto.repo.createRecord({\n repo: agent.session.did,\n collection: '[lexicon-id]',\n record: {\n // ... fields specific to this lexicon\n },\n})\n\nconsole.log('Created:', response.data.uri)\n\\`\\`\\`\n```\n\nSpecific field values for each lexicon (use ONLY fields documented on that page):\n\n1. **activity-claim.md** (`org.hypercerts.claim.activity`):\n - title: \"Open Source Library Maintenance\"\n - shortDescription: \"Maintained and improved the core library throughout 2025\"\n - workScope: { allOf: [\"library-maintenance\"], anyOf: [], noneOf: [] }\n - startDate: \"2025-01-01T00:00:00Z\"\n - endDate: \"2025-12-31T23:59:59Z\"\n - createdAt: new Date().toISOString()\n\n2. **contribution.md** (`org.hypercerts.claim.contribution`):\n - contributors: [\"did:plc:abc123\"]\n - role: \"lead-developer\"\n - description: \"Led the development of core features\"\n - createdAt: new Date().toISOString()\n\n3. **evaluation.md** (`org.hypercerts.claim.evaluation`):\n - subject: { uri: \"at://did:plc:abc123/org.hypercerts.claim.activity/tid123\", cid: \"bafyrei...\" }\n - evaluators: [\"did:plc:evaluator1\"]\n - summary: \"High-quality maintenance work with consistent release cadence\"\n - createdAt: new Date().toISOString()\n\n4. **measurement.md** (`org.hypercerts.claim.measurement`):\n - measurers: [\"did:plc:measurer1\"]\n - metric: \"issues-resolved\"\n - value: \"142\"\n - measurementMethodType: \"automated-count\"\n - createdAt: new Date().toISOString()\n\n5. **attachment.md** (`org.hypercerts.claim.evidence`):\n - title: \"GitHub Repository\"\n - shortDescription: \"Source code repository for the maintained library\"\n - content: { $type: \"org.hypercerts.defs#uri\", uri: \"https://github.com/example/library\" }\n - createdAt: new Date().toISOString()\n\n6. **rights.md** (`org.hypercerts.claim.rights`):\n - rightsName: \"Public Display\"\n - rightsType: \"display\"\n - rightsDescription: \"Right to publicly display this hypercert as proof of contribution\"\n - createdAt: new Date().toISOString()\n\n7. **collection.md** (`org.hypercerts.claim.collection`):\n - title: \"Q1 2025 Open Source Contributions\"\n - shortDescription: \"All open source maintenance work in Q1 2025\"\n - claims: [{ claim: { uri: \"at://did:plc:abc123/org.hypercerts.claim.activity/tid1\", cid: \"bafyrei...\" }, weight: \"50\" }, { claim: { uri: \"at://did:plc:abc123/org.hypercerts.claim.activity/tid2\", cid: \"bafyrei...\" }, weight: \"50\" }]\n - createdAt: new Date().toISOString()\n\n## Writing style\n- Keep the code examples clean and minimal\n- Add a brief comment above each field explaining what it is\n- Use placeholder values that are realistic but clearly fake (did:plc:abc123, etc.)\n- Add a `{% callout %}` note above the code example: \"The SDK is in active development. Package names and API methods may change.\"\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT modify any existing content on these pages — only APPEND the new section.\n- Do NOT change the page titles, frontmatter, or existing sections.\n- Do NOT modify navigation.js.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT invent fields not documented on the page.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:51:49.509773+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:01:37.159464+13:00","closed_at":"2026-02-15T01:01:37.159464+13:00","close_reason":"Added code examples to all 7 Hypercerts Lexicon pages following Stripe docs pattern","dependencies":[{"issue_id":"docs-brh.1","depends_on_id":"docs-brh","type":"parent-child","created_at":"2026-02-14T20:51:49.51116+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":1,"issue_id":"docs-brh.1","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — code examples should be clean, minimal, with brief comments. No preamble. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag). IMPORTANT: The file for evidence/attachment is called 'evidence.md' not 'attachment.md'. The lexicon page title is 'Evidence'. Check the actual file before modifying.","created_at":"2026-02-14T11:58:13Z"}]} -{"id":"docs-brh.2","title":"Write 'Error Handling \u0026 Constraints' reference page","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe's equivalent: \"Error codes\" and \"Error handling\" pages — comprehensive reference for what can go wrong and how to handle it. The hypercerts docs currently have no error handling documentation.\n\n## Files\n- documentation/pages/reference/error-handling.md (create)\n- documentation/lib/navigation.js (modify — add new \"Reference\" section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/reference/error-handling.md` (100–150 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Error Handling \u0026 Constraints\ndescription: Common errors, validation rules, and how to handle them.\n---\n```\n\n**Sections:**\n\n1. **Overview** — When creating or updating records on a PDS, various errors can occur. This page documents common error scenarios and how to handle them.\n\n2. **Record Validation Errors** — Errors that occur when a record does not match its lexicon schema:\n - Missing required fields (e.g., creating an activity claim without `title` or `startDate`)\n - Invalid field types (e.g., passing a number where a string is expected)\n - String length violations (`maxLength` in bytes, `maxGraphemes` in Unicode grapheme clusters)\n - Array length violations (`maxLength` on array fields)\n - Invalid datetime format (must be ISO 8601)\n - Invalid strong reference format (must include both `uri` and `cid`)\n - For each, show the error scenario and the correct fix\n\n3. **Blob Constraints** — Size limits for uploaded blobs:\n - `smallBlob`: up to 10MB (from `org.hypercerts.defs`)\n - `largeBlob`: up to 100MB\n - `smallImage`: up to 5MB\n - `largeImage`: up to 10MB\n - What happens when you exceed these limits\n\n4. **Authentication Errors** — Common auth issues:\n - Invalid credentials\n - Expired session\n - Insufficient permissions\n - How to refresh sessions\n\n5. **Reference Errors** — Issues with strong references:\n - Referenced record does not exist\n - CID mismatch (record was updated since reference was created)\n - Cross-PDS reference resolution failures\n\n6. **Rate Limits \u0026 PDS Constraints** — Operational limits:\n - PDS may impose rate limits on record creation\n - Repository size limits\n - How to handle rate limit responses (retry with backoff)\n - Add a `{% callout %}` noting that specific limits depend on the PDS implementation\n\n7. **Best Practices** — General error handling advice:\n - Always validate records client-side before sending to PDS\n - Use try/catch around all API calls\n - Log errors with context (which record, which field)\n - Show a TypeScript error handling pattern\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add a new top-level section called \"Reference\" AFTER the \"Architecture\" section:\n\n```javascript\n{\n section: 'Reference',\n children: [\n { title: 'Error Handling \u0026 Constraints', path: '/reference/error-handling' },\n ],\n},\n```\n\n## Writing style\n- Reference style: concise, scannable, table-heavy\n- Use tables for error codes/scenarios where appropriate\n- Use fenced code blocks for error examples and fixes\n- Use `{% callout %}` for important warnings\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT add images or new components.\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT invent specific HTTP status codes for ATProto errors — describe error scenarios generically.","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:52:12.431581+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:04:00.181048+13:00","closed_at":"2026-02-15T01:04:00.181048+13:00","close_reason":"Created Error Handling \u0026 Constraints reference page with validation errors, blob constraints, auth errors, reference errors, rate limits, and best practices. Added Reference section to navigation after Tutorials.","dependencies":[{"issue_id":"docs-brh.2","depends_on_id":"docs-brh","type":"parent-child","created_at":"2026-02-14T20:52:12.433007+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":2,"issue_id":"docs-brh.2","author":"Sharfy Adamantine","text":"NAV UPDATE: Navigation reorganized. Add the 'Reference' section AFTER 'Tutorials'. The full nav order is: Get Started → Core Concepts → Architecture → Lexicons → Tutorials → Reference.","created_at":"2026-02-14T11:24:57Z"},{"id":3,"issue_id":"docs-brh.2","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — reference style, concise, scannable, table-heavy. No preamble, no link dumps. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag). NAV PLACEMENT: Add Reference section AFTER 'Tutorials'. Current nav order: Get Started → Core Concepts → Lexicons → Tools → Tutorials.","created_at":"2026-02-14T11:58:19Z"}]} -{"id":"docs-c34","title":"Epic 1: Scaffold Next.js + Markdoc Project","description":"## Summary\nSet up a Next.js project with Markdoc integration inside the existing `documentation/` directory. This is the foundation that all other migration work depends on. The final documentation site should follow a Stripe-docs-inspired design language (clean, white, professional, content-focused).\n\n## Current State of the Repository\nThe repository root is at `/Users/sharfy/Code/hypercerts-atproto-documentation`. Inside it:\n- `documentation/` -- contains the current documentation (a GitBook-managed site with NO build toolchain, NO package.json)\n- `.beads/` -- beads issue tracker database (do not modify)\n- `AGENTS.md` -- agent instructions (do not modify)\n\nThe `documentation/` directory currently contains:\n```\ndocumentation/\n├── .gitbook/\n│ └── assets/\n│ ├── hypercert erd.png\n│ └── hypercerts_for_projects.png\n├── README.md (landing page)\n├── SUMMARY.md (GitBook navigation config)\n├── deep-dive-the-work-scope.md\n├── getting-started/\n│ ├── why-were-building-hypercerts.md\n│ ├── introduction-to-impact-claims.md\n│ ├── the-impact-and-work-space.md\n│ ├── the-hypercerts-infrastructure.md\n│ └── installing-the-sdk.md\n└── lexicons/\n ├── introduction-to-lexicons.md\n ├── general-lexicons/\n │ ├── README.md\n │ ├── shared-defs.md\n │ └── location.md\n └── hypercerts-lexicons/\n ├── README.md\n ├── activity-claim.md\n ├── contribution.md\n ├── evaluation.md\n ├── measurement.md\n ├── attachment.md\n ├── rights.md\n └── collection.md\n```\n\nThat's 17 Markdown content files, 1 navigation file (SUMMARY.md), and 2 PNG images. There is no package.json, no JavaScript, no build toolchain.\n\n## What You Need to Do\n\n### 1. Initialize the Next.js project inside `documentation/`\nRun these commands from inside the `documentation/` directory:\n```bash\nnpm init -y\nnpm install next@latest react@latest react-dom@latest @markdoc/markdoc @markdoc/next.js\n```\n\n### 2. Create `next.config.js`\n```js\nconst withMarkdoc = require('@markdoc/next.js');\n\nmodule.exports = withMarkdoc({ mode: 'static' })({\n pageExtensions: ['md', 'mdoc', 'js', 'jsx', 'ts', 'tsx']\n});\n```\n\n### 3. Add scripts to `package.json`\n```json\n{\n \"scripts\": {\n \"dev\": \"next dev\",\n \"build\": \"next build\",\n \"start\": \"next start\"\n }\n}\n```\n\n### 4. Create the directory structure\nCreate these empty directories (content will be added in later epics):\n```\ndocumentation/\n├── components/ # React components (Layout, Sidebar, etc.)\n├── markdoc/\n│ ├── tags/ # Custom Markdoc tag definitions\n│ └── nodes/ # Custom Markdoc node overrides\n├── pages/ # Next.js pages (content .md files go here)\n│ └── _app.js # App wrapper\n├── public/\n│ └── images/ # Static assets (images)\n├── styles/\n│ └── globals.css # Global stylesheet\n├── next.config.js\n├── package.json\n└── .gitignore\n```\n\n### 5. Create `pages/_app.js`\nA minimal app wrapper that imports global styles:\n```jsx\nimport '../styles/globals.css';\n\nexport default function App({ Component, pageProps }) {\n return \u003cComponent {...pageProps} /\u003e;\n}\n```\nNote: The Layout component wrapping will be added in Epic 4 (Navigation \u0026 Layout). For now, just the bare wrapper is needed.\n\n### 6. Create a minimal `styles/globals.css`\nJust enough to verify the site loads:\n```css\n* { box-sizing: border-box; margin: 0; padding: 0; }\nbody { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; }\n```\n\n### 7. Create a test page at `pages/index.md`\n```markdown\n---\ntitle: Test Page\n---\n\n# Hello Markdoc\n\nThis is a test page to verify the setup works.\n```\n\n### 8. Create `.gitignore`\n```\nnode_modules/\n.next/\nout/\n```\n\n### 9. Verify\n```bash\nnpm run dev # Should start at http://localhost:3000\nnpm run build # Should complete without errors\n```\n\nAfter verifying, you can delete the test `pages/index.md` -- it will be replaced by actual content in Epic 3.\n\n## Acceptance Criteria\n- `documentation/package.json` exists with next, react, react-dom, @markdoc/markdoc, @markdoc/next.js as dependencies\n- `documentation/next.config.js` exists with `withMarkdoc({ mode: 'static' })` and `pageExtensions: ['md', 'mdoc', 'js', 'jsx', 'ts', 'tsx']`\n- Directory structure has: `pages/`, `components/`, `markdoc/`, `markdoc/tags/`, `markdoc/nodes/`, `public/`, `public/images/`, `styles/` directories\n- `pages/_app.js` exists with a basic wrapper that imports globals.css\n- `styles/globals.css` exists with minimal reset\n- `npm run dev` starts the dev server successfully and shows the test page\n- `npm run build` completes without errors\n- `.gitignore` includes node_modules/, .next/, out/\n- The existing GitBook files (README.md, SUMMARY.md, getting-started/, lexicons/, .gitbook/, deep-dive-the-work-scope.md) are NOT deleted or moved yet -- that happens in later epics\n","status":"closed","priority":0,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:41:49.036228+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:03:51.028646+13:00","closed_at":"2026-02-14T13:03:51.028656+13:00"} -{"id":"docs-cfx","title":"Epic: Getting Started \u0026 Onboarding — Fill stubs and add missing onboarding pages","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:47:41.382394+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:08:16.301556+13:00","closed_at":"2026-02-15T01:08:16.301556+13:00","close_reason":"All children complete: cfx.1 (infrastructure), cfx.2 (SDK setup), cfx.3 (quickstart), cfx.4 (identity), cfx.5 (why atproto), cfx.6 (trim), cfx.7 (certified)"} -{"id":"docs-cfx.1","title":"Write 'The Hypercerts Infrastructure' page (fill stub)","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nThe existing page at `documentation/pages/getting-started/the-hypercerts-infrastructure.md` is a stub — it contains only the heading \"Why ATProto?\" with body \"_TBD_\". It is already wired into navigation (position 4 in Getting Started section).\n\n## Files\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify)\n\n## What to do\n\nReplace the stub content with a full page (150–250 lines of Markdoc) covering:\n\n1. **Why ATProto?** — Explain why the Hypercerts Protocol chose AT Protocol as its data layer. Cover these three properties (already described in `documentation/pages/getting-started/why-were-building-hypercerts.md` under \"The Data Layer: AT Protocol\" section starting at line 177):\n - Portable, user-controlled data (PDS/SDS, self-hosting, no lock-in)\n - Shared schemas across applications (lexicons, interoperability)\n - Decentralized trust-graph for impact (DIDs, reputation, endorsements)\n Expand on each with 2-3 paragraphs. Do NOT just copy from the \"Why\" page — this page should go deeper into the technical details.\n\n2. **The Two-Layer Architecture** — Explain how ATProto (data layer) and blockchain (ownership/funding layer) complement each other. Cover:\n - What lives on ATProto: claims, evidence, evaluations, measurements, trust signals\n - What lives on-chain: ownership, tokenization, funding flows, immutability guarantees\n - How they connect: anchoring, references between layers\n\n3. **Key Concepts for Developers** — Brief explanations of:\n - DID (Decentralized Identifier) — what it is, how users get one\n - PDS (Personal Data Server) — where data lives\n - Lexicon — link to the lexicons section for details\n - XRPC — the API protocol ATProto uses\n - Repository — a user's collection of records\n\n4. **How Hypercerts Data Flows** — A narrative walkthrough: a contributor creates a hypercert → it's stored on their PDS → an evaluator on a different PDS references it → a funder sees both via an indexer → ownership is anchored on-chain.\n\n## Writing style\n- Match the tone of `documentation/pages/getting-started/why-were-building-hypercerts.md`: clear, accessible, avoids jargon where possible, explains technical terms when first used.\n- Use `##` for main sections, `####` for subsections (this is the pattern used in existing pages).\n- Keep the existing frontmatter format: `---\\ntitle: The Hypercerts Infrastructure\\n---`\n- Use `{% callout %}` tags for important notes or warnings.\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds). The page must render without errors.\n\n## Dont\n- Do NOT modify navigation.js — the page is already wired in.\n- Do NOT add images or new components.\n- Do NOT copy text verbatim from why-were-building-hypercerts.md — rephrase and expand.\n- Do NOT invent specific technical details about the Hypercerts SDK that do not exist yet.\n- Do NOT use HTML tags — use Markdoc syntax only.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:48:23.218964+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:00:51.05707+13:00","closed_at":"2026-02-15T01:00:51.05707+13:00","close_reason":"Replaced stub with full infrastructure page covering two-layer architecture, key concepts, data flows, and integration patterns","dependencies":[{"issue_id":"docs-cfx.1","depends_on_id":"docs-cfx","type":"parent-child","created_at":"2026-02-14T20:48:23.220708+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-cfx.1","depends_on_id":"docs-cfx.5","type":"blocks","created_at":"2026-02-14T23:35:05.578281+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":4,"issue_id":"docs-cfx.1","author":"Sharfy Adamantine","text":"SCOPE UPDATE: The 'Why ATProto?' section has been moved to a dedicated page (docs-cfx.5). This task should NO LONGER include section 1 ('Why ATProto?'). Instead, the infrastructure page should: (1) Start with 'The Two-Layer Architecture' section, (2) Link to '/getting-started/why-atproto' for the rationale, (3) Keep sections 2-4 as originally specified (Two-Layer Architecture, Key Concepts for Developers, How Hypercerts Data Flows).","created_at":"2026-02-14T10:35:00Z"},{"id":5,"issue_id":"docs-cfx.1","author":"Sharfy Adamantine","text":"NAV UPDATE: The navigation has been reorganized. 'The Hypercerts Infrastructure' is now under the 'Core Concepts' section (not 'Getting Started'). The nav entry already exists — do NOT modify navigation.js. Just fill the stub content.","created_at":"2026-02-14T11:24:28Z"},{"id":6,"issue_id":"docs-cfx.1","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — one-sentence opener, short paragraphs (1-3 sentences), code-first, no preamble ('In this page you will learn...'), no link dumps ('See also', 'Next steps'). Use numbered steps for sequential processes, tables for reference data. Study existing pages for consistency: documentation/pages/getting-started/quickstart.md and documentation/pages/getting-started/why-atproto.md. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag).","created_at":"2026-02-14T11:58:03Z"}]} -{"id":"docs-cfx.2","title":"Write 'Installing the SDK \u0026 Dev Environment Setup' page (fill stub)","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nThe existing page at `documentation/pages/getting-started/installing-the-sdk.md` is a stub — it contains only the title heading with no body content. It is already wired into navigation (position 5 in Getting Started section).\n\nStripe's equivalent: \"Set up your development environment\" — covers CLI installation, SDK setup, API keys, and first API call.\n\n## Files\n- documentation/pages/getting-started/installing-the-sdk.md (modify)\n\n## What to do\n\nReplace the stub with a full page (100–180 lines of Markdoc) covering:\n\n1. **Prerequisites** — What you need before starting:\n - Node.js (specify v18+ or v20+)\n - An AT Protocol account (DID) — link to atproto.com for account creation\n - A PDS (Personal Data Server) — explain that the Hypercerts Foundation runs one, or you can self-host\n\n2. **Install the SDK** — Show installation commands:\n ```bash\n npm install @hypercerts/sdk\n ```\n (Use this as a placeholder package name. Add a callout noting the SDK is in active development and the package name may change.)\n\n3. **Authentication** — Explain how to authenticate:\n - App passwords for development\n - OAuth for production applications\n - Show a code snippet of creating an authenticated client (use TypeScript)\n\n4. **Your First Hypercert** — A minimal code example that:\n - Creates an authenticated session\n - Creates a simple activity claim record with title, shortDescription, startDate, endDate, createdAt\n - Show the expected response structure\n Use the lexicon schema from `documentation/pages/lexicons/hypercerts-lexicons/activity-claim.md` for field names and types.\n\n5. **Development Tools** — Brief mention of:\n - ATProto tooling (e.g., `@atproto/api` package)\n - How to inspect your PDS data\n - Where to get help (link to GitHub, community)\n\n## Writing style\n- Match the tone of existing pages: clear, accessible, explains terms when first used.\n- Use `##` for main sections, `####` for subsections.\n- Use fenced code blocks with language tags (```bash, ```typescript).\n- Use `{% callout %}` for warnings about SDK being in development.\n- Keep the existing frontmatter format: `---\\ntitle: Installing the SDK\\n---`\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT modify navigation.js — the page is already wired in.\n- Do NOT add images or new components.\n- Do NOT invent real API endpoints or real package names that do not exist — clearly mark placeholders with callouts.\n- Do NOT use HTML tags — use Markdoc syntax only.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:48:40.431426+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T23:55:43.31715+13:00","closed_at":"2026-02-14T23:55:43.317301+13:00","dependencies":[{"issue_id":"docs-cfx.2","depends_on_id":"docs-cfx","type":"parent-child","created_at":"2026-02-14T20:48:40.433594+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-cfx.3","title":"Write 'Quickstart Guide' page — zero to first hypercert in 5 minutes","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nThis is a NEW page. Stripe's equivalent is their \"Checkout quickstart\" — a fast, copy-paste-and-run tutorial that gets developers to a working result in under 5 minutes.\n\nCurrently the docs have no quickstart. Developers land on conceptual pages and have no clear path to \"make something work.\"\n\n## Files\n- documentation/pages/getting-started/quickstart.md (create)\n- documentation/lib/navigation.js (modify — add entry)\n\n## What to do\n\n### 1. Create the quickstart page\n\nCreate `documentation/pages/getting-started/quickstart.md` (80–130 lines of Markdoc) with this structure:\n\n**Frontmatter:**\n```\n---\ntitle: Quickstart\ndescription: Create your first hypercert in under 5 minutes.\n---\n```\n\n**Sections:**\n\n1. **Introduction** (2-3 sentences) — \"This guide walks you through creating your first hypercert. By the end, you'll have a published activity claim on your Personal Data Server.\"\n\n2. **Step 1: Get an AT Protocol Account** — Brief instructions to create a DID. Link to atproto.com. Mention the Hypercerts Foundation PDS as an option.\n\n3. **Step 2: Install the SDK** — `npm install @hypercerts/sdk` (placeholder, with callout). Link to the full \"Installing the SDK\" page for details.\n\n4. **Step 3: Authenticate** — Minimal TypeScript code to create an authenticated session using an app password.\n\n5. **Step 4: Create an Activity Claim** — TypeScript code example creating a minimal hypercert with these fields (from the activity claim lexicon at `documentation/pages/lexicons/hypercerts-lexicons/activity-claim.md`):\n - title: \"My First Hypercert\"\n - shortDescription: \"A test contribution to learn the protocol\"\n - startDate: \"2025-01-01T00:00:00Z\"\n - endDate: \"2025-12-31T23:59:59Z\"\n - createdAt: current timestamp\n\n6. **Step 5: Verify Your Hypercert** — Show how to read back the record you just created. Show expected response shape.\n\n7. **Next Steps** — Card links to:\n - \"Introduction to Impact Claims\" (`/getting-started/introduction-to-impact-claims`)\n - \"The Impact and Work Space\" (`/getting-started/the-impact-and-work-space`)\n - \"Activity Claim Lexicon\" (`/lexicons/hypercerts-lexicons/activity-claim`)\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add the quickstart as the FIRST item in the Getting Started section:\n\n```javascript\n{\n section: 'Getting Started',\n children: [\n { title: 'Quickstart', path: '/getting-started/quickstart' }, // ADD THIS LINE\n { title: \"Why We're Building Hypercerts\", path: ... },\n // ... rest unchanged\n ],\n},\n```\n\n## Writing style\n- Action-oriented, imperative voice: \"Install the SDK\", \"Create an activity claim\"\n- Numbered steps with clear code blocks\n- Use `{% callout %}` for prerequisites and warnings about placeholder package names\n- Use `{% card-link %}` for the Next Steps section\n- Fenced code blocks with ```typescript and ```bash language tags\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT exceed 130 lines — this must feel fast and lightweight.\n- Do NOT explain concepts in depth — link to other pages for that.\n- Do NOT add images or new components.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT modify any other existing pages.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:49:01.554509+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:22:02.597523+13:00","closed_at":"2026-02-15T00:22:02.59753+13:00","dependencies":[{"issue_id":"docs-cfx.3","depends_on_id":"docs-cfx","type":"parent-child","created_at":"2026-02-14T20:49:01.556173+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-cfx.3","depends_on_id":"docs-cfx.2","type":"blocks","created_at":"2026-02-14T20:54:30.488814+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":7,"issue_id":"docs-cfx.3","author":"Sharfy Adamantine","text":"IMPORTANT CONTEXT UPDATE: The Hypercerts ecosystem has its own identity provider called **Certified** (certified.app). Key facts for this task:\n\n1. **Certified** is a PDS and identity provider purpose-built for the Hypercerts ecosystem. Users sign up at certified.app to get an ATProto identity without needing to know about Bluesky or ATProto.\n2. **Bluesky accounts also work** — users with existing Bluesky accounts can use them directly with Hypercerts.\n3. The reason Certified exists: for users who don't know what Bluesky is, it would be confusing to sign up for Bluesky when they want to use Hypercerts. Certified provides a clean on-ramp.\n4. The `app.certified.*` lexicon namespace (e.g., `app.certified.location`) contains schemas shared across the entire ecosystem, not specific to the Certified platform.\n5. The real SDK packages are `@hypercerts-org/sdk-core` (Node.js) and `@hypercerts-org/sdk-react` (React). Auth is OAuth-based via `createATProtoSDK()`. See the updated Installing the SDK page for correct API patterns.\n6. In Step 1 (Get an AT Protocol Account), recommend Certified as the primary option, with Bluesky as an alternative.\n7. In Step 2 (Install the SDK), use `pnpm add @hypercerts-org/sdk-core` (not a placeholder).\n8. In Step 3 (Authenticate), use the OAuth pattern: `createATProtoSDK()` → `sdk.authorize()` → `sdk.callback()`.\n9. In Step 4 (Create an Activity Claim), use `sdk.getRepository(session)` → `repo.hypercerts.create({...})`.","created_at":"2026-02-14T11:01:33Z"}]} -{"id":"docs-cfx.4","title":"Write 'Account \u0026 Identity Setup' page","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nThis is a NEW page. Stripe's equivalent: \"Activate your account\" and \"Create and manage multiple accounts.\" In the ATProto world, the equivalent is setting up your DID, choosing a PDS, and understanding your identity.\n\nCurrently the docs mention DIDs and PDS in passing but never explain how to actually set one up.\n\n## Files\n- documentation/pages/getting-started/account-and-identity.md (create)\n- documentation/lib/navigation.js (modify — add entry)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/getting-started/account-and-identity.md` (100–160 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Account \u0026 Identity Setup\ndescription: Set up your AT Protocol identity for use with Hypercerts.\n---\n```\n\n**Sections:**\n\n1. **What is an AT Protocol Identity?** (3-4 paragraphs)\n - Every participant in the Hypercerts ecosystem has a DID (Decentralized Identifier)\n - Your DID is your permanent identity — it stays the same even if you change servers\n - DIDs can represent individuals OR organizations\n - Explain the difference between `did:plc` and `did:web` briefly\n\n2. **Creating Your Account**\n - Option A: Use the Hypercerts Foundation PDS (recommended for getting started)\n - Option B: Use an existing ATProto account (e.g., Bluesky)\n - Option C: Self-host a PDS\n - For each option, provide 2-3 sentences explaining when to choose it\n\n3. **Your Personal Data Server (PDS)**\n - What a PDS stores (your records, blobs, identity)\n - How data portability works (you can move between PDS providers)\n - The relationship between your DID and your PDS\n\n4. **Handles and Domain Verification**\n - How ATProto handles work (e.g., @username.bsky.social)\n - Custom domain handles for organizations\n - Why this matters for trust (a handle like @numpy.org proves organizational identity)\n\n5. **Organization Accounts**\n - Shared Data Servers (SDS) for teams\n - How multiple contributors can write to the same repository\n - Use case: an open-source project with multiple maintainers\n\n6. **Security Best Practices**\n - App passwords vs. main password\n - Rotation keys and account recovery\n - Use `{% callout %}` for security warnings\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add this entry in the Getting Started section AFTER \"Quickstart\" (if it exists) or as the SECOND item:\n\n```javascript\n{ title: 'Account \u0026 Identity Setup', path: '/getting-started/account-and-identity' },\n```\n\n## Writing style\n- Match existing page tone: clear, accessible, explains terms when first used\n- Use `##` for main sections, `####` for subsections\n- Use `{% callout %}` for security warnings and important notes\n- Keep the existing frontmatter format\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT add images or new components.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT invent specific URLs for the Hypercerts Foundation PDS — use placeholder text with a callout.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:49:19.581296+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:59:59.696184+13:00","closed_at":"2026-02-15T00:59:59.696184+13:00","close_reason":"Created Account \u0026 Identity Setup page with developer-focused content on DIDs, handles, domain verification, org accounts, and app passwords. Build passes.","dependencies":[{"issue_id":"docs-cfx.4","depends_on_id":"docs-cfx","type":"parent-child","created_at":"2026-02-14T20:49:19.582568+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":8,"issue_id":"docs-cfx.4","author":"Sharfy Adamantine","text":"NAV UPDATE: The navigation has been reorganized into 'Get Started' (action), 'Core Concepts' (understanding), and 'Lexicons' (reference). The Account \u0026 Identity Setup page should go under 'Get Started' section, AFTER 'What is Certified?'. However — consider whether this page is still needed given that 'What is Certified?' already covers account creation and identity. If you decide to proceed, keep it very tight and focused on developer-specific identity concerns (DIDs, handles, domain verification, org accounts) rather than basic account creation.","created_at":"2026-02-14T11:24:35Z"},{"id":9,"issue_id":"docs-cfx.4","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — one-sentence opener, capability bullet list, short paragraphs (1-3 sentences), no preamble, no link dumps. Study existing pages: documentation/pages/getting-started/quickstart.md and documentation/pages/getting-started/what-is-certified.md. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag). SCOPE NOTE: Given that 'What is Certified?' already covers basic account creation, focus this page on developer-specific identity concerns: DIDs, handles, domain verification, org accounts, app passwords. Keep it tight — 80-120 lines.","created_at":"2026-02-14T11:58:07Z"}]} -{"id":"docs-cfx.5","title":"Write 'Why ATProto?' dedicated page","description":"## Context\n\nDocumentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. Site in `documentation/`. Pages are `.md` files in `documentation/pages/`. Nav in `documentation/lib/navigation.js`. Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nThis is a NEW page. The canonical source material is the leaflet article at https://hypercerts.leaflet.pub/3m6mnb2riz22o — specifically the section titled \"The Hypercerts Architecture \u003e The Data Layer: AT Protocol.\" That same content also appears in `documentation/pages/getting-started/why-were-building-hypercerts.md` lines 177–208, but it is buried at the end of a 240-line manifesto page. This page gives the ATProto rationale its own home.\n\nThe tone should be BOTH faithful to the leaflet's arguments (portable data, shared schemas, trust-graph) AND developer-oriented — expand each property with concrete technical details a builder would care about.\n\n## Files\n- documentation/pages/getting-started/why-atproto.md (create)\n- documentation/lib/navigation.js (modify — add entry)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/getting-started/why-atproto.md` (130–200 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Why ATProto?\ndescription: Why the Hypercerts Protocol is built on AT Protocol.\n---\n```\n\n**Sections:**\n\n1. **Introduction** (3-4 paragraphs)\n - Hypercerts need a data layer that is open, portable, and not controlled by any single institution.\n - ATProto (the decentralized social data protocol that also powers Bluesky) provides this.\n - Briefly state the three properties ATProto gives Hypercerts, then expand on each below.\n\n2. **Portable, User-Controlled Data** (3-4 paragraphs)\n - Faithful to leaflet: Hypercerts stored on PDS/SDS. Users choose where data lives (Hypercerts Foundation servers, other platforms, self-hosted). Contributors, evaluators, and funders retain full control. Can switch providers without losing records.\n - Developer angle: Explain what this means technically — your app reads from a PDS via XRPC, it does not own the data. Users can migrate their entire repository (all records, blobs) to a new PDS. Applications are interchangeable views over user-owned data.\n - Contrast with alternatives: Unlike storing data in a platform's database, the user's records survive if the platform shuts down. Unlike IPFS, data has a clear owner and mutable state.\n\n3. **Shared Schemas Across Applications** (3-4 paragraphs)\n - Faithful to leaflet: ATProto allows common data schemas (lexicons). Hypercerts use shared schemas for contributions, evidence, evaluations. Any ATProto app can understand hypercert data. A contribution recorded in one app can be evaluated in another, viewed in a third, funded elsewhere — no custom integrations needed.\n - Developer angle: Lexicons are JSON schemas that define record structure. When you create a record with collection `org.hypercerts.claim.activity`, any app that knows that lexicon can parse it. This is like having a shared database schema across the entire network. Link to the [Introduction to Lexicons](/lexicons/introduction-to-lexicons) page for details.\n - Why this matters: Interoperability is built in from day one, not bolted on later.\n\n4. **A Decentralized Trust-Graph for Impact** (3-4 paragraphs)\n - Faithful to leaflet: Persistent identities (DIDs) for individuals and organizations. Identities accumulate contributions, evaluations, endorsements over time. This forms a durable trust-graph — a reputation layer that follows users across platforms. Funders can draw from an ecosystem-wide view of identity, contribution, and credibility.\n - Developer angle: DIDs are cryptographically verifiable. A DID like `did:plc:abc123` is permanent even if the user changes handles or servers. When an evaluator creates an evaluation record, their DID is attached — you can trace their entire evaluation history across the network. This enables programmatic trust scoring.\n - Contrast: Unlike siloed reputation systems (e.g., a single platform's star ratings), this reputation is portable and verifiable.\n\n5. **Why Not Other Technologies?** (1-2 paragraphs)\n - Brief comparison with alternatives builders might consider:\n - **Pure blockchain**: Too expensive for rich data, limited schema flexibility, poor for large blobs\n - **IPFS/Filecoin**: Content-addressed but no identity layer, no mutable state, no schemas\n - **Ceramic**: Similar goals but smaller ecosystem, less mature tooling\n - **Traditional databases**: No portability, no interoperability, platform lock-in\n - ATProto provides the best combination of identity, schemas, federation, and an existing growing ecosystem.\n\n6. **ATProto + Blockchain: Better Together** (2-3 paragraphs)\n - ATProto handles the data (claims, evidence, evaluations, trust signals)\n - Blockchain handles ownership (tokenization, funding flows, immutability guarantees)\n - Link to [The Hypercerts Infrastructure](/getting-started/the-hypercerts-infrastructure) page for the full two-layer architecture details.\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add this entry in the Getting Started section AFTER \"The Impact and Work Space\" and BEFORE \"The Hypercerts Infrastructure\":\n\n```javascript\n{ title: 'Why ATProto?', path: '/getting-started/why-atproto' },\n```\n\nThe Getting Started section should become:\n```\nQuickstart (if added)\nWhy We're Building Hypercerts\nIntroduction to Impact Claims\nThe Impact and Work Space\nWhy ATProto? ← NEW\nThe Hypercerts Infrastructure\nInstalling the SDK\n```\n\n## Writing style\n- Faithful to the leaflet's structure and arguments, but expanded with developer-oriented technical details.\n- Clear, accessible, explains terms when first used.\n- Use `##` for main sections, `####` for subsections.\n- Use `{% callout %}` for key insights and comparisons.\n- Link to other docs pages where relevant.\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT modify any existing pages other than navigation.js (the \"Why\" page deduplication is a separate task).\n- Do NOT add images or new components.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT copy the leaflet text verbatim — paraphrase and expand.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T23:34:19.736355+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T23:38:27.777392+13:00","closed_at":"2026-02-14T23:38:27.777397+13:00","dependencies":[{"issue_id":"docs-cfx.5","depends_on_id":"docs-cfx","type":"parent-child","created_at":"2026-02-14T23:34:19.740869+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-cfx.6","title":"Trim ATProto section from 'Why We're Building Hypercerts' page and cross-link to 'Why ATProto?'","description":"## Context\n\nDocumentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. Site in `documentation/`. Pages are `.md` files in `documentation/pages/`.\n\nThe page `documentation/pages/getting-started/why-were-building-hypercerts.md` contains a full \"The Data Layer: AT Protocol\" section (lines 177–208) that duplicates content now covered by the dedicated \"Why ATProto?\" page at `/getting-started/why-atproto`. This task trims the duplicated section and adds a cross-link.\n\n**Dependency:** This task MUST be done AFTER `docs-cfx.5` (\"Write 'Why ATProto?' dedicated page\") is complete.\n\n## Files\n- documentation/pages/getting-started/why-were-building-hypercerts.md (modify)\n\n## What to do\n\nIn the file, find the section \"#### The Data Layer: AT Protocol\" (around line 177). It currently runs ~30 lines covering portable data, shared schemas, and trust-graph in detail.\n\nReplace that entire subsection (from \"#### The Data Layer: AT Protocol\" through the paragraph ending \"...onchain anchoring and tokenization come in.\" around line 208) with a SHORT version (~5–8 lines total):\n\n```markdown\n#### The Data Layer: AT Protocol\n\nThe Hypercerts Protocol is built on AT Protocol (ATProto), the decentralized social data layer that also powers Bluesky. ATProto gives Hypercerts three essential properties: portable, user-controlled data stored on Personal or Shared Data Servers; shared schemas (lexicons) that make hypercert data interoperable across applications; and a decentralized trust-graph built on persistent identities.\n\nFor a full explanation of why ATProto was chosen and how it compares to alternatives, see [Why ATProto?](/getting-started/why-atproto).\n```\n\nKeep the \"#### The Ownership \u0026 Funding Layer: Onchain\" section (lines ~210–220) completely unchanged.\n\n## Writing style\n- Keep it brief — the goal is to summarize in 2-3 sentences and link out.\n- Match the existing page tone.\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0.\n\n## Dont\n- Do NOT modify any other section of this page.\n- Do NOT modify navigation.js.\n- Do NOT change the frontmatter or title.\n- Do NOT remove the \"Ownership \u0026 Funding Layer: Onchain\" section.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T23:34:46.406677+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:57:55.596463+13:00","closed_at":"2026-02-15T00:57:55.596463+13:00","close_reason":"Already completed — ATProto section was trimmed and cross-linked in a previous session","dependencies":[{"issue_id":"docs-cfx.6","depends_on_id":"docs-cfx","type":"parent-child","created_at":"2026-02-14T23:34:46.423629+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-cfx.6","depends_on_id":"docs-cfx.5","type":"blocks","created_at":"2026-02-14T23:34:52.880221+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-cfx.7","title":"Write 'What is Certified?' page — the Hypercerts identity provider","description":"## Context\n\nDocumentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. Site in `documentation/`. Pages are `.md` files in `documentation/pages/`. Nav in `documentation/lib/navigation.js`. Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\n**Certified** (certified.app) is the identity provider and PDS for the Hypercerts ecosystem. It exists because:\n- The Hypercerts Protocol is built on AT Protocol (ATProto), which also powers Bluesky.\n- For users who do not know what Bluesky is, it would be confusing and jarring to sign up for Bluesky when they want to use a Hypercerts application.\n- Certified provides a clean on-ramp: users sign up at certified.app and get an ATProto identity (DID) without needing to understand Bluesky, ATProto, or decentralized protocols.\n- Users with existing Bluesky accounts can also log in to Hypercerts apps directly — Certified is not required, just recommended for newcomers.\n\nThe `app.certified.*` lexicon namespace (e.g., `app.certified.location`) contains schemas that are shared across the entire Hypercerts ecosystem, not specific to the Certified platform itself.\n\n## Files\n- documentation/pages/getting-started/what-is-certified.md (create)\n- documentation/lib/navigation.js (modify — add entry)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/getting-started/what-is-certified.md` (80–120 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: What is Certified?\ndescription: Certified is the identity provider for the Hypercerts ecosystem.\n---\n```\n\n**Sections:**\n\n1. **Introduction** (2-3 paragraphs)\n - Certified (certified.app) is the identity provider and Personal Data Server (PDS) for the Hypercerts ecosystem.\n - When you create a Certified account, you get an AT Protocol identity (a DID — Decentralized Identifier) that works across all applications in the Hypercerts ecosystem.\n - Think of it as your passport to the world of impact certificates.\n\n2. **Why Certified Exists** (2-3 paragraphs)\n - The Hypercerts Protocol is built on AT Protocol, the same decentralized data layer that powers Bluesky.\n - But most Hypercerts users are not Bluesky users. They are researchers, land stewards, open-source maintainers, funders, and evaluators. Asking them to \"sign up for Bluesky\" to use a funding platform would be confusing.\n - Certified solves this by providing a purpose-built sign-up flow. Users create an account at certified.app and immediately have an identity that works with any Hypercerts application — no knowledge of Bluesky, ATProto, or decentralized protocols required.\n\n3. **What You Get** (bulleted list)\n - A DID (Decentralized Identifier) — your permanent, portable identity\n - A PDS (Personal Data Server) — where your hypercerts, evaluations, and other records are stored\n - Access to the entire Hypercerts ecosystem — any app built on the protocol recognizes your Certified identity\n - Data portability — you own your data and can migrate it to another PDS at any time\n\n4. **Already Have a Bluesky Account?** (1-2 paragraphs)\n - If you already have a Bluesky account, you do NOT need a Certified account. Your Bluesky identity is a valid ATProto identity and works with all Hypercerts applications.\n - You can log in to any Hypercerts app using your Bluesky handle (e.g., `alice.bsky.social`).\n - Use `{% callout %}` to make this clear.\n\n5. **The app.certified Namespace** (1-2 paragraphs)\n - The `app.certified.*` lexicon namespace contains schemas shared across the Hypercerts ecosystem.\n - For example, `app.certified.location` defines how geographic locations are attached to hypercerts.\n - These schemas are ecosystem-wide standards, not specific to the Certified platform.\n - Link to the [Location lexicon](/lexicons/general-lexicons/location) page.\n\n6. **Getting Started** (short, with card links)\n - Sign up at [certified.app](https://certified.app)\n - Then follow the [Installing the SDK](/getting-started/installing-the-sdk) guide to start building\n - Or jump to the [Quickstart](/getting-started/quickstart) for a 5-minute walkthrough\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add this entry in the Getting Started section AFTER \"Why We're Building Hypercerts\" and BEFORE \"Introduction to Impact Claims\":\n\n```javascript\n{ title: 'What is Certified?', path: '/getting-started/what-is-certified' },\n```\n\n## Writing style\n- Friendly, welcoming tone — this page is for non-technical users too\n- Use `##` for main sections, `####` for subsections\n- Use `{% callout %}` for the \"Already have Bluesky?\" note\n- Use `{% card-link %}` for the Getting Started links\n- Keep it concise — this is an orientation page, not a deep dive\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT add images or new components.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT make Certified sound mandatory — Bluesky accounts work too.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-15T00:02:06.264262+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:12:43.897475+13:00","closed_at":"2026-02-15T00:12:43.897477+13:00","dependencies":[{"issue_id":"docs-cfx.7","depends_on_id":"docs-cfx","type":"parent-child","created_at":"2026-02-15T00:02:06.266805+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-cij","title":"Epic 6: Images and Asset Migration","description":"## Summary\nMove all image assets from the GitBook-specific `.gitbook/assets/` directory to the standard Next.js `public/images/` directory, rename files to remove spaces, update all references in content files, and delete the old `.gitbook/` directory.\n\n## Context\nThis project is a Next.js + Markdoc documentation site. The project root is `documentation/` inside the repository at `/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`.\n\nThe current documentation was migrated from GitBook, which stores images in a `.gitbook/assets/` directory. In Next.js, static assets live in the `public/` directory and are served from the root URL path. For example, `public/images/foo.png` is accessible at `/images/foo.png`.\n\nBy the time this epic runs:\n- Epic 1 has created the `public/images/` directory\n- Epic 3 may have already updated image references in content files to use `/images/...` paths and converted `\u003cfigure\u003e` HTML to `{% figure %}` Markdoc tags. Check the current state of the files before making changes.\n\n## Current Image Assets\nThe `documentation/.gitbook/assets/` directory contains 2 PNG files:\n- `hypercerts_for_projects.png` -- referenced in the landing page\n- `hypercert erd.png` -- referenced in the landing page and the impact claims page (note: filename has a SPACE)\n\n## Files That Reference Images\nThese files contain image references (paths may already be updated by Epic 3):\n\n1. **pages/index.md** (was README.md) -- 2 image references:\n - `.gitbook/assets/hypercerts_for_projects.png`\n - `.gitbook/assets/hypercert erd.png`\n\n2. **pages/getting-started/introduction-to-impact-claims.md** -- 1 image reference:\n - `../.gitbook/assets/hypercert erd.png`\n\n## Tasks\n\n### 1. Copy and rename image files\n```bash\ncp 'documentation/.gitbook/assets/hypercerts_for_projects.png' documentation/public/images/hypercerts_for_projects.png\ncp 'documentation/.gitbook/assets/hypercert erd.png' documentation/public/images/hypercert-erd.png\n```\nNote: `hypercert erd.png` is renamed to `hypercert-erd.png` (space replaced with hyphen).\n\n### 2. Update image references in content files\nIf not already done by Epic 3, update all image paths:\n- `.gitbook/assets/hypercerts_for_projects.png` -\u003e `/images/hypercerts_for_projects.png`\n- `.gitbook/assets/hypercert erd.png` -\u003e `/images/hypercert-erd.png`\n- `../.gitbook/assets/hypercert erd.png` -\u003e `/images/hypercert-erd.png`\n\nThese may appear as `{% figure src=\"...\" %}` Markdoc tags or as standard `![](...)` Markdown images or as `\u003cfigure\u003e\u003cimg src=\"...\"\u003e` HTML, depending on what Epic 3 did.\n\n### 3. Delete the .gitbook/ directory\n```bash\nrm -rf documentation/.gitbook\n```\n\n### 4. Verify\n- Confirm `documentation/public/images/` contains both PNGs\n- Run `grep -r '.gitbook/' documentation/pages/` -- should return zero results\n- Run `grep -r '.gitbook/' documentation/components/` -- should return zero results\n- Start dev server (`npm run dev`) and verify images render on the relevant pages\n\n## Acceptance Criteria\n- `documentation/public/images/hypercerts_for_projects.png` exists\n- `documentation/public/images/hypercert-erd.png` exists (no space in filename)\n- All image references in content files point to `/images/...` paths\n- `documentation/.gitbook/` directory is deleted\n- `grep -r '.gitbook/' documentation/pages/` returns zero results\n- Images render correctly when viewing pages in the browser via `npm run dev`\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:44:34.862052+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:05:11.939424+13:00","closed_at":"2026-02-14T13:05:11.939425+13:00","dependencies":[{"issue_id":"docs-cij","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:44:34.864383+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-cur","title":"Epic: Add dark mode and search bar to navbar","description":"Add two navbar features inspired by atproto.com: (1) Dark mode toggle with sun/moon icons, class-based theming via .dark on \u003chtml\u003e, localStorage persistence, and dark CSS custom properties for all color tokens. (2) Search pill in the navbar — rounded pill button with search icon and ⌘K hint, opening a modal that filters pages by title from navigation.js. Success: users can toggle dark/light mode and search docs pages from the navbar.","status":"closed","priority":1,"issue_type":"epic","owner":"einstein.climateai.org","created_at":"2026-02-20T19:11:58.629965+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:35:05.254804+08:00","closed_at":"2026-02-20T19:35:05.254804+08:00","close_reason":"024f0c5 Dark mode + search bar complete (PRs #25-27)","labels":["scope:medium"]} -{"id":"docs-cur.1","title":"Add dark mode CSS variables and component overrides","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\n\nAdd an `html.dark` CSS block after the existing `:root` block (after line 137) that overrides ALL design tokens for dark mode. Use the scaffold's dark palette (indigo hue 260) as the reference.\n\n### Dark token values (inside `html.dark { }`):\n```css\nhtml.dark {\n --color-bg: oklch(0.13 0.005 260);\n --color-bg-subtle: oklch(0.17 0.005 260);\n --color-border: oklch(0.25 0.005 260);\n --color-border-strong: oklch(0.30 0.01 260);\n --color-text-primary: oklch(0.85 0.005 260);\n --color-text-heading: oklch(0.95 0.005 260);\n --color-text-title: oklch(0.95 0.005 260);\n --color-text-secondary: oklch(0.65 0.01 260);\n --color-text-sidebar: oklch(0.85 0.005 260);\n --color-link: oklch(0.65 0.15 260);\n --color-link-hover: oklch(0.72 0.15 260);\n --color-accent: oklch(0.65 0.15 260);\n --color-info: oklch(0.65 0.15 260);\n --color-info-bg: oklch(0.20 0.02 260);\n --color-warning: oklch(0.70 0.15 55);\n --color-warning-bg: oklch(0.20 0.02 55);\n --color-danger: oklch(0.65 0.20 25);\n --color-danger-bg: oklch(0.20 0.02 25);\n --color-success: oklch(0.65 0.15 145);\n --color-success-bg: oklch(0.20 0.02 145);\n --hover-bg: oklch(0.20 0.008 260);\n --active-bg: oklch(0.22 0.01 260);\n --focus-ring: 0 0 0 4px oklch(0.65 0.15 260 / 0.36);\n}\n```\n\n### Component-specific dark overrides (add after the html.dark block):\n\n1. **Header glass panel** — `.layout-header` override:\n ```css\n html.dark .layout-header {\n background: oklch(0.13 0.005 260 / 0.7);\n border-bottom-color: oklch(0.95 0 0 / 0.1);\n }\n ```\n\n2. **Logo inversion** — make the black SVG logo white:\n ```css\n html.dark .layout-logo-img {\n filter: invert(1);\n }\n ```\n\n3. **Logo badge** — border and text adjustments:\n ```css\n html.dark .layout-logo-badge {\n border-color: oklch(0.95 0 0 / 0.15);\n color: var(--color-text-secondary);\n }\n ```\n\n4. **Sidebar** — border and active state overrides:\n ```css\n html.dark .sidebar {\n border-right-color: oklch(0.95 0 0 / 0.1);\n }\n html.dark .sidebar-section {\n border-top-color: oklch(0.95 0 0 / 0.1);\n }\n html.dark .sidebar-collapse-btn {\n background: var(--color-bg);\n }\n ```\n\n5. **Scrollbar dark mode**:\n ```css\n html.dark .sidebar-content:hover::-webkit-scrollbar-thumb,\n html.dark .toc:hover::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.15);\n }\n html.dark .sidebar-content::-webkit-scrollbar-thumb:hover,\n html.dark .toc::-webkit-scrollbar-thumb:hover {\n background: rgba(255, 255, 255, 0.25);\n }\n html.dark .sidebar-content:hover,\n html.dark .toc:hover {\n scrollbar-color: rgba(255, 255, 255, 0.15) transparent;\n }\n ```\n\n6. **Inline code dark mode**:\n ```css\n html.dark .layout-content p code,\n html.dark .layout-content li code,\n html.dark .layout-content td code,\n html.dark .layout-content th code {\n background: oklch(0.20 0.008 260);\n border-color: oklch(0.30 0.005 260);\n }\n ```\n\n7. **Dot pattern and hero banner**:\n ```css\n html.dark .dot-pattern {\n fill: oklch(0.22 0.005 260);\n }\n ```\n\n8. **Card link icon box dark mode**:\n ```css\n html.dark .card-link-icon-box {\n box-shadow: inset 0 0 0 1px oklch(0.95 0 0 / 0.15);\n }\n html.dark .card-link:hover .card-link-icon-box {\n box-shadow: inset 0 0 0 1px oklch(0.95 0 0 / 0.3);\n }\n ```\n\n9. **Tables**:\n ```css\n html.dark .layout-content th {\n border-bottom-color: oklch(0.30 0.01 260);\n }\n html.dark .layout-content td {\n border-bottom-color: oklch(0.25 0.005 260);\n }\n ```\n\n10. **Blockquotes**:\n ```css\n html.dark .layout-content blockquote {\n border-left-color: oklch(0.35 0.01 260);\n }\n ```\n\n11. **Pagination**:\n ```css\n html.dark .pagination {\n border-top-color: oklch(0.25 0.005 260);\n }\n html.dark .pagination-link {\n border-color: oklch(0.25 0.005 260);\n }\n html.dark .pagination-link:hover {\n border-color: var(--color-link);\n box-shadow: 0 2px 8px oklch(0.65 0.15 260 / 0.15);\n }\n ```\n\n12. **Breadcrumbs**:\n ```css\n html.dark .breadcrumbs-separator {\n color: oklch(0.45 0.01 260);\n }\n ```\n\n13. **Content links** (underline decoration color):\n ```css\n html.dark .layout-content a {\n text-decoration-color: oklch(0.65 0.15 260 / 0.4);\n }\n ```\n\n14. **HR**:\n ```css\n html.dark .layout-content hr {\n border-top-color: oklch(0.25 0.005 260);\n }\n ```\n\n15. **Sidebar overlay (mobile)**:\n ```css\n html.dark .sidebar-overlay {\n background: rgba(0, 0, 0, 0.6);\n }\n ```\n\n16. **Codeblock header** (already dark, but adjust border for consistency):\n ```css\n html.dark .codeblock {\n border-color: oklch(0.25 0.005 260);\n }\n html.dark .codeblock-header {\n border-bottom-color: oklch(0.25 0.005 260);\n }\n ```\n\n### Placement\nInsert ALL dark mode rules as a single contiguous section right after the `:root { }` block (after line 137, before the @media query on line 139). Add a comment header: `/* ===== Dark Mode ===== */`\n\n## Don't\n- Do NOT add any JavaScript or components — this is CSS only\n- Do NOT modify any existing light mode rules — only ADD new dark mode rules\n- Do NOT change the code block background (Night Owl theme is already dark)\n- Do NOT use @media (prefers-color-scheme) — we use class-based toggling via html.dark\n- Do NOT use Tailwind — this project uses plain CSS","acceptance_criteria":"1. A new 'html.dark { }' block exists in globals.css with all listed token overrides\n2. All 16 component-specific dark override blocks are present\n3. No existing light-mode CSS rules are modified\n4. The dark mode section has a '/* ===== Dark Mode ===== */' comment header\n5. When html element has class 'dark', the page background is dark (oklch 0.13), text is light, header is glass-dark, logo is white (inverted), sidebar borders are subtle white\n6. npm run build -- --webpack succeeds without errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T19:19:35.606514+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:28:58.450998+08:00","closed_at":"2026-02-20T19:28:58.450998+08:00","close_reason":"a0feb77 Add dark mode CSS variables and component overrides","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-cur.1","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:19:35.607472+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-cur.2","title":"Add ThemeToggle component and FOUC prevention script","description":"## Files\n- documentation/components/ThemeToggle.js (create)\n- documentation/components/Layout.js (modify)\n\n## What to do\n\n### 1. Create ThemeToggle.js component\n\nCreate `documentation/components/ThemeToggle.js` that renders a button toggling dark mode. Copy the ATProto pattern: sun icon visible in light mode, moon icon visible in dark mode.\n\n```jsx\nimport { useState, useEffect } from 'react';\n\nexport function ThemeToggle() {\n const [isDark, setIsDark] = useState(false);\n\n useEffect(() =\u003e {\n // Sync state with actual DOM class on mount\n setIsDark(document.documentElement.classList.contains('dark'));\n }, []);\n\n const toggle = () =\u003e {\n const next = !isDark;\n setIsDark(next);\n document.documentElement.classList.toggle('dark', next);\n localStorage.setItem('theme', next ? 'dark' : 'light');\n };\n\n return (\n \u003cbutton\n className=\"theme-toggle\"\n onClick={toggle}\n aria-label=\"Toggle dark mode\"\n type=\"button\"\n \u003e\n {/* Sun icon — visible in light mode */}\n \u003csvg className=\"theme-toggle-sun\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"20\" height=\"20\"\u003e\n \u003cpath d=\"M12.5 10a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0Z\" stroke=\"currentColor\" strokeWidth=\"1.5\" /\u003e\n \u003cpath d=\"M10 5.5v-1M13.182 6.818l.707-.707M14.5 10h1M13.182 13.182l.707.707M10 15.5v-1M6.11 13.889l.708-.707M4.5 10h1M6.11 6.111l.708.707\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n {/* Moon icon — visible in dark mode */}\n \u003csvg className=\"theme-toggle-moon\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"20\" height=\"20\"\u003e\n \u003cpath d=\"M15.224 11.724a5.5 5.5 0 0 1-6.949-6.949 5.5 5.5 0 1 0 6.949 6.949Z\" stroke=\"currentColor\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n \u003c/button\u003e\n );\n}\n```\n\n### 2. Add FOUC prevention script to Layout.js\n\nIn `Layout.js`, add this inline script tag INSIDE the `\u003cHead\u003e` component, right before the closing `\u003c/Head\u003e` tag. This runs before paint to prevent flash of wrong theme:\n\n```jsx\n\u003cscript\n dangerouslySetInnerHTML={{\n __html: `(function(){try{var d=document.documentElement;var t=localStorage.getItem('theme');if(t==='dark'){d.classList.add('dark')}else if(t==='light'){d.classList.remove('dark')}else if(window.matchMedia('(prefers-color-scheme:dark)').matches){d.classList.add('dark')}}catch(e){}})()`\n }}\n/\u003e\n```\n\n### 3. Add ThemeToggle to Layout.js header\n\nImport ThemeToggle at top of Layout.js:\n```jsx\nimport { ThemeToggle } from './ThemeToggle';\n```\n\nAdd a spacer div and the ThemeToggle button to the header, after the logo link. The header inner should become:\n```jsx\n\u003cdiv className=\"layout-header-inner\"\u003e\n {/* hamburger button (unchanged) */}\n {/* logo link (unchanged) */}\n \u003cdiv style={{ flex: 1 }} /\u003e\n \u003cThemeToggle /\u003e\n\u003c/div\u003e\n```\n\nThe `\u003cdiv style={{ flex: 1 }} /\u003e` pushes the toggle to the far right. Keep the hamburger and logo exactly as they are.\n\n### 4. Add CSS for ThemeToggle to globals.css\n\nAdd these styles in the `/* ===== Header ===== */` section (after `.hamburger-btn:hover`, around line 253):\n\n```css\n/* Theme toggle */\n.theme-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--color-text-secondary);\n border-radius: var(--radius-md);\n transition: background var(--transition-fast), color var(--transition-fast);\n}\n\n.theme-toggle:hover {\n background: var(--hover-bg);\n color: var(--color-text-primary);\n}\n\n.theme-toggle-sun {\n display: block;\n}\n\n.theme-toggle-moon {\n display: none;\n}\n\nhtml.dark .theme-toggle-sun {\n display: none;\n}\n\nhtml.dark .theme-toggle-moon {\n display: block;\n}\n```\n\n## Don't\n- Do NOT use React Context or any state management library — just DOM classList and localStorage\n- Do NOT use @media (prefers-color-scheme) in CSS — only in the JS init script as a fallback\n- Do NOT modify any existing Layout.js elements (hamburger, logo, sidebar, main content) — only ADD the spacer div and ThemeToggle\n- Do NOT import ThemeToggle in _app.js — it's used directly in Layout.js, not as a Markdoc component\n- Do NOT use Tailwind","acceptance_criteria":"1. ThemeToggle.js exists at documentation/components/ThemeToggle.js\n2. Clicking the toggle adds/removes 'dark' class on document.documentElement\n3. Theme choice persists in localStorage under key 'theme' (values: 'dark' or 'light')\n4. On page load, the inline script in \u003cHead\u003e applies the correct class BEFORE React hydrates (no FOUC)\n5. Sun icon shows in light mode, moon icon shows in dark mode (controlled via CSS display: none/block)\n6. Toggle button is positioned at the far right of the header bar\n7. If localStorage has no 'theme' key, system preference (prefers-color-scheme: dark) is respected as fallback\n8. npm run build -- --webpack succeeds without errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":30,"created_at":"2026-02-20T19:20:15.172151+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:29:52.241082+08:00","closed_at":"2026-02-20T19:29:52.241082+08:00","close_reason":"41c4f66 Add ThemeToggle component and FOUC prevention script","labels":["scope:small"],"dependencies":[{"issue_id":"docs-cur.2","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:20:15.172988+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-cur.3","title":"Add search pill button and SearchDialog component","description":"## Files\n- documentation/components/SearchDialog.js (create)\n- documentation/components/Layout.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\n\n### 1. Create SearchDialog.js\n\nCreate `documentation/components/SearchDialog.js` — a modal dialog that lets users search docs pages by title. It uses the navigation data from `lib/navigation.js`.\n\n```jsx\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport { useRouter } from 'next/router';\nimport { flattenNavigation } from '../lib/navigation';\n\nexport function SearchDialog({ isOpen, onClose }) {\n const [query, setQuery] = useState('');\n const inputRef = useRef(null);\n const router = useRouter();\n const allPages = flattenNavigation();\n\n // Filter pages by title match (case-insensitive substring)\n const results = query.trim().length \u003e 0\n ? allPages.filter(p =\u003e p.title.toLowerCase().includes(query.toLowerCase()))\n : allPages;\n\n // Focus input when dialog opens\n useEffect(() =\u003e {\n if (isOpen \u0026\u0026 inputRef.current) {\n inputRef.current.focus();\n setQuery('');\n }\n }, [isOpen]);\n\n // Close on Escape\n useEffect(() =\u003e {\n if (!isOpen) return;\n const handler = (e) =\u003e {\n if (e.key === 'Escape') onClose();\n };\n document.addEventListener('keydown', handler);\n return () =\u003e document.removeEventListener('keydown', handler);\n }, [isOpen, onClose]);\n\n const navigate = useCallback((path) =\u003e {\n router.push(path);\n onClose();\n }, [router, onClose]);\n\n if (!isOpen) return null;\n\n return (\n \u003cdiv className=\"search-overlay\" onClick={onClose}\u003e\n \u003cdiv className=\"search-dialog\" onClick={(e) =\u003e e.stopPropagation()}\u003e\n \u003cdiv className=\"search-input-wrapper\"\u003e\n \u003csvg className=\"search-input-icon\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"20\" height=\"20\"\u003e\n \u003cpath d=\"M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n \u003cinput\n ref={inputRef}\n className=\"search-input\"\n type=\"text\"\n placeholder=\"Search documentation...\"\n value={query}\n onChange={(e) =\u003e setQuery(e.target.value)}\n onKeyDown={(e) =\u003e {\n if (e.key === 'Enter' \u0026\u0026 results.length \u003e 0) {\n navigate(results[0].path);\n }\n }}\n /\u003e\n \u003ckbd className=\"search-input-kbd\"\u003eESC\u003c/kbd\u003e\n \u003c/div\u003e\n \u003cdiv className=\"search-results\"\u003e\n {results.length === 0 ? (\n \u003cdiv className=\"search-no-results\"\u003eNo pages found\u003c/div\u003e\n ) : (\n \u003cul className=\"search-results-list\"\u003e\n {results.map((page) =\u003e (\n \u003cli key={page.path}\u003e\n \u003cbutton\n className=\"search-result-item\"\n onClick={() =\u003e navigate(page.path)}\n type=\"button\"\n \u003e\n \u003cspan className=\"search-result-title\"\u003e{page.title}\u003c/span\u003e\n \u003cspan className=\"search-result-path\"\u003e{page.path}\u003c/span\u003e\n \u003c/button\u003e\n \u003c/li\u003e\n ))}\n \u003c/ul\u003e\n )}\n \u003c/div\u003e\n \u003c/div\u003e\n \u003c/div\u003e\n );\n}\n```\n\n### 2. Modify Layout.js — add search button and dialog\n\nAdd these imports at top:\n```jsx\nimport { SearchDialog } from './SearchDialog';\n```\n\nAdd search state inside the Layout component (after the existing useState calls):\n```jsx\nconst [searchOpen, setSearchOpen] = useState(false);\n```\n\nAdd keyboard shortcut effect (after the existing useEffect):\n```jsx\nuseEffect(() =\u003e {\n const handler = (e) =\u003e {\n if ((e.metaKey || e.ctrlKey) \u0026\u0026 e.key === 'k') {\n e.preventDefault();\n setSearchOpen(prev =\u003e !prev);\n }\n };\n document.addEventListener('keydown', handler);\n return () =\u003e document.removeEventListener('keydown', handler);\n}, []);\n```\n\nIn the header, between the logo link and the spacer div, add the search button. The header inner should be:\n```jsx\n\u003cdiv className=\"layout-header-inner\"\u003e\n {/* hamburger button (unchanged) */}\n {/* logo link (unchanged) */}\n \u003cbutton\n className=\"search-pill\"\n onClick={() =\u003e setSearchOpen(true)}\n type=\"button\"\n \u003e\n \u003csvg className=\"search-pill-icon\" viewBox=\"0 0 20 20\" fill=\"none\" width=\"16\" height=\"16\"\u003e\n \u003cpath d=\"M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n \u003cspan className=\"search-pill-text\"\u003eFind something...\u003c/span\u003e\n \u003ckbd className=\"search-pill-kbd\"\u003e\u003cspan\u003e⌘\u003c/span\u003eK\u003c/kbd\u003e\n \u003c/button\u003e\n \u003cdiv style={{ flex: 1 }} /\u003e\n {/* ThemeToggle (added by docs-cur.2 — if not present yet, ignore) */}\n\u003c/div\u003e\n```\n\nAdd the SearchDialog component at the END of the JSX, right before the closing `\u003c/\u003e` fragment:\n```jsx\n\u003cSearchDialog isOpen={searchOpen} onClose={() =\u003e setSearchOpen(false)} /\u003e\n```\n\n### 3. Add CSS for search components to globals.css\n\nAdd these styles at the end of the `/* ===== Header ===== */` section (after theme-toggle styles if present, otherwise after .hamburger-btn:hover):\n\n```css\n/* Search pill button */\n.search-pill {\n display: none;\n align-items: center;\n gap: 8px;\n height: 32px;\n padding: 0 12px 0 8px;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: 9999px;\n cursor: pointer;\n font-size: 14px;\n color: var(--color-text-secondary);\n transition: border-color var(--transition-fast);\n white-space: nowrap;\n}\n\n.search-pill:hover {\n border-color: var(--color-border-strong);\n}\n\n.search-pill-icon {\n flex-shrink: 0;\n color: var(--color-text-secondary);\n}\n\n.search-pill-text {\n font-size: 13px;\n color: var(--color-text-secondary);\n}\n\n.search-pill-kbd {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n margin-left: 8px;\n font-size: 11px;\n font-family: var(--font-sans);\n color: oklch(0.55 0.01 260);\n pointer-events: none;\n}\n\n/* Show pill only on large screens */\n@media (min-width: 769px) {\n .search-pill {\n display: flex;\n }\n}\n```\n\nAdd a new section before the responsive section for search dialog styles:\n```css\n/* ===== Component: SearchDialog ===== */\n.search-overlay {\n position: fixed;\n inset: 0;\n z-index: 200;\n background: oklch(0 0 0 / 0.4);\n display: flex;\n align-items: flex-start;\n justify-content: center;\n padding-top: 15vh;\n}\n\nhtml.dark .search-overlay {\n background: oklch(0 0 0 / 0.6);\n}\n\n.search-dialog {\n width: 90%;\n max-width: 560px;\n max-height: 60vh;\n background: var(--color-bg);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-xl);\n box-shadow: 0 16px 70px oklch(0 0 0 / 0.2);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.search-input-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 16px;\n border-bottom: 1px solid var(--color-border);\n}\n\n.search-input-icon {\n flex-shrink: 0;\n color: var(--color-text-secondary);\n}\n\n.search-input {\n flex: 1;\n border: none;\n background: none;\n outline: none;\n font-size: 16px;\n font-family: var(--font-sans);\n color: var(--color-text-primary);\n}\n\n.search-input::placeholder {\n color: var(--color-text-secondary);\n}\n\n.search-input-kbd {\n font-size: 11px;\n font-family: var(--font-mono);\n color: var(--color-text-secondary);\n background: var(--color-bg-subtle);\n border: 1px solid var(--color-border);\n border-radius: 4px;\n padding: 2px 6px;\n line-height: 1;\n}\n\n.search-results {\n overflow-y: auto;\n max-height: calc(60vh - 52px);\n padding: 8px;\n}\n\n.search-results-list {\n list-style: none;\n padding: 0;\n margin: 0;\n}\n\n.search-result-item {\n display: flex;\n flex-direction: column;\n width: 100%;\n padding: 8px 12px;\n background: none;\n border: none;\n border-radius: var(--radius-md);\n cursor: pointer;\n text-align: left;\n font-family: var(--font-sans);\n transition: background var(--transition-fast);\n}\n\n.search-result-item:hover {\n background: var(--hover-bg);\n}\n\n.search-result-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--color-text-heading);\n}\n\n.search-result-path {\n font-size: 12px;\n color: var(--color-text-secondary);\n font-family: var(--font-mono);\n margin-top: 2px;\n}\n\n.search-no-results {\n padding: 24px;\n text-align: center;\n font-size: 14px;\n color: var(--color-text-secondary);\n}\n```\n\n## Don't\n- Do NOT install any search library (Algolia, FlexSearch, etc.) — use simple substring matching on page titles from navigation.js\n- Do NOT use Tailwind\n- Do NOT modify Sidebar.js, _app.js, or any markdoc files\n- Do NOT add the search pill inside the mobile hamburger menu — it's only in the header\n- Do NOT create any API routes — this is fully client-side","acceptance_criteria":"1. SearchDialog.js exists at documentation/components/SearchDialog.js\n2. A pill-shaped 'Find something...' button appears in the header on screens \u003e= 769px\n3. Clicking the pill or pressing ⌘K (Mac) / Ctrl+K (Windows) opens a centered modal dialog\n4. Typing in the dialog filters pages from navigation.js by case-insensitive title substring\n5. Clicking a result navigates to that page and closes the dialog\n6. Pressing Enter navigates to the first result\n7. Pressing Escape or clicking the overlay closes the dialog\n8. The search dialog is dark-mode-aware (uses CSS variables)\n9. npm run build -- --webpack succeeds without errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T19:21:12.221001+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:32:28.903279+08:00","closed_at":"2026-02-20T19:32:28.903279+08:00","close_reason":"1bb28fe Add search pill button and SearchDialog component","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-cur.3","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:21:12.221976+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.3","depends_on_id":"docs-cur.2","type":"blocks","created_at":"2026-02-20T19:21:12.223132+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-cur.4","title":"Integration: verify dark mode and search build and render correctly","description":"## Files\n- All files from docs-cur.1, docs-cur.2, docs-cur.3 (verify only)\n\n## What to do\n\nRun the full build and verify all dark mode + search features work together:\n\n1. `npm run build -- --webpack` must succeed with zero errors\n2. Verify dark mode tokens are applied correctly by checking CSS\n3. Verify ThemeToggle exists and is imported in Layout.js\n4. Verify SearchDialog exists and is imported in Layout.js\n5. Verify search pill button markup exists in Layout.js header\n6. Verify no duplicate spacer divs or broken header layout\n\nIf any of these checks fail, fix the issue in the relevant file(s). Common issues:\n- Merge conflict artifacts in Layout.js (both docs-cur.2 and docs-cur.3 modify it)\n- Missing import statements\n- Duplicate CSS rules in globals.css\n- FOUC script missing from Head\n\n## Don't\n- Do NOT add new features — this is verification and conflict resolution only\n- Do NOT redesign any component's appearance\n- Do NOT use Tailwind","acceptance_criteria":"1. npm run build -- --webpack succeeds with zero errors\n2. Layout.js imports and renders both ThemeToggle and SearchDialog\n3. Layout.js has the FOUC prevention script in Head\n4. globals.css has the dark mode section with html.dark tokens\n5. globals.css has search pill and search dialog CSS\n6. No merge conflict markers (\u003c\u003c\u003c\u003c\u003c\u003c\u003c etc.) in any file\n7. Header contains: hamburger, logo, search pill, spacer, theme toggle (in that order)","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":15,"created_at":"2026-02-20T19:21:29.21361+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:34:15.1468+08:00","closed_at":"2026-02-20T19:34:15.1468+08:00","close_reason":"3d39aeb integration verified: build passes, dark mode + search render correctly, no conflicts","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-cur.4","depends_on_id":"docs-cur","type":"parent-child","created_at":"2026-02-20T19:21:29.214831+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.4","depends_on_id":"docs-cur.1","type":"blocks","created_at":"2026-02-20T19:21:29.216033+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.4","depends_on_id":"docs-cur.2","type":"blocks","created_at":"2026-02-20T19:21:29.216899+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-cur.4","depends_on_id":"docs-cur.3","type":"blocks","created_at":"2026-02-20T19:21:29.21772+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-d4r","title":"Epic 8: Build Configuration and Vercel Deployment","description":"## Summary\nVerify the Next.js build pipeline works end-to-end, fix any build errors, and configure Vercel deployment for the documentation site.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory inside the repository at `/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`. All commands should be run from the `documentation/` directory.\n\nBy the time this epic runs, Epics 1-7 have:\n- Scaffolded the Next.js + Markdoc project (Epic 1)\n- Created custom Markdoc tags and React components (Epic 2)\n- Migrated all 17 content files to `pages/` (Epic 3)\n- Built the navigation and layout system (Epic 4)\n- Applied Stripe-inspired styling (Epic 5)\n- Moved images to `public/images/` (Epic 6)\n- Fixed all broken links (Epic 7)\n\n## Tasks\n\n### 1. Verify Local Development Server\n```bash\ncd documentation\nnpm run dev\n```\n- Open http://localhost:3000 in a browser\n- Navigate through ALL pages via the sidebar and verify:\n - Pages load without console errors\n - Images display correctly\n - Tables render with proper styling\n - Callout boxes display with colored left borders\n - Column layouts work (side by side on desktop)\n - Sidebar navigation highlights the current page\n - Right-side table of contents shows headings\n - Prev/next pagination links work\n- Fix any runtime errors or warnings\n\n### 2. Verify Production Build\n```bash\ncd documentation\nnpm run build\n```\n- Ensure the build completes with ZERO errors\n- Common issues to watch for:\n - Missing component imports\n - Invalid Markdoc tag syntax in .md files\n - Broken internal links that cause 404s during static generation\n - CSS module import errors\n- Fix any build-time errors before proceeding\n\n### 3. Test Production Server Locally\n```bash\ncd documentation\nnpm run start\n```\n- Verify the production build serves correctly at http://localhost:3000\n- Spot-check 3-5 pages for rendering issues\n- Verify images load (they should be in `public/images/`)\n\n### 4. Configure Static Export (if desired)\nFor pure static hosting, add `output: 'export'` to `next.config.js`:\n```js\nconst withMarkdoc = require('@markdoc/next.js');\nmodule.exports = withMarkdoc({ mode: 'static' })({\n output: 'export',\n pageExtensions: ['md', 'mdoc', 'js', 'jsx', 'ts', 'tsx']\n});\n```\nThen `npm run build` will output static files to the `out/` directory.\n\n### 5. Verify .gitignore\nEnsure `documentation/.gitignore` contains:\n```\nnode_modules/\n.next/\nout/\n```\n\n### 6. Prepare for Vercel Deployment\nCreate or verify that the project is ready for Vercel:\n- The `documentation/` directory should be self-contained with its own `package.json`\n- `npm run build` must succeed\n- The Vercel project settings should be:\n - **Root Directory:** `documentation`\n - **Framework Preset:** Next.js (auto-detected)\n - **Build Command:** `npm run build` (or auto-detected)\n - **Output Directory:** `.next` (or `out` if using static export)\n\nNote: Actually connecting the GitHub repo to Vercel and deploying is a manual step that requires Vercel account access. This epic should ensure the project is deployment-ready.\n\n## Acceptance Criteria\n- `npm run dev` starts without errors and all 17 pages render correctly\n- `npm run build` completes with zero errors\n- `npm run start` serves the production build correctly\n- `.gitignore` excludes `node_modules/`, `.next/`, `out/`\n- All pages, images, tables, callouts, columns, sidebar navigation, and pagination work correctly\n- The project is ready to be deployed to Vercel (self-contained `documentation/` directory with valid `package.json` and build scripts)\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:45:29.358726+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:19:10.831281+13:00","closed_at":"2026-02-14T13:19:10.831287+13:00","dependencies":[{"issue_id":"docs-d4r","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:45:29.360892+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:45:29.363352+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-qsc","type":"blocks","created_at":"2026-02-13T13:45:29.364147+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-7dv","type":"blocks","created_at":"2026-02-13T13:45:29.364846+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-yne","type":"blocks","created_at":"2026-02-13T13:45:29.365508+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-cij","type":"blocks","created_at":"2026-02-13T13:45:29.366168+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-d4r","depends_on_id":"docs-7b4","type":"blocks","created_at":"2026-02-13T13:45:29.366821+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-dfq","title":"Epic 10: Stripe Design Language Refinements","description":"## Goal\n\nRefine the Hypercerts documentation site to match Stripe's documentation design language — not just CSS tokens but the full design language: layout patterns, content presentation, interaction design, and structural elements.\n\n## Context\n\nAll 9 migration epics are complete. The site builds and exports correctly. The current design has the right tokens (colors, fonts, spacing) but lacks the polish and interaction patterns that make Stripe docs feel premium:\n\n1. No custom scrollbar styling — browser defaults look chunky\n2. No heading anchor links — can't easily copy section links\n3. Content area is centered with `margin: 0 auto` — looks asymmetric with sidebar + TOC\n4. `\u003cstrong\u003e` / `\u003cb\u003e` needs darker color and weight 700\n5. Links use `text-decoration: underline` on hover — Stripe uses bottom-border approach\n6. First H2 has too much top margin after H1\n7. Header is too plain — just 'Hypercerts Protocol' text\n8. No smooth transitions on hover states\n9. Landing page lacks card-style navigation links\n10. No breadcrumb navigation\n\n## Acceptance Criteria\n\n- `npm run build --prefix documentation` exits 0\n- All 11 design refinements implemented\n- No regressions to existing content rendering\n- Site remains fully static-exportable\n\n## Technical Notes\n\n- Project uses Next.js 16 + @markdoc/next.js with `--webpack` flag\n- CSS is in `documentation/styles/globals.css` (654 lines)\n- Components in `documentation/components/`\n- Markdoc nodes in `documentation/markdoc/nodes/` (currently empty)\n- `documentation/pages/_app.js` registers Markdoc components","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:23:44.58559+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:10:10.597195+13:00","closed_at":"2026-02-14T20:10:10.597195+13:00","close_reason":"Closed"} -{"id":"docs-dfq.1","title":"Custom scrollbar styling for sidebar, TOC, and code blocks","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd custom thin scrollbar styling throughout the site, matching Stripe docs pattern:\n\n1. Add a global scrollbar utility section in globals.css after the Reset section (around line 9).\n\n2. **Sidebar scrollbar** (`.sidebar`): \n - Width: 6px\n - Track: transparent\n - Thumb: `rgba(0,0,0,0.15)` with border-radius 3px\n - Thumb on hover: `rgba(0,0,0,0.25)`\n - Auto-hide behavior: set thumb to transparent by default, show on `.sidebar:hover` (use `::-webkit-scrollbar-thumb` with transition workaround)\n - Firefox: `scrollbar-width: thin; scrollbar-color: transparent transparent;` by default, `scrollbar-color: rgba(0,0,0,0.15) transparent;` on hover\n\n3. **TOC scrollbar** (`.toc`):\n - Same styling as sidebar\n\n4. **Code block scrollbar** (`.layout-content pre`):\n - Horizontal scrollbar only (already has `overflow-x: auto`)\n - Height: 6px\n - Track: transparent \n - Thumb: `rgba(0,0,0,0.12)` with border-radius 3px\n - Always visible when content overflows (no auto-hide needed)\n\n5. Use both `-webkit-scrollbar` (Chrome/Safari/Edge) and `scrollbar-width`/`scrollbar-color` (Firefox) properties.\n\n## CSS to add (insert after line 8, before Design Tokens):\n\n```css\n/* ===== Scrollbar Styling ===== */\n/* Webkit (Chrome, Safari, Edge) */\n.sidebar::-webkit-scrollbar,\n.toc::-webkit-scrollbar {\n width: 6px;\n}\n\n.sidebar::-webkit-scrollbar-track,\n.toc::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.sidebar::-webkit-scrollbar-thumb,\n.toc::-webkit-scrollbar-thumb {\n background: transparent;\n border-radius: 3px;\n}\n\n.sidebar:hover::-webkit-scrollbar-thumb,\n.toc:hover::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.15);\n}\n\n.sidebar::-webkit-scrollbar-thumb:hover,\n.toc::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.25);\n}\n\n/* Code block horizontal scrollbar */\n.layout-content pre::-webkit-scrollbar {\n height: 6px;\n}\n\n.layout-content pre::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.layout-content pre::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.12);\n border-radius: 3px;\n}\n\n.layout-content pre::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.2);\n}\n\n/* Firefox */\n.sidebar,\n.toc {\n scrollbar-width: thin;\n scrollbar-color: transparent transparent;\n}\n\n.sidebar:hover,\n.toc:hover {\n scrollbar-color: rgba(0, 0, 0, 0.15) transparent;\n}\n\n.layout-content pre {\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.12) transparent;\n}\n```\n\n## Edge cases\n- Do NOT remove or modify existing `overflow-y: auto` on `.sidebar` or `overflow-x: auto` on `.layout-content pre`\n- Do NOT add scrollbar styles to the main body/html element\n- The Firefox `scrollbar-color` properties on `.sidebar` and `.toc` must be added to the EXISTING rule blocks (merge with existing selectors), OR placed after them so they cascade correctly. Since `.sidebar` already has styles at line 162, add the Firefox properties there OR use the new scrollbar section with higher specificity.\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Verify visually that scrollbar styles are present in the built CSS.\n\n## Dont\n- Do not modify any JavaScript files\n- Do not change any existing CSS properties — only ADD new scrollbar-related properties\n- Do not use JavaScript-based scrollbar libraries","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:24:20.195548+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:03:50.061126+13:00","closed_at":"2026-02-14T20:03:50.061126+13:00","close_reason":"Added custom scrollbar styling for sidebar, TOC, and code blocks. Build successful.","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-dfq.1","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:24:20.196559+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-dfq.2","title":"Heading anchor links on hover with Markdoc node override","description":"## Files\n- documentation/markdoc/nodes/heading.markdoc.js (create)\n- documentation/components/Heading.js (create)\n- documentation/pages/_app.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd clickable anchor links (#) that appear on hover next to H2 and H3 headings, matching Stripe docs pattern. This requires a Markdoc heading node override.\n\n### 1. Create `documentation/markdoc/nodes/heading.markdoc.js`\n\nThis file tells @markdoc/next.js to use a custom component for heading nodes:\n\n```js\nimport { nodes } from \"@markdoc/markdoc\";\n\nconst heading = {\n ...nodes.heading,\n render: \"Heading\",\n};\n\nexport default heading;\n```\n\n### 2. Create `documentation/components/Heading.js`\n\nThis React component renders headings with auto-generated IDs and hover anchor links:\n\n```jsx\nimport React from \"react\";\n\nfunction generateId(children) {\n const text = React.Children.toArray(children)\n .filter((child) =\u003e typeof child === \"string\")\n .join(\"\");\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n}\n\nexport function Heading({ level = 2, children, id }) {\n const Tag = `h${level}`;\n const headingId = id || generateId(children);\n\n return (\n \u003cTag id={headingId} className=\"heading-anchor-target\"\u003e\n {children}\n {(level === 2 || level === 3) \u0026\u0026 (\n \u003ca\n href={`#${headingId}`}\n className=\"heading-anchor\"\n aria-label={`Link to this section`}\n onClick={(e) =\u003e {\n e.preventDefault();\n const el = document.getElementById(headingId);\n if (el) {\n el.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n history.pushState(null, \"\", `#${headingId}`);\n }\n }}\n \u003e\n #\n \u003c/a\u003e\n )}\n \u003c/Tag\u003e\n );\n}\n```\n\n### 3. Modify `documentation/pages/_app.js`\n\nImport the Heading component and register it in the components object:\n\nAdd this import at the top:\n```js\nimport { Heading } from \"../components/Heading\";\n```\n\nAdd `Heading` to the components object:\n```js\nconst components = {\n Callout,\n Columns,\n Column,\n Figure,\n Heading,\n};\n```\n\n### 4. Add CSS to `documentation/styles/globals.css`\n\nAdd these styles in the Typography section (after the existing heading styles, around line 416):\n\n```css\n/* Heading anchor links */\n.heading-anchor-target {\n position: relative;\n}\n\n.heading-anchor {\n position: absolute;\n left: -1.2em;\n top: 50%;\n transform: translateY(-50%);\n color: var(--color-text-secondary);\n text-decoration: none;\n font-weight: 400;\n opacity: 0;\n transition: opacity 0.15s ease;\n font-size: 0.85em;\n}\n\n.heading-anchor:hover {\n color: var(--color-link);\n text-decoration: none;\n}\n\n.heading-anchor-target:hover .heading-anchor {\n opacity: 1;\n}\n```\n\n## Edge cases\n- The `generateId` function must handle children that are React elements (not just strings). Use `React.Children.toArray` and filter for strings.\n- H1 headings should NOT get anchor links (only H2 and H3).\n- H4, H5, H6 should render normally without anchor links.\n- The heading ID generation must match the existing ID generation in `TableOfContents.js` (same algorithm: lowercase, replace non-alphanumeric with hyphens, trim hyphens). The TOC component reads IDs from the DOM, so as long as the Heading component sets `id` on the element, it will work.\n- The `id` prop may be passed by Markdoc if the markdown has `{% heading id=\"custom-id\" %}` — respect it if provided.\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Additionally verify:\n```bash\ngrep -r \"heading-anchor\" documentation/styles/globals.css \u0026\u0026 echo \"CSS OK\"\ngrep -r \"Heading\" documentation/components/Heading.js \u0026\u0026 echo \"Component OK\"\ngrep -r \"heading\" documentation/markdoc/nodes/heading.markdoc.js \u0026\u0026 echo \"Node OK\"\ngrep \"Heading\" documentation/pages/_app.js \u0026\u0026 echo \"Registration OK\"\n```\nAll four greps must produce output.\n\n## Dont\n- Do not modify TableOfContents.js — it reads IDs from the DOM and will continue to work\n- Do not add anchor links to H1 headings\n- Do not use any external libraries\n- Do not change the heading ID generation algorithm (must remain compatible with TOC scroll spy)","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:24:42.488252+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:03:48.382557+13:00","closed_at":"2026-02-14T20:03:48.382557+13:00","close_reason":"219b041 Add heading anchor links on hover - implemented Markdoc heading node override with auto-generated IDs and hover anchor links for H2/H3","labels":["scope:small"],"dependencies":[{"issue_id":"docs-dfq.2","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:24:42.502369+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-dfq.3","title":"CSS typography and link refinements","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nRefine typography and link styles to match Stripe docs design language. All changes are CSS-only.\n\n### 1. Strong/bold text styling\nAdd after the existing heading styles (around line 416 area, or wherever heading styles end):\n\n```css\n/* Bold text */\n.layout-content strong,\n.layout-content b {\n color: var(--color-text-heading);\n font-weight: 600;\n}\n```\n\nNote: Use `font-weight: 600` (semibold) not 700 — Stripe uses 600 for inline bold to distinguish from heading weight.\n\n### 2. Link underline refinement\nReplace the existing `.layout-content a:hover` rule (currently at line 444-447) to use border-bottom instead of text-decoration:\n\nChange FROM:\n```css\n.layout-content a:hover {\n color: var(--color-link-hover);\n text-decoration: underline;\n}\n```\n\nChange TO:\n```css\n.layout-content a {\n color: var(--color-link);\n text-decoration: none;\n border-bottom: 1px solid transparent;\n transition: border-color 0.15s ease, color 0.15s ease;\n}\n\n.layout-content a:hover {\n color: var(--color-link-hover);\n text-decoration: none;\n border-bottom-color: var(--color-link-hover);\n}\n```\n\nThis replaces the existing `.layout-content a` (line 439-442) and `.layout-content a:hover` (line 444-447) rules entirely.\n\n### 3. First H2 tighter spacing after H1\nAdd a rule that reduces the top margin of the first H2 that follows an H1:\n\n```css\n/* Tighter coupling between H1 and first H2 */\n.layout-content h1 + h2 {\n margin-top: var(--space-6);\n}\n```\n\nThis overrides the default `margin-top: var(--space-12)` (48px) on H2 to `var(--space-6)` (24px) when it immediately follows an H1.\n\n### 4. Paragraph spacing refinement\nThe current paragraph bottom margin is `var(--space-4)` (16px). This is correct — do NOT change it. But add a rule for the last paragraph in an article to remove bottom margin:\n\n```css\n.layout-content article \u003e p:last-child {\n margin-bottom: 0;\n}\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Additionally:\n```bash\ngrep \"layout-content strong\" documentation/styles/globals.css \u0026\u0026 echo \"Strong OK\"\ngrep \"border-bottom\" documentation/styles/globals.css \u0026\u0026 echo \"Link underline OK\"\ngrep \"h1 + h2\" documentation/styles/globals.css \u0026\u0026 echo \"H2 spacing OK\"\n```\nAll three greps must produce output.\n\n## Dont\n- Do not change font sizes or line heights on headings\n- Do not modify heading colors\n- Do not change the base link color (--color-link: #0570de)\n- Do not modify any JavaScript files\n- Do not remove the existing `a:hover` rule in the Base section (line 93-96) — only modify the `.layout-content a` rules","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:24:59.393717+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:02:45.90143+13:00","closed_at":"2026-02-14T20:02:45.90143+13:00","close_reason":"95604df CSS typography and link refinements","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-dfq.3","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:24:59.395087+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-dfq.4","title":"Fix content area layout - left-align instead of center","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe content area currently uses `margin: 0 auto` which centers it in the available space between sidebar and TOC. This looks asymmetric — the content should be left-aligned in its column with consistent padding, like Stripe docs.\n\n### Change the `.layout-content` rule (currently at line 266-272):\n\nChange FROM:\n```css\n.layout-content {\n flex: 1;\n min-width: 0;\n max-width: var(--content-max-width);\n margin: 0 auto;\n padding: var(--space-8);\n}\n```\n\nChange TO:\n```css\n.layout-content {\n flex: 1;\n min-width: 0;\n max-width: var(--content-max-width);\n padding: var(--space-8) var(--space-10);\n}\n```\n\nKey changes:\n- Remove `margin: 0 auto` (was centering the content)\n- Change padding from `var(--space-8)` (32px all sides) to `var(--space-8) var(--space-10)` (32px top/bottom, 40px left/right)\n- The content will now sit left-aligned in the flex container, with the remaining space naturally pushing toward the TOC\n\n### Also update the mobile responsive rule (currently at line 639-641):\n\nChange FROM:\n```css\n.layout-content {\n padding: var(--space-6) var(--space-4);\n}\n```\n\nChange TO:\n```css\n.layout-content {\n padding: var(--space-6) var(--space-4);\n}\n```\n\n(No change needed for mobile — keep as-is.)\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Verify the built CSS does NOT contain `margin: 0 auto` for `.layout-content`.\n\n## Dont\n- Do not change `max-width: var(--content-max-width)` — keep the 720px max\n- Do not change `flex: 1` or `min-width: 0`\n- Do not modify the mobile responsive padding\n- Do not modify any JavaScript files","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:25:10.679561+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:03:19.531911+13:00","closed_at":"2026-02-14T20:03:19.531911+13:00","close_reason":"7ee8f5d Fix content area layout - left-align instead of center","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-dfq.4","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:25:10.681171+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-dfq.5","title":"Enhanced header with breadcrumbs and visual refinement","description":"## Files\n- documentation/components/Layout.js (modify)\n- documentation/components/Breadcrumbs.js (create)\n- documentation/styles/globals.css (modify)\n\n## What to do\nEnhance the header to be more visually refined like Stripe docs. Add breadcrumb navigation below the header, and improve the header visual treatment.\n\n### 1. Create `documentation/components/Breadcrumbs.js`\n\nA breadcrumb component that shows the current page location based on the navigation tree:\n\n```jsx\nimport Link from \"next/link\";\nimport { useRouter } from \"next/router\";\nimport { navigation } from \"../lib/navigation\";\n\nfunction findBreadcrumbs(nav, targetPath, trail = []) {\n for (const item of nav) {\n if (item.section) {\n const result = findBreadcrumbs(item.children || [], targetPath, [\n ...trail,\n { title: item.section },\n ]);\n if (result) return result;\n } else {\n if (item.path === targetPath) {\n return [...trail, { title: item.title, path: item.path }];\n }\n if (item.children) {\n const result = findBreadcrumbs(item.children, targetPath, [\n ...trail,\n { title: item.title, path: item.path },\n ]);\n if (result) return result;\n }\n }\n }\n return null;\n}\n\nexport function Breadcrumbs() {\n const router = useRouter();\n const currentPath = router.asPath.split(\"#\")[0].split(\"?\")[0];\n\n // Dont show breadcrumbs on home page\n if (currentPath === \"/\") return null;\n\n const crumbs = findBreadcrumbs(navigation, currentPath) || [];\n\n if (crumbs.length \u003c= 1) return null;\n\n return (\n \u003cnav className=\"breadcrumbs\" aria-label=\"Breadcrumb\"\u003e\n \u003col className=\"breadcrumbs-list\"\u003e\n \u003cli className=\"breadcrumbs-item\"\u003e\n \u003cLink href=\"/\" className=\"breadcrumbs-link\"\u003e\n Docs\n \u003c/Link\u003e\n \u003c/li\u003e\n {crumbs.slice(0, -1).map((crumb, i) =\u003e (\n \u003cli key={i} className=\"breadcrumbs-item\"\u003e\n \u003cspan className=\"breadcrumbs-separator\"\u003e/\u003c/span\u003e\n {crumb.path ? (\n \u003cLink href={crumb.path} className=\"breadcrumbs-link\"\u003e\n {crumb.title}\n \u003c/Link\u003e\n ) : (\n \u003cspan className=\"breadcrumbs-text\"\u003e{crumb.title}\u003c/span\u003e\n )}\n \u003c/li\u003e\n ))}\n \u003cli className=\"breadcrumbs-item\"\u003e\n \u003cspan className=\"breadcrumbs-separator\"\u003e/\u003c/span\u003e\n \u003cspan className=\"breadcrumbs-current\"\u003e{crumbs[crumbs.length - 1].title}\u003c/span\u003e\n \u003c/li\u003e\n \u003c/ol\u003e\n \u003c/nav\u003e\n );\n}\n```\n\n### 2. Modify `documentation/components/Layout.js`\n\nAdd the Breadcrumbs component import and render it inside the `\u003cmain\u003e` element, before the `\u003carticle\u003e`:\n\nAdd import at top:\n```js\nimport { Breadcrumbs } from \"./Breadcrumbs\";\n```\n\nInside the JSX, change the `\u003cmain\u003e` section from:\n```jsx\n\u003cmain className=\"layout-content\"\u003e\n \u003carticle\u003e{children}\u003c/article\u003e\n```\n\nTo:\n```jsx\n\u003cmain className=\"layout-content\"\u003e\n \u003cBreadcrumbs /\u003e\n \u003carticle\u003e{children}\u003c/article\u003e\n```\n\n### 3. Add CSS to `documentation/styles/globals.css`\n\nAdd a new section after the Header styles (after line ~153) for breadcrumbs:\n\n```css\n/* ===== Breadcrumbs ===== */\n.breadcrumbs {\n margin-bottom: var(--space-4);\n}\n\n.breadcrumbs-list {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n list-style: none;\n padding: 0;\n margin: 0;\n font-size: 13px;\n line-height: 1.4;\n}\n\n.breadcrumbs-item {\n display: flex;\n align-items: center;\n}\n\n.breadcrumbs-separator {\n margin: 0 6px;\n color: var(--color-text-secondary);\n font-size: 12px;\n}\n\n.breadcrumbs-link {\n color: var(--color-text-secondary);\n text-decoration: none;\n transition: color 0.15s ease;\n}\n\n.breadcrumbs-link:hover {\n color: var(--color-link);\n text-decoration: none;\n}\n\n.breadcrumbs-text {\n color: var(--color-text-secondary);\n}\n\n.breadcrumbs-current {\n color: var(--color-text-primary);\n font-weight: 500;\n}\n```\n\nAlso enhance the header visual treatment. Add a subtle backdrop blur to the header. Modify the existing `.layout-header` rule (line 110-117):\n\nChange FROM:\n```css\n.layout-header {\n position: sticky;\n top: 0;\n z-index: 100;\n height: var(--header-height);\n background: var(--color-bg);\n border-bottom: 1px solid var(--color-border);\n}\n```\n\nChange TO:\n```css\n.layout-header {\n position: sticky;\n top: 0;\n z-index: 100;\n height: var(--header-height);\n background: rgba(255, 255, 255, 0.95);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n border-bottom: 1px solid var(--color-border);\n}\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Additionally:\n```bash\ntest -f documentation/components/Breadcrumbs.js \u0026\u0026 echo \"Breadcrumbs component OK\"\ngrep \"Breadcrumbs\" documentation/components/Layout.js \u0026\u0026 echo \"Layout import OK\"\ngrep \"breadcrumbs\" documentation/styles/globals.css \u0026\u0026 echo \"CSS OK\"\ngrep \"backdrop-filter\" documentation/styles/globals.css \u0026\u0026 echo \"Header blur OK\"\n```\nAll four checks must pass.\n\n## Dont\n- Do not modify the header height or padding\n- Do not add a search bar (out of scope for this task)\n- Do not modify navigation.js\n- Do not modify Sidebar.js or TableOfContents.js\n- Do not change the logo text or link","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:25:33.805204+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:03:55.98582+13:00","closed_at":"2026-02-14T20:03:55.98582+13:00","close_reason":"77ec8dc Enhanced header with breadcrumbs and visual refinement","labels":["scope:small"],"dependencies":[{"issue_id":"docs-dfq.5","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:25:33.806485+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-dfq.6","title":"Transition and hover state polish","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd smooth transitions and refined hover states throughout the site, matching Stripe docs interaction patterns. All changes are CSS-only.\n\n### 1. Global transition timing\nAdd a CSS custom property for the standard transition in the `:root` design tokens section (around line 69, before the closing `}`):\n\n```css\n --transition-fast: 150ms ease;\n --transition-normal: 200ms cubic-bezier(0, 0.09, 0.4, 1);\n```\n\n### 2. Sidebar link transitions\nUpdate the existing `.sidebar-link` rule (line ~214-225). Add/modify the transition property:\n\nChange FROM:\n```css\n transition: background 0.1s ease;\n```\n\nChange TO:\n```css\n transition: background var(--transition-fast), color var(--transition-fast);\n```\n\n### 3. Sidebar active indicator\nAdd a left border indicator for the active sidebar link. Modify `.sidebar-link-active` (line ~233-237):\n\nChange FROM:\n```css\n.sidebar-link-active {\n font-weight: 600;\n color: var(--color-text-title);\n background: var(--hover-bg);\n}\n```\n\nChange TO:\n```css\n.sidebar-link-active {\n font-weight: 600;\n color: var(--color-link);\n background: var(--active-bg);\n border-left: 2px solid var(--color-link);\n padding-left: calc(var(--space-3) - 2px);\n}\n```\n\nAlso update `a.sidebar-link-active:hover` (line ~239-242):\n\nChange FROM:\n```css\na.sidebar-link-active:hover {\n background: var(--hover-bg);\n color: var(--color-text-title);\n}\n```\n\nChange TO:\n```css\na.sidebar-link-active:hover {\n background: var(--active-bg);\n color: var(--color-link);\n}\n```\n\n### 4. Pagination hover enhancement\nUpdate the `.pagination-link` transition (line ~354):\n\nChange FROM:\n```css\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n```\n\nChange TO:\n```css\n transition: border-color var(--transition-normal), box-shadow var(--transition-normal), transform var(--transition-normal);\n```\n\nAdd a subtle lift on hover. Update `.pagination-link:hover` (line ~357-361):\n\nChange FROM:\n```css\n.pagination-link:hover {\n border-color: var(--color-link);\n box-shadow: 0 1px 4px rgba(5, 112, 222, 0.08);\n text-decoration: none;\n}\n```\n\nChange TO:\n```css\n.pagination-link:hover {\n border-color: var(--color-link);\n box-shadow: 0 2px 8px rgba(5, 112, 222, 0.12);\n text-decoration: none;\n transform: translateY(-1px);\n}\n```\n\n### 5. TOC link transitions\nUpdate the `.toc-link` transition (line ~315):\n\nChange FROM:\n```css\n transition: color 0.15s ease, border-color 0.15s ease;\n```\n\nChange TO:\n```css\n transition: color var(--transition-fast), border-color var(--transition-fast);\n```\n\n### 6. Callout subtle left border animation\nAdd a transition to callout on hover:\n\n```css\n.callout {\n transition: box-shadow var(--transition-normal);\n}\n\n.callout:hover {\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);\n}\n```\n\nAdd these rules after the existing `.callout` rule (line ~533-537). Do NOT modify the existing `.callout` rule — add the transition property to a NEW `.callout` rule block that will merge via cascade, OR add the `transition` property directly into the existing `.callout` block.\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Additionally:\n```bash\ngrep \"transition-fast\" documentation/styles/globals.css \u0026\u0026 echo \"Tokens OK\"\ngrep \"transition-normal\" documentation/styles/globals.css \u0026\u0026 echo \"Tokens OK\"\ngrep \"translateY\" documentation/styles/globals.css \u0026\u0026 echo \"Pagination lift OK\"\ngrep \"border-left.*solid.*color-link\" documentation/styles/globals.css \u0026\u0026 echo \"Active indicator OK\"\n```\nAll four checks must pass.\n\n## Dont\n- Do not add page transition animations (route changes) — that requires JavaScript\n- Do not modify any JavaScript files\n- Do not change colors or font sizes\n- Do not add animation keyframes — only use CSS transitions\n- Do not modify the mobile responsive section","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:25:54.998064+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:03:35.256599+13:00","closed_at":"2026-02-14T20:03:35.256599+13:00","close_reason":"Implemented all transition and hover state polish: added transition timing variables, updated sidebar/TOC/pagination transitions, added active indicator with border-left, and callout hover effects","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-dfq.6","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:25:54.998941+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-dfq.7","title":"Landing page card-style navigation links","description":"## Files\n- documentation/pages/index.md (modify)\n- documentation/components/CardLink.js (create)\n- documentation/markdoc/tags/card-link.markdoc.js (create)\n- documentation/pages/_app.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd card-style navigation links to the landing page, similar to Stripe docs landing pages that have categorized link cards with titles, descriptions, and hover effects.\n\n### 1. Create `documentation/components/CardLink.js`\n\nA card component for navigation links:\n\n```jsx\nimport Link from \"next/link\";\n\nexport function CardLink({ title, href, children }) {\n return (\n \u003cLink href={href} className=\"card-link\"\u003e\n \u003cspan className=\"card-link-title\"\u003e{title}\u003c/span\u003e\n {children \u0026\u0026 \u003cspan className=\"card-link-desc\"\u003e{children}\u003c/span\u003e}\n \u003cspan className=\"card-link-arrow\"\u003e→\u003c/span\u003e\n \u003c/Link\u003e\n );\n}\n```\n\n### 2. Create `documentation/markdoc/tags/card-link.markdoc.js`\n\nRegister the CardLink as a Markdoc tag:\n\n```js\nexport default {\n render: \"CardLink\",\n attributes: {\n title: { type: String, required: true },\n href: { type: String, required: true },\n },\n};\n```\n\n### 3. Modify `documentation/pages/_app.js`\n\nAdd import and register:\n\n```js\nimport { CardLink } from \"../components/CardLink\";\n```\n\nAdd to components object:\n```js\nconst components = {\n Callout,\n Columns,\n Column,\n Figure,\n Heading, // (if heading task is done, otherwise omit)\n CardLink,\n};\n```\n\n**IMPORTANT**: The components object may already contain `Heading` if task docs-dfq.2 was completed first. If `Heading` is already there, keep it. If not, do NOT add it — only add `CardLink`.\n\n### 4. Add CSS to `documentation/styles/globals.css`\n\nAdd a new section after the Figure component styles (around line ~597):\n\n```css\n/* ===== Component: CardLink ===== */\n.card-link {\n display: block;\n padding: var(--space-4) var(--space-5);\n border: 1px solid var(--color-border);\n border-radius: var(--radius-lg);\n text-decoration: none;\n transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;\n position: relative;\n margin-bottom: var(--space-3);\n}\n\n.card-link:hover {\n border-color: var(--color-link);\n box-shadow: 0 2px 8px rgba(5, 112, 222, 0.1);\n text-decoration: none;\n transform: translateY(-1px);\n}\n\n.card-link-title {\n display: block;\n font-size: 15px;\n font-weight: 600;\n color: var(--color-link);\n margin-bottom: var(--space-1);\n}\n\n.card-link-desc {\n display: block;\n font-size: 14px;\n color: var(--color-text-secondary);\n line-height: 1.5;\n}\n\n.card-link-arrow {\n position: absolute;\n right: var(--space-4);\n top: 50%;\n transform: translateY(-50%);\n color: var(--color-link);\n font-size: 16px;\n opacity: 0;\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\n\n.card-link:hover .card-link-arrow {\n opacity: 1;\n transform: translateY(-50%) translateX(2px);\n}\n```\n\n### 5. Modify `documentation/pages/index.md`\n\nAdd a \"Quick Start\" section with card links after the existing content. Append to the end of the file:\n\n```markdown\n\n## Quick Start\n\n{% card-link title=\"Why We're Building Hypercerts\" href=\"/getting-started/why-were-building-hypercerts\" %}\nUnderstand the motivation behind the hypercerts protocol\n{% /card-link %}\n\n{% card-link title=\"Introduction to Impact Claims\" href=\"/getting-started/introduction-to-impact-claims\" %}\nLearn about impact claims and how they work\n{% /card-link %}\n\n{% card-link title=\"The Hypercerts Infrastructure\" href=\"/getting-started/the-hypercerts-infrastructure\" %}\nExplore the technical architecture\n{% /card-link %}\n\n{% card-link title=\"Introduction to Lexicons\" href=\"/lexicons/introduction-to-lexicons\" %}\nUnderstand the data schemas that power hypercerts\n{% /card-link %}\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0. Additionally:\n```bash\ntest -f documentation/components/CardLink.js \u0026\u0026 echo \"Component OK\"\ntest -f documentation/markdoc/tags/card-link.markdoc.js \u0026\u0026 echo \"Tag OK\"\ngrep \"CardLink\" documentation/pages/_app.js \u0026\u0026 echo \"Registration OK\"\ngrep \"card-link\" documentation/styles/globals.css \u0026\u0026 echo \"CSS OK\"\ngrep \"card-link\" documentation/pages/index.md \u0026\u0026 echo \"Content OK\"\n```\nAll five checks must pass.\n\n## Dont\n- Do not modify existing content on the landing page — only append new content\n- Do not remove the existing columns/figures layout\n- Do not use any external icon libraries\n- Do not modify navigation.js\n- Do not add more than 4 card links","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:26:17.319286+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:07:08.03882+13:00","closed_at":"2026-02-14T20:07:08.03882+13:00","close_reason":"9969a9b Landing page card-style navigation links","labels":["scope:small"],"dependencies":[{"issue_id":"docs-dfq.7","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:26:17.320306+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-dfq.8","title":"Integration: verify all refinements build and render correctly","description":"## Files\n- (no files to create/modify — verification only)\n\n## What to do\nAfter all other tasks in Epic 10 are complete, verify the full integration:\n\n1. Run `cd documentation \u0026\u0026 npm run build` — must exit 0 with no errors\n2. Verify all 17 pages are in the static export\n3. Spot-check that no CSS conflicts exist between the various refinements\n4. Verify the landing page renders with card links\n5. Verify breadcrumbs appear on non-home pages\n\n## Test\n```bash\ncd documentation \u0026\u0026 npm run build 2\u003e\u00261 | grep -c \"●\" | xargs test 17 -eq\n```\nMust show exactly 17 SSG pages.\n\n```bash\ncd documentation \u0026\u0026 npm run build\n```\nMust exit 0.\n\n## Dont\n- Do not modify any files\n- This is a verification-only task","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T14:26:25.677169+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T20:10:10.53275+13:00","closed_at":"2026-02-14T20:10:10.53275+13:00","close_reason":"Closed","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq","type":"parent-child","created_at":"2026-02-14T14:26:25.678222+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.1","type":"blocks","created_at":"2026-02-14T14:26:31.650675+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.2","type":"blocks","created_at":"2026-02-14T14:26:31.749901+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.3","type":"blocks","created_at":"2026-02-14T14:26:31.849858+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.4","type":"blocks","created_at":"2026-02-14T14:26:31.950473+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.5","type":"blocks","created_at":"2026-02-14T14:26:32.047374+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.6","type":"blocks","created_at":"2026-02-14T14:26:32.141096+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-dfq.8","depends_on_id":"docs-dfq.7","type":"blocks","created_at":"2026-02-14T14:26:32.236529+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-hl0","title":"Fix: inline contributor fields described as bare strings, not objects (from docs-w96.7)","description":"Review of docs-w96.7 found: The 'Additional details' section describes the inline options for `contributorIdentity` and `contributionDetails` as bare strings, but the actual lexicon defines them as objects.\n\n**Evidence from lexicon (activity.json):**\n- `#contributorIdentity` is `type: object` with a required `identity` string field — NOT a bare string\n- `#contributorRole` is `type: object` with a required `role` string field — NOT a bare string\n\n**Inaccurate text in pages/core-concepts/hypercerts-core-data-model.md (line 29):**\n\u003e 'either an inline identity string (a DID)'\n\n**Inaccurate text (line 31):**\n\u003e 'either an inline role string'\n\n**Inaccurate text (line 33):**\n\u003e 'Simple cases use inline strings directly in the activity claim.'\n\n**Inaccurate text in tree (line 79):**\n\u003e 'contributorIdentity: Alice (inline DID or ref to ContributorInformation)'\n\nThe inline option is an inline OBJECT (e.g. `{'$type': 'org.hypercerts.claim.activity#contributorIdentity', identity: 'did:plc:...'}`), not a bare DID string. Describing it as a 'string' misrepresents the schema and will confuse developers implementing the spec.\n\n**Fix:** Change 'inline identity string (a DID)' to 'inline identity object (`#contributorIdentity`)' and 'inline role string' to 'inline role object (`#contributorRole`)'. Update line 33 and the tree comment accordingly.","status":"closed","priority":2,"issue_type":"bug","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","created_at":"2026-03-05T20:09:03.279465073+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:15:12.137296968+06:00","closed_at":"2026-03-05T20:15:12.137296968+06:00","close_reason":"ebedf20 Fix: inline contributor fields are objects, not bare strings","dependencies":[{"issue_id":"docs-hl0","depends_on_id":"docs-w96.7","type":"discovered-from","created_at":"2026-03-05T20:09:06.605350604+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-j3q","title":"Epic: Header \u0026 Accessibility","description":"Fix critical header UX issues: mobile search is invisible (\u003c769px), no top-level nav links, no skip-to-content link (WCAG 2.4.1), no visual group dividers, missing focus-visible styles, search pill lacks affordance. Polish: reduce height 64→56px, add GitHub icon. Covers UX findings #1, 2, 3, 8, 9, 10, 26, 27.","status":"closed","priority":1,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T19:52:34.66104+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:15:43.817985+08:00","closed_at":"2026-02-20T20:15:43.817985+08:00","close_reason":"c6d7f05 all header tasks complete","labels":["scope:medium"]} -{"id":"docs-j3q.1","title":"Mobile search icon + skip-to-content link","description":"## Files\n- components/Layout.js (modify)\n- styles/globals.css (modify)\n\n## What to do\n\n### Mobile search icon (finding #1)\nThe search pill is hidden below 769px with `display:none` and no fallback. Add a **search icon button** visible only on mobile (\u003c769px) that opens the SearchDialog.\n\nIn `Layout.js`, add a button AFTER the logo group and BEFORE `\u003cdiv style={{ flex: 1 }} /\u003e`:\n```jsx\n\u003cbutton\n className=\"search-icon-btn\"\n onClick={() =\u003e setSearchOpen(true)}\n type=\"button\"\n aria-label=\"Search\"\n\u003e\n \u003csvg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\"\u003e\n \u003cpath d=\"M12.01 12a4.25 4.25 0 1 0-6.02-6 4.25 4.25 0 0 0 6.02 6Zm0 0 3.24 3.25\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"1.5\" /\u003e\n \u003c/svg\u003e\n\u003c/button\u003e\n```\n\nCSS for `.search-icon-btn`:\n- `display: none` by default\n- Inside `@media (max-width: 768px)`: `display: flex`, same styling as `.hamburger-btn` (align-items center, justify-content center, background none, border none, cursor pointer, padding var(--space-1), color var(--color-text-primary), border-radius var(--radius-sm), min-width 44px, min-height 44px)\n- `:hover` → `background: var(--hover-bg)`\n- Place the mobile search icon button in the flex spacer area (after logo, before ThemeToggle)\n\n### Skip-to-content link (finding #3)\nAdd a visually-hidden skip link as the **very first child** inside `\u003cheader\u003e`, before `.layout-header-inner`:\n```jsx\n\u003ca href=\"#main-content\" className=\"skip-to-content\"\u003eSkip to content\u003c/a\u003e\n```\n\nAdd `id=\"main-content\"` to `\u003cmain className=\"layout-content\"\u003e`.\n\nCSS for `.skip-to-content`:\n- Position absolute, left -9999px (visually hidden)\n- On `:focus`: position static, display block, padding 8px 16px, background var(--color-accent), color white, text-decoration none, font-weight 600, font-size 14px, z-index 999, text-align center\n- Sits at the very top of the page when focused\n\n## Dont\n- Do NOT remove or modify the existing search pill behavior\n- Do NOT change the ⌘K keyboard shortcut\n- Do NOT remove the hamburger button","acceptance_criteria":"1. On mobile viewport (\u003c769px), a magnifying glass icon button is visible in the header\n2. Clicking the mobile search icon opens the SearchDialog modal\n3. The search pill remains visible on desktop (≥769px) and the mobile icon is hidden\n4. A \"Skip to content\" link is the first focusable element on the page\n5. Pressing Tab on page load focuses the skip link, pressing Enter scrolls to main content\n6. The skip link is visually hidden until focused\n7. Build passes: npm run build -- --webpack","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T19:52:55.462643+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.660671+08:00","closed_at":"2026-02-20T20:06:32.660671+08:00","close_reason":"c617130 mobile search icon + skip-to-content link","labels":["scope:small"],"dependencies":[{"issue_id":"docs-j3q.1","depends_on_id":"docs-j3q","type":"parent-child","created_at":"2026-02-20T19:52:55.463614+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-j3q.2","title":"Top-level nav links + group dividers + search pill affordance","description":"## Files\n- components/Layout.js (modify)\n- styles/globals.css (modify)\n\n## What to do\n\n### Top-level nav links (finding #2)\nAdd navigation links between the logo and the search pill. In `Layout.js`, after the logo `\u003cLink\u003e` and before the search pill `\u003cbutton\u003e`, add a `\u003cnav\u003e` with class `header-nav`:\n\n```jsx\n\u003cnav className=\"header-nav\" aria-label=\"Main navigation\"\u003e\n \u003cLink href=\"/getting-started/quickstart\" className=\"header-nav-link\"\u003eDocs\u003c/Link\u003e\n \u003cLink href=\"/tools/scaffold\" className=\"header-nav-link\"\u003eTools\u003c/Link\u003e\n \u003ca href=\"https://github.com/gainforest/hypercerts\" className=\"header-nav-link\" target=\"_blank\" rel=\"noopener noreferrer\"\u003eGitHub\u003c/a\u003e\n\u003c/nav\u003e\n```\n\nCSS for `.header-nav`:\n- `display: none` by default (hidden on mobile)\n- `@media (min-width: 769px)`: `display: flex; align-items: center; gap: var(--space-2);`\n- `.header-nav-link`: font-size 14px, font-weight 500, color var(--color-text-secondary), text-decoration none, padding 6px 12px, border-radius var(--radius-sm), transition background+color var(--transition-fast)\n- `.header-nav-link:hover`: background var(--hover-bg), color var(--color-text-primary), text-decoration none\n\n### Group dividers (finding #8)\nAdd thin vertical dividers between logo group, nav links, and search+toggle group. Use `\u003cspan className=\"header-divider\" aria-hidden=\"true\" /\u003e` elements in the header-inner, placed:\n1. After the logo `\u003cLink\u003e` (before nav)\n2. After the `\u003cnav\u003e` (before search pill)\n\nCSS for `.header-divider`:\n- `display: none` by default\n- `@media (min-width: 769px)`: display block, width 1px, height 20px, background var(--color-border), margin 0 var(--space-2)\n- Dark mode: `html.dark .header-divider { background: oklch(0.30 0.005 260); }`\n\n### Search pill affordance (finding #10)\nThe search pill blends into the glass header. Add a subtle shadow:\n- `.search-pill`: add `box-shadow: 0 1px 3px oklch(0 0 0 / 0.06);`\n- `.search-pill:hover`: `box-shadow: 0 1px 4px oklch(0 0 0 / 0.10);`\n- Dark mode: `html.dark .search-pill { box-shadow: 0 1px 3px oklch(0 0 0 / 0.2); }`\n\n## Dont\n- Do NOT add more than 3 nav links — keep it minimal\n- Do NOT hide the Docs badge on the logo\n- Do NOT change the flex:1 spacer behavior — nav links go between logo and spacer","acceptance_criteria":"1. On desktop (≥769px), \"Docs\", \"Tools\", \"GitHub\" links are visible between logo and search pill\n2. Nav links are hidden on mobile (\u003c769px)\n3. Thin vertical dividers (1px, 20px tall) separate logo | nav | search groups\n4. Dividers are hidden on mobile\n5. Search pill has a subtle box-shadow distinguishing it from the glass header\n6. Dark mode: dividers and search pill shadow adapt correctly\n7. GitHub link opens in new tab with rel=\"noopener noreferrer\"\n8. Build passes: npm run build -- --webpack","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":40,"created_at":"2026-02-20T19:53:13.289646+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:13:06.870324+08:00","closed_at":"2026-02-20T20:13:06.870324+08:00","close_reason":"94d701f nav links + dividers + search pill affordance","labels":["scope:small"],"dependencies":[{"issue_id":"docs-j3q.2","depends_on_id":"docs-j3q","type":"parent-child","created_at":"2026-02-20T19:53:13.290593+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-j3q.2","depends_on_id":"docs-j3q.1","type":"blocks","created_at":"2026-02-20T19:56:40.289382+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-j3q.3","title":"Focus-visible styles + header height + GitHub icon","description":"## Files\n- components/Layout.js (modify)\n- styles/globals.css (modify)\n\n## What to do\n\n### Focus-visible styles (finding #9)\nAdd `:focus-visible` styles to all interactive header elements that currently lack them:\n\n```css\n.hamburger-btn:focus-visible,\n.search-pill:focus-visible,\n.search-icon-btn:focus-visible,\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: var(--focus-ring);\n}\n```\n\nThe `--focus-ring` token already exists: `0 0 0 4px oklch(0.50 0.18 260 / 0.36)`. Just apply it.\n\nAlso add focus-visible to sidebar collapse button:\n```css\n.sidebar-collapse-btn:focus-visible,\n.sidebar-expand-btn:focus-visible {\n outline: none;\n box-shadow: var(--focus-ring);\n}\n```\n\n### Header height reduction (finding #26)\nChange `--header-height: 64px` to `--header-height: 56px` in `:root`.\n\nNote: the mobile breakpoint already sets `--header-height: 56px`, so this just makes desktop match. Remove the mobile override for header-height inside `@media (max-width: 768px)` since its now redundant.\n\n### GitHub icon in header (finding #27)\nAdd a GitHub icon link AFTER the ThemeToggle and BEFORE the closing `\u003c/div\u003e` of `.layout-header-inner`. Use a simple SVG icon button:\n\n```jsx\n\u003ca\n href=\"https://github.com/gainforest/hypercerts\"\n className=\"header-icon-link\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label=\"GitHub repository\"\n\u003e\n \u003csvg width=\"20\" height=\"20\" viewBox=\"0 0 16 16\" fill=\"currentColor\"\u003e\n \u003cpath d=\"M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z\" /\u003e\n \u003c/svg\u003e\n\u003c/a\u003e\n```\n\nCSS for `.header-icon-link`:\n- display flex, align-items center, justify-content center\n- width 36px, height 36px, border-radius var(--radius-md)\n- color var(--color-text-secondary), background none, text-decoration none\n- transition background+color var(--transition-fast)\n- `:hover` → background var(--hover-bg), color var(--color-text-primary)\n- `:focus-visible` → outline none, box-shadow var(--focus-ring)\n\nNOTE: If docs-j3q.2 already added a GitHub text link in the nav, this adds the icon version in the right group (near theme toggle). Both can coexist — the nav link is for desktop only, the icon is always visible. OR remove the text \"GitHub\" from the nav links added in docs-j3q.2, since this icon replaces it. Use your judgment — if both exist, remove the text one from nav to avoid duplication.\n\n## Dont\n- Do NOT change the logo size\n- Do NOT remove the Docs badge\n- Do NOT change any color tokens","acceptance_criteria":"1. Pressing Tab through header shows visible focus rings on hamburger, search pill, search icon, theme toggle, GitHub icon, sidebar buttons\n2. Focus ring uses the existing --focus-ring token (indigo 4px outline)\n3. Header height is 56px on all viewports (was 64px on desktop, already 56px on mobile)\n4. GitHub icon (octocat) is visible next to the theme toggle\n5. GitHub icon links to https://github.com/gainforest/hypercerts in new tab\n6. No duplicate GitHub links (if nav has text \"GitHub\" AND icon exists, remove the text one)\n7. Build passes: npm run build -- --webpack","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":40,"created_at":"2026-02-20T19:53:35.844215+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:15:43.769125+08:00","closed_at":"2026-02-20T20:15:43.769125+08:00","close_reason":"c6d7f05 focus-visible + header 56px + GitHub icon","labels":["scope:small"],"dependencies":[{"issue_id":"docs-j3q.3","depends_on_id":"docs-j3q","type":"parent-child","created_at":"2026-02-20T19:53:35.845103+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-j3q.3","depends_on_id":"docs-j3q.2","type":"blocks","created_at":"2026-02-20T19:56:40.356242+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-jss","title":"Epic: Add project-level README.md and DEVELOPMENT.md","description":"The documentation project lacks the standard project-level README.md and DEVELOPMENT.md files that our other repos (like hypercerts-scaffold-atproto) have. This epic adds both files following the same structure and style: comprehensive README with quick start, prerequisites, project structure, architecture; and DEVELOPMENT.md with local dev workflow, content authoring guide, Markdoc conventions, contributing, and troubleshooting. Success: a new contributor can clone the repo, run the docs site locally, and add a new page — all by reading README + DEVELOPMENT only.","status":"closed","priority":2,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T18:12:19.646036+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:14:51.247295+08:00","closed_at":"2026-02-20T18:14:51.247299+08:00","labels":["scope:medium"]} -{"id":"docs-jss.1","title":"Create README.md at project root","description":"## Files\n- README.md (create)\n\n## What to do\nCreate a comprehensive README.md at the repo root following the structure and style of hypercerts-scaffold-atproto/README.md. The file must include these sections in order:\n\n1. **Title \u0026 description** — \"Hypercerts Documentation\" heading. One paragraph: this is the official documentation site for the Hypercerts protocol, built with Next.js and Markdoc, deployed on Vercel.\n\n2. **Prerequisites** — Node.js 20+, npm (not pnpm — the project uses npm per vercel.json and package-lock.json).\n\n3. **Quick Start** — Clone, cd documentation/documentation, npm install, npm run dev. Note the nested directory structure (the actual Next.js app lives in documentation/documentation/).\n\n4. **Project Structure** — ASCII tree showing:\n```\n├── documentation/ # Next.js documentation site\n│ ├── components/ # React components (Layout, Sidebar, CodeBlock, etc.)\n│ ├── lib/ # Navigation config and helpers\n│ ├── markdoc/ # Markdoc tag and node definitions\n│ │ ├── nodes/ # Custom node renderers (fence, heading)\n│ │ └── tags/ # Custom tags (callout, card-link, columns, figure)\n│ ├── pages/ # Documentation content (.md files)\n│ │ ├── architecture/\n│ │ ├── core-concepts/\n│ │ ├── ecosystem/\n│ │ ├── getting-started/\n│ │ ├── lexicons/\n│ │ ├── reference/\n│ │ └── tools/\n│ ├── public/ # Static assets\n│ └── styles/ # CSS\n├── heartbeads-agents/ # (reserved)\n├── AGENTS.md # Agent workflow instructions\n└── .beads/ # Issue tracking\n```\n\n5. **Content Sections** — Table listing the 7 doc sections (Get Started, Core Concepts, Tools, Architecture, Reference/Lexicons, Ecosystem \u0026 Vision) with one-line descriptions, matching the style of the scaffold README tables.\n\n6. **Deployment** — State that the site is deployed on Vercel. The build command is `next build --webpack`, install command is `npm install`. Static export via `output: \"export\"` in next.config.js.\n\n7. **Learn More** — Links to: Hypercerts website (https://hypercerts.org), ATProto docs (https://atproto.com/docs), Markdoc docs (https://markdoc.dev), Next.js docs (https://nextjs.org/docs), DEVELOPMENT.md for contributor guide.\n\n## Don't\n- Do NOT mention pnpm — this project uses npm\n- Do NOT include any authentication, OAuth, or SDK content (this is a docs site, not an application)\n- Do NOT copy scaffold-specific sections (environment variables, Redis, JWK keys, etc.)\n- Do NOT add a License section (there is none currently)\n- Keep it under 150 lines","acceptance_criteria":"1. README.md exists at /Users/david/Projects/gainforest/documentation/README.md\n2. Contains all 7 sections listed in spec: title, prerequisites, quick start, project structure, content sections, deployment, learn more\n3. Quick Start instructions work: cd documentation/documentation \u0026\u0026 npm install \u0026\u0026 npm run dev starts the dev server\n4. References npm (not pnpm) throughout\n5. Project structure ASCII tree accurately reflects actual directory layout\n6. No scaffold-specific content (no Redis, OAuth, JWK, SDK references)\n7. File is under 150 lines\n8. All links in Learn More section are valid URLs","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":30,"created_at":"2026-02-20T18:12:47.807873+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:14:51.095793+08:00","closed_at":"2026-02-20T18:14:51.095815+08:00","labels":["scope:small"],"dependencies":[{"issue_id":"docs-jss.1","depends_on_id":"docs-jss","type":"parent-child","created_at":"2026-02-20T18:12:47.809207+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-jss.2","title":"Create DEVELOPMENT.md at project root","description":"## Files\n- DEVELOPMENT.md (create)\n\n## What to do\nCreate a comprehensive DEVELOPMENT.md at the repo root following the structure and style of hypercerts-scaffold-atproto/DEVELOPMENT.md. The file must include these sections in order:\n\n1. **Local Development Workflow** heading, then:\n - **Prerequisites** — Node.js 20+, npm, Git\n - **First-time Setup** — Step-by-step: clone, cd documentation/documentation, npm install, npm run dev, open http://localhost:3000. Note the nested directory structure.\n - **Daily Development** — Just `npm run dev` in the documentation/documentation/ directory.\n - **Build for production** — `npm run build` (produces static export in `out/` directory).\n\n2. **Adding Documentation Pages** — This is the content authoring guide:\n - Explain that pages are Markdoc files (.md) in `documentation/pages/`\n - File location determines URL path (e.g., `pages/getting-started/quickstart.md` → `/getting-started/quickstart`)\n - Every page needs YAML frontmatter with `title` (required) and `description` (optional)\n - Show a template:\n ```markdown\n ---\n title: Page Title\n description: One-line description.\n ---\n\n # Page Title\n\n Content goes here.\n ```\n - After creating a page, add it to the navigation in `documentation/lib/navigation.js`\n - Explain navigation.js structure briefly: top-level items have `title` + `path`, sections have `section` + `children` array, nesting is supported\n\n3. **Markdoc Custom Tags** — List available custom tags with usage examples:\n - `{% callout type=\"note|warning|caution\" %}...{% /callout %}` — callout boxes\n - `{% card-link title=\"...\" href=\"...\" %}...{% /card-link %}` — linked cards\n - `{% columns %}{% column %}...{% /column %}{% column %}...{% /column %}{% /columns %}` — multi-column layout\n - `{% figure src=\"...\" alt=\"...\" caption=\"...\" /%}` — images with captions\n\n4. **Project Architecture** — Brief explanation:\n - Next.js with Markdoc plugin for static site generation\n - `output: \"export\"` produces fully static HTML (no server needed)\n - `pageExtensions: [\"md\", \"mdoc\", \"js\", \"jsx\", \"ts\", \"tsx\"]` — Markdoc files are treated as pages\n - Components in `components/` provide the layout shell (Layout.js), sidebar navigation (Sidebar.js), syntax highlighting (CodeBlock.js), breadcrumbs, etc.\n\n5. **Troubleshooting** — Common issues:\n - \"Module not found\" → run `npm install`\n - Page not appearing in sidebar → check navigation.js\n - Markdoc syntax errors → check tag matching ({% tag %}...{% /tag %})\n - Build fails → ensure Node.js 20+\n\n6. **Contributing** — Follow the same structure as the scaffold:\n - Fork, branch, make changes, test with `npm run build`, open PR\n - Content PRs: new/updated .md files + navigation.js update\n - Component PRs: test visually with `npm run dev`\n\n## Don't\n- Do NOT mention pnpm — this project uses npm\n- Do NOT include SDK, OAuth, Redis, or application-specific content\n- Do NOT copy the \"Updating the Vendor SDK Package\" section from the scaffold\n- Do NOT invent Markdoc tags that do not exist in the markdoc/tags/ directory — only document callout, card-link, columns/column, and figure\n- Keep it under 250 lines","acceptance_criteria":"1. DEVELOPMENT.md exists at /Users/david/Projects/gainforest/documentation/DEVELOPMENT.md\n2. Contains all 6 sections: local dev workflow, adding pages, markdoc custom tags, project architecture, troubleshooting, contributing\n3. First-time setup instructions work: cd documentation/documentation \u0026\u0026 npm install \u0026\u0026 npm run dev\n4. Markdoc tags documented match exactly what exists in documentation/markdoc/tags/: callout, card-link, columns/column, figure (no extras, no missing)\n5. Navigation.js path is correctly referenced as documentation/lib/navigation.js\n6. References npm (not pnpm) throughout\n7. Frontmatter template shows title (required) and description (optional)\n8. File is under 250 lines\n9. No SDK, OAuth, Redis, or application-specific content","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T18:13:19.095143+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:14:51.173268+08:00","closed_at":"2026-02-20T18:14:51.173274+08:00","labels":["scope:small"],"dependencies":[{"issue_id":"docs-jss.2","depends_on_id":"docs-jss","type":"parent-child","created_at":"2026-02-20T18:13:19.096301+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-l7r","title":"Epic: Ecosystem, Community \u0026 Reference Materials","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:47:56.489643+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:08:20.711096+13:00","closed_at":"2026-02-15T01:08:20.711096+13:00","close_reason":"All children complete: l7r.1 (glossary + FAQ), l7r.2 (building on hypercerts)"} -{"id":"docs-l7r.1","title":"Write 'Glossary' and 'FAQ' reference pages","description":"## Context\n\nDocumentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. Site lives in `documentation/`. Pages are `.md` files in `documentation/pages/`. Nav in `documentation/lib/navigation.js`. Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe has inline glossary definitions throughout and extensive support articles. Hypercerts uses many domain-specific terms (DID, PDS, lexicon, work scope, strong reference, etc.) but never defines them in one place. This task creates two pages.\n\n## Files\n- documentation/pages/reference/glossary.md (create)\n- documentation/pages/reference/faq.md (create)\n- documentation/lib/navigation.js (modify — add entries under Reference section)\n\n## What to do\n\n### 1. Create the Glossary page\n\nCreate `documentation/pages/reference/glossary.md` (80–120 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Glossary\ndescription: Key terms and definitions used in the Hypercerts Protocol.\n---\n```\n\nDefine these terms (alphabetical order), each as `#### Term` followed by 1-3 sentence definition:\n\n- **Activity Claim** — The central record in the hypercerts data model. Describes who did what, when, and where. Lexicon: `org.hypercerts.claim.activity`.\n- **AT Protocol (ATProto)** — A decentralized social data protocol. The data layer for Hypercerts. Provides portable identity, shared schemas, and federation.\n- **CID** — Content Identifier. A cryptographic hash of a record's content, used in strong references to ensure tamper-evidence.\n- **Collection** — A group of hypercerts with a shared property, where each claim has a weight. Lexicon: `org.hypercerts.claim.collection`.\n- **Contribution** — A record describing a specific contributor's role in an activity claim. Lexicon: `org.hypercerts.claim.contribution`.\n- **DID (Decentralized Identifier)** — A persistent, cryptographically verifiable identifier for a person or organization. Format: `did:plc:...` or `did:web:...`.\n- **Evaluation** — A third-party assessment of a hypercert or other claim. Created on the evaluator's own PDS. Lexicon: `org.hypercerts.claim.evaluation`.\n- **Evidence** — A piece of supporting documentation attached to a hypercert. Can be a URI or uploaded blob. Lexicon: `org.hypercerts.claim.evidence`.\n- **Hypercert** — A structured digital record of a contribution: who did what, when, where, and with what evidence. The core primitive of the protocol.\n- **Indexer (App View)** — A service that reads from relays and builds queryable views of hypercert data across many PDS instances.\n- **Lexicon** — An ATProto schema definition that specifies the structure of a record type. Like a form template with required and optional fields.\n- **Measurement** — A quantitative observation attached to a claim. Lexicon: `org.hypercerts.claim.measurement`.\n- **PDS (Personal Data Server)** — A server that stores a user's ATProto data. Users can self-host or use a provider.\n- **Relay** — An ATProto service that aggregates data from many PDS instances and streams it to indexers.\n- **Rights** — A record describing what rights a holder has to a hypercert (display, transfer, etc.). Lexicon: `org.hypercerts.claim.rights`.\n- **SDS (Shared Data Server)** — Like a PDS but for organizations. Multiple users can write to the same repository.\n- **Strong Reference** — An ATProto reference that includes both the AT URI and CID of the target record, ensuring the reference is tamper-evident.\n- **Work Scope** — The \"what\" dimension of a hypercert, defined using logical operators (allOf, anyOf, noneOf) to precisely bound the work being claimed.\n- **XRPC** — The RPC protocol used by ATProto for client-server communication.\n\n### 2. Create the FAQ page\n\nCreate `documentation/pages/reference/faq.md` (80–120 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Frequently Asked Questions\ndescription: Common questions about the Hypercerts Protocol.\n---\n```\n\nAnswer these questions (use `##` for each question, 2-4 sentences per answer):\n\n1. **What is a hypercert?** — A structured digital record of a contribution. It captures who did what, when, where, and with what evidence.\n2. **How is this different from the previous (EVM-based) Hypercerts?** — The new protocol is built on AT Protocol instead of purely on-chain. This gives data portability, richer schemas, and lower costs, while still using blockchain for ownership and funding.\n3. **Do I need a blockchain wallet?** — Not to create or evaluate hypercerts. You need an ATProto account (DID). A wallet is only needed if you want to tokenize or fund hypercerts on-chain.\n4. **Is my data public?** — Yes. All ATProto records are public by default. Do not store sensitive personal information in hypercert records.\n5. **Can I delete a hypercert?** — You can delete records from your PDS. However, cached copies may persist in indexers and relays.\n6. **Who can evaluate my hypercert?** — Anyone with an ATProto account. Evaluations are separate records on the evaluator's PDS, linked via strong references.\n7. **How do I fund a hypercert?** — Funding happens on-chain through tokenized ownership. The specific mechanisms depend on the platform you use.\n8. **Can I use my Bluesky account?** — Yes. Bluesky accounts are ATProto accounts. Your existing DID works with Hypercerts.\n9. **What chains are supported?** — The protocol is chain-agnostic for the ownership layer. Specific chain support depends on the implementation.\n10. **How do I get help?** — Link to GitHub repository, community channels (use placeholder URLs with a callout).\n\n### 3. Add to navigation\n\nAdd under the Reference section in `documentation/lib/navigation.js`:\n```javascript\n{ title: 'Glossary', path: '/reference/glossary' },\n{ title: 'FAQ', path: '/reference/faq' },\n```\nIf the Reference section does not yet exist, create it after Architecture.\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0.\n\n## Dont\n- Do NOT add images or new components.\n- Do NOT modify existing pages other than navigation.js.\n- Do NOT use HTML tags.\n- Do NOT invent features or capabilities not described in the existing documentation.","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:54:01.702733+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:06:09.77014+13:00","closed_at":"2026-02-15T01:06:09.77014+13:00","close_reason":"Created Glossary and FAQ reference pages with proper navigation","dependencies":[{"issue_id":"docs-l7r.1","depends_on_id":"docs-l7r","type":"parent-child","created_at":"2026-02-14T20:54:01.704171+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":10,"issue_id":"docs-l7r.1","author":"Sharfy Adamantine","text":"NAV UPDATE: Add under 'Reference' section. Full nav order: Get Started → Core Concepts → Architecture → Lexicons → Tutorials → Reference.","created_at":"2026-02-14T11:25:02Z"},{"id":11,"issue_id":"docs-l7r.1","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — concise definitions, scannable. No preamble, no link dumps. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag). NAV PLACEMENT: Add under Reference section AFTER 'Tutorials'.","created_at":"2026-02-14T11:58:22Z"}]} -{"id":"docs-l7r.2","title":"Write 'Building on Hypercerts' ecosystem guide page","description":"## Context\n\nDocumentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. Site lives in `documentation/`. Pages are `.md` files in `documentation/pages/`. Nav in `documentation/lib/navigation.js`. Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe's equivalent: \"Stripe Apps\" and \"Partners\" pages — guides for third parties who want to build on the platform. The hypercerts \"Why\" page mentions that the protocol is designed for many platforms to build on, but there is no guide for platform builders.\n\n## Files\n- documentation/pages/reference/building-on-hypercerts.md (create)\n- documentation/lib/navigation.js (modify — add entry under Reference section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/reference/building-on-hypercerts.md` (100–150 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Building on Hypercerts\ndescription: A guide for platforms and tools that want to integrate the Hypercerts Protocol.\n---\n```\n\n**Sections:**\n\n1. **Who This Is For** — Platform builders, funding tool developers, evaluation services, dashboards, and anyone building applications that read or write hypercert data.\n\n2. **What You Can Build** — Categories of applications:\n - **Funding Platforms**: Crowdfunding, retroactive funding, milestone-based payouts using tokenized hypercerts\n - **Evaluation Tools**: Services that help domain experts create structured evaluations\n - **Dashboards \u0026 Explorers**: Interfaces that aggregate and display hypercerts across the ecosystem\n - **Impact Portfolios**: Tools that let funders track their portfolio of funded contributions\n - **Automated Agents**: AI systems that create measurements, flag inconsistencies, or assist evaluators\n\n3. **Integration Patterns** — How to integrate:\n - **Read-only**: Query an indexer to display hypercerts (simplest). No PDS needed.\n - **Write via user PDS**: Your app authenticates users via OAuth and writes records to their PDS on their behalf.\n - **Write via platform SDS**: Your platform runs its own Shared Data Server and creates records under its own DID.\n - For each pattern, provide 3-4 sentences explaining when to use it and a brief code sketch.\n\n4. **Running an Indexer** — Brief overview:\n - Subscribe to the relay firehose for hypercert lexicon records\n - Build a queryable database (PostgreSQL, etc.)\n - Expose an API for your application\n - Note: link to ATProto docs for relay subscription details\n\n5. **Interoperability Principles** — What makes the ecosystem work:\n - Use standard lexicons — do not create custom record types for data that fits existing schemas\n - Use strong references — always include CID for tamper-evidence\n - Respect data ownership — records belong to the DID that created them\n - Build for federation — do not assume all data lives on one PDS\n\n6. **Contributing to the Protocol** — How to propose changes:\n - Lexicon evolution process (propose → discuss → implement)\n - Link to GitHub repository (placeholder URL with callout)\n - How to contribute to the SDK\n\n### 2. Add to navigation\n\nAdd under the Reference section in `documentation/lib/navigation.js`:\n```javascript\n{ title: 'Building on Hypercerts', path: '/reference/building-on-hypercerts' },\n```\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0.\n\n## Dont\n- Do NOT add images or new components.\n- Do NOT modify existing pages other than navigation.js.\n- Do NOT use HTML tags.\n- Do NOT invent specific indexer APIs or relay endpoints.","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:54:24.391792+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:05:12.774851+13:00","closed_at":"2026-02-15T01:05:12.774851+13:00","close_reason":"Created Building on Hypercerts guide page with integration patterns, interoperability principles, and contribution guidelines. Build passes.","dependencies":[{"issue_id":"docs-l7r.2","depends_on_id":"docs-l7r","type":"parent-child","created_at":"2026-02-14T20:54:24.393036+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":12,"issue_id":"docs-l7r.2","author":"Sharfy Adamantine","text":"NAV UPDATE: Add under 'Reference' section. Full nav order: Get Started → Core Concepts → Architecture → Lexicons → Tutorials → Reference.","created_at":"2026-02-14T11:25:03Z"},{"id":13,"issue_id":"docs-l7r.2","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — one-sentence opener, capability bullet list, code-first, short paragraphs, no preamble, no link dumps. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag). NAV PLACEMENT: Add under Reference section AFTER 'Tutorials'.","created_at":"2026-02-14T11:58:23Z"}]} -{"id":"docs-m9c","title":"Fix: grammar 'a `org.hypercerts...`' should be 'an `org.hypercerts...`' (from docs-w96.7)","description":"Review of docs-w96.7 found: Two bullet points in the 'Additional details' section use the article 'a' before backtick-quoted NSIDs that start with the vowel 'o', which should be 'an'.\n\n**Evidence from pages/core-concepts/hypercerts-core-data-model.md:**\n\nLine 29:\n\u003e 'a strong reference to a `org.hypercerts.claim.contributorInformation` record'\n\nLine 31:\n\u003e 'a strong reference to a `org.hypercerts.claim.contribution` record'\n\nBoth NSIDs begin with 'org' (vowel sound), so the article should be 'an', not 'a'.\n\n**Fix:** Change both occurrences of 'a `org.hypercerts' to 'an `org.hypercerts'.","status":"closed","priority":3,"issue_type":"bug","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","created_at":"2026-03-05T20:09:12.809579261+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:14:53.439860314+06:00","closed_at":"2026-03-05T20:14:53.439860314+06:00","close_reason":"0c38475 Fix grammar: 'a `org.hypercerts`' → 'an `org.hypercerts`'","dependencies":[{"issue_id":"docs-m9c","depends_on_id":"docs-w96.7","type":"discovered-from","created_at":"2026-03-05T20:09:15.431783479+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-nuw","title":"Epic: Architecture \u0026 Infrastructure Deep Dives","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:47:44.339166+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:08:17.134655+13:00","closed_at":"2026-02-15T01:08:17.134655+13:00","close_reason":"All children complete: nuw.1 (architecture overview), nuw.2 (data flow \u0026 lifecycle)"} -{"id":"docs-nuw.1","title":"Write 'Architecture Overview' page with system diagram","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe's equivalent: \"How Stripe works\" — a high-level architecture page showing all the pieces and how they connect. The hypercerts docs currently describe architecture scattered across the \"Why\" page but have no dedicated architecture overview.\n\n## Files\n- documentation/pages/architecture/overview.md (create)\n- documentation/lib/navigation.js (modify — add new \"Architecture\" section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/architecture/overview.md` (150–220 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Architecture Overview\ndescription: How the Hypercerts Protocol stack fits together.\n---\n```\n\n**Sections:**\n\n1. **The Hypercerts Stack** — High-level overview (3-4 paragraphs). The protocol has three layers:\n - **Application Layer** — Platforms, dashboards, funding tools that users interact with\n - **Data Layer (AT Protocol)** — Where claims, evidence, evaluations live. Includes PDS/SDS, lexicons, indexers/relays.\n - **Ownership Layer (Blockchain)** — Where tokenized ownership, funding flows, and immutability guarantees live.\n \n Describe this as a text-based diagram using a Markdoc code block (no images needed):\n ```\n ┌─────────────────────────────────────────┐\n │ Application Layer │\n │ Funding platforms, dashboards, tools │\n ├─────────────────────────────────────────┤\n │ Data Layer (AT Protocol) │\n │ PDS/SDS → Relay → App View/Indexer │\n ├─────────────────────────────────────────┤\n │ Ownership Layer (Blockchain) │\n │ Tokenization, funding, rights │\n └─────────────────────────────────────────┘\n ```\n\n2. **Data Layer Deep Dive** — How ATProto components work together:\n - PDS (Personal Data Server): stores user's records\n - Relay (formerly BGS): aggregates data across PDS instances\n - App View / Indexer: reads from relay, builds queryable views\n - Lexicons: shared schemas that define record structure\n - Explain the flow: user writes to PDS → relay picks it up → indexer makes it searchable\n\n3. **Ownership Layer Deep Dive** — How blockchain components work:\n - Anchoring: linking ATProto records to on-chain state\n - Tokenization: representing hypercerts as transferable tokens\n - Funding mechanisms: how tokens enable various funding models\n - Multi-chain support: different chains for different communities\n\n4. **How the Layers Connect** — The bridge between ATProto and on-chain:\n - A hypercert's content lives on ATProto (the claim, evidence, evaluations)\n - Its ownership and funding state lives on-chain\n - References between layers (ATProto URI ↔ on-chain token ID)\n - Why this separation matters (data portability + financial guarantees)\n\n5. **Key Design Decisions** — Why the architecture is the way it is:\n - Why not fully on-chain? (cost, flexibility, data portability)\n - Why not fully off-chain? (ownership guarantees, funding mechanisms)\n - Why ATProto over IPFS/Ceramic/other? (identity, federation, existing ecosystem)\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add a new top-level section AFTER the \"Lexicons\" section and BEFORE \"Deep Dive: The Work Scope\":\n\n```javascript\n{\n section: 'Architecture',\n children: [\n { title: 'Architecture Overview', path: '/architecture/overview' },\n ],\n},\n```\n\n## Writing style\n- Technical but accessible — a developer should understand this without prior ATProto knowledge\n- Use `##` for main sections, `####` for subsections\n- Use text-based diagrams in fenced code blocks (no images)\n- Use `{% callout %}` for important design rationale\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT add image files — use text-based diagrams only.\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT create more than one page — just the overview.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:49:44.483676+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:01:28.941433+13:00","closed_at":"2026-02-15T01:01:28.941433+13:00","close_reason":"Created Architecture Overview page with system diagram, added to navigation after Tools section","dependencies":[{"issue_id":"docs-nuw.1","depends_on_id":"docs-nuw","type":"parent-child","created_at":"2026-02-14T20:49:44.485585+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":14,"issue_id":"docs-nuw.1","author":"Sharfy Adamantine","text":"NAV UPDATE: Navigation reorganized. Add the 'Architecture' section AFTER 'Core Concepts' and BEFORE 'Lexicons'. The new nav structure is: Get Started → Core Concepts → Architecture (new) → Lexicons.","created_at":"2026-02-14T11:24:41Z"},{"id":15,"issue_id":"docs-nuw.1","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — one-sentence opener, short paragraphs (1-3 sentences), no preamble, no link dumps. Use text-based diagrams in code blocks. Study existing pages: documentation/pages/getting-started/why-atproto.md. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag). NAV PLACEMENT: Add Architecture section AFTER 'Tools' and BEFORE 'Tutorials'. Current nav order: Get Started → Core Concepts → Lexicons → Tools → Tutorials.","created_at":"2026-02-14T11:58:10Z"}]} -{"id":"docs-nuw.2","title":"Write 'Data Flow \u0026 Lifecycle' page","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe's equivalent: \"Payment lifecycle\" — shows how a payment moves through states from creation to completion. This page does the same for a hypercert.\n\n## Files\n- documentation/pages/architecture/data-flow-and-lifecycle.md (create)\n- documentation/lib/navigation.js (modify — add entry under Architecture section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/architecture/data-flow-and-lifecycle.md` (120–180 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Data Flow \u0026 Lifecycle\ndescription: How a hypercert moves from creation through evaluation to funding.\n---\n```\n\n**Sections:**\n\n1. **The Lifecycle of a Hypercert** — Overview of the stages:\n - **Creation** — A contributor creates an activity claim on their PDS\n - **Enrichment** — Evidence, measurements, and contributions are attached\n - **Evaluation** — Third parties create evaluation records referencing the claim\n - **Discovery** — Indexers aggregate claims; platforms surface them to funders\n - **Funding** — Ownership is tokenized on-chain; funders acquire shares\n - **Accumulation** — More evaluations and evidence accrue over time\n\n2. **Stage 1: Creation** — What happens when a hypercert is created:\n - The contributor writes an `org.hypercerts.claim.activity` record to their PDS\n - The record gets a unique AT URI (e.g., `at://did:plc:abc123/org.hypercerts.claim.activity/tid`)\n - The PDS signs the record and includes it in the user's repository\n - The relay picks up the new record\n\n3. **Stage 2: Enrichment** — Attaching supporting data:\n - Contribution records (`org.hypercerts.claim.contribution`) are created and linked via strong references\n - Evidence records (`org.hypercerts.claim.evidence`) are attached\n - Measurement records (`org.hypercerts.claim.measurement`) provide quantitative data\n - Rights records (`org.hypercerts.claim.rights`) define what rights holders have\n - Location records (`app.certified.location`) anchor work geographically\n - These can live on the SAME PDS or DIFFERENT PDS instances\n\n4. **Stage 3: Evaluation** — Third-party assessment:\n - An evaluator creates an `org.hypercerts.claim.evaluation` record on THEIR OWN PDS\n - The evaluation references the original activity claim via a strong reference (`subject` field)\n - Multiple evaluators can independently evaluate the same claim\n - Evaluations accumulate over time — they are never \"final\"\n\n5. **Stage 4: Discovery \u0026 Indexing** — How data becomes findable:\n - Relays aggregate records from many PDS instances\n - Indexers (App Views) read from relays and build searchable databases\n - Platforms query indexers to surface hypercerts to users\n - Different indexers can build different views of the same data\n\n6. **Stage 5: Funding \u0026 Ownership** — The on-chain layer:\n - A hypercert can be anchored on-chain, creating a token\n - The token represents ownership shares of the contribution\n - Funders acquire shares through various mechanisms (direct purchase, retroactive funding, etc.)\n - On-chain state references the ATProto URI of the original claim\n\n7. **Cross-PDS References** — How records on different servers reference each other:\n - Strong references include both URI and CID (content hash)\n - This ensures references are tamper-evident\n - A record on PDS-A can reference a record on PDS-B\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add this entry under the Architecture section (which should already exist from the Architecture Overview task):\n\n```javascript\n{ title: 'Data Flow \u0026 Lifecycle', path: '/architecture/data-flow-and-lifecycle' },\n```\n\nIf the Architecture section does not yet exist, create it after the Lexicons section.\n\n## Writing style\n- Narrative, walkthrough style — follow a single hypercert through its lifecycle\n- Use `##` for main sections, `####` for subsections\n- Use text-based flow diagrams in fenced code blocks where helpful\n- Reference specific lexicon IDs (e.g., `org.hypercerts.claim.activity`) and link to their documentation pages\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT add image files.\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT duplicate content from the Architecture Overview page — reference it instead.","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:50:08.992973+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:04:48.551227+13:00","closed_at":"2026-02-15T01:04:48.551227+13:00","close_reason":"Completed: Created Data Flow \u0026 Lifecycle page with 6-stage narrative walkthrough, added navigation entry, build passes","dependencies":[{"issue_id":"docs-nuw.2","depends_on_id":"docs-nuw","type":"parent-child","created_at":"2026-02-14T20:50:08.994666+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-nuw.2","depends_on_id":"docs-nuw.1","type":"blocks","created_at":"2026-02-14T20:54:30.68945+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":16,"issue_id":"docs-nuw.2","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — one-sentence opener, short paragraphs, no preamble, no link dumps. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag).","created_at":"2026-02-14T11:58:17Z"}]} -{"id":"docs-qsc","title":"Epic 3: Content Migration - Convert All 17 Markdown Files","description":"## Summary\nConvert all 17 GitBook Markdown files to Markdoc-compatible format and move them into the Next.js `pages/` directory. This involves replacing GitBook-proprietary syntax with Markdoc tags, cleaning up HTML artifacts, fixing frontmatter, and restructuring file paths for Next.js routing.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory at the repo root (`/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`).\n\nThe current GitBook content files are at the `documentation/` root level (e.g., `documentation/README.md`, `documentation/getting-started/why-were-building-hypercerts.md`). They need to be moved into `documentation/pages/` for Next.js file-based routing.\n\nBy the time this epic runs:\n- Epic 1 (Scaffold) has created the `pages/`, `components/`, `markdoc/`, etc. directories\n- Epic 2 (Custom Tags) has created Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`\n- A basic `pages/_app.js` and `pages/index.md` (test page) exist\n\n### Key routing rules:\n- `pages/index.md` -\u003e renders at `/`\n- `pages/getting-started/why-were-building-hypercerts.md` -\u003e renders at `/getting-started/why-were-building-hypercerts`\n- `README.md` files must be renamed to `index.md` (Next.js convention for directory index pages)\n- Internal links should NOT include `.md` extensions\n\n## Content Files to Migrate (17 total)\n\n### File mapping (source -\u003e destination):\n```\ndocumentation/README.md -\u003e documentation/pages/index.md\ndocumentation/deep-dive-the-work-scope.md -\u003e documentation/pages/deep-dive-the-work-scope.md\ndocumentation/getting-started/why-were-building-hypercerts.md -\u003e documentation/pages/getting-started/why-were-building-hypercerts.md\ndocumentation/getting-started/introduction-to-impact-claims.md -\u003e documentation/pages/getting-started/introduction-to-impact-claims.md\ndocumentation/getting-started/the-impact-and-work-space.md -\u003e documentation/pages/getting-started/the-impact-and-work-space.md\ndocumentation/getting-started/the-hypercerts-infrastructure.md -\u003e documentation/pages/getting-started/the-hypercerts-infrastructure.md\ndocumentation/getting-started/installing-the-sdk.md -\u003e documentation/pages/getting-started/installing-the-sdk.md\ndocumentation/lexicons/introduction-to-lexicons.md -\u003e documentation/pages/lexicons/introduction-to-lexicons.md\ndocumentation/lexicons/general-lexicons/README.md -\u003e documentation/pages/lexicons/general-lexicons/index.md\ndocumentation/lexicons/general-lexicons/shared-defs.md -\u003e documentation/pages/lexicons/general-lexicons/shared-defs.md\ndocumentation/lexicons/general-lexicons/location.md -\u003e documentation/pages/lexicons/general-lexicons/location.md\ndocumentation/lexicons/hypercerts-lexicons/README.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/index.md\ndocumentation/lexicons/hypercerts-lexicons/activity-claim.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/activity-claim.md\ndocumentation/lexicons/hypercerts-lexicons/contribution.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/contribution.md\ndocumentation/lexicons/hypercerts-lexicons/evaluation.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/evaluation.md\ndocumentation/lexicons/hypercerts-lexicons/measurement.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/measurement.md\ndocumentation/lexicons/hypercerts-lexicons/attachment.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/attachment.md\ndocumentation/lexicons/hypercerts-lexicons/rights.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/rights.md\ndocumentation/lexicons/hypercerts-lexicons/collection.md -\u003e documentation/pages/lexicons/hypercerts-lexicons/collection.md\n```\n\n## Global Transformations (apply to ALL files)\n\n1. **Remove all `\u0026#x20;` HTML entities** (trailing space artifacts from GitBook) -- found in 6 files, ~9 instances\n2. **Remove all zero-width space characters** (`\\u200b`) from table cells -- found in activity-claim.md, rights.md\n3. **Remove all GitBook anchor tags from headings**: `\u003ca href=\"#...\" id=\"...\"\u003e\u003c/a\u003e` -- found in why-were-building-hypercerts.md (15 instances), activity-claim.md (3 instances)\n4. **Remove trailing `\u003cbr\u003e` tags** at end of files -- found in 6 files\n5. **Convert GitBook layout frontmatter** to simple `title`/`description` frontmatter. The GitBook frontmatter looks like:\n```yaml\nlayout:\n width: default\n title:\n visible: true\n ...\nmetaLinks:\n alternates:\n - https://app.gitbook.com/s/...\n```\nReplace with:\n```yaml\ntitle: Page Title Here\ndescription: Optional description\n```\n6. **Rename README.md -\u003e index.md** (3 files: root, general-lexicons, hypercerts-lexicons)\n7. **Update all internal links** to use Next.js-compatible paths (remove `.md` extensions, use absolute paths from root)\n8. **Add frontmatter** to every file that lacks it (at minimum: `title`)\n\n## File-Specific Transformations\n\n### README.md -\u003e pages/index.md\n- Replace the entire GitBook layout frontmatter block with: `title: Welcome to the Hypercerts Protocol`\n- Convert `{% columns %}`/`{% column %}`/`{% endcolumn %}`/`{% endcolumns %}` to Markdoc syntax: `{% columns %}`/`{% column %}`/`{% /column %}`/`{% /columns %}`\n- Convert 2 `\u003cfigure\u003e\u003cimg src=\".gitbook/assets/...\"\u003e` tags to `{% figure src=\"/images/...\" /%}`:\n - `.gitbook/assets/hypercerts_for_projects.png` -\u003e `/images/hypercerts_for_projects.png`\n - `.gitbook/assets/hypercert erd.png` -\u003e `/images/hypercert-erd.png`\n- Remove or fix broken link `/broken/pages/gNQzZ9R1b3NKrJoQ0Iqa` (read surrounding text to determine intent; if undeterminable, remove the link and keep the text)\n- Remove trailing `\u003cbr\u003e`\n\n### deep-dive-the-work-scope.md -\u003e pages/deep-dive-the-work-scope.md\n- Add frontmatter: `title: \"Deep Dive: The Work Scope\"`\n- Convert `{% hint style=\"info\" %}...{% endhint %}` to `{% callout type=\"info\" %}...{% /callout %}`\n\n### getting-started/why-were-building-hypercerts.md\n- Keep existing `description` frontmatter, add `title: \"Why We're Building Hypercerts\"`\n- Remove all 15 anchor `\u003ca href=\"#...\" id=\"...\"\u003e\u003c/a\u003e` tags from headings\n- Remove trailing `\u003cbr\u003e`\n\n### getting-started/introduction-to-impact-claims.md\n- Replace `icon: bolt` frontmatter with `title: Introduction to Impact Claims`\n- Remove `\u0026#x20;` entities (3 instances)\n- Convert `\u003cfigure\u003e\u003cimg src=\"../.gitbook/assets/hypercert erd.png\"\u003e` to `{% figure src=\"/images/hypercert-erd.png\" alt=\"Hypercert ERD\" /%}`\n\n### getting-started/the-impact-and-work-space.md\n- Add frontmatter: `title: The Impact and Work Space`\n- Remove `\u0026#x20;` entity\n- Fix broken self-referencing anchor link `the-impact-and-work-space.md#the-impact-space` -- the heading `#the-impact-space` does not exist. Check what heading was intended and fix.\n\n### getting-started/the-hypercerts-infrastructure.md\n- Add frontmatter: `title: The Hypercerts Infrastructure`\n- Content is just \"TBD\" -- leave as-is\n\n### getting-started/installing-the-sdk.md\n- Add frontmatter: `title: Installing the SDK`\n- Remove `\u003cbr\u003e`. Content is empty -- leave as-is\n\n### lexicons/introduction-to-lexicons.md\n- Add frontmatter: `title: Introduction to Lexicons`\n- Remove `\u0026#x20;`\n- Fix 2 broken links: `/broken/pages/3UwgsMpD20ErXurhzmTh` and `/broken/pages/FdVczwFhTBj7n5nszh0P` -- read surrounding text to determine intent. If undeterminable, remove the links and keep the text.\n\n### lexicons/general-lexicons/README.md -\u003e pages/lexicons/general-lexicons/index.md\n- Add frontmatter: `title: General Lexicons`\n\n### lexicons/general-lexicons/shared-defs.md\n- Add frontmatter: `title: Shared Definitions`\n- Remove `\u0026#x20;`\n\n### lexicons/general-lexicons/location.md\n- Add frontmatter: `title: Location`\n- Remove `\u0026#x20;` (3 instances)\n- Fix inline HTML in table cell: `\u003cp\u003e\u003ccode\u003eunion\u003c/code\u003e\u003cbr\u003e...\u003c/p\u003e` -\u003e clean Markdown equivalent\n- Fix broken anchor link `location.md#locationtype` -\u003e `#location-type` (the heading is \"### Location Type\" which generates anchor `#location-type`)\n\n### lexicons/hypercerts-lexicons/README.md -\u003e pages/lexicons/hypercerts-lexicons/index.md\n- Add frontmatter: `title: Hypercerts Lexicons`\n\n### lexicons/hypercerts-lexicons/activity-claim.md\n- Add frontmatter: `title: Activity Claim`\n- Remove 3 anchor `\u003ca\u003e` tags from headings\n- Fix malformed table: the table is missing a proper header row. The first data row currently acts as the header. Add a real header row with columns: Property, Type, Required, Description, Comments\n- Remove zero-width space characters (`\\u200b`) from table cells\n- Replace broken GitBook editor URL `https://app.gitbook.com/o/uxU8o4s6mrp88yBisLn5/s/8dkOAon1uQbyqL8RomaJ/hypercert-claim-lexicons/the-impact-and-work-space` with relative link `/getting-started/the-impact-and-work-space`\n- Remove trailing `\u003cbr\u003e`\n\n### lexicons/hypercerts-lexicons/contribution.md\n- Add frontmatter: `title: Contribution`\n- Remove trailing `\u003cbr\u003e`\n\n### lexicons/hypercerts-lexicons/evaluation.md\n- Add frontmatter: `title: Evaluation`\n- Remove trailing `\u003cbr\u003e`\n\n### lexicons/hypercerts-lexicons/measurement.md\n- Add frontmatter: `title: Measurement`\n- Remove `\u0026#x20;`\n\n### lexicons/hypercerts-lexicons/attachment.md\n- Add frontmatter: `title: Evidence`\n- No special syntax changes needed\n\n### lexicons/hypercerts-lexicons/rights.md\n- Add frontmatter: `title: Rights`\n- Fix malformed table: add proper header row (same issue as activity-claim.md)\n- Remove zero-width space characters from table cells\n\n### lexicons/hypercerts-lexicons/collection.md\n- Add frontmatter: `title: Collection`\n- No special syntax changes needed\n\n## Acceptance Criteria\n- All 17 files are in `documentation/pages/` directory with correct paths\n- README.md files renamed to index.md (3 files)\n- No GitBook-specific syntax remains (`{% hint %}`, `{% endcolumns %}`, `{% endcolumn %}`, `{% endhint %}`, etc.)\n- No `\u0026#x20;` entities, no zero-width spaces, no orphan `\u003ca\u003e` anchor tags, no trailing `\u003cbr\u003e`\n- All files have proper YAML frontmatter with at minimum a `title` field\n- All broken links either resolved to correct targets or removed with text preserved\n- Malformed tables in activity-claim.md and rights.md have proper header rows\n- All image paths updated from `.gitbook/assets/` to `/images/`\n- All internal links use paths without `.md` extensions\n- All files render correctly via `npm run dev`\n","status":"closed","priority":0,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:42:41.246087+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:13:55.289826+13:00","closed_at":"2026-02-14T13:13:55.289831+13:00","dependencies":[{"issue_id":"docs-qsc","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:42:41.247806+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-qsc","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:42:41.24883+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-r1z","title":"Epic 9: Cleanup and Verification","description":"## Summary\nFinal cleanup pass to remove all GitBook artifacts, verify link integrity, perform visual QA against the Stripe-inspired design language, and ensure the project is clean and production-ready.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory inside the repository at `/Users/sharfy/Code/hypercerts-atproto-documentation/documentation/`.\n\nBy the time this epic runs, Epics 1-8 have completed:\n- Next.js + Markdoc project scaffolded (Epic 1)\n- Custom Markdoc tags created: callout, columns, column, figure (Epic 2)\n- All 17 content files migrated to `pages/` with cleaned syntax (Epic 3)\n- Three-column layout with sidebar, content, and right TOC built (Epic 4)\n- Stripe-inspired CSS styling applied (Epic 5)\n- Images moved to `public/images/` and `.gitbook/` deleted (Epic 6)\n- All broken links fixed (Epic 7)\n- Build verified and deployment configured (Epic 8)\n\nThis epic is the final quality gate before the migration is considered complete.\n\n## Tasks\n\n### 1. Remove GitBook Artifacts\n\nDelete all files and directories that are GitBook-specific and no longer needed:\n\n```bash\n# Verify .gitbook/ is already deleted (should be gone from Epic 6)\nls -la documentation/.gitbook 2\u003e\u00261 # Should say \"No such file or directory\"\n\n# Delete SUMMARY.md (replaced by programmatic navigation in Epic 4)\nrm -f documentation/SUMMARY.md\n\n# Delete the original GitBook content files that were copied to pages/\n# These are the OLD files at the documentation/ root level (NOT the ones in pages/)\nrm -f documentation/README.md\nrm -f documentation/deep-dive-the-work-scope.md\nrm -rf documentation/getting-started/\nrm -rf documentation/lexicons/\n```\n\n**Important:** Only delete the old root-level files. Do NOT delete anything inside `documentation/pages/`, `documentation/components/`, `documentation/markdoc/`, etc.\n\nAlso check for and remove any remaining GitBook-specific frontmatter in content files. GitBook frontmatter looks like:\n```yaml\nlayout:\n width: default\n title:\n visible: true\nmetaLinks:\n alternates:\n - https://app.gitbook.com/s/...\n```\nIf any of this remains in any file under `pages/`, strip it and leave only `title` and `description` fields.\n\n### 2. Run Artifact Grep Checks\n\nSearch all content files for leftover GitBook artifacts. Every one of these commands should return ZERO results:\n\n```bash\n# GitBook broken page links\ngrep -r '/broken/pages/' documentation/pages/\n\n# GitBook editor URLs\ngrep -r 'app.gitbook.com' documentation/pages/\n\n# GitBook asset paths\ngrep -r '.gitbook/' documentation/pages/\ngrep -r '.gitbook/' documentation/components/\n\n# HTML entity artifacts\ngrep -r '\u0026#x20;' documentation/pages/\n\n# Zero-width spaces (use hex search)\ngrep -rP '\\x{200b}' documentation/pages/ 2\u003e/dev/null || grep -r $'\\u200b' documentation/pages/\n\n# GitBook proprietary tag syntax (should all be converted to Markdoc)\ngrep -r '{% hint' documentation/pages/\ngrep -r '{% endhint' documentation/pages/\ngrep -r '{% endcolumn' documentation/pages/\ngrep -r '{% endcolumns' documentation/pages/\n\n# Orphan HTML tags\ngrep -r '\u003cbr\u003e$' documentation/pages/\ngrep -r '\u003ca href=\"#' documentation/pages/\n```\n\nFix any remaining issues found.\n\n### 3. Verify All Internal Links\n\nStart the dev server and manually navigate to every page via the sidebar:\n\n```bash\ncd documentation \u0026\u0026 npm run dev\n```\n\nPages to check (17 total):\n1. `/` (Welcome / landing page)\n2. `/getting-started/why-were-building-hypercerts`\n3. `/getting-started/introduction-to-impact-claims`\n4. `/getting-started/the-impact-and-work-space`\n5. `/getting-started/the-hypercerts-infrastructure`\n6. `/getting-started/installing-the-sdk`\n7. `/lexicons/introduction-to-lexicons`\n8. `/lexicons/general-lexicons`\n9. `/lexicons/general-lexicons/shared-defs`\n10. `/lexicons/general-lexicons/location`\n11. `/lexicons/hypercerts-lexicons`\n12. `/lexicons/hypercerts-lexicons/activity-claim`\n13. `/lexicons/hypercerts-lexicons/contribution`\n14. `/lexicons/hypercerts-lexicons/evaluation`\n15. `/lexicons/hypercerts-lexicons/measurement`\n16. `/lexicons/hypercerts-lexicons/attachment`\n17. `/lexicons/hypercerts-lexicons/rights`\n18. `/lexicons/hypercerts-lexicons/collection`\n19. `/deep-dive-the-work-scope`\n\nFor each page verify:\n- Page loads without errors (check browser console)\n- No 404s for any internal links on the page\n- Anchor links (e.g., `#location-type`) scroll to the correct heading\n\n### 4. Visual QA Against Stripe Design Language\n\nFor each page, verify the design matches the Stripe-inspired patterns:\n\n**Layout:**\n- [ ] Three-column layout on desktop: left sidebar (~240px), content (~720px max), right TOC (~200px)\n- [ ] Sidebar has subtle 1px right border (#ebeef1), not a heavy shadow\n- [ ] Content area is well-centered with comfortable padding\n- [ ] Header is minimal with site title and 1px bottom border\n\n**Typography:**\n- [ ] Body text is 16px, dark gray (#414552), NOT pure black\n- [ ] Headings use proper hierarchy (H1 largest, H2 with large top margin creating section breaks)\n- [ ] System font stack is used (no custom font loading delays)\n- [ ] Text is anti-aliased (`-webkit-font-smoothing: antialiased`)\n- [ ] Line height is comfortable (~1.65 for body)\n\n**Colors:**\n- [ ] Page background is pure white\n- [ ] Links are blue (#0570de), darken on hover\n- [ ] The page is overwhelmingly neutral/grayscale -- color appears only for links, callouts, and interactive elements\n- [ ] No decorative elements, no gradients, no heavy shadows\n\n**Components:**\n- [ ] Tables are clean: no outer borders, header has bottom border only, no zebra striping\n- [ ] Code blocks have light gray background (#f6f8fa), rounded corners, monospace font\n- [ ] Inline code has subtle gray background pill\n- [ ] Callout boxes (on deep-dive page) have colored left border and tinted background\n- [ ] Column layouts (on landing page) display side-by-side on desktop, stack on mobile\n- [ ] Images render correctly with proper sizing\n\n**Navigation:**\n- [ ] Sidebar section headers are small, semibold, secondary gray\n- [ ] Active page is highlighted in sidebar (bold text + subtle background)\n- [ ] Right TOC shows H2 headings and highlights current section on scroll\n- [ ] Prev/next pagination works at bottom of content area\n- [ ] On mobile (\u003c 768px): sidebar collapses, content goes full width\n\n### 5. Verify Heading Anchor IDs\n\nMarkdoc auto-generates heading IDs from heading text. Verify that the generation is consistent:\n- \"### Location Type\" should generate `#location-type` (lowercase, hyphenated)\n- Any in-page anchor links should match the generated IDs\n- Click each anchor link on pages that have them to confirm they work\n\n### 6. Final File Inventory\n\nVerify the project structure is clean:\n```\ndocumentation/\n├── components/\n│ ├── Callout.js\n│ ├── Column.js\n│ ├── Columns.js\n│ ├── Figure.js\n│ ├── Layout.js\n│ ├── Sidebar.js\n│ └── TableOfContents.js\n├── lib/\n│ └── navigation.js\n├── markdoc/\n│ ├── tags/\n│ │ ├── callout.markdoc.js\n│ │ ├── column.markdoc.js\n│ │ ├── columns.markdoc.js\n│ │ ├── figure.markdoc.js\n│ │ └── index.js\n│ └── nodes/ (may be empty or have heading overrides)\n├── pages/\n│ ├── _app.js\n│ ├── index.md\n│ ├── deep-dive-the-work-scope.md\n│ ├── getting-started/\n│ │ ├── why-were-building-hypercerts.md\n│ │ ├── introduction-to-impact-claims.md\n│ │ ├── the-impact-and-work-space.md\n│ │ ├── the-hypercerts-infrastructure.md\n│ │ └── installing-the-sdk.md\n│ └── lexicons/\n│ ├── introduction-to-lexicons.md\n│ ├── general-lexicons/\n│ │ ├── index.md\n│ │ ├── shared-defs.md\n│ │ └── location.md\n│ └── hypercerts-lexicons/\n│ ├── index.md\n│ ├── activity-claim.md\n│ ├── contribution.md\n│ ├── evaluation.md\n│ ├── measurement.md\n│ ├── attachment.md\n│ ├── rights.md\n│ └── collection.md\n├── public/\n│ └── images/\n│ ├── hypercerts_for_projects.png\n│ └── hypercert-erd.png\n├── styles/\n│ └── globals.css\n├── next.config.js\n├── package.json\n├── package-lock.json\n└── .gitignore\n```\n\nConfirm:\n- No `.gitbook/` directory\n- No `SUMMARY.md`\n- No root-level `.md` content files (only inside `pages/`)\n- No orphaned files outside the standard Next.js structure\n\n### 7. Final Build Check\n```bash\ncd documentation\nnpm run build\n```\nMust succeed with zero errors after all cleanup.\n\n## Acceptance Criteria\n- Zero grep hits for `/broken/pages/`, `app.gitbook.com`, `.gitbook/`, `\u0026#x20;`, zero-width spaces, `{% hint`, `{% endhint`, `{% endcolumn`, `{% endcolumns` in content files\n- `SUMMARY.md` is deleted\n- `.gitbook/` directory is deleted\n- Old root-level content files (README.md, getting-started/, lexicons/, deep-dive-the-work-scope.md) are deleted\n- All 17+ pages render correctly in the browser with no console errors\n- All internal links resolve correctly (no 404s)\n- All heading anchor links work\n- The visual design matches the Stripe-inspired patterns (white bg, gray text, blue links, clean tables, colored callouts, three-column layout)\n- The project directory structure is clean with no orphaned files\n- `npm run build` succeeds with zero errors\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:46:01.998664+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:19:59.577445+13:00","closed_at":"2026-02-14T13:19:59.577453+13:00","dependencies":[{"issue_id":"docs-r1z","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:46:02.000863+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:46:02.002015+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-qsc","type":"blocks","created_at":"2026-02-13T13:46:02.002729+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-7dv","type":"blocks","created_at":"2026-02-13T13:46:02.003372+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-yne","type":"blocks","created_at":"2026-02-13T13:46:02.004004+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-cij","type":"blocks","created_at":"2026-02-13T13:46:02.004627+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-7b4","type":"blocks","created_at":"2026-02-13T13:46:02.00526+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-r1z","depends_on_id":"docs-d4r","type":"blocks","created_at":"2026-02-13T13:46:02.005893+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-rsa","title":"Epic: Rewrite scaffold starter app documentation","description":"Rewrite the scaffold docs page (documentation/pages/tools/scaffold.md) to be genuinely useful for developers. Current docs are too shallow — missing architecture, OAuth flow, tech stack, and have placeholder screenshots. New structure: Tech Stack → Screenshots → Env Vars → Local Setup → Architecture (OAuth, server-side data boundary, Constellation backlinks) → Comprehensive Project Structure → Feature Walkthrough. Remove code snippets (too shallow, will break with SDK changes) and repo context/DID resolution docs (being removed from codebase). Success: a developer can clone the scaffold, understand the architecture, and start building without reading source code.","status":"closed","priority":1,"issue_type":"epic","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","created_at":"2026-02-17T12:00:47.006934865+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:15:39.14809733+06:00","closed_at":"2026-02-17T12:15:39.14809733+06:00","close_reason":"7267eca All 6 child tasks completed — scaffold docs rewritten","labels":["scope:medium"]} -{"id":"docs-rsa.1","title":"Add tech stack table to scaffold docs","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nAdd a \"Tech Stack\" section immediately after the intro paragraph (after the live/source links, before everything else). Format as a table with two columns: Category and Technology.\n\nCategories and values:\n| Category | Technology |\n|----------|-----------|\n| Framework | Next.js 16 (App Router), React 19, TypeScript |\n| Styling | Tailwind CSS 4, shadcn/ui (Radix primitives) |\n| State Management | TanStack React Query v5 |\n| Auth / Protocol | AT Protocol OAuth, `@atproto/oauth-client-node` |\n| Hypercerts SDK | `@hypercerts-org/sdk-core` (pre-release) |\n| Infrastructure | Redis (session + OAuth state storage) |\n| Fonts | Geist, Geist Mono, Syne, Outfit (via next/font) |\n| Icons | Lucide React |\n\n## Dont\n- Add version numbers that will go stale quickly (except where meaningful like \"Next.js 16\" or \"React 19\")\n- Add dev dependencies or build tools\n- Change any other section of the doc","acceptance_criteria":"1. Tech Stack section exists immediately after the intro paragraph\n2. Table has Category and Technology columns\n3. All 8 categories listed above are present\n4. No other sections of the doc are modified","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":30,"created_at":"2026-02-17T12:01:01.278350786+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:06:33.685300718+06:00","closed_at":"2026-02-17T12:06:33.685300718+06:00","close_reason":"43cfd75 Add tech stack table to scaffold docs","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.1","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:01:01.280099149+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-rsa.2","title":"Reorder sections: move env vars and local setup above architecture","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nRestructure the document so sections appear in this order:\n\n1. H1 title + intro paragraph (with live/source links) — keep as-is\n2. ## Tech Stack — (added by docs-rsa.1)\n3. ## What the app does — keep existing content with figure tags\n4. ## Environment Variables — move up from current position, keep table as-is\n5. ## Run it locally — move up, keep content as-is, sits right after env vars\n6. ## Architecture — (will be added by later tasks, leave placeholder: `## Architecture\\n\\n*Coming soon.*`)\n7. ## Project Structure — keep existing but will be rewritten by later task\n8. Remove the \"Key patterns\" section entirely (the getRepoContext, creating a hypercert, and listing hypercerts code snippets)\n\nThe \"What the app does\" section with screenshots stays between Tech Stack and Env Vars because a developer wants to see what they are building before setting it up.\n\n## Dont\n- Rewrite any section content (just move them)\n- Remove the figure tags in \"What the app does\"\n- Remove the callout notes (127.0.0.1 note, pre-release SDK note)\n- Remove the \"Screenshots needed\" callout (will be removed when screenshots are added)","acceptance_criteria":"1. Sections appear in order: Title/intro → Tech Stack → What the app does → Environment Variables → Run it locally → Architecture (placeholder) → Project Structure\n2. Key patterns section (getRepoContext, create, list code snippets) is completely removed\n3. All existing content in kept sections is unchanged (just moved)\n4. The 127.0.0.1 callout note remains with the Run it locally section\n5. The pre-release SDK callout is removed (it was in Key patterns)","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":30,"created_at":"2026-02-17T12:01:16.627412001+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:08:01.546927714+06:00","closed_at":"2026-02-17T12:08:01.546927714+06:00","close_reason":"88b2d2e Reorder sections: env vars and local setup above architecture","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.2","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:01:16.628684422+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.2","depends_on_id":"docs-rsa.1","type":"blocks","created_at":"2026-02-17T12:01:16.630931017+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-rsa.3","title":"Write OAuth flow architecture section","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nReplace the Architecture placeholder with a full \"## Architecture\" section. The first subsection is \"### OAuth Flow\". Write a clear, end-to-end explanation of how ATProto OAuth works in the scaffold. This is the most important section of the entire doc — developers struggle most with auth.\n\nCover these steps in order:\n\n1. **Client metadata discovery** — The ATProto authorization server fetches `\u003cbase-url\u003e/client-metadata.json` (served by `app/client-metadata.json/route.ts`). This returns RFC 7591 client metadata: client_id, redirect_uris, scopes (`atproto transition:generic`), DPoP-bound access tokens. Mention that for loopback dev the client_id format differs from production.\n\n2. **JWKS endpoint** — `\u003cbase-url\u003e/jwks.json` (served by `app/jwks.json/route.ts`) exposes the public key. The auth server uses this to verify client assertion JWTs. The private key comes from `ATPROTO_JWK_PRIVATE` env var (ES256).\n\n3. **Login initiation** — User enters handle → client calls `POST /api/auth/login` → server calls `sdk.authorize(handle)` → returns an authorization URL → browser redirects to the users PDS for consent.\n\n4. **OAuth callback** — PDS redirects back to `/api/auth/callback` with auth code → server calls `sdk.callback(params)` to exchange code for session → session (tokens, refresh tokens) stored in Redis via `RedisSessionStore` (key: `session:\u003cdid\u003e`, no TTL) → `user-did` httpOnly cookie set → user redirected to app.\n\n5. **Session restore on subsequent requests** — Every server-side request reads the `user-did` cookie → calls `sdk.restoreSession(did)` which reads from Redis → tokens auto-refreshed if expired. This is wrapped in Reacts `cache()` for per-request deduplication (multiple calls in one request only hit Redis once).\n\n6. **Temporary OAuth state** — During the auth flow, temporary state is stored in Redis via `RedisStateStore` (key: `oauth-state:\u003cstate\u003e`, 10-minute TTL). This prevents CSRF and is cleaned up automatically.\n\n7. **Logout** — `GET /api/auth/logout` → `session.signOut()` revokes tokens → Redis session deleted → `user-did` cookie deleted.\n\n8. **Loopback compliance** — ATProto OAuth requires `127.0.0.1` not `localhost` per RFC 8252. The app has a proxy that auto-redirects. The `config.ts` auto-detects loopback vs production and adjusts client_id format accordingly.\n\nWrite in prose with subheadings, not as a numbered list. Use a callout for the 127.0.0.1/RFC 8252 note. Do NOT include code snippets — describe the flow conceptually with file paths as references.\n\n## Dont\n- Include TypeScript code blocks — this is architecture docs, not a code tutorial\n- Document the repo-context or DID resolution chain (being removed)\n- Change any section outside of Architecture","acceptance_criteria":"1. Architecture section exists with an OAuth Flow subsection\n2. All 8 steps listed above are covered in prose\n3. File paths are mentioned as references (e.g. app/api/auth/callback/route.ts)\n4. No TypeScript code blocks\n5. RFC 8252 / 127.0.0.1 note is in a callout\n6. Redis role in session + state storage is explained\n7. The cache() deduplication pattern is mentioned","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":60,"created_at":"2026-02-17T12:01:42.60171065+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:09:53.172089509+06:00","closed_at":"2026-02-17T12:09:53.172089509+06:00","close_reason":"c6a57c7 docs: write OAuth flow architecture section","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-rsa.3","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:01:42.602975023+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.3","depends_on_id":"docs-rsa.2","type":"blocks","created_at":"2026-02-17T12:01:42.605286027+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-rsa.4","title":"Write server-side data boundary architecture section","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nAdd a \"### Server-Side Data Boundary\" subsection inside the Architecture section, after the OAuth Flow subsection.\n\nThe core insight to communicate: **ALL data fetching in this app happens server-side.** The ATProto session (tokens, refresh logic) lives on the server in Redis and is only accessible via server-side code. Client components never talk to the PDS directly.\n\nCover these points:\n\n1. **Why server-only** — The ATProto OAuth session is stored in Redis and accessed via an httpOnly cookie. There is no browser-side session. This means any operation that needs authentication (reading/writing hypercerts, profiles, etc.) must go through the server.\n\n2. **Two server-side patterns** — The app uses two ways to run server-side code:\n - **API Routes** (`app/api/`) — Traditional REST-style endpoints. Used for operations that need FormData (file uploads for evidence, images) or complex request/response handling. Examples: `POST /api/certs/create`, `POST /api/certs/add-attachment`, `POST /api/profile/update`.\n - **Server Actions** (`lib/create-actions.ts`, marked with `\"use server\"`) — Called directly from client components without an HTTP round-trip. Used for simpler operations like fetching profile info, adding evaluations/measurements. Examples: `getActiveProfileInfo()`, `addEvaluation()`, `addMeasurement()`.\n\n3. **Client-side data layer** — Client components use TanStack React Query hooks (in `queries/`) to call these server-side endpoints. The pattern is three tiers:\n - **API client** (`lib/api/client.ts`) — Base fetch wrappers with consistent error handling\n - **Domain functions** (`lib/api/hypercerts.ts`, `lib/api/profile.ts`, etc.) — Construct requests and call the API client\n - **Query hooks** (`queries/hypercerts/`, `queries/profile/`, etc.) — TanStack Query wrappers that manage caching, loading states, error toasts, and cache invalidation on mutations\n\n4. **Server components fetch directly** — Pages like `/hypercerts` and `/hypercerts/[uri]` are server components that call the SDK directly on the server (no API route needed). They pass the fetched data as props to client components for interactivity.\n\nWrite in prose. No code blocks. Reference file paths.\n\n## Dont\n- Include code snippets\n- Document specific API request/response shapes\n- Mention repo-context or DID resolution (being removed)\n- Change any section outside of Architecture","acceptance_criteria":"1. Server-Side Data Boundary subsection exists after OAuth Flow\n2. Explains WHY data fetching is server-only (session in Redis, httpOnly cookie)\n3. Describes both API Routes and Server Actions as the two server-side patterns\n4. Describes the 3-tier client data layer (API client → domain functions → query hooks)\n5. Mentions that server components fetch directly without API routes\n6. No code blocks\n7. File paths referenced for each layer","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":45,"created_at":"2026-02-17T12:02:02.796441287+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:11:56.446804532+06:00","closed_at":"2026-02-17T12:11:56.446804532+06:00","close_reason":"9b5cfd7 docs: add server-side data boundary architecture section","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.4","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:02:02.798609989+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.4","depends_on_id":"docs-rsa.3","type":"blocks","created_at":"2026-02-17T12:02:02.801339312+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-rsa.5","title":"Write Constellation backlinks architecture section","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nAdd a \"### Constellation Backlinks\" subsection inside the Architecture section, after the Server-Side Data Boundary subsection.\n\nExplain how the scaffold discovers related records for a hypercert. In ATProto, records reference other records via strong references (AT-URIs), but there is no built-in reverse lookup. The scaffold uses Constellation (an external backlinks service at `https://constellation.microcosm.blue`) to solve this.\n\nCover:\n\n1. **The problem** — When viewing a hypercert detail page, the app needs to find all evidence, evaluations, and measurements that reference that hypercert. ATProto does not provide reverse lookups natively.\n\n2. **How Constellation works** — Constellation indexes ATProto records and provides a backlinks API. You query it with a subject URI (the hypercert AT-URI) and a source collection path, and it returns all records that reference that subject. The scaffold queries three source paths:\n - Evidence: `org.hypercerts.claim.attachment:subjects[com.atproto.repo.strongRef].uri`\n - Evaluations: `org.hypercerts.claim.evaluation:subject.uri`\n - Measurements: `org.hypercerts.claim.measurement:subject.uri`\n\n3. **Where this happens in the code** — The client-side API layer (`lib/api/external/constellation.ts`) calls Constellation. TanStack Query hooks (`queries/hypercerts/use-evidence-query.ts`, `use-evaluations-query.ts`, `use-measurements-query.ts`) use a two-step pattern: first fetch backlinks from Constellation to get record URIs, then fetch each record individually via server actions to get the full data.\n\nWrite in prose. Keep it concise — this is a shorter subsection.\n\n## Dont\n- Include code snippets\n- Explain the Constellation API in detail (link to it if needed)\n- Change any section outside of Architecture","acceptance_criteria":"1. Constellation Backlinks subsection exists after Server-Side Data Boundary\n2. Explains the reverse-lookup problem in ATProto\n3. Lists the three source paths queried (evidence, evaluations, measurements)\n4. Mentions the two-step pattern (backlinks → individual record fetch)\n5. References the relevant file paths\n6. No code blocks","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":30,"created_at":"2026-02-17T12:02:18.640700877+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:13:18.724095484+06:00","closed_at":"2026-02-17T12:13:18.724095484+06:00","close_reason":"a17a0d1 Add Constellation backlinks architecture section","labels":["scope:small"],"dependencies":[{"issue_id":"docs-rsa.5","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:02:18.641962933+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.5","depends_on_id":"docs-rsa.4","type":"blocks","created_at":"2026-02-17T12:02:18.6442514+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-rsa.6","title":"Rewrite project structure section with comprehensive annotated tree","description":"## Files\n- documentation/pages/tools/scaffold.md (modify)\n\n## What to do\nReplace the existing shallow \"Project structure\" section with a comprehensive, annotated directory tree. Group by concern and explain what each directory/file does.\n\nUse a code block for the tree, with inline comments. Structure:\n\n```\nhypercerts-scaffold/\n├── app/\n│ ├── layout.tsx # Root layout (server component, wraps providers)\n│ ├── page.tsx # Landing page (server component)\n│ ├── client-metadata.json/\n│ │ └── route.ts # OAuth client metadata endpoint (RFC 7591)\n│ ├── jwks.json/\n│ │ └── route.ts # JWKS public key endpoint for OAuth\n│ ├── api/\n│ │ ├── auth/\n│ │ │ ├── login/route.ts # POST — initiate OAuth login\n│ │ │ ├── callback/route.ts # GET — OAuth callback, sets session\n│ │ │ └── logout/route.ts # GET — revoke session, clear cookie\n│ │ ├── certs/\n│ │ │ ├── create/route.ts # POST — create hypercert (FormData)\n│ │ │ ├── add-location/route.ts # POST — attach location to hypercert\n│ │ │ └── add-attachment/route.ts # POST — attach evidence/files\n│ │ └── profile/\n│ │ ├── update/route.ts # POST — update Certified profile\n│ │ └── bsky/update/route.ts # POST — update Bluesky profile\n│ ├── hypercerts/\n│ │ ├── page.tsx # List all hypercerts (server component)\n│ │ ├── create/page.tsx # Multi-step creation wizard (client component)\n│ │ └── [hypercertUri]/page.tsx # Hypercert detail view (server component)\n│ ├── profile/page.tsx # Certified profile editor\n│ └── bsky-profile/page.tsx # Bluesky profile editor\n│\n├── lib/\n│ ├── config.ts # Centralized config, env validation, URL detection\n│ ├── hypercerts-sdk.ts # SDK singleton initialization\n│ ├── atproto-session.ts # Session restore helpers (server-only, cached)\n│ ├── redis.ts # Redis client singleton (server-only)\n│ ├── redis-state-store.ts # Redis-backed OAuth state + session stores\n│ ├── create-actions.ts # Server Actions (\"use server\")\n│ ├── blob-utils.ts # Blob/image URL resolution (server-only)\n│ ├── utils.ts # Shared utilities (cn, validators)\n│ ├── types.ts # Core TypeScript types\n│ └── api/ # Client-side API layer\n│ ├── client.ts # Base fetch wrappers (JSON, FormData)\n│ ├── auth.ts # Auth API functions\n│ ├── hypercerts.ts # Hypercert API functions\n│ ├── profile.ts # Profile API functions\n│ ├── query-keys.ts # Centralized TanStack Query key factory\n│ └── external/\n│ ├── bluesky.ts # Bluesky public API (search, profiles)\n│ └── constellation.ts # Constellation backlinks API\n│\n├── providers/\n│ ├── AllProviders.tsx # QueryClientProvider (client component)\n│ └── SignedInProvider.tsx # Auth gate + Navbar (server component)\n│\n├── queries/ # TanStack Query hooks (all client-side)\n│ ├── auth/ # Login/logout mutations\n│ ├── hypercerts/ # Create, attach, list queries/mutations\n│ ├── profile/ # Profile update mutations\n│ └── external/ # Bluesky search, Constellation queries\n│\n├── components/\n│ ├── ui/ # shadcn/ui primitives (button, dialog, etc.)\n│ ├── navbar.tsx # Top navigation\n│ ├── login-dialog.tsx # Login form\n│ ├── hypercerts-create-form.tsx # Create wizard wrapper\n│ ├── evidence-form.tsx # Evidence step\n│ ├── locations-form.tsx # Location step\n│ ├── measurement-form.tsx # Measurement step\n│ ├── evaluation-form.tsx # Evaluation step\n│ ├── hypercert-detail-view.tsx # Detail page client component\n│ ├── profile-form.tsx # Certified profile form\n│ └── bsky-profile-form.tsx # Bluesky profile form\n│\n├── lexicons/ # Auto-generated ATProto lexicon types\n│ └── types/org/hypercerts/claim/ # Hypercert record type definitions\n│\n├── scripts/\n│ └── generate-jwk.mjs # JWK key pair generator (ES256)\n│\n└── vendor/ # Packed SDK tarballs (pre-release)\n```\n\nAfter the tree, add a brief paragraph explaining the key architectural boundaries:\n- `app/` contains pages (server components by default) and API routes\n- `lib/` is split: top-level files are server-only, `lib/api/` is the client-side fetch layer\n- `providers/` has one server component (SignedInProvider) and one client component (AllProviders)\n- `queries/` is entirely client-side TanStack Query hooks\n- `components/` is entirely client-side React components\n- `lexicons/` is auto-generated — do not edit manually\n\n## Dont\n- List every single file in components/ui/ (just say \"shadcn/ui primitives\")\n- List loading.tsx or layout.tsx files unless they do something meaningful\n- Include the tests/ directory (minimal, not useful for docs)\n- Change any section outside of Project Structure","acceptance_criteria":"1. Project structure section contains a comprehensive annotated tree in a code block\n2. All directories listed above are present with inline comments\n3. A summary paragraph after the tree explains the server/client boundaries\n4. components/ui/ is summarized, not listed file-by-file\n5. No other sections are modified","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":45,"created_at":"2026-02-17T12:02:50.906045436+06:00","created_by":"karma.gainforest.id","updated_at":"2026-02-17T12:14:39.474919695+06:00","closed_at":"2026-02-17T12:14:39.474919695+06:00","close_reason":"bf30776 Rewrite project structure section with comprehensive annotated tree","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-rsa.6","depends_on_id":"docs-rsa","type":"parent-child","created_at":"2026-02-17T12:02:50.907462063+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-rsa.6","depends_on_id":"docs-rsa.5","type":"blocks","created_at":"2026-02-17T12:02:50.909892906+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-scg","title":"Epic: Bug Fixes \u0026 Design Polish","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:06:01.764873+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:13:13.114836+13:00","closed_at":"2026-02-14T21:13:13.114836+13:00","close_reason":"All 9 tasks complete. All acceptance tests pass. Build succeeds."} -{"id":"docs-scg.1","title":"Fix SSR hydration mismatch in Layout.js — guard localStorage with typeof window check","description":"## Files\n- documentation/components/Layout.js (modify)\n\n## What to do\nLine 21 calls `localStorage.getItem(\"sidebar-collapsed\")` inside a `useEffect` without guarding against SSR. While `useEffect` only runs client-side in React, Next.js static export can still trigger hydration warnings if the initial render differs from server render.\n\nFix: Wrap the `localStorage` access in a `typeof window !== \"undefined\"` check inside the existing `useEffect` on lines 20-25. Also wrap the `localStorage.setItem` call on line 30 in the same guard.\n\nBefore (line 20-25):\n```js\nuseEffect(() =\u003e {\n const stored = localStorage.getItem(\"sidebar-collapsed\");\n if (stored === \"true\") {\n setSidebarCollapsed(true);\n }\n}, []);\n```\n\nAfter:\n```js\nuseEffect(() =\u003e {\n if (typeof window === \"undefined\") return;\n const stored = localStorage.getItem(\"sidebar-collapsed\");\n if (stored === \"true\") {\n setSidebarCollapsed(true);\n }\n}, []);\n```\n\nAlso update `toggleCollapsed` (line 27-31):\n```js\nconst toggleCollapsed = () =\u003e {\n const next = !sidebarCollapsed;\n setSidebarCollapsed(next);\n if (typeof window !== \"undefined\") {\n localStorage.setItem(\"sidebar-collapsed\", String(next));\n }\n};\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst src = fs.readFileSync(\\\"components/Layout.js\\\", \\\"utf8\\\");\nconst hasGuard = src.includes(\\\"typeof window\\\");\nif (!hasGuard) { console.error(\\\"FAIL: missing typeof window guard\\\"); process.exit(1); }\nconsole.log(\\\"PASS: localStorage guarded with typeof window check\\\");\n\"\n```\n\n## Dont\n- Do not change any other component files\n- Do not change the sidebar collapse behavior or UX\n- Do not add try/catch around localStorage (the guard is sufficient)\n- Do not remove the useEffect or change its dependency array","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:06:16.432116+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:10:35.991575+13:00","closed_at":"2026-02-14T21:10:35.991575+13:00","close_reason":"e4a6983 guard localStorage with typeof window check in Layout.js","dependencies":[{"issue_id":"docs-scg.1","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:06:16.433467+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-scg.2","title":"Fix IntersectionObserver memory leak in TableOfContents.js","description":"## Files\n- documentation/components/TableOfContents.js (modify)\n\n## What to do\nThe IntersectionObserver cleanup in the second useEffect (lines 28-59) is not fully robust. If the component unmounts while headings are being observed, the observer ref may not be properly nullified. Additionally, the observer should be disconnected and nullified in the cleanup function.\n\nFix the cleanup function (lines 54-58) to also null out the ref after disconnecting:\n\nBefore:\n```js\nreturn () =\u003e {\n if (observerRef.current) {\n observerRef.current.disconnect();\n }\n};\n```\n\nAfter:\n```js\nreturn () =\u003e {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n};\n```\n\nAlso add a guard at the top of the first useEffect (lines 9-25) for SSR safety:\n```js\nuseEffect(() =\u003e {\n if (typeof window === \"undefined\") return;\n const article = document.querySelector(\".layout-content article\");\n // ... rest unchanged\n}, []);\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst src = fs.readFileSync(\\\"components/TableOfContents.js\\\", \\\"utf8\\\");\nconst hasNullify = src.includes(\\\"observerRef.current = null\\\");\nif (!hasNullify) { console.error(\\\"FAIL: observer ref not nullified in cleanup\\\"); process.exit(1); }\nconsole.log(\\\"PASS: IntersectionObserver cleanup is robust\\\");\n\"\n```\n\n## Dont\n- Do not change the IntersectionObserver options (rootMargin, threshold)\n- Do not change the scroll spy logic or active heading detection\n- Do not change the JSX/rendering\n- Do not add any new dependencies","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:06:26.158667+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:11:16.194047+13:00","closed_at":"2026-02-14T21:11:16.194047+13:00","close_reason":"e4a6983 - Changes already implemented in docs-scg.1 commit (SSR guard and observer ref nullification)","dependencies":[{"issue_id":"docs-scg.2","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:06:26.15979+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-scg.3","title":"Fix router event listener leak in Sidebar.js — stabilize onClose callback","description":"## Files\n- documentation/components/Sidebar.js (modify)\n\n## What to do\nIn the Sidebar component (lines 116-122), the useEffect registers a `routeChangeComplete` event listener that calls `onClose`. The dependency array includes `[router, onClose]`. Since `onClose` is `() =\u003e setSidebarOpen(false)` — an inline arrow function created each render in Layout.js — this causes the listener to be torn down and re-registered on every render.\n\nFix: Use a ref to hold the latest `onClose` callback, so the effect only depends on `router`. This prevents unnecessary re-registration.\n\nReplace lines 116-122 with:\n```js\nconst onCloseRef = useRef(onClose);\nuseEffect(() =\u003e {\n onCloseRef.current = onClose;\n});\n\nuseEffect(() =\u003e {\n const handleRouteChange = () =\u003e {\n if (onCloseRef.current) onCloseRef.current();\n };\n router.events.on(\"routeChangeComplete\", handleRouteChange);\n return () =\u003e router.events.off(\"routeChangeComplete\", handleRouteChange);\n}, [router]);\n```\n\nYou will also need to add `useRef` to the import on line 1:\n```js\nimport { useState, useEffect, useRef } from \"react\";\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst src = fs.readFileSync(\\\"components/Sidebar.js\\\", \\\"utf8\\\");\nconst hasRef = src.includes(\\\"useRef\\\") \u0026\u0026 src.includes(\\\"onCloseRef\\\");\nconst noOnCloseInDeps = !src.match(/\\\\[router,\\\\s*onClose\\\\]/);\nif (!hasRef) { console.error(\\\"FAIL: missing useRef for onClose\\\"); process.exit(1); }\nif (!noOnCloseInDeps) { console.error(\\\"FAIL: onClose still in useEffect deps\\\"); process.exit(1); }\nconsole.log(\\\"PASS: router event listener stabilized with ref\\\");\n\"\n```\n\n## Dont\n- Do not change the NavItem or NavSection components\n- Do not change the sidebar JSX structure or CSS classes\n- Do not remove the routeChangeComplete listener (it is needed for mobile sidebar close)\n- Do not change the navigation import or data","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:06:36.189703+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:10:39.443531+13:00","closed_at":"2026-02-14T21:10:39.443531+13:00","close_reason":"d5eb099 stabilize onClose callback with useRef in Sidebar.js","dependencies":[{"issue_id":"docs-scg.3","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:06:36.191148+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-scg.4","title":"Fix Evidence nav link — rename title or file to match","description":"## Files\n- documentation/lib/navigation.js (modify)\n\n## What to do\nIn navigation.js line 33, the nav entry has title \"Evidence\" but links to path `/lexicons/hypercerts-lexicons/attachment`. The actual file is `documentation/pages/lexicons/hypercerts-lexicons/attachment.md` which exists. The mismatch is that the nav title says \"Evidence\" but the URL says \"attachment\" — this is confusing but technically works.\n\nThe fix: Rename the file to match the nav title. Create `evidence.md` from `attachment.md` and update the nav path.\n\nSteps:\n1. Copy `documentation/pages/lexicons/hypercerts-lexicons/attachment.md` to `documentation/pages/lexicons/hypercerts-lexicons/evidence.md`\n2. In the new `evidence.md`, update the frontmatter title to \"Evidence\" if it says \"Attachment\"\n3. Update `documentation/lib/navigation.js` line 33: change `path: \"/lexicons/hypercerts-lexicons/attachment\"` to `path: \"/lexicons/hypercerts-lexicons/evidence\"`\n4. Delete the old `attachment.md` file\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst nav = fs.readFileSync(\\\"lib/navigation.js\\\", \\\"utf8\\\");\nconst hasEvidence = nav.includes(\\\"/lexicons/hypercerts-lexicons/evidence\\\");\nconst noAttachment = !nav.includes(\\\"/lexicons/hypercerts-lexicons/attachment\\\");\nconst fileExists = fs.existsSync(\\\"pages/lexicons/hypercerts-lexicons/evidence.md\\\");\nconst oldGone = !fs.existsSync(\\\"pages/lexicons/hypercerts-lexicons/attachment.md\\\");\nif (!hasEvidence) { console.error(\\\"FAIL: nav missing evidence path\\\"); process.exit(1); }\nif (!noAttachment) { console.error(\\\"FAIL: nav still has attachment path\\\"); process.exit(1); }\nif (!fileExists) { console.error(\\\"FAIL: evidence.md does not exist\\\"); process.exit(1); }\nif (!oldGone) { console.error(\\\"FAIL: attachment.md still exists\\\"); process.exit(1); }\nconsole.log(\\\"PASS: Evidence nav link and file are consistent\\\");\n\"\n```\n\n## Dont\n- Do not change any other navigation entries\n- Do not change the content of the file (only the frontmatter title if needed)\n- Do not modify any other files besides navigation.js and the renamed markdown file","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:06:49.936961+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:10:46.051008+13:00","closed_at":"2026-02-14T21:10:46.051008+13:00","close_reason":"494906a Renamed attachment.md to evidence.md and updated navigation link to match","dependencies":[{"issue_id":"docs-scg.4","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:06:49.938055+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-scg.5","title":"Add placeholder content to stub pages (infrastructure + SDK)","description":"## Files\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify)\n- documentation/pages/getting-started/installing-the-sdk.md (modify)\n\n## What to do\nTwo stub pages have no real content. Add meaningful placeholder content that is accurate based on the rest of the documentation. These pages are about the Hypercerts protocol built on ATProto.\n\n### the-hypercerts-infrastructure.md\nReplace the current stub (just \"### Why ATProto?\" and \"_TBD_\") with:\n\n```markdown\n---\ntitle: The Hypercerts Infrastructure\n---\n\n# The Hypercerts Infrastructure\n\nThe Hypercerts protocol is built on top of the AT Protocol (ATProto), a decentralized social networking framework. This page explains the infrastructure choices and how they enable the hypercerts system.\n\n## Why ATProto?\n\nThe AT Protocol provides several properties that make it ideal for hypercerts:\n\n- **Decentralized identity**: Users control their own identity through DIDs (Decentralized Identifiers), ensuring that impact claims are tied to verifiable actors\n- **Lexicon-based schemas**: ATProto uses Lexicons to define data schemas, providing a structured and extensible way to represent hypercerts data\n- **Federation**: Data is stored across federated servers (Personal Data Servers), giving users data sovereignty while maintaining interoperability\n- **Content addressing**: Records are content-addressed, making hypercerts tamper-evident and auditable\n\n## Architecture Overview\n\nThe hypercerts infrastructure consists of:\n\n1. **Personal Data Servers (PDS)**: Store user data including hypercert records, evaluations, and contributions\n2. **Lexicon schemas**: Define the structure of all hypercert-related records (see [Lexicons](/lexicons/introduction-to-lexicons))\n3. **Record types**: Activity claims, contributions, evaluations, measurements, evidence, rights, and collections\n\n## Data Flow\n\nWhen a user creates a hypercert:\n\n1. The claim is structured according to the relevant lexicon schema\n2. The record is stored on the user's PDS\n3. Other users can reference, evaluate, or contribute to the claim using linked records\n4. All records maintain cryptographic integrity through ATProto's content addressing\n```\n\n### installing-the-sdk.md\nReplace the empty stub with:\n\n```markdown\n---\ntitle: Installing the SDK\n---\n\n# Installing the SDK\n\nThis page covers how to set up your development environment for working with the Hypercerts protocol.\n\n## Prerequisites\n\nBefore getting started, ensure you have:\n\n- **Node.js** (v18 or later)\n- **npm** or **yarn** package manager\n- An AT Protocol account (e.g., a Bluesky account)\n\n## Installation\n\nInstall the hypercerts SDK via npm:\n\n```\nnpm install @hypercerts/sdk\n```\n\nOr with yarn:\n\n```\nyarn add @hypercerts/sdk\n```\n\n## Configuration\n\nAfter installation, configure the SDK with your ATProto credentials:\n\n```\nimport { HypercertsClient } from \"@hypercerts/sdk\";\n\nconst client = new HypercertsClient({\n service: \"https://bsky.social\",\n // Additional configuration options\n});\n```\n\n## Next Steps\n\n- Learn about the [lexicon schemas](/lexicons/introduction-to-lexicons) that define hypercert data structures\n- Explore the [work scope](/deep-dive-the-work-scope) concept in depth\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst infra = fs.readFileSync(\\\"pages/getting-started/the-hypercerts-infrastructure.md\\\", \\\"utf8\\\");\nconst sdk = fs.readFileSync(\\\"pages/getting-started/installing-the-sdk.md\\\", \\\"utf8\\\");\nconst infraHasContent = infra.length \u003e 200 \u0026\u0026 !infra.includes(\\\"_TBD_\\\");\nconst sdkHasContent = sdk.length \u003e 200 \u0026\u0026 sdk.includes(\\\"npm\\\");\nif (!infraHasContent) { console.error(\\\"FAIL: infrastructure page still a stub\\\"); process.exit(1); }\nif (!sdkHasContent) { console.error(\\\"FAIL: SDK page still a stub\\\"); process.exit(1); }\nconsole.log(\\\"PASS: Both stub pages have meaningful content\\\");\n\"\n```\n\n## Dont\n- Do not change the frontmatter title fields\n- Do not add any Markdoc custom tags (use standard markdown only)\n- Do not modify any other pages\n- Do not change navigation.js","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:07:10.12344+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:10:58.791793+13:00","closed_at":"2026-02-14T21:10:58.791793+13:00","close_reason":"1e42be8 filled stub pages with meaningful content for infrastructure and SDK","dependencies":[{"issue_id":"docs-scg.5","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:07:10.124804+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-scg.6","title":"Reduce content max-width from 1175px to 800px for readability","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe content area max-width is currently 1175px (line 110: `--content-max-width: 1175px`). This is too wide for comfortable reading. Reduce it to 800px.\n\nChange line 110 in globals.css:\n\nBefore:\n```css\n--content-max-width: 1175px;\n```\n\nAfter:\n```css\n--content-max-width: 800px;\n```\n\nThat is the ONLY change needed. The CSS variable is already used by `.layout-content` on line 445.\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\nconst match = css.match(/--content-max-width:\\\\s*(\\\\d+)px/);\nif (!match) { console.error(\\\"FAIL: content-max-width variable not found\\\"); process.exit(1); }\nconst width = parseInt(match[1]);\nif (width \u003e 850) { console.error(\\\"FAIL: content-max-width is \\\" + width + \\\"px, expected \u003c= 850\\\"); process.exit(1); }\nif (width \u003c 700) { console.error(\\\"FAIL: content-max-width is \\\" + width + \\\"px, too narrow\\\"); process.exit(1); }\nconsole.log(\\\"PASS: content-max-width is \\\" + width + \\\"px\\\");\n\"\n```\n\n## Dont\n- Do not change any other CSS variables\n- Do not change the .layout-content rule itself\n- Do not modify any component files\n- Only touch the single CSS variable declaration","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:07:19.971101+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:11:59.877268+13:00","closed_at":"2026-02-14T21:11:59.877268+13:00","close_reason":"Already completed - max-width changed to 800px in commit 1e42be8","dependencies":[{"issue_id":"docs-scg.6","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:07:19.97185+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-scg.7","title":"Increase content padding — 40px top, 48px sides","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe content area padding is currently `var(--space-6) var(--space-6)` which is `24px 24px` (line 446). Increase to 40px top and 48px sides for better breathing room.\n\nChange line 446 in globals.css:\n\nBefore:\n```css\n.layout-content {\n flex: 1;\n min-width: 0;\n max-width: var(--content-max-width);\n padding: var(--space-6) var(--space-6);\n}\n```\n\nAfter:\n```css\n.layout-content {\n flex: 1;\n min-width: 0;\n max-width: var(--content-max-width);\n padding: var(--space-10) var(--space-12);\n}\n```\n\nNote: `--space-10` = 40px, `--space-12` = 48px (these variables already exist in the design tokens).\n\nAlso update the mobile responsive override on line 916:\nBefore:\n```css\n.layout-content {\n padding: var(--space-6) var(--space-4);\n}\n```\n\nAfter:\n```css\n.layout-content {\n padding: var(--space-6) var(--space-4);\n}\n```\n(Keep mobile padding the same — 24px top, 16px sides is fine for mobile.)\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\n// Find the .layout-content rule (not inside @media)\nconst contentRule = css.match(/\\\\.layout-content\\\\s*\\\\{[^}]*padding:\\\\s*var\\\\(--space-10\\\\)\\\\s+var\\\\(--space-12\\\\)/);\nif (!contentRule) { console.error(\\\"FAIL: .layout-content padding not updated to space-10 space-12\\\"); process.exit(1); }\nconsole.log(\\\"PASS: Content padding increased to 40px 48px\\\");\n\"\n```\n\n## Dont\n- Do not change the mobile responsive padding (keep it as-is)\n- Do not change any other properties in .layout-content\n- Do not modify any component files\n- Do not change the design token values themselves","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:07:29.179182+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:12:00.942832+13:00","closed_at":"2026-02-14T21:12:00.942832+13:00","close_reason":"Already completed - padding changed to space-10/space-12 in commit 1e42be8","dependencies":[{"issue_id":"docs-scg.7","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:07:29.180577+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-scg.8","title":"Reduce H3 font-size from 20px to 18px for heading hierarchy","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nH3 and H2 are both 20px, which breaks visual hierarchy. Reduce H3 to 18px.\n\nChange the `.layout-content h3` rule (lines 576-583):\n\nBefore:\n```css\n.layout-content h3 {\n font-size: 20px;\n font-weight: 600;\n color: var(--color-text-heading);\n line-height: 1.4;\n margin-top: var(--space-8);\n margin-bottom: var(--space-2);\n}\n```\n\nAfter:\n```css\n.layout-content h3 {\n font-size: 18px;\n font-weight: 600;\n color: var(--color-text-heading);\n line-height: 1.4;\n margin-top: var(--space-8);\n margin-bottom: var(--space-2);\n}\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\nconst h3Match = css.match(/\\\\.layout-content\\\\s+h3\\\\s*\\\\{[^}]*font-size:\\\\s*(\\\\d+)px/);\nconst h2Match = css.match(/\\\\.layout-content\\\\s+h2\\\\s*\\\\{[^}]*font-size:\\\\s*(\\\\d+)px/);\nif (!h3Match) { console.error(\\\"FAIL: h3 font-size not found\\\"); process.exit(1); }\nif (!h2Match) { console.error(\\\"FAIL: h2 font-size not found\\\"); process.exit(1); }\nconst h3Size = parseInt(h3Match[1]);\nconst h2Size = parseInt(h2Match[1]);\nif (h3Size \u003e= h2Size) { console.error(\\\"FAIL: h3 (\\\" + h3Size + \\\"px) should be smaller than h2 (\\\" + h2Size + \\\"px)\\\"); process.exit(1); }\nif (h3Size !== 18) { console.error(\\\"FAIL: h3 is \\\" + h3Size + \\\"px, expected 18px\\\"); process.exit(1); }\nconsole.log(\\\"PASS: h3 is 18px, h2 is \\\" + h2Size + \\\"px — proper hierarchy\\\");\n\"\n```\n\n## Dont\n- Do not change H2 font-size (keep at 20px)\n- Do not change H1 or H4 styles\n- Do not change any other H3 properties (weight, color, line-height, margins)\n- Do not modify any component files","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:07:37.278427+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:12:02.042186+13:00","closed_at":"2026-02-14T21:12:02.042186+13:00","close_reason":"Already completed - H3 font-size changed to 18px in commit 1e42be8","dependencies":[{"issue_id":"docs-scg.8","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:07:37.279736+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-scg.9","title":"Increase sidebar section divider spacing from 8px to 20px","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe sidebar section dividers (`.sidebar-section`) have `margin-top: var(--space-2)` and `padding-top: var(--space-2)` which is only 8px each. This makes sections feel cramped. Increase to 20px each.\n\nChange the `.sidebar-section` rule (lines 349-354):\n\nBefore:\n```css\n.sidebar-section {\n margin-top: var(--space-2);\n padding-top: var(--space-2);\n border-top: 1px solid var(--color-border);\n list-style: none;\n}\n```\n\nAfter:\n```css\n.sidebar-section {\n margin-top: var(--space-5);\n padding-top: var(--space-5);\n border-top: 1px solid var(--color-border);\n list-style: none;\n}\n```\n\nNote: `--space-5` = 20px (already defined in design tokens).\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\nconst sectionRule = css.match(/\\\\.sidebar-section\\\\s*\\\\{[^}]*margin-top:\\\\s*var\\\\(--space-(\\\\d+)\\\\)/);\nif (!sectionRule) { console.error(\\\"FAIL: sidebar-section margin-top not found\\\"); process.exit(1); }\nconst spaceNum = parseInt(sectionRule[1]);\nif (spaceNum \u003c 4) { console.error(\\\"FAIL: sidebar-section spacing too tight (space-\\\" + spaceNum + \\\")\\\"); process.exit(1); }\nconsole.log(\\\"PASS: sidebar-section uses space-\\\" + spaceNum + \\\" for margin-top\\\");\n\"\n```\n\n## Dont\n- Do not change the border-top style\n- Do not change the :first-child override (lines 356-360)\n- Do not change any other sidebar styles\n- Do not modify any component files","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T21:07:44.340001+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T21:12:03.031038+13:00","closed_at":"2026-02-14T21:12:03.031038+13:00","close_reason":"Already completed - sidebar spacing changed to space-5 in commit 1e42be8","dependencies":[{"issue_id":"docs-scg.9","depends_on_id":"docs-scg","type":"parent-child","created_at":"2026-02-14T21:07:44.34153+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-sw5","title":"Epic: Landing Page \u0026 Dark Mode","description":"Improve landing page: 21 cards causes decision paralysis (Hick's Law), no primary CTA, every section uses identical 2-col grid (monotony), Ecosystem section has 1 card in 2-col (looks broken). Fix dark mode: crude filter:invert(1) on logo, callout colors collapse. Covers UX findings #7, 20, 23, 24, 25, 35, 38, 39, 40.","status":"closed","priority":2,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T19:55:49.555583+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:38.332975+08:00","closed_at":"2026-02-20T20:06:38.332975+08:00","close_reason":"598de2d all landing page + dark mode tasks complete","labels":["scope:medium"]} -{"id":"docs-sw5.1","title":"Hero CTA button + reduce cards + vary grid layouts + fix Ecosystem orphan","description":"## Files\n- pages/index.md (modify)\n- components/HeroBanner.js (modify)\n- styles/globals.css (modify)\n- markdoc/tags/hero-banner.markdoc.js (modify — add cta-href, cta-text attributes)\n\n## What to do\n\n### Hero CTA button (finding #24)\nAdd a prominent \"Get Started →\" button in the hero banner. \n\nIn `HeroBanner.js`, accept two new props: `ctaHref` and `ctaText`. After the subtitle div, render:\n```jsx\n{ctaHref \u0026\u0026 (\n \u003ca href={ctaHref} className=\"hero-cta\"\u003e\n {ctaText || \"Get Started\"} \u003cspan aria-hidden=\"true\"\u003e→\u003c/span\u003e\n \u003c/a\u003e\n)}\n```\n\nUpdate `markdoc/tags/hero-banner.markdoc.js` to add attributes:\n```js\nattributes: {\n title: { type: String },\n \"cta-href\": { type: String },\n \"cta-text\": { type: String },\n}\n```\n\nMap them in the render: `ctaHref: attrs[\"cta-href\"], ctaText: attrs[\"cta-text\"]`.\n\nCSS for `.hero-cta`:\n```css\n.hero-cta {\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n margin-top: var(--space-6);\n padding: 10px 24px;\n background: var(--color-accent);\n color: white;\n font-family: var(--font-display);\n font-size: 15px;\n font-weight: 600;\n text-decoration: none;\n border-radius: 9999px;\n transition: background var(--transition-fast), transform var(--transition-fast);\n}\n\n.hero-cta:hover {\n background: var(--color-link-hover);\n transform: translateY(-1px);\n text-decoration: none;\n color: white;\n}\n```\n\nIn `pages/index.md`, update the hero-banner tag:\n```\n{% hero-banner title=\"Hypercerts Documentation\" cta-href=\"/getting-started/quickstart\" cta-text=\"Get Started\" %}\n```\n\n### Reduce card count (finding #7)\nCurrently 21 cards across 6 sections. Reduce to the most important cards per section. Edit `pages/index.md`:\n\n**Get Started**: Keep 2 cards (Quickstart + Common Use Cases). Remove \"Creating Your First Hypercert\" and \"Working with Evaluations\" — these are linked from sidebar already.\n\n**Core Concepts**: Keep 2 cards (What are Hypercerts? + Core Data Model). Remove \"Certified Identity\" and \"Why ATProto?\".\n\n**Tools**: Keep all 4 cards — they are the primary tools.\n\n**Architecture**: Keep 2 cards (Architecture Overview + Data Flow). Remove \"Indexers \u0026 Discovery\" and \"Portability \u0026 Scaling\".\n\n**Reference**: Keep 3 cards (Lexicons + Glossary + FAQ). Remove Roadmap.\n\n**Ecosystem**: Remove this entire section header + its single card. The \"Why We Need Hypercerts\" page is niche and better discovered from sidebar.\n\nResult: 13 cards total (was 21). Still comprehensive but less overwhelming.\n\n### Vary grid layouts (finding #23)\nTo break visual monotony, make the Get Started section use a **single column** layout since it only has 2 cards now. Add a `columns` attribute to `card-grid` markdoc tag.\n\nIn `markdoc/tags/card-grid.markdoc.js`, add attribute: `columns: { type: Number, default: 2 }`\n\nIn `CardGrid.js`, accept `columns` prop and set grid-template-columns accordingly:\n```jsx\n\u003cdiv className=\"card-grid\" style={columns === 1 ? { gridTemplateColumns: \"1fr\" } : undefined}\u003e\n```\n\nIn `pages/index.md`, use `{% card-grid columns=1 %}` for the Get Started section.\n\n### Fix Ecosystem orphan (finding #25)\nAlready handled by removing the Ecosystem section above. If not removing, use `columns=1`.\n\n## Dont\n- Do NOT change any card-link component styling\n- Do NOT modify DotPattern\n- Do NOT add new components — only modify existing ones\n- Do NOT remove the hero-banner subtitle text","acceptance_criteria":"1. Hero banner has a prominent \"Get Started →\" pill button linking to /getting-started/quickstart\n2. CTA button has accent color background and hover lift effect\n3. Landing page has ~13 cards (reduced from 21)\n4. \"Get Started\" section uses a single-column card layout\n5. \"Ecosystem \u0026 Vision\" section is removed from the landing page\n6. Remaining sections still show their cards correctly in 2-col grid\n7. Build passes: npm run build -- --webpack","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T19:56:15.6068+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.899022+08:00","closed_at":"2026-02-20T20:06:32.899022+08:00","close_reason":"598de2d hero CTA + reduce cards + vary grids","labels":["scope:small"],"dependencies":[{"issue_id":"docs-sw5.1","depends_on_id":"docs-sw5","type":"parent-child","created_at":"2026-02-20T19:56:15.607693+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-sw5.2","title":"Dark mode logo fix + callout color differentiation","description":"## Files\n- styles/globals.css (modify)\n- public/images/hypercerts_logo_horizontal_white.svg (create)\n\n## What to do\n\n### Dark mode logo (finding #20)\nThe current approach uses `filter: invert(1)` on the black SVG logo for dark mode. This is crude and can produce artifacts.\n\n**Option A (preferred):** Copy `public/images/hypercerts_logo_horizontal.svg` to `public/images/hypercerts_logo_horizontal_white.svg`. Edit the copy to change all `fill=\"#000\"` (or `fill=\"black\"` or unset fill on paths) to `fill=\"#fff\"` (white). If the SVG uses `fill` on individual paths, change those. If it uses no fill (defaults to black via currentColor), add `fill=\"white\"` to the root `\u003csvg\u003e` element.\n\nThen in Layout.js, use conditional rendering or CSS to swap logos:\n- Add a second `\u003cimg\u003e` with class `layout-logo-img layout-logo-img-dark` pointing to the white SVG\n- CSS: `.layout-logo-img-dark { display: none; }`\n- CSS: `html.dark .layout-logo-img { display: none; } html.dark .layout-logo-img-dark { display: block; }`\n- Remove `html.dark .layout-logo-img { filter: invert(1); }` from globals.css\n\n**Option B (simpler, if SVG is complex):** Keep the invert approach but use a better filter:\n```css\nhtml.dark .layout-logo-img {\n filter: invert(1) brightness(0.95);\n}\n```\n\nPrefer Option A if the SVG is simple enough to create a white variant.\n\n### Callout color differentiation in dark mode (finding #35)\nDark mode callout backgrounds collapse to similar tints. Increase the chroma and lightness spread:\n\n```css\nhtml.dark {\n --color-info-bg: oklch(0.18 0.03 260);\n --color-warning-bg: oklch(0.18 0.04 55);\n --color-danger-bg: oklch(0.18 0.04 15);\n --color-success-bg: oklch(0.18 0.04 145);\n}\n```\n\nAlso boost the border colors slightly:\n```css\nhtml.dark {\n --color-info: oklch(0.60 0.18 260);\n --color-warning: oklch(0.65 0.18 55);\n --color-danger: oklch(0.60 0.22 25);\n --color-success: oklch(0.60 0.18 145);\n}\n```\n\nThis creates more visible color distinctions between info (blue), warning (yellow/orange), danger (red), and success (green) callouts.\n\n## Dont\n- Do NOT change light mode callout colors\n- Do NOT modify the callout component structure\n- Do NOT change the logo dimensions or layout position\n- Do NOT remove the logo from the header","acceptance_criteria":"1. Dark mode logo renders as clean white (no filter artifacts or color tinting)\n2. The filter:invert(1) rule is removed from globals.css (if Option A used)\n3. Light mode logo is unchanged (black)\n4. In dark mode, the four callout types (info, warning, danger, success) are visually distinguishable from each other\n5. Callout backgrounds have noticeably different hues in dark mode\n6. Light mode callouts are unchanged\n7. Build passes: npm run build -- --webpack","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":35,"created_at":"2026-02-20T19:56:35.730769+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.756819+08:00","closed_at":"2026-02-20T20:06:32.756819+08:00","close_reason":"c617130 dark mode logo + callout colors","labels":["scope:small"],"dependencies":[{"issue_id":"docs-sw5.2","depends_on_id":"docs-sw5","type":"parent-child","created_at":"2026-02-20T19:56:35.731611+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-urb","title":"Epic: Tutorials \u0026 Use Cases","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:47:47.564891+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:08:18.099134+13:00","closed_at":"2026-02-15T01:08:18.099134+13:00","close_reason":"All children complete: urb.1 (common use cases), urb.2 (creating first hypercert), urb.3 (working with evaluations)"} -{"id":"docs-urb.1","title":"Write 'Common Use Cases' page with 4 real-world scenarios","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe's equivalent: Their \"Common use cases\" section with pages like \"Accept simple payments as a startup\", \"Sell subscriptions as a SaaS startup\", etc. Each shows a concrete scenario with the relevant API calls.\n\nThe hypercerts docs currently have examples embedded in lexicon pages (NumPy maintainers, forest stewardship) but no dedicated use-case pages that show the full workflow.\n\n## Files\n- documentation/pages/tutorials/common-use-cases.md (create)\n- documentation/lib/navigation.js (modify — add new \"Tutorials\" section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/tutorials/common-use-cases.md` (180–260 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Common Use Cases\ndescription: See how hypercerts work for different types of contributions.\n---\n```\n\n**Sections:**\n\n1. **Introduction** (2-3 paragraphs) — Hypercerts are domain-agnostic. This page shows four concrete scenarios to illustrate how the protocol adapts to different types of work.\n\n2. **Use Case 1: Open-Source Software Maintenance** \n - Scenario: A team of 5 developers maintained NumPy throughout 2025\n - What they create:\n - Activity claim: title=\"NumPy Maintenance 2025\", workScope with allOf=[\"numpy\", \"maintenance\"], startDate/endDate for 2025\n - Contributions: one per developer, each with their DID, role=\"maintainer\"\n - Evidence: links to GitHub commits, release notes, CI dashboards\n - Measurements: lines of code, issues resolved, releases published\n - Show the JSON for the activity claim record (use the schema from `documentation/pages/lexicons/hypercerts-lexicons/activity-claim.md`)\n - Who funds this: foundations like the NumFOCUS, corporate sponsors\n\n3. **Use Case 2: Regenerative Land Stewardship**\n - Scenario: A community organization stewarded 500 hectares of forest in Costa Rica from 2020-2025\n - What they create:\n - Activity claim with location reference, workScope with allOf=[\"forest-stewardship\", \"biodiversity\"]\n - Evidence: satellite imagery, biodiversity surveys\n - Measurements: hectares restored, species count, carbon sequestered\n - Location record using the `app.certified.location` lexicon with geojson-point\n - Who funds this: climate funds, carbon credit buyers, government grants\n\n4. **Use Case 3: Scientific Research**\n - Scenario: A research group published a breakthrough paper on mRNA delivery mechanisms\n - What they create:\n - Activity claim for the research project\n - Contributions for each researcher with roles (PI, postdoc, lab technician)\n - Evidence: published paper DOI, dataset links, preprint\n - Evaluations: peer reviews from other scientists\n - Who funds this: research foundations, universities, retroactive public goods funding\n\n5. **Use Case 4: Community Event Organization**\n - Scenario: A local group organized a series of 12 monthly coding workshops for underrepresented groups\n - What they create:\n - Activity claim for the workshop series\n - Contributions for organizers and volunteer instructors\n - Evidence: attendance records, participant feedback, curriculum materials\n - Measurements: number of workshops, total attendees, completion rates\n - Who funds this: corporate diversity programs, community foundations\n\n6. **Choosing the Right Structure** — Brief guidance on:\n - When to use one hypercert vs. multiple (granularity)\n - How to scope work appropriately (link to deep-dive-the-work-scope)\n - When to use collections to group related hypercerts\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add a new top-level section AFTER \"Getting Started\" and BEFORE \"Lexicons\":\n\n```javascript\n{\n section: 'Tutorials',\n children: [\n { title: 'Common Use Cases', path: '/tutorials/common-use-cases' },\n ],\n},\n```\n\n## Writing style\n- Concrete and practical — show actual JSON records, not just descriptions\n- Each use case should feel like a mini-tutorial\n- Use `##` for main sections (each use case), `####` for subsections within\n- Use fenced code blocks with ```json for record examples\n- Link to relevant lexicon pages when referencing specific record types\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT add images or new components.\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT invent lexicon fields that do not exist — use only fields documented in the existing lexicon pages.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:50:37.291363+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:30:16.317876+13:00","closed_at":"2026-02-15T00:30:16.317884+13:00","dependencies":[{"issue_id":"docs-urb.1","depends_on_id":"docs-urb","type":"parent-child","created_at":"2026-02-14T20:50:37.292573+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":17,"issue_id":"docs-urb.1","author":"Sharfy Adamantine","text":"NAV UPDATE: Navigation reorganized. Add the 'Tutorials' section AFTER 'Lexicons'. Use real SDK patterns: @hypercerts-org/sdk-core, createATProtoSDK(), repo.hypercerts.create(). Reference certified.app for account creation. No 'See also' sections.","created_at":"2026-02-14T11:24:46Z"}]} -{"id":"docs-urb.2","title":"Write 'Creating Your First Hypercert' tutorial page","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe's equivalent: \"Accept a payment\" tutorial — a step-by-step walkthrough that goes deeper than the quickstart. While the quickstart (separate task) shows the minimal path, this tutorial explains each step, shows variations, and covers edge cases.\n\n## Files\n- documentation/pages/tutorials/creating-your-first-hypercert.md (create)\n- documentation/lib/navigation.js (modify — add entry under Tutorials section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/tutorials/creating-your-first-hypercert.md` (150–220 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Creating Your First Hypercert\ndescription: A step-by-step guide to creating a complete hypercert with contributions, evidence, and evaluations.\n---\n```\n\n**Sections:**\n\n1. **Introduction** — This tutorial walks through creating a complete hypercert for an open-source project. Unlike the quickstart (which creates a minimal claim), this tutorial shows how to attach contributions, evidence, and measurements.\n\n2. **Prerequisites** — Link to the Installing the SDK page. Assume the reader has a working SDK setup and authenticated session.\n\n3. **Step 1: Plan Your Hypercert** — Before writing code, decide:\n - What work are you claiming? (scope)\n - Who contributed? (contributors)\n - What time period? (start/end dates)\n - What evidence do you have? (links, documents)\n - Use a concrete example: \"Documenting the Hypercerts Protocol, Q1 2025\"\n\n4. **Step 2: Create the Activity Claim** — Full TypeScript code example:\n - Create an `org.hypercerts.claim.activity` record\n - Include all fields: title, shortDescription, description, workScope (with allOf/anyOf/noneOf), startDate, endDate, createdAt\n - Show the workScope object with concrete values\n - Explain each field with inline comments\n - Show the response (AT URI and CID)\n\n5. **Step 3: Add Contributions** — Create contribution records:\n - Create an `org.hypercerts.claim.contribution` record for each contributor\n - Show how to set contributors (array of DIDs), role, description\n - Show how to link contributions to the activity claim using strong references\n - Explain that contributions can be on the same or different PDS\n\n6. **Step 4: Attach Evidence** — Create evidence records:\n - Create an `org.hypercerts.claim.evidence` record\n - Show both URI-based evidence (linking to a GitHub repo) and blob-based evidence (uploading a document)\n - Link evidence to the activity claim\n\n7. **Step 5: Add Measurements** — Create measurement records:\n - Create an `org.hypercerts.claim.measurement` record\n - Show a concrete example: metric=\"documentation-pages-written\", value=\"15\"\n - Link to the activity claim\n\n8. **Step 6: Verify Everything** — Read back all records and show how they connect:\n - List all records in the activity claim collection\n - Show how strong references link records together\n - Verify the complete hypercert structure\n\n9. **What's Next** — Card links to:\n - \"Common Use Cases\" for more scenarios\n - \"Evaluation\" lexicon for how others can evaluate your hypercert\n - \"Collections\" for grouping multiple hypercerts\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add under the Tutorials section (which should exist from the Common Use Cases task):\n\n```javascript\n{ title: 'Creating Your First Hypercert', path: '/tutorials/creating-your-first-hypercert' },\n```\n\n## Writing style\n- Tutorial style: \"we\" voice, step-by-step, explain as you go\n- Every code block should be complete and runnable (with placeholder auth)\n- Use `{% callout %}` for tips and important notes\n- Use fenced code blocks with ```typescript for code, ```json for data structures\n- Reference lexicon field names exactly as documented in existing lexicon pages\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT add images or new components.\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT invent lexicon fields that do not exist in the existing lexicon documentation.\n- Do NOT duplicate the quickstart content — this goes deeper.","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:51:00.775834+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:32:22.691723+13:00","closed_at":"2026-02-15T00:32:22.691728+13:00","dependencies":[{"issue_id":"docs-urb.2","depends_on_id":"docs-urb","type":"parent-child","created_at":"2026-02-14T20:51:00.777066+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-urb.2","depends_on_id":"docs-cfx.3","type":"blocks","created_at":"2026-02-14T20:54:30.584248+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":18,"issue_id":"docs-urb.2","author":"Sharfy Adamantine","text":"NAV UPDATE: Navigation reorganized. Add under 'Tutorials' section (after 'Lexicons' in nav). Use real SDK patterns: @hypercerts-org/sdk-core, createATProtoSDK(), repo.hypercerts.create(). Reference certified.app for account creation. No 'See also' sections. Keep it Stripe-style: action-first, code-heavy, minimal preamble.","created_at":"2026-02-14T11:24:52Z"}]} -{"id":"docs-urb.3","title":"Write 'Working with Evaluations' tutorial page","description":"## Context\n\nThis project is a documentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. The site lives in `documentation/`. Pages are Markdoc `.md` files in `documentation/pages/`. Navigation is defined in `documentation/lib/navigation.js`. Available Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nEvaluations are a core differentiator of the Hypercerts Protocol — they allow third parties to assess claims. The existing evaluation lexicon page (`documentation/pages/lexicons/hypercerts-lexicons/evaluation.md`) documents the schema but does not show how to use it in practice.\n\n## Files\n- documentation/pages/tutorials/working-with-evaluations.md (create)\n- documentation/lib/navigation.js (modify — add entry under Tutorials section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/tutorials/working-with-evaluations.md` (120–170 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Working with Evaluations\ndescription: Learn how to evaluate hypercerts and build trust in the ecosystem.\n---\n```\n\n**Sections:**\n\n1. **What are Evaluations?** (3-4 paragraphs)\n - Evaluations are third-party assessments of hypercert claims\n - They are separate records on the evaluator's own PDS (not embedded in the original claim)\n - Multiple evaluators can independently assess the same claim\n - Evaluations accumulate over time — they are never \"final\"\n - Reference the evaluation lexicon: `org.hypercerts.claim.evaluation`\n\n2. **Creating an Evaluation** — TypeScript code walkthrough:\n - Authenticate as the evaluator (different DID from the claim creator)\n - Create an `org.hypercerts.claim.evaluation` record\n - Set the `subject` field as a strong reference to the activity claim being evaluated\n - Set `evaluators` array with the evaluator's DID(s)\n - Write a `summary` \n - Optionally link to detailed evaluation documents via `evaluations` array (URIs or blobs)\n - Show the complete code and expected response\n\n3. **Evaluation Patterns** — Common approaches:\n - **Expert Review**: A domain expert evaluates a single claim in depth\n - **Community Assessment**: Multiple community members each create lightweight evaluations\n - **Automated Evaluation**: An AI or automated system creates evaluations based on data analysis\n - For each pattern, show a brief code snippet or JSON example\n\n4. **Linking Measurements to Evaluations** — How measurements support evaluations:\n - Measurements (`org.hypercerts.claim.measurement`) provide quantitative data\n - Evaluations can reference measurements as supporting evidence\n - Show how an evaluator might create measurements first, then reference them in their evaluation\n\n5. **Querying Evaluations** — How to find evaluations for a given hypercert:\n - Explain that indexers aggregate evaluations across PDS instances\n - Show a conceptual query: \"find all evaluations where subject references this activity claim\"\n - Note that the specific query API depends on the indexer implementation\n\n6. **Trust and Reputation** — How evaluations build trust:\n - Evaluator identity is tied to their DID\n - Over time, evaluators build a track record\n - Platforms can weight evaluations based on evaluator reputation\n - This creates incentives for high-quality, honest evaluations\n\n### 2. Add to navigation\n\nIn `documentation/lib/navigation.js`, add under the Tutorials section:\n\n```javascript\n{ title: 'Working with Evaluations', path: '/tutorials/working-with-evaluations' },\n```\n\n## Writing style\n- Tutorial style with conceptual depth\n- Use `##` for main sections, `####` for subsections\n- Use fenced code blocks with ```typescript for code, ```json for data\n- Reference the evaluation lexicon page: link to `/lexicons/hypercerts-lexicons/evaluation`\n- Use `{% callout %}` for important conceptual points\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0 (static export succeeds).\n\n## Dont\n- Do NOT add images or new components.\n- Do NOT modify any existing pages other than navigation.js.\n- Do NOT use HTML tags — use Markdoc syntax only.\n- Do NOT invent lexicon fields not in the existing evaluation/measurement lexicon docs.","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:51:21.622368+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:30:20.611995+13:00","closed_at":"2026-02-15T00:30:20.611998+13:00","dependencies":[{"issue_id":"docs-urb.3","depends_on_id":"docs-urb","type":"parent-child","created_at":"2026-02-14T20:51:21.623548+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-uxs","title":"Epic: Testing, Security \u0026 Operations","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:47:53.388787+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:08:19.713621+13:00","closed_at":"2026-02-15T01:08:19.713621+13:00","close_reason":"All children complete: uxs.1 (testing \u0026 security)"} -{"id":"docs-uxs.1","title":"Write 'Testing \u0026 Security' page","description":"## Context\n\nDocumentation site for the Hypercerts Protocol, built with Next.js 16 + Markdoc. Site lives in `documentation/`. Pages are `.md` files in `documentation/pages/`. Nav in `documentation/lib/navigation.js`. Markdoc tags: `{% callout %}`, `{% columns %}`, `{% column %}`, `{% figure %}`, `{% card-link %}`.\n\nStripe has dedicated \"Testing\" and \"Security\" pages. Hypercerts has neither. This single page covers both since the protocol is earlier-stage than Stripe.\n\n## Files\n- documentation/pages/reference/testing-and-security.md (create)\n- documentation/lib/navigation.js (modify — add entry under Reference section)\n\n## What to do\n\n### 1. Create the page\n\nCreate `documentation/pages/reference/testing-and-security.md` (120–170 lines of Markdoc):\n\n**Frontmatter:**\n```\n---\ntitle: Testing \u0026 Security\ndescription: How to test your integration and understand the security model.\n---\n```\n\n**Sections:**\n\n1. **Testing Your Integration** (~50 lines)\n - **Local Development**: How to run a local PDS for testing (link to ATProto docs for self-hosting). Use `{% callout %}` to note this is the recommended approach.\n - **Test Identities**: Create throwaway DIDs for testing. Do not use production identities.\n - **Sandbox Pattern**: Create test activity claims with a workScope tag like \"test\" so they are easily identifiable and can be cleaned up.\n - **Verifying Records**: Show a TypeScript snippet that reads back a record by AT URI using `com.atproto.repo.getRecord` and asserts the fields match expectations.\n - **Cleanup**: How to delete test records using `com.atproto.repo.deleteRecord`.\n\n2. **Security Model** (~40 lines)\n - **Signed Repositories**: Every PDS repository is a signed Merkle tree. Records are tamper-evident.\n - **Strong References**: References include a CID (content hash), so you can verify the referenced record has not changed.\n - **Identity Verification**: DIDs are cryptographically verifiable. Handles can be verified via DNS (did:web) or PLC directory (did:plc).\n - **Data Integrity**: How the combination of signed repos + CID references creates an auditable chain of claims and evaluations.\n\n3. **Authentication Best Practices** (~30 lines)\n - Use app passwords, never main passwords, in code\n - Rotate app passwords regularly\n - Store credentials in environment variables, never in source code\n - Use `{% callout type=\"warning\" %}` for the credential storage warning\n - OAuth for production applications (brief mention, link to ATProto OAuth docs)\n\n4. **Privacy Considerations** (~20 lines)\n - All ATProto records are public by default — do not store sensitive personal data in hypercert records\n - What to include vs. what to keep off-protocol\n - GDPR considerations: right to deletion exists (records can be deleted from PDS), but cached copies may persist in indexers\n - Use `{% callout type=\"warning\" %}` for the \"records are public\" warning\n\n5. **Going Live Checklist** (~15 lines) — Bulleted checklist:\n - [ ] Verified your DID and handle are correct\n - [ ] Tested record creation and reading in a sandbox\n - [ ] Confirmed all required fields pass validation\n - [ ] Stored credentials securely (env vars, not source)\n - [ ] Reviewed records for accidental PII\n - [ ] Tested cross-PDS references if applicable\n\n### 2. Add to navigation\n\nAdd under the Reference section in `documentation/lib/navigation.js`:\n```javascript\n{ title: 'Testing \u0026 Security', path: '/reference/testing-and-security' },\n```\nIf the Reference section does not yet exist, create it after Architecture.\n\n## Test\n```\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5\n```\nMust exit 0.\n\n## Dont\n- Do NOT add images or new components.\n- Do NOT modify existing pages other than navigation.js.\n- Do NOT use HTML tags.\n- Do NOT invent ATProto API methods that do not exist — `com.atproto.repo.getRecord`, `createRecord`, `deleteRecord` are real.","status":"closed","priority":2,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-14T20:53:28.975298+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T01:04:35.690648+13:00","closed_at":"2026-02-15T01:04:35.690648+13:00","close_reason":"Completed: Created Testing \u0026 Security reference page with local dev setup, test patterns, security model, auth best practices, privacy considerations, and go-live checklist. Added nav entry under Reference section. Build passes.","dependencies":[{"issue_id":"docs-uxs.1","depends_on_id":"docs-uxs","type":"parent-child","created_at":"2026-02-14T20:53:28.976166+13:00","created_by":"Sharfy Adamantine"}],"comments":[{"id":19,"issue_id":"docs-uxs.1","author":"Sharfy Adamantine","text":"NAV UPDATE: Add under 'Reference' section. Full nav order: Get Started → Core Concepts → Architecture → Lexicons → Tutorials → Reference.","created_at":"2026-02-14T11:25:02Z"},{"id":20,"issue_id":"docs-uxs.1","author":"Sharfy Adamantine","text":"DESIGN LANGUAGE: Follow Stripe docs (docs.stripe.com) patterns — one-sentence opener, code-first, short paragraphs, no preamble, no link dumps. BUILD COMMAND FIX: Use 'cd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5' (must include --webpack flag). NAV PLACEMENT: Add under Reference section AFTER 'Tutorials'.","created_at":"2026-02-14T11:58:20Z"}]} -{"id":"docs-vbw","title":"Epic 2: Build Custom Markdoc Tags and React Components","description":"## Summary\nCreate custom Markdoc tag definitions and corresponding React components to replace all GitBook-proprietary syntax used in the documentation. These tags will be used when converting content files in later epics.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory. The previous documentation was built with GitBook, which uses proprietary template tags. We need Markdoc equivalents.\n\nThe project uses `@markdoc/next.js` which automatically discovers tag definitions in `markdoc/tags/` and makes them available in `.md` files.\n\n### GitBook syntax that needs Markdoc replacements:\n\n1. **Columns layout** -- Used in `README.md` (2 column groups):\n```\n{% columns %}\n{% column %}\nContent here\n{% endcolumn %}\n{% column %}\nMore content\n{% endcolumn %}\n{% endcolumns %}\n```\n\n2. **Hint/callout boxes** -- Used in `deep-dive-the-work-scope.md` (1 instance):\n```\n{% hint style=\"info\" %}\nThis is an informational callout.\n{% endhint %}\n```\n\n3. **Figure/image with caption** -- Used in `README.md` (2 instances) and `introduction-to-impact-claims.md` (1 instance):\n```html\n\u003cfigure\u003e\u003cimg src=\".gitbook/assets/image.png\" alt=\"\"\u003e\u003cfigcaption\u003e\u003c/figcaption\u003e\u003c/figure\u003e\n```\n\n### How Markdoc tags work with @markdoc/next.js:\n\n1. Tag definitions go in `markdoc/tags/*.markdoc.js` -- these define the tag name, allowed attributes, and which React component to render\n2. React components go in `components/` -- these render the actual HTML\n3. `@markdoc/next.js` auto-discovers tag files in `markdoc/tags/` and wires them up\n4. Components are registered in `pages/_app.js` via a components mapping passed to the renderer\n\n## What You Need to Create\n\n### 1. Callout Tag\n\n**Tag definition** -- `markdoc/tags/callout.markdoc.js`:\n```js\nexport const callout = {\n render: 'Callout',\n children: ['paragraph', 'tag', 'list'],\n attributes: {\n type: {\n type: String,\n default: 'info',\n matches: ['info', 'warning', 'danger', 'success'],\n errorLevel: 'critical'\n },\n title: {\n type: String\n }\n }\n};\n```\n\n**React component** -- `components/Callout.js`:\n\nVisual design (Stripe-inspired):\n- Container with a 4px left border in accent color\n- Light tinted background matching the type\n- Border-radius: 6px on the right side (or all corners)\n- Padding: 16px\n- Body text in regular weight, dark gray\n- If title provided, render it bold above the body\n\nColor mapping by type:\n- `info`: blue left border (#0570de), very light blue background (#f0f7ff)\n- `warning`: orange left border (#c84801), very light orange background (#fef9f0)\n- `danger`: red left border (#df1b41), very light red background (#fef0f4)\n- `success`: green left border (#228403), very light green background (#f0fef0)\n\nThe component should accept `type`, `title`, and `children` props.\n\n**Markdoc usage** (what content authors will write):\n```\n{% callout type=\"info\" %}\nThis is an informational callout.\n{% /callout %}\n```\n\n### 2. Columns / Column Tags\n\n**Tag definitions** -- `markdoc/tags/columns.markdoc.js`:\n```js\nexport const columns = {\n render: 'Columns',\n children: ['tag'],\n attributes: {\n gap: {\n type: String,\n default: '24px'\n }\n }\n};\n```\n\n`markdoc/tags/column.markdoc.js`:\n```js\nexport const column = {\n render: 'Column',\n children: ['paragraph', 'tag', 'list', 'heading', 'image', 'fence'],\n};\n```\n\n**React components**:\n\n`components/Columns.js`:\n- CSS flexbox container: `display: flex; gap: 24px;`\n- On mobile (\u003c 768px): `flex-direction: column` (stack vertically)\n- On desktop: `flex-direction: row` (side by side)\n\n`components/Column.js`:\n- `flex: 1` (equal width columns)\n- Renders children directly\n\n**Markdoc usage**:\n```\n{% columns %}\n{% column %}\nLeft content\n{% /column %}\n{% column %}\nRight content\n{% /column %}\n{% /columns %}\n```\n\n### 3. Figure Tag\n\n**Tag definition** -- `markdoc/tags/figure.markdoc.js`:\n```js\nexport const figure = {\n render: 'Figure',\n selfClosing: true,\n attributes: {\n src: { type: String, required: true },\n alt: { type: String, default: '' },\n caption: { type: String }\n }\n};\n```\n\n**React component** -- `components/Figure.js`:\n\nVisual design:\n- Centered container (`text-align: center; margin: 24px 0`)\n- Image with `max-width: 100%; height: auto; border-radius: 6px`\n- Caption (if provided) rendered below in small text (13px), secondary gray color (#687385), margin-top: 8px\n\n**Markdoc usage**:\n```\n{% figure src=\"/images/hypercert-erd.png\" alt=\"Hypercert ERD\" caption=\"Entity relationship diagram\" /%}\n```\n\n### 4. Export all tags -- `markdoc/tags/index.js`:\n```js\nexport { callout } from './callout.markdoc';\nexport { columns } from './columns.markdoc';\nexport { column } from './column.markdoc';\nexport { figure } from './figure.markdoc';\n```\n\n### 5. Register components in `pages/_app.js`:\nUpdate the existing `_app.js` to pass components:\n```jsx\nimport '../styles/globals.css';\nimport { Callout } from '../components/Callout';\nimport { Columns } from '../components/Columns';\nimport { Column } from '../components/Column';\nimport { Figure } from '../components/Figure';\n\nconst components = {\n Callout,\n Columns,\n Column,\n Figure,\n};\n\nexport default function App({ Component, pageProps }) {\n return \u003cComponent {...pageProps} components={components} /\u003e;\n}\n```\n\nNote: With `@markdoc/next.js`, components can also be passed via the Markdoc config. Check the @markdoc/next.js docs if the above pattern doesn't wire up correctly -- the alternative is to use a `markdoc/config.js` file.\n\n### 6. Verify\nCreate a test page `pages/test-tags.md` with all three tags to verify they render:\n```markdown\n---\ntitle: Tag Test\n---\n\n# Tag Test\n\n{% callout type=\"info\" %}\nThis is an info callout.\n{% /callout %}\n\n{% callout type=\"warning\" title=\"Warning\" %}\nThis is a warning.\n{% /callout %}\n\n{% columns %}\n{% column %}\n**Left column** with some content.\n{% /column %}\n{% column %}\n**Right column** with some content.\n{% /column %}\n{% /columns %}\n\n{% figure src=\"/images/hypercert-erd.png\" alt=\"Test image\" caption=\"A test caption\" /%}\n```\n\nRun `npm run dev` and verify all tags render correctly. Delete the test page after verification.\n\n## Acceptance Criteria\n- `markdoc/tags/callout.markdoc.js` exists with type attribute (info, warning, danger, success)\n- `components/Callout.js` renders a styled callout box with 4px colored left border and tinted background\n- `markdoc/tags/columns.markdoc.js` and `markdoc/tags/column.markdoc.js` exist\n- `components/Columns.js` renders a CSS flexbox multi-column layout that stacks on mobile\n- `components/Column.js` renders individual column content with flex: 1\n- `markdoc/tags/figure.markdoc.js` exists as a self-closing tag with src, alt, caption attributes\n- `components/Figure.js` renders a centered image with optional gray caption text\n- `markdoc/tags/index.js` exports all tags\n- Components are registered in `pages/_app.js` so Markdoc can render them\n- All tags render correctly when used in .md files via `npm run dev`\n","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:42:05.192802+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:04:51.759083+13:00","closed_at":"2026-02-14T13:04:51.759115+13:00","dependencies":[{"issue_id":"docs-vbw","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:42:05.194443+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-vuj","title":"Epic: Reorganize Infrastructure Page \u0026 Glossary","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:31:03.804651+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:07:13.683828+13:00","closed_at":"2026-02-16T12:07:13.683828+13:00","close_reason":"All 4 children completed: glossary enhanced, infrastructure split into 4 pages, navigation updated with collapsible group, build verified"} -{"id":"docs-vuj.1","title":"Enhance glossary with developer key concepts from infrastructure page","description":"## Files\n- documentation/pages/reference/glossary.md (modify)\n\n## What to do\n\nThe 'Key Concepts for Developers' section in the-hypercerts-infrastructure.md has richer, developer-oriented definitions for DID, PDS, Lexicon, XRPC, and Repository. Merge this content into the existing glossary page.\n\n### Specific changes:\n\n1. **DID entry** — Replace the current 1-line definition with the 2-paragraph version from infrastructure page. Keep the format: `did:plc:abc123xyz` example. Add the note about persistent identity across platforms.\n\n2. **PDS entry** — Replace with the 2-paragraph version. Include the note about XRPC, data portability, and that apps are views over your data.\n\n3. **Lexicon entry** — Replace with the version that includes the concrete example (`org.hypercerts.claim.activity` with field names). Add cross-link to [Introduction to Lexicons](/lexicons/introduction-to-lexicons).\n\n4. **XRPC entry** — Replace with the version that mentions `com.atproto.repo.createRecord` and `com.atproto.repo.getRecord` examples. Add note about SDK wrapping XRPC calls.\n\n5. **Repository** — Add new entry (does not exist in current glossary). Include the Merkle Search Tree description, versioning via commits, CID content hashing, and tamper-evidence.\n\n### Format rules:\n- Keep alphabetical ordering of all entries\n- Each entry: `#### Term Name` followed by 1-2 paragraphs\n- Keep existing entries that are NOT being replaced (Activity Claim, CID, Collection, Contribution, Evaluation, Evidence, Hypercert, Indexer, Measurement, Relay, Rights, SDS, Strong Reference, Work Scope)\n- Do NOT add any new terms beyond the 5 listed above\n- Cross-link to other docs pages where the infrastructure page did (e.g., Lexicon entry links to Introduction to Lexicons)\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require('fs');\nconst content = fs.readFileSync('pages/reference/glossary.md', 'utf8');\nconst required = ['#### Repository', 'Merkle Search Tree', 'org.hypercerts.claim.activity', 'com.atproto.repo.createRecord', 'did:plc:', 'Introduction to Lexicons'];\nconst missing = required.filter(r =\u003e !content.includes(r));\nif (missing.length) { console.error('Missing:', missing); process.exit(1); }\n// Check alphabetical order of #### headings\nconst headings = [...content.matchAll(/^#### (.+)$/gm)].map(m =\u003e m[1]);\nconst sorted = [...headings].sort((a, b) =\u003e a.localeCompare(b));\nif (JSON.stringify(headings) !== JSON.stringify(sorted)) { console.error('Not alphabetical:', headings); process.exit(1); }\nconsole.log('PASS: glossary enhanced correctly');\n\"\n```\n\n## Don't\n- Remove any existing glossary entries\n- Change the page title or frontmatter\n- Add entries not listed above (no new terms)\n- Change the H4 heading format for entries","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:31:33.853221+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:01:19.279813+13:00","closed_at":"2026-02-16T12:01:19.279813+13:00","close_reason":"91a76a2 Enhanced glossary with richer developer-oriented definitions from infrastructure page","labels":["scope:small"],"dependencies":[{"issue_id":"docs-vuj.1","depends_on_id":"docs-vuj","type":"parent-child","created_at":"2026-02-16T11:31:33.855+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-vuj.2","title":"Split infrastructure page into 4 sub-pages under infrastructure/ directory","description":"## Files\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify — becomes short overview)\n- documentation/pages/getting-started/infrastructure/indexers-and-discovery.md (create)\n- documentation/pages/getting-started/infrastructure/blockchain-integration.md (create)\n- documentation/pages/getting-started/infrastructure/portability-and-scaling.md (create)\n\n## What to do\n\nThe current infrastructure page is ~193 lines covering 9 sections. Split it into a short overview page + 3 sub-pages. The overview keeps the core architecture explanation; sub-pages get the deep-dive topics.\n\n### 1. Rewrite `the-hypercerts-infrastructure.md` to be a SHORT overview (~60 lines max)\n\nKeep these sections from the original:\n- **The Two-Layer Architecture** (lines 11-29) — keep as-is\n- **How Hypercerts Data Flows** (lines 63-87) — keep as-is\n- **Data Integrity and Trust** (lines 89-103) — keep as-is\n\nREMOVE these sections entirely:\n- **Key Concepts for Developers** (lines 31-61) — being moved to glossary by another task. Replace with a single line: `For definitions of DID, PDS, Lexicon, XRPC, and Repository, see the [Glossary](/reference/glossary).`\n- **Developer Resources** (lines 189-192) — fold into a callout at the bottom\n\nAdd a \"Keep reading\" section at the bottom with links to the 3 sub-pages:\n```\n## Keep Reading\n\n- [Indexers \u0026 Discovery](/getting-started/infrastructure/indexers-and-discovery) — how data is aggregated and queried across the network\n- [Blockchain Integration](/getting-started/infrastructure/blockchain-integration) — minting patterns and on-chain ownership\n- [Portability \u0026 Scaling](/getting-started/infrastructure/portability-and-scaling) — PDS migration, performance, and privacy\n```\n\n### 2. Create `infrastructure/indexers-and-discovery.md`\n\nFrontmatter:\n```yaml\n---\ntitle: Indexers \u0026 Discovery\n---\n```\n\nContent: Move the **Indexers and Discovery** section (lines 105-121) from the original page. Keep all 3 subsections (Why indexers?, What indexers do, Running your own indexer). Keep the code-style API endpoint examples.\n\n### 3. Create `infrastructure/blockchain-integration.md`\n\nFrontmatter:\n```yaml\n---\ntitle: Blockchain Integration\n---\n```\n\nContent: Move the **Blockchain Integration Patterns** section (lines 123-139) from the original page. Keep all 4 patterns (Mint-on-create, Lazy minting, Batch anchoring, Hybrid ownership). Keep the cross-link to the Rights lexicon.\n\n### 4. Create `infrastructure/portability-and-scaling.md`\n\nFrontmatter:\n```yaml\n---\ntitle: Portability \u0026 Scaling\n---\n```\n\nContent: Combine these 3 sections from the original page:\n- **Migration and Portability** (lines 141-158)\n- **Performance and Scalability** (lines 160-172)\n- **Privacy and Access Control** (lines 174-186)\n\nKeep all subsections and content. Use H2 headings for the 3 major sections.\n\n### Format rules:\n- Each new page starts with `# {% $markdoc.frontmatter.title %}` after frontmatter\n- Use H2 (##) for major sections, H4 (####) for subsections (matching existing style)\n- Preserve all cross-links to other pages\n- No content should be lost — every paragraph from the original must appear in exactly one of the 4 files\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require('fs');\nconst path = require('path');\n\n// Check all files exist\nconst files = [\n 'pages/getting-started/the-hypercerts-infrastructure.md',\n 'pages/getting-started/infrastructure/indexers-and-discovery.md',\n 'pages/getting-started/infrastructure/blockchain-integration.md',\n 'pages/getting-started/infrastructure/portability-and-scaling.md',\n];\nfor (const f of files) {\n if (!fs.existsSync(f)) { console.error('Missing:', f); process.exit(1); }\n}\n\n// Check overview is short\nconst overview = fs.readFileSync(files[0], 'utf8');\nconst overviewLines = overview.split('\\n').length;\nif (overviewLines \u003e 80) { console.error('Overview too long:', overviewLines, 'lines (max 80)'); process.exit(1); }\n\n// Check Key Concepts removed from overview\nif (overview.includes('## Key Concepts for Developers')) { console.error('Key Concepts section still in overview'); process.exit(1); }\n\n// Check glossary link added\nif (!overview.includes('[Glossary](/reference/glossary)')) { console.error('Missing glossary link in overview'); process.exit(1); }\n\n// Check sub-pages have required content\nconst indexers = fs.readFileSync(files[1], 'utf8');\nif (!indexers.includes('firehose')) { console.error('Indexers page missing firehose content'); process.exit(1); }\n\nconst blockchain = fs.readFileSync(files[2], 'utf8');\nif (!blockchain.includes('Lazy minting')) { console.error('Blockchain page missing Lazy minting'); process.exit(1); }\nif (!blockchain.includes('Batch anchoring')) { console.error('Blockchain page missing Batch anchoring'); process.exit(1); }\n\nconst portability = fs.readFileSync(files[3], 'utf8');\nif (!portability.includes('Switching PDSs')) { console.error('Portability page missing PDS migration'); process.exit(1); }\nif (!portability.includes('Public by default')) { console.error('Portability page missing privacy section'); process.exit(1); }\n\n// Check Keep Reading links in overview\nif (!overview.includes('/getting-started/infrastructure/indexers-and-discovery')) { console.error('Missing indexers link in overview'); process.exit(1); }\nif (!overview.includes('/getting-started/infrastructure/blockchain-integration')) { console.error('Missing blockchain link in overview'); process.exit(1); }\nif (!overview.includes('/getting-started/infrastructure/portability-and-scaling')) { console.error('Missing portability link in overview'); process.exit(1); }\n\nconsole.log('PASS: infrastructure split correctly');\n\"\n```\n\n## Don't\n- Change any content wording (only move content between files)\n- Add new content that wasn't in the original page\n- Remove the frontmatter from the overview page\n- Use H3 (###) headings — use H2 and H4 to match existing site style\n- Create an index.md in the infrastructure/ directory","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:32:03.659396+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:01:56.630712+13:00","closed_at":"2026-02-16T12:01:56.630712+13:00","close_reason":"2a1d330 Split infrastructure page into overview + 3 sub-pages","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-vuj.2","depends_on_id":"docs-vuj","type":"parent-child","created_at":"2026-02-16T11:32:03.660787+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-vuj.3","title":"Update navigation.js — make Infrastructure a collapsible sidebar group with sub-pages","description":"## Files\n- documentation/lib/navigation.js (modify)\n\n## What to do\n\nIn the 'Understand' section of the navigation array, replace the flat 'The Hypercerts Infrastructure' entry with a collapsible group that has children — exactly like the existing 'Lexicons' entry in the 'Reference' section.\n\n### Current entry (line 20):\n```js\n{ title: 'The Hypercerts Infrastructure', path: '/getting-started/the-hypercerts-infrastructure' },\n```\n\n### Replace with:\n```js\n{\n title: 'The Hypercerts Infrastructure',\n path: '/getting-started/the-hypercerts-infrastructure',\n children: [\n { title: 'Indexers \u0026 Discovery', path: '/getting-started/infrastructure/indexers-and-discovery' },\n { title: 'Blockchain Integration', path: '/getting-started/infrastructure/blockchain-integration' },\n { title: 'Portability \u0026 Scaling', path: '/getting-started/infrastructure/portability-and-scaling' },\n ],\n},\n```\n\nThis makes \"The Hypercerts Infrastructure\" clickable (goes to the overview page) AND expandable (shows 3 sub-pages). The Sidebar component already supports this pattern — see the Lexicons entry.\n\n### No other changes to navigation.js\n\nThe rest of the navigation array must remain exactly as-is. Do not reorder, rename, or remove any other entries.\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst { navigation } = require('./lib/navigation.js');\n\n// Find the Understand section\nconst understand = navigation.find(n =\u003e n.section === 'Understand');\nif (!understand) { console.error('No Understand section'); process.exit(1); }\n\n// Find the Infrastructure entry\nconst infra = understand.children.find(c =\u003e c.title === 'The Hypercerts Infrastructure');\nif (!infra) { console.error('No Infrastructure entry'); process.exit(1); }\n\n// Check it has the right path\nif (infra.path !== '/getting-started/the-hypercerts-infrastructure') {\n console.error('Wrong path:', infra.path); process.exit(1);\n}\n\n// Check it has children\nif (!infra.children || infra.children.length !== 3) {\n console.error('Expected 3 children, got:', infra.children?.length); process.exit(1);\n}\n\n// Check child paths\nconst expectedPaths = [\n '/getting-started/infrastructure/indexers-and-discovery',\n '/getting-started/infrastructure/blockchain-integration',\n '/getting-started/infrastructure/portability-and-scaling',\n];\nconst actualPaths = infra.children.map(c =\u003e c.path);\nfor (const ep of expectedPaths) {\n if (!actualPaths.includes(ep)) { console.error('Missing child path:', ep); process.exit(1); }\n}\n\n// Check total Understand children count unchanged (still 8 top-level items)\nif (understand.children.length !== 8) {\n console.error('Understand section should have 8 children, got:', understand.children.length); process.exit(1);\n}\n\n// Check other sections untouched\nconst sections = navigation.filter(n =\u003e n.section).map(n =\u003e n.section);\nconst expected = ['Get Started', 'Understand', 'Guides', 'Tools', 'Reference'];\nif (JSON.stringify(sections) !== JSON.stringify(expected)) {\n console.error('Sections changed:', sections); process.exit(1);\n}\n\nconsole.log('PASS: navigation updated correctly');\n\"\n```\n\n## Don't\n- Change any other navigation entries\n- Reorder sections or items\n- Remove the path from the Infrastructure entry (it must remain clickable)\n- Add any new exports or functions","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:32:21.026059+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:04:41.918664+13:00","closed_at":"2026-02-16T12:04:41.918664+13:00","close_reason":"4f61df1 Make Infrastructure a collapsible sidebar group with 3 sub-pages","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-vuj.3","depends_on_id":"docs-vuj","type":"parent-child","created_at":"2026-02-16T11:32:21.027769+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.3","depends_on_id":"docs-vuj.2","type":"blocks","created_at":"2026-02-16T11:32:40.404821+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-vuj.4","title":"Integration: verify build succeeds and no broken internal links after infrastructure split","description":"## Files\n- (no files to modify — verification only)\n\n## What to do\n\nRun the Next.js build and verify:\n1. Build completes without errors\n2. All 3 new infrastructure sub-pages are generated in the output\n3. The glossary page builds\n4. No broken internal links (grep for links to old anchors that no longer exist)\n\n### Steps:\n\n1. Run `npm run build` (or `npx next build`) in the documentation/ directory\n2. Verify the build exits 0\n3. Check that these output files exist in `out/` or `.next/`:\n - getting-started/infrastructure/indexers-and-discovery\n - getting-started/infrastructure/blockchain-integration\n - getting-started/infrastructure/portability-and-scaling\n4. Grep all .md files for any links pointing to anchors that were removed from the infrastructure page:\n - `#key-concepts-for-developers` should not appear in any file\n - `#developer-resources` should not appear in any file\n5. If any broken links are found, fix them (update to point to the glossary or the correct sub-page)\n\n## Test\n```bash\ncd documentation \u0026\u0026 npx next build 2\u003e\u00261 | tail -5 \u0026\u0026 echo \"BUILD_OK\"\n```\n\nThe output must contain \"BUILD_OK\" (meaning the build completed and the tail command ran).\n\nAdditionally:\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require('fs');\nconst glob = require('path');\n\n// Recursive file finder\nfunction findMd(dir) {\n let results = [];\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const full = dir + '/' + entry.name;\n if (entry.isDirectory() \u0026\u0026 entry.name !== 'node_modules' \u0026\u0026 entry.name !== '.next') {\n results = results.concat(findMd(full));\n } else if (entry.name.endsWith('.md')) {\n results.push(full);\n }\n }\n return results;\n}\n\nconst files = findMd('pages');\nlet broken = false;\nfor (const f of files) {\n const content = fs.readFileSync(f, 'utf8');\n if (content.includes('#key-concepts-for-developers')) {\n console.error('Broken anchor in', f, ': #key-concepts-for-developers');\n broken = true;\n }\n if (content.includes('#developer-resources')) {\n console.error('Broken anchor in', f, ': #developer-resources');\n broken = true;\n }\n}\nif (broken) process.exit(1);\nconsole.log('PASS: no broken anchors found');\n\"\n```\n\n## Don't\n- Modify any files unless broken links are found (this is primarily a verification task)\n- Skip the build step\n- Ignore build warnings that are actually errors","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-16T11:32:36.383122+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-16T12:06:47.257702+13:00","closed_at":"2026-02-16T12:06:47.257702+13:00","close_reason":"Verification complete: build succeeds, all infrastructure sub-pages generated, no broken anchor links","labels":["scope:small"],"dependencies":[{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj","type":"parent-child","created_at":"2026-02-16T11:32:36.384409+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj.1","type":"blocks","created_at":"2026-02-16T11:32:41.374978+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj.2","type":"blocks","created_at":"2026-02-16T11:32:41.519206+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-vuj.4","depends_on_id":"docs-vuj.3","type":"blocks","created_at":"2026-02-16T11:32:41.62861+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-w96","title":"Epic: Fix incorrect lexicon NSIDs and inaccurate data model descriptions across documentation","description":"The documentation references incorrect lexicon NSIDs and misrepresents how several record types work. The actual lexicons live in /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/. Key problems: (1) org.hypercerts.claim.contributionDetails doesn't exist — the real NSID is org.hypercerts.claim.contribution, (2) org.hypercerts.claim.collection doesn't exist — the real NSID is org.hypercerts.collection, (3) the core data model page misrepresents how contributors work (misses inline identity/role option), (4) missing record types (rights, funding receipt, acknowledgement, work scope). Success: every lexicon NSID in the docs matches the actual lexicon JSON files, and the core data model page accurately describes how records connect.","status":"open","priority":1,"issue_type":"epic","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","created_at":"2026-03-05T19:57:16.959712428+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:06:55.311522251+06:00","labels":["needs-integration-review","scope:medium"]} -{"id":"docs-w96.1","title":"Fix wrong lexicon NSIDs in lexicon index page","description":"## Files\n- pages/lexicons/hypercerts-lexicons/index.md (modify)\n\n## What to do\nFix two incorrect NSIDs in the lexicon index table:\n\n1. Row \"Contribution\": Change `org.hypercerts.claim.contributionDetails` to `org.hypercerts.claim.contribution`. The description says \"two lexicons\" — keep that, but list the correct NSIDs: `org.hypercerts.claim.contributorInformation` and `org.hypercerts.claim.contribution` (NOT contributionDetails).\n\n2. Row \"Collection\": Change `org.hypercerts.claim.collection` to `org.hypercerts.collection` (remove the `.claim.` segment).\n\nThe actual lexicon files are:\n- /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/claim/contribution.json (id: org.hypercerts.claim.contribution)\n- /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/collection.json (id: org.hypercerts.collection)\n\n## Dont\n- Do not change any other rows in the table\n- Do not change the page structure or add new content\n- Do not rename the linked page paths (the /lexicons/hypercerts-lexicons/contribution and /collection links stay the same)","acceptance_criteria":"1. The Contribution row NSID column contains `org.hypercerts.claim.contributorInformation` and `org.hypercerts.claim.contribution` (not contributionDetails)\n2. The Collection row NSID column contains `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n3. No other rows are modified\n4. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":15,"created_at":"2026-03-05T19:57:28.584709816+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:03:30.920162244+06:00","closed_at":"2026-03-05T20:03:30.920162244+06:00","close_reason":"d92a9fa Fix wrong lexicon NSIDs in lexicon index page","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-w96.1","depends_on_id":"docs-w96","type":"parent-child","created_at":"2026-03-05T19:57:28.588772609+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-w96.2","title":"Fix wrong NSID in contribution lexicon doc page","description":"## Files\n- pages/lexicons/hypercerts-lexicons/contribution.md (modify)\n\n## What to do\nFix the incorrect NSID reference for Contribution Details.\n\nLine 21 currently says:\n```\n`org.hypercerts.claim.contributionDetails`\n```\n\nChange it to:\n```\n`org.hypercerts.claim.contribution`\n```\n\nThe actual lexicon file is /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/claim/contribution.json with id \"org.hypercerts.claim.contribution\".\n\nAlso update the section heading on line 19 from \"Contribution Details\" to \"Contribution\" to match the actual lexicon name. Keep the description text explaining what it does.\n\nThe GitHub link on line 27 already points to the correct file (contribution.json), but the display text says `org.hypercerts.claim.contributionDetails` — change it to `org.hypercerts.claim.contribution`.\n\n## Dont\n- Do not change the Contributor Information section (lines 9-17) — that NSID is correct\n- Do not change the page title or frontmatter\n- Do not restructure the page","acceptance_criteria":"1. The NSID shown for the contribution details section is `org.hypercerts.claim.contribution` (not contributionDetails)\n2. The section heading says \"Contribution\" (not \"Contribution Details\")\n3. The GitHub link display text says `org.hypercerts.claim.contribution`\n4. The Contributor Information section is unchanged\n5. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":15,"created_at":"2026-03-05T19:57:41.000060045+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:03:26.349215113+06:00","closed_at":"2026-03-05T20:03:26.349215113+06:00","close_reason":"f403f10 Fix wrong NSID in contribution lexicon doc page","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-w96.2","depends_on_id":"docs-w96","type":"parent-child","created_at":"2026-03-05T19:57:41.002778953+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-w96.3","title":"Fix wrong NSID in collection lexicon doc page","description":"## Files\n- pages/lexicons/hypercerts-lexicons/collection.md (modify)\n\n## What to do\nFix the incorrect NSID on line 7.\n\nCurrently says:\n```\n`org.hypercerts.claim.collection`\n```\n\nChange to:\n```\n`org.hypercerts.collection`\n```\n\nThe actual lexicon file is /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/collection.json with id \"org.hypercerts.collection\" — there is no `.claim.` segment.\n\nAlso update the GitHub link on line 13 — the display text currently says `org.hypercerts.claim.collection`, change it to `org.hypercerts.collection`. The URL itself already points to the correct file path.\n\n## Dont\n- Do not change the page title, frontmatter, or description text\n- Do not add new content","acceptance_criteria":"1. The NSID shown is `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n2. The GitHub link display text says `org.hypercerts.collection`\n3. No other content is changed\n4. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":10,"created_at":"2026-03-05T19:57:47.410685142+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:03:24.579280929+06:00","closed_at":"2026-03-05T20:03:24.579280929+06:00","close_reason":"9b8042c Fix wrong NSID in collection lexicon doc page","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-w96.3","depends_on_id":"docs-w96","type":"parent-child","created_at":"2026-03-05T19:57:47.413144222+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-w96.4","title":"Fix wrong lexicon NSIDs in core data model page","description":"## Files\n- pages/core-concepts/hypercerts-core-data-model.md (modify)\n\n## What to do\nFix two incorrect lexicon NSIDs in the tables on this page.\n\n### Fix 1: Contribution Details NSID (line 32, \"Additional details\" table)\nCurrently says: `org.hypercerts.claim.contributionDetails`\nChange to: `org.hypercerts.claim.contribution`\n\n### Fix 2: Collection NSID (line 63, \"Grouping hypercerts\" table)\nCurrently says: `org.hypercerts.claim.collection`\nChange to: `org.hypercerts.collection`\n\nThe actual lexicon files are:\n- /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/claim/contribution.json (id: org.hypercerts.claim.contribution)\n- /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/collection.json (id: org.hypercerts.collection)\n\n## Dont\n- Do not change any other content on this page — there is a separate task for rewriting the contributor model description and adding missing record types\n- Do not change the table structure or descriptions, only the NSID strings","acceptance_criteria":"1. Line 32 (or the Contribution Details row in the \"Additional details\" table) shows `org.hypercerts.claim.contribution` (not contributionDetails)\n2. Line 63 (or the Collection row in the \"Grouping hypercerts\" table) shows `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n3. No other content on the page is changed\n4. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":10,"created_at":"2026-03-05T19:57:56.276881623+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:03:29.494981296+06:00","closed_at":"2026-03-05T20:03:29.494981296+06:00","close_reason":"d92a9fa Fix wrong lexicon NSIDs in core data model page","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-w96.4","depends_on_id":"docs-w96","type":"parent-child","created_at":"2026-03-05T19:57:56.279903571+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-w96.5","title":"Fix wrong lexicon NSIDs in data-flow-and-lifecycle page","description":"## Files\n- pages/architecture/data-flow-and-lifecycle.md (modify)\n\n## What to do\nFix two incorrect NSIDs:\n\n### Fix 1: Line 59\nCurrently says: `org.hypercerts.claim.contributionDetails`\nChange to: `org.hypercerts.claim.contribution`\n\n### Fix 2: Line 79\nCurrently says: `org.hypercerts.claim.collection`\nChange to: `org.hypercerts.collection`\n\nThe actual lexicon IDs are:\n- org.hypercerts.claim.contribution (file: contribution.json)\n- org.hypercerts.collection (file: collection.json, no .claim. segment)\n\n## Dont\n- Do not change any surrounding text or page structure\n- Only change the two NSID strings","acceptance_criteria":"1. Line 59 (or equivalent) contains `org.hypercerts.claim.contribution` (not contributionDetails)\n2. Line 79 (or equivalent) contains `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n3. No other content is changed\n4. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":10,"created_at":"2026-03-05T19:58:14.602853024+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:04:27.406244913+06:00","closed_at":"2026-03-05T20:04:27.406244913+06:00","close_reason":"91f3ecd Fix wrong lexicon NSIDs in data-flow-and-lifecycle page","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-w96.5","depends_on_id":"docs-w96","type":"parent-child","created_at":"2026-03-05T19:58:14.60526901+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-w96.6","title":"Fix wrong collection NSID in roadmap page","description":"## Files\n- pages/roadmap.md (modify)\n\n## What to do\nFix incorrect collection NSID on line 69.\n\nCurrently says: `org.hypercerts.claim.collection`\nChange to: `org.hypercerts.collection`\n\nThe actual lexicon ID is org.hypercerts.collection (no .claim. segment).\n\n## Dont\n- Do not change any other content on the roadmap page\n- Only change the one NSID string","acceptance_criteria":"1. Line 69 (or the collection row in the table) shows `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n2. No other content is changed\n3. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":5,"created_at":"2026-03-05T19:58:19.75432948+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:04:11.56608607+06:00","closed_at":"2026-03-05T20:04:11.56608607+06:00","close_reason":"3352ec1 Fix wrong collection NSID in roadmap page","labels":["scope:trivial"],"dependencies":[{"issue_id":"docs-w96.6","depends_on_id":"docs-w96","type":"parent-child","created_at":"2026-03-05T19:58:19.756668539+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-w96.7","title":"Rewrite contributor model description and add missing record types in core data model page","description":"## Files\n- pages/core-concepts/hypercerts-core-data-model.md (modify)\n\n## What to do\nThe core data model page has structural inaccuracies about how contributors work. Fix the contributor model description and the \"How records connect\" tree.\n\n### 1. Fix the \"Additional details\" section (lines 26-33)\nThe current description says ContributorInformation and ContributionDetails are \"separate records with their own AT-URI\" that \"can be referenced from the activity claim.\" This is misleading.\n\nRewrite to explain the actual model: The activity claim has a `contributors` array where each entry is a contributor object containing:\n- `contributorIdentity`: either an inline identity string (DID) via `#contributorIdentity`, OR a strong reference to an `org.hypercerts.claim.contributorInformation` record\n- `contributionWeight`: optional relative weight string\n- `contributionDetails`: either an inline role string via `#contributorRole`, OR a strong reference to an `org.hypercerts.claim.contribution` record\n\nEmphasize the dual inline/reference pattern — simple cases use inline strings, richer profiles use separate records.\n\nUpdate the table to reflect the correct lexicon name `org.hypercerts.claim.contribution` (not contributionDetails).\n\n### 2. Fix the \"How records connect\" tree (lines 70-82)\nThe tree currently shows ContributorInformation and ContributionDetails as separate child records. Update it to show them as embedded within contributor objects, reflecting the actual structure. Example:\n\n```text\nActivity Claim (the core record)\n├── contributors[0]\n│ ├── contributorIdentity: Alice (inline DID or ref to ContributorInformation)\n│ ├── contributionWeight: \"1\"\n│ └── contributionDetails: Lead author (inline role or ref to Contribution)\n├── contributors[1]\n│ ├── contributorIdentity: → ContributorInformation record (Bob)\n│ └── contributionDetails: → Contribution record (Technical reviewer, Jan-Mar)\n├── Attachment: GitHub repository link\n├── Measurement: 12 pages written\n├── Measurement: 8,500 words\n└── Evaluation: \"High-quality documentation\" (by Carol)\n```\n\n## Dont\n- Do not change the \"The core record: activity claim\" section (lines 12-23) — the four dimensions table is correct\n- Do not change the \"Grouping hypercerts\" section\n- Do not change the \"Mutability\" or \"What happens next\" sections\n- Do not add new record types (rights, acknowledgement, funding receipt) — this task is fixes only\n- Do not add code examples or API usage — this is a conceptual page\n- Keep the writing style consistent with the rest of the page (concise, factual, no marketing language)","acceptance_criteria":"1. The \"Additional details\" section accurately describes the dual inline/reference pattern for contributors\n2. The contributor table shows `org.hypercerts.claim.contribution` (not contributionDetails)\n3. The \"How records connect\" tree shows contributors as embedded objects with inline/reference options\n4. The four dimensions table in \"The core record\" section is unchanged\n5. The \"Grouping hypercerts\", \"Mutability\", and \"What happens next\" sections are unchanged\n6. No new record types are added to the page\n7. File parses as valid Markdown","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":45,"created_at":"2026-03-05T19:58:48.627566144+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:05:12.194006503+06:00","closed_at":"2026-03-05T20:05:12.194006503+06:00","close_reason":"e55324a Rewrite contributor model description and fix records tree","labels":["scope:small"],"dependencies":[{"issue_id":"docs-w96.7","depends_on_id":"docs-w96","type":"parent-child","created_at":"2026-03-05T19:58:48.630865221+06:00","created_by":"karma.gainforest.id"},{"issue_id":"docs-w96.7","depends_on_id":"docs-w96.4","type":"blocks","created_at":"2026-03-05T19:58:48.635719127+06:00","created_by":"karma.gainforest.id"}]} -{"id":"docs-woj","title":"Epic: Resolve CodeRabbit review for PR #78","description":"Resolve all open CodeRabbit inline review comments on PR #78. 3 comments across 1 file (pages/tools/scaffold.md).","status":"closed","priority":1,"issue_type":"epic","owner":"kzoepa@gmail.com","created_at":"2026-03-05T15:57:13.054348747+06:00","created_by":"kzoeps","updated_at":"2026-04-01T13:47:18.527975056+06:00","closed_at":"2026-03-06T18:07:23.476296+08:00","labels":["needs-integration-review","scope:small"]} -{"id":"docs-woj.1","title":"Fix inconsistent OAuth route namespace in scaffold.md","description":"In pages/tools/scaffold.md, the OAuth Flow prose section still references /api/auth/login, /api/auth/callback, and /api/auth/logout (lines 144, 146, 152) but the directory listing now uses /api/oauth/*. Update those 3 path strings to /api/oauth/login, /api/oauth/callback, and /api/oauth/logout. Also ensure any ePDS subroute mentions match the oauth namespace (e.g., /api/oauth/epds/login and /api/oauth/epds/callback). Only change the path strings in the OAuth Flow prose — do not touch the directory listing block.","acceptance_criteria":"No remaining /api/auth/ path references in the OAuth Flow prose section of pages/tools/scaffold.md","status":"closed","priority":3,"issue_type":"task","owner":"kzoepa@gmail.com","estimated_minutes":10,"created_at":"2026-03-05T15:57:26.427639374+06:00","created_by":"kzoeps","updated_at":"2026-03-05T15:58:31.2136952+06:00","closed_at":"2026-03-05T15:58:31.2136952+06:00","close_reason":"a380a3e aligned OAuth prose paths to /api/oauth/*","labels":["scope:small"],"dependencies":[{"issue_id":"docs-woj.1","depends_on_id":"docs-woj","type":"parent-child","created_at":"2026-03-05T15:57:26.430655391+06:00","created_by":"kzoeps"}]} -{"id":"docs-woj.2","title":"Polish phrasing: deepwiki sentence and EPDS_URL wording in scaffold.md","description":"Two phrasing fixes in pages/tools/scaffold.md: (1) Line 12 — change 'in case you are interested to dive deeper' to 'if you want to dive deeper' in the deepwiki sentence. (2) Line 100 — change '(optional only if you want email/passwordless login)' to '(optional; required only for email/passwordless login)' in the NEXT_PUBLIC_EPDS_URL table row. Only change the exact phrases specified — do not alter any surrounding content.","acceptance_criteria":"Line 12 uses 'if you want to dive deeper' and line 100 uses '(optional; required only for email/passwordless login)'","status":"closed","priority":3,"issue_type":"task","owner":"kzoepa@gmail.com","estimated_minutes":5,"created_at":"2026-03-05T15:57:30.551586454+06:00","created_by":"kzoeps","updated_at":"2026-03-05T15:58:49.926764906+06:00","closed_at":"2026-03-05T15:58:49.926764906+06:00","close_reason":"7b9d9f1 polished deepwiki + EPDS_URL phrasing","labels":["scope:small"],"dependencies":[{"issue_id":"docs-woj.2","depends_on_id":"docs-woj","type":"parent-child","created_at":"2026-03-05T15:57:30.553818774+06:00","created_by":"kzoeps"}]} -{"id":"docs-xdq","title":"Epic: Tools \u0026 Ecosystem — Document CLI, Scaffold, and Hyperboard","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-15T00:42:31.269164+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:54:18.054321+13:00","closed_at":"2026-02-15T00:54:18.054321+13:00","close_reason":"All 4 tasks completed: nav section added, CLI page (145 lines), Scaffold page (139 lines), Hyperboard page (51 lines). Build passes."} -{"id":"docs-xdq.1","title":"Add 'Tools' section to navigation with CLI, Scaffold, and Hyperboard entries","description":"## Files\n- documentation/lib/navigation.js (modify)\n- documentation/pages/tools/hypercerts-cli.md (create)\n- documentation/pages/tools/scaffold.md (create)\n- documentation/pages/tools/hyperboard.md (create)\n\n## What to do\nAdd a new top-level section called \"Tools\" to the navigation array in `documentation/lib/navigation.js`, positioned between the \"Lexicons\" and \"Tutorials\" sections.\n\nThe section should contain three children:\n```js\n{\n section: \"Tools\",\n children: [\n { title: \"Hypercerts CLI\", path: \"/tools/hypercerts-cli\" },\n { title: \"Scaffold Starter App\", path: \"/tools/scaffold\" },\n { title: \"Hyperboard\", path: \"/tools/hyperboard\" },\n ],\n},\n```\n\nAlso create three stub pages so the build does not break. Each stub must have Markdoc frontmatter with a title and description, plus a one-line placeholder:\n\n**documentation/pages/tools/hypercerts-cli.md:**\n```\n---\ntitle: Hypercerts CLI\ndescription: A Go command-line tool for managing hypercerts on ATProto.\n---\nComing soon.\n```\n\n**documentation/pages/tools/scaffold.md:**\n```\n---\ntitle: Scaffold Starter App\ndescription: A Next.js starter app for building on ATProto with the Hypercerts SDK.\n---\nComing soon.\n```\n\n**documentation/pages/tools/hyperboard.md:**\n```\n---\ntitle: Hyperboard\ndescription: A visual board that showcases the contributors to a hypercert.\n---\nComing soon.\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5\n```\nMust exit 0 and show \"✓ Generating static pages\".\n\n## Dont\n- Do not modify any other navigation sections\n- Do not add content beyond the stub placeholder — other tasks handle that\n- Do not rename or reorder existing sections","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-15T00:42:43.269047+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:49:36.320288+13:00","closed_at":"2026-02-15T00:49:36.320288+13:00","close_reason":"a5807b2 Added Tools section to navigation between Lexicons and Tutorials with three stub pages","dependencies":[{"issue_id":"docs-xdq.1","depends_on_id":"docs-xdq","type":"parent-child","created_at":"2026-02-15T00:42:43.270576+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-xdq.2","title":"Write 'Hypercerts CLI' tools page","description":"## Files\n- documentation/pages/tools/hypercerts-cli.md (modify — replace stub)\n\n## Design language — Stripe docs (docs.stripe.com)\nStudy the real Stripe documentation at docs.stripe.com for tone and structure. Key patterns:\n\n1. **One-sentence opener**: The first line tells you what the tool is and what it does. Example from Stripe CLI install page: \"The Stripe CLI lets you build, test, and manage your integration from the command line.\"\n2. **Capability bullet list**: Right after the opener, a short bullet list of what you can do with it. Example: \"You can use the Stripe CLI to: Create, retrieve, update, or delete any of your Stripe resources...\"\n3. **Code first, explain second**: Show the command, then explain what it does — not the other way around.\n4. **Numbered steps** for sequential processes (install, then login, then use).\n5. **Tables for reference data**: Flags, options, record types go in tables.\n6. **Short paragraphs**: 1-3 sentences max per paragraph. Scannable.\n7. **No preamble**: Never start with \"In this page you will learn...\" or \"This guide covers...\"\n8. **No link dumps**: No \"See also\", \"Next steps\", or \"Related pages\" sections at the end.\n9. **Inline prerequisites**: Mention requirements where they are needed, not in a separate section.\n\nAlso study the existing pages in this project for consistency:\n- `documentation/pages/getting-started/quickstart.md`\n- `documentation/pages/tutorials/creating-your-first-hypercert.md`\n\nAvailable Markdoc tags: `{% callout type=\"note\" %}...{% /callout %}`, `{% columns %}`, `{% column %}`.\n\n## What to do\nReplace the stub with a complete documentation page for the Hypercerts CLI (`hc`). Target ~120-160 lines of Markdoc.\n\n### Intro (follow Stripe pattern)\nStart with a one-sentence description, then a capability bullet list:\n\nThe Hypercerts CLI (`hc`) is a command-line tool for managing hypercerts on ATProto. You can use it to:\n\n- Create, read, update, and delete all Hypercerts record types (activities, measurements, evaluations, evidence, and more)\n- Authenticate with any ATProto PDS\n- Run interactively with a terminal UI or non-interactively with flags for CI/CD\n- Resolve identities and inspect any record on the network\n\nThen one sentence: Built in Go on [bluesky-social/indigo](https://github.com/bluesky-social/indigo) with interactive forms powered by [Charm](https://charm.sh) libraries. Source: [github.com/GainForest/hypercerts-cli](https://github.com/GainForest/hypercerts-cli).\n\n### Page structure\n\n1. **Install** section with three numbered options (Stripe uses numbered steps):\n\n 1. Quick install:\n ```bash\n curl -sSL https://raw.githubusercontent.com/GainForest/hypercerts-cli/main/install.sh | bash\n ```\n\n 2. Go install (requires Go 1.25+):\n ```bash\n go install github.com/GainForest/hypercerts-cli/cmd/hc@v0.1.1\n ```\n\n 3. Build from source:\n ```bash\n git clone https://github.com/GainForest/hypercerts-cli\n cd hypercerts-cli\n make build\n ```\n\n2. **Authenticate** section:\n ```bash\n hc account login -u yourhandle.certified.app -p your-app-password\n hc account status\n hc account logout\n ```\n Then a short paragraph: For CI/CD, set `HYPER_USERNAME`, `HYPER_PASSWORD`, and optionally `ATP_PDS_HOST` as environment variables.\n\n3. **Core commands** section — show the full CRUD pattern using activities as the primary example:\n ```bash\n # Create interactively (launches TUI form)\n hc activity create\n\n # Create with flags\n hc activity create \\\n --title \"Rainforest Carbon Study\" \\\n --description \"12-month carbon sequestration measurement\" \\\n --start-date 2025-01-01 \\\n --end-date 2025-12-31\n\n # List\n hc activity ls\n hc activity ls --json\n\n # Get details\n hc activity get \u003crkey\u003e\n\n # Edit\n hc activity edit \u003crkey\u003e --title \"Updated Title\"\n\n # Delete (cascades to linked measurements and attachments)\n hc activity delete \u003crkey\u003e -f\n ```\n\n4. **All record types** — a reference table:\n\n | Command | Record Type | Alias |\n |---------|------------|-------|\n | `hc activity` | `org.hypercerts.claim.activity` | — |\n | `hc measurement` | `org.hypercerts.claim.measurement` | `hc meas` |\n | `hc location` | `app.certified.location` | `hc loc` |\n | `hc attachment` | `org.hypercerts.claim.attachment` | `hc attach` |\n | `hc rights` | `org.hypercerts.claim.rights` | — |\n | `hc evaluation` | `org.hypercerts.claim.evaluation` | `hc eval` |\n | `hc collection` | `org.hypercerts.claim.collection` | `hc coll` |\n | `hc contributor` | `org.hypercerts.claim.contributorInformation` | `hc contrib` |\n | `hc funding` | `org.hypercerts.funding.receipt` | `hc fund` |\n | `hc workscope` | `org.hypercerts.helper.workScopeTag` | `hc ws` |\n\n One sentence: Every type supports `create`, `ls`, `get`, `edit`, `delete` with the same flag patterns shown above.\n\n5. **Generic operations** section:\n ```bash\n hc get at://did:plc:xxx/org.hypercerts.claim.activity/rkey\n hc ls handle.example.com\n hc ls handle.example.com --collection org.hypercerts.claim.activity\n hc resolve handle.example.com\n ```\n\n6. **Interactive UI** — one short paragraph: When you run commands without flags, the CLI launches interactive forms with keyboard navigation, live preview cards during activity creation, multi-select for bulk deletes, and select-or-create patterns when linking records.\n\n7. `{% callout type=\"note\" %}` for cascading deletes: \"Deleting an activity also removes all linked measurements and attachments. Use the `-f` flag to skip confirmation.\"\n\n## Test\n```bash\ncd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5\n```\nMust exit 0. Page must be \u003e100 lines and \u003c200 lines:\n```bash\nwc -l documentation/pages/tools/hypercerts-cli.md\n```\n\n## Dont\n- Do not copy the entire CLI README verbatim — distill it for a documentation audience\n- Do not document every flag for every command — show the pattern with activities, then reference the table\n- Do not use \"See also\", \"Next steps\", or \"Related pages\" sections\n- Do not create a standalone \"Prerequisites\" section\n- Do not start with \"In this page you will learn...\" or similar preamble\n- Do not modify navigation.js (done in docs-xdq.1)\n- Do not use {% figure %} tags (no images available)","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-15T00:43:17.589711+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:51:21.072623+13:00","closed_at":"2026-02-15T00:51:21.072623+13:00","close_reason":"Completed Hypercerts CLI documentation page (145 lines, build passes)","dependencies":[{"issue_id":"docs-xdq.2","depends_on_id":"docs-xdq","type":"parent-child","created_at":"2026-02-15T00:43:17.591208+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-xdq.2","depends_on_id":"docs-xdq.1","type":"blocks","created_at":"2026-02-15T00:44:02.188143+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-xdq.3","title":"Write 'Scaffold Starter App' tools page","description":"## Files\n- documentation/pages/tools/scaffold.md (modify — replace stub)\n\n## Design language — Stripe docs (docs.stripe.com)\nStudy the real Stripe documentation at docs.stripe.com for tone and structure. Key patterns:\n\n1. **One-sentence opener**: The first line tells you what the tool is and what it does. Example from Stripe: \"The Stripe CLI lets you build, test, and manage your integration from the command line.\"\n2. **Capability bullet list**: Right after the opener, a short bullet list of what you can do with it.\n3. **Code first, explain second**: Show the command, then explain what it does.\n4. **Numbered steps** for sequential processes.\n5. **Tables for reference data**: Environment variables, config options go in tables.\n6. **Short paragraphs**: 1-3 sentences max. Scannable.\n7. **No preamble**: Never \"In this page you will learn...\"\n8. **No link dumps**: No \"See also\", \"Next steps\", or \"Related pages\" at the end.\n9. **Inline prerequisites**: Mention requirements where needed, not in a separate section.\n\nAlso study existing pages in this project for consistency:\n- `documentation/pages/getting-started/quickstart.md`\n- `documentation/pages/tutorials/creating-your-first-hypercert.md`\n\nAvailable Markdoc tags: `{% callout type=\"note\" %}...{% /callout %}`, `{% columns %}`, `{% column %}`.\n\n## What to do\nReplace the stub with a complete documentation page for the Hypercerts Scaffold starter app. Target ~100-140 lines of Markdoc.\n\n### Intro (follow Stripe pattern)\nStart with a one-sentence description, then a capability bullet list:\n\nThe Hypercerts Scaffold is a Next.js starter app for building on ATProto with the Hypercerts SDK. Clone it to bootstrap your own application. You get:\n\n- OAuth authentication with ATProto (login, session management, token refresh)\n- Profile management (read and update Certified profiles)\n- Hypercert creation and listing\n- Server-side repository access patterns with React Query on the client\n\nThen one sentence: Live demo at [hypercerts-scaffold.vercel.app](https://hypercerts-scaffold.vercel.app). Source: [github.com/hypercerts-org/hypercerts-scaffold-atproto](https://github.com/hypercerts-org/hypercerts-scaffold-atproto).\n\n### Page structure\n\n1. **Quick start** section with numbered steps:\n\n 1. Clone and install:\n ```bash\n git clone https://github.com/hypercerts-org/hypercerts-scaffold-atproto\n cd hypercerts-scaffold-atproto\n pnpm install\n ```\n\n 2. Configure environment:\n ```bash\n cp .env.example .env.local\n pnpm run generate-jwk \u003e\u003e .env.local\n ```\n\n 3. Start Redis (for session storage):\n ```bash\n docker run -d -p 6379:6379 redis:alpine\n ```\n\n 4. Run the dev server:\n ```bash\n pnpm run dev\n ```\n\n Open `http://127.0.0.1:3000`. Requires Node.js 20+ and pnpm.\n\n `{% callout type=\"note\" %}` — \"Use `127.0.0.1` not `localhost` for local development. ATProto OAuth requires IP-based loopback addresses per RFC 8252. The app auto-redirects, but your `.env.local` must use `127.0.0.1`.\"\n\n2. **Environment variables** section — a table:\n\n | Variable | Description |\n |----------|-------------|\n | `NEXT_PUBLIC_BASE_URL` | App URL (`http://127.0.0.1:3000` for local) |\n | `ATPROTO_JWK_PRIVATE` | OAuth private key (generate with `pnpm run generate-jwk`) |\n | `REDIS_HOST` | Redis hostname |\n | `REDIS_PORT` | Redis port |\n | `REDIS_PASSWORD` | Redis password |\n | `NEXT_PUBLIC_PDS_URL` | PDS URL (e.g. `https://pds-eu-west4.test.certified.app`) |\n\n3. **Architecture** section — brief text description of the stack:\n - Browser: OAuthProvider + SessionProvider + React Query\n - Next.js API routes handle auth callbacks, cert operations, profile management\n - Hypercerts SDK (`@hypercerts-org/sdk-core`) manages OAuth sessions and repository operations\n - Redis stores sessions; PDS stores user data\n\n4. **Key patterns** section — show the two main server-side auth patterns with real code:\n\n **Getting an authenticated repository:**\n ```typescript\n import { getRepoContext } from \"@/lib/repo-context\";\n\n export async function GET() {\n const ctx = await getRepoContext();\n if (!ctx) {\n return Response.json({ error: \"Not authenticated\" }, { status: 401 });\n }\n\n const profile = await ctx.scopedRepo.profile.getCertifiedProfile();\n return Response.json(profile);\n }\n ```\n\n **Creating a hypercert:**\n ```typescript\n await ctx.scopedRepo.hypercert.create({\n title: \"My Hypercert\",\n description: \"A certificate of impact\",\n });\n ```\n\n5. **Project structure** — a brief file tree:\n ```\n app/api/auth/ # OAuth endpoints\n app/api/certs/ # Hypercert CRUD\n app/api/profile/ # Profile management\n components/ # React components\n lib/ # SDK init, repo context, server actions\n providers/ # React context providers\n queries/ # TanStack Query hooks\n ```\n\n6. `{% callout type=\"note\" %}` — \"The scaffold uses a pre-release SDK version (`@hypercerts-org/sdk-core@0.10.0-beta.8`). API changes are expected before 1.0.\"\n\n## Test\n```bash\ncd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5\n```\nMust exit 0. Page must be \u003e80 lines and \u003c180 lines:\n```bash\nwc -l documentation/pages/tools/scaffold.md\n```\n\n## Dont\n- Do not copy the scaffold README verbatim — distill it for a documentation audience\n- Do not include ngrok setup instructions (too niche)\n- Do not start with \"In this page you will learn...\" or similar preamble\n- Do not use \"See also\", \"Next steps\", or \"Related pages\" sections\n- Do not create a standalone \"Prerequisites\" section\n- Do not modify navigation.js (done in docs-xdq.1)\n- Do not use {% figure %} tags","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-15T00:43:41.090634+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:52:31.358324+13:00","closed_at":"2026-02-15T00:52:31.358324+13:00","close_reason":"Completed scaffold documentation page with 139 lines following Stripe docs design language","dependencies":[{"issue_id":"docs-xdq.3","depends_on_id":"docs-xdq","type":"parent-child","created_at":"2026-02-15T00:43:41.091874+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-xdq.3","depends_on_id":"docs-xdq.1","type":"blocks","created_at":"2026-02-15T00:44:02.335743+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-xdq.4","title":"Write 'Hyperboard' tools page","description":"## Files\n- documentation/pages/tools/hyperboard.md (modify — replace stub)\n\n## Design language — Stripe docs (docs.stripe.com)\nStudy the real Stripe documentation at docs.stripe.com for tone and structure. Key patterns:\n\n1. **One-sentence opener**: The first line tells you what the tool is and what it does.\n2. **Capability bullet list**: Right after the opener, a short bullet list of what you can do with it.\n3. **Short paragraphs**: 1-3 sentences max. Scannable.\n4. **No preamble**: Never \"In this page you will learn...\"\n5. **No link dumps**: No \"See also\", \"Next steps\", or \"Related pages\" at the end.\n\nAlso study existing pages in this project for consistency:\n- `documentation/pages/getting-started/quickstart.md`\n- `documentation/pages/getting-started/why-atproto.md`\n\nAvailable Markdoc tags: `{% callout type=\"note\" %}...{% /callout %}`, `{% columns %}`, `{% column %}`.\n\n## What to do\nReplace the stub with a documentation page for Hyperboard. Target ~60-90 lines of Markdoc.\n\nHyperboard is a visual board that showcases the contributors to a hypercert. It displays who contributed to an impact claim, making contributions visible and recognizable.\n\n### Intro (follow Stripe pattern)\nStart with a one-sentence description, then a capability bullet list:\n\nHyperboard displays the contributors to a hypercert as a visual, shareable board. Use it to:\n\n- Showcase who did the work behind an impact claim\n- Display contribution weights and roles\n- Embed contributor recognition on project websites\n- Give funders transparency into who their contributions support\n\nThen one sentence: Because the underlying data lives on ATProto, every Hyperboard is backed by cryptographically signed, publicly verifiable records.\n\n### Page structure\n\n1. **What Hyperboard shows** section — explain what information a Hyperboard displays:\n - The hypercert (activity claim) it is associated with\n - All contributors listed on that hypercert (via `org.hypercerts.claim.contributorInformation` records)\n - Contribution weights or roles (if specified in the contribution details)\n - A visual, shareable representation of impact attribution\n\n2. **How it works** section — explain the data flow in 3-4 short paragraphs:\n - A hypercert is created with contributors listed\n - Hyperboard reads the contributor data from the ATProto repository\n - It renders a visual board showing each contributor and their role\n - Because the data lives on ATProto, the board is verifiable — anyone can check the underlying records\n\n3. **Use cases** section — brief bullet list with bold labels:\n - **Project pages**: Embed a Hyperboard on your project website to showcase your team\n - **Funding transparency**: Show funders exactly who their contributions support\n - **Portfolio**: Contributors can point to Hyperboards as proof of their impact work\n - **Recognition**: Publicly acknowledge contributors in a way that is verifiable and portable\n\n4. `{% callout type=\"note\" %}` — \"Hyperboard reads contributor data directly from ATProto repositories. The underlying records are cryptographically signed and publicly verifiable — anyone can independently confirm who contributed to a hypercert.\"\n\n## Test\n```bash\ncd documentation \u0026\u0026 npx next build --webpack 2\u003e\u00261 | tail -5\n```\nMust exit 0. Page must be \u003e40 lines and \u003c120 lines:\n```bash\nwc -l documentation/pages/tools/hyperboard.md\n```\n\n## Dont\n- Do not invent URLs, repos, or API endpoints for Hyperboard — we do not have those details yet\n- Do not claim Hyperboard is open source or link to a repo (we do not know)\n- Do not start with \"In this page you will learn...\" or similar preamble\n- Do not use \"See also\", \"Next steps\", or \"Related pages\" sections\n- Do not create a standalone \"Prerequisites\" section\n- Do not modify navigation.js (done in docs-xdq.1)\n- Do not use {% figure %} tags\n- Do not speculate about features beyond what is described above","status":"closed","priority":1,"issue_type":"task","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-15T00:43:58.12274+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-15T00:51:05.921335+13:00","closed_at":"2026-02-15T00:51:05.921335+13:00","close_reason":"Completed Hyperboard tools page documentation","dependencies":[{"issue_id":"docs-xdq.4","depends_on_id":"docs-xdq","type":"parent-child","created_at":"2026-02-15T00:43:58.123763+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-xdq.4","depends_on_id":"docs-xdq.1","type":"blocks","created_at":"2026-02-15T00:44:02.447475+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-y5x","title":"Epic: Adopt ATProto docs dotted banner, icon cards, and typography","description":"Adopt three visual elements from atproto.com/docs: (1) SVG dot-pattern banner behind the index page hero, (2) card-style navigation links with icons in bordered square containers in a 2-col grid, (3) larger card titles (24px) and monospace hero heading. The current index page uses plain markdown tables for links. Success: index page has a dotted hero banner and icon-based navigation cards matching ATProto's visual language.","status":"closed","priority":1,"issue_type":"epic","owner":"einstein.climateai.org","created_at":"2026-02-20T18:47:58.243696+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:35:05.304924+08:00","closed_at":"2026-02-20T19:35:05.304924+08:00","close_reason":"e2b1d9d Dotted banner and icon cards complete (PRs #19-23)","labels":["scope:medium"]} -{"id":"docs-y5x.1","title":"Add DotPattern component and hero-banner CSS","description":"## Files\n- documentation/components/DotPattern.js (create)\n- documentation/styles/globals.css (modify)\n\n## What to do\n\n### 1. Create DotPattern.js component\nCreate a new React component that renders an SVG dot pattern, matching atproto.com/docs exactly:\n\n```jsx\nexport function DotPattern({ className }) {\n return (\n \u003csvg\n aria-hidden=\"true\"\n className={`dot-pattern ${className || \"\"}`}\n width=\"100%\"\n height=\"100%\"\n \u003e\n \u003cdefs\u003e\n \u003cpattern\n id=\"dot-grid\"\n width=\"10\"\n height=\"10\"\n patternUnits=\"userSpaceOnUse\"\n x=\"0\"\n y=\"0\"\n \u003e\n \u003crect width=\"2\" height=\"2\" fill=\"current\" /\u003e\n \u003c/pattern\u003e\n \u003c/defs\u003e\n \u003crect width=\"100%\" height=\"100%\" strokeWidth=\"0\" fill=\"url(#dot-grid)\" /\u003e\n \u003c/svg\u003e\n );\n}\n```\n\n### 2. Add CSS for the dot pattern and hero banner in globals.css\nAdd these rules AFTER the existing `/* ===== Component: Figure ===== */` section (before the `/* ===== Responsive ===== */` section):\n\n```css\n/* ===== Component: DotPattern ===== */\n.dot-pattern {\n position: absolute;\n inset: 0;\n fill: oklch(0.91 0.005 260);\n}\n\n/* ===== Component: Hero Banner ===== */\n.hero-banner {\n position: relative;\n margin: calc(-1 * var(--space-6)) 0 var(--space-8) calc(-72px);\n padding: var(--space-12) 72px var(--space-10) 72px;\n}\n\n.hero-banner-content {\n position: relative;\n z-index: 1;\n}\n\n.hero-title {\n font-family: var(--font-mono);\n font-size: 40px;\n font-weight: 500;\n color: var(--color-text-title);\n line-height: 1.15;\n margin: 0 0 var(--space-4) 0;\n letter-spacing: -0.02em;\n}\n\n.hero-subtitle {\n font-size: 18px;\n line-height: 1.6;\n color: var(--color-text-secondary);\n margin: 0;\n max-width: 600px;\n}\n```\n\nAlso add responsive overrides inside the existing mobile media query `@media (max-width: 768px)`:\n```css\n.hero-banner {\n margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-4)) var(--space-6) calc(-1 * var(--space-4));\n padding: var(--space-8) var(--space-4) var(--space-6) var(--space-4);\n}\n\n.hero-title {\n font-size: 28px;\n}\n```\n\nAnd inside `@media (min-width: 769px) and (max-width: 1024px)`:\n```css\n.hero-banner {\n margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-6)) var(--space-6) calc(-1 * var(--space-6));\n padding: var(--space-8) var(--space-6) var(--space-6) var(--space-6);\n}\n\n.hero-title {\n font-size: 32px;\n}\n```\n\n## Don't\n- Do NOT modify index.md yet (that is a separate task)\n- Do NOT register the component in _app.js yet (that is a separate task)\n- Do NOT change any existing CSS rules\n- Do NOT use Tailwind classes — this project uses plain CSS","acceptance_criteria":"1. DotPattern.js exists at documentation/components/DotPattern.js\n2. Component renders an SVG with a \u003cpattern\u003e element using 2x2px dots on 10px grid\n3. globals.css contains .dot-pattern, .hero-banner, .hero-banner-content, .hero-title, .hero-subtitle classes\n4. Hero title uses var(--font-mono) font family\n5. Dot pattern fill uses oklch color matching the design system\n6. Responsive rules exist for mobile and tablet\n7. `npm run build` succeeds","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":20,"created_at":"2026-02-20T18:48:26.142186+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:52:09.196549+08:00","closed_at":"2026-02-20T18:52:09.196549+08:00","close_reason":"87f8610 Add DotPattern component and hero-banner CSS","labels":["scope:small"],"dependencies":[{"issue_id":"docs-y5x.1","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T18:48:26.143511+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-y5x.2","title":"Add icon-card grid CSS and update CardLink component","description":"## Files\n- documentation/components/CardLink.js (modify)\n- documentation/markdoc/tags/card-link.markdoc.js (modify)\n- documentation/styles/globals.css (modify)\n\n## What to do\n\n### 1. Update CardLink.js to support icon prop\nReplace the entire CardLink component with:\n\n```jsx\nimport Link from \"next/link\";\n\nexport function CardLink({ title, href, icon, children }) {\n return (\n \u003cLink href={href} className=\"card-link\"\u003e\n {icon \u0026\u0026 (\n \u003cspan className=\"card-link-icon-box\"\u003e\n \u003cspan className=\"card-link-icon\" dangerouslySetInnerHTML={{ __html: icon }} /\u003e\n \u003c/span\u003e\n )}\n \u003cspan className=\"card-link-text\"\u003e\n \u003cspan className=\"card-link-title\"\u003e{title}\u003c/span\u003e\n {children \u0026\u0026 \u003cspan className=\"card-link-desc\"\u003e{children}\u003c/span\u003e}\n \u003c/span\u003e\n \u003c/Link\u003e\n );\n}\n```\n\n### 2. Update card-link.markdoc.js to accept icon attribute\n```js\nexport default {\n render: \"CardLink\",\n attributes: {\n title: { type: String, required: true },\n href: { type: String, required: true },\n icon: { type: String },\n },\n};\n```\n\n### 3. Replace existing card-link CSS in globals.css\nFind the `/* ===== Component: CardLink ===== */` section and replace ALL card-link rules with:\n\n```css\n/* ===== Component: CardLink ===== */\n.card-link {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--space-5);\n padding: var(--space-4) var(--space-5);\n border: none;\n border-radius: var(--radius-lg);\n text-decoration: none;\n transition: background var(--transition-normal);\n position: relative;\n}\n\n.card-link:hover {\n background: var(--hover-bg);\n text-decoration: none;\n}\n\n.card-link-icon-box {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 56px;\n height: 56px;\n min-width: 56px;\n padding: var(--space-3);\n border-radius: var(--radius-sm);\n box-shadow: inset 0 0 0 1px oklch(0.20 0.01 260 / 0.15);\n transition: box-shadow var(--transition-fast);\n}\n\n.card-link:hover .card-link-icon-box {\n box-shadow: inset 0 0 0 1px oklch(0.20 0.01 260 / 0.3);\n}\n\n.card-link-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color-text-primary);\n}\n\n.card-link-icon svg {\n width: 32px;\n height: 32px;\n fill: none;\n stroke: currentColor;\n stroke-width: 1;\n}\n\n.card-link-text {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n}\n\n.card-link-title {\n display: block;\n font-family: var(--font-display);\n font-size: 20px;\n font-weight: 500;\n color: var(--color-text-heading);\n line-height: 1.3;\n}\n\n.card-link-desc {\n display: block;\n font-size: 14px;\n color: var(--color-text-secondary);\n line-height: 1.5;\n margin-top: 2px;\n}\n\n.card-link-arrow {\n display: none;\n}\n```\n\n### 4. Add card-grid CSS\nAdd AFTER the card-link rules:\n\n```css\n/* ===== Component: Card Grid ===== */\n.card-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: var(--space-2);\n margin: var(--space-4) 0;\n}\n```\n\nAnd add mobile responsive rule inside `@media (max-width: 768px)`:\n```css\n.card-grid {\n grid-template-columns: 1fr;\n}\n\n.card-link-icon-box {\n width: 48px;\n height: 48px;\n min-width: 48px;\n}\n\n.card-link-icon svg {\n width: 24px;\n height: 24px;\n}\n\n.card-link-title {\n font-size: 16px;\n}\n```\n\n## Don't\n- Do NOT modify index.md (separate task)\n- Do NOT modify _app.js\n- Do NOT remove the .card-link-arrow class definition (just set display: none)\n- Do NOT use Tailwind\n- Do NOT change any other component files","acceptance_criteria":"1. CardLink.js accepts an optional `icon` prop containing SVG HTML string\n2. CardLink renders an icon box (56x56px) with a ring/inset-shadow border when icon is provided\n3. card-link.markdoc.js has icon attribute defined as type String\n4. globals.css has .card-grid class with 2-column grid layout\n5. Card title is 20px in Syne (--font-display), weight 500\n6. Icon SVGs inside .card-link-icon are 32px with stroke rendering\n7. Mobile responsive: grid collapses to 1 column, icon box shrinks to 48px\n8. `npm run build` succeeds","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":25,"created_at":"2026-02-20T18:48:51.496801+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:52:29.014512+08:00","closed_at":"2026-02-20T18:52:29.014512+08:00","close_reason":"69b0987 Add icon-card grid CSS and update CardLink component","labels":["scope:small"],"dependencies":[{"issue_id":"docs-y5x.2","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T18:48:51.497896+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-y5x.3","title":"Register DotPattern and CardGrid, rewrite index.md with hero banner and icon cards","description":"## Files\n- documentation/pages/_app.js (modify)\n- documentation/components/CardGrid.js (create)\n- documentation/markdoc/tags/card-grid.markdoc.js (create)\n- documentation/markdoc/tags/hero-banner.markdoc.js (create)\n- documentation/components/HeroBanner.js (create)\n- documentation/pages/index.md (modify)\n\n## What to do\n\n### 1. Create HeroBanner.js component\n```jsx\nimport { DotPattern } from \"./DotPattern\";\n\nexport function HeroBanner({ title, children }) {\n return (\n \u003cdiv className=\"hero-banner\"\u003e\n \u003cDotPattern /\u003e\n \u003cdiv className=\"hero-banner-content\"\u003e\n {title \u0026\u0026 \u003ch1 className=\"hero-title\"\u003e{title}\u003c/h1\u003e}\n {children \u0026\u0026 \u003cp className=\"hero-subtitle\"\u003e{children}\u003c/p\u003e}\n \u003c/div\u003e\n \u003c/div\u003e\n );\n}\n```\n\n### 2. Create hero-banner.markdoc.js\n```js\nexport default {\n render: \"HeroBanner\",\n attributes: {\n title: { type: String, required: true },\n },\n};\n```\n\n### 3. Create CardGrid.js component\n```jsx\nexport function CardGrid({ children }) {\n return \u003cdiv className=\"card-grid\"\u003e{children}\u003c/div\u003e;\n}\n```\n\n### 4. Create card-grid.markdoc.js\n```js\nexport default {\n render: \"CardGrid\",\n children: [\"card-link\"],\n};\n```\n\n### 5. Register new components in _app.js\nAdd imports for DotPattern, HeroBanner, and CardGrid. Add them to the `components` object:\n```js\nimport { DotPattern } from \"../components/DotPattern\";\nimport { HeroBanner } from \"../components/HeroBanner\";\nimport { CardGrid } from \"../components/CardGrid\";\n```\n\nAdd to components object:\n```js\nDotPattern,\nHeroBanner,\nCardGrid,\n```\n\n### 6. Rewrite index.md\nReplace the entire content of index.md with the hero banner and icon card grids. Use inline SVG strings for icons. Here is the exact content:\n\n```markdown\n---\ntitle: Hypercerts Documentation\n---\n\n{% hero-banner title=\"Hypercerts Documentation\" %}\nStructured digital records of contributions — who did what, when, where, and with what supporting documentation. Build applications that create, evaluate, and fund impactful work.\n{% /hero-banner %}\n\n---\n\n## Get started\n\n{% card-grid %}\n{% card-link title=\"Quickstart\" href=\"/getting-started/quickstart\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z\\\"/\u003e\u003c/svg\u003e\" %}\nInstall the SDK and create your first hypercert in under 5 minutes\n{% /card-link %}\n{% card-link title=\"Creating Your First Hypercert\" href=\"/getting-started/creating-your-first-hypercert\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z\\\"/\u003e\u003c/svg\u003e\" %}\nBuild a complete hypercert with contributions, attachments, and measurements\n{% /card-link %}\n{% card-link title=\"Working with Evaluations\" href=\"/getting-started/working-with-evaluations\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.745 3.745 0 011.043 3.296A3.745 3.745 0 0121 12z\\\"/\u003e\u003c/svg\u003e\" %}\nCreate evaluations of other people's work\n{% /card-link %}\n{% card-link title=\"Common Use Cases\" href=\"/getting-started/common-use-cases\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25\\\"/\u003e\u003c/svg\u003e\" %}\nWorked examples for open-source, climate, research, and community projects\n{% /card-link %}\n{% /card-grid %}\n\n## Core concepts\n\n{% card-grid %}\n{% card-link title=\"What are Hypercerts?\" href=\"/core-concepts/what-is-hypercerts\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z\\\"/\u003e\u003c/svg\u003e\" %}\nThe record structure, how people use them, and why they're built on ATProto\n{% /card-link %}\n{% card-link title=\"Core Data Model\" href=\"/core-concepts/hypercerts-core-data-model\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125\\\"/\u003e\u003c/svg\u003e\" %}\nRecord types, dimensions, and how they connect\n{% /card-link %}\n{% card-link title=\"Certified Identity\" href=\"/core-concepts/certified-identity\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z\\\"/\u003e\u003c/svg\u003e\" %}\nHow identity works — DIDs, signing, portability, and wallet linkage\n{% /card-link %}\n{% card-link title=\"Why ATProto?\" href=\"/core-concepts/why-atproto\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M14.25 9.75L16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0020.25 18V6A2.25 2.25 0 0018 3.75H6A2.25 2.25 0 003.75 6v12A2.25 2.25 0 006 20.25z\\\"/\u003e\u003c/svg\u003e\" %}\nWhy the protocol is built on AT Protocol\n{% /card-link %}\n{% /card-grid %}\n\n## Tools\n\n{% card-grid %}\n{% card-link title=\"Scaffold Starter App\" href=\"/tools/scaffold\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nNext.js reference app with OAuth, creation wizard, and browsing\n{% /card-link %}\n{% card-link title=\"Hypercerts CLI\" href=\"/tools/hypercerts-cli\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nCreate and manage hypercerts from the command line\n{% /card-link %}\n{% card-link title=\"Hyperindex\" href=\"/tools/hyperindex\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z\\\"/\u003e\u003c/svg\u003e\" %}\nGraphQL API for querying hypercert records across the network\n{% /card-link %}\n{% card-link title=\"Hyperboard\" href=\"/tools/hyperboard\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nVisual contributor boards for attribution and funding transparency\n{% /card-link %}\n{% /card-grid %}\n\n## Architecture\n\n{% card-grid %}\n{% card-link title=\"Architecture Overview\" href=\"/architecture/overview\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M2.25 7.125C2.25 6.504 2.754 6 3.375 6h6c.621 0 1.125.504 1.125 1.125v3.75c0 .621-.504 1.125-1.125 1.125h-6a1.125 1.125 0 01-1.125-1.125v-3.75zM14.25 8.625c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v8.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-8.25zM3.75 16.125c0-.621.504-1.125 1.125-1.125h5.25c.621 0 1.125.504 1.125 1.125v2.25c0 .621-.504 1.125-1.125 1.125h-5.25a1.125 1.125 0 01-1.125-1.125v-2.25z\\\"/\u003e\u003c/svg\u003e\" %}\nHow the protocol stack fits together\n{% /card-link %}\n{% card-link title=\"Data Flow \u0026 Lifecycle\" href=\"/architecture/data-flow-and-lifecycle\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5\\\"/\u003e\u003c/svg\u003e\" %}\nHow a hypercert moves from creation through evaluation to funding\n{% /card-link %}\n{% card-link title=\"Indexers \u0026 Discovery\" href=\"/architecture/indexers-and-discovery\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-.778.099-1.533.284-2.253\\\"/\u003e\u003c/svg\u003e\" %}\nHow indexers make hypercerts findable across the network\n{% /card-link %}\n{% card-link title=\"Portability \u0026 Scaling\" href=\"/architecture/portability-and-scaling\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3 8.689c0-.864.933-1.405 1.683-.977l7.108 4.062a1.125 1.125 0 010 1.953l-7.108 4.062A1.125 1.125 0 013 16.811V8.69zM12.75 8.689c0-.864.933-1.405 1.683-.977l7.108 4.062a1.125 1.125 0 010 1.953l-7.108 4.062a1.125 1.125 0 01-1.683-.977V8.69z\\\"/\u003e\u003c/svg\u003e\" %}\nMigration, performance, and privacy\n{% /card-link %}\n{% /card-grid %}\n\n## Reference\n\n{% card-grid %}\n{% card-link title=\"Lexicons\" href=\"/lexicons/introduction-to-lexicons\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.491 48.491 0 01-4.163-.3c-1.108-.128-2.105-.65-2.813-1.536A5.99 5.99 0 003 6.375C3 9.101 4.232 12.126 6 14.25c1.768 2.124 3.879 3.768 6 3.768s4.232-1.644 6-3.768c1.768-2.124 3-5.149 3-7.875a5.99 5.99 0 00-.567-2.556c-.708.886-1.705 1.408-2.813 1.536a48.394 48.394 0 01-4.163.3.64.64 0 01-.657-.643v0z\\\"/\u003e\u003c/svg\u003e\" %}\nSchema definitions for every record type\n{% /card-link %}\n{% card-link title=\"Glossary\" href=\"/reference/glossary\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25\\\"/\u003e\u003c/svg\u003e\" %}\nKey terms used across the documentation\n{% /card-link %}\n{% card-link title=\"FAQ\" href=\"/reference/faq\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M8.625 9.75a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375m-13.5 3.01c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.184-4.183a1.14 1.14 0 01.778-.332 48.294 48.294 0 005.83-.498c1.585-.233 2.708-1.626 2.708-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z\\\"/\u003e\u003c/svg\u003e\" %}\nCommon questions about building with Hypercerts\n{% /card-link %}\n{% card-link title=\"Roadmap\" href=\"/roadmap\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M3 3v1.5M3 21v-6m0 0l2.77-.693a9 9 0 016.208.682l.108.054a9 9 0 006.086.71l3.114-.732a48.524 48.524 0 01-.005-10.499l-3.11.732a9 9 0 01-6.085-.711l-.108-.054a9 9 0 00-6.208-.682L3 4.5M3 15V4.5\\\"/\u003e\u003c/svg\u003e\" %}\nDevelopment priorities and phased delivery plan\n{% /card-link %}\n{% /card-grid %}\n\n## Ecosystem \u0026 Vision\n\n{% card-grid %}\n{% card-link title=\"Why We Need Hypercerts\" href=\"/ecosystem/why-we-need-hypercerts\" icon=\"\u003csvg viewBox=\\\"0 0 24 24\\\"\u003e\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" d=\\\"M12 18v-5.25m0 0a6.01 6.01 0 001.5-.189m-1.5.189a6.01 6.01 0 01-1.5-.189m3.75 7.478a12.06 12.06 0 01-4.5 0m3.75 2.383a14.406 14.406 0 01-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 10-7.517 0c.85.493 1.509 1.333 1.509 2.316V18\\\"/\u003e\u003c/svg\u003e\" %}\nThe problem hypercerts solve and why they matter\n{% /card-link %}\n{% /card-grid %}\n```\n\n### Important notes on the SVG icons\n- All SVG icons use Heroicons outline style (24x24 viewBox, stroke-based)\n- No fill attribute — they inherit from the .card-link-icon CSS (fill: none, stroke: currentColor)\n- Each icon is thematically appropriate for its section\n\n## Don't\n- Do NOT modify globals.css (handled by other tasks)\n- Do NOT modify Layout.js or Sidebar.js\n- Do NOT change the navigation.js file\n- Do NOT remove the \"Building on Hypercerts\" or \"Testing \u0026 Deployment\" pages from the card grid — include them in the \"Get started\" section. Wait, the spec above only has 4 cards per section. That's intentional — the index page should show the TOP 4 items per section as cards for visual balance. The remaining pages are still accessible via the sidebar. However, include ALL items from each section as shown in the markdown above.\n- Do NOT add any items that are not in the current index.md","acceptance_criteria":"1. _app.js imports and registers DotPattern, HeroBanner, and CardGrid components\n2. HeroBanner.js renders a div.hero-banner containing DotPattern + title + subtitle\n3. CardGrid.js renders a div.card-grid wrapping children\n4. index.md uses {% hero-banner %} tag with dotted background\n5. index.md uses {% card-grid %} and {% card-link %} tags with icon attributes\n6. Every card-link has an icon prop with valid SVG markup\n7. All links from the original index.md are preserved (no pages dropped)\n8. The page renders with a dotted banner at the top and icon cards below\n9. `npm run build` succeeds with no errors","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":45,"created_at":"2026-02-20T18:50:21.102653+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T18:56:54.783424+08:00","closed_at":"2026-02-20T18:56:54.783424+08:00","close_reason":"e72f0e0 Register DotPattern, HeroBanner, CardGrid; rewrite index.md with hero banner and icon cards","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-y5x.3","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T18:50:21.103916+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-y5x.3","depends_on_id":"docs-y5x.1","type":"blocks","created_at":"2026-02-20T18:50:21.105154+08:00","created_by":"einstein.climateai.org"},{"issue_id":"docs-y5x.3","depends_on_id":"docs-y5x.2","type":"blocks","created_at":"2026-02-20T18:50:21.106109+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-y5x.4","title":"Fix hydration mismatch: HeroBanner nesting and tags barrel file","description":"## Files\n- documentation/markdoc/tags/index.js (create — THIS FILE IS MISSING and is the root cause of tags not rendering)\n- documentation/components/HeroBanner.js (modify)\n\n## What to do\n\n### 1. Create markdoc/tags/index.js barrel file\nThis file is MISSING. Without it, @markdoc/next.js cannot discover any custom tags. The nodes directory already has one (markdoc/nodes/index.js) which is why headings work but tags don't.\n\nCreate `documentation/markdoc/tags/index.js` with:\n\n```js\nimport callout from \"./callout.markdoc\";\nimport columns from \"./columns.markdoc\";\nimport column from \"./column.markdoc\";\nimport figure from \"./figure.markdoc\";\nimport cardLink from \"./card-link.markdoc\";\nimport cardGrid from \"./card-grid.markdoc\";\nimport heroBanner from \"./hero-banner.markdoc\";\n\nexport default {\n callout,\n columns,\n column,\n figure,\n \"card-link\": cardLink,\n \"card-grid\": cardGrid,\n \"hero-banner\": heroBanner,\n};\n```\n\nUses default export because `defaultObject()` in @markdoc/next.js runtime checks for `obj.default` first. Kebab-case tag names must be string keys (can't use hyphens in JS identifiers).\n\n### 2. Fix HeroBanner.js hydration error\nThe hydration mismatch happens because Markdoc wraps the children text in a `\u003cp\u003e` tag, and HeroBanner also wraps children in `\u003cp className=\"hero-subtitle\"\u003e`, creating invalid `\u003cp\u003e\u003cp\u003e...\u003c/p\u003e\u003c/p\u003e` nesting.\n\nReplace HeroBanner.js with:\n\n```jsx\nimport { DotPattern } from \"./DotPattern\";\n\nexport function HeroBanner({ title, children }) {\n return (\n \u003cdiv className=\"hero-banner\"\u003e\n \u003cDotPattern /\u003e\n \u003cdiv className=\"hero-banner-content\"\u003e\n {title \u0026\u0026 \u003ch1 className=\"hero-title\"\u003e{title}\u003c/h1\u003e}\n {children \u0026\u0026 \u003cdiv className=\"hero-subtitle\"\u003e{children}\u003c/div\u003e}\n \u003c/div\u003e\n \u003c/div\u003e\n );\n}\n```\n\nChange: `\u003cp className=\"hero-subtitle\"\u003e` → `\u003cdiv className=\"hero-subtitle\"\u003e`. A `\u003cdiv\u003e` can contain `\u003cp\u003e` children without invalid nesting.\n\n### 3. Verify with clean build\nRun these commands:\n```bash\ncd documentation/documentation\nrm -rf .next out\nnpm run build\n```\n\nThen check the output:\n```bash\ngrep -c \"hero-banner\" out/index.html\ngrep -c \"card-link\" out/index.html\ngrep -c \"dot-pattern\" out/index.html\n```\n\nAll counts should be \u003e 0. If any are 0, the tags barrel file is not working.\n\n### 4. Also verify no hydration errors in dev\n```bash\nnpm run dev\n```\nOpen http://localhost:3000 in the terminal with curl and check there are no `\u003cp\u003e` inside `\u003cp\u003e` patterns:\n```bash\ncurl -s http://localhost:3000 | grep -c \"\u003cp\u003e\u003cp\u003e\"\n```\nShould return 0.\n\n## Don't\n- Do NOT modify any other files\n- Do NOT change the index.md content\n- Do NOT change the card-link, card-grid, or DotPattern components\n- Do NOT modify _app.js\n- Do NOT modify globals.css","acceptance_criteria":"1. documentation/markdoc/tags/index.js exists and exports all 7 tags\n2. `npm run build` succeeds (clean build with rm -rf .next out first)\n3. out/index.html contains \"hero-banner\" class (grep returns \u003e 0 matches)\n4. out/index.html contains \"card-link\" class (grep returns \u003e 0 matches)\n5. out/index.html contains \"dot-pattern\" class (grep returns \u003e 0 matches)\n6. out/index.html does NOT contain \u003cp\u003e\u003cp\u003e nesting\n7. HeroBanner uses \u003cdiv\u003e not \u003cp\u003e for hero-subtitle wrapper\n8. No hydration mismatch errors when running npm run dev","status":"closed","priority":0,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":20,"created_at":"2026-02-20T19:04:56.233516+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:06:41.041634+08:00","closed_at":"2026-02-20T19:06:41.041634+08:00","close_reason":"8982de3 Fix hydration mismatch: add tags barrel file and fix HeroBanner nesting","labels":["scope:small"],"dependencies":[{"issue_id":"docs-y5x.4","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T19:04:56.234214+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-y5x.5","title":"Replace card-link CSS with icon-card layout and add card-grid","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe CardLink component renders icon-box, icon, and text wrapper elements, but the CSS for these classes is missing. The old card-link CSS (block display, border, arrow) does not match the new component structure. Replace it entirely.\n\n### 1. Find and replace the entire card-link CSS section\nFind the section starting with `/* ===== Component: CardLink ===== */` (around line 995) through `.card-link:hover .card-link-arrow` (around line 1043). Replace ALL of it with:\n\n```css\n/* ===== Component: CardLink ===== */\n.card-link {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--space-5);\n padding: var(--space-3) var(--space-4);\n border: none;\n border-radius: var(--radius-lg);\n text-decoration: none;\n transition: background var(--transition-normal);\n position: relative;\n}\n\n.card-link:hover {\n background: var(--hover-bg);\n text-decoration: none;\n}\n\n.card-link-icon-box {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 48px;\n height: 48px;\n min-width: 48px;\n padding: 10px;\n border-radius: 2px;\n box-shadow: inset 0 0 0 1px oklch(0.20 0.01 260 / 0.15);\n transition: box-shadow var(--transition-fast);\n}\n\n.card-link:hover .card-link-icon-box {\n box-shadow: inset 0 0 0 1px oklch(0.20 0.01 260 / 0.3);\n}\n\n.card-link-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--color-text-primary);\n}\n\n.card-link-icon svg {\n width: 28px;\n height: 28px;\n fill: none;\n stroke: currentColor;\n stroke-width: 1;\n}\n\n.card-link-text {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n}\n\n.card-link-title {\n display: block;\n font-family: var(--font-display);\n font-size: 16px;\n font-weight: 500;\n color: var(--color-text-heading);\n line-height: 1.3;\n}\n\n.card-link-desc {\n display: block;\n font-size: 14px;\n color: var(--color-text-secondary);\n line-height: 1.5;\n margin-top: 2px;\n}\n```\n\n### 2. Add card-grid CSS immediately after the card-link section\n\n```css\n/* ===== Component: Card Grid ===== */\n.card-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: var(--space-1);\n margin: var(--space-3) 0;\n}\n```\n\n### 3. Add mobile responsive rules\nInside the existing `@media (max-width: 768px)` block, add:\n\n```css\n.card-grid {\n grid-template-columns: 1fr;\n}\n\n.card-link-icon-box {\n width: 40px;\n height: 40px;\n min-width: 40px;\n}\n\n.card-link-icon svg {\n width: 22px;\n height: 22px;\n}\n\n.card-link-title {\n font-size: 15px;\n}\n```\n\n## Don't\n- Do NOT modify any component files (CardLink.js, HeroBanner.js, etc.)\n- Do NOT modify index.md\n- Do NOT modify _app.js\n- Do NOT change the DotPattern, hero-banner, or hero-title CSS\n- Do NOT add back the old .card-link-arrow styles — the arrow element no longer exists in the component","acceptance_criteria":"1. globals.css contains .card-link-icon-box class with width/height 48px\n2. globals.css contains .card-link-icon svg rule with width/height 28px, fill: none, stroke: currentColor\n3. globals.css contains .card-link-text with flex-direction: column\n4. globals.css contains .card-grid with grid-template-columns: repeat(2, 1fr)\n5. .card-link uses display: flex (not display: block)\n6. No .card-link-arrow rules remain\n7. Mobile responsive rules exist for card-grid (1 column) and card-link-icon-box (40px)\n8. rm -rf .next out \u0026\u0026 npm run build succeeds\n9. SVG icons render at 28px (not full page width)","status":"closed","priority":0,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":15,"created_at":"2026-02-20T19:08:19.964869+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T19:09:55.213289+08:00","closed_at":"2026-02-20T19:09:55.213289+08:00","close_reason":"b4049e0 Replace card-link CSS with icon-card layout and add card-grid","labels":["scope:small"],"dependencies":[{"issue_id":"docs-y5x.5","depends_on_id":"docs-y5x","type":"parent-child","created_at":"2026-02-20T19:08:19.96556+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-yhx","title":"Epic: Sidebar Polish","description":"Improve sidebar UX: active link has no background highlight (color-only, fails for color-blind users), parent-active too bold at 700, no left-border tree connectors for nested items, section header 11px may fail contrast, scrollbar invisible until hover, no mobile drawer close button. Covers UX findings #4, 11, 12, 13, 14, 15, 28, 29, 30. User wants one big PR for all sidebar work.","status":"closed","priority":1,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T19:54:24.88055+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:38.187258+08:00","closed_at":"2026-02-20T20:06:38.187258+08:00","close_reason":"c617130 all sidebar tasks complete","labels":["scope:medium"]} -{"id":"docs-yhx.1","title":"Sidebar: active state + parent weight + section header + scrollbar + spacing + mobile close","description":"## Files\n- styles/globals.css (modify)\n- components/Sidebar.js (modify)\n\n## What to do\nThis is a single big task covering all CSS-focused sidebar fixes. One branch, one PR.\n\n### Active link background highlight (finding #4)\nThe active sidebar link only changes color — color-blind users (~8%) cant distinguish it. Add a background:\n\n```css\n.sidebar-link-active {\n color: var(--color-link);\n font-weight: 500;\n background: var(--active-bg);\n}\n\na.sidebar-link-active:hover {\n background: var(--active-bg);\n color: var(--color-link);\n}\n```\n\nDark mode already has `--active-bg: oklch(0.22 0.01 260)` which works.\n\n### Parent-active weight reduction (finding #12)\nChange `.sidebar-link-parent-active` from `font-weight: 700` to `font-weight: 600`. This makes it less attention-grabbing than the active child.\n\n### Section header size + contrast (finding #14)\nChange `.sidebar-section-header` font-size from `11px` to `12px`. This improves contrast ratio at small sizes.\n\n### Tree connectors for nested items (finding #13)\nAdd a left border on nested nav children lists to create visual tree connectors:\n\n```css\n.sidebar-nav-children {\n list-style: none;\n margin-left: 20px;\n padding-left: 12px;\n border-left: 1px solid var(--color-border);\n}\n```\n\nDark mode: `html.dark .sidebar-nav-children { border-left-color: oklch(0.25 0.005 260); }`\n\n### Scrollbar visibility (finding #15)\nShow a subtle scrollbar by default (not just on hover). Change the initial scrollbar thumb from `transparent` to a light value:\n\nFor webkit:\n```css\n.sidebar-content::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.08);\n border-radius: 3px;\n}\n```\n\nFor Firefox, change `scrollbar-color: transparent transparent` to:\n```css\n.sidebar-content {\n scrollbar-color: rgba(0, 0, 0, 0.08) transparent;\n}\n```\n\nKeep the hover state that makes it darker. Dark mode: use `rgba(255, 255, 255, 0.08)` as default.\n\n### Section bottom padding (finding #30)\nAdd `padding-bottom: var(--space-2)` to `.sidebar-section` for symmetric spacing around section dividers.\n\n### Mobile drawer close button (finding #28)\nIn `Sidebar.js`, when `isOpen` is true (mobile drawer mode), show an X close button at the top-right of the sidebar. Add this inside the `\u003cnav\u003e` element, before the sidebar-content div:\n\n```jsx\n{isOpen \u0026\u0026 (\n \u003cbutton\n className=\"sidebar-close-btn\"\n onClick={onClose}\n aria-label=\"Close navigation\"\n \u003e\n \u003csvg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\"\u003e\n \u003cpath d=\"M5 5l10 10M15 5L5 15\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" /\u003e\n \u003c/svg\u003e\n \u003c/button\u003e\n)}\n```\n\nCSS for `.sidebar-close-btn`:\n- display none by default\n- `@media (max-width: 768px)`: display flex, position absolute, top var(--space-3), right var(--space-3), z-index 5, width 36px, height 36px, align-items center, justify-content center, background none, border none, cursor pointer, color var(--color-text-secondary), border-radius var(--radius-sm)\n- `:hover`: background var(--hover-bg), color var(--color-text-primary)\n\nWhen mobile drawer is open, hide the sidebar-collapse-btn (it is irrelevant on mobile). Add:\n```css\n@media (max-width: 768px) {\n .sidebar-collapse-btn {\n display: none;\n }\n}\n```\n\n### Skip finding #11 (nav icons) and #29 (collapsed icon rail)\nThese are complex structural changes that require modifying navigation.js data structure. Defer to a follow-up task.\n\n## Dont\n- Do NOT remove the sidebar collapse button on desktop\n- Do NOT change the sidebar width\n- Do NOT modify the navigation data structure in lib/navigation.js\n- Do NOT add icons to nav items (deferred)\n- Do NOT change the expand/collapse chevron behavior","acceptance_criteria":"1. Active sidebar link has a visible background highlight (not just color change)\n2. Parent-active links use font-weight 600 (was 700), visually lighter than active child\n3. Section headers render at 12px (was 11px)\n4. Nested nav items (e.g., Lexicons \u003e General \u003e Shared Defs) have a left border tree connector\n5. Scrollbar thumb is faintly visible by default without hovering\n6. Mobile drawer shows an X close button in the top-right corner\n7. Sidebar collapse button is hidden on mobile (irrelevant in drawer mode)\n8. Sections have symmetric top/bottom padding around dividers\n9. Dark mode: tree connectors, scrollbar, active background all adapt correctly\n10. Build passes: npm run build -- --webpack","status":"closed","priority":1,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":50,"created_at":"2026-02-20T19:54:52.786632+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.708722+08:00","closed_at":"2026-02-20T20:06:32.708722+08:00","close_reason":"c617130 sidebar polish","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-yhx.1","depends_on_id":"docs-yhx","type":"parent-child","created_at":"2026-02-20T19:54:52.787687+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-yne","title":"Epic 5: Styling and Visual Design","description":"## Summary\nCreate the complete CSS styling for the documentation site following a design language inspired by Stripe's documentation (docs.stripe.com). The goal is a clean, professional, content-focused documentation site that prioritizes readability and quiet confidence.\n\n## Context\nThis project is a Next.js + Markdoc documentation site in the `documentation/` directory. By the time this epic runs:\n- Epic 1 has scaffolded the project with a minimal `styles/globals.css`\n- Epic 2 has created React components: Callout, Columns, Column, Figure\n- Epic 4 has created Layout, Sidebar, TableOfContents components\n\nAll these components need polished CSS. The styles go in `styles/globals.css` (or can be split into CSS modules if preferred).\n\n## Design Principles\n\nThe Stripe documentation design language follows these core principles:\n1. **Content is the interface** -- every visual decision makes content more scannable, never decorates\n2. **Mostly monochrome, with surgical color** -- the page is 95% grayscale; color appears only for links, callout indicators, and status badges\n3. **Generous but purposeful whitespace** -- everything breathes, nothing floats\n4. **Border, not shadow, for structure** -- 1px light gray borders define regions, not heavy shadows\n5. **Developer-first** -- code blocks are first-class citizens, typography is optimized for technical content\n\n## Typography\n\n### Font stacks\n```css\n--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';\n--font-mono: 'Source Code Pro', Menlo, Monaco, Consolas, monospace;\n```\nUse the system font stack. It loads instantly, feels native on every OS, and is what Stripe uses.\n\n### Font weights\n- Regular: 400 (body text, descriptions)\n- Semibold: 600 (section headers, emphasis, sidebar labels)\n- Bold: 700 (page titles, H1)\n\n### Type scale\n| Element | Size | Weight | Color | Line Height |\n|---------|------|--------|-------|-------------|\n| H1 | 32px | 700 | #21252c (near-black) | 1.25 |\n| H2 | 24px | 700 | #353a44 (dark charcoal) | 1.3 |\n| H3 | 20px | 600 | #353a44 | 1.4 |\n| H4 | 16px | 600 | #353a44 | 1.5 |\n| Body | 16px | 400 | #414552 (dark gray) | 1.65 |\n| Small/Caption | 13px | 400 | #687385 (medium gray) | 1.5 |\n| Code (inline) | 14px | 400 | #414552 | inherit |\n| Code (block) | 14px | 400 | #414552 | 1.55 |\n\n### Anti-aliasing\n```css\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n```\n\n## Color Palette\n\n### Neutrals (the workhorse -- 95% of the page)\n| Token | Value | Usage |\n|-------|-------|-------|\n| --color-bg | #ffffff | Page background, content area |\n| --color-bg-subtle | #f6f8fa | Code block backgrounds, sidebar hover, cards |\n| --color-border | #ebeef1 | Section dividers, sidebar border, card borders |\n| --color-border-strong | #d8dee4 | Table header bottom border, input borders |\n| --color-text-secondary | #687385 | Descriptions, metadata, TOC items, captions |\n| --color-text-primary | #414552 | Body text |\n| --color-text-heading | #353a44 | Headings (H2-H4) |\n| --color-text-title | #21252c | H1 page titles |\n\n### Accent colors (used sparingly -- only for interactive elements and semantic indicators)\n| Token | Value | Usage |\n|-------|-------|-------|\n| --color-link | #0570de | Hyperlinks, interactive text |\n| --color-link-hover | #0055bc | Link hover state (slightly darker) |\n| --color-info | #0570de | Info callout border |\n| --color-info-bg | #f0f7ff | Info callout background |\n| --color-warning | #c84801 | Warning callout border |\n| --color-warning-bg | #fef9f0 | Warning callout background |\n| --color-danger | #df1b41 | Danger callout border |\n| --color-danger-bg | #fef0f4 | Danger callout background |\n| --color-success | #228403 | Success callout border |\n| --color-success-bg | #f0fef0 | Success callout background |\n\n### Focus ring\n```css\n--focus-ring: 0 0 0 4px rgba(5, 112, 222, 0.36);\n```\n\n## Spacing System\n\nUse an 8px base grid. All spacing should be multiples of 8 (with 4px for tight spaces):\n```css\n--space-1: 4px;\n--space-2: 8px;\n--space-3: 12px;\n--space-4: 16px;\n--space-5: 20px;\n--space-6: 24px;\n--space-8: 32px;\n--space-10: 40px;\n--space-12: 48px;\n--space-16: 64px;\n```\n\n### Application:\n- Between paragraphs: 16px\n- Between heading and its first paragraph: 8-12px (tight coupling)\n- Before a new H2 section: 48px (creates visible section breaks)\n- Sidebar item vertical spacing: 4-6px between items, 20-24px between section groups\n- Content area horizontal padding: 32px on each side\n- Content area top padding: 32px below header\n\n## Component Styles\n\n### Layout\n```\nHeader: height 56px, white background, bottom border 1px solid #ebeef1\nSidebar: width 240px, fixed/sticky, right border 1px solid #ebeef1, padding 16px\nContent: max-width 720px, centered, padding 32px\nRight TOC: width 200px, sticky top, padding-left 24px\n```\n\n### Sidebar\n- Section headers: 12px, font-weight 600, color #687385, text-transform uppercase, letter-spacing 0.05em, margin-bottom 8px\n- Nav items: 14px, color #414552, padding 6px 12px, border-radius 6px\n- Active nav item: font-weight 600, background-color #f6f8fa, color #21252c\n- Hover nav item: background-color #f6f8fa\n- Nested items: padding-left +16px\n\n### Table of Contents (right side)\n- Title \"On this page\": 12px, font-weight 600, color #687385, uppercase\n- Items: 13px, color #687385, padding 4px 0\n- Active item: color #0570de (link blue)\n- Left border indicator: 2px solid #0570de on active item (optional)\n\n### Callout boxes (from Epic 2 components)\n```css\n.callout {\n border-left: 4px solid var(--callout-color);\n background: var(--callout-bg);\n border-radius: 0 6px 6px 0;\n padding: 16px;\n margin: 16px 0;\n}\n.callout-title {\n font-weight: 600;\n margin-bottom: 4px;\n}\n```\n\n### Columns layout (from Epic 2 components)\n```css\n.columns {\n display: flex;\n gap: 24px;\n margin: 16px 0;\n}\n.column {\n flex: 1;\n min-width: 0;\n}\n@media (max-width: 768px) {\n .columns { flex-direction: column; }\n}\n```\n\n### Figure (from Epic 2 components)\n```css\n.figure {\n text-align: center;\n margin: 24px 0;\n}\n.figure img {\n max-width: 100%;\n height: auto;\n border-radius: 6px;\n}\n.figure-caption {\n font-size: 13px;\n color: #687385;\n margin-top: 8px;\n}\n```\n\n### Tables\nStripe uses clean, borderless tables with only a header bottom border:\n```css\ntable {\n width: 100%;\n border-collapse: collapse;\n margin: 16px 0;\n font-size: 14px;\n}\nth {\n text-align: left;\n font-weight: 600;\n color: #353a44;\n padding: 8px 12px;\n border-bottom: 1px solid #d8dee4;\n}\ntd {\n padding: 8px 12px;\n color: #414552;\n border-bottom: 1px solid #ebeef1;\n}\ntr:last-child td {\n border-bottom: none;\n}\n```\nNo zebra striping. No outer borders. Clean and minimal.\n\n### Code blocks\n```css\npre {\n background: #f6f8fa;\n border-radius: 6px;\n padding: 16px;\n overflow-x: auto;\n font-family: var(--font-mono);\n font-size: 14px;\n line-height: 1.55;\n margin: 16px 0;\n color: #414552;\n}\n```\nOptionally install `prism-react-renderer` for syntax highlighting. The current docs only have 2 JSON code blocks, but the SDK docs will grow. If adding syntax highlighting, use a light theme that matches the neutral palette.\n\n### Inline code\n```css\ncode {\n background: #f6f8fa;\n border-radius: 4px;\n padding: 2px 6px;\n font-family: var(--font-mono);\n font-size: 0.875em; /* slightly smaller than surrounding text */\n}\n/* Don't style code inside pre blocks */\npre code {\n background: none;\n border-radius: 0;\n padding: 0;\n font-size: inherit;\n}\n```\n\n### Links\n```css\na {\n color: #0570de;\n text-decoration: none;\n}\na:hover {\n color: #0055bc;\n text-decoration: underline;\n}\n```\n\n### Headings\n```css\nh1 { font-size: 32px; font-weight: 700; color: #21252c; margin-top: 0; margin-bottom: 16px; line-height: 1.25; }\nh2 { font-size: 24px; font-weight: 700; color: #353a44; margin-top: 48px; margin-bottom: 12px; line-height: 1.3; }\nh3 { font-size: 20px; font-weight: 600; color: #353a44; margin-top: 32px; margin-bottom: 8px; line-height: 1.4; }\nh4 { font-size: 16px; font-weight: 600; color: #353a44; margin-top: 24px; margin-bottom: 8px; line-height: 1.5; }\n```\n\nNote: `h2` has 48px top margin to create strong visual section breaks (the largest gaps on the page). This is a key Stripe pattern.\n\n### Pagination (prev/next)\n```css\n.pagination {\n display: flex;\n justify-content: space-between;\n margin-top: 48px;\n padding-top: 24px;\n border-top: 1px solid #ebeef1;\n}\n.pagination a {\n color: #0570de;\n font-weight: 500;\n}\n```\n\n### Lists\n```css\nul, ol {\n padding-left: 24px;\n margin: 12px 0;\n}\nli {\n margin: 6px 0;\n line-height: 1.65;\n}\nli \u003e p { margin: 0; }\n```\n\n### Blockquotes\n```css\nblockquote {\n border-left: 4px solid #ebeef1;\n padding: 0 16px;\n margin: 16px 0;\n color: #687385;\n}\n```\n\n### Horizontal rules\n```css\nhr {\n border: none;\n border-top: 1px solid #ebeef1;\n margin: 32px 0;\n}\n```\n\n## Responsive Breakpoints\n```css\n/* Mobile: \u003c 768px - sidebar and TOC hidden, content full width */\n/* Tablet: 768px - 1040px - sidebar visible, TOC hidden */\n/* Desktop: \u003e 1040px - full three-column layout */\n```\n\nOn mobile:\n- Sidebar hidden behind hamburger menu\n- Right TOC hidden entirely\n- Content goes full-width with 16px horizontal padding\n- Tables get `overflow-x: auto` wrapper\n\n## Shadow System\nShadows are used very sparingly -- almost never on the docs site:\n- Most elements: no shadow (flat)\n- Card hover: `0px 2px 5px rgba(48, 49, 61, 0.08)` (barely perceptible)\n- Dropdown/overlay: `0px 5px 15px rgba(0, 0, 0, 0.12), 0px 15px 35px rgba(48, 49, 61, 0.08)`\n\n## Border Radius\n- Inline code: 4px\n- Code blocks, callouts, cards: 6px\n- Buttons, tags, pills: 6px or rounded (9999em for pill shape)\n\n## Acceptance Criteria\n- All CSS is in `styles/globals.css` (or organized into CSS modules)\n- CSS custom properties are defined for colors, fonts, and spacing\n- System font stack is used (no custom font downloads)\n- Anti-aliased text rendering is enabled\n- All headings (H1-H4) follow the type scale with proper size, weight, color, and spacing\n- Body text is 16px, dark gray (#414552), line-height 1.65\n- Links are blue (#0570de), darken on hover, no underline by default\n- Tables are clean: no outer borders, header has bottom border only, proper padding\n- Code blocks have light gray background, rounded corners, monospace font\n- Inline code has subtle gray background pill\n- Callout boxes have colored left border and tinted background per type\n- Columns stack on mobile, flex row on desktop\n- Layout uses three columns on desktop (sidebar 240px, content ~720px, TOC ~200px)\n- Sidebar has 1px right border, section headers in uppercase small text\n- Active nav item has bold text and subtle background\n- Right TOC has scroll spy highlighting in blue\n- Responsive: sidebar/TOC collapse below 768px/1040px breakpoints\n- Pagination has top border separator and blue link text\n- The overall feel is clean, white, professional, content-focused -- no decorative elements, no gradients, no heavy shadows\n- The page looks like it could belong to the same family as docs.stripe.com\n","status":"closed","priority":2,"issue_type":"epic","owner":"sharfy.adamantine@gmail.com","created_at":"2026-02-13T13:43:24.486687+13:00","created_by":"Sharfy Adamantine","updated_at":"2026-02-14T13:18:01.287139+13:00","closed_at":"2026-02-14T13:18:01.287141+13:00","dependencies":[{"issue_id":"docs-yne","depends_on_id":"docs-c34","type":"blocks","created_at":"2026-02-13T13:43:24.48892+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-yne","depends_on_id":"docs-vbw","type":"blocks","created_at":"2026-02-13T13:43:24.490264+13:00","created_by":"Sharfy Adamantine"},{"issue_id":"docs-yne","depends_on_id":"docs-7dv","type":"blocks","created_at":"2026-02-13T13:43:24.491023+13:00","created_by":"Sharfy Adamantine"}]} -{"id":"docs-yzy","title":"Epic: Separate planned funding/tokenization content into dedicated page","description":"Move all planned/TBD content about freeze-then-fund, tokenization, on-chain mechanisms, multi-chain support, and funding patterns out of the main documentation pages and into a single dedicated page. Main docs should describe what exists today (ATProto data layer) and briefly mention that on-chain funding is planned, linking to the dedicated page for details.","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:11:36.619138+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T19:52:57.591064+13:00","closed_at":"2026-02-16T19:52:57.591064+13:00","close_reason":"Closed"} -{"id":"docs-yzy.1","title":"Create architecture/planned-funding-and-tokenization.md — the dedicated page for all planned on-chain content","description":"## Files\n- documentation/pages/architecture/planned-funding-and-tokenization.md (create)\n\n## What to do\n\nCreate a new page that consolidates ALL planned/TBD content about the on-chain funding and tokenization layer. This page is the single source of truth for what's planned but not yet built.\n\nThe page should have this structure:\n\n### Frontmatter\n```\n---\ntitle: \"Planned: Funding \u0026 Tokenization\"\ndescription: How hypercerts will be frozen, anchored on-chain, and funded. This layer is not yet implemented.\n---\n```\n\n### Page content (write this in full):\n\n**Opening paragraph:** The on-chain funding and tokenization layer is not yet implemented. This page describes the planned design. The theory and architecture are sound — the implementation is in progress. For what exists today (the ATProto data layer), see [Architecture Overview](/architecture/overview).\n\n**Section: The Freeze-Then-Fund Model**\nThe core concept: before a hypercert can be funded, its ATProto records must be frozen. Freezing means taking a cryptographic snapshot (CID) of the activity claim and all its associated records at a point in time. This snapshot is then anchored on-chain.\n\nWhy freezing is necessary: a funder must know exactly what they are funding. If the cert's contents could change after funding, the funder might end up paying for a different cert than what they committed to. A hypercert cannot be funded if its contents are still changing.\n\nWhat freezing preserves: the core activity claim — who did what, when, where. The frozen state is what funders commit to.\n\nWhat continues after freezing: evaluations and evidence can still accumulate. These are separate records that reference the frozen claim. The claim's reputation can evolve while its core content remains fixed.\n\n**Section: On-Chain Anchoring**\nWhen a hypercert is frozen, its snapshot CID will be anchored on-chain via a smart contract. The contract stores the AT-URI and the frozen snapshot CID, creating a verifiable link between the data layer and the ownership layer.\n\nThis creates an immutable reference point. Anyone can verify that the on-chain record matches the ATProto data by comparing CIDs.\n\n**Section: Tokenization**\nOnce frozen and anchored, the hypercert can be represented as a transferable token on-chain. A token could represent full ownership or fractional shares. Token holders would have rights defined in the hypercert's org.hypercerts.claim.rights record.\n\nThe specific token standard (ERC-1155, ERC-721, or a custom standard) is being designed. The protocol intends to support multiple token standards for different use cases — from non-transferable recognition to fully tradable certificates.\n\n**Section: Funding Mechanisms**\nOnce frozen and anchored, various funding models can operate on the ownership layer:\n- Direct funding — funders acquire shares directly from the contributor\n- Retroactive funding — rewarding past work based on demonstrated outcomes\n- Impact certificates — creating markets for outcomes\n- Quadratic funding — amplifying small donations through matching pools\n- Milestone-based payouts — releasing funds as work progresses\n\nSmart contracts will enforce rules and distribute payments. The specific implementations are being designed.\n\n**Section: Funding Readiness Patterns**\nDifferent applications may use different patterns for when to freeze:\n\nPattern 1: Freeze-on-create — the claim is frozen immediately upon creation. Simple but means the claim can't be enriched before funding.\n\nPattern 2: Freeze-when-ready — the claim exists on ATProto, accumulates evidence and evaluations, and is frozen only when a funder expresses interest or the contributor decides it's ready. This is the expected default pattern.\n\nPattern 3: Batch freezing — multiple claims are frozen and anchored together periodically. Cost-efficient for high-volume use cases.\n\nPattern 4: Partial freezing — some aspects of the claim are frozen (e.g., the core activity and rights) while others remain mutable (e.g., ongoing measurements). The frozen portion is what funders commit to.\n\n**Section: Multi-Chain Support**\nThe protocol is designed to be chain-agnostic. Different communities may use different chains. The ATProto data layer remains the same regardless of which chain anchors the frozen snapshot. The specific multi-chain architecture is being designed.\n\n**Section: Cross-Layer Example**\nWalk through the full planned flow:\n1. Alice creates an activity claim on her PDS → gets AT-URI\n2. Bob evaluates Alice's claim from his PDS\n3. Alice decides the claim is ready for funding → freezes it\n4. An application anchors the frozen snapshot on-chain → gets token ID\n5. Carol funds the frozen cert on-chain\n6. New evaluations continue accumulating on ATProto, referencing the frozen claim\n7. Carol can verify her funded cert matches the frozen snapshot by comparing CIDs\n\nInclude the ASCII diagram:\n```\nATProto (exists today) On-chain (planned)\n────────────────────── ──────────────────\nActivity Claim Frozen Snapshot\nat://did:alice/... CID: bafyrei...\n ↓ ↓\nEvidence, Evaluations Ownership Record\nMeasurements, Rights Funder: 0xCarol...\n Metadata: { uri, cid }\n```\n\n**Section: What This Will Enable**\n- Retroactive funding of past contributions\n- Composable funding mechanisms across platforms\n- Portable proof of funding (funders can prove what they funded)\n- Independent evolution of data and ownership layers\n\n**Closing:** For the current architecture (what exists today), see [Architecture Overview](/architecture/overview). For the data lifecycle, see [Data Flow \u0026 Lifecycle](/architecture/data-flow-and-lifecycle).\n\n## Test\ntest -f documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'not yet implemented' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'Freeze-Then-Fund' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'cannot.*funded.*chang\\|can.t.*funded.*chang\\|exactly what' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'Funding Readiness Patterns\\|Funding.*Pattern' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\ngrep -q 'Multi-Chain' documentation/pages/architecture/planned-funding-and-tokenization.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Present any of this as implemented/live\n- Invent specific contract addresses or deployed details\n- Make it too short — this is THE comprehensive reference for all planned on-chain work\n- Forget the cross-layer example with the ASCII diagram","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:12:25.547762+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T17:16:00.16776+13:00","closed_at":"2026-02-16T17:16:00.16776+13:00","close_reason":"d937d10 Created comprehensive planned funding and tokenization architecture page","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-yzy.1","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:12:25.549901+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-yzy.2","title":"Update navigation and rewrite blockchain-integration.md to redirect to planned page","description":"## Files\n- documentation/lib/navigation.js (modify)\n- documentation/pages/getting-started/infrastructure/blockchain-integration.md (modify)\n- documentation/pages/getting-started/infrastructure/portability-and-scaling.md (modify)\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify — just the link on line 78)\n\n## What to do\n\n### 1. Update navigation.js\n\nChange line 25 from:\n```js\n{ title: 'Blockchain Integration', path: '/getting-started/infrastructure/blockchain-integration' },\n```\nto:\n```js\n{ title: 'Funding \u0026 Tokenization (Planned)', path: '/architecture/planned-funding-and-tokenization' },\n```\n\nAlso add the new page to the Understand section, after \"Data Flow \u0026 Lifecycle\" (line 30). Add:\n```js\n{ title: 'Planned: Funding \u0026 Tokenization', path: '/architecture/planned-funding-and-tokenization' },\n```\n\nWait — actually, since it's already linked as a child of \"The Hypercerts Infrastructure\", just update that child entry. Don't add a duplicate. So the only change is line 25.\n\n### 2. Rewrite blockchain-integration.md\n\nThis file currently has the old mint-on-create / lazy minting / batch anchoring / hybrid ownership patterns. Replace the entire content with a redirect notice:\n\n```markdown\n---\ntitle: Funding \u0026 Tokenization\n---\n\n# Funding \u0026 Tokenization\n\nThis content has moved to [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\n```\n\n### 3. Update portability-and-scaling.md\n\nLines 36-38 have \"Blockchain scalability\" section that says \"On-chain operations (minting, transfers, sales) are expensive...\" — this is planned content. Replace with:\n\n```markdown\n#### On-chain scalability (planned)\n\nThe on-chain funding and tokenization layer is not yet implemented. When built, on-chain operations will be expensive, so hypercerts will minimize on-chain activity by keeping rich data on ATProto. Only frozen snapshots and funding flows will touch the blockchain. For details on the planned on-chain design, see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\n```\n\nAlso lines 50-52 have \"Access control via smart contracts\" with token-gated data. Replace with:\n\n```markdown\n#### Access control via smart contracts (planned)\n\nIn the planned design, on-chain tokens could have access control logic — for example, granting read access to private ATProto records only to token holders. This is a potential future feature. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\n```\n\n### 4. Update the-hypercerts-infrastructure.md line 78\n\nChange:\n```\n- [Blockchain Integration](/getting-started/infrastructure/blockchain-integration) — minting patterns and on-chain ownership\n```\nto:\n```\n- [Funding \u0026 Tokenization (Planned)](/architecture/planned-funding-and-tokenization) — freeze-then-fund model and on-chain ownership design\n```\n\n## Test\ngrep -q 'planned-funding-and-tokenization' documentation/lib/navigation.js \u0026\u0026 \\\ngrep -q 'moved to' documentation/pages/getting-started/infrastructure/blockchain-integration.md \u0026\u0026 \\\ngrep -q 'planned-funding-and-tokenization' documentation/pages/getting-started/the-hypercerts-infrastructure.md \u0026\u0026 \\\ngrep -q 'not yet implemented' documentation/pages/getting-started/infrastructure/portability-and-scaling.md \u0026\u0026 \\\n! grep -q 'mints an NFT\\|Mint-on-create\\|Lazy minting' documentation/pages/getting-started/infrastructure/blockchain-integration.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Add duplicate nav entries\n- Delete blockchain-integration.md (keep it as a redirect so old links work)\n- Change anything else in navigation.js\n- Change anything else in the-hypercerts-infrastructure.md besides line 78\n- Change anything else in portability-and-scaling.md besides the blockchain scalability and token-gated sections","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:12:50.318417+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T17:18:08.762614+13:00","closed_at":"2026-02-16T17:18:08.762614+13:00","close_reason":"e8dd74d Update navigation and redirect blockchain-integration to planned funding page","labels":["scope:small"],"dependencies":[{"issue_id":"docs-yzy.2","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:12:50.31972+13:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"docs-yzy.2","depends_on_id":"docs-yzy.1","type":"blocks","created_at":"2026-02-16T17:14:24.044876+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-yzy.3","title":"Strip planned content from architecture/overview.md and architecture/data-flow-and-lifecycle.md — link to planned page","description":"## Files\n- documentation/pages/architecture/overview.md (modify)\n- documentation/pages/architecture/data-flow-and-lifecycle.md (modify)\n\n## What to do\n\nRemove detailed planned/TBD content from these two architecture pages. Replace with brief mentions and links to /architecture/planned-funding-and-tokenization. The main docs should describe what EXISTS TODAY (ATProto data layer) and only briefly note that on-chain funding is planned.\n\n### architecture/overview.md\n\n**Line 8:** Change to: \"The Hypercerts Protocol uses AT Protocol for data portability. On-chain anchoring for ownership and funding is [planned](/architecture/planned-funding-and-tokenization).\"\n\n**Line 18 (Ownership Layer):** Condense to 1-2 sentences max: \"The **Ownership Layer** is planned but not yet implemented. The intended design uses a freeze-then-fund model where hypercerts are frozen and anchored on-chain before funding — ensuring funders know exactly what they are paying for. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\"\n\n**Lines 48-70 (Ownership Layer Deep Dive):** DELETE this entire section (Anchoring, Tokenization, Funding Mechanisms, Multi-Chain Support). Replace with a single short paragraph:\n\n\"## Ownership Layer (Planned)\n\nThe ownership layer is not yet implemented. The planned design freezes ATProto records and anchors them on-chain before funding, ensuring funders know exactly what they are paying for. For the full planned design — including anchoring, tokenization, funding mechanisms, and multi-chain support — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n**Lines 72-95 (How the Layers Connect):** Keep the first paragraph about content living on ATProto (line 76). Remove lines 78-80 (detailed planned flow) and the cross-layer example (lines 86-95). Replace with:\n\n\"A hypercert's **ownership and funding state** will live on-chain once the tokenization layer is built. The planned bridge is a freeze-then-fund mechanism. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full cross-layer design.\"\n\nKeep the callout (lines 82-84) but simplify: \"The separation matters. ATProto provides data portability — users can switch servers, applications can read across the network, and records outlive any single platform. On-chain anchoring will provide ownership and funding guarantees. Neither layer can provide both properties alone.\"\n\n**Lines 105-107 (Why Not Fully Off-Chain?):** Condense. Keep the core point (mutable records are fine for collaboration but funding requires immutability) but remove the detailed freeze-then-fund explanation. Add link.\n\n**Lines 115-119 (Why This Separation):** Simplify. Remove \"through the planned freeze-then-fund mechanism\" and \"Once the tokenization layer is built\". Just say: \"Each layer does what it does best. ATProto handles identity, data portability, and schemas. On-chain anchoring will handle ownership, funding, and immutability. This separation reduces costs, increases flexibility, and maintains portability.\"\n\n**Lines 121-131 (What This Enables):** Remove \"(Planned)\" labels and the detailed planned descriptions. Keep the 4 bullet points but make them concise. For the two that work today, keep as-is. For the two that are planned, just say \"planned\" in one phrase and link to the planned page.\n\n**Add to Next Steps:** Add a link: \"For the planned on-chain funding and tokenization design, see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n### architecture/data-flow-and-lifecycle.md\n\n**Line 22 (Funding summary):** Condense to: \"**Funding** connects ownership to the claim. The on-chain funding layer is [planned but not yet implemented](/architecture/planned-funding-and-tokenization). The intended design freezes ATProto records before funding to ensure funders know exactly what they are paying for.\"\n\n**Line 24 (Accumulation):** Simplify to: \"**Accumulation** continues indefinitely. More evaluations arrive. Additional evidence gets attached. The data layer continues evolving.\"\n\n**Line 29 (diagram):** Change \"On-chain (planned)\" to just \"On-chain*\" and add a footnote: \"*On-chain layer is planned. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n**Lines 146-187 (Stage 5: Funding \u0026 Ownership):** DELETE the detailed content. Replace with a short section:\n\n\"## Stage 5: Funding \u0026 Ownership (Planned)\n\nThe on-chain funding layer is not yet implemented. The planned design: before a hypercert can be funded, its ATProto records are frozen and the snapshot is anchored on-chain. This ensures funders know exactly what they are paying for — the cert's contents cannot change after freezing.\n\nFor the full planned design — including anchoring, tokenization, funding mechanisms, funding readiness patterns, and multi-chain support — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n**Lines 205-218 (Ownership Transfers, Long-Term Value in Stage 6):** Remove the \"Ownership Transfers\" subsection entirely (it's all planned content). Keep \"Long-Term Value\" but simplify: \"The separation of data and ownership enables long-term value accumulation. A hypercert's reputation can grow as evaluations accumulate. The data remains portable and accessible regardless of future ownership changes.\"\n\nRemove the timeline diagram (lines 213-218) or simplify it to only show what exists today:\n```\nTime →\n─────────────────────────────────────────────────────────\nCreation Evaluation 1 Discovery Evaluation 2 Evidence Evaluation 3\n ↓ ↓ ↓ ↓ ↓ ↓\n PDS PDS-Eva1 Indexer PDS-Eva2 PDS PDS-Eva3\n```\n\n## Test\n! grep -q 'Multi-Chain Support' documentation/pages/architecture/overview.md \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/architecture/overview.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/architecture/data-flow-and-lifecycle.md | grep -qv '^0$' \u0026\u0026 \\\n! grep -q 'Anchoring (Planned)' documentation/pages/architecture/overview.md \u0026\u0026 \\\n! grep -q 'Tokenization (Planned)' documentation/pages/architecture/overview.md \u0026\u0026 \\\n! grep -q 'Funding Mechanisms (Planned)' documentation/pages/architecture/overview.md \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the Data Layer sections — those describe what exists today\n- Remove the Key Design Decisions section entirely — just simplify it\n- Remove the \"What This Enables\" section — just simplify it\n- Add new planned content — we're REMOVING it and linking to the dedicated page\n- Change Stages 1-4 in data-flow-and-lifecycle.md\n- Change the Cross-PDS References or \"What This Flow Enables\" sections in data-flow-and-lifecycle.md","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:13:45.724521+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T17:19:22.582072+13:00","closed_at":"2026-02-16T17:19:22.582072+13:00","close_reason":"4ed89bd Strip planned content from architecture docs and link to planned page","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-yzy.3","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:13:45.725402+13:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"docs-yzy.3","depends_on_id":"docs-yzy.1","type":"blocks","created_at":"2026-02-16T17:14:24.15891+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-yzy.4","title":"Strip planned content from 5 getting-started and reference pages — link to planned page","description":"## Files\n- documentation/pages/getting-started/the-hypercerts-infrastructure.md (modify)\n- documentation/pages/getting-started/why-atproto.md (modify)\n- documentation/pages/getting-started/why-were-building-hypercerts.md (modify)\n- documentation/pages/getting-started/introduction-to-impact-claims.md (modify)\n- documentation/pages/reference/faq.md (modify)\n\n## What to do\n\nRemove detailed planned/TBD content from these 5 pages. Replace with brief mentions and links to /architecture/planned-funding-and-tokenization.\n\n### the-hypercerts-infrastructure.md\n\n**Line 7:** Change to: \"Hypercerts runs on AT Protocol for data. On-chain anchoring for ownership and funding is [planned](/architecture/planned-funding-and-tokenization).\"\n\n**Lines 19-23 (The funding layer section):** Condense to 2-3 sentences max:\n\n\"#### The funding layer: on-chain anchoring (planned)\n\nThe on-chain funding layer is not yet implemented. The planned design uses a freeze-then-fund model: before a hypercert can be funded, its ATProto records are frozen and anchored on-chain, ensuring funders know exactly what they are paying for. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full design.\"\n\n**Lines 25-29 (How the layers connect):** Condense. Keep the AT-URI example. Remove the detailed planned flow. Just say: \"A claim can exist on ATProto without ever being frozen for funding. The two layers are loosely coupled and complement each other.\"\n\n**Lines 53-57 (Step 4):** Condense to:\n\n\"#### 4. The hypercert is frozen and funded (planned)\n\nIn the planned design, Carol's funding app will freeze Alice's hypercert and anchor the snapshot on-chain. Carol knows exactly what she is paying for because the frozen claim cannot change. The tokenization layer is not yet implemented — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\"\n\n### why-atproto.md\n\n**Lines 49-51 (ATProto + Blockchain section):** Condense to:\n\n\"ATProto handles the data layer — claims, evidence, evaluations, trust signals. On-chain anchoring is planned to handle the funding layer. The intended design: hypercerts are frozen and anchored on-chain before funding, ensuring funders know exactly what they're paying for. The tokenization layer is not yet implemented — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full design.\"\n\n### why-were-building-hypercerts.md\n\n**Line 182:** Keep as-is: \"That's where onchain anchoring comes in — and eventually, tokenization.\"\n\n**Lines 184-192 (The Ownership \u0026 Funding Layer):** Condense to:\n\n\"#### The Funding Layer: On-chain Anchoring (Planned)\n\nATProto provides the data layer. But funding requires a stronger guarantee: funders need to know that what they're paying for won't change after the fact. The planned approach is freeze-then-fund — before a hypercert can be funded, its ATProto records are frozen and anchored on-chain. The tokenization layer is not yet implemented. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for the full design.\"\n\n### introduction-to-impact-claims.md\n\n**Line 21:** Condense to: \"Hypercerts can exist as a standalone activity claim, or they can be [frozen and funded](/architecture/planned-funding-and-tokenization) on-chain (planned).\"\n\n### faq.md\n\n**Line 18 (How is this different):** Condense to: \"The new protocol is built on AT Protocol instead of purely on-chain. This gives data portability, richer schemas, and lower costs. On-chain anchoring for funding is [planned](/architecture/planned-funding-and-tokenization) but not yet implemented.\"\n\n**Lines 20-22 (Do I need a blockchain wallet?):** Condense to: \"Not to create or evaluate hypercerts — you only need an ATProto account (DID). A blockchain wallet will be needed for on-chain funding once the [tokenization layer](/architecture/planned-funding-and-tokenization) is built.\"\n\n**Lines 37-38 (How do I fund?):** Condense to: \"The on-chain funding layer is not yet implemented. The planned design freezes ATProto records before funding to ensure funders know exactly what they are paying for. See [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization) for details.\"\n\n**Lines 44-46 (What chains?):** Condense to: \"The protocol intends to be chain-agnostic. The on-chain layer is not yet implemented — see [Planned: Funding \u0026 Tokenization](/architecture/planned-funding-and-tokenization).\"\n\n## Test\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/the-hypercerts-infrastructure.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/why-atproto.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/why-were-building-hypercerts.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/getting-started/introduction-to-impact-claims.md | grep -qv '^0$' \u0026\u0026 \\\ngrep -c 'planned-funding-and-tokenization' documentation/pages/reference/faq.md | grep -qv '^0$' \u0026\u0026 \\\necho \"PASS\" || echo \"FAIL\"\n\n## Don't\n- Remove the data layer sections — those describe what exists today\n- Remove the data flow walkthrough (Steps 1-3 in infrastructure) — those are accurate\n- Remove the Data Integrity and Trust section in infrastructure — accurate\n- Change anything in why-were-building-hypercerts.md before line 172\n- Add new planned content — we're condensing and linking\n- Change the \"Keep Reading\" links in infrastructure (except the blockchain-integration link which is handled by another task)","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T17:14:18.331984+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-03T14:42:32.355416674+06:00","closed_at":"2026-02-17T05:24:43.922182+13:00","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-yzy.4","depends_on_id":"docs-yzy","type":"parent-child","created_at":"2026-02-16T17:14:18.333338+13:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"docs-yzy.4","depends_on_id":"docs-yzy.1","type":"blocks","created_at":"2026-02-16T17:14:24.250458+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-z1q","title":"Epic: Search Improvements","description":"Fix search UX: no keyboard navigation of results (arrow keys dont work), no fuzzy matching (typo = 0 results), empty state shows all pages instead of popular links, no result grouping by section. Covers UX findings #21, 22, 36, 37.","status":"closed","priority":2,"issue_type":"epic","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","created_at":"2026-02-20T19:54:59.33049+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:38.262731+08:00","closed_at":"2026-02-20T20:06:38.262731+08:00","close_reason":"1094f5d all search tasks complete","labels":["scope:medium"]} -{"id":"docs-z1q.1","title":"Search keyboard nav + fuzzy matching + result grouping + empty state","description":"## Files\n- components/SearchDialog.js (modify)\n- styles/globals.css (modify)\n- lib/navigation.js (modify — add section info to flattenNavigation)\n\n## What to do\n\n### Keyboard navigation of results (finding #21)\nAdd arrow key support to the search dialog. Track a `selectedIndex` state (default -1 = none selected):\n\n1. Add `const [selectedIndex, setSelectedIndex] = useState(-1)` state\n2. Reset selectedIndex to 0 when results change (in a useEffect on results.length or query)\n3. In the `onKeyDown` handler on the input, add:\n - ArrowDown: `e.preventDefault(); setSelectedIndex(i =\u003e Math.min(i + 1, results.length - 1))`\n - ArrowUp: `e.preventDefault(); setSelectedIndex(i =\u003e Math.max(i - 1, 0))`\n - Enter: navigate to `results[selectedIndex]` (or results[0] if selectedIndex is -1)\n4. Add class `search-result-item-selected` to the item at `selectedIndex`\n5. Use a ref to scroll the selected item into view: `itemRefs[selectedIndex]?.scrollIntoView({ block: \"nearest\" })`\n\nCSS for `.search-result-item-selected`:\n```css\n.search-result-item-selected {\n background: var(--hover-bg);\n}\n```\n\n### Fuzzy matching (finding #22)\nReplace the exact substring match with a simple fuzzy algorithm. Do NOT add external dependencies.\n\nImplement a `fuzzyMatch(query, text)` function:\n- Convert both to lowercase\n- Check if all characters of query appear in text in order (not necessarily contiguous)\n- Return true/false\n\n```js\nfunction fuzzyMatch(query, text) {\n const q = query.toLowerCase();\n const t = text.toLowerCase();\n let qi = 0;\n for (let ti = 0; ti \u003c t.length \u0026\u0026 qi \u003c q.length; ti++) {\n if (t[ti] === q[qi]) qi++;\n }\n return qi === q.length;\n}\n```\n\nUse fuzzyMatch for filtering, then sort by: exact substring match first, then fuzzy-only matches.\n\n### Result grouping by section (finding #37)\nModify `flattenNavigation` in `lib/navigation.js` to also return the section name for each page. Change the return type from `{ title, path }` to `{ title, path, section }`.\n\nIn `flattenNavigation`, track the current section:\n```js\nexport function flattenNavigation(nav = navigation, currentSection = \"\") {\n const result = [];\n for (const item of nav) {\n const section = item.section || currentSection;\n if (item.path) {\n result.push({ title: item.title, path: item.path, section });\n }\n if (item.children) {\n result.push(...flattenNavigation(item.children, section));\n }\n }\n return result;\n}\n```\n\nIn SearchDialog, group results by section. Render section headers between groups:\n```jsx\n{Object.entries(groupedResults).map(([section, items]) =\u003e (\n \u003cli key={section}\u003e\n \u003cdiv className=\"search-result-section\"\u003e{section || \"General\"}\u003c/div\u003e\n \u003cul className=\"search-results-list\"\u003e\n {items.map(page =\u003e (/* existing result item */))}\n \u003c/ul\u003e\n \u003c/li\u003e\n))}\n```\n\nCSS for `.search-result-section`: font-size 11px, font-weight 700, text-transform uppercase, letter-spacing 0.06em, color var(--color-text-secondary), padding 8px 12px 4px, margin-top 4px.\n\n### Empty state (finding #36)\nWhen query is empty, instead of showing ALL pages, show a curated set of 5-6 popular pages grouped as \"Quick Links\":\n- Quickstart\n- What are Hypercerts?\n- Core Data Model\n- Scaffold Starter App\n- Architecture Overview\n- Glossary\n\nFilter these from allPages by path. Show them under a \"Quick Links\" section header.\n\nWhen query has no matches, show \"No results for [query]\" with the quick links below it as fallback.\n\n## Dont\n- Do NOT add any npm dependencies (no fuse.js, no external fuzzy library)\n- Do NOT change the Escape key behavior\n- Do NOT change the ⌘K shortcut\n- Do NOT change the search dialog visual layout (width, position, overlay)","acceptance_criteria":"1. Arrow Down/Up keys move a visual highlight through search results\n2. Enter navigates to the currently highlighted result\n3. Typing a partial/misspelled query still returns relevant results (e.g., \"quikstart\" matches \"Quickstart\")\n4. Results are grouped under section headers (e.g., \"Get Started\", \"Core Concepts\", \"Tools\")\n5. Empty state shows 5-6 Quick Links instead of dumping all 40 pages\n6. No external dependencies were added\n7. selectedIndex resets when query changes\n8. Build passes: npm run build -- --webpack","status":"closed","priority":2,"issue_type":"task","assignee":"einstein.climateai.org","owner":"einstein.climateai.org","estimated_minutes":50,"created_at":"2026-02-20T19:55:26.436073+08:00","created_by":"einstein.climateai.org","updated_at":"2026-02-20T20:06:32.852484+08:00","closed_at":"2026-02-20T20:06:32.852484+08:00","close_reason":"1094f5d search keyboard nav + fuzzy + grouping","labels":["scope:medium"],"dependencies":[{"issue_id":"docs-z1q.1","depends_on_id":"docs-z1q","type":"parent-child","created_at":"2026-02-20T19:55:26.437153+08:00","created_by":"einstein.climateai.org"}]} -{"id":"docs-z6h","title":"Epic: Mobile Responsiveness Fixes","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T23:33:01.220879+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T23:41:13.491601+13:00","closed_at":"2026-02-16T23:41:13.491601+13:00","close_reason":"b2ca35e All 5 mobile responsiveness tasks complete"} -{"id":"docs-z6h.1","title":"Add mobile typography scaling — reduce heading sizes below 768px","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd mobile-specific font size reductions inside the existing `@media (max-width: 768px)` block (line 990-1034).\n\nAdd these rules before the closing `}` of the mobile media query (before line 1034):\n\n```css\n/* Typography scaling for mobile */\n.layout-content h1 {\n font-size: 26px;\n margin-bottom: var(--space-4);\n}\n\n.layout-content h2 {\n font-size: 20px;\n line-height: 28px;\n margin-top: var(--space-8);\n}\n\n.layout-content h3 {\n font-size: 15px;\n}\n```\n\nContext: Currently h1 is 32px (line 580), h2 is 24px (line 589), h3 is 16px (line 598) on all screen sizes. 32px h1 is too large on a 375px-wide phone screen.\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\nconst mobileBlock = css.split(\\\"@media (max-width: 768px)\\\").slice(1).join(\\\"\\\");\nconst hasH1 = /\\\\.layout-content\\\\s+h1[^}]*font-size:\\\\s*2[4-8]px/.test(mobileBlock);\nconst hasH2 = /\\\\.layout-content\\\\s+h2[^}]*font-size:\\\\s*(1[8-9]|2[0-2])px/.test(mobileBlock);\nif (!hasH1) { console.error(\\\"FAIL: no mobile h1 scaling\\\"); process.exit(1); }\nif (!hasH2) { console.error(\\\"FAIL: no mobile h2 scaling\\\"); process.exit(1); }\nconsole.log(\\\"PASS\\\");\n\"\n```\n\n## Dont\n- Do not change the desktop font sizes (lines 579-613)\n- Do not create a new media query — add to the existing one at line 990\n- Do not change any other CSS rules\n- Do not modify any JS files","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T23:33:25.213288+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T23:39:28.347708+13:00","closed_at":"2026-02-16T23:39:28.347708+13:00","close_reason":"67d28e0 Add mobile typography scaling — reduce heading sizes below 768px","labels":["scope:small"],"dependencies":[{"issue_id":"docs-z6h.1","depends_on_id":"docs-z6h","type":"parent-child","created_at":"2026-02-16T23:33:25.215354+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-z6h.2","title":"Increase touch target sizes for sidebar and TOC links on mobile","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nSidebar links (line 400-411) have `padding: 6px var(--space-3)` with `line-height: 20px`, giving a total height of ~32px. The minimum touch target for mobile is 44px per WCAG guidelines.\n\nAdd these rules inside the existing `@media (max-width: 768px)` block (line 990-1034), before the closing `}`:\n\n```css\n/* Increase touch targets for mobile */\n.sidebar-link {\n padding: 10px var(--space-3);\n min-height: 44px;\n display: flex;\n align-items: center;\n}\n```\n\nNote: TOC links (line 502-510) have `padding: var(--space-1) 0` (~28px total height), but the TOC is already hidden on mobile via `@media (max-width: 1200px)` at line 983-987, so no fix needed there.\n\nAlso increase the hamburger button touch target (line 215-225). It currently has `padding: var(--space-1)` (4px). Change the hamburger button padding to ensure 44px minimum:\n\n```css\n.hamburger-btn {\n padding: var(--space-2);\n min-width: 44px;\n min-height: 44px;\n}\n```\n\nThis hamburger rule goes inside the mobile media query since the button is only visible on mobile anyway.\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\nconst mobileBlock = css.split(\\\"@media (max-width: 768px)\\\").slice(1).join(\\\"\\\");\nconst hasSidebarPadding = /\\\\.sidebar-link[^}]*min-height:\\\\s*44px/.test(mobileBlock);\nconst hasHamburger = /\\\\.hamburger-btn[^}]*min-height:\\\\s*44px/.test(mobileBlock);\nif (!hasSidebarPadding) { console.error(\\\"FAIL: sidebar links not 44px\\\"); process.exit(1); }\nif (!hasHamburger) { console.error(\\\"FAIL: hamburger not 44px\\\"); process.exit(1); }\nconsole.log(\\\"PASS\\\");\n\"\n```\n\n## Dont\n- Do not change the desktop sidebar link styles (lines 400-411)\n- Do not change TOC link styles (TOC is already hidden on mobile)\n- Do not create a new media query — add to the existing one at line 990\n- Do not modify any JS files","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T23:33:38.128986+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T23:39:06.53761+13:00","closed_at":"2026-02-16T23:39:06.53761+13:00","close_reason":"ac33796 Increase touch target sizes for sidebar and TOC links on mobile","labels":["scope:small"],"dependencies":[{"issue_id":"docs-z6h.2","depends_on_id":"docs-z6h","type":"parent-child","created_at":"2026-02-16T23:33:38.130326+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-z6h.3","title":"Add tablet breakpoint (768-1024px) with content padding adjustment","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nCurrently there are only 3 breakpoints:\n- `@media (min-width: 1400px)` — line 138 (wider sidebar)\n- `@media (max-width: 1200px)` — line 983 (hides TOC)\n- `@media (max-width: 768px)` — line 990 (mobile layout)\n\nThe 768-1024px range (tablet) has no specific styles. The sidebar takes 250px and the content area has large padding, leaving cramped content on tablets.\n\nAdd a new media query between the TOC-hiding rule (line 987) and the mobile rule (line 990):\n\n```css\n/* Tablet: reduce content padding, narrow sidebar */\n@media (min-width: 769px) and (max-width: 1024px) {\n :root {\n --sidebar-width: 220px;\n }\n\n .layout-content {\n padding: var(--space-6) var(--space-6);\n }\n\n .layout-content h1 {\n font-size: 28px;\n }\n}\n```\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\nconst hasTablet = css.includes(\\\"max-width: 1024px\\\") \u0026\u0026 css.includes(\\\"min-width: 769px\\\");\nconst hasSidebarWidth = /--sidebar-width:\\s*220px/.test(css);\nif (!hasTablet) { console.error(\\\"FAIL: no tablet breakpoint\\\"); process.exit(1); }\nif (!hasSidebarWidth) { console.error(\\\"FAIL: no tablet sidebar width\\\"); process.exit(1); }\nconsole.log(\\\"PASS\\\");\n\"\n```\n\n## Dont\n- Do not modify the existing breakpoints at lines 138, 983, or 990\n- Do not change any desktop styles\n- Do not modify any JS files","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T23:33:49.225736+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T23:40:14.781069+13:00","closed_at":"2026-02-16T23:40:14.781069+13:00","close_reason":"ac33796 Already implemented in docs-z6h.2 commit - tablet breakpoint exists and test passes","labels":["scope:small"],"dependencies":[{"issue_id":"docs-z6h.3","depends_on_id":"docs-z6h","type":"parent-child","created_at":"2026-02-16T23:33:49.227103+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-z6h.4","title":"Reduce header height on mobile and optimize breadcrumbs","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nThe header is 64px tall on all screen sizes (line 111: `--header-height: 64px`). On mobile, this takes significant vertical space. Breadcrumbs can also wrap to multiple lines on narrow screens.\n\nAdd these rules inside the existing `@media (max-width: 768px)` block (line 990-1034), before the closing `}`:\n\n```css\n/* Reduce header height on mobile */\n:root {\n --header-height: 56px;\n}\n\n.layout-logo-img {\n height: 24px;\n}\n\n/* Compact breadcrumbs on mobile */\n.breadcrumbs {\n margin-bottom: var(--space-3);\n}\n\n.breadcrumbs-list {\n flex-wrap: nowrap;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.breadcrumbs-item {\n white-space: nowrap;\n}\n```\n\nContext:\n- Header height is set via CSS variable `--header-height` (line 111), used by the header (line 186), sidebar top offset (line 997), and overlay top offset (line 1015). Overriding the variable in the mobile block cascades to all of these.\n- Logo image is 28px tall (line 211). Reducing to 24px keeps proportion with the shorter header.\n- Breadcrumbs currently use `flex-wrap: wrap` (line 239). On mobile, long breadcrumb trails should scroll horizontally instead of wrapping.\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\nconst mobileBlock = css.split(\\\"@media (max-width: 768px)\\\").slice(1).join(\\\"\\\");\nconst hasHeader = /--header-height:\\s*5[2-8]px/.test(mobileBlock);\nconst hasBreadcrumbs = /\\\\.breadcrumbs-list[^}]*flex-wrap:\\s*nowrap/.test(mobileBlock);\nif (!hasHeader) { console.error(\\\"FAIL: no mobile header height\\\"); process.exit(1); }\nif (!hasBreadcrumbs) { console.error(\\\"FAIL: no breadcrumb nowrap\\\"); process.exit(1); }\nconsole.log(\\\"PASS\\\");\n\"\n```\n\n## Dont\n- Do not change the desktop header height (line 111)\n- Do not change the desktop breadcrumb styles (lines 232-268)\n- Do not create a new media query — add to the existing one at line 990\n- Do not modify any JS files","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T23:34:02.999673+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T23:40:29.109311+13:00","closed_at":"2026-02-16T23:40:29.109311+13:00","close_reason":"b2ca35e Reduce header height on mobile and optimize breadcrumbs","labels":["scope:small"],"dependencies":[{"issue_id":"docs-z6h.4","depends_on_id":"docs-z6h","type":"parent-child","created_at":"2026-02-16T23:34:03.001195+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"docs-z6h.5","title":"Add small mobile breakpoint (\u003c 480px) and pagination stacking","description":"## Files\n- documentation/styles/globals.css (modify)\n\n## What to do\nAdd a small mobile breakpoint for very narrow screens (iPhone SE at 375px, older Android at 360px). Also stack pagination cards vertically on mobile.\n\nAdd a new media query AFTER the existing mobile block (after line 1034):\n\n```css\n/* Small mobile: extra narrow screens */\n@media (max-width: 480px) {\n .sidebar {\n width: 85vw;\n min-width: 85vw;\n }\n\n .layout-header-inner {\n padding: 0 var(--space-3);\n }\n\n .layout-content {\n padding: var(--space-4) var(--space-3);\n }\n}\n```\n\nAlso add pagination stacking inside the existing `@media (max-width: 768px)` block (before line 1034):\n\n```css\n/* Stack pagination on mobile */\n.pagination {\n flex-direction: column;\n}\n\n.pagination-link {\n width: 100%;\n}\n\n.pagination-link-next {\n text-align: left;\n}\n```\n\nContext:\n- The sidebar is fixed at 250px width (line 108). On a 375px screen, that leaves only 125px visible content — too cramped. Using 85vw gives ~319px sidebar with some visible background.\n- Header inner padding is `0 var(--space-8)` (32px, line 195). On tiny screens, reduce to 12px.\n- Pagination uses `display: flex` with `justify-content: space-between` (lines 527-534). On mobile, prev/next cards should stack vertically.\n\n## Test\n```bash\ncd documentation \u0026\u0026 node -e \"\nconst fs = require(\\\"fs\\\");\nconst css = fs.readFileSync(\\\"styles/globals.css\\\", \\\"utf8\\\");\nconst hasSmallMobile = css.includes(\\\"max-width: 480px\\\");\nconst has85vw = /\\\\.sidebar[^}]*width:\\\\s*85vw/.test(css);\nconst mobileBlock = css.split(\\\"@media (max-width: 768px)\\\").slice(1).join(\\\"\\\");\nconst hasPaginationStack = /\\\\.pagination[^}]*flex-direction:\\\\s*column/.test(mobileBlock);\nif (!hasSmallMobile) { console.error(\\\"FAIL: no 480px breakpoint\\\"); process.exit(1); }\nif (!has85vw) { console.error(\\\"FAIL: no 85vw sidebar\\\"); process.exit(1); }\nif (!hasPaginationStack) { console.error(\\\"FAIL: no pagination stacking\\\"); process.exit(1); }\nconsole.log(\\\"PASS\\\");\n\"\n```\n\n## Dont\n- Do not modify the existing breakpoints\n- Do not change desktop styles\n- Do not modify any JS files\n- Do not change the sidebar width CSS variable — override the width directly on .sidebar","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-16T23:34:17.12088+13:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-16T23:39:44.212351+13:00","closed_at":"2026-02-16T23:39:44.212351+13:00","close_reason":"b2ca35e Add small mobile breakpoint (\u003c480px) and pagination stacking","labels":["scope:small"],"dependencies":[{"issue_id":"docs-z6h.5","depends_on_id":"docs-z6h","type":"parent-child","created_at":"2026-02-16T23:34:17.122179+13:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-5sb","title":"Epic: Fix last-updated date rendering bug — DOM injection → React component","description":"The LastUpdated component uses DOM injection (useEffect + appendChild) which races with Markdoc content rendering, causing the date to appear above the h1 on some pages. Fix: rewrite as a proper React component rendered inside \u003carticle\u003e after {children}. Changes already written on branch fix/last-updated-bug — needs commit, push, and PR. Success: last-updated date always appears at the bottom of the article content, never above the h1.","status":"open","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-03-06T18:09:25.532372+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:09:25.532372+08:00","labels":["scope:trivial"]} -{"id":"hypercerts-atproto-documentation-5sb.1","title":"Commit, push, and PR the last-updated bug fix (branch fix/last-updated-bug)","description":"## Files\n- components/LastUpdated.js (modified — already on branch)\n- components/Layout.js (modified — already on branch)\n\n## What to do\nThe changes are already written on branch fix/last-updated-bug. Just:\n1. Verify npm run build succeeds\n2. git add components/LastUpdated.js components/Layout.js\n3. git commit -m 'fix: rewrite LastUpdated as React component to fix rendering position'\n4. git push -u origin fix/last-updated-bug\n5. gh pr create targeting main\n\nThe changes: LastUpdated.js was rewritten from DOM injection (useEffect + appendChild) to a proper React component that returns \u003cp className='last-updated'\u003e. Layout.js moved \u003cLastUpdated /\u003e from before \u003carticle\u003e to inside \u003carticle\u003e after {children}.\n\n## Don't\n- Modify the changes — they're already correct\n- Touch lib/lastUpdated.json (it has regenerated dates, that's expected)","acceptance_criteria":"1. PR created targeting main. 2. npm run build succeeds. 3. LastUpdated.js does not import useEffect. 4. Layout.js renders \u003cLastUpdated /\u003e inside \u003carticle\u003e after {children}.","status":"open","priority":1,"issue_type":"task","owner":"sharfy-test.climateai.org","estimated_minutes":10,"created_at":"2026-03-06T18:09:35.388648+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:09:35.388648+08:00","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-5sb.1","depends_on_id":"hypercerts-atproto-documentation-5sb","type":"parent-child","created_at":"2026-03-06T18:09:35.389834+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-7fu","title":"Epic: Full-text search — index page content and search by keyword","description":"## Problem\nThe search bar (Cmd+K) only matches against page titles from the hardcoded navigation tree. It cannot find keywords in page body content, headings, or descriptions. For example, searching 'OAuth' returns nothing because no page is titled 'OAuth'.\n\n## Goals\n1. Build a search index at build time that includes page titles, descriptions, headings (h2/h3), and body text from all .md files\n2. Use a lightweight client-side search library (FlexSearch) for fast full-text keyword search\n3. Show search results with matched context snippets so users can see where the keyword appears\n4. Keep the existing UX (Cmd+K modal, keyboard navigation, quick links) intact\n\n## Key files\n- components/SearchDialog.js — current search UI with fuzzy title matching\n- lib/navigation.js — current data source (title + path only)\n- styles/globals.css — search dialog styles (lines 1537-1665)\n- pages/**/*.md — content to be indexed\n\n## Architecture\n- Build script generates public/search-index.json from all .md files\n- SearchDialog loads index on first open, initializes FlexSearch\n- Results show title, section, and a snippet of matched body text\n- Field boosting: title matches rank higher than body matches\n\n## Scope\nSearch dialog replacement + build script. No changes to page rendering or navigation.","status":"open","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-03-09T16:50:30.512727+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-09T16:50:30.512727+08:00","labels":["scope:medium"]} -{"id":"hypercerts-atproto-documentation-7fu.1","title":"Build-time search index generator — extract titles, headings, descriptions, and body text from all .md files","description":"## Files\n- lib/generate-search-index.js (create)\n- package.json (modify — add to build/dev scripts)\n\n## What to do\nCreate a Node.js build script that reads all .md files under pages/, extracts searchable content, and writes a JSON index to public/search-index.json.\n\n### Script behavior:\n1. Walk pages/ recursively, find all .md files (same pattern as lib/generate-last-updated.js)\n2. For each file, extract:\n - `path`: route path (e.g. \"/getting-started/quickstart\") — same logic as generate-last-updated.js\n - `title`: from YAML frontmatter `title` field (between `---` delimiters at top of file)\n - `description`: from YAML frontmatter `description` field\n - `headings`: array of h2/h3 text (lines starting with `## ` or `### `)\n - `body`: full plain text of the markdown with frontmatter, code blocks, Markdoc tags (`{% ... %}`), markdown syntax (`#`, `*`, `[`, etc.), and HTML tags stripped out. Collapse multiple whitespace/newlines into single spaces. Trim to max 5000 chars per page.\n3. Look up the section for each path from the navigation tree. Import `flattenNavigation` from lib/navigation.js — but since this is a CommonJS script and navigation.js uses ESM exports, instead parse the navigation structure manually or use a simple path-to-section mapping. The simplest approach: hardcode a function that maps path prefixes to sections:\n - /getting-started/* → 'Get Started'\n - /core-concepts/* → 'Core Concepts'\n - /tools/* → 'Tools'\n - /architecture/* → 'Architecture'\n - /lexicons/* → 'Reference'\n - /reference/* → 'Reference'\n - /ecosystem/* → 'Ecosystem \u0026 Vision'\n - /roadmap → 'Reference'\n - / → 'Get Started'\n4. Write output as JSON array to public/search-index.json:\n```json\n[\n {\n \"path\": \"/getting-started/quickstart\",\n \"title\": \"Quickstart\",\n \"description\": \"Create your first hypercert...\",\n \"section\": \"Get Started\",\n \"headings\": [\"Install dependencies\", \"Authenticate\", ...],\n \"body\": \"This guide walks through creating...\"\n },\n ...\n]\n```\n\n### Frontmatter parsing:\nSimple regex — no YAML library needed:\n```js\nconst fmMatch = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n// then extract title: and description: lines\n```\n\n### Markdoc/markdown stripping:\nRemove these patterns from body text:\n- Frontmatter block (`---...---`)\n- Code blocks (`\\`\\`\\`....\\`\\`\\``)\n- Markdoc tags (`{% callout ... %}`, `{% /callout %}`, `{% table %}`, etc.)\n- Heading markers (`# `, `## `, `### `)\n- Markdown links: `[text](url)` → `text`\n- Markdown bold/italic: `**text**` → `text`, `*text*` → `text`\n- Inline code backticks\n- HTML tags\n- Collapse whitespace\n\n### package.json changes:\nUpdate both scripts to run the index generator before next:\n```json\n\"dev\": \"node lib/generate-search-index.js \u0026\u0026 node lib/generate-last-updated.js \u0026\u0026 next dev --webpack\",\n\"build\": \"node lib/generate-search-index.js \u0026\u0026 node lib/generate-last-updated.js \u0026\u0026 next build --webpack\"\n```\n\n## Don't\n- Use any npm dependencies — this is pure Node.js (fs, path, regex)\n- Include files outside pages/ (no AGENTS.md, no .beads/, no README)\n- Include the pages/index.md home page body (it's mostly card markup) — include title only\n- Modify any existing files except package.json","acceptance_criteria":"1. Running `node lib/generate-search-index.js` creates public/search-index.json\n2. The JSON file is a valid JSON array with one entry per .md page (~43 entries)\n3. Each entry has path, title, section, headings (array), body (string), and description (string or empty)\n4. Body text does not contain markdown syntax, Markdoc tags, code blocks, or frontmatter\n5. Body text is max 5000 chars per page\n6. `pnpm build` succeeds and includes the search index generation\n7. The search index file is under 500KB total","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":45,"created_at":"2026-03-09T16:51:18.334027+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-09T17:05:01.575079+08:00","closed_at":"2026-03-09T17:05:01.575079+08:00","close_reason":"6417d68 Build-time search index generator implemented - extracts titles, headings, descriptions, and body text from all .md files","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-7fu.1","depends_on_id":"hypercerts-atproto-documentation-7fu","type":"parent-child","created_at":"2026-03-09T16:51:18.336538+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-7fu.2","title":"Replace SearchDialog with FlexSearch-powered full-text search","description":"## Files\n- components/SearchDialog.js (modify — major rewrite)\n- package.json (modify — add flexsearch dependency)\n\n## What to do\nReplace the current title-only fuzzy search with FlexSearch-powered full-text search that queries the search index generated by task 7fu.1.\n\n### 1. Add FlexSearch dependency\n```bash\npnpm add flexsearch\n```\n\n### 2. Rewrite SearchDialog.js\n\n#### Index loading:\n- On first dialog open, fetch `/search-index.json` (it's in public/)\n- Store the raw data in a module-level variable (not state) so it persists across opens\n- Create a FlexSearch Document index with these fields:\n```js\nimport { Document } from 'flexsearch';\n\nconst index = new Document({\n document: {\n id: 'path',\n index: ['title', 'description', 'headings', 'body'],\n },\n tokenize: 'forward',\n});\n```\n- Add all entries from the JSON to the index\n- Show a loading state ('Loading...') while fetching on first open\n\n#### Search:\n- On query change (debounce 150ms), search the index:\n```js\nconst results = index.search(query, { limit: 20, enrich: true });\n```\n- FlexSearch Document returns results grouped by field. Merge and deduplicate by path, prioritizing: title matches first, then description, then headings, then body.\n- For each result, look up the full entry from the raw data to get title, path, section, description, body.\n\n#### Snippet generation:\n- For body matches, generate a context snippet: find the first occurrence of the query in the body text, extract ~120 chars around it, and wrap the matched term in a `\u003cmark\u003e` tag.\n- Helper function:\n```js\nfunction getSnippet(body, query, contextChars = 60) {\n const lower = body.toLowerCase();\n const idx = lower.indexOf(query.toLowerCase());\n if (idx === -1) return '';\n const start = Math.max(0, idx - contextChars);\n const end = Math.min(body.length, idx + query.length + contextChars);\n let snippet = '';\n if (start \u003e 0) snippet += '...';\n snippet += body.slice(start, end);\n if (end \u003c body.length) snippet += '...';\n return snippet;\n}\n```\n\n#### Result display:\nEach result item should show:\n- `.search-result-title` — page title (existing class)\n- `.search-result-snippet` — the context snippet with matched term highlighted (NEW class, see CSS task)\n- `.search-result-path` — the URL path (existing class)\n\nKeep the existing structure:\n- Empty state: Quick Links (unchanged)\n- Results: grouped by section (unchanged grouping logic)\n- No results: 'No results' message + Quick Links (unchanged)\n- Keyboard navigation: Arrow Up/Down, Enter, Escape (unchanged)\n\n#### Keep from current implementation:\n- `QUICK_LINK_PATHS` array and quick links logic\n- Keyboard navigation (arrow keys, enter, escape)\n- `useRouter` for navigation\n- Focus management (focus input on open)\n- Overlay click-to-close\n- All existing class names for styling compatibility\n\n### 3. Remove old code:\n- Remove the `fuzzyMatch` function\n- Remove the `flattenNavigation` import (no longer needed)\n\n## Don't\n- Change the search dialog's visual layout or structure — only the data source and matching logic\n- Remove keyboard navigation\n- Remove quick links\n- Change any CSS class names (add new ones, don't rename existing)\n- Import from lib/navigation.js — the search index JSON replaces it for search","acceptance_criteria":"1. Searching 'OAuth' returns results (pages that mention OAuth in their body/headings)\n2. Searching 'createRecord' returns results (appears in code examples in body text)\n3. Searching 'PDS' returns multiple results with context snippets showing where PDS appears\n4. Title matches appear before body-only matches in results\n5. Results are grouped by section (Get Started, Core Concepts, etc.)\n6. Keyboard navigation (arrow keys, enter, escape) still works\n7. Quick Links still appear when search is empty\n8. 'No results' message appears for nonsense queries\n9. pnpm build succeeds\n10. No console errors when opening/using search","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":60,"created_at":"2026-03-09T16:51:46.000565+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-09T17:09:51.234016+08:00","closed_at":"2026-03-09T17:09:51.234016+08:00","close_reason":"7cb8d0e Replace SearchDialog with FlexSearch-powered full-text search","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-7fu.2","depends_on_id":"hypercerts-atproto-documentation-7fu","type":"parent-child","created_at":"2026-03-09T16:51:46.001904+08:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"hypercerts-atproto-documentation-7fu.2","depends_on_id":"hypercerts-atproto-documentation-7fu.1","type":"blocks","created_at":"2026-03-09T16:51:46.003258+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-7fu.3","title":"Add CSS for search result snippets and mark highlighting","description":"## Files\n- styles/globals.css (modify — add rules after line ~1665, the end of the SearchDialog section)\n\n## What to do\nAdd CSS rules for the new search result snippet element and the `\u003cmark\u003e` highlight used for matched terms.\n\n### Add these rules:\n```css\n.search-result-snippet {\n font-size: 12px;\n color: var(--color-text-secondary);\n line-height: 1.5;\n margin-top: 2px;\n overflow: hidden;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search-result-snippet mark {\n background: oklch(0.85 0.15 85);\n color: inherit;\n border-radius: 2px;\n padding: 0 2px;\n}\n\nhtml.dark .search-result-snippet mark {\n background: oklch(0.45 0.12 85);\n color: var(--color-text-primary);\n}\n```\n\n### Context:\n- `.search-result-snippet` sits between `.search-result-title` and `.search-result-path` in each result item\n- The `\u003cmark\u003e` tag wraps the matched search term within the snippet\n- The snippet is clamped to 2 lines to keep results compact\n- Light mode uses a warm yellow highlight; dark mode uses a muted amber\n\n## Don't\n- Modify any existing CSS rules\n- Change existing class names\n- Add rules outside the SearchDialog CSS section","acceptance_criteria":"1. .search-result-snippet rule exists in globals.css\n2. .search-result-snippet mark rule exists with background highlight color\n3. html.dark .search-result-snippet mark rule exists for dark mode\n4. Snippet text is clamped to 2 lines via -webkit-line-clamp\n5. pnpm build succeeds","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":15,"created_at":"2026-03-09T16:51:58.988935+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-09T17:11:30.040681+08:00","closed_at":"2026-03-09T17:11:30.040681+08:00","close_reason":"9f49190 Add CSS for search result snippets and mark highlighting","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-7fu.3","depends_on_id":"hypercerts-atproto-documentation-7fu","type":"parent-child","created_at":"2026-03-09T16:51:58.990183+08:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"hypercerts-atproto-documentation-7fu.3","depends_on_id":"hypercerts-atproto-documentation-7fu.2","type":"blocks","created_at":"2026-03-09T16:51:58.991534+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-buj","title":"Epic: Fix remaining factual errors — old NSIDs, PDS validation claim, certified.ink URL","description":"17 instances of old NSIDs (org.hypercerts.claim.attachment/evaluation/measurement should be org.hypercerts.context.*) across 5 pages, plus 1 incorrect PDS validation claim in data-flow-and-lifecycle.md line 38, plus 1 certified.ink → certified.app in cel-work-scopes.md line 196. Success: zero instances of old context-namespace NSIDs in non-lexicon pages, no claims that PDS validates against schemas, certified.app used everywhere.","status":"open","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-03-06T18:07:54.799315+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:07:54.799315+08:00","labels":["scope:medium"]} -{"id":"hypercerts-atproto-documentation-buj.1","title":"Fix old NSIDs in data-flow-and-lifecycle.md (3 instances) and PDS validation error","description":"## Files\n- pages/architecture/data-flow-and-lifecycle.md (modify)\n\n## What to do\nReplace 3 old NSIDs with correct context-namespace NSIDs:\n- Line 63: `org.hypercerts.claim.attachment` → `org.hypercerts.context.attachment`\n- Line 67: `org.hypercerts.claim.measurement` → `org.hypercerts.context.measurement`\n- Line 101: `org.hypercerts.claim.evaluation` → `org.hypercerts.context.evaluation`\n\nFix PDS validation error:\n- Line 38: Change 'The PDS validates the record against the lexicon schema.' to 'The PDS stores the record in the contributor'\\''s repository.' (ATProto PDS instances are schema-agnostic — they do NOT validate records against lexicon schemas. Validation happens at the indexer/app view layer.)\n\n## Don't\n- Change any other content on this page\n- Add explanations about why PDS doesn't validate — just fix the sentence\n- Touch the ASCII diagrams","acceptance_criteria":"1. Zero instances of org.hypercerts.claim.attachment, org.hypercerts.claim.measurement, or org.hypercerts.claim.evaluation in the file. 2. Line 38 no longer claims PDS validates against lexicon schemas. 3. The three correct NSIDs (org.hypercerts.context.attachment, org.hypercerts.context.measurement, org.hypercerts.context.evaluation) appear in the file. 4. npm run build succeeds.","status":"open","priority":1,"issue_type":"task","owner":"sharfy-test.climateai.org","estimated_minutes":15,"created_at":"2026-03-06T18:08:07.273568+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:08:07.273568+08:00","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-buj.1","depends_on_id":"hypercerts-atproto-documentation-buj","type":"parent-child","created_at":"2026-03-06T18:08:07.274562+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-buj.2","title":"Fix old NSIDs in hypercerts-core-data-model.md (3 instances)","description":"## Files\n- pages/core-concepts/hypercerts-core-data-model.md (modify)\n\n## What to do\nReplace 3 old NSIDs in the table at lines 52-54:\n- Line 52: `org.hypercerts.claim.attachment` → `org.hypercerts.context.attachment`\n- Line 53: `org.hypercerts.claim.measurement` → `org.hypercerts.context.measurement`\n- Line 54: `org.hypercerts.claim.evaluation` → `org.hypercerts.context.evaluation`\n\nThese are in the 'Records that attach to a hypercert' table, in the Lexicon column.\n\n## Don't\n- Change any other content on this page\n- Modify the table structure or other columns\n- Touch the contributor model section (it was recently rewritten and is correct)","acceptance_criteria":"1. Zero instances of org.hypercerts.claim.attachment, org.hypercerts.claim.measurement, or org.hypercerts.claim.evaluation in the file. 2. The three correct NSIDs (org.hypercerts.context.attachment, org.hypercerts.context.measurement, org.hypercerts.context.evaluation) appear in the Lexicon column. 3. npm run build succeeds.","status":"open","priority":1,"issue_type":"task","owner":"sharfy-test.climateai.org","estimated_minutes":10,"created_at":"2026-03-06T18:08:15.674869+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:08:15.674869+08:00","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-buj.2","depends_on_id":"hypercerts-atproto-documentation-buj","type":"parent-child","created_at":"2026-03-06T18:08:15.677058+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-buj.3","title":"Fix old NSIDs in working-with-evaluations.md (4 instances)","description":"## Files\n- pages/getting-started/working-with-evaluations.md (modify)\n\n## What to do\nReplace 4 old NSIDs in code examples:\n- Line 24: `collection: \"org.hypercerts.claim.evaluation\"` → `collection: \"org.hypercerts.context.evaluation\"`\n- Line 32: `\\$type: \"org.hypercerts.claim.evaluation\"` → `\\$type: \"org.hypercerts.context.evaluation\"`\n- Line 49: `collection: \"org.hypercerts.claim.measurement\"` → `collection: \"org.hypercerts.context.measurement\"`\n- Line 63: `\\$type: \"org.hypercerts.claim.measurement\"` → `\\$type: \"org.hypercerts.context.measurement\"`\n\nThese are inside JSON code blocks showing createRecord API calls.\n\n## Don't\n- Change any prose or explanatory text\n- Modify the code block structure\n- Change any other fields in the JSON examples","acceptance_criteria":"1. Zero instances of org.hypercerts.claim.evaluation or org.hypercerts.claim.measurement in the file. 2. The correct NSIDs org.hypercerts.context.evaluation and org.hypercerts.context.measurement appear in the code examples. 3. npm run build succeeds.","status":"open","priority":1,"issue_type":"task","owner":"sharfy-test.climateai.org","estimated_minutes":10,"created_at":"2026-03-06T18:08:20.585413+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:08:20.585413+08:00","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-buj.3","depends_on_id":"hypercerts-atproto-documentation-buj","type":"parent-child","created_at":"2026-03-06T18:08:20.587488+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-buj.4","title":"Fix old NSIDs in roadmap.md (4 instances)","description":"## Files\n- pages/roadmap.md (modify)\n\n## What to do\nReplace 4 old NSIDs:\n- Line 67: `org.hypercerts.claim.evaluation` → `org.hypercerts.context.evaluation`\n- Line 68: `org.hypercerts.claim.measurement` → `org.hypercerts.context.measurement`\n- Line 70: `org.hypercerts.claim.attachment` → `org.hypercerts.context.attachment`\n- Line 133: `org.hypercerts.claim.evaluation` → `org.hypercerts.context.evaluation`\n\n## Don't\n- Change any other content on this page\n- Modify the roadmap structure or timeline items","acceptance_criteria":"1. Zero instances of org.hypercerts.claim.evaluation, org.hypercerts.claim.measurement, or org.hypercerts.claim.attachment in the file. 2. The correct org.hypercerts.context.* NSIDs appear in their place. 3. npm run build succeeds.","status":"open","priority":1,"issue_type":"task","owner":"sharfy-test.climateai.org","estimated_minutes":10,"created_at":"2026-03-06T18:08:24.201423+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:08:24.201423+08:00","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-buj.4","depends_on_id":"hypercerts-atproto-documentation-buj","type":"parent-child","created_at":"2026-03-06T18:08:24.202924+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-buj.5","title":"Fix old NSIDs in scaffold.md (3 instances)","description":"## Files\n- pages/tools/scaffold.md (modify)\n\n## What to do\nReplace 3 old NSIDs in the Constellation Backlinks section (lines 183-185):\n- Line 183: `org.hypercerts.claim.attachment:subjects` → `org.hypercerts.context.attachment:subjects`\n- Line 184: `org.hypercerts.claim.evaluation:subject.uri` → `org.hypercerts.context.evaluation:subject.uri`\n- Line 185: `org.hypercerts.claim.measurement:subject.uri` → `org.hypercerts.context.measurement:subject.uri`\n\nThese are in a bullet list showing Constellation query source paths.\n\n## Don't\n- Change any other content on this page\n- Modify the Constellation explanation or query pattern description","acceptance_criteria":"1. Zero instances of org.hypercerts.claim.attachment, org.hypercerts.claim.evaluation, or org.hypercerts.claim.measurement in the file. 2. The correct org.hypercerts.context.* NSIDs appear in the Constellation Backlinks section. 3. npm run build succeeds.","status":"open","priority":1,"issue_type":"task","owner":"sharfy-test.climateai.org","estimated_minutes":10,"created_at":"2026-03-06T18:08:27.890918+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:08:27.890918+08:00","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-buj.5","depends_on_id":"hypercerts-atproto-documentation-buj","type":"parent-child","created_at":"2026-03-06T18:08:27.892863+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-buj.6","title":"Fix certified.ink → certified.app in cel-work-scopes.md","description":"## Files\n- pages/core-concepts/cel-work-scopes.md (modify)\n\n## What to do\n- Line 196: Change `[Certified](https://certified.ink)` to `[Certified](https://certified.app)`\n\n## Don't\n- Change any other content on this page","acceptance_criteria":"1. Zero instances of certified.ink in the file. 2. The link reads [Certified](https://certified.app). 3. npm run build succeeds.","status":"open","priority":2,"issue_type":"task","owner":"sharfy-test.climateai.org","estimated_minutes":5,"created_at":"2026-03-06T18:08:29.843637+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:08:29.843637+08:00","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-buj.6","depends_on_id":"hypercerts-atproto-documentation-buj","type":"parent-child","created_at":"2026-03-06T18:08:29.846267+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-g74","title":"Epic: Trim data-flow-and-lifecycle.md — remove repetition and tighten","description":"data-flow-and-lifecycle.md is 229 lines with ~50-70 lines of repetition. Key issues: (1) Cross-PDS References section (lines 190-209) duplicates the cross-server explanation already in Stage 2 (lines 85-95). (2) 'What This Flow Enables' section (lines 211-221) restates concepts already covered in the stage descriptions. (3) The lifecycle overview (lines 10-32) and the detailed stages overlap significantly. Goal: cut to ~160-170 lines by removing the standalone Cross-PDS References section (fold any unique content into Stage 2), removing or heavily trimming 'What This Flow Enables', and tightening the lifecycle overview. Keep all 6 stages, all ASCII diagrams, and all correct technical content.","status":"open","priority":2,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-03-06T18:08:39.578011+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:08:39.578011+08:00","labels":["scope:small"]} -{"id":"hypercerts-atproto-documentation-g74.1","title":"Trim data-flow-and-lifecycle.md — remove duplicate Cross-PDS section and tighten","description":"## Files\n- pages/architecture/data-flow-and-lifecycle.md (modify)\n\n## What to do\n1. **Delete the standalone 'Cross-PDS References' section** (lines 190-209). The cross-server explanation in Stage 2 (lines 85-95) already covers this. If the standalone section has any unique content (CID verification details at lines 205-207), fold those 1-2 sentences into the Stage 2 cross-server paragraph.\n\n2. **Delete or heavily trim 'What This Flow Enables'** (lines 211-221). These 4 bullet points restate what the stages already explain. Either delete entirely or reduce to a single sentence like 'This lifecycle supports retroactive funding, cross-platform collaboration, independent evaluation, and evolving reputation — because each stage is decoupled and records accumulate over time.'\n\n3. **Tighten the lifecycle overview** (lines 10-32). The overview and Stage 1-6 sections overlap. Reduce the overview to ~8 lines max — just the stage names and one-line descriptions, not full paragraphs.\n\n4. Keep all 6 stage sections, all ASCII diagrams, and all Next Steps links.\n\n## Don't\n- Change any NSIDs (those are being fixed in a separate task)\n- Remove or modify any of the 6 stage sections themselves\n- Remove ASCII art diagrams\n- Change the page title or frontmatter\n- Add new content","acceptance_criteria":"1. The standalone 'Cross-PDS References' section (## Cross-PDS References) no longer exists as a separate section. 2. The 'What This Flow Enables' section is either deleted or reduced to ≤3 lines. 3. The lifecycle overview section is ≤10 lines of prose (not counting the ASCII diagram). 4. All 6 stages (Creation, Enrichment, Evaluation, Discovery \u0026 Indexing, Funding \u0026 Ownership, Accumulation) still exist. 5. Total file length is ≤180 lines. 6. npm run build succeeds.","status":"open","priority":2,"issue_type":"task","owner":"sharfy-test.climateai.org","estimated_minutes":30,"created_at":"2026-03-06T18:08:54.698366+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:08:54.698366+08:00","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-g74.1","depends_on_id":"hypercerts-atproto-documentation-g74","type":"parent-child","created_at":"2026-03-06T18:08:54.699403+08:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"hypercerts-atproto-documentation-g74.1","depends_on_id":"hypercerts-atproto-documentation-buj.1","type":"blocks","created_at":"2026-03-06T18:08:54.700848+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-gde","title":"Epic: Add contributor weight calculation guidance to docs","description":"## Why\nThe protocol intentionally leaves contributionWeight as a free-form relative string. The docs currently say 'optional relative weight string' but give no guidance on HOW to choose values. Users need practical methods.\n\n## What success looks like\n- The contribution lexicon page includes a new section explaining weight calculation approaches (equal split, role-based, activity-based, peer assessment, outcome-based, composite)\n- The hyperboards page explains how weights translate to visual tile sizes\n- Both sections are consistent with existing docs (weights are strings, don't need to sum to 100, protocol is weight-agnostic)\n\n## Key constraints\n- This is guidance/recommendations, NOT protocol-level requirements\n- Must be clear that the protocol does not enforce any particular method\n- Must reference prior art (Coordinape, SourceCred, Drips) where relevant\n- Must include concrete examples with sample weight values\n- Tone and style must match existing documentation","status":"open","priority":2,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-03-09T15:17:44.729159+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-09T15:17:44.729159+08:00","labels":["scope:medium"]} -{"id":"hypercerts-atproto-documentation-gde.1","title":"Add weight calculation methods section to contribution.md","description":"## Files\n- pages/lexicons/hypercerts-lexicons/contribution.md (modify)\n\n## What to do\nAdd a new section titled \"## Choosing contribution weights\" after line 25 (after \"The activity claim's `contributors` array also supports contribution weights to indicate relative effort or impact.\") and before the final \"For the full schema\" paragraph.\n\nThe section should contain:\n\n### Opening paragraph\nState that the protocol is intentionally weight-agnostic — it stores relative values as strings and does not enforce any calculation method. Applications and project leads choose how to assign weights. Then present the following methods.\n\n### Method 1: Equal split\n- Every contributor gets weight `\"1\"`\n- Best for: collaborative work where contributions are hard to separate, small teams\n- Example: 4 contributors each with `contributionWeight: \"1\"`\n\n### Method 2: Role-based multipliers\n- Assign a base multiplier per role\n- Table: Lead/Creator = 3, Core contributor = 2, Reviewer/Advisor = 1, Minor contributor = 0.5\n- Best for: teams with clear role hierarchies\n- Example: Lead author `\"3\"`, two core contributors `\"2\"` each, one reviewer `\"1\"`\n\n### Method 3: Activity-based (git signals)\n- Derive weights from repository data: commits, lines changed, PRs merged, issues closed\n- Best for: open-source projects with public commit history\n- Caveat: biased toward code-heavy contributions; does not capture design, coordination, or review work well\n- Example: Alice (450 commits) `\"45\"`, Bob (350 commits) `\"35\"`, Carol (200 commits) `\"20\"`\n\n### Method 4: Peer assessment\n- Each contributor receives a fixed budget of points (e.g. 100) to distribute among peers (not themselves)\n- Final weight = total points received\n- Best for: teams that value qualitative judgment and want to reduce self-reporting bias\n- Reference: similar to Coordinape GIVE circles\n- Caveat: requires active participation from all contributors\n- Example: After a round, Alice receives 140 points → `\"140\"`, Bob receives 90 → `\"90\"`, Carol receives 70 → `\"70\"`\n\n### Method 5: Outcome-based\n- Weight by measurable deliverables: features shipped, milestones hit, KPIs moved\n- Best for: impact-focused projects with quantifiable outputs\n- Caveat: hardest to quantify fairly; risk of rewarding easily-measured work over important-but-hard-to-measure work\n- Example: Alice delivered 3 milestones → `\"3\"`, Bob delivered 1 → `\"1\"`\n\n### Method 6: Composite (recommended starting point)\n- Combine multiple signals with configurable coefficients\n- Formula: `finalWeight = (α × roleWeight) + (β × activityScore) + (γ × peerScore)` where α + β + γ = 1\n- Best for: projects that want a balanced view\n- Include a concrete worked example with 3 contributors showing the math\n- Reference: SourceCred, Drips as prior art for composite scoring\n\n### Closing callout\nUse a Markdoc callout ({% callout type=\"note\" %}) stating: \"Remember: weights are stored as strings and do not need to sum to any particular value. The methods above produce relative values — applications like Hyperboards normalize them for display.\"\n\n## Style requirements\n- Use the same Markdoc formatting as the rest of the docs (## headings, tables, code examples with backtick strings)\n- Keep each method to ~4-8 lines of prose max — this is a reference, not an essay\n- Use concrete contributionWeight string examples (e.g. `\"70\"`) throughout\n\n## Don't\n- Add any new frontmatter fields\n- Modify the existing content above or below the new section (lines 1-25 and the final \"For the full schema\" paragraph must stay exactly as they are)\n- Present any method as the \"official\" or \"required\" approach\n- Add links to external sites other than Coordinape, SourceCred, and Drips","acceptance_criteria":"1. contribution.md contains a new \"## Choosing contribution weights\" section between the existing \"contribution weights to indicate relative effort or impact\" paragraph and the \"For the full schema\" paragraph\n2. All 6 methods (equal split, role-based, activity-based, peer assessment, outcome-based, composite) are present with examples\n3. Each method includes at least one concrete contributionWeight string example\n4. A Markdoc callout exists reminding readers that weights are relative strings\n5. The composite method includes a worked example with actual numbers for 3 contributors\n6. Prior art references (Coordinape, SourceCred, Drips) are mentioned\n7. No existing content is modified — only new content is inserted\n8. The page renders correctly with `pnpm dev` (no Markdoc errors)","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":45,"created_at":"2026-03-09T15:18:18.236859+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-09T15:46:33.420087+08:00","closed_at":"2026-03-09T15:46:33.420087+08:00","close_reason":"83d7769 Add weight calculation methods section to contribution.md","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-gde.1","depends_on_id":"hypercerts-atproto-documentation-gde","type":"parent-child","created_at":"2026-03-09T15:18:18.23837+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-gde.2","title":"Add weight visualization guidance to hyperboards.md","description":"## Files\n- pages/tools/hyperboards.md (modify)\n\n## What to do\nAdd a new section titled \"## How weights become tile sizes\" after the \"## How it works\" section (after line 28, after the paragraph about cryptographic signing) and before the \"## Embedding\" section.\n\nThe section should contain:\n\n### Explanation of normalization\nExplain that Hyperboards normalizes contributor weights into proportional tile areas using this formula:\n\n`tileArea = contributorWeight / sumOfAllWeights`\n\nFor example, if three contributors have weights `\"70\"`, `\"20\"`, and `\"10\"`:\n- Alice: 70 / 100 = 70% of the board area\n- Bob: 20 / 100 = 20% of the board area\n- Carol: 10 / 100 = 10% of the board area\n\nD3 recomputes tile positions from the weights on every render.\n\n### What happens with missing or invalid weights\nExplain that contributors without a `contributionWeight` default to a weight of 1. Specifically, there are two fallback layers:\n1. If `contributionWeight` is undefined or null, the string `\"1\"` is used\n2. If the string cannot be parsed as a number (e.g. empty string), the number `1` is used\n\nContributors are never excluded — they always appear on the board with at least a weight of 1.\n\n### Drag-to-resize behavior\nExplain that when a user drags to resize tiles in the editor, this directly updates the `contributionWeight` stored in the contributor's ATProto activity record on their PDS. There is no separate layout layer — the weight IS the layout. Specifically, the new weight is written back as `contributionWeight: String(Math.round(value * 10) / 10)`, so weights are rounded to one decimal place on save.\n\n### Choosing weights for visual clarity\nAdd a brief practical tip: for boards with many contributors, using percentage-style weights (summing to 100) makes the visual proportions intuitive. For boards with few contributors, simple multipliers (like `\"3\"`, `\"2\"`, `\"1\"`) work well.\n\n### Cross-reference\nAdd a sentence linking to the contribution lexicon page: \"For methods to calculate contributor weights, see [Choosing contribution weights](/lexicons/hypercerts-lexicons/contribution#choosing-contribution-weights).\"\n\n## Style requirements\n- Match the existing tone: practical, concise, second-person (\"you\")\n- Use a code block or table for the normalization example\n- Keep the entire section to ~15-25 lines of markdown\n\n## Don't\n- Modify any existing content in the file\n- Add information about Hyperboards features that are not already mentioned on the page\n- Reference specific source code lines or internal function names (adjustEntriesForNewPercentage, handleSave, etc.) — describe behavior, not implementation\n- Add new frontmatter fields","acceptance_criteria":"1. hyperboards.md contains a new \"## How weights become tile sizes\" section between \"## How it works\" and \"## Embedding\"\n2. The normalization formula (contributorWeight / sumOfAllWeights) is explained with a concrete 3-contributor example\n3. Missing/invalid weight behavior is documented: defaults to 1 via two fallback layers (null/undefined → \"1\", unparseable → 1), contributors are never excluded\n4. Drag-to-resize is documented as directly updating the contributionWeight on the PDS (no separate layout layer), with weights rounded to one decimal place on save\n5. A practical tip about choosing weight scales is included\n6. A cross-reference link to the contribution lexicon weight section exists\n7. No internal function names or source code references appear in the text\n8. No existing content is modified\n9. The page renders correctly with pnpm dev (no Markdoc errors)","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":30,"created_at":"2026-03-09T15:18:39.534616+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-09T15:48:19.024581+08:00","closed_at":"2026-03-09T15:48:19.024581+08:00","close_reason":"3098b94 Add weight visualization guidance to hyperboards.md","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-gde.2","depends_on_id":"hypercerts-atproto-documentation-gde","type":"parent-child","created_at":"2026-03-09T15:18:39.535424+08:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"hypercerts-atproto-documentation-gde.2","depends_on_id":"hypercerts-atproto-documentation-gde.1","type":"blocks","created_at":"2026-03-09T15:18:43.176619+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-hl0","title":"Fix: inline contributor fields described as bare strings, not objects (from hypercerts-atproto-documentation-w96.7)","description":"Review of hypercerts-atproto-documentation-w96.7 found: The 'Additional details' section describes the inline options for `contributorIdentity` and `contributionDetails` as bare strings, but the actual lexicon defines them as objects.\n\n**Evidence from lexicon (activity.json):**\n- `#contributorIdentity` is `type: object` with a required `identity` string field — NOT a bare string\n- `#contributorRole` is `type: object` with a required `role` string field — NOT a bare string\n\n**Inaccurate text in pages/core-concepts/hypercerts-core-data-model.md (line 29):**\n\u003e 'either an inline identity string (a DID)'\n\n**Inaccurate text (line 31):**\n\u003e 'either an inline role string'\n\n**Inaccurate text (line 33):**\n\u003e 'Simple cases use inline strings directly in the activity claim.'\n\n**Inaccurate text in tree (line 79):**\n\u003e 'contributorIdentity: Alice (inline DID or ref to ContributorInformation)'\n\nThe inline option is an inline OBJECT (e.g. `{'$type': 'org.hypercerts.claim.activity#contributorIdentity', identity: 'did:plc:...'}`), not a bare DID string. Describing it as a 'string' misrepresents the schema and will confuse developers implementing the spec.\n\n**Fix:** Change 'inline identity string (a DID)' to 'inline identity object (`#contributorIdentity`)' and 'inline role string' to 'inline role object (`#contributorRole`)'. Update line 33 and the tree comment accordingly.","status":"closed","priority":2,"issue_type":"bug","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","created_at":"2026-03-05T20:09:03.279465073+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:15:12.137296968+06:00","closed_at":"2026-03-05T20:15:12.137296968+06:00","close_reason":"ebedf20 Fix: inline contributor fields are objects, not bare strings","dependencies":[{"issue_id":"hypercerts-atproto-documentation-hl0","depends_on_id":"hypercerts-atproto-documentation-w96.7","type":"discovered-from","created_at":"2026-03-05T20:09:06.605350604+06:00","created_by":"karma.gainforest.id"}]} -{"id":"hypercerts-atproto-documentation-j5j","title":"Epic: Trim why-we-need-hypercerts.md — condense economics essay","description":"pages/ecosystem/why-we-need-hypercerts.md is 180 lines. It reads more like a white paper than product documentation. Key verbose sections: (1) 'Why This Happens: We Confuse Price with Value' (lines 23-44, ~22 lines of economics theory), (2) 'What Hypercerts Unlock' subsections (lines 107-146, ~40 lines), (3) 'Who Buys Hypercerts' (lines 148-170, ~23 lines of buyer personas). Goal: trim to ~120-130 lines by condensing the economics theory, making the unlock section more scannable, and shortening buyer personas. This is a vision page so some essay style is acceptable — just tighten, don't gut.","status":"open","priority":3,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-03-06T18:09:03.220089+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:09:03.220089+08:00","labels":["scope:small"]} -{"id":"hypercerts-atproto-documentation-j5j.1","title":"Trim why-we-need-hypercerts.md — condense economics theory and buyer personas","description":"## Files\n- pages/ecosystem/why-we-need-hypercerts.md (modify)\n\n## What to do\n1. **Condense 'Why This Happens: We Confuse Price with Value'** (lines 23-44). Cut from ~22 lines to ~8-10 lines. Keep the core insight (markets see private value but miss collective value; governments can't keep up) but remove the extended explanation of how markets work and the historical context about government systems. The 3 bullet examples (forest, science, journalism) can stay as they're concrete.\n\n2. **Tighten 'What Hypercerts Unlock' subsections** (lines 107-146). Each of the 5 subsections is a full paragraph. Reduce each to 2-3 sentences max. The key ideas to preserve: (1) portable funding mechanisms, (2) proof of contribution not just spending, (3) evaluators become essential, (4) fast decisions + slow feedback loops, (5) AI makes verifiable data critical.\n\n3. **Condense 'Who Buys Hypercerts'** (lines 148-170). Cut from ~23 lines to ~10 lines. Convert the 4 detailed buyer personas into a compact list with 1-2 sentences each instead of full paragraphs.\n\n4. Keep the overall narrative arc: Problem → Shift needed → Hypercerts as solution → What they unlock → Who buys → Where we're headed.\n\n## Don't\n- Remove any of the 4 buyer categories (philanthropists, organizations, communities, governments)\n- Remove any of the 5 'unlock' subsections\n- Change the page title, frontmatter, or section headings\n- Add new content\n- Change the opening 'Simple Observation' section (lines 10-21) — it's already concise","acceptance_criteria":"1. Total file length is ≤135 lines. 2. All 4 buyer categories still mentioned. 3. All 5 'unlock' subsections still present. 4. The 'Price with Value' section is ≤12 lines. 5. npm run build succeeds.","status":"open","priority":3,"issue_type":"task","owner":"sharfy-test.climateai.org","estimated_minutes":45,"created_at":"2026-03-06T18:09:17.244134+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:09:17.244134+08:00","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-j5j.1","depends_on_id":"hypercerts-atproto-documentation-j5j","type":"parent-child","created_at":"2026-03-06T18:09:17.245619+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-kqm","title":"Epic: Replace Hyperscan links with GitHub lexicon repo links across all lexicon pages","description":"All 15 individual lexicon pages currently link to the Hyperscan lexicon browser for their schema. These should instead link directly to the JSON file in the GitHub lexicon repo (https://github.com/hypercerts-org/hypercerts-lexicon). The two index pages and introduction page have no Hyperscan links and need no changes.","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-03-05T19:44:35.507703+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-04-01T13:47:18.534322467+06:00","closed_at":"2026-03-06T18:07:24.416919+08:00","labels":["scope:small"]} -{"id":"hypercerts-atproto-documentation-kqm.1","title":"Replace Hyperscan browser links with GitHub lexicon repo links in all 15 lexicon pages","description":"## Files\n- pages/lexicons/hypercerts-lexicons/activity-claim.md (modify)\n- pages/lexicons/hypercerts-lexicons/contribution.md (modify)\n- pages/lexicons/hypercerts-lexicons/attachment.md (modify)\n- pages/lexicons/hypercerts-lexicons/measurement.md (modify)\n- pages/lexicons/hypercerts-lexicons/evaluation.md (modify)\n- pages/lexicons/hypercerts-lexicons/collection.md (modify)\n- pages/lexicons/hypercerts-lexicons/rights.md (modify)\n- pages/lexicons/hypercerts-lexicons/funding-receipt.md (modify)\n- pages/lexicons/hypercerts-lexicons/acknowledgement.md (modify)\n- pages/lexicons/certified-lexicons/profile.md (modify)\n- pages/lexicons/certified-lexicons/shared-defs.md (modify)\n- pages/lexicons/certified-lexicons/location.md (modify)\n- pages/lexicons/certified-lexicons/badge-definition.md (modify)\n- pages/lexicons/certified-lexicons/badge-award.md (modify)\n- pages/lexicons/certified-lexicons/badge-response.md (modify)\n\n## What to do\nIn each file, replace the last line which currently reads:\n`For the full schema, see the [Hyperscan lexicon browser](https://www.hyperscan.dev/agents/lexicon/\u003cNSID\u003e).`\n\nWith:\n`For the full schema, see [`\u003cNSID\u003e`](\u003cgithub-url\u003e) in the lexicon repo.`\n\nWhere \u003cgithub-url\u003e is `https://github.com/hypercerts-org/hypercerts-lexicon/blob/main/lexicons/\u003cpath\u003e.json`\n\nThe exact NSID → GitHub path mapping:\n- org.hypercerts.claim.activity → lexicons/org/hypercerts/claim/activity.json\n- org.hypercerts.claim.contributorInformation → lexicons/org/hypercerts/claim/contributorInformation.json\n- org.hypercerts.claim.contributionDetails → lexicons/org/hypercerts/claim/contribution.json\n- org.hypercerts.context.attachment → lexicons/org/hypercerts/context/attachment.json\n- org.hypercerts.context.measurement → lexicons/org/hypercerts/context/measurement.json\n- org.hypercerts.context.evaluation → lexicons/org/hypercerts/context/evaluation.json\n- org.hypercerts.claim.collection → lexicons/org/hypercerts/collection.json\n- org.hypercerts.claim.rights → lexicons/org/hypercerts/claim/rights.json\n- org.hypercerts.funding.receipt → lexicons/org/hypercerts/funding/receipt.json\n- org.hypercerts.context.acknowledgement → lexicons/org/hypercerts/context/acknowledgement.json\n- app.certified.actor.profile → lexicons/app/certified/actor/profile.json\n- app.certified.defs → lexicons/app/certified/defs.json\n- app.certified.location → lexicons/app/certified/location.json\n- app.certified.badge.definition → lexicons/app/certified/badge/definition.json\n- app.certified.badge.award → lexicons/app/certified/badge/award.json\n- app.certified.badge.response → lexicons/app/certified/badge/response.json\n\nNote: contribution.md has TWO links (one for contributorInformation, one for contributionDetails). Both must be updated.\n\n## Dont\n- Do not change any other content on the pages\n- Do not change the index pages or introduction page\n- Do not reword the surrounding sentence — just swap the link target and anchor text","acceptance_criteria":"All 15 lexicon pages link to the correct GitHub JSON file in https://github.com/hypercerts-org/hypercerts-lexicon/blob/main/lexicons/. No Hyperscan links remain in pages/lexicons/. npm run build succeeds.","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":30,"created_at":"2026-03-05T19:44:51.254641+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-05T19:48:03.329015+08:00","closed_at":"2026-03-05T19:48:03.329015+08:00","close_reason":"1514237 Replace Hyperscan links with GitHub lexicon repo links in all 15 lexicon pages","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-kqm.1","depends_on_id":"hypercerts-atproto-documentation-kqm","type":"parent-child","created_at":"2026-03-05T19:44:51.263686+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-m9c","title":"Fix: grammar 'a `org.hypercerts...`' should be 'an `org.hypercerts...`' (from hypercerts-atproto-documentation-w96.7)","description":"Review of hypercerts-atproto-documentation-w96.7 found: Two bullet points in the 'Additional details' section use the article 'a' before backtick-quoted NSIDs that start with the vowel 'o', which should be 'an'.\n\n**Evidence from pages/core-concepts/hypercerts-core-data-model.md:**\n\nLine 29:\n\u003e 'a strong reference to a `org.hypercerts.claim.contributorInformation` record'\n\nLine 31:\n\u003e 'a strong reference to a `org.hypercerts.claim.contribution` record'\n\nBoth NSIDs begin with 'org' (vowel sound), so the article should be 'an', not 'a'.\n\n**Fix:** Change both occurrences of 'a `org.hypercerts' to 'an `org.hypercerts'.","status":"closed","priority":3,"issue_type":"bug","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","created_at":"2026-03-05T20:09:12.809579261+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:14:53.439860314+06:00","closed_at":"2026-03-05T20:14:53.439860314+06:00","close_reason":"0c38475 Fix grammar: 'a `org.hypercerts`' → 'an `org.hypercerts`'","dependencies":[{"issue_id":"hypercerts-atproto-documentation-m9c","depends_on_id":"hypercerts-atproto-documentation-w96.7","type":"discovered-from","created_at":"2026-03-05T20:09:15.431783479+06:00","created_by":"karma.gainforest.id"}]} -{"id":"hypercerts-atproto-documentation-pzc","title":"Epic: Fix TOC sidebar heading hierarchy — add visual distinction between h2 and h3","description":"## Problem\nThe right-side table of contents (TOC) sidebar shows all headings at the same visual level — there is no indentation or meaningful visual distinction between h2 and h3 entries. The `toc-link-h3` CSS class only reduces font-size by 1px (13px vs 14px) with zero indentation, making the hierarchy invisible.\n\n## Goals\n1. Add clear visual indentation for h3 entries in the TOC so users can see section hierarchy at a glance\n2. Ensure the fix works across all pages that have mixed h2/h3 headings (scaffold, testing-and-deployment, architecture pages, etc.)\n3. Keep the design clean and consistent with the existing site aesthetic\n\n## Key files\n- `styles/globals.css` (lines 938-940: `.toc-link-h3` rule)\n- `components/TableOfContents.js` (renders TOC with level-aware classes)\n\n## Scope\nCSS-only fix for the visual distinction. No changes to the TOC component logic needed — it already correctly tracks heading levels and applies the right classes.","status":"closed","priority":1,"issue_type":"epic","owner":"sharfy-test.climateai.org","created_at":"2026-03-06T14:11:10.366951+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T18:07:25.241988+08:00","closed_at":"2026-03-06T18:07:25.241988+08:00","close_reason":"4e902e4 Child pzc.1 closed — TOC h3 indentation fix applied","labels":["scope:small"]} -{"id":"hypercerts-atproto-documentation-pzc.1","title":"Add CSS indentation and visual distinction for h3 entries in TOC sidebar","description":"## Files\n- styles/globals.css (modify — lines 938-940)\n\n## What to do\nUpdate the `.toc-link-h3` CSS rule and add a new `.toc-item` variant to create clear visual hierarchy for h3 entries in the right-side TOC sidebar.\n\n### Current CSS (line 938-940):\n```css\n.toc-link-h3 {\n font-size: 13px;\n}\n```\n\n### Required changes:\n1. Add `padding-left: 12px` to `.toc-link-h3` to indent h3 entries under their parent h2\n2. Reduce the font-size slightly more to `12.5px` or `0.8125rem` (13px is too close to 14px to notice)\n3. Optionally reduce the opacity or use `var(--color-text-tertiary)` for non-active h3 links to further differentiate\n\n### Context:\n- The `TableOfContents.js` component already applies `toc-link-h3` class to h3 entries (line 96)\n- The `.toc-item` div wraps each link and provides the left border + padding\n- Pages with h3 headings include: using-the-scaffold-app.md (Step 1-7), testing-and-deployment.md, building-on-hypercerts.md, architecture pages, tools/scaffold.md, cel-work-scopes.md\n- The border-left on `.toc-item` should still align for h3 items — the indentation should be on the text/link, not the border\n\n## Don't\n- Modify TableOfContents.js — the component logic is correct\n- Change the h2 styling — only add distinction for h3\n- Add JavaScript-based indentation — this is CSS-only\n- Break the active state highlighting (`.toc-link-active` and `.toc-item-active` must still work)","acceptance_criteria":"1. On pages with mixed h2/h3 headings (e.g. /getting-started/using-the-scaffold-app), h3 entries in the right TOC sidebar are visually indented relative to h2 entries\n2. The indentation is at least 10px of additional left padding on the link text\n3. h2 entries remain at their current position (no regression)\n4. Active state highlighting still works correctly for both h2 and h3 entries\n5. The site builds without errors: pnpm build succeeds\n6. Dark mode and light mode both render the TOC correctly","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":20,"created_at":"2026-03-06T14:11:28.829438+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T15:41:41.988682+08:00","closed_at":"2026-03-06T15:41:41.988682+08:00","close_reason":"04910ec Add CSS indentation and visual distinction for h3 entries in TOC sidebar","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-pzc.1","depends_on_id":"hypercerts-atproto-documentation-pzc","type":"parent-child","created_at":"2026-03-06T14:11:28.83069+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-pzc.2","title":"Fix TOC border indentation for h3 — add toc-item-h3 class and shift border inward","description":"## Problem\nThe previous fix (pzc.1) only added padding-left to the link text, but the left border on `.toc-item` stays at the same position for h2 and h3. The vertical border line needs to shift inward for h3 entries to create visible nesting.\n\n## Files\n- components/TableOfContents.js (modify — line 92)\n- styles/globals.css (modify — lines 910-942)\n\n## What to do\n\n### 1. TableOfContents.js — Add level class to the wrapper div\nOn line 92, the div currently gets:\n```jsx\nclassName={`toc-item${activeId === id ? \" toc-item-active\" : \"\"}`}\n```\nChange to:\n```jsx\nclassName={`toc-item${level === 3 ? \" toc-item-h3\" : \"\"}${activeId === id ? \" toc-item-active\" : \"\"}`}\n```\n\n### 2. styles/globals.css — Add .toc-item-h3 rule\nAdd after the existing `.toc-item` rule (after line 914):\n```css\n.toc-item-h3 {\n margin-left: 12px;\n}\n```\n\n### 3. styles/globals.css — Update .toc-link-h3\nKeep the font-size reduction but REMOVE the padding-left (the margin-left on the parent div handles indentation now):\n```css\n.toc-link-h3 {\n font-size: 13px;\n}\n```\n(Revert to original — no padding-left, no opacity.)\n\n## Why this works\n`margin-left: 12px` on the `.toc-item` div shifts the entire item including its `border-left` inward. This makes the vertical border line visually nest under the h2 items above, which is exactly what the user wants.\n\n## Don't\n- Add padding-left to the link — that was the wrong approach (pzc.1)\n- Change the h2 styling\n- Break active state highlighting\n- Change the scroll spy or heading extraction logic","acceptance_criteria":"1. On /tools/scaffold, h3 entries (e.g. 'Sign in with ATProto', 'OAuth Flow') have their left border visually indented ~12px to the right compared to h2 entries (e.g. 'Tech Stack', 'Architecture')\n2. The vertical border line for h3 items starts further right than h2 items — creating a visible step/nesting in the border\n3. Active state border highlighting works for both h2 and h3 items\n4. h2 items are unchanged from current behavior\n5. pnpm build succeeds without errors\n6. Works in both light and dark mode","status":"closed","priority":1,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":30,"created_at":"2026-03-06T22:12:26.022914+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-03-06T22:13:44.585921+08:00","closed_at":"2026-03-06T22:13:44.585921+08:00","close_reason":"556aeba Fix TOC border indentation for h3 by adding toc-item-h3 class with margin-left to shift border inward","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-pzc.2","depends_on_id":"hypercerts-atproto-documentation-pzc","type":"parent-child","created_at":"2026-03-06T22:12:26.026373+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-qu1","title":"Epic: Draft pages system — unlisted pages with /drafts index","description":"Add a draft page system to the documentation site. Pages with `draft: true` in frontmatter should be excluded from the sidebar navigation and prev/next pagination, but remain accessible by direct URL. A /drafts index page lists all draft pages so devs can find them. This lets the team write documentation ahead of time without exposing it to public visitors. Success: draft pages are invisible in sidebar/pagination, accessible by URL, and discoverable at /drafts.","status":"closed","priority":2,"issue_type":"epic","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","created_at":"2026-02-23T16:52:19.61403+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-04-01T13:47:18.536260095+06:00","closed_at":"2026-03-06T18:07:25.629367+08:00","labels":["needs-integration-review","scope:medium"]} -{"id":"hypercerts-atproto-documentation-qu1.1","title":"Add getDraftPages() utility to lib/navigation.js","description":"## Files\n- documentation/lib/navigation.js (modify)\n\n## What to do\nAdd a new exported function `getDraftPages()` that:\n1. Uses Node.js `fs` and `path` to recursively scan `pages/` directory for `.md` and `.mdoc` files\n2. Reads the frontmatter of each file (parse YAML between `---` fences)\n3. Returns an array of `{ title, path, description }` for files where `draft: true` is in frontmatter\n4. The `path` should be the URL path (e.g., `pages/drafts/my-page.md` → `/drafts/my-page`)\n\nIMPORTANT: This function will only be called at build time (in getStaticProps), so using `fs` is fine. Do NOT import `fs` at the top level — use dynamic require inside the function body so it does not break client-side bundles.\n\nExample return value:\n```js\n[\n { title: \"Token Bridge Design\", path: \"/drafts/token-bridge\", description: \"Early design notes...\" },\n { title: \"Governance Model\", path: \"/architecture/governance\", description: undefined }\n]\n```\n\n## Don't\n- Import fs/path at the module top level (breaks client bundle)\n- Modify the existing `navigation`, `flattenNavigation`, or `getPrevNext` exports\n- Add any npm dependencies","acceptance_criteria":"1. `getDraftPages()` is exported from `lib/navigation.js`\n2. It returns an array of objects with `title`, `path`, and `description` keys\n3. It only includes pages where frontmatter has `draft: true`\n4. It does NOT include pages where `draft` is missing or `false`\n5. The path values are correct URL paths (no `.md` extension, no `pages/` prefix, `index.md` maps to parent dir)\n6. No top-level `fs` or `path` import exists in the file\n7. Existing exports (`navigation`, `flattenNavigation`, `getPrevNext`) still work unchanged","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":30,"created_at":"2026-02-23T16:52:36.411067+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-23T17:04:55.059341+08:00","closed_at":"2026-02-23T17:04:55.059341+08:00","close_reason":"e03fb4a Add getDraftPages() utility function","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-qu1.1","depends_on_id":"hypercerts-atproto-documentation-qu1","type":"parent-child","created_at":"2026-02-23T16:52:36.411868+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-qu1.2","title":"Create /drafts index page","description":"## Files\n- documentation/pages/drafts/index.js (create)\n\n## What to do\nCreate a Next.js page at `/drafts` that lists all draft pages. This is a `.js` page (not `.md`) because it needs `getStaticProps` to call `getDraftPages()`.\n\nThe page should:\n1. Import and call `getDraftPages()` from `../../lib/navigation` inside `getStaticProps`\n2. Return the list as a prop\n3. Render a simple page with:\n - Title: \"Draft Pages\" (set via frontmatter-like props so Layout picks it up)\n - A subtitle/description: \"These pages are work-in-progress and not yet listed in the sidebar.\"\n - A list of draft pages, each showing:\n - The page title as a clickable link (use Next.js `Link`)\n - The description underneath (if available), in secondary text color\n - The URL path in small monospace text (so devs can reference it)\n - If no draft pages exist, show a message: \"No draft pages yet.\"\n4. Pass `markdoc: { frontmatter: { title: \"Draft Pages\" } }` in the props so the Layout component picks up the page title correctly\n\nStyle the page using the existing CSS variables and class conventions from globals.css. Use inline styles or a `\u003cstyle jsx\u003e` block — do NOT modify globals.css for this page.\n\n## Don't\n- Use .md/.mdoc format (needs getStaticProps for dynamic content)\n- Add this page to the `navigation` array in lib/navigation.js (it should be unlisted)\n- Modify globals.css\n- Add any npm dependencies","acceptance_criteria":"1. Visiting /drafts in the browser renders a page titled \"Draft Pages\"\n2. The page lists all .md files that have `draft: true` in frontmatter\n3. Each listed page shows its title as a clickable link, optional description, and URL path\n4. Clicking a link navigates to the draft page\n5. The page title appears in the browser tab as \"Draft Pages - Hypercerts Protocol\"\n6. The /drafts page does NOT appear in the sidebar navigation\n7. When no draft pages exist, the message \"No draft pages yet.\" is shown\n8. The page uses the site Layout (header, sidebar visible but /drafts not highlighted)","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":45,"created_at":"2026-02-23T16:52:53.435836+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-23T17:09:18.570394+08:00","closed_at":"2026-02-23T17:09:18.570394+08:00","close_reason":"8951cf5 Create /drafts index page","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-qu1.2","depends_on_id":"hypercerts-atproto-documentation-qu1","type":"parent-child","created_at":"2026-02-23T16:52:53.436646+08:00","created_by":"sharfy-test.climateai.org"},{"issue_id":"hypercerts-atproto-documentation-qu1.2","depends_on_id":"hypercerts-atproto-documentation-qu1.1","type":"blocks","created_at":"2026-02-23T16:52:53.437875+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-qu1.3","title":"Show DRAFT banner on draft pages in dev mode","description":"## Files\n- documentation/components/Layout.js (modify)\n\n## What to do\nWhen a page has `draft: true` in its frontmatter, show a visible banner at the top of the article content area. This helps devs immediately see they are viewing a draft page.\n\nThe banner should:\n1. Check `frontmatter?.draft === true`\n2. Render a yellow/amber banner above the `\u003carticle\u003e` element (but below the Breadcrumbs)\n3. Banner text: \"📝 This page is a draft and is not listed in the sidebar navigation.\"\n4. Style: amber/yellow background (`#fef3c7`), dark amber text (`#92400e`), 1px border (`#fcd34d`), rounded corners, padding, `font-size: 14px`, `font-weight: 500`\n5. Use inline styles on the banner div (do NOT modify globals.css)\n\nThe banner should show in BOTH dev and production — it is useful for anyone who navigates to the URL directly.\n\n## Don't\n- Modify globals.css\n- Modify any other component\n- Change how frontmatter is passed (it is already available as the `frontmatter` prop)\n- Add any conditional logic based on NODE_ENV (show the banner always)","acceptance_criteria":"1. Pages with `draft: true` frontmatter show a yellow banner reading \"📝 This page is a draft and is not listed in the sidebar navigation.\"\n2. Pages without `draft: true` do NOT show the banner\n3. The banner appears between the Breadcrumbs and the article content\n4. The banner has amber/yellow styling (background #fef3c7, text #92400e, border #fcd34d)\n5. No changes to globals.css\n6. Existing pages render exactly as before (no visual regression)","status":"closed","priority":3,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":15,"created_at":"2026-02-23T16:53:07.104334+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-23T17:01:51.204858+08:00","closed_at":"2026-02-23T17:01:51.204858+08:00","close_reason":"42ad0d0 Show DRAFT banner on draft pages","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-qu1.3","depends_on_id":"hypercerts-atproto-documentation-qu1","type":"parent-child","created_at":"2026-02-23T16:53:07.105221+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-qu1.4","title":"Exclude draft pages from prev/next pagination","description":"## Files\n- documentation/lib/navigation.js (modify)\n\n## What to do\nCurrently `flattenNavigation()` and `getPrevNext()` operate on the manual `navigation` array, which only contains non-draft pages. This is already correct — draft pages are not in the `navigation` array, so they will never appear in prev/next links.\n\nHowever, when a user is ON a draft page, `getPrevNext()` will return `{ prev: null, next: null }` because the draft page path won't be found in the flat list. This is the desired behavior — draft pages should not show prev/next pagination.\n\n**No code changes needed for navigation.js.** The task is to verify this behavior and ensure the Layout handles the case gracefully (it already does — pagination is conditionally rendered with `{(prev || next) \u0026\u0026 ...}`).\n\nThe real work: create a sample draft page to test the system end-to-end.\n\n## Files\n- documentation/pages/drafts/example-draft.md (create)\n\nCreate a sample draft page at `pages/drafts/example-draft.md` with this content:\n\n```markdown\n---\ntitle: Example Draft Page\ndescription: This is a sample draft page to verify the draft system works.\ndraft: true\n---\n\n# Example Draft Page\n\nThis page exists to verify the draft system. It should:\n\n- ✅ Be accessible at `/drafts/example-draft`\n- ✅ NOT appear in the sidebar navigation\n- ✅ NOT appear in prev/next pagination\n- ✅ Show a draft banner (if that task is complete)\n- ✅ Be listed on the `/drafts` index page (if that task is complete)\n\n## Sample content\n\nThis is placeholder content for testing purposes. Replace or remove this page when real draft content is added.\n```\n\n## Don't\n- Modify navigation.js (the behavior is already correct)\n- Add the example draft page to the navigation array\n- Modify Layout.js or Sidebar.js","acceptance_criteria":"1. The file `pages/drafts/example-draft.md` exists with `draft: true` in frontmatter\n2. Visiting `/drafts/example-draft` renders the page with its content\n3. The page does NOT appear in the sidebar navigation\n4. The page does NOT show prev/next pagination links\n5. The navigation array in lib/navigation.js is unchanged","status":"closed","priority":2,"issue_type":"task","assignee":"sharfy-test.climateai.org","owner":"sharfy-test.climateai.org","estimated_minutes":10,"created_at":"2026-02-23T16:53:22.058897+08:00","created_by":"sharfy-test.climateai.org","updated_at":"2026-02-23T17:00:44.51411+08:00","closed_at":"2026-02-23T17:00:44.51411+08:00","close_reason":"a09a1f5 Add example draft page for testing draft system","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-qu1.4","depends_on_id":"hypercerts-atproto-documentation-qu1","type":"parent-child","created_at":"2026-02-23T16:53:22.060017+08:00","created_by":"sharfy-test.climateai.org"}]} -{"id":"hypercerts-atproto-documentation-w96","title":"Epic: Fix incorrect lexicon NSIDs and inaccurate data model descriptions across documentation","description":"The documentation references incorrect lexicon NSIDs and misrepresents how several record types work. The actual lexicons live in /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/. Key problems: (1) org.hypercerts.claim.contributionDetails doesn't exist — the real NSID is org.hypercerts.claim.contribution, (2) org.hypercerts.claim.collection doesn't exist — the real NSID is org.hypercerts.collection, (3) the core data model page misrepresents how contributors work (misses inline identity/role option), (4) missing record types (rights, funding receipt, acknowledgement, work scope). Success: every lexicon NSID in the docs matches the actual lexicon JSON files, and the core data model page accurately describes how records connect.","status":"closed","priority":1,"issue_type":"epic","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","created_at":"2026-03-05T19:57:16.959712428+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-06T18:07:22.511914+08:00","closed_at":"2026-03-06T18:07:22.511914+08:00","close_reason":"4e902e4 All 7 children closed — NSID fixes and contributor model rewrite merged","labels":["needs-integration-review","scope:medium"]} -{"id":"hypercerts-atproto-documentation-w96.1","title":"Fix wrong lexicon NSIDs in lexicon index page","description":"## Files\n- pages/lexicons/hypercerts-lexicons/index.md (modify)\n\n## What to do\nFix two incorrect NSIDs in the lexicon index table:\n\n1. Row \"Contribution\": Change `org.hypercerts.claim.contributionDetails` to `org.hypercerts.claim.contribution`. The description says \"two lexicons\" — keep that, but list the correct NSIDs: `org.hypercerts.claim.contributorInformation` and `org.hypercerts.claim.contribution` (NOT contributionDetails).\n\n2. Row \"Collection\": Change `org.hypercerts.claim.collection` to `org.hypercerts.collection` (remove the `.claim.` segment).\n\nThe actual lexicon files are:\n- /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/claim/contribution.json (id: org.hypercerts.claim.contribution)\n- /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/collection.json (id: org.hypercerts.collection)\n\n## Dont\n- Do not change any other rows in the table\n- Do not change the page structure or add new content\n- Do not rename the linked page paths (the /lexicons/hypercerts-lexicons/contribution and /collection links stay the same)","acceptance_criteria":"1. The Contribution row NSID column contains `org.hypercerts.claim.contributorInformation` and `org.hypercerts.claim.contribution` (not contributionDetails)\n2. The Collection row NSID column contains `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n3. No other rows are modified\n4. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":15,"created_at":"2026-03-05T19:57:28.584709816+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:03:30.920162244+06:00","closed_at":"2026-03-05T20:03:30.920162244+06:00","close_reason":"d92a9fa Fix wrong lexicon NSIDs in lexicon index page","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-w96.1","depends_on_id":"hypercerts-atproto-documentation-w96","type":"parent-child","created_at":"2026-03-05T19:57:28.588772609+06:00","created_by":"karma.gainforest.id"}]} -{"id":"hypercerts-atproto-documentation-w96.2","title":"Fix wrong NSID in contribution lexicon doc page","description":"## Files\n- pages/lexicons/hypercerts-lexicons/contribution.md (modify)\n\n## What to do\nFix the incorrect NSID reference for Contribution Details.\n\nLine 21 currently says:\n```\n`org.hypercerts.claim.contributionDetails`\n```\n\nChange it to:\n```\n`org.hypercerts.claim.contribution`\n```\n\nThe actual lexicon file is /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/claim/contribution.json with id \"org.hypercerts.claim.contribution\".\n\nAlso update the section heading on line 19 from \"Contribution Details\" to \"Contribution\" to match the actual lexicon name. Keep the description text explaining what it does.\n\nThe GitHub link on line 27 already points to the correct file (contribution.json), but the display text says `org.hypercerts.claim.contributionDetails` — change it to `org.hypercerts.claim.contribution`.\n\n## Dont\n- Do not change the Contributor Information section (lines 9-17) — that NSID is correct\n- Do not change the page title or frontmatter\n- Do not restructure the page","acceptance_criteria":"1. The NSID shown for the contribution details section is `org.hypercerts.claim.contribution` (not contributionDetails)\n2. The section heading says \"Contribution\" (not \"Contribution Details\")\n3. The GitHub link display text says `org.hypercerts.claim.contribution`\n4. The Contributor Information section is unchanged\n5. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":15,"created_at":"2026-03-05T19:57:41.000060045+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:03:26.349215113+06:00","closed_at":"2026-03-05T20:03:26.349215113+06:00","close_reason":"f403f10 Fix wrong NSID in contribution lexicon doc page","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-w96.2","depends_on_id":"hypercerts-atproto-documentation-w96","type":"parent-child","created_at":"2026-03-05T19:57:41.002778953+06:00","created_by":"karma.gainforest.id"}]} -{"id":"hypercerts-atproto-documentation-w96.3","title":"Fix wrong NSID in collection lexicon doc page","description":"## Files\n- pages/lexicons/hypercerts-lexicons/collection.md (modify)\n\n## What to do\nFix the incorrect NSID on line 7.\n\nCurrently says:\n```\n`org.hypercerts.claim.collection`\n```\n\nChange to:\n```\n`org.hypercerts.collection`\n```\n\nThe actual lexicon file is /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/collection.json with id \"org.hypercerts.collection\" — there is no `.claim.` segment.\n\nAlso update the GitHub link on line 13 — the display text currently says `org.hypercerts.claim.collection`, change it to `org.hypercerts.collection`. The URL itself already points to the correct file path.\n\n## Dont\n- Do not change the page title, frontmatter, or description text\n- Do not add new content","acceptance_criteria":"1. The NSID shown is `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n2. The GitHub link display text says `org.hypercerts.collection`\n3. No other content is changed\n4. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":10,"created_at":"2026-03-05T19:57:47.410685142+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:03:24.579280929+06:00","closed_at":"2026-03-05T20:03:24.579280929+06:00","close_reason":"9b8042c Fix wrong NSID in collection lexicon doc page","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-w96.3","depends_on_id":"hypercerts-atproto-documentation-w96","type":"parent-child","created_at":"2026-03-05T19:57:47.413144222+06:00","created_by":"karma.gainforest.id"}]} -{"id":"hypercerts-atproto-documentation-w96.4","title":"Fix wrong lexicon NSIDs in core data model page","description":"## Files\n- pages/core-concepts/hypercerts-core-data-model.md (modify)\n\n## What to do\nFix two incorrect lexicon NSIDs in the tables on this page.\n\n### Fix 1: Contribution Details NSID (line 32, \"Additional details\" table)\nCurrently says: `org.hypercerts.claim.contributionDetails`\nChange to: `org.hypercerts.claim.contribution`\n\n### Fix 2: Collection NSID (line 63, \"Grouping hypercerts\" table)\nCurrently says: `org.hypercerts.claim.collection`\nChange to: `org.hypercerts.collection`\n\nThe actual lexicon files are:\n- /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/claim/contribution.json (id: org.hypercerts.claim.contribution)\n- /home/kzoeps/Projects/gainforest/hypercerts-lexicon/lexicons/org/hypercerts/collection.json (id: org.hypercerts.collection)\n\n## Dont\n- Do not change any other content on this page — there is a separate task for rewriting the contributor model description and adding missing record types\n- Do not change the table structure or descriptions, only the NSID strings","acceptance_criteria":"1. Line 32 (or the Contribution Details row in the \"Additional details\" table) shows `org.hypercerts.claim.contribution` (not contributionDetails)\n2. Line 63 (or the Collection row in the \"Grouping hypercerts\" table) shows `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n3. No other content on the page is changed\n4. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":10,"created_at":"2026-03-05T19:57:56.276881623+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:03:29.494981296+06:00","closed_at":"2026-03-05T20:03:29.494981296+06:00","close_reason":"d92a9fa Fix wrong lexicon NSIDs in core data model page","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-w96.4","depends_on_id":"hypercerts-atproto-documentation-w96","type":"parent-child","created_at":"2026-03-05T19:57:56.279903571+06:00","created_by":"karma.gainforest.id"}]} -{"id":"hypercerts-atproto-documentation-w96.5","title":"Fix wrong lexicon NSIDs in data-flow-and-lifecycle page","description":"## Files\n- pages/architecture/data-flow-and-lifecycle.md (modify)\n\n## What to do\nFix two incorrect NSIDs:\n\n### Fix 1: Line 59\nCurrently says: `org.hypercerts.claim.contributionDetails`\nChange to: `org.hypercerts.claim.contribution`\n\n### Fix 2: Line 79\nCurrently says: `org.hypercerts.claim.collection`\nChange to: `org.hypercerts.collection`\n\nThe actual lexicon IDs are:\n- org.hypercerts.claim.contribution (file: contribution.json)\n- org.hypercerts.collection (file: collection.json, no .claim. segment)\n\n## Dont\n- Do not change any surrounding text or page structure\n- Only change the two NSID strings","acceptance_criteria":"1. Line 59 (or equivalent) contains `org.hypercerts.claim.contribution` (not contributionDetails)\n2. Line 79 (or equivalent) contains `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n3. No other content is changed\n4. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":10,"created_at":"2026-03-05T19:58:14.602853024+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:04:27.406244913+06:00","closed_at":"2026-03-05T20:04:27.406244913+06:00","close_reason":"91f3ecd Fix wrong lexicon NSIDs in data-flow-and-lifecycle page","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-w96.5","depends_on_id":"hypercerts-atproto-documentation-w96","type":"parent-child","created_at":"2026-03-05T19:58:14.60526901+06:00","created_by":"karma.gainforest.id"}]} -{"id":"hypercerts-atproto-documentation-w96.6","title":"Fix wrong collection NSID in roadmap page","description":"## Files\n- pages/roadmap.md (modify)\n\n## What to do\nFix incorrect collection NSID on line 69.\n\nCurrently says: `org.hypercerts.claim.collection`\nChange to: `org.hypercerts.collection`\n\nThe actual lexicon ID is org.hypercerts.collection (no .claim. segment).\n\n## Dont\n- Do not change any other content on the roadmap page\n- Only change the one NSID string","acceptance_criteria":"1. Line 69 (or the collection row in the table) shows `org.hypercerts.collection` (not org.hypercerts.claim.collection)\n2. No other content is changed\n3. File parses as valid Markdown","status":"closed","priority":1,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":5,"created_at":"2026-03-05T19:58:19.75432948+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:04:11.56608607+06:00","closed_at":"2026-03-05T20:04:11.56608607+06:00","close_reason":"3352ec1 Fix wrong collection NSID in roadmap page","labels":["scope:trivial"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-w96.6","depends_on_id":"hypercerts-atproto-documentation-w96","type":"parent-child","created_at":"2026-03-05T19:58:19.756668539+06:00","created_by":"karma.gainforest.id"}]} -{"id":"hypercerts-atproto-documentation-w96.7","title":"Rewrite contributor model description and add missing record types in core data model page","description":"## Files\n- pages/core-concepts/hypercerts-core-data-model.md (modify)\n\n## What to do\nThe core data model page has structural inaccuracies about how contributors work. Fix the contributor model description and the \"How records connect\" tree.\n\n### 1. Fix the \"Additional details\" section (lines 26-33)\nThe current description says ContributorInformation and ContributionDetails are \"separate records with their own AT-URI\" that \"can be referenced from the activity claim.\" This is misleading.\n\nRewrite to explain the actual model: The activity claim has a `contributors` array where each entry is a contributor object containing:\n- `contributorIdentity`: either an inline identity string (DID) via `#contributorIdentity`, OR a strong reference to an `org.hypercerts.claim.contributorInformation` record\n- `contributionWeight`: optional relative weight string\n- `contributionDetails`: either an inline role string via `#contributorRole`, OR a strong reference to an `org.hypercerts.claim.contribution` record\n\nEmphasize the dual inline/reference pattern — simple cases use inline strings, richer profiles use separate records.\n\nUpdate the table to reflect the correct lexicon name `org.hypercerts.claim.contribution` (not contributionDetails).\n\n### 2. Fix the \"How records connect\" tree (lines 70-82)\nThe tree currently shows ContributorInformation and ContributionDetails as separate child records. Update it to show them as embedded within contributor objects, reflecting the actual structure. Example:\n\n```text\nActivity Claim (the core record)\n├── contributors[0]\n│ ├── contributorIdentity: Alice (inline DID or ref to ContributorInformation)\n│ ├── contributionWeight: \"1\"\n│ └── contributionDetails: Lead author (inline role or ref to Contribution)\n├── contributors[1]\n│ ├── contributorIdentity: → ContributorInformation record (Bob)\n│ └── contributionDetails: → Contribution record (Technical reviewer, Jan-Mar)\n├── Attachment: GitHub repository link\n├── Measurement: 12 pages written\n├── Measurement: 8,500 words\n└── Evaluation: \"High-quality documentation\" (by Carol)\n```\n\n## Dont\n- Do not change the \"The core record: activity claim\" section (lines 12-23) — the four dimensions table is correct\n- Do not change the \"Grouping hypercerts\" section\n- Do not change the \"Mutability\" or \"What happens next\" sections\n- Do not add new record types (rights, acknowledgement, funding receipt) — this task is fixes only\n- Do not add code examples or API usage — this is a conceptual page\n- Keep the writing style consistent with the rest of the page (concise, factual, no marketing language)","acceptance_criteria":"1. The \"Additional details\" section accurately describes the dual inline/reference pattern for contributors\n2. The contributor table shows `org.hypercerts.claim.contribution` (not contributionDetails)\n3. The \"How records connect\" tree shows contributors as embedded objects with inline/reference options\n4. The four dimensions table in \"The core record\" section is unchanged\n5. The \"Grouping hypercerts\", \"Mutability\", and \"What happens next\" sections are unchanged\n6. No new record types are added to the page\n7. File parses as valid Markdown","status":"closed","priority":2,"issue_type":"task","assignee":"karma.gainforest.id","owner":"karma.gainforest.id","estimated_minutes":45,"created_at":"2026-03-05T19:58:48.627566144+06:00","created_by":"karma.gainforest.id","updated_at":"2026-03-05T20:05:12.194006503+06:00","closed_at":"2026-03-05T20:05:12.194006503+06:00","close_reason":"e55324a Rewrite contributor model description and fix records tree","labels":["scope:small"],"dependencies":[{"issue_id":"hypercerts-atproto-documentation-w96.7","depends_on_id":"hypercerts-atproto-documentation-w96","type":"parent-child","created_at":"2026-03-05T19:58:48.630865221+06:00","created_by":"karma.gainforest.id"},{"issue_id":"hypercerts-atproto-documentation-w96.7","depends_on_id":"hypercerts-atproto-documentation-w96.4","type":"blocks","created_at":"2026-03-05T19:58:48.635719127+06:00","created_by":"karma.gainforest.id"}]} diff --git a/.beads/metadata.json b/.beads/metadata.json deleted file mode 100644 index c787975e..00000000 --- a/.beads/metadata.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "database": "beads.db", - "jsonl_export": "issues.jsonl" -} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 807d5983..00000000 --- a/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ - -# Use bd merge for beads JSONL files -.beads/issues.jsonl merge=beads diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..a48c70f9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +# Opens a pull request when a new version of the Hypercerts packages is published. +# Merging it updates the generated lexicon schema tables and the design-system styles. +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: daily + allow: + - dependency-name: "@hypercerts-org/lexicon" + - dependency-name: "@hypercerts-org/ui-react" + commit-message: + prefix: chore + labels: [] diff --git a/.github/workflows/docs-ci.yml b/.github/workflows/docs-ci.yml index 4260c4a4..6cd2b912 100644 --- a/.github/workflows/docs-ci.yml +++ b/.github/workflows/docs-ci.yml @@ -7,20 +7,16 @@ on: paths: - '.github/workflows/docs*.yml' - 'docs-sources.yml' - - 'docs/remote-markdown.md' - 'components/**' - - 'lib/external-docs*.js' - - 'lib/generate-docs-fingerprint.js' - - 'lib/generate-external-docs-manifest.js' - - 'lib/generate-raw-pages.js' - - 'lib/generate-search-index.js' - - 'lib/compare-docs-fingerprint.js' + - 'lib/**' + - 'styles/**' - 'markdoc/**' - 'next.config.js' - 'pages/**' - 'test/**' - 'package.json' - - 'package-lock.json' + - 'pnpm-lock.yaml' + - 'pnpm-workspace.yaml' permissions: contents: read @@ -40,19 +36,25 @@ jobs: with: persist-credentials: false + - name: Setup pnpm + uses: pnpm/action-setup@v6.1.0 + - name: Setup Node uses: actions/setup-node@v6.4.0 with: node-version: 22 - cache: npm + cache: pnpm - name: Install dependencies - run: npm ci + run: pnpm install --frozen-lockfile - name: Run tests - run: npm test + run: pnpm test - name: Build static documentation - run: npm run build + run: pnpm run build env: GITHUB_TOKEN: ${{ github.token }} + + - name: Check internal links + run: pnpm run check:links diff --git a/.github/workflows/docs-refresh.yml b/.github/workflows/docs-refresh.yml index 88f034fb..1206e3d9 100644 --- a/.github/workflows/docs-refresh.yml +++ b/.github/workflows/docs-refresh.yml @@ -34,17 +34,20 @@ jobs: with: persist-credentials: false + - name: Setup pnpm + uses: pnpm/action-setup@v6.1.0 + - name: Setup Node uses: actions/setup-node@v6.4.0 with: node-version: 22 - cache: npm + cache: pnpm - name: Install dependencies - run: npm ci + run: pnpm install --frozen-lockfile - name: Generate current docs fingerprint - run: npm run docs:fingerprint -- --output current-docs-fingerprint.json + run: pnpm run docs:fingerprint --output current-docs-fingerprint.json env: DOCS_SOURCE_TOKEN: ${{ secrets.DOCS_SOURCE_TOKEN }} GITHUB_TOKEN: ${{ github.token }} diff --git a/.gitignore b/.gitignore index 06937bad..3bd32a35 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ current-docs-fingerprint.json deployed-docs-fingerprint.json lib/external-docs-content.json lib/lastUpdated.json +lib/release-catalog.json diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index fc5058d2..00000000 --- a/AGENTS.md +++ /dev/null @@ -1,61 +0,0 @@ -# Agent Instructions - -## Git Workflow — Branch + PR (MANDATORY) - -**NEVER commit directly to `main`.** All work goes through feature branches and pull requests. - -### Starting work - -```bash -# 1. Start from up-to-date main -git checkout main && git pull - -# 2. Create a feature branch -git checkout -b -``` - -### Committing and pushing - -```bash -# Commit your changes on the feature branch -git add -git commit -m "" - -# Push the branch to remote -git push -u origin -``` - -### Creating the PR - -After pushing, create a pull request targeting `main`: - -```bash -gh pr create --title "" --body "" --base main -``` - -## Landing the Plane (Session Completion) - -**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds. - -**MANDATORY WORKFLOW:** - -1. **Run quality gates** (if code changed) - Tests, linters, builds -2. **PUSH TO REMOTE** - This is MANDATORY: - ```bash - git push - git status # MUST show "up to date with origin" - ``` -3. **Create PR if not already done** - Every branch needs a PR: - ```bash - gh pr create --title "" --body "<body>" --base main - ``` -4. **Verify** - All changes committed, pushed, and PR created -5. **Hand off** - Provide context for next session - -**CRITICAL RULES:** -- NEVER commit directly to `main` — always use a feature branch -- NEVER force push to `main` -- Work is NOT complete until `git push` succeeds AND a PR exists -- NEVER stop before pushing - that leaves work stranded locally -- NEVER say "ready to push when you are" - YOU must push -- If push fails, resolve and retry until it succeeds diff --git a/README.md b/README.md new file mode 100644 index 00000000..d5448b74 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# Hypercerts documentation + +The source of [docs.hypercerts.org](https://docs.hypercerts.org), the documentation for building on Hypercerts. + +Hypercerts is an open protocol for describing work and the trust around it: who did what, the evidence behind it, who reviewed or endorsed it, and who funded it. The records live on [AT Protocol](https://atproto.com), in repositories their authors control, so any application can read and build on them. For the high-level introduction, see [hypercerts.org](https://hypercerts.org). + +## What the documentation covers + +- **Guide**: the concepts, from why Hypercerts builds on AT Protocol to how work, evidence, evaluations, and funding connect. +- **Client Integration**: how to build an application on Hypercerts. +- **Reference**: the record schemas (Lexicons), the API and SDK, and the services that make up the stack. +- **Changes**: protocol releases and the versions of each component. + +## Where things are + +| Path | Contents | +|---|---| +| `pages/` | The documentation pages, written in Markdown | +| `components/`, `styles/` | The site's React components and styles | +| `markdoc/` | Custom Markdown tags used in pages, such as cards, callouts, and diagrams | +| `lib/` | Navigation and the scripts that generate search, schema tables, and release data at build time | +| `test/` | Tests for those scripts | +| `docs/` | Notes for maintainers, listed below | + +The site is built with [Next.js](https://nextjs.org) and [Markdoc](https://markdoc.dev), and follows the Hypercerts design system. + +## Run it locally + +Requires Node.js 22 or later and [pnpm](https://pnpm.io). + +```bash +pnpm install +pnpm run dev +``` + +Then open [http://localhost:3000](http://localhost:3000). + +Before opening a pull request: + +```bash +pnpm test +pnpm run build +pnpm run check:links +``` + +## Contributing + +All pages are written in this repository and changed through pull requests to `main`. Write about the current state of the protocol and its services; release history belongs in changelogs. + +Two notes for maintainers: + +- [How the documentation is organized](docs/information-architecture.md): the sections, the structure of Lexicon and service pages, the writing rules, who owns what, and **what to update by hand when a Lexicon version or a service is released**. +- [Build-time changelog imports](docs/remote-markdown.md): how component changelogs and version badges are fetched, and how protocol releases are published. + +## Related repositories + +- [hypercerts-lexicon](https://github.com/hypercerts-org/hypercerts-lexicon): the record schemas +- [certified-group-service](https://github.com/hypercerts-org/certified-group-service), [hypercerts-relay](https://github.com/hypercerts-org/hypercerts-relay), [hypercerts-feed-service](https://github.com/hypercerts-org/hypercerts-feed-service): services in the stack +- [certified-app](https://github.com/hypercerts-org/certified-app): the Certified account app diff --git a/components/AccountRecordsDiagram.js b/components/AccountRecordsDiagram.js new file mode 100644 index 00000000..d079cd11 --- /dev/null +++ b/components/AccountRecordsDiagram.js @@ -0,0 +1,151 @@ +import React from 'react'; + +/** Accounts shown in the left column, each with the records its repository holds. */ +const ACCOUNTS = [ + { + name: 'Project', + did: 'did:plc:project', + y: 44, + records: [ + { label: 'Profile', type: 'profile' }, + { label: 'Activity claim', type: 'activity' }, + { label: 'Progress update', type: 'attachment' }, + ], + }, + { name: 'Evaluator', did: 'did:plc:evaluator', y: 202, records: [{ label: 'Evaluation', type: 'evaluation' }] }, + { name: 'Funder', did: 'did:plc:funder', y: 292, records: [{ label: 'Funding receipt', type: 'funding receipt' }] }, +]; + +const APPS = [ + { name: 'Funding platform', y: 44 }, + { name: 'Evaluation tool', y: 150 }, + { name: 'Directory', y: 256 }, +]; + +const REPO_X = 24; +const REPO_W = 252; +const CHIP_X = REPO_X + 14; +const CHIP_W = REPO_W - 28; +const CHIP_H = 24; +const HEADER_H = 34; +const APP_X = 540; +const APP_W = 196; +const APP_H = 86; + +const repoHeight = (account) => HEADER_H + account.records.length * (CHIP_H + 6) + 8; +const chipY = (account, index) => account.y + HEADER_H + index * (CHIP_H + 6); +const repoMidY = (account) => account.y + repoHeight(account) / 2; + +/** The activity chip that the evaluation and funding receipt link to. */ +const ACTIVITY_Y = chipY(ACCOUNTS[0], 1) + CHIP_H / 2; + +/** + * Show that each account keeps its own records, links connect them, and apps read across accounts through the network. + * Used on the Why AT Protocol? Guide page. + */ +export function AccountRecordsDiagram() { + const evaluationY = chipY(ACCOUNTS[1], 0) + CHIP_H / 2; + const receiptY = chipY(ACCOUNTS[2], 0) + CHIP_H / 2; + + return ( + <figure className="guide-diagram"> + <div className="guide-diagram-scroll"> + <svg className="guide-diagram-svg" viewBox="0 0 760 426" role="img" aria-labelledby="account-records-title account-records-desc"> + <title id="account-records-title">Records live with their authors + + A project, an evaluator, and a funder each keep records in their own repository under their own DID. The evaluation and the funding receipt link to the project's activity claim. A relay and an indexer collect records from all three repositories, and a funding platform, an evaluation tool, and a directory each read across them. The project can move its repository to another host and keep its DID, records, and incoming links. + + + + + + + + + + + Accounts own their records + The network collects them + Apps read across accounts + + {ACCOUNTS.map((account) => ( + + + {account.name} + {account.did} + {account.records.map((record, index) => ( + + + {record.label} + {record.type} + + ))} + + ))} + + + + links + + + Relay + + Indexer + + + {ACCOUNTS.map((account, index) => { + const startY = repoMidY(account); + const endY = 151 + index * 8; + return ( + + ); + })} + + {APPS.map((app, index) => { + const startY = 226 + index * 8; + const endY = app.y + APP_H / 2 + 6; + return ( + + + + + {[0, 1, 2].map((dot) => ( + + ))} + {app.name} + + + + ); + })} + + + Portability + + Host A + + moves + + Host B + The project changes hosts. Its DID, its records, + and the links others made to them stay the same. + + +
+ Each account keeps its own records. Links connect them, and any app can read across all of them. +
+ + ); +} diff --git a/components/Breadcrumbs.js b/components/Breadcrumbs.js index 0270ef53..5244a186 100644 --- a/components/Breadcrumbs.js +++ b/components/Breadcrumbs.js @@ -1,13 +1,13 @@ -import Link from "next/link"; +import { Breadcrumb } from "@hypercerts-org/ui-react"; import { useRouter } from "next/router"; import { navigation } from "../lib/navigation"; function findBreadcrumbs(nav, targetPath, trail = []) { for (const item of nav) { - if (item.section) { + if (item.section || item.group) { const result = findBreadcrumbs(item.children || [], targetPath, [ ...trail, - { title: item.section }, + { title: item.section || item.group }, ]); if (result) return result; } else { @@ -37,31 +37,13 @@ export function Breadcrumbs() { if (crumbs.length <= 1) return null; - return ( - - ); + const items = [ + { label: 'Docs', href: '/' }, + ...crumbs.map((crumb, i) => ({ + label: crumb.title, + href: i < crumbs.length - 1 ? crumb.path : undefined, + })), + ]; + + return ; } diff --git a/components/Callout.js b/components/Callout.js index ed13de08..a685a751 100644 --- a/components/Callout.js +++ b/components/Callout.js @@ -1,12 +1,13 @@ import React from 'react'; +import { Banner } from '@hypercerts-org/ui-react'; -export function Callout({ type = 'info', title, children }) { - const typeClass = `callout--${type}`; +const TONES = { info: 'info', note: 'info', warning: 'warning', danger: 'danger', success: 'success' }; +/** A callout in page content, rendered with the design system's Banner. */ +export function Callout({ type = 'info', title, children }) { return ( -
- {title &&
{title}
} -
{children}
-
+ + {children} + ); } diff --git a/components/CardLink.js b/components/CardLink.js index c860124c..79146a6c 100644 --- a/components/CardLink.js +++ b/components/CardLink.js @@ -1,6 +1,7 @@ import Link from "next/link"; +import { Badge } from "@hypercerts-org/ui-react"; -export function CardLink({ title, href, icon, children }) { +export function CardLink({ title, href, icon, badge, children }) { return ( {icon && ( @@ -10,6 +11,7 @@ export function CardLink({ title, href, icon, children }) { )} {title} + {badge && {badge}} {children && {children}} diff --git a/components/CodeBlock.js b/components/CodeBlock.js index 49cb1774..ed543c4b 100644 --- a/components/CodeBlock.js +++ b/components/CodeBlock.js @@ -1,28 +1,47 @@ import { useState } from 'react'; -import { Highlight, themes } from 'prism-react-renderer'; +import { Highlight } from 'prism-react-renderer'; -const LANGUAGE_META = { - typescript: { label: 'TypeScript', color: '#3178c6' }, - javascript: { label: 'JavaScript', color: '#f0db4f' }, - bash: { label: 'Terminal', color: '#4eaa25' }, - shell: { label: 'Terminal', color: '#4eaa25' }, - json: { label: 'JSON', color: '#a0a0a0' }, - jsx: { label: 'JSX', color: '#61dafb' }, - tsx: { label: 'TSX', color: '#3178c6' }, - css: { label: 'CSS', color: '#264de4' }, - html: { label: 'HTML', color: '#e34c26' }, - markdown: { label: 'Markdown', color: '#a0a0a0' }, +const LANGUAGE_LABELS = { + typescript: 'TypeScript', + ts: 'TypeScript', + javascript: 'JavaScript', + js: 'JavaScript', + bash: 'Terminal', + shell: 'Terminal', + json: 'JSON', + jsx: 'JSX', + tsx: 'TSX', + css: 'CSS', + html: 'HTML', + markdown: 'Markdown', + text: 'Text', }; -function getLangMeta(language) { +/** + * Syntax colours from the design system's text layer: ink, muted grey, and the one accent. + * The values are CSS variables, so the theme follows light and dark mode. + */ +const codeTheme = { + plain: { color: 'var(--color-code-text)', backgroundColor: 'transparent' }, + styles: [ + { types: ['comment', 'prolog', 'doctype', 'cdata'], style: { color: 'var(--color-code-muted)', fontStyle: 'italic' } }, + { types: ['punctuation', 'operator'], style: { color: 'var(--color-code-muted)' } }, + { types: ['string', 'char', 'attr-value', 'template-string', 'inserted'], style: { color: 'var(--color-accent)' } }, + { types: ['keyword', 'tag', 'selector', 'important', 'atrule', 'builtin'], style: { color: 'var(--color-code-text)', fontWeight: '600' } }, + { types: ['number', 'boolean', 'constant', 'symbol', 'deleted'], style: { color: 'var(--color-accent)' } }, + { types: ['property', 'attr-name', 'function', 'class-name', 'variable'], style: { color: 'var(--color-code-text)' } }, + ], +}; + +function getLangLabel(language) { const key = (language || '').toLowerCase(); - return LANGUAGE_META[key] || { label: language || 'Code', color: '#6b7280' }; + return LANGUAGE_LABELS[key] || language || 'Code'; } export function CodeBlock({ content, language, children }) { const [copied, setCopied] = useState(false); const code = (content || children || '').replace(/\n$/, ''); - const meta = getLangMeta(language); + const label = getLangLabel(language); const handleCopy = async () => { try { @@ -44,13 +63,7 @@ export function CodeBlock({ content, language, children }) { return (
-
- - {meta.label} -
+ {label}
- + {({ tokens, getLineProps, getTokenProps }) => (
             
diff --git a/components/Column.js b/components/Column.js
deleted file mode 100644
index f89177eb..00000000
--- a/components/Column.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import React from 'react';
-
-export function Column({ children }) {
-  return 
{children}
; -} diff --git a/components/Columns.js b/components/Columns.js deleted file mode 100644 index 87cf1bea..00000000 --- a/components/Columns.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -export function Columns({ children }) { - return
{children}
; -} diff --git a/components/CopyRawButton.js b/components/CopyRawButton.js index 801c362e..c49f36e4 100644 --- a/components/CopyRawButton.js +++ b/components/CopyRawButton.js @@ -1,5 +1,6 @@ import { useState } from 'react'; import { useRouter } from 'next/router'; +import { Button } from '@hypercerts-org/ui-react'; /** * Map a documentation route to the generated local Markdown artifact used by page actions. @@ -55,12 +56,7 @@ export function CopyRawButton() { return (
- - + +
); } diff --git a/components/DocsHero.js b/components/DocsHero.js new file mode 100644 index 00000000..6c57083e --- /dev/null +++ b/components/DocsHero.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { Eyebrow, Heading } from '@hypercerts-org/ui-react'; + +/** + * The documentation landing hero: eyebrow, a heading that turns from roman to italic, + * and a standfirst, on the warm ground with one guilloche cropped past the edge. + */ +export function DocsHero({ eyebrow, title, turn, children }) { + return ( +
+ +
+ {eyebrow && {eyebrow}} + + {title} + +
{children}
+
+
+ ); +} diff --git a/components/DocsSection.js b/components/DocsSection.js new file mode 100644 index 00000000..c37b3b76 --- /dev/null +++ b/components/DocsSection.js @@ -0,0 +1,35 @@ +import Link from 'next/link'; + +const icons = { + guide: , + integration: , + reference: <>, + changes: <>, +}; + +/** A section introduction and its curated links on the documentation landing page. */ +export function DocsSection({ title, href, icon, description, layout = 'cards', children }) { + const headingId = `docs-${icon}`; + + return ( +
+
+ +

+ + {title} + + +

+

{description}

+
+
{children}
+
+ ); +} diff --git a/components/DotPattern.js b/components/DotPattern.js deleted file mode 100644 index ee9d426f..00000000 --- a/components/DotPattern.js +++ /dev/null @@ -1,24 +0,0 @@ -export function DotPattern({ className }) { - return ( - - ); -} diff --git a/components/Figure.js b/components/Figure.js deleted file mode 100644 index 19da95bb..00000000 --- a/components/Figure.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; - -export function Figure({ src, alt = '', caption }) { - return ( -
- {alt} - {caption &&
{caption}
} -
- ); -} diff --git a/components/HeroBanner.js b/components/HeroBanner.js deleted file mode 100644 index 595add17..00000000 --- a/components/HeroBanner.js +++ /dev/null @@ -1,18 +0,0 @@ -import { DotPattern } from "./DotPattern"; - -export function HeroBanner({ title, children, ctaHref, ctaText }) { - return ( -
- -
- {title &&

{title}

} - {children &&
{children}
} - {ctaHref && ( - - {ctaText || "Get Started"} - - )} -
-
- ); -} diff --git a/components/LastUpdated.js b/components/LastUpdated.js index 75f8aa3c..a60db19e 100644 --- a/components/LastUpdated.js +++ b/components/LastUpdated.js @@ -2,10 +2,10 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; import lastUpdated from '../lib/lastUpdated.json'; -export function LastUpdated() { +export function LastUpdated({ hidden = false }) { const router = useRouter(); const currentPath = router.asPath.split('#')[0].split('?')[0]; - const date = lastUpdated[currentPath]; + const date = hidden ? undefined : lastUpdated[currentPath]; useEffect(() => { // Always remove any stale last-updated element from a previous route, diff --git a/components/Layout.js b/components/Layout.js index 1aab6980..3d3cdaf4 100644 --- a/components/Layout.js +++ b/components/Layout.js @@ -4,7 +4,7 @@ import Link from 'next/link'; import { useRouter } from 'next/router'; import { Sidebar } from './Sidebar'; import { TableOfContents } from './TableOfContents'; -import { getPrevNext } from '../lib/navigation'; +import { getNavigationSection, getPrevNext, navigation } from '../lib/navigation'; import { LastUpdated } from './LastUpdated'; import { Breadcrumbs } from './Breadcrumbs'; import { ThemeToggle } from './ThemeToggle'; @@ -18,12 +18,27 @@ const DEFAULT_DESCRIPTION = 'Documentation for the Hypercerts Protocol — structured, verifiable records of impact work built on AT Protocol.'; const OG_IMAGE = `${SITE_URL}/images/hypercerts_logo.png`; +function SectionLinks({ currentSection }) { + return navigation.map(({ section, children }) => ( + + {section} + + )); +} + export default function Layout({ children, frontmatter }) { const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [searchOpen, setSearchOpen] = useState(false); const router = useRouter(); const currentPath = router.asPath.split('#')[0].split('?')[0]; + const isLanding = currentPath === '/'; + const currentSection = getNavigationSection(currentPath)?.section; const { prev, next } = getPrevNext(currentPath); const title = frontmatter?.title; @@ -35,7 +50,7 @@ export default function Layout({ children, frontmatter }) { const jsonLd = { '@context': 'https://schema.org', - '@type': 'TechArticle', + '@type': isLanding ? 'WebPage' : 'TechArticle', headline: title || SITE_NAME, description, url: canonicalUrl, @@ -61,6 +76,10 @@ export default function Layout({ children, frontmatter }) { } }, []); + useEffect(() => { + setSidebarOpen(false); + }, [currentPath]); + useEffect(() => { const handler = (e) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { @@ -91,7 +110,7 @@ export default function Layout({ children, frontmatter }) { {/* Open Graph */} - + @@ -120,35 +139,27 @@ export default function Layout({ children, frontmatter }) { -
+
Skip to content
- +
+ {!isLanding && ( + + )} - Certified - Certified -
-
+
-
- setSidebarOpen(false)} - collapsed={sidebarCollapsed} - onToggleCollapse={toggleCollapsed} - /> + {isLanding && ( + + )} -
- - {frontmatter && } - +
+ {!isLanding && ( + setSidebarOpen(false)} + collapsed={sidebarCollapsed} + onToggleCollapse={toggleCollapsed} + /> + )} + +
+ {!isLanding && ( + <> + + {frontmatter && } + + )} + {/* Mounted on the landing page too, so it can remove the line it appended on the page before */} +
- + {!isLanding && ( + + )}
setSearchOpen(false)} /> diff --git a/components/SearchDialog.js b/components/SearchDialog.js index b451eff9..f0cc0af4 100644 --- a/components/SearchDialog.js +++ b/components/SearchDialog.js @@ -23,12 +23,12 @@ function getSnippet(body, query, contextChars = 60) { // Paths for the curated quick links shown in the empty state const QUICK_LINK_PATHS = [ - '/getting-started/quickstart', + '/guide', + '/client-integration', + '/reference', + '/releases', '/core-concepts/what-is-hypercerts', '/core-concepts/hypercerts-core-data-model', - '/tools/scaffold', - '/architecture/overview', - '/reference/glossary', ]; export function SearchDialog({ isOpen, onClose }) { diff --git a/components/SharedLanguageDiagram.js b/components/SharedLanguageDiagram.js new file mode 100644 index 00000000..c800b9e2 --- /dev/null +++ b/components/SharedLanguageDiagram.js @@ -0,0 +1,97 @@ +import React from 'react'; + +const CARD_W = 180; +const CARD_H = 74; +const ACTIVITY = { x: 290, y: 150, w: 180, h: 88 }; +const LEFT_X = 16; +const RIGHT_X = 564; + +/** Records around the activity: each is published separately and links to the activity. */ +const RECORDS = [ + { title: 'Energy project', type: 'collection (project)', by: 'the project', x: ACTIVITY.x, y: 20, relation: 'includes' }, + { title: 'Installation report', type: 'attachment', by: 'the project', x: LEFT_X, y: 70, relation: 'documents' }, + { title: 'Energy production', type: 'measurement', by: 'a monitoring partner', x: RIGHT_X, y: 70, relation: 'measures' }, + { title: 'Funding receipt', type: 'funding receipt', by: 'a funder', x: LEFT_X, y: 294, relation: 'funds' }, + { title: 'Specialist evaluation', type: 'evaluation', by: 'a specialist', x: RIGHT_X, y: 294, relation: 'assesses' }, +]; + +/** Approximate pill width for a short relation label at 11px. */ +const pillWidth = (label) => label.length * 6.4 + 16; + +/** + * Connect a record card to the activity card. + * Returns the path and its midpoint, where the relation label sits. + */ +function connector(record) { + const activityMidX = ACTIVITY.x + ACTIVITY.w / 2; + + if (record.x === ACTIVITY.x) { + const startY = record.y + CARD_H; + const endY = ACTIVITY.y - 2; + return { d: `M${activityMidX} ${startY} V${endY}`, midX: activityMidX, midY: (startY + endY) / 2 }; + } + + const fromLeft = record.x < ACTIVITY.x; + const cardMidY = record.y + CARD_H / 2; + const startX = fromLeft ? record.x + CARD_W : record.x; + const endX = fromLeft ? ACTIVITY.x - 2 : ACTIVITY.x + ACTIVITY.w + 2; + const endY = cardMidY < ACTIVITY.y + ACTIVITY.h / 2 ? ACTIVITY.y + 24 : ACTIVITY.y + ACTIVITY.h - 24; + const bendX = (startX + endX) / 2; + return { + d: `M${startX} ${cardMidY} C ${bendX} ${cardMidY}, ${bendX} ${endY}, ${endX} ${endY}`, + midX: bendX, + midY: (cardMidY + endY) / 2, + }; +} + +/** + * Show one activity claim with the separate records that describe, measure, assess, fund, and group it. + * Used on the A Shared Language Guide page. + */ +export function SharedLanguageDiagram() { + return ( +
+
+ + Records around one piece of work + + A solar installation activity claim, published by the project, sits at the center. A community energy project collection includes it. An installation report from the project documents it. An energy production measurement from a monitoring partner measures it. A specialist evaluation assesses it. A funding receipt from a funder records support for it. Each record is separate and links to the activity. + + + + + + + + {RECORDS.map((record) => { + const line = connector(record); + return ( + + + + {record.relation} + + ); + })} + + {RECORDS.map((record) => ( + + + {record.title} + {record.type} + by {record.by} + + ))} + + + Activity claim + Solar installation + by the project + +
+
+ Each box is a separate record, published by its own account. Links point from each record to the work it concerns. +
+
+ ); +} diff --git a/components/Sidebar.js b/components/Sidebar.js index 909cf3ec..6a770e92 100644 --- a/components/Sidebar.js +++ b/components/Sidebar.js @@ -1,7 +1,7 @@ -import { useState, useEffect, useRef } from 'react'; +import { useEffect, useRef } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { navigation } from '../lib/navigation'; +import { getNavigationSection, navigation } from '../lib/navigation'; function isActive(path, currentPath) { return path === currentPath; @@ -15,73 +15,44 @@ function isChildActive(item, currentPath) { return false; } -function NavItem({ item, currentPath, depth = 0 }) { +/** + * Render one navigation entry. An entry with children is a category: its row links to the category page, + * and its children are shown while that page or one of its descendants is active. + */ +function NavItem({ item, currentPath }) { const hasChildren = item.children && item.children.length > 0; const active = item.path && isActive(item.path, currentPath); const childActive = hasChildren && isChildActive(item, currentPath); - const [expanded, setExpanded] = useState(childActive || active); + const expanded = hasChildren && (active || childActive); + const className = `sidebar-link${active ? ' sidebar-link-active' : ''}${childActive && !active ? ' sidebar-link-parent-active' : ''}`; - useEffect(() => { - if (childActive || active) { - setExpanded(true); - } - }, [childActive, active]); + const content = ( + <> + + {item.title} + {item.badge && {item.badge}} + + {hasChildren && ( + + )} + + ); return (
  • -
    - {item.path ? ( - - {item.title} - - ) : ( - - {item.title} - - )} - {hasChildren && ( - - )} -
    - {hasChildren && expanded && ( + {item.path ? ( + + {content} + + ) : ( + {content} + )} + {expanded && (
      {item.children.map((child) => ( - + ))}
    )} @@ -89,6 +60,20 @@ function NavItem({ item, currentPath, depth = 0 }) { ); } +/** Render a titled subsection inside a documentation section without adding a nesting level. */ +function NavGroup({ item, currentPath }) { + return ( +
  • +

    {item.group}

    +
      + {item.children.map((child) => ( + + ))} +
    +
  • + ); +} + function NavSection({ item, currentPath }) { if (item.section) { return ( @@ -96,11 +81,9 @@ function NavSection({ item, currentPath }) {

    {item.section}

      {item.children.map((child) => ( - + child.group + ? + : ))}
    @@ -112,6 +95,8 @@ function NavSection({ item, currentPath }) { export function Sidebar({ isOpen, onClose, collapsed, onToggleCollapse }) { const router = useRouter(); const currentPath = router.asPath.split('#')[0].split('?')[0]; + const currentSection = getNavigationSection(currentPath); + const visibleNavigation = currentSection ? [currentSection] : navigation; const onCloseRef = useRef(onClose); useEffect(() => { @@ -169,8 +154,26 @@ export function Sidebar({ isOpen, onClose, collapsed, onToggleCollapse }) { {/* Nav content — hidden when collapsed */}
    +
    + Sections + {navigation.map((section) => { + const overview = section.children.find((child) => child.path); + if (!overview) return null; + + const active = section.section === currentSection?.section; + return ( + + {section.section} + + ); + })} +