test(chapters): twelve proposals each keep their own summons
Phase 6 of docs/chapter-uploads.md, which asked for three things to be checked rather than assumed. All three already hold, so this is their tests and the record of it. The proposal list already groups: a "Waiting for you" header over the sessions isAwaitingApproval picks out, "Everything else" under them, over a query already ordered newest first. The chapter summary is already `name - N words - N chunks` rather than "Chapter", so twelve rows are twelve distinguishable rows. And isAbout already compares frostSigningSessionId when both rows carry one, falling back to the clock only when either does not. The last of those is the one with a history -- two concurrent sessions broke it once, which is why the column exists -- so it is now tested at twelve, with the answers given in the opposite order to the asking, so that neither the clock nor the list order could produce the right answer by accident. Plus one proposal failing and settling only its own row, which is the transcript's version of why an upload is N sessions and not one. Noted for Phase 8 rather than fixed here: twenty open chapter proposals is roughly 900 KB of unsignedEventJson parsed and summarised on every emission of that query, and summarize then re-splits each chapter's text to count its chunks. Phase 8 replaces that derived count with the session's own item count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -182,4 +182,42 @@ class TranscriptRequestStateTest {
|
||||
|
||||
assertEquals(setOf(hostKey.id), ChatMessage.answeredRequests(transcript))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `twelve open proposals each keep their own summons`() {
|
||||
// An upload of twelve files opens twelve sessions at once, interleaved in
|
||||
// one stretch of transcript. The clock fallback would have the first
|
||||
// answer drop every later request, which is the failure two concurrent
|
||||
// sessions already had once -- and twelve is where a path that still
|
||||
// falls back to the clock would show.
|
||||
val requests = (1..12).map { n ->
|
||||
line(ChatMessage.TYPE_FROST_APPROVAL_NEEDED, at = 100L + n, session = "session-" + n)
|
||||
}
|
||||
|
||||
// Answered in the opposite order to the asking, so neither the clock nor
|
||||
// the list order could produce the right answer by accident.
|
||||
val answers = listOf(12, 7, 1).map { n ->
|
||||
line(ChatMessage.TYPE_FROST_NONCE, at = 200L + n, session = "session-" + n)
|
||||
}
|
||||
|
||||
val answered = ChatMessage.answeredRequests(requests + answers)
|
||||
|
||||
assertEquals(
|
||||
setOf(requests[11].id, requests[6].id, requests[0].id),
|
||||
answered,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `one proposal failing settles only itself`() {
|
||||
val requests = (1..12).map { n ->
|
||||
line(ChatMessage.TYPE_FROST_APPROVAL_NEEDED, at = 100L + n, session = "session-" + n)
|
||||
}
|
||||
val failure = line(ChatMessage.TYPE_FROST_FAILED, at = 300, session = "session-5")
|
||||
|
||||
// The other eleven chapters are still in front of the group. A batch is
|
||||
// all-or-nothing within a session and nothing across them, which is the
|
||||
// whole reason an upload is N sessions rather than one.
|
||||
assertEquals(setOf(requests[4].id), ChatMessage.settledRequests(requests + listOf(failure)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,23 +615,39 @@ The transcript and the proposal list were built for two concurrent sessions —
|
||||
scaffolding were open together. Twelve is the same problem at a scale that will
|
||||
find whatever that fix missed.
|
||||
|
||||
What to check rather than assume:
|
||||
What to check rather than assume — and **all three already held**, so this
|
||||
phase is its tests and this paragraph:
|
||||
|
||||
- `ProposalListScreen` with twenty open sessions: the ones waiting on the reader
|
||||
gather at the top, and `ProposedEvent.summarize` gives a chapter a name rather
|
||||
than "Event of kind 30302". Chapters are summarised today; confirm the
|
||||
summary is the chapter's *name*, since twelve rows reading "Chapter" are
|
||||
twelve rows a member cannot choose between.
|
||||
- Twelve "Review" lines in the transcript, each resolving its own session.
|
||||
`answeredRequests`/`settledRequests` read `frostSigningSessionId` when both
|
||||
rows have one; twelve is where a fallback-to-clock path would show.
|
||||
- Twelve approval gates. No batching of the gate — that is the one thing the
|
||||
batch-signing note refuses, and for the same reason: "a member who cannot
|
||||
scroll the whole list should not be able to approve it". What can be done is
|
||||
make the *list* cheap to work through, which is the point above.
|
||||
- **The list already groups.** `ProposalListScreen` renders a "Waiting for you"
|
||||
header over the sessions `FrostSigningManager.isAwaitingApproval` picks out and
|
||||
an "Everything else" header under them, over a query that is already
|
||||
`ORDER BY createdAt DESC`. Twelve proposals arrive sorted and the three that
|
||||
need the reader are at the top.
|
||||
- **The summary already names the chapter.** `ProposedEvent.summarize`'s chapter
|
||||
branch is `name · N words · N chunks`, not "Chapter" — so twelve rows are
|
||||
twelve distinguishable rows.
|
||||
- **`isAbout` already matches on the session.** `answeredRequests` and
|
||||
`settledRequests` compare `frostSigningSessionId` when both rows carry one and
|
||||
fall back to the clock only when either does not. Now tested at twelve, with
|
||||
the answers given in the opposite order to the asking so that neither the
|
||||
clock nor the list order could produce the right answer by accident, plus one
|
||||
failure settling only its own row.
|
||||
- **Twelve approval gates, unbatched.** Unchanged by design: the gate shows every
|
||||
event rather than a count, because "a member who cannot scroll the whole list
|
||||
should not be able to approve it".
|
||||
|
||||
No new `ChatMessage` type, no new set to add to.
|
||||
|
||||
### One thing N does make worse, and it is Phase 8's to fix
|
||||
|
||||
`observeSessionsForChatRoom` carries every session's items, and a chapter item's
|
||||
`unsignedEventJson` holds the whole markdown. Twenty open chapter proposals is
|
||||
roughly 900 KB of JSON parsed and summarised **on every emission** — and
|
||||
`summarize` then re-splits each chapter's text to count its chunks, which is the
|
||||
derived count Phase 8 replaces with the session's own item count. Fixing it
|
||||
there removes the re-split; the parse is inherent to the query and is the price
|
||||
of the list showing what each proposal signs without one query per row.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 — the splitter, which is a decision about translation
|
||||
|
||||
Reference in New Issue
Block a user