@cat-factory/app 0.179.0 → 0.181.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.
- package/README.md +4 -1
- package/app/components/documents/TaskContextDocs.vue +20 -4
- package/app/components/initiative/InitiativePlanningWindow.vue +149 -76
- package/app/components/initiative/InitiativeTrackerWindow.vue +408 -366
- package/app/components/tasks/BugHuntModal.vue +91 -9
- package/app/components/tasks/ContextIssuePicker.vue +1 -1
- package/app/components/tasks/TaskContextIssues.vue +22 -4
- package/app/composables/useResultViewRunMeta.spec.ts +74 -0
- package/app/composables/useResultViewRunMeta.ts +98 -0
- package/app/docs/consumer-extensions.md +11 -10
- package/app/modular/panels/inspector.logic.spec.ts +12 -5
- package/app/modular/panels/inspector.logic.ts +14 -5
- package/app/utils/initiative.spec.ts +81 -2
- package/app/utils/initiative.ts +32 -0
- package/app/{components/tasks/ContextIssuePicker.logic.spec.ts → utils/taskSources.spec.ts} +16 -5
- package/app/{components/tasks/ContextIssuePicker.logic.ts → utils/taskSources.ts} +13 -11
- package/i18n/locales/de.json +7 -1
- package/i18n/locales/en.json +7 -1
- package/i18n/locales/es.json +7 -1
- package/i18n/locales/fr.json +7 -1
- package/i18n/locales/he.json +7 -1
- package/i18n/locales/it.json +7 -1
- package/i18n/locales/ja.json +7 -1
- package/i18n/locales/pl.json +7 -1
- package/i18n/locales/tr.json +7 -1
- package/i18n/locales/uk.json +7 -1
- package/package.json +1 -1
|
@@ -19,19 +19,40 @@ import {
|
|
|
19
19
|
pendingCheckpointPhase,
|
|
20
20
|
} from '~/utils/initiative'
|
|
21
21
|
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
22
|
+
import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
22
23
|
|
|
23
24
|
const board = useBoardStore()
|
|
24
25
|
const initiatives = useInitiativesStore()
|
|
25
26
|
const { t } = useI18n()
|
|
26
27
|
const toast = useToast()
|
|
27
28
|
|
|
28
|
-
const { open, blockId, close } = useResultView('initiative-tracker', {
|
|
29
|
+
const { open, blockId, instanceId, stepIndex, close } = useResultView('initiative-tracker', {
|
|
29
30
|
onOpen: ({ blockId }) => void initiatives.load(blockId),
|
|
30
31
|
})
|
|
31
32
|
|
|
32
33
|
const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
|
|
33
34
|
const initiative = computed(() => (blockId.value ? initiatives.forBlock(blockId.value) : null))
|
|
34
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The planning run's step this window speaks for, plus its run details, resolved through the
|
|
38
|
+
* shared seam so the tracker reports the same "which run is this / how did the model do" facts as
|
|
39
|
+
* every other agent window. The window is registered for the analyst, the planner AND the
|
|
40
|
+
* (model-less) committer, and is reached from the board card / inspector as often as from the run
|
|
41
|
+
* timeline — `useResultViewRunMeta` owns picking the right step for both cases.
|
|
42
|
+
*/
|
|
43
|
+
const {
|
|
44
|
+
step: metaStep,
|
|
45
|
+
instanceId: runId,
|
|
46
|
+
position,
|
|
47
|
+
totalSteps,
|
|
48
|
+
runFailed,
|
|
49
|
+
failureAt,
|
|
50
|
+
} = useResultViewRunMeta('initiative-tracker', {
|
|
51
|
+
blockId: () => blockId.value,
|
|
52
|
+
instanceId: () => instanceId.value,
|
|
53
|
+
stepIndex: () => stepIndex.value,
|
|
54
|
+
})
|
|
55
|
+
|
|
35
56
|
const phases = computed(() => initiative.value?.phases ?? [])
|
|
36
57
|
function itemsOf(phaseId: string): InitiativeItem[] {
|
|
37
58
|
return (initiative.value?.items ?? []).filter((i) => i.phaseId === phaseId)
|
|
@@ -180,7 +201,7 @@ async function savePolicy() {
|
|
|
180
201
|
icon-class="bg-indigo-500/15 text-indigo-300"
|
|
181
202
|
:title="initiative?.title ?? block?.title ?? t('initiative.tracker.title')"
|
|
182
203
|
:subtitle="t('initiative.tracker.subtitle')"
|
|
183
|
-
width="
|
|
204
|
+
width="5xl"
|
|
184
205
|
testid="initiative-tracker-window"
|
|
185
206
|
@close="close"
|
|
186
207
|
>
|
|
@@ -201,396 +222,417 @@ async function savePolicy() {
|
|
|
201
222
|
</UBadge>
|
|
202
223
|
</template>
|
|
203
224
|
|
|
204
|
-
<div class="min-h-0 flex-1
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
<UIcon name="i-lucide-milestone" class="h-8 w-8 opacity-40" />
|
|
211
|
-
<p class="text-sm">{{ t('initiative.tracker.empty') }}</p>
|
|
212
|
-
</div>
|
|
213
|
-
|
|
214
|
-
<template v-else>
|
|
215
|
-
<!-- Paused at a phase checkpoint (D2): a completed checkpoint phase is awaiting
|
|
216
|
-
review before the next phase spawns. Read the phase's artifacts/PRs below,
|
|
217
|
-
then resume (continue) or cancel (stop) the initiative right here. -->
|
|
218
|
-
<section
|
|
219
|
-
v-if="pausedAtCheckpoint"
|
|
220
|
-
class="mb-4 rounded-lg border border-amber-500/40 bg-amber-500/10 p-3.5"
|
|
221
|
-
data-testid="initiative-checkpoint-pause"
|
|
225
|
+
<div class="flex min-h-0 flex-1">
|
|
226
|
+
<div class="min-w-0 flex-1 overflow-y-auto px-5 py-4">
|
|
227
|
+
<!-- No entity yet (module unwired / still creating) -->
|
|
228
|
+
<div
|
|
229
|
+
v-if="!initiative"
|
|
230
|
+
class="flex h-full flex-col items-center justify-center gap-2 text-center text-slate-400"
|
|
222
231
|
>
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
232
|
+
<UIcon name="i-lucide-milestone" class="h-8 w-8 opacity-40" />
|
|
233
|
+
<p class="text-sm">{{ t('initiative.tracker.empty') }}</p>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
<template v-else>
|
|
237
|
+
<!-- Paused at a phase checkpoint (D2): a completed checkpoint phase is awaiting
|
|
238
|
+
review before the next phase spawns. Read the phase's artifacts/PRs below,
|
|
239
|
+
then resume (continue) or cancel (stop) the initiative right here. -->
|
|
240
|
+
<section
|
|
241
|
+
v-if="pausedAtCheckpoint"
|
|
242
|
+
class="mb-4 rounded-lg border border-amber-500/40 bg-amber-500/10 p-3.5"
|
|
243
|
+
data-testid="initiative-checkpoint-pause"
|
|
244
|
+
>
|
|
245
|
+
<div class="flex items-start gap-2.5">
|
|
246
|
+
<UIcon name="i-lucide-pause-circle" class="mt-0.5 h-4 w-4 shrink-0 text-amber-300" />
|
|
247
|
+
<div class="min-w-0 flex-1">
|
|
248
|
+
<h3 class="text-[13px] font-semibold text-amber-200">
|
|
249
|
+
{{ t('initiative.checkpoint.pausedTitle') }}
|
|
250
|
+
</h3>
|
|
251
|
+
<p class="mt-0.5 text-[12px] leading-relaxed text-amber-100/80">
|
|
252
|
+
{{ t('initiative.checkpoint.pausedBody', { phase: checkpointPhase!.title }) }}
|
|
253
|
+
</p>
|
|
254
|
+
<div class="mt-2.5 flex flex-wrap gap-2">
|
|
255
|
+
<button
|
|
256
|
+
class="rounded bg-indigo-600 px-2.5 py-1 text-[11px] font-medium text-white hover:bg-indigo-500 disabled:opacity-50"
|
|
257
|
+
:disabled="initiatives.controlling"
|
|
258
|
+
data-testid="initiative-checkpoint-resume"
|
|
259
|
+
@click="checkpointControl('resume')"
|
|
260
|
+
>
|
|
261
|
+
{{ t('initiative.inspector.resume') }}
|
|
262
|
+
</button>
|
|
263
|
+
<button
|
|
264
|
+
class="rounded border border-rose-500/50 px-2.5 py-1 text-[11px] font-medium text-rose-300 hover:bg-rose-500/10 disabled:opacity-50"
|
|
265
|
+
:disabled="initiatives.controlling"
|
|
266
|
+
data-testid="initiative-checkpoint-cancel"
|
|
267
|
+
@click="checkpointControl('cancel')"
|
|
268
|
+
>
|
|
269
|
+
{{ t('initiative.inspector.cancel') }}
|
|
270
|
+
</button>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</section>
|
|
275
|
+
|
|
276
|
+
<!-- Goal & constraints -->
|
|
277
|
+
<section v-if="initiative.goal" class="mb-4">
|
|
278
|
+
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
279
|
+
{{ t('initiative.tracker.goal') }}
|
|
280
|
+
</h3>
|
|
281
|
+
<p class="whitespace-pre-wrap text-[13px] leading-relaxed text-slate-300">
|
|
282
|
+
{{ initiative.goal }}
|
|
283
|
+
</p>
|
|
284
|
+
</section>
|
|
285
|
+
<section v-if="initiative.constraints?.length" class="mb-4">
|
|
286
|
+
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
287
|
+
{{ t('initiative.tracker.constraints') }}
|
|
288
|
+
</h3>
|
|
289
|
+
<ul class="list-inside list-disc text-[13px] text-slate-300">
|
|
290
|
+
<li v-for="(c, i) in initiative.constraints" :key="i">{{ c }}</li>
|
|
291
|
+
</ul>
|
|
292
|
+
</section>
|
|
293
|
+
<section v-if="initiative.nonGoals?.length" class="mb-4">
|
|
294
|
+
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
295
|
+
{{ t('initiative.tracker.nonGoals') }}
|
|
296
|
+
</h3>
|
|
297
|
+
<ul class="list-inside list-disc text-[13px] text-slate-300">
|
|
298
|
+
<li v-for="(g, i) in initiative.nonGoals" :key="i">{{ g }}</li>
|
|
299
|
+
</ul>
|
|
300
|
+
</section>
|
|
301
|
+
<section v-if="initiative.analysisSummary" class="mb-4">
|
|
302
|
+
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
303
|
+
{{ t('initiative.tracker.analysis') }}
|
|
304
|
+
</h3>
|
|
305
|
+
<p class="whitespace-pre-wrap text-[13px] leading-relaxed text-slate-300">
|
|
306
|
+
{{ initiative.analysisSummary }}
|
|
307
|
+
</p>
|
|
308
|
+
</section>
|
|
309
|
+
|
|
310
|
+
<!-- Awaiting planning -->
|
|
311
|
+
<div
|
|
312
|
+
v-if="phases.length === 0"
|
|
313
|
+
class="mb-4 rounded-lg border border-dashed border-slate-700 p-4 text-center text-[12px] text-slate-400"
|
|
314
|
+
>
|
|
315
|
+
{{ t('initiative.tracker.noPlan') }}
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
<!-- Phases + items -->
|
|
319
|
+
<section v-for="phase in phases" :key="phase.id" class="mb-5">
|
|
320
|
+
<h3 class="mb-1 flex items-center gap-2 text-sm font-semibold text-slate-200">
|
|
321
|
+
<span>{{ t('initiative.tracker.phase', { title: phase.title }) }}</span>
|
|
322
|
+
<!-- Checkpoint annotation (D2): this phase pauses the initiative for human review
|
|
323
|
+
once its items settle. Cleared → already reviewed; the pending one → awaiting
|
|
324
|
+
review; otherwise an upcoming gate. -->
|
|
325
|
+
<UBadge
|
|
326
|
+
v-if="phase.checkpoint"
|
|
327
|
+
:color="
|
|
328
|
+
phase.checkpointClearedAt
|
|
329
|
+
? 'neutral'
|
|
330
|
+
: awaitingReviewPhaseId === phase.id
|
|
331
|
+
? 'warning'
|
|
332
|
+
: 'info'
|
|
333
|
+
"
|
|
334
|
+
variant="subtle"
|
|
335
|
+
size="sm"
|
|
336
|
+
:data-testid="`initiative-phase-checkpoint-${phase.id}`"
|
|
337
|
+
>
|
|
338
|
+
<UIcon name="i-lucide-flag" class="mr-1 h-3 w-3" />
|
|
339
|
+
{{
|
|
340
|
+
phase.checkpointClearedAt
|
|
341
|
+
? t('initiative.checkpoint.cleared')
|
|
342
|
+
: awaitingReviewPhaseId === phase.id
|
|
343
|
+
? t('initiative.checkpoint.awaiting')
|
|
344
|
+
: t('initiative.checkpoint.badge')
|
|
345
|
+
}}
|
|
346
|
+
</UBadge>
|
|
347
|
+
</h3>
|
|
348
|
+
<p v-if="phase.goal" class="mb-2 text-[12px] text-slate-400">{{ phase.goal }}</p>
|
|
349
|
+
<div class="overflow-x-auto rounded-lg border border-slate-800">
|
|
350
|
+
<table class="w-full text-[12px]">
|
|
351
|
+
<thead>
|
|
352
|
+
<tr class="border-b border-slate-800 text-left text-slate-500">
|
|
353
|
+
<th class="px-3 py-2 font-medium">{{ t('initiative.tracker.colItem') }}</th>
|
|
354
|
+
<th class="px-3 py-2 font-medium">{{ t('initiative.tracker.colStatus') }}</th>
|
|
355
|
+
<th class="px-3 py-2 font-medium">{{ t('initiative.tracker.colPr') }}</th>
|
|
356
|
+
</tr>
|
|
357
|
+
</thead>
|
|
358
|
+
<tbody>
|
|
359
|
+
<tr
|
|
360
|
+
v-for="item in itemsOf(phase.id)"
|
|
361
|
+
:key="item.id"
|
|
362
|
+
class="border-b border-slate-800/60 last:border-0"
|
|
363
|
+
>
|
|
364
|
+
<td class="px-3 py-2 align-top">
|
|
365
|
+
<div class="font-medium text-slate-200">{{ item.title }}</div>
|
|
366
|
+
<div v-if="item.dependsOn?.length" class="mt-0.5 text-[10px] text-slate-500">
|
|
367
|
+
{{
|
|
368
|
+
t('initiative.tracker.dependsOn', {
|
|
369
|
+
items: item.dependsOn.join(', '),
|
|
370
|
+
})
|
|
371
|
+
}}
|
|
372
|
+
</div>
|
|
373
|
+
<div v-if="item.note" class="mt-0.5 text-[10px] text-amber-300/80">
|
|
374
|
+
{{ item.note }}
|
|
375
|
+
</div>
|
|
376
|
+
<div
|
|
377
|
+
v-if="editable && (item.status === 'blocked' || item.status === 'pending')"
|
|
378
|
+
class="mt-1 flex gap-1.5"
|
|
379
|
+
>
|
|
380
|
+
<button
|
|
381
|
+
v-if="item.status === 'blocked'"
|
|
382
|
+
class="rounded border border-slate-700 px-1.5 py-0.5 text-[10px] text-slate-300 hover:bg-slate-800 disabled:opacity-50"
|
|
383
|
+
:disabled="initiatives.curating"
|
|
384
|
+
:data-testid="`initiative-item-retry-${item.id}`"
|
|
385
|
+
@click="itemAction(item, 'retry')"
|
|
386
|
+
>
|
|
387
|
+
{{ t('initiative.curation.retry') }}
|
|
388
|
+
</button>
|
|
389
|
+
<button
|
|
390
|
+
class="rounded border border-slate-700 px-1.5 py-0.5 text-[10px] text-slate-300 hover:bg-slate-800 disabled:opacity-50"
|
|
391
|
+
:disabled="initiatives.curating"
|
|
392
|
+
:data-testid="`initiative-item-skip-${item.id}`"
|
|
393
|
+
@click="itemAction(item, 'skip')"
|
|
394
|
+
>
|
|
395
|
+
{{ t('initiative.curation.skip') }}
|
|
396
|
+
</button>
|
|
397
|
+
</div>
|
|
398
|
+
</td>
|
|
399
|
+
<td class="px-3 py-2 align-top">
|
|
400
|
+
<UBadge
|
|
401
|
+
:color="INITIATIVE_ITEM_STATUS_CHIPS[item.status]"
|
|
402
|
+
variant="subtle"
|
|
403
|
+
size="sm"
|
|
404
|
+
>
|
|
405
|
+
{{ t(INITIATIVE_ITEM_STATUS_LABEL_KEYS[item.status]) }}
|
|
406
|
+
</UBadge>
|
|
407
|
+
</td>
|
|
408
|
+
<td class="px-3 py-2 align-top">
|
|
409
|
+
<a
|
|
410
|
+
v-if="item.pr"
|
|
411
|
+
:href="item.pr.url"
|
|
412
|
+
target="_blank"
|
|
413
|
+
rel="noopener"
|
|
414
|
+
class="text-sky-400 hover:underline"
|
|
415
|
+
>
|
|
416
|
+
{{ item.pr.number ? `#${item.pr.number}` : t('initiative.tracker.prLink') }}
|
|
417
|
+
</a>
|
|
418
|
+
<span v-else class="text-slate-600">—</span>
|
|
419
|
+
</td>
|
|
420
|
+
</tr>
|
|
421
|
+
</tbody>
|
|
422
|
+
</table>
|
|
423
|
+
</div>
|
|
424
|
+
</section>
|
|
425
|
+
|
|
426
|
+
<!-- Execution policy -->
|
|
427
|
+
<section v-if="initiative.policy" class="mb-4">
|
|
428
|
+
<div class="mb-1 flex items-center gap-2">
|
|
429
|
+
<h3 class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
430
|
+
{{ t('initiative.tracker.policy') }}
|
|
228
431
|
</h3>
|
|
229
|
-
<
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
432
|
+
<button
|
|
433
|
+
v-if="editable && !editingPolicy"
|
|
434
|
+
class="rounded border border-slate-700 px-1.5 py-0.5 text-[10px] text-slate-300 hover:bg-slate-800"
|
|
435
|
+
data-testid="initiative-policy-edit"
|
|
436
|
+
@click="startEditPolicy"
|
|
437
|
+
>
|
|
438
|
+
{{ t('initiative.curation.edit') }}
|
|
439
|
+
</button>
|
|
440
|
+
</div>
|
|
441
|
+
<ul v-if="!editingPolicy" class="text-[12px] text-slate-300">
|
|
442
|
+
<li>
|
|
443
|
+
{{
|
|
444
|
+
t('initiative.tracker.maxConcurrent', {
|
|
445
|
+
count: initiative.policy.maxConcurrent,
|
|
446
|
+
})
|
|
447
|
+
}}
|
|
448
|
+
</li>
|
|
449
|
+
<li v-for="(rule, i) in policyRules" :key="i">
|
|
450
|
+
<code class="text-sky-300">{{ rule.pipelineId }}</code>
|
|
451
|
+
· {{ ruleAxes(rule) }}
|
|
452
|
+
</li>
|
|
453
|
+
<li>
|
|
454
|
+
{{ t('initiative.tracker.defaultPipeline') }}
|
|
455
|
+
<code class="text-sky-300">{{ initiative.policy.defaultPipelineId }}</code>
|
|
456
|
+
</li>
|
|
457
|
+
</ul>
|
|
458
|
+
<!-- Edit form: the two scalar knobs; planner-authored rules are preserved. -->
|
|
459
|
+
<div v-else class="flex flex-col gap-2 rounded-lg border border-slate-800 p-3">
|
|
460
|
+
<label class="flex items-center gap-2 text-[12px] text-slate-300">
|
|
461
|
+
<span class="w-40">{{ t('initiative.curation.maxConcurrentField') }}</span>
|
|
462
|
+
<input
|
|
463
|
+
v-model.number="policyForm.maxConcurrent"
|
|
464
|
+
type="number"
|
|
465
|
+
min="1"
|
|
466
|
+
max="20"
|
|
467
|
+
class="w-20 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-slate-200"
|
|
468
|
+
data-testid="initiative-policy-max-concurrent"
|
|
469
|
+
/>
|
|
470
|
+
</label>
|
|
471
|
+
<label class="flex items-center gap-2 text-[12px] text-slate-300">
|
|
472
|
+
<span class="w-40">{{ t('initiative.curation.defaultPipelineField') }}</span>
|
|
473
|
+
<input
|
|
474
|
+
v-model="policyForm.defaultPipelineId"
|
|
475
|
+
type="text"
|
|
476
|
+
class="flex-1 rounded border border-slate-700 bg-slate-950 px-2 py-1 font-mono text-[11px] text-slate-200"
|
|
477
|
+
data-testid="initiative-policy-default-pipeline"
|
|
478
|
+
/>
|
|
479
|
+
</label>
|
|
480
|
+
<div class="flex gap-2">
|
|
233
481
|
<button
|
|
234
|
-
class="rounded bg-indigo-600 px-2
|
|
235
|
-
:disabled="initiatives.
|
|
236
|
-
data-testid="initiative-
|
|
237
|
-
@click="
|
|
482
|
+
class="rounded bg-indigo-600 px-2 py-1 text-[11px] text-white hover:bg-indigo-500 disabled:opacity-50"
|
|
483
|
+
:disabled="initiatives.curating"
|
|
484
|
+
data-testid="initiative-policy-save"
|
|
485
|
+
@click="savePolicy"
|
|
238
486
|
>
|
|
239
|
-
{{ t('initiative.
|
|
487
|
+
{{ t('initiative.curation.save') }}
|
|
240
488
|
</button>
|
|
241
489
|
<button
|
|
242
|
-
class="rounded border border-
|
|
243
|
-
|
|
244
|
-
data-testid="initiative-checkpoint-cancel"
|
|
245
|
-
@click="checkpointControl('cancel')"
|
|
490
|
+
class="rounded border border-slate-700 px-2 py-1 text-[11px] text-slate-300 hover:bg-slate-800"
|
|
491
|
+
@click="editingPolicy = false"
|
|
246
492
|
>
|
|
247
|
-
{{ t('initiative.
|
|
493
|
+
{{ t('initiative.curation.cancel') }}
|
|
248
494
|
</button>
|
|
249
495
|
</div>
|
|
250
496
|
</div>
|
|
251
|
-
</
|
|
252
|
-
</section>
|
|
253
|
-
|
|
254
|
-
<!-- Goal & constraints -->
|
|
255
|
-
<section v-if="initiative.goal" class="mb-4">
|
|
256
|
-
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
257
|
-
{{ t('initiative.tracker.goal') }}
|
|
258
|
-
</h3>
|
|
259
|
-
<p class="whitespace-pre-wrap text-[13px] leading-relaxed text-slate-300">
|
|
260
|
-
{{ initiative.goal }}
|
|
261
|
-
</p>
|
|
262
|
-
</section>
|
|
263
|
-
<section v-if="initiative.constraints?.length" class="mb-4">
|
|
264
|
-
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
265
|
-
{{ t('initiative.tracker.constraints') }}
|
|
266
|
-
</h3>
|
|
267
|
-
<ul class="list-inside list-disc text-[13px] text-slate-300">
|
|
268
|
-
<li v-for="(c, i) in initiative.constraints" :key="i">{{ c }}</li>
|
|
269
|
-
</ul>
|
|
270
|
-
</section>
|
|
271
|
-
<section v-if="initiative.nonGoals?.length" class="mb-4">
|
|
272
|
-
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
273
|
-
{{ t('initiative.tracker.nonGoals') }}
|
|
274
|
-
</h3>
|
|
275
|
-
<ul class="list-inside list-disc text-[13px] text-slate-300">
|
|
276
|
-
<li v-for="(g, i) in initiative.nonGoals" :key="i">{{ g }}</li>
|
|
277
|
-
</ul>
|
|
278
|
-
</section>
|
|
279
|
-
<section v-if="initiative.analysisSummary" class="mb-4">
|
|
280
|
-
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
281
|
-
{{ t('initiative.tracker.analysis') }}
|
|
282
|
-
</h3>
|
|
283
|
-
<p class="whitespace-pre-wrap text-[13px] leading-relaxed text-slate-300">
|
|
284
|
-
{{ initiative.analysisSummary }}
|
|
285
|
-
</p>
|
|
286
|
-
</section>
|
|
287
|
-
|
|
288
|
-
<!-- Awaiting planning -->
|
|
289
|
-
<div
|
|
290
|
-
v-if="phases.length === 0"
|
|
291
|
-
class="mb-4 rounded-lg border border-dashed border-slate-700 p-4 text-center text-[12px] text-slate-400"
|
|
292
|
-
>
|
|
293
|
-
{{ t('initiative.tracker.noPlan') }}
|
|
294
|
-
</div>
|
|
497
|
+
</section>
|
|
295
498
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
<
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
499
|
+
<!-- Logs -->
|
|
500
|
+
<section v-if="initiative.decisions?.length" class="mb-4">
|
|
501
|
+
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
502
|
+
{{ t('initiative.tracker.decisions') }}
|
|
503
|
+
</h3>
|
|
504
|
+
<ul class="list-inside list-disc text-[13px] text-slate-300">
|
|
505
|
+
<li v-for="d in initiative.decisions" :key="d.id">
|
|
506
|
+
<span class="font-medium">{{ d.title }}</span>
|
|
507
|
+
<span v-if="d.detail" class="text-slate-400"> — {{ d.detail }}</span>
|
|
508
|
+
</li>
|
|
509
|
+
</ul>
|
|
510
|
+
</section>
|
|
511
|
+
<section v-if="initiative.deviations?.length" class="mb-4">
|
|
512
|
+
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
513
|
+
{{ t('initiative.tracker.deviations') }}
|
|
514
|
+
</h3>
|
|
515
|
+
<ul class="list-inside list-disc text-[13px] text-slate-300">
|
|
516
|
+
<li v-for="d in initiative.deviations" :key="d.id">
|
|
517
|
+
<code v-if="d.itemId" class="text-slate-400">{{ d.itemId }}</code>
|
|
518
|
+
{{ d.description }}
|
|
519
|
+
<span v-if="d.resolution" class="text-slate-400"> → {{ d.resolution }}</span>
|
|
520
|
+
</li>
|
|
521
|
+
</ul>
|
|
522
|
+
</section>
|
|
523
|
+
<section v-if="initiative.followUps?.length" class="mb-4">
|
|
524
|
+
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
525
|
+
{{ t('initiative.tracker.followUps') }}
|
|
526
|
+
</h3>
|
|
527
|
+
<ul class="flex flex-col gap-2 text-[13px] text-slate-300">
|
|
528
|
+
<li
|
|
529
|
+
v-for="f in initiative.followUps"
|
|
530
|
+
:key="f.id"
|
|
531
|
+
class="rounded-lg border border-slate-800 p-2.5"
|
|
532
|
+
:data-testid="`initiative-followup-${f.id}`"
|
|
533
|
+
>
|
|
534
|
+
<div class="flex items-start gap-2">
|
|
535
|
+
<div class="min-w-0 flex-1">
|
|
536
|
+
<span class="font-medium">{{ f.title }}</span>
|
|
537
|
+
<span v-if="f.detail" class="text-slate-400"> — {{ f.detail }}</span>
|
|
538
|
+
</div>
|
|
539
|
+
<UBadge
|
|
540
|
+
:color="INITIATIVE_FOLLOWUP_STATUS_CHIPS[f.status]"
|
|
541
|
+
variant="subtle"
|
|
542
|
+
size="sm"
|
|
543
|
+
>
|
|
544
|
+
{{ t(INITIATIVE_FOLLOWUP_STATUS_LABEL_KEYS[f.status]) }}
|
|
545
|
+
</UBadge>
|
|
546
|
+
</div>
|
|
547
|
+
<!-- Triage actions for an open follow-up (only while executing) -->
|
|
548
|
+
<div v-if="editable && f.status === 'open'" class="mt-2">
|
|
549
|
+
<div v-if="promotingId === f.id" class="flex flex-col gap-2">
|
|
550
|
+
<label class="flex items-center gap-2 text-[12px]">
|
|
551
|
+
<span class="text-slate-400">{{ t('initiative.curation.phaseField') }}</span>
|
|
552
|
+
<select
|
|
553
|
+
v-model="promoteForm.phaseId"
|
|
554
|
+
class="flex-1 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-slate-200"
|
|
555
|
+
data-testid="initiative-promote-phase"
|
|
556
|
+
>
|
|
557
|
+
<option v-for="p in phases" :key="p.id" :value="p.id">
|
|
558
|
+
{{ p.title }}
|
|
559
|
+
</option>
|
|
560
|
+
</select>
|
|
561
|
+
</label>
|
|
562
|
+
<input
|
|
563
|
+
v-model="promoteForm.title"
|
|
564
|
+
type="text"
|
|
565
|
+
class="rounded border border-slate-700 bg-slate-950 px-2 py-1 text-[12px] text-slate-200"
|
|
566
|
+
:placeholder="t('initiative.curation.itemTitlePlaceholder')"
|
|
567
|
+
data-testid="initiative-promote-title"
|
|
568
|
+
/>
|
|
569
|
+
<div class="flex gap-2">
|
|
358
570
|
<button
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
@click="itemAction(item, 'retry')"
|
|
571
|
+
class="rounded bg-indigo-600 px-2 py-1 text-[11px] text-white hover:bg-indigo-500 disabled:opacity-50"
|
|
572
|
+
:disabled="initiatives.curating || !promoteForm.phaseId"
|
|
573
|
+
data-testid="initiative-promote-submit"
|
|
574
|
+
@click="submitPromote(f)"
|
|
364
575
|
>
|
|
365
|
-
{{ t('initiative.curation.
|
|
576
|
+
{{ t('initiative.curation.promoteConfirm') }}
|
|
366
577
|
</button>
|
|
367
578
|
<button
|
|
368
|
-
class="rounded border border-slate-700 px-
|
|
369
|
-
|
|
370
|
-
:data-testid="`initiative-item-skip-${item.id}`"
|
|
371
|
-
@click="itemAction(item, 'skip')"
|
|
579
|
+
class="rounded border border-slate-700 px-2 py-1 text-[11px] text-slate-300 hover:bg-slate-800"
|
|
580
|
+
@click="promotingId = null"
|
|
372
581
|
>
|
|
373
|
-
{{ t('initiative.curation.
|
|
582
|
+
{{ t('initiative.curation.cancel') }}
|
|
374
583
|
</button>
|
|
375
584
|
</div>
|
|
376
|
-
</
|
|
377
|
-
<
|
|
378
|
-
<UBadge
|
|
379
|
-
:color="INITIATIVE_ITEM_STATUS_CHIPS[item.status]"
|
|
380
|
-
variant="subtle"
|
|
381
|
-
size="sm"
|
|
382
|
-
>
|
|
383
|
-
{{ t(INITIATIVE_ITEM_STATUS_LABEL_KEYS[item.status]) }}
|
|
384
|
-
</UBadge>
|
|
385
|
-
</td>
|
|
386
|
-
<td class="px-3 py-2 align-top">
|
|
387
|
-
<a
|
|
388
|
-
v-if="item.pr"
|
|
389
|
-
:href="item.pr.url"
|
|
390
|
-
target="_blank"
|
|
391
|
-
rel="noopener"
|
|
392
|
-
class="text-sky-400 hover:underline"
|
|
393
|
-
>
|
|
394
|
-
{{ item.pr.number ? `#${item.pr.number}` : t('initiative.tracker.prLink') }}
|
|
395
|
-
</a>
|
|
396
|
-
<span v-else class="text-slate-600">—</span>
|
|
397
|
-
</td>
|
|
398
|
-
</tr>
|
|
399
|
-
</tbody>
|
|
400
|
-
</table>
|
|
401
|
-
</div>
|
|
402
|
-
</section>
|
|
403
|
-
|
|
404
|
-
<!-- Execution policy -->
|
|
405
|
-
<section v-if="initiative.policy" class="mb-4">
|
|
406
|
-
<div class="mb-1 flex items-center gap-2">
|
|
407
|
-
<h3 class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
408
|
-
{{ t('initiative.tracker.policy') }}
|
|
409
|
-
</h3>
|
|
410
|
-
<button
|
|
411
|
-
v-if="editable && !editingPolicy"
|
|
412
|
-
class="rounded border border-slate-700 px-1.5 py-0.5 text-[10px] text-slate-300 hover:bg-slate-800"
|
|
413
|
-
data-testid="initiative-policy-edit"
|
|
414
|
-
@click="startEditPolicy"
|
|
415
|
-
>
|
|
416
|
-
{{ t('initiative.curation.edit') }}
|
|
417
|
-
</button>
|
|
418
|
-
</div>
|
|
419
|
-
<ul v-if="!editingPolicy" class="text-[12px] text-slate-300">
|
|
420
|
-
<li>
|
|
421
|
-
{{
|
|
422
|
-
t('initiative.tracker.maxConcurrent', {
|
|
423
|
-
count: initiative.policy.maxConcurrent,
|
|
424
|
-
})
|
|
425
|
-
}}
|
|
426
|
-
</li>
|
|
427
|
-
<li v-for="(rule, i) in policyRules" :key="i">
|
|
428
|
-
<code class="text-sky-300">{{ rule.pipelineId }}</code>
|
|
429
|
-
· {{ ruleAxes(rule) }}
|
|
430
|
-
</li>
|
|
431
|
-
<li>
|
|
432
|
-
{{ t('initiative.tracker.defaultPipeline') }}
|
|
433
|
-
<code class="text-sky-300">{{ initiative.policy.defaultPipelineId }}</code>
|
|
434
|
-
</li>
|
|
435
|
-
</ul>
|
|
436
|
-
<!-- Edit form: the two scalar knobs; planner-authored rules are preserved. -->
|
|
437
|
-
<div v-else class="flex flex-col gap-2 rounded-lg border border-slate-800 p-3">
|
|
438
|
-
<label class="flex items-center gap-2 text-[12px] text-slate-300">
|
|
439
|
-
<span class="w-40">{{ t('initiative.curation.maxConcurrentField') }}</span>
|
|
440
|
-
<input
|
|
441
|
-
v-model.number="policyForm.maxConcurrent"
|
|
442
|
-
type="number"
|
|
443
|
-
min="1"
|
|
444
|
-
max="20"
|
|
445
|
-
class="w-20 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-slate-200"
|
|
446
|
-
data-testid="initiative-policy-max-concurrent"
|
|
447
|
-
/>
|
|
448
|
-
</label>
|
|
449
|
-
<label class="flex items-center gap-2 text-[12px] text-slate-300">
|
|
450
|
-
<span class="w-40">{{ t('initiative.curation.defaultPipelineField') }}</span>
|
|
451
|
-
<input
|
|
452
|
-
v-model="policyForm.defaultPipelineId"
|
|
453
|
-
type="text"
|
|
454
|
-
class="flex-1 rounded border border-slate-700 bg-slate-950 px-2 py-1 font-mono text-[11px] text-slate-200"
|
|
455
|
-
data-testid="initiative-policy-default-pipeline"
|
|
456
|
-
/>
|
|
457
|
-
</label>
|
|
458
|
-
<div class="flex gap-2">
|
|
459
|
-
<button
|
|
460
|
-
class="rounded bg-indigo-600 px-2 py-1 text-[11px] text-white hover:bg-indigo-500 disabled:opacity-50"
|
|
461
|
-
:disabled="initiatives.curating"
|
|
462
|
-
data-testid="initiative-policy-save"
|
|
463
|
-
@click="savePolicy"
|
|
464
|
-
>
|
|
465
|
-
{{ t('initiative.curation.save') }}
|
|
466
|
-
</button>
|
|
467
|
-
<button
|
|
468
|
-
class="rounded border border-slate-700 px-2 py-1 text-[11px] text-slate-300 hover:bg-slate-800"
|
|
469
|
-
@click="editingPolicy = false"
|
|
470
|
-
>
|
|
471
|
-
{{ t('initiative.curation.cancel') }}
|
|
472
|
-
</button>
|
|
473
|
-
</div>
|
|
474
|
-
</div>
|
|
475
|
-
</section>
|
|
476
|
-
|
|
477
|
-
<!-- Logs -->
|
|
478
|
-
<section v-if="initiative.decisions?.length" class="mb-4">
|
|
479
|
-
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
480
|
-
{{ t('initiative.tracker.decisions') }}
|
|
481
|
-
</h3>
|
|
482
|
-
<ul class="list-inside list-disc text-[13px] text-slate-300">
|
|
483
|
-
<li v-for="d in initiative.decisions" :key="d.id">
|
|
484
|
-
<span class="font-medium">{{ d.title }}</span>
|
|
485
|
-
<span v-if="d.detail" class="text-slate-400"> — {{ d.detail }}</span>
|
|
486
|
-
</li>
|
|
487
|
-
</ul>
|
|
488
|
-
</section>
|
|
489
|
-
<section v-if="initiative.deviations?.length" class="mb-4">
|
|
490
|
-
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
491
|
-
{{ t('initiative.tracker.deviations') }}
|
|
492
|
-
</h3>
|
|
493
|
-
<ul class="list-inside list-disc text-[13px] text-slate-300">
|
|
494
|
-
<li v-for="d in initiative.deviations" :key="d.id">
|
|
495
|
-
<code v-if="d.itemId" class="text-slate-400">{{ d.itemId }}</code>
|
|
496
|
-
{{ d.description }}
|
|
497
|
-
<span v-if="d.resolution" class="text-slate-400"> → {{ d.resolution }}</span>
|
|
498
|
-
</li>
|
|
499
|
-
</ul>
|
|
500
|
-
</section>
|
|
501
|
-
<section v-if="initiative.followUps?.length" class="mb-4">
|
|
502
|
-
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
503
|
-
{{ t('initiative.tracker.followUps') }}
|
|
504
|
-
</h3>
|
|
505
|
-
<ul class="flex flex-col gap-2 text-[13px] text-slate-300">
|
|
506
|
-
<li
|
|
507
|
-
v-for="f in initiative.followUps"
|
|
508
|
-
:key="f.id"
|
|
509
|
-
class="rounded-lg border border-slate-800 p-2.5"
|
|
510
|
-
:data-testid="`initiative-followup-${f.id}`"
|
|
511
|
-
>
|
|
512
|
-
<div class="flex items-start gap-2">
|
|
513
|
-
<div class="min-w-0 flex-1">
|
|
514
|
-
<span class="font-medium">{{ f.title }}</span>
|
|
515
|
-
<span v-if="f.detail" class="text-slate-400"> — {{ f.detail }}</span>
|
|
516
|
-
</div>
|
|
517
|
-
<UBadge
|
|
518
|
-
:color="INITIATIVE_FOLLOWUP_STATUS_CHIPS[f.status]"
|
|
519
|
-
variant="subtle"
|
|
520
|
-
size="sm"
|
|
521
|
-
>
|
|
522
|
-
{{ t(INITIATIVE_FOLLOWUP_STATUS_LABEL_KEYS[f.status]) }}
|
|
523
|
-
</UBadge>
|
|
524
|
-
</div>
|
|
525
|
-
<!-- Triage actions for an open follow-up (only while executing) -->
|
|
526
|
-
<div v-if="editable && f.status === 'open'" class="mt-2">
|
|
527
|
-
<div v-if="promotingId === f.id" class="flex flex-col gap-2">
|
|
528
|
-
<label class="flex items-center gap-2 text-[12px]">
|
|
529
|
-
<span class="text-slate-400">{{ t('initiative.curation.phaseField') }}</span>
|
|
530
|
-
<select
|
|
531
|
-
v-model="promoteForm.phaseId"
|
|
532
|
-
class="flex-1 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-slate-200"
|
|
533
|
-
data-testid="initiative-promote-phase"
|
|
534
|
-
>
|
|
535
|
-
<option v-for="p in phases" :key="p.id" :value="p.id">
|
|
536
|
-
{{ p.title }}
|
|
537
|
-
</option>
|
|
538
|
-
</select>
|
|
539
|
-
</label>
|
|
540
|
-
<input
|
|
541
|
-
v-model="promoteForm.title"
|
|
542
|
-
type="text"
|
|
543
|
-
class="rounded border border-slate-700 bg-slate-950 px-2 py-1 text-[12px] text-slate-200"
|
|
544
|
-
:placeholder="t('initiative.curation.itemTitlePlaceholder')"
|
|
545
|
-
data-testid="initiative-promote-title"
|
|
546
|
-
/>
|
|
547
|
-
<div class="flex gap-2">
|
|
585
|
+
</div>
|
|
586
|
+
<div v-else class="flex gap-1.5">
|
|
548
587
|
<button
|
|
549
|
-
class="rounded
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
@click="submitPromote(f)"
|
|
588
|
+
class="rounded border border-slate-700 px-1.5 py-0.5 text-[10px] text-slate-300 hover:bg-slate-800"
|
|
589
|
+
data-testid="initiative-followup-promote"
|
|
590
|
+
@click="startPromote(f)"
|
|
553
591
|
>
|
|
554
|
-
{{ t('initiative.curation.
|
|
592
|
+
{{ t('initiative.curation.promote') }}
|
|
555
593
|
</button>
|
|
556
594
|
<button
|
|
557
|
-
class="rounded border border-slate-700 px-
|
|
558
|
-
|
|
595
|
+
class="rounded border border-slate-700 px-1.5 py-0.5 text-[10px] text-slate-300 hover:bg-slate-800 disabled:opacity-50"
|
|
596
|
+
:disabled="initiatives.curating"
|
|
597
|
+
data-testid="initiative-followup-dismiss"
|
|
598
|
+
@click="dismissFollowUp(f)"
|
|
559
599
|
>
|
|
560
|
-
{{ t('initiative.curation.
|
|
600
|
+
{{ t('initiative.curation.dismiss') }}
|
|
561
601
|
</button>
|
|
562
602
|
</div>
|
|
563
603
|
</div>
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
604
|
+
</li>
|
|
605
|
+
</ul>
|
|
606
|
+
</section>
|
|
607
|
+
<section v-if="initiative.caveats?.length" class="mb-4">
|
|
608
|
+
<h3 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
609
|
+
{{ t('initiative.tracker.caveats') }}
|
|
610
|
+
</h3>
|
|
611
|
+
<ul class="list-inside list-disc text-[13px] text-slate-300">
|
|
612
|
+
<li v-for="(c, i) in initiative.caveats" :key="i">{{ c }}</li>
|
|
613
|
+
</ul>
|
|
614
|
+
</section>
|
|
615
|
+
</template>
|
|
616
|
+
</div>
|
|
617
|
+
|
|
618
|
+
<!-- Run details: the shared run-metadata + LLM model-activity block every agent window
|
|
619
|
+
carries (step position, live duration, model, run id, calls + token usage). Resolved
|
|
620
|
+
through `useResultViewRunMeta`, so it is present on the card / inspector entry point
|
|
621
|
+
too — where this window carries no step index of its own. -->
|
|
622
|
+
<aside
|
|
623
|
+
v-if="metaStep"
|
|
624
|
+
data-testid="initiative-tracker-run-meta"
|
|
625
|
+
class="hidden w-60 shrink-0 flex-col gap-4 overflow-y-auto border-s border-slate-800 bg-slate-900/50 px-4 py-4 lg:flex"
|
|
626
|
+
>
|
|
627
|
+
<StepRunMeta
|
|
628
|
+
:step="metaStep"
|
|
629
|
+
:instance-id="runId"
|
|
630
|
+
:step-number="position"
|
|
631
|
+
:total-steps="totalSteps"
|
|
632
|
+
:run-failed="runFailed"
|
|
633
|
+
:failure-at="failureAt"
|
|
634
|
+
/>
|
|
635
|
+
</aside>
|
|
594
636
|
</div>
|
|
595
637
|
</ResultWindowShell>
|
|
596
638
|
</template>
|