Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build validate audit eval site graph track synthesize recap-dry clockbench fair repos hub check help
.PHONY: build validate audit eval site graph track synthesize recap-dry clockbench fair repos hub skill check help

help:
@echo "longevity-loop — an AI-native compounding loop for aging science"
Expand All @@ -11,7 +11,8 @@ help:
@echo " make fair Score open aging datasets for FAIR/reproducibility → docs/FAIR.md (G2)"
@echo " make repos Per-repo KG + agentic-tooling generation recipes → docs/REPOS.md (hub)"
@echo " make hub Validate the JIT deep artifacts under hub/ (add --refresh for staleness)"
@echo " make check validate + audit + eval + clockbench + fair + repos + hub + build drift-gate (CI finish line)"
@echo " make skill Regenerate the /longevity-loop SKILL.md (backbone + hub toolset disclosure)"
@echo " make check validate + audit + eval + clockbench + fair + repos + hub + skill + build drift-gate (CI finish line)"

build:
python3 scripts/build.py
Expand Down Expand Up @@ -65,8 +66,12 @@ repos:
hub:
python3 scripts/hub_gen.py --check

# Regenerate the invocable /longevity-loop skill (backbone tools + hub toolset disclosure).
skill:
python3 scripts/skill_gen.py

# Finish line: data well-formed, still an AI-native loop, executions honestly eval'd (no fake ✅),
# the clock-disagreement + FAIR + repo-recipe + hub artifacts self-verify, and docs match.
# the clock-disagreement + FAIR + repo-recipe + hub + skill artifacts self-verify, and docs match.
check: validate audit eval
python3 scripts/clockbench.py --selftest
python3 scripts/fair.py --selftest
Expand All @@ -76,4 +81,6 @@ check: validate audit eval
python3 scripts/repos.py --check
python3 scripts/hub_gen.py --selftest
python3 scripts/hub_gen.py --check
python3 scripts/skill_gen.py --selftest
python3 scripts/skill_gen.py --check
python3 scripts/build.py --check
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Sequenced bridge: convert the computational finding + your credential network (G

<h2 id="execution">✅ Roadmap Execution</h2>

**13/24 done (54%).** 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).
**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).

