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
25 changes: 25 additions & 0 deletions .eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,31 @@ export default function configureEleventy(eleventyConfig) {
eleventyConfig.addFilter('edcmMarkdown', value => md.render(String(value || ''))
.replace(/<pre(?![^>]*\btabindex=)([^>]*)>/g, '<pre tabindex="0"$1>')
.replace(/<math(?![^>]*\btabindex=)(?=[^>]*\bdisplay="block")/g, '<math tabindex="0"'));

// The eight publication Article drafts predate the current human-facing UI.
// Normalize their repeated presentation at build time so the Article is primary,
// footnotes are named literally, source/provenance is available on demand, and
// ordinary previous/next/Way navigation remains visible outside that disclosure.
// This is a static HTML transform; it does not depend on browser JavaScript.
eleventyConfig.addTransform('article-reading-hierarchy', function(content) {
const outputPath = this.page?.outputPath || '';
if (!/\/articles\/article-(?:one|two|three|four|five|six|seven|eight)\/index\.html$/.test(outputPath)) return content;
return String(content)
.replace(
/<section class="panel">\s*<h2>Source boundary<\/h2>\s*<p>([\s\S]*?)<\/p>\s*<p>([\s\S]*?)<\/p>\s*<\/section>/,
(_match, boundaryText, linkLine) => {
const links = [...linkLine.matchAll(/<a\b[^>]*>[\s\S]*?<\/a>/g)].map(match => match[0]);
const sourceLink = links.shift() || '';
const navigation = links.length
? `<nav class="article-context-nav" aria-label="Related Article navigation">${links.join(' · ')}</nav>`
: '';
return `<details class="provenance-disclosure"><summary>Source &amp; provenance</summary><h2>Source boundary</h2><p>${boundaryText}</p>${sourceLink ? `<p>${sourceLink}</p>` : ''}</details>${navigation}`;
}
)
.replaceAll('Speaker A · exact canon excerpt', 'Article')
.replaceAll('Speaker B · footnote conversation', 'Footnotes');
});

// Exact canonical body lines of a unit: skip the heading line, stop before
// footnote lines, notes blocks, separators, and sub-headings. Mirrors the
// body extraction in scripts/verify-article-canon.mjs; keep the two in step.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"build": "npm run validate && eleventy && pagefind --site _site && node scripts/write-build-info.mjs",
"pretest": "node scripts/prepare-tests.mjs",
"test": "node --test tests/aicontext.test.mjs tests/edcm-mathematics.test.mjs tests/gonol-relationships.test.mjs tests/post-merge-reconciliation.test.mjs tests/llms-build.test.mjs tests/canon-parser.test.mjs tests/canon-integrity.test.mjs tests/textbook-integrity.test.mjs tests/math-rendering.test.mjs tests/narratives.test.mjs tests/offline-project-snapshot.test.mjs tests/repo-coverage.test.mjs tests/research-ledger.test.mjs tests/works-registry.test.mjs tests/site-contract.test.mjs",
"test:generated": "node --test tests/generated-site.test.mjs tests/textbook-generated.test.mjs tests/math-generated.test.mjs && node tests/links.test.mjs",
"test:generated": "node --test tests/generated-site.test.mjs tests/human-ui-generated.test.mjs tests/textbook-generated.test.mjs tests/math-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
18 changes: 9 additions & 9 deletions src/chapters/chapter.njk
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ eleventyComputed:
<header class="page-head chapter-head">
<p class="eyebrow">Distributed textbook · Chapter {{ chapter.display_number }}</p>
<div class="status-row">
<span class="status status-canon">exact repository source</span>
<span class="status {% if chapter.number == 7 %}status-frontier{% else %}status-research{% endif %}">{{ chapter.status }}</span>
{% if chapter.fallback %}<span class="status status-hmmm">fallback</span>{% endif %}
</div>
<p class="lede">This chapter is displayed from <code>{{ chapter.repository }}:{{ chapter.path }}</code>. 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.</p>
<p class="lede">Each chapter retains the claim authority and status vocabulary of the repository that carries it. Source identity and verification metadata are available after the chapter text.</p>
</header>
<section class="provenance chapter-provenance">
{% if chapter.content %}
<article class="textbook-chapter" data-chapter-number="{{ chapter.number }}" data-source-repository="{{ chapter.repository }}">
{{ chapter.content | markdown | safe }}
</article>
<details class="provenance chapter-provenance">
<summary>Source &amp; provenance</summary>
<h2>Source evidence</h2>
<dl class="meta">
<dt>Repository</dt><dd><code>{{ chapter.repository }}</code></dd>
Expand All @@ -29,13 +33,9 @@ eleventyComputed:
<dt>Retrieved</dt><dd>{{ chapter.retrievedAt or "hmmm" }}</dd>
</dl>
<div class="actions"><a class="button secondary" href="{{ chapter.sourceUrl }}">Open exact source in GitHub</a></div>
</section>
{% if chapter.content %}
<article class="textbook-chapter" data-chapter-number="{{ chapter.number }}" data-source-repository="{{ chapter.repository }}">
{{ chapter.content | markdown | safe }}
</article>
</details>
{% else %}
<section class="hmmm"><h1>Chapter {{ chapter.display_number }} source unavailable</h1><p>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.</p><p>{{ chapter.retrievalError }}</p></section>
<section class="hmmm"><h1>Chapter {{ chapter.display_number }} source unavailable</h1><p>The offline build has no retained content snapshot for this chapter. Its declared source remains available through the repository link below; no substitute text has been invented.</p><p>{{ chapter.retrievalError }}</p><details class="provenance chapter-provenance"><summary>Source &amp; provenance</summary><h2>Source evidence</h2><p><a href="{{ chapter.sourceUrl }}">Open declared source in GitHub</a></p></details></section>
{% endif %}
<nav class="chapter-pagination" aria-label="Textbook chapters">
{% if pagination.href.previous %}<a class="button secondary" href="{{ pagination.href.previous }}">Previous chapter</a>{% endif %}
Expand Down
6 changes: 3 additions & 3 deletions src/lab/index.njk
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
layout: layouts/base.njk
title: Article Lab
description: A structured conversation between canonical body text, notes, research, absurd-limit tests, practice failures, practice strengths, and domain applications.
description: A structured relation between canonical Article text, footnotes, research, absurd-limit tests, practice failures, practice strengths, and domain applications.
---
<header class="page-head">
<p class="eyebrow">Interpretation with visible seams</p>
<h1>Article Lab</h1>
<p class="lede">The Way tree is the primary Article Lab index. This compatibility view collects the eight developed Rights Article Labs: body text and notes remain distinct speakers; reductio ad absurdum exposes destructive overextensions; practice and domain applications test the reading; study-only research remains visibly bounded.</p>
<p class="lede">The Way tree is the primary Article Lab index. This compatibility view collects the eight developed Rights Article Labs: Article text and footnotes remain distinct; reductio ad absurdum exposes destructive overextensions; practice and domain applications test the reading; study-only research remains visibly bounded.</p>
<div class="actions"><a class="button" href="/way/">Open the Way tree</a></div>
</header>
<section>
Expand All @@ -19,7 +19,7 @@ description: A structured conversation between canonical body text, notes, resea
<a class="card" href="/lab/{{ unit.routeSlug }}/">
<span class="status status-interpretation">Lab</span>
<h3>{{ record.public_title }}</h3>
<p>{{ unit.notes.length }} separately parsed note{% if unit.notes.length != 1 %}s{% endif %} · {{ claims.length }} study-linked claim{% if claims.length != 1 %}s{% endif %} · {{ record.applications | length }} application domains</p>
<p>{{ unit.notes.length }} separately parsed footnote{% if unit.notes.length != 1 %}s{% endif %} · {{ claims.length }} study-linked claim{% if claims.length != 1 %}s{% endif %} · {{ record.applications | length }} application domains</p>
</a>
{% endif %}
{% endfor %}
Expand Down
15 changes: 8 additions & 7 deletions src/lab/unit.njk
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,22 @@ title: "Lab: {{ unit.title }}"
{% set claims = research_data.claims | where("article", unit.id) %}
{% set gapMatches = research_data.gaps | where("article", unit.id) %}
{% set gap = gapMatches[0] %}
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="/way/">Start · Way tree</a> / Article Lab / {{ unit.title }}</nav>
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="/way/">The Way</a> / Article Lab / {{ unit.title }}</nav>
<header class="page-head">
<p class="eyebrow">Interpretive laboratory</p>
<h1>{% if lab %}{{ lab.public_title }}{% else %}{{ unit.title }}{% endif %}</h1>
<p class="lede">The body text, notes, research, failure tests, and applications remain separate layers. This is a review surface, not substitute canon and not an edcmbone metric runtime.</p>
<p class="lede">The Article, footnotes, research, failure tests, and applications remain separate layers. This is a review surface, not substitute canon and not an edcmbone metric runtime.</p>
</header>
<section>
<h2>Conversation</h2>
<article class="panel turn"><span class="status status-canon">Speaker A · main text</span>
<h2>Article and footnotes</h2>
<article class="panel turn"><span class="status status-canon">Article</span>
<blockquote class="reading">{% for line in unit.content | canonUnitBody %}<p>{{ line }}</p>{% endfor %}</blockquote>
<p><a href="/source/{{ unit.routeSlug }}/">Open exact source and provenance</a></p></article>
<details class="provenance-disclosure"><summary>Source &amp; provenance</summary><p><a href="/source/{{ unit.routeSlug }}/">Open exact source and provenance</a></p></details>
</article>
{% if unit.notes.length %}
{% for note in unit.notes %}<article class="panel turn note"><span class="status status-interpretation">Speaker B · note {{ note.marker }}</span><p>{{ note.text }}</p></article>{% endfor %}
{% for note in unit.notes %}<article class="panel turn note"><span class="status status-interpretation">Footnotes</span><p><strong>{{ note.marker }}</strong> {{ note.text }}</p></article>{% endfor %}
{% else %}
<article class="hmmm"><h3>No separately parsed note text</h3><p>This unit may contain no numbered annotations, or its tension may be structural rather than footnoted.</p></article>
<article class="hmmm"><h3>No separately parsed footnotes</h3><p>This unit may contain no numbered annotations, or its tension may be structural rather than footnoted.</p></article>
{% endif %}
</section>
{% if lab %}
Expand Down
13 changes: 7 additions & 6 deletions src/preamble/index.njk
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,29 @@ permalink: /preamble/
---
{% set preambles = generated.canon.units | where("title", "Preamble") %}
{% set preamble = preambles[0] %}
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="/">Home</a> / Preamble</nav>
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="/home/">Home</a> / Preamble</nav>
<header class="page-head">
<p class="eyebrow">One-click canon entrance</p>
<h1>Preamble</h1>
<div class="status-row"><span class="status status-canon">exact canon-derived unit</span></div>
<p class="lede">The Preamble is a first-order entrance to The Interdependent Way. This stable route is generated from the selected canonical source and does not maintain a separate copy.</p>
<p class="lede">Read the Preamble first. Source evidence and provenance remain available below the text when verification is needed.</p>
</header>
{% if preamble %}
<section>
<h2>Canonical text</h2>
<pre class="source-block">{{ preamble.content }}</pre>
</section>
<section class="panel">
<details class="provenance chapter-provenance">
<summary>Source &amp; provenance</summary>
<h2>Source evidence</h2>
<dl class="meta">
<dt>Canonical repository</dt><dd><code>{{ generated.canon.source.repository }}</code></dd>
<dt>Canonical path</dt><dd><code>{{ generated.canon.source.path }}</code></dd>
<dt>Source lines</dt><dd>{{ preamble.startLine }}–{{ preamble.endLine }}</dd>
<dt>Unit digest</dt><dd><code>{{ preamble.hash }}</code></dd>
</dl>
</section>
<div class="actions"><a class="button" href="/source/{{ preamble.routeSlug }}/">Read exact source view</a><a class="button secondary" href="/way/">Continue through The Way</a></div>
<div class="actions"><a class="button secondary" href="/source/{{ preamble.routeSlug }}/">Open exact source view</a></div>
</details>
<div class="actions"><a class="button" href="/way/">Continue through The Way</a></div>
{% else %}
<section class="hmmm"><h2>hmmm</h2><p>The selected canon did not produce a Preamble unit. The release tests must treat this as a broken public contract.</p></section>
{% endif %}
8 changes: 4 additions & 4 deletions src/start.njk
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
layout: layouts/base.njk
title: Start here
title: Site orientation
permalink: /start/
description: Orientation to The Interdependent Way, its layered source model, project constellation, and research boundaries.
---
<header class="page-head"><p class="eyebrow">Orientation before density</p><h1>Start here</h1><p class="lede">The Interdependent Way is simultaneously a canonical text, an interpretive problem, a research program, a public project constellation, and a set of frontier claims. Those layers are related, but they are not interchangeable.</p></header>
<header class="page-head"><p class="eyebrow">Orientation before density</p><h1>Site orientation</h1><p class="lede">The Interdependent Way is simultaneously a canonical text, an interpretive problem, a research program, a public project constellation, and a set of frontier claims. Those layers are related, but they are not interchangeable.</p></header>
<section><h2>How to read this site</h2><div class="grid"><article class="card"><span class="status status-canon">Canon</span><h3>What the source says</h3><p>Exact text and mechanically derived structure. Canon wins whenever a companion reading disagrees.</p></article><article class="card"><span class="status status-interpretation">Interpretation</span><h3>What a reading proposes</h3><p>Orientation, plain-language companions, and tension analysis. Useful scaffolding, always subordinate to source.</p></article><article class="card"><span class="status status-research">Research</span><h3>What studies found</h3><p>Only primary studies and systematic evidence-synthesis studies count. Support, dissent, mixed results, limits, and missing directions are classified by review rather than automated sentiment.</p></article><article class="card"><span class="status status-public">Non-study context</span><h3>What rules or institutions say</h3><p>Legislation, treaties, standards, guidelines, frameworks, and doctrine may matter as context, but they are not science and never count as Research.</p></article><article class="card"><span class="status status-implemented">Implementation</span><h3>What has been built</h3><p>Repositories and artifacts that attempt to embody, test, measure, publish, or challenge part of the work.</p></article><article class="card"><span class="status status-frontier">Frontier</span><h3>What remains unsettled</h3><p>Experimental or incomplete claims remain named without borrowing certainty from neighboring formal work.</p></article><article class="card"><span class="status status-hmmm">hmmm</span><h3>What is honestly unresolved</h3><p>A boundary object that records the missing constraint and preserves a place for continuation.</p></article></div></section>
<section class="panel"><h2>A suggested first path</h2><ol><li>Open the <a href="/way/">Way tree</a> and choose one section.</li><li>Expand its title to read the exact canon text.</li><li>Where offered, open that unit’s Article Lab to see body text and notes treated as separate speakers.</li><li>Open exact source when you are ready to verify wording and provenance.</li><li>Use <a href="/projects/">Projects</a> to see what has actually been implemented.</li></ol></section>
<div class="actions"><a class="button" href="/way/">Explore the Way</a><a class="button secondary" href="/projects/">Map the projects</a></div>
<section class="panel"><h2>A suggested first path</h2><ol><li>Open <a href="/way/">The Way</a> and choose one section.</li><li>Expand its title to read the canon text.</li><li>Where offered, open that unit’s Article Lab to examine the Article and Footnotes as distinct layers.</li><li>Open Source &amp; provenance only when you need to verify wording or origin.</li><li>Use <a href="/projects/">Projects</a> to see what has actually been implemented.</li></ol></section>
<div class="actions"><a class="button" href="/way/">Explore The Way</a><a class="button secondary" href="/projects/">Map the projects</a></div>
42 changes: 42 additions & 0 deletions tests/human-ui-generated.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';

test('human reading surfaces keep provenance collapsed and Article labels literal', async () => {
const canon = JSON.parse(await readFile('src/_data/generated/canon.json', 'utf8'));
const articleLab = JSON.parse(await readFile('src/_data/article_lab.json', 'utf8'));
const firstLabRecord = articleLab[0];
const firstLabUnit = canon.units.find(unit => unit.id === firstLabRecord.unit_id);
assert.ok(firstLabUnit, 'first Article Lab must map to a canon unit');

const [base, preamble, chapterZero, lab, publicationArticle, orientation] = await Promise.all([
readFile('_site/home/index.html', 'utf8'),
readFile('_site/preamble/index.html', 'utf8'),
readFile('_site/chapters/chapter-zero/index.html', 'utf8'),
readFile(`_site/lab/${firstLabUnit.routeSlug}/index.html`, 'utf8'),
readFile('_site/articles/article-one/index.html', 'utf8'),
readFile('_site/start/index.html', 'utf8')
]);

assert.match(base, /<a href="\/way\/">The Way<\/a>/);
assert.doesNotMatch(base, /<a[^>]*>Start<\/a>/);

assert.match(preamble, /<details class="provenance chapter-provenance">[\s\S]*<summary>Source &amp; provenance<\/summary>/);
assert.ok(preamble.indexOf('Humanity faces extinction') < preamble.indexOf('Source &amp; provenance'));

assert.match(chapterZero, /<details class="provenance chapter-provenance">[\s\S]*<summary>Source &amp; provenance<\/summary>/);
assert.ok(chapterZero.indexOf('Zero is not nothing') < chapterZero.indexOf('Source &amp; provenance'));

assert.match(lab, />Article<\/span>/);
assert.match(lab, />Footnotes<\/span>/);
assert.doesNotMatch(lab, /Speaker A|Speaker B/);
assert.match(lab, /<details class="provenance-disclosure">[\s\S]*<summary>Source &amp; provenance<\/summary>/);

assert.match(publicationArticle, />Article<\/p>/);
assert.match(publicationArticle, />Footnotes<\/p>/);
assert.doesNotMatch(publicationArticle, /Speaker A|Speaker B/);
assert.match(publicationArticle, /<details class="provenance-disclosure">[\s\S]*<summary>Source &amp; provenance<\/summary>/);

assert.match(orientation, /<h1>Site orientation<\/h1>/);
assert.doesNotMatch(orientation, /Start here|separate speakers/);
});
Loading
Loading