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
42 changes: 0 additions & 42 deletions .github/workflows/deploy.yml

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Verify canon authority
run: node -e "const c=require('./src/_data/generated/canon.json'); if(c.source.repository!=='wayseer00/wayseer.github.io'||c.source.path!=='canon/the_interdependent_way.md') process.exit(1)"
run: node -e "const c=require('./src/_data/generated/canon.json'); if(c.source.repository!=='wayseer00/main'||c.source.path!=='canon/INTERDEPENDENT_WAY.txt') process.exit(1)"
- name: Verify canon unit evidence
run: node -e "const c=require('./src/_data/generated/canon.json'); if(!c.source.contentSha256||c.source.contentSha256.length!==64||!c.units.length||c.units.some(u=>!u.id||!u.hash)) process.exit(1)"
run: node -e "const c=require('./src/_data/generated/canon.json'); if(!c.source.contentSha256||c.source.contentSha256.length!==64||(!c.source.fallback&&(!c.source.commit||!c.source.blob))||!c.units.length||c.units.some(u=>!u.id||!u.hash)) process.exit(1)"
- name: Verify repository route coverage
run: node -e "const r=require('./src/_data/generated/repos.json'); if(r.publicRepoCount!==r.generatedRouteCount||new Set(r.repositories.map(x=>x.slug)).size!==r.repositories.length) process.exit(1)"
- name: Verify recovery inputs
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This repository builds `interdependentway.org`: a static-first, progressively la

## What is authoritative

The canonical identity remains `wayseer00/wayseer.github.io:canon/the_interdependent_way.md`. The repository copy at `canon/the_interdependent_way.md` is a recovery mirror. Build output records whether the remote source or recovery mirror supplied the current snapshot, together with SHA-256 provenance.
The canonical text lives in `wayseer00/main:canon/INTERDEPENDENT_WAY.txt`, and nowhere in this repository supersedes it. The repository copy at `canon/the_interdependent_way.md` is a recovery mirror only. Build output records whether the remote source or recovery mirror supplied the current snapshot, together with SHA-256 provenance; successful remote retrieval also records the resolved source commit and blob SHA.

## Architecture

Expand Down
4 changes: 2 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ The production site is generated into `_site` by Eleventy. Pages are complete HT

## Data paths

- `scripts/fetch-canon.mjs` retrieves the Wayseer canonical alias `wayseer00/wayseer.github.io:canon/the_interdependent_way.md`. The transferred repository copy at `canon/the_interdependent_way.md` is recovery-only and is recorded as a fallback when used.
- `scripts/parse-canon.mjs` generates units, line ranges, note text, relationships, and SHA-256 digests.
- `scripts/fetch-canon.mjs` retrieves the Wayseer text canon from `wayseer00/main:canon/INTERDEPENDENT_WAY.txt`. The transferred repository copy at `canon/the_interdependent_way.md` is recovery-only and is recorded as a fallback when used.
- `scripts/parse-canon.mjs` generates units, line ranges, note text, relationships, and SHA-256 digests from either Markdown-style recovery mirrors or the plain-text canonical file.
- `scripts/fetch-github-org.mjs` discovers every public organization repository and merges GitHub facts with `.interdependency/project.yml` or reviewed central overrides.
- Pagefind indexes the generated site after Eleventy finishes.

Expand Down
56 changes: 48 additions & 8 deletions scripts/fetch-canon.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,70 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
// === END MODULE_BUILD ===
// === BOUNDARIES ===
// id: canon_network_boundary
// network: read-only HTTPS request to raw.githubusercontent.com
// network: read-only HTTPS request to allowlisted GitHub API and raw content endpoints
// storage: writes generated snapshots beneath src/_data/snapshots
// failure: falls back to the repository mirror and records fallback=true
// === END BOUNDARIES ===

const canonical = {
repository: 'wayseer00/wayseer.github.io',
path: 'canon/the_interdependent_way.md',
repository: 'wayseer00/main',
path: 'canon/INTERDEPENDENT_WAY.txt',
branch: 'main',
url: 'https://raw.githubusercontent.com/wayseer00/wayseer.github.io/main/canon/the_interdependent_way.md'
webUrl: 'https://github.com/wayseer00/main/blob/main/canon/INTERDEPENDENT_WAY.txt'
};
const localMirror = 'canon/the_interdependent_way.md';
const githubApiOrigin = 'https://api.github.com';
const rawOrigin = 'https://raw.githubusercontent.com';
const allowedOrigins = new Set([githubApiOrigin, rawOrigin]);
const githubHeaders = ['-H', 'Accept: application/vnd.github+json', '-H', 'X-GitHub-Api-Version: 2022-11-28'];
if (process.env.GITHUB_TOKEN) githubHeaders.push('-H', `Authorization: Bearer ${process.env.GITHUB_TOKEN}`);