- [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
Expand All @@ -183,6 +183,7 @@ Sequenced bridge: convert the computational finding + your credential network (G
- [x] **E22** (P1 · Foundation) — Publish the tracker.json data contract for the interactive portfolio instance (data-first slice)
- [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)

### Eval reports — before → after

Expand Down Expand Up @@ -212,6 +213,7 @@ Sequenced bridge: convert the computational finding + your credential network (G
| ✅ E22 | a stable, derived JSON contract the agentic-portfolio instance can consume (no drift) | no machine-readable mission/progress feed for an external interactive surface | site/tracker.json — mission + 48% progress + 21 milestones + roadmap + low-hanging + frontier + 12 hubs + links, generated by build_site.py, deployed on Pages |
| ✅ 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 |

---

Expand Down
43 changes: 43 additions & 0 deletions SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
name: longevity-loop
description: Drive the longevity-loop (an AI-native compounding loop for aging science). Backbone = the loop's own gated tools (clockbench, FAIR, synthesizer, recap, the make-check gate). Progressive disclosure = per-repo toolsets under hub/ (e.g. compute aging clocks with pyaging), loaded on trigger. Use for aging-biomarker analysis, the Biolearn leaderboard, tracking the aging frontier, or any longevity-loop task.
---

# /longevity-loop

_GENERATED by `scripts/skill_gen.py` from the repo's tools + `hub/` — do not hand-edit; run `make skill`. Paths are relative to the longevity-loop repo root (this file's repo)._

The loop's discipline governs every action: **no evidence ⇒ no claim** · computational results on open data stay separate from any wet-lab claim · **`make check` is the gate** (nothing ships red). Full dev guide: `CLAUDE.md`; what we're shooting: `docs/PROBLEMS.md`.

## Backbone — the loop's own tools (always available)

| Tool | When | Notes |
|---|---|---|
| `make check` | before any commit — the finish-line gate | validate + audit + eval + clockbench + fair + repos + hub + drift |
| `python3 scripts/clockbench.py` | quantify cross-clock disagreement (gaps-analysis G1) | `--demo` or `--input clocks.csv` |
| `python3 scripts/fair.py` | score open aging datasets for FAIR/reproducibility (G2) | → docs/FAIR.md |
| `python3 scripts/synthesize.py` | draft frontier→roadmap proposals (Europe PMC) | → data/_synthesis.md (human-gated) |
| `python3 scripts/recap.py` | daily build-in-public explainer | `--dry-run` offline |
| `make build` | regenerate README/ROADMAP/NULLS/FAIR from data/*.yml | never hand-edit generated docs |

## 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`).

| Toolset | Load when (trigger) | Read | Source @ SHA |
|---|---|---|---|
| `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)

For any repo in **`docs/REPOS.md`** without a `hub/` toolset, generate it on demand (the hub is index + JIT, not a frozen warehouse):

```bash
anyagent reverse <repo-url> --markdown # → an agentic-tooling blueprint
# shape it into hub/<repo>/SKILL.md + a grounded knowledge-graph.json,
# stamp manifest.json with the repo's HEAD SHA (git ls-remote <url> HEAD), then:
make hub # gate integrity + freshness
make skill # this skill re-discovers it automatically
```

> Backbone vs disclosure, the rule (from anyagent): a tool useful on *every* longevity task is backbone; a tool that names one repo/technology is disclosed on demand. Adding a hub toolset never bloats the backbone — it appears in the table above, loaded only on its trigger.
9 changes: 9 additions & 0 deletions data/executions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,12 @@
before: "hub had the recipe layer only; no deep artifact proving the JIT layer works"
after: "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"
proof: "hub/pyaging/* · scripts/hub_gen.py · docs/HUB_ARCHITECTURE.md"
- id: E25
phase: "P1 · Foundation"
item: "Make /longevity-loop an invocable skill that uses every hub toolset (backbone + progressive disclosure)"
done: true
eval:
metric: "an invocable /longevity-loop skill that reaches all per-repo toolsets without bloat"
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"
146 changes: 146 additions & 0 deletions scripts/skill_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#!/usr/bin/env python3
"""skill_gen — generate the invocable `/longevity-loop` SKILL.md.

Answers "make sure /longevity-loop can use all the toolsets made from other repos" with the
right split (mirrors anyagent's own backbone-vs-attachment rule):

• BACKBONE (baked in, always loaded): the loop's OWN gated tools — make check, clockbench,
fair, synthesize, recap, build. Small + relevant on every longevity task.
• PROGRESSIVE DISCLOSURE (loaded on trigger): the per-repo toolsets under hub/<repo>/SKILL.md
(pyaging today, more later). Domain-specific — carried only when the task needs that repo,
or backbone bloat wins.

The disclosure index is DERIVED from hub/*/ (frontmatter + manifest), so a new toolset wires
itself into the skill automatically — no hand-editing. `make check` drift-gates the result.

skill_gen.py # write SKILL.md
skill_gen.py --check # compare (drift gate)
skill_gen.py --selftest # assert hub toolsets are discovered
"""
from __future__ import annotations

import json
import pathlib
import re
import sys

ROOT = pathlib.Path(__file__).resolve().parent.parent
HUB = ROOT / "hub"
OUT = ROOT / "SKILL.md"

# Backbone: the loop's own tools, always available (repo-relative commands).
BACKBONE = [
("`make check`", "before any commit — the finish-line gate", "validate + audit + eval + clockbench + fair + repos + hub + drift"),
("`python3 scripts/clockbench.py`", "quantify cross-clock disagreement (gaps-analysis G1)", "`--demo` or `--input clocks.csv`"),
("`python3 scripts/fair.py`", "score open aging datasets for FAIR/reproducibility (G2)", "→ docs/FAIR.md"),
("`python3 scripts/synthesize.py`", "draft frontier→roadmap proposals (Europe PMC)", "→ data/_synthesis.md (human-gated)"),
("`python3 scripts/recap.py`", "daily build-in-public explainer", "`--dry-run` offline"),
("`make build`", "regenerate README/ROADMAP/NULLS/FAIR from data/*.yml", "never hand-edit generated docs"),
]


def frontmatter(md: str) -> dict:
m = re.match(r"^---\n(.*?)\n---\n", md, re.S)
if not m:
return {}
out = {}
for line in m.group(1).splitlines():
if ":" in line:
k, v = line.split(":", 1)
out[k.strip()] = v.strip()
return out


def toolsets() -> list[dict]:
"""Discover hub/<repo>/ toolsets from their SKILL.md frontmatter + manifest."""
out = []
for man_path in sorted(HUB.glob("*/manifest.json")):
d = man_path.parent
man = json.loads(man_path.read_text())
fm = frontmatter((d / "SKILL.md").read_text()) if (d / "SKILL.md").exists() else {}
out.append({
"repo": d.name, "name": fm.get("name", d.name),
"trigger": fm.get("description", man.get("one_line", "")),
"path": f"hub/{d.name}/SKILL.md", "source": man.get("repo", ""),
"sha": man.get("source_sha", ""),
})
return out


def render() -> str:
ts = toolsets()
L = [
"---",
"name: longevity-loop",
"description: Drive the longevity-loop (an AI-native compounding loop for aging science). "
"Backbone = the loop's own gated tools (clockbench, FAIR, synthesizer, recap, the make-check "
"gate). Progressive disclosure = per-repo toolsets under hub/ (e.g. compute aging clocks with "
"pyaging), loaded on trigger. Use for aging-biomarker analysis, the Biolearn leaderboard, "
"tracking the aging frontier, or any longevity-loop task.",
"---", "",
"# /longevity-loop", "",
"_GENERATED by `scripts/skill_gen.py` from the repo's tools + `hub/` — do not hand-edit; "
"run `make skill`. Paths are relative to the longevity-loop repo root (this file's repo)._", "",
"The loop's discipline governs every action: **no evidence ⇒ no claim** · computational "
"results on open data stay separate from any wet-lab claim · **`make check` is the gate** "
"(nothing ships red). Full dev guide: `CLAUDE.md`; what we're shooting: `docs/PROBLEMS.md`.", "",
"## Backbone — the loop's own tools (always available)", "",
"| Tool | When | Notes |", "|---|---|---|",
]
for tool, when, note in BACKBONE:
L.append(f"| {tool} | {when} | {note} |")
L += ["",
"## Progressive disclosure — per-repo toolsets (load on trigger)", "",
f"**{len(ts)} 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 |", "|---|---|---|---|"]
for t in ts:
L.append(f"| `{t['name']}` | {t['trigger']} | `{t['path']}` | {t['source']} @ `{t['sha']}` |")
L += ["",
"## Reaching a toolset that doesn't exist yet (just-in-time)", "",
"For any repo in **`docs/REPOS.md`** without a `hub/` toolset, generate it on demand (the hub "
"is index + JIT, not a frozen warehouse):", "",
"```bash",
"anyagent reverse <repo-url> --markdown # → an agentic-tooling blueprint",
"# shape it into hub/<repo>/SKILL.md + a grounded knowledge-graph.json,",
"# stamp manifest.json with the repo's HEAD SHA (git ls-remote <url> HEAD), then:",
"make hub # gate integrity + freshness",
"make skill # this skill re-discovers it automatically",
"```", "",
"> Backbone vs disclosure, the rule (from anyagent): a tool useful on *every* longevity task "
"is backbone; a tool that names one repo/technology is disclosed on demand. Adding a hub "
"toolset never bloats the backbone — it appears in the table above, loaded only on its trigger.", ""]
return "\n".join(L)


def selftest() -> int:
ts = toolsets()
assert ts, "expected ≥1 hub toolset (hub/*/SKILL.md)"
assert any(t["repo"] == "pyaging" for t in ts), "pyaging exemplar should be discovered"
for t in ts:
assert t["name"] and t["trigger"] and t["sha"], f"toolset {t['repo']} missing fields"
print(f"skill_gen selftest: OK — {len(ts)} hub toolset(s) discovered "
f"({', '.join(t['repo'] for t in ts)}).")
return 0


def main() -> int:
if "--selftest" in sys.argv:
return selftest()
text = render()
if "--check" in sys.argv:
current = OUT.read_text() if OUT.exists() else ""
if current != text:
print("Out of date: SKILL.md. Run `make skill`.", file=sys.stderr)
return 1
print("SKILL.md is up to date.")
return 0
OUT.write_text(text)
print(f"Wrote {OUT.relative_to(ROOT)} — {len(toolsets())} toolset(s) disclosed.")
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading