From 4292641b19d050eff27d184e63e9016916277769 Mon Sep 17 00:00:00 2001 From: Erin Spencer Date: Sun, 2 Aug 2026 23:36:08 -0700 Subject: [PATCH] Publish Public Gonol relationship lab --- .eleventy.js | 9 + package.json | 5 +- ...check-gonol-relationship-display-drift.mjs | 112 ++++ src/_data/gonol_relationship_display.json | 140 +++++ src/_data/gonol_relationship_publication.json | 24 + src/artifacts/gonol-relationships.njk | 200 ++++++ src/artifacts/index.njk | 1 + src/assets/css/site.css | 79 +++ .../gonol-relationship-receipt-v1.schema.json | 234 +++++++ src/assets/js/gonol-relationships.js | 569 ++++++++++++++++++ tests/accessibility.spec.mjs | 2 +- tests/gonol-relationships.test.mjs | 113 ++++ tests/site.spec.mjs | 47 ++ 13 files changed, 1532 insertions(+), 3 deletions(-) create mode 100644 scripts/check-gonol-relationship-display-drift.mjs create mode 100644 src/_data/gonol_relationship_display.json create mode 100644 src/_data/gonol_relationship_publication.json create mode 100644 src/artifacts/gonol-relationships.njk create mode 100644 src/assets/data/gonol-relationship-receipt-v1.schema.json create mode 100644 src/assets/js/gonol-relationships.js create mode 100644 tests/gonol-relationships.test.mjs diff --git a/.eleventy.js b/.eleventy.js index 51fb6bb..1c51fe6 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -7,6 +7,14 @@ import { installMathRenderer } from './scripts/markdown-math.mjs'; // entrypoint: npm run build // tests: tests/site-contract.test.mjs, tests/math-rendering.test.mjs, tests/narratives.test.mjs, tests/generated-site.test.mjs // === END MODULE_BUILD === + +// === CONTRACTS === +// id: website_gonol_contract_has_one_exact_public_copy +// given: the commit-pinned UCNS gonol relationship contract enters the Eleventy data graph +// then: the same source bytes are copied to the public data route without runtime fetching or schema shadowing +// class: evidence +// since: 2026-08-03 +// === END CONTRACTS === // Usage: run `npm run build`; Eleventy emits the static site, copies root machine instructions, and preserves dependency-free public reading paths. export default function configureEleventy(eleventyConfig) { @@ -14,6 +22,7 @@ export default function configureEleventy(eleventyConfig) { eleventyConfig.setLibrary('md', md); eleventyConfig.addPassthroughCopy({ 'src/assets': 'assets', + 'src/_data/gonol_relationship_display.json': 'assets/data/gonol-relationship-display-v1.json', 'CNAME': 'CNAME', 'llms.txt': 'llms.txt', 'artifacts/four-cuts-1.html': 'artifacts/four-cuts/index.html', diff --git a/package.json b/package.json index c42fd6f..30623e0 100644 --- a/package.json +++ b/package.json @@ -14,18 +14,19 @@ "build:llms": "node scripts/build-llms.mjs --apply", "check:llms": "node scripts/build-llms.mjs --check", "check:edcm-reference": "node scripts/check-edcm-reference-drift.mjs", + "check:gonol-relationships": "node scripts/check-gonol-relationship-display-drift.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/aicontext.test.mjs tests/edcm-mathematics.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": "node --test tests/aicontext.test.mjs tests/edcm-mathematics.test.mjs tests/gonol-relationships.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:browser": "playwright test", "test:e2e": "playwright test tests/site.spec.mjs", "test:a11y": "playwright test tests/accessibility.spec.mjs", "test:links": "node tests/links.test.mjs", "test:performance": "node scripts/performance-placeholder.mjs", - "check": "npm run audit:workflows && npm run check:llms && npm run check:edcm-reference && npm run build && npm test && npm run test:generated", + "check": "npm run audit:workflows && npm run check:llms && npm run check:edcm-reference && npm run check:gonol-relationships && npm run build && npm test && npm run test:generated", "refresh:works": "node scripts/fetch-works.mjs" }, "dependencies": { diff --git a/scripts/check-gonol-relationship-display-drift.mjs b/scripts/check-gonol-relationship-display-drift.mjs new file mode 100644 index 0000000..bcdd805 --- /dev/null +++ b/scripts/check-gonol-relationship-display-drift.mjs @@ -0,0 +1,112 @@ +// === MODULE_BUILD === +// id: gonol_relationship_display_drift_check +// module_name: check-gonol-relationship-display-drift +// module_kind: validator +// summary: verify the website consumes one exact commit-pinned UCNS relationship-display contract and receipt schema +// owner: Erin Spencer +// public_surface: validateGonolRelationshipPublication +// internal_surface: readJson, sha256, assert +// auth_boundary: none +// storage_boundary: read-only repository files +// network_boundary: none; validation never follows a moving branch +// user_data_boundary: none +// admin_only: false +// tests: tests/gonol-relationships.test.mjs +// rollout: dependent publication consumer of the draft UCNS relationship-display contract +// rollback: remove the artifact page, pinned data, validator, and route tests together +// since: 2026-08-03 +// unresolved: producer merge order, seven-gonol geometry, continuous frame, English lexical floor, and embedding law +// === END MODULE_BUILD === + +// === CONTRACTS === +// id: website_gonol_contract_pin_is_exact +// given: the local UCNS display contract and receipt schema are validated +// then: repository, commit, path, blob, SHA-256, primitive range, Public Gonol fixture, and unresolved standings match the declared publication pin +// class: evidence +// since: 2026-08-03 +// +// id: website_gonol_consumer_does_not_select_geometry +// given: primitive seven or a missing comparison policy is inspected +// then: unresolved geometry and represented-only comparison remain explicit without a consumer default +// class: safety +// since: 2026-08-03 +// === END CONTRACTS === + +import { createHash } from 'node:crypto'; +import { readFileSync } from 'node:fs'; + +const CONTRACT_PATH = 'src/_data/gonol_relationship_display.json'; +const PUBLICATION_PATH = 'src/_data/gonol_relationship_publication.json'; +const RECEIPT_SCHEMA_PATH = 'src/assets/data/gonol-relationship-receipt-v1.schema.json'; + +function sha256(bytes) { + return createHash('sha256').update(bytes).digest('hex'); +} + +function readJson(path) { + return JSON.parse(readFileSync(path, 'utf8')); +} + +function assert(condition, message) { + if (!condition) throw new Error(`gonol relationship publication drift: ${message}`); +} + +export function validateGonolRelationshipPublication() { + const contractBytes = readFileSync(CONTRACT_PATH); + const schemaBytes = readFileSync(RECEIPT_SCHEMA_PATH); + const contract = JSON.parse(contractBytes.toString('utf8')); + const publication = readJson(PUBLICATION_PATH); + const receiptSchema = JSON.parse(schemaBytes.toString('utf8')); + + assert(publication.source.repository === 'The-Interdependency/ucns', 'source repository changed'); + assert(publication.source.commit === '9025957b4063c4748429cb56b52d3c9a56157c42', 'source commit changed'); + assert(publication.source.path === 'docs/gonol-relationship-display-v1.json', 'source path changed'); + assert(publication.source.blob === 'fdb94ff829a42c267de5f00f2a752550352a444d', 'source blob changed'); + assert(sha256(contractBytes) === publication.source.sha256, 'contract byte digest changed'); + assert(publication.source.sha256 === 'd1ac67d4a5d5b4cd0622056af94b24a1404f1f5c7d7dfeb59ee28e5edb73aad5', 'declared contract digest changed'); + + assert(contract.schema_id === 'ucns.gonol-relationship-display', 'contract schema id changed'); + assert(contract.schema_version === '0.1.0', 'contract schema version changed'); + assert(contract.selection_effect === 'none', 'consumer cannot select the contract'); + assert(JSON.stringify(contract.primitive_arities) === '[1,2,3,7]', 'primitive range changed'); + + const primitives = new Map(contract.primitives.map(primitive => [primitive.arity, primitive])); + assert(primitives.get(1)?.projection_id === 'figure-eight-centerline', 'single projection changed'); + assert(primitives.get(2)?.visible_boundary_intersections === 2, 'vesica intersection count changed'); + assert(primitives.get(2)?.scope_circle?.radius === 1.5, 'vesica scope radius changed'); + assert(primitives.get(2)?.relationship_circle?.radius === 0.5, 'vesica relationship radius changed'); + assert(JSON.stringify(primitives.get(3)?.pairwise_vesicas) === '[["A","B"],["B","C"],["C","A"]]', 'triquetra pair order changed'); + assert(primitives.get(3)?.each_vesica_retained === true, 'triquetra must retain each vesica'); + assert(primitives.get(7)?.geometry_standing === 'hmmm-unresolved', 'seven geometry must remain unresolved'); + assert(primitives.get(7)?.pairing_plan_required === true, 'seven requires an explicit pairing plan'); + + const tokens = contract.public_gonol.tokens; + assert(tokens.length === 157, 'Public Gonol arity changed'); + assert(new Set(tokens).size === 157, 'Public Gonol tokens are not unique'); + assert(tokens[0] === ' ', 'Public Gonol origin changed'); + assert(tokens.indexOf('0') === 139, 'digit zero position changed'); + assert(sha256(Buffer.from(JSON.stringify(tokens))) === contract.public_gonol.sha256, 'Public Gonol source-compatible digest changed'); + assert(contract.public_gonol.space_manifestation_code_points.length === 25, 'SPACE manifestation pin changed'); + assert(new Set(contract.public_gonol.space_manifestation_code_points).size === 25, 'SPACE manifestation pin is not unique'); + assert(contract.public_gonol.space_manifestation_code_points.includes('U+0020'), 'U+0020 origin manifestation missing'); + + assert(sha256(schemaBytes) === publication.receipt_schema.sha256, 'receipt schema byte digest changed'); + assert(receiptSchema.properties?.schema_id?.const === 'ucns.gonol-relationship-receipt', 'receipt schema identity changed'); + assert(receiptSchema.properties?.schema_version?.const === '0.1.0', 'receipt schema version changed'); + assert(publication.authority_transfer === false, 'publication cannot receive authority'); + assert(publication.measurement_status_transfer === false, 'publication cannot receive measurement status'); + assert(publication.empirical_status_transfer === false, 'publication cannot receive empirical status'); + assert(publication.producer_authenticated === false, 'content identity cannot claim producer authentication'); + + return { + contractSha256: publication.source.sha256, + receiptSchemaSha256: publication.receipt_schema.sha256, + primitiveArities: contract.primitive_arities, + publicGonolArity: tokens.length + }; +} + +const result = validateGonolRelationshipPublication(); +if (process.argv[1]?.endsWith('check-gonol-relationship-display-drift.mjs')) { + console.log(`gonol relationship publication: pinned ${result.contractSha256}; primitives ${result.primitiveArities.join('/')}; Public Gonol ${result.publicGonolArity}`); +} diff --git a/src/_data/gonol_relationship_display.json b/src/_data/gonol_relationship_display.json new file mode 100644 index 0000000..fdb94ff --- /dev/null +++ b/src/_data/gonol_relationship_display.json @@ -0,0 +1,140 @@ +{ + "schema_id": "ucns.gonol-relationship-display", + "schema_version": "0.1.0", + "authority": "Erin Spencer", + "recorded_on": "2026-08-03", + "record_status": "authority-declared-option-preserving-proposal", + "selection_effect": "none", + "primitive_arities": [1, 2, 3, 7], + "scale_contract": { + "admission": "any-declared-native-scale", + "display_policy_id": "ucns.display.relation-equalized", + "display_policy_version": "0.1.0", + "display_radius": 1, + "native_scale_retained": true, + "visible_radius_represents_native_scale": false, + "comparison_policy_default": null, + "comparison_policy_required_for_result": true, + "information_loss": [ + "native-scale magnitude is not encoded by visible radius", + "continuous Mobius band width and local-frame motion are not encoded by the centerline projection" + ] + }, + "primitives": [ + { + "arity": 1, + "primitive_id": "single-mobius", + "projection_id": "figure-eight-centerline", + "operand_count": 1, + "pairwise_receipt_count": 0, + "geometry_standing": "declared-static-projection", + "path": { + "parameter": "t in [0, 2*pi)", + "x": "sin(t)", + "y": "sin(t)*cos(t)" + }, + "crossing_semantics": "two traversal occurrences at one projected coordinate; not a vertex, seam, second gonol, or Structural Null", + "continuous_frame_required_for_motion": true + }, + { + "arity": 2, + "primitive_id": "vesica-mobius", + "projection_id": "equal-circle-vesica-piscis", + "operand_count": 2, + "pairwise_receipt_count": 1, + "geometry_standing": "declared-static-projection", + "operand_radius": 1, + "centers": [ + {"operand_slot": "A", "x_exact": "-1/2", "y_exact": "0", "x_binary64": -0.5, "y_binary64": 0}, + {"operand_slot": "B", "x_exact": "1/2", "y_exact": "0", "x_binary64": 0.5, "y_binary64": 0} + ], + "center_distance": 1, + "visible_boundary_intersections": 2, + "intersection_points_exact": [ + {"x": "0", "y": "sqrt(3)/2"}, + {"x": "0", "y": "-sqrt(3)/2"} + ], + "pairwise_vesicas": [["A", "B"]], + "scope_circle": {"center": "operand midpoint", "radius": 1.5}, + "relationship_circle": {"center": "operand midpoint", "radius": 0.5}, + "derived_circle_semantics": "display layers only; no implicit operand, gonol, vertex, or Structural Null promotion" + }, + { + "arity": 3, + "primitive_id": "triquetra-retained-vesicas", + "projection_id": "equilateral-three-circle-triquetra", + "operand_count": 3, + "pairwise_receipt_count": 3, + "geometry_standing": "declared-static-projection", + "operand_radius": 1, + "centers": [ + {"operand_slot": "A", "x_exact": "-1/2", "y_exact": "-sqrt(3)/6", "x_binary64": -0.5, "y_binary64": -0.28867513459481287}, + {"operand_slot": "B", "x_exact": "1/2", "y_exact": "-sqrt(3)/6", "x_binary64": 0.5, "y_binary64": -0.28867513459481287}, + {"operand_slot": "C", "x_exact": "0", "y_exact": "sqrt(3)/3", "x_binary64": 0, "y_binary64": 0.5773502691896257} + ], + "pairwise_vesicas": [["A", "B"], ["B", "C"], ["C", "A"]], + "each_vesica_retained": true, + "coordinate_coincidences_merge_occurrences": false, + "joint_overlap_semantics": "joint context; not an implicit fourth pair, gonol, or vertex" + }, + { + "arity": 7, + "primitive_id": "full-ucns-seven", + "projection_id": null, + "operand_count": 7, + "pairwise_receipt_count": null, + "geometry_standing": "hmmm-unresolved", + "pairing_plan_required": true, + "forbidden_implicit_substitutions": ["flower-of-life", "heptagon", "complete-graph", "seven-circle-rosette"] + } + ], + "public_gonol": { + "id": "public-gonol-157", + "arity": 157, + "token_identity": "unicode-code-point", + "source_repository": "The-Interdependency/a0-betatest", + "source_commit": "7af8debf6ef3905f01baff02b43d8c3bee16ccbc", + "source_path": "backend/interdependent_lib/gonal/gonal.py", + "sha256": "55d10c84529a4d7bc7714786357e977b68d9df2ac3f73d20e229580b552c2ef5", + "origin_position": 0, + "origin_token": " ", + "digit_zero_position": 139, + "normalization": "none-preserve-source", + "space_manifestation_code_points": [ + "U+0009", "U+000A", "U+000B", "U+000C", "U+000D", "U+0020", "U+0085", "U+00A0", + "U+1680", "U+2000", "U+2001", "U+2002", "U+2003", "U+2004", "U+2005", "U+2006", + "U+2007", "U+2008", "U+2009", "U+200A", "U+2028", "U+2029", "U+202F", "U+205F", "U+3000" + ], + "space_semantics": "source-preserved boundary and superpositioned nesting interface; not a word-gonol vertex", + "occurrence_address_vector": ["source_ordinal", "unicode_scalar_value", "carrier_position_or_null"], + "vector_standing": "identity-and-address-evidence-not-geometric-embedding", + "tokens": [ + " ", "A", "!", "\"", "B", "#", "$", "C", "%", "(", "D", "&", "'", "E", "1", "*", + "F", "+", "[", "G", ",", "-", "H", ".", "/", "I", "3", "{", "J", ":", ";", "K", + "=", "?", "L", "<", "@", "M", "5", "\\", "N", "^", "_", "O", "‘", "`", "P", "|", + "~", "Q", "7", "…", "R", "“", "—", "S", "–", "·", "T", "°", "«", "U", "9", "±", + "V", "×", "÷", "W", "√", "∂", "X", "∫", "∑", "Y", "∏", "∇", "Z", "∞", "≈", "≠", + "a", "≤", "≥", "b", "→", "←", "c", ")", "↑", "d", "↓", "2", "e", "↔", "⊕", "f", + "]", "⊗", "g", "⊙", "⊘", "h", "∈", "4", "i", "}", "∉", "j", "⊂", "⊃", "k", "⊆", + ">", "l", "⊇", "6", "m", "∩", "∪", "n", "∧", "’", "o", "∨", "¬", "p", "∀", "8", + "q", "∃", "”", "r", "⊢", "⊨", "s", "∴", "∵", "t", "»", "0", "u", "≡", "ψ", "v", + "φ", "ω", "w", "α", "β", "x", "γ", "δ", "y", "λ", "π", "z", "σ" + ] + }, + "receipt_schema": "ucns.gonol-relationship-receipt/0.1.0", + "nonclaims": [ + "not a complete UCNSObject", + "not a selected Mobius embedding or continuous motion law", + "not canonical structural or numerical comparison", + "not a higher-gonol composition law", + "not a completed English lexical-floor embedding", + "not proof, measurement, empirical, certification, or producer-authentication evidence" + ], + "hmmm": [ + "continuous local-frame geometry across the Mobius band", + "seven-gonol display geometry and pairing plan", + "promotion law for derived scope or relationship circles", + "cross-scope and higher-gonol composition", + "English root policy, lexical source completion, and hyperdimensional embedding law" + ] +} diff --git a/src/_data/gonol_relationship_publication.json b/src/_data/gonol_relationship_publication.json new file mode 100644 index 0000000..06de7d6 --- /dev/null +++ b/src/_data/gonol_relationship_publication.json @@ -0,0 +1,24 @@ +{ + "source": { + "repository": "The-Interdependency/ucns", + "commit": "9025957b4063c4748429cb56b52d3c9a56157c42", + "path": "docs/gonol-relationship-display-v1.json", + "blob": "fdb94ff829a42c267de5f00f2a752550352a444d", + "sha256": "d1ac67d4a5d5b4cd0622056af94b24a1404f1f5c7d7dfeb59ee28e5edb73aad5", + "url": "https://github.com/The-Interdependency/ucns/blob/9025957b4063c4748429cb56b52d3c9a56157c42/docs/gonol-relationship-display-v1.json" + }, + "receipt_schema": { + "path": "/assets/data/gonol-relationship-receipt-v1.schema.json", + "source_path": "docs/gonol-relationship-receipt-v1.schema.json", + "blob": "d83e4423612160a8a500e4d5bc4a9379bd4260b8", + "sha256": "a6565d92e233768fd306f21f2a40a0f38867b32c5740e26fd299c0e23af1d9bf" + }, + "producer_pr": "https://github.com/The-Interdependency/ucns/pull/168", + "publication_status": "dependent-draft-consumer", + "authority_transfer": false, + "proof_status_transfer": false, + "measurement_status_transfer": false, + "empirical_status_transfer": false, + "producer_authenticated": false, + "hmmm": "The seven-gonol geometry, continuous moving frame, cross-scope composition, completed English lexical floor, root policy, and hyperdimensional embedding law remain unresolved." +} diff --git a/src/artifacts/gonol-relationships.njk b/src/artifacts/gonol-relationships.njk new file mode 100644 index 0000000..eb3387a --- /dev/null +++ b/src/artifacts/gonol-relationships.njk @@ -0,0 +1,200 @@ +--- +layout: layouts/base.njk +title: "Public Gonol relationship lab" +description: Assemble exact Unicode occurrence-address vectors on the Public Gonol and inspect one-, two-, three-, and seven-gonol relationship displays without hiding unresolved UCNS choices. +permalink: /artifacts/gonol-relationships/ +--- + + +
+

