publish docs index and github pages workflow

This commit is contained in:
dirtydishes 2026-05-19 14:59:58 -04:00
parent 276d48950d
commit 4bacf2c2f8
5 changed files with 1311 additions and 0 deletions

View file

@ -14,6 +14,7 @@
{"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-tqk","title":"publish docs/ to github pages with navigable index","description":"Set up docs deployment so repository docs are published to dirtydishes.github.io/islandflow/docs with a nicer, browsable experience than a raw file listing.","status":"closed","priority":2,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-19T18:56:02Z","created_by":"dirtydishes","updated_at":"2026-05-19T18:59:55Z","started_at":"2026-05-19T18:56:04Z","closed_at":"2026-05-19T18:59:55Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-lm6","title":"Clarify repo turn documentation scope","description":"Update AGENTS.md so repository turn documentation clearly uses repo-local docs/turns and impeccable styling, without inheriting global non-repo computer-task styling.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-19T12:05:07Z","created_by":"dirtydishes","updated_at":"2026-05-19T12:06:12Z","started_at":"2026-05-19T12:05:14Z","closed_at":"2026-05-19T12:06:12Z","close_reason":"Verified AGENTS.md now scopes repo turn docs to docs/turns and makes impeccable the styling authority; added turn documentation.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-lm6","title":"Clarify repo turn documentation scope","description":"Update AGENTS.md so repository turn documentation clearly uses repo-local docs/turns and impeccable styling, without inheriting global non-repo computer-task styling.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-19T12:05:07Z","created_by":"dirtydishes","updated_at":"2026-05-19T12:06:12Z","started_at":"2026-05-19T12:05:14Z","closed_at":"2026-05-19T12:06:12Z","close_reason":"Verified AGENTS.md now scopes repo turn docs to docs/turns and makes impeccable the styling authority; added turn documentation.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-6iq","title":"Update README for current project state","description":"Resolve README merge conflicts and document the current project state, including the smart money classification taxonomy, Next.js update, and deployment workflow changes.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-19T11:37:24Z","created_by":"dirtydishes","updated_at":"2026-05-19T11:40:01Z","started_at":"2026-05-19T11:37:31Z","closed_at":"2026-05-19T11:40:01Z","close_reason":"README conflict resolved and current project state documented, including smart-money taxonomy, Next.js update, and deployment workflow.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-6iq","title":"Update README for current project state","description":"Resolve README merge conflicts and document the current project state, including the smart money classification taxonomy, Next.js update, and deployment workflow changes.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-19T11:37:24Z","created_by":"dirtydishes","updated_at":"2026-05-19T11:40:01Z","started_at":"2026-05-19T11:37:31Z","closed_at":"2026-05-19T11:40:01Z","close_reason":"README conflict resolved and current project state documented, including smart-money taxonomy, Next.js update, and deployment workflow.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-lib","title":"Upgrade apps/web to Next.js 16.2.6","description":"Upgrade the web app dependency stack to Next.js 16.2.6 with React 19, refresh Bun and mirrored Docker workspace lockfiles, keep runtime behavior unchanged, fix any focused web test fallout, validate the web build and targeted route tests, and document the completed work.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-19T11:04:51Z","created_by":"dirtydishes","updated_at":"2026-05-19T11:31:23Z","started_at":"2026-05-19T11:04:57Z","closed_at":"2026-05-19T11:31:23Z","close_reason":"Upgraded apps/web to Next.js 16.2.6 with React 19, refreshed Bun lockfiles including the Docker workspace mirror, fixed the React 19 nullable ref type issue, and validated the web build, focused tests, Docker workspace sync, and route smoke checks.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-lib","title":"Upgrade apps/web to Next.js 16.2.6","description":"Upgrade the web app dependency stack to Next.js 16.2.6 with React 19, refresh Bun and mirrored Docker workspace lockfiles, keep runtime behavior unchanged, fix any focused web test fallout, validate the web build and targeted route tests, and document the completed work.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-19T11:04:51Z","created_by":"dirtydishes","updated_at":"2026-05-19T11:31:23Z","started_at":"2026-05-19T11:04:57Z","closed_at":"2026-05-19T11:31:23Z","close_reason":"Upgraded apps/web to Next.js 16.2.6 with React 19, refreshed Bun lockfiles including the Docker workspace mirror, fixed the React 19 nullable ref type issue, and validated the web build, focused tests, Docker workspace sync, and route smoke checks.","dependency_count":0,"dependent_count":0,"comment_count":0}

56
.github/workflows/docs-pages.yml vendored Normal file
View file

@ -0,0 +1,56 @@
name: Publish Docs
on:
push:
branches:
- main
paths:
- "docs/**"
- "scripts/generate-docs-index.mjs"
- ".github/workflows/docs-pages.yml"
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure Pages
uses: actions/configure-pages@v5
- name: Build docs index
run: node scripts/generate-docs-index.mjs
- name: Prepare static site payload
run: |
mkdir -p site/docs
cp -R docs/. site/docs/
printf '%s\n' '<!doctype html><meta charset="utf-8"><meta http-equiv="refresh" content="0; url=./docs/"><title>Islandflow Docs</title><a href="./docs/">Continue to docs</a>' > site/index.html
touch site/.nojekyll
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: site
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

638
docs/index.html Normal file
View file

@ -0,0 +1,638 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Islandflow Docs</title>
<style>
:root {
--bg: #f4f6f8;
--surface: #ffffff;
--surface-muted: #e8edf2;
--text: #1a2433;
--muted: #5b6a80;
--border: #ccd5df;
--accent: #0f766e;
--accent-soft: #d1fae5;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: "Avenir Next", "Segoe UI", sans-serif;
background: radial-gradient(circle at top right, #e2f8f2, var(--bg) 35%);
color: var(--text);
}
main {
max-width: 1120px;
margin: 0 auto;
padding: 32px 16px 48px;
}
.header {
display: grid;
gap: 12px;
}
h1 {
margin: 0;
font-size: clamp(1.8rem, 2.3vw, 2.4rem);
font-weight: 760;
}
.subtitle {
margin: 0;
color: var(--muted);
max-width: 60ch;
}
.toolbar {
margin-top: 10px;
padding: 14px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface);
display: grid;
gap: 12px;
}
.stats {
font-size: 0.95rem;
color: var(--muted);
}
.search {
width: 100%;
border: 1px solid var(--border);
border-radius: 8px;
font: inherit;
font-size: 1rem;
padding: 10px 12px;
background: #fff;
}
.search:focus {
outline: 2px solid color-mix(in srgb, var(--accent) 30%, white);
outline-offset: 0;
border-color: var(--accent);
}
.chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.chip {
text-decoration: none;
color: var(--text);
background: var(--surface-muted);
padding: 6px 10px;
border-radius: 999px;
font-size: 0.85rem;
border: 1px solid transparent;
}
.chip span {
color: var(--muted);
}
.chip:hover {
border-color: var(--accent);
}
.groups {
margin-top: 20px;
display: grid;
gap: 16px;
}
.group {
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface);
padding: 14px;
}
.group.hidden {
display: none;
}
.group h2 {
margin: 0 0 10px;
font-size: 1.1rem;
}
.group h2 span {
color: var(--muted);
font-weight: 520;
}
.doc-list {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: 6px;
}
.doc-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 8px 10px;
border-radius: 6px;
}
.doc-item.hidden {
display: none;
}
.doc-item:hover {
background: #f5faf8;
}
.doc-link {
color: var(--text);
text-decoration: none;
font-family: "IBM Plex Mono", "SFMono-Regular", Consolas, monospace;
font-size: 0.92rem;
overflow-wrap: anywhere;
}
.doc-link:hover {
color: var(--accent);
text-decoration: underline;
}
.meta {
display: flex;
align-items: center;
gap: 10px;
color: var(--muted);
font-size: 0.82rem;
white-space: nowrap;
}
.tag {
background: var(--accent-soft);
color: #065f46;
border-radius: 999px;
padding: 3px 8px;
font-size: 0.78rem;
}
.empty {
margin-top: 20px;
border: 1px dashed var(--border);
border-radius: 8px;
background: var(--surface);
color: var(--muted);
padding: 20px;
text-align: center;
display: none;
}
</style>
</head>
<body>
<main>
<header class="header">
<h1>Islandflow docs index</h1>
<p class="subtitle">A browsable index of files under <code>docs/</code> with filtering and grouped navigation.</p>
</header>
<section class="toolbar">
<div class="stats"><strong id="visible-count">35</strong> of <strong>35</strong> files shown</div>
<input id="doc-search" class="search" type="search" placeholder="Filter by filename or folder..." autocomplete="off" />
<nav class="chips"><a class="chip" href="#category-turns">turns <span>28</span></a>
<a class="chip" href="#category-daily-git">daily-git <span>1</span></a>
<a class="chip" href="#category-general">general <span>2</span></a>
<a class="chip" href="#category-plans">plans <span>2</span></a>
<a class="chip" href="#category-root">root <span>2</span></a></nav>
</section>
<section class="groups" id="groups">
<section class="group" id="category-turns">
<h2>turns <span>28</span></h2>
<ul class="doc-list">
<li class="doc-item" data-search="turns/2026-05-19-publish-docs-pages-index.html turns">
<a class="doc-link" href="./turns/2026-05-19-publish-docs-pages-index.html">turns/2026-05-19-publish-docs-pages-index.html</a>
<div class="meta">
<span class="tag">html</span>
<span>6.7 KB</span>
<span>May 19, 2026, 2:59 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-18-native-public-edge-cutover.html turns">
<a class="doc-link" href="./turns/2026-05-18-native-public-edge-cutover.html">turns/2026-05-18-native-public-edge-cutover.html</a>
<div class="meta">
<span class="tag">html</span>
<span>19 KB</span>
<span>May 19, 2026, 2:48 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-19-reconcile-pr-conflicts.html turns">
<a class="doc-link" href="./turns/2026-05-19-reconcile-pr-conflicts.html">turns/2026-05-19-reconcile-pr-conflicts.html</a>
<div class="meta">
<span class="tag">html</span>
<span>9.8 KB</span>
<span>May 19, 2026, 2:48 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-18-native-fast-iterative-deploy.html turns">
<a class="doc-link" href="./turns/2026-05-18-native-fast-iterative-deploy.html">turns/2026-05-18-native-fast-iterative-deploy.html</a>
<div class="meta">
<span class="tag">html</span>
<span>9.0 KB</span>
<span>May 19, 2026, 2:48 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-19-0805-clarify-repo-turn-doc-rules.html turns">
<a class="doc-link" href="./turns/2026-05-19-0805-clarify-repo-turn-doc-rules.html">turns/2026-05-19-0805-clarify-repo-turn-doc-rules.html</a>
<div class="meta">
<span class="tag">html</span>
<span>6.4 KB</span>
<span>May 19, 2026, 8:05 AM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-19-0739-update-readme-current-state.html turns">
<a class="doc-link" href="./turns/2026-05-19-0739-update-readme-current-state.html">turns/2026-05-19-0739-update-readme-current-state.html</a>
<div class="meta">
<span class="tag">html</span>
<span>9.8 KB</span>
<span>May 19, 2026, 7:39 AM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-19-upgrade-nextjs-16.html turns">
<a class="doc-link" href="./turns/2026-05-19-upgrade-nextjs-16.html">turns/2026-05-19-upgrade-nextjs-16.html</a>
<div class="meta">
<span class="tag">html</span>
<span>9.0 KB</span>
<span>May 19, 2026, 7:31 AM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-18-news-wire-view.html turns">
<a class="doc-link" href="./turns/2026-05-18-news-wire-view.html">turns/2026-05-18-news-wire-view.html</a>
<div class="meta">
<span class="tag">html</span>
<span>7.0 KB</span>
<span>May 18, 2026, 4:54 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-17-forgejo-deploy-remote-resolution.html turns">
<a class="doc-link" href="./turns/2026-05-17-forgejo-deploy-remote-resolution.html">turns/2026-05-17-forgejo-deploy-remote-resolution.html</a>
<div class="meta">
<span class="tag">html</span>
<span>5.3 KB</span>
<span>May 17, 2026, 11:22 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-17-add-fast-deploy-mode.html turns">
<a class="doc-link" href="./turns/2026-05-17-add-fast-deploy-mode.html">turns/2026-05-17-add-fast-deploy-mode.html</a>
<div class="meta">
<span class="tag">html</span>
<span>5.5 KB</span>
<span>May 17, 2026, 10:53 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-17-1101-clickhouse-alert-context.html turns">
<a class="doc-link" href="./turns/2026-05-17-1101-clickhouse-alert-context.html">turns/2026-05-17-1101-clickhouse-alert-context.html</a>
<div class="meta">
<span class="tag">html</span>
<span>6.4 KB</span>
<span>May 17, 2026, 10:21 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-17-clickhouse-alert-context.html turns">
<a class="doc-link" href="./turns/2026-05-17-clickhouse-alert-context.html">turns/2026-05-17-clickhouse-alert-context.html</a>
<div class="meta">
<span class="tag">html</span>
<span>11 KB</span>
<span>May 17, 2026, 10:21 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-17-deploy-allowlist-pr-packaging.html turns">
<a class="doc-link" href="./turns/2026-05-17-deploy-allowlist-pr-packaging.html">turns/2026-05-17-deploy-allowlist-pr-packaging.html</a>
<div class="meta">
<span class="tag">html</span>
<span>5.2 KB</span>
<span>May 17, 2026, 10:21 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-17-configure-beads-dolt-remote.html turns">
<a class="doc-link" href="./turns/2026-05-17-configure-beads-dolt-remote.html">turns/2026-05-17-configure-beads-dolt-remote.html</a>
<div class="meta">
<span class="tag">html</span>
<span>7.7 KB</span>
<span>May 17, 2026, 10:07 AM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-16-live-tape-scroll-hold-history.html turns">
<a class="doc-link" href="./turns/2026-05-16-live-tape-scroll-hold-history.html">turns/2026-05-16-live-tape-scroll-hold-history.html</a>
<div class="meta">
<span class="tag">html</span>
<span>6.3 KB</span>
<span>May 17, 2026, 5:06 AM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-17-0331-fix-live-tape-scroll-stability.html turns">
<a class="doc-link" href="./turns/2026-05-17-0331-fix-live-tape-scroll-stability.html">turns/2026-05-17-0331-fix-live-tape-scroll-stability.html</a>
<div class="meta">
<span class="tag">html</span>
<span>5.6 KB</span>
<span>May 17, 2026, 5:06 AM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-16-1725-durable-options-tape-history.html turns">
<a class="doc-link" href="./turns/2026-05-16-1725-durable-options-tape-history.html">turns/2026-05-16-1725-durable-options-tape-history.html</a>
<div class="meta">
<span class="tag">html</span>
<span>7.5 KB</span>
<span>May 17, 2026, 5:06 AM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-16-1752-speed-up-docker-deploys.html turns">
<a class="doc-link" href="./turns/2026-05-16-1752-speed-up-docker-deploys.html">turns/2026-05-16-1752-speed-up-docker-deploys.html</a>
<div class="meta">
<span class="tag">html</span>
<span>7.9 KB</span>
<span>May 17, 2026, 5:06 AM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-16-2159-fix-durable-options-history-routing.html turns">
<a class="doc-link" href="./turns/2026-05-16-2159-fix-durable-options-history-routing.html">turns/2026-05-16-2159-fix-durable-options-history-routing.html</a>
<div class="meta">
<span class="tag">html</span>
<span>6.4 KB</span>
<span>May 17, 2026, 5:06 AM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-15-add-duplicate-vps-compose-warning.html turns">
<a class="doc-link" href="./turns/2026-05-15-add-duplicate-vps-compose-warning.html">turns/2026-05-15-add-duplicate-vps-compose-warning.html</a>
<div class="meta">
<span class="tag">html</span>
<span>7.2 KB</span>
<span>May 15, 2026, 9:28 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-15-clarify-docker-first-deploy-workflow.html turns">
<a class="doc-link" href="./turns/2026-05-15-clarify-docker-first-deploy-workflow.html">turns/2026-05-15-clarify-docker-first-deploy-workflow.html</a>
<div class="meta">
<span class="tag">html</span>
<span>6.5 KB</span>
<span>May 15, 2026, 9:12 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-15-dual-runtime-deploy-workflow.html turns">
<a class="doc-link" href="./turns/2026-05-15-dual-runtime-deploy-workflow.html">turns/2026-05-15-dual-runtime-deploy-workflow.html</a>
<div class="meta">
<span class="tag">html</span>
<span>7.9 KB</span>
<span>May 15, 2026, 8:52 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-15-deploy-preflight-docker-workspace-check.html turns">
<a class="doc-link" href="./turns/2026-05-15-deploy-preflight-docker-workspace-check.html">turns/2026-05-15-deploy-preflight-docker-workspace-check.html</a>
<div class="meta">
<span class="tag">html</span>
<span>3.6 KB</span>
<span>May 15, 2026, 7:03 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-15-fix-docker-workspace-lockfile-sync.html turns">
<a class="doc-link" href="./turns/2026-05-15-fix-docker-workspace-lockfile-sync.html">turns/2026-05-15-fix-docker-workspace-lockfile-sync.html</a>
<div class="meta">
<span class="tag">html</span>
<span>3.6 KB</span>
<span>May 15, 2026, 6:56 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-15-quiet-terminal-view.html turns">
<a class="doc-link" href="./turns/2026-05-15-quiet-terminal-view.html">turns/2026-05-15-quiet-terminal-view.html</a>
<div class="meta">
<span class="tag">html</span>
<span>5.3 KB</span>
<span>May 15, 2026, 6:55 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-14-1824-adapt-terminal-view.html turns">
<a class="doc-link" href="./turns/2026-05-14-1824-adapt-terminal-view.html">turns/2026-05-14-1824-adapt-terminal-view.html</a>
<div class="meta">
<span class="tag">html</span>
<span>6.6 KB</span>
<span>May 15, 2026, 6:55 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-14-1833-reconcile-pr-conflicts.html turns">
<a class="doc-link" href="./turns/2026-05-14-1833-reconcile-pr-conflicts.html">turns/2026-05-14-1833-reconcile-pr-conflicts.html</a>
<div class="meta">
<span class="tag">html</span>
<span>5.6 KB</span>
<span>May 15, 2026, 6:55 PM</span>
</div>
</li>
<li class="doc-item" data-search="turns/2026-05-14-harden-terminal-view.html turns">
<a class="doc-link" href="./turns/2026-05-14-harden-terminal-view.html">turns/2026-05-14-harden-terminal-view.html</a>
<div class="meta">
<span class="tag">html</span>
<span>9.2 KB</span>
<span>May 15, 2026, 6:55 PM</span>
</div>
</li>
</ul>
</section>
<section class="group" id="category-daily-git">
<h2>daily-git <span>1</span></h2>
<ul class="doc-list">
<li class="doc-item" data-search="daily-git/2026-05-19-standup-summary-2026-05-18.html daily-git">
<a class="doc-link" href="./daily-git/2026-05-19-standup-summary-2026-05-18.html">daily-git/2026-05-19-standup-summary-2026-05-18.html</a>
<div class="meta">
<span class="tag">html</span>
<span>16 KB</span>
<span>May 19, 2026, 2:55 PM</span>
</div>
</li>
</ul>
</section>
<section class="group" id="category-general">
<h2>general <span>2</span></h2>
<ul class="doc-list">
<li class="doc-item" data-search="general/2026-05-18-standup-summary-2026-05-17.html general">
<a class="doc-link" href="./general/2026-05-18-standup-summary-2026-05-17.html">general/2026-05-18-standup-summary-2026-05-17.html</a>
<div class="meta">
<span class="tag">html</span>
<span>19 KB</span>
<span>May 18, 2026, 9:05 AM</span>
</div>
</li>
<li class="doc-item" data-search="general/2026-05-17-standup-summary-2026-05-16.html general">
<a class="doc-link" href="./general/2026-05-17-standup-summary-2026-05-16.html">general/2026-05-17-standup-summary-2026-05-16.html</a>
<div class="meta">
<span class="tag">html</span>
<span>17 KB</span>
<span>May 17, 2026, 10:07 AM</span>
</div>
</li>
</ul>
</section>
<section class="group" id="category-plans">
<h2>plans <span>2</span></h2>
<ul class="doc-list">
<li class="doc-item" data-search="plans/2026-05-18-native-fast-iterative-deploy-plan.html plans">
<a class="doc-link" href="./plans/2026-05-18-native-fast-iterative-deploy-plan.html">plans/2026-05-18-native-fast-iterative-deploy-plan.html</a>
<div class="meta">
<span class="tag">html</span>
<span>3.8 KB</span>
<span>May 19, 2026, 2:48 PM</span>
</div>
</li>
<li class="doc-item" data-search="plans/2026-05-16-1711-durable-options-tape-history.html plans">
<a class="doc-link" href="./plans/2026-05-16-1711-durable-options-tape-history.html">plans/2026-05-16-1711-durable-options-tape-history.html</a>
<div class="meta">
<span class="tag">html</span>
<span>12 KB</span>
<span>May 17, 2026, 5:06 AM</span>
</div>
</li>
</ul>
</section>
<section class="group" id="category-root">
<h2>root <span>2</span></h2>
<ul class="doc-list">
<li class="doc-item" data-search="clickhouse-reset-runbook.md root">
<a class="doc-link" href="./clickhouse-reset-runbook.md">clickhouse-reset-runbook.md</a>
<div class="meta">
<span class="tag">md</span>
<span>3.0 KB</span>
<span>May 17, 2026, 5:06 AM</span>
</div>
</li>
<li class="doc-item" data-search="terminal-audit-2026-05-14-0432.html root">
<a class="doc-link" href="./terminal-audit-2026-05-14-0432.html">terminal-audit-2026-05-14-0432.html</a>
<div class="meta">
<span class="tag">html</span>
<span>22 KB</span>
<span>May 15, 2026, 6:55 PM</span>
</div>
</li>
</ul>
</section>
</section>
<p class="empty" id="empty-state">No files match that filter.</p>
</main>
<script>
const searchInput = document.getElementById("doc-search");
const items = Array.from(document.querySelectorAll(".doc-item"));
const groups = Array.from(document.querySelectorAll(".group"));
const visibleCount = document.getElementById("visible-count");
const emptyState = document.getElementById("empty-state");
function applyFilter(query) {
const normalized = query.trim().toLowerCase();
let shown = 0;
for (const item of items) {
const searchable = item.dataset.search || "";
const isVisible = normalized.length === 0 || searchable.includes(normalized);
item.classList.toggle("hidden", !isVisible);
if (isVisible) shown += 1;
}
for (const group of groups) {
const hasVisibleItems = group.querySelector(".doc-item:not(.hidden)") !== null;
group.classList.toggle("hidden", !hasVisibleItems);
}
visibleCount.textContent = String(shown);
emptyState.style.display = shown === 0 ? "block" : "none";
}
searchInput.addEventListener("input", () => applyFilter(searchInput.value));
applyFilter("");
</script>
</body>
</html>

