@cat-factory/app 0.227.0 → 0.228.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.
- package/README.md +25 -0
- package/app/components/board/AddTaskModal.vue +11 -7
- package/app/components/board/RecurringPipelineModal.vue +11 -7
- package/app/components/board/nodes/TaskCard.vue +68 -8
- package/app/components/bootstrap/BootstrapModal.vue +11 -7
- package/app/components/documents/RepoContextDocPicker.vue +4 -1
- package/app/components/fragments/FragmentLibraryManager.vue +19 -18
- package/app/components/gates/GateResultView.vue +3 -3
- package/app/components/github/AddServiceFromRepoModal.vue +10 -9
- package/app/components/outcome/OutcomeSummaryWindow.vue +614 -0
- package/app/components/panels/AgentStepDetail.vue +9 -7
- package/app/components/panels/InspectorPanel.vue +4 -4
- package/app/components/panels/ResultWindowShell.logic.spec.ts +4 -0
- package/app/components/panels/inspector/ServiceTestConfig.vue +12 -11
- package/app/components/panels/inspector/TaskExecution.vue +40 -7
- package/app/components/pipeline/PipelineProgress.vue +5 -4
- package/app/components/providers/ApiKeysSection.vue +3 -1
- package/app/components/ralph/RalphLoopResultView.vue +3 -3
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +14 -14
- package/app/composables/useRunDeepLink.ts +12 -4
- package/app/modular/result-views.ts +4 -0
- package/app/pages/index.vue +21 -18
- package/app/stores/execution.ts +6 -6
- package/app/stores/ui/resultViews.ts +28 -0
- package/app/stores/ui.dispatch.spec.ts +55 -0
- package/app/utils/runOutcome.spec.ts +492 -0
- package/app/utils/runOutcome.ts +509 -0
- package/i18n/locales/de.json +107 -0
- package/i18n/locales/en.json +107 -0
- package/i18n/locales/es.json +107 -0
- package/i18n/locales/fr.json +107 -0
- package/i18n/locales/he.json +107 -0
- package/i18n/locales/it.json +107 -0
- package/i18n/locales/ja.json +107 -0
- package/i18n/locales/pl.json +107 -0
- package/i18n/locales/tr.json +107 -0
- package/i18n/locales/uk.json +107 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -181,6 +181,15 @@ hoc where it can be avoided:
|
|
|
181
181
|
control, editable, as soon as any value it edits is set (`false` included: a tri-state
|
|
182
182
|
`false` is a choice, not absence), so basic mode can never conceal a setting a run will
|
|
183
183
|
actually use.
|
|
184
|
+
- **The tier may also change which of two routes to the same thing LEADS**, and that is not a
|
|
185
|
+
hidden capability: a `pr_ready` task card offers the outcome summary in both tiers and drops
|
|
186
|
+
the raw pull-request chip in basic, because the card the button opens carries that same link
|
|
187
|
+
at the top. What basic mode may never do is remove the only route (the rule above); ordering
|
|
188
|
+
two routes by which one a tier's reader wants first is what the tier is for. **Write the
|
|
189
|
+
condition as that INVARIANT, not as `isAdvanced` alone** (`TaskCard`'s `showPrChip`: keep the
|
|
190
|
+
chip wherever the outcome card is not offered), because the surface that carries the hidden
|
|
191
|
+
half is itself conditional, and two predicates that must agree by coincidence eventually do
|
|
192
|
+
not: the day the leading route hides, `isAdvanced` alone takes the last route with it.
|
|
184
193
|
|
|
185
194
|
## Agent tiers (basic / intermediate / advanced)
|
|
186
195
|
|
|
@@ -700,6 +709,22 @@ example ships in [`deploy/frontend`](../../deploy/frontend) (the `acme:security`
|
|
|
700
709
|
additionally leads with `TaskReviewTarget`, linking the pull request it reviews:
|
|
701
710
|
distinct from the execution panel's link to the PR a run PRODUCED, which a review
|
|
702
711
|
task never has.
|
|
712
|
+
- **Outcome summary** (`components/outcome`): the run-keyed result window, and what
|
|
713
|
+
the board card and the inspector open to "read the result". It renders the reduction
|
|
714
|
+
in `utils/runOutcome.ts` (requirement coverage joined to the service spec, the
|
|
715
|
+
tester's verdict, the captured views, the recorded checks) and keeps the pull request
|
|
716
|
+
at the top, so the diff stays one click away rather than being the starting point.
|
|
717
|
+
The only result window keyed by a RUN rather than a step: it opens with
|
|
718
|
+
`stepIndex: null` and composes from the whole instance, which is also why it passes
|
|
719
|
+
no `stepRef` to the shell (there is no step to restart from). Available in both
|
|
720
|
+
interface tiers; what `basic` changes is which affordance leads (see
|
|
721
|
+
[interface modes](#interface-modes-basic--advanced)).
|
|
722
|
+
Two rules bind anything added to it. Every entry point gates on `hasOutcomeToShow`
|
|
723
|
+
from the same reduction, so the card is never offered onto sections that all read
|
|
724
|
+
"nothing here". And the window is BLOCK-keyed with the run riding along: a block
|
|
725
|
+
naming a run the store never hydrated is a distinct fact from a task that never ran
|
|
726
|
+
(`RunUnavailableGap`), so a new section reports that case rather than composing from
|
|
727
|
+
the empty step list, which would read as a pipeline that produced nothing.
|
|
703
728
|
- **Pipeline builder** (`components/pipeline`): assemble/edit agent chains and
|
|
704
729
|
watch `PipelineProgress`. `PipelinePicker` (+ its `PipelinePreview` pane) is the
|
|
705
730
|
single way a pipeline is chosen anywhere (add-task, run settings, the recurring
|
|
@@ -59,12 +59,7 @@ const { t } = useI18n()
|
|
|
59
59
|
|
|
60
60
|
const { linkPending, presentLinkFailures } = useContextLinking()
|
|
61
61
|
|
|
62
|
-
const open = computed(
|
|
63
|
-
get: () => ui.addTaskContainerId !== null,
|
|
64
|
-
set: (v: boolean) => {
|
|
65
|
-
if (!v) void requestClose()
|
|
66
|
-
},
|
|
67
|
-
})
|
|
62
|
+
const open = computed(() => ui.addTaskContainerId !== null)
|
|
68
63
|
|
|
69
64
|
const container = computed(() =>
|
|
70
65
|
ui.addTaskContainerId ? board.getBlock(ui.addTaskContainerId) : undefined,
|
|
@@ -591,6 +586,15 @@ const { requestClose } = useUnsavedGuard({
|
|
|
591
586
|
}),
|
|
592
587
|
})
|
|
593
588
|
|
|
589
|
+
// The template's v-model binding: dismissal (Escape / backdrop) routes through the guard.
|
|
590
|
+
// Declared after the guard so the setter's `requestClose` reference is never in its TDZ.
|
|
591
|
+
const modalOpen = computed({
|
|
592
|
+
get: () => open.value,
|
|
593
|
+
set: (v: boolean) => {
|
|
594
|
+
if (!v) void requestClose()
|
|
595
|
+
},
|
|
596
|
+
})
|
|
597
|
+
|
|
594
598
|
// A recurring task only needs a target frame (its details are filled in the schedule
|
|
595
599
|
// modal); every other type needs a title. A review task additionally needs a target PR.
|
|
596
600
|
// The Ralph loop's completion criterion (its `ralph.validationCommand` agent-config id). The
|
|
@@ -757,7 +761,7 @@ function openReviewFrictionDialog(conflict: NonNullable<ReturnType<typeof parseC
|
|
|
757
761
|
</script>
|
|
758
762
|
|
|
759
763
|
<template>
|
|
760
|
-
<UModal v-model:open="
|
|
764
|
+
<UModal v-model:open="modalOpen" :title="t('board.addTask.title')">
|
|
761
765
|
<template #body>
|
|
762
766
|
<div class="space-y-4" data-testid="add-task-modal">
|
|
763
767
|
<p v-if="container" class="text-xs text-slate-400">
|
|
@@ -22,12 +22,7 @@ const toast = useToast()
|
|
|
22
22
|
const access = useWorkspaceAccess()
|
|
23
23
|
const { t, te } = useI18n()
|
|
24
24
|
|
|
25
|
-
const open = computed(
|
|
26
|
-
get: () => ui.addRecurringFrameId !== null,
|
|
27
|
-
set: (v: boolean) => {
|
|
28
|
-
if (!v) void requestClose()
|
|
29
|
-
},
|
|
30
|
-
})
|
|
25
|
+
const open = computed(() => ui.addRecurringFrameId !== null)
|
|
31
26
|
|
|
32
27
|
const frame = computed(() =>
|
|
33
28
|
ui.addRecurringFrameId ? board.getBlock(ui.addRecurringFrameId) : undefined,
|
|
@@ -240,6 +235,15 @@ const { requestClose } = useUnsavedGuard({
|
|
|
240
235
|
}),
|
|
241
236
|
})
|
|
242
237
|
|
|
238
|
+
// The template's v-model binding: dismissal (Escape / backdrop) routes through the guard.
|
|
239
|
+
// Declared after the guard so the setter's `requestClose` reference is never in its TDZ.
|
|
240
|
+
const modalOpen = computed({
|
|
241
|
+
get: () => open.value,
|
|
242
|
+
set: (v: boolean) => {
|
|
243
|
+
if (!v) void requestClose()
|
|
244
|
+
},
|
|
245
|
+
})
|
|
246
|
+
|
|
243
247
|
// The board field required for the picked source must be filled before a bug-intake schedule saves.
|
|
244
248
|
const intakeReady = computed(() => {
|
|
245
249
|
if (!showIntake.value) return true
|
|
@@ -339,7 +343,7 @@ async function add() {
|
|
|
339
343
|
</script>
|
|
340
344
|
|
|
341
345
|
<template>
|
|
342
|
-
<UModal v-model:open="
|
|
346
|
+
<UModal v-model:open="modalOpen" :title="t('board.recurring.title')">
|
|
343
347
|
<template #body>
|
|
344
348
|
<div class="space-y-4">
|
|
345
349
|
<p v-if="frame" class="text-xs text-slate-400">
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Block } from '~/types/domain'
|
|
3
3
|
import { STATUS_META, MODULE_META, taskTypeMeta } from '~/utils/catalog'
|
|
4
|
+
import { composeRunOutcome, hasOutcomeToShow } from '~/utils/runOutcome'
|
|
4
5
|
import AgentFailureCard from '~/components/board/AgentFailureCard.vue'
|
|
5
6
|
import TaskPipelineMini from './TaskPipelineMini.vue'
|
|
6
7
|
|
|
@@ -72,6 +73,40 @@ const prLabel = computed(() =>
|
|
|
72
73
|
pr.value?.number ? t('board.task.prNumber', { number: pr.value.number }) : t('board.task.pr'),
|
|
73
74
|
)
|
|
74
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Reading the result starts at the OUTCOME summary (what changed in product terms, with the
|
|
78
|
+
* captured evidence), and the pull request is one click inside it. In BASIC mode that replaces
|
|
79
|
+
* the card's raw PR chip: the diff is still exactly as reachable, through a surface that says
|
|
80
|
+
* what the diff is about first. Advanced mode keeps both, since a reader who wants the diff
|
|
81
|
+
* directly is the reader that tier is for.
|
|
82
|
+
*
|
|
83
|
+
* Offered only where there is something to read, asked of the SAME reduction the window renders
|
|
84
|
+
* and the inspector's button gates on: a card that offered "read the result" on a task whose
|
|
85
|
+
* every section says "nothing here" would teach people the surface is empty. A task marked done
|
|
86
|
+
* by hand, with no pull request and no run, is that task.
|
|
87
|
+
*/
|
|
88
|
+
const uiMode = useUiModeStore()
|
|
89
|
+
const outcomeReadable = computed(() => {
|
|
90
|
+
const block = task.value
|
|
91
|
+
if (!block) return false
|
|
92
|
+
return hasOutcomeToShow(
|
|
93
|
+
composeRunOutcome({ block, instance: execution.getInstance(block.executionId) ?? null }),
|
|
94
|
+
)
|
|
95
|
+
})
|
|
96
|
+
/**
|
|
97
|
+
* The card's raw pull-request chip, which basic mode drops in favour of the outcome card
|
|
98
|
+
* carrying the same link at the top. Written as the INVARIANT ("the diff never stops being
|
|
99
|
+
* reachable from this card") rather than as `isAdvanced` alone, so the tier can only ever
|
|
100
|
+
* reorder two routes and never remove the last one: where the outcome card is not offered,
|
|
101
|
+
* the chip stays in both tiers.
|
|
102
|
+
*/
|
|
103
|
+
const showPrChip = computed(
|
|
104
|
+
() => Boolean(pr.value) && (uiMode.isAdvanced || !outcomeReadable.value),
|
|
105
|
+
)
|
|
106
|
+
function openOutcome() {
|
|
107
|
+
ui.openOutcome(props.taskId, task.value?.executionId ?? null)
|
|
108
|
+
}
|
|
109
|
+
|
|
75
110
|
// This task's current agent run (if any). A failed run must surface the shared
|
|
76
111
|
// failure banner + retry — NOT a stuck progress bar — so the card never looks
|
|
77
112
|
// like it's still working after the run has terminated.
|
|
@@ -402,8 +437,20 @@ function selectTask() {
|
|
|
402
437
|
|
|
403
438
|
<template v-if="task.status === 'pr_ready'">
|
|
404
439
|
<UButton
|
|
405
|
-
v-if="
|
|
406
|
-
|
|
440
|
+
v-if="outcomeReadable"
|
|
441
|
+
color="primary"
|
|
442
|
+
variant="soft"
|
|
443
|
+
size="xs"
|
|
444
|
+
icon="i-lucide-clipboard-check"
|
|
445
|
+
:title="t('board.task.readOutcomeHint')"
|
|
446
|
+
data-testid="task-open-outcome"
|
|
447
|
+
@click.stop="openOutcome"
|
|
448
|
+
>
|
|
449
|
+
{{ t('board.task.readOutcome') }}
|
|
450
|
+
</UButton>
|
|
451
|
+
<UButton
|
|
452
|
+
v-if="showPrChip"
|
|
453
|
+
:to="pr?.url"
|
|
407
454
|
target="_blank"
|
|
408
455
|
rel="noopener"
|
|
409
456
|
external
|
|
@@ -436,12 +483,25 @@ function selectTask() {
|
|
|
436
483
|
</UButton>
|
|
437
484
|
</template>
|
|
438
485
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
486
|
+
<!-- A merged task is the one people come back to READ, so its result stays openable
|
|
487
|
+
rather than collapsing to a tick the moment it lands. -->
|
|
488
|
+
<template v-else-if="task.status === 'done'">
|
|
489
|
+
<span class="inline-flex items-center gap-1 text-[9px] text-emerald-400">
|
|
490
|
+
<UIcon name="i-lucide-check-check" class="h-3 w-3" /> {{ t('board.task.implemented') }}
|
|
491
|
+
</span>
|
|
492
|
+
<UButton
|
|
493
|
+
v-if="outcomeReadable"
|
|
494
|
+
color="neutral"
|
|
495
|
+
variant="ghost"
|
|
496
|
+
size="xs"
|
|
497
|
+
icon="i-lucide-clipboard-check"
|
|
498
|
+
:title="t('board.task.readOutcomeHint')"
|
|
499
|
+
data-testid="task-open-outcome"
|
|
500
|
+
@click.stop="openOutcome"
|
|
501
|
+
>
|
|
502
|
+
{{ t('board.task.readOutcome') }}
|
|
503
|
+
</UButton>
|
|
504
|
+
</template>
|
|
445
505
|
</div>
|
|
446
506
|
|
|
447
507
|
<!-- structural metadata: assigned module -->
|
|
@@ -18,12 +18,7 @@ const { freeFramePosition, focusFrame } = useFramePlacement()
|
|
|
18
18
|
const { t } = useI18n()
|
|
19
19
|
const { confirmAction, toastDone } = useConfirmAction()
|
|
20
20
|
|
|
21
|
-
const open = computed(
|
|
22
|
-
get: () => ui.bootstrapOpen,
|
|
23
|
-
set: (v: boolean) => {
|
|
24
|
-
if (!v) void requestClose()
|
|
25
|
-
},
|
|
26
|
-
})
|
|
21
|
+
const open = computed(() => ui.bootstrapOpen)
|
|
27
22
|
|
|
28
23
|
// Load the workspace's reference architectures + recent jobs, plus (best-effort)
|
|
29
24
|
// the GitHub repos the user can access so the base form can pick from them.
|
|
@@ -100,6 +95,15 @@ const { requestClose } = useUnsavedGuard({
|
|
|
100
95
|
}),
|
|
101
96
|
})
|
|
102
97
|
|
|
98
|
+
// The template's v-model binding: dismissal (Escape / backdrop) routes through the guard.
|
|
99
|
+
// Declared after the guard so the setter's `requestClose` reference is never in its TDZ.
|
|
100
|
+
const modalOpen = computed({
|
|
101
|
+
get: () => open.value,
|
|
102
|
+
set: (v: boolean) => {
|
|
103
|
+
if (!v) void requestClose()
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
|
|
103
107
|
// Mirror of the backend `slugField` rule (@cat-factory/contracts bootstrap
|
|
104
108
|
// schema): the new repo name is a SINGLE GitHub name segment — no "owner/"
|
|
105
109
|
// prefix — so reject a bad value inline before we hit the API. Kept in sync with
|
|
@@ -420,7 +424,7 @@ const statusLabel = computed<Record<BootstrapStatus, string>>(() => ({
|
|
|
420
424
|
</script>
|
|
421
425
|
|
|
422
426
|
<template>
|
|
423
|
-
<UModal v-model:open="
|
|
427
|
+
<UModal v-model:open="modalOpen" :title="t('bootstrap.title')" :ui="{ content: 'max-w-2xl' }">
|
|
424
428
|
<template #body>
|
|
425
429
|
<div class="space-y-6">
|
|
426
430
|
<!-- Three states, because each promises the user something different about the repo.
|
|
@@ -75,6 +75,10 @@ watch(selectedRepoId, (id) => {
|
|
|
75
75
|
if (found) selectedRepo.value = found
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
+
// The file-search query (its matches live in the file-selection section below). Declared
|
|
79
|
+
// above `clearRepo` and the repo-switch watcher, which both reset it.
|
|
80
|
+
const fileQuery = ref('')
|
|
81
|
+
|
|
78
82
|
function clearRepo() {
|
|
79
83
|
selectedRepoId.value = undefined
|
|
80
84
|
selectedRepo.value = undefined
|
|
@@ -127,7 +131,6 @@ watch(selectedRepoId, (id) => {
|
|
|
127
131
|
if (id !== undefined) void ensureFilesLoaded()
|
|
128
132
|
})
|
|
129
133
|
|
|
130
|
-
const fileQuery = ref('')
|
|
131
134
|
// Matches are computed client-side from the cached tree (no per-keystroke server call).
|
|
132
135
|
// A query is required so a large repo never renders thousands of rows at once; results
|
|
133
136
|
// are capped for the same reason.
|
|
@@ -171,6 +171,19 @@ const editDraft = ref<{
|
|
|
171
171
|
brief: string
|
|
172
172
|
tags: string
|
|
173
173
|
} | null>(null)
|
|
174
|
+
// The linked SHORT VERSION is an OVERRIDE of what the platform does by default (condense a
|
|
175
|
+
// long standard automatically, fold a short one in full), so it follows the override rule:
|
|
176
|
+
// hidden in basic mode while unset, revealed as soon as the fragment carries one — a
|
|
177
|
+
// basic-mode curator is never left unable to see or clear a brief a teammate linked.
|
|
178
|
+
//
|
|
179
|
+
// Both flags are LATCHED at the moment the form opens rather than tracking the live draft.
|
|
180
|
+
// Recomputing per keystroke makes the control delete itself the instant a basic-mode curator
|
|
181
|
+
// empties it — mid-edit, under the cursor, on the one interaction (clearing, to hand the
|
|
182
|
+
// standard back to auto-generation) the rule exists to keep reachable.
|
|
183
|
+
const uiMode = useUiModeStore()
|
|
184
|
+
const showEditBrief = ref(false)
|
|
185
|
+
const showDraftBrief = computed(() => showOverrideField(uiMode.isAdvanced, null))
|
|
186
|
+
|
|
174
187
|
function startEdit(f: (typeof library.fragments)[number]) {
|
|
175
188
|
editDraft.value = {
|
|
176
189
|
id: f.id,
|
|
@@ -192,19 +205,6 @@ const editValid = computed(
|
|
|
192
205
|
!!editDraft.value.summary.trim() &&
|
|
193
206
|
!!editDraft.value.body.trim(),
|
|
194
207
|
)
|
|
195
|
-
// The linked SHORT VERSION is an OVERRIDE of what the platform does by default (condense a
|
|
196
|
-
// long standard automatically, fold a short one in full), so it follows the override rule:
|
|
197
|
-
// hidden in basic mode while unset, revealed as soon as the fragment carries one — a
|
|
198
|
-
// basic-mode curator is never left unable to see or clear a brief a teammate linked.
|
|
199
|
-
//
|
|
200
|
-
// Both flags are LATCHED at the moment the form opens rather than tracking the live draft.
|
|
201
|
-
// Recomputing per keystroke makes the control delete itself the instant a basic-mode curator
|
|
202
|
-
// empties it — mid-edit, under the cursor, on the one interaction (clearing, to hand the
|
|
203
|
-
// standard back to auto-generation) the rule exists to keep reachable.
|
|
204
|
-
const uiMode = useUiModeStore()
|
|
205
|
-
const showEditBrief = ref(false)
|
|
206
|
-
const showDraftBrief = computed(() => showOverrideField(uiMode.isAdvanced, null))
|
|
207
|
-
|
|
208
208
|
async function saveEdit() {
|
|
209
209
|
const d = editDraft.value
|
|
210
210
|
if (!d || !editValid.value) return
|
|
@@ -277,11 +277,6 @@ async function removeFragment(id: string) {
|
|
|
277
277
|
// Link a Confluence/Notion page or GitHub file as a fragment that is re-resolved
|
|
278
278
|
// from the source at run time (a living source of truth, not a frozen snapshot).
|
|
279
279
|
const docDraft = ref({ source: '' as DocumentSourceKind | '', ref: '', tags: '' })
|
|
280
|
-
const docDraftValid = computed(() => {
|
|
281
|
-
if (docLinkDisabled.value || !docDraft.value.source) return false
|
|
282
|
-
// The GitHub picker validates on staged files; every other path on the free-text ref.
|
|
283
|
-
return usingDocPicker.value ? docFilePaths.value.length > 0 : !!docDraft.value.ref.trim()
|
|
284
|
-
})
|
|
285
280
|
|
|
286
281
|
// ---- GitHub file picker (documents tab) -----------------------------------
|
|
287
282
|
// For a GitHub source, let the user search a repo + browse to one or MORE files
|
|
@@ -364,6 +359,12 @@ const stagedDocRefs = computed(() =>
|
|
|
364
359
|
/** When the rich picker drives the ref(s); otherwise the free-text field does. */
|
|
365
360
|
const usingDocPicker = computed(() => showGithubDocPicker.value)
|
|
366
361
|
|
|
362
|
+
const docDraftValid = computed(() => {
|
|
363
|
+
if (docLinkDisabled.value || !docDraft.value.source) return false
|
|
364
|
+
// The GitHub picker validates on staged files; every other path on the free-text ref.
|
|
365
|
+
return usingDocPicker.value ? docFilePaths.value.length > 0 : !!docDraft.value.ref.trim()
|
|
366
|
+
})
|
|
367
|
+
|
|
367
368
|
/**
|
|
368
369
|
* A pasted GitHub file/directory URL resolved to a repo + location: select the repo
|
|
369
370
|
* (through the same refs the search select drives), then stage the file or jump the
|
|
@@ -25,9 +25,6 @@ const access = useWorkspaceAccess()
|
|
|
25
25
|
const { open, blockId, instanceId, stepIndex, close } = useResultView('gate')
|
|
26
26
|
const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
|
|
27
27
|
const prUrl = computed(() => block.value?.pullRequest?.url ?? null)
|
|
28
|
-
const headerTitle = computed(
|
|
29
|
-
() => `${meta.value.label}${block.value ? ` — ${block.value.title}` : ''}`,
|
|
30
|
-
)
|
|
31
28
|
|
|
32
29
|
const instance = computed(() =>
|
|
33
30
|
instanceId.value === null ? null : (execution.getInstance(instanceId.value) ?? null),
|
|
@@ -42,6 +39,9 @@ const isCi = computed(() => step.value?.agentKind === 'ci')
|
|
|
42
39
|
const isHumanReview = computed(() => step.value?.agentKind === 'human-review')
|
|
43
40
|
const isDocQuality = computed(() => step.value?.agentKind === 'doc-quality')
|
|
44
41
|
const meta = computed(() => agentKindMeta(step.value?.agentKind ?? 'ci'))
|
|
42
|
+
const headerTitle = computed(
|
|
43
|
+
() => `${meta.value.label}${block.value ? ` — ${block.value.title}` : ''}`,
|
|
44
|
+
)
|
|
45
45
|
const helperKind = computed(() =>
|
|
46
46
|
isHumanReview.value
|
|
47
47
|
? 'fixer'
|
|
@@ -189,6 +189,16 @@ function removeSelected(path: string) {
|
|
|
189
189
|
if (i >= 0) selectedDirectories.value.splice(i, 1)
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
// The just-added whole-repo service, kept on the board store so the user can configure it
|
|
193
|
+
// (test infra + fragments) right here — the same controls as the inspector. Only the
|
|
194
|
+
// whole-repo flow surfaces this inline configure step; a monorepo adds several services at
|
|
195
|
+
// once and they're configured later in the inspector. Declared above the watcher and
|
|
196
|
+
// `resetSelection` below, both of which clear it.
|
|
197
|
+
const configuredBlockId = ref<string | undefined>(undefined)
|
|
198
|
+
const configuredBlock = computed(() =>
|
|
199
|
+
configuredBlockId.value ? board.getBlock(configuredBlockId.value) : undefined,
|
|
200
|
+
)
|
|
201
|
+
|
|
192
202
|
// On repo change, capture the picked repo (from the volatile loaded list, before a later
|
|
193
203
|
// search replaces it), seed the monorepo toggle from its persisted flag, and clear the rest.
|
|
194
204
|
watch(selectedRepoId, (id) => {
|
|
@@ -227,15 +237,6 @@ function openManageInstall() {
|
|
|
227
237
|
if (manageInstallUrl.value) window.open(manageInstallUrl.value, '_blank', 'noopener')
|
|
228
238
|
}
|
|
229
239
|
|
|
230
|
-
// The just-added whole-repo service, kept on the board store so the user can configure it
|
|
231
|
-
// (test infra + fragments) right here — the same controls as the inspector. Only the
|
|
232
|
-
// whole-repo flow surfaces this inline configure step; a monorepo adds several services at
|
|
233
|
-
// once and they're configured later in the inspector.
|
|
234
|
-
const configuredBlockId = ref<string | undefined>(undefined)
|
|
235
|
-
const configuredBlock = computed(() =>
|
|
236
|
-
configuredBlockId.value ? board.getBlock(configuredBlockId.value) : undefined,
|
|
237
|
-
)
|
|
238
|
-
|
|
239
240
|
// On open: ensure we know the connection + which repos the App can access, and
|
|
240
241
|
// the workspace's already-tracked repos (to flag ones already on the board).
|
|
241
242
|
// Declared after every ref resetSelection() touches so the `immediate` run
|