{"id":47783904,"url":"https://github.com/stsoftwareau/neat-ai-explore","last_synced_at":"2026-04-03T14:04:05.359Z","repository":{"id":329473011,"uuid":"1118757490","full_name":"stSoftwareAU/NEAT-AI-Explore","owner":"stSoftwareAU","description":"Visualize the creature.","archived":false,"fork":false,"pushed_at":"2026-03-18T00:05:23.000Z","size":120670,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"Develop","last_synced_at":"2026-03-18T13:11:04.205Z","etag":null,"topics":["ai","neat","pwa"],"latest_commit_sha":null,"homepage":"https://stsoftwareau.github.io/NEAT-AI-Explore/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stSoftwareAU.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-18T08:31:51.000Z","updated_at":"2026-03-18T00:05:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stSoftwareAU/NEAT-AI-Explore","commit_stats":null,"previous_names":["stsoftwareau/neat-ai-explore"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/stSoftwareAU/NEAT-AI-Explore","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stSoftwareAU%2FNEAT-AI-Explore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stSoftwareAU%2FNEAT-AI-Explore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stSoftwareAU%2FNEAT-AI-Explore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stSoftwareAU%2FNEAT-AI-Explore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stSoftwareAU","download_url":"https://codeload.github.com/stSoftwareAU/NEAT-AI-Explore/tar.gz/refs/heads/Develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stSoftwareAU%2FNEAT-AI-Explore/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31355841,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T08:03:20.796Z","status":"ssl_error","status_checked_at":"2026-04-03T08:00:37.834Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","neat","pwa"],"created_at":"2026-04-03T14:03:41.136Z","updated_at":"2026-04-03T14:04:05.344Z","avatar_url":"https://github.com/stSoftwareAU.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🧠 NEAT-AI Explore\n\n[![Licence: Apache 2.0](https://img.shields.io/badge/Licence-Apache%202.0-blue.svg)](LICENSE)\n[![GitHub Pages](https://img.shields.io/badge/Demo-GitHub%20Pages-brightgreen)](https://stsoftwareau.github.io/NEAT-AI-Explore/)\n[![Deno](https://img.shields.io/badge/Tests-Deno-000000?logo=deno)](https://deno.com/)\n[![Version](https://img.shields.io/badge/Version-0.1.0-orange)](version.json)\n\nA static HTML/JS/CSS viewer for exploring a **NEAT network snapshot** (neurons,\nsynapses, impacts, and recorded activations). This is a debug tool for\ninvestigating why discovery candidates fail or succeed.\n\n---\n\n## 🚀 Try it now (example snapshot)\n\n- **Trace explorer**:\n  [Open Explorer on GitHub Pages](https://stsoftwareau.github.io/NEAT-AI-Explore/)\n- **Graph explorer (3D neighbourhood view)**:\n  [Open Graph Explorer on GitHub Pages](https://stsoftwareau.github.io/NEAT-AI-Explore/graph/)\n- **Snapshot repo (default example snapshot)**:\n  [NEAT-AI-Snapshot](https://github.com/stSoftwareAU/NEAT-AI-Snapshot)\n  (published via GitHub Pages as\n  `https://stsoftwareau.github.io/NEAT-AI-Snapshot/`)\n\nBoth views **auto-load the default snapshot** on first open, so you can click\nstraight in.\n\n---\n\n## 📱 GitHub Pages + PWA\n\nThis repo is configured to deploy a **Progressive Web App (PWA)** to **GitHub\nPages**. The published site lives in `docs/` (mirrors the approach used in\n`../GRQ-health`).\n\n- **Published folder**: `docs/` (contains `index.html`, `app.js`, `styles.css`,\n  etc.)\n- **PWA files**: `docs/manifest.webmanifest`, `docs/sw.js`, `docs/icons/*`,\n  `docs/screenshots/*`\n- **Shared JS modules**: `docs/impact_attribution.js` and\n  `docs/impact_diagnostics.js` are the single source of truth (imported by both\n  the app and tests)\n- **Deploy workflow**: `.github/workflows/deploy.yml` (push to `Develop`)\n\n---\n\n## 🏗️ Architecture Overview\n\n```mermaid\ngraph TD\n    subgraph repo[\"📁 Repository\"]\n        direction TB\n        subgraph docs_dir[\"docs/ — Published PWA\"]\n            direction TB\n            index[\"index.html\u003cbr/\u003e(Trace Explorer)\"]\n            appjs[\"app.js\"]\n            styles[\"styles.css\"]\n            sw[\"sw.js\u003cbr/\u003e(Service Worker)\"]\n            manifest[\"manifest.webmanifest\"]\n\n            subgraph shared[\"shared/ — Reusable Modules\"]\n                direction TB\n                snap_loader[\"snapshot_loader.js\"]\n                graph_analysis[\"graph_analysis.js\"]\n                colour_maps[\"colour_maps.js\"]\n                creature_overview[\"creature_overview.js\"]\n                theme_mod[\"theme.js\"]\n                correlation[\"correlation.js\"]\n                diagnostics[\"diagnostics_scan.js\"]\n                sparkline[\"sparkline.js\"]\n                discovery[\"discovery.js\"]\n            end\n\n            impact_attr[\"impact_attribution.js\"]\n            impact_diag[\"impact_diagnostics.js\"]\n\n            subgraph graph_dir[\"graph/ — 3D Explorer\"]\n                graph_html[\"index.html\"]\n                graph_js[\"graph.js\"]\n                graph_css[\"graph.css\"]\n            end\n        end\n\n        subgraph tests_dir[\"tests/ — Deno Tests\"]\n            test_files[\"*_test.ts files\"]\n        end\n\n        quality[\"quality.sh\"]\n    end\n\n    index --\u003e appjs\n    appjs --\u003e shared\n    appjs --\u003e impact_attr\n    appjs --\u003e impact_diag\n    graph_js --\u003e shared\n    test_files -.-\u003e|import \u0026 test| shared\n    test_files -.-\u003e|import \u0026 test| impact_attr\n    test_files -.-\u003e|import \u0026 test| impact_diag\n    quality -.-\u003e|fmt + lint + test| tests_dir\n\n    style docs_dir fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20\n    style shared fill:#e3f2fd,stroke:#1565c0,color:#0d47a1\n    style graph_dir fill:#fff3e0,stroke:#e65100,color:#bf360c\n    style tests_dir fill:#fce4ec,stroke:#c62828,color:#b71c1c\n    style quality fill:#f3e5f5,stroke:#6a1b9a,color:#4a148c\n```\n\n---\n\n## 🔖 Versioning (SemVer)\n\nThis repo uses **Semantic Versioning** (**SemVer**, `MAJOR.MINOR.PATCH`) as the\nhuman-facing version number. See [SemVer](https://semver.org/).\n\n- **Source of truth**: `version.json`\n- **PR automation**: if a PR targets `Develop` and does not change\n  `version.json`, a GitHub Action will automatically bump the **patch** version\n  and push it to the PR branch.\n- **Deploy cache busting**: GitHub Pages deploy replaces a `__BUILD_ID__`\n  placeholder in `docs/index.html` and `docs/sw.js` with the commit SHA, so\n  users receive updated assets without needing to clear caches.\n\n---\n\n## ⚡ Quick Start\n\n1. **Export a snapshot** from NEAT-AI-Discovery using\n   `export_visualisation_snapshot`:\n\n   ```json\n   {\n     \"parquetFile\": \"/path/to/records.parquet\",\n     \"creature\": { ... },\n     \"outFile\": \"./snapshot.json\"\n   }\n   ```\n\n2. **Serve the `docs/` folder** with any HTTP server:\n\n   ```bash\n   cd docs\n   python3 -m http.server 8000\n   ```\n\n3. **Open in browser**: `http://localhost:8000`\n\n4. **Load your snapshot**:\n   - Use the file picker to load a local JSON file\n   - Or use `?snapshotUrl=./snapshot.json` (alias: `?file=...`)\n   - For presigned URLs (recommended): use\n     `?snapshotUrlB64=\u003cbase64url(utf8(url))\u003e`\n   - Or copy your snapshot to `docs/` and click \"Fetch\" with the default path\n\n### Default snapshot (PWA-friendly)\n\nWhen opened with **no query parameters**, the app now **auto-loads** the default\nsnapshot URL (hosted via GitHub Pages). This makes the installed PWA usable on\niPhone/iPad without needing the file picker.\n\n### Tooltips (single-file snapshots)\n\nThe viewer can display human-friendly observation names and descriptions using a\n`tooltips` object embedded in the snapshot (e.g.\n`snapshot.tooltips[\"input-0\"] =\n{ label, description }`). This avoids needing a\nseparate `Tooltips.json` file.\n\nOptional fields:\n\n- **group**: a short group name used for clustering/summary in the Observations\n  dashboard (e.g. `macro`, `rates`, `equities`). Example:\n  `snapshot.tooltips[\"input-0\"] = { label, description, group: \"rates\" }`.\n\n### ☁️ Loading snapshots from S3 (presigned URLs)\n\nIf you load a snapshot via a presigned S3 URL from GitHub Pages, the S3 bucket\nmust allow **CORS** for the GitHub Pages origin, otherwise the browser will\nblock the request.\n\n- **Allowed origin**: `https://stsoftwareau.github.io`\n- **Allowed methods**: `GET`, `HEAD`\n- **Allowed headers**: `*`\n\n\u003e **💡 Tip:** If you upload `snapshot.json.gz`, either set object metadata\n\u003e `Content-Encoding: gzip` and `Content-Type: application/json` so browsers\n\u003e transparently decompress, _or_ ensure your browser supports\n\u003e `DecompressionStream` (the app will decompress `.gz` client-side when\n\u003e possible).\n\n---\n\n## ✨ Features\n\n- **Creature overview dashboard**: After loading a snapshot, see an at-a-glance\n  summary of the neural network — neuron/synapse counts, activation function\n  distribution, network depth, and an interactive mini topology diagram. Click\n  any layer to navigate into the trace explorer.\n- **Trace explorer**: Click an output neuron → see inbound synapses → click to\n  go upstream toward observations → repeat until you reach inputs. Builds a\n  breadcrumb trail.\n- **Synapse Sorting**: Sort inbound synapses by |weight|, weight, or |mean\n  contribution|.\n- **Synapse Colour Coding**: Synapse edges and rows are colour-coded by weight\n  strength — green for excitatory (positive), red for inhibitory (negative),\n  grey for weak/near-zero. A collapsible colour legend explains the scale.\n- **Neuron Detail Cards**: Shows type, squash (colour-coded badge), bias, impact\n  score, and recorded stats in themed card components with inline sparkline\n  charts for activation history and error distribution histograms.\n- **Reconstruction Checks**: If enabled in export, shows max value/activation\n  deltas to identify recording or squash function mismatches.\n- **Graph explorer**: A 3D neighbourhood view of the NEAT network to build\n  intuition about local connectivity and high-impact pathways.\n\n---\n\n## 🔍 What the Explorer shows (example snapshot)\n\nThe published app auto-loads a default snapshot (hosted separately so this repo\ndoesn't churn with large snapshot artefacts).\n\nSome interesting findings from that snapshot:\n\n- **output-0 is dominated by a single upstream hidden neuron**:\n  `hidden-discovery-739a5119-b981-4ae6-91d1-ca8cc33abc5a → output-0` receives\n  ~74.6% of the inbound allocated impact (using the viewer's heuristic\n  allocation).\n- **A second hidden neuron is the next biggest contributor**:\n  `hidden-discovery-6aae3201-115b-4dc4-beee-2d7428399e14 → output-0` receives\n  ~14.1% of the inbound allocated impact.\n- **There are prunable candidates**: ~7.6% of non-input neurons have exported\n  impact \u003c 1e-8 (highlighted as \"suspicious\" in the UI).\n\nSnapshot metadata:\n\n- **exportedAt**: 20251219T043800Z\n- **discoveryVersion**: 0.2.12\n- **Network size**: 471 neurons, 16,719 synapses\n\n---\n\n## 📱 Responsiveness (PWA screenshots)\n\nThese screenshots are generated from a real browser at iPhone/iPad/desktop\nviewports (see `scripts/generate_pwa_assets.py`).\n\n### iPhone (neurons)\n\n![iPhone screenshot](docs/screenshots/iphone-screenshot.png)\n\n### iPhone (inbound impact allocation modal)\n\n![iPhone inbound modal](docs/screenshots/iphone-inbound-modal.png)\n\n### iPad (neurons)\n\n![iPad screenshot](docs/screenshots/ipad-screenshot.png)\n\n### iPad (inbound impact allocation modal)\n\n![iPad inbound modal](docs/screenshots/ipad-inbound-modal.png)\n\n### Desktop (inbound impact allocation modal)\n\n![Desktop inbound modal](docs/screenshots/desktop-inbound-modal.png)\n\n---\n\n## 🎮 Graph explorer (3D neighbourhood view)\n\nThe graph explorer is an intuition-building alternative visualisation for large\nNEAT networks.\n\n- **Entry point**: `docs/graph/index.html`\n- **Controls**:\n  - Drag to look\n  - Mouse wheel to zoom\n  - WASD / arrow keys to fly\n  - Click a neuron to focus it (HUD shows key properties + flags)\n  - **Touch**: single tap to focus (with ripple), long press for tooltip, pinch\n    to zoom toward midpoint, two-finger pan with momentum, swipe left/right to\n    cycle neurons in the trace path\n- **Layout**: focus-centric neighbourhood view (directly linked neurons are\n  closest; moving focus recomputes the local neighbourhood layout)\n- **Legend**: includes mapping notes (size/colour/bias/degree)\n\n### Graph explorer (desktop)\n\n![Graph explorer desktop](docs/screenshots/graph-desktop.png)\n\n### Graph explorer (click-to-focus HUD)\n\n![Graph explorer focus HUD](docs/screenshots/graph-desktop-focus.png)\n\n### Graph explorer (tilt + zoom)\n\n![Graph explorer tilt](docs/screenshots/graph-desktop-tilt.png)\n\n---\n\n## 🧭 Direction terminology (to avoid confusion)\n\nThe NEAT network computation direction and the explorer navigation direction are\n**opposite**:\n\n```mermaid\ngraph LR\n    subgraph computation[\"🧠 Network Computation Direction\"]\n        direction LR\n        obs[\"Observations\u003cbr/\u003e(Inputs)\"]\n        hidden[\"Hidden\u003cbr/\u003eNeurons\"]\n        out[\"Output\u003cbr/\u003eNeurons\"]\n        obs --\u003e|\"activation\u003cbr/\u003eflows forward\"| hidden\n        hidden --\u003e|\"weighted\u003cbr/\u003esignals\"| out\n    end\n\n    subgraph navigation[\"🔍 Explorer Navigation Direction\"]\n        direction RL\n        out2[\"Output\u003cbr/\u003eNeurons\"]\n        hidden2[\"Hidden\u003cbr/\u003eNeurons\"]\n        obs2[\"Observations\u003cbr/\u003e(Inputs)\"]\n        out2 --\u003e|\"click to\u003cbr/\u003etrace upstream\"| hidden2\n        hidden2 --\u003e|\"follow inbound\u003cbr/\u003esynapses\"| obs2\n    end\n\n    style computation fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20\n    style navigation fill:#e3f2fd,stroke:#1565c0,color:#0d47a1\n    style obs fill:#fff9c4,stroke:#f9a825,color:#f57f17\n    style out fill:#c8e6c9,stroke:#388e3c,color:#1b5e20\n    style obs2 fill:#fff9c4,stroke:#f9a825,color:#f57f17\n    style out2 fill:#c8e6c9,stroke:#388e3c,color:#1b5e20\n```\n\n- **Inbound synapses (UI)**: synapses that flow from an upstream neuron into the\n  currently selected neuron (i.e. arrows point _toward_ the current neuron)\n\n---\n\n## 📥 Snapshot Loading Flow\n\nHow snapshots reach the viewer through `snapshot_loader.js`:\n\n```mermaid\ngraph TD\n    subgraph sources[\"📥 Snapshot Sources\"]\n        file_picker[\"File Picker\u003cbr/\u003e(local JSON)\"]\n        url_param[\"?snapshotUrl=\u003cbr/\u003eor ?file=\"]\n        b64_param[\"?snapshotUrlB64=\u003cbr/\u003e(base64url encoded)\"]\n        default[\"No params\u003cbr/\u003e(auto-load default)\"]\n    end\n\n    file_picker --\u003e|\"blob: URL\"| normalise\n    url_param --\u003e|\"raw URL\"| normalise\n    b64_param --\u003e|\"decode base64url\u003cbr/\u003e→ UTF-8 URL\"| decode[\"decodeBase64UrlToUtf8\"]\n    decode --\u003e security\n    default --\u003e|\"DEFAULT_SNAPSHOT_URL\u003cbr/\u003efrom config.js\"| normalise\n\n    security[\"isDangerousUrlScheme?\"]\n    normalise[\"normaliseSnapshotUrl\"]\n    normalise --\u003e security\n\n    security --\u003e|\"❌ javascript: / data:\"| blocked[\"Blocked\u003cbr/\u003e(security)\"]\n    security --\u003e|\"✅ safe\"| fetch_snap[\"fetch() snapshot\"]\n\n    fetch_snap --\u003e gzip{\"gzip\u003cbr/\u003ecompressed?\"}\n    gzip --\u003e|\"yes (.gz)\"| decompress[\"DecompressionStream\u003cbr/\u003e(client-side gunzip)\"]\n    gzip --\u003e|\"no\"| parse[\"JSON.parse\"]\n    decompress --\u003e parse\n\n    parse --\u003e normalise_creature[\"normaliseCreature\"]\n    normalise_creature --\u003e viewer[\"🖥️ Explorer renders\u003cbr/\u003ethe snapshot\"]\n\n    style sources fill:#fff3e0,stroke:#e65100,color:#bf360c\n    style blocked fill:#ffcdd2,stroke:#c62828,color:#b71c1c\n    style viewer fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20\n    style security fill:#fff9c4,stroke:#f9a825,color:#f57f17\n```\n\n---\n\n## 📦 Snapshot JSON Format\n\nThe expected format matches the output of NEAT-AI-Discovery's\n`export_visualisation_snapshot` function:\n\n```mermaid\nclassDiagram\n    class Snapshot {\n        meta\n        creature\n        recording\n        derived\n        tooltips?\n    }\n\n    class Meta {\n        exportedAt : string\n        discoveryVersion : string\n        parquetFile : string\n    }\n\n    class Creature {\n        neurons : Neuron[]\n        synapses : Synapse[]\n        input : number\n        output : number\n    }\n\n    class Neuron {\n        uuid : string\n        type : input | hidden | output\n        squash : string\n        bias : number\n    }\n\n    class Synapse {\n        from : string\n        to : string\n        weight : number\n    }\n\n    class Recording {\n        obsIndices : number[]\n        neurons : NeuronRecording map\n    }\n\n    class NeuronRecording {\n        activation : number[]\n        value : number[]\n        errors : number[][]\n        stats : object\n    }\n\n    class Derived {\n        impactsByNeuronUuid : number map\n        synapses : DerivedSynapse map\n        reconstructionChecks : object[]\n    }\n\n    class DerivedSynapse {\n        fromUuid : string\n        toUuid : string\n        weight : number\n        contribution : number[]\n        stats : object\n    }\n\n    Snapshot --\u003e Meta\n    Snapshot --\u003e Creature\n    Snapshot --\u003e Recording\n    Snapshot --\u003e Derived\n    Creature --\u003e \"0..*\" Neuron\n    Creature --\u003e \"0..*\" Synapse\n    Recording --\u003e \"0..*\" NeuronRecording\n    Derived --\u003e \"0..*\" DerivedSynapse\n```\n\n```json\n{\n  \"meta\": {\n    \"exportedAt\": \"20251218T...\",\n    \"discoveryVersion\": \"0.2.10\",\n    \"parquetFile\": \"/path/to/records.parquet\"\n  },\n  \"creature\": {\n    \"neurons\": [\"...\"],\n    \"synapses\": [\"...\"],\n    \"input\": 20,\n    \"output\": 1\n  },\n  \"recording\": {\n    \"obsIndices\": [0, 1, 2, \"...\"],\n    \"neurons\": {\n      \"output-0\": {\n        \"activation\": [\"...\"],\n        \"value\": [\"...\"],\n        \"errors\": [[\"...\"], \"...\"],\n        \"stats\": { \"...\": \"...\" }\n      }\n    }\n  },\n  \"derived\": {\n    \"impactsByNeuronUuid\": { \"output-0\": 1.0, \"...\": \"...\" },\n    \"synapses\": {\n      \"hidden-0→output-0\": {\n        \"fromUuid\": \"hidden-0\",\n        \"toUuid\": \"output-0\",\n        \"weight\": 2.0,\n        \"contribution\": [\"...\"],\n        \"stats\": { \"meanContribution\": 1.5, \"...\": \"...\" }\n      }\n    },\n    \"reconstructionChecks\": [\"...\"]\n  }\n}\n```\n\n---\n\n## 🧪 Testing\n\nTests use [Deno](https://deno.com/) and live in `tests/`. Run them with:\n\n```bash\ndeno test -A\n```\n\nOr use the quality gate (format + lint + test):\n\n```bash\n./quality.sh\n```\n\n### Quality gate pipeline\n\n```mermaid\ngraph LR\n    start[\"./quality.sh\"] --\u003e fmt\n\n    subgraph pipeline[\"Quality Gate Pipeline\"]\n        direction LR\n        fmt[\"📐 deno fmt\u003cbr/\u003e--check\"]\n        lint[\"🔍 deno lint\"]\n        test[\"🧪 deno test -A\"]\n        fmt --\u003e|\"pass\"| lint\n        lint --\u003e|\"pass\"| test\n    end\n\n    test --\u003e|\"all pass\"| ok[\"✅ OK\"]\n    fmt --\u003e|\"fail\"| fix_fmt[\"Fix formatting\"]\n    lint --\u003e|\"fail\"| fix_lint[\"Fix lint issues\"]\n    test --\u003e|\"fail\"| fix_test[\"Fix failing tests\"]\n\n    style pipeline fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20\n    style ok fill:#c8e6c9,stroke:#388e3c,color:#1b5e20\n    style fix_fmt fill:#ffcdd2,stroke:#c62828,color:#b71c1c\n    style fix_lint fill:#ffcdd2,stroke:#c62828,color:#b71c1c\n    style fix_test fill:#ffcdd2,stroke:#c62828,color:#b71c1c\n```\n\n### Unit tests vs benchmarks\n\n- **Unit tests verify correctness** — import a module, call a function with\n  known inputs, assert the result. They must not measure performance.\n- **Benchmarks verify performance** — use `deno bench` or a dedicated script to\n  measure execution time. Benchmarks are expected to be slow and must not run as\n  part of the unit-test suite.\n- Unit tests run in parallel, so any timing-based assertion is unreliable by\n  design.\n\n### \"What\" tests vs \"how\" tests\n\nWrite **\"what\" tests** — tests that exercise real behaviour:\n\n```ts\nimport { myFunction } from \"../module.js\";\nDeno.test(\"myFunction doubles positive numbers\", () =\u003e {\n  assertEquals(myFunction(3), 6);\n});\n```\n\nDo **not** write **\"how\" tests** — tests that read source files as text and grep\nfor implementation patterns:\n\n```ts\n// BAD — breaks on any refactor and tests nothing useful\nconst src = await Deno.readTextFile(\"module.js\");\nassert(src.includes(\"Math.pow\"));\n```\n\n\u003e **⚠️ Note:** If you swap quicksort for mergesort, \"what\" tests still pass\n\u003e (correct results). \"How\" tests break even though behaviour is unchanged, or\n\u003e worse, they pass while the code is actually broken.\n\n### What can be unit-tested\n\nOnly pure, DOM-free modules can be tested in Deno:\n\n| Module                             | Testable functions                                                                                                                        |\n| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |\n| `docs/impact_attribution.js`       | `computeImpactBreakdownToOutputs`, `computeInboundSynapseImpactAllocation`                                                                |\n| `docs/impact_diagnostics.js`       | `squashDerivative`, `computeGradientProxyImpact`, `summariseSeriesStats`, etc.                                                            |\n| `docs/shared/config.js`            | `DEFAULT_SNAPSHOT_URL`, `SNAPSHOT_FALLBACK_URLS`, `ALLOWED_SNAPSHOT_ORIGINS`                                                              |\n| `docs/shared/graph_analysis.js`    | `buildGraphIndex`, `computeReachableToOutputs`, `computeTopContributingInputs`                                                            |\n| `docs/shared/snapshot_loader.js`   | `normaliseSnapshotUrl`, `decodeBase64UrlToUtf8`, `isDangerousUrlScheme`, `normaliseCreature`                                              |\n| `docs/shared/colour_maps.js`       | `hash32`, `u01ToSigned`, `u32ToU01`, `neuronColourRgb01`, `synapseWeightStrength01`, `synapseWeightColourRgb01`, `synapseWeightColourCss` |\n| `docs/shared/creature_overview.js` | `computeNeuronBreakdown`, `computeSynapseStats`, `computeNetworkDepth`, `computeActivationDistribution`, `computeLayerTopology`           |\n| `docs/shared/transitions.js`       | `prefersReducedMotion`, `synapseStaggerDelay`, duration constants                                                                         |\n| `docs/shared/touch_gestures.js`    | `classifyTouch`, `detectSwipeDirection`, `momentumStep`, `clampMomentum`, `pinchZoomToward`, `clampZoomDistance`                          |\n| `docs/shared/sparkline.js`         | `computeSparklinePoints`, `computeErrorHistogram`, `squashBadge`, `flattenErrors`                                                         |\n| `docs/shared/correlation.js`       | `pearsonCorrelation`, `sampleSeries`, `computeTopInputCorrelations`                                                                       |\n| `docs/shared/discovery.js`         | `normaliseCandidate`, `extractDiscoveryCandidates`                                                                                        |\n| `docs/shared/diagnostics_scan.js`  | `scan1d`, `scan2d`, `computeNonFiniteIssues`, `computeErrorConcentrationIssues`                                                           |\n| `docs/shared/theme.js`             | `normaliseThemeMode`, `cycleThemeMode`, `themeModeLabel`, `themeModeGlyph`                                                                |\n| `docs/shared/ui_helpers.js`        | `escapeHtml`, `extractTooltips`                                                                                                           |\n\n\u003e **💡 Tip:** Browser-only code (DOM, WebGL, Service Worker) cannot be\n\u003e unit-tested in Deno — skip it rather than faking it with grep-based\n\u003e assertions.\n\n---\n\n## 🇦🇺 Australian English\n\nComments and documentation use Australian English spelling (e.g., \"colour\",\n\"behaviour\", \"organisation\").\n\n\u003e **⚠️ Note:** CSS properties like `prefers-color-scheme` and JavaScript API\n\u003e names retain American English spelling as they are web standards.\n\n---\n\n## 🖼️ PWA asset generation (icons + screenshots)\n\nIcons and screenshots are generated by starting a local web server and opening\nthe app in a headless browser (Playwright).\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\npython -m pip install -U pip\npython -m pip install pillow playwright\npython -m playwright install chromium\npython scripts/generate_pwa_assets.py\n```\n\nOutputs:\n\n- `docs/favicon.ico`\n- `docs/icons/icon-\u003csize\u003ex\u003csize\u003e.png`\n- `docs/icons/icon-source.png`\n- `docs/screenshots/desktop-screenshot.png`\n- `docs/screenshots/desktop-inbound-modal.png`\n- `docs/screenshots/mobile-screenshot.png`\n- `docs/screenshots/iphone-screenshot.png`\n- `docs/screenshots/iphone-inbound-modal.png`\n- `docs/screenshots/ipad-screenshot.png`\n- `docs/screenshots/ipad-inbound-modal.png`\n\nLast updated: 15-Jan-2026\n\n---\n\n## 📄 Licence\n\nApache Licence 2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstsoftwareau%2Fneat-ai-explore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstsoftwareau%2Fneat-ai-explore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstsoftwareau%2Fneat-ai-explore/lists"}