2424 format_check_failures ,
2525 run_all_checks ,
2626)
27- from ralphify .contexts import Context , discover_contexts , resolve_contexts , run_all_contexts
27+ from ralphify .contexts import (
28+ Context ,
29+ ContextResult ,
30+ discover_contexts ,
31+ resolve_contexts ,
32+ run_all_contexts ,
33+ )
2834from ralphify ._frontmatter import parse_frontmatter
2935from ralphify .instructions import Instruction , discover_instructions , resolve_instructions
3036
@@ -215,33 +221,48 @@ def _handle_loop_transitions(
215221 return True , primitives
216222
217223
224+ def _run_contexts_phase (
225+ contexts : list [Context ],
226+ project_root : Path ,
227+ state : RunState ,
228+ emitter : EventEmitter ,
229+ ) -> list [ContextResult ]:
230+ """Execute all contexts and emit the resolved event.
231+
232+ Mirrors :func:`_run_checks_phase` — the loop has a clear context-running
233+ phase separate from the text-only prompt assembly step.
234+ """
235+ results = run_all_contexts (contexts , project_root )
236+ emitter .emit (Event (
237+ type = EventType .CONTEXTS_RESOLVED ,
238+ run_id = state .run_id ,
239+ data = {"iteration" : state .iteration , "count" : len (contexts )},
240+ ))
241+ return results
242+
243+
218244def _assemble_prompt (
219245 config : RunConfig ,
220246 primitives : EnabledPrimitives ,
247+ context_results : list [ContextResult ],
221248 check_failures_text : str ,
222249 state : RunState ,
223250 emitter : EventEmitter ,
224251) -> str :
225- """Build the full prompt for one iteration.
252+ """Build the full prompt for one iteration (text assembly only) .
226253
227- Reads the prompt source, resolves contexts and instructions, and
228- appends any check-failure feedback from the previous iteration.
254+ Reads the prompt source, resolves pre-computed context results and
255+ instructions, and appends any check-failure feedback from the previous
256+ iteration. All subprocess I/O happens in the caller before this
257+ function is reached.
229258 """
230- iteration = state .iteration
231-
232259 if config .prompt_text :
233260 prompt = config .prompt_text
234261 else :
235262 raw = Path (config .prompt_file ).read_text ()
236263 _ , prompt = parse_frontmatter (raw )
237- if primitives .contexts :
238- context_results = run_all_contexts (primitives .contexts , config .project_root )
264+ if context_results :
239265 prompt = resolve_contexts (prompt , context_results )
240- emitter .emit (Event (
241- type = EventType .CONTEXTS_RESOLVED ,
242- run_id = state .run_id ,
243- data = {"iteration" : iteration , "count" : len (primitives .contexts )},
244- ))
245266 if primitives .instructions :
246267 prompt = resolve_instructions (prompt , primitives .instructions )
247268 if check_failures_text :
@@ -250,7 +271,7 @@ def _assemble_prompt(
250271 emitter .emit (Event (
251272 type = EventType .PROMPT_ASSEMBLED ,
252273 run_id = state .run_id ,
253- data = {"iteration" : iteration , "prompt_length" : len (prompt )},
274+ data = {"iteration" : state . iteration , "prompt_length" : len (prompt )},
254275 ))
255276 return prompt
256277
@@ -439,8 +460,17 @@ def run_loop(
439460 data = {"iteration" : iteration },
440461 ))
441462
463+ # Run contexts phase (subprocess I/O)
464+ context_results : list [ContextResult ] = []
465+ if primitives .contexts :
466+ context_results = _run_contexts_phase (
467+ primitives .contexts , config .project_root , state , emitter ,
468+ )
469+
470+ # Assemble prompt (text resolution only)
442471 prompt = _assemble_prompt (
443- config , primitives , check_failures_text , state , emitter ,
472+ config , primitives , context_results , check_failures_text ,
473+ state , emitter ,
444474 )
445475
446476 returncode = _execute_agent (
0 commit comments