{"repo":"NousResearch/hermes-agent","public_id":"336","display_label":"Darwinian Evolver evolutionary optimization skill proposals","summary":"Multiple submissions proposing a 'Darwinian Evolver' skill that applies evolutionary algorithms to code and prompt optimization. The members appear to be overlapping or duplicate feature requests and pull requests for the same concept.","subsystem":"skills","counts":{"members":4,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":2},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":26760,"merged_at":"2026-05-16T04:56:07+00:00"},"resolution":{"state":"resolved","by":"pr-26760"},"freshness":{"state":"verified","last_verified_at":"2026-05-16T04:56:07+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.5},"fix_edges":[{"issue_number":336,"pr_number":26760,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-05-16T04:56:07+00:00"}],"members":[{"kind":"issue","number":336,"title":"Feature: Darwinian Evolver Skill — Evolutionary Code & Prompt Optimization","url":null,"state":"closed"},{"kind":"pr","number":339,"title":"feat: add Darwinian Evolver skill for evolutionary optimization","url":null,"state":"closed","close_reason":{"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":12719,"title":"feat(skill): darwinian-evolver v1.0 — evolutionary optimizer platform","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#26760","number":26760},{"kind":"pr","value":"#336","number":336}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":26760,"title":"feat(skill): darwinian-evolver optional skill","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["optional-skills/research/darwinian-evolver/SKILL.md","optional-skills/research/darwinian-evolver/scripts/parrot_openrouter.py","optional-skills/research/darwinian-evolver/scripts/show_snapshot.py","optional-skills/research/darwinian-evolver/templates/custom_problem_template.py","scripts/release.py","tests/skills/test_darwinian_evolver_skill.py","website/docs/reference/optional-skills-catalog.md","website/docs/user-guide/skills/optional/research/research-darwinian-evolver.md","website/sidebars.ts"],"fix_hunks":[{"file":"optional-skills/research/darwinian-evolver/SKILL.md","hunks":["@@ -0,0 +1,199 @@\n+---\n+name: darwinian-evolver\n+description: Evolve prompts/regex/SQL/code with Imbue's evolution loop.\n+version: 0.1.0\n+author: Bihruze (Asahi0x), Hermes Agent\n+license: MIT\n+platforms: [linux, macos]\n+metadata:\n+  hermes:\n+    tags: [evolution, optimization, prompt-engineering, research]\n+    related_skills: [arxiv, jupyter-live-kernel]\n+---\n+\n+# Darwinian Evolver\n+\n+Run Imbue's [darwinian_evolver](https://github.com/imbue-ai/darwinian_evolver) — an\n+LLM-driven evolutionary search loop — to optimize a **prompt, regex, SQL query,\n+or small code snippet** against a fitness function.\n+\n+Status: thin wrapper around the upstream tool. The skill installs it, walks the\n+agent through writing a `Problem` definition (organism + evaluator + mutator),\n+and drives the loop via the upstream CLI or a small custom Python driver.\n+\n+**License:** the upstream tool is **AGPL-3.0**. The skill ONLY ever invokes it\n+via the upstream CLI or a `subprocess`/`uv run` call (mere aggregation). Do NOT\n+import upstream classes into Hermes itself.\n+\n+## When to Use\n+\n+- User says \"optimize this prompt\", \"evolve a regex for X\", \"auto-improve this\n+  code/SQL\", \"search for a better instruction\".\n+- You have a scorer (exact match, regex pass-rate, unit test, LLM-judge, runtime\n+  metric) AND a starting candidate (organism). If you don't have a scorer, stop\n+  and define one first — that's the hard part.\n+- Cost is OK: a typical run is 50–500 LLM calls. On gpt-4o-mini that's pennies;\n+  on Claude Sonnet it can be a few dollars.\n+\n+Do **not** use this when:\n+- The optimization target is differentiable (use gradient descent / DSPy).\n+- You only need to try 2–3 variants — just write them by hand.\n+- The fitness signal is purely subjective with no measurable criterion.\n+\n+## Prerequisites\n+\n+- Python ≥3.11\n+- `git`, `uv` (or `pip`)\n+- One of: `OPENROUTER_API_KEY`, `ANTHROPIC_API_KEY`, or `OPENAI_API_KEY`\n+\n+The skill ships a small `parrot_openrouter.py` driver that uses `OPENROUTER_API_KEY`\n+via the OpenAI SDK, so any model on OpenRouter works. The upstream CLI itself\n+hardcodes Anthropic and needs `ANTHROPIC_API_KEY`.\n+\n+## Install (One-Time)\n+\n+Run via the `terminal` tool:\n+\n+```bash\n+mkdir -p ~/.hermes/cache/darwinian-evolver && cd ~/.hermes/cache/darwinian-evolver\n+[ -d darwinian_evolver ] || git clone --depth 1 https://github.com/imbue-ai/darwinian_evolver.git\n+cd darwinian_evolver && uv sync\n+```\n+\n+Verify:\n+\n+```bash\n+cd ~/.hermes/cache/darwinian-evolver/darwinian_evolver \\\n+  && uv run darwinian_evolver --help | head -5\n+```\n+\n+## Quick Start — The Built-In Parrot Example\n+\n+Tiny smoke test (requires `ANTHROPIC_API_KEY`):\n+\n+```bash\n+cd ~/.hermes/cache/darwinian-evolver/darwinian_evolver\n+uv run darwinian_evolver parrot \\\n+  --num_iterations 2 \\\n+  --num_parents_per_iteration 2 \\\n+  --mutator_concurrency 2 --evaluator_concurrency 2 \\\n+  --output_dir /tmp/parrot_demo\n+```\n+\n+Outputs:\n+- `/tmp/parrot_demo/snapshots/iteration_N.pkl` — pickled population per iteration\n+- `/tmp/parrot_demo/<jsonl>` — per-iteration JSON log (path printed at end)\n+\n+Open `~/.hermes/cache/darwinian-evolver/darwinian_evolver/darwinian_evolver/lineage_visualizer.html`\n+in a browser and load the JSON log to see the evolutionary tree.\n+\n+## Quick Start — OpenRouter Driver (No Anthropic Key)\n+\n+The skill ships `scripts/parrot_openrouter.py` — same parrot problem, but the\n+LLM call goes through OpenRouter so any provider works.\n+\n+```bash\n+# From wherever the skill is installed:\n+SKILL_DIR=~/.hermes/skills/research/darwinian-evolver\n+DE_DIR=~/.hermes/cache/darwinian-evolver/darwinian_evolver\n+\n+cd \"$DE_DIR\" && \\\n+  EVOLVER_MODEL='openai/gpt-4o-mini' \\\n+  uv run --with openai python \"$SKILL_DIR/scripts/parrot_openrouter.py\" \\\n+    --num_iterations 3 --num_parents_per_iteration 2 \\\n+    --output_dir /tmp/parrot_or\n+```\n+\n+Inspect the result with `scripts/show_snapshot.py`:\n+\n+```bash\n+uv run --with openai python \"$SKILL_DIR/scripts/show_snapshot.py\" \\\n+  /tmp/parrot_or/snapshots/iteration_3.pkl\n+```\n+\n+Expected output: 7 evolved prompt templates ranked by score, with the best\n+landing around 0.6–0.8 (the seed `Say {{ phrase }}` scored 0.000).\n+\n+## Defining a Custom Problem\n+\n+The skill ships `templates/custom_problem_template.py` — copy, edit, run.\n+Three things you must define:\n+\n+1. **`Organism`** — a Pydantic `BaseModel` subclass holding the artifact being\n+   evolved (`prompt_template: str`, `regex_pattern: str`, `sql_query: str`,\n+   `code_block: str`, etc.). Add a `run(*args)` method that exercises it.\n+\n+2. **`Evaluator`** — `.evaluate(organism) -> EvaluationResult(score=..., trainable_failure_cases=[...], holdout_failure_cases=[...], is_viable=True)`.\n+   - **`score`** is in `[0, 1]`. Higher is better.\n+   - **`trainable_failure_cases`** — what the mutator sees. Include enough\n+     context (input, expected, actual) for the LLM to diagnose.\n+   - **`holdout_failure_cases`** — kept out of the mutator's view. Use these\n+     to detect overfitting.\n+   - **`is_viable=True`** unless the organism is completely broken (raises,\n+     returns None, etc.). A 0-score viable organism is fine — it just gets\n+     down-weighted in parent selection.\n+\n+3. **`Mutator`** — `.mutate(organism, failure_cases, learning_log_entries) -> list[Organism]`.\n+   Typically: build an LLM prompt that includes the current organism + a\n+   failure case + an ask to propose a fix; parse the LLM's response; return\n+   a new `Organism`. Return `[]` on parse failure — the loop handles it.\n+\n+Then write a driver script that wires `Problem(initial_organism, evaluator, [mutators])`\n+into `EvolveProblemLoop` and iterates over `loop.run(num_iterations=N)` — the\n+shipped `scripts/parrot_openrouter.py` is the reference.\n+\n+## Hyperparameters That Actually Matter\n+\n+| flag | default | when to change |\n+|---|---|---|\n+| `--num_iterations` | 5 | bump to 10–20 once you trust the evaluator |\n+| `--num_parents_per_iteration` | 4 | drop to 2 for cheap exploration |\n+| `--mutator_concurrency` | 10 | drop to 2–4 to avoid rate limits |\n+| `--evaluator_concurrency` | 10 | same; evaluator hits the LLM too |\n+| `--batch_size` | 1 | raise to 3–5 once your mutator handles multiple failures |\n+| `--verify_mutations` | off | turn on once mutator is wasteful (>10× cost saving on later runs per Imbue) |\n+| `--midpoint_score` | `p75` | leave alone unless scores cluster |\n+| `--sharpness` | 10 | leave alone |\n+\n+## Pitfalls\n+\n+1. **`Initial organism must be viable`** — set `is_viable=True` in your\n+   `EvaluationResult` even on a 0-score seed. The loop refuses non-viable\n+   organisms because they imply the loop has nothing to evolve from.\n+2. **Provider content filters kill runs.** Azure-backed OpenRouter models\n+   reject phrases like \"ignore previous instructions\" with HTTP 400. Wrap\n+   the LLM call in `try/except` and return `f\"<LLM_ERROR: {e}>\"` — the\n+   evolver will just score that organism 0 and move on.\n+3. **`loop.run()` is a generator** — calling it doesn't run anything until\n+   you iterate. Use `for snap in loop.run(num_iterations=N):`.\n+4. **Snapshots are nested pickles.** `iteration_N.pkl` contains a dict with\n+   `population_snapshot` (more pickled bytes). To unpickle you must have the\n+   `Organism` class importable under the same dotted path it was pickled at.\n+5. **Concurrency defaults are aggressive.** 10/10 will hit rate limits on\n+   most providers. Start with 2/2.\n+6. **CLI is hardcoded to Anthropic.** `uv run darwinian_evolver <problem>`\n+   reaches for `ANTHROPIC_API_KEY` and uses Claude Sonnet. To use any other\n+   provider, write a driver like `parrot_openrouter.py`.\n+7. **AGPL.** Never `from darwinian_evolver import ...` inside Hermes core.\n+   Custom driver scripts under `~/.hermes/skills/...` are user-side and fine.\n+8. **No PyPI package.** `pip install darwinian-evolver` will pull the wrong\n+   thing. Always install from the GitHub repo.\n+\n+## Verification\n+\n+After install + a parrot run, exit code 0 from this is sufficient:\n+\n+```bash\n+DE_DIR=~/.hermes/cache/darwinian-evolver/darwinian_evolver\n+ls \"$DE_DIR/darwinian_evolver/lineage_visualizer.html\" >/dev/null && \\\n+cd \"$DE_DIR\" && uv run darwinian_evolver --help >/dev/null && \\\n+echo \"darwinian-evolver: OK\"\n+```\n+\n+## References\n+\n+- [Imbue research post](https://imbue.com/research/2026-02-27-darwinian-evolver/)\n+- [ARC-AGI-2 results](https://imbue.com/research/2026-02-27-arc-agi-2-evolution/)\n+- [imbue-ai/darwinian_evolver](https://github.com/imbue-ai/darwinian_evolver) (AGPL-3.0)\n+- [Darwin Gödel Machines](https://arxiv.org/abs/2505.22954)\n+- [PromptBreeder](https://arxiv.org/abs/2309.16797)"]},{"file":"optional-skills/research/darwinian-evolver/scripts/parrot_openrouter.py","hunks":["@@ -0,0 +1,218 @@\n+\"\"\"\n+parrot_openrouter: same as the upstream `parrot` example but the LLM call goes\n+through OpenRouter (OpenAI SDK) instead of Anthropic native. Lets us run an\n+end-to-end evolution with whatever model the user already has paid access to.\n+\n+Run with:\n+    uv --project darwinian_evolver run python parrot_openrouter.py \\\n+        --num_iterations 3 --output_dir /tmp/parrot_out\n+\n+Reads `OPENROUTER_API_KEY` from the environment.\n+\"\"\"\n+from __future__ import annotations\n+\n+import argparse\n+import os\n+import sys\n+from pathlib import Path\n+\n+import jinja2\n+from openai import OpenAI\n+\n+# Vendored problem types from upstream (AGPL — only run via subprocess in production)\n+from darwinian_evolver.cli_common import build_hyperparameter_config_from_args\n+from darwinian_evolver.cli_common import register_hyperparameter_args\n+from darwinian_evolver.cli_common import parse_learning_log_view_type\n+from darwinian_evolver.evolve_problem_loop import EvolveProblemLoop\n+from darwinian_evolver.learning_log import LearningLogEntry\n+from darwinian_evolver.problem import EvaluationFailureCase\n+from darwinian_evolver.problem import EvaluationResult\n+from darwinian_evolver.problem import Evaluator\n+from darwinian_evolver.problem import Mutator\n+from darwinian_evolver.problem import Organism\n+from darwinian_evolver.problem import Problem\n+\n+DEFAULT_MODEL = os.environ.get(\"EVOLVER_MODEL\", \"openai/gpt-4o-mini\")\n+\n+\n+def _client() -> OpenAI:\n+    key = os.environ.get(\"OPENROUTER_API_KEY\")\n+    if not key:\n+        sys.exit(\"OPENROUTER_API_KEY is not set\")\n+    return OpenAI(api_key=key, base_url=\"https://openrouter.ai/api/v1\")\n+\n+\n+def _prompt_llm(prompt: str) -> str:\n+    try:\n+        r = _client().chat.completions.create(\n+            model=DEFAULT_MODEL,\n+            max_tokens=1024,\n+            messages=[{\"role\": \"user\", \"content\": prompt}],\n+        )\n+        return r.choices[0].message.content or \"\"\n+    except Exception as e:\n+        # Treat any provider error (rate limit, content filter, schema reject)\n+        # as a failed response. The evolver will simply see this as a low score\n+        # on this organism and move on — much friendlier than killing the run.\n+        return f\"<LLM_ERROR: {type(e).__name__}: {e}>\"\n+\n+\n+class ParrotOrganism(Organism):\n+    prompt_template: str\n+\n+    def run(self, phrase: str) -> str:\n+        try:\n+            prompt = jinja2.Template(self.prompt_template).render(phrase=phrase)\n+        except jinja2.exceptions.TemplateError as e:\n+            return f\"Error rendering prompt: {e}\"\n+        if not prompt:\n+            return \"\"\n+        return _prompt_llm(prompt)\n+\n+\n+class ParrotEvaluationFailureCase(EvaluationFailureCase):\n+    phrase: str\n+    response: str\n+\n+\n+class ImproveParrotMutator(Mutator[ParrotOrganism, ParrotEvaluationFailureCase]):\n+    IMPROVEMENT_PROMPT_TEMPLATE = \"\"\"\n+We want to build a prompt that causes an LLM to repeat back a given phrase verbatim.\n+\n+The current prompt template is:\n+```\n+{{ organism.prompt_template }}\n+```\n+\n+Unfortunately, on this phrase:\n+```\n+{{ failure_case.phrase }}\n+```\n+the LLM responded with:\n+```\n+{{ failure_case.response }}\n+```\n+\n+Diagnose what went wrong, then propose an improved prompt template. Put the new\n+template in the LAST triple-backtick block of your response.\n+\"\"\".strip()\n+\n+    def mutate(\n+        self,\n+        organism: ParrotOrganism,\n+        failure_cases: list[ParrotEvaluationFailureCase],\n+        learning_log_entries: list[LearningLogEntry],\n+    ) -> list[ParrotOrganism]:\n+        fc = failure_cases[0]\n+        prompt = jinja2.Template(self.IMPROVEMENT_PROMPT_TEMPLATE).render(\n+            organism=organism, failure_case=fc\n+        )\n+        try:\n+            resp = _prompt_llm(prompt)\n+            parts = resp.split(\"```\")\n+            if len(parts) < 3:\n+                return []\n+            new_tpl = parts[-2].strip()\n+            return [ParrotOrganism(prompt_template=new_tpl)]\n+        except Exception as e:\n+            print(f\"mutate error: {e}\", file=sys.stderr)\n+            return []\n+\n+\n+class ParrotEvaluator(Evaluator[ParrotOrganism, EvaluationResult, ParrotEvaluationFailureCase]):\n+    TRAINABLE_PHRASES = [\n+        \"Hello world.\",\n+        \"bla\",\n+        \"Bla\",\n+        \"bla.\",\n+        '\"bla bla\".',\n+        \"Just say 'foo' once with no extra words.\",\n+    ]\n+    HOLDOUT_PHRASES = [\n+        \"bla, but only once.\",\n+        \"'bla'\",\n+    ]\n+\n+    def evaluate(self, organism: ParrotOrganism) -> EvaluationResult:\n+        train_fails: list[ParrotEvaluationFailureCase] = []\n+        hold_fails: list[ParrotEvaluationFailureCase] = []\n+        for i, p in enumerate(self.TRAINABLE_PHRASES):\n+            r = organism.run(p)\n+            if r != p:\n+                train_fails.append(ParrotEvaluationFailureCase(\n+                    phrase=p, response=r, data_point_id=f\"trainable_{i}\"))\n+        for i, p in enumerate(self.HOLDOUT_PHRASES):\n+            r = organism.run(p)\n+            if r != p:\n+                hold_fails.append(ParrotEvaluationFailureCase(\n+                    phrase=p, response=r, data_point_id=f\"holdout_{i}\"))\n+        n_total = len(self.TRAINABLE_PHRASES) + len(self.HOLDOUT_PHRASES)\n+        n_ok = n_total - len(train_fails) - len(hold_fails)\n+        return EvaluationResult(\n+            score=n_ok / n_total,\n+            trainable_failure_cases=train_fails,\n+            holdout_failure_cases=hold_fails,\n+            # Always viable. Even a 0-score seed is a valid starting point; the\n+            # mutator should still get a chance to fix it.\n+            is_viable=True,\n+        )\n+\n+\n+def make_problem() -> Problem:\n+    return Problem[ParrotOrganism, EvaluationResult, ParrotEvaluationFailureCase](\n+        evaluator=ParrotEvaluator(),\n+        mutators=[ImproveParrotMutator()],\n+        initial_organism=ParrotOrganism(prompt_template=\"Say {{ phrase }}\"),\n+    )\n+\n+\n+def main() -> int:\n+    ap = argparse.ArgumentParser()\n+    register_hyperparameter_args(ap.add_argument_group(\"hyperparameters\"))\n+    ap.add_argument(\"--num_iterations\", type=int, default=3)\n+    ap.add_argument(\"--mutator_concurrency\", type=int, default=4)\n+    ap.add_argument(\"--evaluator_concurrency\", type=int, default=4)\n+    ap.add_argument(\"--output_dir\", type=str, required=True)\n+    args = ap.parse_args()\n+\n+    out = Path(args.output_dir)\n+    out.mkdir(parents=True, exist_ok=True)\n+\n+    hp = build_hyperparameter_config_from_args(args)\n+    loop = EvolveProblemLoop(\n+        problem=make_problem(),\n+        learning_log_view_type=parse_learning_log_view_type(hp.learning_log_view_type),\n+        num_parents_per_iteration=hp.num_parents_per_iteration,\n+        mutator_concurrency=args.mutator_concurrency,\n+        evaluator_concurrency=args.evaluator_concurrency,\n+        fixed_midpoint_score=hp.fixed_midpoint_score,\n+        midpoint_score_percentile=hp.midpoint_score_percentile,\n+        sharpness=hp.sharpness,\n+        novelty_weight=hp.novelty_weight,\n+        batch_size=hp.batch_size,\n+        should_verify_mutations=hp.verify_mutations,\n+    )\n+\n+    import json\n+    log_path = out / \"results.jsonl\"\n+    snap_dir = out / \"snapshots\"\n+    snap_dir.mkdir(exist_ok=True)\n+    print(\"Evaluating initial organism...\")\n+    for snap in loop.run(num_iterations=args.num_iterations):\n+        (snap_dir / f\"iteration_{snap.iteration}.pkl\").write_bytes(snap.snapshot)\n+        _, best_eval = snap.best_organism_result\n+        print(f\"iter={snap.iteration} pop={snap.population_size} \"\n+              f\"best_score={best_eval.score:.3f}\")\n+        with log_path.open(\"a\") as f:\n+            f.write(json.dumps({\n+                \"iteration\": snap.iteration,\n+                \"best_score\": best_eval.score,\n+                \"pop_size\": snap.population_size,\n+                \"score_percentiles\": {str(k): v for k, v in snap.score_percentiles.items()},\n+            }) + \"\\n\")\n+    print(f\"\\nDone. Results in: {out}\")\n+    return 0\n+\n+\n+if __name__ == \"__main__\":\n+    sys.exit(main())"]},{"file":"optional-skills/research/darwinian-evolver/scripts/show_snapshot.py","hunks":["@@ -0,0 +1,69 @@\n+\"\"\"\n+show_snapshot.py — Dump the population from a darwinian-evolver snapshot pickle.\n+\n+Usage:\n+    python show_snapshot.py PATH/TO/iteration_N.pkl [--field prompt_template]\n+\n+The script is intentionally Organism-agnostic: it walks `org.__dict__` and prints\n+all str fields. By default it shows `prompt_template` if present; pass --field to\n+target a different attribute (e.g. `regex_pattern`, `sql_query`, `code_block`).\n+\"\"\"\n+from __future__ import annotations\n+\n+import argparse\n+import pickle\n+import sys\n+from pathlib import Path\n+\n+\n+def main() -> int:\n+    ap = argparse.ArgumentParser()\n+    ap.add_argument(\"snapshot\", type=Path)\n+    ap.add_argument(\n+        \"--field\",\n+        default=None,\n+        help=\"Organism attribute to display. Defaults to the first str field found.\",\n+    )\n+    ap.add_argument(\"--top\", type=int, default=None, help=\"Show only top N by score.\")\n+    args = ap.parse_args()\n+\n+    if not args.snapshot.exists():\n+        sys.exit(f\"snapshot not found: {args.snapshot}\")\n+\n+    # The outer pickle wraps a dict; the inner pickle contains the actual organism\n+    # objects, which must be importable under their original dotted path. If you\n+    # ran a custom driver, make sure its module is on sys.path before calling this.\n+    outer = pickle.loads(args.snapshot.read_bytes())\n+    if not isinstance(outer, dict) or \"population_snapshot\" not in outer:\n+        sys.exit(\"not a darwinian-evolver snapshot (no population_snapshot key)\")\n+    inner = pickle.loads(outer[\"population_snapshot\"])\n+    pairs = inner[\"organisms\"]  # list of (Organism, EvaluationResult)\n+\n+    print(f\"# organisms: {len(pairs)}\\n\")\n+    ranked = sorted(pairs, key=lambda p: getattr(p[1], \"score\", 0) or 0, reverse=True)\n+    if args.top:\n+        ranked = ranked[: args.top]\n+\n+    for i, (org, res) in enumerate(ranked):\n+        score = getattr(res, \"score\", float(\"nan\"))\n+        print(f\"=== rank {i} score={score:.3f} ===\")\n+        # pick field\n+        field = args.field\n+        if field is None:\n+            for k, v in vars(org).items():\n+                if isinstance(v, str) and not k.startswith(\"_\") and k not in (\"id\",):\n+                    field = k\n+                    break\n+        val = getattr(org, field, None) if field else None\n+        if val is None:\n+            print(f\"  (no string field; org fields: {list(vars(org).keys())})\")\n+        else:\n+            print(f\"  {field} ({len(val)} chars):\")\n+            for ln in val.splitlines()[:30]:\n+                print(f\"    {ln}\")\n+        print()\n+    return 0\n+\n+\n+if __name__ == \"__main__\":\n+    sys.exit(main())"]},{"file":"optional-skills/research/darwinian-evolver/templates/custom_problem_template.py","hunks":["@@ -0,0 +1,240 @@\n+\"\"\"\n+Template: a custom darwinian-evolver problem.\n+\n+Copy this file, fill in the THREE marked spots (Organism, Evaluator, Mutator),\n+then run it as a driver script. The skeleton handles all the wiring so you only\n+write the domain-specific logic.\n+\n+To run:\n+    cd ~/.hermes/cache/darwinian-evolver/darwinian_evolver\n+    OPENROUTER_API_KEY=... uv run --with openai python /path/to/this_file.py \\\n+        --num_iterations 3 --num_parents_per_iteration 2 \\\n+        --output_dir /tmp/my_problem\n+\n+The pattern mirrors `scripts/parrot_openrouter.py` (the working reference).\n+\"\"\"\n+from __future__ import annotations\n+\n+import argparse\n+import os\n+import sys\n+from pathlib import Path\n+\n+from openai import OpenAI\n+\n+# Upstream types (AGPL — invoked via subprocess in production; importing here\n+# is fine for skill-side driver scripts the user owns).\n+from darwinian_evolver.cli_common import (\n+    build_hyperparameter_config_from_args,\n+    parse_learning_log_view_type,\n+    register_hyperparameter_args,\n+)\n+from darwinian_evolver.evolve_problem_loop import EvolveProblemLoop\n+from darwinian_evolver.learning_log import LearningLogEntry\n+from darwinian_evolver.problem import (\n+    EvaluationFailureCase,\n+    EvaluationResult,\n+    Evaluator,\n+    Mutator,\n+    Organism,\n+    Problem,\n+)\n+\n+DEFAULT_MODEL = os.environ.get(\"EVOLVER_MODEL\", \"openai/gpt-4o-mini\")\n+\n+\n+def _client() -> OpenAI:\n+    key = os.environ.get(\"OPENROUTER_API_KEY\")\n+    if not key:\n+        sys.exit(\"OPENROUTER_API_KEY is not set\")\n+    return OpenAI(api_key=key, base_url=\"https://openrouter.ai/api/v1\")\n+\n+\n+def _prompt_llm(prompt: str, max_tokens: int = 1024) -> str:\n+    try:\n+        r = _client().chat.completions.create(\n+            model=DEFAULT_MODEL,\n+            max_tokens=[redacted:secret-assignment]\n+            messages=[{\"role\": \"user\", \"content\": prompt}],\n+        )\n+        return r.choices[0].message.content or \"\"\n+    except Exception as e:\n+        # Never let one bad LLM response kill the run.\n+        return f\"<LLM_ERROR: {type(e).__name__}: {e}>\"\n+\n+\n+# ---------------------------------------------------------------------------\n+# 1. ORGANISM — what you are evolving.\n+# ---------------------------------------------------------------------------\n+class MyOrganism(Organism):\n+    # TODO: replace with your artifact field. Common shapes:\n+    #   prompt_template: str\n+    #   regex_pattern: str\n+    #   sql_query: str\n+    #   code_block: str\n+    artifact: str\n+\n+    def run(self, *inputs) -> str:\n+        \"\"\"Exercise the organism on a test input. Return whatever your\n+        evaluator wants to score.\"\"\"\n+        # TODO: implement. For prompt evolution this typically calls _prompt_llm\n+        # with the artifact rendered against the input. For regex/SQL it would\n+        # call `re.findall(self.artifact, input)` / execute SQL / etc.\n+        raise NotImplementedError\n+\n+\n+# ---------------------------------------------------------------------------\n+# 2. EVALUATOR — score organisms and surface failures the mutator can learn from.\n+# ---------------------------------------------------------------------------\n+class MyFailureCase(EvaluationFailureCase):\n+    # TODO: include enough context for the LLM to diagnose the failure.\n+    input: str\n+    expected: str\n+    actual: str\n+\n+\n+class MyEvaluator(Evaluator[MyOrganism, EvaluationResult, MyFailureCase]):\n+    # Split your dataset. Mutator only sees trainable; holdout detects overfitting.\n+    TRAINABLE = [\n+        # TODO: list of (input, expected) tuples\n+        # (\"input1\", \"expected1\"),\n+    ]\n+    HOLDOUT = [\n+        # TODO: separate set the mutator never sees\n+    ]\n+\n+    def evaluate(self, organism: MyOrganism) -> EvaluationResult:\n+        train_fails: list[MyFailureCase] = []\n+        hold_fails: list[MyFailureCase] = []\n+        for i, (inp, expected) in enumerate(self.TRAINABLE):\n+            actual = organism.run(inp)\n+            if actual != expected:\n+                train_fails.append(MyFailureCase(\n+                    input=inp, expected=expected, actual=actual,\n+                    data_point_id=f\"trainable_{i}\",\n+                ))\n+        for i, (inp, expected) in enumerate(self.HOLDOUT):\n+            actual = organism.run(inp)\n+            if actual != expected:\n+                hold_fails.append(MyFailureCase(\n+                    input=inp, expected=expected, actual=actual,\n+                    data_point_id=f\"holdout_{i}\",\n+                ))\n+        n_total = len(self.TRAINABLE) + len(self.HOLDOUT)\n+        n_ok = n_total - len(train_fails) - len(hold_fails)\n+        return EvaluationResult(\n+            score=n_ok / n_total if n_total else 0.0,\n+            trainable_failure_cases=train_fails,\n+            holdout_failure_cases=hold_fails,\n+            # Always-viable. The evolver only blocks completely-broken organisms;\n+            # a 0-score organism is fine and will simply be sampled less often.\n+            is_viable=True,\n+        )\n+\n+\n+# ---------------------------------------------------------------------------\n+# 3. MUTATOR — LLM proposes an improved organism from a failure case.\n+# ---------------------------------------------------------------------------\n+class MyMutator(Mutator[MyOrganism, MyFailureCase]):\n+    PROMPT = \"\"\"\n+The current artifact is:\n+```\n+{artifact}\n+```\n+\n+On this input:\n+```\n+{input}\n+```\n+it produced:\n+```\n+{actual}\n+```\n+but we wanted:\n+```\n+{expected}\n+```\n+\n+Diagnose what went wrong, then propose an improved version of the artifact.\n+Put the new version in the LAST triple-backtick block of your response.\n+\"\"\".strip()\n+\n+    def mutate(\n+        self,\n+        organism: MyOrganism,\n+        failure_cases: list[MyFailureCase],\n+        learning_log_entries: list[LearningLogEntry],\n+    ) -> list[MyOrganism]:\n+        fc = failure_cases[0]\n+        prompt = self.PROMPT.format(\n+            artifact=organism.artifact,\n+            input=fc.input,\n+            actual=fc.actual,\n+            expected=fc.expected,\n+        )\n+        resp = _prompt_llm(prompt)\n+        parts = resp.split(\"```\")\n+        if len(parts) < 3:\n+            return []\n+        new_artifact = parts[-2].strip()\n+        # Strip an opening language tag like \"python\\n\" or \"sql\\n\"\n+        if \"\\n\" in new_artifact:\n+            first_line, rest = new_artifact.split(\"\\n\", 1)\n+            if first_line and not first_line.startswith(\" \") and len(first_line) < 20:\n+                new_artifact = rest\n+        return [MyOrganism(artifact=new_artifact)]\n+\n+\n+# ---------------------------------------------------------------------------\n+# Driver — fills in the EvolveProblemLoop boilerplate. You shouldn't need to\n+# touch anything below this line for a typical run.\n+# ---------------------------------------------------------------------------\n+def make_problem() -> Problem:\n+    initial = MyOrganism(artifact=\"TODO: starting artifact here\")  # TODO\n+    return Problem[MyOrganism, EvaluationResult, MyFailureCase](\n+        evaluator=MyEvaluator(),\n+        mutators=[MyMutator()],\n+        initial_organism=initial,\n+    )\n+\n+\n+def main() -> int:\n+    ap = argparse.ArgumentParser()\n+    register_hyperparameter_args(ap.add_argument_group(\"hyperparameters\"))\n+    ap.add_argument(\"--num_iterations\", type=int, default=3)\n+    ap.add_argument(\"--mutator_concurrency\", type=int, default=2)\n+    ap.add_argument(\"--evaluator_concurrency\", type=int, default=2)\n+    ap.add_argument(\"--output_dir\", type=str, required=True)\n+    args = ap.parse_args()\n+\n+    out = Path(args.output_dir)\n+    out.mkdir(parents=True, exist_ok=True)\n+    (out / \"snapshots\").mkdir(exist_ok=True)\n+\n+    hp = build_hyperparameter_config_from_args(args)\n+    loop = EvolveProblemLoop(\n+        problem=make_problem(),\n+        learning_log_view_type=parse_learning_log_view_type(hp.learning_log_view_type),\n+        num_parents_per_iteration=hp.num_parents_per_iteration,\n+        mutator_concurrency=args.mutator_concurrency,\n+        evaluator_concurrency=args.evaluator_concurrency,\n+        fixed_midpoint_score=hp.fixed_midpoint_score,\n+        midpoint_score_percentile=hp.midpoint_score_percentile,\n+        sharpness=hp.sharpness,\n+        novelty_weight=hp.novelty_weight,\n+        batch_size=hp.batch_size,\n+        should_verify_mutations=hp.verify_mutations,\n+    )\n+\n+    print(\"Evaluating initial organism...\")\n+    for snap in loop.run(num_iterations=args.num_iterations):\n+        (out / \"snapshots\" / f\"iteration_{snap.iteration}.pkl\").write_bytes(snap.snapshot)\n+        _, best = snap.best_organism_result\n+        print(f\"iter={snap.iteration} pop={snap.population_size} best_score={best.score:.3f}\")\n+\n+    print(f\"\\nDone. Results in: {out}\")\n+    return 0\n+\n+\n+if __name__ == \"__main__\":\n+    sys.exit(main())"]},{"file":"scripts/release.py","hunks":["@@ -59,6 +59,7 @@\n     \"[redacted:email]\": \"mikeyobrien\",\n     \"[redacted:email]\": \"qiyin-code\",\n     \"[redacted:email]\": \"aashizpoudel\",\n+    \"98262967+Bihruze@users.noreply.github.com\": \"Bihruze\",\n     \"[redacted:email]\": \"nidhi-singh02\",\n     \"30312689+aashizpoudel@users.noreply.github.com\": \"aashizpoudel\",\n     \"[redacted:email]\": \"olisikh\","]},{"file":"tests/skills/test_darwinian_evolver_skill.py","hunks":["@@ -0,0 +1,102 @@\n+\"\"\"\n+Smoke tests for the darwinian-evolver optional skill.\n+\n+We can't actually run the evolution loop in CI (it needs network + a paid LLM),\n+so these tests verify:\n+  - SKILL.md frontmatter conforms to the hardline format\n+  - shipped scripts parse as valid Python\n+  - the scripts reference the right env var / module paths\n+\"\"\"\n+from __future__ import annotations\n+\n+import ast\n+import re\n+from pathlib import Path\n+\n+import pytest\n+import yaml\n+\n+SKILL_DIR = Path(__file__).resolve().parents[2] / \"optional-skills\" / \"research\" / \"darwinian-evolver\"\n+\n+\n[redacted:email](scope=\"module\")\n+def frontmatter() -> dict:\n+    src = (SKILL_DIR / \"SKILL.md\").read_text()\n+    m = re.search(r\"^---\\n(.*?)\\n---\", src, re.DOTALL)\n+    assert m, \"SKILL.md missing YAML frontmatter\"\n+    return yaml.safe_load(m.group(1))\n+\n+\n+def test_skill_dir_exists() -> None:\n+    assert SKILL_DIR.is_dir(), f\"missing skill dir: {SKILL_DIR}\"\n+\n+\n+def test_skill_md_present() -> None:\n+    assert (SKILL_DIR / \"SKILL.md\").is_file()\n+\n+\n+def test_description_under_60_chars(frontmatter) -> None:\n+    desc = frontmatter[\"description\"]\n+    assert len(desc) <= 60, f\"description is {len(desc)} chars (hardline ≤60): {desc!r}\"\n+\n+\n+def test_name_matches_dir(frontmatter) -> None:\n+    assert frontmatter[\"name\"] == \"darwinian-evolver\"\n+\n+\n+def test_platforms_excludes_windows(frontmatter) -> None:\n+    # Upstream uses func_timeout (POSIX signals) and uv subprocess pipelines; the\n+    # skill is gated [linux, macos]. If we ever port to Windows, update this test\n+    # to assert [\"linux\", \"macos\", \"windows\"].\n+    assert \"windows\" not in frontmatter[\"platforms\"]\n+    assert set(frontmatter[\"platforms\"]) >= {\"linux\", \"macos\"}\n+\n+\n+def test_author_credits_contributor(frontmatter) -> None:\n+    author = frontmatter[\"author\"]\n+    assert \"Bihruze\" in author, f\"author should credit the original contributor: {author!r}\"\n+\n+\n+def test_license_mit(frontmatter) -> None:\n+    assert frontmatter[\"license\"] == \"MIT\"\n+\n+\n[redacted:email](\n+    \"path\",\n+    [\n+        \"scripts/parrot_openrouter.py\",\n+        \"scripts/show_snapshot.py\",\n+        \"templates/custom_problem_template.py\",\n+    ],\n+)\n+def test_shipped_scripts_parse(path: str) -> None:\n+    src = (SKILL_DIR / path).read_text()\n+    ast.parse(src)  # raises SyntaxError on broken Python\n+\n+\n+def test_parrot_script_uses_openrouter() -> None:\n+    src = (SKILL_DIR / \"scripts\" / \"parrot_openrouter.py\").read_text()\n+    assert \"OPENROUTER_API_KEY\" in src, \"parrot driver should read OPENROUTER_API_KEY\"\n+    assert \"openrouter.ai/api/v1\" in src, \"parrot driver should target OpenRouter\"\n+    assert \"EVOLVER_MODEL\" in src, \"model should be overridable via EVOLVER_MODEL\"\n+\n+\n+def test_parrot_script_has_error_swallowing() -> None:\n+    \"\"\"Provider content-filter / rate-limit must not kill the run — see Pitfall 2.\"\"\"\n+    src = (SKILL_DIR / \"scripts\" / \"parrot_openrouter.py\").read_text()\n+    assert \"LLM_ERROR\" in src, \"_prompt_llm should swallow provider errors and tag them\"\n+\n+\n+def test_skill_calls_out_agpl(frontmatter) -> None:\n+    \"\"\"The upstream tool is AGPL-3.0. The skill MUST flag this so users don't\n+    import it into MIT-licensed code by accident.\"\"\"\n+    src = (SKILL_DIR / \"SKILL.md\").read_text()\n+    assert \"AGPL\" in src, \"SKILL.md must mention upstream AGPL license\"\n+\n+\n+def test_skill_pitfalls_section_present() -> None:\n+    src = (SKILL_DIR / \"SKILL.md\").read_text()\n+    assert \"## Pitfalls\" in src\n+    # Pitfalls we discovered during the spike — keep them in sync with reality.\n+    assert \"Initial organism must be viable\" in src\n+    assert \"generator\" in src  # loop.run() pitfall"]},{"file":"website/docs/reference/optional-skills-catalog.md","hunks":["@@ -161,6 +161,7 @@ hermes skills uninstall <skill-name>\n | Skill | Description |\n |-------|-------------|\n | [**bioinformatics**](/docs/user-guide/skills/optional/research/research-bioinformatics) | Gateway to 400+ bioinformatics skills from bioSkills and ClawBio. Covers genomics, transcriptomics, single-cell, variant calling, pharmacogenomics, metagenomics, structural biology, and more. Fetches domain-specific reference material on... |\n+| [**darwinian-evolver**](/docs/user-guide/skills/optional/research/research-darwinian-evolver) | Evolve prompts/regex/SQL/code with Imbue's evolution loop. |\n | [**domain-intel**](/docs/user-guide/skills/optional/research/research-domain-intel) | Passive domain reconnaissance using Python stdlib. Subdomain discovery, SSL certificate inspection, WHOIS lookups, DNS records, domain availability checks, and bulk multi-domain analysis. No API keys required. |\n | [**drug-discovery**](/docs/user-guide/skills/optional/research/research-drug-discovery) | Pharmaceutical research assistant for drug discovery workflows. Search bioactive compounds on ChEMBL, calculate drug-likeness (Lipinski Ro5, QED, TPSA, synthetic accessibility), look up drug-drug interactions via OpenFDA, interpret ADMET... |\n | [**duckduckgo-search**](/docs/user-guide/skills/optional/research/research-duckduckgo-search) | Free web search via DuckDuckGo — text, news, images, videos. No API key needed. Prefer the `ddgs` CLI when installed; use the Python DDGS library only after verifying that `ddgs` is available in the current runtime. |"]},{"file":"website/docs/user-guide/skills/optional/research/research-darwinian-evolver.md","hunks":["@@ -0,0 +1,217 @@\n+---\n+title: \"Darwinian Evolver — Evolve prompts/regex/SQL/code with Imbue's evolution loop\"\n+sidebar_label: \"Darwinian Evolver\"\n+description: \"Evolve prompts/regex/SQL/code with Imbue's evolution loop\"\n+---\n+\n+{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}\n+\n+# Darwinian Evolver\n+\n+Evolve prompts/regex/SQL/code with Imbue's evolution loop.\n+\n+## Skill metadata\n+\n+| | |\n+|---|---|\n+| Source | Optional — install with `hermes skills install official/research/darwinian-evolver` |\n+| Path | `optional-skills/research/darwinian-evolver` |\n+| Version | `0.1.0` |\n+| Author | Bihruze (Asahi0x), Hermes Agent |\n+| License | MIT |\n+| Platforms | linux, macos |\n+| Tags | `evolution`, `optimization`, `prompt-engineering`, `research` |\n+| Related skills | [`arxiv`](/docs/user-guide/skills/bundled/research/research-arxiv), [`jupyter-live-kernel`](/docs/user-guide/skills/bundled/data-science/data-science-jupyter-live-kernel) |\n+\n+## Reference: full SKILL.md\n+\n+:::info\n+The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.\n+:::\n+\n+# Darwinian Evolver\n+\n+Run Imbue's [darwinian_evolver](https://github.com/imbue-ai/darwinian_evolver) — an\n+LLM-driven evolutionary search loop — to optimize a **prompt, regex, SQL query,\n+or small code snippet** against a fitness function.\n+\n+Status: thin wrapper around the upstream tool. The skill installs it, walks the\n+agent through writing a `Problem` definition (organism + evaluator + mutator),\n+and drives the loop via the upstream CLI or a small custom Python driver.\n+\n+**License:** the upstream tool is **AGPL-3.0**. The skill ONLY ever invokes it\n+via the upstream CLI or a `subprocess`/`uv run` call (mere aggregation). Do NOT\n+import upstream classes into Hermes itself.\n+\n+## When to Use\n+\n+- User says \"optimize this prompt\", \"evolve a regex for X\", \"auto-improve this\n+  code/SQL\", \"search for a better instruction\".\n+- You have a scorer (exact match, regex pass-rate, unit test, LLM-judge, runtime\n+  metric) AND a starting candidate (organism). If you don't have a scorer, stop\n+  and define one first — that's the hard part.\n+- Cost is OK: a typical run is 50–500 LLM calls. On gpt-4o-mini that's pennies;\n+  on Claude Sonnet it can be a few dollars.\n+\n+Do **not** use this when:\n+- The optimization target is differentiable (use gradient descent / DSPy).\n+- You only need to try 2–3 variants — just write them by hand.\n+- The fitness signal is purely subjective with no measurable criterion.\n+\n+## Prerequisites\n+\n+- Python ≥3.11\n+- `git`, `uv` (or `pip`)\n+- One of: `OPENROUTER_API_KEY`, `ANTHROPIC_API_KEY`, or `OPENAI_API_KEY`\n+\n+The skill ships a small `parrot_openrouter.py` driver that uses `OPENROUTER_API_KEY`\n+via the OpenAI SDK, so any model on OpenRouter works. The upstream CLI itself\n+hardcodes Anthropic and needs `ANTHROPIC_API_KEY`.\n+\n+## Install (One-Time)\n+\n+Run via the `terminal` tool:\n+\n+```bash\n+mkdir -p ~/.hermes/cache/darwinian-evolver && cd ~/.hermes/cache/darwinian-evolver\n+[ -d darwinian_evolver ] || git clone --depth 1 https://github.com/imbue-ai/darwinian_evolver.git\n+cd darwinian_evolver && uv sync\n+```\n+\n+Verify:\n+\n+```bash\n+cd ~/.hermes/cache/darwinian-evolver/darwinian_evolver \\\n+  && uv run darwinian_evolver --help | head -5\n+```\n+\n+## Quick Start — The Built-In Parrot Example\n+\n+Tiny smoke test (requires `ANTHROPIC_API_KEY`):\n+\n+```bash\n+cd ~/.hermes/cache/darwinian-evolver/darwinian_evolver\n+uv run darwinian_evolver parrot \\\n+  --num_iterations 2 \\\n+  --num_parents_per_iteration 2 \\\n+  --mutator_concurrency 2 --evaluator_concurrency 2 \\\n+  --output_dir /tmp/parrot_demo\n+```\n+\n+Outputs:\n+- `/tmp/parrot_demo/snapshots/iteration_N.pkl` — pickled population per iteration\n+- `/tmp/parrot_demo/<jsonl>` — per-iteration JSON log (path printed at end)\n+\n+Open `~/.hermes/cache/darwinian-evolver/darwinian_evolver/darwinian_evolver/lineage_visualizer.html`\n+in a browser and load the JSON log to see the evolutionary tree.\n+\n+## Quick Start — OpenRouter Driver (No Anthropic Key)\n+\n+The skill ships `scripts/parrot_openrouter.py` — same parrot problem, but the\n+LLM call goes through OpenRouter so any provider works.\n+\n+```bash\n+# From wherever the skill is installed:\n+SKILL_DIR=~/.hermes/skills/research/darwinian-evolver\n+DE_DIR=~/.hermes/cache/darwinian-evolver/darwinian_evolver\n+\n+cd \"$DE_DIR\" && \\\n+  EVOLVER_MODEL='openai/gpt-4o-mini' \\\n+  uv run --with openai python \"$SKILL_DIR/scripts/parrot_openrouter.py\" \\\n+    --num_iterations 3 --num_parents_per_iteration 2 \\\n+    --output_dir /tmp/parrot_or\n+```\n+\n+Inspect the result with `scripts/show_snapshot.py`:\n+\n+```bash\n+uv run --with openai python \"$SKILL_DIR/scripts/show_snapshot.py\" \\\n+  /tmp/parrot_or/snapshots/iteration_3.pkl\n+```\n+\n+Expected output: 7 evolved prompt templates ranked by score, with the best\n+landing around 0.6–0.8 (the seed `Say {{ phrase }}` scored 0.000).\n+\n+## Defining a Custom Problem\n+\n+The skill ships `templates/custom_problem_template.py` — copy, edit, run.\n+Three things you must define:\n+\n+1. **`Organism`** — a Pydantic `BaseModel` subclass holding the artifact being\n+   evolved (`prompt_template: str`, `regex_pattern: str`, `sql_query: str`,\n+   `code_block: str`, etc.). Add a `run(*args)` method that exercises it.\n+\n+2. **`Evaluator`** — `.evaluate(organism) -> EvaluationResult(score=..., trainable_failure_cases=[...], holdout_failure_cases=[...], is_viable=True)`.\n+   - **`score`** is in `[0, 1]`. Higher is better.\n+   - **`trainable_failure_cases`** — what the mutator sees. Include enough\n+     context (input, expected, actual) for the LLM to diagnose.\n+   - **`holdout_failure_cases`** — kept out of the mutator's view. Use these\n+     to detect overfitting.\n+   - **`is_viable=True`** unless the organism is completely broken (raises,\n+     returns None, etc.). A 0-score viable organism is fine — it just gets\n+     down-weighted in parent selection.\n+\n+3. **`Mutator`** — `.mutate(organism, failure_cases, learning_log_entries) -> list[Organism]`.\n+   Typically: build an LLM prompt that includes the current organism + a\n+   failure case + an ask to propose a fix; parse the LLM's response; return\n+   a new `Organism`. Return `[]` on parse failure — the loop handles it.\n+\n+Then write a driver script that wires `Problem(initial_organism, evaluator, [mutators])`\n+into `EvolveProblemLoop` and iterates over `loop.run(num_iterations=N)` — the\n+shipped `scripts/parrot_openrouter.py` is the reference.\n+\n+## Hyperparameters That Actually Matter\n+\n+| flag | default | when to change |\n+|---|---|---|\n+| `--num_iterations` | 5 | bump to 10–20 once you trust the evaluator |\n+| `--num_parents_per_iteration` | 4 | drop to 2 for cheap exploration |\n+| `--mutator_concurrency` | 10 | drop to 2–4 to avoid rate limits |\n+| `--evaluator_concurrency` | 10 | same; evaluator hits the LLM too |\n+| `--batch_size` | 1 | raise to 3–5 once your mutator handles multiple failures |\n+| `--verify_mutations` | off | turn on once mutator is wasteful (>10× cost saving on later runs per Imbue) |\n+| `--midpoint_score` | `p75` | leave alone unless scores cluster |\n+| `--sharpness` | 10 | leave alone |\n+\n+## Pitfalls\n+\n+1. **`Initial organism must be viable`** — set `is_viable=True` in your\n+   `EvaluationResult` even on a 0-score seed. The loop refuses non-viable\n+   organisms because they imply the loop has nothing to evolve from.\n+2. **Provider content filters kill runs.** Azure-backed OpenRouter models\n+   reject phrases like \"ignore previous instructions\" with HTTP 400. Wrap\n+   the LLM call in `try/except` and return `f\"<LLM_ERROR: {e}>\"` — the\n+   evolver will just score that organism 0 and move on.\n+3. **`loop.run()` is a generator** — calling it doesn't run anything until\n+   you iterate. Use `for snap in loop.run(num_iterations=N):`.\n+4. **Snapshots are nested pickles.** `iteration_N.pkl` contains a dict with\n+   `population_snapshot` (more pickled bytes). To unpickle you must have the\n+   `Organism` class importable under the same dotted path it was pickled at.\n+5. **Concurrency defaults are aggressive.** 10/10 will hit rate limits on\n+   most providers. Start with 2/2.\n+6. **CLI is hardcoded to Anthropic.** `uv run darwinian_evolver <problem>`\n+   reaches for `ANTHROPIC_API_KEY` and uses Claude Sonnet. To use any other\n+   provider, write a driver like `parrot_openrouter.py`.\n+7. **AGPL.** Never `from darwinian_evolver import ...` inside Hermes core.\n+   Custom driver scripts under `~/.hermes/skills/...` are user-side and fine.\n+8. **No PyPI package.** `pip install darwinian-evolver` will pull the wrong\n+   thing. Always install from the GitHub repo.\n+\n+## Verification\n+\n+After install + a parrot run, exit code 0 from this is sufficient:\n+\n+```bash\n+DE_DIR=~/.hermes/cache/darwinian-evolver/darwinian_evolver\n+ls \"$DE_DIR/darwinian_evolver/lineage_visualizer.html\" >/dev/null && \\\n+cd \"$DE_DIR\" && uv run darwinian_evolver --help >/dev/null && \\\n+echo \"darwinian-evolver: OK\"\n+```\n+\n+## References\n+\n+- [Imbue research post](https://imbue.com/research/2026-02-27-darwinian-evolver/)\n+- [ARC-AGI-2 results](https://imbue.com/research/2026-02-27-arc-agi-2-evolution/)\n+- [imbue-ai/darwinian_evolver](https://github.com/imbue-ai/darwinian_evolver) (AGPL-3.0)\n+- [Darwin Gödel Machines](https://arxiv.org/abs/2505.22954)\n+- [PromptBreeder](https://arxiv.org/abs/2309.16797)"]},{"file":"website/sidebars.ts","hunks":["@@ -547,6 +547,7 @@ const sidebars: SidebarsConfig = {\n                   collapsed: true,\n                   items: [\n                     'user-guide/skills/optional/research/research-bioinformatics',\n+                    'user-guide/skills/optional/research/research-darwinian-evolver',\n                     'user-guide/skills/optional/research/research-domain-intel',\n                     'user-guide/skills/optional/research/research-drug-discovery',\n                     'user-guide/skills/optional/research/research-duckduckgo-search',\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}