Index: tests/collections/.expect/string-ctx-manage.txt
===================================================================
--- tests/collections/.expect/string-ctx-manage.txt	(revision 804bf6779b6e4f0147f60446d70db7ced6f81ec8)
+++ tests/collections/.expect/string-ctx-manage.txt	(revision 7b0e8b700f1cc88f52caa78276608cd8378524a0)
@@ -4,3 +4,4 @@
 bye
 hi
+bye
 done
Index: tests/collections/.expect/string-gc.txt
===================================================================
--- tests/collections/.expect/string-gc.txt	(revision 804bf6779b6e4f0147f60446d70db7ced6f81ec8)
+++ tests/collections/.expect/string-gc.txt	(revision 7b0e8b700f1cc88f52caa78276608cd8378524a0)
@@ -38,2 +38,13 @@
 x from 5 to 15
 y from 5 to 15
+======================== fillNoCompact
+about to expand, a = aaa
+expanded, a = aaa
+about to expand, a = aaa
+expanded, a = aaa
+about to expand, a = aaa
+expanded, a = aaa
+about to expand, a = aaa
+expanded, a = aaa
+about to expand, a = aaa
+expanded, a = aaa
Index: tests/collections/string-ctx-manage.cfa
===================================================================
--- tests/collections/string-ctx-manage.cfa	(revision 804bf6779b6e4f0147f60446d70db7ced6f81ec8)
+++ tests/collections/string-ctx-manage.cfa	(revision 7b0e8b700f1cc88f52caa78276608cd8378524a0)
@@ -36,13 +36,12 @@
     string y = x; // y allocates into private pad, implying eager copy
     assert( y.inner->Handle.s != x.inner->Handle.s);
+    assert( DEBUG_string_bytes_in_heap(y.inner->Handle.ulink) == y.inner->Handle.lnth );  // y is in a perfectly fitting heap
     sout | y; // hi
 
-    // -- following hits "need to implement actual growth"
-    //    and it passes if I modify string_res.cfa to oversize the owned heaps
-
-    // x = "bye";
-    // y = x; // into private y => eager copy
-    // assert( y.inner->Handle.s != x.inner->Handle.s);
-    // sout | y; // bye
+    x = "bye";
+    y = x; // into private y => eager copy
+    assert( y.inner->Handle.s != x.inner->Handle.s);
+//    assert( DEBUG_string_bytes_in_heap(y.inner->Handle.ulink) == y.inner->Handle.lnth );  // optimization required
+    sout | y; // bye
 }
 
Index: tests/collections/string-gc.cfa
===================================================================
--- tests/collections/string-gc.cfa	(revision 804bf6779b6e4f0147f60446d70db7ced6f81ec8)
+++ tests/collections/string-gc.cfa	(revision 7b0e8b700f1cc88f52caa78276608cd8378524a0)
@@ -120,6 +120,34 @@
 }
 
+void fillNoCompact() {
+    // show that allocating in a heap filled with mostly live strings (no collectable garbage) causes heap growth
+
+    sout | "======================== fillNoCompact";
+
+    size_t lastTimeBytesAvail = bytesRemaining();
+    assert( lastTimeBytesAvail >= 200 ); // starting this test with nontrivial room
+
+    // mostly fill the pad
+    string_res a = "aaa";  // will have to be moved
+    string_res z = "zzz";
+    for (i; 5) {
+        while ( bytesRemaining() > 10 ) {
+            z += ".";
+        }
+        sout | "about to expand, a = " | a;
+        while ( bytesRemaining() <= 10 ) {
+            z += ".";
+        }
+        sout | "expanded, a = " | a;
+
+        // each growth gives more usable space than the last
+        assert( bytesRemaining() > lastTimeBytesAvail );
+        lastTimeBytesAvail = bytesRemaining();
+    }
+}
+
 int main() {
     basicFillCompact();
     fillCompact_withSharedEdits();
+    fillNoCompact();
 }