Commit-pinned UCNS display consumer · interactive evidence receipt

+

Public Gonol relationship lab

+

Assemble exact Unicode occurrences against the 157-position Public Gonol, then place one, two, three, or seven gonols in an option-preserving relationship view. Native scales remain visible even when the circles are equalized for display.

+
+ + + +
+

Relationship workspace

+ + + +
+
+ Primitive arity +

One, two, three, and seven coexist as the declared range. Selecting one changes the view; it does not demote the others.

+
+ {% for arity in gonol_relationship_display.primitive_arities %} + + {% endfor %} +
+
+ +
+ Explicit view policies + + + +

Visible radii use {{ gonol_relationship_display.scale_contract.display_policy_id }}/{{ gonol_relationship_display.scale_contract.display_policy_version }}. They never claim that native scales are equal.

+
+
+ +
+
+
+

Centerline projection · not the carrier

+

Vesica Möbius

+
+

declared static projection

+
+

The two centerlines intersect at two visible coordinate events. The lens, both full operand circles, outer scope circle, and inner relationship circle remain present.

+ + Static vesica Möbius relationship + Two equal circles whose centers are one radius apart, with their complete overlap lens, an enclosing scope circle, and a smaller relationship circle inside the lens. + + + + + + + + + + + A + B + +

operand centerline retained vesica scope circle relationship circle

