Hold an open call out of its run without taking one out of a group

Being open did two things to grouping, and only one of them was wanted. It held
a call standing on its own out of the run it belongs to, so a command finishing
behind the card being read no longer shuts it and folds it away mid-sentence.
It also took a call *out* of the group it was already inside, and that is what
made collapsing jump: grouping is what gives a row its identity, so one tap
rebuilt the rows around the finger -- opening a call inside a group split the
group into two pieces with mismatched keys, and closing one replaced three rows
with one, which no anchor survives. Measured at 450px of jump, with the card
that was closed going with it.

So the held-out set is now the screen's, not the transcript's: a call that has
never been drawn inside a group and is open stands out of its run, and a call
that has been in one stays in it whatever the reader does to it. Being inside a
group once is a fact about what the reader has been shown, which is why the
screen is what remembers it.

Checked with ktfmtFormat, compileDebugKotlin, lintDebug and testDebugUnitTest,
and on the emulator against the sandbox: opening a call inside an open group of
six leaves it one group of six and closing it returns every row to the pixel it
came from; a call opened while standing alone survives a reply landing behind
it, and folds back into "Called 3 tools" when it is closed without moving the
rows below it.
This commit is contained in:
iris-ai committed 2026-09-16 01:57:50 -04:00
1 parent 463acb28fa
commit b86a5dc37a
4 files changed
+73 -36

No files matched your search

@@ -5,9 +5,10 @@ import kotlin.test.assertEquals
import kotlin.test.assertTrue
/**
* How a run of tool calls is cut into rows: a call still running, open, or last in the transcript
* is drawn on its own, and every piece the cut leaves behind still has a key of its own -- two rows
* sharing one key take the app down, and a key that moves takes the reader's place with it.
* How a run of tool calls is cut into rows: the call still running, the last call in the
* transcript, and one held out because the reader has it open are drawn on their own, and every
* piece the cut leaves behind still has a key of its own -- two rows sharing one key take the app
* down, and a key that moves takes the reader's place with it.
*/
class ToolRowsTest {
private var seq = 0L
@@ -74,8 +75,12 @@ class ToolRowsTest {
assertKeysDistinct(rows)
}
/**
* A call held out is one the reader opened while it stood on its own; being overtaken while
* they read it does not fold it away, and closing it hands it back to its run.
*/
@Test
fun an_open_call_stays_out_of_its_group_until_it_is_closed() {
fun a_held_out_call_stays_out_of_its_group() {
val calls =
listOf(
call("a"),
@@ -85,15 +90,12 @@ class ToolRowsTest {
reply(),
)
val whileOpen = groupToolRuns(calls, expandedTools = setOf("b"))
val whileHeld = groupToolRuns(calls, heldOut = setOf("d"))
val afterItCloses = groupToolRuns(calls)
assertEquals(
listOf(listOf("a"), listOf("b"), listOf("c", "d"), listOf("reply")),
shape(whileOpen),
)
assertTrue(whileOpen[1] is TranscriptRow.Single, "$whileOpen")
assertKeysDistinct(whileOpen)
assertEquals(listOf(listOf("a", "b", "c"), listOf("d"), listOf("reply")), shape(whileHeld))
assertTrue(whileHeld[1] is TranscriptRow.Single, "$whileHeld")
assertKeysDistinct(whileHeld)
assertEquals(listOf(listOf("a", "b", "c", "d"), listOf("reply")), shape(afterItCloses))
assertTrue(afterItCloses.first() is TranscriptRow.Tools, "$afterItCloses")
}