SPB Git

spb/modelmap Public License

Internal cartography of local LLMs on Apple Silicon — registered, gated, negative-first. Public atlas at modelmap.io.

Python 66.3% JavaScript 24.5% CSS 8.1% Shell 0.7%
35.1 KB · 650 lines javascript
Raw Blame History
1// ============================================================================2//  Project   : modelmap3//  File      : site/server.js4//  Purpose   : modelmap.io platform — routes and pages (English, light theme)5//  Author    : Simon-Pierre Boucher6//  Contact   : contact@spboucher.ai7//  Website   : https://modelmap.io8//  Created   : 2026-08-129//  Modified  : 2026-08-1210//  Platform  : macOS / Apple Silicon (arm64) — Node.js (deployed on MacLustr)11//  License   : All rights reserved (research code)12// ============================================================================13"use strict";1415const path = require("path");16const express = require("express");17const C = require("./lib/content");18const R = require("./lib/render");19const Comments = require("./lib/comments");20const Charts = require("./lib/charts");2122// ------------------------------------------------------------ chart builders23/** Layer-profile charts for a probes atlas entry (real A/B + twin null). */24function probeMapCharts(onlyProp, mapRel) {25  const m = C.readJson(mapRel || "atlas/qwen3-0.6b-4bit/probes/v1/map.json");26  if (!m || !m.properties) return "";27  const props = onlyProp ? [onlyProp] : Object.keys(m.properties);28  return props.map((prop) => {29    const p = m.properties[prop];30    if (!p) return "";31    const sel = (rows) => rows.map((r) => r.selectivity_mean);32    const caption = p.verdict33      ? `Verdict: ${R.esc(p.verdict)}. Differential (real−twin) claims only; Level 1, ` +34        "5 seeds, shuffled-label controls inside every probe, token-balanced classes."35      : "The twin (dashed) matches the trained model — the registered validity gate " +36        "failed and this map is published as a NEGATIVE result: on these promptsets, probes " +37        "read the tokenizer + architecture prior, not learned computation. Level 1, 5 seeds, " +38        "shuffled-label controls inside every probe.";39    return Charts.layerLineSvg({40      title: `${prop} — probe selectivity by layer (trained vs random-init twin)`,41      yLabel: "selectivity",42      yMin: 0, yMax: 1,43      series: [44        { label: "trained · set A", color: Charts.S1, values: sel(p.per_layer.A) },45        { label: "trained · set B", color: Charts.S2, values: sel(p.per_layer.B) },46        { label: "random-init twin", color: Charts.REF, dash: true, ref: true,47          values: p.twin_null_per_layer_A.map((r) => r.selectivity_mean) },48      ],49      caption,50    });51  }).join("");52}53const MAP_V2 = "atlas/qwen3-0.6b-4bit/probes/v2/map.json";54const MAP_INT = "atlas/qwen3-0.6b-4bit/interventions/v1/map.json";5556/** Causal direction-erasure BAND map chart (interventions map, run #4 format). */57function interventionsMapCharts(mapRel) {58  const m = C.readJson(mapRel || MAP_INT);59  if (!m || !m.per_layer || !m.per_layer.mean) return "";60  const all = [...m.per_layer.mean, ...m.per_layer.min, ...m.per_layer.random_direction_damage];61  const yMin = Math.floor(Math.min(...all, 0) * 2) / 2 - 0.5;62  const yMax = Math.ceil(Math.max(...all) * 2) / 2 + 0.5;63  const band = m.band || {};64  return Charts.layerLineSvg({65    title: "agreement — causal direction-erasure BAND map (specific damage by layer)",66    yLabel: "specific margin damage",67    yMin, yMax,68    series: [69      { label: "mean of 6 fresh sources", color: Charts.S1, values: m.per_layer.mean },70      { label: "worst source (min)", color: Charts.S2, values: m.per_layer.min },71      { label: "random-direction damage", color: Charts.REF, dash: true, ref: true,72        values: m.per_layer.random_direction_damage },73    ],74    caption: `The published claim is the BAND (layers ${band.early_layers ? band.early_layers.join("–") : "2–15"}): ` +75      "erasing the diff-of-means agreement direction at any single early-band layer destroys most of " +76      "the grammatical margin (baseline " + (m.baseline_margin ? m.baseline_margin.toFixed(2) : "—") +77      "), replicated across six fresh direction estimates on a fresh behavioral bank (Level 2). " +78      "The late band is displayed but carries no claim — run #3's per-layer version was refused by " +79      "the publication gate as estimator-unstable. Anti-correlates with the probes/v2 decodability " +80      "ranking (survival ledger 0/2): decodability peaks ≠ causal joints.",81  });82}8384/** expH charts: warm/cold storage panels + capture-overhead grouped bars. */85function storageChart() {86  const d = C.expHData();87  let html = "";88  if (d.warm && d.cold) {89    const rnd = (rows) => rows.filter((r) => r.op === "random-batch")90      .map((r) => ({ label: r.format.replace("zarr-", "zarr‑"), value: r.gb_per_s_mean }));91    html += Charts.barPanelsSvg({92      title: "Activation-store random-batch read throughput — the ordering inverts cold",93      yLabel: "GB/s", unit: "GB/s",94      panels: [95        { title: "warm cache · M5 Max (run #1)", bars: rnd(d.warm) },96        { title: "cold cache · M3 Ultra (run #2)", bars: rnd(d.cold) },97      ],98      caption: "Run #2 falsified the registered hypothesis: raw mmap wins warm (memory speed) " +99        "but collapses cold to page-fault IO (~8–16 KiB, QD 1) while chunked zarr reads 32 MiB " +100        "blocks. The rule is IO granularity, not the container.",101    });102  }103  return html;104}105106function captureChart() {107  const d = C.expHData();108  let html = "";109  if (d.compute.length) {110    const key = (c) => `${c.backend}·${c.source}`;111    const groupsMap = new Map();112    for (const c of d.compute) {113      const ms = 1000 * c.s_per_forward.reduce((a, b) => a + b, 0) / c.s_per_forward.length;114      if (!groupsMap.has(key(c))) groupsMap.set(key(c), { backend: c.backend, source: c.source, values: {} });115      groupsMap.get(key(c)).values[c.mode] = ms;116    }117    const label = (g) => g.backend === "torch-mps" ? "torch-MPS (synthetic)" :118      g.source === "synthetic" ? "MLX (synthetic)" : "MLX (Qwen3-0.6B-4bit)";119    html += Charts.groupedBarSvg({120      title: "Capture overhead per forward pass — retention is nearly free under MLX",121      yLabel: "ms / forward", unit: "ms",122      groups: [...groupsMap.values()].map((g) => ({ label: label(g), values: g.values })),123      modes: [124        { key: "plain", label: "plain", color: Charts.S1 },125        { key: "retain", label: "retain", color: Charts.S2 },126        { key: "retain+copy+write", label: "retain+copy+write", color: Charts.S3 },127      ],128      caption: "Runs #1 and #3: per-layer retention costs 1.02× (MLX synthetic) and 1.004× " +129        "(real 4-bit checkpoint) vs 1.22× on torch-MPS; copy+write stays ≤1.5× everywhere. " +130        "3 repeats per cell; manifests embedded in the raw JSON.",131    });132  }133  return html;134}135136function expHCharts() {137  return storageChart() + captureChart();138}139140const app = express();141const PORT = process.env.PORT || 8140;142143app.set("trust proxy", true); // behind ngrok — X-Forwarded-For carries the real IP144app.use("/static", express.static(path.join(__dirname, "public"), { maxAge: "1h" }));145app.use(express.urlencoded({ extended: false, limit: "16kb" }));146147function page(res, opts) {148  res.send(R.layout({ ...opts, buildInfo: C.buildInfo() }));149}150151function metaChips(data) {152  if (!data || !Object.keys(data).length) return "";153  const fields = ["document", "author", "created", "modified", "status"];154  const chips = fields155    .filter((f) => data[f])156    .map((f) => `<span class="chip"><span class="chip-k">${f}</span>${R.esc(data[f])}</span>`)157    .join("");158  return chips ? `<div class="chips">${chips}</div>` : "";159}160161// ---------------------------------------------------------------- home162app.get("/", (req, res) => {163  const s = C.stats();164  const log = C.parseLogEntries().slice(-3).reverse();165  const phases = [166    ["Phase 1 — Ultra-deep literature research (§4.1–4.10)", s.notes > 0 ? "in progress" : "pending", s.notes > 0 ? `${s.notes} theme notes · ${s.sources} sources` : "ten mandatory areas, primary sources first"],167    ["Phase 2 — State-of-the-art map", C.exists("research/state_of_the_art.md") ? "done" : "pending", "technique taxonomy with epistemic status"],168    ["Phase 3 — Research gaps", s.gaps ? "done" : "pending", s.gaps ? `${s.gaps} falsifiable candidate directions` : "≥20 substantially different candidate directions"],169    ["Phase 4 — Candidate ranking", C.exists("research/candidate_ranking.md") ? "done" : "pending", "10-axis scoring, 3–5 prototype candidates"],170    ["Phases 5–6 — Framework & micro-experiments A–H", s.experimentsDone ? "in progress" : "pending", `${s.experimentsDone}/${s.experiments} experiments completed`],171    ["Phases 7–9 — Prototypes → methodology", C.exists("research/methodology.md") ? "done" : "pending", "evidence-driven; the methodology is a primary deliverable"],172    ["Phase 10 — Atlas pipeline & this platform", "in progress", `platform live · ${s.atlasEntries} map${s.atlasEntries === 1 ? "" : "s"} published`],173    ["Phase 11 — Novelty verification", C.exists("research/novelty_check.md") ? "done" : "pending", "assume not novel until evidence suggests otherwise"],174  ];175  const phaseHtml = phases176    .map(177      ([name, st, detail]) => `<li class="phase ${st.replace(" ", "-")}">178        <span class="phase-dot"></span><div><strong>${name}</strong><span class="phase-detail">${detail}</span></div>179        <span class="badge badge-${st.replace(" ", "-")}">${st}</span></li>`180    )181    .join("");182  const logHtml = log183    .map(184      (e) => `<article class="log-entry"><h3>${R.esc(e.title)}</h3>185      <div class="md">${R.markdownToHtml(e.body.split("\n").slice(0, 8).join("\n"), "research/LOG.md")}</div>186      <a class="more" href="/doc/research/LOG.md">Full research log →</a></article>`187    )188    .join("");189190  const body = `191<section class="hero">192  <p class="kicker">Independent interpretability research · Apple Silicon</p>193  <h1>An internal cartography of<br>local large language models</h1>194  <p class="lede">Given an already-trained open-weight model, what can actually be <em>known, measured,195  localized, and mapped</em> about its internal organization — where knowledge lives, how computation196  is distributed, which structures are stable across inputs, layers, scales, and model families —197  using only a consumer-grade Mac? Every map published here is versioned, provenanced,198  confidence-labeled, and regenerable.</p>199  <p class="lede-eq"><code>what a probe reports ≠ what the model computes ≠ what is stable across methods ≠ what survives intervention</code></p>200</section>201202<section class="tiles">203  <div class="tile"><span class="tile-value">${s.sources}</span><span class="tile-label">sources reviewed</span></div>204  <div class="tile"><span class="tile-value">${s.gaps}</span><span class="tile-label">research gaps identified</span></div>205  <div class="tile"><span class="tile-value">${s.experimentsDone}<span class="tile-denom">/${s.experiments}</span></span><span class="tile-label">experiments completed</span></div>206  <div class="tile"><span class="tile-value">${s.atlasEntries}</span><span class="tile-label">atlas maps published</span></div>207</section>208209<section class="split">210  <div class="card">211    <h2>The evidence standard</h2>212    <p>A claim enters the atlas only at the confidence level its evidence supports.213    Correlational maps and causally-verified maps are never conflated — visually or textually.214    The two metrics tracked for every map are its <strong>replication rate</strong> (does it215    reproduce under resampling of seeds and data?) and its <strong>causal confirmation rate</strong>216    (what fraction of localized claims survive intervention?).</p>217    ${R.confidenceLadder()}218    <p class="note-line">Negative results — techniques whose maps do not replicate — are first-class,219    publishable findings here.</p>220  </div>221  <div class="card">222    <h2>Research phases</h2>223    <ul class="phases">${phaseHtml}</ul>224  </div>225</section>226227${(() => {228    const causal = interventionsMapCharts();229    const probe2 = probeMapCharts("agreement", MAP_V2);230    const probe1 = probeMapCharts("lang_id");231    const exph = expHCharts();232    return causal || probe1 || probe2 || exph ? `<section class="card">233  <h2>First measured maps</h2>234  <p class="lede-small">Every figure below is regenerated from committed code + versioned results —235  raw JSON with hardware manifests under <a href="/results">Results</a>. Full per-property maps live236  in the <a href="/atlas">Atlas</a>.</p>237  ${causal}${probe2}${probe1}${exph}238</section>` : "";239  })()}240241<section class="card">242  <h2>Latest from the research log</h2>243  <div class="log-grid">${logHtml || "<p>No log entries yet.</p>"}</div>244</section>`;245  page(res, { title: "Home", active: "Home", body });246});247248// ---------------------------------------------------------------- research index249app.get("/research", (req, res) => {250  const docs = [251    ["research/LOG.md", "Research log", "Append-only, auditable record of every question, experiment, result, and decision."],252    ["research/state_of_the_art.md", "State of the art", "Phase 2 — technique taxonomy with epistemic status (established / contested / debunked-in-part)."],253    ["research/research_gaps.md", "Research gaps", "Phase 3 — ≥20 falsifiable candidate directions, each with its smallest Mac-runnable falsifying experiment."],254    ["research/candidate_ranking.md", "Candidate ranking", "Phase 4 — 10-axis scoring and 3–5 selected prototype candidates."],255    ["research/methodology.md", "Methodology", "Phase 9 — the formal pipeline turning (checkpoint + corpora + budget) into confidence-labeled atlas entries."],256    ["research/novelty_check.md", "Novelty check", "Phase 11 — final novelty verification (assume not novel until shown otherwise)."],257    ["research/bibliography.md", "Bibliography", "Every consulted source with URL and access date."],258  ];259  const cards = docs260    .map(([rel, title, desc]) => {261      const ok = C.exists(rel);262      return `<a class="doc-card ${ok ? "" : "disabled"}" href="${ok ? "/doc/" + rel : "#"}">263        <h3>${title}</h3><p>${desc}</p>${ok ? "" : '<span class="badge badge-pending">not yet written</span>'}</a>`;264    })265    .join("");266  const notes = (C.listDir("research/notes") || [])267    .filter((f) => f.name.endsWith(".md") && f.name !== "README.md")268    .map((f) => {269      const md = C.readMarkdown(f.rel);270      const doc = md && md.data.document ? md.data.document : f.name;271      return `<a class="doc-card" href="/doc/${f.rel}"><h3>${R.esc(f.name.replace(".md", "").replace(/_/g, " "))}</h3>272        <p class="mono-small">${R.esc(doc)}</p></a>`;273    })274    .join("");275  const body = `<h1 class="page-title">Research documents</h1>276<div class="doc-grid">${cards}</div>277<h2 class="section-title">Phase 1 literature notes</h2>278<div class="doc-grid">${notes || '<p class="note-line">No literature notes yet — Phase 1 has not started.</p>'}</div>279<p class="note-line">The project charter itself is public: <a href="/doc/CLAUDE.md">read the full research charter</a>.</p>`;280  page(res, { title: "Research", active: "Research", body });281});282283// ---------------------------------------------------------------- markdown viewer284app.get(/^\/doc\/(.+)$/, (req, res) => {285  const rel = req.params[0];286  const md = rel.endsWith(".md") ? C.readMarkdown(rel) : null;287  if (!md) return notFound(res);288  const body = `<article class="doc">289    <p class="crumb"><a href="/research">Research</a> / ${R.esc(rel)}</p>290    ${metaChips(md.data)}291    <div class="md">${R.markdownToHtml(md.content, rel)}</div>292  </article>`;293  page(res, { title: md.data.document || rel, active: "Research", body });294});295296// ---------------------------------------------------------------- experiments297app.get("/experiments", (req, res) => {298  const exps = C.listExperiments();299  const cards = exps300    .map(301      (e) => `<a class="doc-card" href="/experiments/${e.id}">302      <h3>${R.esc(e.id)}</h3><p>${R.esc(e.purpose)}</p>303      <span class="badge badge-${e.status === "completed" ? "done" : e.status === "has results" ? "in-progress" : "pending"}">${e.status}</span>304      ${e.runs ? `<span class="runs">${e.runs} result run${e.runs > 1 ? "s" : ""}</span>` : ""}</a>`305    )306    .join("");307  const body = `<h1 class="page-title">Experiments</h1>308<p class="lede-small">Every experiment carries a registered hypothesis with an explicit falsification criterion309(seven-field scientific block), mandatory controls and nulls, raw results, and an analysis. Negative results are kept.310Micro-experiments A–H establish the noise floor, the correlational→causal survival rate, and the capture cost311frontier before any large model is mapped.</p>312<div class="doc-grid">${cards}</div>`;313  page(res, { title: "Experiments", active: "Experiments", body });314});315316app.get("/experiments/:id", (req, res) => {317  const exps = C.listExperiments();318  const exp = exps.find((e) => e.id === req.params.id);319  if (!exp) return notFound(res);320  const sections = [];321  for (const [file, title] of [["hypothesis.md", "Hypothesis"], ["analysis.md", "Analysis"], ["README.md", "README"]]) {322    const md = C.readMarkdown(path.posix.join(exp.rel, file));323    if (md && md.content.trim().length > 40) {324      sections.push(`<section class="card"><h2>${title}</h2>${metaChips(md.data)}325        <div class="md">${R.markdownToHtml(md.content, path.posix.join(exp.rel, file))}</div></section>`);326    }327  }328  const runs = C.listResultRuns().filter((r) => r.experiment === exp.id);329  const runsHtml = runs.length330    ? `<section class="card"><h2>Result runs</h2><ul class="file-list">` +331      runs.map((r) => r.files.map((f) => `<li><a href="/results/${f.rel}">${r.timestamp} / ${f.name}</a> <span class="mono-small">${(f.size / 1024).toFixed(1)} KiB</span></li>`).join("")).join("") +332      `</ul></section>`333    : "";334  const codeLink = C.exists(path.posix.join(exp.rel, "benchmark.py"))335    ? `<p><a class="more" href="/file/${exp.rel}/benchmark.py">View benchmark implementation (benchmark.py) →</a></p>`336    : "";337  let charts = "";338  if (exp.id === "expH_capture_cost_frontier") charts = expHCharts();339  if (exp.id === "expA_probe_reliability") {340    charts = probeMapCharts(null, MAP_V2) + probeMapCharts();341  }342  if (exp.id === "expC_causal_verification") charts = interventionsMapCharts();343  if (charts) charts = `<section class="card"><h2>Result maps</h2>${charts}</section>`;344  const body = `<p class="crumb"><a href="/experiments">Experiments</a> / ${R.esc(exp.id)}</p>345<h1 class="page-title">${R.esc(exp.id)}</h1><p class="lede-small">${R.esc(exp.purpose)}</p>346${codeLink}${charts}${sections.join("")}${runsHtml}`;347  page(res, { title: exp.id, active: "Experiments", body });348});349350// ---------------------------------------------------------------- atlas351app.get("/atlas", (req, res) => {352  const entries = C.listAtlasEntries();353  const byModel = {};354  for (const e of entries) (byModel[e.model] ||= []).push(e);355  const groups = Object.keys(byModel).sort().map((model) => {356    const cards = byModel[model]357      .map(358        (e) => `<a class="doc-card" href="/atlas/${e.model}/${e.mapType}/${e.version}">359        <h3>${R.esc(e.mapType)} <span class="mono-small">${R.esc(e.version)}</span></h3>360        <p>${e.provenance && e.provenance.created ? "created " + R.esc(e.provenance.created) : ""}</p>361        ${R.levelBadge(e.level)}</a>`362      )363      .join("");364    return `<h2 class="section-title">${R.esc(model)}</h2><div class="doc-grid">${cards}</div>`;365  }).join("");366  const empty = `<div class="card">367    <h2>No maps published yet</h2>368    <p>The atlas fills only after the mapping methodology exists (Phase 9) and each map clears its369    evidence bar. Every entry will carry a completed <code>provenance.json</code> (commit, config,370    model hash, hardware, dates) and a <code>confidence.md</code> stating its evidence level.371    Nothing is published that cannot be regenerated from committed code and versioned data.</p>372    ${R.confidenceLadder()}373  </div>`;374  const body = `<h1 class="page-title">Atlas</h1>375<p class="lede-small">Versioned map artifacts: <code>atlas/&lt;model&gt;/&lt;map type&gt;/&lt;version&gt;</code>.376Only open-weight models are mapped, and no claim appears here above its evidence level.</p>377${groups || empty}`;378  page(res, { title: "Atlas", active: "Atlas", body });379});380381app.get("/atlas/:model/:mapType/:version", (req, res) => {382  const { model, mapType, version } = req.params;383  const entry = C.listAtlasEntries().find(384    (e) => e.model === model && e.mapType === mapType && e.version === version385  );386  if (!entry) return notFound(res);387  const confidence = C.readMarkdown(path.posix.join(entry.rel, "confidence.md"));388  const prov = entry.provenance389    ? `<pre class="codeblock"><code class="hljs">${R.highlightFile(JSON.stringify(entry.provenance, null, 2), "provenance.json")}</code></pre>`390    : "<p>provenance.json missing — this entry is not publishable.</p>";391  const files = entry.files392    .map((f) => `<li><a href="/results/${f.rel}">${f.name}</a> <span class="mono-small">${(f.size / 1024).toFixed(1)} KiB</span></li>`)393    .join("");394  const mapRel = path.posix.join(entry.rel, "map.json");395  const mapCharts = !C.exists(mapRel) ? "" :396    mapType === "probes" ? probeMapCharts(null, mapRel) :397    mapType === "interventions" ? interventionsMapCharts(mapRel) : "";398  const body = `<p class="crumb"><a href="/atlas">Atlas</a> / ${R.esc(entry.rel)}</p>399<h1 class="page-title">${R.esc(model)} — ${R.esc(mapType)} <span class="mono-small">${R.esc(version)}</span></h1>400<p>${R.levelBadge(entry.level)}</p>401${mapCharts ? `<section class="card"><h2>The map</h2>${mapCharts}</section>` : ""}402${confidence ? `<section class="card"><h2>Confidence</h2><div class="md">${R.markdownToHtml(confidence.content, entry.rel + "/confidence.md")}</div></section>` : ""}403<section class="card"><h2>Provenance</h2>${prov}</section>404<section class="card"><h2>Files</h2><ul class="file-list">${files}</ul></section>`;405  page(res, { title: `${model}/${mapType}/${version}`, active: "Atlas", body });406});407408// ---------------------------------------------------------------- results409app.get("/results", (req, res) => {410  const runs = C.listResultRuns();411  const rows = runs412    .map(413      (r) => `<tr><td><a href="/experiments/${r.experiment}">${r.experiment}</a></td>414      <td class="mono-small">${r.timestamp}</td>415      <td>${r.files.map((f) => `<a href="/results/${f.rel}">${f.name}</a>`).join(" · ")}</td></tr>`416    )417    .join("");418  const body = `<h1 class="page-title">Raw results</h1>419<p class="lede-small">Every result is reproducible from commit hash + config + model hash + seed +420hardware manifest, and each JSON embeds the manifest of the exact machine that produced it.</p>421<table class="results-table"><thead><tr><th>Experiment</th><th>Run (UTC)</th><th>Files</th></tr></thead>422<tbody>${rows || '<tr><td colspan="3">No result runs yet.</td></tr>'}</tbody></table>`;423  page(res, { title: "Results", active: "Results", body });424});425426app.get(/^\/results\/(.+)$/, (req, res) => {427  const raw0 = req.params[0];428  const rel = raw0.startsWith("results/") || raw0.startsWith("atlas/") ? raw0 : "results/" + raw0;429  const raw = C.readText(rel);430  if (raw === null) return notFound(res);431  let bodyContent;432  if (rel.endsWith(".json")) {433    const obj = C.readJson(rel);434    bodyContent = `<pre class="codeblock"><code class="hljs">${R.highlightFile(JSON.stringify(obj, null, 2), "x.json")}</code></pre>`;435  } else {436    bodyContent = `<pre class="codeblock"><code>${R.esc(raw.slice(0, 200000))}</code></pre>`;437  }438  const body = `<p class="crumb"><a href="/results">Results</a> / ${R.esc(rel)}</p><div class="card">${bodyContent}</div>`;439  page(res, { title: rel, active: "Results", body });440});441442// ---------------------------------------------------------------- code browser443const CODE_ROOTS = ["src", "tools", "benchmarks", "experiments", "Makefile", "pyproject.toml", "CITATION.cff"];444app.get("/code", (req, res) => {445  const sections = CODE_ROOTS.map((root) => {446    if (!C.exists(root)) return "";447    const st = C.listDir(root);448    if (st === null) {449      return `<li><a href="/file/${root}">${root}</a></li>`;450    }451    const files = C.walk(root, (f) => C.isTextFile(f.rel) && !f.rel.includes("results/"));452    return `<li class="tree-root"><strong>${root}/</strong><ul>` +453      files.map((f) => `<li><a href="/file/${f.rel}">${f.rel.slice(root.length + 1)}</a></li>`).join("") +454      `</ul></li>`;455  }).join("");456  const body = `<h1 class="page-title">Code</h1>457<p class="lede-small">The mapping library (<code>src/modelmap/</code>), tooling, benchmark harness, and experiment458implementations. Every file carries the project's author header; MLX / PyTorch-MPS / Metal is the primary compute459path — CUDA is never a core dependency.</p>460<ul class="tree">${sections}</ul>`;461  page(res, { title: "Code", active: "Code", body });462});463464app.get(/^\/file\/(.+)$/, (req, res) => {465  const rel = req.params[0];466  if (!C.isTextFile(rel)) return notFound(res);467  const raw = C.readText(rel);468  if (raw === null) return notFound(res);469  const name = rel.split("/").pop();470  const body = `<p class="crumb"><a href="/code">Code</a> / ${R.esc(rel)}</p>471<div class="card file-card"><div class="file-head"><span class="mono-small">${R.esc(rel)}</span>472<span class="mono-small">${raw.split("\n").length} lines</span></div>473<pre class="codeblock"><code class="hljs">${R.highlightFile(raw.slice(0, 400000), name)}</code></pre></div>`;474  page(res, { title: name, active: "Code", body });475});476477// ---------------------------------------------------------------- about478app.get("/about", (req, res) => {479  const questions = [480    ["Q1 — Localization", "Where inside a pretrained LLM do capabilities, knowledge domains, languages, and behaviors reside? Localized (layers, heads, neurons, weight blocks, directions) or diffuse?"],481    ["Q2 — Structure", "Which stable internal structures exist across inputs — circuits, feature directions, attention patterns, activation manifolds, weight-space geometry — and at what granularity are they real rather than artifacts of the probing method?"],482    ["Q3 — Comparability", "Can internal maps be compared across model sizes, checkpoints, quantization levels, and families? Is there a common coordinate system for model internals?"],483    ["Q4 — Cost", "Which mapping techniques are feasible on a 16–64 GB Mac, at which model sizes, and what is the accuracy/cost frontier of local interpretability?"],484    ["Q5 — Utility", "Do the maps predict anything useful — quantization sensitivity, pruning tolerance, working-set behavior, failure modes, editing targets?"],485  ];486  const qHtml = questions487    .map(([q, d]) => `<tr><th>${q}</th><td>${d}</td></tr>`)488    .join("");489  const body = `<h1 class="page-title">About this project</h1>490<div class="card md">491<p><strong>modelmap</strong> is an independent research project by492<strong>Simon-Pierre Boucher</strong> (<a href="mailto:contact@spboucher.ai">contact@spboucher.ai</a>) building a493systematic methodology — and the tooling behind it — to discover, measure, and map the internal structure of494pretrained open-weight LLMs running locally on consumer Apple Silicon hardware, and publishing the resulting maps495as a rigorous, reproducible public atlas on this site.</p>496<p>The ideal outcome is not a gallery of suggestive visualizations. It is a <strong>reproducible cartographic497standard for open-weight models</strong> — every map versioned, provenanced, confidence-labeled, and regenerable498by anyone with a Mac — turning “we think the model does X” into “here is the map, its evidence level, and the499script that rebuilds it.”</p>500<h2>Core research questions</h2>501<table class="kv-table">${qHtml}</table>502<h2>The evidence standard</h2>503${R.confidenceLadder()}504<p>Every mapping technique runs with controls: shuffled labels for probes, random-direction baselines for steering,505randomly-initialized-model baselines where meaningful, resample vs zero ablations. Structure is never claimed506without showing the null. Replication across ≥3 seeds and ≥2 prompt sets, bootstrap confidence intervals, effect507sizes, and multiple-comparison correction are mandatory for every published number.</p>508<h2>Target hardware</h2>509<p>Apple Silicon Mac (M1–M4 family), 16–64 GB unified memory, internal NVMe SSD, Metal GPU sharing memory with the510CPU. Local interpretability on consumer hardware is itself an under-served niche: most tooling assumes CUDA511clusters. Making rigorous mapping feasible on a Mac is part of the contribution. The stretch target is a full,512causally-verified, versioned atlas of a 7B–14B model produced end-to-end on a single 32–64 GB Mac, comparable513across ≥2 quantization levels and ≥2 model sizes.</p>514<h2>Sister project</h2>515<p><a href="https://www.localvm.dev">localvm-research</a> investigates out-of-core LLM execution on the same516hardware class — running models larger than memory. The projects cross-pollinate: modelmap's working-set and517localization maps (Q5) feed localvm's execution decisions, and localvm's cost measurements calibrate modelmap's518capture-feasibility frontier (Experiment H).</p>519<p>Failure is an acceptable outcome — the charter defines explicit failure criteria, and the520<a href="/doc/research/LOG.md">log</a> records why an approach died, not just what survived.</p>521</div>`;522  page(res, { title: "About", active: "About", body });523});524525// ---------------------------------------------------------------- publications526const FIGS = {527  "causal-band": () => interventionsMapCharts(),528  "probes-agreement": () => probeMapCharts("agreement", MAP_V2),529  "probes-arith": () => probeMapCharts("arith_valid", MAP_V2),530  "probes-word-order": () => probeMapCharts("word_order", MAP_V2),531  "probes-null": () => probeMapCharts("lang_id"),532  "storage": () => storageChart(),533  "capture": () => captureChart(),534};535536function listPublications() {537  return (C.listDir("publications") || [])538    .filter((f) => f.name.endsWith(".md"))539    .map((f) => {540      const md = C.readMarkdown(f.rel);541      return md ? { rel: f.rel, id: f.name.replace(".md", ""), data: md.data } : null;542    })543    .filter(Boolean)544    .sort((a, b) => String(b.data.created).localeCompare(String(a.data.created)));545}546547app.get("/publications", (req, res) => {548  const pubs = listPublications();549  const cards = pubs.map((p) => `<a class="pub-card" href="/publications/${p.id}">550    <div class="pub-card-meta"><span class="pub-id">${R.esc(p.data.pub_id || p.id)}</span>551    <span class="badge badge-${p.data.status === "final" ? "done" : "in-progress"}">${R.esc(p.data.status || "draft")}</span>552    <span class="pub-date">${R.esc(String(p.data.created))}</span></div>553    <h3>${R.esc(p.data.title || p.id)}</h3>554    <p>${R.esc(p.data.abstract ? String(p.data.abstract).slice(0, 300) + "…" : "")}</p>555    <span class="more">Read the report →</span></a>`).join("");556  const body = `<h1 class="page-title">Publications</h1>557<p class="lede-small">Official technical reports of this project. Every number is regenerable from558the stated commit; figures are rendered live from the same versioned results as the559<a href="/atlas">Atlas</a>; negative results and publication-gate refusals are reported560with the same prominence as positive findings.</p>561${cards || '<p class="note-line">No publications yet.</p>'}`;562  page(res, { title: "Publications", active: "Publications", body });563});564565app.get("/publications/:id", (req, res) => {566  const rel = `publications/${req.params.id}.md`;567  const md = C.readMarkdown(rel);568  if (!md) return notFound(res);569  let html = R.markdownToHtml(md.content, rel);570  html = html.replace(/<p>\{\{fig:([a-z0-9-]+)\}\}<\/p>/g, (_, key) =>571    FIGS[key] ? FIGS[key]() : "");572  const d = md.data;573  const body = `<article class="doc pub">574  <p class="crumb"><a href="/publications">Publications</a> / ${R.esc(d.pub_id || req.params.id)}</p>575  <header class="pub-head">576    <div class="pub-card-meta"><span class="pub-id">${R.esc(d.pub_id || req.params.id)}</span>577    <span class="badge badge-${d.status === "final" ? "done" : "in-progress"}">${R.esc(d.status || "draft")}</span>578    <span class="pub-date">${R.esc(String(d.created))}</span></div>579    <h1>${R.esc(d.title || req.params.id)}</h1>580    <p class="pub-byline">${R.esc(d.author || "Simon-Pierre Boucher")} ·581    <a href="mailto:${R.esc(d.contact || "contact@spboucher.ai")}">${R.esc(d.contact || "contact@spboucher.ai")}</a> ·582    <a href="https://modelmap.io">modelmap.io</a></p>583    ${d.abstract ? `<div class="pub-abstract"><strong>Abstract.</strong> ${R.esc(String(d.abstract))}</div>` : ""}584  </header>585  <div class="md">${html}</div>586  ${d.cite ? `<div class="pub-cite"><strong>How to cite</strong><pre class="codeblock"><code>${R.esc(String(d.cite))}</code></pre></div>` : ""}587</article>`;588  page(res, { title: d.title || req.params.id, active: "Publications", body });589});590591// ---------------------------------------------------------------- comments592function commentsPage(res, { flash = "", flashKind = "ok" } = {}) {593  const items = Comments.list();594  const listHtml = items.length595    ? `<ul class="comment-list">` + items.map((c) => `<li class="comment">596        <div class="comment-head"><span class="comment-name">${R.esc(c.name)}</span>597        <span class="comment-date">${R.esc(String(c.created).slice(0, 10))}</span></div>598        <div class="comment-body">${R.esc(c.message)}</div></li>`).join("") + `</ul>`599    : `<p class="note-line">No comments yet — be the first.</p>`;600  const body = `<h1 class="page-title">Comments</h1>601<p class="lede-small">Questions, critiques, replication reports, pointers to related work — all welcome.602Methodological challenges are especially valued: this project publishes its negative results,603and a comment that breaks a map is a contribution.</p>604${flash ? `<div class="flash flash-${flashKind}">${R.esc(flash)}</div>` : ""}605<section class="card">606  <h2>Leave a comment</h2>607  <form class="comment-form" method="POST" action="/comments">608    <div><label for="c-name">Name (optional)</label>609    <input type="text" id="c-name" name="name" maxlength="60" autocomplete="name" placeholder="Your name"></div>610    <div class="hp-field" aria-hidden="true"><label for="c-website">Website</label>611    <input type="text" id="c-website" name="website" tabindex="-1" autocomplete="off"></div>612    <div><label for="c-message">Comment</label>613    <textarea id="c-message" name="message" maxlength="2000" required614    placeholder="Your comment — plain text, max 2000 characters"></textarea></div>615    <button class="btn" type="submit">Post comment</button>616  </form>617</section>618<h2 class="section-title">${items.length ? items.length + " comment" + (items.length > 1 ? "s" : "") : "Comments"}</h2>619${listHtml}`;620  page(res, { title: "Comments", active: "Comments", body });621}622623app.get("/comments", (req, res) => {624  const flash = req.query.ok === "1" ? "Thank you — your comment is published." :625    req.query.err ? String(req.query.err) : "";626  commentsPage(res, { flash, flashKind: req.query.ok === "1" ? "ok" : "err" });627});628629app.post("/comments", (req, res) => {630  const out = Comments.add({631    name: req.body.name,632    message: req.body.message,633    honeypot: req.body.website,634    ip: req.ip,635  });636  if (out.ok) return res.redirect(303, "/comments?ok=1");637  return res.redirect(303, "/comments?err=" + encodeURIComponent(out.error || "Could not post."));638});639640// ---------------------------------------------------------------- misc641app.get("/health", (req, res) => res.json({ ok: true, app: "modelmap-web", author: "Simon-Pierre Boucher", website: "https://modelmap.io" }));642643function notFound(res) {644  res.status(404);645  page(res, { title: "Not found", active: "", body: `<div class="card"><h1>404</h1><p>That page does not exist. <a href="/">Back to home</a>.</p></div>` });646}647app.use((req, res) => notFound(res));648649app.listen(PORT, () => console.log(`modelmap-web listening on :${PORT}`));650