View file

@ -0,0 +1,195 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Turn Report - Publish Docs to GitHub Pages</title>
<style>
:root {
--bg: #f6f8fb;
--surface: #ffffff;
--text: #172133;
--muted: #56637a;
--border: #d5dde8;
--accent: #0b7a75;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: "Inter", "Segoe UI", sans-serif;
line-height: 1.5;
}
main {
max-width: 980px;
margin: 0 auto;
padding: 28px 16px 44px;
}
h1, h2 { line-height: 1.2; }
h1 { margin: 0 0 8px; font-size: clamp(1.6rem, 2.2vw, 2.1rem); }
h2 {
margin: 0;
font-size: 1.18rem;
}
.meta {
margin: 0 0 20px;
color: var(--muted);
}
section {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 14px 16px;
margin-bottom: 12px;
}
p, li { margin: 0; }
p + p { margin-top: 8px; }
ul {
margin: 10px 0 0;
padding-left: 18px;
}
li + li { margin-top: 6px; }
code {
font-family: "IBM Plex Mono", "SFMono-Regular", Consolas, monospace;
font-size: 0.92em;
}
pre {
margin: 10px 0 0;
overflow-x: auto;
padding: 12px;
border-radius: 8px;
background: #0f172a;
color: #e2e8f0;
border: 1px solid #1e293b;
}
.note {
margin-top: 10px;
color: var(--muted);
font-size: 0.95rem;
}
a { color: var(--accent); }
</style>
</head>
<body>
<main>
<h1>Publish docs/ to GitHub Pages with navigable index</h1>
<p class="meta">Completed on May 19, 2026 at 9:38 AM ET.</p>
<section>
<h2>Summary</h2>
<p>
Added an automated docs publishing flow to GitHub Pages and generated a new
<code>docs/index.html</code> browsing experience so docs are easy to navigate at
<code>/islandflow/docs/</code>.
</p>
</section>
<section>
<h2>Changes Made</h2>
<ul>
<li>Added <code>scripts/generate-docs-index.mjs</code> to build a browsable index of files under <code>docs/</code>.</li>
<li>Added <code>.github/workflows/docs-pages.yml</code> to publish docs to GitHub Pages on pushes to <code>main</code>.</li>
<li>Generated <code>docs/index.html</code> from current docs content.</li>
<li>Configured deployment artifact layout so docs are available at <code>/docs/</code> under the project Pages site.</li>
</ul>
</section>
<section>
<h2>Context</h2>
<p>
The repository already stores operational and implementation documentation under
<code>docs/</code>, but there was no dedicated GitHub Pages pipeline and no curated
index page for discovery. This task focused on syncing that folder to Pages and
making it easy to browse by category and filename.
</p>
</section>
<section>
<h2>Important Implementation Details</h2>
<ul>
<li>The index generator excludes hidden files and avoids self-including <code>docs/index.html</code>.</li>
<li>Files are grouped by first path segment (<code>turns</code>, <code>general</code>, <code>plans</code>, and others) with quick category chips.</li>
<li>The index includes client-side filtering so users can search docs by path text in-browser.</li>
<li>Pages deployment packages a <code>site/</code> payload where docs are copied into <code>site/docs</code> and root redirects to <code>./docs/</code>.</li>
</ul>
</section>
<section>
<h2>Relevant Diff Snippets</h2>
<p class="note">
Snippets are shown in a compact style aligned with <a href="https://diffs.com/docs">diffs.com</a> presentation patterns.
</p>
<pre><code class="language-diff">+++ .github/workflows/docs-pages.yml
name: Publish Docs
on:
push:
branches: [main]
paths:
- "docs/**"
- "scripts/generate-docs-index.mjs"
- ".github/workflows/docs-pages.yml"
workflow_dispatch:
jobs:
build:
steps:
- uses: actions/checkout@v4
- uses: actions/configure-pages@v5
- run: node scripts/generate-docs-index.mjs
- run: cp -R docs/. site/docs/
- uses: actions/upload-pages-artifact@v3
deploy:
needs: build
steps:
- uses: actions/deploy-pages@v4</code></pre>
<pre><code class="language-diff">+++ scripts/generate-docs-index.mjs
const files = await collectDocsFiles(docsDir);
const html = renderDocument(files);
await fs.writeFile(outputFile, html, "utf8");
// Generated index features:
// - grouped sections
// - search filter
// - file size and modified time metadata
// - links preserving docs folder structure</code></pre>
</section>
<section>
<h2>Expected Impact for End-Users</h2>
<ul>
<li>Docs are reachable via a stable Pages URL path: <code>dirtydishes.github.io/islandflow/docs/</code>.</li>
<li>Readers can quickly scan categories and search by filename instead of relying on raw directory browsing.</li>
<li>New docs added to the repository are published automatically on <code>main</code> pushes.</li>
</ul>
</section>
<section>
<h2>Validation</h2>
<ul>
<li>Ran <code>node scripts/generate-docs-index.mjs</code> successfully.</li>
<li>Ran <code>node --check scripts/generate-docs-index.mjs</code> for syntax validation.</li>
<li>Confirmed generated index contains expected navigation/search markers and category anchors.</li>
</ul>
</section>
<section>
<h2>Issues, Limitations, and Mitigations</h2>
<ul>
<li>GitHub Pages must be enabled for this repository and set to GitHub Actions deployment.</li>
<li>The index reflects files present at build time and does not include full-text search inside documents.</li>
<li>Markdown files are linked as-is; rendering behavior depends on GitHub Pages static hosting behavior.</li>
</ul>
</section>
<section>
<h2>Follow-up Work</h2>
<ul>
<li>Add a docs landing page summary for key collections (turn docs, runbooks, daily notes).</li>
<li>Optionally add link-checking in CI for docs URLs and local references.</li>
<li>Consider tagging docs with metadata for richer filtering by date, topic, and type.</li>
</ul>
</section>
</main>
</body>
</html>

