@@ -118,10 +118,20 @@ async fn load_epic(_conn: Option<&Connection>, flow_dir: &Path, epic_id: &str) -
118118 flowctl_core:: json_store:: epic_read ( flow_dir, epic_id) . ok ( )
119119}
120120
121- async fn get_runtime ( conn : Option < & Connection > , _flow_dir : & Path , task_id : & str ) -> Option < RuntimeState > {
122- let conn = conn?;
123- let repo = flowctl_db:: RuntimeRepo :: new ( conn. clone ( ) ) ;
124- repo. get ( task_id) . await . ok ( ) . flatten ( )
121+ async fn get_runtime ( _conn : Option < & Connection > , flow_dir : & Path , task_id : & str ) -> Option < RuntimeState > {
122+ // Read from JSON state and convert to RuntimeState for compatibility
123+ let state = flowctl_core:: json_store:: state_read ( flow_dir, task_id) . ok ( ) ?;
124+ Some ( RuntimeState {
125+ task_id : task_id. to_string ( ) ,
126+ assignee : state. assignee ,
127+ claimed_at : state. claimed_at ,
128+ completed_at : state. completed_at ,
129+ duration_secs : state. duration_seconds ,
130+ blocked_reason : state. blocked_reason ,
131+ baseline_rev : state. baseline_rev ,
132+ final_rev : state. final_rev ,
133+ retry_count : state. retry_count ,
134+ } )
125135}
126136
127137/// Load all tasks for an epic from JSON files.
@@ -221,10 +231,17 @@ async fn propagate_upstream_failure(
221231 continue ;
222232 }
223233
224- // Update DB (sole source of truth)
225- if let Some ( conn) = conn {
226- let task_repo = flowctl_db:: TaskRepo :: new ( conn. clone ( ) ) ;
227- let _ = task_repo. update_status ( tid, Status :: UpstreamFailed ) . await ;
234+ // Update JSON state
235+ if let Ok ( mut state) = flowctl_core:: json_store:: state_read ( flow_dir, tid) {
236+ state. status = Status :: UpstreamFailed ;
237+ state. updated_at = Utc :: now ( ) ;
238+ let _ = flowctl_core:: json_store:: state_write ( flow_dir, tid, & state) ;
239+ } else {
240+ let state = flowctl_core:: json_store:: TaskState {
241+ status : Status :: UpstreamFailed ,
242+ ..Default :: default ( )
243+ } ;
244+ let _ = flowctl_core:: json_store:: state_write ( flow_dir, tid, & state) ;
228245 }
229246
230247 affected. push ( tid. clone ( ) ) ;
@@ -246,31 +263,28 @@ async fn handle_task_failure(
246263 if max_retries > 0 && current_retry_count < max_retries {
247264 let new_retry_count = current_retry_count + 1 ;
248265
249- if let Some ( conn) = conn {
250- let task_repo = flowctl_db:: TaskRepo :: new ( conn. clone ( ) ) ;
251- let _ = task_repo. update_status ( task_id, Status :: UpForRetry ) . await ;
252-
253- let runtime_repo = flowctl_db:: RuntimeRepo :: new ( conn. clone ( ) ) ;
254- let rt = RuntimeState {
255- task_id : task_id. to_string ( ) ,
256- assignee : runtime. as_ref ( ) . and_then ( |r| r. assignee . clone ( ) ) ,
257- claimed_at : None ,
258- completed_at : None ,
259- duration_secs : None ,
260- blocked_reason : None ,
261- baseline_rev : runtime. as_ref ( ) . and_then ( |r| r. baseline_rev . clone ( ) ) ,
262- final_rev : None ,
263- retry_count : new_retry_count,
264- } ;
265- let _ = runtime_repo. upsert ( & rt) . await ;
266- }
266+ let task_state = flowctl_core:: json_store:: TaskState {
267+ status : Status :: UpForRetry ,
268+ assignee : runtime. as_ref ( ) . and_then ( |r| r. assignee . clone ( ) ) ,
269+ claimed_at : None ,
270+ completed_at : None ,
271+ evidence : None ,
272+ blocked_reason : None ,
273+ duration_seconds : None ,
274+ baseline_rev : runtime. as_ref ( ) . and_then ( |r| r. baseline_rev . clone ( ) ) ,
275+ final_rev : None ,
276+ retry_count : new_retry_count,
277+ updated_at : Utc :: now ( ) ,
278+ } ;
279+ let _ = flowctl_core:: json_store:: state_write ( flow_dir, task_id, & task_state) ;
267280
268281 ( Status :: UpForRetry , Vec :: new ( ) )
269282 } else {
270- if let Some ( conn) = conn {
271- let task_repo = flowctl_db:: TaskRepo :: new ( conn. clone ( ) ) ;
272- let _ = task_repo. update_status ( task_id, Status :: Failed ) . await ;
273- }
283+ let task_state = flowctl_core:: json_store:: TaskState {
284+ status : Status :: Failed ,
285+ ..Default :: default ( )
286+ } ;
287+ let _ = flowctl_core:: json_store:: state_write ( flow_dir, task_id, & task_state) ;
274288
275289 let affected = propagate_upstream_failure ( conn, flow_dir, task_id) . await ;
276290 ( Status :: Failed , affected)
@@ -373,13 +387,14 @@ pub async fn start_task(
373387 Some ( now)
374388 } ;
375389
376- let runtime_state = RuntimeState {
377- task_id : req . task_id . clone ( ) ,
390+ let task_state = flowctl_core :: json_store :: TaskState {
391+ status : Status :: InProgress ,
378392 assignee : Some ( new_assignee) ,
379393 claimed_at,
380394 completed_at : None ,
381- duration_secs : None ,
395+ evidence : None ,
382396 blocked_reason : None ,
397+ duration_seconds : None ,
383398 baseline_rev : existing_rt
384399 . as_ref ( )
385400 . and_then ( |rt| rt. baseline_rev . clone ( ) ) ,
@@ -388,21 +403,12 @@ pub async fn start_task(
388403 . as_ref ( )
389404 . map ( |rt| rt. retry_count )
390405 . unwrap_or ( 0 ) ,
406+ updated_at : Utc :: now ( ) ,
391407 } ;
392408
393- // Write SQLite (authoritative)
394- if let Some ( conn) = conn {
395- let task_repo = flowctl_db:: TaskRepo :: new ( conn. clone ( ) ) ;
396- task_repo
397- . update_status ( & req. task_id , Status :: InProgress )
398- . await
399- . map_err ( ServiceError :: from) ?;
400- let runtime_repo = flowctl_db:: RuntimeRepo :: new ( conn. clone ( ) ) ;
401- runtime_repo
402- . upsert ( & runtime_state)
403- . await
404- . map_err ( ServiceError :: from) ?;
405- }
409+ // Write to JSON state file
410+ flowctl_core:: json_store:: state_write ( flow_dir, & req. task_id , & task_state)
411+ . map_err ( |e| ServiceError :: IoError ( std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e. to_string ( ) ) ) ) ?;
406412
407413 Ok ( StartTaskResponse {
408414 task_id : req. task_id ,
@@ -547,34 +553,29 @@ pub async fn done_task(
547553 let tests = to_list ( evidence_obj. get ( "tests" ) ) ;
548554 let prs = to_list ( evidence_obj. get ( "prs" ) ) ;
549555
550- // Write SQLite (sole source of truth)
551- if let Some ( conn) = conn {
552- let task_repo = flowctl_db:: TaskRepo :: new ( conn. clone ( ) ) ;
553- let _ = task_repo. update_status ( & req. task_id , Status :: Done ) . await ;
554-
555- let runtime_repo = flowctl_db:: RuntimeRepo :: new ( conn. clone ( ) ) ;
556+ // Write to JSON state file
557+ {
556558 let now = Utc :: now ( ) ;
557- let rt = RuntimeState {
558- task_id : req. task_id . clone ( ) ,
559+ let ev = Evidence {
560+ commits : commits. clone ( ) ,
561+ tests : tests. clone ( ) ,
562+ prs : prs. clone ( ) ,
563+ ..Evidence :: default ( )
564+ } ;
565+ let task_state = flowctl_core:: json_store:: TaskState {
566+ status : Status :: Done ,
559567 assignee : runtime. as_ref ( ) . and_then ( |r| r. assignee . clone ( ) ) ,
560568 claimed_at : runtime. as_ref ( ) . and_then ( |r| r. claimed_at ) ,
561569 completed_at : Some ( now) ,
562- duration_secs : duration_seconds ,
570+ evidence : Some ( ev ) ,
563571 blocked_reason : None ,
572+ duration_seconds : duration_seconds. map ( |d| d as u64 ) ,
564573 baseline_rev : runtime. as_ref ( ) . and_then ( |r| r. baseline_rev . clone ( ) ) ,
565574 final_rev : runtime. as_ref ( ) . and_then ( |r| r. final_rev . clone ( ) ) ,
566575 retry_count : runtime. as_ref ( ) . map ( |r| r. retry_count ) . unwrap_or ( 0 ) ,
576+ updated_at : now,
567577 } ;
568- let _ = runtime_repo. upsert ( & rt) . await ;
569-
570- let ev = Evidence {
571- commits : commits. clone ( ) ,
572- tests : tests. clone ( ) ,
573- prs : prs. clone ( ) ,
574- ..Evidence :: default ( )
575- } ;
576- let evidence_repo = flowctl_db:: EvidenceRepo :: new ( conn. clone ( ) ) ;
577- let _ = evidence_repo. upsert ( & req. task_id , & ev) . await ;
578+ let _ = flowctl_core:: json_store:: state_write ( flow_dir, & req. task_id , & task_state) ;
578579 }
579580
580581 // Archive review receipt if present
@@ -632,25 +633,23 @@ pub async fn block_task(
632633 ) ) ;
633634 }
634635
635- // Write SQLite (authoritative)
636- if let Some ( conn) = conn {
637- let task_repo = flowctl_db:: TaskRepo :: new ( conn. clone ( ) ) ;
638- let _ = task_repo. update_status ( & req. task_id , Status :: Blocked ) . await ;
639-
640- let runtime_repo = flowctl_db:: RuntimeRepo :: new ( conn. clone ( ) ) ;
641- let existing = runtime_repo. get ( & req. task_id ) . await . ok ( ) . flatten ( ) ;
642- let rt = RuntimeState {
643- task_id : req. task_id . clone ( ) ,
636+ // Write to JSON state file
637+ {
638+ let existing = flowctl_core:: json_store:: state_read ( flow_dir, & req. task_id ) . ok ( ) ;
639+ let task_state = flowctl_core:: json_store:: TaskState {
640+ status : Status :: Blocked ,
644641 assignee : existing. as_ref ( ) . and_then ( |r| r. assignee . clone ( ) ) ,
645642 claimed_at : existing. as_ref ( ) . and_then ( |r| r. claimed_at ) ,
646643 completed_at : None ,
647- duration_secs : None ,
644+ evidence : existing . as_ref ( ) . and_then ( |r| r . evidence . clone ( ) ) ,
648645 blocked_reason : Some ( reason. clone ( ) ) ,
646+ duration_seconds : None ,
649647 baseline_rev : existing. as_ref ( ) . and_then ( |r| r. baseline_rev . clone ( ) ) ,
650648 final_rev : None ,
651649 retry_count : existing. as_ref ( ) . map ( |r| r. retry_count ) . unwrap_or ( 0 ) ,
650+ updated_at : Utc :: now ( ) ,
652651 } ;
653- let _ = runtime_repo . upsert ( & rt ) . await ;
652+ let _ = flowctl_core :: json_store :: state_write ( flow_dir , & req . task_id , & task_state ) ;
654653 }
655654
656655 Ok ( BlockTaskResponse {
@@ -788,24 +787,9 @@ pub async fn restart_task(
788787 // Execute reset
789788 let mut reset_ids = Vec :: new ( ) ;
790789 for tid in & to_reset {
791- if let Some ( conn) = conn {
792- let task_repo = flowctl_db:: TaskRepo :: new ( conn. clone ( ) ) ;
793- let _ = task_repo. update_status ( tid, Status :: Todo ) . await ;
794-
795- let runtime_repo = flowctl_db:: RuntimeRepo :: new ( conn. clone ( ) ) ;
796- let rt = RuntimeState {
797- task_id : tid. clone ( ) ,
798- assignee : None ,
799- claimed_at : None ,
800- completed_at : None ,
801- duration_secs : None ,
802- blocked_reason : None ,
803- baseline_rev : None ,
804- final_rev : None ,
805- retry_count : 0 ,
806- } ;
807- let _ = runtime_repo. upsert ( & rt) . await ;
808- }
790+ // Reset to blank JSON state
791+ let blank = flowctl_core:: json_store:: TaskState :: default ( ) ;
792+ let _ = flowctl_core:: json_store:: state_write ( flow_dir, tid, & blank) ;
809793
810794 reset_ids. push ( tid. clone ( ) ) ;
811795 }
0 commit comments