about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2016-12-11 16:18:18 -0800
committerKartik K. Agaram <vc@akkartik.com>2016-12-11 16:18:18 -0800
commit294b2ab35983ebe95698835bb54bca8bd3eec101 (patch)
treefbc74bea6cefd7b8f527d36a7b7c6804dd886414
parentd5c86dfd8706e6b3ceee7843464797e6fcad4259 (diff)
downloadmu-294b2ab35983ebe95698835bb54bca8bd3eec101.tar.gz
3705 - switch to tested file-system primitives
-rw-r--r--088file.mu65
-rw-r--r--090scenario_filesystem_test.mu14
-rw-r--r--092socket.mu2
-rw-r--r--101run_sandboxed.cc2
-rw-r--r--102persist.cc118
-rw-r--r--cannot_write_tests_for2
-rw-r--r--edit/004-programming-environment.mu81
-rw-r--r--edit/005-sandbox.mu170
-rw-r--r--edit/006-sandbox-copy.mu128
-rw-r--r--edit/007-sandbox-delete.mu42
-rw-r--r--edit/008-sandbox-edit.mu108
-rw-r--r--edit/009-sandbox-test.mu45
-rw-r--r--edit/010-sandbox-trace.mu90
-rw-r--r--edit/011-errors.mu341
-rw-r--r--html/088file.mu.html65
-rw-r--r--html/090scenario_filesystem_test.mu.html17
-rw-r--r--html/092socket.mu.html2
-rw-r--r--html/101run_sandboxed.cc.html2
-rw-r--r--html/102persist.cc.html154
-rw-r--r--html/edit/004-programming-environment.mu.html81
-rw-r--r--html/edit/005-sandbox.mu.html170
-rw-r--r--html/edit/006-sandbox-copy.mu.html128
-rw-r--r--html/edit/007-sandbox-delete.mu.html42
-rw-r--r--html/edit/008-sandbox-edit.mu.html108
-rw-r--r--html/edit/009-sandbox-test.mu.html45
-rw-r--r--html/edit/010-sandbox-trace.mu.html90
-rw-r--r--html/edit/011-errors.mu.html341
-rw-r--r--sandbox/004-programming-environment.mu31
-rw-r--r--sandbox/005-sandbox.mu143
-rw-r--r--sandbox/006-sandbox-copy.mu34
-rw-r--r--sandbox/007-sandbox-delete.mu42
-rw-r--r--sandbox/008-sandbox-edit.mu102
-rw-r--r--sandbox/009-sandbox-test.mu36
-rw-r--r--sandbox/010-sandbox-trace.mu28
-rw-r--r--sandbox/011-errors.mu233
35 files changed, 1598 insertions, 1504 deletions
diff --git a/088file.mu b/088file.mu
index b7358327..69e72933 100644
--- a/088file.mu
+++ b/088file.mu
@@ -1,5 +1,12 @@
 # Wrappers around file system primitives that take a 'resources' object and
 # are thus easier to test.
