key = "continuous_research" title = "Continuous Research" blurb = "Harvest new arXiv papers, read them against your projects, and write a two-host podcast script for the morning." requires_repo = true # The vault, not `rust_sdlc`. Every other recipe defaults to the Rust SDLC team, # which is why `continuous_research` has only ever been reachable as a checkbox # in Advanced rather than as a mission type. default_team_template = "continuous_research" # ── How the papers arrive ──────────────────────────────────────────── # # The harvest is NOT an agent phase. `library::run_to_vault` already does # arXiv search → seen-set check → PDF fetch → blob shelf → vault note, and it # takes a `mission_id` so the run is attributed. Asking an agent to redo that # would be slower, non-deterministic, and would lose the `corpus_items` # seen-set that is the entire reason a recurring mission knows what it already # covered. `mission_orchestrator::on_launch` runs it before the phases start # and drops a manifest at `ContinuousResearch//harvest.jsonl` — the path # `templates/teams/continuous_research.toml` already tells the harvester role to # write, now produced by the code that actually does the harvesting. # # The mission binds the VAULT repo, so `/mission/repo` is the card catalogue: # the agents read the notes the harvest just wrote and commit their analysis and # script back onto the run's own branch, never `main`. [[phases]] kind = "research" order_idx = 0 [phases.config] produces = ["md"] default_topology = "hub_spoke" # `research`, not a new `read` kind. An unrecognised kind falls through to # `purposes_for`'s `_ => ["mission"]` and is absent from `PRODUCING_KINDS` # (phase_runner.rs), so it would get a generic directive AND be exempt from the # empty-delivery rule — a phase that could produce nothing and still pass. Two # `research` phases differentiated by `task` keeps both guards. task = """ Read today's harvested papers and judge them against the operator's projects. Start from ContinuousResearch//harvest.jsonl — that is the list of papers \ that are NEW since the last run. Papers already covered are not in it, and you \ should not go looking for them. For each paper write an entry in ContinuousResearch//analysis.md \ containing: what it actually does (not what its abstract claims), whether the \ evidence supports it, and — the part that matters — WHICH of the operator's \ projects it bears on and what concrete change it would imply. Name a file, a module or a \ roadmap item wherever you can. Depth comes from the paper itself: the note carries the abstract, and `curl` on \ the arXiv abstract page gets you the rest. Do not review a paper from its title. A paper with no bearing on any project is a real and useful finding — say so in \ one line and move on. Do not manufacture relevance. THE PROJECTS THIS SERVES ARE IN THE BRIEF ABOVE. `phase_task_text` puts the \ mission's description there verbatim, so that is where the operator names what \ they are working on and what each project needs. If the brief names no \ projects, say so plainly in analysis.md rather than inventing a target — a \ digest that guesses at relevance is worse than one that admits it has no \ context. """ done_when = "ContinuousResearch//analysis.md exists and contains, for every paper in that day's harvest.jsonl, a judgement of the work and a statement of which project it bears on or that it bears on none" max_iterations = 2 # Nothing here is compiled, so `on_green_tests` would gate on a suite that does # not exist and land every branch `-wip`. The vault is prose. commit_policy = "always" [[phases]] kind = "research" order_idx = 1 [phases.config] produces = ["md"] default_topology = "hub_spoke" task = """ Turn today's analysis into a podcast script for two hosts. Read ContinuousResearch//analysis.md and write \ ContinuousResearch//script.md as a conversation between HOST and GUEST. \ Lead with what changed for our projects, not with a list of papers — the \ listener is on a treadmill, not at a desk. Also write ContinuousResearch//episode.json: { "title": "", "highlights": ["<10-70 chars each, at most 5>"] } Those bounds are the podcast API's, not a style preference — a highlight \ outside them is rejected. Pitch it at a bright sixteen-year-old: someone curious who has NOT read the \ paper, does not know the jargon, and cannot pause to look anything up. Define \ an acronym the first time in the sentence, then use it. Describe the mechanism \ rather than naming it — "a road map with motorways and side streets" beats \ "a hierarchical navigable small world graph". That is not dumbing down; it is \ what forces you to say what the thing actually does. Numbers are HEARD. Never write an arXiv id — "arxiv 2608.12888" is heard as \ "two six zero eight point one two eight eight eight"; name the paper instead. \ Two decimal places at most, and round in speech: "roughly a third faster" \ beats "34.7% faster". No URLs, no figure references. Target seven minutes of speech, roughly 1,000 words. Say the specific thing: \ "this changes how we prune the search index in clawhdf5" beats "researchers \ propose a novel method". Skip a paper entirely rather than pad the episode \ with one that does not matter. """ done_when = "ContinuousResearch//script.md contains a two-host dialogue covering the analysis, and episode.json contains a title and a highlights array whose entries are each between 10 and 70 characters" max_iterations = 2 commit_policy = "always"