@@ -146,10 +146,11 @@ fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), std::io::Error> {
146146/// Apply a `Changes` batch via the service-layer `ChangesApplier`.
147147///
148148/// This opens the libSQL event DB, spins a short-lived tokio runtime, and
149- /// applies all mutations (JSON store writes + event logging) in order.
149+ /// applies all mutations (JSON store writes + event logging + DB upserts ) in order.
150150/// Returns the number of mutations applied. Calls `error_exit` on failure.
151151pub fn apply_changes ( flow_dir : & Path , changes : & flowctl_core:: changes:: Changes ) -> usize {
152152 use crate :: output:: error_exit;
153+ use flowctl_core:: changes:: Mutation ;
153154 use flowctl_service:: changes:: ChangesApplier ;
154155
155156 if changes. is_empty ( ) {
@@ -158,7 +159,7 @@ pub fn apply_changes(flow_dir: &Path, changes: &flowctl_core::changes::Changes)
158159
159160 let actor = resolve_actor ( ) ;
160161
161- // Open DB for event logging (best-effort: if DB unavailable, fall back to JSON-only)
162+ // Open DB for event logging + entity upserts
162163 let cwd = env:: current_dir ( ) . unwrap_or_else ( |_| PathBuf :: from ( "." ) ) ;
163164 let rt = tokio:: runtime:: Builder :: new_current_thread ( )
164165 . enable_all ( )
@@ -177,6 +178,32 @@ pub fn apply_changes(flow_dir: &Path, changes: &flowctl_core::changes::Changes)
177178
178179 let result = applier. apply ( changes) . await
179180 . unwrap_or_else ( |e| error_exit ( & format ! ( "Failed to apply changes: {e}" ) ) ) ;
181+
182+ // Dual-write: upsert entities into DB tables so commands like `gap add`
183+ // (which validate via EpicRepo::get) can find them.
184+ let conn2 = db. connect ( )
185+ . unwrap_or_else ( |e| error_exit ( & format ! ( "Failed to connect to DB: {e}" ) ) ) ;
186+ let epic_repo = flowctl_db:: EpicRepo :: new ( conn2. clone ( ) ) ;
187+ let task_repo = flowctl_db:: TaskRepo :: new ( conn2) ;
188+
189+ for mutation in & changes. mutations {
190+ match mutation {
191+ Mutation :: CreateEpic { epic } | Mutation :: UpdateEpic { epic } => {
192+ let _ = epic_repo. upsert ( epic) . await ;
193+ }
194+ Mutation :: RemoveEpic { id } => {
195+ let _ = epic_repo. delete ( id) . await ;
196+ }
197+ Mutation :: CreateTask { task } | Mutation :: UpdateTask { task } => {
198+ let _ = task_repo. upsert ( task) . await ;
199+ }
200+ Mutation :: RemoveTask { id } => {
201+ let _ = task_repo. delete ( id) . await ;
202+ }
203+ _ => { }
204+ }
205+ }
206+
180207 // Leak the DB handle to keep it alive (same pattern as db_shim)
181208 std:: mem:: forget ( db) ;
182209 result. applied
0 commit comments