Rows are bounded by limit/maxLimit (internal/server/review.go:39) and text by observationTextCap (:45), with :36-38 reasoning explicitly about the SDK's double serialization. Nothing bounds the class tally: store.List groups over the entire filtered match (internal/store/store.go:465-466), so every distinct class in the match lands in the result even at limit=1.
Classes are caller-defined free-form strings the recording invariant forbids rejecting, so cardinality is not something the server controls. A client coining a novel class per call grows class_counts to the table's class cardinality on every review.
TestReviewClassCountsSpanTheWholeMatch pins the spanning behavior at cardinality 2 — the property that makes it useful is the same one that makes it unbounded, so this is a contract judgment call rather than a plain bug fix.
Direction: cap the tally and flag it, consistent with how rows and text already behave, or document the cardinality assumption in the tool description.
Rows are bounded by
limit/maxLimit(internal/server/review.go:39) and text byobservationTextCap(:45), with:36-38reasoning explicitly about the SDK's double serialization. Nothing bounds the class tally:store.Listgroups over the entire filtered match (internal/store/store.go:465-466), so every distinct class in the match lands in the result even atlimit=1.Classes are caller-defined free-form strings the recording invariant forbids rejecting, so cardinality is not something the server controls. A client coining a novel class per call grows
class_countsto the table's class cardinality on every review.TestReviewClassCountsSpanTheWholeMatchpins the spanning behavior at cardinality 2 — the property that makes it useful is the same one that makes it unbounded, so this is a contract judgment call rather than a plain bug fix.Direction: cap the tally and flag it, consistent with how rows and text already behave, or document the cardinality assumption in the tool description.