Commit 1 of 4:
f2bccc945d
feat(store): record which engine a thread and a run used
next
whole branch
Review threads live on the whole diff, not on one commit, so
none are shown here - a thread's line means something in the
branch's final form, and painting it into an intermediate
step would put it on code it is not about.
app/Models/Conversation.ts modified +12 -0 Changes to app/Models/Conversation.ts ⋯ 69 lines Show the 69 lines above this hunk @@ -70,5 +70,17 @@ export default defineModel({ 70 70 validation : { rule : schema . string ( ) } , 71 71 factory : ( ) => '' , 72 72 } , 73 + 74 + 75 + 76 + 77 + 78 + 79 + engine : { 80 + order : 8 , 81 + fillable : true , 82 + validation : { rule : schema . enum ( [ 'claude' , 'codex' ] ) } , 83 + factory : ( ) => 'claude' , 84 + } , 73 85 } , 74 86 } as const )
app/Models/Run.ts modified +12 -0 Changes to app/Models/Run.ts ⋯ 109 lines Show the 109 lines above this hunk @@ -110,5 +110,17 @@ export default defineModel({ 110 110 validation : { rule : schema . number ( ) } , 111 111 factory : ( ) => Date . now ( ) , 112 112 } , 113 + 114 + 115 + 116 + 117 + 118 + 119 + engine : { 120 + order : 14 , 121 + fillable : true , 122 + validation : { rule : schema . enum ( [ 'claude' , 'codex' ] ) } , 123 + factory : faker => faker . helpers . arrayElement ( [ 'claude' , 'codex' ] ) , 124 + } , 113 125 } , 114 126 } as const )
app/Uplink/model-store.ts modified +2 -0 Changes to app/Uplink/model-store.ts ⋯ 37 lines Show the 37 lines above this hunk @@ -38,6 +38,7 @@ export class ModelStore implements Store { 38 38 cwd : row . cwd ? ? null , 39 39 lastActiveAt : row . last_active_at ? ? null , 40 40 moreText : row . more_text ? ? null , 41 + engine : row . engine ? ? null , 41 42 } 42 43 } 43 44 ⋯ 5 lines Show the 5 lines above this hunk @@ -49,6 +50,7 @@ export class ModelStore implements Store { 49 50 cwd : conversation . cwd , 50 51 lastActiveAt : conversation . lastActiveAt , 51 52 moreText : conversation . moreText , 53 + engine : conversation . engine , 52 54 } 53 55 const existing = await Conversation . where ( 'chat_guid' , conversation . chatGuid ) . first ( ) as { update : ( v : object ) => Promise < unknown > } | undefined 54 56 if ( existing )
app/Uplink/sqlite-store.ts modified +11 -5 Changes to app/Uplink/sqlite-store.ts ⋯ 36 lines Show the 36 lines above this hunk @@ -37,7 +37,7 @@ export interface RunRow extends RunRecord { 37 37 * old it is , and so the day a change needs more than a new column - a backfill , 38 38 * a rename - has a version to key off . 39 39 * / 40 - const SCHEMA_VERSION = 1 40 + const SCHEMA_VERSION = 2 41 41 42 42 export interface TableSpec { 43 43 name : string ⋯ 34 lines Show the 34 lines above this hunk @@ -78,6 +78,7 @@ export const TABLES: TableSpec[] = [ 78 78 cwd : 'TEXT' , 79 79 last_active_at : 'INTEGER' , 80 80 more_text : 'TEXT' , 81 + engine : 'TEXT' , 81 82 } , 82 83 } , 83 84 { ⋯ 13 lines Show the 13 lines above this hunk @@ -97,6 +98,7 @@ export const TABLES: TableSpec[] = [ 97 98 last_activity : 'TEXT' , 98 99 started_at : 'INTEGER' , 99 100 finished_at : 'INTEGER' , 101 + engine : 'TEXT' , 100 102 } , 101 103 } , 102 104 ] ⋯ 12 lines Show the 12 lines above this hunk @@ -115,6 +117,7 @@ export const RUN_COLUMNS: Record<keyof RunRecord, string> = { 115 117 lastActivity : 'last_activity' , 116 118 startedAt : 'started_at' , 117 119 finishedAt : 'finished_at' , 120 + engine : 'engine' , 118 121 } 119 122 120 123 ⋯ 115 lines Show the 115 lines above this hunk @@ -236,17 +239,19 @@ export class SqliteStore implements Store { 236 239 237 240 238 241 242 + 239 243 240 244 241 245 242 246 243 247 244 - 245 - 248 + 249 + 246 250 247 251 248 - 249 - 252 + 253 + 254 + 250 255 251 256 252 257 ⋯ 30 lines Show the 30 lines above this hunk @@ -283,6 +288,7 @@ export class SqliteStore implements Store { 283 288 284 289 285 290 291 + 286 292 287 293 288 294
app/Uplink/store.ts modified +10 -0 Changes to app/Uplink/store.ts ⋯ 3 lines Show the 3 lines above this hunk @@ -4,6 +4,8 @@4 4 * the model - backed store in `model-store.ts` , tests use `MemoryStore` . 5 5 * / 6 6 7 + import type { EngineId } from './engine' 8 + 7 9 export type RunStatus = 'queued' | 'running' | 'done' | 'failed' | 'stopped' 8 10 9 11 export interface ConversationRecord { ⋯ 5 lines Show the 5 lines above this hunk @@ -15,6 +17,12 @@ export interface ConversationRecord { 15 17 lastActiveAt : number | null 16 18 17 19 moreText : string | null 20 + 21 + 22 + 23 + 24 + 25 + engine : EngineId | null 18 26 } 19 27 20 28 export interface RunRecord { ⋯ 10 lines Show the 10 lines above this hunk @@ -31,6 +39,8 @@ export interface RunRecord { 31 39 lastActivity ? : string | null 32 40 startedAt ? : number | null 33 41 finishedAt ? : number | null 42 + 43 + engine ? : EngineId | null 34 44 } 35 45 36 46 export interface Store {
app/Uplink/uplink.ts modified +1 -0 Changes to app/Uplink/uplink.ts ⋯ 300 lines Show the 300 lines above this hunk @@ -301,6 +301,7 @@ export class Uplink { 301 301 cwd : null , 302 302 lastActiveAt : null , 303 303 moreText : null , 304 + engine : null , 304 305 } 305 306 } 306 307
database/migrations/1789486698560-alter-conversations-columns.sql added +3 -0 Changes to database/migrations/1789486698560-alter-conversations-columns.sql @@ -0,0 +1,3 @@ 1 + - - qb : generated 2 + - - @ generated by `buddy migrate:regenerate` - edits will be overwritten 3 + ALTER TABLE "conversations" ADD COLUMN "engine" TEXT CHECK ( "engine" IN ( 'claude' , 'codex' ) ) ;
database/migrations/1789486698561-alter-runs-columns.sql added +3 -0 Changes to database/migrations/1789486698561-alter-runs-columns.sql @@ -0,0 +1,3 @@ 1 + - - qb : generated 2 + - - @ generated by `buddy migrate:regenerate` - edits will be overwritten 3 + ALTER TABLE "runs" ADD COLUMN "engine" TEXT CHECK ( "engine" IN ( 'claude' , 'codex' ) ) ;
storage/framework/database/model-snapshot.sqlite.json modified +26 -2 Changes to storage/framework/database/model-snapshot.sqlite.json ⋯ 89 lines Show the 89 lines above this hunk @@ -90,6 +90,18 @@90 90 "isUnique" : false , 91 91 "isNullable" : true , 92 92 "hasDefault" : false 93 + } , 94 + { 95 + "name" : "engine" , 96 + "type" : "enum" , 97 + "isPrimaryKey" : false , 98 + "isUnique" : false , 99 + "isNullable" : true , 100 + "hasDefault" : false , 101 + "enumValues" : [ 102 + "claude" , 103 + "codex" 104 + ] 93 105 } 94 106 ] , 95 107 "indexes" : [ ⋯ 161 lines Show the 161 lines above this hunk @@ -257,6 +269,18 @@257 269 "isUnique" : false , 258 270 "isNullable" : true , 259 271 "hasDefault" : false 272 + } , 273 + { 274 + "name" : "engine" , 275 + "type" : "enum" , 276 + "isPrimaryKey" : false , 277 + "isUnique" : false , 278 + "isNullable" : true , 279 + "hasDefault" : false , 280 + "enumValues" : [ 281 + "claude" , 282 + "codex" 283 + ] 260 284 } 261 285 ] , 262 286 "indexes" : [ ] ⋯ 129 lines Show the 129 lines above this hunk @@ -392,7 +416,7 @@392 416 } 393 417 ] 394 418 } , 395 - "hash" : "cf904d0837380746ef9f95409635f2960b57dbac539b69229ea1da12f911d4b5" , 419 + "hash" : "3273a7f2b7ad412f972eb4decd23ad15b2cb9ac86d757bd5f520ed81f2e3d919" , 396 420 "dialect" : "sqlite" , 397 - "updatedAt" : "2026-09- 28T21 : 27 : 50 . 923Z " 421 + "updatedAt" : "2026-09- 29T15 : 42 : 55 . 226Z " 398 422 }
tests/unit/uplink/sqlite-store.test.ts modified +1 -1 Changes to tests/unit/uplink/sqlite-store.test.ts ⋯ 29 lines Show the 29 lines above this hunk @@ -30,7 +30,7 @@ describe('SqliteStore', () => { 30 30 await store . setCursor ( 42 ) 31 31 expect ( await store . cursor ( ) ) . toBe ( 42 ) 32 32 33 - const conversation = { chatGuid : 'g' , handle : ME , service : 'iMessage' , sessionId : 's' , cwd : '/tmp' , lastActiveAt : 5 , moreText : 'x' . repeat ( 3000 ) } 33 + const conversation = { chatGuid : 'g' , handle : ME , service : 'iMessage' , sessionId : 's' , cwd : '/tmp' , lastActiveAt : 5 , moreText : 'x' . repeat ( 3000 ) , engine : null } 34 34 await store . saveConversation ( conversation ) 35 35 await store . saveConversation ( { ... conversation , sessionId : 's2' } ) 36 36 expect ( await store . conversation ( 'g' ) ) . toEqual ( { ... conversation , sessionId : 's2' } )