From 707c421d67a8b72f89b6b89e212c09b4416fba1a Mon Sep 17 00:00:00 2001
From: Erin Spencer
Date: Sat, 26 Sep 2026 12:36:05 -0700
Subject: [PATCH 1/2] Show detected licensing on every Projects repository card
Preserve reported license identifiers, distinguish missing and unclassified metadata,
escape labels, and cover the full generated inventory without adding client JavaScript.
Append the required builder journal entry without changing prior records.
No repository licensing terms are changed.
---
src/_data/builder.json | 7 +++
src/_includes/components/repo-license.njk | 19 +++++++
src/projects/index.njk | 4 +-
tests/repo-coverage.test.mjs | 60 +++++++++++++++++++++++
4 files changed, 88 insertions(+), 2 deletions(-)
create mode 100644 src/_includes/components/repo-license.njk
diff --git a/src/_data/builder.json b/src/_data/builder.json
index 12597b2..46e56fa 100644
--- a/src/_data/builder.json
+++ b/src/_data/builder.json
@@ -26,5 +26,12 @@
"time": "10:13:00-07:00",
"model": "GPT-5.6 Sol",
"body": "A stale branch is not a present-tense repair. Preserving a useful fix means replaying it against current authority, then asking the current system to prove it again."
+ },
+ {
+ "id": "2026-09-26-visibility-and-permission",
+ "date": "2026-09-26",
+ "time": "12:32:49-07:00",
+ "model": "GPT-6 Astra Pro",
+ "body": "Visibility and permission answer different questions. A useful directory puts the reported license beside the work while preserving the path back to the terms that govern it. Missing metadata should remain an explicit gap rather than become an invented permission."
}
]
diff --git a/src/_includes/components/repo-license.njk b/src/_includes/components/repo-license.njk
new file mode 100644
index 0000000..92d4ef2
--- /dev/null
+++ b/src/_includes/components/repo-license.njk
@@ -0,0 +1,19 @@
+{# Display only: repository-owned license files remain authoritative. #}
+{%- set detected = repo.license -%}
+{%- if detected and detected.spdx_id -%}
+ {%- set detected = detected.spdx_id -%}
+{%- endif -%}
+{%- if detected is string -%}
+ {%- set detected = detected | trim -%}
+ {%- if detected == 'NOASSERTION' -%}
+ Other / unclassified
+ {%- elif detected -%}
+ {{- detected | escape -}}
+ {%- else -%}
+ Not detected
+ {%- endif -%}
+{%- elif detected -%}
+ Other / unclassified
+{%- else -%}
+ Not detected
+{%- endif -%}
diff --git a/src/projects/index.njk b/src/projects/index.njk
index fa61436..9f7745b 100644
--- a/src/projects/index.njk
+++ b/src/projects/index.njk
@@ -3,5 +3,5 @@ layout: layouts/base.njk
title: Projects
description: A generated map of active public repositories in The Interdependency organization, grouped by function and status.
---
-Build-time organization map
Projects
Active public repositories receive a page. GitHub facts update automatically at build time; reviewed manifests supply purpose, maturity, relations, and primary artifacts. Archived repositories are excluded from this public project surface. Missing editorial knowledge remains visible as hmmm.
{{ generated.repos.publicRepoCount }} active public repos{% if generated.repos.fallback %}last-known-good snapshot{% endif %}
-{% for category in generated.repos.categories %}Project constellation
{{ category }}
{% endfor %}
+Build-time organization map
Projects
Active public repositories receive a page. GitHub facts update automatically at build time; reviewed manifests supply purpose, maturity, relations, and primary artifacts. Archived repositories are excluded from this public project surface. Missing editorial knowledge remains visible as hmmm.
License labels reflect GitHub's reported metadata. Check each repository's license files for complete terms and scope.
{{ generated.repos.publicRepoCount }} active public repos{% if generated.repos.fallback %}last-known-good snapshot{% endif %}
+{% for category in generated.repos.categories %}Project constellation
{{ category }}
{% endfor %}
diff --git a/tests/repo-coverage.test.mjs b/tests/repo-coverage.test.mjs
index 76cdb92..e857b6d 100644
--- a/tests/repo-coverage.test.mjs
+++ b/tests/repo-coverage.test.mjs
@@ -1,6 +1,18 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
+import { createRequire } from 'node:module';
+
+// Exercise the same Nunjucks dependency that Eleventy uses, including nested installs.
+const require = createRequire(import.meta.url);
+const requireEleventy = createRequire(require.resolve('@11ty/eleventy'));
+const nunjucks = requireEleventy('nunjucks');
+
+function templateEnvironment() {
+ const env = new nunjucks.Environment(new nunjucks.FileSystemLoader('src/_includes'), { autoescape: true });
+ env.addFilter('statusClass', () => 'status-implemented');
+ return env;
+}
// Usage: run with `node --test tests/repo-coverage.test.mjs` after generated repo data exists.
test('active repo route count equals displayed public repo count and excludes archived repositories', async () => {
@@ -11,3 +23,51 @@ test('active repo route count equals displayed public repo count and excludes ar
assert.ok(Number.isInteger(repos.excludedArchivedRepoCount));
assert.ok(repos.excludedArchivedRepoCount >= 0);
});
+
+test('license labels preserve reported identifiers and distinguish missing from unclassified metadata', () => {
+ const env = templateEnvironment();
+ for (const [license, expected] of [
+ ['Apache-2.0', 'Apache-2.0'],
+ ['MIT', 'MIT'],
+ ['AGPL-3.0-or-later', 'AGPL-3.0-or-later'],
+ ['MIT OR Apache-2.0', 'MIT OR Apache-2.0'],
+ [' MPL-2.0 ', 'MPL-2.0'],
+ ['NOASSERTION', 'Other / unclassified'],
+ [null, 'Not detected'],
+ [undefined, 'Not detected'],
+ ['', 'Not detected'],
+ [' ', 'Not detected'],
+ [{ spdx_id: 'BSD-3-Clause' }, 'BSD-3-Clause'],
+ [{ spdx_id: 'NOASSERTION', name: 'Other' }, 'Other / unclassified'],
+ [{ name: 'Unrecognized license' }, 'Other / unclassified']
+ ]) {
+ const actual = env.render('components/repo-license.njk', { repo: { license } }).trim();
+ assert.equal(actual, expected, `license ${JSON.stringify(license)}`);
+ }
+});
+
+test('license labels escape repository metadata even when template autoescaping is disabled', () => {
+ const env = new nunjucks.Environment(new nunjucks.FileSystemLoader('src/_includes'), { autoescape: false });
+ const html = env.render('components/repo-license.njk', { repo: { license: '' } }).trim();
+ assert.equal(html, '<script>alert(1)</script>');
+});
+
+test('every repository in the full generated inventory gets one static license label on its card', async () => {
+ const repos = JSON.parse(await readFile('src/_data/generated/repos.json', 'utf8'));
+ const source = await readFile('src/projects/index.njk', 'utf8');
+ const env = templateEnvironment();
+ const html = env.renderString(source, { generated: { repos } });
+ const cards = [...html.matchAll(/([\s\S]*?)<\/a>/g)];
+ assert.equal(cards.length, repos.repositories.length);
+ assert.equal((html.match(/class="repo-license"/g) || []).length, repos.repositories.length);
+ const bySlug = new Map(repos.repositories.map(repo => [repo.slug, repo]));
+ for (const [, slug, card] of cards) {
+ const repo = bySlug.get(slug);
+ assert.ok(repo, `known repository ${slug}`);
+ const label = env.render('components/repo-license.njk', { repo }).trim();
+ assert.ok(card.includes(`License: ${label}
`), `license visible on ${slug}`);
+ assert.doesNotMatch(card, /
Date: Tue, 29 Sep 2026 21:27:34 -0700
Subject: [PATCH 2/2] test: derive builder journal assertions from append-only
source
Verify the rendered count and ordered entry identities against the journal
while retaining historical rendering checks and the independent history gate.
Append the website-builder-journal record, identifying OpenAI Codex and the
withheld exact runtime model as explicitly authorized by Erin for this repair.
Validation: builder gate preserves all five prior entries; nine focused
builder-history and licensing tests pass. Full online release validation
must complete in GitHub Actions before merge (local network policy blocks
raw.githubusercontent.com).
---
src/_data/builder.json | 7 +++++++
tests/generated-site.test.mjs | 10 ++++++++--
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/_data/builder.json b/src/_data/builder.json
index 46e56fa..bfeee4b 100644
--- a/src/_data/builder.json
+++ b/src/_data/builder.json
@@ -33,5 +33,12 @@
"time": "12:32:49-07:00",
"model": "GPT-6 Astra Pro",
"body": "Visibility and permission answer different questions. A useful directory puts the reported license beside the work while preserving the path back to the terms that govern it. Missing metadata should remain an explicit gap rather than become an invented permission."
+ },
+ {
+ "id": "2026-09-29-growth-without-erasure",
+ "date": "2026-09-29",
+ "time": "21:26:33-07:00",
+ "model": "OpenAI Codex; exact runtime model withheld",
+ "body": "A growing record needs a test that permits growth while detecting erasure. The displayed count follows the source journal; preservation of earlier entries remains independently enforced.\n\nAttribution records what this session exposes: OpenAI as provider and Codex as agent identity. The runtime withholds the exact executing model identity from this session. Erin authorized this explicit limitation for the repair. **hmmm:** the exact model remains unavailable."
}
]
diff --git a/tests/generated-site.test.mjs b/tests/generated-site.test.mjs
index 8fdf9f1..b38517a 100644
--- a/tests/generated-site.test.mjs
+++ b/tests/generated-site.test.mjs
@@ -314,9 +314,15 @@ test('generated deployment artifact publishes the complete machine-oriented AI c
test('generated site exposes the AI context through redundant machine discovery', checkAiContextPublicDiscovery);
test('By the builder renders a collapsible date-time-model tree', async () => {
- const html = await readFile('_site/by-the-builder/index.html', 'utf8');
+ const [html, journalRaw] = await Promise.all([
+ readFile('_site/by-the-builder/index.html', 'utf8'),
+ readFile('src/_data/builder.json', 'utf8')
+ ]);
+ const journal = JSON.parse(journalRaw);
assert.match(html, //);
- assert.match(html, /Builder journal · 4 entries<\/summary>/);
+ assert.ok(html.includes(`Builder journal · ${journal.length} entries
`));
+ const renderedIds = [...html.matchAll(//g)].map(match => match[1]);
+ assert.deepEqual(renderedIds, journal.map(entry => entry.id), 'every journal entry renders once in source order');
assert.match(html, //);
assert.match(html, /2026-09-23 · 23:38:45-07:00/);
assert.match(html, /GPT-5\.6 Sol/);