+#
+# - start-reading - asynchronously open a file, returning a channel source for
+#   receiving the results
+# - start-writing - asynchronously open a file, returning a channel sink for
+#   the data to write
+# - slurp - synchronously read from a file
+# - dump - synchronously write to a file
 
 container resources [
   lock:bool
@@ -11,25 +18,42 @@ container resource [
   contents:text
 ]
 
-def start-reading resources:&:resources, filename:text -> contents:&:source:char [
+def start-reading resources:&:resources, filename:text -> contents:&:source:char, error?:bool [
   local-scope
   load-ingredients
+  error? <- copy 0/false
   {
     break-unless resources
     # fake file system
-    contents <- start-reading-from-fake-resources resources, filename
+    contents, error? <- start-reading-from-fake-resource resources, filename
     return
   }
   # real file system
   file:num <- $open-file-for-reading filename
-  assert file, [file not found]
+  return-unless file, 0/contents, 1/error?
   contents:&:source:char, sink:&:sink:char <- new-channel 30
   start-running receive-from-file file, sink
 ]
 
-def start-reading-from-fake-resources resources:&:resources, resource:text -> contents:&:source:char [
+def slurp resources:&:resources, filename:text -> contents:text, error?:bool [
   local-scope
   load-ingredients
+  source:&:source:char, error?:bool <- start-reading resources, filename
+  return-if error?, 0/contents
+  buf:&:buffer <- new-buffer 30/capacity
+  {
+    c:char, done?:bool, source <- read source
+    break-if done?
+    buf <- append buf, c
+    loop
+  }
+  contents <- buffer-to-array buf
+]
+
+def start-reading-from-fake-resource resources:&:resources, resource:text -> contents:&:source:char, error?:bool [
+  local-scope
+  load-ingredients
+  error? <- copy 0/no-error
   i:num <- copy 0
   data:&:@:resource <- get *resources, data:offset
   len:num <- length *data
@@ -46,7 +70,7 @@ def start-reading-from-fake-resources resources:&:resources, resource:text -> co
     start-running receive-from-text curr-contents, sink
     return
   }
-  return 0/not-found
+  return 0/not-found, 1/error
 ]
 
 def receive-from-file file:num, sink:&:sink:char -> sink:&:sink:char [
@@ -78,18 +102,20 @@ def receive-from-text contents:text, sink:&:sink:char -> sink:&:sink:char [
   sink <- close sink
 ]
 
-def start-writing resources:&:resources, filename:text -> sink:&:sink:char, routine-id:num [
+def start-writing resources:&:resources, filename:text -> sink:&:sink:char, routine-id:num, error?:bool [
   local-scope
   load-ingredients
+  error? <- copy 0/false
   source:&:source:char, sink:&:sink:char <- new-channel 30
   {
     break-unless resources
     # fake file system
-    routine-id <- start-running transmit-to-fake-file resources, filename, source
+    routine-id <- start-running transmit-to-fake-resource resources, filename, source
     return
   }
   # real file system
   file:num <- $open-file-for-writing filename
+  return-unless file, 0/sink, 0/routine-id, 1/error?
   {
     break-if file
     msg:text <- append [no such file: ] filename
@@ -98,6 +124,29 @@ def start-writing resources:&:resources, filename:text -> sink:&:sink:char, rout
   routine-id <- start-running transmit-to-file file, source
 ]
 
+def dump resources:&:resources, filename:text, contents:text -> resources:&:resources, error?:bool [
+  local-scope
+  load-ingredients
+  # todo: really create an empty file
+  return-unless contents, resources, 0/no-error
+  sink-file:&:sink:char, write-routine:num, error?:bool <- start-writing resources, filename
+  return-if error?
+  i:num <- copy 0
+  len:num <- length *contents
+  {
+    done?:bool <- greater-or-equal i, len
+    break-if done?
+    c:char <- index *contents, i
+    sink-file <- write sink-file, c
+    i <- add i, 1
+    loop
+  }
+  close sink-file
+  # make sure to wait for the file to be actually written to disk
+  # (Mu practices structured concurrency: http://250bpm.com/blog:71)
+  wait-for-routine write-routine
+]
+
 def transmit-to-file file:num, source:&:source:char -> source:&:source:char [
   local-scope
   load-ingredients
@@ -110,7 +159,7 @@ def transmit-to-file file:num, source:&:source:char -> source:&:source:char [
   file <- $close-file file
 ]
 
-def transmit-to-fake-file resources:&:resources, filename:text, source:&:source:char -> resources:&:resources, source:&:source:char [
+def transmit-to-fake-resource resources:&:resources, filename:text, source:&:source:char -> resources:&:resources, source:&:source:char [
   local-scope
   load-ingredients
   lock:location <- get-location *resources, lock:offset
diff --git a/090scenario_filesystem_test.mu b/090scenario_filesystem_test.mu
index 6d212f88..b487bfe0 100644
--- a/090scenario_filesystem_test.mu
+++ b/090scenario_filesystem_test.mu
@@ -97,17 +97,3 @@ scenario write-to-existing-file-preserves-other-files [
     11 <- 1  # other files also continue to persist unchanged
   ]
 ]
-
-def slurp resources:&:resources, filename:text -> contents:text [
-  local-scope
-  load-ingredients
-  source:&:source:char <- start-reading resources, filename
-  buf:&:buffer <- new-buffer 30/capacity
-  {
-    c:char, done?:bool, source <- read source
-    break-if done?
-    buf <- append buf, c
-    loop
-  }
-  contents <- buffer-to-array buf
-]
diff --git a/092socket.mu b/092socket.mu
index 6cc43659..e9955f4a 100644
--- a/092socket.mu
+++ b/092socket.mu
@@ -77,7 +77,7 @@ def start-reading-from-network resources:&:resources, uri:text -> contents:&:sou
   {
     break-unless resources
     # fake network
-    contents <- start-reading-from-fake-resources resources, uri
+    contents <- start-reading-from-fake-resource resources, uri
     return
   }
   # real network
diff --git a/101run_sandboxed.cc b/101run_sandboxed.cc
index 66a19426..a3b6292e 100644
--- a/101run_sandboxed.cc
+++ b/101run_sandboxed.cc
@@ -427,8 +427,8 @@ int trace_error_contents() {
     if (*--p->contents.end() != '\n') out << '\n';
   }
   string result = out.str();
-  if (result.empty()) return 0;
   truncate(result);
+  if (result.empty()) return 0;
   return new_mu_text(result);
 }
 
diff --git a/102persist.cc b/102persist.cc
deleted file mode 100644
index 642385df..00000000
--- a/102persist.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-//: Dead simple persistence.
-//:   'restore' - reads string from a file
-//:   'save' - writes string to a file
-
-:(before "End Primitive Recipe Declarations")
-RESTORE,
-:(before "End Primitive Recipe Numbers")
-put(Recipe_ordinal, "restore", RESTORE);
-:(before "End Primitive Recipe Checks")
-case RESTORE: {
-  if (SIZE(inst.ingredients) != 1) {
-    raise << maybe(get(Recipe, r).name) << "'restore' requires exactly one ingredient, but got '" << inst.original_string << "'\n" << end();
-    break;
-  }
-  string filename;
-  if (is_literal_text(inst.ingredients.at(0))) {
-    ;
-  }
-  else if (is_mu_text(inst.ingredients.at(0))) {
-    ;
-  }
-  else {
-    raise << maybe(get(Recipe, r).name) << "first ingredient of 'restore' should be a string, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
-    break;
-  }
-  break;
-}
-:(before "End Primitive Recipe Implementations")
-case RESTORE: {
-  string filename;
-  if (is_literal_text(current_instruction().ingredients.at(0))) {
-    filename = current_instruction().ingredients.at(0).name;
-  }
-  else if (is_mu_text(current_instruction().ingredients.at(0))) {
-    filename = read_mu_text(ingredients.at(0).at(0));
-  }
-  if (Current_scenario) {
-    // do nothing in tests
-    products.resize(1);
-    products.at(0).push_back(0);
-    break;
-  }
-  string contents = slurp("lesson/"+filename);
-  products.resize(1);
-  if (contents.empty())
-    products.at(0).push_back(0);
-  else
-    products.at(0).push_back(new_mu_text(contents));
-  break;
-}
-
-:(code)
-// http://cpp.indi.frih.net/blog/2014/09/how-to-read-an-entire-file-into-memory-in-cpp
-string slurp(const string& filename) {
-  ifstream fin(filename.c_str());
-  fin.peek();
-  if (!fin) return "";  // don't bother checking errno
-  ostringstream result;
-  result << fin.rdbuf();
-  fin.close();
-  return result.str();
-}
-
-:(before "End Primitive Recipe Declarations")
-SAVE,
-:(before "End Primitive Recipe Numbers")
-put(Recipe_ordinal, "save", SAVE);
-:(before "End Primitive Recipe Checks")
-case SAVE: {
-  if (SIZE(inst.ingredients) != 2) {
-    raise << maybe(get(Recipe, r).name) << "'save' requires exactly two ingredients, but got '" << inst.original_string << "'\n" << end();
-    break;
-  }
-  if (is_literal_text(inst.ingredients.at(0))) {
-    ;
-  }
-  else if (is_mu_text(inst.ingredients.at(0))) {
-    ;
-  }
-  else {
-    raise << maybe(get(Recipe, r).name) << "first ingredient of 'save' should be a string, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
-    break;
-  }
-  if (!is_mu_text(inst.ingredients.at(1))) {
-    raise << maybe(get(Recipe, r).name) << "second ingredient of 'save' should be an address:@:char, but got '" << to_string(inst.ingredients.at(1)) << "'\n" << end();
-    break;
-  }
-  break;
-}
-:(before "End Primitive Recipe Implementations")
-case SAVE: {
-  if (Current_scenario) break;  // do nothing in tests
-  string filename;
-  if (is_literal_text(current_instruction().ingredients.at(0))) {
-    filename = current_instruction().ingredients.at(0).name;
-  }
-  else if (is_mu_text(current_instruction().ingredients.at(0))) {
-    filename = read_mu_text(ingredients.at(0).at(0));
-  }
-  ofstream fout(("lesson/"+filename).c_str());
-  if (!fout) break;
-  string contents = read_mu_text(ingredients.at(1).at(0));
-  fout << contents;
-  fout.close();
-  if (!exists("lesson/.git")) break;
-  // bug in git: git diff -q messes up --exit-code
-  // explicitly say '--all' for git 1.9
-  int status = system("cd lesson; git add --all .; git diff HEAD --exit-code >/dev/null || git commit -a -m . >/dev/null");
-  if (status != 0)
-    raise << "error in commit: contents " << contents << '\n' << end();
-  break;
-}
-
-:(code)
-bool exists(const string& filename) {
-  struct stat dummy;
-  return 0 == stat(filename.c_str(), &dummy);
-}
diff --git a/cannot_write_tests_for b/cannot_write_tests_for
index 01b5cbe8..1cdaa20a 100644
--- a/cannot_write_tests_for
+++ b/cannot_write_tests_for
@@ -5,7 +5,7 @@
 4. hide/show screen
 5. more touch event types
 6. sandbox isolation
-7. read/write files
+7. errors in reading/writing files (missing directory, others?)
 
 termbox issues are implementation-specific and not worth testing:
   whether we clear junk from other processes
diff --git a/edit/004-programming-environment.mu b/edit/004-programming-environment.mu
index 332b7e18..7efa3d47 100644
--- a/edit/004-programming-environment.mu
+++ b/edit/004-programming-environment.mu
@@ -6,12 +6,9 @@
 def! main [
   local-scope
   open-console
-  initial-recipe:text <- restore [recipes.mu]
-  initial-sandbox:text <- new []
-  hide-screen 0/screen
-  env:&:environment <- new-programming-environment 0/screen, initial-recipe, initial-sandbox
+  env:&:environment <- new-programming-environment 0/filesystem, 0/screen
   render-all 0/screen, env, render
-  event-loop 0/screen, 0/console, env
+  event-loop 0/screen, 0/console, env, 0/filesystem
   # never gets here
 ]
 
@@ -21,24 +18,25 @@ container environment [
   sandbox-in-focus?:bool  # false => cursor in recipes; true => cursor in current-sandbox
 ]
 
-def new-programming-environment screen:&:screen, initial-recipe-contents:text, initial-sandbox-contents:text -> result:&:environment [
+def new-programming-environment resources:&:resources, screen:&:screen, test-sandbox-editor-contents:text -> result:&:environment [
   local-scope
   load-ingredients
   width:num <- screen-width screen
   result <- new environment:type
   # recipe editor on the left
+  initial-recipe-contents:text <- slurp resources, [lesson/recipes.mu]  # ignore errors
   divider:num, _ <- divide-with-remainder width, 2
   recipes:&:editor <- new-editor initial-recipe-contents, 0/left, divider/right
   # sandbox editor on the right
   sandbox-left:num <- add divider, 1
-  current-sandbox:&:editor <- new-editor initial-sandbox-contents, sandbox-left, width/right
+  current-sandbox:&:editor <- new-editor test-sandbox-editor-contents, sandbox-left, width/right
   *result <- put *result, recipes:offset, recipes
   *result <- put *result, current-sandbox:offset, current-sandbox
   *result <- put *result, sandbox-in-focus?:offset, 0/false
   <programming-environment-initialization>
 ]
 
-def event-loop screen:&:screen, console:&:console, env:&:environment -> screen:&:screen, console:&:console, env:&:environment [
+def event-loop screen:&:screen, console:&:console, env:&:environment, resources:&:resources -> screen:&:screen, console:&:console, env:&:environment, resources:&:resources [
   local-scope
   load-ingredients
   recipes:&:editor <- get *env, recipes:offset
@@ -286,7 +284,12 @@ scenario point-at-multiple-editors [
   trace-until 100/app  # trace too long
   assume-screen 30/width, 5/height
   # initialize both halves of screen
-  env:&:environment <- new-programming-environment screen, [abc], [def]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |abc|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [def]  # contents of sandbox editor
   # focus on both sides
   assume-console [
     left-click 1, 1
@@ -294,7 +297,7 @@ scenario point-at-multiple-editors [
   ]
   # check cursor column in each
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     recipes:&:editor <- get *env, recipes:offset
     5:num/raw <- get *recipes, cursor-column:offset
     sandbox:&:editor <- get *env, current-sandbox:offset
@@ -311,7 +314,12 @@ scenario edit-multiple-editors [
   trace-until 100/app  # trace too long
   assume-screen 30/width, 5/height
   # initialize both halves of screen
-  env:&:environment <- new-programming-environment screen, [abc], [def]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |abc|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [def]  # contents of sandbox
   render-all screen, env, render
   # type one letter in each of them
   assume-console [
@@ -321,7 +329,7 @@ scenario edit-multiple-editors [
     type [1]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     recipes:&:editor <- get *env, recipes:offset
     5:num/raw <- get *recipes, cursor-column:offset
     sandbox:&:editor <- get *env, current-sandbox:offset
@@ -330,7 +338,8 @@ scenario edit-multiple-editors [
   screen-should-contain [
     .           run (F4)           .  # this line has a different background, but we don't test that yet
     .a0bc           ┊d1ef          .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊──────────────.
+    .               ┊──────────────.
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊              .
     .               ┊              .
   ]
   memory-should-contain [
@@ -345,39 +354,27 @@ scenario edit-multiple-editors [
   screen-should-contain [
     .           run (F4)           .
     .a0bc           ┊d1␣f          .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊──────────────.
+    .               ┊──────────────.
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊              .
     .               ┊              .
   ]
 ]
 
-scenario multiple-editors-cover-only-their-own-areas [
-  local-scope
-  trace-until 100/app  # trace too long
-  assume-screen 60/width, 10/height
-  run [
-    env:&:environment <- new-programming-environment screen, [abc], [def]
-    render-all screen, env, render
-  ]
-  # divider isn't messed up
-  screen-should-contain [
-    .                                         run (F4)           .
-    .abc                           ┊def                          .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────.
-    .                              ┊                             .
-    .                              ┊                             .
-  ]
-]
-
 scenario editor-in-focus-keeps-cursor [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 30/width, 5/height
-  env:&:environment <- new-programming-environment screen, [abc], [def]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |abc|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [def]
   render-all screen, env, render
   # initialize programming environment and highlight cursor
   assume-console []
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -385,7 +382,8 @@ scenario editor-in-focus-keeps-cursor [
   screen-should-contain [
     .           run (F4)           .
     .␣bc            ┊def           .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊──────────────.
+    .               ┊──────────────.
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊              .
     .               ┊              .
   ]
   # now try typing a letter
@@ -393,7 +391,7 @@ scenario editor-in-focus-keeps-cursor [
     type [z]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -401,7 +399,8 @@ scenario editor-in-focus-keeps-cursor [
   screen-should-contain [
     .           run (F4)           .
     .z␣bc           ┊def           .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊──────────────.
+    .               ┊──────────────.
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊              .
     .               ┊              .
   ]
 ]
@@ -410,10 +409,12 @@ scenario backspace-in-sandbox-editor-joins-lines [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 30/width, 5/height
+  assume-resources [
+  ]
   # initialize sandbox side with two lines
-  s:text <- new [abc
+  test-sandbox-editor-contents:text <- new [abc
 def]
-  env:&:environment <- new-programming-environment screen, [], s:text
+  env:&:environment <- new-programming-environment resources, screen, test-sandbox-editor-contents
   render-all screen, env, render
   screen-should-contain [
     .           run (F4)           .
@@ -428,7 +429,7 @@ def]
     press backspace
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
diff --git a/edit/005-sandbox.mu b/edit/005-sandbox.mu
index 0b1b82f1..7476875d 100644
--- a/edit/005-sandbox.mu
+++ b/edit/005-sandbox.mu
@@ -10,13 +10,10 @@
 def! main [
   local-scope
   open-console
-  initial-recipe:text <- restore [recipes.mu]
-  initial-sandbox:text <- new []
-  hide-screen 0/screen
-  env:&:environment <- new-programming-environment 0/screen, initial-recipe, initial-sandbox
+  env:&:environment <- new-programming-environment 0/filesystem, 0/screen
   env <- restore-sandboxes env
   render-all 0/screen, env, render
-  event-loop 0/screen, 0/console, env
+  event-loop 0/screen, 0/console, env, 0/filesystem
   # never gets here
 ]
 
@@ -46,14 +43,16 @@ scenario run-and-show-results [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
   # recipe editor is empty
+  assume-resources [
+  ]
   # sandbox editor contains an instruction without storing outputs
-  env:&:environment <- new-programming-environment screen, [], [divide-with-remainder 11, 3]
+  env:&:environment <- new-programming-environment resources, screen, [divide-with-remainder 11, 3]
   # run the code in the editors
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that screen prints the results
   screen-should-contain [
@@ -103,7 +102,7 @@ scenario run-and-show-results [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that screen prints both sandboxes
   screen-should-contain [
@@ -129,7 +128,7 @@ after <global-keypress> [
     do-run?:bool <- equal k, 65532/F4
     break-unless do-run?
     screen <- update-status screen, [running...       ], 245/grey
-    error?:bool, env, screen <- run-sandboxes env, screen
+    error?:bool <- run-sandboxes env, resources, screen
     # F4 might update warnings and results on both sides
     screen <- render-all screen, env, render
     {
@@ -141,10 +140,10 @@ after <global-keypress> [
   }
 ]
 
-def run-sandboxes env:&:environment, screen:&:screen -> errors-found?:bool, env:&:environment, screen:&:screen [
+def run-sandboxes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [
   local-scope
   load-ingredients
-  errors-found?:bool, env, screen <- update-recipes env, screen
+  errors-found?:bool <- update-recipes env, resources, screen
   return-if errors-found?
   # check contents of right editor (sandbox)
   <run-sandboxes-begin>
@@ -170,7 +169,7 @@ def run-sandboxes env:&:environment, screen:&:screen -> errors-found?:bool, env:
     *current-sandbox <- put *current-sandbox, top-of-screen:offset, init
   }
   # save all sandboxes before running, just in case we die when running
-  save-sandboxes env
+  save-sandboxes env, resources
   # run all sandboxes
   curr:&:sandbox <- get *env, sandbox:offset
   idx:num <- copy 0
@@ -184,14 +183,14 @@ def run-sandboxes env:&:environment, screen:&:screen -> errors-found?:bool, env:
   <run-sandboxes-end>
 ]
 
-# copy code from recipe editor, persist to disk, load
+# load code from disk
 # replaced in a later layer (whereupon errors-found? will actually be set)
-def update-recipes env:&:environment, screen:&:screen -> errors-found?:bool, env:&:environment, screen:&:screen [
+def update-recipes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [
   local-scope
   load-ingredients
   recipes:&:editor <- get *env, recipes:offset
   in:text <- editor-contents recipes
-  save [recipes.mu], in  # newlayer: persistence
+  resources <- dump resources, [lesson/recipes.mu], in
   reload in
   errors-found? <- copy 0/false
 ]
@@ -213,7 +212,7 @@ def update-status screen:&:screen, msg:text, color:num -> screen:&:screen [
   screen <- print screen, msg, color, 238/grey/background
 ]
 
-def save-sandboxes env:&:environment [
+def save-sandboxes env:&:environment, resources:&:resources -> resources:&:resources [
   local-scope
   load-ingredients
   current-sandbox:&:editor <- get *env, current-sandbox:offset
@@ -224,8 +223,8 @@ def save-sandboxes env:&:environment [
   {
     break-unless curr
     data:text <- get *curr, data:offset
-    filename:text <- to-text idx
-    save filename, data
+    filename:text <- append [lesson/], idx
+    resources <- dump resources, filename, data
     <end-save-sandbox>
     idx <- add idx, 1
     curr <- get *curr, next-sandbox:offset
@@ -415,7 +414,7 @@ def render-text screen:&:screen, s:text, left:num, right:num, color:num, row:num
 ]
 
 # assumes programming environment has no sandboxes; restores them from previous session
-def restore-sandboxes env:&:environment -> env:&:environment [
+def restore-sandboxes env:&:environment, resources:&:resources -> env:&:environment [
   local-scope
   load-ingredients
   # read all scenarios, pushing them to end of a list of scenarios
@@ -423,8 +422,8 @@ def restore-sandboxes env:&:environment -> env:&:environment [
   curr:&:sandbox <- copy 0
   prev:&:sandbox <- copy 0
   {
-    filename:text <- to-text idx
-    contents:text <- restore filename
+    filename:text <- append [lesson/], idx
+    contents:text <- slurp resources, filename
     break-unless contents  # stop at first error; assuming file didn't exist
                            # todo: handle empty sandbox
     # create new sandbox for file
@@ -519,27 +518,32 @@ scenario run-updates-results [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 12/height
   # define a recipe (no indent for the 'add' line below so column numbers are more obvious)
-  recipes:text <- new [ 
-recipe foo [
-local-scope
-z:num <- add 2, 2
-reply z
-]]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      ||
+      |recipe foo [|
+      |  local-scope|
+      |  z:num <- add 2, 2|
+      |  reply z|
+      |]|
+    ]
+  ]
   # sandbox editor contains an instruction without storing outputs
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  env:&:environment <- new-programming-environment resources, screen, [foo]  # contents of sandbox editor
   # run the code in the editors
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
     .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .local-scope                                       ┊0   edit          copy            delete         .
-    .z:num <- add 2, 2                                 ┊foo                                              .
-    .reply z                                           ┊4                                                .
+    .  local-scope                                     ┊0   edit          copy            delete         .
+    .  z:num <- add 2, 2                               ┊foo                                              .
+    .  reply z                                         ┊4                                                .
     .]                                                 ┊─────────────────────────────────────────────────.
+    .                                                  ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
     .                                                  ┊                                                 .
   ]
@@ -551,17 +555,18 @@ reply z
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that screen updates the result on the right
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
     .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .local-scope                                       ┊0   edit          copy            delete         .
-    .z:num <- add 2, 3                                 ┊foo                                              .
-    .reply z                                           ┊5                                                .
+    .  local-scope                                     ┊0   edit          copy            delete         .
+    .  z:num <- add 2, 3                               ┊foo                                              .
+    .  reply z                                         ┊5                                                .
     .]                                                 ┊─────────────────────────────────────────────────.
+    .                                                  ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
     .                                                  ┊                                                 .
   ]
@@ -571,15 +576,17 @@ scenario run-instruction-manages-screen-per-sandbox [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 20/height
-  # left editor is empty
-  # right editor contains an instruction
-  env:&:environment <- new-programming-environment screen, [], [print-integer screen, 4]
+  # empty recipes
+  assume-resources [
+  ]
+  # sandbox editor contains an instruction
+  env:&:environment <- new-programming-environment resources, screen, [print-integer screen, 4]  # contents of sandbox editor
   # run the code in the editor
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that it prints a little toy screen
   screen-should-contain [
@@ -642,13 +649,15 @@ scenario scrolling-down-past-bottom-of-recipe-editor [
   local-scope
   trace-until 100/app
   assume-screen 100/width, 10/height
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   assume-console [
     press enter
     press down-arrow
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   # no scroll
   screen-should-contain [
     .                                                                                 run (F4)           .
@@ -663,14 +672,16 @@ scenario cursor-down-in-recipe-editor [
   local-scope
   trace-until 100/app
   assume-screen 100/width, 10/height
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   assume-console [
     press enter
     press up-arrow
     press down-arrow  # while cursor isn't at bottom
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   cursor:char <- copy 9251/␣
   print screen, cursor
   # cursor moves back to bottom
@@ -741,7 +752,9 @@ scenario scrolling-down-past-bottom-of-recipe-editor-2 [
   local-scope
   trace-until 100/app
   assume-screen 100/width, 10/height
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   assume-console [
     # add a line
@@ -751,7 +764,7 @@ scenario scrolling-down-past-bottom-of-recipe-editor-2 [
     # try to scroll
     press page-down  # or ctrl-f
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   # no scroll, and cursor remains at top line
   screen-should-contain [
     .                                                                                 run (F4)           .
@@ -766,7 +779,9 @@ scenario scrolling-down-past-bottom-of-recipe-editor-3 [
   local-scope
   trace-until 100/app
   assume-screen 100/width, 10/height
-  env:&:environment <- new-programming-environment screen, [], [ab
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [ab
 cd]
   render-all screen, env, render
   assume-console [
@@ -777,7 +792,7 @@ cd]
     # move cursor
     press down-arrow
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   cursor:char <- copy 9251/␣
   print screen, cursor
   # no scroll on recipe side, cursor moves on sandbox side
@@ -796,14 +811,16 @@ scenario scrolling-down-past-bottom-of-sandbox-editor [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
-  # initialize sandbox side
-  env:&:environment <- new-programming-environment screen, [], [add 2, 2]
+  # initialize
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 2, 2]
   render-all screen, env, render
   assume-console [
     # create a sandbox
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
@@ -817,7 +834,7 @@ scenario scrolling-down-past-bottom-of-sandbox-editor [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -835,7 +852,7 @@ scenario scrolling-down-past-bottom-of-sandbox-editor [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -924,30 +941,33 @@ def previous-sandbox env:&:environment, in:&:sandbox -> out:&:sandbox [
   return curr
 ]
 
-scenario scrolling-down-on-recipe-side [
+scenario scrolling-down-past-bottom-on-recipe-side [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # initialize sandbox side and create a sandbox
-  recipes:text <- new [ 
-]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      ||  # file containing just a newline
+    ]
+  ]
   # create a sandbox
-  env:&:environment <- new-programming-environment screen, recipes:text, [add 2, 2]
+  env:&:environment <- new-programming-environment resources, screen, [add 2, 2]
   render-all screen, env, render
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   # hit 'down' in recipe editor
   assume-console [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
-  # cursor moves down on recipe side
+  # cursor doesn't move when the end is already on-screen
   screen-should-contain [
     .                                                                                 run (F4)           .
     .␣                                                 ┊                                                 .
@@ -962,7 +982,9 @@ scenario scrolling-through-multiple-sandboxes [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes
   assume-console [
@@ -972,7 +994,7 @@ scenario scrolling-through-multiple-sandboxes [
     type [add 1, 1]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   cursor:char <- copy 9251/␣
   print screen, cursor
   screen-should-contain [
@@ -992,7 +1014,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -1014,7 +1036,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # just second sandbox displayed
   screen-should-contain [
@@ -1031,7 +1053,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # no change
   screen-should-contain [
@@ -1048,7 +1070,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # back to displaying both sandboxes without editor
   screen-should-contain [
@@ -1067,7 +1089,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -1089,7 +1111,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -1113,7 +1135,9 @@ scenario scrolling-manages-sandbox-index-correctly [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create a sandbox
   assume-console [
@@ -1121,7 +1145,7 @@ scenario scrolling-manages-sandbox-index-correctly [
     type [add 1, 1]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
@@ -1137,7 +1161,7 @@ scenario scrolling-manages-sandbox-index-correctly [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # sandbox editor hidden; first sandbox displayed
   # cursor moves to first sandbox
@@ -1155,7 +1179,7 @@ scenario scrolling-manages-sandbox-index-correctly [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # back to displaying both sandboxes as well as editor
   screen-should-contain [
@@ -1173,7 +1197,7 @@ scenario scrolling-manages-sandbox-index-correctly [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # sandbox editor hidden; first sandbox displayed
   # cursor moves to first sandbox
diff --git a/edit/006-sandbox-copy.mu b/edit/006-sandbox-copy.mu
index c966ae78..9df5e625 100644
--- a/edit/006-sandbox-copy.mu
+++ b/edit/006-sandbox-copy.mu
@@ -5,24 +5,22 @@ scenario copy-a-sandbox-to-editor [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
-  # basic recipe
-  recipes:text <- new [ 
-recipe foo [
-  reply 4
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
   # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 1, 1                                         .
+    .                                                  ┊2                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -31,16 +29,16 @@ recipe foo [
     left-click 3, 69
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # it copies into editor
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .                                                  ┊add 1, 1                                         .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 1, 1                                         .
+    .                                                  ┊2                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -49,15 +47,15 @@ recipe foo [
     type [0]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊0foo                                             .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .                                                  ┊0add 1, 1                                        .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 1, 1                                         .
+    .                                                  ┊2                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -67,24 +65,22 @@ scenario copy-a-sandbox-to-editor-2 [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
-  # basic recipe
-  recipes:text <- new [ 
-recipe foo [
-  reply 4
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
   # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 1, 1                                         .
+    .                                                  ┊2                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -93,16 +89,16 @@ recipe foo [
     left-click 3, 84
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # it copies into editor
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .                                                  ┊add 1, 1                                         .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 1, 1                                         .
+    .                                                  ┊2                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -111,15 +107,15 @@ recipe foo [
     type [0]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊0foo                                             .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .                                                  ┊0add 1, 1                                        .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 1, 1                                         .
+    .                                                  ┊2                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -224,24 +220,22 @@ scenario copy-fails-if-sandbox-editor-not-empty [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
-  # basic recipe
-  recipes:text <- new [ 
-recipe foo [
-  reply 4
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
   # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 1, 1                                         .
+    .                                                  ┊2                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -252,16 +246,16 @@ recipe foo [
     left-click 3, 70  # click 'copy' button
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # copy doesn't happen
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊0                                                .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 1, 1                                         .
+    .                                                  ┊2                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -270,15 +264,15 @@ recipe foo [
     type [1]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊01                                               .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 1, 1                                         .
+    .                                                  ┊2                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
diff --git a/edit/007-sandbox-delete.mu b/edit/007-sandbox-delete.mu
index 555d2859..4fa3c37d 100644
--- a/edit/007-sandbox-delete.mu
+++ b/edit/007-sandbox-delete.mu
@@ -4,7 +4,9 @@ scenario deleting-sandboxes [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   # run a few commands
   assume-console [
     left-click 1, 80
@@ -13,7 +15,7 @@ scenario deleting-sandboxes [
     type [add 2, 2]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
@@ -34,7 +36,7 @@ scenario deleting-sandboxes [
     left-click 7, 85
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
@@ -52,7 +54,7 @@ scenario deleting-sandboxes [
     left-click 3, 99
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
@@ -151,7 +153,9 @@ scenario deleting-sandbox-after-scroll [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes and scroll to second
   assume-console [
@@ -162,7 +166,7 @@ scenario deleting-sandbox-after-scroll [
     press F4
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊─────────────────────────────────────────────────.
@@ -177,7 +181,7 @@ scenario deleting-sandbox-after-scroll [
     left-click 6, 99
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # second sandbox shows in editor; scroll resets to display first sandbox
   screen-should-contain [
@@ -196,7 +200,9 @@ scenario deleting-top-sandbox-after-scroll [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes and scroll to second
   assume-console [
@@ -207,7 +213,7 @@ scenario deleting-top-sandbox-after-scroll [
     press F4
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊─────────────────────────────────────────────────.
@@ -222,7 +228,7 @@ scenario deleting-top-sandbox-after-scroll [
     left-click 2, 99
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # second sandbox shows in editor; scroll resets to display first sandbox
   screen-should-contain [
@@ -241,7 +247,9 @@ scenario deleting-final-sandbox-after-scroll [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes and scroll to second
   assume-console [
@@ -253,7 +261,7 @@ scenario deleting-final-sandbox-after-scroll [
     press page-down
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊─────────────────────────────────────────────────.
@@ -268,7 +276,7 @@ scenario deleting-final-sandbox-after-scroll [
     left-click 2, 99
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # implicitly scroll up to first sandbox
   screen-should-contain [
@@ -288,7 +296,9 @@ scenario deleting-updates-sandbox-count [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes
   assume-console [
@@ -298,7 +308,7 @@ scenario deleting-updates-sandbox-count [
     type [add 1, 1]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
@@ -318,7 +328,7 @@ scenario deleting-updates-sandbox-count [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # shouldn't go past last sandbox
   screen-should-contain [
diff --git a/edit/008-sandbox-edit.mu b/edit/008-sandbox-edit.mu
index dcd0967d..a857e88c 100644
--- a/edit/008-sandbox-edit.mu
+++ b/edit/008-sandbox-edit.mu
@@ -1,27 +1,25 @@
 ## editing sandboxes after they've been created
 
-scenario clicking-on-a-sandbox-moves-it-to-editor [
+scenario clicking-on-sandbox-edit-button-moves-it-to-editor [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
-  # basic recipe
-  recipes:text <- new [ 
-recipe foo [
-  reply 4
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 2, 2]
   # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 2, 2                                         .
+    .                                                  ┊4                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -30,16 +28,13 @@ recipe foo [
     left-click 3, 55
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # it pops back into editor
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊                                                 .
-    .]                                                 ┊                                                 .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
+    .                                                  ┊add 2, 2                                         .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
   # cursor should be in the right place
@@ -47,41 +42,36 @@ recipe foo [
     type [0]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊0foo                                             .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊                                                 .
-    .]                                                 ┊                                                 .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
+    .                                                  ┊0add 2, 2                                        .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
 ]
 
-scenario clicking-on-a-sandbox-moves-it-to-editor-2 [
+scenario clicking-on-sandbox-edit-button-moves-it-to-editor-2 [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
-  # basic recipe
-  recipes:text <- new [ 
-recipe foo [
-  reply 4
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 2, 2]
   # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊0   edit          copy            delete         .
+    .                                                  ┊add 2, 2                                         .
+    .                                                  ┊4                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -90,16 +80,13 @@ recipe foo [
     left-click 3, 68
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # it pops back into editor
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊                                                 .
-    .]                                                 ┊                                                 .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
+    .                                                  ┊add 2, 2                                         .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
   # cursor should be in the right place
@@ -107,15 +94,12 @@ recipe foo [
     type [0]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊0foo                                             .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊                                                 .
-    .]                                                 ┊                                                 .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
+    .                                                  ┊0add 2, 2                                        .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
 ]
@@ -178,13 +162,15 @@ scenario sandbox-with-print-can-be-edited [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 20/height
   # left editor is empty
-  # right editor contains an instruction
-  env:&:environment <- new-programming-environment screen, [], [print-integer screen, 4]
+  assume-resources [
+  ]
+  # right editor contains a print instruction
+  env:&:environment <- new-programming-environment resources, screen, [print-integer screen, 4]
   # run the sandbox
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
@@ -205,7 +191,7 @@ scenario sandbox-with-print-can-be-edited [
     left-click 3, 65
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
@@ -221,7 +207,9 @@ scenario editing-sandbox-after-scrolling-resets-scroll [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes and scroll to second
   assume-console [
@@ -233,7 +221,7 @@ scenario editing-sandbox-after-scrolling-resets-scroll [
     press page-down
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊─────────────────────────────────────────────────.
@@ -248,7 +236,7 @@ scenario editing-sandbox-after-scrolling-resets-scroll [
     left-click 2, 55
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # second sandbox shows in editor; scroll resets to display first sandbox
   screen-should-contain [
@@ -268,7 +256,9 @@ scenario editing-sandbox-updates-sandbox-count [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes
   assume-console [
@@ -278,7 +268,7 @@ scenario editing-sandbox-updates-sandbox-count [
     type [add 1, 1]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
@@ -297,7 +287,7 @@ scenario editing-sandbox-updates-sandbox-count [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # no change in contents
   screen-should-contain [
@@ -319,7 +309,7 @@ scenario editing-sandbox-updates-sandbox-count [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # screen should show just final sandbox with the right index (1)
   screen-should-contain [
diff --git a/edit/009-sandbox-test.mu b/edit/009-sandbox-test.mu
index fe9ef059..39b58ecb 100644
--- a/edit/009-sandbox-test.mu
+++ b/edit/009-sandbox-test.mu
@@ -5,22 +5,25 @@ scenario sandbox-click-on-result-toggles-color-to-green [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # basic recipe
-  recipes:text <- new [ 
-recipe foo [
-  reply 4
-]]
-  env:&:environment <- new-programming-environment screen, recipes:text, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  reply 4|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
+    .recipe foo [                                      ┊                                                 .
+    .  reply 4                                         ┊─────────────────────────────────────────────────.
+    .]                                                 ┊0   edit          copy            delete         .
+    .                                                  ┊foo                                              .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
@@ -30,7 +33,7 @@ recipe foo [
     left-click 5, 51
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # color toggles to green
   screen-should-contain-in-color 2/green, [
@@ -50,26 +53,24 @@ recipe foo [
   ]
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .␣                                                 ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  reply 4                                         ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
+    .␣ecipe foo [                                      ┊                                                 .
+    .  reply 4                                         ┊─────────────────────────────────────────────────.
+    .]                                                 ┊0   edit          copy            delete         .
+    .                                                  ┊foo                                              .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊4                                                .
     .                                                  ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
-    .                                                  ┊                                                 .
-    .                                                  ┊                                                 .
   ]
   # now change the result
   # then rerun
   assume-console [
-    left-click 3, 11  # cursor to end of line
+    left-click 2, 11  # cursor to end of line
     press backspace
     type [3]
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # result turns red
   screen-should-contain-in-color 1/red, [
@@ -96,14 +97,14 @@ before <end-save-sandbox> [
     expected-response:text <- get *curr, expected-response:offset
     break-unless expected-response
     filename <- append filename, [.out]
-    save filename, expected-response
+    resources <- dump resources, filename, expected-response
   }
 ]
 
 before <end-restore-sandbox> [
   {
     filename <- append filename, [.out]
-    contents <- restore filename
+    contents <- slurp resources, filename
     break-unless contents
     *curr <- put *curr, expected-response:offset, contents
   }
@@ -128,7 +129,7 @@ after <global-touch> [
     break-unless sandbox
     # toggle its expected-response, and save session
     sandbox <- toggle-expected-response sandbox
-    save-sandboxes env
+    save-sandboxes env, resources
     hide-screen screen
     screen <- render-sandbox-side screen, env, render
     screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
diff --git a/edit/010-sandbox-trace.mu b/edit/010-sandbox-trace.mu
index 90ee417e..65337127 100644
--- a/edit/010-sandbox-trace.mu
+++ b/edit/010-sandbox-trace.mu
@@ -5,22 +5,25 @@ scenario sandbox-click-on-code-toggles-app-trace [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # basic recipe
-  recipes:text <- new [ 
-recipe foo [
-  stash [abc]
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  stash [abc]|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  stash [abc]                                     ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
+    .recipe foo [                                      ┊                                                 .
+    .  stash [abc]                                     ┊─────────────────────────────────────────────────.
+    .]                                                 ┊0   edit          copy            delete         .
+    .                                                  ┊foo                                              .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -29,17 +32,17 @@ recipe foo [
     left-click 4, 51
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
   # trace now printed and cursor shouldn't have budged
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .␣                                                 ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  stash [abc]                                     ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
+    .␣ecipe foo [                                      ┊                                                 .
+    .  stash [abc]                                     ┊─────────────────────────────────────────────────.
+    .]                                                 ┊0   edit          copy            delete         .
+    .                                                  ┊foo                                              .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊abc                                              .
   ]
   screen-should-contain-in-color 245/grey, [
@@ -55,16 +58,16 @@ recipe foo [
     left-click 4, 55
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     print screen, cursor
   ]
   # trace hidden again
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .␣                                                 ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  stash [abc]                                     ┊0   edit          copy            delete         .
-    .]                                                 ┊foo                                              .
+    .␣ecipe foo [                                      ┊                                                 .
+    .  stash [abc]                                     ┊─────────────────────────────────────────────────.
+    .]                                                 ┊0   edit          copy            delete         .
+    .                                                  ┊foo                                              .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -75,24 +78,27 @@ scenario sandbox-shows-app-trace-and-result [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # basic recipe
-  recipes:text <- new [ 
-recipe foo [
-  stash [abc]
-  reply 4
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  stash [abc]|
+      |  reply 4|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  stash [abc]                                     ┊0   edit          copy            delete         .
-    .  reply 4                                         ┊foo                                              .
-    .]                                                 ┊4                                                .
+    .recipe foo [                                      ┊                                                 .
+    .  stash [abc]                                     ┊─────────────────────────────────────────────────.
+    .  reply 4                                         ┊0   edit          copy            delete         .
+    .]                                                 ┊foo                                              .
+    .                                                  ┊4                                                .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
   ]
@@ -101,16 +107,16 @@ recipe foo [
     left-click 4, 51
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # trace now printed above result
   screen-should-contain [
     .                                                                                 run (F4)           .
-    .                                                  ┊                                                 .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  stash [abc]                                     ┊0   edit          copy            delete         .
-    .  reply 4                                         ┊foo                                              .
-    .]                                                 ┊abc                                              .
+    .recipe foo [                                      ┊                                                 .
+    .  stash [abc]                                     ┊─────────────────────────────────────────────────.
+    .  reply 4                                         ┊0   edit          copy            delete         .
+    .]                                                 ┊foo                                              .
+    .                                                  ┊abc                                              .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊8 instructions run                               .
     .                                                  ┊4                                                .
     .                                                  ┊─────────────────────────────────────────────────.
@@ -122,13 +128,15 @@ scenario clicking-on-app-trace-does-nothing [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
-  env:&:environment <- new-programming-environment screen, [], [stash 123456789]
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [stash 123456789]
   # create and expand the trace
   assume-console [
     press F4
     left-click 4, 51
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                                                                                 run (F4)           .
     .                                                  ┊                                                 .
@@ -142,7 +150,7 @@ scenario clicking-on-app-trace-does-nothing [
     left-click 5, 57
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # no change; doesn't die
   screen-should-contain [
diff --git a/edit/011-errors.mu b/edit/011-errors.mu
index b48cd5fa..ad4d8836 100644
--- a/edit/011-errors.mu
+++ b/edit/011-errors.mu
@@ -5,12 +5,12 @@ container environment [
 ]
 
 # copy code from recipe editor, persist to disk, load, save any errors
-def! update-recipes env:&:environment, screen:&:screen -> errors-found?:bool, env:&:environment, screen:&:screen [
+def! update-recipes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [
   local-scope
   load-ingredients
   recipes:&:editor <- get *env, recipes:offset
   in:text <- editor-contents recipes
-  save [recipes.mu], in
+  resources <- dump resources, [lesson/recipes.mu], in
   recipe-errors:text <- reload in
   *env <- put *env, recipe-errors:offset, recipe-errors
   # if recipe editor has errors, stop
@@ -118,23 +118,36 @@ scenario run-shows-errors-in-get [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
-  recipes:text <- new [ 
-recipe foo [
-  get 123:num, foo:offset
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  get 123:num, foo:offset|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
+  render-all screen, env, render
+  screen-should-contain [
+    .                                                                                 run (F4)           .
+    .recipe foo [                                      ┊foo                                              .
+    .  get 123:num, foo:offset                         ┊─────────────────────────────────────────────────.
+    .]                                                 ┊                                                 .
+    .                                                  ┊                                                 .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
+    .                                                  ┊                                                 .
+  ]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                                                                   run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  get 123:num, foo:offset                         ┊                                                 .
+    .recipe foo [                                      ┊foo                                              .
+    .  get 123:num, foo:offset                         ┊─────────────────────────────────────────────────.
     .]                                                 ┊                                                 .
+    .                                                  ┊                                                 .
     .foo: unknown element 'foo' in container 'number'  ┊                                                 .
     .foo: first ingredient of 'get' should be a contai↩┊                                                 .
     .ner, but got '123:num'                            ┊                                                 .
@@ -158,7 +171,9 @@ scenario run-updates-status-with-first-erroneous-sandbox [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   assume-console [
     left-click 3, 80
     # create invalid sandbox 1
@@ -169,7 +184,7 @@ scenario run-updates-status-with-first-erroneous-sandbox [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # status line shows that error is in first sandbox
   screen-should-contain [
@@ -181,7 +196,9 @@ scenario run-updates-status-with-first-erroneous-sandbox-2 [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
-  env:&:environment <- new-programming-environment screen, [], []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   assume-console [
     left-click 3, 80
     # create invalid sandbox 2
@@ -195,7 +212,7 @@ scenario run-updates-status-with-first-erroneous-sandbox-2 [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # status line shows that error is in second sandbox
   screen-should-contain [
@@ -207,13 +224,13 @@ scenario run-hides-errors-from-past-sandboxes [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
-  env:&:environment <- new-programming-environment screen, [], [get foo, x:offset]  # invalid
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [get foo, x:offset]  # invalid
   assume-console [
     press F4  # generate error
   ]
-  run [
-    event-loop screen, console, env
-  ]
+  event-loop screen, console, env, resources
   assume-console [
     left-click 3, 58
     press ctrl-k
@@ -221,7 +238,7 @@ scenario run-hides-errors-from-past-sandboxes [
     press F4  # update sandbox
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # error should disappear
   screen-should-contain [
@@ -241,26 +258,31 @@ scenario run-updates-errors-for-shape-shifting-recipes [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
   # define a shape-shifting recipe with an error
-  recipes:text <- new [recipe foo x:_elem -> z:_elem [
-local-scope
-load-ingredients
-y:&:num <- copy 0
-z <- add x, y
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo 2]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo x:_elem -> z:_elem [|
+      |  local-scope|
+      |  load-ingredients|
+      |  y:&:num <- copy 0|
+      |  z <- add x, y|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo 2]
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .  errors found (0)                                                               run (F4)           .
     .recipe foo x:_elem -> z:_elem [                   ┊                                                 .
-    .local-scope                                       ┊─────────────────────────────────────────────────.
-    .load-ingredients                                  ┊0   edit          copy            delete         .
-    .y:&:num <- copy 0                                 ┊foo 2                                            .
-    .z <- add x, y                                     ┊foo_2: 'add' requires number ingredients, but go↩.
+    .  local-scope                                     ┊─────────────────────────────────────────────────.
+    .  load-ingredients                                ┊0   edit          copy            delete         .
+    .  y:&:num <- copy 0                               ┊foo 2                                            .
+    .  z <- add x, y                                   ┊foo_2: 'add' requires number ingredients, but go↩.
     .]                                                 ┊t 'y'                                            .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊─────────────────────────────────────────────────.
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
     .                                                  ┊                                                 .
   ]
   # now rerun everything
@@ -268,18 +290,19 @@ z <- add x, y
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # error should remain unchanged
   screen-should-contain [
     .  errors found (0)                                                               run (F4)           .
     .recipe foo x:_elem -> z:_elem [                   ┊                                                 .
-    .local-scope                                       ┊─────────────────────────────────────────────────.
-    .load-ingredients                                  ┊0   edit          copy            delete         .
-    .y:&:num <- copy 0                                 ┊foo 2                                            .
-    .z <- add x, y                                     ┊foo_3: 'add' requires number ingredients, but go↩.
+    .  local-scope                                     ┊─────────────────────────────────────────────────.
+    .  load-ingredients                                ┊0   edit          copy            delete         .
+    .  y:&:num <- copy 0                               ┊foo 2                                            .
+    .  z <- add x, y                                   ┊foo_3: 'add' requires number ingredients, but go↩.
     .]                                                 ┊t 'y'                                            .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊─────────────────────────────────────────────────.
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
     .                                                  ┊                                                 .
   ]
 ]
@@ -289,17 +312,21 @@ scenario run-avoids-spurious-errors-on-reloading-shape-shifting-recipes [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
   # overload a well-known shape-shifting recipe
-  recipes:text <- new [recipe length l:&:list:_elem -> n:num [
-]]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe length l:&:list:_elem -> n:num [|
+      |]|
+    ]
+  ]
   # call code that uses other variants of it, but not it itself
-  sandbox:text <- new [x:&:list:num <- copy 0
+  test-sandbox:text <- new [x:&:list:num <- copy 0
 to-text x]
-  env:&:environment <- new-programming-environment screen, recipes, sandbox
+  env:&:environment <- new-programming-environment resources, screen, test-sandbox
   # run it once
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   # no errors anywhere on screen (can't check anything else, since to-text will return an address)
   screen-should-contain-in-color 1/red, [
     .                                                                                                    .
@@ -323,7 +350,7 @@ to-text x]
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # still no errors
   screen-should-contain-in-color 1/red, [
@@ -349,24 +376,30 @@ scenario run-shows-missing-type-errors [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
-  recipes:text <- new [ 
-recipe foo [
-  x <- copy 0
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  x <- copy 0|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                                                                   run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  x <- copy 0                                     ┊                                                 .
+    .recipe foo [                                      ┊foo                                              .
+    .  x <- copy 0                                     ┊─────────────────────────────────────────────────.
     .]                                                 ┊                                                 .
+    .                                                  ┊                                                 .
     .foo: missing type for 'x' in 'x <- copy 0'        ┊                                                 .
+    .foo: can't copy '0' to 'x'; types don't match     ┊                                                 .
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
+    .                                                  ┊                                                 .
   ]
 ]
 
@@ -375,22 +408,23 @@ scenario run-shows-unbalanced-bracket-errors [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
   # recipe is incomplete (unbalanced '[')
-  recipes:text <- new [ 
-recipe foo \\[
-  x <- copy 0
-]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo \\\[|
+      |  x <- copy 0|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                                                                   run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo \\[                                      ┊─────────────────────────────────────────────────.
-    .  x <- copy 0                                     ┊                                                 .
+    .recipe foo \\[                                      ┊foo                                              .
+    .  x <- copy 0                                     ┊─────────────────────────────────────────────────.
     .                                                  ┊                                                 .
     .9: unbalanced '\\[' for recipe                      ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
@@ -402,27 +436,30 @@ scenario run-shows-get-on-non-container-errors [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
-  recipes:text <- new [ 
-recipe foo [
-  local-scope
-  x:&:point <- new point:type
-  get x:&:point, 1:offset
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  local-scope|
+      |  x:&:point <- new point:type|
+      |  get x:&:point, 1:offset|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                                                                   run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  local-scope                                     ┊                                                 .
+    .recipe foo [                                      ┊foo                                              .
+    .  local-scope                                     ┊─────────────────────────────────────────────────.
     .  x:&:point <- new point:type                     ┊                                                 .
     .  get x:&:point, 1:offset                         ┊                                                 .
     .]                                                 ┊                                                 .
+    .                                                  ┊                                                 .
     .foo: first ingredient of 'get' should be a contai↩┊                                                 .
     .ner, but got 'x:&:point'                          ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
@@ -434,29 +471,32 @@ scenario run-shows-non-literal-get-argument-errors [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 15/height
-  recipes:text <- new [ 
-recipe foo [
-  local-scope
-  x:num <- copy 0
-  y:&:point <- new point:type
-  get *y:&:point, x:num
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  local-scope|
+      |  x:num <- copy 0|
+      |  y:&:point <- new point:type|
+      |  get *y:&:point, x:num|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                                                                   run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  local-scope                                     ┊                                                 .
+    .recipe foo [                                      ┊foo                                              .
+    .  local-scope                                     ┊─────────────────────────────────────────────────.
     .  x:num <- copy 0                                 ┊                                                 .
     .  y:&:point <- new point:type                     ┊                                                 .
     .  get *y:&:point, x:num                           ┊                                                 .
     .]                                                 ┊                                                 .
+    .                                                  ┊                                                 .
     .foo: second ingredient of 'get' should have type ↩┊                                                 .
     .'offset', but got 'x:num'                         ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
@@ -467,25 +507,28 @@ recipe foo [
 scenario run-shows-errors-everytime [
   local-scope
   trace-until 100/app  # trace too long
-  # try to run a file with an error
   assume-screen 100/width, 15/height
-  recipes:text <- new [ 
-recipe foo [
-  local-scope
-  x:num <- copy y:num
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  # try to run a file with an error
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  local-scope|
+      |  x:num <- copy y:num|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .  errors found                                                                   run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  local-scope                                     ┊                                                 .
+    .recipe foo [                                      ┊foo                                              .
+    .  local-scope                                     ┊─────────────────────────────────────────────────.
     .  x:num <- copy y:num                             ┊                                                 .
     .]                                                 ┊                                                 .
+    .                                                  ┊                                                 .
     .foo: tried to read ingredient 'y' in 'x:num <- co↩┊                                                 .
     .py y:num' but it hasn't been written to yet       ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
@@ -496,15 +539,15 @@ recipe foo [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                                                                   run (F4)           .
-    .                                                  ┊foo                                              .
-    .recipe foo [                                      ┊─────────────────────────────────────────────────.
-    .  local-scope                                     ┊                                                 .
+    .recipe foo [                                      ┊foo                                              .
+    .  local-scope                                     ┊─────────────────────────────────────────────────.
     .  x:num <- copy y:num                             ┊                                                 .
     .]                                                 ┊                                                 .
+    .                                                  ┊                                                 .
     .foo: tried to read ingredient 'y' in 'x:num <- co↩┊                                                 .
     .py y:num' but it hasn't been written to yet       ┊                                                 .
     .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
@@ -516,15 +559,15 @@ scenario run-instruction-and-print-errors [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
-  # right editor contains an illegal instruction
-  sandbox:text <- new [get 1234:num, foo:offset]
-  env:&:environment <- new-programming-environment screen, [], sandbox
-  # run the code in the editors
+  assume-resources [
+  ]
+  # sandbox editor contains an illegal instruction
+  env:&:environment <- new-programming-environment resources, screen, [get 1234:num, foo:offset]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that screen prints error message in red
   screen-should-contain [
@@ -578,16 +621,17 @@ scenario run-instruction-and-print-errors-only-once [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
-  # right editor contains an illegal instruction
-  sandbox:text <- new [get 1234:num, foo:offset]
-  env:&:environment <- new-programming-environment screen, [], sandbox
+  assume-resources [
+  ]
+  # sandbox editor contains an illegal instruction
+  env:&:environment <- new-programming-environment resources, screen, [get 1234:num, foo:offset]
   # run the code in the editors multiple times
   assume-console [
     press F4
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that screen prints error message just once
   screen-should-contain [
@@ -608,20 +652,23 @@ scenario sandbox-can-handle-infinite-loop [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 100/width, 20/height
-  # left editor is empty
-  recipes:text <- new [recipe foo [
-  {
-    loop
-  }
-]]
-  # right editor contains an instruction
-  env:&:environment <- new-programming-environment screen, recipes, [foo]
+  # sandbox editor will trigger an infinite loop
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  {|
+      |    loop|
+      |  }|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   # run the sandbox
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found (0)                                                               run (F4)           .
@@ -630,7 +677,8 @@ scenario sandbox-can-handle-infinite-loop [
     .    loop                                          ┊0   edit          copy            delete         .
     .  }                                               ┊foo                                              .
     .]                                                 ┊took too long!                                   .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊─────────────────────────────────────────────────.
+    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊                                                 .
     .                                                  ┊                                                 .
   ]
 ]
@@ -640,50 +688,55 @@ scenario sandbox-with-errors-shows-trace [
   trace-until 100/app  # trace too long
   assume-screen 100/width, 10/height
   # generate a stash and a error
-  recipes:text <- new [recipe foo [
-local-scope
-a:num <- next-ingredient
-b:num <- next-ingredient
-stash [dividing by], b
-_, c:num <- divide-with-remainder a, b
-reply b
-]]
-  env:&:environment <- new-programming-environment screen, recipes, [foo 4, 0]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  local-scope|
+      |  a:num <- next-ingredient|
+      |  b:num <- next-ingredient|
+      |  stash [dividing by], b|
+      |  _, c:num <- divide-with-remainder a, b|
+      |  reply b|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo 4, 0]
   # run
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   # screen prints error message
   screen-should-contain [
     .  errors found (0)                                                               run (F4)           .
     .recipe foo [                                      ┊                                                 .
-    .local-scope                                       ┊─────────────────────────────────────────────────.
-    .a:num <- next-ingredient                          ┊0   edit          copy            delete         .
-    .b:num <- next-ingredient                          ┊foo 4, 0                                         .
-    .stash [dividing by], b                            ┊foo: divide by zero in '_, c:num <- divide-with-↩.
-    ._, c:num <- divide-with-remainder a, b            ┊remainder a, b'                                  .
-    .reply b                                           ┊─────────────────────────────────────────────────.
+    .  local-scope                                     ┊─────────────────────────────────────────────────.
+    .  a:num <- next-ingredient                        ┊0   edit          copy            delete         .
+    .  b:num <- next-ingredient                        ┊foo 4, 0                                         .
+    .  stash [dividing by], b                          ┊foo: divide by zero in '_, c:num <- divide-with-↩.
+    .  _, c:num <- divide-with-remainder a, b          ┊remainder a, b'                                  .
+    .  reply b                                         ┊─────────────────────────────────────────────────.
     .]                                                 ┊                                                 .
+    .                                                  ┊                                                 .
   ]
   # click on the call in the sandbox
   assume-console [
     left-click 4, 55
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # screen should expand trace
   screen-should-contain [
     .  errors found (0)                                                               run (F4)           .
     .recipe foo [                                      ┊                                                 .
-    .local-scope                                       ┊─────────────────────────────────────────────────.
-    .a:num <- next-ingredient                          ┊0   edit          copy            delete         .
-    .b:num <- next-ingredient                          ┊foo 4, 0                                         .
-    .stash [dividing by], b                            ┊dividing by 0                                    .
-    ._, c:num <- divide-with-remainder a, b            ┊14 instructions run                              .
-    .reply b                                           ┊foo: divide by zero in '_, c:num <- divide-with-↩.
+    .  local-scope                                     ┊─────────────────────────────────────────────────.
+    .  a:num <- next-ingredient                        ┊0   edit          copy            delete         .
+    .  b:num <- next-ingredient                        ┊foo 4, 0                                         .
+    .  stash [dividing by], b                          ┊dividing by 0                                    .
+    .  _, c:num <- divide-with-remainder a, b          ┊14 instructions run                              .
+    .  reply b                                         ┊foo: divide by zero in '_, c:num <- divide-with-↩.
     .]                                                 ┊remainder a, b'                                  .
-    .┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┊─────────────────────────────────────────────────.
+    .                                                  ┊─────────────────────────────────────────────────.
   ]
 ]
diff --git a/html/088file.mu.html b/html/088file.mu.html
index 49801745..aae7cae6 100644
--- a/html/088file.mu.html
+++ b/html/088file.mu.html
@@ -33,6 +33,13 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
 <pre id='vimCodeElement'>
 <span class="Comment"># Wrappers around file system primitives that take a 'resources' object and</span>
 <span class="Comment"># are thus easier to test.</span>
+<span class="Comment">#</span>
+<span class="Comment"># - start-reading - asynchronously open a file, returning a channel source for</span>
+<span class="Comment">#   receiving the results</span>
+<span class="Comment"># - start-writing - asynchronously open a file, returning a channel sink for</span>
+<span class="Comment">#   the data to write</span>
+<span class="Comment"># - slurp - synchronously read from a file</span>
+<span class="Comment"># - dump - synchronously write to a file</span>
 
 <span class="muData">container</span> resources [
   lock:bool
@@ -44,25 +51,42 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   contents:text
 ]
 
-<span class="muRecipe">def</span> start-reading resources:&amp;:resources, filename:text<span class="muRecipe"> -&gt; </span>contents:&amp;:source:char [
+<span class="muRecipe">def</span> start-reading resources:&amp;:resources, filename:text<span class="muRecipe"> -&gt; </span>contents:&amp;:source:char, error?:bool [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
+  error? <span class="Special">&lt;-</span> copy <span class="Constant">0/false</span>
   <span class="Delimiter">{</span>
     <span class="muControl">break-unless</span> resources
     <span class="Comment"># fake file system</span>
-    contents <span class="Special">&lt;-</span> start-reading-from-fake-resources resources, filename
+    contents, error? <span class="Special">&lt;-</span> start-reading-from-fake-resource resources, filename
     <span class="muControl">return</span>
   <span class="Delimiter">}</span>
   <span class="Comment"># real file system</span>
   file:num <span class="Special">&lt;-</span> $open-file-for-reading filename
-  assert file, <span class="Constant">[file not found]</span>
+  <span class="muControl">return-unless</span> file, <span class="Constant">0/contents</span>, <span class="Constant">1/error?</span>
   contents:&amp;:source:char, sink:&amp;:sink:char <span class="Special">&lt;-</span> new-channel<span class="Constant"> 30</span>
   start-running receive-from-file file, sink
 ]
 
-<span class="muRecipe">def</span> start-reading-from-fake-resources resources:&amp;:resources, resource:text<span class="muRecipe"> -&gt; </span>contents:&amp;:source:char [
+<span class="muRecipe">def</span> slurp resources:&amp;:resources, filename:text<span class="muRecipe"> -&gt; </span>contents:text, error?:bool [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
+  source:&amp;:source:char, error?:bool <span class="Special">&lt;-</span> start-reading resources, filename
+  <span class="muControl">return-if</span> error?, <span class="Constant">0/contents</span>
+  buf:&amp;:buffer <span class="Special">&lt;-</span> new-buffer <span class="Constant">30/capacity</span>
+  <span class="Delimiter">{</span>
+    c:char, done?:bool, source <span class="Special">&lt;-</span> read source
+    <span class="muControl">break-if</span> done?
+    buf <span class="Special">&lt;-</span> append buf, c
+    <span class="muControl">loop</span>
+  <span class="Delimiter">}</span>
+  contents <span class="Special">&lt;-</span> buffer-to-array buf
+]
+
+<span class="muRecipe">def</span> start-reading-from-fake-resource resources:&amp;:resources, resource:text<span class="muRecipe"> -&gt; </span>contents:&amp;:source:char, error?:bool [
+  <span class="Constant">local-scope</span>
+  <span class="Constant">load-ingredients</span>
+  error? <span class="Special">&lt;-</span> copy <span class="Constant">0/no-error</span>
   i:num <span class="Special">&lt;-</span> copy<span class="Constant"> 0</span>
   data:&amp;:@:resource <span class="Special">&lt;-</span> get *resources, <span class="Constant">data:offset</span>
   len:num <span class="Special">&lt;-</span> length *data
@@ -79,7 +103,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     start-running receive-from-text curr-contents, sink
     <span class="muControl">return</span>
   <span class="Delimiter">}</span>
-  <span class="muControl">return</span> <span class="Constant">0/not-found</span>
+  <span class="muControl">return</span> <span class="Constant">0/not-found</span>, <span class="Constant">1/error</span>
 ]
 
 <span class="muRecipe">def</span> receive-from-file file:num, sink:&amp;:sink:char<span class="muRecipe"> -&gt; </span>sink:&amp;:sink:char [
@@ -111,18 +135,20 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   sink <span class="Special">&lt;-</span> close sink
 ]
 
-<span class="muRecipe">def</span> start-writing resources:&amp;:resources, filename:text<span class="muRecipe"> -&gt; </span>sink:&amp;:sink:char, routine-id:num [
+<span class="muRecipe">def</span> start-writing resources:&amp;:resources, filename:text<span class="muRecipe"> -&gt; </span>sink:&amp;:sink:char, routine-id:num, error?:bool [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
+  error? <span class="Special">&lt;-</span> copy <span class="Constant">0/false</span>
   source:&amp;:source:char, sink:&amp;:sink:char <span class="Special">&lt;-</span> new-channel<span class="Constant"> 30</span>
   <span class="Delimiter">{</span>
     <span class="muControl">break-unless</span> resources
     <span class="Comment"># fake file system</span>
-    routine-id <span class="Special">&lt;-</span> start-running transmit-to-fake-file resources, filename, source
+    routine-id <span class="Special">&lt;-</span> start-running transmit-to-fake-resource resources, filename, source
     <span class="muControl">return</span>
   <span class="Delimiter">}</span>
   <span class="Comment"># real file system</span>
   file:num <span class="Special">&lt;-</span> $open-file-for-writing filename
+  <span class="muControl">return-unless</span> file, <span class="Constant">0/sink</span>, <span class="Constant">0/routine-id</span>, <span class="Constant">1/error?</span>
   <span class="Delimiter">{</span>
     <span class="muControl">break-if</span> file
     msg:text <span class="Special">&lt;-</span> append <span class="Constant">[no such file: ]</span> filename
@@ -131,6 +157,29 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   routine-id <span class="Special">&lt;-</span> start-running transmit-to-file file, source
 ]
 
+<span class="muRecipe">def</span> dump resources:&amp;:resources, filename:text, contents:text<span class="muRecipe"> -&gt; </span>resources:&amp;:resources, error?:bool [
+  <span class="Constant">local-scope</span>
+  <span class="Constant">load-ingredients</span>
+  <span class="Comment"># todo: really create an empty file</span>
+  <span class="muControl">return-unless</span> contents, resources, <span class="Constant">0/no-error</span>
+  sink-file:&amp;:sink:char, write-routine:num, error?:bool <span class="Special">&lt;-</span> start-writing resources, filename
+  <span class="muControl">return-if</span> error?
+  i:num <span class="Special">&lt;-</span> copy<span class="Constant"> 0</span>
+  len:num <span class="Special">&lt;-</span> length *contents
+  <span class="Delimiter">{</span>
+    done?:bool <span class="Special">&lt;-</span> greater-or-equal i, len
+    <span class="muControl">break-if</span> done?
+    c:char <span class="Special">&lt;-</span> index *contents, i
+    sink-file <span class="Special">&lt;-</span> write sink-file, c
+    i <span class="Special">&lt;-</span> add i,<span class="Constant"> 1</span>
+    <span class="muControl">loop</span>
+  <span class="Delimiter">}</span>
+  close sink-file
+  <span class="Comment"># make sure to wait for the file to be actually written to disk</span>
+  <span class="Comment"># (Mu practices structured concurrency: <a href="http://250bpm.com/blog:71)">http://250bpm.com/blog:71)</a></span>
+  wait-for-routine write-routine
+]
+
 <span class="muRecipe">def</span> transmit-to-file file:num, source:&amp;:source:char<span class="muRecipe"> -&gt; </span>source:&amp;:source:char [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
@@ -143,7 +192,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   file <span class="Special">&lt;-</span> $close-file file
 ]
 
-<span class="muRecipe">def</span> transmit-to-fake-file resources:&amp;:resources, filename:text, source:&amp;:source:char<span class="muRecipe"> -&gt; </span>resources:&amp;:resources, source:&amp;:source:char [
+<span class="muRecipe">def</span> transmit-to-fake-resource resources:&amp;:resources, filename:text, source:&amp;:source:char<span class="muRecipe"> -&gt; </span>resources:&amp;:resources, source:&amp;:source:char [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
   lock:location <span class="Special">&lt;-</span> get-location *resources, <span class="Constant">lock:offset</span>
diff --git a/html/090scenario_filesystem_test.mu.html b/html/090scenario_filesystem_test.mu.html
index 91d039bb..20aad6a6 100644
--- a/html/090scenario_filesystem_test.mu.html
+++ b/html/090scenario_filesystem_test.mu.html
@@ -13,12 +13,9 @@
 pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
 body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color: #080808; }
 * { font-size: 12pt; font-size: 1em; }
-.Delimiter { color: #800080; }
-.muControl { color: #c0a020; }
 .Comment { color: #9090ff; }
 .Constant { color: #00a0a0; }
 .Special { color: #c00000; }
-.muRecipe { color: #ff8700; }
 .muScenario { color: #00af00; }
 -->
 </style>
@@ -130,20 +127,6 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
    <span class="Constant"> 11</span> <span class="Special">&lt;-</span><span class="Constant"> 1</span>  <span class="Comment"># other files also continue to persist unchanged</span>
   ]
 ]
-
-<span class="muRecipe">def</span> slurp resources:&amp;:resources, filename:text<span class="muRecipe"> -&gt; </span>contents:text [
-  <span class="Constant">local-scope</span>
-  <span class="Constant">load-ingredients</span>
-  source:&amp;:source:char <span class="Special">&lt;-</span> start-reading resources, filename
-  buf:&amp;:buffer <span class="Special">&lt;-</span> new-buffer <span class="Constant">30/capacity</span>
-  <span class="Delimiter">{</span>
-    c:char, done?:bool, source <span class="Special">&lt;-</span> read source
-    <span class="muControl">break-if</span> done?
-    buf <span class="Special">&lt;-</span> append buf, c
-    <span class="muControl">loop</span>
-  <span class="Delimiter">}</span>
-  contents <span class="Special">&lt;-</span> buffer-to-array buf
-]
 </pre>
 </body>
 </html>
diff --git a/html/092socket.mu.html b/html/092socket.mu.html
index f33fb8bd..cbff28cc 100644
--- a/html/092socket.mu.html
+++ b/html/092socket.mu.html
@@ -111,7 +111,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Delimiter">{</span>
     <span class="muControl">break-unless</span> resources
     <span class="Comment"># fake network</span>
-    contents <span class="Special">&lt;-</span> start-reading-from-fake-resources resources, uri
+    contents <span class="Special">&lt;-</span> start-reading-from-fake-resource resources, uri
     <span class="muControl">return</span>
   <span class="Delimiter">}</span>
   <span class="Comment"># real network</span>
diff --git a/html/101run_sandboxed.cc.html b/html/101run_sandboxed.cc.html
index 7b5da51b..0090e189 100644
--- a/html/101run_sandboxed.cc.html
+++ b/html/101run_sandboxed.cc.html
@@ -463,8 +463,8 @@ string strip_comments<span class="Delimiter">(</span>string in<span class="Delim
     <span class="Normal">if</span> <span class="Delimiter">(</span>*--p<span class="Delimiter">-&gt;</span>contents<span class="Delimiter">.</span>end<span class="Delimiter">()</span> != <span class="cSpecial">'\n'</span><span class="Delimiter">)</span> out &lt;&lt; <span class="cSpecial">'\n'</span><span class="Delimiter">;</span>
   <span class="Delimiter">}</span>
   string result = out<span class="Delimiter">.</span>str<span class="Delimiter">();</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>result<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Identifier">return</span> <span class="Constant">0</span><span class="Delimiter">;</span>
   truncate<span class="Delimiter">(</span>result<span class="Delimiter">);</span>
+  <span class="Normal">if</span> <span class="Delimiter">(</span>result<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Identifier">return</span> <span class="Constant">0</span><span class="Delimiter">;</span>
   <span class="Identifier">return</span> new_mu_text<span class="Delimiter">(</span>result<span class="Delimiter">);</span>
 <span class="Delimiter">}</span>
 
diff --git a/html/102persist.cc.html b/html/102persist.cc.html
deleted file mode 100644
index 863546d2..00000000
--- a/html/102persist.cc.html
+++ /dev/null
@@ -1,154 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
-<head>
-<meta http-equiv="content-type" content="text/html; charset=UTF-8">
-<title>Mu - 102persist.cc</title>
-<meta name="Generator" content="Vim/7.4">
-<meta name="plugin-version" content="vim7.4_v2">
-<meta name="syntax" content="cpp">
-<meta name="settings" content="use_css,pre_wrap,no_foldcolumn,expand_tabs,prevent_copy=">
-<meta name="colorscheme" content="minimal">
-<style type="text/css">
-<!--
-pre { white-space: pre-wrap; font-family: monospace; color: #eeeeee; background-color: #080808; }
-body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color: #080808; }
-* { font-size: 12pt; font-size: 1em; }
-.Constant { color: #00a0a0; }
-.Comment { color: #9090ff; }
-.Delimiter { color: #800080; }
-.cSpecial { color: #008000; }
-.Identifier { color: #c0a020; }
-.Normal { color: #eeeeee; background-color: #080808; padding-bottom: 1px; }
--->
-</style>
-
-<script type='text/javascript'>
-<!--
-
--->
-</script>
-</head>
-<body>
-<pre id='vimCodeElement'>
-<span class="Comment">//: Dead simple persistence.</span>
-<span class="Comment">//:   'restore' - reads string from a file</span>
-<span class="Comment">//:   'save' - writes string to a file</span>
-
-<span class="Delimiter">:(before &quot;End Primitive Recipe Declarations&quot;)</span>
-RESTORE<span class="Delimiter">,</span>
-<span class="Delimiter">:(before &quot;End Primitive Recipe Numbers&quot;)</span>
-put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;restore&quot;</span><span class="Delimiter">,</span> RESTORE<span class="Delimiter">);</span>
-<span class="Delimiter">:(before &quot;End Primitive Recipe Checks&quot;)</span>
-<span class="Normal">case</span> RESTORE: <span class="Delimiter">{</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">1</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
-    raise &lt;&lt; maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;'restore' requires exactly one ingredient, but got '&quot;</span> &lt;&lt; inst<span class="Delimiter">.</span>original_string &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
-    <span class="Identifier">break</span><span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  string filename<span class="Delimiter">;</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>is_literal_text<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
-    <span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Normal">else</span> <span class="Normal">if</span> <span class="Delimiter">(</span>is_mu_text<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
-    <span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Normal">else</span> <span class="Delimiter">{</span>
-    raise &lt;&lt; maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;first ingredient of 'restore' should be a string, but got '&quot;</span> &lt;&lt; to_string<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">))</span> &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
-    <span class="Identifier">break</span><span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Identifier">break</span><span class="Delimiter">;</span>
-<span class="Delimiter">}</span>
-<span class="Delimiter">:(before &quot;End Primitive Recipe Implementations&quot;)</span>
-<span class="Normal">case</span> RESTORE: <span class="Delimiter">{</span>
-  string filename<span class="Delimiter">;</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>is_literal_text<span class="Delimiter">(</span>current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
-    filename = current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name<span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Normal">else</span> <span class="Normal">if</span> <span class="Delimiter">(</span>is_mu_text<span class="Delimiter">(</span>current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
-    filename = read_mu_text<span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
-  <span class="Delimiter">}</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario<span class="Delimiter">)</span> <span class="Delimiter">{</span>
-    <span class="Comment">// do nothing in tests</span>
-    products<span class="Delimiter">.</span>resize<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">);</span>
-    products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>push_back<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
-    <span class="Identifier">break</span><span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  string contents = slurp<span class="Delimiter">(</span><span class="Constant">&quot;lesson/&quot;</span>+filename<span class="Delimiter">);</span>
-  products<span class="Delimiter">.</span>resize<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">);</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>contents<span class="Delimiter">.</span>empty<span class="Delimiter">())</span>
-    products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>push_back<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">);</span>
-  <span class="Normal">else</span>
-    products<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>push_back<span class="Delimiter">(</span>new_mu_text<span class="Delimiter">(</span>contents<span class="Delimiter">));</span>
-  <span class="Identifier">break</span><span class="Delimiter">;</span>
-<span class="Delimiter">}</span>
-
-<span class="Delimiter">:(code)</span>
-<span class="Comment">// <a href="http://cpp.indi.frih.net/blog/2014/09/how-to-read-an-entire-file-into-memory-in-cpp">http://cpp.indi.frih.net/blog/2014/09/how-to-read-an-entire-file-into-memory-in-cpp</a></span>
-string slurp<span class="Delimiter">(</span><span class="Normal">const</span> string&amp; filename<span class="Delimiter">)</span> <span class="Delimiter">{</span>
-  ifstream fin<span class="Delimiter">(</span>filename<span class="Delimiter">.</span>c_str<span class="Delimiter">());</span>
-  fin<span class="Delimiter">.</span>peek<span class="Delimiter">();</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>!fin<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">&quot;&quot;</span><span class="Delimiter">;</span>  <span class="Comment">// don't bother checking errno</span>
-  ostringstream result<span class="Delimiter">;</span>
-  result &lt;&lt; fin<span class="Delimiter">.</span>rdbuf<span class="Delimiter">();</span>
-  fin<span class="Delimiter">.</span>close<span class="Delimiter">();</span>
-  <span class="Identifier">return</span> result<span class="Delimiter">.</span>str<span class="Delimiter">();</span>
-<span class="Delimiter">}</span>
-
-<span class="Delimiter">:(before &quot;End Primitive Recipe Declarations&quot;)</span>
-SAVE<span class="Delimiter">,</span>
-<span class="Delimiter">:(before &quot;End Primitive Recipe Numbers&quot;)</span>
-put<span class="Delimiter">(</span>Recipe_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;save&quot;</span><span class="Delimiter">,</span> SAVE<span class="Delimiter">);</span>
-<span class="Delimiter">:(before &quot;End Primitive Recipe Checks&quot;)</span>
-<span class="Normal">case</span> SAVE: <span class="Delimiter">{</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">)</span> != <span class="Constant">2</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
-    raise &lt;&lt; maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;'save' requires exactly two ingredients, but got '&quot;</span> &lt;&lt; inst<span class="Delimiter">.</span>original_string &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
-    <span class="Identifier">break</span><span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>is_literal_text<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
-    <span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Normal">else</span> <span class="Normal">if</span> <span class="Delimiter">(</span>is_mu_text<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
-    <span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Normal">else</span> <span class="Delimiter">{</span>
-    raise &lt;&lt; maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;first ingredient of 'save' should be a string, but got '&quot;</span> &lt;&lt; to_string<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">))</span> &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
-    <span class="Identifier">break</span><span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>!is_mu_text<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
-    raise &lt;&lt; maybe<span class="Delimiter">(</span>get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">).</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;second ingredient of 'save' should be an address:@:char, but got '&quot;</span> &lt;&lt; to_string<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">))</span> &lt;&lt; <span class="Constant">&quot;'</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span>
-    <span class="Identifier">break</span><span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Identifier">break</span><span class="Delimiter">;</span>
-<span class="Delimiter">}</span>
-<span class="Delimiter">:(before &quot;End Primitive Recipe Implementations&quot;)</span>
-<span class="Normal">case</span> SAVE: <span class="Delimiter">{</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>Current_scenario<span class="Delimiter">)</span> <span class="Identifier">break</span><span class="Delimiter">;</span>  <span class="Comment">// do nothing in tests</span>
-  string filename<span class="Delimiter">;</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>is_literal_text<span class="Delimiter">(</span>current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
-    filename = current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>name<span class="Delimiter">;</span>
-  <span class="Delimiter">}</span>
-  <span class="Normal">else</span> <span class="Normal">if</span> <span class="Delimiter">(</span>is_mu_text<span class="Delimiter">(</span>current_instruction<span class="Delimiter">().</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">)))</span> <span class="Delimiter">{</span>
-    filename = read_mu_text<span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
-  <span class="Delimiter">}</span>
-  ofstream fout<span class="Delimiter">((</span><span class="Constant">&quot;lesson/&quot;</span>+filename<span class="Delimiter">).</span>c_str<span class="Delimiter">());</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>!fout<span class="Delimiter">)</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
-  string contents = read_mu_text<span class="Delimiter">(</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span><span class="Constant">1</span><span class="Delimiter">).</span>at<span class="Delimiter">(</span><span class="Constant">0</span><span class="Delimiter">));</span>
-  fout &lt;&lt; contents<span class="Delimiter">;</span>
-  fout<span class="Delimiter">.</span>close<span class="Delimiter">();</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>!exists<span class="Delimiter">(</span><span class="Constant">&quot;lesson/.git&quot;</span><span class="Delimiter">))</span> <span class="Identifier">break</span><span class="Delimiter">;</span>
-  <span class="Comment">// bug in git: git diff -q messes up --exit-code</span>
-  <span class="Comment">// explicitly say '--all' for git 1.9</span>
-  <span class="Normal">int</span> status = system<span class="Delimiter">(</span><span class="Constant">&quot;cd lesson; git add --all .; git diff HEAD --exit-code &gt;/dev/null || git commit -a -m . &gt;/dev/null&quot;</span><span class="Delimiter">);</span>
-  <span class="Normal">if</span> <span class="Delimiter">(</span>status != <span class="Constant">0</span><span class="Delimiter">)</span>
-    raise &lt;&lt; <span class="Constant">&quot;error in commit: contents &quot;</span> &lt;&lt; contents &lt;&lt; <span class="cSpecial">'\n'</span> &lt;&lt; end<span class="Delimiter">();</span>
-  <span class="Identifier">break</span><span class="Delimiter">;</span>
-<span class="Delimiter">}</span>
-
-<span class="Delimiter">:(code)</span>
-<span class="Normal">bool</span> exists<span class="Delimiter">(</span><span class="Normal">const</span> string&amp; filename<span class="Delimiter">)</span> <span class="Delimiter">{</span>
-  <span class="Normal">struct</span> stat dummy<span class="Delimiter">;</span>
-  <span class="Identifier">return</span> <span class="Constant">0</span> == stat<span class="Delimiter">(</span>filename<span class="Delimiter">.</span>c_str<span class="Delimiter">(),</span> &amp;dummy<span class="Delimiter">);</span>
-<span class="Delimiter">}</span>
-</pre>
-</body>
-</html>
-<!-- vim: set foldmethod=manual : -->
diff --git a/html/edit/004-programming-environment.mu.html b/html/edit/004-programming-environment.mu.html
index 776c336e..9cbe6f6f 100644
--- a/html/edit/004-programming-environment.mu.html
+++ b/html/edit/004-programming-environment.mu.html
@@ -41,12 +41,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
 <span class="muRecipe">def!</span> main [
   <span class="Constant">local-scope</span>
   open-console
-  initial-recipe:text <span class="Special">&lt;-</span> restore <span class="Constant">[recipes.mu]</span>
-  initial-sandbox:text <span class="Special">&lt;-</span> new <span class="Constant">[]</span>
-  hide-screen <span class="Constant">0/screen</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment <span class="Constant">0/screen</span>, initial-recipe, initial-sandbox
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment <span class="Constant">0/filesystem</span>, <span class="Constant">0/screen</span>
   render-all <span class="Constant">0/screen</span>, env, render
-  event-loop <span class="Constant">0/screen</span>, <span class="Constant">0/console</span>, env
+  event-loop <span class="Constant">0/screen</span>, <span class="Constant">0/console</span>, env, <span class="Constant">0/filesystem</span>
   <span class="Comment"># never gets here</span>
 ]
 
@@ -56,24 +53,25 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   sandbox-in-focus?:bool  <span class="Comment"># false =&gt; cursor in recipes; true =&gt; cursor in current-sandbox</span>
 ]
 
-<span class="muRecipe">def</span> new-programming-environment screen:&amp;:screen, initial-recipe-contents:text, initial-sandbox-contents:text<span class="muRecipe"> -&gt; </span>result:&amp;:environment [
+<span class="muRecipe">def</span> new-programming-environment resources:&amp;:resources, screen:&amp;:screen, test-sandbox-editor-contents:text<span class="muRecipe"> -&gt; </span>result:&amp;:environment [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
   width:num <span class="Special">&lt;-</span> screen-width screen
   result <span class="Special">&lt;-</span> new <span class="Constant">environment:type</span>
   <span class="Comment"># recipe editor on the left</span>
+  initial-recipe-contents:text <span class="Special">&lt;-</span> slurp resources, <span class="Constant">[lesson/recipes.mu]</span>  <span class="Comment"># ignore errors</span>
   divider:num, _ <span class="Special">&lt;-</span> divide-with-remainder width,<span class="Constant"> 2</span>
   recipes:&amp;:editor <span class="Special">&lt;-</span> new-editor initial-recipe-contents, <span class="Constant">0/left</span>, divider/right
   <span class="Comment"># sandbox editor on the right</span>
   sandbox-left:num <span class="Special">&lt;-</span> add divider,<span class="Constant"> 1</span>
-  current-sandbox:&amp;:editor <span class="Special">&lt;-</span> new-editor initial-sandbox-contents, sandbox-left, width/right
+  current-sandbox:&amp;:editor <span class="Special">&lt;-</span> new-editor test-sandbox-editor-contents, sandbox-left, width/right
   *result <span class="Special">&lt;-</span> put *result, <span class="Constant">recipes:offset</span>, recipes
   *result <span class="Special">&lt;-</span> put *result, <span class="Constant">current-sandbox:offset</span>, current-sandbox
   *result <span class="Special">&lt;-</span> put *result, <span class="Constant">sandbox-in-focus?:offset</span>, <span class="Constant">0/false</span>
 <span class="Constant">  &lt;programming-environment-initialization&gt;</span>
 ]
 
-<span class="muRecipe">def</span> event-loop screen:&amp;:screen, console:&amp;:console, env:&amp;:environment<span class="muRecipe"> -&gt; </span>screen:&amp;:screen, console:&amp;:console, env:&amp;:environment [
+<span class="muRecipe">def</span> event-loop screen:&amp;:screen, console:&amp;:console, env:&amp;:environment, resources:&amp;:resources<span class="muRecipe"> -&gt; </span>screen:&amp;:screen, console:&amp;:console, env:&amp;:environment, resources:&amp;:resources [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
   recipes:&amp;:editor <span class="Special">&lt;-</span> get *env, <span class="Constant">recipes:offset</span>
@@ -321,7 +319,12 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">30/width</span>, <span class="Constant">5/height</span>
   <span class="Comment"># initialize both halves of screen</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[abc]</span>, <span class="Constant">[def]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+<span class="Constant">      |abc|</span>
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[def]</span>  <span class="Comment"># contents of sandbox editor</span>
   <span class="Comment"># focus on both sides</span>
   assume-console [
     left-click<span class="Constant"> 1</span>,<span class="Constant"> 1</span>
@@ -329,7 +332,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   ]
   <span class="Comment"># check cursor column in each</span>
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     recipes:&amp;:editor <span class="Special">&lt;-</span> get *env, <span class="Constant">recipes:offset</span>
     5:num/<span class="Special">raw</span> <span class="Special">&lt;-</span> get *recipes, <span class="Constant">cursor-column:offset</span>
     sandbox:&amp;:editor <span class="Special">&lt;-</span> get *env, <span class="Constant">current-sandbox:offset</span>
@@ -346,7 +349,12 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">30/width</span>, <span class="Constant">5/height</span>
   <span class="Comment"># initialize both halves of screen</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[abc]</span>, <span class="Constant">[def]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+<span class="Constant">      |abc|</span>
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[def]</span>  <span class="Comment"># contents of sandbox</span>
   render-all screen, env, render
   <span class="Comment"># type one letter in each of them</span>
   assume-console [
@@ -356,7 +364,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[1]</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     recipes:&amp;:editor <span class="Special">&lt;-</span> get *env, <span class="Constant">recipes:offset</span>
     5:num/<span class="Special">raw</span> <span class="Special">&lt;-</span> get *recipes, <span class="Constant">cursor-column:offset</span>
     sandbox:&amp;:editor <span class="Special">&lt;-</span> get *env, <span class="Constant">current-sandbox:offset</span>
@@ -365,7 +373,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   screen-should-contain [
    <span class="Constant"> .           run (F4)           .  # this line has a different background, but we don't test that yet</span>
    <span class="Constant"> .a0bc           ╎d1ef          .</span>
-<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎──────────────.</span>
+   <span class="Constant"> .               ╎──────────────.</span>
+   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎              .</span>
    <span class="Constant"> .               ╎              .</span>
   ]
   memory-should-contain [
@@ -380,39 +389,27 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   screen-should-contain [
    <span class="Constant"> .           run (F4)           .</span>
    <span class="Constant"> .a0bc           ╎d1␣f          .</span>
-<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎──────────────.</span>
+   <span class="Constant"> .               ╎──────────────.</span>
+   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎              .</span>
    <span class="Constant"> .               ╎              .</span>
   ]
 ]
 
-<span class="muScenario">scenario</span> multiple-editors-cover-only-their-own-areas [
-  <span class="Constant">local-scope</span>
-  trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
-  assume-screen <span class="Constant">60/width</span>, <span class="Constant">10/height</span>
-  run [
-    env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[abc]</span>, <span class="Constant">[def]</span>
-    render-all screen, env, render
-  ]
-  <span class="Comment"># divider isn't messed up</span>
-  screen-should-contain [
-   <span class="Constant"> .                                         run (F4)           .</span>
-   <span class="Constant"> .abc                           ╎def                          .</span>
-<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────.</span>
-   <span class="Constant"> .                              ╎                             .</span>
-   <span class="Constant"> .                              ╎                             .</span>
-  ]
-]
-
 <span class="muScenario">scenario</span> editor-in-focus-keeps-cursor [
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">30/width</span>, <span class="Constant">5/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[abc]</span>, <span class="Constant">[def]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+<span class="Constant">      |abc|</span>
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[def]</span>
   render-all screen, env, render
   <span class="Comment"># initialize programming environment and highlight cursor</span>
   assume-console <span class="Constant">[]</span>
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
@@ -420,7 +417,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   screen-should-contain [
    <span class="Constant"> .           run (F4)           .</span>
    <span class="Constant"> .␣bc            ╎def           .</span>
-<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎──────────────.</span>
+   <span class="Constant"> .               ╎──────────────.</span>
+   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎              .</span>
    <span class="Constant"> .               ╎              .</span>
   ]
   <span class="Comment"># now try typing a letter</span>
@@ -428,7 +426,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[z]</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
@@ -436,7 +434,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   screen-should-contain [
    <span class="Constant"> .           run (F4)           .</span>
    <span class="Constant"> .z␣bc           ╎def           .</span>
-<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎──────────────.</span>
+   <span class="Constant"> .               ╎──────────────.</span>
+   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎              .</span>
    <span class="Constant"> .               ╎              .</span>
   ]
 ]
@@ -445,10 +444,12 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">30/width</span>, <span class="Constant">5/height</span>
+  assume-resources [
+  ]
   <span class="Comment"># initialize sandbox side with two lines</span>
-  s:text <span class="Special">&lt;-</span> new <span class="Constant">[abc</span>
+  test-sandbox-editor-contents:text <span class="Special">&lt;-</span> new <span class="Constant">[abc</span>
 <span class="Constant">def]</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, s:text
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, test-sandbox-editor-contents
   render-all screen, env, render
   screen-should-contain [
    <span class="Constant"> .           run (F4)           .</span>
@@ -463,7 +464,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press backspace
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
diff --git a/html/edit/005-sandbox.mu.html b/html/edit/005-sandbox.mu.html
index b7afa986..095fa72d 100644
--- a/html/edit/005-sandbox.mu.html
+++ b/html/edit/005-sandbox.mu.html
@@ -45,13 +45,10 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
 <span class="muRecipe">def!</span> main [
   <span class="Constant">local-scope</span>
   open-console
-  initial-recipe:text <span class="Special">&lt;-</span> restore <span class="Constant">[recipes.mu]</span>
-  initial-sandbox:text <span class="Special">&lt;-</span> new <span class="Constant">[]</span>
-  hide-screen <span class="Constant">0/screen</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment <span class="Constant">0/screen</span>, initial-recipe, initial-sandbox
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment <span class="Constant">0/filesystem</span>, <span class="Constant">0/screen</span>
   env <span class="Special">&lt;-</span> restore-sandboxes env
   render-all <span class="Constant">0/screen</span>, env, render
-  event-loop <span class="Constant">0/screen</span>, <span class="Constant">0/console</span>, env
+  event-loop <span class="Constant">0/screen</span>, <span class="Constant">0/console</span>, env, <span class="Constant">0/filesystem</span>
   <span class="Comment"># never gets here</span>
 ]
 
@@ -81,14 +78,16 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
   <span class="Comment"># recipe editor is empty</span>
+  assume-resources [
+  ]
   <span class="Comment"># sandbox editor contains an instruction without storing outputs</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[divide-with-remainder 11, 3]</span>
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[divide-with-remainder 11, 3]</span>
   <span class="Comment"># run the code in the editors</span>
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># check that screen prints the results</span>
   screen-should-contain [
@@ -138,7 +137,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># check that screen prints both sandboxes</span>
   screen-should-contain [
@@ -164,7 +163,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     do-run?:bool <span class="Special">&lt;-</span> equal k, <span class="Constant">65532/F4</span>
     <span class="muControl">break-unless</span> do-run?
     screen <span class="Special">&lt;-</span> update-status screen, <span class="Constant">[running...       ]</span>, <span class="Constant">245/grey</span>
-    error?:bool, env, screen <span class="Special">&lt;-</span> run-sandboxes env, screen
+    error?:bool <span class="Special">&lt;-</span> run-sandboxes env, resources, screen
     <span class="Comment"># F4 might update warnings and results on both sides</span>
     screen <span class="Special">&lt;-</span> render-all screen, env, render
     <span class="Delimiter">{</span>
@@ -176,10 +175,10 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Delimiter">}</span>
 ]
 
-<span class="muRecipe">def</span> run-sandboxes env:&amp;:environment, screen:&amp;:screen<span class="muRecipe"> -&gt; </span>errors-found?:bool, env:&amp;:environment, screen:&amp;:screen [
+<span class="muRecipe">def</span> run-sandboxes env:&amp;:environment, resources:&amp;:resources, screen:&amp;:screen<span class="muRecipe"> -&gt; </span>errors-found?:bool, env:&amp;:environment, resources:&amp;:resources, screen:&amp;:screen [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
-  errors-found?:bool, env, screen <span class="Special">&lt;-</span> update-recipes env, screen
+  errors-found?:bool <span class="Special">&lt;-</span> update-recipes env, resources, screen
   <span class="muControl">return-if</span> errors-found?
   <span class="Comment"># check contents of right editor (sandbox)</span>
 <span class="Constant">  &lt;run-sandboxes-begin&gt;</span>
@@ -205,7 +204,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     *current-sandbox <span class="Special">&lt;-</span> put *current-sandbox, <span class="Constant">top-of-screen:offset</span>, init
   <span class="Delimiter">}</span>
   <span class="Comment"># save all sandboxes before running, just in case we die when running</span>
-  save-sandboxes env
+  save-sandboxes env, resources
   <span class="Comment"># run all sandboxes</span>
   curr:&amp;:sandbox <span class="Special">&lt;-</span> get *env, <span class="Constant">sandbox:offset</span>
   idx:num <span class="Special">&lt;-</span> copy<span class="Constant"> 0</span>
@@ -219,14 +218,14 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
 <span class="Constant">  &lt;run-sandboxes-end&gt;</span>
 ]
 
-<span class="Comment"># copy code from recipe editor, persist to disk, load</span>
+<span class="Comment"># load code from disk</span>
 <span class="Comment"># replaced in a later layer (whereupon errors-found? will actually be set)</span>
-<span class="muRecipe">def</span> update-recipes env:&amp;:environment, screen:&amp;:screen<span class="muRecipe"> -&gt; </span>errors-found?:bool, env:&amp;:environment, screen:&amp;:screen [
+<span class="muRecipe">def</span> update-recipes env:&amp;:environment, resources:&amp;:resources, screen:&amp;:screen<span class="muRecipe"> -&gt; </span>errors-found?:bool, env:&amp;:environment, resources:&amp;:resources, screen:&amp;:screen [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
   recipes:&amp;:editor <span class="Special">&lt;-</span> get *env, <span class="Constant">recipes:offset</span>
   in:text <span class="Special">&lt;-</span> editor-contents recipes
-  save <span class="Constant">[recipes.mu]</span>, in  <span class="Comment"># newlayer: persistence</span>
+  resources <span class="Special">&lt;-</span> dump resources, <span class="Constant">[lesson/recipes.mu]</span>, in
   reload in
   errors-found? <span class="Special">&lt;-</span> copy <span class="Constant">0/false</span>
 ]
@@ -248,7 +247,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   screen <span class="Special">&lt;-</span> print screen, msg, color, <span class="Constant">238/grey/background</span>
 ]
 
-<span class="muRecipe">def</span> save-sandboxes env:&amp;:environment [
+<span class="muRecipe">def</span> save-sandboxes env:&amp;:environment, resources:&amp;:resources<span class="muRecipe"> -&gt; </span>resources:&amp;:resources [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
   current-sandbox:&amp;:editor <span class="Special">&lt;-</span> get *env, <span class="Constant">current-sandbox:offset</span>
@@ -259,8 +258,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Delimiter">{</span>
     <span class="muControl">break-unless</span> curr
     data:text <span class="Special">&lt;-</span> get *curr, <span class="Constant">data:offset</span>
-    filename:text <span class="Special">&lt;-</span> to-text idx
-    save filename, data
+    filename:text <span class="Special">&lt;-</span> append <span class="Constant">[lesson/]</span>, idx
+    resources <span class="Special">&lt;-</span> dump resources, filename, data
 <span class="Constant">    &lt;end-save-sandbox&gt;</span>
     idx <span class="Special">&lt;-</span> add idx,<span class="Constant"> 1</span>
     curr <span class="Special">&lt;-</span> get *curr, <span class="Constant">next-sandbox:offset</span>
@@ -450,7 +449,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
 ]
 
 <span class="Comment"># assumes programming environment has no sandboxes; restores them from previous session</span>
-<span class="muRecipe">def</span> restore-sandboxes env:&amp;:environment<span class="muRecipe"> -&gt; </span>env:&amp;:environment [
+<span class="muRecipe">def</span> restore-sandboxes env:&amp;:environment, resources:&amp;:resources<span class="muRecipe"> -&gt; </span>env:&amp;:environment [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
   <span class="Comment"># read all scenarios, pushing them to end of a list of scenarios</span>
@@ -458,8 +457,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   curr:&amp;:sandbox <span class="Special">&lt;-</span> copy<span class="Constant"> 0</span>
   prev:&amp;:sandbox <span class="Special">&lt;-</span> copy<span class="Constant"> 0</span>
   <span class="Delimiter">{</span>
-    filename:text <span class="Special">&lt;-</span> to-text idx
-    contents:text <span class="Special">&lt;-</span> restore filename
+    filename:text <span class="Special">&lt;-</span> append <span class="Constant">[lesson/]</span>, idx
+    contents:text <span class="Special">&lt;-</span> slurp resources, filename
     <span class="muControl">break-unless</span> contents  <span class="Comment"># stop at first error; assuming file didn't exist</span>
                            <span class="Comment"># todo: handle empty sandbox</span>
     <span class="Comment"># create new sandbox for file</span>
@@ -554,27 +553,32 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">12/height</span>
   <span class="Comment"># define a recipe (no indent for the 'add' line below so column numbers are more obvious)</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">local-scope</span>
-<span class="Constant">z:num &lt;- add 2, 2</span>
-<span class="Constant">reply z</span>
-<span class="Constant">]</span>]
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+<span class="Constant">      ||</span>
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  local-scope|</span>
+<span class="Constant">      |  z:num &lt;- add 2, 2|</span>
+<span class="Constant">      |  reply z|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
   <span class="Comment"># sandbox editor contains an instruction without storing outputs</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>  <span class="Comment"># contents of sandbox editor</span>
   <span class="Comment"># run the code in the editors</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .local-scope                                       ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .z:num &lt;- add 2, 2                                 ╎foo                                              .</span>
-   <span class="Constant"> .reply z                                           ╎4                                                .</span>
+   <span class="Constant"> .  local-scope                                     ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .  z:num &lt;- add 2, 2                               ╎foo                                              .</span>
+   <span class="Constant"> .  reply z                                         ╎4                                                .</span>
    <span class="Constant"> .]                                                 ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -586,17 +590,18 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># check that screen updates the result on the right</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .local-scope                                       ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .z:num &lt;- add 2, 3                                 ╎foo                                              .</span>
-   <span class="Constant"> .reply z                                           ╎5                                                .</span>
+   <span class="Constant"> .  local-scope                                     ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .  z:num &lt;- add 2, 3                               ╎foo                                              .</span>
+   <span class="Constant"> .  reply z                                         ╎5                                                .</span>
    <span class="Constant"> .]                                                 ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -606,15 +611,17 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">20/height</span>
-  <span class="Comment"># left editor is empty</span>
-  <span class="Comment"># right editor contains an instruction</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[print-integer screen, 4]</span>
+  <span class="Comment"># empty recipes</span>
+  assume-resources [
+  ]
+  <span class="Comment"># sandbox editor contains an instruction</span>
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[print-integer screen, 4]</span>  <span class="Comment"># contents of sandbox editor</span>
   <span class="Comment"># run the code in the editor</span>
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># check that it prints a little toy screen</span>
   screen-should-contain [
@@ -677,13 +684,15 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   assume-console [
     press enter
     press down-arrow
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   <span class="Comment"># no scroll</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
@@ -698,14 +707,16 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   assume-console [
     press enter
     press up-arrow
     press down-arrow  <span class="Comment"># while cursor isn't at bottom</span>
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
   print screen, cursor
   <span class="Comment"># cursor moves back to bottom</span>
@@ -776,7 +787,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   assume-console [
     <span class="Comment"># add a line</span>
@@ -786,7 +799,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     <span class="Comment"># try to scroll</span>
     press page-down  <span class="Comment"># or ctrl-f</span>
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   <span class="Comment"># no scroll, and cursor remains at top line</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
@@ -801,7 +814,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[ab</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[ab</span>
 <span class="Constant">cd]</span>
   render-all screen, env, render
   assume-console [
@@ -812,7 +827,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     <span class="Comment"># move cursor</span>
     press down-arrow
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
   print screen, cursor
   <span class="Comment"># no scroll on recipe side, cursor moves on sandbox side</span>
@@ -831,14 +846,16 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  <span class="Comment"># initialize sandbox side</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[add 2, 2]</span>
+  <span class="Comment"># initialize</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[add 2, 2]</span>
   render-all screen, env, render
   assume-console [
     <span class="Comment"># create a sandbox</span>
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
@@ -852,7 +869,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
@@ -870,7 +887,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
@@ -959,30 +976,33 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="muControl">return</span> curr
 ]
 
-<span class="muScenario">scenario</span> scrolling-down-on-recipe-side [
+<span class="muScenario">scenario</span> scrolling-down-past-bottom-on-recipe-side [
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># initialize sandbox side and create a sandbox</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">||</span>  <span class="Comment"># file containing just a newline</span>
+    ]
+  ]
   <span class="Comment"># create a sandbox</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes:text, <span class="Constant">[add 2, 2]</span>
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[add 2, 2]</span>
   render-all screen, env, render
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   <span class="Comment"># hit 'down' in recipe editor</span>
   assume-console [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
-  <span class="Comment"># cursor moves down on recipe side</span>
+  <span class="Comment"># cursor doesn't move when the end is already on-screen</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .␣                                                 ╎                                                 .</span>
@@ -997,7 +1017,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># initialize environment</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   <span class="Comment"># create 2 sandboxes</span>
   assume-console [
@@ -1007,7 +1029,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[add 1, 1]</span>
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
   print screen, cursor
   screen-should-contain [
@@ -1027,7 +1049,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
@@ -1049,7 +1071,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># just second sandbox displayed</span>
   screen-should-contain [
@@ -1066,7 +1088,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># no change</span>
   screen-should-contain [
@@ -1083,7 +1105,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># back to displaying both sandboxes without editor</span>
   screen-should-contain [
@@ -1102,7 +1124,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
@@ -1124,7 +1146,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
@@ -1148,7 +1170,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># initialize environment</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   <span class="Comment"># create a sandbox</span>
   assume-console [
@@ -1156,7 +1180,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[add 1, 1]</span>
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
@@ -1172,7 +1196,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># sandbox editor hidden; first sandbox displayed</span>
   <span class="Comment"># cursor moves to first sandbox</span>
@@ -1190,7 +1214,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># back to displaying both sandboxes as well as editor</span>
   screen-should-contain [
@@ -1208,7 +1232,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># sandbox editor hidden; first sandbox displayed</span>
   <span class="Comment"># cursor moves to first sandbox</span>
diff --git a/html/edit/006-sandbox-copy.mu.html b/html/edit/006-sandbox-copy.mu.html
index 3459f1f2..b6e9f280 100644
--- a/html/edit/006-sandbox-copy.mu.html
+++ b/html/edit/006-sandbox-copy.mu.html
@@ -39,24 +39,22 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  <span class="Comment"># basic recipe</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  reply 4</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  <span class="Comment"># empty recipes</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[add 1, 1]</span>  <span class="Comment"># contents of sandbox editor</span>
   <span class="Comment"># run it</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+   <span class="Constant"> .                                                  ╎2                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -65,16 +63,16 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 69</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># it copies into editor</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+   <span class="Constant"> .                                                  ╎2                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -83,15 +81,15 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[0]</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎0foo                                             .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+   <span class="Constant"> .                                                  ╎0add 1, 1                                        .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+   <span class="Constant"> .                                                  ╎2                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -101,24 +99,22 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  <span class="Comment"># basic recipe</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  reply 4</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  <span class="Comment"># empty recipes</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[add 1, 1]</span>  <span class="Comment"># contents of sandbox editor</span>
   <span class="Comment"># run it</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+   <span class="Constant"> .                                                  ╎2                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -127,16 +123,16 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 84</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># it copies into editor</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+   <span class="Constant"> .                                                  ╎2                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -145,15 +141,15 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[0]</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎0foo                                             .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+   <span class="Constant"> .                                                  ╎0add 1, 1                                        .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+   <span class="Constant"> .                                                  ╎2                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -258,24 +254,22 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  <span class="Comment"># basic recipe</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  reply 4</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  <span class="Comment"># empty recipes</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[add 1, 1]</span>  <span class="Comment"># contents of sandbox editor</span>
   <span class="Comment"># run it</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+   <span class="Constant"> .                                                  ╎2                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -286,16 +280,16 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 70</span>  <span class="Comment"># click 'copy' button</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># copy doesn't happen</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎0                                                .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+   <span class="Constant"> .                                                  ╎2                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -304,15 +298,15 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[1]</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎01                                               .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 1, 1                                         .</span>
+   <span class="Constant"> .                                                  ╎2                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
diff --git a/html/edit/007-sandbox-delete.mu.html b/html/edit/007-sandbox-delete.mu.html
index 48028d51..06dd8fc5 100644
--- a/html/edit/007-sandbox-delete.mu.html
+++ b/html/edit/007-sandbox-delete.mu.html
@@ -38,7 +38,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   <span class="Comment"># run a few commands</span>
   assume-console [
     left-click<span class="Constant"> 1</span>,<span class="Constant"> 80</span>
@@ -47,7 +49,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[add 2, 2]</span>
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
@@ -68,7 +70,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 7</span>,<span class="Constant"> 85</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
@@ -86,7 +88,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 99</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
@@ -185,7 +187,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># initialize environment</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   <span class="Comment"># create 2 sandboxes and scroll to second</span>
   assume-console [
@@ -196,7 +200,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
@@ -211,7 +215,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 6</span>,<span class="Constant"> 99</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># second sandbox shows in editor; scroll resets to display first sandbox</span>
   screen-should-contain [
@@ -230,7 +234,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># initialize environment</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   <span class="Comment"># create 2 sandboxes and scroll to second</span>
   assume-console [
@@ -241,7 +247,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
@@ -256,7 +262,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 2</span>,<span class="Constant"> 99</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># second sandbox shows in editor; scroll resets to display first sandbox</span>
   screen-should-contain [
@@ -275,7 +281,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># initialize environment</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   <span class="Comment"># create 2 sandboxes and scroll to second</span>
   assume-console [
@@ -287,7 +295,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
@@ -302,7 +310,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 2</span>,<span class="Constant"> 99</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># implicitly scroll up to first sandbox</span>
   screen-should-contain [
@@ -322,7 +330,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># initialize environment</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   <span class="Comment"># create 2 sandboxes</span>
   assume-console [
@@ -332,7 +342,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[add 1, 1]</span>
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
@@ -352,7 +362,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># shouldn't go past last sandbox</span>
   screen-should-contain [
diff --git a/html/edit/008-sandbox-edit.mu.html b/html/edit/008-sandbox-edit.mu.html
index 97ca51e3..3725c7ce 100644
--- a/html/edit/008-sandbox-edit.mu.html
+++ b/html/edit/008-sandbox-edit.mu.html
@@ -34,28 +34,26 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
 <pre id='vimCodeElement'>
 <span class="SalientComment">## editing sandboxes after they've been created</span>
 
-<span class="muScenario">scenario</span> clicking-on-a-sandbox-moves-it-to-editor [
+<span class="muScenario">scenario</span> clicking-on-sandbox-edit-button-moves-it-to-editor [
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  <span class="Comment"># basic recipe</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  reply 4</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  <span class="Comment"># empty recipes</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[add 2, 2]</span>
   <span class="Comment"># run it</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 2, 2                                         .</span>
+   <span class="Constant"> .                                                  ╎4                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -64,16 +62,13 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 55</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># it pops back into editor</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎                                                 .</span>
-   <span class="Constant"> .]                                                 ╎                                                 .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎add 2, 2                                         .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
   <span class="Comment"># cursor should be in the right place</span>
@@ -81,41 +76,36 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[0]</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎0foo                                             .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎                                                 .</span>
-   <span class="Constant"> .]                                                 ╎                                                 .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎0add 2, 2                                        .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
 ]
 
-<span class="muScenario">scenario</span> clicking-on-a-sandbox-moves-it-to-editor-2 [
+<span class="muScenario">scenario</span> clicking-on-sandbox-edit-button-moves-it-to-editor-2 [
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  <span class="Comment"># basic recipe</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  reply 4</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  <span class="Comment"># empty recipes</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[add 2, 2]</span>
   <span class="Comment"># run it</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎add 2, 2                                         .</span>
+   <span class="Constant"> .                                                  ╎4                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -124,16 +114,13 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 68</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># it pops back into editor</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎                                                 .</span>
-   <span class="Constant"> .]                                                 ╎                                                 .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎add 2, 2                                         .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
   <span class="Comment"># cursor should be in the right place</span>
@@ -141,15 +128,12 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[0]</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎0foo                                             .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎                                                 .</span>
-   <span class="Constant"> .]                                                 ╎                                                 .</span>
-   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎0add 2, 2                                        .</span>
+<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
 ]
@@ -212,13 +196,15 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">20/height</span>
   <span class="Comment"># left editor is empty</span>
-  <span class="Comment"># right editor contains an instruction</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[print-integer screen, 4]</span>
+  assume-resources [
+  ]
+  <span class="Comment"># right editor contains a print instruction</span>
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[print-integer screen, 4]</span>
   <span class="Comment"># run the sandbox</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
@@ -239,7 +225,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 65</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
@@ -255,7 +241,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># initialize environment</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   <span class="Comment"># create 2 sandboxes and scroll to second</span>
   assume-console [
@@ -267,7 +255,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
@@ -282,7 +270,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 2</span>,<span class="Constant"> 55</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># second sandbox shows in editor; scroll resets to display first sandbox</span>
   screen-should-contain [
@@ -302,7 +290,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># initialize environment</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   render-all screen, env, render
   <span class="Comment"># create 2 sandboxes</span>
   assume-console [
@@ -312,7 +302,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     type <span class="Constant">[add 1, 1]</span>
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
@@ -331,7 +321,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># no change in contents</span>
   screen-should-contain [
@@ -353,7 +343,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># screen should show just final sandbox with the right index (1)</span>
   screen-should-contain [
diff --git a/html/edit/009-sandbox-test.mu.html b/html/edit/009-sandbox-test.mu.html
index 30c731ea..b6e0ab9b 100644
--- a/html/edit/009-sandbox-test.mu.html
+++ b/html/edit/009-sandbox-test.mu.html
@@ -40,22 +40,25 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># basic recipe</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  reply 4</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes:text, <span class="Constant">[foo]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  reply 4|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
   <span class="Comment"># run it</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
+   <span class="Constant"> .recipe foo [                                      ╎                                                 .</span>
+   <span class="Constant"> .  reply 4                                         ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .]                                                 ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎foo                                              .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
@@ -65,7 +68,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 5</span>,<span class="Constant"> 51</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># color toggles to green</span>
   screen-should-contain-in-color <span class="Constant">2/green</span>, [
@@ -85,26 +88,24 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   ]
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .␣                                                 ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
+   <span class="Constant"> .␣ecipe foo [                                      ╎                                                 .</span>
+   <span class="Constant"> .  reply 4                                         ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .]                                                 ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎foo                                              .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎4                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
   <span class="Comment"># now change the result</span>
   <span class="Comment"># then rerun</span>
   assume-console [
-    left-click<span class="Constant"> 3</span>,<span class="Constant"> 11</span>  <span class="Comment"># cursor to end of line</span>
+    left-click<span class="Constant"> 2</span>,<span class="Constant"> 11</span>  <span class="Comment"># cursor to end of line</span>
     press backspace
     type <span class="Constant">[3]</span>
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># result turns red</span>
   screen-should-contain-in-color <span class="Constant">1/red</span>, [
@@ -131,14 +132,14 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     expected-response:text <span class="Special">&lt;-</span> get *curr, <span class="Constant">expected-response:offset</span>
     <span class="muControl">break-unless</span> expected-response
     filename <span class="Special">&lt;-</span> append filename, <span class="Constant">[.out]</span>
-    save filename, expected-response
+    resources <span class="Special">&lt;-</span> dump resources, filename, expected-response
   <span class="Delimiter">}</span>
 ]
 
 <span class="muRecipe">before</span> <span class="Constant">&lt;end-restore-sandbox&gt;</span> [
   <span class="Delimiter">{</span>
     filename <span class="Special">&lt;-</span> append filename, <span class="Constant">[.out]</span>
-    contents <span class="Special">&lt;-</span> restore filename
+    contents <span class="Special">&lt;-</span> slurp resources, filename
     <span class="muControl">break-unless</span> contents
     *curr <span class="Special">&lt;-</span> put *curr, <span class="Constant">expected-response:offset</span>, contents
   <span class="Delimiter">}</span>
@@ -163,7 +164,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     <span class="muControl">break-unless</span> sandbox
     <span class="Comment"># toggle its expected-response, and save session</span>
     sandbox <span class="Special">&lt;-</span> toggle-expected-response sandbox
-    save-sandboxes env
+    save-sandboxes env, resources
     hide-screen screen
     screen <span class="Special">&lt;-</span> render-sandbox-side screen, env, render
     screen <span class="Special">&lt;-</span> update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
diff --git a/html/edit/010-sandbox-trace.mu.html b/html/edit/010-sandbox-trace.mu.html
index 66943462..ab32cbf0 100644
--- a/html/edit/010-sandbox-trace.mu.html
+++ b/html/edit/010-sandbox-trace.mu.html
@@ -40,22 +40,25 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># basic recipe</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  stash [abc]</span>
-]]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  stash [abc]</span>|
+<span class="Constant">      |]|</span>
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
   <span class="Comment"># run it</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  stash [abc]                                     ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
+   <span class="Constant"> .recipe foo [                                      ╎                                                 .</span>
+   <span class="Constant"> .  stash [abc]                                     ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .]                                                 ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎foo                                              .</span>
 <span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -64,17 +67,17 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 4</span>,<span class="Constant"> 51</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <span class="Special">&lt;-</span> copy <span class="Constant">9251/␣</span>
     print screen, cursor
   ]
   <span class="Comment"># trace now printed and cursor shouldn't have budged</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .␣                                                 ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  stash [abc]                                     ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
+   <span class="Constant"> .␣ecipe foo [                                      ╎                                                 .</span>
+   <span class="Constant"> .  stash [abc]                                     ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .]                                                 ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎foo                                              .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎abc                                              .</span>
   ]
   screen-should-contain-in-color <span class="Constant">245/grey</span>, [
@@ -90,16 +93,16 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 4</span>,<span class="Constant"> 55</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     print screen, cursor
   ]
   <span class="Comment"># trace hidden again</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .␣                                                 ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  stash [abc]                                     ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .]                                                 ╎foo                                              .</span>
+   <span class="Constant"> .␣ecipe foo [                                      ╎                                                 .</span>
+   <span class="Constant"> .  stash [abc]                                     ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .]                                                 ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .                                                  ╎foo                                              .</span>
 <span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -110,24 +113,27 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># basic recipe</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  stash [abc]</span>
-  <span class="muControl">reply</span><span class="Constant"> 4</span>
-]]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  stash [abc]</span>|
+      <span class="Constant">|</span>  <span class="muControl">reply</span> 4|
+<span class="Constant">      |]|</span>
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
   <span class="Comment"># run it</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  stash [abc]                                     ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .  reply 4                                         ╎foo                                              .</span>
-   <span class="Constant"> .]                                                 ╎4                                                .</span>
+   <span class="Constant"> .recipe foo [                                      ╎                                                 .</span>
+   <span class="Constant"> .  stash [abc]                                     ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .]                                                 ╎foo                                              .</span>
+   <span class="Constant"> .                                                  ╎4                                                .</span>
 <span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
@@ -136,16 +142,16 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 4</span>,<span class="Constant"> 51</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># trace now printed above result</span>
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎                                                 .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  stash [abc]                                     ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .  reply 4                                         ╎foo                                              .</span>
-   <span class="Constant"> .]                                                 ╎abc                                              .</span>
+   <span class="Constant"> .recipe foo [                                      ╎                                                 .</span>
+   <span class="Constant"> .  stash [abc]                                     ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .  reply 4                                         ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .]                                                 ╎foo                                              .</span>
+   <span class="Constant"> .                                                  ╎abc                                              .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎8 instructions run                               .</span>
    <span class="Constant"> .                                                  ╎4                                                .</span>
    <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
@@ -157,13 +163,15 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[stash 123456789]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[stash 123456789]</span>
   <span class="Comment"># create and expand the trace</span>
   assume-console [
     press F4
     left-click<span class="Constant"> 4</span>,<span class="Constant"> 51</span>
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .                                                                                 run (F4)           .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
@@ -177,7 +185,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     left-click<span class="Constant"> 5</span>,<span class="Constant"> 57</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># no change; doesn't die</span>
   screen-should-contain [
diff --git a/html/edit/011-errors.mu.html b/html/edit/011-errors.mu.html
index 91e48c57..72f21172 100644
--- a/html/edit/011-errors.mu.html
+++ b/html/edit/011-errors.mu.html
@@ -40,12 +40,12 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
 ]
 
 <span class="Comment"># copy code from recipe editor, persist to disk, load, save any errors</span>
-<span class="muRecipe">def!</span> update-recipes env:&amp;:environment, screen:&amp;:screen<span class="muRecipe"> -&gt; </span>errors-found?:bool, env:&amp;:environment, screen:&amp;:screen [
+<span class="muRecipe">def!</span> update-recipes env:&amp;:environment, resources:&amp;:resources, screen:&amp;:screen<span class="muRecipe"> -&gt; </span>errors-found?:bool, env:&amp;:environment, resources:&amp;:resources, screen:&amp;:screen [
   <span class="Constant">local-scope</span>
   <span class="Constant">load-ingredients</span>
   recipes:&amp;:editor <span class="Special">&lt;-</span> get *env, <span class="Constant">recipes:offset</span>
   in:text <span class="Special">&lt;-</span> editor-contents recipes
-  save <span class="Constant">[recipes.mu]</span>, in
+  resources <span class="Special">&lt;-</span> dump resources, <span class="Constant">[lesson/recipes.mu]</span>, in
   recipe-errors:text <span class="Special">&lt;-</span> reload in
   *env <span class="Special">&lt;-</span> put *env, <span class="Constant">recipe-errors:offset</span>, recipe-errors
   <span class="Comment"># if recipe editor has errors, stop</span>
@@ -153,23 +153,36 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  get 123:num, foo:offset</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  get 123:num, foo:offset|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
+  render-all screen, env, render
+  screen-should-contain [
+   <span class="Constant"> .                                                                                 run (F4)           .</span>
+   <span class="Constant"> .recipe foo [                                      ╎foo                                              .</span>
+   <span class="Constant"> .  get 123:num, foo:offset                         ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .]                                                 ╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
+   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
+  ]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .  errors found                                                                   run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  get 123:num, foo:offset                         ╎                                                 .</span>
+   <span class="Constant"> .recipe foo [                                      ╎foo                                              .</span>
+   <span class="Constant"> .  get 123:num, foo:offset                         ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .]                                                 ╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .foo: unknown element 'foo' in container 'number'  ╎                                                 .</span>
    <span class="Constant"> .foo: first ingredient of 'get' should be a contai↩╎                                                 .</span>
    <span class="Constant"> .ner, but got '123:num'                            ╎                                                 .</span>
@@ -193,7 +206,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   assume-console [
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 80</span>
     <span class="Comment"># create invalid sandbox 1</span>
@@ -204,7 +219,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># status line shows that error is in first sandbox</span>
   screen-should-contain [
@@ -216,7 +231,9 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[]</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[]</span>
   assume-console [
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 80</span>
     <span class="Comment"># create invalid sandbox 2</span>
@@ -230,7 +247,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># status line shows that error is in second sandbox</span>
   screen-should-contain [
@@ -242,13 +259,13 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, <span class="Constant">[get foo, x:offset]</span>  <span class="Comment"># invalid</span>
+  assume-resources [
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[get foo, x:offset]</span>  <span class="Comment"># invalid</span>
   assume-console [
     press F4  <span class="Comment"># generate error</span>
   ]
-  run [
-    event-loop screen, console, env
-  ]
+  event-loop screen, console, env, resources
   assume-console [
     left-click<span class="Constant"> 3</span>,<span class="Constant"> 58</span>
     press ctrl-k
@@ -256,7 +273,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4  <span class="Comment"># update sandbox</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># error should disappear</span>
   screen-should-contain [
@@ -276,26 +293,31 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
   <span class="Comment"># define a shape-shifting recipe with an error</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[recipe foo x:_elem -&gt; z:_elem [</span>
-<span class="Constant">local-scope</span>
-<span class="Constant">load-ingredients</span>
-<span class="Constant">y:&amp;:num &lt;- copy 0</span>
-<span class="Constant">z &lt;- add x, y</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo 2]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo x:_elem<span class="muRecipe"> -&gt; </span>z:_elem <span class="Constant">[|</span>
+<span class="Constant">      |  local-scope|</span>
+<span class="Constant">      |  load-ingredients|</span>
+<span class="Constant">      |  y:&amp;:num &lt;- copy 0|</span>
+<span class="Constant">      |  z &lt;- add x, y|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo 2]</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .  errors found (0)                                                               run (F4)           .</span>
    <span class="Constant"> .recipe foo x:_elem -&gt; z:_elem [                   ╎                                                 .</span>
-   <span class="Constant"> .local-scope                                       ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .load-ingredients                                  ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .y:&amp;:num &lt;- copy 0                                 ╎foo 2                                            .</span>
-   <span class="Constant"> .z &lt;- add x, y                                     ╎foo_2: 'add' requires number ingredients, but go↩.</span>
+   <span class="Constant"> .  local-scope                                     ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .  load-ingredients                                ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .  y:&amp;:num &lt;- copy 0                               ╎foo 2                                            .</span>
+   <span class="Constant"> .  z &lt;- add x, y                                   ╎foo_2: 'add' requires number ingredients, but go↩.</span>
    <span class="Constant"> .]                                                 ╎t 'y'                                            .</span>
-<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
   <span class="Comment"># now rerun everything</span>
@@ -303,18 +325,19 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># error should remain unchanged</span>
   screen-should-contain [
    <span class="Constant"> .  errors found (0)                                                               run (F4)           .</span>
    <span class="Constant"> .recipe foo x:_elem -&gt; z:_elem [                   ╎                                                 .</span>
-   <span class="Constant"> .local-scope                                       ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .load-ingredients                                  ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .y:&amp;:num &lt;- copy 0                                 ╎foo 2                                            .</span>
-   <span class="Constant"> .z &lt;- add x, y                                     ╎foo_3: 'add' requires number ingredients, but go↩.</span>
+   <span class="Constant"> .  local-scope                                     ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .  load-ingredients                                ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .  y:&amp;:num &lt;- copy 0                               ╎foo 2                                            .</span>
+   <span class="Constant"> .  z &lt;- add x, y                                   ╎foo_3: 'add' requires number ingredients, but go↩.</span>
    <span class="Constant"> .]                                                 ╎t 'y'                                            .</span>
-<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
 ]
@@ -324,17 +347,21 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
   <span class="Comment"># overload a well-known shape-shifting recipe</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[recipe length l:&amp;:list:_elem -&gt; n:num [</span>
-<span class="Constant">]</span>]
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> length l:&amp;:list:_elem<span class="muRecipe"> -&gt; </span>n:num <span class="Constant">[|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
   <span class="Comment"># call code that uses other variants of it, but not it itself</span>
-  sandbox:text <span class="Special">&lt;-</span> new <span class="Constant">[x:&amp;:list:num &lt;- copy 0</span>
+  test-sandbox:text <span class="Special">&lt;-</span> new <span class="Constant">[x:&amp;:list:num &lt;- copy 0</span>
 <span class="Constant">to-text x]</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, sandbox
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, test-sandbox
   <span class="Comment"># run it once</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   <span class="Comment"># no errors anywhere on screen (can't check anything else, since to-text will return an address)</span>
   screen-should-contain-in-color <span class="Constant">1/red</span>, [
    <span class="Constant"> .                                                                                                    .</span>
@@ -358,7 +385,7 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># still no errors</span>
   screen-should-contain-in-color <span class="Constant">1/red</span>, [
@@ -384,24 +411,30 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  x &lt;- copy 0</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  x &lt;- copy 0|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .  errors found                                                                   run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  x &lt;- copy 0                                     ╎                                                 .</span>
+   <span class="Constant"> .recipe foo [                                      ╎foo                                              .</span>
+   <span class="Constant"> .  x &lt;- copy 0                                     ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .]                                                 ╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .foo: missing type for 'x' in 'x &lt;- copy 0'        ╎                                                 .</span>
+   <span class="Constant"> .foo: can't copy '0' to 'x'; types don't match     ╎                                                 .</span>
+   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
 ]
 
@@ -410,22 +443,23 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
   <span class="Comment"># recipe is incomplete (unbalanced '[')</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo \\[</span>
-<span class="Constant">  x &lt;- copy 0</span>
-<span class="Constant">]</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo \\\<span class="Constant">[|</span>
+<span class="Constant">      |  x &lt;- copy 0|</span>
+<span class="Constant">    ]</span>
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .  errors found                                                                   run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo \\[                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  x &lt;- copy 0                                     ╎                                                 .</span>
+   <span class="Constant"> .recipe foo \\[                                      ╎foo                                              .</span>
+   <span class="Constant"> .  x &lt;- copy 0                                     ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .9: unbalanced '\\[' for recipe                      ╎                                                 .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
@@ -437,27 +471,30 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  local-scope</span>
-<span class="Constant">  x:&amp;:point &lt;- new point:type</span>
-<span class="Constant">  get x:&amp;:point, 1:offset</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  local-scope|</span>
+<span class="Constant">      |  x:&amp;:point &lt;- new point:type|</span>
+<span class="Constant">      |  get x:&amp;:point, 1:offset|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .  errors found                                                                   run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  local-scope                                     ╎                                                 .</span>
+   <span class="Constant"> .recipe foo [                                      ╎foo                                              .</span>
+   <span class="Constant"> .  local-scope                                     ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .  x:&amp;:point &lt;- new point:type                     ╎                                                 .</span>
    <span class="Constant"> .  get x:&amp;:point, 1:offset                         ╎                                                 .</span>
    <span class="Constant"> .]                                                 ╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .foo: first ingredient of 'get' should be a contai↩╎                                                 .</span>
    <span class="Constant"> .ner, but got 'x:&amp;:point'                          ╎                                                 .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
@@ -469,29 +506,32 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  local-scope</span>
-<span class="Constant">  x:num &lt;- copy 0</span>
-<span class="Constant">  y:&amp;:point &lt;- new point:type</span>
-<span class="Constant">  get *y:&amp;:point, x:num</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  local-scope|</span>
+<span class="Constant">      |  x:num &lt;- copy 0|</span>
+<span class="Constant">      |  y:&amp;:point &lt;- new point:type|</span>
+<span class="Constant">      |  get *y:&amp;:point, x:num|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .  errors found                                                                   run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  local-scope                                     ╎                                                 .</span>
+   <span class="Constant"> .recipe foo [                                      ╎foo                                              .</span>
+   <span class="Constant"> .  local-scope                                     ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .  x:num &lt;- copy 0                                 ╎                                                 .</span>
    <span class="Constant"> .  y:&amp;:point &lt;- new point:type                     ╎                                                 .</span>
    <span class="Constant"> .  get *y:&amp;:point, x:num                           ╎                                                 .</span>
    <span class="Constant"> .]                                                 ╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .foo: second ingredient of 'get' should have type ↩╎                                                 .</span>
    <span class="Constant"> .'offset', but got 'x:num'                         ╎                                                 .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
@@ -502,25 +542,28 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
 <span class="muScenario">scenario</span> run-shows-errors-everytime [
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
-  <span class="Comment"># try to run a file with an error</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">15/height</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[ </span>
-<span class="Constant">recipe foo [</span>
-<span class="Constant">  local-scope</span>
-<span class="Constant">  x:num &lt;- copy y:num</span>
-<span class="Constant">]</span>]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  <span class="Comment"># try to run a file with an error</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  local-scope|</span>
+<span class="Constant">      |  x:num &lt;- copy y:num|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
    <span class="Constant"> .  errors found                                                                   run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  local-scope                                     ╎                                                 .</span>
+   <span class="Constant"> .recipe foo [                                      ╎foo                                              .</span>
+   <span class="Constant"> .  local-scope                                     ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .  x:num &lt;- copy y:num                             ╎                                                 .</span>
    <span class="Constant"> .]                                                 ╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .foo: tried to read ingredient 'y' in 'x:num &lt;- co↩╎                                                 .</span>
    <span class="Constant"> .py y:num' but it hasn't been written to yet       ╎                                                 .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
@@ -531,15 +574,15 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .  errors found                                                                   run (F4)           .</span>
-   <span class="Constant"> .                                                  ╎foo                                              .</span>
-   <span class="Constant"> .recipe foo [                                      ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .  local-scope                                     ╎                                                 .</span>
+   <span class="Constant"> .recipe foo [                                      ╎foo                                              .</span>
+   <span class="Constant"> .  local-scope                                     ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .  x:num &lt;- copy y:num                             ╎                                                 .</span>
    <span class="Constant"> .]                                                 ╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
    <span class="Constant"> .foo: tried to read ingredient 'y' in 'x:num &lt;- co↩╎                                                 .</span>
    <span class="Constant"> .py y:num' but it hasn't been written to yet       ╎                                                 .</span>
    <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
@@ -551,15 +594,15 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  <span class="Comment"># right editor contains an illegal instruction</span>
-  sandbox:text <span class="Special">&lt;-</span> new <span class="Constant">[get 1234:num, foo:offset]</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, sandbox
-  <span class="Comment"># run the code in the editors</span>
+  assume-resources [
+  ]
+  <span class="Comment"># sandbox editor contains an illegal instruction</span>
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[get 1234:num, foo:offset]</span>
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># check that screen prints error message in red</span>
   screen-should-contain [
@@ -613,16 +656,17 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
-  <span class="Comment"># right editor contains an illegal instruction</span>
-  sandbox:text <span class="Special">&lt;-</span> new <span class="Constant">[get 1234:num, foo:offset]</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, <span class="Constant">[]</span>, sandbox
+  assume-resources [
+  ]
+  <span class="Comment"># sandbox editor contains an illegal instruction</span>
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[get 1234:num, foo:offset]</span>
   <span class="Comment"># run the code in the editors multiple times</span>
   assume-console [
     press F4
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># check that screen prints error message just once</span>
   screen-should-contain [
@@ -643,20 +687,23 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   <span class="Constant">local-scope</span>
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">20/height</span>
-  <span class="Comment"># left editor is empty</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[recipe foo [</span>
-<span class="Constant">  {</span>
-<span class="Constant">    loop</span>
-<span class="Constant">  }</span>
-<span class="Constant">]</span>]
-  <span class="Comment"># right editor contains an instruction</span>
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo]</span>
+  <span class="Comment"># sandbox editor will trigger an infinite loop</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  {|</span>
+<span class="Constant">      |    loop|</span>
+<span class="Constant">      |  }|</span>
+<span class="Constant">      |]</span>|
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo]</span>
   <span class="Comment"># run the sandbox</span>
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
    <span class="Constant"> .  errors found (0)                                                               run (F4)           .</span>
@@ -665,7 +712,8 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
    <span class="Constant"> .    loop                                          ╎0   edit          copy            delete         .</span>
    <span class="Constant"> .  }                                               ╎foo                                              .</span>
    <span class="Constant"> .]                                                 ╎took too long!                                   .</span>
-<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎                                                 .</span>
    <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
 ]
@@ -675,51 +723,56 @@ body { font-size: 12pt; font-family: monospace; color: #eeeeee; background-color
   trace-until <span class="Constant">100/app</span>  <span class="Comment"># trace too long</span>
   assume-screen <span class="Constant">100/width</span>, <span class="Constant">10/height</span>
   <span class="Comment"># generate a stash and a error</span>
-  recipes:text <span class="Special">&lt;-</span> new <span class="Constant">[recipe foo [</span>
-<span class="Constant">local-scope</span>
-<span class="Constant">a:num &lt;- next-ingredient</span>
-<span class="Constant">b:num &lt;- next-ingredient</span>
-<span class="Constant">stash [dividing by]</span>, b
-_, c:num <span class="Special">&lt;-</span> divide-with-remainder a, b
-<span class="muControl">reply</span> b
-]]
-  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment screen, recipes, <span class="Constant">[foo 4, 0]</span>
+  assume-resources [
+    <span class="Constant">[lesson/recipes.mu]</span> <span class="Special">&lt;-</span> [
+      <span class="Constant">|recipe</span> foo <span class="Constant">[|</span>
+<span class="Constant">      |  local-scope|</span>
+<span class="Constant">      |  a:num &lt;- next-ingredient|</span>
+<span class="Constant">      |  b:num &lt;- next-ingredient|</span>
+<span class="Constant">      |  stash [dividing by]</span>, b|
+      <span class="Constant">|</span>  _, c:num <span class="Special">&lt;-</span> divide-with-remainder a, b|
+      <span class="Constant">|</span>  <span class="muControl">reply</span> b|
+<span class="Constant">      |]|</span>
+    ]
+  ]
+  env:&amp;:environment <span class="Special">&lt;-</span> new-programming-environment resources, screen, <span class="Constant">[foo 4, 0]</span>
   <span class="Comment"># run</span>
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   <span class="Comment"># screen prints error message</span>
   screen-should-contain [
    <span class="Constant"> .  errors found (0)                                                               run (F4)           .</span>
    <span class="Constant"> .recipe foo [                                      ╎                                                 .</span>
-   <span class="Constant"> .local-scope                                       ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .a:num &lt;- next-ingredient                          ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .b:num &lt;- next-ingredient                          ╎foo 4, 0                                         .</span>
-   <span class="Constant"> .stash [dividing by], b                            ╎foo: divide by zero in '_, c:num &lt;- divide-with-↩.</span>
-   <span class="Constant"> ._, c:num &lt;- divide-with-remainder a, b            ╎remainder a, b'                                  .</span>
-   <span class="Constant"> .reply b                                           ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .  local-scope                                     ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .  a:num &lt;- next-ingredient                        ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .  b:num &lt;- next-ingredient                        ╎foo 4, 0                                         .</span>
+   <span class="Constant"> .  stash [dividing by], b                          ╎foo: divide by zero in '_, c:num &lt;- divide-with-↩.</span>
+   <span class="Constant"> .  _, c:num &lt;- divide-with-remainder a, b          ╎remainder a, b'                                  .</span>
+   <span class="Constant"> .  reply b                                         ╎─────────────────────────────────────────────────.</span>
    <span class="Constant"> .]                                                 ╎                                                 .</span>
+   <span class="Constant"> .                                                  ╎                                                 .</span>
   ]
   <span class="Comment"># click on the call in the sandbox</span>
   assume-console [
     left-click<span class="Constant"> 4</span>,<span class="Constant"> 55</span>
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   <span class="Comment"># screen should expand trace</span>
   screen-should-contain [
    <span class="Constant"> .  errors found (0)                                                               run (F4)           .</span>
    <span class="Constant"> .recipe foo [                                      ╎                                                 .</span>
-   <span class="Constant"> .local-scope                                       ╎─────────────────────────────────────────────────.</span>
-   <span class="Constant"> .a:num &lt;- next-ingredient                          ╎0   edit          copy            delete         .</span>
-   <span class="Constant"> .b:num &lt;- next-ingredient                          ╎foo 4, 0                                         .</span>
-   <span class="Constant"> .stash [dividing by], b                            ╎dividing by 0                                    .</span>
-   <span class="Constant"> ._, c:num &lt;- divide-with-remainder a, b            ╎14 instructions run                              .</span>
-   <span class="Constant"> .reply b                                           ╎foo: divide by zero in '_, c:num &lt;- divide-with-↩.</span>
+   <span class="Constant"> .  local-scope                                     ╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .  a:num &lt;- next-ingredient                        ╎0   edit          copy            delete         .</span>
+   <span class="Constant"> .  b:num &lt;- next-ingredient                        ╎foo 4, 0                                         .</span>
+   <span class="Constant"> .  stash [dividing by], b                          ╎dividing by 0                                    .</span>
+   <span class="Constant"> .  _, c:num &lt;- divide-with-remainder a, b          ╎14 instructions run                              .</span>
+   <span class="Constant"> .  reply b                                         ╎foo: divide by zero in '_, c:num &lt;- divide-with-↩.</span>
    <span class="Constant"> .]                                                 ╎remainder a, b'                                  .</span>
-<span class="Constant">    .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.</span>
+   <span class="Constant"> .                                                  ╎─────────────────────────────────────────────────.</span>
   ]
 ]
 </pre>
diff --git a/sandbox/004-programming-environment.mu b/sandbox/004-programming-environment.mu
index 84898f71..13502d2a 100644
--- a/sandbox/004-programming-environment.mu
+++ b/sandbox/004-programming-environment.mu
@@ -3,15 +3,9 @@
 def! main [
   local-scope
   open-console
-  initial-sandbox:text <- new []
-  hide-screen 0/screen
-  env:&:environment <- new-programming-environment 0/screen, initial-sandbox
-  env <- restore-sandboxes env
-  render-sandbox-side 0/screen, env, render
-  current-sandbox:&:editor <- get *env, current-sandbox:offset
-  update-cursor 0/screen, current-sandbox, env
-  show-screen 0/screen
-  event-loop 0/screen, 0/console, env
+  env:&:environment <- new-programming-environment 0/filesystem, 0/screen
+  render-all 0/screen, env, render
+  event-loop 0/screen, 0/console, env, 0/filesystem
   # never gets here
 ]
 
@@ -19,26 +13,18 @@ container environment [
   current-sandbox:&:editor
 ]
 
-def new-programming-environment screen:&:screen, initial-sandbox-contents:text -> result:&:environment, screen:&:screen [
+def new-programming-environment resources:&:resources, screen:&:screen, test-sandbox-editor-contents:text -> result:&:environment [
   local-scope
   load-ingredients
   width:num <- screen-width screen
-  height:num <- screen-height screen
-  # top menu
   result <- new environment:type
-  draw-horizontal screen, 0, 0/left, width, 32/space, 0/black, 238/grey
-  button-start:num <- subtract width, 20
-  button-on-screen?:bool <- greater-or-equal button-start, 0
-  assert button-on-screen?, [screen too narrow for menu]
-  screen <- move-cursor screen, 0/row, button-start
-  print screen, [ run (F4) ], 255/white, 161/reddish
   # sandbox editor
-  current-sandbox:&:editor <- new-editor initial-sandbox-contents, 0, width/right
+  current-sandbox:&:editor <- new-editor test-sandbox-editor-contents, 0/left, width/right
   *result <- put *result, current-sandbox:offset, current-sandbox
   <programming-environment-initialization>
 ]
 
-def event-loop screen:&:screen, console:&:console, env:&:environment -> screen:&:screen, console:&:console, env:&:environment [
+def event-loop screen:&:screen, console:&:console, env:&:environment, resources:&:resources -> screen:&:screen, console:&:console, env:&:environment, resources:&:resources [
   local-scope
   load-ingredients
   current-sandbox:&:editor <- get *env, current-sandbox:offset
@@ -357,8 +343,3 @@ after <global-type> [
     loop +next-event
   }
 ]
-
-# dummy
-def restore-sandboxes env:&:environment -> env:&:environment [
-  # do nothing; redefined later
-]
diff --git a/sandbox/005-sandbox.mu b/sandbox/005-sandbox.mu
index 9fd2a08b..b5ccd98f 100644
--- a/sandbox/005-sandbox.mu
+++ b/sandbox/005-sandbox.mu
@@ -7,6 +7,16 @@
 # This layer draws the menubar buttons in non-editable sandboxes but they
 # don't do anything yet. Later layers implement each button.
 
+def! main [
+  local-scope
+  open-console
+  env:&:environment <- new-programming-environment 0/filesystem, 0/screen
+  env <- restore-sandboxes env
+  render-all 0/screen, env, render
+  event-loop 0/screen, 0/console, env, 0/filesystem
+  # never gets here
+]
+
 container environment [
   sandbox:&:sandbox  # list of sandboxes, from top to bottom. TODO: switch to &:list:sandbox
   render-from:num
@@ -32,14 +42,17 @@ scenario run-and-show-results [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 15/height
+  # recipes.mu is empty
+  assume-resources [
+  ]
   # sandbox editor contains an instruction without storing outputs
-  env:&:environment <- new-programming-environment screen, [divide-with-remainder 11, 3]
+  env:&:environment <- new-programming-environment resources, screen, [divide-with-remainder 11, 3]
   # run the code in the editors
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that screen prints the results
   screen-should-contain [
@@ -82,7 +95,7 @@ scenario run-and-show-results [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that screen prints both sandboxes
   screen-should-contain [
@@ -108,9 +121,7 @@ after <global-keypress> [
     do-run?:bool <- equal k, 65532/F4
     break-unless do-run?
     screen <- update-status screen, [running...       ], 245/grey
-    test-recipes:text, _/optional <- next-ingredient
-    error?:bool, env, screen <- run-sandboxes env, screen, test-recipes
-    test-recipes <- copy 0  # abandon
+    error?:bool <- run-sandboxes env, resources, screen
     # F4 might update warnings and results on both sides
     screen <- render-all screen, env, render
     {
@@ -122,10 +133,10 @@ after <global-keypress> [
   }
 ]
 
-def run-sandboxes env:&:environment, screen:&:screen, test-recipes:text -> errors-found?:bool, env:&:environment, screen:&:screen [
+def run-sandboxes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, resources:&:resources, screen:&:screen [
   local-scope
   load-ingredients
-  errors-found?:bool, env, screen <- update-recipes env, screen, test-recipes
+  errors-found?:bool <- update-recipes env, resources, screen
   # check contents of editor
   <run-sandboxes-begin>
   current-sandbox:&:editor <- get *env, current-sandbox:offset
@@ -150,7 +161,7 @@ def run-sandboxes env:&:environment, screen:&:screen, test-recipes:text -> error
     *current-sandbox <- put *current-sandbox, top-of-screen:offset, init
   }
   # save all sandboxes before running, just in case we die when running
-  save-sandboxes env
+  save-sandboxes env, resources
   # run all sandboxes
   curr:&:sandbox <- get *env, sandbox:offset
   idx:num <- copy 0
@@ -164,20 +175,13 @@ def run-sandboxes env:&:environment, screen:&:screen, test-recipes:text -> error
   <run-sandboxes-end>
 ]
 
-# load code from recipes.mu, or from test-recipes in tests
+# load code from disk
 # replaced in a later layer (whereupon errors-found? will actually be set)
-def update-recipes env:&:environment, screen:&:screen, test-recipes:text -> errors-found?:bool, env:&:environment, screen:&:screen [
+def update-recipes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, screen:&:screen [
   local-scope
   load-ingredients
-  {
-    break-if test-recipes
-    in:text <- restore [recipes.mu]  # newlayer: persistence
-    reload in
-  }
-  {
-    break-unless test-recipes
-    reload test-recipes
-  }
+  in:text <- slurp resources, [lesson/recipes.mu]
+  reload in
   errors-found? <- copy 0/false
 ]
 
@@ -198,7 +202,7 @@ def update-status screen:&:screen, msg:text, color:num -> screen:&:screen [
   screen <- print screen, msg, color, 238/grey/background
 ]
 
-def save-sandboxes env:&:environment [
+def save-sandboxes env:&:environment, resources:&:resources -> resources:&:resources [
   local-scope
   load-ingredients
   current-sandbox:&:editor <- get *env, current-sandbox:offset
@@ -209,8 +213,8 @@ def save-sandboxes env:&:environment [
   {
     break-unless curr
     data:text <- get *curr, data:offset
-    filename:text <- to-text idx
-    save filename, data
+    filename:text <- append [lesson/], idx
+    resources <- dump resources, filename, data
     <end-save-sandbox>
     idx <- add idx, 1
     curr <- get *curr, next-sandbox:offset
@@ -401,7 +405,7 @@ def render-text screen:&:screen, s:text, left:num, right:num, color:num, row:num
 ]
 
 # assumes programming environment has no sandboxes; restores them from previous session
-def! restore-sandboxes env:&:environment -> env:&:environment [
+def restore-sandboxes env:&:environment, resources:&:resources -> env:&:environment [
   local-scope
   load-ingredients
   # read all scenarios, pushing them to end of a list of scenarios
@@ -409,8 +413,8 @@ def! restore-sandboxes env:&:environment -> env:&:environment [
   curr:&:sandbox <- copy 0
   prev:&:sandbox <- copy 0
   {
-    filename:text <- to-text idx
-    contents:text <- restore filename
+    filename:text <- append [lesson/], idx
+    contents:text <- slurp resources, filename
     break-unless contents  # stop at first error; assuming file didn't exist
                            # todo: handle empty sandbox
     # create new sandbox for file
@@ -505,19 +509,23 @@ scenario run-updates-results [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 12/height
   # define a recipe (no indent for the 'add' line below so column numbers are more obvious)
-  recipes:text <- new [ 
-recipe foo [
-local-scope
-z:num <- add 2, 2
-reply z
-]]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      ||
+      |recipe foo [|
+      |  local-scope|
+      |  z:num <- add 2, 2|
+      |  reply z|
+      |]|
+    ]
+  ]
   # sandbox editor contains an instruction without storing outputs
-  env:&:environment <- new-programming-environment screen, [foo]
+  env:&:environment <- new-programming-environment resources, screen, [foo]  # contents of sandbox editor
   # run the code in the editors
   assume-console [
     press F4
   ]
-  event-loop screen, console, env, recipes
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -529,17 +537,21 @@ reply z
     .                                                  .
   ]
   # make a change (incrementing one of the args to 'add'), then rerun
-  recipes:text <- new [ 
-def foo [
-local-scope
-z:num <- add 2, 3
-return z
-]]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      ||
+      |recipe foo [|
+      |  local-scope|
+      |  z:num <- add 2, 3|
+      |  reply z|
+      |]|
+    ]
+  ]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   # check that screen updates the result on the right
   screen-should-contain [
@@ -558,14 +570,17 @@ scenario run-instruction-manages-screen-per-sandbox [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
-  # editor contains an instruction
-  env:&:environment <- new-programming-environment screen, [print-integer screen, 4]
+  # empty recipes
+  assume-resources [
+  ]
+  # sandbox editor contains an instruction
+  env:&:environment <- new-programming-environment resources, screen, [print-integer screen, 4]  # contents of sandbox editor
   # run the code in the editor
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that it prints a little toy screen
   screen-should-contain [
@@ -629,13 +644,15 @@ scenario scrolling-down-past-bottom-of-sandbox-editor [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # initialize
-  env:&:environment <- new-programming-environment screen, [add 2, 2]
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 2, 2]
   render-all screen, env, render
   assume-console [
     # create a sandbox
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -651,7 +668,7 @@ scenario scrolling-down-past-bottom-of-sandbox-editor [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -671,7 +688,7 @@ scenario scrolling-down-past-bottom-of-sandbox-editor [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -765,7 +782,9 @@ scenario scrolling-through-multiple-sandboxes [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes
   assume-console [
@@ -775,7 +794,7 @@ scenario scrolling-through-multiple-sandboxes [
     type [add 1, 1]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   cursor:char <- copy 9251/␣
   print screen, cursor
   screen-should-contain [
@@ -797,7 +816,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -821,7 +840,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # just second sandbox displayed
   screen-should-contain [
@@ -838,7 +857,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # no change
   screen-should-contain [
@@ -855,7 +874,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # back to displaying both sandboxes without editor
   screen-should-contain [
@@ -876,7 +895,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -900,7 +919,7 @@ scenario scrolling-through-multiple-sandboxes [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -926,7 +945,9 @@ scenario scrolling-manages-sandbox-index-correctly [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create a sandbox
   assume-console [
@@ -934,7 +955,7 @@ scenario scrolling-manages-sandbox-index-correctly [
     type [add 1, 1]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -950,7 +971,7 @@ scenario scrolling-manages-sandbox-index-correctly [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # sandbox editor hidden; first sandbox displayed
   # cursor moves to first sandbox
@@ -968,7 +989,7 @@ scenario scrolling-manages-sandbox-index-correctly [
     press page-up
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # back to displaying both sandboxes as well as editor
   screen-should-contain [
@@ -986,7 +1007,7 @@ scenario scrolling-manages-sandbox-index-correctly [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # sandbox editor hidden; first sandbox displayed
   # cursor moves to first sandbox
diff --git a/sandbox/006-sandbox-copy.mu b/sandbox/006-sandbox-copy.mu
index 7201afd7..995f4c7c 100644
--- a/sandbox/006-sandbox-copy.mu
+++ b/sandbox/006-sandbox-copy.mu
@@ -5,11 +5,15 @@ scenario copy-a-sandbox-to-editor [
   local-scope
   trace-until 50/app  # trace too long
   assume-screen 50/width, 10/height
-  env:&:environment <- new-programming-environment screen, [add 1, 1]
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
+  # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -47,7 +51,7 @@ scenario copy-a-sandbox-to-editor [
     type [0]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                               run (F4)           .
@@ -67,11 +71,15 @@ scenario copy-a-sandbox-to-editor-2 [
   local-scope
   trace-until 50/app  # trace too long
   assume-screen 50/width, 10/height
-  env:&:environment <- new-programming-environment screen, [add 1, 1]
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
+  # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -84,12 +92,12 @@ scenario copy-a-sandbox-to-editor-2 [
     .                                                  .
     .                                                  .
   ]
-  # click at right edge of 'copy' button
+  # click at right edge of 'copy' button (just before 'delete')
   assume-console [
     left-click 3, 33
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # it copies into editor
   screen-should-contain [
@@ -109,7 +117,7 @@ scenario copy-a-sandbox-to-editor-2 [
     type [0]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                               run (F4)           .
@@ -218,11 +226,15 @@ scenario copy-fails-if-sandbox-editor-not-empty [
   local-scope
   trace-until 50/app  # trace too long
   assume-screen 50/width, 10/height
-  env:&:environment <- new-programming-environment screen, [add 1, 1]
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
+  # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -258,7 +270,7 @@ scenario copy-fails-if-sandbox-editor-not-empty [
     type [1]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                               run (F4)           .
diff --git a/sandbox/007-sandbox-delete.mu b/sandbox/007-sandbox-delete.mu
index 576d6761..ddfbf692 100644
--- a/sandbox/007-sandbox-delete.mu
+++ b/sandbox/007-sandbox-delete.mu
@@ -4,7 +4,9 @@ scenario deleting-sandboxes [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 15/height
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   # run a few commands
   assume-console [
     type [divide-with-remainder 11, 3]
@@ -12,7 +14,7 @@ scenario deleting-sandboxes [
     type [add 2, 2]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -33,7 +35,7 @@ scenario deleting-sandboxes [
     left-click 7, 34
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                               run (F4)           .
@@ -50,7 +52,7 @@ scenario deleting-sandboxes [
     left-click 3, 49
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                               run (F4)           .
@@ -148,7 +150,9 @@ scenario deleting-sandbox-after-scroll [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes and scroll to second
   assume-console [
@@ -159,7 +163,7 @@ scenario deleting-sandbox-after-scroll [
     press F4
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .──────────────────────────────────────────────────.
@@ -177,7 +181,7 @@ scenario deleting-sandbox-after-scroll [
     left-click 6, 34
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # second sandbox shows in editor; scroll resets to display first sandbox
   screen-should-contain [
@@ -196,7 +200,9 @@ scenario deleting-top-sandbox-after-scroll [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes and scroll to second
   assume-console [
@@ -207,7 +213,7 @@ scenario deleting-top-sandbox-after-scroll [
     press F4
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .──────────────────────────────────────────────────.
@@ -225,7 +231,7 @@ scenario deleting-top-sandbox-after-scroll [
     left-click 2, 34
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # second sandbox shows in editor; scroll resets to display first sandbox
   screen-should-contain [
@@ -244,7 +250,9 @@ scenario deleting-final-sandbox-after-scroll [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes and scroll to second
   assume-console [
@@ -256,7 +264,7 @@ scenario deleting-final-sandbox-after-scroll [
     press page-down
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .──────────────────────────────────────────────────.
@@ -271,7 +279,7 @@ scenario deleting-final-sandbox-after-scroll [
     left-click 2, 34
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # implicitly scroll up to first sandbox
   screen-should-contain [
@@ -291,7 +299,9 @@ scenario deleting-updates-sandbox-count [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 10/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes
   assume-console [
@@ -301,7 +311,7 @@ scenario deleting-updates-sandbox-count [
     type [add 1, 1]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -321,7 +331,7 @@ scenario deleting-updates-sandbox-count [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # shouldn't go past last sandbox
   screen-should-contain [
diff --git a/sandbox/008-sandbox-edit.mu b/sandbox/008-sandbox-edit.mu
index 7565e391..ff45d87b 100644
--- a/sandbox/008-sandbox-edit.mu
+++ b/sandbox/008-sandbox-edit.mu
@@ -1,15 +1,18 @@
 ## editing sandboxes after they've been created
 
-scenario clicking-on-a-sandbox-moves-it-to-editor [
+scenario clicking-on-sandbox-edit-button-moves-it-to-editor [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 10/height
-  # run something
-  env:&:environment <- new-programming-environment screen, [add 2, 2]
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 2, 2]
+  # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -20,12 +23,64 @@ scenario clicking-on-a-sandbox-moves-it-to-editor [
     .──────────────────────────────────────────────────.
     .                                                  .
   ]
-  # click somewhere on the sandbox
+  # click at left edge of 'edit' button
   assume-console [
     left-click 3, 4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
+  ]
+  # it pops back into editor
+  screen-should-contain [
+    .                               run (F4)           .
+    .add 2, 2                                          .
+    .──────────────────────────────────────────────────.
+    .                                                  .
+  ]
+  # cursor should be in the right place
+  assume-console [
+    type [0]
+  ]
+  run [
+    event-loop screen, console, env, resources
+  ]
+  screen-should-contain [
+    .                               run (F4)           .
+    .0add 2, 2                                         .
+    .──────────────────────────────────────────────────.
+    .                                                  .
+  ]
+]
+
+scenario clicking-on-sandbox-edit-button-moves-it-to-editor-2 [
+  local-scope
+  trace-until 100/app  # trace too long
+  assume-screen 50/width, 10/height
+  # empty recipes
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [add 2, 2]
+  # run it
+  assume-console [
+    press F4
+  ]
+  event-loop screen, console, env, resources
+  screen-should-contain [
+    .                               run (F4)           .
+    .                                                  .
+    .──────────────────────────────────────────────────.
+    .0   edit           copy           delete          .
+    .add 2, 2                                          .
+    .4                                                 .
+    .──────────────────────────────────────────────────.
+    .                                                  .
+  ]
+  # click at right edge of 'edit' button (just before 'copy')
+  assume-console [
+    left-click 3, 18
+  ]
+  run [
+    event-loop screen, console, env, resources
   ]
   # it pops back into editor
   screen-should-contain [
@@ -39,7 +94,7 @@ scenario clicking-on-a-sandbox-moves-it-to-editor [
     type [0]
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                               run (F4)           .
@@ -104,13 +159,16 @@ scenario sandbox-with-print-can-be-edited [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
-  # run a print instruction
-  env:&:environment <- new-programming-environment screen, [print-integer screen, 4]
+  # empty recipes
+  assume-resources [
+  ]
+  # right editor contains a print instruction
+  env:&:environment <- new-programming-environment resources, screen, [print-integer screen, 4]
   # run the sandbox
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -131,7 +189,7 @@ scenario sandbox-with-print-can-be-edited [
     left-click 3, 18
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .                               run (F4)           .
@@ -147,7 +205,9 @@ scenario editing-sandbox-after-scrolling-resets-scroll [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
   # create 2 sandboxes and scroll to second
   assume-console [
@@ -159,7 +219,7 @@ scenario editing-sandbox-after-scrolling-resets-scroll [
     press page-down
     press page-down
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .──────────────────────────────────────────────────.
@@ -174,7 +234,7 @@ scenario editing-sandbox-after-scrolling-resets-scroll [
     left-click 2, 10
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # second sandbox shows in editor; scroll resets to display first sandbox
   screen-should-contain [
@@ -194,9 +254,11 @@ scenario editing-sandbox-updates-sandbox-count [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # initialize environment
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   render-all screen, env, render
-  # create 2 sandboxes and scroll to second
+  # create 2 sandboxes
   assume-console [
     press ctrl-n
     type [add 2, 2]
@@ -204,7 +266,7 @@ scenario editing-sandbox-updates-sandbox-count [
     type [add 1, 1]
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -221,7 +283,7 @@ scenario editing-sandbox-updates-sandbox-count [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # no change in contents
   screen-should-contain [
@@ -241,9 +303,9 @@ scenario editing-sandbox-updates-sandbox-count [
     press page-down
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
-  # screen should show just final sandbox
+  # screen should show just final sandbox with the right index (1)
   screen-should-contain [
     .                               run (F4)           .
     .──────────────────────────────────────────────────.
diff --git a/sandbox/009-sandbox-test.mu b/sandbox/009-sandbox-test.mu
index 1c36a769..2ee45a1e 100644
--- a/sandbox/009-sandbox-test.mu
+++ b/sandbox/009-sandbox-test.mu
@@ -5,16 +5,19 @@ scenario sandbox-click-on-result-toggles-color-to-green [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # basic recipe
-  recipes:text <- new [ 
-recipe foo [
-  reply 4
-]]
-  env:&:environment <- new-programming-environment screen, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  reply 4|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   # run it
   assume-console [
     press F4
   ]
-  event-loop screen, console, env, recipes
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -30,7 +33,7 @@ recipe foo [
     left-click 5, 21
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   # color toggles to green
   screen-should-contain-in-color 2/green, [
@@ -58,16 +61,19 @@ recipe foo [
     .                                                  .
   ]
   # now change the result
-  new-recipes:text <- new [ 
-recipe foo [
-  reply 3
-]]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  reply 3|
+      |]|
+    ]
+  ]
   # then rerun
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env, new-recipes
+    event-loop screen, console, env, resources
   ]
   # result turns red
   screen-should-contain-in-color 1/red, [
@@ -93,14 +99,14 @@ before <end-save-sandbox> [
     expected-response:text <- get *curr, expected-response:offset
     break-unless expected-response
     filename <- append filename, [.out]
-    save filename, expected-response
+    resources <- dump resources, filename, expected-response
   }
 ]
 
 before <end-restore-sandbox> [
   {
     filename <- append filename, [.out]
-    contents <- restore filename
+    contents <- slurp resources, filename
     break-unless contents
     *curr <- put *curr, expected-response:offset, contents
   }
@@ -125,7 +131,7 @@ after <global-touch> [
     break-unless sandbox
     # toggle its expected-response, and save session
     sandbox <- toggle-expected-response sandbox
-    save-sandboxes env
+    save-sandboxes env, resources
     hide-screen screen
     screen <- render-sandbox-side screen, env, render
     screen <- update-cursor screen, current-sandbox, env
diff --git a/sandbox/010-sandbox-trace.mu b/sandbox/010-sandbox-trace.mu
index 3368c491..72f6fe03 100644
--- a/sandbox/010-sandbox-trace.mu
+++ b/sandbox/010-sandbox-trace.mu
@@ -4,12 +4,14 @@ scenario sandbox-click-on-code-toggles-app-trace [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 10/height
-  env:&:environment <- new-programming-environment screen, [stash [abc]]
   # run a stash instruction
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [stash [abc]]
   assume-console [
     press F4
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -24,7 +26,7 @@ scenario sandbox-click-on-code-toggles-app-trace [
     left-click 4, 21
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     cursor:char <- copy 9251/␣
     print screen, cursor
   ]
@@ -50,7 +52,7 @@ scenario sandbox-click-on-code-toggles-app-trace [
     left-click 4, 25
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
     print screen, cursor
   ]
   # trace hidden again
@@ -70,13 +72,15 @@ scenario sandbox-shows-app-trace-and-result [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 10/height
   # run a stash instruction and some code
-  sandbox:text <- new [stash [abc]
+  assume-resources [
+  ]
+  test-sandbox:text <- new [stash [abc]
 add 2, 2]
+  env:&:environment <- new-programming-environment resources, screen, test-sandbox
   assume-console [
     press F4
   ]
-  env:&:environment <- new-programming-environment screen, sandbox
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -93,7 +97,7 @@ add 2, 2]
     left-click 4, 21
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # trace now printed above result
   screen-should-contain [
@@ -114,13 +118,15 @@ scenario clicking-on-app-trace-does-nothing [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 10/height
-  env:&:environment <- new-programming-environment screen, [stash 123456789]
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [stash 123456789]
   # create and expand the trace
   assume-console [
     press F4
     left-click 4, 1
   ]
-  event-loop screen, console, env
+  event-loop screen, console, env, resources
   screen-should-contain [
     .                               run (F4)           .
     .                                                  .
@@ -134,7 +140,7 @@ scenario clicking-on-app-trace-does-nothing [
     left-click 5, 7
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # no change; doesn't die
   screen-should-contain [
diff --git a/sandbox/011-errors.mu b/sandbox/011-errors.mu
index 7e620e55..7c04621d 100644
--- a/sandbox/011-errors.mu
+++ b/sandbox/011-errors.mu
@@ -4,21 +4,12 @@ container environment [
   recipe-errors:text
 ]
 
-# copy code from recipe editor, save to disk, load, save any errors
-# test-recipes is a hook for testing
-def! update-recipes env:&:environment, screen:&:screen, test-recipes:text -> errors-found?:bool, env:&:environment, screen:&:screen [
+# load code from disk, save any errors
+def! update-recipes env:&:environment, resources:&:resources, screen:&:screen -> errors-found?:bool, env:&:environment, screen:&:screen [
   local-scope
   load-ingredients
-  {
-    break-if test-recipes
-    in:text <- restore [recipes.mu]
-    recipe-errors:text <- reload in
-    *env <- put *env, recipe-errors:offset, recipe-errors
-  }
-  {
-    break-unless test-recipes
-    recipe-errors:text <- reload test-recipes
-  }
+  in:text <- slurp resources, [lesson/recipes.mu]
+  recipe-errors:text <- reload in
   *env <- put *env, recipe-errors:offset, recipe-errors
   # if recipe editor has errors, stop
   {
@@ -128,16 +119,26 @@ scenario run-shows-errors-in-get [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
-  recipes:text <- new [ 
-recipe foo [
-  get 123:num, foo:offset
-]]
-  env:&:environment <- new-programming-environment screen, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  get 123:num, foo:offset|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
+  render-all screen, env, render
+  screen-should-contain [
+    .                               run (F4)           .
+    .foo                                               .
+    .──────────────────────────────────────────────────.
+    .                                                  .
+  ]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                 run (F4)           .
@@ -159,7 +160,9 @@ scenario run-updates-status-with-first-erroneous-sandbox [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   assume-console [
     # create invalid sandbox 1
     type [get foo, x:offset]
@@ -169,7 +172,7 @@ scenario run-updates-status-with-first-erroneous-sandbox [
     press F4
   ]
   run [
-    event-loop screen, console, env, []
+    event-loop screen, console, env, resources
   ]
   # status line shows that error is in first sandbox
   screen-should-contain [
@@ -181,7 +184,9 @@ scenario run-updates-status-with-first-erroneous-sandbox-2 [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
-  env:&:environment <- new-programming-environment screen, []
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, []
   assume-console [
     # create invalid sandbox 2
     type [get foo, x:offset]
@@ -194,7 +199,7 @@ scenario run-updates-status-with-first-erroneous-sandbox-2 [
     press F4
   ]
   run [
-    event-loop screen, console, env, []
+    event-loop screen, console, env, resources
   ]
   # status line shows that error is in second sandbox
   screen-should-contain [
@@ -206,11 +211,13 @@ scenario run-hides-errors-from-past-sandboxes [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
-  env:&:environment <- new-programming-environment screen, [get foo, x:offset]   # invalid
+  assume-resources [
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [get foo, x:offset]  # invalid
   assume-console [
     press F4  # generate error
   ]
-  event-loop screen, console, env, []
+  event-loop screen, console, env, resources
   assume-console [
     left-click 3, 10
     press ctrl-k
@@ -218,7 +225,7 @@ scenario run-hides-errors-from-past-sandboxes [
     press F4  # update sandbox
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # error should disappear
   screen-should-contain [
@@ -238,17 +245,21 @@ scenario run-updates-errors-for-shape-shifting-recipes [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # define a shape-shifting recipe with an error
-  recipes:text <- new [recipe foo x:_elem -> z:_elem [
-local-scope
-load-ingredients
-y:&:num <- copy 0
-z <- add x, y
-]]
-  env:&:environment <- new-programming-environment screen, [foo 2]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo x:_elem -> z:_elem [|
+      |  local-scope|
+      |  load-ingredients|
+      |  y:&:num <- copy 0|
+      |  z <- add x, y|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo 2]
   assume-console [
     press F4
   ]
-  event-loop screen, console, env, recipes
+  event-loop screen, console, env, resources
   screen-should-contain [
     .  errors found (0)             run (F4)           .
     .                                                  .
@@ -265,7 +276,7 @@ z <- add x, y
     press F4
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   # error should remain unchanged
   screen-should-contain [
@@ -286,17 +297,21 @@ scenario run-avoids-spurious-errors-on-reloading-shape-shifting-recipes [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # overload a well-known shape-shifting recipe
-  recipes:text <- new [recipe length l:&:list:_elem -> n:num [
-]]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe length l:&:list:_elem -> n:num [|
+      |]|
+    ]
+  ]
   # call code that uses other variants of it, but not it itself
-  sandbox:text <- new [x:&:list:num <- copy 0
+  test-sandbox:text <- new [x:&:list:num <- copy 0
 to-text x]
-  env:&:environment <- new-programming-environment screen, sandbox
+  env:&:environment <- new-programming-environment resources, screen, test-sandbox
   # run it once
   assume-console [
     press F4
   ]
-  event-loop screen, console, env, recipes
+  event-loop screen, console, env, resources
   # no errors anywhere on screen (can't check anything else, since to-text will return an address)
   screen-should-contain-in-color 1/red, [
     .                                                  .
@@ -314,7 +329,7 @@ to-text x]
     press F4
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   # still no errors
   screen-should-contain-in-color 1/red, [
@@ -334,16 +349,19 @@ scenario run-shows-missing-type-errors [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
-  recipes:text <- new [ 
-recipe foo [
-  x <- copy 0
-]]
-  env:&:environment <- new-programming-environment screen, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  x <- copy 0|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                 run (F4)           .
@@ -360,16 +378,18 @@ scenario run-shows-unbalanced-bracket-errors [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # recipe is incomplete (unbalanced '[')
-  recipes:text <- new [ 
-recipe foo \\[
-  x <- copy 0
-]
-  env:&:environment <- new-programming-environment screen, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo \\\[|
+      |  x <- copy 0|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                 run (F4)           .
@@ -388,18 +408,21 @@ scenario run-shows-get-on-non-container-errors [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
-  recipes:text <- new [ 
-recipe foo [
-  local-scope
-  x:&:point <- new point:type
-  get x:&:point, 1:offset
-]]
-  env:&:environment <- new-programming-environment screen, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  local-scope|
+      |  x:&:point <- new point:type|
+      |  get x:&:point, 1:offset|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                 run (F4)           .
@@ -416,19 +439,22 @@ scenario run-shows-non-literal-get-argument-errors [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
-  recipes:text <- new [ 
-recipe foo [
-  local-scope
-  x:num <- copy 0
-  y:&:point <- new point:type
-  get *y:&:point, x:num
-]]
-  env:&:environment <- new-programming-environment screen, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  local-scope|
+      |  x:num <- copy 0|
+      |  y:&:point <- new point:type|
+      |  get *y:&:point, x:num|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                 run (F4)           .
@@ -446,16 +472,19 @@ scenario run-shows-errors-everytime [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # try to run a file with an error
-  recipes:text <- new [ 
-recipe foo [
-  local-scope
-  x:num <- copy y:num
-]]
-  env:&:environment <- new-programming-environment screen, [foo]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  local-scope|
+      |  x:num <- copy y:num|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo]
   assume-console [
     press F4
   ]
-  event-loop screen, console, env, recipes
+  event-loop screen, console, env, resources
   screen-should-contain [
     .  errors found                 run (F4)           .
     .                                                  .
@@ -470,7 +499,7 @@ recipe foo [
     press F4
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found                 run (F4)           .
@@ -487,12 +516,15 @@ scenario run-instruction-and-print-errors [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 15/height
-  env:&:environment <- new-programming-environment screen, [get 1:&:point, 1:offset]
+  assume-resources [
+  ]
+  # editor contains an illegal instruction
+  env:&:environment <- new-programming-environment resources, screen, [get 1:&:point, 1:offset]
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that screen prints error message in red
   screen-should-contain [
@@ -523,16 +555,17 @@ scenario run-instruction-and-print-errors-only-once [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 10/height
+  assume-resources [
+  ]
   # editor contains an illegal instruction
-  sandbox:text <- new [get 1234:num, foo:offset]
-  env:&:environment <- new-programming-environment screen, sandbox
+  env:&:environment <- new-programming-environment resources, screen, [get 1234:num, foo:offset]
   # run the code in the editors multiple times
   assume-console [
     press F4
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   # check that screen prints error message just once
   screen-should-contain [
@@ -553,17 +586,19 @@ scenario sandbox-can-handle-infinite-loop [
   local-scope
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
+  assume-resources [
+  ]
   # editor contains an infinite loop
-  sandbox:text <- new [{
+  test-sandbox:text <- new [{
 loop
 }]
-  env:&:environment <- new-programming-environment screen, sandbox
+  env:&:environment <- new-programming-environment resources, screen, test-sandbox
   # run the sandbox
   assume-console [
     press F4
   ]
   run [
-    event-loop screen, console, env
+    event-loop screen, console, env, resources
   ]
   screen-should-contain [
     .  errors found (0)             run (F4)           .
@@ -584,20 +619,24 @@ scenario sandbox-with-errors-shows-trace [
   trace-until 100/app  # trace too long
   assume-screen 50/width, 20/height
   # generate a stash and a error
-  recipes:text <- new [recipe foo [
-local-scope
-a:num <- next-ingredient
-b:num <- next-ingredient
-stash [dividing by], b
-_, c:num <- divide-with-remainder a, b
-reply b
-]]
-  env:&:environment <- new-programming-environment screen, [foo 4, 0]
+  assume-resources [
+    [lesson/recipes.mu] <- [
+      |recipe foo [|
+      |  local-scope|
+      |  a:num <- next-ingredient|
+      |  b:num <- next-ingredient|
+      |  stash [dividing by], b|
+      |  _, c:num <- divide-with-remainder a, b|
+      |  reply b|
+      |]|
+    ]
+  ]
+  env:&:environment <- new-programming-environment resources, screen, [foo 4, 0]
   # run
   assume-console [
     press F4
   ]
-  event-loop screen, console, env, recipes
+  event-loop screen, console, env, resources
   # screen prints error message
   screen-should-contain [
     .  errors found (0)             run (F4)           .
@@ -615,7 +654,7 @@ reply b
     left-click 4, 15
   ]
   run [
-    event-loop screen, console, env, recipes
+    event-loop screen, console, env, resources
   ]
   # screen should expand trace
   screen-should-contain [