Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .claude/launch.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "preview-dist",
"runtimeExecutable": "npx",
"runtimeArgs": ["vite", "preview", "--port", "4173", "--strictPort"],
"port": 4173
},
{
"name": "dev",
"runtimeExecutable": "npm",
Expand Down
66 changes: 66 additions & 0 deletions .github/workflows/deploy-gh-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Rollback deploy to GitHub Pages

# Emergency path only. The press runs on Cloudflare Pages
# (deploy.yml); this workflow re-publishes to the gh-pages branch by
# hand if Cloudflare is down or the cutover has to be reversed.
# Run it, then point kernel.chat DNS back at GitHub Pages.
on:
workflow_dispatch: {}

permissions:
contents: write
pages: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Design adherence — no raw hex in the editorial design-system layer
run: npm run lint:adherence

- name: Editorial law — artifact-first, no POPEYE naming, no emoji
run: npm run lint:editorial

- name: Type-check
run: npx tsc --noEmit

- name: Build sitemap from the issue registry
run: npm run sitemap

- name: Build
run: npm run build
env:
VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}
VITE_SUPABASE_KEY: ${{ secrets.VITE_SUPABASE_KEY }}
VITE_STRIPE_PUBLISHABLE_KEY: ${{ secrets.VITE_STRIPE_PUBLISHABLE_KEY }}
VITE_STRIPE_KERNEL_PRICE_ID: ${{ secrets.VITE_STRIPE_KERNEL_PRICE_ID }}
VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }}
VITE_POSTHOG_KEY: ${{ secrets.VITE_POSTHOG_KEY }}

- name: Build artifact index
run: npm run artifacts:index

- name: Include editorial artifacts
run: |
mkdir -p dist/artifacts
cp artifacts/*.html dist/artifacts/

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
cname: kernel.chat
enable_jekyll: false
19 changes: 10 additions & 9 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
name: Deploy to GitHub Pages
name: Deploy to Cloudflare Pages

on:
push:
branches: [main]
workflow_dispatch: {}

permissions:
contents: write
pages: write
contents: read

jobs:
deploy:
Expand All @@ -34,6 +33,9 @@ jobs:
- name: Type-check
run: npx tsc --noEmit

- name: Build sitemap from the issue registry
run: npm run sitemap

- name: Build
run: npm run build
env:
Expand All @@ -52,10 +54,9 @@ jobs:
mkdir -p dist/artifacts
cp artifacts/*.html dist/artifacts/

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
cname: kernel.chat
enable_jekyll: false
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=kernel-chat
29 changes: 29 additions & 0 deletions SCRATCHPAD.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,35 @@

> This file persists context between Claude Code sessions.

## Session 2026-07-20 — UX anti-pattern audit → real URLs (PR #62)

- Ran the design-QA audit against live kernel.chat. Editorial surface
healthy (no overflow, alt clean, reduced-motion complete, focus
visible). The real anti-patterns: (1) artifact index + 421 were
404 live (fixed by merging fix/editorial-enforcement → main, now
200); (2) every deep link served HTTP 404 → hash bounce; (3) the
outline:none / clickable-div debt all sits in UNROUTED legacy
surfaces — EnginePage is the parked chat product, not junk.
- Merge to main taught check-editorial.mjs the documented-exception
path: ARTIFACT_WAIVER ("no separate artifact edition" in rendered
colophon copy) — 427 uses it legitimately.
- **PR #62 (`feat/real-urls-cloudflare`)**, three commits:
1. createBrowserRouter + legacyHashRedirect.ts (module-scope, BEFORE
router creation — the router captures location at import time, a
boot-sequence shim renders the wrong route); _redirects; sitemap
generator (scripts/build-sitemap.mjs, 77 URLs); PostHog
capture_pageview 'history_change'; deploy.yml → Cloudflare Pages
(wrangler-action v3, project kernel-chat); deploy-gh-pages.yml
rollback; e2e path URLs; PUBLISHING.md §VII rewritten.
2. Cut nine dead pages (4,068 lines) incl. PricingPage; engine
tree parked, untouched.
3. A11y: back-to-top 98×13 → 122×44 (nowrap needed — 40px-wide
colophon column wraps padded labels); skip link → #feature-well.
- **BLOCKED on Isaac before merge:** create CF Pages project
`kernel-chat` + repo secrets CLOUDFLARE_API_TOKEN /
CLOUDFLARE_ACCOUNT_ID; DNS cutover after pages.dev verifies.
- Gates at branch head: tsc clean, 794/794 tests, 236KB gzip.

## Session 2026-07-20 (cont.) — ISSUE 427: THE MOAT IS REALITY

- Pressed ISSUE 427 · FEB 2027 from a reader-supplied transcript of
Expand Down
2 changes: 1 addition & 1 deletion e2e/tests/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ test.describe('Authentication', () => {
})

test('redirects unauthenticated users from protected routes', async ({ page }) => {
await page.goto('/#/admin')
await page.goto('/admin')
await page.waitForSelector('.ka-gate, .ka-landing, .engine-body', { timeout: 15000 })
// Should not show admin page
const admin = await page.$('.admin-page')
Expand Down
6 changes: 3 additions & 3 deletions e2e/tests/issue-415-close.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'

test.describe('ISSUE 415 — close primitive', () => {
test('reader can show more items, then stop, and gets a receipt', async ({ page }) => {
await page.goto('/#/issues/415')
await page.goto('/issues/415')
await expect(page.getByRole('button', { name: 'Show me one more' })).toBeVisible()
await expect(page.getByRole('button', { name: "I'll stop here" })).toBeVisible()

Expand All @@ -21,7 +21,7 @@ test.describe('ISSUE 415 — close primitive', () => {

test('mobile viewport renders both controls at equal size', async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 })
await page.goto('/#/issues/415')
await page.goto('/issues/415')
const more = page.getByRole('button', { name: 'Show me one more' })
const stop = page.getByRole('button', { name: "I'll stop here" })
await expect(more).toBeVisible()
Expand All @@ -36,7 +36,7 @@ test.describe('ISSUE 415 — close primitive', () => {
page.on('console', (msg) => {
if (msg.type() === 'error') errors.push(msg.text())
})
await page.goto('/#/issues/415')
await page.goto('/issues/415')
await page.getByRole('button', { name: 'Show me one more' }).click()
await page.getByRole('button', { name: "I'll stop here" }).click()
expect(errors).toEqual([])
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"lint:adherence": "node scripts/check-adherence.mjs",
"lint:editorial": "node scripts/check-editorial.mjs",
"artifacts:index": "node scripts/build-artifact-index.mjs",
"sitemap": "node scripts/build-sitemap.mjs",
"preview": "vite preview",
"test": "vitest run",
"test:e2e": "playwright test",
Expand Down
4 changes: 4 additions & 0 deletions public/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Cloudflare Pages SPA fallback. Real files in the upload manifest
# (artifacts/*.html, sitemap.xml, issue plates) are served before this
# rule applies, so only unmatched paths fall through to the app shell.
/* /index.html 200
Loading
Loading