@cat-factory/app 0.121.1 → 0.121.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, beforeEach } from 'vitest'
2
- import type { BootstrapJob } from '~/types/domain'
2
+ import type { BootstrapJob, EnvConfigRepairJob } from '~/types/domain'
3
3
  import { useAgentRunsStore } from '~/stores/agentRuns'
4
4
 
5
5
  /** Minimal BootstrapJob factory — only the fields the store's reconcile logic touches. */
@@ -72,3 +72,56 @@ describe('agentRuns store — monotonic bootstrap reconcile', () => {
72
72
  expect(store.bootstrapJobs[0]!.status).toBe('succeeded')
73
73
  })
74
74
  })
75
+
76
+ /** Minimal EnvConfigRepairJob factory — only the fields the store touches. */
77
+ function repairJob(id: string, over: Partial<EnvConfigRepairJob> = {}): EnvConfigRepairJob {
78
+ return {
79
+ id,
80
+ workspaceId: 'ws_test',
81
+ owner: 'acme',
82
+ repo: 'svc',
83
+ branch: 'main',
84
+ status: 'running',
85
+ ok: null,
86
+ issues: [],
87
+ subtasks: null,
88
+ error: null,
89
+ failure: null,
90
+ createdAt: 1,
91
+ updatedAt: 1,
92
+ ...over,
93
+ }
94
+ }
95
+
96
+ describe('agentRuns store — env-config-repair (plain useUpsertList adoption, candidate #3)', () => {
97
+ let store: ReturnType<typeof useAgentRunsStore>
98
+ beforeEach(() => {
99
+ store = useAgentRunsStore()
100
+ })
101
+
102
+ it('upsertEnvConfigRepair prepends a new run (newest-first) and replaces in place by id', () => {
103
+ store.upsertEnvConfigRepair(repairJob('r1'))
104
+ store.upsertEnvConfigRepair(repairJob('r2'))
105
+ // Newest prepended, so r2 leads r1.
106
+ expect(store.envConfigRepairJobs.map((j) => j.id)).toEqual(['r2', 'r1'])
107
+ // A later event for r1 replaces it IN PLACE (no reorder), unlike a monotonic-guarded list.
108
+ store.upsertEnvConfigRepair(repairJob('r1', { status: 'succeeded' }))
109
+ expect(store.envConfigRepairJobs.map((j) => j.id)).toEqual(['r2', 'r1'])
110
+ expect(store.envConfigRepairById('r1')?.status).toBe('succeeded')
111
+ })
112
+
113
+ it('envConfigRepairById looks a run up by id (absent → undefined)', () => {
114
+ store.upsertEnvConfigRepair(repairJob('r1'))
115
+ expect(store.envConfigRepairById('r1')?.id).toBe('r1')
116
+ expect(store.envConfigRepairById('nope')).toBeUndefined()
117
+ })
118
+
119
+ it('hydrateEnvConfigRepair replaces the cache newest-first', () => {
120
+ store.upsertEnvConfigRepair(repairJob('stale'))
121
+ store.hydrateEnvConfigRepair([
122
+ repairJob('old', { createdAt: 1 }),
123
+ repairJob('new', { createdAt: 9 }),
124
+ ])
125
+ expect(store.envConfigRepairJobs.map((j) => j.id)).toEqual(['new', 'old'])
126
+ })
127
+ })
@@ -9,6 +9,7 @@ import type {
9
9
  } from '~/types/domain'
10
10
  import { useWorkspaceStore } from '~/stores/workspace'
11
11
  import { useExecutionStore } from '~/stores/execution'
12
+ import { useUpsertList } from '~/composables/useUpsertList'
12
13
 
13
14
  /**
14
15
  * A coarse, per-block view of the current "agent run" against a block, regardless
@@ -62,9 +63,15 @@ export const useAgentRunsStore = defineStore('agentRuns', () => {
62
63
  * Env-config-repair runs for this workspace, newest-first. These have NO board block —
63
64
  * they're surfaced only on the infrastructure-providers window (looked up by the
64
65
  * `repairJobId` the `bootstrapRepo` response returned), so they're held separately and
65
- * NOT merged into {@link byBlock}.
66
+ * NOT merged into {@link byBlock}. Unlike the bootstrap list this is a PLAIN find-by-id
67
+ * upsert (no `updatedAt` monotonic guard), so it routes through the shared
68
+ * {@link useUpsertList} helper (the last plain-upsert holdout, refactoring candidate #3).
66
69
  */
67
- const envConfigRepairJobs = ref<EnvConfigRepairJob[]>([])
70
+ const {
71
+ items: envConfigRepairJobs,
72
+ upsert: upsertEnvConfigRepair,
73
+ get: envConfigRepairById,
74
+ } = useUpsertList<EnvConfigRepairJob>({ key: (j) => j.id, prepend: true })
68
75
 
69
76
  /**
70
77
  * Reconcile the cached bootstrap runs with a server snapshot for `workspaceId`. A snapshot is
@@ -96,27 +103,11 @@ export const useAgentRunsStore = defineStore('agentRuns', () => {
96
103
  bootstrapJobs.value = [...reconciled, ...preserved].sort((a, b) => b.createdAt - a.createdAt)
97
104
  }
98
105
 
99
- /** Replace the cached env-config-repair runs with a server snapshot. */
106
+ /** Replace the cached env-config-repair runs with a server snapshot (newest-first). */
100
107
  function hydrateEnvConfigRepair(jobs: EnvConfigRepairJob[]) {
101
108
  envConfigRepairJobs.value = [...jobs].sort((a, b) => b.createdAt - a.createdAt)
102
109
  }
103
110
 
104
- /**
105
- * Patch an env-config-repair run from a real-time `env-config-repair` event (or after
106
- * launching one): replace it in place by id, else prepend it. Keeps the infra window's
107
- * "repairing…" indicator reactive to live progress / outcome without a refetch.
108
- */
109
- function upsertEnvConfigRepair(job: EnvConfigRepairJob) {
110
- const i = envConfigRepairJobs.value.findIndex((j) => j.id === job.id)
111
- if (i >= 0) envConfigRepairJobs.value[i] = job
112
- else envConfigRepairJobs.value.unshift(job)
113
- }
114
-
115
- /** Look up a single env-config-repair run by id (the infra window tracks one by `repairJobId`). */
116
- function envConfigRepairById(id: string): EnvConfigRepairJob | undefined {
117
- return envConfigRepairJobs.value.find((j) => j.id === id)
118
- }
119
-
120
111
  /**
121
112
  * Patch a bootstrap run from a real-time `bootstrap` event (or after launching
122
113
  * one): replace it in place by id, else prepend it. Keeps the service card