diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8836ac9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: + pull_request: + paths-ignore: + - 'README.md' + - 'CLAUDE.md' + - 'agent.md' + push: + branches: + - main + paths-ignore: + - 'README.md' + - 'CLAUDE.md' + - 'agent.md' + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + NEXT_TELEMETRY_DISABLED: 1 + +concurrency: + group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.9 + + - name: Restore Next.js cache + uses: actions/cache@v5 + with: + path: .next/cache + key: ${{ runner.os }}-nextjs-${{ hashFiles('bun.lock', 'package.json') }}-${{ hashFiles('pages/**/*', 'components/**/*', 'lib/**/*', 'data/**/*', 'styles/**/*', 'content/**/*', 'public/**/*', 'next.config.js', 'postcss.config.js', 'tailwind.config.js') }} + restore-keys: | + ${{ runner.os }}-nextjs-${{ hashFiles('bun.lock', 'package.json') }}- + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Build site + run: bun run build diff --git a/components/research/Hypothesis.jsx b/components/research/Hypothesis.jsx new file mode 100644 index 0000000..cc573e1 --- /dev/null +++ b/components/research/Hypothesis.jsx @@ -0,0 +1,39 @@ +export default function Hypothesis({ + name, + constraint, + source, + children, + border = true +}) { + return ( +
  • +
    + + + + + {name} + {constraint} + +
    +

    + + {source} + +

    +

    {children}

    +
    +
    +
  • + ) +} diff --git a/components/research/SafeGuarantee.jsx b/components/research/SafeGuarantee.jsx new file mode 100644 index 0000000..d9a1e05 --- /dev/null +++ b/components/research/SafeGuarantee.jsx @@ -0,0 +1,84 @@ +import { useState, useEffect, useRef } from 'react' + +const FORMAL_INVARIANTS = [ + '\u2200 key, next(key) \u2260 0 \u2192 reachable(SENTINEL, key)', + '\u2200 key \u2260 0, next(key) \u2260 0 \u2194 reachable(SENTINEL, key)', + '\u2200 a b, reachable(a, b) \u2227 reachable(b, a) \u2192 a = b' +] + +export default function SafeGuarantee() { + const [showEnglish, setShowEnglish] = useState(true) + const timerRef = useRef(null) + + useEffect(() => { + timerRef.current = setTimeout(() => setShowEnglish(false), 5000) + return () => clearTimeout(timerRef.current) + }, []) + + const handleToggle = () => { + if (timerRef.current) { + clearTimeout(timerRef.current) + timerRef.current = null + } + setShowEnglish((prev) => !prev) + } + + return ( +
    + + +
    +
    + {FORMAL_INVARIANTS.map((formal, i) => ( + + {formal} + + ))} +
    +
    +

    + The owners mapping forms a proper loop-free circular linked list. +

    +
    +
    +
    + ) +} diff --git a/components/research/TikZDiagram.jsx b/components/research/TikZDiagram.jsx new file mode 100644 index 0000000..d20b2fb --- /dev/null +++ b/components/research/TikZDiagram.jsx @@ -0,0 +1,50 @@ +import { useEffect, useRef } from 'react' + +export default function TikZDiagram({ tikz, className = '' }) { + const ref = useRef(null) + + useEffect(() => { + const container = ref.current + if (!container) return + + // Add TikZJax fonts CSS if not already present + if (!document.querySelector('link[href*="tikzjax.com"]')) { + const link = document.createElement('link') + link.rel = 'stylesheet' + link.type = 'text/css' + link.href = 'https://tikzjax.com/v1/fonts.css' + document.head.appendChild(link) + } + + // Create the