diff --git a/README.md b/README.md
index 3b01831..267c16c 100644
--- a/README.md
+++ b/README.md
@@ -157,7 +157,7 @@ Sequenced bridge: convert the computational finding + your credential network (G
✅ Roadmap Execution
-**14/25 done (56%).** Each execution is a checkbox with a before→after eval; a box only ticks with a real result (`done` requires a non-pending before AND after — no evidence ⇒ not done, gated in CI).
+**15/26 done (58%).** Each execution is a checkbox with a before→after eval; a box only ticks with a real result (`done` requires a non-pending before AND after — no evidence ⇒ not done, gated in CI).
- [x] **E1** (P1 · Foundation) — Stand up the AI-native loop (repo + self-audit + gated `make check`)
- [x] **E2** (P1 · Foundation) — Map the field into a verified knowledge base
@@ -184,6 +184,7 @@ Sequenced bridge: convert the computational finding + your credential network (G
- [x] **E23** (P1 · Foundation) — Hub architecture (knowledge·tooling·experts per cited repo) + the per-repo generation-recipe layer
- [x] **E24** (P1 · Foundation) — Hub Phase-2: JIT deep artifacts for one repo (pyaging) — knowledge graph + gated skill, SHA-stamped
- [x] **E25** (P1 · Foundation) — Make /longevity-loop an invocable skill that uses every hub toolset (backbone + progressive disclosure)
+- [x] **E26** (P1 · Foundation) — Fan the hub out to Biolearn (North-Star toolset) — auto-wires into /longevity-loop
### Eval reports — before → after
@@ -214,6 +215,7 @@ Sequenced bridge: convert the computational finding + your credential network (G
| ✅ E23 | a fresh, cheap, future-proof way to offer a KG + agentic tooling for each cited repo | cited repos listed in stack.yml but no KG/tooling per repo; no design for how to do it well | docs/HUB_ARCHITECTURE.md (index+JIT design; reuse graphify/understand/reverse; MCP; SHA-cached) + scripts/repos.py → docs/REPOS.md (22 repos, per-repo KG + reverse recipes), gated in make check |
| ✅ E24 | a real, grounded, provenance-stamped KG + skill generated from a cited repo, with a freshness gate | hub had the recipe layer only; no deep artifact proving the JIT layer works | hub/pyaging/ — knowledge-graph.json (20 nodes/edges) + compute-aging-clocks SKILL.md + manifest (SHA c2b3000e3c2c); scripts/hub_gen.py gates integrity (make check) + checks live staleness (FRESH); grounded in pyaging's real API |
| ✅ E25 | an invocable /longevity-loop skill that reaches all per-repo toolsets without bloat | hub toolsets existed (hub/*/SKILL.md) but nothing exposed them through one invocable skill | SKILL.md generated by scripts/skill_gen.py — backbone = the loop's own tools; progressive disclosure = hub toolsets (auto-discovered from hub/, 1 today: compute-aging-clocks) + a JIT recipe; drift-gated in make check; installed at ~/.claude/skills/longevity-loop |
+| ✅ E26 | the North-Star repo has a grounded KG + gated skill, auto-disclosed by /longevity-loop | hub had 1 exemplar (pyaging); Biolearn (the leaderboard/loader for E6/E7) had no toolset | hub/biolearn/ — knowledge-graph.json (19 nodes/edges) + biolearn-leaderboard SKILL.md (real API, ties to E6/E7) + manifest (SHA 0d714f5a0c0a, FRESH); skill_gen auto-discovered it → SKILL.md now discloses 2 toolsets, no hand-editing |
---
diff --git a/SKILL.md b/SKILL.md
index 51f4fba..6a33ba1 100644
--- a/SKILL.md
+++ b/SKILL.md
@@ -22,10 +22,11 @@ The loop's discipline governs every action: **no evidence ⇒ no claim** · comp
## Progressive disclosure — per-repo toolsets (load on trigger)
-**1 toolset(s)** generated from cited repos (the hub — see `docs/HUB_ARCHITECTURE.md`). Load a toolset's `SKILL.md` **only when its trigger matches** — don't carry them all. Each is grounded in its source repo at a pinned commit SHA (freshness: `make hub` / `python3 scripts/hub_gen.py --refresh`).
+**2 toolset(s)** generated from cited repos (the hub — see `docs/HUB_ARCHITECTURE.md`). Load a toolset's `SKILL.md` **only when its trigger matches** — don't carry them all. Each is grounded in its source repo at a pinned commit SHA (freshness: `make hub` / `python3 scripts/hub_gen.py --refresh`).
| Toolset | Load when (trigger) | Read | Source @ SHA |
|---|---|---|---|
+| `biolearn-leaderboard` | Load the Biomarkers-of-Aging Challenge data, compute a panel of aging clocks, and build a leaderboard submission with Biolearn. Trigger for running Turn 01 on real data (E6), the first leaderboard submission (E7), or any standardized aging-biomarker load/score task. | `hub/biolearn/SKILL.md` | bio-learn/biolearn @ `0d714f5a0c0a` |
| `compute-aging-clocks` | Compute a panel of biological-age (epigenetic) clocks from methylation/omics data with pyaging, then hand the panel to clockbench to quantify cross-clock disagreement. Trigger when you need biological-age estimates, a clock panel, or the disagreement signal for the Biomarkers-of-Aging / Turn-01 work. | `hub/pyaging/SKILL.md` | rsinghlab/pyaging @ `c2b3000e3c2c` |
## Reaching a toolset that doesn't exist yet (just-in-time)
diff --git a/data/executions.yml b/data/executions.yml
index eabb29e..4d3a3a4 100644
--- a/data/executions.yml
+++ b/data/executions.yml
@@ -186,3 +186,12 @@
before: "hub toolsets existed (hub/*/SKILL.md) but nothing exposed them through one invocable skill"
after: "SKILL.md generated by scripts/skill_gen.py — backbone = the loop's own tools; progressive disclosure = hub toolsets (auto-discovered from hub/, 1 today: compute-aging-clocks) + a JIT recipe; drift-gated in make check; installed at ~/.claude/skills/longevity-loop"
proof: "SKILL.md · scripts/skill_gen.py · make check"
+- id: E26
+ phase: "P1 · Foundation"
+ item: "Fan the hub out to Biolearn (North-Star toolset) — auto-wires into /longevity-loop"
+ done: true
+ eval:
+ metric: "the North-Star repo has a grounded KG + gated skill, auto-disclosed by /longevity-loop"
+ before: "hub had 1 exemplar (pyaging); Biolearn (the leaderboard/loader for E6/E7) had no toolset"
+ after: "hub/biolearn/ — knowledge-graph.json (19 nodes/edges) + biolearn-leaderboard SKILL.md (real API, ties to E6/E7) + manifest (SHA 0d714f5a0c0a, FRESH); skill_gen auto-discovered it → SKILL.md now discloses 2 toolsets, no hand-editing"
+ proof: "hub/biolearn/* · SKILL.md (2 toolsets) · make check"
diff --git a/hub/biolearn/SKILL.md b/hub/biolearn/SKILL.md
new file mode 100644
index 0000000..62dae59
--- /dev/null
+++ b/hub/biolearn/SKILL.md
@@ -0,0 +1,59 @@
+---
+name: biolearn-leaderboard
+description: Load the Biomarkers-of-Aging Challenge data, compute a panel of aging clocks, and build a leaderboard submission with Biolearn. Trigger for running Turn 01 on real data (E6), the first leaderboard submission (E7), or any standardized aging-biomarker load/score task.
+source: bio-learn/biolearn @ 0d714f5a0c0a
+generated_by: longevity-loop hub (scripts/hub_gen.py) — grounded in bio-learn.github.io docs, not invented
+---
+
+# biolearn-leaderboard (via Biolearn)
+
+Generated by the longevity-loop **hub** (docs/HUB_ARCHITECTURE.md) as a Phase-2 deep toolset.
+Biolearn is the **North Star** — the standardized platform the Biomarkers-of-Aging Challenge
+leaderboard runs on — so this toolset is the direct path to the loop's low-hanging fruit
+(**E6** run Turn 01, **E7** first submission). Grounded at the pinned `source` SHA; regenerate
+when it changes (`scripts/hub_gen.py --refresh`).
+
+## When to use
+You need to load the challenge (or GEO/NHANES/Framingham) data in a standardized form, compute
+aging clocks, or assemble a leaderboard submission — i.e. to run Turn 01 for real or to submit.
+
+## Install
+```bash
+pip install biolearn
+```
+
+## The documented workflow (Biolearn's real API)
+```python
+from biolearn.data_library import DataLibrary
+from biolearn.model_gallery import ModelGallery
+
+# 1. Load a dataset (standardized) -> GeoData with .dnam (methylation) and .metadata (age, outcomes).
+data = DataLibrary().get("").load() # exact id: see the "Exploring the Challenge Data" example
+# data.dnam # methylation matrix (samples x CpGs)
+# data.metadata # age, sex, outcomes, ...
+
+# 2. Compute a clock (or a panel) from the Model Gallery.
+gallery = ModelGallery()
+result = gallery.get("Horvathv1").predict(data) # also PhenoAge, GrimAge, DunedinPACE, ...
+# result holds the predicted biological age per sample.
+```
+
+## Two examples that ARE E6 and E7 (use them directly)
+Biolearn ships these — start from them rather than writing from scratch:
+- **"Exploring the Challenge Data"** (`auto_examples/02_challenge_submissions/`) → the exact loader
+ for Turn 01 / E6.
+- **"Building a competition submission using an existing model"** → the E7 submission path.
+
+## Hand off to the loop (why this toolset exists)
+Compute a **panel** of clocks, export the per-clock predicted ages to a CSV (rows = samples,
+header = clock names), then measure cross-clock disagreement — the measurable core of G1:
+```bash
+python3 scripts/clockbench.py --input clocks.csv
+```
+That is the full E6 chain: Biolearn (load + score the panel) → clockbench (disagreement) →
+fill `turns/turn-01-biolearn-baseline/PROOF.md` → submit (E7).
+
+## Discipline
+Predicted ages / ranks are **computational results on public data** — label them as such, report
+the null if CIs overlap, and keep them separate from any wet-lab claim. Verify exact dataset IDs
+and clock names against the current Biolearn examples (they evolve); `no evidence ⇒ no claim`.
diff --git a/hub/biolearn/knowledge-graph.json b/hub/biolearn/knowledge-graph.json
new file mode 100644
index 0000000..bd30205
--- /dev/null
+++ b/hub/biolearn/knowledge-graph.json
@@ -0,0 +1,52 @@
+{
+ "schema": "longevity-loop hub knowledge graph — portable nodes/edges JSON",
+ "source": {
+ "repo": "bio-learn/biolearn",
+ "url": "https://github.com/bio-learn/biolearn",
+ "sha": "0d714f5a0c0a",
+ "grounded_in": "bio-learn.github.io quickstart + auto_examples index (documented API + real example titles)"
+ },
+ "nodes": [
+ {"id": "repo:biolearn", "label": "biolearn", "type": "repo", "note": "standardized aging-biomarker platform; powers the Biomarkers-of-Aging Challenge"},
+ {"id": "mod:data_library", "label": "biolearn.data_library", "type": "module"},
+ {"id": "mod:model_gallery", "label": "biolearn.model_gallery", "type": "module"},
+ {"id": "cls:DataLibrary", "label": "DataLibrary", "type": "class", "note": ".get(id).load() -> GeoData"},
+ {"id": "cls:GeoData", "label": "GeoData", "type": "class", "note": "attributes: .dnam (methylation matrix), .metadata (age, outcomes)"},
+ {"id": "cls:ModelGallery", "label": "ModelGallery", "type": "class", "note": ".get(clock).predict(dnam)"},
+ {"id": "fn:load", "label": "DataLibrary.get().load()", "type": "function"},
+ {"id": "fn:predict", "label": "ModelGallery.get().predict()", "type": "function"},
+ {"id": "concept:clock", "label": "aging clock", "type": "concept"},
+ {"id": "clock:Horvath", "label": "Horvath", "type": "clock"},
+ {"id": "clock:DunedinPACE", "label": "DunedinPACE", "type": "clock"},
+ {"id": "data:geo", "label": "GEO", "type": "data-source"},
+ {"id": "data:nhanes", "label": "NHANES", "type": "data-source"},
+ {"id": "data:framingham", "label": "Framingham", "type": "data-source"},
+ {"id": "ex:challenge", "label": "example: Exploring the Challenge Data", "type": "example", "note": "auto_examples/02_challenge_submissions/"},
+ {"id": "ex:submission", "label": "example: Building a competition submission", "type": "example"},
+ {"id": "ext:leaderboard", "label": "Biomarkers of Aging Challenge (leaderboard)", "type": "downstream"},
+ {"id": "ext:turn01", "label": "longevity-loop Turn 01 (E6)", "type": "downstream"},
+ {"id": "ext:clockbench", "label": "longevity-loop clockbench (G1)", "type": "downstream"}
+ ],
+ "edges": [
+ {"source": "repo:biolearn", "target": "mod:data_library", "rel": "contains"},
+ {"source": "repo:biolearn", "target": "mod:model_gallery", "rel": "contains"},
+ {"source": "mod:data_library", "target": "cls:DataLibrary", "rel": "defines"},
+ {"source": "mod:data_library", "target": "cls:GeoData", "rel": "defines"},
+ {"source": "mod:model_gallery", "target": "cls:ModelGallery", "rel": "defines"},
+ {"source": "cls:DataLibrary", "target": "fn:load", "rel": "has"},
+ {"source": "fn:load", "target": "cls:GeoData", "rel": "produces"},
+ {"source": "cls:ModelGallery", "target": "fn:predict", "rel": "has"},
+ {"source": "fn:predict", "target": "concept:clock", "rel": "runs"},
+ {"source": "clock:Horvath", "target": "concept:clock", "rel": "is-a"},
+ {"source": "clock:DunedinPACE", "target": "concept:clock", "rel": "is-a"},
+ {"source": "cls:DataLibrary", "target": "data:geo", "rel": "loads-from"},
+ {"source": "cls:DataLibrary", "target": "data:nhanes", "rel": "loads-from"},
+ {"source": "cls:DataLibrary", "target": "data:framingham", "rel": "loads-from"},
+ {"source": "ex:challenge", "target": "ext:turn01", "rel": "enables"},
+ {"source": "ex:submission", "target": "ext:leaderboard", "rel": "enables"},
+ {"source": "repo:biolearn", "target": "ext:leaderboard", "rel": "powers"},
+ {"source": "fn:predict", "target": "ext:clockbench", "rel": "feeds"},
+ {"source": "cls:GeoData", "target": "ext:turn01", "rel": "consumed-by"}
+ ],
+ "counts": {"nodes": 19, "edges": 19}
+}
diff --git a/hub/biolearn/manifest.json b/hub/biolearn/manifest.json
new file mode 100644
index 0000000..0727170
--- /dev/null
+++ b/hub/biolearn/manifest.json
@@ -0,0 +1,15 @@
+{
+ "repo": "bio-learn/biolearn",
+ "url": "https://github.com/bio-learn/biolearn",
+ "source_sha": "0d714f5a0c0a",
+ "generated_at": "2026-07-19",
+ "license_note": "not verified this run — confirm at the repo before redistribution",
+ "one_line": "Open, standardized platform for aging biomarkers — dataset loaders + a model gallery of clocks; the Biomarkers-of-Aging Challenge leaderboard runs on it.",
+ "artifacts": ["knowledge-graph.json", "SKILL.md"],
+ "engines": {
+ "knowledge_graph": "grounded from bio-learn.github.io quickstart + example index (production: graphify / understand-anything)",
+ "skill": "grounded from Biolearn's documented API + example titles (production: anyagent reverse -> refine)"
+ },
+ "provenance_note": "Grounded in bio-learn.github.io docs + example titles at source_sha; exact dataset IDs / args are deferred to the cited example files rather than invented (no evidence => no claim). Regenerate when source_sha changes (scripts/hub_gen.py --refresh).",
+ "why": "Biolearn IS the North Star — the leaderboard + the challenge-data/clock loader that E6 (run Turn 01) and E7 (first leaderboard submission) directly depend on. Highest-value toolset in the hub."
+}