Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6d022d2
Define distributed textbook chapter sources
erinepshovel-code Jul 21, 2026
839a5c2
Fetch distributed textbook chapters
erinepshovel-code Jul 21, 2026
111335c
Wire textbook refresh and tests
erinepshovel-code Jul 21, 2026
5e8b7f4
Prepare textbook data for offline tests
erinepshovel-code Jul 21, 2026
d1a22b6
Ignore generated textbook dataset
erinepshovel-code Jul 21, 2026
f42aa95
Render distributed textbook Markdown
erinepshovel-code Jul 21, 2026
714b2f1
Validate distributed textbook coverage
erinepshovel-code Jul 21, 2026
4cb3cf4
Add distributed textbook index
erinepshovel-code Jul 21, 2026
19f2769
Add distributed textbook chapter reader
erinepshovel-code Jul 21, 2026
f2f2249
Add textbook to primary navigation
erinepshovel-code Jul 21, 2026
ff37262
Feature chapters zero through seven on home
erinepshovel-code Jul 21, 2026
5209c99
Style distributed textbook reader
erinepshovel-code Jul 21, 2026
9587b54
Test distributed textbook source integrity
erinepshovel-code Jul 21, 2026
799408d
Verify all distributed textbook chapter routes
erinepshovel-code Jul 21, 2026
00c25d6
Test distributed textbook navigation
erinepshovel-code Jul 21, 2026
0c90fbc
Audit textbook routes for accessibility
erinepshovel-code Jul 21, 2026
97c94c0
Document distributed textbook routes and provenance
erinepshovel-code Jul 21, 2026
bc260a3
Assert static textbook architecture
erinepshovel-code Jul 21, 2026
5212f46
Publish textbook chapter identities in build manifest
erinepshovel-code Jul 21, 2026
48395ca
Verify textbook identities in public build manifest
erinepshovel-code Jul 21, 2026
d5ee176
Run textbook artifact provenance test
erinepshovel-code Jul 21, 2026
51fe107
Compute chapter browser titles
erinepshovel-code Jul 21, 2026
1a80550
Make textbook rendering assertions markup-aware
erinepshovel-code Jul 21, 2026
902f8ed
Use exact allowlist assertions in textbook tests
erinepshovel-code Jul 21, 2026
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
5 changes: 3 additions & 2 deletions .eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import markdownIt from 'markdown-it';

// === MODULE_BUILD ===
// id: eleventy_site_configuration
// purpose: Build the static-first public knowledge system and copy deliberate fallback artifacts.
// purpose: Build the static-first public knowledge system, render exact distributed-textbook Markdown, and copy deliberate fallback artifacts.
// entrypoint: npm run build
// tests: tests/site-contract.test.mjs
// tests: tests/site-contract.test.mjs, tests/generated-site.test.mjs
// === END MODULE_BUILD ===

