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
17 changes: 17 additions & 0 deletions .github/workflows/validate-site.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Validate public hub

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate HTML, discovery files and project directory
run: python3 scripts/validate_site.py
4 changes: 4 additions & 0 deletions favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 88 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,54 @@
<meta name="google-site-verification" content="5psM2cP_edBLrQk06yRjOZQbfEOt4QIpe4GjxoWLhUU">
<meta name="description" content="Open Future Forum publishes executive research, open datasets, market maps, and practical resources for leaders and executive communities.">
<meta name="theme-color" content="#101828">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Open Future Forum">
<meta property="og:title" content="Open Future Forum Research and Resources">
<meta property="og:description" content="Executive research, open datasets, market maps and practical resources from Open Future Forum.">
<meta property="og:url" content="https://openfutureforum.github.io/">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="https://openfutureforum.github.io/">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="alternate" href="/projects.json" type="application/json" title="Open Future Forum project directory">
<title>Open Future Forum Research and Resources</title>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph": [
{
"@type": "Organization",
"@id": "https://openfutureforum.com/#organization",
"name": "Open Future Forum",
"url": "https://openfutureforum.com/",
"sameAs": [
"https://github.com/OpenFutureForum",
"https://openfutureforum.github.io/"
]
},
{
"@type": "WebSite",
"@id": "https://openfutureforum.github.io/#website",
"name": "Open Future Forum Research and Resources",
"url": "https://openfutureforum.github.io/",
"description": "Executive research, open datasets, market maps and practical resources from Open Future Forum.",
"publisher": { "@id": "https://openfutureforum.com/#organization" }
},
{
"@type": "CollectionPage",
"@id": "https://openfutureforum.github.io/#collection",
"url": "https://openfutureforum.github.io/",
"name": "Open Future Forum public research directory",
"isPartOf": { "@id": "https://openfutureforum.github.io/#website" },
"about": [
"Executive leadership",
"Executive communities",
"Enterprise artificial intelligence",
"CXO ecosystems"
]
}
]
}
</script>
<style>
:root {
color-scheme: light;
Expand Down Expand Up @@ -248,6 +294,22 @@
.principle-grid h3 { margin: 0 0 10px; font-size: 1.15rem; }
.principle-grid p { margin: 0; color: #d0d5dd; }

.system-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 20px;
}

.system-step {
padding: 24px;
border: 1px solid var(--line);
border-radius: 16px;
background: var(--soft);
}

.system-step h3 { margin: 0 0 10px; font-size: 1.2rem; }
.system-step p { margin: 0; color: var(--muted); }

.repo-list {
margin: 0;
padding: 0;
Expand Down Expand Up @@ -290,7 +352,7 @@
.nav-links { align-items: flex-end; flex-direction: column; gap: 6px; }
.hero { padding: 64px 0 56px; }
section { padding: 56px 0; }
.cards, .principle-grid { grid-template-columns: 1fr; }
.cards, .principle-grid, .system-grid { grid-template-columns: 1fr; }
.repo-list li { grid-template-columns: 1fr; gap: 6px; }
.card { min-height: 0; }
}
Expand All @@ -306,6 +368,7 @@
<div class="wrap nav">
<a class="brand" href="/">Open Future Forum</a>
<nav class="nav-links" aria-label="Primary navigation">
<a href="#how-it-works">How it works</a>
<a href="#projects">Research and resources</a>
<a href="#repositories">Repository directory</a>
<a href="https://openfutureforum.com/">OpenFutureForum.com</a>
Expand All @@ -326,6 +389,30 @@ <h1>Evidence, maps and practical tools for executive leaders.</h1>
</div>
</section>

<section id="how-it-works" aria-labelledby="system-heading">
<div class="wrap">
<div class="section-heading">
<p class="eyebrow">One connected research system</p>
<h2 id="system-heading">Read the answer. Check the evidence.</h2>
<p>Open Future Forum separates public reading, canonical publication and source verification so that each resource has a clear role.</p>
</div>
<div class="system-grid">
<div class="system-step">
<h3>Public reading</h3>
<p>Human-readable project sites organize research, indexes and practical guidance around executive decisions.</p>
</div>
<div class="system-step">
<h3>Canonical publication</h3>
<p>Where a report has an official publication page, that page remains the source of record and is linked explicitly.</p>
</div>
<div class="system-step">
<h3>Open verification</h3>
<p>GitHub repositories expose methods, source trails, data, release history and correction paths where available.</p>
</div>
</div>
</div>
</section>

<section id="projects" aria-labelledby="projects-heading">
<div class="wrap">
<div class="section-heading">
Expand Down
57 changes: 57 additions & 0 deletions projects.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"schema_version": "1.0",
"publisher": {
"name": "Open Future Forum",
"url": "https://openfutureforum.com/",
"github_organization": "https://github.com/OpenFutureForum"
},
"directory_url": "https://openfutureforum.github.io/",
"last_reviewed": "2026-08-16",
"projects": [
{
"id": "cxo-ecosystem-index",
"name": "CXO Ecosystem Index",
"type": "open index",
"status": "public",
"public_url": "https://openfutureforum.github.io/cxo-ecosystem-index/",
"repository_url": "https://github.com/OpenFutureForum/cxo-ecosystem-index",
"description": "A structured view of companies, communities, advisors and resources serving leaders across the C-suite."
},
{
"id": "executive-communities-index",
"name": "Executive Communities Index",
"type": "open index",
"status": "public",
"public_url": "https://openfutureforum.github.io/executive-communities-index/",
"repository_url": "https://github.com/OpenFutureForum/executive-communities-index",
"description": "A transparent directory for comparing executive communities by role, format, geography and participation model."
},
{
"id": "executive-community-playbook",
"name": "Executive Community Playbook",
"type": "practical guidance",
"status": "public",
"public_url": "https://openfutureforum.github.io/executive-community-playbook/",
"repository_url": "https://github.com/OpenFutureForum/executive-community-playbook",
"description": "Practical guidance, governance principles and working templates for building trusted executive communities."
},
{
"id": "executive-ai-research",
"name": "Executive AI Research",
"type": "research library",
"status": "public",
"public_url": "https://openfutureforum.github.io/executive-ai-research/",
"canonical_publication_url": "https://openfutureforum.com/research/",
"repository_url": "https://github.com/OpenFutureForum/executive-ai-research",
"description": "Role-specific reports, datasets and evidence on how executives, investors and operators are approaching AI."
},
{
"id": "executive-intelligence-index",
"name": "Executive Intelligence Index",
"type": "research framework",
"status": "repository only",
"repository_url": "https://github.com/OpenFutureForum/executive-intelligence-index",
"description": "A cited map of the books, people, research, ideas, evidence and media shaping executive decision-making."
}
]
}
4 changes: 4 additions & 0 deletions robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
User-agent: *
Allow: /

