Stop regenerating the world on every check
A card vanishing and coming back seconds later, with different words, was never about latency: every pass deleted its whole family and re-inserted it, so each round minted new row ids. The rail keys on suggestion.id, so a full remount was guaranteed — new id, new created_at (hence the re-fired chime), and a fresh explanation from a model that re-reasons every time it is asked. One unchanged mistake carried three different explanations in a single sitting. Passes now reconcile instead of replace. A re-proposed edit keeps its row: its id, its created_at, and the wording she has already read. And the grammar checkpoint stops asking about sentences nobody touched — the document is split into hashed sentences, checked_chunks records which ones a family has read, and only the difference is sent. When nothing changed it doesn't call the model at all, and doesn't spend its rate-limit slot on having done nothing. The tone is part of a sentence's identity: cached advice was written for the old register, so switching doc type re-reads every line. replaceMechanics reconciles too, which mattered more than expected — the rule pack fires 250 ms after a keystroke, so it was re-minting every local card's id several times a sentence. Only the grammar checkpoint is chunked. Voice is a property of the whole document, and the collocation coach is a button she pressed asking for a fresh read. No client change was needed; stable ids were the whole of it. Claude-Session: https://claude.ai/code/session_016y6gyuHkQXPiEuW8RGQyua
This commit is contained in:
@@ -20,10 +20,17 @@ import (
|
||||
type stubClient struct {
|
||||
response string
|
||||
calls int
|
||||
// The full prompt of the most recent call, so a test can assert which
|
||||
// sentences a chunked pass actually asked about.
|
||||
lastPrompt string
|
||||
}
|
||||
|
||||
func (s *stubClient) Complete(_ context.Context, _ llm.CompletionRequest) (string, error) {
|
||||
func (s *stubClient) Complete(_ context.Context, req llm.CompletionRequest) (string, error) {
|
||||
s.calls++
|
||||
s.lastPrompt = ""
|
||||
for _, m := range req.Messages {
|
||||
s.lastPrompt += m.Content + "\n"
|
||||
}
|
||||
return s.response, nil
|
||||
}
|
||||
|
||||
@@ -64,6 +71,17 @@ func newTestServer(t *testing.T, client llm.LLMClient) (http.Handler, string, *H
|
||||
return authed, docID, h
|
||||
}
|
||||
|
||||
// setDocText rewrites the seeded document, standing in for the writer editing.
|
||||
// The grammar checkpoint only asks the model about sentences that changed since
|
||||
// it last read the document, so a test that wants a second real pass has to
|
||||
// change something first — as she always has.
|
||||
func setDocText(t *testing.T, h *Handler, docID, text string) {
|
||||
t.Helper()
|
||||
if _, err := h.DB.Exec(`UPDATE documents SET content_text = ? WHERE id = ?`, text, docID); err != nil {
|
||||
t.Fatalf("update doc text: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func do(t *testing.T, srv http.Handler, method, path, body string) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
var r *http.Request
|
||||
@@ -183,6 +201,7 @@ func TestFickleEditsSuppressed(t *testing.T) {
|
||||
]}`}
|
||||
srv, docID, h := newTestServer(t, client)
|
||||
h.Limit = llm.NewRateLimiter(0)
|
||||
setDocText(t, h, docID, `He left "early," because of the rain. The cat always have a calm face.`)
|
||||
|
||||
rec := do(t, srv, http.MethodPost, "/docs/"+docID+"/check", "")
|
||||
var got []db.Suggestion
|
||||
@@ -194,6 +213,10 @@ func TestFickleEditsSuppressed(t *testing.T) {
|
||||
do(t, srv, http.MethodPost, "/suggestions/"+s.ID+"/accept", "")
|
||||
}
|
||||
|
||||
// Both edits are now in the document, which is what re-opens those sentences
|
||||
// for a second reading.
|
||||
setDocText(t, h, docID, `He left "early," due to the rain. The cat always has a calm face.`)
|
||||
|
||||
// Reversal of the first accept (note the " → ' quote churn) and a re-polish of
|
||||
// the second accept must both be dropped; only the unrelated edit survives.
|
||||
client.response = `{"suggestions":[
|
||||
@@ -314,7 +337,9 @@ func TestCollocationPassCoexists(t *testing.T) {
|
||||
t.Fatalf("collocation response should carry all three families, got %+v", got)
|
||||
}
|
||||
|
||||
// A grammar checkpoint must NOT wipe the voice or collocation flags.
|
||||
// A grammar checkpoint must NOT wipe the voice or collocation flags. She fixes
|
||||
// the flagged sentence, so its own grammar row goes and nothing replaces it.
|
||||
setDocText(t, h, docID, "I have two apples.")
|
||||
client.response = `{"suggestions":[]}`
|
||||
rec = do(t, srv, http.MethodPost, "/docs/"+docID+"/check", "")
|
||||
if rec.Code != http.StatusOK {
|
||||
|
||||
Reference in New Issue
Block a user