@cat-factory/app 0.162.0 → 0.162.1

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.
@@ -15,6 +15,7 @@ import StepExecutionHistory from '~/components/board/StepExecutionHistory.vue'
15
15
  import { useStepTimer } from '~/composables/useStepTimer'
16
16
  import { useStepProse } from '~/composables/useStepProse'
17
17
  import { useStepApproval } from '~/composables/useStepApproval'
18
+ import { dedicatedParkView } from '~/utils/pipelineRender'
18
19
 
19
20
  // Detail overlay for a single pipeline step. Opened by clicking an agent in the
20
21
  // inspector list (TaskExecution) or the focus-view pipeline (PipelineProgress) via
@@ -151,6 +152,25 @@ const approvalId = computed(() => step.value?.approval?.id ?? null)
151
152
  // approve/request-changes/reject rail, it shows the shared iteration-cap prompt
152
153
  // (one more round / proceed / stop & reset), resolved through its own endpoint.
153
154
  const companionExceeded = computed(() => approvalPending.value && !!step.value?.companion?.exceeded)
155
+ // A park a DEDICATED window owns (fork choice / follow-up triage): the generic approve
156
+ // resolver refuses these server-side, so the rail is replaced by a redirect to that window.
157
+ // Computed live, since a coder step can park on one WHILE this overlay is already open
158
+ // (the routing in `dispatchStepView` only covers the open click).
159
+ const dedicatedPark = computed(() => (step.value ? dedicatedParkView(step.value) : null))
160
+ /** The generic approve/request-changes/reject rail applies (no dedicated surface owns the park). */
161
+ const genericApprovalPending = computed(
162
+ () => approvalPending.value && !companionExceeded.value && !dedicatedPark.value,
163
+ )
164
+
165
+ /** Jump from this overlay to the window that can actually resolve the dedicated park. */
166
+ function openDedicatedWindow() {
167
+ const c = ctx.value
168
+ const park = dedicatedPark.value
169
+ if (!c || !park) return
170
+ close()
171
+ if (park === 'follow-ups') ui.openFollowUps(c.instanceId, c.stepIndex)
172
+ else ui.openForkDecision(c.instanceId, c.stepIndex)
173
+ }
154
174
 
