Skip to content

Commit 3022472

Browse files
Necmttnclaude
andauthored
fix(sessions): SurrealDB 3.1.2 ORDER BY idiom parse error (#540) (#543)
`SELECT VALUE text_excerpt ... ORDER BY seq` is rejected by SurrealDB >=3.1.2 with "Parse error: Missing order idiom `seq` in statement selection" because the order idiom is absent from the projection. This broke `ax sessions here` and `ax sessions around` for any user on a current SurrealDB. Select `seq` into the projection and pluck the field instead of using SELECT VALUE. Same fix applied to the two dojo skill-spar first-user-turn lookups. Guard test asserts the enrichment SQL no longer emits the rejected shape. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0c3f7ef commit 3022472

4 files changed

Lines changed: 23 additions & 17 deletions

File tree

apps/axctl/src/dashboard/sessions-query.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ describe("listSessionsHere", () => {
111111
expect(enrich).toContain("AND role = 'user'");
112112
expect(enrich).toContain("LIMIT 1");
113113
expect(enrich).not.toContain("session IN");
114+
// Regression #540: SurrealDB >=3.1.2 rejects `SELECT VALUE x ... ORDER BY seq`
115+
// ("Missing order idiom `seq` in statement selection") because the order
116+
// idiom is absent from the projection. The first-user-message subquery must
117+
// therefore select `seq` and pluck the field rather than use SELECT VALUE.
118+
expect(enrich).not.toMatch(/SELECT\s+VALUE\b[^)]*ORDER BY seq/);
119+
expect(enrich).toMatch(/SELECT text_excerpt, seq FROM turn[^)]*ORDER BY seq/);
114120
});
115121

116122
test("orders by started_at DESC", async () => {

apps/axctl/src/dashboard/sessions-query.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const enrichSessions = (
103103
>(
104104
`SELECT
105105
(SELECT count() FROM turn WHERE session = ${lit} GROUP ALL)[0].count AS turn_count,
106-
(SELECT VALUE text_excerpt FROM turn WHERE session = ${lit} AND role = 'user' ORDER BY seq ASC LIMIT 1)[0] AS first_user_message
106+
(SELECT text_excerpt, seq FROM turn WHERE session = ${lit} AND role = 'user' ORDER BY seq ASC LIMIT 1)[0].text_excerpt AS first_user_message
107107
FROM ONLY ${lit};`,
108108
);
109109
const enriched = result?.[0] ?? null;

apps/axctl/src/dojo/skill-spar.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ describe("resolveSkillSparTask", () => {
485485
[{ id: "session:s1", source: "claude" }],
486486
],
487487
// turn text_excerpt for the picked session
488-
"text_excerpt FROM turn WHERE session": [["Fix the bug"]],
488+
"text_excerpt, seq FROM turn WHERE session": [[{ text_excerpt: "Fix the bug" }]],
489489
},
490490
});
491491

@@ -517,7 +517,7 @@ describe("resolveSkillSparTask", () => {
517517
"source FROM": [
518518
[{ id: "session:s2", source: "claude" }],
519519
],
520-
"text_excerpt FROM turn WHERE session": [["Do the loaded task"]],
520+
"text_excerpt, seq FROM turn WHERE session": [[{ text_excerpt: "Do the loaded task" }]],
521521
},
522522
});
523523

@@ -547,7 +547,7 @@ describe("resolveSkillSparTask", () => {
547547
{ id: "session:s2", source: "claude" },
548548
],
549549
],
550-
"text_excerpt FROM turn WHERE session": [["Newer task"]],
550+
"text_excerpt, seq FROM turn WHERE session": [[{ text_excerpt: "Newer task" }]],
551551
},
552552
});
553553

@@ -618,7 +618,7 @@ describe("resolveSkillSparTask", () => {
618618
[{ id: "session:s42" }],
619619
],
620620
// Turn text_excerpt for the explicit session
621-
"text_excerpt FROM turn WHERE session": [["Custom task"]],
621+
"text_excerpt, seq FROM turn WHERE session": [[{ text_excerpt: "Custom task" }]],
622622
},
623623
});
624624