+
+ + {% set operandSlots = ["A", "B", "C", "D", "E", "F", "G"] %} + {% set operandNames = ["root", "route", "language", "meaning", "relation", "scale", "context"] %} + {% set operandScales = ["word", "word", "lexical-family", "declared-scale-D", "declared-scale-E", "declared-scale-F", "declared-scale-G"] %} +
+

Exact operands

+

Enter exact source text or a serialized gonol. No normalization or case folding is applied. A source-preserved SPACE manifestation is a nesting boundary, not a word-gonol vertex; a non-SPACE unassigned scalar remains positive coverage evidence.

+
+ {% for slot in operandSlots %} +
1 %} hidden{% endif %}> +

Operand {{ slot }}

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
Awaiting exact occurrence assembly.
+
+ {% endfor %} +
+
+ +
+
+
+

Exact source-provenance carrier · 157 Unicode code points

+

Public Gonol occurrence view

+
+ +
+

Each address remains present as a frame position. Highlighting registers only the non-SPACE Unicode occurrences possessed by the selected operand. Address position is not a source-derived geometric coordinate.

+
Awaiting exact occurrence assembly.
+
    + {% for token in gonol_relationship_display.public_gonol.tokens %} +
  1. + {{ loop.index0 }} + {{ token | json }} + +
  2. + {% endfor %} +
+
+ +
+

Pairwise vesica receipts

+

Primitive three retains all three vesicas. Coordinate coincidence does not merge their receipt identities.

+
+ {% for pair in ["AB", "BC", "CA"] %} +
+

Retained vesica {{ pair[0] }} ↔ {{ pair[1] }}

+

{{ pair[0] }} ↔ {{ pair[1] }}

+
Awaiting relationship receipt.
+
+ {% endfor %} +
+
+ +
+

Complete relationship receipt

+

The generated JSON remains local to this page. It identifies the commit-pinned producer contract, native scales, explicit projection and comparison policies, retained pairwise relationships, declared information loss, and unresolved constraints.

+
+ + Open receipt schema + Open machine contract +
+
Awaiting exact relationship assembly.
+

+
+
+ +
+

Primitive range and display standing

+
+ + + + {% for primitive in gonol_relationship_display.primitives %} + + + + + + + {% endfor %} + +
ArityPrimitiveProjectionRelationship boundary
{{ primitive.arity }}{{ primitive.primitive_id }}{{ primitive.projection_id or "hmmm — unresolved" }}{% if primitive.arity == 1 %}One continuous path; projected crossing is not a vertex.{% elif primitive.arity == 2 %}One vesica, two visible centerline intersections, scope and relationship circles retained.{% elif primitive.arity == 3 %}AB, BC, and CA remain three recoverable vesicas inside the joint triquetra view.{% else %}Seven identities may be retained; no geometry or pairing count is inferred.{% endif %}
+
+
+ +
+

Source identity and drift boundary

+
+
Repository
{{ gonol_relationship_publication.source.repository }}
+
Commit
{{ gonol_relationship_publication.source.commit }}
+
Path
{{ gonol_relationship_publication.source.path }}
+
Git blob
{{ gonol_relationship_publication.source.blob }}
+
SHA-256
{{ gonol_relationship_publication.source.sha256 }}
+
Public Gonol source
{{ gonol_relationship_display.public_gonol.source_repository }}@{{ gonol_relationship_display.public_gonol.source_commit }}
+
Public Gonol digest
{{ gonol_relationship_display.public_gonol.sha256 }}
+
+

Open exact UCNS machine contract

+
+ +

hmmm

{{ gonol_relationship_publication.hmmm }}

The occurrence-address vector starts the English embedding work by preserving exact character identity, source order, multiplicity, carrier address, and scale declaration. It is not the completed English lexical floor or the missing hyperdimensional geometric assignment law.

+ + + + diff --git a/src/artifacts/index.njk b/src/artifacts/index.njk index 4812fd9..be9d458 100644 --- a/src/artifacts/index.njk +++ b/src/artifacts/index.njk @@ -5,6 +5,7 @@ description: Public visual studies, interactive explainers, compact publications ---

Public artifacts

Things made to be used, tested, and shared

Artifacts inherit the site’s navigation, epistemic labels, accessibility floor, and fallback discipline while keeping the visual character required by their subject.

+ Interactive · source-pinned

Public Gonol relationships

Assemble exact Unicode occurrence-address vectors and inspect one, vesica, triquetra, or unresolved seven-gonol displays while every native scale and pairwise receipt remains visible.

Commit-pinned copy

EDCM mathematics

The exact EDCM mathematical reference, reproduced from one immutable EDCM commit without transferring mathematical or status authority to this website.

Data visualization

Four Cuts of the Same Country

Wealth and federal income tax shown through four honest but non-identical cuts, with incompatible seams left visible rather than falsely aligned.

