| name | research-cycle |
| description | Drive the new 6-stage research pipeline (init→survey→axiom→propose→dispatch→loop→review) via Hermes cron. Replaces deprecated auto-research and research-host cron drivers. Reads ledgers/<tag>/TODO.md, picks next stage, invokes claude -p with the appropriate /research-* command. Halts on SOTA/budget/BLOCK/human-flag. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| metadata | {"hermes":{"tags":["research","cron","autonomous","hermes-native"],"related_skills":["claude-code-router","codex-academic","gemini-academic"]}} |
research-cycle — Hermes-native research orchestrator
Replaces the deprecated:
~/.hermes/scripts/deprecated/auto-research.py (drove /exp-run, deprecated)
~/.hermes/scripts/deprecated/research-host-cron.py (drove /research-host, deprecated)
Drives the NEW 7-command pipeline (commands at ~/.claude/commands/research-*.md):
research-init <tag> ← Stage 0: human-only, sets up sprint
research-survey ← Stage 1: cron picks up here
research-axiom ← Stage 2
research-propose ← Stage 3
research-dispatch ← Stage 4 (loop start)
research-loop "<idea>" ← Stage 5
research-review ← Stage 6 (loop end)
← back to dispatch for next cycle
Procedure
Step 1 — pre-checks (Python; the script does this)
python3 ~/.hermes/scripts/research-cycle.py --dry-run
Returns next stage + halt status. If halted (SOTA / budget / BLOCK / human flag), stop here.
Step 2 — actual cron tick
python3 ~/.hermes/scripts/research-cycle.py
The script:
- Finds latest active sprint (
~/research/rgmare-lite/ledgers/*/sprint.yaml mtime)
- Reads
TODO.md to determine next stage ([ ] items, then loop phase detection)
- Halt-checks: human flag (
~/.hermes/shared/.research-halt), SOTA, budget, last-BLOCK
- Invokes
claude -p --model claude-sonnet-4-6 "/research-<stage> [args]" with stdin=DEVNULL
- For
/research-loop, extracts the rank-1 idea from latest dispatch/cycle-N/ranked.jsonl
- For
/research-review, extracts cycle number from latest dispatch
- Captures stdout/stderr, parses verdict, logs to
~/.hermes/shared/research-cycle-log.jsonl
- Writes status JSON to
~/.hermes/shared/.last-research-cycle-status
Step 3 — Hermes wakes summary agent (only if exit_code == 0)
The script emits {"wakeAgent": <bool>, "stage": ..., "verdict": ...} on stdout. Hermes uses this to decide whether to spawn a summary agent.
Halt conditions (priority order)
| Order | Condition | Action |
|---|
| 1 | ~/.hermes/shared/.research-halt exists | yield; user must rm it to resume |
| 2 | No active sprint (ledgers/*/sprint.yaml empty) | yield; user must run /research-init <tag> |
| 3 | results.tsv has keep row meeting target_value from sprint.yaml | yield; SOTA reached |
| 4 | Cycle count ≥ budget_cycles from sprint.yaml | yield; budget exhausted |
| 5 | Last research-review verdict was BLOCK and not yet resolved | yield; user must inspect |
| 6 | claude -p timed out (>90 min default) | log error; do not retry this tick |
Permission boundary for claude-code
When the cron driver invokes claude -p, it uses:
working_dir: ~/research/rgmare-lite
permission_mode: dangerously-skip-permissions
extra_constraints:
- "Operate ONLY within ~/research/rgmare-lite/ — do not touch other workspaces"
- "If the next /research-* command's HARD GATE fails, exit 1; do NOT improvise"
- "Respect the constitution at contracts/constitution.v0.1.0.yaml"
- "Never ask 'should I continue' — the human is asleep; loop until halt condition fires"
Cron registration (NOT auto-applied — user must approve)
To register, the user adds a job entry to ~/.hermes/cron/jobs.json. This skill does NOT auto-edit jobs.json. Suggested entry:
{
"id": "<auto-generated>",
"name": "Research Cycle",
"prompt": "The script already drove one stage of the research pipeline. Output above is the result. Summarize what was done in 2-3 sentences. If verdict=BLOCK, surface it. Save sprint progress to memory.",
"skills": ["research-cycle"],
"skill": "research-cycle",
"model": null,
"provider": null,
"base_url": null,
"script": "research-cycle.py",
"schedule": {
"kind": "interval",
"minutes": 90,
"display": "every 90m"
},
"schedule_display": "every 90m",
"repeat": {"times": null, "completed": 0},
"enabled": false,
"state": "scheduled",
"deliver": "local"
}
Recommended cadence: 90 minutes (gives /research-loop time to complete + reviewer time, but quickly enough that a missed cycle isn't catastrophic).
Set enabled: true only after user confirms a sprint is active.
Status outputs
~/.hermes/shared/.last-research-cycle-status — single JSON snapshot of last tick
~/.hermes/shared/research-cycle-log.jsonl — append-only log per tick
~/.hermes/shared/.research-halt (file presence) — human halt flag
Anti-patterns
- ❌ Do NOT enable this cron without an active sprint (will fire every 90m doing nothing — noisy)
- ❌ Do NOT remove the halt flag check — that's the user's emergency brake
- ❌ Do NOT increase CLAUDE_TIMEOUT beyond 90 min without reason — research-loop is bounded by autoresearch's 5-min training; longer means something hung
- ❌ Do NOT auto-resolve a BLOCK verdict — human decides whether the BLOCK is fixable or the sprint is dead
- ❌ Do NOT touch
auto-research.py or research-host-cron.py — they're in deprecated/ for archive purposes only