@@ -647,7 +647,7 @@ describe("resolveSkillSparTask", () => {
647647
"source FROM": [
648648
[{ id: "session:s1", source: "claude" }],
649649
],
650-
"text_excerpt FROM turn WHERE session": [["Task text"]],
650+
"text_excerpt, seq FROM turn WHERE session": [[{ text_excerpt: "Task text" }]],
651651
},
652652
});
653653

@@ -685,7 +685,7 @@ describe("resolveSkillSparTask", () => {
685685
"source FROM": [
686686
[{ id: "session:s1", source: "claude" }],
687687
],
688-
"text_excerpt FROM turn WHERE session": [["HEAD task"]],
688+
"text_excerpt, seq FROM turn WHERE session": [[{ text_excerpt: "HEAD task" }]],
689689
},
690690
});
691691

@@ -751,7 +751,7 @@ describe("resolveSkillSparTask", () => {
751751
{ id: "session:s2", source: "claude" },
752752
],
753753
],
754-
"text_excerpt FROM turn WHERE session": [["Winner via loaded ts"]],
754+
"text_excerpt, seq FROM turn WHERE session": [[{ text_excerpt: "Winner via loaded ts" }]],
755755
},
756756
});
757757

@@ -777,7 +777,7 @@ describe("resolveSkillSparTask", () => {
777777
"source FROM": [
778778
[{ id: "session:s1", source: "claude" }],
779779
],
780-
"text_excerpt FROM turn WHERE session": [["Repo task"]],
780+
"text_excerpt, seq FROM turn WHERE session": [[{ text_excerpt: "Repo task" }]],
781781
},
782782
});
783783

@@ -834,7 +834,7 @@ describe("resolveSkillSparTask", () => {
834834
[{ id: "session:s1", source: "claude" }],
835835
],
836836
// turn query returns empty: no user turns in this session
837-
"text_excerpt FROM turn WHERE session": [[]],
837+
"text_excerpt, seq FROM turn WHERE session": [[]],
838838
},
839839
});
840840

@@ -855,7 +855,7 @@ describe("resolveSkillSparTask", () => {
855855
[{ id: "session:s42" }],
856856
],
857857
// turn query returns empty
858-
"text_excerpt FROM turn WHERE session": [[]],
858+
"text_excerpt, seq FROM turn WHERE session": [[]],
859859
},
860860
});
861861

apps/axctl/src/dojo/skill-spar.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -392,10 +392,10 @@ export const resolveSkillSparTask = (
392392
baselineSession = sess.id;
393393

394394
// Derive first user turn text_excerpt (the canonical first-message field).
395-
const promptRows = yield* db.query<[Array<string | null>]>(
396-
`SELECT VALUE text_excerpt FROM turn WHERE session = ${recordLiteral("session", key)} AND role = 'user' ORDER BY seq ASC LIMIT 1;`,
395+
const promptRows = yield* db.query<[Array<{ text_excerpt: string | null }>]>(
396+
`SELECT text_excerpt, seq FROM turn WHERE session = ${recordLiteral("session", key)} AND role = 'user' ORDER BY seq ASC LIMIT 1;`,
397397
);
398-
const promptText = promptRows?.[0]?.[0] ?? null;
398+
const promptText = promptRows?.[0]?.[0]?.text_excerpt ?? null;
399399
if (!promptText) {
400400
return yield* Effect.fail(
401401
new SparCaptureError(
@@ -476,10 +476,10 @@ export const resolveSkillSparTask = (
476476

477477
// Derive the task text from the first user turn in the chosen session.
478478
const bestKey = recordKeyPart(best!.id, "session") ?? best!.id;
479-
const promptRows = yield* db.query<[Array<string | null>]>(
480-
`SELECT VALUE text_excerpt FROM turn WHERE session = ${recordLiteral("session", bestKey)} AND role = 'user' ORDER BY seq ASC LIMIT 1;`,
479+
const promptRows = yield* db.query<[Array<{ text_excerpt: string | null }>]>(
480+
`SELECT text_excerpt, seq FROM turn WHERE session = ${recordLiteral("session", bestKey)} AND role = 'user' ORDER BY seq ASC LIMIT 1;`,
481481
);
482-
const promptText = promptRows?.[0]?.[0] ?? null;
482+
const promptText = promptRows?.[0]?.[0]?.text_excerpt ?? null;
483483
if (!promptText) {
484484
return yield* Effect.fail(
485485
new SparCaptureError(

0 commit comments

Comments
 (0)