Sitemap: https://openfutureforum.github.io/sitemap.xml
99 changes: 99 additions & 0 deletions scripts/validate_site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""Validate the Open Future Forum Pages hub using only the standard library."""

from __future__ import annotations

import json
from html.parser import HTMLParser
from pathlib import Path
from urllib.parse import urlparse
from xml.etree import ElementTree


ROOT = Path(__file__).resolve().parents[1]


class HubParser(HTMLParser):
def __init__(self) -> None:
super().__init__()
self.ids: list[str] = []
self.links: list[str] = []
self.headings: list[int] = []
self.canonical: list[str] = []
self.json_ld: list[str] = []
self._json_ld_buffer: list[str] | None = None

def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
values = dict(attrs)
if values.get("id"):
self.ids.append(values["id"])
if tag == "a" and values.get("href"):
self.links.append(values["href"])
if tag in {"h1", "h2", "h3", "h4", "h5", "h6"}:
self.headings.append(int(tag[1]))
if tag == "link" and values.get("rel") == "canonical" and values.get("href"):
self.canonical.append(values["href"])
if tag == "script" and values.get("type") == "application/ld+json":
self._json_ld_buffer = []

def handle_data(self, data: str) -> None:
if self._json_ld_buffer is not None:
self._json_ld_buffer.append(data)

def handle_endtag(self, tag: str) -> None:
if tag == "script" and self._json_ld_buffer is not None:
self.json_ld.append("".join(self._json_ld_buffer))
self._json_ld_buffer = None


def main() -> None:
required = [
ROOT / "index.html",
ROOT / "favicon.svg",
ROOT / "projects.json",
ROOT / "robots.txt",
ROOT / "sitemap.xml",
]
missing = [path.name for path in required if not path.exists()]
assert not missing, f"Missing public files: {', '.join(missing)}"

parser = HubParser()
parser.feed((ROOT / "index.html").read_text(encoding="utf-8"))

assert len(parser.ids) == len(set(parser.ids)), "Duplicate HTML IDs found"
assert parser.headings and parser.headings[0] == 1, "The first heading must be H1"
assert parser.headings.count(1) == 1, "The page must contain exactly one H1"
assert parser.canonical == ["https://openfutureforum.github.io/"], "Unexpected canonical URL"
assert parser.json_ld, "JSON-LD is required"
for block in parser.json_ld:
json.loads(block)

for link in parser.links:
parsed = urlparse(link)
assert parsed.scheme != "http", f"Insecure link: {link}"
if link.startswith("#"):
assert link[1:] in parser.ids, f"Missing fragment target: {link}"

directory = json.loads((ROOT / "projects.json").read_text(encoding="utf-8"))
projects = directory.get("projects", [])
assert len(projects) == 5, f"Expected 5 substantive projects, found {len(projects)}"
ids = [project["id"] for project in projects]
assert len(ids) == len(set(ids)), "Duplicate project IDs found"
for project in projects:
assert project.get("name") and project.get("description"), "Incomplete project record"
assert project.get("repository_url", "").startswith("https://"), "Invalid repository URL"

sitemap = ElementTree.parse(ROOT / "sitemap.xml")
namespace = {"sm": "http://www.sitemaps.org/schemas/sitemap/0.9"}
urls = [node.text for node in sitemap.findall("sm:url/sm:loc", namespace)]
assert "https://openfutureforum.github.io/" in urls, "Hub missing from sitemap"
assert all(url and url.startswith("https://openfutureforum.github.io/") for url in urls)

robots = (ROOT / "robots.txt").read_text(encoding="utf-8")
assert "Sitemap: https://openfutureforum.github.io/sitemap.xml" in robots

print(f"Hub validation passed: {len(projects)} projects, {len(urls)} sitemap URLs")


if __name__ == "__main__":
main()
8 changes: 8 additions & 0 deletions sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://openfutureforum.github.io/</loc></url>
<url><loc>https://openfutureforum.github.io/cxo-ecosystem-index/</loc></url>
<url><loc>https://openfutureforum.github.io/executive-communities-index/</loc></url>
<url><loc>https://openfutureforum.github.io/executive-community-playbook/</loc></url>
<url><loc>https://openfutureforum.github.io/executive-ai-research/</loc></url>
</urlset>
Loading