@cat-factory/app 0.105.0 → 0.106.0

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.
@@ -19,8 +19,15 @@ const initiative = computed(() => initiatives.forBlock(props.block.id))
19
19
 
20
20
  const status = computed<InitiativeStatus>(() => initiative.value?.status ?? 'planning')
21
21
 
22
- // The ONLY pipeline runnable on an initiative block (see the engine's runnable guard).
23
- const planningPipeline = computed(() => pipelines.pipelines.find((p) => p.id === 'pl_initiative'))
22
+ // The planning pipeline runnable on this initiative block: its preset descriptor's
23
+ // `planningPipelineId` (a preset picks its own planning pipeline — the generic preset keeps
24
+ // `pl_initiative`). `planningPipelineIdFor` returns null for a named preset that hasn't hydrated,
25
+ // so "Run planning" stays disabled rather than launching the wrong (generic interviewer) pipeline.
26
+ // The engine's runnable guard still enforces that only an initiative-shaped pipeline runs here.
27
+ const planningPipeline = computed(() => {
28
+ const id = initiatives.planningPipelineIdFor(initiative.value)
29
+ return id ? pipelines.pipelines.find((p) => p.id === id) : undefined
30
+ })
24
31
  const running = computed(() => !!props.block.executionId)
25
32
 
26
33
  // The interviewer has parked the planning run with questions awaiting answers.
@@ -3,9 +3,13 @@ import { computed, ref } from 'vue'
3
3
  import type {
4
4
  Initiative,
5
5
  InitiativeExecutionPolicy,
6
+ InitiativePresetDescriptor,
6
7
  PromoteInitiativeFollowUpInput,
7
8
  UpdateInitiativeItemInput,
8
9
  } from '~/types/domain'
10
+
11
+ /** The built-in generic preset id (mirrors kernel's `GENERIC_INITIATIVE_PRESET_ID`). */
12
+ const GENERIC_PRESET_ID = 'preset_generic'
9
13
  import { useWorkspaceStore } from '~/stores/workspace'
10
14
  import { useBoardStore } from '~/stores/board'
11
15
 
@@ -30,6 +34,13 @@ export const useInitiativesStore = defineStore('initiatives', () => {
30
34
  const byBlock = ref<Record<string, Initiative>>({})
31
35
  /** True while a create call is in flight (the modal's submit spinner). */
32
36
  const creating = ref(false)
37
+ /**
38
+ * The registered initiative-preset descriptors from the snapshot (built-in generic + any a
39
+ * deployment mixed in). Drives the create picker and — already today — which planning pipeline
40
+ * "Run planning" starts. Empty until a snapshot hydrates it; the SPA falls back to the generic
41
+ * pipeline when a preset can't be resolved.
42
+ */
43
+ const presets = ref<InitiativePresetDescriptor[]>([])
33
44
 
34
45
  const all = computed(() => Object.values(byBlock.value))
35
46
 
@@ -37,6 +48,25 @@ export const useInitiativesStore = defineStore('initiatives', () => {
37
48
  return byBlock.value[blockId] ?? null
38
49
  }
39
50
 
51
+ /** Resolve a preset descriptor by id (defaulting to the generic preset), or null when unknown. */
52
+ function presetById(presetId: string | undefined): InitiativePresetDescriptor | null {
53
+ return presets.value.find((p) => p.id === (presetId ?? GENERIC_PRESET_ID)) ?? null
54
+ }
55
+
56
+ /**
57
+ * The planning pipeline id to start for an initiative: its preset descriptor's
58
+ * `planningPipelineId`. No preset (or the built-in generic) → `pl_initiative`, which is always in
59
+ * the catalog. For a NAMED preset we return `null` (not the generic pipeline) when presets
60
+ * haven't hydrated or the snapshot omitted this descriptor — so the caller keeps "Run planning"
61
+ * disabled rather than silently launching the interviewer over an already-seeded skip-interview
62
+ * initiative.
63
+ */
64
+ function planningPipelineIdFor(initiative: Initiative | null): string | null {
65
+ const presetId = initiative?.presetId
66
+ if (!presetId || presetId === GENERIC_PRESET_ID) return 'pl_initiative'
67
+ return presetById(presetId)?.planningPipelineId ?? null
68
+ }
69
+
40
70
  /**
41
71
  * Rebuild the cache from a snapshot (the hydrate fan-out). The snapshot is authoritative
42
72
  * for EXISTENCE (entities it omits are dropped — they were deleted), but NOT for freshness:
@@ -56,6 +86,12 @@ export const useInitiativesStore = defineStore('initiatives', () => {
56
86
  byBlock.value = map
57
87
  }
58
88
 
89
+ /** Replace the registered preset descriptors from a snapshot (idempotent on reload). */
90
+ function hydratePresets(next: InitiativePresetDescriptor[] | undefined) {
91
+ if (next === undefined) return
92
+ presets.value = next
93
+ }
94
+
59
95
  /** Patch from a live `initiative` stream event or a call response (newest rev wins). */
60
96
  function upsert(initiative: Initiative) {
61
97
  const existing = byBlock.value[initiative.blockId]
@@ -237,13 +273,17 @@ export const useInitiativesStore = defineStore('initiatives', () => {
237
273
  return {
238
274
  available,
239
275
  byBlock,
276
+ presets,
240
277
  all,
241
278
  creating,
242
279
  resuming,
243
280
  controlling,
244
281
  curating,
245
282
  forBlock,
283
+ presetById,
284
+ planningPipelineIdFor,
246
285
  hydrate,
286
+ hydratePresets,
247
287
  upsert,
248
288
  create,
249
289
  load,
@@ -131,6 +131,9 @@ export const useWorkspaceStore = defineStore(
131
131
  useServiceFragmentDefaultsStore().hydrate(snapshot.serviceFragmentDefaults?.fragmentIds)
132
132
  useRecurringPipelinesStore().hydrate(snapshot.recurringPipelines ?? [])
133
133
  useInitiativesStore().hydrate(snapshot.initiatives)
134
+ // Registered initiative presets (built-in generic + any a deployment mixed in): drive the
135
+ // create picker and which planning pipeline "Run planning" starts. Workspace-independent.
136
+ useInitiativesStore().hydratePresets(snapshot.initiativePresets)
134
137
  useTrackerStore().hydrate(snapshot.trackerSettings)
135
138
  useServicesStore().hydrate(snapshot.mounts ?? [], snapshot.serviceCatalog ?? [])
136
139
  // Merge the deployment's registered custom agent kinds into the palette catalog so a
@@ -13,6 +13,7 @@ export type {
13
13
  InitiativeItemStatus,
14
14
  InitiativePhase,
15
15
  InitiativePipelineRule,
16
+ InitiativePresetDescriptor,
16
17
  InitiativeQa,
17
18
  InitiativeStatus,
18
19
  PromoteInitiativeFollowUpInput,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.105.0",
3
+ "version": "0.106.0",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,7 +34,7 @@
34
34
  "valibot": "^1.4.2",
35
35
  "vue": "3.5.39",
36
36
  "wretch": "^3.0.9",
37
- "@cat-factory/contracts": "0.115.0"
37
+ "@cat-factory/contracts": "0.116.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@toad-contracts/testing": "0.3.2",