function fetchRemote() {
if (process.env.OFFLINE === '1') throw new Error('offline requested');
return execFileSync('curl', ['-fsSL', '--retry', '2', '--max-time', '30', canonical.url], {
function curlText(target, extraHeaders = []) {
const url = target instanceof URL ? target : new URL(target);
if (url.protocol !== 'https:' || !allowedOrigins.has(url.origin)) {
throw new Error(`refusing non-allowlisted canon target: ${url.origin}`);
}
return execFileSync('curl', ['-fsSL', '--retry', '2', '--max-time', '30', ...extraHeaders, url.href], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe']
});
}

function githubApiUrl(pathname, search = {}) {
const url = new URL(pathname, githubApiOrigin);
for (const [key, value] of Object.entries(search)) url.searchParams.set(key, String(value));
return url;
}

function getJson(target) {
return JSON.parse(curlText(target, githubHeaders));
}

function fetchRemote() {
if (process.env.OFFLINE === '1') throw new Error('offline requested');
const [owner, repo] = canonical.repository.split('/');
const encodedOwner = encodeURIComponent(owner);
const encodedRepo = encodeURIComponent(repo);
const encodedPath = canonical.path.split('/').map(segment => encodeURIComponent(segment)).join('/');
const apiBase = `/repos/${encodedOwner}/${encodedRepo}`;
const commitInfo = getJson(githubApiUrl(`${apiBase}/commits/${encodeURIComponent(canonical.branch)}`));
const commit = commitInfo.sha;
if (!/^[a-f0-9]{40}$/i.test(commit)) throw new Error('canon branch did not resolve to a commit SHA');
const fileInfo = getJson(githubApiUrl(`${apiBase}/contents/${encodedPath}`, { ref: commit }));
if (fileInfo.type !== 'file' || !fileInfo.sha) throw new Error('canon path did not resolve to a file blob');
const resolvedUrl = new URL(`/${encodedOwner}/${encodedRepo}/${commit}/${encodedPath}`, rawOrigin);
const text = curlText(resolvedUrl);
return { text, commit, blob: fileInfo.sha, resolvedUrl: resolvedUrl.href };
}

await mkdir('src/_data/snapshots', { recursive: true });
let text;
let fallback = false;
let retrievalError = null;
let remote = { commit: null, blob: null, resolvedUrl: null };
try {
text = fetchRemote();
remote = fetchRemote();
text = remote.text;
} catch (error) {
fallback = true;
retrievalError = String(error?.message || error);
Expand All @@ -51,6 +88,9 @@ try {
const contentSha256 = createHash('sha256').update(text).digest('hex');
const metadata = {
...canonical,
commit: remote.commit,
blob: remote.blob,
resolvedUrl: remote.resolvedUrl,
retrievedAt: new Date().toISOString(),
contentSha256,
fallback,
Expand Down
62 changes: 47 additions & 15 deletions scripts/parse-canon.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import slugify from 'slugify';
// tests: tests/canon-integrity.test.mjs
// === END MODULE_BUILD ===

const parserVersion = '0.3.0';
const parserVersion = '0.4.1';
const provenance = JSON.parse(await readFile('src/_data/snapshots/canon.provenance.json', 'utf8'));
const raw = await readFile('src/_data/snapshots/canon.last-known-good.md', 'utf8');
const text = raw.replace(/^---\n[\s\S]*?\n---\n/, '');
Expand All @@ -19,7 +19,6 @@ const units = [];
const sections = [];
let current = null;
let sectionId = 'source';
const articleBySection = new Map();

function slug(value) {
return slugify(value, { lower: true, strict: true }) || 'unit';
Expand All @@ -30,36 +29,69 @@ function boundedRouteSlug(id) {
const suffix = createHash('sha256').update(id).digest('hex').slice(0, 10);
return `${candidate.slice(0, 84).replace(/-+$/, '')}-${suffix}`;
}
function detectHeading(line) {
const markdown = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
if (markdown) return { level: markdown[1].length, title: markdown[2].replace(/#+$/, '').trim() };
const title = line.trim();
if (!title) return null;
if (title === 'The Interdependent Way') return { level: 1, title };
if (/^(Awakening|The Interdefinables|Human consciousness emerges from|Preamble|Etiquette of the Body Politic)$/i.test(title)) {
return { level: 2, title };
}
if (/^Rights[\w\s’'&\-⁰¹²³⁴⁵⁶⁷⁸⁹]+of The Way[⁰¹²³⁴⁵⁶⁷⁸⁹]*$/i.test(title)) return { level: 2, title };
if (/^Addendum:\s+.+$/i.test(title)) return { level: 2, title };
if (/^Article\s+(One|Two|Three|Four|Five|Six|Seven|Eight)(?:\s+\([^)]+\))?$/i.test(title)) return { level: 3, title };
if (/^(Binary essences meaningfully, divided; then, rooted\.|Trinary perceptual focal states of complex system spirals:.+|Trinary states of social perception:|Archetype passions of possession\..+|Summary|One-sentence takeaway \(exactly as previously given\))$/i.test(title)) {
return { level: 3, title };
}
return null;
}
function extractNotes(content) {
const notes = [];
for (const line of content.split(/\r?\n/)) {
const bracket = /^\s*\[([^\]]+)\]\s+(.+)$/.exec(line);
if (bracket) {
notes.push({ marker: `[${bracket[1]}]`, text: bracket[2].trim() });
continue;
}
const superscript = /^\s*>?\s*([⁰¹²³⁴⁵⁶⁷⁸⁹]+)\s*(.+)$/.exec(line);
if (superscript) {
notes.push({ marker: superscript[1], text: superscript[2].trim() });
continue;
Comment thread
erinepshovel-code marked this conversation as resolved.
}
const digit = /^\s*>?\s*(\d+)\s+(.+)$/.exec(line);
if (digit) notes.push({ marker: digit[1], text: digit[2].trim() });
}
return notes;
}
function extractNoteMarkers(content) {
const markers = [];
for (const match of content.matchAll(/\[[^\]]+\]|[⁰¹²³⁴⁵⁶⁷⁸⁹]+/g)) markers.push(match[0]);
return [...new Set(markers)];
}
function finish(endLine) {
if (!current) return;
current.endLine = endLine;
current.content = current.lines.join('\n').trim();
current.hash = createHash('sha256').update(current.content).digest('hex');
const notePattern = /^\s*\[([^\]]+)\]\s+(.+)$/gm;
current.notes = [...current.content.matchAll(notePattern)].map(match => ({ marker: `[${match[1]}]`, text: match[2].trim() }));
current.noteMarkers = [...new Set([...current.content.matchAll(/\[([^\]]+)\]/g)].map(match => `[${match[1]}]`))];
current.notes = extractNotes(current.content);
current.noteMarkers = extractNoteMarkers(current.content);
units.push(current);
}

for (let index = 0; index < lines.length; index += 1) {
const heading = /^(#{1,6})\s+(.+?)\s*$/.exec(lines[index]);
const heading = detectHeading(lines[index]);
Comment thread
erinepshovel-code marked this conversation as resolved.
if (!heading) {
if (current) current.lines.push(lines[index]);
continue;
}
finish(index);
const level = heading[1].length;
const title = heading[2].replace(/#+$/, '').trim();
if (level <= 3) {
const { level, title } = heading;
if (level <= 2) {
Comment thread
erinepshovel-code marked this conversation as resolved.
sectionId = slug(title).replace(/^the-/, '');
if (!sections.some(section => section.id === sectionId)) sections.push({ id: sectionId, title, level, line: index + 1 });
}
let localId = slug(title);
if (/^article\s+/i.test(title)) {
const count = (articleBySection.get(sectionId) || 0) + 1;
articleBySection.set(sectionId, count);
localId = `article-${count}`;
}
const localId = slug(title);
current = {
id: `${sectionId}.${localId}`,
title,
Expand Down
5 changes: 3 additions & 2 deletions scripts/validate-content.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import { access, readFile } from 'node:fs/promises';

const canon = JSON.parse(await readFile('src/_data/generated/canon.json', 'utf8'));
const repos = JSON.parse(await readFile('src/_data/generated/repos.json', 'utf8'));
if (canon.source.repository !== 'wayseer00/wayseer.github.io') throw new Error(`unexpected canon repository: ${canon.source.repository}`);
if (canon.source.path !== 'canon/the_interdependent_way.md') throw new Error(`unexpected canon path: ${canon.source.path}`);
if (canon.source.repository !== 'wayseer00/main') throw new Error(`unexpected canon repository: ${canon.source.repository}`);
Comment thread
erinepshovel-code marked this conversation as resolved.
if (canon.source.path !== 'canon/INTERDEPENDENT_WAY.txt') throw new Error(`unexpected canon path: ${canon.source.path}`);
if (!canon.source.contentSha256 || canon.source.contentSha256.length !== 64) throw new Error('canon missing SHA-256 digest');
if (!canon.source.fallback && (!canon.source.commit || !canon.source.blob)) throw new Error('remote canon provenance missing commit or blob SHA');
if (!canon.units.length || canon.units.some(unit => !unit.hash || !unit.id)) throw new Error('canon units missing identity or hash');
if (repos.publicRepoCount !== repos.generatedRouteCount) throw new Error('repo route mismatch');
if (new Set(repos.repositories.map(repo => repo.slug)).size !== repos.repositories.length) throw new Error('duplicate project slug');
Expand Down
2 changes: 1 addition & 1 deletion src/source/unit.njk
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ title: "Source: {{ unit.title }}"
---
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="/">Home</a> / <a href="/way/{{ unit.routeSlug }}/">{{ unit.title }}</a> / Exact source</nav>
<header class="page-head"><p class="eyebrow">Exact source · deliberate depth</p><h1>{{ unit.title }}</h1><p class="lede">Verbatim source content and machine-verifiable provenance. Commentary elsewhere on this site is subordinate to this layer.</p></header>
<dl class="meta provenance"><dt>Canonical repository alias</dt><dd>{{ generated.canon.source.repository }}</dd><dt>Path</dt><dd>{{ generated.canon.source.path }}</dd><dt>Branch</dt><dd>{{ generated.canon.source.branch }}</dd><dt>Retrieved</dt><dd>{{ generated.canon.source.retrievedAt }}</dd><dt>Recovery mirror used</dt><dd>{{ generated.canon.source.fallback }}</dd><dt>Canonical unit ID</dt><dd><code>{{ unit.id }}</code></dd><dt>Stable route</dt><dd><code>{{ unit.routeSlug }}</code></dd><dt>Source lines</dt><dd>{{ unit.startLine }}–{{ unit.endLine }}</dd><dt>Unit digest</dt><dd><code>{{ unit.hash }}</code></dd><dt>Document digest</dt><dd><code>{{ generated.canon.source.contentSha256 }}</code></dd></dl>
<dl class="meta provenance"><dt>Canonical repository</dt><dd>{{ generated.canon.source.repository }}</dd><dt>Path</dt><dd>{{ generated.canon.source.path }}</dd><dt>Branch</dt><dd>{{ generated.canon.source.branch }}</dd><dt>Resolved commit</dt><dd><code>{{ generated.canon.source.commit or 'fallback mirror' }}</code></dd><dt>Blob</dt><dd><code>{{ generated.canon.source.blob or 'fallback mirror' }}</code></dd><dt>Retrieved</dt><dd>{{ generated.canon.source.retrievedAt }}</dd><dt>Recovery mirror used</dt><dd>{{ generated.canon.source.fallback }}</dd><dt>Canonical unit ID</dt><dd><code>{{ unit.id }}</code></dd><dt>Stable route</dt><dd><code>{{ unit.routeSlug }}</code></dd><dt>Source lines</dt><dd>{{ unit.startLine }}–{{ unit.endLine }}</dd><dt>Unit digest</dt><dd><code>{{ unit.hash }}</code></dd><dt>Document digest</dt><dd><code>{{ generated.canon.source.contentSha256 }}</code></dd></dl>
<pre class="source-block">{{ unit.content }}</pre>
8 changes: 6 additions & 2 deletions tests/canon-integrity.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import { readFile } from 'node:fs/promises';

test('canon data preserves Wayseer identity, provenance, and stable unit evidence', async () => {
const canon = JSON.parse(await readFile('src/_data/generated/canon.json', 'utf8'));
assert.equal(canon.source.repository, 'wayseer00/wayseer.github.io');
assert.equal(canon.source.path, 'canon/the_interdependent_way.md');
assert.equal(canon.source.repository, 'wayseer00/main');
assert.equal(canon.source.path, 'canon/INTERDEPENDENT_WAY.txt');
assert.match(canon.source.contentSha256, /^[a-f0-9]{64}$/);
if (!canon.source.fallback) {
assert.match(canon.source.commit, /^[a-f0-9]{40}$/);
assert.match(canon.source.blob, /^[a-f0-9]{40}$/);
}
assert.ok(canon.units.length > 0);
const routes = new Set();
for (const unit of canon.units) {
Expand Down
Loading