A composed operation calls several declared operations and hands back one Result. Every
step's rows land in one flat list, in step order, and nothing in that list says which
step a row came from.
// internal/store/batch.go:164
into.Rows = append(into.Rows, sub.Rows...)
into.Count = len(into.Rows)
So a caller that composes "the page" and "the total" gets a list and has to tell the two apart by what is in the documents. Two legs over the same collection are indistinguishable.
Result (internal/store/ops.go:326-341) has one
Rows []map[string]any with tag json:"rows,omitempty" and one
Count. There is no per-step field, on the struct or on the wire.runCalled's own doc, internal/store/batch.go:145-150, states it:
"The rows of every step land in one flat list, in step order… Nothing in that list says
which step a row came from. That is a real limit and it is written here rather than discovered: a
composed read is for answers whose shapes are told apart by what is in them — a page and its
total — and not for ones that need a label to be read at all."batch.go:226), so this is not new behaviour composition
introduced — composition is what made it matter.Truncated is unioned across legs (batch.go:167-171), so a caller cannot
tell which leg was truncated either.Does it lose data, break a promise on a public surface, or stop somebody installing and running? No. Nothing is lost — every row is there, in a defined order. Nothing is promised that is not delivered: the limit is documented at the function that causes it and in the CHANGELOG entry for composed operations, so a caller is told before they hit it. Nothing is blocked: the documented use — legs whose shapes differ — works.
Defer, and this is the deferral in this list that deserves the most discomfort,
because the reason to fix it in 1.0.0 is not the triage question but the price of waiting. Labelling
the answer means changing what Result looks like on the wire, and that is a public
surface that freezes at the tag.
It is deferred anyway because the shape of the fix is not decided, and shipping a half-decided wire change is worse than shipping a documented limit. A per-step key, a nested result per leg, and a synthetic column are three different products, and choosing under release pressure is how a compatibility surface acquires a field somebody regrets.
Truncated. A single boolean over K legs is the same
problem one field over.