Bootstrap modelmap: charter-compliant skeleton, tooling, and modelmap.io platform
- Repo skeleton per charter §3 with mandatory author headers everywhere - tools/check_headers.py (gates commits), new_experiment.py, new_map.py - benchmarks: hardware_manifest.py (sysctl-based) + harness skeleton - experiments/micro A-H scaffolded with empty seven-field blocks - site/: modelmap.io platform (Express, light theme, English), coherent with localvm-research/web — Home, Research, Experiments, Atlas (with confidence Levels 0-3), Results, Code, About; attribution footer Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Showing 58 changed files with +3,907 and −0
added
.gitignore
+13 −0
@@ -0,0 +1,13 @@ | ||
| 1 | +.DS_Store | |
| 2 | +__pycache__/ | |
| 3 | +*.pyc | |
| 4 | +.venv/ | |
| 5 | +node_modules/ | |
| 6 | +site/content/ | |
| 7 | +results/**/*.bin | |
| 8 | +*.log | |
| 9 | +.ruff_cache/ | |
| 10 | +.pytest_cache/ | |
| 11 | +# large binary artifacts live outside git (local artifact store) | |
| 12 | +atlas/**/*.zarr | |
| 13 | +atlas/**/activations/ | |
added
CITATION.cff
+28 −0
@@ -0,0 +1,28 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : CITATION.cff | |
| 4 | +# Purpose : Citation metadata for the modelmap research project | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
| 13 | +cff-version: 1.2.0 | |
| 14 | +message: "If you use findings or maps from this project, please cite it as below." | |
| 15 | +title: "modelmap — Internal Cartography of Local Large Language Models" | |
| 16 | +authors: | |
| 17 | + - family-names: "Boucher" | |
| 18 | + given-names: "Simon-Pierre" | |
| 19 | + email: "contact@spboucher.ai" | |
| 20 | +url: "https://modelmap.io" | |
| 21 | +date-released: "2026-08-12" | |
| 22 | +version: "0.1.0" | |
| 23 | +keywords: | |
| 24 | + - mechanistic interpretability | |
| 25 | + - large language models | |
| 26 | + - Apple Silicon | |
| 27 | + - model atlas | |
| 28 | + - reproducibility | |
added
CLAUDE.md
+631 −0
@@ -0,0 +1,631 @@ | ||
| 1 | +# CLAUDE.md | |
| 2 | + | |
| 3 | +**Project codename:** `modelmap` — Internal Cartography of Local Large Language Models | |
| 4 | +**Public site:** https://modelmap.io (the living research atlas & documentation site) | |
| 5 | +**Principal investigator / Author:** Simon-Pierre Boucher — <contact@spboucher.ai> | |
| 6 | +**Primary platform:** Apple Silicon Mac (macOS 14+) | |
| 7 | +**Sister project:** `localvm-research` (out-of-core execution) — findings must cross-pollinate | |
| 8 | +**Document status:** Living research charter. Claude must treat this as the authoritative project specification. | |
| 9 | + | |
| 10 | +--- | |
| 11 | + | |
| 12 | +## 0. Administrative conventions (MANDATORY — read before anything else) | |
| 13 | + | |
| 14 | +### 0.1 Author header requirement | |
| 15 | + | |
| 16 | +**Every single source file created in this project — without exception — must begin with a standardized author header.** | |
| 17 | + | |
| 18 | +This applies to: Python, C++, Swift, Objective-C, Metal shaders, JavaScript/TypeScript (for the modelmap.io site), shell scripts, Makefiles, CMake files, and any configuration file that supports comments. | |
| 19 | + | |
| 20 | +#### Python / shell / YAML / TOML header | |
| 21 | + | |
| 22 | +```python | |
| 23 | +# ============================================================================= | |
| 24 | +# Project : modelmap | |
| 25 | +# File : <relative/path/to/file.py> | |
| 26 | +# Purpose : <one-line description of what this file does> | |
| 27 | +# Author : Simon-Pierre Boucher | |
| 28 | +# Contact : contact@spboucher.ai | |
| 29 | +# Website : https://modelmap.io | |
| 30 | +# Created : <YYYY-MM-DD> | |
| 31 | +# Modified : <YYYY-MM-DD> | |
| 32 | +# Platform : macOS / Apple Silicon (arm64) | |
| 33 | +# License : All rights reserved (research code) | |
| 34 | +# ============================================================================= | |
| 35 | +``` | |
| 36 | + | |
| 37 | +#### C++ / Metal / Swift / JS / TS header | |
| 38 | + | |
| 39 | +```cpp | |
| 40 | +// ============================================================================ | |
| 41 | +// Project : modelmap | |
| 42 | +// File : <relative/path/to/file> | |
| 43 | +// Purpose : <one-line description> | |
| 44 | +// Author : Simon-Pierre Boucher | |
| 45 | +// Contact : contact@spboucher.ai | |
| 46 | +// Website : https://modelmap.io | |
| 47 | +// Created : <YYYY-MM-DD> | |
| 48 | +// Modified : <YYYY-MM-DD> | |
| 49 | +// Platform : macOS / Apple Silicon (arm64) — MLX / Metal / MPS | |
| 50 | +// License : All rights reserved (research code) | |
| 51 | +// ============================================================================ | |
| 52 | +``` | |
| 53 | + | |
| 54 | +#### Markdown research documents (front matter) | |
| 55 | + | |
| 56 | +```markdown | |
| 57 | +--- | |
| 58 | +project: modelmap | |
| 59 | +document: <name> | |
| 60 | +author: Simon-Pierre Boucher | |
| 61 | +contact: contact@spboucher.ai | |
| 62 | +website: https://modelmap.io | |
| 63 | +created: <YYYY-MM-DD> | |
| 64 | +status: draft | reviewed | final | |
| 65 | +--- | |
| 66 | +``` | |
| 67 | + | |
| 68 | +Rules: | |
| 69 | + | |
| 70 | +1. The header must be the **first content** of the file (after a shebang if present). | |
| 71 | +2. `Modified` must be updated on every substantial change. | |
| 72 | +3. Write `tools/check_headers.py` early; it must fail if any tracked source file lacks a conforming header, and it runs before every commit. | |
| 73 | +4. Generated artifacts (maps, JSON atlases, plots) must embed `"author": "Simon-Pierre Boucher", "contact": "contact@spboucher.ai", "website": "https://modelmap.io"` in their metadata wherever the format permits. | |
| 74 | +5. Every published map on modelmap.io must carry a visible attribution footer. | |
| 75 | + | |
| 76 | +### 0.2 macOS-first design constraint | |
| 77 | + | |
| 78 | +**Everything must be designed to run on a Mac.** Specifically: | |
| 79 | + | |
| 80 | +* Target machine: Apple Silicon (M1–M4 family), 16–64 GB unified memory, internal NVMe SSD. | |
| 81 | +* Default compute paths: **MLX**, **PyTorch with MPS backend**, **NumPy/Accelerate**, custom **Metal** kernels where hooks/probes need speed. CUDA is allowed only as an isolated optional validation path (`src/backends/cuda_optional/`), never a core dependency. | |
| 82 | +* Activation capture at scale generates large tensors: design the capture pipeline for **unified memory limits** (streaming to disk, memory-mapped activation stores, chunked capture) — never assume activations fit in RAM. | |
| 83 | +* Storage of maps/activations must respect APFS behavior and Apple NVMe characteristics; use efficient columnar/array formats (safetensors, zarr, parquet, HDF5) benchmarked **on macOS**, not assumed from Linux numbers. | |
| 84 | +* Instrumentation must use macOS-native sources: mach `task_info`, `vm_stat`, `fs_usage`, `powermetrics` (graceful degradation without sudo), `sysctl hw.*`. | |
| 85 | +* Build tooling: `uv`/`pip` + venv for Python; `cmake` + AppleClang for C++; `xcrun -sdk macosx metal` for shaders; standard Node toolchain for the modelmap.io site (static-site friendly — the site must build and preview locally on the Mac). | |
| 86 | +* A hardware manifest (`benchmarks/hardware_manifest.py`) records chip, P/E core counts, GPU cores, RAM, SSD model, macOS version, and all software versions into every result and every published map. | |
| 87 | + | |
| 88 | +### 0.3 Repository discipline | |
| 89 | + | |
| 90 | +* Git from day one; meaningful commits; no result reported from an uncommitted tree. | |
| 91 | +* Every map must be reproducible from: commit hash + config file + model hash + seed + hardware manifest. | |
| 92 | +* Python: type hints, `ruff`, `pytest` (numerical probes get correctness tests against tiny reference models). | |
| 93 | +* Datasets of prompts used for mapping are versioned artifacts with checksums. | |
| 94 | +* Large binary artifacts (activation dumps) live outside git (local artifact store with an index file committed to git). | |
| 95 | + | |
| 96 | +--- | |
| 97 | + | |
| 98 | +## 1. Mission | |
| 99 | + | |
| 100 | +Your objective is to investigate, design, implement, and experimentally validate a systematic methodology — and the tooling behind it — to **discover, measure, and map the internal structure of pretrained open-weight LLMs running locally on consumer Apple Silicon hardware**, and to publish the resulting maps as a rigorous, reproducible public atlas at **modelmap.io**. | |
| 101 | + | |
| 102 | +The target problem is **not**: | |
| 103 | + | |
| 104 | +* training new models; | |
| 105 | +* fine-tuning models to change their behavior; | |
| 106 | +* reproducing an existing interpretability library (TransformerLens, NNsight, SAELens, Captum) without adding anything; | |
| 107 | +* producing pretty but unfalsifiable visualizations; | |
| 108 | +* benchmarking model *outputs* (that is leaderboard work, not cartography); | |
| 109 | +* speculative "the model thinks X" storytelling without measurement. | |
| 110 | + | |
| 111 | +The target is broader and more rigorous: | |
| 112 | + | |
| 113 | +> **Given an already-trained open-weight model, determine what can actually be known, measured, localized, and mapped about its internal organization — where knowledge lives, how computation is distributed, which structures are stable across inputs, layers, scales, and model families — using only local, consumer-grade Mac hardware, and turn that knowledge into reproducible, comparable, publishable maps.** | |
| 114 | + | |
| 115 | +Do not assume in advance which mapping techniques are informative. | |
| 116 | +Do not force a particular interpretability framework. | |
| 117 | +Do not begin implementation before understanding the state of the art. | |
| 118 | +Your job is to discover what can genuinely be mapped — and what cannot. | |
| 119 | + | |
| 120 | +--- | |
| 121 | + | |
| 122 | +## 2. Core research questions | |
| 123 | + | |
| 124 | +Investigate: | |
| 125 | + | |
| 126 | +> **Q1 — Localization.** Where inside a pretrained LLM do specific capabilities, knowledge domains, languages, and behaviors reside? Are they localized (layers, heads, neurons, weight blocks, feature directions) or diffuse? | |
| 127 | + | |
| 128 | +> **Q2 — Structure.** What 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? | |
| 129 | + | |
| 130 | +> **Q3 — Comparability.** Can internal maps be compared across model sizes, checkpoints, quantization levels, and model families? Is there a common "coordinate system" for model internals? | |
| 131 | + | |
| 132 | +> **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? | |
| 133 | + | |
| 134 | +> **Q5 — Utility.** Do the maps predict anything useful — quantization sensitivity, pruning tolerance, working-set behavior (feeding `localvm-research`), failure modes, editing targets? | |
| 135 | + | |
| 136 | +The most important distinction to keep sharp throughout: | |
| 137 | + | |
| 138 | +```text | |
| 139 | +what a probing method reports | |
| 140 | +≠ | |
| 141 | +what the model actually computes | |
| 142 | +≠ | |
| 143 | +what is stable across methods, seeds, and datasets | |
| 144 | +≠ | |
| 145 | +what is causally verified by intervention | |
| 146 | +``` | |
| 147 | + | |
| 148 | +A claim only enters the atlas at the confidence level its evidence supports. Correlational maps and causally-verified maps must never be visually or textually conflated. | |
| 149 | + | |
| 150 | +### Guiding principle | |
| 151 | + | |
| 152 | +Do not optimize first for spectacular visuals. Initially optimize for discovering **which measurements are real** — reproducible, method-robust, causally grounded. | |
| 153 | + | |
| 154 | +Negative results ("technique X produces maps that do not replicate across seeds") are first-class publishable findings on modelmap.io. | |
| 155 | + | |
| 156 | +--- | |
| 157 | + | |
| 158 | +## 3. Project structure (real research-project layout) | |
| 159 | + | |
| 160 | +Create this skeleton (with header-compliant placeholder files) before Phase 1 concludes: | |
| 161 | + | |
| 162 | +```text | |
| 163 | +modelmap/ | |
| 164 | +├── CLAUDE.md # this charter | |
| 165 | +├── README.md # public summary (written last, updated continuously) | |
| 166 | +├── CITATION.cff # author: Simon-Pierre Boucher | |
| 167 | +├── LICENSE | |
| 168 | +├── pyproject.toml | |
| 169 | +├── Makefile # setup, lint, test, capture, map, site, headers | |
| 170 | +│ | |
| 171 | +├── research/ # the scientific paper trail | |
| 172 | +│ ├── LOG.md # dated, append-only research log | |
| 173 | +│ ├── state_of_the_art.md # Phase 2 deliverable | |
| 174 | +│ ├── research_gaps.md # Phase 3 deliverable | |
| 175 | +│ ├── candidate_ranking.md # Phase 4 deliverable | |
| 176 | +│ ├── methodology.md # the formal mapping methodology (Phase 9) | |
| 177 | +│ ├── novelty_check.md # Phase 11 deliverable | |
| 178 | +│ ├── bibliography.md # every source, links + access dates | |
| 179 | +│ └── notes/ # per-topic reading notes | |
| 180 | +│ | |
| 181 | +├── src/ | |
| 182 | +│ └── modelmap/ | |
| 183 | +│ ├── __init__.py | |
| 184 | +│ ├── models/ # loading (safetensors/GGUF), architecture introspection | |
| 185 | +│ ├── capture/ # activation hooks, streaming capture, mmap stores | |
| 186 | +│ ├── probes/ # linear probes, logit lens, tuned lens, causal probes | |
| 187 | +│ ├── interventions/ # ablation, patching, steering, weight zeroing | |
| 188 | +│ ├── features/ # SAE training/inference, dictionary methods | |
| 189 | +│ ├── geometry/ # weight-space analysis: SVD, spectra, similarity (CKA…) | |
| 190 | +│ ├── graphs/ # circuit extraction, attribution graphs | |
| 191 | +│ ├── atlas/ # map schema, serialization, versioning, provenance | |
| 192 | +│ ├── viz/ # figure + interactive-map generation | |
| 193 | +│ ├── backends/ # mlx_backend / mps_backend / cpu_backend / cuda_optional | |
| 194 | +│ ├── instrumentation/ # macOS-native cost measurement | |
| 195 | +│ └── stats/ # replication tests, bootstrap CIs, multiple-comparison control | |
| 196 | +│ | |
| 197 | +├── experiments/ | |
| 198 | +│ ├── micro/ | |
| 199 | +│ │ ├── expA_probe_reliability/ | |
| 200 | +│ │ ├── expB_localization_vs_diffusion/ | |
| 201 | +│ │ ├── expC_causal_verification/ | |
| 202 | +│ │ ├── expD_cross_input_stability/ | |
| 203 | +│ │ ├── expE_weight_geometry/ | |
| 204 | +│ │ ├── expF_quantization_map_drift/ | |
| 205 | +│ │ ├── expG_cross_model_alignment/ | |
| 206 | +│ │ └── expH_capture_cost_frontier/ | |
| 207 | +│ ├── candidate_01/ | |
| 208 | +│ ├── candidate_02/ | |
| 209 | +│ └── candidate_03/ | |
| 210 | +│ └── (each: README.md, hypothesis.md, implementation/, | |
| 211 | +│ benchmark.py, results/, analysis.md) | |
| 212 | +│ | |
| 213 | +├── atlas/ # the maps themselves (versioned data products) | |
| 214 | +│ └── <model_id>/<map_type>/<version>/ | |
| 215 | +│ ├── map.json / map.zarr | |
| 216 | +│ ├── provenance.json # commit, config, model hash, hardware, dates | |
| 217 | +│ └── confidence.md # evidence level: correlational | replicated | causal | |
| 218 | +│ | |
| 219 | +├── site/ # modelmap.io source (static-site, builds locally on Mac) | |
| 220 | +│ ├── content/ # one page per model, per map type, per finding | |
| 221 | +│ ├── components/ # interactive map viewers | |
| 222 | +│ └── data/ # published atlas exports | |
| 223 | +│ | |
| 224 | +├── benchmarks/ | |
| 225 | +│ ├── harness.py | |
| 226 | +│ ├── hardware_manifest.py | |
| 227 | +│ └── promptsets/ # versioned mapping corpora: code, math, FR, EN, facts… | |
| 228 | +│ | |
| 229 | +├── results/ # raw + aggregated results | |
| 230 | +│ └── <experiment_id>/<timestamp>/ | |
| 231 | +│ | |
| 232 | +├── tools/ | |
| 233 | +│ ├── check_headers.py | |
| 234 | +│ ├── new_experiment.py # scaffolds compliant experiment dirs | |
| 235 | +│ ├── new_map.py # scaffolds a compliant atlas entry with provenance | |
| 236 | +│ └── publish.py # atlas → site/data export with attribution | |
| 237 | +│ | |
| 238 | +└── docs/ | |
| 239 | +``` | |
| 240 | + | |
| 241 | +Every experiment directory is scaffolded by `tools/new_experiment.py` with the seven-field scientific block (§10). Every atlas entry is scaffolded by `tools/new_map.py` and **cannot be published without a completed `provenance.json` and `confidence.md`**. | |
| 242 | + | |
| 243 | +--- | |
| 244 | + | |
| 245 | +## 4. Phase 1 — Ultra-deep literature research | |
| 246 | + | |
| 247 | +Before proposing a methodology, perform an extremely deep search of current literature, repositories, technical reports, papers, preprints, blog posts, implementations, and discussions. | |
| 248 | + | |
| 249 | +Search arXiv, OpenReview, conference proceedings (NeurIPS, ICML, ICLR, ACL, EMNLP), the mechanistic-interpretability community corpus (Anthropic interpretability publications, Transformer Circuits thread, Neel Nanda's work, EleutherAI, LessWrong/Alignment Forum technical posts), GitHub (TransformerLens, NNsight, SAELens, Penzai, Captum, baukit), Hugging Face, and Apple/MLX engineering material. | |
| 250 | + | |
| 251 | +Prefer primary sources. For every important technique find the paper **and** the implementation, and note whether it runs on Apple Silicon. Log everything in `research/bibliography.md` with URLs and access dates. Follow citations backward and forward. | |
| 252 | + | |
| 253 | +### Areas that must be investigated | |
| 254 | + | |
| 255 | +#### 4.1 Observational probing | |
| 256 | +Linear probes; logit lens; tuned lens; early decoding; representation reading; concept erasure (LEACE-style); probing classifier pitfalls and controls; selectivity/controls literature (what probes overfit to). | |
| 257 | + | |
| 258 | +#### 4.2 Causal / interventional methods | |
| 259 | +Activation patching; path patching; attribution patching; causal tracing (ROME-style locating); ablations (zero, mean, resample); causal scrubbing; interchange interventions; steering vectors; representation engineering; distributed alignment search. | |
| 260 | + | |
| 261 | +#### 4.3 Feature decomposition | |
| 262 | +Sparse autoencoders (SAEs) and their variants (gated, top-k, jumpReLU, crosscoders, transcoders); dictionary learning; superposition theory; polysemanticity; feature splitting; SAE evaluation problems (reconstruction vs interpretability vs faithfulness); automated feature labeling and its failure modes. | |
| 263 | + | |
| 264 | +#### 4.4 Circuits and computational graphs | |
| 265 | +Induction heads; IOI-style circuit analyses; attention head taxonomies; MLP key–value memory view; attribution graphs; automated circuit discovery (ACDC and successors); edge/node pruning of computational graphs; faithfulness metrics for circuits. | |
| 266 | + | |
| 267 | +#### 4.5 Weight-space cartography (no forward pass required) | |
| 268 | +SVD spectra of weight matrices; effective rank per layer; weight statistics across depth; outlier channels/dimensions; cross-layer similarity (CKA, CCA, Procrustes); model stitching; permutation alignment / git re-basin; mode connectivity; delta analysis between checkpoints and between base/instruct variants; MoE router and expert structure. | |
| 269 | + | |
| 270 | +#### 4.6 Knowledge localization and editing | |
| 271 | +ROME, MEMIT and successors; where facts are stored vs where they are retrieved; the localization-vs-editing critique literature (does editing success actually imply localization?); knowledge neurons; multilingual knowledge sharing. | |
| 272 | + | |
| 273 | +#### 4.7 Cross-model and cross-scale comparison | |
| 274 | +Representation similarity across scales and families; universality of features/circuits; platonic representation hypothesis debates; tokenizer effects on comparability; quantization effects on internal representations (largely unmapped — note carefully). | |
| 275 | + | |
| 276 | +#### 4.8 Statistics and epistemology of interpretability | |
| 277 | +Illusions in interpretability results; seed sensitivity; dataset sensitivity of probes; multiple-comparison problems when scanning thousands of neurons/heads; pre-registration analogues; faithfulness vs plausibility; benchmarking interpretability methods (e.g., finding planted circuits in known models). | |
| 278 | + | |
| 279 | +#### 4.9 Systems side (local mapping at scale) | |
| 280 | +Activation caching formats and costs; hook overhead in MLX vs PyTorch-MPS; streaming SAE training on limited memory; disk layouts for random access into activation stores; what interpretability workloads exist for GGUF/quantized runtimes (llama.cpp introspection); GPU capture on Metal. | |
| 281 | + | |
| 282 | +#### 4.10 Visualization and atlas design | |
| 283 | +Prior art on model atlases (Neuroscope, Neuronpedia, OpenAI microscope-style efforts, feature browsers); what made them useful or misleading; provenance and versioning practices for data products; how to encode uncertainty visually. | |
| 284 | + | |
| 285 | +--- | |
| 286 | + | |
| 287 | +## 5. Phase 2 — State-of-the-art map | |
| 288 | + | |
| 289 | +Produce `research/state_of_the_art.md`. Organize every relevant technique by: | |
| 290 | + | |
| 291 | +```text | |
| 292 | +Technique | |
| 293 | +What it claims to reveal | |
| 294 | +Observational or causal? | |
| 295 | +Model access required (weights / activations / gradients) | |
| 296 | +Compute + memory cost class | |
| 297 | +Feasible on 16–64 GB Apple Silicon? (measured or estimated, at which model sizes) | |
| 298 | +Known failure modes / illusions | |
| 299 | +Reproducibility record | |
| 300 | +Open-source implementation (and does it build on macOS arm64 / MPS / MLX?) | |
| 301 | +Main limitation | |
| 302 | +Opportunity for extension | |
| 303 | +``` | |
| 304 | + | |
| 305 | +Do not simply summarize papers. Identify overlaps, already-tried combinations, and ideas that seem novel but are known. Explicitly flag the epistemic status of each technique (established / contested / debunked-in-part). | |
| 306 | + | |
| 307 | +--- | |
| 308 | + | |
| 309 | +## 6. Phase 3 — Identify genuine gaps | |
| 310 | + | |
| 311 | +Produce `research/research_gaps.md`. For every promising gap explain: | |
| 312 | + | |
| 313 | +1. what existing work does; | |
| 314 | +2. what it does not do; | |
| 315 | +3. why the missing capability might matter; | |
| 316 | +4. whether there is a plausible mathematical or empirical reason it could work; | |
| 317 | +5. the biggest reason it might fail; | |
| 318 | +6. the smallest experiment capable of falsifying it — **runnable on a Mac**. | |
| 319 | + | |
| 320 | +Generate at least **20 substantially different candidate directions**. Do not make them superficial variants. | |
| 321 | + | |
| 322 | +Example categories (non-binding): quantization-aware internal maps; weight-only cheap cartography as a pre-screen for expensive activation methods; cross-model coordinate systems; maps that predict systems properties (working sets, pruning tolerance — bridge to `localvm`); method-agreement scoring (map = intersection of techniques); longitudinal maps across a model family; multilingual localization atlases; local-hardware-feasible SAE recipes; causal-verification pipelines that scale down; uncertainty-first atlas schemas. | |
| 323 | + | |
| 324 | +These are examples only. Do not constrain the search to them. | |
| 325 | + | |
| 326 | +--- | |
| 327 | + | |
| 328 | +## 7. Phase 4 — Rank candidate approaches | |
| 329 | + | |
| 330 | +Create `research/candidate_ranking.md`. Score every idea 1–10 with written reasoning on: | |
| 331 | + | |
| 332 | +```text | |
| 333 | +Scientific value (what new knowledge does the map contain?) | |
| 334 | +Epistemic soundness (can claims be causally verified?) | |
| 335 | +Novelty | |
| 336 | +Feasibility on Apple Silicon (memory, time, kernel availability) | |
| 337 | +Reproducibility potential | |
| 338 | +Cross-model generality | |
| 339 | +Public atlas value (is it worth publishing on modelmap.io?) | |
| 340 | +Implementation complexity | |
| 341 | +Synergy with localvm-research | |
| 342 | +Risk | |
| 343 | +``` | |
| 344 | + | |
| 345 | +Select roughly **3–5 strongest candidates** for prototyping. | |
| 346 | + | |
| 347 | +Prefer directions that produce **comparable, versionable, falsifiable map artifacts** rather than one-off analyses. | |
| 348 | + | |
| 349 | +--- | |
| 350 | + | |
| 351 | +## 8. Phase 5 — Experimental framework | |
| 352 | + | |
| 353 | +Build the framework **before** mapping large models. Correctness and measurement first; optimization later. | |
| 354 | + | |
| 355 | +Preferred stack (Mac-first): | |
| 356 | + | |
| 357 | +```text | |
| 358 | +Python (MLX, PyTorch-MPS, NumPy, safetensors, zarr) for research tooling | |
| 359 | +C++ / Metal where hook or SAE throughput demands it | |
| 360 | +TypeScript + static-site generator for modelmap.io (local build/preview on the Mac) | |
| 361 | +CUDA only as isolated optional validation | |
| 362 | +``` | |
| 363 | + | |
| 364 | +### 8.1 Model progression | |
| 365 | + | |
| 366 | +```text | |
| 367 | +0.5B–1B → 3B → 7B–8B → 14B → 32B | |
| 368 | +``` | |
| 369 | + | |
| 370 | +Move larger only when evidence and the cost frontier (Experiment H) support it. Prefer modern open-weight families with multiple sizes (to enable cross-scale maps) and both base + instruct checkpoints (to enable delta maps). | |
| 371 | + | |
| 372 | +### 8.2 Instrumentation | |
| 373 | + | |
| 374 | +Measure, for every mapping run: | |
| 375 | + | |
| 376 | +```text | |
| 377 | +resident/peak RAM activation bytes captured | |
| 378 | +disk bytes written/read capture overhead vs plain inference | |
| 379 | +wall-clock per mapped unit GPU/CPU utilization (P vs E cores) | |
| 380 | +hook dispatch overhead SAE training throughput | |
| 381 | +thermal state / throttling energy via powermetrics when available | |
| 382 | +``` | |
| 383 | + | |
| 384 | +And for every scientific result: | |
| 385 | + | |
| 386 | +```text | |
| 387 | +replication across ≥3 seeds replication across ≥2 prompt sets | |
| 388 | +bootstrap confidence intervals effect sizes, not just p-values | |
| 389 | +method agreement (≥2 techniques) causal verification status | |
| 390 | +multiple-comparison correction when scanning many units | |
| 391 | +``` | |
| 392 | + | |
| 393 | +### 8.3 Most important scientific metric | |
| 394 | + | |
| 395 | +Track, for every map: | |
| 396 | + | |
| 397 | +```text | |
| 398 | +REPLICATION RATE (does the map reproduce under resampling of seeds/data?) | |
| 399 | +CAUSAL CONFIRMATION RATE (what fraction of localized claims survive intervention?) | |
| 400 | +``` | |
| 401 | + | |
| 402 | +A beautiful map that does not replicate is worth less than a boring one that does. | |
| 403 | + | |
| 404 | +### 8.4 Baselines and controls | |
| 405 | + | |
| 406 | +Every mapping technique must be run with controls: shuffled labels for probes; random-direction baselines for steering; randomly-initialized-model baselines where meaningful; resample ablations vs zero ablations. Never claim structure without showing the null. | |
| 407 | + | |
| 408 | +--- | |
| 409 | + | |
| 410 | +## 9. Phase 6 — Micro-experiments | |
| 411 | + | |
| 412 | +Each lives in `experiments/micro/` with the standard scaffold. | |
| 413 | + | |
| 414 | +### Experiment A — Probe reliability | |
| 415 | +For a small model, train linear probes for a set of properties across all layers, with shuffled-label controls, ≥5 seeds, ≥2 datasets. Quantify how much probe accuracy varies with seed/data. Establish the noise floor every later map must exceed. | |
| 416 | + | |
| 417 | +### Experiment B — Localization vs diffusion | |
| 418 | +For selected capabilities (a language, a knowledge domain, arithmetic, code syntax), measure how concentrated the supporting signal is across layers/heads/neurons/weight blocks. Ask: does removing the top-k localized units remove the capability, or does it survive (diffusion)? Test across code, math, chat, French, English, factual recall, reasoning. | |
| 419 | + | |
| 420 | +### Experiment C — Causal verification pipeline | |
| 421 | +Take the top findings from A/B and verify them with activation patching and ablation. Measure the correlational→causal survival rate. This number calibrates all future confidence labels in the atlas. | |
| 422 | + | |
| 423 | +### Experiment D — Cross-input stability | |
| 424 | +For fixed internal structures (heads, features, directions), measure stability across prompts, domains, context lengths, and sampling temperature. Compute overlap/Jaccard of "important units" across inputs. Determine which map types are input-conditional vs input-invariant. | |
| 425 | + | |
| 426 | +### Experiment E — Weight-space geometry (zero-forward-pass maps) | |
| 427 | +Compute per-layer SVD spectra, effective ranks, outlier dimensions, and cross-layer CKA purely from weights. Test whether these cheap maps predict anything measured in A–D. If yes, weight-only cartography becomes a fast pre-screen for any model. | |
| 428 | + | |
| 429 | +### Experiment F — Quantization map drift | |
| 430 | +Map the same model at FP16, Q8, Q4, Q2 (where practical). Measure how much probes, feature directions, and localized circuits drift under quantization. This is under-explored territory and directly relevant to local models, which are almost always quantized. | |
| 431 | + | |
| 432 | +### Experiment G — Cross-model alignment | |
| 433 | +For two sizes of the same family, attempt to align internal maps (representation similarity, feature matching, permutation alignment). Determine whether a shared coordinate system is achievable and at what granularity. | |
| 434 | + | |
| 435 | +### Experiment H — Capture cost frontier (macOS-specific) | |
| 436 | +Measure the real cost of activation capture on the Mac: hook overhead in MLX vs PyTorch-MPS, streaming write throughput to the internal NVMe, storage formats (safetensors vs zarr vs raw mmap), maximum feasible capture per model size within 16/32/64 GB. Produce a published "what can you map on which Mac" table — itself a modelmap.io artifact. | |
| 437 | + | |
| 438 | +--- | |
| 439 | + | |
| 440 | +## 10. Scientific discipline | |
| 441 | + | |
| 442 | +For every experiment write, in `hypothesis.md` / `analysis.md`: | |
| 443 | + | |
| 444 | +```text | |
| 445 | +Hypothesis | |
| 446 | +Falsification criterion | |
| 447 | +Method (including controls) | |
| 448 | +Baseline / null | |
| 449 | +Result (with effect sizes and CIs) | |
| 450 | +Interpretation (with explicit confidence level) | |
| 451 | +Next experiment | |
| 452 | +``` | |
| 453 | + | |
| 454 | +Avoid confirmation bias. If a technique produces illusory structure, record it — that is a finding. **Do not silently discard failures.** | |
| 455 | + | |
| 456 | +### Evidence standard | |
| 457 | + | |
| 458 | +Never write "this neuron does X", "this layer stores Y", "feature Z represents W" without stating: the method, the controls, the replication status, and whether the claim is correlational or causally verified. Report: number of seeds, prompt sets, effect sizes, CIs, hardware, model+quantization, software versions. | |
| 459 | + | |
| 460 | +### Confidence taxonomy (used everywhere, including on the site) | |
| 461 | + | |
| 462 | +```text | |
| 463 | +Level 0 — anecdotal (single run, no controls; never published alone) | |
| 464 | +Level 1 — correlational (controlled, replicated ≥3 seeds, ≥2 datasets) | |
| 465 | +Level 2 — method-robust (Level 1 + agreement across ≥2 independent techniques) | |
| 466 | +Level 3 — causal (Level 2 + intervention confirms the claim) | |
| 467 | +``` | |
| 468 | + | |
| 469 | +Every atlas entry carries its level in `confidence.md` and visibly on modelmap.io. | |
| 470 | + | |
| 471 | +--- | |
| 472 | + | |
| 473 | +## 11. Phase 7 — Prototype candidate methodologies | |
| 474 | + | |
| 475 | +For each of the strongest candidates create `experiments/candidate_0N/` with the standard contents. All implementations must run on the primary Mac target without CUDA. Each candidate's `analysis.md` must end with an explicit recommendation: promote to atlas pipeline, iterate, or reject (with reasons). | |
| 476 | + | |
| 477 | +--- | |
| 478 | + | |
| 479 | +## 12. Phase 8 — Automatic research loop | |
| 480 | + | |
| 481 | +Operate as a research agent. After every experiment: analyze; pick the most informative next experiment; re-search the literature when results surprise; update hypotheses; modify the methodology; rerun; compare; continue until evidence strongly favors or rejects the approach. Do not follow a rigid roadmap if experiments contradict it. | |
| 482 | + | |
| 483 | +Maintain `research/LOG.md` (append-only): date/time with timezone, question, experiment, result, interpretation, decision. The entire reasoning process must be auditable. | |
| 484 | + | |
| 485 | +--- | |
| 486 | + | |
| 487 | +## 13. Phase 9 — Formalize the mapping methodology | |
| 488 | + | |
| 489 | +Once micro-experiments and candidates converge, write `research/methodology.md`: the formal, reusable pipeline that turns *(model checkpoint + prompt corpora + budget)* into *(versioned atlas entries with confidence levels)*. | |
| 490 | + | |
| 491 | +It must specify: capture protocol; probe/feature/circuit procedures actually retained; mandatory controls; replication requirements; causal-verification requirements per confidence level; cost model per Mac tier; map schema; provenance schema; publication checklist. | |
| 492 | + | |
| 493 | +The methodology is itself a primary deliverable — it should let any researcher with a Mac reproduce a map. | |
| 494 | + | |
| 495 | +--- | |
| 496 | + | |
| 497 | +## 14. Phase 10 — Build the atlas pipeline and modelmap.io | |
| 498 | + | |
| 499 | +Build the production pipeline supported by evidence. A possible CLI shape (finalize only after research): | |
| 500 | + | |
| 501 | +```bash | |
| 502 | +modelmap capture MODEL_PATH --promptset benchmarks/promptsets/core --out atlas/ | |
| 503 | +modelmap analyze atlas/<model_id> --maps probes,features,geometry,circuits | |
| 504 | +modelmap verify atlas/<model_id> --level causal | |
| 505 | +modelmap publish atlas/<model_id> --site site/ | |
| 506 | +``` | |
| 507 | + | |
| 508 | +### The site (modelmap.io) | |
| 509 | + | |
| 510 | +* Static-site architecture, built and previewed locally on the Mac; every page generated from atlas data, never hand-edited numbers. | |
| 511 | +* One page per model; per map type; interactive viewers (layer/head/feature browsers) built from `atlas/` exports. | |
| 512 | +* Every visualization displays: confidence level, provenance (commit, model hash, date, hardware), and how to reproduce. | |
| 513 | +* A methodology section publishing `research/methodology.md`. | |
| 514 | +* A negative-results section. Publishing what does **not** replicate is part of the mission. | |
| 515 | +* Attribution footer on every page: *Simon-Pierre Boucher — contact@spboucher.ai*. | |
| 516 | + | |
| 517 | +### Hard constraints | |
| 518 | + | |
| 519 | +* Only open-weight models are mapped; the original checkpoint is always the object of study. | |
| 520 | +* No claim appears on modelmap.io above its evidence level. | |
| 521 | +* Nothing is published that cannot be regenerated from committed code + versioned data. | |
| 522 | + | |
| 523 | +--- | |
| 524 | + | |
| 525 | +## 15. Target hardware | |
| 526 | + | |
| 527 | +Primary research target: | |
| 528 | + | |
| 529 | +```text | |
| 530 | +Apple Silicon Mac (laptop or desktop) | |
| 531 | +16–64 GB unified memory | |
| 532 | +internal Apple NVMe SSD | |
| 533 | +Metal-capable GPU sharing memory with CPU | |
| 534 | +``` | |
| 535 | + | |
| 536 | +Apple Silicon matters here because: unified memory lets capture pipelines avoid device copies; MLX enables custom, low-overhead hooks; fast internal SSDs make large activation stores workable; and — crucially — **local interpretability on consumer hardware is itself an under-served research niche**: most interpretability tooling assumes CUDA clusters. Making rigorous mapping feasible on a Mac is part of the contribution. | |
| 537 | + | |
| 538 | +Document (without necessarily implementing) how each pipeline stage would map to x86 + discrete GPU. | |
| 539 | + | |
| 540 | +### Stretch target | |
| 541 | + | |
| 542 | +```text | |
| 543 | +A full, causally-verified, versioned atlas of a 7B–14B open-weight model, | |
| 544 | +produced end-to-end on a single 32–64 GB Mac, | |
| 545 | +comparable across ≥2 quantization levels and ≥2 model sizes, | |
| 546 | +published on modelmap.io with complete provenance. | |
| 547 | +``` | |
| 548 | + | |
| 549 | +Do not assume this is achievable. The research must establish the actual limits. | |
| 550 | + | |
| 551 | +--- | |
| 552 | + | |
| 553 | +## 16. What counts as a breakthrough | |
| 554 | + | |
| 555 | +A result is scientifically interesting if at least one of the following is demonstrated: | |
| 556 | + | |
| 557 | +**A.** A mapping methodology whose claims survive causal verification at a measured, reported rate — with the rate itself published. | |
| 558 | +**B.** Cheap (weight-only or low-capture) maps that reliably predict expensive properties (capability localization, quantization sensitivity, pruning tolerance, working sets). | |
| 559 | +**C.** A cross-model coordinate system allowing internal maps to be meaningfully compared across sizes or families. | |
| 560 | +**D.** The first systematic map of how quantization deforms internal structure of local models. | |
| 561 | +**E.** A demonstrated bridge to systems research: maps that materially improve out-of-core execution decisions in `localvm-research`. | |
| 562 | +**F.** A rigorous negative result: demonstration that a widely-used mapping technique fails replication or causal verification under controlled conditions. | |
| 563 | + | |
| 564 | +## 17. Failure criteria | |
| 565 | + | |
| 566 | +Be willing to conclude that an approach does not work. For example: | |
| 567 | + | |
| 568 | +```text | |
| 569 | +probe results are dominated by seed/dataset noise | |
| 570 | +localized structure evaporates under causal testing | |
| 571 | +SAE features do not replicate across training runs | |
| 572 | +maps are quantization-fragile to the point of uselessness | |
| 573 | +cross-model alignment finds no stable correspondence | |
| 574 | +capture costs exceed consumer-Mac feasibility at useful model sizes | |
| 575 | +method agreement is near chance | |
| 576 | +``` | |
| 577 | + | |
| 578 | +If these occur, document them in `research/LOG.md` and the relevant `analysis.md`, publish the negative result where warranted, and pivot. | |
| 579 | + | |
| 580 | +--- | |
| 581 | + | |
| 582 | +## 18. Deliverables | |
| 583 | + | |
| 584 | +```text | |
| 585 | +README.md, CITATION.cff | |
| 586 | +research/ (state_of_the_art, research_gaps, candidate_ranking, | |
| 587 | + methodology, novelty_check, LOG, bibliography) | |
| 588 | +src/ (the modelmap library) | |
| 589 | +experiments/, benchmarks/, results/ | |
| 590 | +atlas/ (versioned map artifacts with provenance + confidence) | |
| 591 | +site/ (modelmap.io source) | |
| 592 | +tools/, docs/ | |
| 593 | +``` | |
| 594 | + | |
| 595 | +The final README must explain: the problem; what existing interpretability work does; what gap was found; the methodology; why it is sound; experimental evidence (numbers, named Mac hardware); the atlas and site; limitations; exact Mac reproduction instructions; future research. | |
| 596 | + | |
| 597 | +--- | |
| 598 | + | |
| 599 | +## 19. Phase 11 — Novelty verification | |
| 600 | + | |
| 601 | +Before claiming novelty of any methodology, map type, or finding, perform a dedicated final literature search using terminology derived from what was actually built — and every plausible synonym and conceptual equivalent (e.g., "model atlas", "neuron catalog", "feature browser", "representation cartography", "interpretability benchmark", "quantized-model interpretability"). Assume an idea is **not** novel until evidence suggests otherwise. Record process and conclusion in `research/novelty_check.md`. | |
| 602 | + | |
| 603 | +--- | |
| 604 | + | |
| 605 | +## 20. Most important instruction | |
| 606 | + | |
| 607 | +Do not become attached to any particular technique suggested in this document. | |
| 608 | + | |
| 609 | +The purpose of this project is **not to implement a preconceived SAE browser, neuron catalog, or circuit viewer**. Those ideas are merely clues. | |
| 610 | + | |
| 611 | +The actual assignment is: | |
| 612 | + | |
| 613 | +> **Search deeply enough, reason independently enough, control and replicate rigorously enough, and intervene causally enough to discover what can truly be known about the inside of a local LLM on consumer Mac hardware — and publish only that, at its honest confidence level, on modelmap.io.** | |
| 614 | + | |
| 615 | +Start with literature. Then generate hypotheses. Then falsify them experimentally. Let evidence determine the methodology. | |
| 616 | + | |
| 617 | +The ideal outcome is not another gallery of suggestive visualizations. The ideal outcome is a **reproducible cartographic standard for open-weight models** — where every map is versioned, provenanced, confidence-labeled, and regenerable by anyone with a Mac — turning: | |
| 618 | + | |
| 619 | +```text | |
| 620 | +"we think the model does X" | |
| 621 | +``` | |
| 622 | + | |
| 623 | +into: | |
| 624 | + | |
| 625 | +```text | |
| 626 | +"here is the map, its evidence level, and the script that rebuilds it" | |
| 627 | +``` | |
| 628 | + | |
| 629 | +--- | |
| 630 | + | |
| 631 | +*Author: Simon-Pierre Boucher — contact@spboucher.ai — https://modelmap.io — All research artifacts in this repository carry this attribution.* | |
added
LICENSE
+9 −0
@@ -0,0 +1,9 @@ | ||
| 1 | +All rights reserved (research code) | |
| 2 | + | |
| 3 | +Copyright (c) 2026 Simon-Pierre Boucher <contact@spboucher.ai> | |
| 4 | +https://modelmap.io | |
| 5 | + | |
| 6 | +This repository contains research code and research artifacts. No permission is | |
| 7 | +granted to use, copy, modify, or distribute this software or the published maps | |
| 8 | +without prior written consent from the author, except for reading and citing | |
| 9 | +the published research with attribution. | |
added
Makefile
+34 −0
@@ -0,0 +1,34 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : Makefile | |
| 4 | +# Purpose : Common tasks — setup, lint, test, headers check, site build/run | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
| 13 | +.PHONY: setup lint test headers site-sync site-run site-deps | |
| 14 | + | |
| 15 | +setup: | |
| 16 | + python3 -m venv .venv && .venv/bin/pip install -e ".[dev]" | |
| 17 | + | |
| 18 | +lint: | |
| 19 | + .venv/bin/ruff check src tools benchmarks | |
| 20 | + | |
| 21 | +test: | |
| 22 | + .venv/bin/pytest -q | |
| 23 | + | |
| 24 | +headers: | |
| 25 | + python3 tools/check_headers.py | |
| 26 | + | |
| 27 | +site-deps: | |
| 28 | + cd site && npm install | |
| 29 | + | |
| 30 | +site-sync: | |
| 31 | + site/sync-content.sh | |
| 32 | + | |
| 33 | +site-run: site-sync | |
| 34 | + cd site && PORT=8140 node server.js | |
added
README.md
+71 −0
@@ -0,0 +1,71 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: README | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# modelmap — Internal Cartography of Local Large Language Models | |
| 12 | + | |
| 13 | +**modelmap** investigates what can actually be known, measured, localized, and mapped | |
| 14 | +about the internal organization of pretrained open-weight LLMs — where knowledge lives, | |
| 15 | +how computation is distributed, which structures are stable across inputs, layers, | |
| 16 | +scales, and model families — **using only local, consumer-grade Apple Silicon hardware**, | |
| 17 | +and publishes the resulting maps as a rigorous, reproducible public atlas at | |
| 18 | +**[modelmap.io](https://modelmap.io)**. | |
| 19 | + | |
| 20 | +## Core research questions | |
| 21 | + | |
| 22 | +- **Q1 — Localization.** Where do capabilities, knowledge domains, languages, and behaviors reside? Localized or diffuse? | |
| 23 | +- **Q2 — Structure.** Which internal structures are stable across inputs — and at what granularity are they real rather than probing artifacts? | |
| 24 | +- **Q3 — Comparability.** Can internal maps be compared across sizes, checkpoints, quantization levels, and families? | |
| 25 | +- **Q4 — Cost.** Which mapping techniques are feasible on a 16–64 GB Mac, and what is the accuracy/cost frontier of local interpretability? | |
| 26 | +- **Q5 — Utility.** Do the maps predict anything useful — quantization sensitivity, pruning tolerance, working sets, failure modes? | |
| 27 | + | |
| 28 | +The distinction kept sharp throughout: | |
| 29 | + | |
| 30 | +```text | |
| 31 | +what a probing method reports | |
| 32 | +≠ what the model actually computes | |
| 33 | +≠ what is stable across methods, seeds, and datasets | |
| 34 | +≠ what is causally verified by intervention | |
| 35 | +``` | |
| 36 | + | |
| 37 | +## Confidence taxonomy | |
| 38 | + | |
| 39 | +Every atlas entry carries an explicit evidence level, visible on modelmap.io: | |
| 40 | + | |
| 41 | +```text | |
| 42 | +Level 0 — anecdotal (single run, no controls; never published alone) | |
| 43 | +Level 1 — correlational (controlled, replicated ≥3 seeds, ≥2 datasets) | |
| 44 | +Level 2 — method-robust (Level 1 + agreement across ≥2 independent techniques) | |
| 45 | +Level 3 — causal (Level 2 + intervention confirms the claim) | |
| 46 | +``` | |
| 47 | + | |
| 48 | +## Status | |
| 49 | + | |
| 50 | +Project bootstrapped 2026-08-12. Phase 1 (ultra-deep literature research) is next. | |
| 51 | +The full research charter is in [CLAUDE.md](CLAUDE.md); the auditable paper trail | |
| 52 | +lives in [research/LOG.md](research/LOG.md). | |
| 53 | + | |
| 54 | +Sister project: **localvm-research** (out-of-core LLM execution on consumer Macs) — | |
| 55 | +findings cross-pollinate, in particular working-set prediction (Q5). | |
| 56 | + | |
| 57 | +## Layout | |
| 58 | + | |
| 59 | +```text | |
| 60 | +research/ paper trail: log, state of the art, gaps, ranking, methodology | |
| 61 | +src/modelmap/ the mapping library (MLX / PyTorch-MPS / Metal; CUDA never core) | |
| 62 | +experiments/ micro-experiments A–H and prototype candidates (registered hypotheses) | |
| 63 | +atlas/ versioned map artifacts with provenance + confidence levels | |
| 64 | +site/ modelmap.io platform source (builds and previews locally on the Mac) | |
| 65 | +benchmarks/ harness, hardware manifest, versioned prompt corpora | |
| 66 | +tools/ check_headers, new_experiment, new_map, publish | |
| 67 | +``` | |
| 68 | + | |
| 69 | +--- | |
| 70 | + | |
| 71 | +*Author: Simon-Pierre Boucher — contact@spboucher.ai — https://modelmap.io — All rights reserved (research code).* | |
added
atlas/README.md
+21 −0
@@ -0,0 +1,21 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: Atlas index | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Atlas — versioned map artifacts | |
| 12 | + | |
| 13 | +Layout: `atlas/<model_id>/<map_type>/<version>/` containing `map.json` (or | |
| 14 | +`map.zarr`), `provenance.json` (commit, config, model hash, hardware, dates) | |
| 15 | +and `confidence.md` (evidence level: correlational | replicated | causal). | |
| 16 | + | |
| 17 | +Entries are scaffolded by `tools/new_map.py` and **cannot be published without | |
| 18 | +a completed provenance.json and confidence.md**. No claim appears on | |
| 19 | +modelmap.io above its evidence level. | |
| 20 | + | |
| 21 | +*(No maps yet — the atlas fills only after the methodology (Phase 9) exists.)* | |
added
benchmarks/hardware_manifest.py
+71 −0
@@ -0,0 +1,71 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# ============================================================================= | |
| 3 | +# Project : modelmap | |
| 4 | +# File : benchmarks/hardware_manifest.py | |
| 5 | +# Purpose : Record chip, cores, RAM, SSD, macOS + software versions per run | |
| 6 | +# Author : Simon-Pierre Boucher | |
| 7 | +# Contact : contact@spboucher.ai | |
| 8 | +# Website : https://modelmap.io | |
| 9 | +# Created : 2026-08-12 | |
| 10 | +# Modified : 2026-08-12 | |
| 11 | +# Platform : macOS / Apple Silicon (arm64) | |
| 12 | +# License : All rights reserved (research code) | |
| 13 | +# ============================================================================= | |
| 14 | +"""Hardware manifest for reproducibility (charter §0.2). | |
| 15 | + | |
| 16 | +Every result file and every published map embeds this manifest so that any | |
| 17 | +number can be traced to the exact machine and software stack that produced it. | |
| 18 | +""" | |
| 19 | + | |
| 20 | +from __future__ import annotations | |
| 21 | + | |
| 22 | +import json | |
| 23 | +import platform | |
| 24 | +import subprocess | |
| 25 | +import sys | |
| 26 | +from importlib import metadata | |
| 27 | + | |
| 28 | + | |
| 29 | +def _sysctl(key: str) -> str: | |
| 30 | + try: | |
| 31 | + return subprocess.run( | |
| 32 | + ["sysctl", "-n", key], capture_output=True, text=True, check=True | |
| 33 | + ).stdout.strip() | |
| 34 | + except subprocess.CalledProcessError: | |
| 35 | + return "" | |
| 36 | + | |
| 37 | + | |
| 38 | +def _pkg_version(name: str) -> str | None: | |
| 39 | + try: | |
| 40 | + return metadata.version(name) | |
| 41 | + except metadata.PackageNotFoundError: | |
| 42 | + return None | |
| 43 | + | |
| 44 | + | |
| 45 | +def manifest() -> dict: | |
| 46 | + mem_bytes = int(_sysctl("hw.memsize") or 0) | |
| 47 | + return { | |
| 48 | + "author": "Simon-Pierre Boucher", | |
| 49 | + "contact": "contact@spboucher.ai", | |
| 50 | + "website": "https://modelmap.io", | |
| 51 | + "chip": { | |
| 52 | + "brand": _sysctl("machdep.cpu.brand_string"), | |
| 53 | + "cores_total": int(_sysctl("hw.ncpu") or 0), | |
| 54 | + "cores_performance": int(_sysctl("hw.perflevel0.physicalcpu") or 0), | |
| 55 | + "cores_efficiency": int(_sysctl("hw.perflevel1.physicalcpu") or 0), | |
| 56 | + }, | |
| 57 | + "memory": {"unified_gb": round(mem_bytes / 2**30, 1), "pagesize": int(_sysctl("hw.pagesize") or 0)}, | |
| 58 | + "os": {"system": platform.system(), "version": platform.mac_ver()[0], "arch": platform.machine()}, | |
| 59 | + "software": { | |
| 60 | + "python": platform.python_version(), | |
| 61 | + "numpy": _pkg_version("numpy"), | |
| 62 | + "mlx": _pkg_version("mlx"), | |
| 63 | + "torch": _pkg_version("torch"), | |
| 64 | + "safetensors": _pkg_version("safetensors"), | |
| 65 | + }, | |
| 66 | + } | |
| 67 | + | |
| 68 | + | |
| 69 | +if __name__ == "__main__": | |
| 70 | + json.dump(manifest(), sys.stdout, indent=2) | |
| 71 | + print() | |
added
benchmarks/harness.py
+61 −0
@@ -0,0 +1,61 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# ============================================================================= | |
| 3 | +# Project : modelmap | |
| 4 | +# File : benchmarks/harness.py | |
| 5 | +# Purpose : Benchmark harness skeleton — runs experiments, embeds manifests | |
| 6 | +# Author : Simon-Pierre Boucher | |
| 7 | +# Contact : contact@spboucher.ai | |
| 8 | +# Website : https://modelmap.io | |
| 9 | +# Created : 2026-08-12 | |
| 10 | +# Modified : 2026-08-12 | |
| 11 | +# Platform : macOS / Apple Silicon (arm64) | |
| 12 | +# License : All rights reserved (research code) | |
| 13 | +# ============================================================================= | |
| 14 | +"""Experiment harness (skeleton — finalized during Phase 5). | |
| 15 | + | |
| 16 | +Contract established by the charter: every result JSON written under | |
| 17 | +results/<experiment_id>/<UTC timestamp>/ embeds the hardware manifest, the | |
| 18 | +git commit, the config, and the seed(s) used, so any map is reproducible from | |
| 19 | +commit + config + model hash + seed + manifest. | |
| 20 | +""" | |
| 21 | + | |
| 22 | +from __future__ import annotations | |
| 23 | + | |
| 24 | +import datetime | |
| 25 | +import json | |
| 26 | +import subprocess | |
| 27 | +from pathlib import Path | |
| 28 | + | |
| 29 | +from hardware_manifest import manifest | |
| 30 | + | |
| 31 | +ROOT = Path(__file__).resolve().parent.parent | |
| 32 | + | |
| 33 | + | |
| 34 | +def git_commit() -> str: | |
| 35 | + try: | |
| 36 | + return subprocess.run( | |
| 37 | + ["git", "rev-parse", "HEAD"], cwd=ROOT, capture_output=True, text=True, check=True | |
| 38 | + ).stdout.strip() | |
| 39 | + except subprocess.CalledProcessError: | |
| 40 | + return "unknown" | |
| 41 | + | |
| 42 | + | |
| 43 | +def result_dir(experiment_id: str) -> Path: | |
| 44 | + ts = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ") | |
| 45 | + out = ROOT / "results" / experiment_id / ts | |
| 46 | + out.mkdir(parents=True) | |
| 47 | + return out | |
| 48 | + | |
| 49 | + | |
| 50 | +def write_result(experiment_id: str, payload: dict, config: dict | None = None) -> Path: | |
| 51 | + out = result_dir(experiment_id) | |
| 52 | + doc = { | |
| 53 | + "experiment": experiment_id, | |
| 54 | + "commit": git_commit(), | |
| 55 | + "config": config or {}, | |
| 56 | + "manifest": manifest(), | |
| 57 | + **payload, | |
| 58 | + } | |
| 59 | + path = out / "results.json" | |
| 60 | + path.write_text(json.dumps(doc, indent=2) + "\n") | |
| 61 | + return path | |
added
benchmarks/promptsets/README.md
+17 −0
@@ -0,0 +1,17 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: Prompt sets index | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Mapping prompt corpora | |
| 12 | + | |
| 13 | +Versioned artifacts with checksums (charter §0.3). Planned corpora: code, | |
| 14 | +math, French, English, factual recall, reasoning, chat. Each corpus ships as | |
| 15 | +a checksummed JSONL file plus a manifest recording its version and origin. | |
| 16 | + | |
| 17 | +*(No corpora committed yet — designed during Phase 5.)* | |
added
docs/README.md
+14 −0
@@ -0,0 +1,14 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: Docs index | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Documentation | |
| 12 | + | |
| 13 | +Reserved for user-facing documentation of the modelmap library and pipeline | |
| 14 | +(written once the methodology stabilizes, Phase 9+). | |
added
experiments/candidate_01/README.md
+14 −0
@@ -0,0 +1,14 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: candidate_01 | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# candidate_01 | |
| 12 | + | |
| 13 | +Reserved for a Phase 7 prototype candidate — selected from `research/candidate_ranking.md` (Phase 4). | |
| 14 | +Not yet assigned: candidates are chosen only after the Phase 1-3 literature work. | |
added
experiments/candidate_02/README.md
+14 −0
@@ -0,0 +1,14 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: candidate_02 | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# candidate_02 | |
| 12 | + | |
| 13 | +Reserved for a Phase 7 prototype candidate — selected from `research/candidate_ranking.md` (Phase 4). | |
| 14 | +Not yet assigned: candidates are chosen only after the Phase 1-3 literature work. | |
added
experiments/candidate_03/README.md
+14 −0
@@ -0,0 +1,14 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: candidate_03 | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# candidate_03 | |
| 12 | + | |
| 13 | +Reserved for a Phase 7 prototype candidate — selected from `research/candidate_ranking.md` (Phase 4). | |
| 14 | +Not yet assigned: candidates are chosen only after the Phase 1-3 literature work. | |
added
experiments/micro/expA_probe_reliability/README.md
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expA_probe_reliability | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# expA_probe_reliability | |
| 12 | + | |
| 13 | +Probe reliability — noise floor of linear probes across seeds and datasets | |
| 14 | + | |
| 15 | +Status: scaffolded — hypothesis to be registered before any benchmark runs (charter §10). | |
added
experiments/micro/expA_probe_reliability/hypothesis.md
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expA_probe_reliability — hypothesis | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Hypothesis — expA_probe_reliability | |
| 12 | + | |
| 13 | +> Probe reliability — noise floor of linear probes across seeds and datasets | |
| 14 | + | |
| 15 | +```text | |
| 16 | +Hypothesis : (to be registered before the first run) | |
| 17 | +Falsification criterion : (explicit kill-number, registered in advance) | |
| 18 | +Method : (including controls) | |
| 19 | +Baseline / null : (shuffled labels / random directions / random init) | |
| 20 | +Result : (pending) | |
| 21 | +Interpretation : (pending — with explicit confidence level) | |
| 22 | +Next experiment : (pending) | |
| 23 | +``` | |
added
experiments/micro/expB_localization_vs_diffusion/README.md
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expB_localization_vs_diffusion | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# expB_localization_vs_diffusion | |
| 12 | + | |
| 13 | +Localization vs diffusion — how concentrated is the signal supporting a capability | |
| 14 | + | |
| 15 | +Status: scaffolded — hypothesis to be registered before any benchmark runs (charter §10). | |
added
experiments/micro/expB_localization_vs_diffusion/hypothesis.md
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expB_localization_vs_diffusion — hypothesis | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Hypothesis — expB_localization_vs_diffusion | |
| 12 | + | |
| 13 | +> Localization vs diffusion — how concentrated is the signal supporting a capability | |
| 14 | + | |
| 15 | +```text | |
| 16 | +Hypothesis : (to be registered before the first run) | |
| 17 | +Falsification criterion : (explicit kill-number, registered in advance) | |
| 18 | +Method : (including controls) | |
| 19 | +Baseline / null : (shuffled labels / random directions / random init) | |
| 20 | +Result : (pending) | |
| 21 | +Interpretation : (pending — with explicit confidence level) | |
| 22 | +Next experiment : (pending) | |
| 23 | +``` | |
added
experiments/micro/expC_causal_verification/README.md
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expC_causal_verification | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# expC_causal_verification | |
| 12 | + | |
| 13 | +Causal verification pipeline — correlational-to-causal survival rate | |
| 14 | + | |
| 15 | +Status: scaffolded — hypothesis to be registered before any benchmark runs (charter §10). | |
added
experiments/micro/expC_causal_verification/hypothesis.md
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expC_causal_verification — hypothesis | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Hypothesis — expC_causal_verification | |
| 12 | + | |
| 13 | +> Causal verification pipeline — correlational-to-causal survival rate | |
| 14 | + | |
| 15 | +```text | |
| 16 | +Hypothesis : (to be registered before the first run) | |
| 17 | +Falsification criterion : (explicit kill-number, registered in advance) | |
| 18 | +Method : (including controls) | |
| 19 | +Baseline / null : (shuffled labels / random directions / random init) | |
| 20 | +Result : (pending) | |
| 21 | +Interpretation : (pending — with explicit confidence level) | |
| 22 | +Next experiment : (pending) | |
| 23 | +``` | |
added
experiments/micro/expD_cross_input_stability/README.md
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expD_cross_input_stability | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# expD_cross_input_stability | |
| 12 | + | |
| 13 | +Cross-input stability — which map types are input-invariant | |
| 14 | + | |
| 15 | +Status: scaffolded — hypothesis to be registered before any benchmark runs (charter §10). | |
added
experiments/micro/expD_cross_input_stability/hypothesis.md
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expD_cross_input_stability — hypothesis | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Hypothesis — expD_cross_input_stability | |
| 12 | + | |
| 13 | +> Cross-input stability — which map types are input-invariant | |
| 14 | + | |
| 15 | +```text | |
| 16 | +Hypothesis : (to be registered before the first run) | |
| 17 | +Falsification criterion : (explicit kill-number, registered in advance) | |
| 18 | +Method : (including controls) | |
| 19 | +Baseline / null : (shuffled labels / random directions / random init) | |
| 20 | +Result : (pending) | |
| 21 | +Interpretation : (pending — with explicit confidence level) | |
| 22 | +Next experiment : (pending) | |
| 23 | +``` | |
added
experiments/micro/expE_weight_geometry/README.md
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expE_weight_geometry | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# expE_weight_geometry | |
| 12 | + | |
| 13 | +Weight-space geometry — zero-forward-pass maps (SVD, effective rank, CKA) | |
| 14 | + | |
| 15 | +Status: scaffolded — hypothesis to be registered before any benchmark runs (charter §10). | |
added
experiments/micro/expE_weight_geometry/hypothesis.md
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expE_weight_geometry — hypothesis | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Hypothesis — expE_weight_geometry | |
| 12 | + | |
| 13 | +> Weight-space geometry — zero-forward-pass maps (SVD, effective rank, CKA) | |
| 14 | + | |
| 15 | +```text | |
| 16 | +Hypothesis : (to be registered before the first run) | |
| 17 | +Falsification criterion : (explicit kill-number, registered in advance) | |
| 18 | +Method : (including controls) | |
| 19 | +Baseline / null : (shuffled labels / random directions / random init) | |
| 20 | +Result : (pending) | |
| 21 | +Interpretation : (pending — with explicit confidence level) | |
| 22 | +Next experiment : (pending) | |
| 23 | +``` | |
added
experiments/micro/expF_quantization_map_drift/README.md
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expF_quantization_map_drift | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# expF_quantization_map_drift | |
| 12 | + | |
| 13 | +Quantization map drift — how FP16/Q8/Q4/Q2 deform internal structure | |
| 14 | + | |
| 15 | +Status: scaffolded — hypothesis to be registered before any benchmark runs (charter §10). | |
added
experiments/micro/expF_quantization_map_drift/hypothesis.md
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expF_quantization_map_drift — hypothesis | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Hypothesis — expF_quantization_map_drift | |
| 12 | + | |
| 13 | +> Quantization map drift — how FP16/Q8/Q4/Q2 deform internal structure | |
| 14 | + | |
| 15 | +```text | |
| 16 | +Hypothesis : (to be registered before the first run) | |
| 17 | +Falsification criterion : (explicit kill-number, registered in advance) | |
| 18 | +Method : (including controls) | |
| 19 | +Baseline / null : (shuffled labels / random directions / random init) | |
| 20 | +Result : (pending) | |
| 21 | +Interpretation : (pending — with explicit confidence level) | |
| 22 | +Next experiment : (pending) | |
| 23 | +``` | |
added
experiments/micro/expG_cross_model_alignment/README.md
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expG_cross_model_alignment | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# expG_cross_model_alignment | |
| 12 | + | |
| 13 | +Cross-model alignment — shared coordinate systems across model sizes | |
| 14 | + | |
| 15 | +Status: scaffolded — hypothesis to be registered before any benchmark runs (charter §10). | |
added
experiments/micro/expG_cross_model_alignment/hypothesis.md
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expG_cross_model_alignment — hypothesis | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Hypothesis — expG_cross_model_alignment | |
| 12 | + | |
| 13 | +> Cross-model alignment — shared coordinate systems across model sizes | |
| 14 | + | |
| 15 | +```text | |
| 16 | +Hypothesis : (to be registered before the first run) | |
| 17 | +Falsification criterion : (explicit kill-number, registered in advance) | |
| 18 | +Method : (including controls) | |
| 19 | +Baseline / null : (shuffled labels / random directions / random init) | |
| 20 | +Result : (pending) | |
| 21 | +Interpretation : (pending — with explicit confidence level) | |
| 22 | +Next experiment : (pending) | |
| 23 | +``` | |
added
experiments/micro/expH_capture_cost_frontier/README.md
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expH_capture_cost_frontier | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# expH_capture_cost_frontier | |
| 12 | + | |
| 13 | +Capture cost frontier — what can you map on which Mac (macOS-specific) | |
| 14 | + | |
| 15 | +Status: scaffolded — hypothesis to be registered before any benchmark runs (charter §10). | |
added
experiments/micro/expH_capture_cost_frontier/hypothesis.md
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: expH_capture_cost_frontier — hypothesis | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Hypothesis — expH_capture_cost_frontier | |
| 12 | + | |
| 13 | +> Capture cost frontier — what can you map on which Mac (macOS-specific) | |
| 14 | + | |
| 15 | +```text | |
| 16 | +Hypothesis : (to be registered before the first run) | |
| 17 | +Falsification criterion : (explicit kill-number, registered in advance) | |
| 18 | +Method : (including controls) | |
| 19 | +Baseline / null : (shuffled labels / random directions / random init) | |
| 20 | +Result : (pending) | |
| 21 | +Interpretation : (pending — with explicit confidence level) | |
| 22 | +Next experiment : (pending) | |
| 23 | +``` | |
added
pyproject.toml
+39 −0
@@ -0,0 +1,39 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : pyproject.toml | |
| 4 | +# Purpose : Python project configuration for the modelmap library | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
| 13 | +[project] | |
| 14 | +name = "modelmap" | |
| 15 | +version = "0.1.0" | |
| 16 | +description = "Internal cartography of local large language models on Apple Silicon" | |
| 17 | +authors = [{ name = "Simon-Pierre Boucher", email = "contact@spboucher.ai" }] | |
| 18 | +requires-python = ">=3.11" | |
| 19 | +dependencies = [ | |
| 20 | + "numpy", | |
| 21 | + "safetensors", | |
| 22 | +] | |
| 23 | + | |
| 24 | +[project.optional-dependencies] | |
| 25 | +dev = ["ruff", "pytest"] | |
| 26 | + | |
| 27 | +[build-system] | |
| 28 | +requires = ["setuptools>=68"] | |
| 29 | +build-backend = "setuptools.build_meta" | |
| 30 | + | |
| 31 | +[tool.setuptools.packages.find] | |
| 32 | +where = ["src"] | |
| 33 | + | |
| 34 | +[tool.ruff] | |
| 35 | +line-length = 100 | |
| 36 | +target-version = "py311" | |
| 37 | + | |
| 38 | +[tool.pytest.ini_options] | |
| 39 | +testpaths = ["tests", "src"] | |
added
research/LOG.md
+35 −0
@@ -0,0 +1,35 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: Research log (append-only) | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# modelmap — Research log | |
| 12 | + | |
| 13 | +Append-only. Every entry records: date/time with timezone, question, experiment, | |
| 14 | +result, interpretation, decision. The entire reasoning process must be auditable. | |
| 15 | + | |
| 16 | +--- | |
| 17 | + | |
| 18 | +## 2026-08-12 00:30 EDT — Project bootstrap | |
| 19 | + | |
| 20 | +**Question.** None yet — administrative bootstrap. | |
| 21 | + | |
| 22 | +**Action.** Repository initialized from the research charter (`CLAUDE.md`): | |
| 23 | +full skeleton per charter §3, mandatory author headers on every source file, | |
| 24 | +`tools/check_headers.py` written first (charter §0.1 rule 3), experiment | |
| 25 | +directories A–H scaffolded with empty seven-field scientific blocks | |
| 26 | +(hypotheses will be registered before any run), and the public platform | |
| 27 | +(`site/`) built and deployed to https://www.modelmap.io on MacLustr node | |
| 28 | +M3U96b (PM2 + ngrok), mirroring the architecture of the sister project | |
| 29 | +localvm-research (www.localvm.dev) so both research platforms stay coherent. | |
| 30 | + | |
| 31 | +**Decision.** Next step is Phase 1: ultra-deep literature research across the | |
| 32 | +ten areas of charter §4 (observational probing, causal methods, feature | |
| 33 | +decomposition, circuits, weight-space cartography, knowledge editing, | |
| 34 | +cross-model comparison, statistics/epistemology, systems, atlas design). | |
| 35 | +No mapping code is written before the state-of-the-art map (Phase 2) exists. | |
added
research/bibliography.md
+20 −0
@@ -0,0 +1,20 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: Bibliography | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Bibliography | |
| 12 | + | |
| 13 | +Every consulted source, with URL and access date. Populated during Phase 1 | |
| 14 | +(charter §4). Entries use the form: | |
| 15 | + | |
| 16 | +```text | |
| 17 | +- <Authors> — <Title> (<year>). <URL> — accessed <YYYY-MM-DD>. [themes] | |
| 18 | +``` | |
| 19 | + | |
| 20 | +*(No sources logged yet — Phase 1 has not started.)* | |
added
research/notes/README.md
+16 −0
@@ -0,0 +1,16 @@ | ||
| 1 | +--- | |
| 2 | +project: modelmap | |
| 3 | +document: Reading notes index | |
| 4 | +author: Simon-Pierre Boucher | |
| 5 | +contact: contact@spboucher.ai | |
| 6 | +website: https://modelmap.io | |
| 7 | +created: 2026-08-12 | |
| 8 | +status: draft | |
| 9 | +--- | |
| 10 | + | |
| 11 | +# Phase 1 reading notes | |
| 12 | + | |
| 13 | +One markdown file per theme (charter §4.1–§4.10). Each note records: the | |
| 14 | +primary sources read, what each technique claims to reveal, whether an | |
| 15 | +open-source implementation exists and runs on Apple Silicon, known failure | |
| 16 | +modes, and candidate extensions. | |
added
site/lib/content.js
+225 −0
@@ -0,0 +1,225 @@ | ||
| 1 | +// ============================================================================ | |
| 2 | +// Project : modelmap | |
| 3 | +// File : site/lib/content.js | |
| 4 | +// Purpose : Content index over the synced research-repo snapshot (content/) | |
| 5 | +// Author : Simon-Pierre Boucher | |
| 6 | +// Contact : contact@spboucher.ai | |
| 7 | +// Website : https://modelmap.io | |
| 8 | +// Created : 2026-08-12 | |
| 9 | +// Modified : 2026-08-12 | |
| 10 | +// Platform : macOS / Apple Silicon (arm64) — Node.js (deployed on MacLustr) | |
| 11 | +// License : All rights reserved (research code) | |
| 12 | +// ============================================================================ | |
| 13 | +"use strict"; | |
| 14 | + | |
| 15 | +const fs = require("fs"); | |
| 16 | +const path = require("path"); | |
| 17 | +const matter = require("gray-matter"); | |
| 18 | + | |
| 19 | +const CONTENT_ROOT = path.join(__dirname, "..", "content"); | |
| 20 | + | |
| 21 | +const TEXT_EXT = new Set([ | |
| 22 | + ".md", ".py", ".sh", ".toml", ".yaml", ".yml", ".cff", ".json", | |
| 23 | + ".js", ".css", ".metal", ".cpp", ".h", ".hpp", ".swift", ".m", ".txt", ".log", | |
| 24 | +]); | |
| 25 | + | |
| 26 | +/** Resolve a repo-relative path safely inside content/, or return null. */ | |
| 27 | +function safeResolve(rel) { | |
| 28 | + const abs = path.resolve(CONTENT_ROOT, rel); | |
| 29 | + if (!abs.startsWith(CONTENT_ROOT + path.sep) && abs !== CONTENT_ROOT) return null; | |
| 30 | + return abs; | |
| 31 | +} | |
| 32 | + | |
| 33 | +function exists(rel) { | |
| 34 | + const abs = safeResolve(rel); | |
| 35 | + return abs !== null && fs.existsSync(abs); | |
| 36 | +} | |
| 37 | + | |
| 38 | +function readText(rel) { | |
| 39 | + const abs = safeResolve(rel); | |
| 40 | + if (!abs || !fs.existsSync(abs) || !fs.statSync(abs).isFile()) return null; | |
| 41 | + return fs.readFileSync(abs, "utf8"); | |
| 42 | +} | |
| 43 | + | |
| 44 | +/** Read a markdown file, returning { data (front matter), content }. */ | |
| 45 | +function readMarkdown(rel) { | |
| 46 | + const raw = readText(rel); | |
| 47 | + if (raw === null) return null; | |
| 48 | + try { | |
| 49 | + return matter(raw); | |
| 50 | + } catch { | |
| 51 | + return { data: {}, content: raw }; | |
| 52 | + } | |
| 53 | +} | |
| 54 | + | |
| 55 | +/** List a directory inside content/: [{name, rel, dir, size}] */ | |
| 56 | +function listDir(rel) { | |
| 57 | + const abs = safeResolve(rel || "."); | |
| 58 | + if (!abs || !fs.existsSync(abs) || !fs.statSync(abs).isDirectory()) return null; | |
| 59 | + return fs | |
| 60 | + .readdirSync(abs) | |
| 61 | + .filter((n) => !n.startsWith(".") && n !== "node_modules") | |
| 62 | + .map((n) => { | |
| 63 | + const st = fs.statSync(path.join(abs, n)); | |
| 64 | + return { | |
| 65 | + name: n, | |
| 66 | + rel: path.posix.join(rel || "", n), | |
| 67 | + dir: st.isDirectory(), | |
| 68 | + size: st.size, | |
| 69 | + }; | |
| 70 | + }) | |
| 71 | + .sort((a, b) => (a.dir !== b.dir ? (a.dir ? -1 : 1) : a.name.localeCompare(b.name))); | |
| 72 | +} | |
| 73 | + | |
| 74 | +/** Recursively collect files under rel matching a predicate. */ | |
| 75 | +function walk(rel, predicate, out = []) { | |
| 76 | + const entries = listDir(rel); | |
| 77 | + if (!entries) return out; | |
| 78 | + for (const e of entries) { | |
| 79 | + if (e.dir) walk(e.rel, predicate, out); | |
| 80 | + else if (predicate(e)) out.push(e); | |
| 81 | + } | |
| 82 | + return out; | |
| 83 | +} | |
| 84 | + | |
| 85 | +function isTextFile(rel) { | |
| 86 | + return TEXT_EXT.has(path.extname(rel).toLowerCase()) || path.basename(rel) === "Makefile"; | |
| 87 | +} | |
| 88 | + | |
| 89 | +/** Parse research/LOG.md into entries [{title, body}] (## headings). */ | |
| 90 | +function parseLogEntries() { | |
| 91 | + const md = readMarkdown("research/LOG.md"); | |
| 92 | + if (!md) return []; | |
| 93 | + const parts = md.content.split(/^## /m).slice(1); | |
| 94 | + return parts.map((p) => { | |
| 95 | + const nl = p.indexOf("\n"); | |
| 96 | + return { title: p.slice(0, nl).trim(), body: p.slice(nl + 1).replace(/^---\s*$/m, "").trim() }; | |
| 97 | + }); | |
| 98 | +} | |
| 99 | + | |
| 100 | +/** Experiment cards: micro experiments + candidates. */ | |
| 101 | +function listExperiments() { | |
| 102 | + const out = []; | |
| 103 | + for (const base of ["experiments/micro", "experiments"]) { | |
| 104 | + const entries = listDir(base) || []; | |
| 105 | + for (const e of entries) { | |
| 106 | + if (!e.dir || e.name === "micro") continue; | |
| 107 | + const readme = readMarkdown(path.posix.join(e.rel, "README.md")); | |
| 108 | + const hasAnalysis = (readText(path.posix.join(e.rel, "analysis.md")) || "").length > 400; | |
| 109 | + const runs = listResultRuns().filter((r) => r.experiment === e.name); | |
| 110 | + let purpose = ""; | |
| 111 | + if (readme) { | |
| 112 | + const line = readme.content.split("\n").find((l) => l.trim() && !l.startsWith("#")); | |
| 113 | + purpose = line ? line.trim() : ""; | |
| 114 | + } | |
| 115 | + out.push({ | |
| 116 | + id: e.name, | |
| 117 | + rel: e.rel, | |
| 118 | + purpose, | |
| 119 | + status: runs.length > 0 && hasAnalysis ? "completed" : runs.length > 0 ? "has results" : "scaffolded", | |
| 120 | + runs: runs.length, | |
| 121 | + }); | |
| 122 | + } | |
| 123 | + } | |
| 124 | + return out; | |
| 125 | +} | |
| 126 | + | |
| 127 | +/** Result runs: results/<experiment>/<timestamp>/*.json */ | |
| 128 | +function listResultRuns() { | |
| 129 | + const out = []; | |
| 130 | + for (const exp of listDir("results") || []) { | |
| 131 | + if (!exp.dir) continue; | |
| 132 | + for (const run of listDir(exp.rel) || []) { | |
| 133 | + if (!run.dir) continue; | |
| 134 | + const files = (listDir(run.rel) || []).filter((f) => !f.dir); | |
| 135 | + out.push({ experiment: exp.name, timestamp: run.name, rel: run.rel, files }); | |
| 136 | + } | |
| 137 | + } | |
| 138 | + return out.sort((a, b) => b.timestamp.localeCompare(a.timestamp)); | |
| 139 | +} | |
| 140 | + | |
| 141 | +/** | |
| 142 | + * Atlas entries: atlas/<model_id>/<map_type>/<version>/ with provenance.json | |
| 143 | + * and confidence.md. The confidence level is parsed from confidence.md; an | |
| 144 | + * entry with no committed level is reported as "unrated" and the platform | |
| 145 | + * never presents it above Level 0. | |
| 146 | + */ | |
| 147 | +function listAtlasEntries() { | |
| 148 | + const out = []; | |
| 149 | + for (const model of listDir("atlas") || []) { | |
| 150 | + if (!model.dir) continue; | |
| 151 | + for (const mapType of listDir(model.rel) || []) { | |
| 152 | + if (!mapType.dir) continue; | |
| 153 | + for (const version of listDir(mapType.rel) || []) { | |
| 154 | + if (!version.dir) continue; | |
| 155 | + const provenance = readJson(path.posix.join(version.rel, "provenance.json")); | |
| 156 | + const confidence = readText(path.posix.join(version.rel, "confidence.md")) || ""; | |
| 157 | + const m = confidence.match(/^Level\s*:\s*([0-3])\b/m); | |
| 158 | + out.push({ | |
| 159 | + model: model.name, | |
| 160 | + mapType: mapType.name, | |
| 161 | + version: version.name, | |
| 162 | + rel: version.rel, | |
| 163 | + provenance, | |
| 164 | + level: m ? Number(m[1]) : null, | |
| 165 | + files: (listDir(version.rel) || []).filter((f) => !f.dir), | |
| 166 | + }); | |
| 167 | + } | |
| 168 | + } | |
| 169 | + } | |
| 170 | + return out; | |
| 171 | +} | |
| 172 | + | |
| 173 | +function readJson(rel) { | |
| 174 | + const raw = readText(rel); | |
| 175 | + if (raw === null) return null; | |
| 176 | + try { | |
| 177 | + return JSON.parse(raw); | |
| 178 | + } catch { | |
| 179 | + return null; | |
| 180 | + } | |
| 181 | +} | |
| 182 | + | |
| 183 | +/** Build info written by sync-content.sh (commit, counts, date). */ | |
| 184 | +function buildInfo() { | |
| 185 | + return readJson("build-info.json") || {}; | |
| 186 | +} | |
| 187 | + | |
| 188 | +/** Site-wide stats for the home page. */ | |
| 189 | +function stats() { | |
| 190 | + const bib = readText("research/bibliography.md") || ""; | |
| 191 | + const sources = bib.split("\n").filter((l) => l.trim().startsWith("- ")).length; | |
| 192 | + const gapsDoc = readText("research/research_gaps.md") || ""; | |
| 193 | + const gaps = (gapsDoc.match(/^#{2,3}\s+G\d{2}/gm) || []).length; | |
| 194 | + const experiments = listExperiments(); | |
| 195 | + const notes = (listDir("research/notes") || []).filter((f) => f.name.endsWith(".md") && f.name !== "README.md").length; | |
| 196 | + return { | |
| 197 | + sources, | |
| 198 | + gaps, | |
| 199 | + notes, | |
| 200 | + experiments: experiments.length, | |
| 201 | + experimentsDone: experiments.filter((e) => e.status === "completed").length, | |
| 202 | + resultRuns: listResultRuns().length, | |
| 203 | + logEntries: parseLogEntries().length, | |
| 204 | + atlasEntries: listAtlasEntries().length, | |
| 205 | + build: buildInfo(), | |
| 206 | + }; | |
| 207 | +} | |
| 208 | + | |
| 209 | +module.exports = { | |
| 210 | + CONTENT_ROOT, | |
| 211 | + safeResolve, | |
| 212 | + exists, | |
| 213 | + readText, | |
| 214 | + readMarkdown, | |
| 215 | + readJson, | |
| 216 | + listDir, | |
| 217 | + walk, | |
| 218 | + isTextFile, | |
| 219 | + parseLogEntries, | |
| 220 | + listExperiments, | |
| 221 | + listResultRuns, | |
| 222 | + listAtlasEntries, | |
| 223 | + buildInfo, | |
| 224 | + stats, | |
| 225 | +}; | |
added
site/lib/render.js
+134 −0
@@ -0,0 +1,134 @@ | ||
| 1 | +// ============================================================================ | |
| 2 | +// Project : modelmap | |
| 3 | +// File : site/lib/render.js | |
| 4 | +// Purpose : HTML layout, markdown/code rendering, confidence-level widgets | |
| 5 | +// Author : Simon-Pierre Boucher | |
| 6 | +// Contact : contact@spboucher.ai | |
| 7 | +// Website : https://modelmap.io | |
| 8 | +// Created : 2026-08-12 | |
| 9 | +// Modified : 2026-08-12 | |
| 10 | +// Platform : macOS / Apple Silicon (arm64) — Node.js (deployed on MacLustr) | |
| 11 | +// License : All rights reserved (research code) | |
| 12 | +// ============================================================================ | |
| 13 | +"use strict"; | |
| 14 | + | |
| 15 | +const { marked } = require("marked"); | |
| 16 | +const hljs = require("highlight.js"); | |
| 17 | + | |
| 18 | +function esc(s) { | |
| 19 | + return String(s).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); | |
| 20 | +} | |
| 21 | + | |
| 22 | +// Rewrite relative markdown links to platform routes; highlight code blocks. | |
| 23 | +const renderer = new marked.Renderer(); | |
| 24 | +renderer.code = function ({ text, lang }) { | |
| 25 | + let html; | |
| 26 | + if (lang && hljs.getLanguage(lang)) { | |
| 27 | + html = hljs.highlight(text, { language: lang }).value; | |
| 28 | + } else { | |
| 29 | + html = esc(text); | |
| 30 | + } | |
| 31 | + return `<pre class="codeblock"><code class="hljs">${html}</code></pre>`; | |
| 32 | +}; | |
| 33 | + | |
| 34 | +function markdownToHtml(md, baseRel) { | |
| 35 | + marked.use({ | |
| 36 | + renderer, | |
| 37 | + walkTokens(token) { | |
| 38 | + if (token.type === "link" && token.href && !/^(https?:|mailto:|#|\/)/.test(token.href)) { | |
| 39 | + const base = baseRel.includes("/") ? baseRel.slice(0, baseRel.lastIndexOf("/")) : ""; | |
| 40 | + const joined = (base ? base + "/" : "") + token.href; | |
| 41 | + const norm = joined.split("/").reduce((acc, part) => { | |
| 42 | + if (part === "..") acc.pop(); | |
| 43 | + else if (part !== "." && part !== "") acc.push(part); | |
| 44 | + return acc; | |
| 45 | + }, []).join("/"); | |
| 46 | + token.href = norm.endsWith(".md") ? "/doc/" + norm : "/file/" + norm; | |
| 47 | + } | |
| 48 | + }, | |
| 49 | + }); | |
| 50 | + return marked.parse(md); | |
| 51 | +} | |
| 52 | + | |
| 53 | +function highlightFile(text, filename) { | |
| 54 | + const ext = filename.slice(filename.lastIndexOf(".") + 1).toLowerCase(); | |
| 55 | + const langMap = { | |
| 56 | + py: "python", sh: "bash", js: "javascript", css: "css", json: "json", | |
| 57 | + toml: "ini", yaml: "yaml", yml: "yaml", cff: "yaml", md: "markdown", | |
| 58 | + metal: "cpp", cpp: "cpp", h: "cpp", hpp: "cpp", swift: "swift", m: "objectivec", | |
| 59 | + }; | |
| 60 | + const lang = filename === "Makefile" ? "makefile" : langMap[ext]; | |
| 61 | + if (lang && hljs.getLanguage(lang)) return hljs.highlight(text, { language: lang }).value; | |
| 62 | + return esc(text); | |
| 63 | +} | |
| 64 | + | |
| 65 | +const NAV = [ | |
| 66 | + ["/", "Home"], | |
| 67 | + ["/research", "Research"], | |
| 68 | + ["/experiments", "Experiments"], | |
| 69 | + ["/atlas", "Atlas"], | |
| 70 | + ["/results", "Results"], | |
| 71 | + ["/code", "Code"], | |
| 72 | + ["/about", "About"], | |
| 73 | +]; | |
| 74 | + | |
| 75 | +const LEVELS = [ | |
| 76 | + ["0", "anecdotal", "single run, no controls — never published alone"], | |
| 77 | + ["1", "correlational", "controlled, replicated ≥3 seeds, ≥2 datasets"], | |
| 78 | + ["2", "method-robust", "Level 1 + agreement across ≥2 independent techniques"], | |
| 79 | + ["3", "causal", "Level 2 + intervention confirms the claim"], | |
| 80 | +]; | |
| 81 | + | |
| 82 | +/** Confidence badge for an atlas entry (null level → "unrated"). */ | |
| 83 | +function levelBadge(level) { | |
| 84 | + if (level === null || level === undefined) { | |
| 85 | + return `<span class="badge badge-pending">unrated</span>`; | |
| 86 | + } | |
| 87 | + return `<span class="badge badge-level-${level}">Level ${level} — ${LEVELS[level][1]}</span>`; | |
| 88 | +} | |
| 89 | + | |
| 90 | +/** The confidence taxonomy ladder (used on Home / Atlas / About). */ | |
| 91 | +function confidenceLadder() { | |
| 92 | + return `<ol class="ladder">` + LEVELS.map( | |
| 93 | + ([n, name, desc]) => `<li class="ladder-step"><span class="ladder-num lv${n}">L${n}</span> | |
| 94 | + <div><strong>${name}</strong><span class="ladder-desc">${desc}</span></div></li>` | |
| 95 | + ).join("") + `</ol>`; | |
| 96 | +} | |
| 97 | + | |
| 98 | +function layout({ title, active, body, buildInfo = {} }) { | |
| 99 | + const nav = NAV.map( | |
| 100 | + ([href, label]) => | |
| 101 | + `<a href="${href}" class="${active === label ? "active" : ""}">${label}</a>` | |
| 102 | + ).join(""); | |
| 103 | + const commit = buildInfo.commit ? buildInfo.commit.slice(0, 7) : "dev"; | |
| 104 | + const synced = buildInfo.generated ? buildInfo.generated.slice(0, 10) : ""; | |
| 105 | + return `<!DOCTYPE html> | |
| 106 | +<html lang="en"> | |
| 107 | +<head> | |
| 108 | +<meta charset="utf-8"> | |
| 109 | +<meta name="viewport" content="width=device-width, initial-scale=1"> | |
| 110 | +<title>${esc(title)} · modelmap</title> | |
| 111 | +<meta name="description" content="modelmap — internal cartography of local large language models on consumer Apple Silicon. A reproducible, confidence-labeled research atlas by Simon-Pierre Boucher."> | |
| 112 | +<link rel="stylesheet" href="/static/style.css"> | |
| 113 | +<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='0.9em' font-size='90'>🗺️</text></svg>"> | |
| 114 | +</head> | |
| 115 | +<body> | |
| 116 | +<header class="site-header"> | |
| 117 | + <div class="wrap header-row"> | |
| 118 | + <a class="brand" href="/">modelmap<span class="brand-dim">.io</span></a> | |
| 119 | + <nav class="site-nav">${nav}</nav> | |
| 120 | + </div> | |
| 121 | +</header> | |
| 122 | +<main class="wrap">${body}</main> | |
| 123 | +<footer class="site-footer"> | |
| 124 | + <div class="wrap"> | |
| 125 | + <span>© 2026 Simon-Pierre Boucher — <a href="mailto:contact@spboucher.ai">contact@spboucher.ai</a> · <a href="https://modelmap.io">modelmap.io</a> · All rights reserved (research code)</span> | |
| 126 | + <span class="footer-meta">snapshot ${esc(commit)}${synced ? " · synced " + esc(synced) : ""} · sister project: <a href="https://www.localvm.dev">localvm-research</a></span> | |
| 127 | + </div> | |
| 128 | +</footer> | |
| 129 | +<script src="/static/app.js" defer></script> | |
| 130 | +</body> | |
| 131 | +</html>`; | |
| 132 | +} | |
| 133 | + | |
| 134 | +module.exports = { esc, markdownToHtml, highlightFile, layout, levelBadge, confidenceLadder, LEVELS }; | |
added
site/package-lock.json
+961 −0
@@ -0,0 +1,961 @@ | ||
| 1 | +{ | |
| 2 | + "name": "modelmap-web", | |
| 3 | + "version": "1.0.0", | |
| 4 | + "lockfileVersion": 3, | |
| 5 | + "requires": true, | |
| 6 | + "packages": { | |
| 7 | + "": { | |
| 8 | + "name": "modelmap-web", | |
| 9 | + "version": "1.0.0", | |
| 10 | + "license": "UNLICENSED", | |
| 11 | + "dependencies": { | |
| 12 | + "express": "^4.21.0", | |
| 13 | + "gray-matter": "^4.0.3", | |
| 14 | + "highlight.js": "^11.10.0", | |
| 15 | + "marked": "^14.1.0" | |
| 16 | + } | |
| 17 | + }, | |
| 18 | + "node_modules/accepts": { | |
| 19 | + "version": "1.3.8", | |
| 20 | + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", | |
| 21 | + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", | |
| 22 | + "license": "MIT", | |
| 23 | + "dependencies": { | |
| 24 | + "mime-types": "~2.1.34", | |
| 25 | + "negotiator": "0.6.3" | |
| 26 | + }, | |
| 27 | + "engines": { | |
| 28 | + "node": ">= 0.6" | |
| 29 | + } | |
| 30 | + }, | |
| 31 | + "node_modules/argparse": { | |
| 32 | + "version": "1.0.10", | |
| 33 | + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", | |
| 34 | + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", | |
| 35 | + "license": "MIT", | |
| 36 | + "dependencies": { | |
| 37 | + "sprintf-js": "~1.0.2" | |
| 38 | + } | |
| 39 | + }, | |
| 40 | + "node_modules/array-flatten": { | |
| 41 | + "version": "1.1.1", | |
| 42 | + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", | |
| 43 | + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", | |
| 44 | + "license": "MIT" | |
| 45 | + }, | |
| 46 | + "node_modules/body-parser": { | |
| 47 | + "version": "1.20.6", | |
| 48 | + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", | |
| 49 | + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", | |
| 50 | + "license": "MIT", | |
| 51 | + "dependencies": { | |
| 52 | + "bytes": "~3.1.2", | |
| 53 | + "content-type": "~1.0.5", | |
| 54 | + "debug": "2.6.9", | |
| 55 | + "depd": "2.0.0", | |
| 56 | + "destroy": "~1.2.0", | |
| 57 | + "http-errors": "~2.0.1", | |
| 58 | + "iconv-lite": "~0.4.24", | |
| 59 | + "on-finished": "~2.4.1", | |
| 60 | + "qs": "~6.15.1", | |
| 61 | + "raw-body": "~2.5.3", | |
| 62 | + "type-is": "~1.6.18", | |
| 63 | + "unpipe": "~1.0.0" | |
| 64 | + }, | |
| 65 | + "engines": { | |
| 66 | + "node": ">= 0.8", | |
| 67 | + "npm": "1.2.8000 || >= 1.4.16" | |
| 68 | + } | |
| 69 | + }, | |
| 70 | + "node_modules/bytes": { | |
| 71 | + "version": "3.1.2", | |
| 72 | + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", | |
| 73 | + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", | |
| 74 | + "license": "MIT", | |
| 75 | + "engines": { | |
| 76 | + "node": ">= 0.8" | |
| 77 | + } | |
| 78 | + }, | |
| 79 | + "node_modules/call-bind-apply-helpers": { | |
| 80 | + "version": "1.0.2", | |
| 81 | + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", | |
| 82 | + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", | |
| 83 | + "license": "MIT", | |
| 84 | + "dependencies": { | |
| 85 | + "es-errors": "^1.3.0", | |
| 86 | + "function-bind": "^1.1.2" | |
| 87 | + }, | |
| 88 | + "engines": { | |
| 89 | + "node": ">= 0.4" | |
| 90 | + } | |
| 91 | + }, | |
| 92 | + "node_modules/call-bound": { | |
| 93 | + "version": "1.0.4", | |
| 94 | + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", | |
| 95 | + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", | |
| 96 | + "license": "MIT", | |
| 97 | + "dependencies": { | |
| 98 | + "call-bind-apply-helpers": "^1.0.2", | |
| 99 | + "get-intrinsic": "^1.3.0" | |
| 100 | + }, | |
| 101 | + "engines": { | |
| 102 | + "node": ">= 0.4" | |
| 103 | + }, | |
| 104 | + "funding": { | |
| 105 | + "url": "https://github.com/sponsors/ljharb" | |
| 106 | + } | |
| 107 | + }, | |
| 108 | + "node_modules/content-disposition": { | |
| 109 | + "version": "0.5.4", | |
| 110 | + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", | |
| 111 | + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", | |
| 112 | + "license": "MIT", | |
| 113 | + "dependencies": { | |
| 114 | + "safe-buffer": "5.2.1" | |
| 115 | + }, | |
| 116 | + "engines": { | |
| 117 | + "node": ">= 0.6" | |
| 118 | + } | |
| 119 | + }, | |
| 120 | + "node_modules/content-type": { | |
| 121 | + "version": "1.0.5", | |
| 122 | + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", | |
| 123 | + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", | |
| 124 | + "license": "MIT", | |
| 125 | + "engines": { | |
| 126 | + "node": ">= 0.6" | |
| 127 | + } | |
| 128 | + }, | |
| 129 | + "node_modules/cookie": { | |
| 130 | + "version": "0.7.2", | |
| 131 | + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", | |
| 132 | + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", | |
| 133 | + "license": "MIT", | |
| 134 | + "engines": { | |
| 135 | + "node": ">= 0.6" | |
| 136 | + } | |
| 137 | + }, | |
| 138 | + "node_modules/cookie-signature": { | |
| 139 | + "version": "1.0.7", | |
| 140 | + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", | |
| 141 | + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", | |
| 142 | + "license": "MIT" | |
| 143 | + }, | |
| 144 | + "node_modules/debug": { | |
| 145 | + "version": "2.6.9", | |
| 146 | + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", | |
| 147 | + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", | |
| 148 | + "license": "MIT", | |
| 149 | + "dependencies": { | |
| 150 | + "ms": "2.0.0" | |
| 151 | + } | |
| 152 | + }, | |
| 153 | + "node_modules/depd": { | |
| 154 | + "version": "2.0.0", | |
| 155 | + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", | |
| 156 | + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", | |
| 157 | + "license": "MIT", | |
| 158 | + "engines": { | |
| 159 | + "node": ">= 0.8" | |
| 160 | + } | |
| 161 | + }, | |
| 162 | + "node_modules/destroy": { | |
| 163 | + "version": "1.2.0", | |
| 164 | + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", | |
| 165 | + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", | |
| 166 | + "license": "MIT", | |
| 167 | + "engines": { | |
| 168 | + "node": ">= 0.8", | |
| 169 | + "npm": "1.2.8000 || >= 1.4.16" | |
| 170 | + } | |
| 171 | + }, | |
| 172 | + "node_modules/dunder-proto": { | |
| 173 | + "version": "1.0.1", | |
| 174 | + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", | |
| 175 | + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", | |
| 176 | + "license": "MIT", | |
| 177 | + "dependencies": { | |
| 178 | + "call-bind-apply-helpers": "^1.0.1", | |
| 179 | + "es-errors": "^1.3.0", | |
| 180 | + "gopd": "^1.2.0" | |
| 181 | + }, | |
| 182 | + "engines": { | |
| 183 | + "node": ">= 0.4" | |
| 184 | + } | |
| 185 | + }, | |
| 186 | + "node_modules/ee-first": { | |
| 187 | + "version": "1.1.1", | |
| 188 | + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | |
| 189 | + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", | |
| 190 | + "license": "MIT" | |
| 191 | + }, | |
| 192 | + "node_modules/encodeurl": { | |
| 193 | + "version": "2.0.0", | |
| 194 | + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", | |
| 195 | + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", | |
| 196 | + "license": "MIT", | |
| 197 | + "engines": { | |
| 198 | + "node": ">= 0.8" | |
| 199 | + } | |
| 200 | + }, | |
| 201 | + "node_modules/es-define-property": { | |
| 202 | + "version": "1.0.1", | |
| 203 | + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", | |
| 204 | + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", | |
| 205 | + "license": "MIT", | |
| 206 | + "engines": { | |
| 207 | + "node": ">= 0.4" | |
| 208 | + } | |
| 209 | + }, | |
| 210 | + "node_modules/es-errors": { | |
| 211 | + "version": "1.3.0", | |
| 212 | + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", | |
| 213 | + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", | |
| 214 | + "license": "MIT", | |
| 215 | + "engines": { | |
| 216 | + "node": ">= 0.4" | |
| 217 | + } | |
| 218 | + }, | |
| 219 | + "node_modules/es-object-atoms": { | |
| 220 | + "version": "1.1.2", | |
| 221 | + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", | |
| 222 | + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", | |
| 223 | + "license": "MIT", | |
| 224 | + "dependencies": { | |
| 225 | + "es-errors": "^1.3.0" | |
| 226 | + }, | |
| 227 | + "engines": { | |
| 228 | + "node": ">= 0.4" | |
| 229 | + } | |
| 230 | + }, | |
| 231 | + "node_modules/escape-html": { | |
| 232 | + "version": "1.0.3", | |
| 233 | + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", | |
| 234 | + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", | |
| 235 | + "license": "MIT" | |
| 236 | + }, | |
| 237 | + "node_modules/esprima": { | |
| 238 | + "version": "4.0.1", | |
| 239 | + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", | |
| 240 | + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", | |
| 241 | + "license": "BSD-2-Clause", | |
| 242 | + "bin": { | |
| 243 | + "esparse": "bin/esparse.js", | |
| 244 | + "esvalidate": "bin/esvalidate.js" | |
| 245 | + }, | |
| 246 | + "engines": { | |
| 247 | + "node": ">=4" | |
| 248 | + } | |
| 249 | + }, | |
| 250 | + "node_modules/etag": { | |
| 251 | + "version": "1.8.1", | |
| 252 | + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", | |
| 253 | + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", | |
| 254 | + "license": "MIT", | |
| 255 | + "engines": { | |
| 256 | + "node": ">= 0.6" | |
| 257 | + } | |
| 258 | + }, | |
| 259 | + "node_modules/express": { | |
| 260 | + "version": "4.22.2", | |
| 261 | + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", | |
| 262 | + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", | |
| 263 | + "license": "MIT", | |
| 264 | + "dependencies": { | |
| 265 | + "accepts": "~1.3.8", | |
| 266 | + "array-flatten": "1.1.1", | |
| 267 | + "body-parser": "~1.20.5", | |
| 268 | + "content-disposition": "~0.5.4", | |
| 269 | + "content-type": "~1.0.4", | |
| 270 | + "cookie": "~0.7.1", | |
| 271 | + "cookie-signature": "~1.0.6", | |
| 272 | + "debug": "2.6.9", | |
| 273 | + "depd": "2.0.0", | |
| 274 | + "encodeurl": "~2.0.0", | |
| 275 | + "escape-html": "~1.0.3", | |
| 276 | + "etag": "~1.8.1", | |
| 277 | + "finalhandler": "~1.3.1", | |
| 278 | + "fresh": "~0.5.2", | |
| 279 | + "http-errors": "~2.0.0", | |
| 280 | + "merge-descriptors": "1.0.3", | |
| 281 | + "methods": "~1.1.2", | |
| 282 | + "on-finished": "~2.4.1", | |
| 283 | + "parseurl": "~1.3.3", | |
| 284 | + "path-to-regexp": "~0.1.12", | |
| 285 | + "proxy-addr": "~2.0.7", | |
| 286 | + "qs": "~6.15.1", | |
| 287 | + "range-parser": "~1.2.1", | |
| 288 | + "safe-buffer": "5.2.1", | |
| 289 | + "send": "~0.19.0", | |
| 290 | + "serve-static": "~1.16.2", | |
| 291 | + "setprototypeof": "1.2.0", | |
| 292 | + "statuses": "~2.0.1", | |
| 293 | + "type-is": "~1.6.18", | |
| 294 | + "utils-merge": "1.0.1", | |
| 295 | + "vary": "~1.1.2" | |
| 296 | + }, | |
| 297 | + "engines": { | |
| 298 | + "node": ">= 0.10.0" | |
| 299 | + }, | |
| 300 | + "funding": { | |
| 301 | + "type": "opencollective", | |
| 302 | + "url": "https://opencollective.com/express" | |
| 303 | + } | |
| 304 | + }, | |
| 305 | + "node_modules/extend-shallow": { | |
| 306 | + "version": "2.0.1", | |
| 307 | + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", | |
| 308 | + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", | |
| 309 | + "license": "MIT", | |
| 310 | + "dependencies": { | |
| 311 | + "is-extendable": "^0.1.0" | |
| 312 | + }, | |
| 313 | + "engines": { | |
| 314 | + "node": ">=0.10.0" | |
| 315 | + } | |
| 316 | + }, | |
| 317 | + "node_modules/finalhandler": { | |
| 318 | + "version": "1.3.2", | |
| 319 | + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", | |
| 320 | + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", | |
| 321 | + "license": "MIT", | |
| 322 | + "dependencies": { | |
| 323 | + "debug": "2.6.9", | |
| 324 | + "encodeurl": "~2.0.0", | |
| 325 | + "escape-html": "~1.0.3", | |
| 326 | + "on-finished": "~2.4.1", | |
| 327 | + "parseurl": "~1.3.3", | |
| 328 | + "statuses": "~2.0.2", | |
| 329 | + "unpipe": "~1.0.0" | |
| 330 | + }, | |
| 331 | + "engines": { | |
| 332 | + "node": ">= 0.8" | |
| 333 | + } | |
| 334 | + }, | |
| 335 | + "node_modules/forwarded": { | |
| 336 | + "version": "0.2.0", | |
| 337 | + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", | |
| 338 | + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", | |
| 339 | + "license": "MIT", | |
| 340 | + "engines": { | |
| 341 | + "node": ">= 0.6" | |
| 342 | + } | |
| 343 | + }, | |
| 344 | + "node_modules/fresh": { | |
| 345 | + "version": "0.5.2", | |
| 346 | + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", | |
| 347 | + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", | |
| 348 | + "license": "MIT", | |
| 349 | + "engines": { | |
| 350 | + "node": ">= 0.6" | |
| 351 | + } | |
| 352 | + }, | |
| 353 | + "node_modules/function-bind": { | |
| 354 | + "version": "1.1.2", | |
| 355 | + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", | |
| 356 | + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", | |
| 357 | + "license": "MIT", | |
| 358 | + "funding": { | |
| 359 | + "url": "https://github.com/sponsors/ljharb" | |
| 360 | + } | |
| 361 | + }, | |
| 362 | + "node_modules/get-intrinsic": { | |
| 363 | + "version": "1.3.0", | |
| 364 | + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", | |
| 365 | + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", | |
| 366 | + "license": "MIT", | |
| 367 | + "dependencies": { | |
| 368 | + "call-bind-apply-helpers": "^1.0.2", | |
| 369 | + "es-define-property": "^1.0.1", | |
| 370 | + "es-errors": "^1.3.0", | |
| 371 | + "es-object-atoms": "^1.1.1", | |
| 372 | + "function-bind": "^1.1.2", | |
| 373 | + "get-proto": "^1.0.1", | |
| 374 | + "gopd": "^1.2.0", | |
| 375 | + "has-symbols": "^1.1.0", | |
| 376 | + "hasown": "^2.0.2", | |
| 377 | + "math-intrinsics": "^1.1.0" | |
| 378 | + }, | |
| 379 | + "engines": { | |
| 380 | + "node": ">= 0.4" | |
| 381 | + }, | |
| 382 | + "funding": { | |
| 383 | + "url": "https://github.com/sponsors/ljharb" | |
| 384 | + } | |
| 385 | + }, | |
| 386 | + "node_modules/get-proto": { | |
| 387 | + "version": "1.0.1", | |
| 388 | + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", | |
| 389 | + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", | |
| 390 | + "license": "MIT", | |
| 391 | + "dependencies": { | |
| 392 | + "dunder-proto": "^1.0.1", | |
| 393 | + "es-object-atoms": "^1.0.0" | |
| 394 | + }, | |
| 395 | + "engines": { | |
| 396 | + "node": ">= 0.4" | |
| 397 | + } | |
| 398 | + }, | |
| 399 | + "node_modules/gopd": { | |
| 400 | + "version": "1.2.0", | |
| 401 | + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", | |
| 402 | + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", | |
| 403 | + "license": "MIT", | |
| 404 | + "engines": { | |
| 405 | + "node": ">= 0.4" | |
| 406 | + }, | |
| 407 | + "funding": { | |
| 408 | + "url": "https://github.com/sponsors/ljharb" | |
| 409 | + } | |
| 410 | + }, | |
| 411 | + "node_modules/gray-matter": { | |
| 412 | + "version": "4.0.3", | |
| 413 | + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", | |
| 414 | + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", | |
| 415 | + "license": "MIT", | |
| 416 | + "dependencies": { | |
| 417 | + "js-yaml": "^3.13.1", | |
| 418 | + "kind-of": "^6.0.2", | |
| 419 | + "section-matter": "^1.0.0", | |
| 420 | + "strip-bom-string": "^1.0.0" | |
| 421 | + }, | |
| 422 | + "engines": { | |
| 423 | + "node": ">=6.0" | |
| 424 | + } | |
| 425 | + }, | |
| 426 | + "node_modules/has-symbols": { | |
| 427 | + "version": "1.1.0", | |
| 428 | + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", | |
| 429 | + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", | |
| 430 | + "license": "MIT", | |
| 431 | + "engines": { | |
| 432 | + "node": ">= 0.4" | |
| 433 | + }, | |
| 434 | + "funding": { | |
| 435 | + "url": "https://github.com/sponsors/ljharb" | |
| 436 | + } | |
| 437 | + }, | |
| 438 | + "node_modules/hasown": { | |
| 439 | + "version": "2.0.4", | |
| 440 | + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", | |
| 441 | + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", | |
| 442 | + "license": "MIT", | |
| 443 | + "dependencies": { | |
| 444 | + "function-bind": "^1.1.2" | |
| 445 | + }, | |
| 446 | + "engines": { | |
| 447 | + "node": ">= 0.4" | |
| 448 | + } | |
| 449 | + }, | |
| 450 | + "node_modules/highlight.js": { | |
| 451 | + "version": "11.12.0", | |
| 452 | + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.12.0.tgz", | |
| 453 | + "integrity": "sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==", | |
| 454 | + "license": "BSD-3-Clause", | |
| 455 | + "engines": { | |
| 456 | + "node": ">=12.0.0" | |
| 457 | + } | |
| 458 | + }, | |
| 459 | + "node_modules/http-errors": { | |
| 460 | + "version": "2.0.1", | |
| 461 | + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", | |
| 462 | + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", | |
| 463 | + "license": "MIT", | |
| 464 | + "dependencies": { | |
| 465 | + "depd": "~2.0.0", | |
| 466 | + "inherits": "~2.0.4", | |
| 467 | + "setprototypeof": "~1.2.0", | |
| 468 | + "statuses": "~2.0.2", | |
| 469 | + "toidentifier": "~1.0.1" | |
| 470 | + }, | |
| 471 | + "engines": { | |
| 472 | + "node": ">= 0.8" | |
| 473 | + }, | |
| 474 | + "funding": { | |
| 475 | + "type": "opencollective", | |
| 476 | + "url": "https://opencollective.com/express" | |
| 477 | + } | |
| 478 | + }, | |
| 479 | + "node_modules/iconv-lite": { | |
| 480 | + "version": "0.4.24", | |
| 481 | + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", | |
| 482 | + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", | |
| 483 | + "license": "MIT", | |
| 484 | + "dependencies": { | |
| 485 | + "safer-buffer": ">= 2.1.2 < 3" | |
| 486 | + }, | |
| 487 | + "engines": { | |
| 488 | + "node": ">=0.10.0" | |
| 489 | + } | |
| 490 | + }, | |
| 491 | + "node_modules/inherits": { | |
| 492 | + "version": "2.0.4", | |
| 493 | + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", | |
| 494 | + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", | |
| 495 | + "license": "ISC" | |
| 496 | + }, | |
| 497 | + "node_modules/ipaddr.js": { | |
| 498 | + "version": "1.9.1", | |
| 499 | + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", | |
| 500 | + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", | |
| 501 | + "license": "MIT", | |
| 502 | + "engines": { | |
| 503 | + "node": ">= 0.10" | |
| 504 | + } | |
| 505 | + }, | |
| 506 | + "node_modules/is-extendable": { | |
| 507 | + "version": "0.1.1", | |
| 508 | + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", | |
| 509 | + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", | |
| 510 | + "license": "MIT", | |
| 511 | + "engines": { | |
| 512 | + "node": ">=0.10.0" | |
| 513 | + } | |
| 514 | + }, | |
| 515 | + "node_modules/js-yaml": { | |
| 516 | + "version": "3.15.1", | |
| 517 | + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", | |
| 518 | + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", | |
| 519 | + "license": "MIT", | |
| 520 | + "dependencies": { | |
| 521 | + "argparse": "^1.0.7", | |
| 522 | + "esprima": "^4.0.0" | |
| 523 | + }, | |
| 524 | + "bin": { | |
| 525 | + "js-yaml": "bin/js-yaml.js" | |
| 526 | + } | |
| 527 | + }, | |
| 528 | + "node_modules/kind-of": { | |
| 529 | + "version": "6.0.3", | |
| 530 | + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", | |
| 531 | + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", | |
| 532 | + "license": "MIT", | |
| 533 | + "engines": { | |
| 534 | + "node": ">=0.10.0" | |
| 535 | + } | |
| 536 | + }, | |
| 537 | + "node_modules/marked": { | |
| 538 | + "version": "14.1.4", | |
| 539 | + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.4.tgz", | |
| 540 | + "integrity": "sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==", | |
| 541 | + "license": "MIT", | |
| 542 | + "bin": { | |
| 543 | + "marked": "bin/marked.js" | |
| 544 | + }, | |
| 545 | + "engines": { | |
| 546 | + "node": ">= 18" | |
| 547 | + } | |
| 548 | + }, | |
| 549 | + "node_modules/math-intrinsics": { | |
| 550 | + "version": "1.1.0", | |
| 551 | + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", | |
| 552 | + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", | |
| 553 | + "license": "MIT", | |
| 554 | + "engines": { | |
| 555 | + "node": ">= 0.4" | |
| 556 | + } | |
| 557 | + }, | |
| 558 | + "node_modules/media-typer": { | |
| 559 | + "version": "0.3.0", | |
| 560 | + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", | |
| 561 | + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", | |
| 562 | + "license": "MIT", | |
| 563 | + "engines": { | |
| 564 | + "node": ">= 0.6" | |
| 565 | + } | |
| 566 | + }, | |
| 567 | + "node_modules/merge-descriptors": { | |
| 568 | + "version": "1.0.3", | |
| 569 | + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", | |
| 570 | + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", | |
| 571 | + "license": "MIT", | |
| 572 | + "funding": { | |
| 573 | + "url": "https://github.com/sponsors/sindresorhus" | |
| 574 | + } | |
| 575 | + }, | |
| 576 | + "node_modules/methods": { | |
| 577 | + "version": "1.1.2", | |
| 578 | + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", | |
| 579 | + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", | |
| 580 | + "license": "MIT", | |
| 581 | + "engines": { | |
| 582 | + "node": ">= 0.6" | |
| 583 | + } | |
| 584 | + }, | |
| 585 | + "node_modules/mime": { | |
| 586 | + "version": "1.6.0", | |
| 587 | + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", | |
| 588 | + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", | |
| 589 | + "license": "MIT", | |
| 590 | + "bin": { | |
| 591 | + "mime": "cli.js" | |
| 592 | + }, | |
| 593 | + "engines": { | |
| 594 | + "node": ">=4" | |
| 595 | + } | |
| 596 | + }, | |
| 597 | + "node_modules/mime-db": { | |
| 598 | + "version": "1.52.0", | |
| 599 | + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", | |
| 600 | + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", | |
| 601 | + "license": "MIT", | |
| 602 | + "engines": { | |
| 603 | + "node": ">= 0.6" | |
| 604 | + } | |
| 605 | + }, | |
| 606 | + "node_modules/mime-types": { | |
| 607 | + "version": "2.1.35", | |
| 608 | + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", | |
| 609 | + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", | |
| 610 | + "license": "MIT", | |
| 611 | + "dependencies": { | |
| 612 | + "mime-db": "1.52.0" | |
| 613 | + }, | |
| 614 | + "engines": { | |
| 615 | + "node": ">= 0.6" | |
| 616 | + } | |
| 617 | + }, | |
| 618 | + "node_modules/ms": { | |
| 619 | + "version": "2.0.0", | |
| 620 | + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", | |
| 621 | + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", | |
| 622 | + "license": "MIT" | |
| 623 | + }, | |
| 624 | + "node_modules/negotiator": { | |
| 625 | + "version": "0.6.3", | |
| 626 | + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", | |
| 627 | + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", | |
| 628 | + "license": "MIT", | |
| 629 | + "engines": { | |
| 630 | + "node": ">= 0.6" | |
| 631 | + } | |
| 632 | + }, | |
| 633 | + "node_modules/object-inspect": { | |
| 634 | + "version": "1.13.4", | |
| 635 | + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", | |
| 636 | + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", | |
| 637 | + "license": "MIT", | |
| 638 | + "engines": { | |
| 639 | + "node": ">= 0.4" | |
| 640 | + }, | |
| 641 | + "funding": { | |
| 642 | + "url": "https://github.com/sponsors/ljharb" | |
| 643 | + } | |
| 644 | + }, | |
| 645 | + "node_modules/on-finished": { | |
| 646 | + "version": "2.4.1", | |
| 647 | + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", | |
| 648 | + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", | |
| 649 | + "license": "MIT", | |
| 650 | + "dependencies": { | |
| 651 | + "ee-first": "1.1.1" | |
| 652 | + }, | |
| 653 | + "engines": { | |
| 654 | + "node": ">= 0.8" | |
| 655 | + } | |
| 656 | + }, | |
| 657 | + "node_modules/parseurl": { | |
| 658 | + "version": "1.3.3", | |
| 659 | + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", | |
| 660 | + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", | |
| 661 | + "license": "MIT", | |
| 662 | + "engines": { | |
| 663 | + "node": ">= 0.8" | |
| 664 | + } | |
| 665 | + }, | |
| 666 | + "node_modules/path-to-regexp": { | |
| 667 | + "version": "0.1.13", | |
| 668 | + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", | |
| 669 | + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", | |
| 670 | + "license": "MIT" | |
| 671 | + }, | |
| 672 | + "node_modules/proxy-addr": { | |
| 673 | + "version": "2.0.7", | |
| 674 | + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", | |
| 675 | + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", | |
| 676 | + "license": "MIT", | |
| 677 | + "dependencies": { | |
| 678 | + "forwarded": "0.2.0", | |
| 679 | + "ipaddr.js": "1.9.1" | |
| 680 | + }, | |
| 681 | + "engines": { | |
| 682 | + "node": ">= 0.10" | |
| 683 | + } | |
| 684 | + }, | |
| 685 | + "node_modules/qs": { | |
| 686 | + "version": "6.15.3", | |
| 687 | + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", | |
| 688 | + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", | |
| 689 | + "license": "BSD-3-Clause", | |
| 690 | + "dependencies": { | |
| 691 | + "es-define-property": "^1.0.1", | |
| 692 | + "side-channel": "^1.1.1" | |
| 693 | + }, | |
| 694 | + "engines": { | |
| 695 | + "node": ">=0.6" | |
| 696 | + }, | |
| 697 | + "funding": { | |
| 698 | + "url": "https://github.com/sponsors/ljharb" | |
| 699 | + } | |
| 700 | + }, | |
| 701 | + "node_modules/range-parser": { | |
| 702 | + "version": "1.2.1", | |
| 703 | + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", | |
| 704 | + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", | |
| 705 | + "license": "MIT", | |
| 706 | + "engines": { | |
| 707 | + "node": ">= 0.6" | |
| 708 | + } | |
| 709 | + }, | |
| 710 | + "node_modules/raw-body": { | |
| 711 | + "version": "2.5.3", | |
| 712 | + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", | |
| 713 | + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", | |
| 714 | + "license": "MIT", | |
| 715 | + "dependencies": { | |
| 716 | + "bytes": "~3.1.2", | |
| 717 | + "http-errors": "~2.0.1", | |
| 718 | + "iconv-lite": "~0.4.24", | |
| 719 | + "unpipe": "~1.0.0" | |
| 720 | + }, | |
| 721 | + "engines": { | |
| 722 | + "node": ">= 0.8" | |
| 723 | + } | |
| 724 | + }, | |
| 725 | + "node_modules/safe-buffer": { | |
| 726 | + "version": "5.2.1", | |
| 727 | + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", | |
| 728 | + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", | |
| 729 | + "funding": [ | |
| 730 | + { | |
| 731 | + "type": "github", | |
| 732 | + "url": "https://github.com/sponsors/feross" | |
| 733 | + }, | |
| 734 | + { | |
| 735 | + "type": "patreon", | |
| 736 | + "url": "https://www.patreon.com/feross" | |
| 737 | + }, | |
| 738 | + { | |
| 739 | + "type": "consulting", | |
| 740 | + "url": "https://feross.org/support" | |
| 741 | + } | |
| 742 | + ], | |
| 743 | + "license": "MIT" | |
| 744 | + }, | |
| 745 | + "node_modules/safer-buffer": { | |
| 746 | + "version": "2.1.2", | |
| 747 | + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", | |
| 748 | + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", | |
| 749 | + "license": "MIT" | |
| 750 | + }, | |
| 751 | + "node_modules/section-matter": { | |
| 752 | + "version": "1.0.0", | |
| 753 | + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", | |
| 754 | + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", | |
| 755 | + "license": "MIT", | |
| 756 | + "dependencies": { | |
| 757 | + "extend-shallow": "^2.0.1", | |
| 758 | + "kind-of": "^6.0.0" | |
| 759 | + }, | |
| 760 | + "engines": { | |
| 761 | + "node": ">=4" | |
| 762 | + } | |
| 763 | + }, | |
| 764 | + "node_modules/send": { | |
| 765 | + "version": "0.19.2", | |
| 766 | + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", | |
| 767 | + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", | |
| 768 | + "license": "MIT", | |
| 769 | + "dependencies": { | |
| 770 | + "debug": "2.6.9", | |
| 771 | + "depd": "2.0.0", | |
| 772 | + "destroy": "1.2.0", | |
| 773 | + "encodeurl": "~2.0.0", | |
| 774 | + "escape-html": "~1.0.3", | |
| 775 | + "etag": "~1.8.1", | |
| 776 | + "fresh": "~0.5.2", | |
| 777 | + "http-errors": "~2.0.1", | |
| 778 | + "mime": "1.6.0", | |
| 779 | + "ms": "2.1.3", | |
| 780 | + "on-finished": "~2.4.1", | |
| 781 | + "range-parser": "~1.2.1", | |
| 782 | + "statuses": "~2.0.2" | |
| 783 | + }, | |
| 784 | + "engines": { | |
| 785 | + "node": ">= 0.8.0" | |
| 786 | + } | |
| 787 | + }, | |
| 788 | + "node_modules/send/node_modules/ms": { | |
| 789 | + "version": "2.1.3", | |
| 790 | + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", | |
| 791 | + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", | |
| 792 | + "license": "MIT" | |
| 793 | + }, | |
| 794 | + "node_modules/serve-static": { | |
| 795 | + "version": "1.16.3", | |
| 796 | + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", | |
| 797 | + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", | |
| 798 | + "license": "MIT", | |
| 799 | + "dependencies": { | |
| 800 | + "encodeurl": "~2.0.0", | |
| 801 | + "escape-html": "~1.0.3", | |
| 802 | + "parseurl": "~1.3.3", | |
| 803 | + "send": "~0.19.1" | |
| 804 | + }, | |
| 805 | + "engines": { | |
| 806 | + "node": ">= 0.8.0" | |
| 807 | + } | |
| 808 | + }, | |
| 809 | + "node_modules/setprototypeof": { | |
| 810 | + "version": "1.2.0", | |
| 811 | + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", | |
| 812 | + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", | |
| 813 | + "license": "ISC" | |
| 814 | + }, | |
| 815 | + "node_modules/side-channel": { | |
| 816 | + "version": "1.1.1", | |
| 817 | + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", | |
| 818 | + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", | |
| 819 | + "license": "MIT", | |
| 820 | + "dependencies": { | |
| 821 | + "es-errors": "^1.3.0", | |
| 822 | + "object-inspect": "^1.13.4", | |
| 823 | + "side-channel-list": "^1.0.1", | |
| 824 | + "side-channel-map": "^1.0.1", | |
| 825 | + "side-channel-weakmap": "^1.0.2" | |
| 826 | + }, | |
| 827 | + "engines": { | |
| 828 | + "node": ">= 0.4" | |
| 829 | + }, | |
| 830 | + "funding": { | |
| 831 | + "url": "https://github.com/sponsors/ljharb" | |
| 832 | + } | |
| 833 | + }, | |
| 834 | + "node_modules/side-channel-list": { | |
| 835 | + "version": "1.0.1", | |
| 836 | + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", | |
| 837 | + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", | |
| 838 | + "license": "MIT", | |
| 839 | + "dependencies": { | |
| 840 | + "es-errors": "^1.3.0", | |
| 841 | + "object-inspect": "^1.13.4" | |
| 842 | + }, | |
| 843 | + "engines": { | |
| 844 | + "node": ">= 0.4" | |
| 845 | + }, | |
| 846 | + "funding": { | |
| 847 | + "url": "https://github.com/sponsors/ljharb" | |
| 848 | + } | |
| 849 | + }, | |
| 850 | + "node_modules/side-channel-map": { | |
| 851 | + "version": "1.0.1", | |
| 852 | + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", | |
| 853 | + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", | |
| 854 | + "license": "MIT", | |
| 855 | + "dependencies": { | |
| 856 | + "call-bound": "^1.0.2", | |
| 857 | + "es-errors": "^1.3.0", | |
| 858 | + "get-intrinsic": "^1.2.5", | |
| 859 | + "object-inspect": "^1.13.3" | |
| 860 | + }, | |
| 861 | + "engines": { | |
| 862 | + "node": ">= 0.4" | |
| 863 | + }, | |
| 864 | + "funding": { | |
| 865 | + "url": "https://github.com/sponsors/ljharb" | |
| 866 | + } | |
| 867 | + }, | |
| 868 | + "node_modules/side-channel-weakmap": { | |
| 869 | + "version": "1.0.2", | |
| 870 | + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", | |
| 871 | + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", | |
| 872 | + "license": "MIT", | |
| 873 | + "dependencies": { | |
| 874 | + "call-bound": "^1.0.2", | |
| 875 | + "es-errors": "^1.3.0", | |
| 876 | + "get-intrinsic": "^1.2.5", | |
| 877 | + "object-inspect": "^1.13.3", | |
| 878 | + "side-channel-map": "^1.0.1" | |
| 879 | + }, | |
| 880 | + "engines": { | |
| 881 | + "node": ">= 0.4" | |
| 882 | + }, | |
| 883 | + "funding": { | |
| 884 | + "url": "https://github.com/sponsors/ljharb" | |
| 885 | + } | |
| 886 | + }, | |
| 887 | + "node_modules/sprintf-js": { | |
| 888 | + "version": "1.0.3", | |
| 889 | + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", | |
| 890 | + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", | |
| 891 | + "license": "BSD-3-Clause" | |
| 892 | + }, | |
| 893 | + "node_modules/statuses": { | |
| 894 | + "version": "2.0.2", | |
| 895 | + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", | |
| 896 | + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", | |
| 897 | + "license": "MIT", | |
| 898 | + "engines": { | |
| 899 | + "node": ">= 0.8" | |
| 900 | + } | |
| 901 | + }, | |
| 902 | + "node_modules/strip-bom-string": { | |
| 903 | + "version": "1.0.0", | |
| 904 | + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", | |
| 905 | + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", | |
| 906 | + "license": "MIT", | |
| 907 | + "engines": { | |
| 908 | + "node": ">=0.10.0" | |
| 909 | + } | |
| 910 | + }, | |
| 911 | + "node_modules/toidentifier": { | |
| 912 | + "version": "1.0.1", | |
| 913 | + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", | |
| 914 | + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", | |
| 915 | + "license": "MIT", | |
| 916 | + "engines": { | |
| 917 | + "node": ">=0.6" | |
| 918 | + } | |
| 919 | + }, | |
| 920 | + "node_modules/type-is": { | |
| 921 | + "version": "1.6.18", | |
| 922 | + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", | |
| 923 | + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", | |
| 924 | + "license": "MIT", | |
| 925 | + "dependencies": { | |
| 926 | + "media-typer": "0.3.0", | |
| 927 | + "mime-types": "~2.1.24" | |
| 928 | + }, | |
| 929 | + "engines": { | |
| 930 | + "node": ">= 0.6" | |
| 931 | + } | |
| 932 | + }, | |
| 933 | + "node_modules/unpipe": { | |
| 934 | + "version": "1.0.0", | |
| 935 | + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", | |
| 936 | + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", | |
| 937 | + "license": "MIT", | |
| 938 | + "engines": { | |
| 939 | + "node": ">= 0.8" | |
| 940 | + } | |
| 941 | + }, | |
| 942 | + "node_modules/utils-merge": { | |
| 943 | + "version": "1.0.1", | |
| 944 | + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", | |
| 945 | + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", | |
| 946 | + "license": "MIT", | |
| 947 | + "engines": { | |
| 948 | + "node": ">= 0.4.0" | |
| 949 | + } | |
| 950 | + }, | |
| 951 | + "node_modules/vary": { | |
| 952 | + "version": "1.1.2", | |
| 953 | + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", | |
| 954 | + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", | |
| 955 | + "license": "MIT", | |
| 956 | + "engines": { | |
| 957 | + "node": ">= 0.8" | |
| 958 | + } | |
| 959 | + } | |
| 960 | + } | |
| 961 | +} | |
added
site/package.json
+18 −0
@@ -0,0 +1,18 @@ | ||
| 1 | +{ | |
| 2 | + "name": "modelmap-web", | |
| 3 | + "version": "1.0.0", | |
| 4 | + "description": "modelmap.io — living research atlas for the internal cartography of local LLMs — Simon-Pierre Boucher <contact@spboucher.ai>", | |
| 5 | + "author": "Simon-Pierre Boucher <contact@spboucher.ai>", | |
| 6 | + "license": "UNLICENSED", | |
| 7 | + "private": true, | |
| 8 | + "main": "server.js", | |
| 9 | + "scripts": { | |
| 10 | + "start": "node server.js" | |
| 11 | + }, | |
| 12 | + "dependencies": { | |
| 13 | + "express": "^4.21.0", | |
| 14 | + "gray-matter": "^4.0.3", | |
| 15 | + "highlight.js": "^11.10.0", | |
| 16 | + "marked": "^14.1.0" | |
| 17 | + } | |
| 18 | +} | |
added
site/public/app.js
+32 −0
@@ -0,0 +1,32 @@ | ||
| 1 | +// ============================================================================ | |
| 2 | +// Project : modelmap | |
| 3 | +// File : site/public/app.js | |
| 4 | +// Purpose : Client-side interactivity (chart hover tooltips) | |
| 5 | +// Author : Simon-Pierre Boucher | |
| 6 | +// Contact : contact@spboucher.ai | |
| 7 | +// Website : https://modelmap.io | |
| 8 | +// Created : 2026-08-12 | |
| 9 | +// Modified : 2026-08-12 | |
| 10 | +// Platform : Web (deployed from macOS / Apple Silicon) | |
| 11 | +// License : All rights reserved (research code) | |
| 12 | +// ============================================================================ | |
| 13 | +"use strict"; | |
| 14 | + | |
| 15 | +(function () { | |
| 16 | + const fig = document.querySelector(".chart-fig"); | |
| 17 | + if (!fig) return; | |
| 18 | + const tip = fig.querySelector(".chart-tip"); | |
| 19 | + if (!tip) return; | |
| 20 | + fig.querySelectorAll(".pt").forEach((pt) => { | |
| 21 | + pt.addEventListener("mouseenter", () => { | |
| 22 | + tip.textContent = pt.getAttribute("data-tip"); | |
| 23 | + tip.hidden = false; | |
| 24 | + }); | |
| 25 | + pt.addEventListener("mousemove", (ev) => { | |
| 26 | + const r = fig.getBoundingClientRect(); | |
| 27 | + tip.style.left = Math.min(ev.clientX - r.left + 14, r.width - tip.offsetWidth - 4) + "px"; | |
| 28 | + tip.style.top = ev.clientY - r.top - 34 + "px"; | |
| 29 | + }); | |
| 30 | + pt.addEventListener("mouseleave", () => { tip.hidden = true; }); | |
| 31 | + }); | |
| 32 | +})(); | |
added
site/public/style.css
+230 −0
@@ -0,0 +1,230 @@ | ||
| 1 | +/* ============================================================================ | |
| 2 | + * Project : modelmap | |
| 3 | + * File : site/public/style.css | |
| 4 | + * Purpose : Light-theme styling for the modelmap.io research atlas | |
| 5 | + * Author : Simon-Pierre Boucher | |
| 6 | + * Contact : contact@spboucher.ai | |
| 7 | + * Website : https://modelmap.io | |
| 8 | + * Created : 2026-08-12 | |
| 9 | + * Modified : 2026-08-12 | |
| 10 | + * Platform : Web (deployed from macOS / Apple Silicon) | |
| 11 | + * License : All rights reserved (research code) | |
| 12 | + * ==========================================================================*/ | |
| 13 | + | |
| 14 | +:root { | |
| 15 | + color-scheme: light; | |
| 16 | + --page: #f9f9f7; | |
| 17 | + --surface: #fcfcfb; | |
| 18 | + --ink: #0b0b0b; | |
| 19 | + --ink-2: #52514e; | |
| 20 | + --muted: #898781; | |
| 21 | + --grid: #e1e0d9; | |
| 22 | + --baseline: #c3c2b7; | |
| 23 | + --border: rgba(11, 11, 11, 0.10); | |
| 24 | + --accent: #6d4fc4; | |
| 25 | + --accent-dark: #53389e; | |
| 26 | + --accent-bg: #f1edfa; | |
| 27 | + --good: #0ca30c; | |
| 28 | + --good-text: #006300; | |
| 29 | + --warn: #eda100; | |
| 30 | +} | |
| 31 | + | |
| 32 | +* { box-sizing: border-box; } | |
| 33 | +html { -webkit-text-size-adjust: 100%; } | |
| 34 | +body { | |
| 35 | + margin: 0; | |
| 36 | + font-family: system-ui, -apple-system, "Segoe UI", sans-serif; | |
| 37 | + background: var(--page); | |
| 38 | + color: var(--ink); | |
| 39 | + line-height: 1.6; | |
| 40 | + font-size: 16px; | |
| 41 | +} | |
| 42 | +.wrap { max-width: 1080px; margin: 0 auto; padding: 0 24px; } | |
| 43 | +a { color: var(--accent-dark); text-decoration: none; } | |
| 44 | +a:hover { text-decoration: underline; } | |
| 45 | +code, pre, .mono-small { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; } | |
| 46 | + | |
| 47 | +/* header */ | |
| 48 | +.site-header { | |
| 49 | + background: var(--surface); | |
| 50 | + border-bottom: 1px solid var(--grid); | |
| 51 | + position: sticky; top: 0; z-index: 10; | |
| 52 | +} | |
| 53 | +.header-row { display: flex; align-items: center; justify-content: space-between; height: 56px; } | |
| 54 | +.brand { font-weight: 700; font-size: 18px; color: var(--ink); letter-spacing: -0.02em; } | |
| 55 | +.brand-dim { color: var(--muted); font-weight: 500; } | |
| 56 | +.site-nav { display: flex; gap: 4px; flex-wrap: wrap; } | |
| 57 | +.site-nav a { | |
| 58 | + padding: 6px 12px; border-radius: 6px; color: var(--ink-2); font-size: 14px; font-weight: 500; | |
| 59 | +} | |
| 60 | +.site-nav a:hover { background: var(--page); text-decoration: none; color: var(--ink); } | |
| 61 | +.site-nav a.active { color: var(--accent-dark); background: var(--accent-bg); } | |
| 62 | + | |
| 63 | +/* hero */ | |
| 64 | +.hero { padding: 56px 0 8px; } | |
| 65 | +.kicker { | |
| 66 | + text-transform: uppercase; letter-spacing: 0.08em; font-size: 12px; font-weight: 600; | |
| 67 | + color: var(--accent-dark); margin: 0 0 12px; | |
| 68 | +} | |
| 69 | +.hero h1 { font-size: 40px; line-height: 1.15; letter-spacing: -0.02em; margin: 0 0 16px; } | |
| 70 | +.lede { font-size: 18px; color: var(--ink-2); max-width: 760px; margin: 0 0 12px; } | |
| 71 | +.lede-small { color: var(--ink-2); max-width: 760px; } | |
| 72 | +.lede-eq code { | |
| 73 | + font-size: 13px; background: var(--surface); border: 1px solid var(--grid); | |
| 74 | + border-radius: 6px; padding: 6px 10px; color: var(--ink-2); display: inline-block; | |
| 75 | +} | |
| 76 | + | |
| 77 | +/* stat tiles */ | |
| 78 | +.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin: 32px 0; } | |
| 79 | +.tile { | |
| 80 | + background: var(--surface); border: 1px solid var(--grid); border-radius: 10px; | |
| 81 | + padding: 20px 22px; display: flex; flex-direction: column; gap: 2px; | |
| 82 | +} | |
| 83 | +.tile-value { font-size: 34px; font-weight: 700; letter-spacing: -0.02em; } | |
| 84 | +.tile-denom { font-size: 20px; color: var(--muted); font-weight: 500; } | |
| 85 | +.tile-label { color: var(--ink-2); font-size: 13.5px; } | |
| 86 | + | |
| 87 | +/* layout blocks */ | |
| 88 | +.split { display: grid; grid-template-columns: 3fr 2fr; gap: 20px; margin: 8px 0 20px; } | |
| 89 | +@media (max-width: 880px) { .split { grid-template-columns: 1fr; } .hero h1 { font-size: 30px; } } | |
| 90 | +.card { | |
| 91 | + background: var(--surface); border: 1px solid var(--grid); border-radius: 10px; | |
| 92 | + padding: 24px 26px; margin-bottom: 20px; | |
| 93 | +} | |
| 94 | +.card h2 { margin-top: 0; font-size: 20px; letter-spacing: -0.01em; } | |
| 95 | +.more { font-weight: 600; font-size: 14px; } | |
| 96 | + | |
| 97 | +/* phases */ | |
| 98 | +.phases { list-style: none; margin: 0; padding: 0; } | |
| 99 | +.phase { display: flex; align-items: flex-start; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--grid); } | |
| 100 | +.phase:last-child { border-bottom: 0; } | |
| 101 | +.phase strong { display: block; font-size: 14.5px; } | |
| 102 | +.phase-detail { font-size: 13px; color: var(--muted); } | |
| 103 | +.phase-dot { width: 10px; height: 10px; border-radius: 50%; margin-top: 7px; flex: 0 0 auto; background: var(--baseline); } | |
| 104 | +.phase.done .phase-dot { background: var(--good); } | |
| 105 | +.phase.in-progress .phase-dot { background: var(--accent); } | |
| 106 | +.phase .badge { margin-left: auto; } | |
| 107 | +.badge { | |
| 108 | + font-size: 11.5px; font-weight: 600; padding: 3px 9px; border-radius: 20px; | |
| 109 | + white-space: nowrap; align-self: center; border: 1px solid var(--grid); color: var(--ink-2); | |
| 110 | +} | |
| 111 | +.badge-done { color: var(--good-text); background: #eef7ee; border-color: #cfe8cf; } | |
| 112 | +.badge-in-progress, .badge-has-results { color: var(--accent-dark); background: var(--accent-bg); border-color: #dcd2f2; } | |
| 113 | +.badge-pending, .badge-scaffolded { color: var(--muted); background: var(--page); } | |
| 114 | + | |
| 115 | +/* confidence levels — the atlas ladder and per-map badges */ | |
| 116 | +.ladder { list-style: none; margin: 16px 0; padding: 0; } | |
| 117 | +.ladder-step { display: flex; align-items: flex-start; gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--grid); } | |
| 118 | +.ladder-step:last-child { border-bottom: 0; } | |
| 119 | +.ladder-step strong { display: block; font-size: 14.5px; } | |
| 120 | +.ladder-desc { font-size: 13px; color: var(--muted); display: block; } | |
| 121 | +.ladder-num { | |
| 122 | + flex: 0 0 auto; width: 34px; height: 34px; border-radius: 8px; display: flex; | |
| 123 | + align-items: center; justify-content: center; font-weight: 700; font-size: 13px; | |
| 124 | + font-family: ui-monospace, Menlo, monospace; margin-top: 2px; | |
| 125 | + background: var(--page); border: 1px solid var(--grid); color: var(--muted); | |
| 126 | +} | |
| 127 | +.ladder-num.lv1 { background: #fdf3e2; border-color: #f3ddb0; color: #9a6a00; } | |
| 128 | +.ladder-num.lv2 { background: var(--accent-bg); border-color: #dcd2f2; color: var(--accent-dark); } | |
| 129 | +.ladder-num.lv3 { background: #eef7ee; border-color: #cfe8cf; color: var(--good-text); } | |
| 130 | +.badge-level-0 { color: var(--muted); background: var(--page); } | |
| 131 | +.badge-level-1 { color: #9a6a00; background: #fdf3e2; border-color: #f3ddb0; } | |
| 132 | +.badge-level-2 { color: var(--accent-dark); background: var(--accent-bg); border-color: #dcd2f2; } | |
| 133 | +.badge-level-3 { color: var(--good-text); background: #eef7ee; border-color: #cfe8cf; } | |
| 134 | + | |
| 135 | +/* doc cards */ | |
| 136 | +.page-title { font-size: 30px; letter-spacing: -0.02em; margin: 40px 0 8px; } | |
| 137 | +.section-title { font-size: 20px; margin: 32px 0 8px; } | |
| 138 | +.doc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; margin: 16px 0; } | |
| 139 | +.doc-card { | |
| 140 | + background: var(--surface); border: 1px solid var(--grid); border-radius: 10px; | |
| 141 | + padding: 18px 20px; color: var(--ink); display: block; | |
| 142 | +} | |
| 143 | +.doc-card:hover { border-color: var(--accent); text-decoration: none; } | |
| 144 | +.doc-card.disabled { opacity: 0.55; pointer-events: none; } | |
| 145 | +.doc-card h3 { margin: 0 0 6px; font-size: 16px; } | |
| 146 | +.doc-card p { margin: 0 0 8px; font-size: 13.5px; color: var(--ink-2); } | |
| 147 | +.runs { font-size: 12px; color: var(--muted); margin-left: 8px; } | |
| 148 | +.note-line { color: var(--ink-2); font-size: 14px; } | |
| 149 | + | |
| 150 | +/* markdown body */ | |
| 151 | +.doc { max-width: 860px; margin: 32px auto; } | |
| 152 | +.crumb { font-size: 13px; color: var(--muted); margin: 24px 0 4px; } | |
| 153 | +.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 18px; } | |
| 154 | +.chip { | |
| 155 | + font-size: 12px; background: var(--surface); border: 1px solid var(--grid); | |
| 156 | + border-radius: 20px; padding: 3px 12px; color: var(--ink-2); | |
| 157 | +} | |
| 158 | +.chip-k { color: var(--muted); margin-right: 6px; text-transform: uppercase; font-size: 10px; letter-spacing: 0.05em; } | |
| 159 | +.md { overflow-wrap: break-word; } | |
| 160 | +.md h1 { font-size: 28px; letter-spacing: -0.02em; } | |
| 161 | +.md h2 { font-size: 21px; margin-top: 36px; border-bottom: 1px solid var(--grid); padding-bottom: 6px; } | |
| 162 | +.md h3 { font-size: 17px; margin-top: 28px; } | |
| 163 | +.md code { background: #f1f0ec; border-radius: 4px; padding: 1px 5px; font-size: 0.88em; } | |
| 164 | +.md pre code, .codeblock code { background: none; padding: 0; font-size: 13px; } | |
| 165 | +.codeblock, .md pre { | |
| 166 | + background: #f6f5f2; border: 1px solid var(--grid); border-radius: 8px; | |
| 167 | + padding: 14px 16px; overflow-x: auto; line-height: 1.5; | |
| 168 | +} | |
| 169 | +.md table { border-collapse: collapse; width: 100%; font-size: 14px; display: block; overflow-x: auto; } | |
| 170 | +.md th, .md td { border: 1px solid var(--grid); padding: 6px 10px; text-align: left; vertical-align: top; } | |
| 171 | +.md th { background: var(--page); } | |
| 172 | +.md blockquote { border-left: 3px solid var(--accent); margin-left: 0; padding-left: 16px; color: var(--ink-2); } | |
| 173 | + | |
| 174 | +/* log */ | |
| 175 | +.log-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; } | |
| 176 | +.log-entry h3 { font-size: 15px; margin: 0 0 8px; } | |
| 177 | +.log-entry .md { font-size: 13.5px; color: var(--ink-2); } | |
| 178 | + | |
| 179 | +/* results table / kv table / file lists */ | |
| 180 | +.results-table, .kv-table { border-collapse: collapse; width: 100%; font-size: 14.5px; background: var(--surface); } | |
| 181 | +.results-table th, .results-table td, .kv-table th, .kv-table td { | |
| 182 | + border: 1px solid var(--grid); padding: 8px 12px; text-align: left; | |
| 183 | +} | |
| 184 | +.results-table th { background: var(--page); } | |
| 185 | +.kv-table th { background: var(--page); width: 180px; } | |
| 186 | +.file-list { list-style: none; padding: 0; } | |
| 187 | +.file-list li { padding: 6px 0; border-bottom: 1px solid var(--grid); } | |
| 188 | +.tree { list-style: none; padding-left: 0; } | |
| 189 | +.tree ul { list-style: none; padding-left: 20px; } | |
| 190 | +.tree-root { margin-bottom: 14px; } | |
| 191 | +.tree li { padding: 2px 0; font-size: 14.5px; } | |
| 192 | +.mono-small { font-size: 12.5px; color: var(--muted); } | |
| 193 | +.file-card { padding: 0; overflow: hidden; } | |
| 194 | +.file-head { display: flex; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--grid); background: var(--page); } | |
| 195 | +.file-card .codeblock { border: 0; border-radius: 0; margin: 0; } | |
| 196 | + | |
| 197 | +/* charts (server-rendered SVG, used once atlas data exists) */ | |
| 198 | +.chart-fig { margin: 18px 0 8px; position: relative; } | |
| 199 | +.chart-fig svg { width: 100%; height: auto; } | |
| 200 | +.chart-fig .grid { stroke: var(--grid); stroke-width: 1; } | |
| 201 | +.chart-fig .axis { stroke: var(--baseline); stroke-width: 1; } | |
| 202 | +.chart-fig .tick { fill: var(--muted); font-size: 11px; font-family: ui-monospace, Menlo, monospace; } | |
| 203 | +.chart-fig .axis-title { fill: var(--ink-2); font-size: 12px; } | |
| 204 | +.chart-fig .series-label { font-size: 12px; font-weight: 600; } | |
| 205 | +.chart-fig .pt { cursor: pointer; } | |
| 206 | +.chart-fig figcaption { font-size: 12.5px; color: var(--muted); margin-top: 4px; } | |
| 207 | +.chart-tip { | |
| 208 | + position: absolute; pointer-events: none; background: var(--ink); color: #fff; | |
| 209 | + font-size: 12.5px; padding: 5px 10px; border-radius: 6px; white-space: nowrap; z-index: 5; | |
| 210 | +} | |
| 211 | + | |
| 212 | +/* hljs light theme (subset, GitHub-like) */ | |
| 213 | +.hljs { color: #24292e; } | |
| 214 | +.hljs-keyword, .hljs-meta .hljs-keyword { color: #d73a49; } | |
| 215 | +.hljs-string, .hljs-attr { color: #032f62; } | |
| 216 | +.hljs-comment, .hljs-quote { color: #6a737d; font-style: italic; } | |
| 217 | +.hljs-number, .hljs-literal { color: #005cc5; } | |
| 218 | +.hljs-title, .hljs-function .hljs-title { color: #6f42c1; } | |
| 219 | +.hljs-built_in, .hljs-type { color: #e36209; } | |
| 220 | +.hljs-section { color: #005cc5; font-weight: 600; } | |
| 221 | +.hljs-params { color: #24292e; } | |
| 222 | +.hljs-variable, .hljs-template-variable { color: #e36209; } | |
| 223 | + | |
| 224 | +/* footer */ | |
| 225 | +.site-footer { | |
| 226 | + border-top: 1px solid var(--grid); margin-top: 48px; padding: 20px 0 32px; | |
| 227 | + background: var(--surface); font-size: 13px; color: var(--ink-2); | |
| 228 | +} | |
| 229 | +.site-footer .wrap { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px; } | |
| 230 | +.footer-meta { color: var(--muted); } | |
added
site/server.js
+386 −0
@@ -0,0 +1,386 @@ | ||
| 1 | +// ============================================================================ | |
| 2 | +// Project : modelmap | |
| 3 | +// File : site/server.js | |
| 4 | +// Purpose : modelmap.io platform — routes and pages (English, light theme) | |
| 5 | +// Author : Simon-Pierre Boucher | |
| 6 | +// Contact : contact@spboucher.ai | |
| 7 | +// Website : https://modelmap.io | |
| 8 | +// Created : 2026-08-12 | |
| 9 | +// Modified : 2026-08-12 | |
| 10 | +// Platform : macOS / Apple Silicon (arm64) — Node.js (deployed on MacLustr) | |
| 11 | +// License : All rights reserved (research code) | |
| 12 | +// ============================================================================ | |
| 13 | +"use strict"; | |
| 14 | + | |
| 15 | +const path = require("path"); | |
| 16 | +const express = require("express"); | |
| 17 | +const C = require("./lib/content"); | |
| 18 | +const R = require("./lib/render"); | |
| 19 | + | |
| 20 | +const app = express(); | |
| 21 | +const PORT = process.env.PORT || 8140; | |
| 22 | + | |
| 23 | +app.use("/static", express.static(path.join(__dirname, "public"), { maxAge: "1h" })); | |
| 24 | + | |
| 25 | +function page(res, opts) { | |
| 26 | + res.send(R.layout({ ...opts, buildInfo: C.buildInfo() })); | |
| 27 | +} | |
| 28 | + | |
| 29 | +function metaChips(data) { | |
| 30 | + if (!data || !Object.keys(data).length) return ""; | |
| 31 | + const fields = ["document", "author", "created", "modified", "status"]; | |
| 32 | + const chips = fields | |
| 33 | + .filter((f) => data[f]) | |
| 34 | + .map((f) => `<span class="chip"><span class="chip-k">${f}</span>${R.esc(data[f])}</span>`) | |
| 35 | + .join(""); | |
| 36 | + return chips ? `<div class="chips">${chips}</div>` : ""; | |
| 37 | +} | |
| 38 | + | |
| 39 | +// ---------------------------------------------------------------- home | |
| 40 | +app.get("/", (req, res) => { | |
| 41 | + const s = C.stats(); | |
| 42 | + const log = C.parseLogEntries().slice(-3).reverse(); | |
| 43 | + const phases = [ | |
| 44 | + ["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"], | |
| 45 | + ["Phase 2 — State-of-the-art map", C.exists("research/state_of_the_art.md") ? "done" : "pending", "technique taxonomy with epistemic status"], | |
| 46 | + ["Phase 3 — Research gaps", s.gaps ? "done" : "pending", s.gaps ? `${s.gaps} falsifiable candidate directions` : "≥20 substantially different candidate directions"], | |
| 47 | + ["Phase 4 — Candidate ranking", C.exists("research/candidate_ranking.md") ? "done" : "pending", "10-axis scoring, 3–5 prototype candidates"], | |
| 48 | + ["Phases 5–6 — Framework & micro-experiments A–H", s.experimentsDone ? "in progress" : "pending", `${s.experimentsDone}/${s.experiments} experiments completed`], | |
| 49 | + ["Phases 7–9 — Prototypes → methodology", C.exists("research/methodology.md") ? "done" : "pending", "evidence-driven; the methodology is a primary deliverable"], | |
| 50 | + ["Phase 10 — Atlas pipeline & this platform", "in progress", `platform live · ${s.atlasEntries} map${s.atlasEntries === 1 ? "" : "s"} published`], | |
| 51 | + ["Phase 11 — Novelty verification", C.exists("research/novelty_check.md") ? "done" : "pending", "assume not novel until evidence suggests otherwise"], | |
| 52 | + ]; | |
| 53 | + const phaseHtml = phases | |
| 54 | + .map( | |
| 55 | + ([name, st, detail]) => `<li class="phase ${st.replace(" ", "-")}"> | |
| 56 | + <span class="phase-dot"></span><div><strong>${name}</strong><span class="phase-detail">${detail}</span></div> | |
| 57 | + <span class="badge badge-${st.replace(" ", "-")}">${st}</span></li>` | |
| 58 | + ) | |
| 59 | + .join(""); | |
| 60 | + const logHtml = log | |
| 61 | + .map( | |
| 62 | + (e) => `<article class="log-entry"><h3>${R.esc(e.title)}</h3> | |
| 63 | + <div class="md">${R.markdownToHtml(e.body.split("\n").slice(0, 8).join("\n"), "research/LOG.md")}</div> | |
| 64 | + <a class="more" href="/doc/research/LOG.md">Full research log →</a></article>` | |
| 65 | + ) | |
| 66 | + .join(""); | |
| 67 | + | |
| 68 | + const body = ` | |
| 69 | +<section class="hero"> | |
| 70 | + <p class="kicker">Independent interpretability research · Apple Silicon</p> | |
| 71 | + <h1>An internal cartography of<br>local large language models</h1> | |
| 72 | + <p class="lede">Given an already-trained open-weight model, what can actually be <em>known, measured, | |
| 73 | + localized, and mapped</em> about its internal organization — where knowledge lives, how computation | |
| 74 | + is distributed, which structures are stable across inputs, layers, scales, and model families — | |
| 75 | + using only a consumer-grade Mac? Every map published here is versioned, provenanced, | |
| 76 | + confidence-labeled, and regenerable.</p> | |
| 77 | + <p class="lede-eq"><code>what a probe reports ≠ what the model computes ≠ what is stable across methods ≠ what survives intervention</code></p> | |
| 78 | +</section> | |
| 79 | + | |
| 80 | +<section class="tiles"> | |
| 81 | + <div class="tile"><span class="tile-value">${s.sources}</span><span class="tile-label">sources reviewed</span></div> | |
| 82 | + <div class="tile"><span class="tile-value">${s.gaps}</span><span class="tile-label">research gaps identified</span></div> | |
| 83 | + <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> | |
| 84 | + <div class="tile"><span class="tile-value">${s.atlasEntries}</span><span class="tile-label">atlas maps published</span></div> | |
| 85 | +</section> | |
| 86 | + | |
| 87 | +<section class="split"> | |
| 88 | + <div class="card"> | |
| 89 | + <h2>The evidence standard</h2> | |
| 90 | + <p>A claim enters the atlas only at the confidence level its evidence supports. | |
| 91 | + Correlational maps and causally-verified maps are never conflated — visually or textually. | |
| 92 | + The two metrics tracked for every map are its <strong>replication rate</strong> (does it | |
| 93 | + reproduce under resampling of seeds and data?) and its <strong>causal confirmation rate</strong> | |
| 94 | + (what fraction of localized claims survive intervention?).</p> | |
| 95 | + ${R.confidenceLadder()} | |
| 96 | + <p class="note-line">Negative results — techniques whose maps do not replicate — are first-class, | |
| 97 | + publishable findings here.</p> | |
| 98 | + </div> | |
| 99 | + <div class="card"> | |
| 100 | + <h2>Research phases</h2> | |
| 101 | + <ul class="phases">${phaseHtml}</ul> | |
| 102 | + </div> | |
| 103 | +</section> | |
| 104 | + | |
| 105 | +<section class="card"> | |
| 106 | + <h2>Latest from the research log</h2> | |
| 107 | + <div class="log-grid">${logHtml || "<p>No log entries yet.</p>"}</div> | |
| 108 | +</section>`; | |
| 109 | + page(res, { title: "Home", active: "Home", body }); | |
| 110 | +}); | |
| 111 | + | |
| 112 | +// ---------------------------------------------------------------- research index | |
| 113 | +app.get("/research", (req, res) => { | |
| 114 | + const docs = [ | |
| 115 | + ["research/LOG.md", "Research log", "Append-only, auditable record of every question, experiment, result, and decision."], | |
| 116 | + ["research/state_of_the_art.md", "State of the art", "Phase 2 — technique taxonomy with epistemic status (established / contested / debunked-in-part)."], | |
| 117 | + ["research/research_gaps.md", "Research gaps", "Phase 3 — ≥20 falsifiable candidate directions, each with its smallest Mac-runnable falsifying experiment."], | |
| 118 | + ["research/candidate_ranking.md", "Candidate ranking", "Phase 4 — 10-axis scoring and 3–5 selected prototype candidates."], | |
| 119 | + ["research/methodology.md", "Methodology", "Phase 9 — the formal pipeline turning (checkpoint + corpora + budget) into confidence-labeled atlas entries."], | |
| 120 | + ["research/novelty_check.md", "Novelty check", "Phase 11 — final novelty verification (assume not novel until shown otherwise)."], | |
| 121 | + ["research/bibliography.md", "Bibliography", "Every consulted source with URL and access date."], | |
| 122 | + ]; | |
| 123 | + const cards = docs | |
| 124 | + .map(([rel, title, desc]) => { | |
| 125 | + const ok = C.exists(rel); | |
| 126 | + return `<a class="doc-card ${ok ? "" : "disabled"}" href="${ok ? "/doc/" + rel : "#"}"> | |
| 127 | + <h3>${title}</h3><p>${desc}</p>${ok ? "" : '<span class="badge badge-pending">not yet written</span>'}</a>`; | |
| 128 | + }) | |
| 129 | + .join(""); | |
| 130 | + const notes = (C.listDir("research/notes") || []) | |
| 131 | + .filter((f) => f.name.endsWith(".md") && f.name !== "README.md") | |
| 132 | + .map((f) => { | |
| 133 | + const md = C.readMarkdown(f.rel); | |
| 134 | + const doc = md && md.data.document ? md.data.document : f.name; | |
| 135 | + return `<a class="doc-card" href="/doc/${f.rel}"><h3>${R.esc(f.name.replace(".md", "").replace(/_/g, " "))}</h3> | |
| 136 | + <p class="mono-small">${R.esc(doc)}</p></a>`; | |
| 137 | + }) | |
| 138 | + .join(""); | |
| 139 | + const body = `<h1 class="page-title">Research documents</h1> | |
| 140 | +<div class="doc-grid">${cards}</div> | |
| 141 | +<h2 class="section-title">Phase 1 literature notes</h2> | |
| 142 | +<div class="doc-grid">${notes || '<p class="note-line">No literature notes yet — Phase 1 has not started.</p>'}</div> | |
| 143 | +<p class="note-line">The project charter itself is public: <a href="/doc/CLAUDE.md">read the full research charter</a>.</p>`; | |
| 144 | + page(res, { title: "Research", active: "Research", body }); | |
| 145 | +}); | |
| 146 | + | |
| 147 | +// ---------------------------------------------------------------- markdown viewer | |
| 148 | +app.get(/^\/doc\/(.+)$/, (req, res) => { | |
| 149 | + const rel = req.params[0]; | |
| 150 | + const md = rel.endsWith(".md") ? C.readMarkdown(rel) : null; | |
| 151 | + if (!md) return notFound(res); | |
| 152 | + const body = `<article class="doc"> | |
| 153 | + <p class="crumb"><a href="/research">Research</a> / ${R.esc(rel)}</p> | |
| 154 | + ${metaChips(md.data)} | |
| 155 | + <div class="md">${R.markdownToHtml(md.content, rel)}</div> | |
| 156 | + </article>`; | |
| 157 | + page(res, { title: md.data.document || rel, active: "Research", body }); | |
| 158 | +}); | |
| 159 | + | |
| 160 | +// ---------------------------------------------------------------- experiments | |
| 161 | +app.get("/experiments", (req, res) => { | |
| 162 | + const exps = C.listExperiments(); | |
| 163 | + const cards = exps | |
| 164 | + .map( | |
| 165 | + (e) => `<a class="doc-card" href="/experiments/${e.id}"> | |
| 166 | + <h3>${R.esc(e.id)}</h3><p>${R.esc(e.purpose)}</p> | |
| 167 | + <span class="badge badge-${e.status === "completed" ? "done" : e.status === "has results" ? "in-progress" : "pending"}">${e.status}</span> | |
| 168 | + ${e.runs ? `<span class="runs">${e.runs} result run${e.runs > 1 ? "s" : ""}</span>` : ""}</a>` | |
| 169 | + ) | |
| 170 | + .join(""); | |
| 171 | + const body = `<h1 class="page-title">Experiments</h1> | |
| 172 | +<p class="lede-small">Every experiment carries a registered hypothesis with an explicit falsification criterion | |
| 173 | +(seven-field scientific block), mandatory controls and nulls, raw results, and an analysis. Negative results are kept. | |
| 174 | +Micro-experiments A–H establish the noise floor, the correlational→causal survival rate, and the capture cost | |
| 175 | +frontier before any large model is mapped.</p> | |
| 176 | +<div class="doc-grid">${cards}</div>`; | |
| 177 | + page(res, { title: "Experiments", active: "Experiments", body }); | |
| 178 | +}); | |
| 179 | + | |
| 180 | +app.get("/experiments/:id", (req, res) => { | |
| 181 | + const exps = C.listExperiments(); | |
| 182 | + const exp = exps.find((e) => e.id === req.params.id); | |
| 183 | + if (!exp) return notFound(res); | |
| 184 | + const sections = []; | |
| 185 | + for (const [file, title] of [["hypothesis.md", "Hypothesis"], ["analysis.md", "Analysis"], ["README.md", "README"]]) { | |
| 186 | + const md = C.readMarkdown(path.posix.join(exp.rel, file)); | |
| 187 | + if (md && md.content.trim().length > 40) { | |
| 188 | + sections.push(`<section class="card"><h2>${title}</h2>${metaChips(md.data)} | |
| 189 | + <div class="md">${R.markdownToHtml(md.content, path.posix.join(exp.rel, file))}</div></section>`); | |
| 190 | + } | |
| 191 | + } | |
| 192 | + const runs = C.listResultRuns().filter((r) => r.experiment === exp.id); | |
| 193 | + const runsHtml = runs.length | |
| 194 | + ? `<section class="card"><h2>Result runs</h2><ul class="file-list">` + | |
| 195 | + 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("") + | |
| 196 | + `</ul></section>` | |
| 197 | + : ""; | |
| 198 | + const codeLink = C.exists(path.posix.join(exp.rel, "benchmark.py")) | |
| 199 | + ? `<p><a class="more" href="/file/${exp.rel}/benchmark.py">View benchmark implementation (benchmark.py) →</a></p>` | |
| 200 | + : ""; | |
| 201 | + const body = `<p class="crumb"><a href="/experiments">Experiments</a> / ${R.esc(exp.id)}</p> | |
| 202 | +<h1 class="page-title">${R.esc(exp.id)}</h1><p class="lede-small">${R.esc(exp.purpose)}</p> | |
| 203 | +${codeLink}${sections.join("")}${runsHtml}`; | |
| 204 | + page(res, { title: exp.id, active: "Experiments", body }); | |
| 205 | +}); | |
| 206 | + | |
| 207 | +// ---------------------------------------------------------------- atlas | |
| 208 | +app.get("/atlas", (req, res) => { | |
| 209 | + const entries = C.listAtlasEntries(); | |
| 210 | + const byModel = {}; | |
| 211 | + for (const e of entries) (byModel[e.model] ||= []).push(e); | |
| 212 | + const groups = Object.keys(byModel).sort().map((model) => { | |
| 213 | + const cards = byModel[model] | |
| 214 | + .map( | |
| 215 | + (e) => `<a class="doc-card" href="/atlas/${e.model}/${e.mapType}/${e.version}"> | |
| 216 | + <h3>${R.esc(e.mapType)} <span class="mono-small">${R.esc(e.version)}</span></h3> | |
| 217 | + <p>${e.provenance && e.provenance.created ? "created " + R.esc(e.provenance.created) : ""}</p> | |
| 218 | + ${R.levelBadge(e.level)}</a>` | |
| 219 | + ) | |
| 220 | + .join(""); | |
| 221 | + return `<h2 class="section-title">${R.esc(model)}</h2><div class="doc-grid">${cards}</div>`; | |
| 222 | + }).join(""); | |
| 223 | + const empty = `<div class="card"> | |
| 224 | + <h2>No maps published yet</h2> | |
| 225 | + <p>The atlas fills only after the mapping methodology exists (Phase 9) and each map clears its | |
| 226 | + evidence bar. Every entry will carry a completed <code>provenance.json</code> (commit, config, | |
| 227 | + model hash, hardware, dates) and a <code>confidence.md</code> stating its evidence level. | |
| 228 | + Nothing is published that cannot be regenerated from committed code and versioned data.</p> | |
| 229 | + ${R.confidenceLadder()} | |
| 230 | + </div>`; | |
| 231 | + const body = `<h1 class="page-title">Atlas</h1> | |
| 232 | +<p class="lede-small">Versioned map artifacts: <code>atlas/<model>/<map type>/<version></code>. | |
| 233 | +Only open-weight models are mapped, and no claim appears here above its evidence level.</p> | |
| 234 | +${groups || empty}`; | |
| 235 | + page(res, { title: "Atlas", active: "Atlas", body }); | |
| 236 | +}); | |
| 237 | + | |
| 238 | +app.get("/atlas/:model/:mapType/:version", (req, res) => { | |
| 239 | + const { model, mapType, version } = req.params; | |
| 240 | + const entry = C.listAtlasEntries().find( | |
| 241 | + (e) => e.model === model && e.mapType === mapType && e.version === version | |
| 242 | + ); | |
| 243 | + if (!entry) return notFound(res); | |
| 244 | + const confidence = C.readMarkdown(path.posix.join(entry.rel, "confidence.md")); | |
| 245 | + const prov = entry.provenance | |
| 246 | + ? `<pre class="codeblock"><code class="hljs">${R.highlightFile(JSON.stringify(entry.provenance, null, 2), "provenance.json")}</code></pre>` | |
| 247 | + : "<p>provenance.json missing — this entry is not publishable.</p>"; | |
| 248 | + const files = entry.files | |
| 249 | + .map((f) => `<li><a href="/results/${f.rel}">${f.name}</a> <span class="mono-small">${(f.size / 1024).toFixed(1)} KiB</span></li>`) | |
| 250 | + .join(""); | |
| 251 | + const body = `<p class="crumb"><a href="/atlas">Atlas</a> / ${R.esc(entry.rel)}</p> | |
| 252 | +<h1 class="page-title">${R.esc(model)} — ${R.esc(mapType)} <span class="mono-small">${R.esc(version)}</span></h1> | |
| 253 | +<p>${R.levelBadge(entry.level)}</p> | |
| 254 | +${confidence ? `<section class="card"><h2>Confidence</h2><div class="md">${R.markdownToHtml(confidence.content, entry.rel + "/confidence.md")}</div></section>` : ""} | |
| 255 | +<section class="card"><h2>Provenance</h2>${prov}</section> | |
| 256 | +<section class="card"><h2>Files</h2><ul class="file-list">${files}</ul></section>`; | |
| 257 | + page(res, { title: `${model}/${mapType}/${version}`, active: "Atlas", body }); | |
| 258 | +}); | |
| 259 | + | |
| 260 | +// ---------------------------------------------------------------- results | |
| 261 | +app.get("/results", (req, res) => { | |
| 262 | + const runs = C.listResultRuns(); | |
| 263 | + const rows = runs | |
| 264 | + .map( | |
| 265 | + (r) => `<tr><td><a href="/experiments/${r.experiment}">${r.experiment}</a></td> | |
| 266 | + <td class="mono-small">${r.timestamp}</td> | |
| 267 | + <td>${r.files.map((f) => `<a href="/results/${f.rel}">${f.name}</a>`).join(" · ")}</td></tr>` | |
| 268 | + ) | |
| 269 | + .join(""); | |
| 270 | + const body = `<h1 class="page-title">Raw results</h1> | |
| 271 | +<p class="lede-small">Every result is reproducible from commit hash + config + model hash + seed + | |
| 272 | +hardware manifest, and each JSON embeds the manifest of the exact machine that produced it.</p> | |
| 273 | +<table class="results-table"><thead><tr><th>Experiment</th><th>Run (UTC)</th><th>Files</th></tr></thead> | |
| 274 | +<tbody>${rows || '<tr><td colspan="3">No result runs yet.</td></tr>'}</tbody></table>`; | |
| 275 | + page(res, { title: "Results", active: "Results", body }); | |
| 276 | +}); | |
| 277 | + | |
| 278 | +app.get(/^\/results\/(.+)$/, (req, res) => { | |
| 279 | + const raw0 = req.params[0]; | |
| 280 | + const rel = raw0.startsWith("results/") || raw0.startsWith("atlas/") ? raw0 : "results/" + raw0; | |
| 281 | + const raw = C.readText(rel); | |
| 282 | + if (raw === null) return notFound(res); | |
| 283 | + let bodyContent; | |
| 284 | + if (rel.endsWith(".json")) { | |
| 285 | + const obj = C.readJson(rel); | |
| 286 | + bodyContent = `<pre class="codeblock"><code class="hljs">${R.highlightFile(JSON.stringify(obj, null, 2), "x.json")}</code></pre>`; | |
| 287 | + } else { | |
| 288 | + bodyContent = `<pre class="codeblock"><code>${R.esc(raw.slice(0, 200000))}</code></pre>`; | |
| 289 | + } | |
| 290 | + const body = `<p class="crumb"><a href="/results">Results</a> / ${R.esc(rel)}</p><div class="card">${bodyContent}</div>`; | |
| 291 | + page(res, { title: rel, active: "Results", body }); | |
| 292 | +}); | |
| 293 | + | |
| 294 | +// ---------------------------------------------------------------- code browser | |
| 295 | +const CODE_ROOTS = ["src", "tools", "benchmarks", "experiments", "Makefile", "pyproject.toml", "CITATION.cff"]; | |
| 296 | +app.get("/code", (req, res) => { | |
| 297 | + const sections = CODE_ROOTS.map((root) => { | |
| 298 | + if (!C.exists(root)) return ""; | |
| 299 | + const st = C.listDir(root); | |
| 300 | + if (st === null) { | |
| 301 | + return `<li><a href="/file/${root}">${root}</a></li>`; | |
| 302 | + } | |
| 303 | + const files = C.walk(root, (f) => C.isTextFile(f.rel) && !f.rel.includes("results/")); | |
| 304 | + return `<li class="tree-root"><strong>${root}/</strong><ul>` + | |
| 305 | + files.map((f) => `<li><a href="/file/${f.rel}">${f.rel.slice(root.length + 1)}</a></li>`).join("") + | |
| 306 | + `</ul></li>`; | |
| 307 | + }).join(""); | |
| 308 | + const body = `<h1 class="page-title">Code</h1> | |
| 309 | +<p class="lede-small">The mapping library (<code>src/modelmap/</code>), tooling, benchmark harness, and experiment | |
| 310 | +implementations. Every file carries the project's author header; MLX / PyTorch-MPS / Metal is the primary compute | |
| 311 | +path — CUDA is never a core dependency.</p> | |
| 312 | +<ul class="tree">${sections}</ul>`; | |
| 313 | + page(res, { title: "Code", active: "Code", body }); | |
| 314 | +}); | |
| 315 | + | |
| 316 | +app.get(/^\/file\/(.+)$/, (req, res) => { | |
| 317 | + const rel = req.params[0]; | |
| 318 | + if (!C.isTextFile(rel)) return notFound(res); | |
| 319 | + const raw = C.readText(rel); | |
| 320 | + if (raw === null) return notFound(res); | |
| 321 | + const name = rel.split("/").pop(); | |
| 322 | + const body = `<p class="crumb"><a href="/code">Code</a> / ${R.esc(rel)}</p> | |
| 323 | +<div class="card file-card"><div class="file-head"><span class="mono-small">${R.esc(rel)}</span> | |
| 324 | +<span class="mono-small">${raw.split("\n").length} lines</span></div> | |
| 325 | +<pre class="codeblock"><code class="hljs">${R.highlightFile(raw.slice(0, 400000), name)}</code></pre></div>`; | |
| 326 | + page(res, { title: name, active: "Code", body }); | |
| 327 | +}); | |
| 328 | + | |
| 329 | +// ---------------------------------------------------------------- about | |
| 330 | +app.get("/about", (req, res) => { | |
| 331 | + const questions = [ | |
| 332 | + ["Q1 — Localization", "Where inside a pretrained LLM do capabilities, knowledge domains, languages, and behaviors reside? Localized (layers, heads, neurons, weight blocks, directions) or diffuse?"], | |
| 333 | + ["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?"], | |
| 334 | + ["Q3 — Comparability", "Can internal maps be compared across model sizes, checkpoints, quantization levels, and families? Is there a common coordinate system for model internals?"], | |
| 335 | + ["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?"], | |
| 336 | + ["Q5 — Utility", "Do the maps predict anything useful — quantization sensitivity, pruning tolerance, working-set behavior, failure modes, editing targets?"], | |
| 337 | + ]; | |
| 338 | + const qHtml = questions | |
| 339 | + .map(([q, d]) => `<tr><th>${q}</th><td>${d}</td></tr>`) | |
| 340 | + .join(""); | |
| 341 | + const body = `<h1 class="page-title">About this project</h1> | |
| 342 | +<div class="card md"> | |
| 343 | +<p><strong>modelmap</strong> is an independent research project by | |
| 344 | +<strong>Simon-Pierre Boucher</strong> (<a href="mailto:contact@spboucher.ai">contact@spboucher.ai</a>) building a | |
| 345 | +systematic methodology — and the tooling behind it — to discover, measure, and map the internal structure of | |
| 346 | +pretrained open-weight LLMs running locally on consumer Apple Silicon hardware, and publishing the resulting maps | |
| 347 | +as a rigorous, reproducible public atlas on this site.</p> | |
| 348 | +<p>The ideal outcome is not a gallery of suggestive visualizations. It is a <strong>reproducible cartographic | |
| 349 | +standard for open-weight models</strong> — every map versioned, provenanced, confidence-labeled, and regenerable | |
| 350 | +by anyone with a Mac — turning “we think the model does X” into “here is the map, its evidence level, and the | |
| 351 | +script that rebuilds it.”</p> | |
| 352 | +<h2>Core research questions</h2> | |
| 353 | +<table class="kv-table">${qHtml}</table> | |
| 354 | +<h2>The evidence standard</h2> | |
| 355 | +${R.confidenceLadder()} | |
| 356 | +<p>Every mapping technique runs with controls: shuffled labels for probes, random-direction baselines for steering, | |
| 357 | +randomly-initialized-model baselines where meaningful, resample vs zero ablations. Structure is never claimed | |
| 358 | +without showing the null. Replication across ≥3 seeds and ≥2 prompt sets, bootstrap confidence intervals, effect | |
| 359 | +sizes, and multiple-comparison correction are mandatory for every published number.</p> | |
| 360 | +<h2>Target hardware</h2> | |
| 361 | +<p>Apple Silicon Mac (M1–M4 family), 16–64 GB unified memory, internal NVMe SSD, Metal GPU sharing memory with the | |
| 362 | +CPU. Local interpretability on consumer hardware is itself an under-served niche: most tooling assumes CUDA | |
| 363 | +clusters. Making rigorous mapping feasible on a Mac is part of the contribution. The stretch target is a full, | |
| 364 | +causally-verified, versioned atlas of a 7B–14B model produced end-to-end on a single 32–64 GB Mac, comparable | |
| 365 | +across ≥2 quantization levels and ≥2 model sizes.</p> | |
| 366 | +<h2>Sister project</h2> | |
| 367 | +<p><a href="https://www.localvm.dev">localvm-research</a> investigates out-of-core LLM execution on the same | |
| 368 | +hardware class — running models larger than memory. The projects cross-pollinate: modelmap's working-set and | |
| 369 | +localization maps (Q5) feed localvm's execution decisions, and localvm's cost measurements calibrate modelmap's | |
| 370 | +capture-feasibility frontier (Experiment H).</p> | |
| 371 | +<p>Failure is an acceptable outcome — the charter defines explicit failure criteria, and the | |
| 372 | +<a href="/doc/research/LOG.md">log</a> records why an approach died, not just what survived.</p> | |
| 373 | +</div>`; | |
| 374 | + page(res, { title: "About", active: "About", body }); | |
| 375 | +}); | |
| 376 | + | |
| 377 | +// ---------------------------------------------------------------- misc | |
| 378 | +app.get("/health", (req, res) => res.json({ ok: true, app: "modelmap-web", author: "Simon-Pierre Boucher", website: "https://modelmap.io" })); | |
| 379 | + | |
| 380 | +function notFound(res) { | |
| 381 | + res.status(404); | |
| 382 | + 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>` }); | |
| 383 | +} | |
| 384 | +app.use((req, res) => notFound(res)); | |
| 385 | + | |
| 386 | +app.listen(PORT, () => console.log(`modelmap-web listening on :${PORT}`)); | |
added
site/sync-content.sh
+45 −0
@@ -0,0 +1,45 @@ | ||
| 1 | +#!/bin/zsh | |
| 2 | +# ============================================================================= | |
| 3 | +# Project : modelmap | |
| 4 | +# File : site/sync-content.sh | |
| 5 | +# Purpose : Snapshot the research repo into site/content/ + build-info.json | |
| 6 | +# Author : Simon-Pierre Boucher | |
| 7 | +# Contact : contact@spboucher.ai | |
| 8 | +# Website : https://modelmap.io | |
| 9 | +# Created : 2026-08-12 | |
| 10 | +# Modified : 2026-08-12 | |
| 11 | +# Platform : macOS / Apple Silicon (arm64) | |
| 12 | +# License : All rights reserved (research code) | |
| 13 | +# ============================================================================= | |
| 14 | +set -euo pipefail | |
| 15 | + | |
| 16 | +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" | |
| 17 | +CONTENT="$REPO_ROOT/site/content" | |
| 18 | + | |
| 19 | +mkdir -p "$CONTENT" | |
| 20 | +# NOTE: rsync applies filters in order — excludes MUST precede the broad | |
| 21 | +# includes, or large binary artifacts (activation dumps, zarr stores) slip in. | |
| 22 | +rsync -a --delete \ | |
| 23 | + --exclude='*.bin' --exclude='*.log' --exclude='__pycache__' --exclude='.git' \ | |
| 24 | + --exclude='node_modules' --exclude='site' --exclude='*.zarr' --exclude='activations' \ | |
| 25 | + --include='CLAUDE.md' --include='README.md' --include='CITATION.cff' \ | |
| 26 | + --include='Makefile' --include='pyproject.toml' \ | |
| 27 | + --include='research/***' --include='experiments/***' --include='benchmarks/***' \ | |
| 28 | + --include='src/***' --include='tools/***' --include='results/***' \ | |
| 29 | + --include='atlas/***' --include='docs/***' \ | |
| 30 | + --exclude='*' \ | |
| 31 | + "$REPO_ROOT/" "$CONTENT/" | |
| 32 | + | |
| 33 | +COMMIT=$(git -C "$REPO_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown") | |
| 34 | +COMMITS=$(git -C "$REPO_ROOT" rev-list --count HEAD 2>/dev/null || echo 0) | |
| 35 | +cat > "$CONTENT/build-info.json" <<EOF | |
| 36 | +{ | |
| 37 | + "author": "Simon-Pierre Boucher", | |
| 38 | + "contact": "contact@spboucher.ai", | |
| 39 | + "website": "https://modelmap.io", | |
| 40 | + "commit": "$COMMIT", | |
| 41 | + "commits": $COMMITS, | |
| 42 | + "generated": "$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| 43 | +} | |
| 44 | +EOF | |
| 45 | +echo "content synced at commit ${COMMIT:0:7} ($COMMITS commits)" | |
added
src/modelmap/__init__.py
+17 −0
@@ -0,0 +1,17 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/__init__.py | |
| 4 | +# Purpose : modelmap package root — internal cartography of local LLMs | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
| 13 | + | |
| 14 | +__version__ = "0.1.0" | |
| 15 | +__author__ = "Simon-Pierre Boucher" | |
| 16 | +__contact__ = "contact@spboucher.ai" | |
| 17 | +__website__ = "https://modelmap.io" | |
added
src/modelmap/atlas/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/atlas/__init__.py | |
| 4 | +# Purpose : Map schema, serialization, versioning, provenance | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/backends/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/backends/__init__.py | |
| 4 | +# Purpose : Compute backends: MLX / MPS / CPU (CUDA optional, isolated) | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/capture/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/capture/__init__.py | |
| 4 | +# Purpose : Activation hooks, streaming capture, memory-mapped stores | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/features/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/features/__init__.py | |
| 4 | +# Purpose : SAE training/inference and dictionary-learning methods | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/geometry/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/geometry/__init__.py | |
| 4 | +# Purpose : Weight-space analysis: SVD spectra, effective rank, CKA | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/graphs/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/graphs/__init__.py | |
| 4 | +# Purpose : Circuit extraction and attribution graphs | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/instrumentation/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/instrumentation/__init__.py | |
| 4 | +# Purpose : macOS-native cost measurement (task_info, powermetrics) | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/interventions/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/interventions/__init__.py | |
| 4 | +# Purpose : Ablation, activation patching, steering, weight zeroing | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/models/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/models/__init__.py | |
| 4 | +# Purpose : Model loading (safetensors/GGUF) and architecture introspection | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/probes/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/probes/__init__.py | |
| 4 | +# Purpose : Linear probes, logit lens, tuned lens, causal probes | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/stats/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/stats/__init__.py | |
| 4 | +# Purpose : Replication tests, bootstrap CIs, multiple-comparison control | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
src/modelmap/viz/__init__.py
+12 −0
@@ -0,0 +1,12 @@ | ||
| 1 | +# ============================================================================= | |
| 2 | +# Project : modelmap | |
| 3 | +# File : src/modelmap/viz/__init__.py | |
| 4 | +# Purpose : Figure and interactive-map generation | |
| 5 | +# Author : Simon-Pierre Boucher | |
| 6 | +# Contact : contact@spboucher.ai | |
| 7 | +# Website : https://modelmap.io | |
| 8 | +# Created : 2026-08-12 | |
| 9 | +# Modified : 2026-08-12 | |
| 10 | +# Platform : macOS / Apple Silicon (arm64) | |
| 11 | +# License : All rights reserved (research code) | |
| 12 | +# ============================================================================= | |
added
tools/check_headers.py
+106 −0
@@ -0,0 +1,106 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# ============================================================================= | |
| 3 | +# Project : modelmap | |
| 4 | +# File : tools/check_headers.py | |
| 5 | +# Purpose : Fail if any tracked source file lacks the mandatory author header | |
| 6 | +# Author : Simon-Pierre Boucher | |
| 7 | +# Contact : contact@spboucher.ai | |
| 8 | +# Website : https://modelmap.io | |
| 9 | +# Created : 2026-08-12 | |
| 10 | +# Modified : 2026-08-12 | |
| 11 | +# Platform : macOS / Apple Silicon (arm64) | |
| 12 | +# License : All rights reserved (research code) | |
| 13 | +# ============================================================================= | |
| 14 | +"""Header compliance checker (charter §0.1). | |
| 15 | + | |
| 16 | +Scans git-tracked source files and verifies each begins (after an optional | |
| 17 | +shebang) with the standardized author header, or — for markdown — with YAML | |
| 18 | +front matter carrying the author fields. Exits non-zero on any violation, | |
| 19 | +so it can gate commits. | |
| 20 | +""" | |
| 21 | + | |
| 22 | +from __future__ import annotations | |
| 23 | + | |
| 24 | +import subprocess | |
| 25 | +import sys | |
| 26 | +from pathlib import Path | |
| 27 | + | |
| 28 | +ROOT = Path(__file__).resolve().parent.parent | |
| 29 | + | |
| 30 | +HASH_COMMENT_EXT = {".py", ".sh", ".zsh", ".yaml", ".yml", ".toml", ".cff"} | |
| 31 | +SLASH_COMMENT_EXT = {".c", ".cpp", ".h", ".hpp", ".metal", ".swift", ".m", ".js", ".ts", ".css"} | |
| 32 | +MARKDOWN_EXT = {".md"} | |
| 33 | +HASH_COMMENT_NAMES = {"Makefile"} | |
| 34 | + | |
| 35 | +REQUIRED_TOKENS = ( | |
| 36 | + "Project : modelmap", | |
| 37 | + "Author : Simon-Pierre Boucher", | |
| 38 | + "Contact : contact@spboucher.ai", | |
| 39 | +) | |
| 40 | +MD_REQUIRED_TOKENS = ( | |
| 41 | + "project: modelmap", | |
| 42 | + "author: Simon-Pierre Boucher", | |
| 43 | + "contact: contact@spboucher.ai", | |
| 44 | +) | |
| 45 | + | |
| 46 | +# Files that carry no comments / are data or licenses. CLAUDE.md is the | |
| 47 | +# authoritative charter (its content is the spec itself, not a research doc). | |
| 48 | +EXEMPT = {"LICENSE", ".gitignore", "package.json", "package-lock.json", "CLAUDE.md"} | |
| 49 | + | |
| 50 | + | |
| 51 | +def tracked_files() -> list[Path]: | |
| 52 | + out = subprocess.run( | |
| 53 | + ["git", "ls-files"], cwd=ROOT, capture_output=True, text=True, check=True | |
| 54 | + ).stdout | |
| 55 | + return [ROOT / line for line in out.splitlines() if line.strip()] | |
| 56 | + | |
| 57 | + | |
| 58 | +def head_of(path: Path, n_bytes: int = 4096) -> str: | |
| 59 | + try: | |
| 60 | + return path.read_text(errors="replace")[:n_bytes] | |
| 61 | + except OSError: | |
| 62 | + return "" | |
| 63 | + | |
| 64 | + | |
| 65 | +def check(path: Path) -> str | None: | |
| 66 | + """Return an error string, or None if compliant / not applicable.""" | |
| 67 | + name = path.name | |
| 68 | + if name in EXEMPT: | |
| 69 | + return None | |
| 70 | + ext = path.suffix.lower() | |
| 71 | + text = head_of(path) | |
| 72 | + if text.startswith("#!"): | |
| 73 | + text = text.split("\n", 1)[1] if "\n" in text else "" | |
| 74 | + | |
| 75 | + if ext in MARKDOWN_EXT: | |
| 76 | + if not text.lstrip().startswith("---"): | |
| 77 | + return "missing YAML front matter" | |
| 78 | + missing = [t for t in MD_REQUIRED_TOKENS if t not in text] | |
| 79 | + return f"front matter missing: {', '.join(missing)}" if missing else None | |
| 80 | + | |
| 81 | + if ext in HASH_COMMENT_EXT or ext in SLASH_COMMENT_EXT or name in HASH_COMMENT_NAMES: | |
| 82 | + missing = [t for t in REQUIRED_TOKENS if t not in text] | |
| 83 | + return f"header missing: {', '.join(missing)}" if missing else None | |
| 84 | + | |
| 85 | + return None # extension not governed by the header rule | |
| 86 | + | |
| 87 | + | |
| 88 | +def main() -> int: | |
| 89 | + errors: list[tuple[Path, str]] = [] | |
| 90 | + for f in tracked_files(): | |
| 91 | + if not f.is_file(): | |
| 92 | + continue | |
| 93 | + err = check(f) | |
| 94 | + if err: | |
| 95 | + errors.append((f, err)) | |
| 96 | + if errors: | |
| 97 | + print(f"check_headers: {len(errors)} non-compliant file(s):") | |
| 98 | + for f, err in errors: | |
| 99 | + print(f" {f.relative_to(ROOT)}: {err}") | |
| 100 | + return 1 | |
| 101 | + print("check_headers: all tracked source files carry the mandatory header.") | |
| 102 | + return 0 | |
| 103 | + | |
| 104 | + | |
| 105 | +if __name__ == "__main__": | |
| 106 | + sys.exit(main()) | |
added
tools/new_experiment.py
+80 −0
@@ -0,0 +1,80 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# ============================================================================= | |
| 3 | +# Project : modelmap | |
| 4 | +# File : tools/new_experiment.py | |
| 5 | +# Purpose : Scaffold a charter-compliant experiment directory (§10 block) | |
| 6 | +# Author : Simon-Pierre Boucher | |
| 7 | +# Contact : contact@spboucher.ai | |
| 8 | +# Website : https://modelmap.io | |
| 9 | +# Created : 2026-08-12 | |
| 10 | +# Modified : 2026-08-12 | |
| 11 | +# Platform : macOS / Apple Silicon (arm64) | |
| 12 | +# License : All rights reserved (research code) | |
| 13 | +# ============================================================================= | |
| 14 | +"""Scaffold an experiment directory with the seven-field scientific block. | |
| 15 | + | |
| 16 | +Usage: | |
| 17 | + python3 tools/new_experiment.py experiments/micro/expX_name "one-line purpose" | |
| 18 | +""" | |
| 19 | + | |
| 20 | +from __future__ import annotations | |
| 21 | + | |
| 22 | +import datetime | |
| 23 | +import sys | |
| 24 | +from pathlib import Path | |
| 25 | + | |
| 26 | +ROOT = Path(__file__).resolve().parent.parent | |
| 27 | + | |
| 28 | +FRONT = """--- | |
| 29 | +project: modelmap | |
| 30 | +document: {doc} | |
| 31 | +author: Simon-Pierre Boucher | |
| 32 | +contact: contact@spboucher.ai | |
| 33 | +website: https://modelmap.io | |
| 34 | +created: {date} | |
| 35 | +status: draft | |
| 36 | +--- | |
| 37 | +""" | |
| 38 | + | |
| 39 | +BLOCK = """```text | |
| 40 | +Hypothesis : (to be registered before the first run) | |
| 41 | +Falsification criterion : (explicit kill-number, registered in advance) | |
| 42 | +Method : (including controls) | |
| 43 | +Baseline / null : (shuffled labels / random directions / random init) | |
| 44 | +Result : (pending) | |
| 45 | +Interpretation : (pending — with explicit confidence level) | |
| 46 | +Next experiment : (pending) | |
| 47 | +``` | |
| 48 | +""" | |
| 49 | + | |
| 50 | + | |
| 51 | +def main() -> int: | |
| 52 | + if len(sys.argv) < 3: | |
| 53 | + print(__doc__) | |
| 54 | + return 2 | |
| 55 | + rel, purpose = sys.argv[1], sys.argv[2] | |
| 56 | + date = datetime.date.today().isoformat() | |
| 57 | + exp = ROOT / rel | |
| 58 | + name = exp.name | |
| 59 | + if exp.exists(): | |
| 60 | + print(f"error: {rel} already exists") | |
| 61 | + return 1 | |
| 62 | + (exp / "implementation").mkdir(parents=True) | |
| 63 | + (exp / "results").mkdir() | |
| 64 | + (exp / "README.md").write_text( | |
| 65 | + FRONT.format(doc=name, date=date) + f"\n# {name}\n\n{purpose}\n" | |
| 66 | + ) | |
| 67 | + (exp / "hypothesis.md").write_text( | |
| 68 | + FRONT.format(doc=f"{name} — hypothesis", date=date) | |
| 69 | + + f"\n# Hypothesis — {name}\n\n> {purpose}\n\n" + BLOCK | |
| 70 | + ) | |
| 71 | + (exp / "analysis.md").write_text( | |
| 72 | + FRONT.format(doc=f"{name} — analysis", date=date) | |
| 73 | + + f"\n# Analysis — {name}\n\n*(pending — written only after results exist)*\n" | |
| 74 | + ) | |
| 75 | + print(f"scaffolded {rel}") | |
| 76 | + return 0 | |
| 77 | + | |
| 78 | + | |
| 79 | +if __name__ == "__main__": | |
| 80 | + sys.exit(main()) | |
added
tools/new_map.py
+103 −0
@@ -0,0 +1,103 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# ============================================================================= | |
| 3 | +# Project : modelmap | |
| 4 | +# File : tools/new_map.py | |
| 5 | +# Purpose : Scaffold a compliant atlas entry with provenance + confidence | |
| 6 | +# Author : Simon-Pierre Boucher | |
| 7 | +# Contact : contact@spboucher.ai | |
| 8 | +# Website : https://modelmap.io | |
| 9 | +# Created : 2026-08-12 | |
| 10 | +# Modified : 2026-08-12 | |
| 11 | +# Platform : macOS / Apple Silicon (arm64) | |
| 12 | +# License : All rights reserved (research code) | |
| 13 | +# ============================================================================= | |
| 14 | +"""Scaffold an atlas entry: atlas/<model_id>/<map_type>/<version>/. | |
| 15 | + | |
| 16 | +An entry cannot be published without a completed provenance.json and | |
| 17 | +confidence.md (charter §3). This tool creates both as explicit stubs that | |
| 18 | +tools/publish.py refuses to export until completed. | |
| 19 | + | |
| 20 | +Usage: | |
| 21 | + python3 tools/new_map.py <model_id> <map_type> [version] | |
| 22 | +""" | |
| 23 | + | |
| 24 | +from __future__ import annotations | |
| 25 | + | |
| 26 | +import datetime | |
| 27 | +import json | |
| 28 | +import subprocess | |
| 29 | +import sys | |
| 30 | +from pathlib import Path | |
| 31 | + | |
| 32 | +ROOT = Path(__file__).resolve().parent.parent | |
| 33 | + | |
| 34 | + | |
| 35 | +def git_commit() -> str: | |
| 36 | + try: | |
| 37 | + return subprocess.run( | |
| 38 | + ["git", "rev-parse", "HEAD"], cwd=ROOT, capture_output=True, text=True, check=True | |
| 39 | + ).stdout.strip() | |
| 40 | + except subprocess.CalledProcessError: | |
| 41 | + return "unknown" | |
| 42 | + | |
| 43 | + | |
| 44 | +CONFIDENCE_TEMPLATE = """--- | |
| 45 | +project: modelmap | |
| 46 | +document: {model_id}/{map_type}/{version} — confidence | |
| 47 | +author: Simon-Pierre Boucher | |
| 48 | +contact: contact@spboucher.ai | |
| 49 | +website: https://modelmap.io | |
| 50 | +created: {date} | |
| 51 | +status: draft | |
| 52 | +--- | |
| 53 | + | |
| 54 | +# Confidence — {model_id} / {map_type} / {version} | |
| 55 | + | |
| 56 | +```text | |
| 57 | +Level : (0 anecdotal | 1 correlational | 2 method-robust | 3 causal) | |
| 58 | +Seeds : (n) | |
| 59 | +Prompt sets: (n) | |
| 60 | +Methods in agreement : (list) | |
| 61 | +Causal verification : (none | patching | ablation | ...) | |
| 62 | +``` | |
| 63 | + | |
| 64 | +**NOT PUBLISHABLE until this file states an evidence level with its support.** | |
| 65 | +""" | |
| 66 | + | |
| 67 | + | |
| 68 | +def main() -> int: | |
| 69 | + if len(sys.argv) < 3: | |
| 70 | + print(__doc__) | |
| 71 | + return 2 | |
| 72 | + model_id, map_type = sys.argv[1], sys.argv[2] | |
| 73 | + version = sys.argv[3] if len(sys.argv) > 3 else "v0" | |
| 74 | + date = datetime.date.today().isoformat() | |
| 75 | + entry = ROOT / "atlas" / model_id / map_type / version | |
| 76 | + if entry.exists(): | |
| 77 | + print(f"error: {entry.relative_to(ROOT)} already exists") | |
| 78 | + return 1 | |
| 79 | + entry.mkdir(parents=True) | |
| 80 | + provenance = { | |
| 81 | + "author": "Simon-Pierre Boucher", | |
| 82 | + "contact": "contact@spboucher.ai", | |
| 83 | + "website": "https://modelmap.io", | |
| 84 | + "model_id": model_id, | |
| 85 | + "map_type": map_type, | |
| 86 | + "version": version, | |
| 87 | + "commit": git_commit(), | |
| 88 | + "model_hash": "PENDING", | |
| 89 | + "config": "PENDING", | |
| 90 | + "seed": None, | |
| 91 | + "hardware_manifest": "PENDING", | |
| 92 | + "created": date, | |
| 93 | + } | |
| 94 | + (entry / "provenance.json").write_text(json.dumps(provenance, indent=2) + "\n") | |
| 95 | + (entry / "confidence.md").write_text( | |
| 96 | + CONFIDENCE_TEMPLATE.format(model_id=model_id, map_type=map_type, version=version, date=date) | |
| 97 | + ) | |
| 98 | + print(f"scaffolded atlas/{model_id}/{map_type}/{version}") | |
| 99 | + return 0 | |
| 100 | + | |
| 101 | + | |
| 102 | +if __name__ == "__main__": | |
| 103 | + sys.exit(main()) | |
| 104 | ||