diff --git a/.eleventy.js b/.eleventy.js
index e0446bb..b9bcf6d 100644
--- a/.eleventy.js
+++ b/.eleventy.js
@@ -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) {
@@ -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 || '')));
return {
dir: { input: 'src', output: '_site', includes: '_includes', data: '_data' },
diff --git a/.gitignore b/.gitignore
index ba3421c..8d3e13d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@ _site/
# Build-time generated data
/src/_data/generated/canon.json
+/src/_data/generated/textbook.json
# Python
__pycache__/
diff --git a/README.md b/README.md
index 97fdc61..d03dcc3 100644
--- a/README.md
+++ b/README.md
@@ -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.
@@ -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:
@@ -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
@@ -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).
diff --git a/package.json b/package.json
index 73ebae8..1a14a1f 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/scripts/fetch-textbook.mjs b/scripts/fetch-textbook.mjs
new file mode 100644
index 0000000..c1659a7
--- /dev/null
+++ b/scripts/fetch-textbook.mjs
@@ -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'));
+ 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}`);
diff --git a/scripts/prepare-tests.mjs b/scripts/prepare-tests.mjs
index f55694a..25faf86 100644
--- a/scripts/prepare-tests.mjs
+++ b/scripts/prepare-tests.mjs
@@ -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
@@ -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' });
}
diff --git a/scripts/validate-content.mjs b/scripts/validate-content.mjs
index fc796bd..bcd85fe 100644
--- a/scripts/validate-content.mjs
+++ b/scripts/validate-content.mjs
@@ -5,18 +5,18 @@ import { access, readFile } from 'node:fs/promises';
// id: generated_content_gate
// module_name: validate-content
// module_kind: instrument
-// summary: Refuses deployment when canon identity, heading hierarchy, snapshot integrity, generated route coverage, or recovery artifacts drift.
+// summary: Refuses deployment when canon identity, heading hierarchy, textbook coverage, snapshot integrity, generated route coverage, or recovery artifacts drift.
// owner: Erin Spencer
// public_surface: npm run validate
-// internal_surface: canon snapshot digest, heading hierarchy, and repository-route assertions
+// internal_surface: canon snapshot digest, heading hierarchy, distributed-textbook provenance, and repository-route assertions
// auth_boundary: none
// storage_boundary: read
// network_boundary: none
// user_data_boundary: none
// admin_only: false
-// tests: tests/canon-parser.test.mjs, tests/canon-integrity.test.mjs, tests/repo-coverage.test.mjs, tests/site-contract.test.mjs
+// tests: tests/canon-parser.test.mjs, tests/canon-integrity.test.mjs, tests/textbook-integrity.test.mjs, tests/repo-coverage.test.mjs, tests/site-contract.test.mjs
// rollout: required by npm run build and npm run check
-// rollback: remove the gate only with an explicit replacement preserving provenance, hierarchy, and route checks
+// rollback: remove the gate only with an explicit replacement preserving provenance, hierarchy, textbook, and route checks
// === END MODULE_BUILD ===
// Usage: run `npm run validate`; it refreshes data first and exits nonzero on any integrity mismatch.
// Limits: validates repository artifacts, not GitHub Pages settings or public DNS.
@@ -37,6 +37,8 @@ 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'));
+const textbook = JSON.parse(await readFile('src/_data/generated/textbook.json', 'utf8'));
+const textbookSources = JSON.parse(await readFile('src/_data/textbook_sources.json', 'utf8'));
const snapshotRaw = await readFile('src/_data/snapshots/canon.last-known-good.md', 'utf8');
const snapshotText = snapshotRaw.replace(/^---\n[\s\S]*?\n---\n/, '');
const snapshotHash = createHash('sha256').update(snapshotText).digest('hex');
@@ -60,8 +62,29 @@ if (preamble.level !== 2 || preamble.section !== 'preamble') throw new Error('Pr
if (!canon.source.fallback) {
const interdefinablesIndex = canon.sections.findIndex(section => section.title === 'The Interdefinables');
const preambleIndex = canon.sections.findIndex(section => section.title === 'Preamble');
- if (interdefinablesIndex < 0 || preambleIndex !== interdefinablesIndex + 1) {
- throw new Error('Preamble must be the next major section after The Interdefinables');
+ if (interdefinablesIndex < 0 || preambleIndex !== interdefinablesIndex + 1) throw new Error('Preamble must be the next major section after The Interdefinables');
+}
+
+if (textbook.schema !== 'interdependency.distributed-textbook/1.0.0') throw new Error(`unexpected textbook schema: ${textbook.schema}`);
+if (!Array.isArray(textbookSources) || textbookSources.length !== 8) throw new Error('textbook source manifest must contain exactly eight chapters');
+if (!Array.isArray(textbook.chapters) || textbook.chapters.length !== 8 || textbook.chapterCount !== 8) throw new Error('generated textbook must contain chapters zero through seven');
+for (let number = 0; number < 8; number += 1) {
+ const source = textbookSources[number];
+ const chapter = textbook.chapters[number];
+ if (source.number !== number || chapter.number !== number) throw new Error(`textbook chapter order drift at ${number}`);
+ for (const key of ['slug', 'title', 'repository', 'path', 'branch']) {
+ if (chapter[key] !== source[key]) throw new Error(`textbook chapter ${number} ${key} drift`);
+ }
+}
+if (new Set(textbook.chapters.map(chapter => chapter.slug)).size !== 8) throw new Error('duplicate textbook chapter slug');
+if (new Set(textbook.chapters.map(chapter => `${chapter.repository}:${chapter.path}`)).size !== 8) throw new Error('duplicate textbook source location');
+if (process.env.OFFLINE !== '1') {
+ if (!textbook.complete || textbook.fallback) throw new Error('production textbook refresh is incomplete or using fallback content');
+ for (const chapter of textbook.chapters) {
+ if (!chapter.content?.trim()) throw new Error(`chapter ${chapter.number} content missing`);
+ if (!/^[a-f0-9]{64}$/.test(chapter.contentSha256 || '')) throw new Error(`chapter ${chapter.number} digest missing`);
+ if (!/^[a-f0-9]{40}$/.test(chapter.commit || '') || !/^[a-f0-9]{40}$/.test(chapter.blob || '')) throw new Error(`chapter ${chapter.number} source identity missing`);
+ if (!chapter.sourceUrl?.startsWith(`https://github.com/${chapter.repository}/blob/${chapter.commit}/`)) throw new Error(`chapter ${chapter.number} exact source URL missing`);
}
}
@@ -69,4 +92,4 @@ if (repos.publicRepoCount !== repos.generatedRouteCount) throw new Error('repo r
if (new Set(repos.repositories.map(repo => repo.slug)).size !== repos.repositories.length) throw new Error('duplicate project slug');
await access('fallback/index.html');
await access('artifacts/four-cuts-1.html');
-console.log(`validated ${canon.units.length} canon units, ${canon.notes.length} notes, canonical heading hierarchy, and ${repos.publicRepoCount} repositories`);
+console.log(`validated ${canon.units.length} canon units, ${canon.notes.length} notes, ${textbook.chapterCount} textbook chapters, canonical hierarchy, and ${repos.publicRepoCount} repositories`);
diff --git a/scripts/write-build-info.mjs b/scripts/write-build-info.mjs
index 785b037..5586eec 100644
--- a/scripts/write-build-info.mjs
+++ b/scripts/write-build-info.mjs
@@ -5,16 +5,16 @@ import { readFile, writeFile } from 'node:fs/promises';
// id: public_build_identity
// module_name: write-build-info
// module_kind: worker
-// summary: Publishes machine-readable site and canon identities for post-deployment verification.
+// summary: Publishes machine-readable site, canon, and distributed-textbook identities for post-deployment verification.
// owner: Erin Spencer
// public_surface: _site/build.json
-// internal_surface: git commit resolution and canonical provenance projection
+// internal_surface: git commit resolution, canonical provenance projection, chapter-source identity projection
// auth_boundary: none
// storage_boundary: write
// network_boundary: none
// user_data_boundary: none
// admin_only: false
-// tests: tests/generated-site.test.mjs
+// tests: tests/generated-site.test.mjs, tests/textbook-integrity.test.mjs
// rollout: runs at the end of npm run build
// rollback: remove build.json and both live identity checks together
// === END MODULE_BUILD ===
@@ -23,7 +23,7 @@ import { readFile, writeFile } from 'node:fs/promises';
// === BOUNDARIES ===
// id: public_build_identity_boundary
-// summary: Reads canon provenance and writes public build identity into the generated site.
+// summary: Reads canon and textbook provenance and writes public build identity into the generated site.
// auth_boundary: none
// storage_boundary: write
// network_boundary: none
@@ -44,6 +44,7 @@ function localCommit() {
}
const canon = JSON.parse(await readFile('src/_data/generated/canon.json', 'utf8'));
+const textbook = JSON.parse(await readFile('src/_data/generated/textbook.json', 'utf8'));
const commit = process.env.GITHUB_SHA || localCommit();
const info = {
repository: process.env.GITHUB_REPOSITORY || 'The-Interdependency/The-Interdependency.github.io',
@@ -56,8 +57,26 @@ const info = {
blob: canon.source.blob,
contentSha256: canon.source.contentSha256,
fallback: Boolean(canon.source.fallback)
+ },
+ distributedTextbook: {
+ schema: textbook.schema,
+ chapterCount: textbook.chapterCount,
+ complete: Boolean(textbook.complete),
+ fallback: Boolean(textbook.fallback),
+ chapters: textbook.chapters.map(chapter => ({
+ number: chapter.number,
+ slug: chapter.slug,
+ title: chapter.title,
+ repository: chapter.repository,
+ path: chapter.path,
+ commit: chapter.commit,
+ blob: chapter.blob,
+ contentSha256: chapter.contentSha256,
+ status: chapter.status,
+ fallback: Boolean(chapter.fallback)
+ }))
}
};
await writeFile('_site/build.json', `${JSON.stringify(info, null, 2)}\n`);
-console.log(`build identity ${commit}`);
+console.log(`build identity ${commit} textbook-chapters=${textbook.chapterCount}`);
diff --git a/src/_data/textbook_sources.json b/src/_data/textbook_sources.json
new file mode 100644
index 0000000..d48e29d
--- /dev/null
+++ b/src/_data/textbook_sources.json
@@ -0,0 +1,90 @@
+[
+ {
+ "number": 0,
+ "display_number": "Zero",
+ "slug": "chapter-zero",
+ "title": "Meta Energy Theory — Axioms, Postulates, and Theorems",
+ "repository": "The-Interdependency/metapat",
+ "path": "CHAPTER_ZERO.md",
+ "branch": "main",
+ "status": "root ontology and theory",
+ "summary": "The invariant vocabulary of distinction, boundary, simplex, tensor, energy-state, registration, and question."
+ },
+ {
+ "number": 1,
+ "display_number": "One",
+ "slug": "chapter-one",
+ "title": "The Subtractive Foundations of the Unit Carrier",
+ "repository": "The-Interdependency/ucns",
+ "path": "docs/chapter-1.md",
+ "branch": "main",
+ "status": "carrier foundations",
+ "summary": "The directed 720° carrier, Structural Null, separated zeros, valuation triad, pairing, typed dispatch, and retained memory."
+ },
+ {
+ "number": 2,
+ "display_number": "Two",
+ "slug": "chapter-two",
+ "title": "Measurement Without Transfer",
+ "repository": "The-Interdependency/edcm",
+ "path": "docs/chapter-2.md",
+ "branch": "main",
+ "status": "measurement discipline",
+ "summary": "Typed absence, frozen canon, evidence compartments, proof-transfer firewalls, identity epochs, and an honest measurement frontier."
+ },
+ {
+ "number": 3,
+ "display_number": "Three",
+ "slug": "chapter-three",
+ "title": "Modules That Speak for Themselves",
+ "repository": "The-Interdependency/skill-lib",
+ "path": "docs/chapter-3.md",
+ "branch": "main",
+ "status": "self-declaration method",
+ "summary": "Module Self-Declared Metadata in Markdown: declarations beside implementation, machine reconciliation, and drift-resistant evidence."
+ },
+ {
+ "number": 4,
+ "display_number": "Four",
+ "slug": "chapter-four",
+ "title": "Canon Without Inversion",
+ "repository": "The-Interdependency/interdependent-lib",
+ "path": "docs/chapter-4.md",
+ "branch": "main",
+ "status": "cross-repository canon placement",
+ "summary": "How a meta-package carries family-level canon without inverting runtime dependencies or duplicating authority."
+ },
+ {
+ "number": 5,
+ "display_number": "Five",
+ "slug": "chapter-five",
+ "title": "One Architecture, Four Layers",
+ "repository": "The-Interdependency/ptcna",
+ "path": "docs/chapter-5.md",
+ "branch": "main",
+ "status": "architecture",
+ "summary": "The Prime Tensor Circled Neural Architecture as one package with neural, circle, seed, and core layers."
+ },
+ {
+ "number": 6,
+ "display_number": "Six",
+ "slug": "chapter-six",
+ "title": "The Instrument",
+ "repository": "The-Interdependency/a0",
+ "path": "docs/chapter-6.md",
+ "branch": "main",
+ "status": "research instrument",
+ "summary": "a0 as the codebase, a0p as the public instrument, and persistent agent structure distinct from interchangeable model providers."
+ },
+ {
+ "number": 7,
+ "display_number": "Seven",
+ "slug": "chapter-seven",
+ "title": "The Echo",
+ "repository": "The-Interdependency/zfae",
+ "path": "docs/chapter-7.md",
+ "branch": "main",
+ "status": "theory under development",
+ "summary": "Zeta Function Alpha Echo as an emergent event claim, explicitly bounded as theory with a falsifiable prediction."
+ }
+]
diff --git a/src/_includes/layouts/base.njk b/src/_includes/layouts/base.njk
index 3b4125b..7a306e0 100644
--- a/src/_includes/layouts/base.njk
+++ b/src/_includes/layouts/base.njk
@@ -24,6 +24,7 @@
Start
Preamble
The Way
+ Textbook
Article Lab
Projects
Artifacts
@@ -36,12 +37,13 @@
diff --git a/src/assets/css/site.css b/src/assets/css/site.css
index 58b52ac..d9995be 100644
--- a/src/assets/css/site.css
+++ b/src/assets/css/site.css
@@ -84,6 +84,28 @@ h3 { font-size: clamp(1.2rem, 2vw, 1.55rem); }
.canon-outline .unit-level-4 { margin-left: clamp(2rem, 6vw, 5rem); }
.canon-outline .unit-level-3 a { border-left: .25rem solid var(--violet); }
.canon-outline .unit-level-4 a { border-left: .25rem solid var(--cyan); background: rgba(9, 13, 24, .76); }
+.chapter-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); gap: 1rem; padding: 0; margin: 1.5rem 0 3rem; list-style: none; }
+.chapter-grid li { min-width: 0; }
+.chapter-card { height: 100%; }
+.chapter-number { display: block; margin-bottom: .4rem; color: rgba(174, 187, 210, .42); font: 800 clamp(2.8rem, 7vw, 5rem)/1 Georgia, "Times New Roman", serif; letter-spacing: -.04em; }
+.chapter-card small { display: block; margin-top: 1rem; color: var(--silver); overflow-wrap: anywhere; }
+.chapter-head { margin-bottom: 1.5rem; }
+.chapter-provenance { margin: 0 0 2rem; }
+.textbook-chapter { max-width: 55rem; margin: 3rem auto; padding: clamp(1.1rem, 4vw, 3.2rem); border: 1px solid var(--line); border-radius: var(--radius); background: linear-gradient(180deg, rgba(16,24,42,.96), rgba(7,10,18,.96)); box-shadow: var(--shadow); }
+.textbook-chapter > :first-child { margin-top: 0; }
+.textbook-chapter h1 { font-size: clamp(2.2rem, 5vw, 4.6rem); }
+.textbook-chapter h1 + h1 { margin-top: .6rem; color: var(--cyan); font-size: clamp(1.55rem, 3.5vw, 2.9rem); letter-spacing: -.025em; }
+.textbook-chapter h2 { margin-top: 3.5rem; padding-top: 1rem; border-top: 1px solid var(--line); }
+.textbook-chapter h3 { margin-top: 2.2rem; }
+.textbook-chapter p, .textbook-chapter li { max-width: 76ch; }
+.textbook-chapter blockquote { margin: 1.5rem 0; padding: .2rem 1.2rem; border-left: .3rem solid var(--violet); color: var(--silver); }
+.textbook-chapter pre { max-width: 100%; padding: 1rem; overflow: auto; border: 1px solid var(--line); border-radius: .7rem; background: #05070c; }
+.textbook-chapter code { overflow-wrap: anywhere; }
+.textbook-chapter table { display: block; width: 100%; overflow-x: auto; border-collapse: collapse; }
+.textbook-chapter th, .textbook-chapter td { padding: .65rem .8rem; border: 1px solid var(--line); text-align: left; vertical-align: top; }
+.textbook-chapter th { background: var(--night-soft); }
+.textbook-chapter hr { margin: 3rem 0; border: 0; border-top: 1px solid var(--line); }
+.chapter-pagination { display: flex; align-items: center; justify-content: space-between; gap: .75rem; flex-wrap: wrap; margin: 3rem 0; }
.source-block { white-space: pre-wrap; overflow-wrap: anywhere; padding: 1rem; border: 1px solid var(--line); border-radius: .8rem; background: #070a12; color: #dce6fa; font: .92rem/1.6 ui-monospace, SFMono-Regular, Consolas, monospace; }
dl.meta { display: grid; grid-template-columns: minmax(9rem, .35fr) 1fr; gap: .45rem 1rem; }
dl.meta dt { color: var(--silver); }
@@ -110,8 +132,10 @@ summary { cursor: pointer; font-weight: 800; }
.index-list a { grid-template-columns: 1fr; gap: .15rem; }
.canon-outline .unit-level-3 { margin-left: .65rem; }
.canon-outline .unit-level-4 { margin-left: 1.3rem; }
+ .textbook-chapter { padding: 1rem; }
+ .chapter-pagination .button { flex: 1 1 100%; }
.site-footer { grid-template-columns: 1fr; }
.hmmm-boundary { grid-column: auto; grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; } }
-@media print { body { background: white; color: black; } body::before, .site-header, .actions, .noscript { display: none; } .site-main { max-width: none; padding: 0; } .card, .panel, .provenance, .hmmm, details { background: white; color: black; box-shadow: none; } a { color: black; } }
+@media print { body { background: white; color: black; } body::before, .site-header, .actions, .noscript { display: none; } .site-main { max-width: none; padding: 0; } .card, .panel, .provenance, .hmmm, details, .textbook-chapter { background: white; color: black; box-shadow: none; } a { color: black; } }
diff --git a/src/chapters/chapter.njk b/src/chapters/chapter.njk
new file mode 100644
index 0000000..088fc64
--- /dev/null
+++ b/src/chapters/chapter.njk
@@ -0,0 +1,45 @@
+---
+layout: layouts/base.njk
+pagination:
+ data: generated.textbook.chapters
+ size: 1
+ alias: chapter
+permalink: "/chapters/{{ chapter.slug }}/"
+eleventyComputed:
+ title: "Chapter {{ chapter.display_number }} · {{ chapter.title }}"
+---
+
+ Distributed textbook · Chapter {{ chapter.display_number }} This chapter is displayed from The offline build has no retained content snapshot for this chapter. Its declared source remains available through the repository link above; no substitute text has been invented. {{ chapter.retrievalError }} Questions, corrections, and status changes belong to Zero through Seven · eight repositories · one reading sequence The textbook is distributed by design. Each chapter lives with the repository whose subject it teaches, while this surface restores the complete reading order. The source repository retains its own license, status vocabulary, and authority; citation does not transfer theorem, proof, empirical, or canonical status between chapters. This textbook is distinct from the canonical text of The Interdependent Way, which remains in Complete sequence {{ chapter.summary }} The chapters now share a public reading surface, not a merged authority. Corrections still belong first in the repository that owns the chapter; this site should then refresh and display the new exact source identity. Canon · interpretation · research · implementation · frontier Canon · textbook · interpretation · research · implementation · frontier The Interdependent Way is a dense living text and a constellation of attempts to test, explain, implement, challenge, and preserve it. The Preamble is directly available here; deeper orientation and exact source remain available without hiding the entrance. The Interdependent Way is a dense living text and a constellation of attempts to test, explain, implement, challenge, and preserve it. The Preamble is directly available here; the distributed Interdependency textbook now restores chapters Zero through Seven into one reading sequence. Distributed Interdependency textbook Eight chapters now form one continuous technical reading path across Eight rights-article laboratories Re-enter the opening threshold and its reminder that you are not alone. Enter the opening civic claim directly through a stable route generated from the selected canon. Move through the preamble, articles, etiquette, addenda, and their internal structure without beginning at the raw wall of text. Follow the technical architecture from root ontology through carrier, measurement, self-declaration, canon placement, architecture, instrument, and echo. Test each Rights article through its notes, absurd limits, practice contrasts, domain applications, research support, dissent, and limits. Follow selected canon units through companion readings, applications, handbook seeds, and script drafts without losing source boundaries. Every public organization repository receives a generated space, grouped by function and refreshed at build time. Current field snapshot Project data was generated {{ generated.repos.snapshotAt | dateOnly }}{% if generated.repos.fallback %} from the last verified snapshot{% endif %}. Canon digest Project data was generated {{ generated.repos.snapshotAt | dateOnly }}{% if generated.repos.fallback %} from the last verified snapshot{% endif %}. Canon digest Reviewed research now makes initial contact with all eight Rights articles, but coverage remains uneven and deliberately open to stronger dissent, replication, field studies, disability-led critique, and non-Western evidence. The textbook now has a complete public reading spine, while each chapter remains independently revisable in its source repository. The Article Lab research remains deliberately open to stronger dissent, replication, field studies, disability-led critique, and non-Western evidence.{{ chapter.repository }}:{{ chapter.path }}. Its repository retains its license, status vocabulary, and claim authority; this reader does not transfer those statuses to any other chapter or to The Interdependent Way canon.Source evidence
+
+
+Chapter {{ chapter.display_number }} source unavailable
hmmm
{{ chapter.repository }}. This page will follow the source on the next verified build.The Interdependency Textbook
+ Reading boundary
+ wayseer00/main:canon/INTERDEPENDENT_WAY.txt. These chapters explain the wider Interdependency technical architecture and preserve their repository-local claims.Chapters Zero through Seven
+
+ {% for chapter in generated.textbook.chapters %}
+
+{{ chapter.title }}
+ Source state
+
+hmmm
A way through complexity without pretending the tension is gone.
- Chapters Zero through Seven
+ metapat, ucns, edcm, skill-lib, interdependent-lib, ptcna, a0, and zfae. Each chapter remains bound to the license, status vocabulary, and authority of the repository that carries it.From canon excerpt to public practice
@@ -26,6 +32,7 @@ permalink: /home/
Canon-derivedReturn to Awakening
Read the Preamble
Read the Way
Read Chapters Zero–Seven
Enter the Article Lab
Read public articles
Map the Projects
{{ generated.repos.publicRepoCount }} public repositories · {{ generated.canon.units.length }} canonical units
- {{ generated.canon.source.contentSha256 }}.{{ generated.repos.publicRepoCount }} public repositories · {{ generated.canon.units.length }} canonical units · {{ generated.textbook.chapterCount }} textbook chapters
+ {{ generated.canon.source.contentSha256 }}. Textbook complete: {{ generated.textbook.complete }}.hmmm
hmmm