diff --git a/src/assets/css/site.css b/src/assets/css/site.css index 9009876..41a21e1 100644 --- a/src/assets/css/site.css +++ b/src/assets/css/site.css @@ -156,6 +156,81 @@ details { margin: 1rem 0; } summary { cursor: pointer; font-weight: 800; } .category { margin-top: 4rem; padding-top: 1rem; border-top: 1px solid var(--line); } .artifact-frame { width: 100%; min-height: 72vh; border: 1px solid var(--line); border-radius: var(--radius); background: #111; } +.gonol-lab { display: grid; gap: 1.5rem; } +.gonol-lab > *, .gonol-operand-grid, .gonol-pair-grid { min-width: 0; } +.gonol-control-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; } +.gonol-control-grid fieldset { min-width: 0; margin: 0; } +.gonol-control-grid legend { padding: 0 .4rem; color: var(--starlight); font: 800 1.05rem/1.2 Georgia, "Times New Roman", serif; } +.gonol-segmented { display: grid; grid-template-columns: repeat(4, 1fr); gap: .55rem; margin-top: 1rem; } +.gonol-segmented label { position: relative; cursor: pointer; } +.gonol-segmented input { position: absolute; inset: 50% auto auto 50%; width: 1px; height: 1px; opacity: 0; } +.gonol-segmented span { display: grid; min-height: 3.1rem; place-items: center; border: 1px solid var(--line); border-radius: .7rem; background: var(--night); color: var(--silver); font: 800 1.15rem/1 ui-monospace, monospace; } +.gonol-segmented input:checked + span { border-color: var(--cyan); color: var(--night); background: var(--cyan); box-shadow: 0 0 0 2px var(--night), 0 0 0 4px var(--cyan); } +.gonol-segmented input:focus-visible + span { outline: 3px solid var(--amber); outline-offset: 4px; } +.gonol-policy-controls { display: grid; gap: .65rem; align-content: start; } +.gonol-policy-controls select, .gonol-stage-head select, .gonol-field-row input, .gonol-field-stack textarea { width: 100%; min-height: 2.75rem; padding: .6rem .7rem; border: 1px solid var(--line); border-radius: .55rem; color: var(--starlight); background: var(--night); font: inherit; } +.gonol-field-stack textarea { resize: vertical; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; } +.gonol-check { display: flex; align-items: flex-start; gap: .55rem; margin-top: .3rem; } +.gonol-check input { width: 1.15rem; height: 1.15rem; margin-top: .22rem; accent-color: var(--cyan); } +.field-help { margin: .45rem 0 0; color: var(--silver); font-size: .92rem; } +.gonol-stage-panel { overflow: hidden; } +.gonol-stage-head { display: flex; align-items: start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; } +.gonol-stage-head h3, .gonol-stage-head p { margin-top: 0; } +.gonol-stage-head label { display: grid; gap: .35rem; color: var(--silver); font-size: .9rem; } +.gonol-stage { display: block; width: 100%; min-height: 22rem; margin: 1rem 0; border: 1px solid var(--line); border-radius: .9rem; background: radial-gradient(circle at 50% 48%, rgba(123, 201, 216, .08), transparent 35%), #070a12; } +.gonol-operand-ring, .gonol-scope-ring, .gonol-relationship-ring, .gonol-figure-eight { vector-effect: non-scaling-stroke; fill: none; } +.gonol-operand-ring { stroke: var(--starlight); stroke-width: 3; } +.gonol-ring-a { stroke: var(--cyan); } +.gonol-ring-b { stroke: var(--violet); } +.gonol-ring-c { stroke: var(--scarlet); } +.gonol-lens { fill: rgba(240, 195, 106, .22); stroke: var(--amber); stroke-width: 1.5; vector-effect: non-scaling-stroke; } +.gonol-lens-bc { fill: rgba(123, 201, 216, .2); } +.gonol-lens-ca { fill: rgba(155, 135, 245, .2); } +.gonol-scope-ring { stroke: var(--silver); stroke-width: 1.5; stroke-dasharray: 11 8; opacity: .72; } +.gonol-relationship-ring { stroke: var(--amber); stroke-width: 2; stroke-dasharray: 2 6; } +.gonol-intersection { fill: var(--night); stroke: var(--amber); stroke-width: 3; vector-effect: non-scaling-stroke; } +.gonol-svg-label { fill: var(--starlight); font: 800 1.15rem/1 ui-monospace, monospace; text-anchor: middle; paint-order: stroke; stroke: var(--night); stroke-width: 5; } +.gonol-svg-scale { fill: var(--silver); font: 700 .72rem/1 ui-monospace, monospace; text-anchor: middle; paint-order: stroke; stroke: var(--night); stroke-width: 4; } +.gonol-stage-legend { display: flex; flex-wrap: wrap; gap: .65rem 1.2rem; color: var(--silver); font-size: .82rem; } +.gonol-stage-legend span { display: inline-flex; align-items: center; gap: .4rem; } +.gonol-stage-legend i { width: 1.9rem; height: .7rem; border: 2px solid currentColor; } +.legend-operand { color: var(--cyan); border-radius: 999px; } +.legend-lens { color: var(--amber); background: rgba(240, 195, 106, .22); } +.legend-scope { color: var(--silver); border-style: dashed !important; } +.legend-relation { color: var(--amber); border-style: dotted !important; } +.gonol-figure-eight { stroke: var(--cyan); stroke-width: 4; } +.gonol-address { fill: var(--silver); opacity: .38; } +.gonol-address-active { fill: var(--amber); opacity: 1; stroke: var(--night); stroke-width: 1.5; } +.gonol-projection-crossing { fill: var(--night); stroke: var(--violet); stroke-width: 2; stroke-dasharray: 3 3; } +.gonol-crossing-label { fill: var(--silver); font: 700 .72rem/1 ui-monospace, monospace; text-anchor: middle; } +.gonol-identity-box { fill: rgba(22, 33, 58, .92); stroke: var(--line); stroke-width: 2; } +.gonol-identity-label { fill: var(--starlight); font: 800 .9rem/1 ui-monospace, monospace; } +.gonol-identity-box + .gonol-identity-label + .gonol-svg-scale { text-anchor: start; } +.gonol-seven-hmmm { fill: var(--amber); font: 800 .85rem/1 ui-monospace, monospace; text-anchor: middle; } +.gonol-operand-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); gap: 1rem; } +.gonol-operand { min-width: 0; } +.gonol-operand h4, .gonol-pair h4 { margin: .25rem 0 1rem; font: 800 1.15rem/1.2 Georgia, "Times New Roman", serif; } +.gonol-field-row { display: grid; grid-template-columns: 6.8rem minmax(0, 1fr); gap: .7rem; align-items: center; margin-bottom: .65rem; } +.gonol-field-row label, .gonol-field-stack label { color: var(--silver); font-size: .88rem; } +.gonol-field-stack { display: grid; gap: .35rem; } +.gonol-output { width: 100%; min-width: 0; max-height: 24rem; margin-top: 1rem; overflow: auto; white-space: normal; } +.gonol-output pre, .gonol-code { width: 100%; min-width: 0; margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: #dce6fa; font: .82rem/1.55 ui-monospace, SFMono-Regular, Consolas, monospace; } +.gonol-public-panel { overflow: hidden; } +.public-gonol-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(3.6rem, 1fr)); gap: .35rem; max-height: 34rem; margin: 1rem 0 0; padding: .8rem; overflow: auto; list-style: none; border: 1px solid var(--line); border-radius: .8rem; background: var(--night); } +.public-gonol-grid li { position: relative; display: grid; min-height: 3.8rem; place-items: center; padding: .35rem; border: 1px solid rgba(174, 187, 210, .16); border-radius: .45rem; color: var(--silver); background: rgba(16, 24, 42, .68); } +.public-gonol-grid li.is-origin { border-style: dashed; border-color: var(--violet); } +.public-gonol-grid li.is-possessed { border-width: 2px; border-color: var(--amber); color: var(--starlight); background: rgba(240, 195, 106, .12); } +.public-gonol-position { position: absolute; top: .14rem; left: .28rem; color: rgba(174, 187, 210, .7); font: 700 .58rem/1 ui-monospace, monospace; } +.public-gonol-grid code { font-size: .78rem; } +.public-gonol-count { position: absolute; top: .12rem; right: .25rem; color: var(--amber); font: 800 .65rem/1 ui-monospace, monospace; } +.gonol-pair-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: 1rem; } +.gonol-code { max-height: 25rem; overflow: auto; } +.gonol-live-status { min-height: 1.6rem; color: var(--cyan); } +.gonol-primitive-table-wrap { width: 100%; overflow-x: auto; } +.gonol-primitive-table { width: 100%; min-width: 48rem; border-collapse: collapse; } +.gonol-primitive-table th, .gonol-primitive-table td { padding: .75rem; border: 1px solid var(--line); text-align: left; vertical-align: top; } +.gonol-primitive-table thead th { background: var(--night-soft); } +.gonol-noscript { border-radius: .7rem; } .site-footer { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; padding: 3rem max(1rem, calc((100vw - var(--measure))/2)); border-top: 1px solid var(--line); background: #070a12; color: var(--silver); } .footer-provenance { display: flex; flex-direction: column; font: .78rem/1.7 ui-monospace, monospace; overflow-wrap: anywhere; } .hmmm-boundary { grid-column: 1 / -1; display: grid; grid-template-columns: 5rem 1fr; gap: 1rem; padding-top: 1rem; border-top: 1px solid var(--line); } @@ -180,6 +255,10 @@ summary { cursor: pointer; font-weight: 800; } .criterion .status { justify-self: start; } .journal-list li:nth-child(even) .journal-entry { margin-left: 0; } .continuation-list { columns: 1; } + .gonol-control-grid { grid-template-columns: 1fr; } + .gonol-operand-grid, .gonol-pair-grid { grid-template-columns: minmax(0, 1fr); } + .gonol-field-row { grid-template-columns: 1fr; gap: .25rem; } + .gonol-stage { min-height: 17rem; } .site-footer { grid-template-columns: 1fr; } .hmmm-boundary { grid-column: auto; grid-template-columns: 1fr; } } diff --git a/src/assets/data/gonol-relationship-receipt-v1.schema.json b/src/assets/data/gonol-relationship-receipt-v1.schema.json new file mode 100644 index 0000000..d83e442 --- /dev/null +++ b/src/assets/data/gonol-relationship-receipt-v1.schema.json @@ -0,0 +1,234 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://theinterdependentway.org/schemas/ucns/gonol-relationship-receipt-v1.schema.json", + "title": "UCNS gonol relationship receipt", + "description": "Option-preserving evidence receipt for the declared one-, two-, three-, and seven-gonol display primitives.", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_id", + "schema_version", + "receipt_id", + "primitive_arity", + "operands", + "display_policy", + "pairing_plan", + "pairwise_receipts", + "evidence_status", + "provenance", + "information_loss", + "hmmm" + ], + "properties": { + "schema_id": {"const": "ucns.gonol-relationship-receipt"}, + "schema_version": {"const": "0.1.0"}, + "receipt_id": {"type": "string", "minLength": 1}, + "primitive_arity": {"enum": [1, 2, 3, 7]}, + "operands": { + "type": "array", + "minItems": 1, + "maxItems": 7, + "items": {"$ref": "#/$defs/operand"} + }, + "display_policy": {"$ref": "#/$defs/policy"}, + "pairing_plan": { + "type": "object", + "additionalProperties": false, + "required": ["plan_id", "plan_version", "pairs", "standing"], + "properties": { + "plan_id": {"type": "string", "minLength": 1}, + "plan_version": {"type": "string", "minLength": 1}, + "pairs": { + "type": "array", + "items": { + "type": "array", + "prefixItems": [ + {"type": "string", "minLength": 1}, + {"type": "string", "minLength": 1} + ], + "items": false, + "minItems": 2, + "maxItems": 2 + } + }, + "standing": {"enum": ["declared-static-projection", "hmmm-unresolved"]} + } + }, + "pairwise_receipts": { + "type": "array", + "items": {"$ref": "#/$defs/pairwiseReceipt"} + }, + "joint_context": { + "oneOf": [ + {"type": "null"}, + { + "type": "object", + "additionalProperties": false, + "required": ["standing", "evidence", "promoted_to_gonol"], + "properties": { + "standing": {"enum": ["represented-evidence", "hmmm-unresolved"]}, + "evidence": {"type": "array", "items": {"type": "string"}}, + "promoted_to_gonol": {"const": false} + } + } + ] + }, + "evidence_status": {"enum": ["represented-evidence", "candidate-measured-evidence"]}, + "provenance": { + "type": "object", + "additionalProperties": false, + "required": ["contract_repository", "contract_commit", "contract_path", "contract_sha256", "producer_authenticated"], + "properties": { + "contract_repository": {"const": "The-Interdependency/ucns"}, + "contract_commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, + "contract_path": {"const": "docs/gonol-relationship-display-v1.json"}, + "contract_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "producer_authenticated": {"const": false}, + "consumer_reference": {"type": ["string", "null"]} + } + }, + "information_loss": {"type": "array", "items": {"type": "string", "minLength": 1}}, + "hmmm": {"type": "array", "items": {"type": "string", "minLength": 1}} + }, + "allOf": [ + { + "if": {"properties": {"primitive_arity": {"const": 1}}}, + "then": { + "properties": { + "operands": {"minItems": 1, "maxItems": 1}, + "pairwise_receipts": {"minItems": 0, "maxItems": 0}, + "pairing_plan": { + "properties": {"pairs": {"minItems": 0, "maxItems": 0}} + } + } + } + }, + { + "if": {"properties": {"primitive_arity": {"const": 2}}}, + "then": { + "properties": { + "operands": {"minItems": 2, "maxItems": 2}, + "pairwise_receipts": {"minItems": 1, "maxItems": 1}, + "pairing_plan": { + "properties": {"pairs": {"minItems": 1, "maxItems": 1}} + } + } + } + }, + { + "if": {"properties": {"primitive_arity": {"const": 3}}}, + "then": { + "properties": { + "operands": {"minItems": 3, "maxItems": 3}, + "pairwise_receipts": {"minItems": 3, "maxItems": 3}, + "pairing_plan": { + "properties": {"pairs": {"minItems": 3, "maxItems": 3}} + } + } + } + }, + { + "if": {"properties": {"primitive_arity": {"const": 7}}}, + "then": { + "properties": { + "operands": {"minItems": 7, "maxItems": 7}, + "pairing_plan": { + "properties": {"standing": {"const": "hmmm-unresolved"}} + } + } + } + } + ], + "$defs": { + "operand": { + "type": "object", + "additionalProperties": false, + "required": [ + "operand_id", + "gonol_id", + "occurrence_ordinal", + "source_identity", + "content_adapter", + "content_digest", + "native_scale", + "payload_reference", + "evidence_status", + "hmmm" + ], + "properties": { + "operand_id": {"type": "string", "minLength": 1}, + "gonol_id": {"type": "string", "minLength": 1}, + "occurrence_ordinal": {"type": "integer", "minimum": 0}, + "source_identity": {"type": "string", "minLength": 1}, + "content_adapter": { + "type": "object", + "additionalProperties": false, + "required": ["name", "version"], + "properties": { + "name": {"type": "string", "minLength": 1}, + "version": {"type": "string", "minLength": 1} + } + }, + "content_digest": {"type": ["string", "null"], "pattern": "^[0-9a-f]{64}$"}, + "native_scale": { + "type": "object", + "additionalProperties": false, + "required": ["scale_id", "declaration", "numeric_rank"], + "properties": { + "scale_id": {"type": "string", "minLength": 1}, + "declaration": {"type": "string", "minLength": 1}, + "numeric_rank": {"type": ["number", "null"]} + } + }, + "payload_reference": {"type": "string", "minLength": 1}, + "retained_structure_reference": {"type": ["string", "null"]}, + "trajectory_reference": {"type": ["string", "null"]}, + "evidence_status": {"enum": ["represented-evidence", "candidate-measured-evidence"]}, + "hmmm": {"type": "array", "items": {"type": "string", "minLength": 1}} + } + }, + "policy": { + "type": "object", + "additionalProperties": false, + "required": ["policy_id", "policy_version", "standing", "parameters", "information_loss"], + "properties": { + "policy_id": {"type": "string", "minLength": 1}, + "policy_version": {"type": "string", "minLength": 1}, + "standing": {"enum": ["unresolved", "experiment-candidate", "declared-static-projection"]}, + "parameters": {"type": "object"}, + "information_loss": {"type": "array", "items": {"type": "string", "minLength": 1}} + } + }, + "pairwiseReceipt": { + "type": "object", + "additionalProperties": false, + "required": [ + "pair_id", + "left_operand_id", + "right_operand_id", + "relationship_status", + "comparison_policy", + "comparison_outcome", + "evidence", + "information_loss", + "hmmm" + ], + "properties": { + "pair_id": {"type": "string", "minLength": 1}, + "left_operand_id": {"type": "string", "minLength": 1}, + "right_operand_id": {"type": "string", "minLength": 1}, + "relationship_status": {"enum": ["represented-evidence", "candidate-measured-evidence"]}, + "comparison_policy": { + "oneOf": [ + {"type": "null"}, + {"$ref": "#/$defs/policy"} + ] + }, + "comparison_outcome": {}, + "evidence": {"type": "array", "items": {"type": "string", "minLength": 1}}, + "information_loss": {"type": "array", "items": {"type": "string", "minLength": 1}}, + "hmmm": {"type": "array", "items": {"type": "string", "minLength": 1}} + } + } + } +} diff --git a/src/assets/js/gonol-relationships.js b/src/assets/js/gonol-relationships.js new file mode 100644 index 0000000..7a9b302 --- /dev/null +++ b/src/assets/js/gonol-relationships.js @@ -0,0 +1,569 @@ +// === MODULE_BUILD === +// id: public_gonol_relationship_lab +// module_name: gonol-relationships +// module_kind: interaction +// summary: assemble exact Public Gonol occurrence-address evidence and render commit-pinned one-, two-, three-, and seven-gonol relationship receipts +// owner: Erin Spencer +// public_surface: browser artifact at /artifacts/gonol-relationships/ +// internal_surface: exact UTF-8 digest, occurrence vectorizer, SVG projections, relationship receipt assembler +// auth_boundary: none +// storage_boundary: generated receipts remain in browser memory until the user explicitly downloads one +// network_boundary: none; contract and schema are static commit-pinned assets +// user_data_boundary: entered text remains local to the page and is never normalized, uploaded, or persisted by this module +// admin_only: false +// tests: tests/gonol-relationships.test.mjs, tests/site.spec.mjs, tests/accessibility.spec.mjs +// rollout: dependent draft consumer of UCNS gonol relationship-display contract 0.1.0 +// rollback: remove this module with the artifact page and pinned source data +// since: 2026-08-03 +// unresolved: continuous Mobius frame, seven-form geometry, higher-gonol composition, English lexical floor, root policy, and hyperdimensional embedding +// === END MODULE_BUILD === + +// === CONTRACTS === +// id: public_gonol_occurrences_remain_exact +// given: source text enters an operand field +// then: Unicode scalar occurrences, order, multiplicity, SPACE boundaries, unassigned scalars, and invalid surrogate evidence remain explicit without normalization +// class: evidence +// since: 2026-08-03 +// +// id: gonol_relationship_primitives_preserve_options +// given: the user selects primitive arity one, two, three, or seven +// then: one uses a figure-eight projection, two retains the complete vesica and derived circles, three retains AB/BC/CA vesicas, and seven displays no guessed geometry +// class: doctrine +// since: 2026-08-03 +// +// id: gonol_comparison_requires_explicit_policy +// given: operands at any declared native scales enter a relationship +// then: representation is always available while a comparison outcome exists only under the selected named candidate policy and visible-radius equalization remains declared loss +// class: safety +// since: 2026-08-03 +// +// id: gonol_receipt_retains_producer_boundary +// given: an interactive relationship receipt is emitted +// then: exact UCNS commit, path, blob, contract digest, native scales, policy standing, losses, and hmmm fields remain present without authority transfer +// class: evidence +// since: 2026-08-03 +// === END CONTRACTS === + +const lab = document.querySelector('[data-gonol-lab]'); + +if (lab) { + const SVG_NS = 'http://www.w3.org/2000/svg'; + const SLOTS = ['A', 'B', 'C', 'D', 'E', 'F', 'G']; + const PAIRS = { + 1: [], + 2: [['A', 'B']], + 3: [['A', 'B'], ['B', 'C'], ['C', 'A']], + 7: [] + }; + + const contract = JSON.parse(document.querySelector('#gonol-relationship-contract').textContent); + const publication = JSON.parse(document.querySelector('#gonol-relationship-publication').textContent); + const tokenPosition = new Map(contract.public_gonol.tokens.map((token, position) => [token, position])); + const spaceCodePoints = new Set(contract.public_gonol.space_manifestation_code_points); + + const stage = lab.querySelector('[data-gonol-stage]'); + const stageHeading = lab.querySelector('#relationship-display-heading'); + const stageDescription = lab.querySelector('[data-stage-description]'); + const stageStanding = lab.querySelector('[data-stage-standing]'); + const stageLegend = lab.querySelector('.gonol-stage-legend'); + const comparisonControl = lab.querySelector('[data-comparison-policy]'); + const derivedLayersControl = lab.querySelector('[data-derived-layers]'); + const publicOperandControl = lab.querySelector('[data-public-operand]'); + const vectorOutput = lab.querySelector('[data-vector-output]'); + const receiptOutput = lab.querySelector('[data-receipt-output]'); + const statusOutput = lab.querySelector('[data-gonol-status]'); + const downloadButton = lab.querySelector('[data-download-receipt]'); + const gridItems = Array.from(lab.querySelectorAll('[data-carrier-position]')); + const operandViews = new Map(SLOTS.map(slot => { + const root = lab.querySelector(`[data-operand="${slot}"]`); + return [slot, { + root, + name: root.querySelector('[data-operand-name]'), + scale: root.querySelector('[data-operand-scale]'), + source: root.querySelector('[data-operand-source]'), + payload: root.querySelector('[data-operand-payload]'), + snapshot: root.querySelector('[data-operand-snapshot]') + }]; + })); + + let currentReceiptText = ''; + let updateSequence = 0; + let updateTimer = 0; + + function svgElement(name, attributes = {}, text = null) { + const element = document.createElementNS(SVG_NS, name); + for (const [key, value] of Object.entries(attributes)) element.setAttribute(key, String(value)); + if (text !== null) element.textContent = text; + return element; + } + + function activeArity() { + return Number(lab.querySelector('input[name="gonol-arity"]:checked')?.value || 2); + } + + function codePointLabel(value) { + return `U+${value.codePointAt(0).toString(16).toUpperCase().padStart(4, '0')}`; + } + + function containsUnpairedSurrogate(value) { + for (let index = 0; index < value.length; index += 1) { + const unit = value.charCodeAt(index); + if (unit >= 0xD800 && unit <= 0xDBFF) { + const next = value.charCodeAt(index + 1); + if (!(next >= 0xDC00 && next <= 0xDFFF)) return true; + index += 1; + } else if (unit >= 0xDC00 && unit <= 0xDFFF) { + return true; + } + } + return false; + } + + async function sha256Utf8(value) { + if (containsUnpairedSurrogate(value) || !globalThis.crypto?.subtle) return null; + const digest = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(value)); + return Array.from(new Uint8Array(digest), byte => byte.toString(16).padStart(2, '0')).join(''); + } + + function vectorize(value) { + return Array.from(value).map((sourceValue, sourceOrdinal) => { + const scalar = sourceValue.codePointAt(0); + const invalidSurrogate = scalar >= 0xD800 && scalar <= 0xDFFF; + const codePoint = codePointLabel(sourceValue); + const isSpace = !invalidSurrogate && spaceCodePoints.has(codePoint); + const assignedPosition = isSpace ? 0 : tokenPosition.get(sourceValue); + return { + source_ordinal: sourceOrdinal, + code_point: codePoint, + source_value: sourceValue, + carrier_position: invalidSurrogate ? null : (assignedPosition ?? null), + role: invalidSurrogate ? 'rejected-surrogate' : (isSpace ? 'space-boundary' : 'possessed-character-occurrence'), + instantiates_vertex: !invalidSurrogate && !isSpace, + carrier_assignment: invalidSurrogate ? 'rejected' : (assignedPosition === undefined && !isSpace ? 'unassigned-retained' : 'assigned') + }; + }); + } + + async function assembleOperand(slot, occurrenceOrdinal) { + const view = operandViews.get(slot); + const payload = view.payload.value; + const vectors = vectorize(payload); + const invalidSurrogate = containsUnpairedSurrogate(payload); + const digest = await sha256Utf8(payload); + const gonolId = view.name.value || `hmmm-unset-gonol-${slot}`; + const scaleId = view.scale.value || `hmmm-unset-scale-${slot}`; + const sourceIdentity = view.source.value || `hmmm-unset-source-${slot}`; + const hmmm = [ + 'complete retained-structure and trajectory references were not supplied by this interactive adapter', + 'occurrence-address vectors are identity evidence, not geometric embedding coordinates' + ]; + if (invalidSurrogate) hmmm.push('payload contains an unpaired surrogate; strict Unicode-scalar admission and exact UTF-8 digest fail closed'); + if (!globalThis.crypto?.subtle) hmmm.push('Web Crypto SHA-256 is unavailable; content digest remains unresolved'); + + const snapshot = { + operand_id: `operand-${slot}`, + gonol_id: gonolId, + source_identity: sourceIdentity, + native_scale: scaleId, + exact_payload: payload, + content_digest_sha256: digest, + occurrence_count: vectors.length, + vertex_occurrence_count: vectors.filter(vector => vector.instantiates_vertex).length, + space_boundary_count: vectors.filter(vector => vector.role === 'space-boundary').length, + carrier_unassigned_count: vectors.filter(vector => vector.carrier_assignment === 'unassigned-retained').length, + invalid_surrogate_count: vectors.filter(vector => vector.role === 'rejected-surrogate').length, + hmmm + }; + view.snapshot.textContent = JSON.stringify(snapshot, null, 2); + + return { + slot, + payload, + vectors, + snapshot, + receiptOperand: { + operand_id: `operand-${slot}`, + gonol_id: gonolId, + occurrence_ordinal: occurrenceOrdinal, + source_identity: sourceIdentity, + content_adapter: { name: 'website.exact-utf8-text', version: '0.1.0' }, + content_digest: digest, + native_scale: { scale_id: scaleId, declaration: scaleId, numeric_rank: null }, + payload_reference: digest ? `interactive-session:sha256:${digest}` : `interactive-session:hmmm-unresolved:${slot}`, + retained_structure_reference: null, + trajectory_reference: null, + evidence_status: 'represented-evidence', + hmmm + } + }; + } + + function resetStage(title, description) { + const titleElement = svgElement('title', { id: 'gonol-stage-title' }, title); + const descriptionElement = svgElement('desc', { id: 'gonol-stage-desc' }, description); + const drawing = svgElement('g', { 'aria-hidden': 'true' }); + stage.replaceChildren(titleElement, descriptionElement, drawing); + return drawing; + } + + function addLabel(group, x, y, operand) { + group.append( + svgElement('text', { class: 'gonol-svg-label', x, y }, operand.slot), + svgElement('text', { class: 'gonol-svg-scale', x, y: y + 20 }, operand.receiptOperand.native_scale.scale_id) + ); + } + + function circleIntersections(left, right, radius) { + const dx = right.x - left.x; + const dy = right.y - left.y; + const distance = Math.hypot(dx, dy); + const midpoint = { x: (left.x + right.x) / 2, y: (left.y + right.y) / 2 }; + const height = Math.sqrt(Math.max(0, radius * radius - (distance * distance) / 4)); + const perpendicular = { x: -dy / distance, y: dx / distance }; + return [ + { x: midpoint.x + perpendicular.x * height, y: midpoint.y + perpendicular.y * height }, + { x: midpoint.x - perpendicular.x * height, y: midpoint.y - perpendicular.y * height } + ]; + } + + function addLens(defs, group, left, right, radius, pairId) { + const clipId = `gonol-clip-${pairId.toLowerCase()}`; + const clip = svgElement('clipPath', { id: clipId }); + clip.append(svgElement('circle', { cx: right.x, cy: right.y, r: radius })); + defs.append(clip); + group.append(svgElement('circle', { + class: `gonol-lens gonol-lens-${pairId.toLowerCase()}`, + cx: left.x, + cy: left.y, + r: radius, + 'clip-path': `url(#${clipId})`, + 'data-pair': pairId + })); + } + + function addDerivedPairLayers(group, left, right, radius, pairId) { + if (!derivedLayersControl.checked) return; + const midpoint = { x: (left.x + right.x) / 2, y: (left.y + right.y) / 2 }; + group.append( + svgElement('circle', { class: 'gonol-scope-ring', cx: midpoint.x, cy: midpoint.y, r: radius * 1.5, 'data-pair': pairId }), + svgElement('circle', { class: 'gonol-relationship-ring', cx: midpoint.x, cy: midpoint.y, r: radius * 0.5, 'data-pair': pairId }) + ); + } + + function addPairIntersections(group, left, right, radius, pairId) { + for (const point of circleIntersections(left, right, radius)) { + group.append(svgElement('circle', { + class: 'gonol-intersection', + cx: point.x, + cy: point.y, + r: 5, + 'data-pair-intersection': pairId + })); + } + } + + function renderSingle(operand) { + const description = `One continuous figure-eight projection for ${operand.receiptOperand.gonol_id}. The projected crossing is not a vertex, seam, second gonol, or Structural Null.`; + const drawing = resetStage('Single Möbius gonol as a figure-eight projection', description); + const pathPoints = []; + for (let index = 0; index <= 240; index += 1) { + const t = (Math.PI * 2 * index) / 240; + const x = 320 + 210 * Math.sin(t); + const y = 210 - 178 * Math.sin(t) * Math.cos(t); + pathPoints.push(`${index === 0 ? 'M' : 'L'}${x.toFixed(2)},${y.toFixed(2)}`); + } + drawing.append(svgElement('path', { class: 'gonol-figure-eight', d: pathPoints.join(' ') })); + + const counts = new Map(); + for (const vector of operand.vectors) { + if (vector.instantiates_vertex && vector.carrier_position !== null) counts.set(vector.carrier_position, (counts.get(vector.carrier_position) || 0) + 1); + } + contract.public_gonol.tokens.forEach((token, position) => { + const t = (Math.PI * 2 * position) / contract.public_gonol.arity; + const x = 320 + 210 * Math.sin(t); + const y = 210 - 178 * Math.sin(t) * Math.cos(t); + const count = counts.get(position) || 0; + drawing.append(svgElement('circle', { + class: count ? 'gonol-address gonol-address-active' : 'gonol-address', + cx: x, + cy: y, + r: count ? Math.min(8, 4 + count) : 2, + 'data-address-position': position + })); + }); + drawing.append( + svgElement('circle', { class: 'gonol-projection-crossing', cx: 320, cy: 210, r: 12 }), + svgElement('text', { class: 'gonol-crossing-label', x: 320, y: 235 }, 'projection crossing · no vertex') + ); + stageHeading.textContent = 'Single Möbius gonol'; + stageDescription.textContent = description; + stageStanding.textContent = 'figure-eight projection'; + stageStanding.className = 'status status-interpretation'; + stageLegend.hidden = true; + } + + function renderVesica(operands) { + const radius = 120; + const centers = { A: { x: 250, y: 210 }, B: { x: 370, y: 210 } }; + const description = 'Two equalized centerlines are one radius apart. Both complete circles, the two visible intersection occurrences, their vesica, the outer scope circle, and the inner relationship circle remain recoverable.'; + const drawing = resetStage('Vesica Möbius relationship', description); + const defs = svgElement('defs'); + stage.insertBefore(defs, drawing); + addLens(defs, drawing, centers.A, centers.B, radius, 'AB'); + addDerivedPairLayers(drawing, centers.A, centers.B, radius, 'AB'); + drawing.append( + svgElement('circle', { class: 'gonol-operand-ring gonol-ring-a', cx: centers.A.x, cy: centers.A.y, r: radius }), + svgElement('circle', { class: 'gonol-operand-ring gonol-ring-b', cx: centers.B.x, cy: centers.B.y, r: radius }) + ); + addPairIntersections(drawing, centers.A, centers.B, radius, 'AB'); + addLabel(drawing, 205, 208, operands[0]); + addLabel(drawing, 435, 208, operands[1]); + stageHeading.textContent = 'Vesica Möbius'; + stageDescription.textContent = description; + stageStanding.textContent = 'declared static projection'; + stageStanding.className = 'status status-interpretation'; + stageLegend.hidden = false; + } + + function renderTriquetra(operands) { + const radius = 120; + const centers = { + A: { x: 250, y: 260 }, + B: { x: 370, y: 260 }, + C: { x: 310, y: 156.07695154586736 } + }; + const description = 'Three equalized centerlines form an equilateral construction. AB, BC, and CA remain three complete vesicas with separate receipt and intersection identities; the central overlap is joint context, not a fourth gonol.'; + const drawing = resetStage('Triquetra with three retained vesicas', description); + const defs = svgElement('defs'); + stage.insertBefore(defs, drawing); + for (const [leftSlot, rightSlot] of PAIRS[3]) { + const pairId = `${leftSlot}${rightSlot}`; + addLens(defs, drawing, centers[leftSlot], centers[rightSlot], radius, pairId); + addDerivedPairLayers(drawing, centers[leftSlot], centers[rightSlot], radius, pairId); + } + for (const slot of ['A', 'B', 'C']) { + drawing.append(svgElement('circle', { class: `gonol-operand-ring gonol-ring-${slot.toLowerCase()}`, cx: centers[slot].x, cy: centers[slot].y, r: radius })); + } + for (const [leftSlot, rightSlot] of PAIRS[3]) addPairIntersections(drawing, centers[leftSlot], centers[rightSlot], radius, `${leftSlot}${rightSlot}`); + addLabel(drawing, 205, 284, operands[0]); + addLabel(drawing, 435, 284, operands[1]); + addLabel(drawing, 310, 105, operands[2]); + stageHeading.textContent = 'Triquetra with retained vesicas'; + stageDescription.textContent = description; + stageStanding.textContent = 'three pairwise receipts'; + stageStanding.className = 'status status-interpretation'; + stageLegend.hidden = false; + } + + function renderSeven(operands) { + const description = 'Seven operand identities are retained. UCNS has not supplied a seven-gonol placement or pairing plan, so this view draws no circles, edges, overlaps, center, heptagon, rosette, or flower-of-life substitute.'; + const drawing = resetStage('Seven-gonol identity rack; geometry unresolved', description); + operands.forEach((operand, index) => { + const column = index % 2; + const row = Math.floor(index / 2); + const x = column ? 340 : 70; + const y = 56 + row * 78; + drawing.append( + svgElement('rect', { class: 'gonol-identity-box', x, y, width: 230, height: 56, rx: 12 }), + svgElement('text', { class: 'gonol-identity-label', x: x + 14, y: y + 24 }, `${operand.slot} · ${operand.receiptOperand.gonol_id}`), + svgElement('text', { class: 'gonol-svg-scale', x: x + 14, y: y + 43 }, operand.receiptOperand.native_scale.scale_id) + ); + }); + drawing.append(svgElement('text', { class: 'gonol-seven-hmmm', x: 320, y: 390 }, 'hmmm — seven-form geometry and pairing plan unresolved')); + stageHeading.textContent = 'Seven-gonol primitive'; + stageDescription.textContent = description; + stageStanding.textContent = 'hmmm unresolved'; + stageStanding.className = 'status status-hmmm'; + stageLegend.hidden = true; + } + + function renderStage(arity, operands) { + if (arity === 1) renderSingle(operands[0]); + else if (arity === 2) renderVesica(operands); + else if (arity === 3) renderTriquetra(operands); + else renderSeven(operands); + } + + function updateVisibility(arity) { + SLOTS.forEach((slot, index) => { + operandViews.get(slot).root.hidden = index >= arity; + const option = publicOperandControl.querySelector(`option[value="${slot}"]`); + option.hidden = index >= arity; + }); + if (SLOTS.indexOf(publicOperandControl.value) >= arity) publicOperandControl.value = 'A'; + for (const pairElement of lab.querySelectorAll('.gonol-pair[data-pair]')) { + const pairId = pairElement.dataset.pair; + pairElement.hidden = !PAIRS[arity].some(pair => pair.join('') === pairId); + } + } + + function updatePublicGonol(selectedOperand) { + const counts = new Map(); + for (const vector of selectedOperand.vectors) { + if (vector.instantiates_vertex && vector.carrier_position !== null) counts.set(vector.carrier_position, (counts.get(vector.carrier_position) || 0) + 1); + } + for (const item of gridItems) { + const position = Number(item.dataset.carrierPosition); + const count = counts.get(position) || 0; + item.classList.toggle('is-possessed', count > 0); + item.classList.toggle('is-origin', position === contract.public_gonol.origin_position); + const countLabel = item.querySelector('[data-position-count]'); + countLabel.hidden = count === 0; + countLabel.textContent = count > 1 ? `×${count}` : '●'; + countLabel.setAttribute('aria-label', count ? `${count} possessed occurrence${count === 1 ? '' : 's'}` : ''); + } + vectorOutput.textContent = JSON.stringify({ + operand_id: selectedOperand.receiptOperand.operand_id, + native_scale: selectedOperand.receiptOperand.native_scale, + vector_policy: { + id: 'ucns.public-gonol-occurrence-address', + version: '0.1.0', + standing: contract.public_gonol.vector_standing, + dimensions: contract.public_gonol.occurrence_address_vector + }, + occurrences: selectedOperand.vectors + }, null, 2); + } + + function comparisonPolicy() { + if (comparisonControl.value === 'none') return null; + return { + policy_id: 'website.comparison.exact-utf8-bytes', + policy_version: '0.1.0', + standing: 'experiment-candidate', + parameters: { adapter: 'website.exact-utf8-text/0.1.0', normalization: 'none-preserve-source' }, + information_loss: [ + 'compares exact serialized UTF-8 bytes only', + 'does not establish canonical structural, semantic, geometric, native-scale, or completion equivalence' + ] + }; + } + + function pairwiseReceipt(left, right) { + const policy = comparisonPolicy(); + const comparisonAvailable = Boolean(policy && left.receiptOperand.content_digest && right.receiptOperand.content_digest); + const pairId = `${left.slot}${right.slot}`; + return { + pair_id: `vesica-${pairId.toLowerCase()}`, + left_operand_id: left.receiptOperand.operand_id, + right_operand_id: right.receiptOperand.operand_id, + relationship_status: comparisonAvailable ? 'candidate-measured-evidence' : 'represented-evidence', + comparison_policy: policy, + comparison_outcome: !policy ? null : (comparisonAvailable ? { + status: 'candidate-output', + exact_utf8_equal: left.receiptOperand.content_digest === right.receiptOperand.content_digest, + native_scale_label_equal: left.receiptOperand.native_scale.scale_id === right.receiptOperand.native_scale.scale_id, + left_sha256: left.receiptOperand.content_digest, + right_sha256: right.receiptOperand.content_digest + } : { + status: 'hmmm-unavailable', + reason: 'strict Unicode-scalar admission or Web Crypto digest is unavailable for at least one operand' + }), + evidence: [ + `vesica ${left.slot}↔${right.slot} remains a distinct pairwise relationship occurrence`, + 'equalized display circles have radius 1 and center distance 1', + 'both full operand centerlines, the lens, two intersection occurrences, scope circle, and relationship circle remain recoverable' + ], + information_loss: contract.scale_contract.information_loss, + hmmm: [ + 'visible overlap is a relationship projection, not a source-derived distance or completion claim', + 'native-scale labels have no supplied cross-scale numerical law' + ] + }; + } + + function assembleReceipt(arity, operands) { + const pairs = PAIRS[arity]; + const bySlot = new Map(operands.map(operand => [operand.slot, operand])); + const pairwiseReceipts = pairs.map(([left, right]) => pairwiseReceipt(bySlot.get(left), bySlot.get(right))); + const candidateMeasured = pairwiseReceipts.some(receipt => receipt.relationship_status === 'candidate-measured-evidence'); + const digestIdentity = operands.map(operand => operand.receiptOperand.content_digest?.slice(0, 12) || 'hmmm').join('-'); + const primitive = contract.primitives.find(candidate => candidate.arity === arity); + return { + schema_id: 'ucns.gonol-relationship-receipt', + schema_version: '0.1.0', + receipt_id: `website-gonol-relation:${arity}:${digestIdentity}`, + primitive_arity: arity, + operands: operands.map(operand => operand.receiptOperand), + display_policy: { + policy_id: contract.scale_contract.display_policy_id, + policy_version: contract.scale_contract.display_policy_version, + standing: arity === 7 ? 'unresolved' : 'declared-static-projection', + parameters: { display_radius: contract.scale_contract.display_radius, projection_id: primitive.projection_id }, + information_loss: contract.scale_contract.information_loss + }, + pairing_plan: { + plan_id: arity === 7 ? 'ucns.pairing.hmmm-unresolved' : `ucns.pairing.primitive-${arity}`, + plan_version: '0.1.0', + pairs: pairs.map(pair => pair.map(slot => `operand-${slot}`)), + standing: arity === 7 ? 'hmmm-unresolved' : 'declared-static-projection' + }, + pairwise_receipts: pairwiseReceipts, + joint_context: arity === 3 ? { + standing: 'represented-evidence', + evidence: ['central three-way overlap retained as joint context beside all three pairwise vesicas'], + promoted_to_gonol: false + } : null, + evidence_status: candidateMeasured ? 'candidate-measured-evidence' : 'represented-evidence', + provenance: { + contract_repository: publication.source.repository, + contract_commit: publication.source.commit, + contract_path: publication.source.path, + contract_sha256: publication.source.sha256, + producer_authenticated: false, + consumer_reference: 'The-Interdependency/The-Interdependency.github.io:/artifacts/gonol-relationships/' + }, + information_loss: contract.scale_contract.information_loss, + hmmm: arity === 7 ? contract.hmmm.concat(['no seven-form geometry or pairing count was inferred by the website']) : contract.hmmm + }; + } + + function updatePairOutputs(receipt) { + const byPair = new Map(receipt.pairwise_receipts.map(pair => [pair.left_operand_id.slice(-1) + pair.right_operand_id.slice(-1), pair])); + for (const element of lab.querySelectorAll('[data-pair-receipt]')) { + const pairId = element.closest('[data-pair]').dataset.pair; + element.textContent = byPair.has(pairId) ? JSON.stringify(byPair.get(pairId), null, 2) : 'No pairwise receipt belongs to the selected primitive.'; + } + } + + async function update() { + const sequence = ++updateSequence; + const arity = activeArity(); + updateVisibility(arity); + const operands = await Promise.all(SLOTS.slice(0, arity).map(assembleOperand)); + if (sequence !== updateSequence) return; + + const selectedOperand = operands.find(operand => operand.slot === publicOperandControl.value) || operands[0]; + updatePublicGonol(selectedOperand); + renderStage(arity, operands); + const receipt = assembleReceipt(arity, operands); + updatePairOutputs(receipt); + currentReceiptText = JSON.stringify(receipt, null, 2); + receiptOutput.textContent = currentReceiptText; + statusOutput.textContent = arity === 7 + ? 'Seven operand identities retained; geometry and pairing remain hmmm.' + : `Primitive ${arity} receipt updated with ${receipt.pairwise_receipts.length} retained pairwise vesica${receipt.pairwise_receipts.length === 1 ? '' : 's'}.`; + } + + function scheduleUpdate() { + window.clearTimeout(updateTimer); + updateTimer = window.setTimeout(update, 80); + } + + lab.addEventListener('input', scheduleUpdate); + lab.addEventListener('change', scheduleUpdate); + downloadButton.addEventListener('click', () => { + if (!currentReceiptText) return; + const href = URL.createObjectURL(new Blob([currentReceiptText], { type: 'application/json' })); + const link = document.createElement('a'); + link.href = href; + link.download = `gonol-relationship-${activeArity()}-receipt.json`; + document.body.append(link); + link.click(); + link.remove(); + URL.revokeObjectURL(href); + statusOutput.textContent = 'Relationship receipt downloaded.'; + }); + + update(); +} diff --git a/tests/accessibility.spec.mjs b/tests/accessibility.spec.mjs index 87ca2f4..ff9bf53 100644 --- a/tests/accessibility.spec.mjs +++ b/tests/accessibility.spec.mjs @@ -14,7 +14,7 @@ if (!firstLabUnit) throw new Error(`missing Lab canon unit ${articleLab[0].unit_ for (const route of [ '/', '/home/', '/preamble/', '/chapters/', '/chapters/chapter-zero/', '/chapters/chapter-seven/', '/articles/', '/articles/article-two/', '/narratives/', '/narratives/jack-and-diane/', '/way/', '/lab/', `/lab/${firstLabUnit.routeSlug}/`, '/projects/', - '/artifacts/edcm-mathematics/', '/artifacts/four-cuts/', '/fallback/', '/research/method/' + '/artifacts/gonol-relationships/', '/artifacts/edcm-mathematics/', '/artifacts/four-cuts/', '/fallback/', '/research/method/' ]) { test(`${route} has no serious or critical automated accessibility violations`, async ({ page }) => { await page.goto(route); diff --git a/tests/gonol-relationships.test.mjs b/tests/gonol-relationships.test.mjs new file mode 100644 index 0000000..907d29a --- /dev/null +++ b/tests/gonol-relationships.test.mjs @@ -0,0 +1,113 @@ +// === CHECKS === +// id: check_website_gonol_contract_pin +// proves: website_gonol_contract_pin_is_exact +// call: self::pinned contract validation tests +// mutates: none +// cleanup: none +// +// id: check_website_gonol_contract_public_copy +// proves: website_gonol_contract_has_one_exact_public_copy +// call: self::Eleventy passthrough source test +// mutates: none +// cleanup: none +// +// id: check_public_gonol_occurrence_exactness +// proves: public_gonol_occurrences_remain_exact +// call: self::Public Gonol fixture and source-boundary tests +// mutates: none +// cleanup: none +// +// id: check_gonol_primitive_option_range +// proves: gonol_relationship_primitives_preserve_options +// call: self::primitive geometry and hmmm tests +// mutates: none +// cleanup: none +// +// id: check_gonol_comparison_policy_boundary +// proves: gonol_comparison_requires_explicit_policy +// call: self::interaction source policy tests +// mutates: none +// cleanup: none +// +// id: check_gonol_receipt_provenance_boundary +// proves: gonol_receipt_retains_producer_boundary +// call: self::page and validator provenance tests +// mutates: none +// cleanup: none +// +// id: check_website_gonol_no_selection +// proves: website_gonol_consumer_does_not_select_geometry +// call: self::seven-form and represented-only tests +// mutates: none +// cleanup: none +// === END CHECKS === + +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; + +import { validateGonolRelationshipPublication } from '../scripts/check-gonol-relationship-display-drift.mjs'; + +test('commit-pinned producer contract and receipt schema remain exact', () => { + const result = validateGonolRelationshipPublication(); + assert.equal(result.contractSha256, 'd1ac67d4a5d5b4cd0622056af94b24a1404f1f5c7d7dfeb59ee28e5edb73aad5'); + assert.equal(result.receiptSchemaSha256, 'a6565d92e233768fd306f21f2a40a0f38867b32c5740e26fd299c0e23af1d9bf'); + assert.deepEqual(result.primitiveArities, [1, 2, 3, 7]); + assert.equal(result.publicGonolArity, 157); +}); + +test('Public Gonol fixture preserves exact source and non-vertex SPACE boundary', async () => { + const contract = JSON.parse(await readFile('src/_data/gonol_relationship_display.json', 'utf8')); + const fixture = contract.public_gonol; + assert.equal(fixture.tokens.length, 157); + assert.equal(new Set(fixture.tokens).size, 157); + assert.equal(fixture.tokens[0], ' '); + assert.equal(fixture.tokens.indexOf('0'), 139); + assert.equal(fixture.normalization, 'none-preserve-source'); + assert.equal(fixture.space_manifestation_code_points.length, 25); + assert.match(fixture.space_semantics, /not a word-gonol vertex/); + assert.equal(fixture.vector_standing, 'identity-and-address-evidence-not-geometric-embedding'); +}); + +test('vesica and triquetra geometry retain every declared relationship piece', async () => { + const contract = JSON.parse(await readFile('src/_data/gonol_relationship_display.json', 'utf8')); + const primitives = new Map(contract.primitives.map(primitive => [primitive.arity, primitive])); + assert.equal(primitives.get(1).projection_id, 'figure-eight-centerline'); + assert.equal(primitives.get(2).visible_boundary_intersections, 2); + assert.equal(primitives.get(2).scope_circle.radius, 1.5); + assert.equal(primitives.get(2).relationship_circle.radius, 0.5); + assert.deepEqual(primitives.get(3).pairwise_vesicas, [['A', 'B'], ['B', 'C'], ['C', 'A']]); + assert.equal(primitives.get(3).each_vesica_retained, true); + assert.equal(primitives.get(7).geometry_standing, 'hmmm-unresolved'); + assert.equal(primitives.get(7).pairing_plan_required, true); +}); + +test('artifact is static-first, copy-compatible, and keeps comparison explicit', async () => { + const [page, script, config, packageJson, index] = await Promise.all([ + readFile('src/artifacts/gonol-relationships.njk', 'utf8'), + readFile('src/assets/js/gonol-relationships.js', 'utf8'), + readFile('.eleventy.js', 'utf8'), + readFile('package.json', 'utf8'), + readFile('src/artifacts/index.njk', 'utf8') + ]); + + assert.match(page, /permalink: \/artifacts\/gonol-relationships\//); + assert.match(page, /Represent relationship only — no comparison/); + assert.match(page, /Exact UTF-8 bytes — experiment candidate/); + assert.match(page, /data-receipt-output/); + assert.match(page, /source-block gonol-output/); + assert.match(page, /data-public-gonol-grid[^>]*tabindex="0"/); + assert.match(page, /data-pair-receipt[^>]*tabindex="0"/); + assert.match(page, /Seven identities may be retained; no geometry or pairing count is inferred/); + assert.match(index, /href="\/artifacts\/gonol-relationships\/"/); + + assert.doesNotMatch(script, /\bfetch\s*\(/); + assert.doesNotMatch(script, /\.normalize\s*\(/); + assert.match(script, /comparison_policy: policy/); + assert.match(script, /no seven-form geometry or pairing count was inferred/); + assert.match(script, /\['A', 'B'\], \['B', 'C'\], \['C', 'A'\]/); + assert.match(script, /producer_authenticated: false/); + + assert.match(config, /gonol_relationship_display\.json.*gonol-relationship-display-v1\.json/); + assert.match(packageJson, /check:gonol-relationships/); +}); diff --git a/tests/site.spec.mjs b/tests/site.spec.mjs index d0be403..7db8e01 100644 --- a/tests/site.spec.mjs +++ b/tests/site.spec.mjs @@ -28,6 +28,7 @@ const routes = [ ['/source/', /Source/], ['/projects/', /Projects/], ['/artifacts/', /Artifacts/], + ['/artifacts/gonol-relationships/', /Public Gonol relationship lab/], ['/artifacts/edcm-mathematics/', /EDCM mathematical reference/], ['/research/method/', /Legislation is not science/], ['/fallback/', /Emergency static edition/] @@ -180,6 +181,52 @@ test('EDCM mathematics stays source-bound and renders without a runtime math dep await expect(page.locator('.hmmm')).toContainText('content identities detect copy drift'); }); +test('Public Gonol lab preserves vesica pieces, triquetra pair receipts, and unresolved seven geometry', async ({ page }) => { + await page.goto('/artifacts/gonol-relationships/'); + await expect(page.locator('h1')).toHaveText('Public Gonol relationship lab'); + await expect(page.locator('[data-gonol-status]')).toContainText('Primitive 2 receipt updated'); + await expect(page.locator('[data-gonol-stage] .gonol-operand-ring')).toHaveCount(2); + await expect(page.locator('[data-gonol-stage] .gonol-lens')).toHaveCount(1); + await expect(page.locator('[data-gonol-stage] .gonol-intersection')).toHaveCount(2); + await expect(page.locator('[data-gonol-stage] .gonol-scope-ring')).toHaveCount(1); + await expect(page.locator('[data-gonol-stage] .gonol-relationship-ring')).toHaveCount(1); + await expect(page.locator('.gonol-pair:not([hidden])')).toHaveCount(1); + + const activeOperandOutput = page.locator('.gonol-operand:not([hidden]) .gonol-output').first(); + await expect(activeOperandOutput.locator(':scope > .copy-button')).toHaveCount(1); + const completeReceiptOutput = page.locator('[data-receipt-output]').locator('..'); + await expect(completeReceiptOutput.locator(':scope > .copy-button')).toHaveCount(1); + + await page.locator('#gonol-payload-A').fill('root'); + await page.locator('#gonol-payload-B').fill('root'); + await page.locator('[data-comparison-policy]').selectOption('exact-utf8'); + await expect(page.locator('[data-receipt-output]')).toContainText('"exact_utf8_equal": true'); + await expect(page.locator('[data-vector-output]')).toContainText('"code_point": "U+0072"'); + + await page.locator('.gonol-segmented label', { has: page.locator('input[value="3"]') }).click(); + await expect(page.locator('[data-gonol-status]')).toContainText('3 retained pairwise vesicas'); + await expect(page.locator('[data-gonol-stage] .gonol-lens')).toHaveCount(3); + await expect(page.locator('[data-gonol-stage] [data-pair-intersection]')).toHaveCount(6); + await expect(page.locator('.gonol-pair:not([hidden])')).toHaveCount(3); + for (const pair of ['AB', 'BC', 'CA']) { + await expect(page.locator(`.gonol-pair[data-pair="${pair}"]`)).toContainText(`vesica-${pair.toLowerCase()}`); + } + + await page.locator('.gonol-segmented label', { has: page.locator('input[value="7"]') }).click(); + await expect(page.locator('[data-gonol-status]')).toContainText('geometry and pairing remain hmmm'); + await expect(page.locator('[data-gonol-stage] .gonol-identity-box')).toHaveCount(7); + await expect(page.locator('[data-gonol-stage] .gonol-operand-ring')).toHaveCount(0); + await expect(page.locator('[data-receipt-output]')).toContainText('no seven-form geometry or pairing count was inferred'); +}); + +test('Public Gonol lab contains long receipts on a narrow viewport', async ({ page }) => { + await page.setViewportSize({ width: 390, height: 844 }); + await page.goto('/artifacts/gonol-relationships/'); + await expect(page.locator('[data-gonol-status]')).toContainText('Primitive 2 receipt updated'); + const widths = await page.evaluate(() => ({ viewport: window.innerWidth, document: document.documentElement.scrollWidth })); + expect(widths.document).toBeLessThanOrEqual(widths.viewport); +}); + test('all eight Rights Article Labs are linked from the Way tree and expose the shared contact structure', async ({ page }) => { await page.goto('/way/'); for (const record of labRoutes) await expect(page.locator(`a[href="${record.route}"]`)).toHaveCount(1);