@@ -326,10 +326,19 @@ impl Document {
326326
327327 /// Get the current context resolver (includes all blocks and parent context)
328328 pub fn get_context_resolver ( & self ) -> ContextResolver {
329- match & self . parent_context {
330- Some ( parent) => ContextResolver :: from_blocks_with_parent ( & self . blocks , parent) ,
331- None => ContextResolver :: from_blocks ( & self . blocks ) ,
329+ let mut resolver = match & self . parent_context {
330+ Some ( parent) => ContextResolver :: from_parent ( parent) ,
331+ None => ContextResolver :: new ( ) ,
332+ } ;
333+
334+ if let Some ( ref workspace_root) = self . workspace_root {
335+ let mut workspace_context = HashMap :: new ( ) ;
336+ workspace_context. insert ( "root" . to_string ( ) , workspace_root. clone ( ) ) ;
337+ resolver. add_extra_template_context ( "workspace" . to_string ( ) , workspace_context) ;
332338 }
339+
340+ resolver. push_blocks ( & self . blocks ) ;
341+ resolver
333342 }
334343
335344 /// Build an execution context for a block, capturing all context from blocks above it
@@ -353,31 +362,35 @@ impl Document {
353362 . get_block_index ( block_id)
354363 . ok_or ( DocumentError :: BlockNotFound ( * block_id) ) ?;
355364
356- // Build context resolver from all blocks above this one (with parent context if set)
365+ // Build context resolver - add extra context BEFORE processing blocks
366+ // so that templates like {{ workspace.root }} can resolve during block processing
357367 let mut context_resolver = match & self . parent_context {
358- Some ( parent) => {
359- ContextResolver :: from_blocks_with_parent ( & self . blocks [ ..position] , parent)
360- }
361- None => ContextResolver :: from_blocks ( & self . blocks [ ..position] ) ,
368+ Some ( parent) => ContextResolver :: from_parent ( parent) ,
369+ None => ContextResolver :: new ( ) ,
362370 } ;
363- if let Some ( extra_template_context) = extra_template_context {
364- for ( namespace, context) in extra_template_context {
365- context_resolver. add_extra_template_context ( namespace. clone ( ) , context. clone ( ) ) ;
366- }
367- }
368371
369- // Add workspace template context if available
372+ // Add workspace template context first (before blocks are processed)
370373 if let Some ( ref workspace_root) = self . workspace_root {
371374 let mut workspace_context = HashMap :: new ( ) ;
372375 workspace_context. insert ( "root" . to_string ( ) , workspace_root. clone ( ) ) ;
373376 context_resolver. add_extra_template_context ( "workspace" . to_string ( ) , workspace_context) ;
374377 }
375378
379+ // Add any extra template context passed by caller
380+ if let Some ( extra_template_context) = extra_template_context {
381+ for ( namespace, context) in extra_template_context {
382+ context_resolver. add_extra_template_context ( namespace. clone ( ) , context. clone ( ) ) ;
383+ }
384+ }
385+
376386 let mut runbook_template_context = HashMap :: new ( ) ;
377387 runbook_template_context. insert ( "id" . to_string ( ) , self . id . clone ( ) ) ;
378388 context_resolver
379389 . add_extra_template_context ( "runbook" . to_string ( ) , runbook_template_context) ;
380390
391+ // Now process blocks - templates will resolve against the context we just set up
392+ context_resolver. push_blocks ( & self . blocks [ ..position] ) ;
393+
381394 let block_outputs = self
382395 . blocks
383396 . iter ( )
@@ -423,12 +436,18 @@ impl Document {
423436 . get_block_index ( block_id)
424437 . ok_or ( DocumentError :: BlockNotFound ( * block_id) ) ?;
425438
426- let resolver = match & self . parent_context {
427- Some ( parent) => {
428- ContextResolver :: from_blocks_with_parent ( & self . blocks [ ..position] , parent)
429- }
430- None => ContextResolver :: from_blocks ( & self . blocks [ ..position] ) ,
439+ let mut resolver = match & self . parent_context {
440+ Some ( parent) => ContextResolver :: from_parent ( parent) ,
441+ None => ContextResolver :: new ( ) ,
431442 } ;
443+
444+ if let Some ( ref workspace_root) = self . workspace_root {
445+ let mut workspace_context = HashMap :: new ( ) ;
446+ workspace_context. insert ( "root" . to_string ( ) , workspace_root. clone ( ) ) ;
447+ resolver. add_extra_template_context ( "workspace" . to_string ( ) , workspace_context) ;
448+ }
449+
450+ resolver. push_blocks ( & self . blocks [ ..position] ) ;
432451 Ok ( ResolvedContext :: from_resolver ( & resolver) )
433452 }
434453
@@ -462,18 +481,23 @@ impl Document {
462481 let mut errors = Vec :: new ( ) ;
463482 let start = start_index. unwrap_or ( 0 ) ;
464483
484+ // Build context resolver - add extra context BEFORE processing blocks
485+ // so that templates like {{ workspace.root }} can resolve during block processing
465486 let mut context_resolver = match & self . parent_context {
466- Some ( parent) => ContextResolver :: from_blocks_with_parent ( & self . blocks [ ..start ] , parent) ,
467- None => ContextResolver :: from_blocks ( & self . blocks [ ..start ] ) ,
487+ Some ( parent) => ContextResolver :: from_parent ( parent) ,
488+ None => ContextResolver :: new ( ) ,
468489 } ;
469490
470- // Add workspace template context if available
491+ // Add workspace template context first (before blocks are processed)
471492 if let Some ( ref workspace_root) = self . workspace_root {
472493 let mut workspace_context = HashMap :: new ( ) ;
473494 workspace_context. insert ( "root" . to_string ( ) , workspace_root. clone ( ) ) ;
474495 context_resolver. add_extra_template_context ( "workspace" . to_string ( ) , workspace_context) ;
475496 }
476497
498+ // Now process blocks[..start] with workspace context available
499+ context_resolver. push_blocks ( & self . blocks [ ..start] ) ;
500+
477501 for i in start..self . blocks . len ( ) {
478502 let block_id = self . blocks [ i] . id ( ) ;
479503
0 commit comments