export default function configureEleventy(eleventyConfig) {
Expand All @@ -20,6 +20,7 @@ export default function configureEleventy(eleventyConfig) {
eleventyConfig.addFilter('dateOnly', value => value ? String(value).slice(0, 10) : 'hmmm');
eleventyConfig.addFilter('where', (items, key, value) => (items || []).filter(item => item?.[key] === value));
eleventyConfig.addFilter('statusClass', value => `status-${String(value || 'hmmm').toLowerCase().replace(/[^a-z0-9]+/g, '-')}`);
eleventyConfig.addFilter('markdown', value => md.render(String(value || '')));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve textbook math delimiters

For any fetched chapter that uses LaTeX-style math, this filter sends the text through plain markdown-it, which treats backslashes before ( and [ as Markdown escapes; for example \(\mathcal{O}\) and \[ ... \] render as (\mathcal{O}) and [ ... ]. That corrupts mathematical notation while the chapter page still presents the rendered text as exact source material, so the textbook renderer should either support math or protect those delimiters before Markdown rendering.

Useful? React with 👍 / 👎.


return {
dir: { input: 'src', output: '_site', includes: '_includes', data: '_data' },
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ _site/

# Build-time generated data
/src/_data/generated/canon.json
/src/_data/generated/textbook.json

# Python
__pycache__/
Expand Down
46 changes: 38 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
# The Interdependent Way public knowledge system

This repository builds `interdependentway.org`: a static-first, progressively layered entrance to The Interdependent Way, its deliberate tensions, surrounding research, public artifacts, and the repositories attempting implementation.
This repository builds `interdependentway.org`: a static-first, progressively layered entrance to The Interdependent Way, its deliberate tensions, the distributed Interdependency textbook, surrounding research, public artifacts, and the repositories attempting implementation.

## What is authoritative

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.
The canonical text of **The Interdependent Way** 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.

The **distributed Interdependency textbook** is a separate technical reading sequence. Chapters Zero through Seven remain owned by their source repositories:

| Chapter | Title | Source |
|---:|---|---|
| 0 | Meta Energy Theory — Axioms, Postulates, and Theorems | `The-Interdependency/metapat:CHAPTER_ZERO.md` |
| 1 | The Subtractive Foundations of the Unit Carrier | `The-Interdependency/ucns:docs/chapter-1.md` |
| 2 | Measurement Without Transfer | `The-Interdependency/edcm:docs/chapter-2.md` |
| 3 | Modules That Speak for Themselves | `The-Interdependency/skill-lib:docs/chapter-3.md` |
| 4 | Canon Without Inversion | `The-Interdependency/interdependent-lib:docs/chapter-4.md` |
| 5 | One Architecture, Four Layers | `The-Interdependency/ptcna:docs/chapter-5.md` |
| 6 | The Instrument | `The-Interdependency/a0:docs/chapter-6.md` |
| 7 | The Echo | `The-Interdependency/zfae:docs/chapter-7.md` |

This site displays exact, provenance-bearing chapter snapshots. It does not merge licenses or transfer theorem, proof, empirical, frontier, or canonical status between repositories.

## Architecture

- `/` is the canon-derived **Awakening** splash page and public threshold.
- `/home/` is the complete living knowledge-system entrance.
- `/preamble/` remains one click from Awakening and from the primary navigation.
- `/chapters/` is the unified index for the eight-repository textbook.
- `/chapters/chapter-zero/` through `/chapters/chapter-seven/` render exact source Markdown with commit, blob, digest, and repository links.
- Eleventy generates complete HTML into `_site`.
- Pagefind supplies static search.
- GitHub organization and canon data are retrieved at build time, never in a visitor’s browser.
- GitHub organization, canon, and textbook source data are retrieved at build time, never in a visitor’s browser.
- Every public organization repository receives a generated project page.
- `.interdependency/project.yml` supplies reviewed project purpose, maturity, relationships, and links.
- `fallback/` is a dependency-free emergency edition.
Expand All @@ -28,18 +45,30 @@ npm run dev
npm run check
```

Use the recovery snapshots without network access:
Refresh only the textbook source set:

```bash
npm run refresh:textbook
```

Use recovery snapshots without network access:

```bash
OFFLINE=1 npm run build
```

An offline build uses the retained last-known-good textbook snapshot when one exists. If a clean checkout has no retained chapter content, it renders metadata-only `hmmm` records rather than inventing or silently copying text.

Route checks after a build:

```text
/ Awakening threshold
/home/ knowledge-system entrance
/preamble/ direct canonical Preamble
/ Awakening threshold
/home/ knowledge-system entrance
/preamble/ direct canonical Preamble
/chapters/ textbook index
/chapters/chapter-zero/ Chapter Zero
...
/chapters/chapter-seven/ Chapter Seven
```

Add reviewed project metadata to a repository:
Expand All @@ -60,6 +89,7 @@ Place that file at `.interdependency/project.yml`. Until it exists, the public p
## Status language

- **canon** — exact or mechanically derived from the canonical source
- **distributed textbook** — exact source material whose status remains local to its owning repository
- **interpretation** — explanatory material subject to review
- **research** — externally sourced support, dissent, or context
- **implemented** — a working public surface exists
Expand All @@ -68,6 +98,6 @@ Place that file at `.interdependency/project.yml`. Until it exists, the public p

## Release discipline

GitHub Actions runs the workflow action audit, provenance refresh, article-to-canon exactness gate, build, validation, tests, static search generation, browser checks, accessibility checks, deployment, and live build-identity verification. The workflow audit requires full-length commit SHA pins and rejects tag refs, short SHAs, stale SHAs, or unapproved pins for the GitHub-owned actions used by this site. Failed builds do not replace the last successful Pages artifact. Emergency fallback deployment is explicit rather than automatic.
GitHub Actions runs the workflow action audit, canon and textbook provenance refresh, article-to-canon exactness gate, build, validation, tests, static search generation, browser checks, accessibility checks, deployment, and live build-identity verification. The textbook gate requires all eight current source files during an online production build and rejects missing content, reordered chapters, changed source locations, missing source identities, or silent fallback. The workflow action audit requires full-length commit SHA pins and rejects tag refs, short SHAs, stale SHAs, or unapproved pins for the GitHub-owned actions used by this site. Failed builds do not replace the last successful Pages artifact. Emergency fallback deployment is explicit rather than automatic.

Repository source cannot configure the Pages source, custom domain, DNS, HTTPS, or branch protection. The required administrative settings and the release-truth contract are documented in [`docs/pages-release.md`](docs/pages-release.md).
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@
"type": "module",
"scripts": {
"dev": "npm run refresh:data && eleventy --serve",
"refresh:data": "npm run refresh:canon && npm run refresh:github",
"refresh:data": "npm run refresh:canon && npm run refresh:github && npm run refresh:textbook",
"refresh:canon": "node scripts/fetch-canon.mjs && node scripts/parse-canon.mjs",
"refresh:github": "node scripts/fetch-github-org.mjs",
"refresh:textbook": "node scripts/fetch-textbook.mjs",
"research:enrich": "node scripts/enrich-citations.mjs",
"audit:workflows": "node scripts/audit-workflows.mjs",
"prevalidate": "npm run refresh:data",
"validate": "node scripts/validate-content.mjs && node scripts/verify-generated-routes.mjs && node scripts/verify-article-canon.mjs",
"build": "npm run validate && eleventy && pagefind --site _site && node scripts/write-build-info.mjs",
"pretest": "node scripts/prepare-tests.mjs",
"test": "node --test tests/canon-parser.test.mjs tests/canon-integrity.test.mjs tests/offline-project-snapshot.test.mjs tests/repo-coverage.test.mjs tests/research-ledger.test.mjs tests/site-contract.test.mjs",
"test:generated": "node --test tests/generated-site.test.mjs && node tests/links.test.mjs",
"test": "node --test tests/canon-parser.test.mjs tests/canon-integrity.test.mjs tests/textbook-integrity.test.mjs tests/offline-project-snapshot.test.mjs tests/repo-coverage.test.mjs tests/research-ledger.test.mjs tests/site-contract.test.mjs",
"test:generated": "node --test tests/generated-site.test.mjs tests/textbook-generated.test.mjs && node tests/links.test.mjs",
"test:browser": "playwright test",
"test:e2e": "playwright test tests/site.spec.mjs",
"test:a11y": "playwright test tests/accessibility.spec.mjs",
Expand Down
173 changes: 173 additions & 0 deletions scripts/fetch-textbook.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { createHash } from 'node:crypto';
import { execFileSync } from 'node:child_process';
import { mkdir, readFile, writeFile } from 'node:fs/promises';

// === MODULE_BUILD ===
// id: distributed_textbook_fetch
// module_name: fetch-textbook
// module_kind: service
// summary: Resolves chapters zero through seven from their owning repositories and emits one provenance-bearing reading dataset.
// owner: Erin Spencer
// public_surface: npm run refresh:textbook, generated.textbook
// internal_surface: allowlisted GitHub API and raw-file retrieval, chapter-source validation, snapshot fallback
// auth_boundary: optional read-only GITHUB_TOKEN
// storage_boundary: writes generated textbook data and a last-known-good snapshot
// network_boundary: allowlisted read-only HTTPS to api.github.com and raw.githubusercontent.com
// user_data_boundary: none
// admin_only: false
// tests: tests/textbook-integrity.test.mjs, tests/generated-site.test.mjs, tests/site.spec.mjs
// rollout: required by refresh:data before validation and Eleventy generation
// rollback: remove refresh:textbook and the generated chapter routes; source repositories remain unchanged
// === END MODULE_BUILD ===
// Usage: run `npm run refresh:textbook`; set OFFLINE=1 to use a retained snapshot or metadata-only hmmm records.
// Limits: displays exact source Markdown but does not transfer license, theorem, proof, empirical, or canonical status between repositories.

// === BOUNDARIES ===
// id: distributed_textbook_network_boundary
// summary: Reads only the eight declared chapter files from allowlisted GitHub endpoints.
// auth_boundary: optional GitHub read token
// storage_boundary: write beneath src/_data/generated and src/_data/snapshots
// network_boundary: external read-only
// user_data_boundary: none
// admin_only: false
// pii: none
// secrets: GITHUB_TOKEN is passed only as an HTTPS authorization header and never written
// side_effects: generated dataset and snapshot writes
// owner: Erin Spencer
// === END BOUNDARIES ===

const apiOrigin = 'https://api.github.com';
const rawOrigin = 'https://raw.githubusercontent.com';
const allowedOrigins = new Set([apiOrigin, rawOrigin]);
const manifest = JSON.parse(await readFile('src/_data/textbook_sources.json', 'utf8'));
const snapshotPath = 'src/_data/snapshots/textbook.last-known-good.json';
const generatedPath = 'src/_data/generated/textbook.json';
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 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 textbook target: ${url.origin}`);
return execFileSync('curl', ['-fsSL', '--retry', '2', '--max-time', '30', ...extraHeaders, url.href], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe']
});
}

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

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

function encodedRepository(repository) {
const [owner, repo] = repository.split('/');
if (!owner || !repo) throw new Error(`invalid chapter repository ${repository}`);
return { owner: encodeURIComponent(owner), repo: encodeURIComponent(repo) };
}

function encodedPath(path) {
return path.split('/').map(segment => encodeURIComponent(segment)).join('/');
}

function validateManifest() {
if (!Array.isArray(manifest) || manifest.length !== 8) throw new Error('textbook source manifest must contain chapters zero through seven');
const numbers = manifest.map(source => source.number);
if (numbers.some((number, index) => number !== index)) throw new Error('textbook source manifest must be ordered 0 through 7');
if (new Set(manifest.map(source => source.slug)).size !== 8) throw new Error('textbook chapter slugs must be unique');
if (new Set(manifest.map(source => `${source.repository}:${source.path}`)).size !== 8) throw new Error('textbook chapter source locations must be unique');
}

function metadataOnly(source, error) {
return {
...source,
content: null,
contentSha256: null,
commit: null,
blob: null,
resolvedUrl: null,
sourceUrl: `https://github.com/${source.repository}/blob/${source.branch}/${source.path}`,
retrievedAt: null,
fallback: true,
retrievalError: error
};
}

function fetchOne(source) {
const { owner, repo } = encodedRepository(source.repository);
const path = encodedPath(source.path);
const base = `/repos/${owner}/${repo}`;
const commitInfo = getJson(apiUrl(`${base}/commits/${encodeURIComponent(source.branch)}`));
const commit = commitInfo.sha;
if (!/^[a-f0-9]{40}$/i.test(commit)) throw new Error(`${source.repository} branch did not resolve to a commit SHA`);
const fileInfo = getJson(apiUrl(`${base}/contents/${path}`, { ref: commit }));
if (fileInfo.type !== 'file' || !fileInfo.sha) throw new Error(`${source.repository}/${source.path} did not resolve to a file blob`);
const resolvedUrl = new URL(`/${owner}/${repo}/${commit}/${path}`, rawOrigin);
const content = curlText(resolvedUrl);
if (!content.trim()) throw new Error(`${source.repository}/${source.path} is empty`);
if (!content.includes(source.title)) throw new Error(`${source.repository}/${source.path} no longer contains expected title: ${source.title}`);
const chapterHeading = source.number === 0 ? '# Chapter Zero' : `# Chapter ${source.number}:`;
if (!content.includes(chapterHeading)) throw new Error(`${source.repository}/${source.path} no longer declares ${chapterHeading}`);
return {
...source,
content,
contentSha256: createHash('sha256').update(content).digest('hex'),
commit,
blob: fileInfo.sha,
resolvedUrl: resolvedUrl.href,
sourceUrl: `https://github.com/${source.repository}/blob/${commit}/${source.path}`,
retrievedAt: new Date().toISOString(),
fallback: false,
retrievalError: null
};
}

async function readSnapshot() {
try {
const snapshot = JSON.parse(await readFile(snapshotPath, 'utf8'));
return Array.isArray(snapshot?.chapters) ? snapshot : null;
} catch {
return null;
}
}

validateManifest();
await mkdir('src/_data/generated', { recursive: true });
await mkdir('src/_data/snapshots', { recursive: true });
const previous = await readSnapshot();
const previousByNumber = new Map((previous?.chapters || []).map(chapter => [chapter.number, chapter]));
const offline = process.env.OFFLINE === '1';
const chapters = [];

for (const source of manifest) {
if (offline) {
const retained = previousByNumber.get(source.number);
chapters.push(retained ? { ...retained, ...source, fallback: true, retrievalError: 'offline requested; retained last-known-good chapter content' } : metadataOnly(source, 'offline requested; no retained chapter snapshot'));
Comment on lines +148 to +149

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reuse snapshots only for the same source

When OFFLINE=1 and textbook_sources.json changes the repository, path, branch, or title for an existing chapter number, this reuses the retained snapshot by number alone and then overlays the new manifest fields onto the old content and digest. An offline build can therefore display stale text while attributing it to a different source; only reuse the retained chapter when its source identity still matches, otherwise emit the metadata-only fallback.

Useful? React with 👍 / 👎.

continue;
}
try {
chapters.push(fetchOne(source));
} catch (error) {
const retained = previousByNumber.get(source.number);
const message = String(error?.message || error);
chapters.push(retained ? { ...retained, ...source, fallback: true, retrievalError: message } : metadataOnly(source, message));
}
}

const dataset = {
schema: 'interdependency.distributed-textbook/1.0.0',
title: 'The Interdependency — Chapters Zero Through Seven',
generatedAt: new Date().toISOString(),
fallback: chapters.some(chapter => chapter.fallback),
complete: chapters.every(chapter => Boolean(chapter.content)),
chapterCount: chapters.length,
chapters
};

await writeFile(generatedPath, JSON.stringify(dataset, null, 2));
if (!offline && chapters.every(chapter => !chapter.fallback && chapter.content)) await writeFile(snapshotPath, JSON.stringify(dataset, null, 2));
console.log(`textbook chapters=${chapters.length} complete=${dataset.complete} fallback=${dataset.fallback}`);
7 changes: 4 additions & 3 deletions scripts/prepare-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { execFileSync } from 'node:child_process';

// === MODULE_BUILD ===
// id: offline_test_data_preparation
// purpose: Regenerate canon and project data deterministically before npm test without requiring network access or a full site build.
// purpose: Regenerate canon, project, and distributed-textbook data deterministically before npm test without requiring network access or a full site build.
// entrypoint: npm test via the pretest lifecycle
// tests: tests/canon-integrity.test.mjs, tests/repo-coverage.test.mjs
// tests: tests/canon-integrity.test.mjs, tests/textbook-integrity.test.mjs, tests/repo-coverage.test.mjs
// === END MODULE_BUILD ===
// === BOUNDARIES ===
// id: offline_test_preparation
Expand All @@ -17,7 +17,8 @@ const env = { ...process.env, OFFLINE: '1', GITHUB_TOKEN: '' };
for (const script of [
'scripts/fetch-canon.mjs',
'scripts/parse-canon.mjs',
'scripts/fetch-github-org.mjs'
'scripts/fetch-github-org.mjs',
'scripts/fetch-textbook.mjs'
]) {
execFileSync(process.execPath, [script], { env, stdio: 'inherit' });
}
Loading
Loading