Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5366921

Browse files
authored
fix(quest): stable secondary sort for latest-brief reads (#23) (#75)
Signed-off-by: SAY-5 <[email protected]>
1 parent 454c311 commit 5366921

2 files changed

Lines changed: 91 additions & 2 deletions

File tree

internal/quest/brief.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func LastBriefAt(ctx context.Context, db *sql.DB, projectID string) (time.Time,
6868
qerr := db.QueryRowContext(ctx,
6969
`SELECT created_at FROM task_notes
7070
WHERE project_id = ? AND task_id = ? AND note LIKE '[brief]%'
71-
ORDER BY created_at DESC LIMIT 1`,
71+
ORDER BY created_at DESC, id DESC LIMIT 1`,
7272
projectID, briefTaskID,
7373
).Scan(&createdAt)
7474
if qerr != nil {
@@ -95,7 +95,7 @@ func LastBrief(ctx context.Context, db *sql.DB, projectID string) (agentID, text
9595
qerr := db.QueryRowContext(ctx,
9696
`SELECT agent_id, note, created_at FROM task_notes
9797
WHERE project_id = ? AND task_id = ? AND note LIKE '[brief]%'
98-
ORDER BY created_at DESC LIMIT 1`,
98+
ORDER BY created_at DESC, id DESC LIMIT 1`,
9999
projectID, briefTaskID,
100100
).Scan(&agent, &note, &createdAt)
101101
if qerr != nil {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package quest
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
"time"
8+
)
9+
10+
// Regression for #23: "latest brief" reads were non-deterministic when
11+
// two task_notes rows shared the same created_at string. The
12+
// LastBrief / LastBriefAt queries used `ORDER BY created_at DESC LIMIT
13+
// 1` only, so SQLite was free to return either tied row. Adding `id
14+
// DESC` as a secondary sort key makes the highest-id (latest-inserted)
15+
// row the unambiguous winner across runs.
16+
//
17+
// The test seeds five briefs with byte-identical RFC3339Nano
18+
// timestamps, then reads the latest brief sameSecondBriefIterations
19+
// times and asserts every read returns the same row (the one with the
20+
// highest auto-increment id) — i.e. the most recently inserted brief.
21+
22+
const sameSecondBriefIterations = 10
23+
24+
func TestLastBrief_StableOrderForSameTimestampInserts(t *testing.T) {
25+
db, pid := newTestDB(t)
26+
ctx := context.Background()
27+
28+
ts := time.Date(2026, 5, 5, 12, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)
29+
const n = 5
30+
for i := 0; i < n; i++ {
31+
text := fmt.Sprintf("brief-%02d", i)
32+
if _, err := db.ExecContext(ctx,
33+
`INSERT INTO task_notes (project_id, task_id, agent_id, note, created_at)
34+
VALUES (?, ?, ?, ?, ?)`,
35+
pid, briefTaskID, "agent", briefPrefix+text, ts,
36+
); err != nil {
37+
t.Fatalf("seed brief %d: %v", i, err)
38+
}
39+
}
40+
41+
// Highest auto-increment id == most recently inserted brief.
42+
const wantText = "brief-04"
43+
44+
for i := 0; i < sameSecondBriefIterations; i++ {
45+
_, got, _, err := LastBrief(ctx, db, pid)
46+
if err != nil {
47+
t.Fatalf("LastBrief iter %d: %v", i, err)
48+
}
49+
if got != wantText {
50+
t.Fatalf("LastBrief iter %d: got %q, want %q", i, got, wantText)
51+
}
52+
}
53+
}
54+
55+
func TestLastBriefAt_StableOrderForSameTimestampInserts(t *testing.T) {
56+
db, pid := newTestDB(t)
57+
ctx := context.Background()
58+
59+
ts := time.Date(2026, 5, 5, 12, 0, 0, 0, time.UTC).Format(time.RFC3339Nano)
60+
const n = 5
61+
for i := 0; i < n; i++ {
62+
text := fmt.Sprintf("brief-%02d", i)
63+
if _, err := db.ExecContext(ctx,
64+
`INSERT INTO task_notes (project_id, task_id, agent_id, note, created_at)
65+
VALUES (?, ?, ?, ?, ?)`,
66+
pid, briefTaskID, "agent", briefPrefix+text, ts,
67+
); err != nil {
68+
t.Fatalf("seed brief %d: %v", i, err)
69+
}
70+
}
71+
72+
first, err := LastBriefAt(ctx, db, pid)
73+
if err != nil {
74+
t.Fatalf("LastBriefAt: %v", err)
75+
}
76+
if first.IsZero() {
77+
t.Fatal("LastBriefAt returned zero time after seeding")
78+
}
79+
80+
for i := 1; i < sameSecondBriefIterations; i++ {
81+
next, err := LastBriefAt(ctx, db, pid)
82+
if err != nil {
83+
t.Fatalf("LastBriefAt iter %d: %v", i, err)
84+
}
85+
if !next.Equal(first) {
86+
t.Fatalf("LastBriefAt iter %d: got %v, want %v", i, next, first)
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)