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:
1 parent
428026cdfd
commit
f8a0ee1cdb
2 files changed
+67
-13
No files matched your search
+38
@@ -182,4 +182,42 @@ class TranscriptRequestStateTest {
|
|||||||
|
|
||||||
assertEquals(setOf(hostKey.id), ChatMessage.answeredRequests(transcript))
|
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)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+29
-13
@@ -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
|
scaffolding were open together. Twelve is the same problem at a scale that will
|
||||||
find whatever that fix missed.
|
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
|
- **The list already groups.** `ProposalListScreen` renders a "Waiting for you"
|
||||||
gather at the top, and `ProposedEvent.summarize` gives a chapter a name rather
|
header over the sessions `FrostSigningManager.isAwaitingApproval` picks out and
|
||||||
than "Event of kind 30302". Chapters are summarised today; confirm the
|
an "Everything else" header under them, over a query that is already
|
||||||
summary is the chapter's *name*, since twelve rows reading "Chapter" are
|
`ORDER BY createdAt DESC`. Twelve proposals arrive sorted and the three that
|
||||||
twelve rows a member cannot choose between.
|
need the reader are at the top.
|
||||||
- Twelve "Review" lines in the transcript, each resolving its own session.
|
- **The summary already names the chapter.** `ProposedEvent.summarize`'s chapter
|
||||||
`answeredRequests`/`settledRequests` read `frostSigningSessionId` when both
|
branch is `name · N words · N chunks`, not "Chapter" — so twelve rows are
|
||||||
rows have one; twelve is where a fallback-to-clock path would show.
|
twelve distinguishable rows.
|
||||||
- Twelve approval gates. No batching of the gate — that is the one thing the
|
- **`isAbout` already matches on the session.** `answeredRequests` and
|
||||||
batch-signing note refuses, and for the same reason: "a member who cannot
|
`settledRequests` compare `frostSigningSessionId` when both rows carry one and
|
||||||
scroll the whole list should not be able to approve it". What can be done is
|
fall back to the clock only when either does not. Now tested at twelve, with
|
||||||
make the *list* cheap to work through, which is the point above.
|
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.
|
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
|
## Phase 7 — the splitter, which is a decision about translation
|
||||||
|
|||||||
Reference in new issue
Block a user