155
175
  function close() {
156
176
  // Reset the approval-mode sub-states so reopening the same step is clean
@@ -159,13 +179,14 @@ function close() {
159
179
  ui.closeStepDetail()
160
180
  }
161
181
 
162
- // The GitHub-style approval/review state machine for a pending gate step.
182
+ // The GitHub-style approval/review state machine for a pending gate step. A park a
183
+ // dedicated window owns is NOT reviewable here, so it doesn't count as pending.
163
184
  const approval = useStepApproval({
164
185
  step: () => step.value,
165
186
  scrollEl: () => scrollEl.value,
166
187
  instanceId: () => ctx.value?.instanceId,
167
188
  approvalId: () => approvalId.value,
168
- approvalPending: () => approvalPending.value,
189
+ approvalPending: () => approvalPending.value && !dedicatedPark.value,
169
190
  companionExceeded: () => companionExceeded.value,
170
191
  close,
171
192
  })
@@ -292,7 +313,7 @@ async function copyOutput() {
292
313
  </div>
293
314
  <div class="ms-auto flex items-center gap-1.5">
294
315
  <UBadge
295
- v-if="approvalPending && !companionExceeded"
316
+ v-if="genericApprovalPending"
296
317
  color="warning"
297
318
  variant="subtle"
298
319
  size="sm"
@@ -302,7 +323,7 @@ async function copyOutput() {
302
323
  {{ t('panels.stepDetail.approvalRequired') }}
303
324
  </UBadge>
304
325
  <UBadge
305
- v-else-if="companionExceeded"
326
+ v-else-if="companionExceeded || dedicatedPark"
306
327
  color="warning"
307
328
  variant="subtle"
308
329
  size="sm"
@@ -396,6 +417,37 @@ async function copyOutput() {
396
417
  @resolve="resolveCompanionCap"
397
418
  />
398
419
 
420
+ <!-- a park a dedicated window owns (fork choice / follow-up triage): the
421
+ generic approval rail can't resolve it (the server refuses), so point
422
+ the human at the window that can -->
423
+ <div
424
+ v-if="dedicatedPark"
425
+ class="rounded-xl border border-amber-500/30 bg-amber-500/5 p-4"
426
+ data-testid="dedicated-park-redirect"
427
+ >
428
+ <p class="text-[13px] leading-relaxed text-amber-200/90">
429
+ {{
430
+ dedicatedPark === 'follow-ups'
431
+ ? t('panels.stepDetail.followUpsParked')
432
+ : t('panels.stepDetail.forkParked')
433
+ }}
434
+ </p>
435
+ <UButton
436
+ class="mt-3"
437
+ color="primary"
438
+ size="sm"
439
+ :icon="dedicatedPark === 'follow-ups' ? 'i-lucide-compass' : 'i-lucide-git-fork'"
440
+ data-testid="dedicated-park-open"
441
+ @click="openDedicatedWindow"
442
+ >
443
+ {{
444
+ dedicatedPark === 'follow-ups'
445
+ ? t('panels.stepDetail.openFollowUps')
446
+ : t('panels.stepDetail.chooseApproach')
447
+ }}
448
+ </UButton>
449
+ </div>
450
+
399
451
  <!-- ephemeral environment lifecycle (spinning up / running / shut down /
400
452
  errored + the exact error), when this step runs against one -->
401
453
  <EnvironmentStatusPanel v-if="stepEnvironment" :environment="stepEnvironment" />
@@ -545,7 +597,7 @@ async function copyOutput() {
545
597
  class="reader-prose mt-1 text-[13px] leading-relaxed text-slate-300"
546
598
  :class="[
547
599
  s.depth > 0 ? 'ps-6' : '',
548
- approvalPending && !editing && !companionExceeded ? 'review-mode' : '',
600
+ genericApprovalPending && !editing ? 'review-mode' : '',
549
601
  ]"
550
602
  @click="onProseClick"
551
603
  v-html="s.bodyHtml"
@@ -567,7 +619,7 @@ async function copyOutput() {
567
619
  Approve / Request changes / Reject. A end-side rail on wide screens; a
568
620
  bottom sheet (still reachable) below lg, so the gate is always actionable. -->
569
621
  <aside
570
- v-if="approvalPending && !companionExceeded"
622
+ v-if="genericApprovalPending"
571
623
  class="absolute inset-x-0 bottom-0 z-10 flex max-h-[70dvh] flex-col rounded-t-2xl border-t border-slate-700 bg-slate-900/95 shadow-2xl backdrop-blur lg:static lg:inset-auto lg:z-auto lg:max-h-none lg:w-96 lg:shrink-0 lg:rounded-none lg:border-s lg:border-t-0 lg:border-slate-800 lg:bg-slate-900/60 lg:shadow-none lg:backdrop-blur-none"
572
624
  >
573
625
  <div class="border-b border-slate-800 px-4 py-3">
@@ -6,6 +6,7 @@ import {
6
6
  COMPANION_STATE_META,
7
7
  isCompanionKind,
8
8
  containerPhaseLabel,
9
+ dedicatedParkView,
9
10
  } from '~/utils/pipelineRender'
10
11
  import AgentFailureCard from '~/components/board/AgentFailureCard.vue'
11
12
  import AgentFailureHistory from '~/components/board/AgentFailureHistory.vue'
@@ -154,6 +155,12 @@ function openForkFor(i: number) {
154
155
  if (instance.value) ui.openForkDecision(instance.value.id, i)
155
156
  }
156
157
 
158
+ // Open the follow-up triage window for a coder step parked on undecided follow-up items
159
+ // (its dedicated chip — the generic approve resolver refuses this park server-side).
160
+ function openFollowUpsFor(i: number) {
161
+ if (instance.value) ui.openFollowUps(instance.value.id, i)
162
+ }
163
+
157
164
  // Open the PR deep-review findings-selection window for a pr-reviewer step parked awaiting
158
165
  // a selection (its dedicated chip, mirroring the fork-decision one above).
159
166
  function openPrReviewFor(i: number) {
@@ -425,7 +432,7 @@ async function mergePr() {
425
432
  v-else-if="
426
433
  s.approval &&
427
434
  s.approval.status === 'pending' &&
428
- s.forkDecision?.status === 'awaiting_choice'
435
+ dedicatedParkView(s) === 'fork-decision'
429
436
  "
430
437
  color="primary"
431
438
  variant="soft"
@@ -435,6 +442,23 @@ async function mergePr() {
435
442
  >
436
443
  {{ t('inspector.execution.chooseApproach') }}
437
444
  </UButton>
445
+ <!-- A coder step parked on undecided follow-up items: triage them (file /
446
+ send back / answer / dismiss) in the dedicated window, not a plain
447
+ approval — the generic approve resolver refuses this park. -->
448
+ <UButton
449
+ v-else-if="
450
+ s.approval &&
451
+ s.approval.status === 'pending' &&
452
+ dedicatedParkView(s) === 'follow-ups'
453
+ "
454
+ color="primary"
455
+ variant="soft"
456
+ size="xs"
457
+ icon="i-lucide-compass"
458
+ @click="openFollowUpsFor(i)"
459
+ >
460
+ {{ t('inspector.execution.triageFollowUps') }}
461
+ </UButton>
438
462
  <!-- A pr-reviewer step parked awaiting a finding selection: open the dedicated
439
463
  findings-selection window, not the generic approval gate. -->
440
464
  <UButton
@@ -10,6 +10,7 @@ import {
10
10
  isFailedStep,
11
11
  FAILED_STEP_META,
12
12
  containerPhaseLabel,
13
+ dedicatedParkView,
13
14
  } from '~/utils/pipelineRender'
14
15
  import { prReviewPhase } from '~/utils/prReviewProgress'
15
16
  import StepMetricsBar from '~/components/observability/StepMetricsBar.vue'
@@ -74,7 +75,9 @@ function followUpLabel(step: PipelineStep): string {
74
75
  /** The active fork-decision phase status on a coder step (proposing / awaiting a choice). */
75
76
  function forkPhase(step: PipelineStep): 'proposing' | 'awaiting_choice' | null {
76
77
  const status = step.forkDecision?.status
77
- return status === 'proposing' || status === 'awaiting_choice' ? status : null
78
+ if (status === 'proposing') return 'proposing'
79
+ // `answering` (a chat reply in flight) still belongs to the fork window's choice phase.
80
+ return status === 'awaiting_choice' || status === 'answering' ? 'awaiting_choice' : null
78
81
  }
79
82
 
80
83
  /**
@@ -670,9 +673,16 @@ const ITEM_ICON: Record<string, string> = {
670
673
  {{ reviewStageLabel(s.agentKind) }}
671
674
  </div>
672
675
 
673
- <!-- approval gate: review (and edit) the proposal before continuing -->
676
+ <!-- approval gate: review (and edit) the proposal before continuing. A park a
677
+ dedicated window owns (fork choice / follow-up triage) renders its own chip
678
+ above instead — the generic approve resolver refuses those server-side. -->
674
679
  <div
675
- v-else-if="s.approval && s.approval.status === 'pending' && !prReviewAwaiting(s)"
680
+ v-else-if="
681
+ s.approval &&
682
+ s.approval.status === 'pending' &&
683
+ !prReviewAwaiting(s) &&
684
+ !dedicatedParkView(s)
685
+ "
676
686
  class="mt-3"
677
687
  >
678
688
  <UButton
@@ -100,14 +100,15 @@ export function useStepApproval(opts: {
100
100
  () => !!feedback.value.trim() || reviewComments.value.length > 0,
101
101
  )
102
102
 
103
- // Plain approve: accept the agent's proposal verbatim and advance.
103
+ // Plain approve: accept the agent's proposal verbatim and advance. Every action below
104
+ // closes the overlay ONLY when the command actually ran — a server refusal (surfaced as
105
+ // a toast by the store) or a cancelled credential prompt keeps the review open.
104
106
  async function approve() {
105
107
  const id = opts.approvalId()
106
108
  if (!opts.instanceId() || !id || submitting.value) return
107
109
  submitting.value = true
108
110
  try {
109
- await execution.approveStep(opts.instanceId()!, id)
110
- opts.close()
111
+ if (await execution.approveStep(opts.instanceId()!, id)) opts.close()
111
112
  } finally {
112
113
  submitting.value = false
113
114
  }
@@ -130,8 +131,7 @@ export function useStepApproval(opts: {
130
131
  if (!opts.instanceId() || !id || submitting.value) return
131
132
  submitting.value = true
132
133
  try {
133
- await execution.approveStep(opts.instanceId()!, id, draftProposal.value)
134
- opts.close()
134
+ if (await execution.approveStep(opts.instanceId()!, id, draftProposal.value)) opts.close()
135
135
  } finally {
136
136
  submitting.value = false
137
137
  }
@@ -141,7 +141,7 @@ export function useStepApproval(opts: {
141
141
  if (!opts.instanceId() || !id || submitting.value || !canRequestChanges.value) return
142
142
  submitting.value = true
143
143
  try {
144
- await execution.requestStepChanges(opts.instanceId()!, id, {
144
+ const ok = await execution.requestStepChanges(opts.instanceId()!, id, {
145
145
  feedback: feedback.value.trim() || undefined,
146
146
  comments: reviewComments.value.length
147
147
  ? reviewComments.value.map((c) => ({
@@ -152,7 +152,7 @@ export function useStepApproval(opts: {
152
152
  }))
153
153
  : undefined,
154
154
  })
155
- opts.close()
155
+ if (ok) opts.close()
156
156
  } finally {
157
157
  submitting.value = false
158
158
  }
@@ -168,8 +168,9 @@ export function useStepApproval(opts: {
168
168
  if (!opts.instanceId() || !id || submitting.value) return
169
169
  submitting.value = true
170
170
  try {
171
- await execution.rejectStep(opts.instanceId()!, id, feedback.value.trim() || undefined)
172
- opts.close()
171
+ if (await execution.rejectStep(opts.instanceId()!, id, feedback.value.trim() || undefined)) {
172
+ opts.close()
173
+ }
173
174
  } finally {
174
175
  submitting.value = false
175
176
  rejectArmed.value = false
@@ -69,35 +69,66 @@ export function createExecutionCommands(ctx: ExecutionCommandContext) {
69
69
  })
70
70
  }
71
71
 
72
- /** Approve a step's gated proposal (optionally edited); the run advances. */
73
- async function approveStep(instanceId: string, approvalId: string, proposal?: string) {
72
+ /**
73
+ * Approve a step's gated proposal (optionally edited); the run advances. Returns false —
74
+ * with the failure surfaced as an actionable toast — when the server refused (e.g. a 409
75
+ * for a park a dedicated window owns) or the user cancelled the credential prompt, so the
76
+ * approval rail can stay open instead of closing over a silently-failed approve.
77
+ */
78
+ async function approveStep(
79
+ instanceId: string,
80
+ approvalId: string,
81
+ proposal?: string,
82
+ ): Promise<boolean> {
74
83
  const ws = useWorkspaceStore()
75
84
  const personal = usePersonalSubscriptionsStore()
76
- return await personal.withCredential(async (password) => {
77
- await api.approveStep(ws.requireId(), instanceId, approvalId, { proposal }, password)
78
- await ws.refresh()
79
- })
85
+ try {
86
+ return await personal.withCredential(async (password) => {
87
+ await api.approveStep(ws.requireId(), instanceId, approvalId, { proposal }, password)
88
+ await ws.refresh()
89
+ })
90
+ } catch (e) {
91
+ runErrors.present(e, 'errors.action.approveFailed')
92
+ return false
93
+ }
80
94
  }
81
95
 
82
- /** Request changes on a gated proposal; the step re-runs with the review. */
96
+ /** Request changes on a gated proposal; the step re-runs with the review. Returns false
97
+ * (with a toast) on refusal / a cancelled credential prompt, like {@link approveStep}. */
83
98
  async function requestStepChanges(
84
99
  instanceId: string,
85
100
  approvalId: string,
86
101
  review: RequestStepChangesInput,
87
- ) {
102
+ ): Promise<boolean> {
88
103
  const ws = useWorkspaceStore()
89
104
  const personal = usePersonalSubscriptionsStore()
90
- return await personal.withCredential(async (password) => {
91
- await api.requestStepChanges(ws.requireId(), instanceId, approvalId, review, password)
92
- await ws.refresh()
93
- })
105
+ try {
106
+ return await personal.withCredential(async (password) => {
107
+ await api.requestStepChanges(ws.requireId(), instanceId, approvalId, review, password)
108
+ await ws.refresh()
109
+ })
110
+ } catch (e) {
111
+ runErrors.present(e, 'errors.action.requestChangesFailed')
112
+ return false
113
+ }
94
114
  }
95
115
 
96
- /** Reject a gated proposal; the run stops entirely (a retryable failure). */
97
- async function rejectStep(instanceId: string, approvalId: string, reason?: string) {
116
+ /** Reject a gated proposal; the run stops entirely (a retryable failure). Returns false
117
+ * (with a toast) when the server refused. */
118
+ async function rejectStep(
119
+ instanceId: string,
120
+ approvalId: string,
121
+ reason?: string,
122
+ ): Promise<boolean> {
98
123
  const ws = useWorkspaceStore()
99
- await api.rejectStep(ws.requireId(), instanceId, approvalId, { reason })
100
- await ws.refresh()
124
+ try {
125
+ await api.rejectStep(ws.requireId(), instanceId, approvalId, { reason })
126
+ await ws.refresh()
127
+ return true
128
+ } catch (e) {
129
+ runErrors.present(e, 'errors.action.rejectFailed')
130
+ return false
131
+ }
101
132
  }
102
133
 
103
134
  /**
@@ -1,6 +1,7 @@
1
1
  import { ref } from 'vue'
2
2
  import { useExecutionStore } from '~/stores/execution'
3
3
  import { agentKindMeta } from '~/utils/catalog'
4
+ import { dedicatedParkView } from '~/utils/pipelineRender'
4
5
 
5
6
  /**
6
7
  * The step-inspection / result-view slice of the UI store: the dedicated result-view overlay
@@ -73,12 +74,15 @@ export function createUiResultViews() {
73
74
  // A step carrying a PR deep-review parks with BOTH a pending approval and
74
75
  // `prReview.status`, so the generic approval button funnels here; route it to the
75
76
  // findings-selection window regardless of catalog/manifest state (mirrors consensus).
77
+ // Likewise a coder parked on the implementation-fork choice or on undecided follow-up
78
+ // items: those parks ride `step.approval` too, but the generic approve resolver refuses
79
+ // them server-side, so the step must open the window that CAN resolve it.
76
80
  const view = step?.consensus?.enabled
77
81
  ? 'consensus-session'
78
82
  : step?.prReview
79
83
  ? 'pr-review'
80
84
  : step
81
- ? agentKindMeta(step.agentKind).resultView
85
+ ? (dedicatedParkView(step) ?? agentKindMeta(step.agentKind).resultView)
82
86
  : undefined
83
87
  if (view && instance) {
84
88
  // The brainstorm window is shared by both stages; carry which one from the step's kind.
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import type { PipelineStep } from '~/types/execution'
3
+ import { dedicatedParkView } from './pipelineRender'
4
+
5
+ /** A minimal coder step; the predicate only reads approval/followUps/forkDecision. */
6
+ const step = (over: Partial<PipelineStep>): PipelineStep =>
7
+ ({
8
+ agentKind: 'coder',
9
+ state: 'waiting_decision',
10
+ approval: { id: 'ap_1', status: 'pending', proposal: '' },
11
+ ...over,
12
+ }) as PipelineStep
13
+
14
+ const followUps = (statuses: string[]) => ({
15
+ enabled: true,
16
+ items: statuses.map((status, i) => ({
17
+ id: `fu_${i}`,
18
+ kind: 'follow_up',
19
+ title: 't',
20
+ detail: '',
21
+ status,
22
+ createdAt: 0,
23
+ updatedAt: 0,
24
+ })),
25
+ loops: 0,
26
+ })
27
+
28
+ describe('dedicatedParkView', () => {
29
+ // The regression this pins: a coder parked on the follow-up gate (or the fork choice)
30
+ // carries a pending `step.approval`, but the generic approve resolver 409s on it — the
31
+ // surfaces must route these parks to their dedicated window, never the "Approve &
32
+ // proceed" rail.
33
+ it('owns a follow-up park (pending approval + undecided items)', () => {
34
+ expect(
35
+ dedicatedParkView(step({ followUps: followUps(['pending', 'answered']) as never })),
36
+ ).toBe('follow-ups')
37
+ })
38
+
39
+ it('does not claim a step whose follow-up items are all decided', () => {
40
+ expect(
41
+ dedicatedParkView(step({ followUps: followUps(['answered', 'dismissed']) as never })),
42
+ ).toBeNull()
43
+ })
44
+
45
+ it('does not claim a WORKING coder that is still streaming items (no approval raised)', () => {
46
+ // Clicking a live step must keep opening the ordinary detail (progress + output).
47
+ expect(
48
+ dedicatedParkView(
49
+ step({ state: 'working', approval: null, followUps: followUps(['pending']) as never }),
50
+ ),
51
+ ).toBeNull()
52
+ })
53
+
54
+ it('owns the fork park while awaiting a choice, and while a chat reply is in flight', () => {
55
+ expect(dedicatedParkView(step({ forkDecision: { status: 'awaiting_choice' } as never }))).toBe(
56
+ 'fork-decision',
57
+ )
58
+ expect(dedicatedParkView(step({ forkDecision: { status: 'answering' } as never }))).toBe(
59
+ 'fork-decision',
60
+ )
61
+ })
62
+
63
+ it('releases the step once the fork is resolved (chosen / single_path / skipped)', () => {
64
+ for (const status of ['chosen', 'single_path', 'skipped', 'proposing']) {
65
+ expect(dedicatedParkView(step({ forkDecision: { status } as never }))).toBeNull()
66
+ }
67
+ })
68
+
69
+ it('leaves a plain approval park to the generic rail', () => {
70
+ expect(dedicatedParkView(step({}))).toBeNull()
71
+ })
72
+ })
@@ -133,6 +133,32 @@ export function isCompanionKind(kind: string): boolean {
133
133
  )
134
134
  }
135
135
 
136
+ /**
137
+ * The dedicated window that owns a step's approval park, when the park is NOT a generic
138
+ * prose approval: the implementation-fork window while a coder waits on (or chats about)
139
+ * an approach choice, or the follow-up triage window while surfaced items are undecided.
140
+ * The generic approve/request-changes/reject resolvers deliberately refuse these parks
141
+ * server-side (`assertNotIterativeGate`), so every surface that offers a step's pending
142
+ * approval must route these to their window instead of the generic "Approve & proceed"
143
+ * rail — which would blink a 409 and resolve nothing.
144
+ */
145
+ export function dedicatedParkView(step: PipelineStep): 'follow-ups' | 'fork-decision' | null {
146
+ // The fork park sits BEFORE the coder's build dispatch; `answering` (a chat turn in
147
+ // flight) still belongs to the fork window, which renders the pending reply.
148
+ const fork = step.forkDecision?.status
149
+ if (fork === 'awaiting_choice' || fork === 'answering') return 'fork-decision'
150
+ // Follow-ups only own the park itself: while the coder is still WORKING (streaming
151
+ // items, no approval raised) a step click should keep opening the ordinary detail.
152
+ if (
153
+ step.approval?.status === 'pending' &&
154
+ step.followUps?.enabled &&
155
+ step.followUps.items.some((i) => i.status === 'pending')
156
+ ) {
157
+ return 'follow-ups'
158
+ }
159
+ return null
160
+ }
161
+
136
162
  /**
137
163
  * The friendly label for a container's live phase (clone → "Preparing workspace",
138
164
  * agent → "Agent running", …), falling back to the raw phase string for an unknown/new
@@ -1298,6 +1298,7 @@
1298
1298
  "open": "Öffnen",
1299
1299
  "mergePr": "PR mergen",
1300
1300
  "chooseApproach": "Ansatz wählen",
1301
+ "triageFollowUps": "Folgeaufgaben entscheiden",
1301
1302
  "elapsedTooltip": "Verstrichene Zeit für diesen Schritt",
1302
1303
  "reviewFindings": "Befunde prüfen"
1303
1304
  },
@@ -1531,6 +1532,10 @@
1531
1532
  "details": "Details",
1532
1533
  "approvalRequired": "Freigabe erforderlich",
1533
1534
  "decisionRequired": "Entscheidung erforderlich",
1535
+ "followUpsParked": "Der Coder hat Folgeaufgaben aufgeworfen, die entschieden werden müssen, bevor der Lauf fortgesetzt werden kann. Bearbeite jeden Punkt (anlegen, zurückgeben, beantworten oder verwerfen) im Folgeaufgaben-Fenster.",
1536
+ "forkParked": "Dieser Coder-Schritt wartet darauf, dass ein Implementierungsansatz gewählt wird, bevor Code geschrieben wird. Wähle einen vorgeschlagenen Ansatz (oder gib einen eigenen ein) im Entscheidungsfenster.",
1537
+ "openFollowUps": "Folgeaufgaben öffnen",
1538
+ "chooseApproach": "Ansatz wählen",
1534
1539
  "expandAll": "Alle Abschnitte ausklappen",
1535
1540
  "collapseAll": "Alle Abschnitte einklappen",
1536
1541
  "copyRawOutput": "Rohausgabe kopieren",
@@ -4231,7 +4236,10 @@
4231
4236
  "retryFailed": "Wiederholung fehlgeschlagen",
4232
4237
  "startFailed": "Start fehlgeschlagen",
4233
4238
  "mergeFailed": "Zusammenführen fehlgeschlagen",
4234
- "restartFailed": "Neustart fehlgeschlagen"
4239
+ "restartFailed": "Neustart fehlgeschlagen",
4240
+ "approveFailed": "Freigeben fehlgeschlagen",
4241
+ "requestChangesFailed": "Anfordern von Änderungen fehlgeschlagen",
4242
+ "rejectFailed": "Ablehnen fehlgeschlagen"
4235
4243
  },
4236
4244
  "reviewFriction": {
4237
4245
  "warnTitle": "Aufgaben warten auf Prüfung",
@@ -492,7 +492,10 @@
492
492
  "retryFailed": "Retry failed",
493
493
  "startFailed": "Failed to start",
494
494
  "mergeFailed": "Failed to merge",
495
- "restartFailed": "Failed to restart"
495
+ "restartFailed": "Failed to restart",
496
+ "approveFailed": "Failed to approve",
497
+ "requestChangesFailed": "Failed to request changes",
498
+ "rejectFailed": "Failed to reject"
496
499
  },
497
500
  "reviewFriction": {
498
501
  "warnTitle": "Tasks waiting on review",
@@ -1040,6 +1043,7 @@
1040
1043
  "open": "Open",
1041
1044
  "mergePr": "Merge PR",
1042
1045
  "chooseApproach": "Choose approach",
1046
+ "triageFollowUps": "Decide follow-ups",
1043
1047
  "elapsedTooltip": "Elapsed time on this step",
1044
1048
  "reviewFindings": "Review findings"
1045
1049
  },
@@ -1273,6 +1277,10 @@
1273
1277
  "details": "Details",
1274
1278
  "approvalRequired": "Approval required",
1275
1279
  "decisionRequired": "Decision required",
1280
+ "followUpsParked": "The Coder surfaced follow-up items that need a decision before the run can continue. Triage each item (file, send back, answer, or dismiss) in the follow-ups window.",
1281
+ "forkParked": "This Coder step is waiting for an implementation approach to be chosen before any code is written. Pick a proposed approach (or enter your own) in the fork-decision window.",
1282
+ "openFollowUps": "Open follow-ups",
1283
+ "chooseApproach": "Choose approach",
1276
1284
  "expandAll": "Expand all sections",
1277
1285
  "collapseAll": "Collapse all sections",
1278
1286
  "copyRawOutput": "Copy raw output",
@@ -456,7 +456,10 @@
456
456
  "retryFailed": "El reintento falló",
457
457
  "startFailed": "No se pudo iniciar",
458
458
  "mergeFailed": "No se pudo fusionar",
459
- "restartFailed": "No se pudo reiniciar"
459
+ "restartFailed": "No se pudo reiniciar",
460
+ "approveFailed": "No se pudo aprobar",
461
+ "requestChangesFailed": "No se pudieron solicitar cambios",
462
+ "rejectFailed": "No se pudo rechazar"
460
463
  },
461
464
  "reviewFriction": {
462
465
  "warnTitle": "Tareas esperando revisión",
@@ -980,6 +983,7 @@
980
983
  "body": "Inicia un pipeline para ver el historial de ejecución aquí."
981
984
  },
982
985
  "chooseApproach": "Elegir enfoque",
986
+ "triageFollowUps": "Decidir seguimientos",
983
987
  "elapsedTooltip": "Tiempo transcurrido en este paso",
984
988
  "reviewFindings": "Revisar hallazgos"
985
989
  },
@@ -1213,6 +1217,10 @@
1213
1217
  "details": "Detalles",
1214
1218
  "approvalRequired": "Se requiere aprobación",
1215
1219
  "decisionRequired": "Se requiere una decisión",
1220
+ "followUpsParked": "El Coder planteó seguimientos que necesitan una decisión antes de que la ejecución pueda continuar. Resuelve cada elemento (crear, devolver, responder o descartar) en la ventana de seguimientos.",
1221
+ "forkParked": "Este paso del Coder espera a que se elija un enfoque de implementación antes de escribir código. Elige un enfoque propuesto (o introduce el tuyo) en la ventana de decisión.",
1222
+ "openFollowUps": "Abrir seguimientos",
1223
+ "chooseApproach": "Elegir enfoque",
1216
1224
  "expandAll": "Expandir todas las secciones",
1217
1225
  "collapseAll": "Contraer todas las secciones",
1218
1226
  "copyRawOutput": "Copiar salida sin procesar",
@@ -456,7 +456,10 @@
456
456
  "retryFailed": "Échec de la nouvelle tentative",
457
457
  "startFailed": "Échec du démarrage",
458
458
  "mergeFailed": "Échec de la fusion",
459
- "restartFailed": "Échec du redémarrage"
459
+ "restartFailed": "Échec du redémarrage",
460
+ "approveFailed": "Échec de l'approbation",
461
+ "requestChangesFailed": "Échec de la demande de modifications",
462
+ "rejectFailed": "Échec du rejet"
460
463
  },
461
464
  "reviewFriction": {
462
465
  "warnTitle": "Tâches en attente de revue",
@@ -980,6 +983,7 @@
980
983
  "body": "Lancez un pipeline pour voir l'historique d'exécution ici."
981
984
  },
982
985
  "chooseApproach": "Choisir l'approche",
986
+ "triageFollowUps": "Trancher les suivis",
983
987
  "elapsedTooltip": "Temps écoulé sur cette étape",
984
988
  "reviewFindings": "Examiner les remarques"
985
989
  },
@@ -1213,6 +1217,10 @@
1213
1217
  "details": "Détails",
1214
1218
  "approvalRequired": "Approbation requise",
1215
1219
  "decisionRequired": "Décision requise",
1220
+ "followUpsParked": "Le Coder a soulevé des suivis qui doivent être tranchés avant que l'exécution puisse continuer. Traitez chaque élément (créer, renvoyer, répondre ou écarter) dans la fenêtre des suivis.",
1221
+ "forkParked": "Cette étape du Coder attend le choix d'une approche d'implémentation avant d'écrire du code. Choisissez une approche proposée (ou saisissez la vôtre) dans la fenêtre de décision.",
1222
+ "openFollowUps": "Ouvrir les suivis",
1223
+ "chooseApproach": "Choisir l'approche",
1216
1224
  "expandAll": "Développer toutes les sections",
1217
1225
  "collapseAll": "Réduire toutes les sections",
1218
1226
  "copyRawOutput": "Copier la sortie brute",
@@ -456,7 +456,10 @@
456
456
  "retryFailed": "הניסיון החוזר נכשל",
457
457
  "startFailed": "ההפעלה נכשלה",
458
458
  "mergeFailed": "המיזוג נכשל",
459
- "restartFailed": "ההפעלה מחדש נכשלה"
459
+ "restartFailed": "ההפעלה מחדש נכשלה",
460
+ "approveFailed": "האישור נכשל",
461
+ "requestChangesFailed": "בקשת השינויים נכשלה",
462
+ "rejectFailed": "הדחייה נכשלה"
460
463
  },
461
464
  "reviewFriction": {
462
465
  "warnTitle": "משימות ממתינות לסקירה",
@@ -980,6 +983,7 @@
980
983
  "body": "התחל pipeline כדי לראות כאן את היסטוריית ההרצות."
981
984
  },
982
985
  "chooseApproach": "בחר גישה",
986
+ "triageFollowUps": "הכרע בהמשכים",
983
987
  "elapsedTooltip": "הזמן שחלף בשלב זה",
984
988
  "reviewFindings": "עיין בממצאים"
985
989
  },
@@ -1213,6 +1217,10 @@
1213
1217
  "details": "פרטים",
1214
1218
  "approvalRequired": "נדרש אישור",
1215
1219
  "decisionRequired": "נדרשת החלטה",
1220
+ "followUpsParked": "ה-Coder העלה המשכים שדורשים הכרעה לפני שהריצה תוכל להמשיך. טפל בכל פריט (תייק, החזר, ענה או דחה) בחלון ההמשכים.",
1221
+ "forkParked": "שלב ה-Coder הזה ממתין לבחירת גישת מימוש לפני כתיבת קוד. בחר גישה מוצעת (או הזן גישה משלך) בחלון ההחלטה.",
1222
+ "openFollowUps": "פתח את ההמשכים",
1223
+ "chooseApproach": "בחר גישה",
1216
1224
  "expandAll": "הרחב את כל המקטעים",
1217
1225
  "collapseAll": "כווץ את כל המקטעים",
1218
1226
  "copyRawOutput": "העתק פלט גולמי",
@@ -1298,6 +1298,7 @@
1298
1298
  "open": "Aperta",
1299
1299
  "mergePr": "Unisci PR",
1300
1300
  "chooseApproach": "Scegli approccio",
1301
+ "triageFollowUps": "Decidi i follow-up",
1301
1302
  "elapsedTooltip": "Tempo trascorso su questo passaggio",
1302
1303
  "reviewFindings": "Esamina i rilievi"
1303
1304
  },
@@ -1531,6 +1532,10 @@
1531
1532
  "details": "Dettagli",
1532
1533
  "approvalRequired": "Approvazione richiesta",
1533
1534
  "decisionRequired": "Decisione richiesta",
1535
+ "followUpsParked": "Il Coder ha sollevato follow-up che richiedono una decisione prima che la run possa continuare. Gestisci ogni elemento (crea, rimanda indietro, rispondi o scarta) nella finestra dei follow-up.",
1536
+ "forkParked": "Questo passo del Coder attende la scelta di un approccio di implementazione prima di scrivere codice. Scegli un approccio proposto (o inserisci il tuo) nella finestra di decisione.",
1537
+ "openFollowUps": "Apri i follow-up",
1538
+ "chooseApproach": "Scegli approccio",
1534
1539
  "expandAll": "Espandi tutte le sezioni",
1535
1540
  "collapseAll": "Comprimi tutte le sezioni",
1536
1541
  "copyRawOutput": "Copia l'output grezzo",
@@ -4231,7 +4236,10 @@
4231
4236
  "retryFailed": "Nuovo tentativo non riuscito",
4232
4237
  "startFailed": "Avvio non riuscito",
4233
4238
  "mergeFailed": "Merge non riuscito",
4234
- "restartFailed": "Riavvio non riuscito"
4239
+ "restartFailed": "Riavvio non riuscito",
4240
+ "approveFailed": "Approvazione non riuscita",
4241
+ "requestChangesFailed": "Richiesta di modifiche non riuscita",
4242
+ "rejectFailed": "Rifiuto non riuscito"
4235
4243
  },
4236
4244
  "reviewFriction": {
4237
4245
  "warnTitle": "Attività in attesa di revisione",
@@ -456,7 +456,10 @@
456
456
  "retryFailed": "再試行に失敗しました",
457
457
  "startFailed": "開始に失敗しました",
458
458
  "mergeFailed": "マージに失敗しました",
459
- "restartFailed": "再起動に失敗しました"
459
+ "restartFailed": "再起動に失敗しました",
460
+ "approveFailed": "承認に失敗しました",
461
+ "requestChangesFailed": "変更リクエストに失敗しました",
462
+ "rejectFailed": "却下に失敗しました"
460
463
  },
461
464
  "reviewFriction": {
462
465
  "warnTitle": "レビュー待ちのタスク",
@@ -980,6 +983,7 @@
980
983
  "body": "パイプラインを開始すると、ここに実行履歴が表示されます。"
981
984
  },
982
985
  "chooseApproach": "アプローチを選択",
986
+ "triageFollowUps": "フォローアップを決定",
983
987
  "elapsedTooltip": "このステップの経過時間",
984
988
  "reviewFindings": "指摘を確認"
985
989
  },
@@ -1213,6 +1217,10 @@
1213
1217
  "details": "詳細",
1214
1218
  "approvalRequired": "承認が必要",
1215
1219
  "decisionRequired": "判断が必要",
1220
+ "followUpsParked": "Coderが決定待ちのフォローアップ項目を提起しています。実行を続行するには、フォローアップウィンドウで各項目を処理してください(起票、差し戻し、回答、または却下)。",
1221
+ "forkParked": "このCoderステップは、コードを書く前に実装アプローチの選択を待っています。決定ウィンドウで提案されたアプローチを選ぶか、独自のアプローチを入力してください。",
1222
+ "openFollowUps": "フォローアップを開く",
1223
+ "chooseApproach": "アプローチを選択",
1216
1224
  "expandAll": "すべてのセクションを展開",
1217
1225
  "collapseAll": "すべてのセクションを折りたたむ",
1218
1226
  "copyRawOutput": "生の出力をコピー",
@@ -456,7 +456,10 @@
456
456
  "retryFailed": "Ponowienie nie powiodło się",
457
457
  "startFailed": "Nie udało się uruchomić",
458
458
  "mergeFailed": "Nie udało się scalić",
459
- "restartFailed": "Nie udało się ponownie uruchomić"
459
+ "restartFailed": "Nie udało się ponownie uruchomić",
460
+ "approveFailed": "Nie udało się zatwierdzić",
461
+ "requestChangesFailed": "Nie udało się poprosić o zmiany",
462
+ "rejectFailed": "Nie udało się odrzucić"
460
463
  },
461
464
  "reviewFriction": {
462
465
  "warnTitle": "Zadania czekające na przegląd",
@@ -980,6 +983,7 @@
980
983
  "body": "Uruchom pipeline, aby zobaczyć tutaj historię uruchomień."
981
984
  },
982
985
  "chooseApproach": "Wybierz podejście",
986
+ "triageFollowUps": "Rozstrzygnij działania następcze",
983
987
  "elapsedTooltip": "Czas, który upłynął na tym kroku",
984
988
  "reviewFindings": "Przejrzyj ustalenia"
985
989
  },
@@ -1213,6 +1217,10 @@
1213
1217
  "details": "Szczegóły",
1214
1218
  "approvalRequired": "Wymagane zatwierdzenie",
1215
1219
  "decisionRequired": "Wymagana decyzja",
1220
+ "followUpsParked": "Coder zgłosił działania następcze, które wymagają rozstrzygnięcia, zanim uruchomienie będzie mogło kontynuować. Rozstrzygnij każdy element (utwórz, odeślij, odpowiedz lub odrzuć) w oknie działań następczych.",
1221
+ "forkParked": "Ten krok Codera czeka na wybór podejścia do implementacji, zanim powstanie kod. Wybierz zaproponowane podejście (lub wpisz własne) w oknie decyzji.",
1222
+ "openFollowUps": "Otwórz działania następcze",
1223
+ "chooseApproach": "Wybierz podejście",
1216
1224
  "expandAll": "Rozwiń wszystkie sekcje",
1217
1225
  "collapseAll": "Zwiń wszystkie sekcje",
1218
1226
  "copyRawOutput": "Kopiuj surowy wynik",
@@ -456,7 +456,10 @@
456
456
  "retryFailed": "Yeniden deneme başarısız oldu",
457
457
  "startFailed": "Başlatılamadı",
458
458
  "mergeFailed": "Birleştirilemedi",
459
- "restartFailed": "Yeniden başlatılamadı"
459
+ "restartFailed": "Yeniden başlatılamadı",
460
+ "approveFailed": "Onaylanamadı",
461
+ "requestChangesFailed": "Değişiklik istenemedi",
462
+ "rejectFailed": "Reddedilemedi"
460
463
  },
461
464
  "reviewFriction": {
462
465
  "warnTitle": "Görevler incelemeyi bekliyor",
@@ -980,6 +983,7 @@
980
983
  "body": "Çalıştırma geçmişini burada görmek için bir pipeline başlatın."
981
984
  },
982
985
  "chooseApproach": "Yaklaşım seç",
986
+ "triageFollowUps": "Takipleri karara bağla",
983
987
  "elapsedTooltip": "Bu adımda geçen süre",
984
988
  "reviewFindings": "Bulguları incele"
985
989
  },
@@ -1213,6 +1217,10 @@
1213
1217
  "details": "Ayrıntılar",
1214
1218
  "approvalRequired": "Onay gerekli",
1215
1219
  "decisionRequired": "Karar gerekli",
1220
+ "followUpsParked": "Coder, çalıştırmanın devam edebilmesi için karara bağlanması gereken takipler ortaya çıkardı. Her maddeyi takipler penceresinde ele alın (kaydet, geri gönder, yanıtla veya yok say).",
1221
+ "forkParked": "Bu Coder adımı, kod yazılmadan önce bir uygulama yaklaşımının seçilmesini bekliyor. Karar penceresinde önerilen bir yaklaşımı seçin (veya kendi yaklaşımınızı girin).",
1222
+ "openFollowUps": "Takipleri aç",
1223
+ "chooseApproach": "Yaklaşım seç",
1216
1224
  "expandAll": "Tüm bölümleri genişlet",
1217
1225
  "collapseAll": "Tüm bölümleri daralt",
1218
1226
  "copyRawOutput": "Ham çıktıyı kopyala",
@@ -456,7 +456,10 @@
456
456
  "retryFailed": "Не вдалося повторити",
457
457
  "startFailed": "Не вдалося запустити",
458
458
  "mergeFailed": "Не вдалося злити",
459
- "restartFailed": "Не вдалося перезапустити"
459
+ "restartFailed": "Не вдалося перезапустити",
460
+ "approveFailed": "Не вдалося затвердити",
461
+ "requestChangesFailed": "Не вдалося запросити зміни",
462
+ "rejectFailed": "Не вдалося відхилити"
460
463
  },
461
464
  "reviewFriction": {
462
465
  "warnTitle": "Завдання чекають на рев’ю",
@@ -980,6 +983,7 @@
980
983
  "body": "Запустіть pipeline, щоб побачити тут історію запусків."
981
984
  },
982
985
  "chooseApproach": "Обрати підхід",
986
+ "triageFollowUps": "Вирішити подальші дії",
983
987
  "elapsedTooltip": "Час, що минув на цьому кроці",
984
988
  "reviewFindings": "Переглянути зауваження"
985
989
  },
@@ -1213,6 +1217,10 @@
1213
1217
  "details": "Деталі",
1214
1218
  "approvalRequired": "Потрібне затвердження",
1215
1219
  "decisionRequired": "Потрібне рішення",
1220
+ "followUpsParked": "Coder підняв подальші дії, які потребують рішення, перш ніж запуск зможе продовжитися. Опрацюйте кожен елемент (створіть, поверніть, дайте відповідь або відхиліть) у вікні подальших дій.",
1221
+ "forkParked": "Цей крок Coder чекає на вибір підходу до реалізації, перш ніж буде написано код. Виберіть запропонований підхід (або введіть власний) у вікні рішення.",
1222
+ "openFollowUps": "Відкрити подальші дії",
1223
+ "chooseApproach": "Вибрати підхід",
1216
1224
  "expandAll": "Розгорнути всі розділи",
1217
1225
  "collapseAll": "Згорнути всі розділи",
1218
1226
  "copyRawOutput": "Скопіювати сирий вивід",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.162.0",
3
+ "version": "0.162.1",
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",
@@ -40,7 +40,7 @@
40
40
  "valibot": "^1.4.2",
41
41
  "vue": "3.5.40",
42
42
  "wretch": "^3.0.9",
43
- "@cat-factory/contracts": "0.176.0"
43
+ "@cat-factory/contracts": "0.177.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@toad-contracts/testing": "0.3.2",