View file

@ -0,0 +1,421 @@
import { promises as fs } from "node:fs";
import path from "node:path";
const docsDir = path.resolve(process.cwd(), "docs");
const outputFile = path.join(docsDir, "index.html");
const dateFormatter = new Intl.DateTimeFormat("en-US", {
dateStyle: "medium",
timeStyle: "short",
});
function escapeHtml(value) {
return value
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#39;");
}
function formatBytes(bytes) {
if (bytes < 1024) {
return `${bytes} B`;
}
const units = ["KB", "MB", "GB"];
let size = bytes / 1024;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex += 1;
}
return `${size.toFixed(size >= 10 ? 0 : 1)} ${units[unitIndex]}`;
}
function docsHref(relativePath) {
const encoded = relativePath
.split("/")
.map((part) => encodeURIComponent(part))
.join("/");
return `./${encoded}`;
}
async function collectDocsFiles(rootDir, currentDir = rootDir, acc = []) {
const entries = await fs.readdir(currentDir, { withFileTypes: true });
const sortedEntries = entries.sort((a, b) => a.name.localeCompare(b.name));
for (const entry of sortedEntries) {
if (entry.name.startsWith(".")) {
continue;
}
const absolutePath = path.join(currentDir, entry.name);
const relativePath = path.relative(rootDir, absolutePath).replaceAll(path.sep, "/");
if (relativePath === "index.html") {
continue;
}
if (entry.isDirectory()) {
await collectDocsFiles(rootDir, absolutePath, acc);
continue;
}
if (entry.isFile()) {
const stats = await fs.stat(absolutePath);
acc.push({
relativePath,
category: relativePath.includes("/") ? relativePath.split("/")[0] : "root",
sizeBytes: stats.size,
modifiedAt: stats.mtime,
});
}
}
return acc;
}
function groupByCategory(items) {
const groups = new Map();
for (const item of items) {
if (!groups.has(item.category)) {
groups.set(item.category, []);
}
groups.get(item.category).push(item);
}
return groups;
}
function sortedCategories(groups) {
const preferredOrder = ["turns", "daily-git", "general", "plans", "root"];
const groupNames = [...groups.keys()];
return groupNames.sort((a, b) => {
const aIndex = preferredOrder.indexOf(a);
const bIndex = preferredOrder.indexOf(b);
if (aIndex !== -1 || bIndex !== -1) {
if (aIndex === -1) return 1;
if (bIndex === -1) return -1;
return aIndex - bIndex;
}
return a.localeCompare(b);
});
}
function renderDocument(items) {
const sortedItems = [...items].sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
const groups = groupByCategory(sortedItems);
const categories = sortedCategories(groups);
const totalCount = sortedItems.length;
const categoryChips = categories
.map((category) => {
const count = groups.get(category).length;
return `<a class="chip" href="#category-${escapeHtml(category)}">${escapeHtml(
category
)} <span>${count}</span></a>`;
})
.join("\n");
const groupsMarkup = categories
.map((category) => {
const entries = groups.get(category);
const entryMarkup = entries
.map((entry) => {
const extension = path.extname(entry.relativePath).replace(".", "") || "file";
const searchable = `${entry.relativePath} ${category}`.toLowerCase();
return `
<li class="doc-item" data-search="${escapeHtml(searchable)}">
<a class="doc-link" href="${docsHref(entry.relativePath)}">${escapeHtml(
entry.relativePath
)}</a>
<div class="meta">
<span class="tag">${escapeHtml(extension)}</span>
<span>${escapeHtml(formatBytes(entry.sizeBytes))}</span>
<span>${escapeHtml(dateFormatter.format(entry.modifiedAt))}</span>
</div>
</li>
`;
})
.join("\n");
return `
<section class="group" id="category-${escapeHtml(category)}">
<h2>${escapeHtml(category)} <span>${entries.length}</span></h2>
<ul class="doc-list">
${entryMarkup}
</ul>
</section>
`;
})
.join("\n");
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Islandflow Docs</title>
<style>
:root {
--bg: #f4f6f8;
--surface: #ffffff;
--surface-muted: #e8edf2;
--text: #1a2433;
--muted: #5b6a80;
--border: #ccd5df;
--accent: #0f766e;
--accent-soft: #d1fae5;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: "Avenir Next", "Segoe UI", sans-serif;
background: radial-gradient(circle at top right, #e2f8f2, var(--bg) 35%);
color: var(--text);
}
main {
max-width: 1120px;
margin: 0 auto;
padding: 32px 16px 48px;
}
.header {
display: grid;
gap: 12px;
}
h1 {
margin: 0;
font-size: clamp(1.8rem, 2.3vw, 2.4rem);
font-weight: 760;
}
.subtitle {
margin: 0;
color: var(--muted);
max-width: 60ch;
}
.toolbar {
margin-top: 10px;
padding: 14px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface);
display: grid;
gap: 12px;
}
.stats {
font-size: 0.95rem;
color: var(--muted);
}
.search {
width: 100%;
border: 1px solid var(--border);
border-radius: 8px;
font: inherit;
font-size: 1rem;
padding: 10px 12px;
background: #fff;
}
.search:focus {
outline: 2px solid color-mix(in srgb, var(--accent) 30%, white);
outline-offset: 0;
border-color: var(--accent);
}
.chips {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.chip {
text-decoration: none;
color: var(--text);
background: var(--surface-muted);
padding: 6px 10px;
border-radius: 999px;
font-size: 0.85rem;
border: 1px solid transparent;
}
.chip span {
color: var(--muted);
}
.chip:hover {
border-color: var(--accent);
}
.groups {
margin-top: 20px;
display: grid;
gap: 16px;
}
.group {
border: 1px solid var(--border);
border-radius: 8px;
background: var(--surface);
padding: 14px;
}
.group.hidden {
display: none;
}
.group h2 {
margin: 0 0 10px;
font-size: 1.1rem;
}
.group h2 span {
color: var(--muted);
font-weight: 520;
}
.doc-list {
margin: 0;
padding: 0;
list-style: none;
display: grid;
gap: 6px;
}
.doc-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 8px 10px;
border-radius: 6px;
}
.doc-item.hidden {
display: none;
}
.doc-item:hover {
background: #f5faf8;
}
.doc-link {
color: var(--text);
text-decoration: none;
font-family: "IBM Plex Mono", "SFMono-Regular", Consolas, monospace;
font-size: 0.92rem;
overflow-wrap: anywhere;
}
.doc-link:hover {
color: var(--accent);
text-decoration: underline;
}
.meta {
display: flex;
align-items: center;
gap: 10px;
color: var(--muted);
font-size: 0.82rem;
white-space: nowrap;
}
.tag {
background: var(--accent-soft);
color: #065f46;
border-radius: 999px;
padding: 3px 8px;
font-size: 0.78rem;
}
.empty {
margin-top: 20px;
border: 1px dashed var(--border);
border-radius: 8px;
background: var(--surface);
color: var(--muted);
padding: 20px;
text-align: center;
display: none;
}
</style>
</head>
<body>
<main>
<header class="header">
<h1>Islandflow docs index</h1>
<p class="subtitle">A browsable index of files under <code>docs/</code> with filtering and grouped navigation.</p>
</header>
<section class="toolbar">
<div class="stats"><strong id="visible-count">${totalCount}</strong> of <strong>${totalCount}</strong> files shown</div>
<input id="doc-search" class="search" type="search" placeholder="Filter by filename or folder..." autocomplete="off" />
<nav class="chips">${categoryChips}</nav>
</section>
<section class="groups" id="groups">${groupsMarkup}</section>
<p class="empty" id="empty-state">No files match that filter.</p>
</main>
<script>
const searchInput = document.getElementById("doc-search");
const items = Array.from(document.querySelectorAll(".doc-item"));
const groups = Array.from(document.querySelectorAll(".group"));
const visibleCount = document.getElementById("visible-count");
const emptyState = document.getElementById("empty-state");
function applyFilter(query) {
const normalized = query.trim().toLowerCase();
let shown = 0;
for (const item of items) {
const searchable = item.dataset.search || "";
const isVisible = normalized.length === 0 || searchable.includes(normalized);
item.classList.toggle("hidden", !isVisible);
if (isVisible) shown += 1;
}
for (const group of groups) {
const hasVisibleItems = group.querySelector(".doc-item:not(.hidden)") !== null;
group.classList.toggle("hidden", !hasVisibleItems);
}
visibleCount.textContent = String(shown);
emptyState.style.display = shown === 0 ? "block" : "none";
}
searchInput.addEventListener("input", () => applyFilter(searchInput.value));
applyFilter("");
</script>
</body>
</html>
`;
}
async function main() {
const files = await collectDocsFiles(docsDir);
const html = renderDocument(files);
await fs.writeFile(outputFile, html, "utf8");
console.log(`Generated ${outputFile} with ${files.length} entries.`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});