diff --git a/scripts/build-site.mjs b/scripts/build-site.mjs index b828261..f1cdc4e 100644 --- a/scripts/build-site.mjs +++ b/scripts/build-site.mjs @@ -483,6 +483,8 @@ ${cfg.faq.map((f) => `

${esc(f.q)}

${esc(f.a)}

--level 2`. - put(join('.well-known', 'flashyos.json'), handshakeJson()); + put(join('.well-known', 'flashyos.json'), handshakeJson(undefined, { source: cfg.links.repo })); put(join('.well-known', 'flashyos-charter.json'), charterJson()); written.push('.well-known/flashyos.json', '.well-known/flashyos-charter.json'); diff --git a/scripts/mesh.mjs b/scripts/mesh.mjs index e433e4d..1494438 100644 --- a/scripts/mesh.mjs +++ b/scripts/mesh.mjs @@ -92,7 +92,7 @@ export function capabilitiesOf(charter) { * the two surfaces cannot disagree. `apiBase`/`webBase`/`appBase` default to the * estate's hosts; a caller may override for a test. */ -export function handshakeFromCharter(charter, { apiBase = 'https://api.flashyos.com', webBase = 'https://flashyos.com', appBase = 'https://app.flashyos.com' } = {}) { +export function handshakeFromCharter(charter, { apiBase = 'https://api.flashyos.com', webBase = 'https://flashyos.com', appBase = 'https://app.flashyos.com', source = null } = {}) { return { mesh: MESH, org: { slug: charter.slug, name: charter.name, profile: `${appBase}/org/${charter.slug}` }, @@ -100,6 +100,12 @@ export function handshakeFromCharter(charter, { apiBase = 'https://api.flashyos. wants: `${apiBase}/api/v1/network/roadmap`, api: apiBase, join: `${webBase}/join`, + // Where this document's source lives — a convention the v1 contract + // ignores, read by flashyos's estate-source survey against the register. + // The value is `links.repo` from site.config.json, the one place this + // repository already declares itself; absent, no field is emitted rather + // than a guess. + ...(source ? { 'x-source': source } : {}), }; } @@ -109,6 +115,6 @@ export function charterJson(charter = validateCharter(loadCharter())) { } /** The bytes served at /.well-known/flashyos.json (the derived handshake). */ -export function handshakeJson(charter = validateCharter(loadCharter())) { - return JSON.stringify(handshakeFromCharter(charter), null, 2) + '\n'; +export function handshakeJson(charter = validateCharter(loadCharter()), options = {}) { + return JSON.stringify(handshakeFromCharter(charter, options), null, 2) + '\n'; } diff --git a/site/.well-known/flashyos.json b/site/.well-known/flashyos.json index 1d85889..8688c75 100644 --- a/site/.well-known/flashyos.json +++ b/site/.well-known/flashyos.json @@ -10,5 +10,6 @@ ], "wants": "https://api.flashyos.com/api/v1/network/roadmap", "api": "https://api.flashyos.com", - "join": "https://flashyos.com/join" + "join": "https://flashyos.com/join", + "x-source": "https://github.com/FlashyLabs/web4" } diff --git a/site/llms.txt b/site/llms.txt index 25af89c..7bdde7c 100644 --- a/site/llms.txt +++ b/site/llms.txt @@ -3,6 +3,8 @@ # The human front door to the stack: the map, the principles, and how a protocol joins. # No repository on the map is public or launched today; this site makes zero external requests. +Source: https://github.com/FlashyLabs/web4 + ## Pages - https://flashylabs.github.io/web4/ — the pitch and the nine-layer table, rendered from stack.json. - https://flashylabs.github.io/web4/stack/ — every layer, its question, its repositories and its status. diff --git a/test/mesh.test.mjs b/test/mesh.test.mjs index a033854..5bce5a1 100644 --- a/test/mesh.test.mjs +++ b/test/mesh.test.mjs @@ -42,7 +42,10 @@ test('charterJson round-trips the charter exactly', () => { }); test('the served mesh files are the derived bytes', () => { - assert.equal(read('site/.well-known/flashyos.json'), handshakeJson()); + // The build passes site.config.json's `links.repo` as the handshake's source + // (test/source.test.mjs holds that), so the derivation here does too. + const cfg = JSON.parse(read('site.config.json')); + assert.equal(read('site/.well-known/flashyos.json'), handshakeJson(undefined, { source: cfg.links.repo })); assert.equal(read('site/.well-known/flashyos-charter.json'), charterJson()); }); diff --git a/test/source.test.mjs b/test/source.test.mjs new file mode 100644 index 0000000..d82597b --- /dev/null +++ b/test/source.test.mjs @@ -0,0 +1,39 @@ +// The served handshake and llms.txt say where this repository's source lives, +// and they say what site.config.json's `links.repo` says — the one place this +// repository already declares itself. The register comparison runs only when +// flashyos is checked out beside this repository (unknown, never passed, when +// it is not). This site carries no Organization JSON-LD, so there is no +// `sameAs` to hold; the FAQPage node is the only structured data it emits. +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { existsSync, readFileSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { handshakeFromCharter, loadCharter, validateCharter } from '../scripts/mesh.mjs'; + +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); +const cfg = JSON.parse(readFileSync(join(ROOT, 'site.config.json'), 'utf8')); + +test('the built handshake carries x-source from links.repo', () => { + const doc = JSON.parse(readFileSync(join(ROOT, 'site', '.well-known', 'flashyos.json'), 'utf8')); + assert.equal(doc['x-source'], cfg.links.repo); +}); + +test('the built llms.txt carries a Source: line from links.repo', () => { + const line = readFileSync(join(ROOT, 'site', 'llms.txt'), 'utf8').split('\n').find((l) => l.startsWith('Source: ')); + assert.equal(line, `Source: ${cfg.links.repo}`); +}); + +test('a handshake derived without a source carries no x-source rather than a guess', () => { + const charter = validateCharter(loadCharter()); + assert.equal('x-source' in handshakeFromCharter(charter), false); + assert.equal(handshakeFromCharter(charter, { source: 'https://example.invalid/r' })['x-source'], 'https://example.invalid/r'); +}); + +test('agrees with the estate register when flashyos is checked out beside this repository', (t) => { + const p = join(ROOT, '..', 'flashyos', 'estate.register.json'); + if (!existsSync(p)) { t.diagnostic('UNKNOWN: no flashyos checkout beside this repository — the register was not compared'); return; } + const row = JSON.parse(readFileSync(p, 'utf8')).rows.find((r) => r.slug === 'web4'); + if (!row) { t.diagnostic('UNKNOWN: the sibling flashyos checkout has no web4 row on its current branch'); return; } + assert.equal(cfg.links.repo, row.url); +});