@cat-factory/app 0.271.0 → 0.273.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/app/components/tasks/BugHuntModal.logic.spec.ts +83 -0
- package/app/components/tasks/BugHuntModal.logic.ts +54 -0
- package/app/components/tasks/BugHuntModal.vue +149 -25
- package/app/stores/bugHunt.spec.ts +122 -1
- package/app/stores/bugHunt.ts +35 -1
- package/app/stores/tasks.spec.ts +2 -0
- package/app/utils/failureKinds.ts +1 -0
- package/app/utils/intakePredicates.spec.ts +1 -0
- package/i18n/locales/de.json +11 -1
- package/i18n/locales/en.json +11 -1
- package/i18n/locales/es.json +11 -1
- package/i18n/locales/fr.json +11 -1
- package/i18n/locales/he.json +11 -1
- package/i18n/locales/it.json +11 -1
- package/i18n/locales/ja.json +11 -1
- package/i18n/locales/pl.json +11 -1
- package/i18n/locales/tr.json +11 -1
- package/i18n/locales/uk.json +11 -1
- package/package.json +4 -4
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import type { TaskSourceState } from '@cat-factory/contracts'
|
|
3
|
+
import { boardFromService, huntRequest } from './BugHuntModal.logic'
|
|
4
|
+
|
|
5
|
+
// What scopes a hunt. The rule is small and the failures are silent in both directions: a
|
|
6
|
+
// repo-backed tracker that sends a board would be refused on submit, and one that sent an empty
|
|
7
|
+
// string instead of an explicit null would read as a board named as blank.
|
|
8
|
+
|
|
9
|
+
function state(overrides: Partial<TaskSourceState> = {}): TaskSourceState {
|
|
10
|
+
return {
|
|
11
|
+
source: 'github',
|
|
12
|
+
label: 'GitHub Issues',
|
|
13
|
+
icon: 'i-lucide-github',
|
|
14
|
+
credentialFields: [],
|
|
15
|
+
refLabel: 'Issue URL',
|
|
16
|
+
refPlaceholder: 'acme/web#123',
|
|
17
|
+
available: true,
|
|
18
|
+
enabled: true,
|
|
19
|
+
ridesVcsProvider: 'github',
|
|
20
|
+
supportsIntake: true,
|
|
21
|
+
ignoredIntakePredicates: [],
|
|
22
|
+
repoBacked: true,
|
|
23
|
+
...overrides,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const FORM = { containerId: 'blk-1', board: '', issueType: '', labels: '' }
|
|
28
|
+
|
|
29
|
+
describe('boardFromService', () => {
|
|
30
|
+
it('follows what the SOURCE declares, not which source it is', () => {
|
|
31
|
+
expect(boardFromService(state({ source: 'acme:forge' }))).toBe(true)
|
|
32
|
+
expect(boardFromService(state({ source: 'jira', repoBacked: false }))).toBe(false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('treats an unresolved source as having a board, so a control is still rendered', () => {
|
|
36
|
+
expect(boardFromService(undefined)).toBe(false)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
describe('huntRequest', () => {
|
|
41
|
+
it('sends an explicit null board for a repo-backed tracker, whatever was typed before', () => {
|
|
42
|
+
const request = huntRequest({ ...FORM, source: state(), board: 'someone-else/web' })
|
|
43
|
+
|
|
44
|
+
// Null, never '' and never the stale text: the backend REFUSES a board named for such a
|
|
45
|
+
// source, so a hunt that carried one would be rejected rather than scoped.
|
|
46
|
+
expect(request).toEqual({ containerId: 'blk-1', board: null })
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('sends the trimmed board a repo-less tracker names', () => {
|
|
50
|
+
const source = state({ source: 'jira', repoBacked: false })
|
|
51
|
+
|
|
52
|
+
expect(huntRequest({ ...FORM, source, board: ' PROJ ' })).toEqual({
|
|
53
|
+
containerId: 'blk-1',
|
|
54
|
+
board: 'PROJ',
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('describes no scan until a repo-less tracker has a board', () => {
|
|
59
|
+
const source = state({ source: 'jira', repoBacked: false })
|
|
60
|
+
|
|
61
|
+
expect(huntRequest({ ...FORM, source, board: ' ' })).toBeNull()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('describes no scan without a container, which decides the repository too', () => {
|
|
65
|
+
expect(huntRequest({ ...FORM, source: state(), containerId: undefined })).toBeNull()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('carries only the predicates that were actually filled in', () => {
|
|
69
|
+
const request = huntRequest({
|
|
70
|
+
...FORM,
|
|
71
|
+
source: state(),
|
|
72
|
+
issueType: ' defect ',
|
|
73
|
+
labels: 'regression, , checkout ',
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
expect(request).toEqual({
|
|
77
|
+
containerId: 'blk-1',
|
|
78
|
+
board: null,
|
|
79
|
+
issueType: 'defect',
|
|
80
|
+
labels: ['regression', 'checkout'],
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { RunBugHuntInput, TaskSourceState } from '@cat-factory/contracts'
|
|
2
|
+
|
|
3
|
+
// The pure half of BugHuntModal: what SCOPES a hunt. Extracted for the reason every `*.logic.ts`
|
|
4
|
+
// here is (a decision worth a test should not need a mounted component to reach), and this one
|
|
5
|
+
// carries the whole rule the surface exists to enforce: a repo-backed tracker hunts the
|
|
6
|
+
// repository of the service the bug will land in, and names no board of its own.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Whether this tracker's board is the chosen service's repository rather than a choice.
|
|
10
|
+
*
|
|
11
|
+
* Read off the source's declared `repoBacked`, never its id: a deployment that registers its own
|
|
12
|
+
* repo-backed source, or one running GitLab instead of GitHub, must behave identically, and a
|
|
13
|
+
* source list compared here would be a second authority that drifts from the backend's.
|
|
14
|
+
* An unresolved source (still loading, or one this workspace no longer offers) is NOT repo-backed:
|
|
15
|
+
* the answer decides which control to render, and rendering none is the option a user cannot
|
|
16
|
+
* correct.
|
|
17
|
+
*/
|
|
18
|
+
export function boardFromService(source: TaskSourceState | undefined): boolean {
|
|
19
|
+
return source?.repoBacked === true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The scan request, or null when the form does not yet name one.
|
|
24
|
+
*
|
|
25
|
+
* `board` is explicitly `null` for a repo-backed tracker rather than an empty string: the backend
|
|
26
|
+
* REFUSES a board named for such a source instead of ignoring it, so the difference between "no
|
|
27
|
+
* board to name" and "a board named as blank" has to survive this far. The container is required
|
|
28
|
+
* either way: it is where an adopted bug lands, and on a repo-backed tracker it is also what
|
|
29
|
+
* decides which repository is read at all.
|
|
30
|
+
*/
|
|
31
|
+
export function huntRequest(input: {
|
|
32
|
+
source: TaskSourceState | undefined
|
|
33
|
+
containerId: string | undefined
|
|
34
|
+
board: string
|
|
35
|
+
issueType: string
|
|
36
|
+
labels: string
|
|
37
|
+
}): RunBugHuntInput | null {
|
|
38
|
+
const { containerId } = input
|
|
39
|
+
if (!input.source || !containerId) return null
|
|
40
|
+
const fromService = boardFromService(input.source)
|
|
41
|
+
const board = input.board.trim()
|
|
42
|
+
if (!fromService && !board) return null
|
|
43
|
+
const issueType = input.issueType.trim()
|
|
44
|
+
const labels = input.labels
|
|
45
|
+
.split(',')
|
|
46
|
+
.map((label) => label.trim())
|
|
47
|
+
.filter((label) => label.length > 0)
|
|
48
|
+
return {
|
|
49
|
+
containerId,
|
|
50
|
+
board: fromService ? null : board,
|
|
51
|
+
...(issueType ? { issueType } : {}),
|
|
52
|
+
...(labels.length ? { labels } : {}),
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
// Bug hunt: pick a connected tracker,
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// Bug hunt: pick a connected tracker, scope the scan, and let the platform rank that board's
|
|
3
|
+
// open, UNASSIGNED bugs by impact against implementation complexity. Confirming a candidate
|
|
4
|
+
// adopts it as a bug task in the chosen container and starts the bug-fix pipeline.
|
|
5
|
+
//
|
|
6
|
+
// What SCOPES the scan depends on the tracker, and the source states which (`repoBacked`): a
|
|
7
|
+
// repo-backed one (GitHub Issues, GitLab Issues) hunts the repository the chosen service is
|
|
8
|
+
// linked to and offers NO board control, because its issues live in one repo per service and the
|
|
9
|
+
// only honest answer is the one the backend resolves. Every other tracker names a board of its
|
|
10
|
+
// own. Never a picker either way that could aim a hunt at a repository this board holds no
|
|
11
|
+
// service for.
|
|
5
12
|
//
|
|
6
13
|
// The interactive dual of the recurring bug-triage schedule: same reading and same pipeline,
|
|
7
14
|
// but a human picks the bug instead of the oldest match being claimed unattended.
|
|
@@ -37,11 +44,14 @@ import {
|
|
|
37
44
|
} from '~/utils/sourcePicker'
|
|
38
45
|
import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
|
|
39
46
|
import { appliesIntakePredicate } from '~/utils/intakePredicates'
|
|
47
|
+
import { boardFromService as isBoardFromService, huntRequest } from './BugHuntModal.logic'
|
|
40
48
|
|
|
41
49
|
const { t, d, n } = useI18n()
|
|
42
50
|
const ui = useUiStore()
|
|
43
51
|
const tasks = useTasksStore()
|
|
44
52
|
const hunt = useBugHuntStore()
|
|
53
|
+
const board = useBoardStore()
|
|
54
|
+
const github = useGitHubStore()
|
|
45
55
|
const toast = useToast()
|
|
46
56
|
const { present } = usePipelineErrorToast()
|
|
47
57
|
|
|
@@ -130,6 +140,36 @@ const boardItems = computed(() =>
|
|
|
130
140
|
})),
|
|
131
141
|
)
|
|
132
142
|
|
|
143
|
+
/** This tracker's board is the chosen service's own repository (see the logic module). */
|
|
144
|
+
const boardFromService = computed(() => isBoardFromService(descriptor.value))
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* WHICH repository a repo-backed hunt will read, named before it runs.
|
|
148
|
+
*
|
|
149
|
+
* The premise of this whole branch is that the platform picks the board on the user's behalf, so
|
|
150
|
+
* withholding the value until the results block (`scannedBoard`) states it only after a billable
|
|
151
|
+
* scan has already been paid for. Resolved the way the backend resolves it — walk the chosen
|
|
152
|
+
* container up to its service frame, read that frame's repo link — so the two cannot name
|
|
153
|
+
* different repositories. Null while the projection is still loading or the service holds no
|
|
154
|
+
* link; the field then says what it always said, and the not-linked case stays the backend's to
|
|
155
|
+
* refuse (`boardNeedsRepo`), since an unloaded projection and an unlinked service look identical
|
|
156
|
+
* from here.
|
|
157
|
+
*/
|
|
158
|
+
const scopedRepo = computed(() => {
|
|
159
|
+
const container = containerId.value ? board.getBlock(containerId.value) : undefined
|
|
160
|
+
const frame = container ? board.serviceOf(container) : undefined
|
|
161
|
+
const repo = frame ? github.repoForBlock(frame.id) : undefined
|
|
162
|
+
return repo ? `${repo.owner}/${repo.name}` : null
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The service this hunt is scoped to has no repository linked, so it has no issues to read. The
|
|
167
|
+
* one scan failure worded here instead of in a toast: it names something to fix on this board,
|
|
168
|
+
* and it belongs beside the scope it invalidates.
|
|
169
|
+
*/
|
|
170
|
+
const REPO_NOT_LINKED: TaskSourceReadReason = 'repo_not_linked'
|
|
171
|
+
const huntNeedsRepo = computed(() => hunt.huntErrorReason === REPO_NOT_LINKED)
|
|
172
|
+
|
|
133
173
|
/**
|
|
134
174
|
* The tracker CANNOT enumerate boards, so the user types the scope in themselves. Keyed on the
|
|
135
175
|
* backend's reason code, not on "any error": an unreachable tracker or an expired token would
|
|
@@ -140,10 +180,30 @@ const boardIsFreeText = computed(
|
|
|
140
180
|
() => !hunt.boardsLoading && hunt.boardsErrorReason === BOARDS_UNSUPPORTED,
|
|
141
181
|
)
|
|
142
182
|
|
|
183
|
+
/**
|
|
184
|
+
* The refusals this surface words ITSELF, keyed on the backend's reason.
|
|
185
|
+
*
|
|
186
|
+
* The backend does not localize prose, so a reason with no entry here renders the server's
|
|
187
|
+
* untranslated English — the honest last resort for a cause this modal was not built to explain,
|
|
188
|
+
* and the wrong answer for one it was. Both entries are reachable only from a client that
|
|
189
|
+
* disagrees with the backend about which sources are repo-backed (a stale SPA build, a raced
|
|
190
|
+
* `repoBacked` read), which is exactly when a user is least served by raw backend prose.
|
|
191
|
+
* `repo_not_linked` is deliberately absent: it is not a message but a warning rendered beside the
|
|
192
|
+
* scope it invalidates.
|
|
193
|
+
*/
|
|
194
|
+
const REFUSAL_KEYS: Partial<Record<TaskSourceReadReason, string>> = {
|
|
195
|
+
board_from_service: 'bugHunt.refusal.boardFromService',
|
|
196
|
+
missing_board: 'bugHunt.refusal.missingBoard',
|
|
197
|
+
}
|
|
198
|
+
function refusalText(reason: string | null, fallback: string | null): string | null {
|
|
199
|
+
const key = reason ? REFUSAL_KEYS[reason as TaskSourceReadReason] : undefined
|
|
200
|
+
return key ? t(key) : fallback
|
|
201
|
+
}
|
|
202
|
+
|
|
143
203
|
/** A board read that failed for a reason the user has to fix — shown, never silently swallowed. */
|
|
144
204
|
const boardsFailure = computed(() =>
|
|
145
205
|
!hunt.boardsLoading && hunt.boardsError !== null && !boardIsFreeText.value
|
|
146
|
-
? hunt.boardsError
|
|
206
|
+
? refusalText(hunt.boardsErrorReason, hunt.boardsError)
|
|
147
207
|
: null,
|
|
148
208
|
)
|
|
149
209
|
|
|
@@ -157,7 +217,17 @@ function createdAtDate(createdAt: string): Date | null {
|
|
|
157
217
|
return Number.isNaN(parsed.getTime()) ? null : parsed
|
|
158
218
|
}
|
|
159
219
|
|
|
160
|
-
|
|
220
|
+
/** The scan this form currently describes, or null while it does not describe one. */
|
|
221
|
+
const request = computed(() =>
|
|
222
|
+
huntRequest({
|
|
223
|
+
source: descriptor.value,
|
|
224
|
+
containerId: containerId.value,
|
|
225
|
+
board: boardId.value,
|
|
226
|
+
issueType: issueType.value,
|
|
227
|
+
labels: labels.value,
|
|
228
|
+
}),
|
|
229
|
+
)
|
|
230
|
+
const canHunt = computed(() => request.value !== null)
|
|
161
231
|
|
|
162
232
|
watch(open, (isOpen) => {
|
|
163
233
|
if (!isOpen) return
|
|
@@ -168,7 +238,7 @@ watch(open, (isOpen) => {
|
|
|
168
238
|
awaitingConnect.value = null
|
|
169
239
|
source.value = ui.bugHunt?.source ?? tasks.offeredSources[0]?.source ?? undefined
|
|
170
240
|
resetContainer()
|
|
171
|
-
|
|
241
|
+
loadBoardsFor(source.value)
|
|
172
242
|
})
|
|
173
243
|
|
|
174
244
|
// Switching tracker invalidates both the board list and any ranking already on screen: the
|
|
@@ -176,24 +246,40 @@ watch(open, (isOpen) => {
|
|
|
176
246
|
watch(source, (next) => {
|
|
177
247
|
boardId.value = ''
|
|
178
248
|
hunt.reset()
|
|
179
|
-
|
|
249
|
+
loadBoardsFor(next)
|
|
180
250
|
})
|
|
181
251
|
|
|
252
|
+
// For a repo-backed tracker the service IS the board, so moving the hunt to another service moves
|
|
253
|
+
// it to another repository: the shortlist on screen belongs to the old one and must go with it.
|
|
254
|
+
// A repo-less tracker keeps its results, since the container only decides where an adopted bug
|
|
255
|
+
// lands and the scan is still of the board that was asked for.
|
|
256
|
+
watch(containerId, () => {
|
|
257
|
+
if (boardFromService.value) hunt.reset()
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Boards are listed only for a tracker that HAS a board to choose; asking otherwise is refused
|
|
262
|
+
* server-side. The other branch is not a no-op: the previous tracker's list (or the warning its
|
|
263
|
+
* failed listing left behind) has to go, or it renders under a tracker with no board field.
|
|
264
|
+
*/
|
|
265
|
+
function loadBoardsFor(next: TaskSourceKind | undefined) {
|
|
266
|
+
if (!next) return
|
|
267
|
+
if (isBoardFromService(tasks.descriptorFor(next))) {
|
|
268
|
+
hunt.dropBoards(next)
|
|
269
|
+
// The repo projection is lazy and nothing on the board opens it, so the field that names the
|
|
270
|
+
// repository this hunt will read asks for it here — only on the branch that has one.
|
|
271
|
+
void github.ensureLoaded().catch(() => {})
|
|
272
|
+
} else hunt.loadBoards(next)
|
|
273
|
+
}
|
|
274
|
+
|
|
182
275
|
async function runHunt() {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
.filter((l) => l.length > 0)
|
|
188
|
-
const ok = await hunt.hunt(source.value, {
|
|
189
|
-
board: boardId.value.trim(),
|
|
190
|
-
...(issueType.value.trim() ? { issueType: issueType.value.trim() } : {}),
|
|
191
|
-
...(parsedLabels.length ? { labels: parsedLabels } : {}),
|
|
192
|
-
})
|
|
193
|
-
if (!ok) {
|
|
276
|
+
const input = request.value
|
|
277
|
+
if (!source.value || !input) return
|
|
278
|
+
const ok = await hunt.hunt(source.value, input)
|
|
279
|
+
if (!ok && !huntNeedsRepo.value) {
|
|
194
280
|
toast.add({
|
|
195
281
|
title: t('bugHunt.huntFailed'),
|
|
196
|
-
description: hunt.huntError ?? undefined,
|
|
282
|
+
description: refusalText(hunt.huntErrorReason, hunt.huntError) ?? undefined,
|
|
197
283
|
icon: 'i-lucide-triangle-alert',
|
|
198
284
|
color: 'error',
|
|
199
285
|
})
|
|
@@ -306,10 +392,21 @@ const STATUS_KEYS: Record<BugHuntAnalysisStatus, string> = {
|
|
|
306
392
|
</UFormField>
|
|
307
393
|
|
|
308
394
|
<UFormField :label="t('bugHunt.board')">
|
|
395
|
+
<!-- This tracker's issues belong to one repository per service, so the board is
|
|
396
|
+
STATED rather than asked: the repository the service below is linked to. No
|
|
397
|
+
control at all, because every value one could offer here is either that repo
|
|
398
|
+
(nothing to choose) or another one this board holds no service for. -->
|
|
399
|
+
<p
|
|
400
|
+
v-if="boardFromService"
|
|
401
|
+
class="flex items-center gap-1.5 py-1 text-sm text-slate-300"
|
|
402
|
+
>
|
|
403
|
+
<UIcon name="i-lucide-folder-git-2" class="h-4 w-4 shrink-0" />
|
|
404
|
+
<span class="truncate">{{ scopedRepo ?? t('bugHunt.boardFromService') }}</span>
|
|
405
|
+
</p>
|
|
309
406
|
<!-- A tracker that can't enumerate its boards gets a free-text field rather than
|
|
310
407
|
an empty picker, so the hunt is still usable. -->
|
|
311
408
|
<UInput
|
|
312
|
-
v-if="boardIsFreeText"
|
|
409
|
+
v-else-if="boardIsFreeText"
|
|
313
410
|
v-model="boardId"
|
|
314
411
|
:placeholder="t('bugHunt.boardPlaceholder')"
|
|
315
412
|
class="w-full"
|
|
@@ -322,9 +419,14 @@ const STATUS_KEYS: Record<BugHuntAnalysisStatus, string> = {
|
|
|
322
419
|
:placeholder="t('bugHunt.pickBoard')"
|
|
323
420
|
class="w-full"
|
|
324
421
|
/>
|
|
422
|
+
<!-- The service holds no repository, so there are no issues to read. Said here
|
|
423
|
+
rather than in a toast: it invalidates the scope named right above it. -->
|
|
424
|
+
<p v-if="huntNeedsRepo" class="mt-1 text-xs text-amber-400">
|
|
425
|
+
{{ t('bugHunt.boardNeedsRepo') }}
|
|
426
|
+
</p>
|
|
325
427
|
<!-- A board read that failed for a fixable reason (unreachable site, expired
|
|
326
428
|
token): named, so the user isn't left with an empty picker and no cause. -->
|
|
327
|
-
<p v-if="boardsFailure" class="mt-1 text-xs text-amber-400">
|
|
429
|
+
<p v-else-if="boardsFailure" class="mt-1 text-xs text-amber-400">
|
|
328
430
|
{{ t('bugHunt.boardsFailed', { reason: boardsFailure }) }}
|
|
329
431
|
</p>
|
|
330
432
|
</UFormField>
|
|
@@ -347,16 +449,32 @@ const STATUS_KEYS: Record<BugHuntAnalysisStatus, string> = {
|
|
|
347
449
|
</UFormField>
|
|
348
450
|
</div>
|
|
349
451
|
|
|
350
|
-
<!-- Where an adopted bug lands
|
|
351
|
-
|
|
352
|
-
|
|
452
|
+
<!-- Where an adopted bug lands, and on a repo-backed tracker WHICH REPOSITORY is
|
|
453
|
+
scanned, so the wording says both rather than leaving the scope unexplained. Stated
|
|
454
|
+
when the frame this hunt was opened from is the only legal target; a choice (scoped
|
|
455
|
+
to that frame) when it has modules, or over the whole board when the hunt was opened
|
|
456
|
+
standalone. -->
|
|
457
|
+
<!-- Two blocks rather than one with a computed `keypath`: the i18n extractor reads a
|
|
458
|
+
bound keypath as the key itself, so a dynamic one is a key missing from every
|
|
459
|
+
locale. Every other `<i18n-t>` in the SPA names its key statically for that reason. -->
|
|
353
460
|
<p v-if="containerStated" class="text-xs text-slate-400">
|
|
354
|
-
<i18n-t keypath="bugHunt.
|
|
461
|
+
<i18n-t v-if="boardFromService" keypath="bugHunt.huntingIn" tag="span" scope="global">
|
|
462
|
+
<template #container>
|
|
463
|
+
<span class="font-medium text-slate-200">{{ pinnedContainer!.title }}</span>
|
|
464
|
+
</template>
|
|
465
|
+
</i18n-t>
|
|
466
|
+
<i18n-t v-else keypath="bugHunt.adoptingInto" tag="span" scope="global">
|
|
355
467
|
<template #container>
|
|
356
468
|
<span class="font-medium text-slate-200">{{ pinnedContainer!.title }}</span>
|
|
357
469
|
</template>
|
|
358
470
|
</i18n-t>
|
|
359
471
|
</p>
|
|
472
|
+
<!-- Two fields rather than one with a computed key, for the reason the two blocks above
|
|
473
|
+
are two: the i18n extractor reads a bound key as the key itself, so a dynamic one
|
|
474
|
+
leaves BOTH real keys unreferenced and a dead-key sweep prunes them. -->
|
|
475
|
+
<UFormField v-else-if="boardFromService" :label="t('bugHunt.huntIn')">
|
|
476
|
+
<USelect v-model="containerId" :items="containerItems" class="w-full" />
|
|
477
|
+
</UFormField>
|
|
360
478
|
<UFormField v-else :label="t('bugHunt.adoptInto')">
|
|
361
479
|
<USelect v-model="containerId" :items="containerItems" class="w-full" />
|
|
362
480
|
</UFormField>
|
|
@@ -379,6 +497,12 @@ const STATUS_KEYS: Record<BugHuntAnalysisStatus, string> = {
|
|
|
379
497
|
<!-- Results -->
|
|
380
498
|
<div v-if="hunt.hasResult" class="space-y-3 border-t border-slate-800 pt-3">
|
|
381
499
|
<p class="text-xs text-slate-400">
|
|
500
|
+
<!-- The board the scan actually ran against, named because on a repo-backed tracker
|
|
501
|
+
the platform resolved it: the person reading the shortlist should not have to
|
|
502
|
+
infer which repository it came out of. -->
|
|
503
|
+
<span class="text-slate-500">
|
|
504
|
+
{{ t('bugHunt.scannedBoard', { board: hunt.result!.board }) }}
|
|
505
|
+
</span>
|
|
382
506
|
{{ t(STATUS_KEYS[hunt.result!.analysisStatus]) }}
|
|
383
507
|
<span v-if="hunt.result!.model" class="text-slate-500">
|
|
384
508
|
{{ t('bugHunt.viaModel', { model: hunt.result!.model }) }}
|
|
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
|
2
2
|
import { useBugHuntStore } from '~/stores/bugHunt'
|
|
3
3
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
4
4
|
import { ApiError } from '~/composables/api/errors'
|
|
5
|
-
import type { TaskSourceKind, TrackerBoardsView } from '~/types/domain'
|
|
5
|
+
import type { BugHuntResult, TaskSourceKind, TrackerBoardsView } from '~/types/domain'
|
|
6
6
|
|
|
7
7
|
// What the board picker does with a FAILED board read. Only one failure means "this tracker
|
|
8
8
|
// cannot enumerate boards, so type one in"; every other failure has to stay visible as an error,
|
|
@@ -23,20 +23,43 @@ const boardsUnsupported = () =>
|
|
|
23
23
|
function stubApi(): {
|
|
24
24
|
store: ReturnType<typeof useBugHuntStore>
|
|
25
25
|
serve: (fn: () => Promise<unknown>) => void
|
|
26
|
+
serveHunt: (fn: () => Promise<unknown>) => void
|
|
26
27
|
} {
|
|
27
28
|
let handler: () => Promise<unknown> = () => Promise.resolve({ source: 'jira', boards: [] })
|
|
29
|
+
let huntHandler: () => Promise<unknown> = () => Promise.resolve(huntResult())
|
|
28
30
|
vi.stubGlobal('useApi', () => ({
|
|
29
31
|
listTrackerBoards: (_ws: string, _source: TaskSourceKind) =>
|
|
30
32
|
handler() as Promise<TrackerBoardsView>,
|
|
33
|
+
runBugHunt: (_ws: string, _source: TaskSourceKind, _input: unknown) =>
|
|
34
|
+
huntHandler() as Promise<BugHuntResult>,
|
|
31
35
|
}))
|
|
32
36
|
return {
|
|
33
37
|
store: useBugHuntStore(),
|
|
34
38
|
serve: (fn) => {
|
|
35
39
|
handler = fn
|
|
36
40
|
},
|
|
41
|
+
serveHunt: (fn) => {
|
|
42
|
+
huntHandler = fn
|
|
43
|
+
},
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
|
|
47
|
+
/** An empty but well-formed scan result, so a success case asserts on the store, not the shape. */
|
|
48
|
+
function huntResult(): BugHuntResult {
|
|
49
|
+
return {
|
|
50
|
+
source: 'github',
|
|
51
|
+
board: 'acme/web',
|
|
52
|
+
analysisStatus: 'empty',
|
|
53
|
+
model: null,
|
|
54
|
+
candidates: [],
|
|
55
|
+
scanned: 0,
|
|
56
|
+
truncated: false,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The one scan input shape the modal builds; the store only passes it through. */
|
|
61
|
+
const SCAN = { containerId: 'blk_auth', board: null }
|
|
62
|
+
|
|
40
63
|
describe('bug hunt store — board listing failures', () => {
|
|
41
64
|
beforeEach(() => {
|
|
42
65
|
useWorkspaceStore().workspaceId = 'ws1'
|
|
@@ -79,6 +102,22 @@ describe('bug hunt store — board listing failures', () => {
|
|
|
79
102
|
expect(store.boards.map((b) => b.id)).toEqual(['t1'])
|
|
80
103
|
})
|
|
81
104
|
|
|
105
|
+
it('drops the previous tracker failure when the next one has no board to list', async () => {
|
|
106
|
+
// A repo-backed tracker renders no board field at all, so a stale "boards could not be
|
|
107
|
+
// loaded" warning would sit under a control that is not there, blaming this tracker for the
|
|
108
|
+
// last one's outage.
|
|
109
|
+
const { store, serve } = stubApi()
|
|
110
|
+
serve(() => Promise.reject(apiError(502, 'upstream')))
|
|
111
|
+
await store.loadBoards('jira')
|
|
112
|
+
|
|
113
|
+
store.dropBoards('github')
|
|
114
|
+
|
|
115
|
+
expect(store.boardsSource).toBe('github')
|
|
116
|
+
expect(store.boards).toEqual([])
|
|
117
|
+
expect(store.boardsError).toBeNull()
|
|
118
|
+
expect(store.boardsErrorReason).toBeNull()
|
|
119
|
+
})
|
|
120
|
+
|
|
82
121
|
it('a source switch mid-flight never lands the older tracker failure on the newer one', async () => {
|
|
83
122
|
const { store, serve } = stubApi()
|
|
84
123
|
let rejectJira!: (e: unknown) => void
|
|
@@ -100,4 +139,86 @@ describe('bug hunt store — board listing failures', () => {
|
|
|
100
139
|
expect(store.boardsErrorReason).toBeNull()
|
|
101
140
|
expect(store.boardsError).toBeNull()
|
|
102
141
|
})
|
|
142
|
+
|
|
143
|
+
it('leaves nothing loading when the next tracker has no board to list', async () => {
|
|
144
|
+
// The abandoned listing's own `finally` will not run until it settles, which for a hanging
|
|
145
|
+
// tracker is never. A picker (or a Hunt button) gated on the flag would wait on a request
|
|
146
|
+
// nobody is waiting for.
|
|
147
|
+
const { store, serve } = stubApi()
|
|
148
|
+
serve(() => new Promise(() => {}))
|
|
149
|
+
void store.loadBoards('jira')
|
|
150
|
+
expect(store.boardsLoading).toBe(true)
|
|
151
|
+
|
|
152
|
+
store.dropBoards('github')
|
|
153
|
+
|
|
154
|
+
expect(store.boardsLoading).toBe(false)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('a superseded listing never reports the tracker now loading as done', async () => {
|
|
158
|
+
const { store, serve } = stubApi()
|
|
159
|
+
let settleJira!: (v: unknown) => void
|
|
160
|
+
serve(() => new Promise((res) => (settleJira = res)))
|
|
161
|
+
const inFlight = store.loadBoards('jira')
|
|
162
|
+
|
|
163
|
+
serve(() => new Promise(() => {}))
|
|
164
|
+
void store.loadBoards('linear')
|
|
165
|
+
settleJira({ source: 'jira', boards: [] })
|
|
166
|
+
await inFlight
|
|
167
|
+
|
|
168
|
+
expect(store.boardsSource).toBe('linear')
|
|
169
|
+
expect(store.boardsLoading).toBe(true)
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
describe('bug hunt store — scan failures', () => {
|
|
174
|
+
beforeEach(() => {
|
|
175
|
+
useWorkspaceStore().workspaceId = 'ws1'
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('keeps the backend reason for the one failure the surface words itself', async () => {
|
|
179
|
+
// `repo_not_linked` names something fixable on this board, so the modal states it beside the
|
|
180
|
+
// scope it invalidates instead of raising a toast. That routing reads ONLY this field.
|
|
181
|
+
const { store, serveHunt } = stubApi()
|
|
182
|
+
serveHunt(() => Promise.reject(apiError(422, 'validation', { reason: 'repo_not_linked' })))
|
|
183
|
+
|
|
184
|
+
expect(await store.hunt('github', SCAN)).toBe(false)
|
|
185
|
+
|
|
186
|
+
expect(store.huntErrorReason).toBe('repo_not_linked')
|
|
187
|
+
expect(store.huntError).toBeTruthy()
|
|
188
|
+
expect(store.result).toBeNull()
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('records NO reason for a scan that simply failed, so it stays a toast', async () => {
|
|
192
|
+
const { store, serveHunt } = stubApi()
|
|
193
|
+
serveHunt(() => Promise.reject(apiError(502, 'upstream')))
|
|
194
|
+
|
|
195
|
+
expect(await store.hunt('jira', { containerId: 'blk_auth', board: 'PROJ' })).toBe(false)
|
|
196
|
+
|
|
197
|
+
expect(store.huntErrorReason).toBeNull()
|
|
198
|
+
expect(store.huntError).toBeTruthy()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('clears a previous scan failure once a later scan succeeds', async () => {
|
|
202
|
+
const { store, serveHunt } = stubApi()
|
|
203
|
+
serveHunt(() => Promise.reject(apiError(422, 'validation', { reason: 'repo_not_linked' })))
|
|
204
|
+
await store.hunt('github', SCAN)
|
|
205
|
+
|
|
206
|
+
serveHunt(() => Promise.resolve(huntResult()))
|
|
207
|
+
expect(await store.hunt('github', SCAN)).toBe(true)
|
|
208
|
+
|
|
209
|
+
expect(store.huntError).toBeNull()
|
|
210
|
+
expect(store.huntErrorReason).toBeNull()
|
|
211
|
+
expect(store.result?.board).toBe('acme/web')
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('drops the reason on reset, so a re-opened hunt never re-states the old refusal', async () => {
|
|
215
|
+
const { store, serveHunt } = stubApi()
|
|
216
|
+
serveHunt(() => Promise.reject(apiError(422, 'validation', { reason: 'repo_not_linked' })))
|
|
217
|
+
await store.hunt('github', SCAN)
|
|
218
|
+
|
|
219
|
+
store.reset()
|
|
220
|
+
|
|
221
|
+
expect(store.huntErrorReason).toBeNull()
|
|
222
|
+
expect(store.huntError).toBeNull()
|
|
223
|
+
})
|
|
103
224
|
})
|
package/app/stores/bugHunt.ts
CHANGED
|
@@ -38,6 +38,13 @@ export const useBugHuntStore = defineStore('bugHunt', () => {
|
|
|
38
38
|
const result = ref<BugHuntResult | null>(null)
|
|
39
39
|
const hunting = ref(false)
|
|
40
40
|
const huntError = ref<string | null>(null)
|
|
41
|
+
/**
|
|
42
|
+
* The backend's machine-readable reason for a failed scan, kept for the same reason
|
|
43
|
+
* {@link boardsErrorReason} is: `repo_not_linked` says the service this hunt is scoped to has
|
|
44
|
+
* no repository to read issues from, which is a state the person can fix on the board and the
|
|
45
|
+
* only one the surface words itself. Every other failure stays a toast.
|
|
46
|
+
*/
|
|
47
|
+
const huntErrorReason = ref<string | null>(null)
|
|
41
48
|
/** The candidate currently being adopted, so only its own row shows a spinner. */
|
|
42
49
|
const adopting = ref<string | null>(null)
|
|
43
50
|
|
|
@@ -62,20 +69,44 @@ export const useBugHuntStore = defineStore('bugHunt', () => {
|
|
|
62
69
|
boardsError.value = e instanceof Error ? e.message : String(e)
|
|
63
70
|
boardsErrorReason.value = apiErrorReason(e)
|
|
64
71
|
} finally {
|
|
65
|
-
|
|
72
|
+
// Only the listing the picker is still showing owns the flag. A superseded one clearing it
|
|
73
|
+
// unconditionally would report the tracker now being loaded as done, which is the same
|
|
74
|
+
// mistake in the opposite direction from the one `dropBoards` avoids.
|
|
75
|
+
if (boardsSource.value === source) boardsLoading.value = false
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Forget the board listing, because the tracker now in the picker has none to offer: its board
|
|
81
|
+
* is the chosen service's repository, resolved server-side. Called INSTEAD of `loadBoards`, so a
|
|
82
|
+
* previous tracker's list (or its failure, which the surface renders as a warning) cannot sit
|
|
83
|
+
* under a tracker that has no board field at all. `boardsSource` moves with it, so a listing
|
|
84
|
+
* still in flight for that previous tracker lands on nothing rather than reviving the picker.
|
|
85
|
+
*/
|
|
86
|
+
function dropBoards(source: TaskSourceKind): void {
|
|
87
|
+
boardsSource.value = source
|
|
88
|
+
boards.value = []
|
|
89
|
+
boardsError.value = null
|
|
90
|
+
boardsErrorReason.value = null
|
|
91
|
+
// The listing in flight belongs to the tracker being left, and its `finally` will not run
|
|
92
|
+
// until it settles — indefinitely, if that tracker hangs. Clearing the flag with the rest of
|
|
93
|
+
// the state is what makes "land on nothing" true of the WHOLE listing rather than of four of
|
|
94
|
+
// its five fields, so a reader gating on it cannot wait on a request nobody is waiting for.
|
|
95
|
+
boardsLoading.value = false
|
|
96
|
+
}
|
|
97
|
+
|
|
69
98
|
/** Run a hunt and keep its ranked result. Returns false when the scan itself failed. */
|
|
70
99
|
async function hunt(source: TaskSourceKind, input: RunBugHuntInput): Promise<boolean> {
|
|
71
100
|
hunting.value = true
|
|
72
101
|
huntError.value = null
|
|
102
|
+
huntErrorReason.value = null
|
|
73
103
|
try {
|
|
74
104
|
result.value = await api.runBugHunt(workspace.requireId(), source, input)
|
|
75
105
|
return true
|
|
76
106
|
} catch (e) {
|
|
77
107
|
result.value = null
|
|
78
108
|
huntError.value = e instanceof Error ? e.message : String(e)
|
|
109
|
+
huntErrorReason.value = apiErrorReason(e)
|
|
79
110
|
return false
|
|
80
111
|
} finally {
|
|
81
112
|
hunting.value = false
|
|
@@ -117,6 +148,7 @@ export const useBugHuntStore = defineStore('bugHunt', () => {
|
|
|
117
148
|
function reset(): void {
|
|
118
149
|
result.value = null
|
|
119
150
|
huntError.value = null
|
|
151
|
+
huntErrorReason.value = null
|
|
120
152
|
adopting.value = null
|
|
121
153
|
}
|
|
122
154
|
|
|
@@ -131,8 +163,10 @@ export const useBugHuntStore = defineStore('bugHunt', () => {
|
|
|
131
163
|
hasResult,
|
|
132
164
|
hunting,
|
|
133
165
|
huntError,
|
|
166
|
+
huntErrorReason,
|
|
134
167
|
adopting,
|
|
135
168
|
loadBoards,
|
|
169
|
+
dropBoards,
|
|
136
170
|
hunt,
|
|
137
171
|
adopt,
|
|
138
172
|
reset,
|
package/app/stores/tasks.spec.ts
CHANGED
|
@@ -34,6 +34,8 @@ const jiraDescriptor: TaskSourceState = {
|
|
|
34
34
|
enabled: true,
|
|
35
35
|
supportsIntake: true,
|
|
36
36
|
ignoredIntakePredicates: [],
|
|
37
|
+
// Jira issues belong to a project, not a repository, so a hunt on it picks a board.
|
|
38
|
+
repoBacked: false,
|
|
37
39
|
// Jira carries its own credentials, so it rides no VCS connection.
|
|
38
40
|
ridesVcsProvider: null,
|
|
39
41
|
}
|
|
@@ -26,6 +26,7 @@ export const FAILURE_KIND_KEYS: Record<AgentFailureKind, string> = {
|
|
|
26
26
|
dispatch: 'platformObservability.failureKind.dispatch',
|
|
27
27
|
environment: 'platformObservability.failureKind.environment',
|
|
28
28
|
evicted: 'platformObservability.failureKind.evicted',
|
|
29
|
+
harness_shutdown: 'platformObservability.failureKind.harness_shutdown',
|
|
29
30
|
timeout: 'platformObservability.failureKind.timeout',
|
|
30
31
|
agent: 'platformObservability.failureKind.agent',
|
|
31
32
|
job_failed: 'platformObservability.failureKind.job_failed',
|
package/i18n/locales/de.json
CHANGED
|
@@ -3848,6 +3848,7 @@
|
|
|
3848
3848
|
"dispatch": "Zustellung",
|
|
3849
3849
|
"environment": "Umgebung",
|
|
3850
3850
|
"evicted": "Verdrängt",
|
|
3851
|
+
"harness_shutdown": "Harness beendet",
|
|
3851
3852
|
"timeout": "Zeitüberschreitung",
|
|
3852
3853
|
"agent": "Agent",
|
|
3853
3854
|
"job_failed": "Job fehlgeschlagen",
|
|
@@ -4296,7 +4297,9 @@
|
|
|
4296
4297
|
"tracker": "Tracker",
|
|
4297
4298
|
"board": "Board",
|
|
4298
4299
|
"pickBoard": "Board auswählen",
|
|
4299
|
-
"boardPlaceholder": "Projektschlüssel
|
|
4300
|
+
"boardPlaceholder": "Projektschlüssel oder Team-ID",
|
|
4301
|
+
"boardFromService": "Das mit diesem Service verknüpfte Repository",
|
|
4302
|
+
"boardNeedsRepo": "Mit diesem Service ist kein Repository verknüpft, es gibt also keine Tickets zu durchsuchen. Verknüpfe eines im Service-Panel.",
|
|
4300
4303
|
"boardsFailed": "Boards konnten nicht geladen werden: {reason}",
|
|
4301
4304
|
"issueType": "Vorgangstyp",
|
|
4302
4305
|
"issueTypeHelp": "Standard ist bug. Wird von Trackern ohne Vorgangstypen ignoriert.",
|
|
@@ -4305,6 +4308,8 @@
|
|
|
4305
4308
|
"labelsHelp": "Durch Komma getrennt. Alle müssen vorhanden sein.",
|
|
4306
4309
|
"adoptInto": "Ausgewählten Fehler hinzufügen zu",
|
|
4307
4310
|
"adoptingInto": "Der ausgewählte Fehler landet in {container}",
|
|
4311
|
+
"huntIn": "Zu durchsuchender Service",
|
|
4312
|
+
"huntingIn": "Durchsucht wird das mit {container} verknüpfte Repository; dort landet auch der ausgewählte Fehler.",
|
|
4308
4313
|
"run": "Jagen",
|
|
4309
4314
|
"running": "Board wird gelesen und die Funde werden bewertet…",
|
|
4310
4315
|
"huntFailed": "Die Jagd ist fehlgeschlagen",
|
|
@@ -4317,6 +4322,7 @@
|
|
|
4317
4322
|
"noCandidates": "Auf diesem Board gab es keine offenen, nicht zugewiesenen Fehler.",
|
|
4318
4323
|
"ratings": "Auswirkung {impact}/5, Aufwand {complexity}/5, Konfidenz {confidence}",
|
|
4319
4324
|
"viaModel": "Bewertet von {model}.",
|
|
4325
|
+
"scannedBoard": "{board} durchsucht.",
|
|
4320
4326
|
"truncated": "Es wurden nur die ersten {count} passenden Fehler geprüft; dieses Board enthält mehr.",
|
|
4321
4327
|
"comments": "{count} Kommentar | {count} Kommentare",
|
|
4322
4328
|
"confidence": {
|
|
@@ -4330,6 +4336,10 @@
|
|
|
4330
4336
|
"failed": "Die Bewertung konnte nicht abgeschlossen werden, daher sind diese unbewertet.",
|
|
4331
4337
|
"over_budget": "Dieser Arbeitsbereich hat sein Ausgabenbudget überschritten, daher wurden diese nicht bewertet.",
|
|
4332
4338
|
"empty": "Nichts zu bewerten."
|
|
4339
|
+
},
|
|
4340
|
+
"refusal": {
|
|
4341
|
+
"boardFromService": "Dieser Tracker durchsucht das Repository, mit dem der gewählte Service verknüpft ist, und hat daher kein eigenes Board.",
|
|
4342
|
+
"missingBoard": "Wähle das Board aus, auf dem gesucht werden soll."
|
|
4333
4343
|
}
|
|
4334
4344
|
},
|
|
4335
4345
|
"pipeline": {
|
package/i18n/locales/en.json
CHANGED
|
@@ -1892,6 +1892,7 @@
|
|
|
1892
1892
|
"dispatch": "Dispatch",
|
|
1893
1893
|
"environment": "Environment",
|
|
1894
1894
|
"evicted": "Evicted",
|
|
1895
|
+
"harness_shutdown": "Harness shut down",
|
|
1895
1896
|
"timeout": "Timeout",
|
|
1896
1897
|
"agent": "Agent",
|
|
1897
1898
|
"job_failed": "Job failed",
|
|
@@ -4897,7 +4898,9 @@
|
|
|
4897
4898
|
"tracker": "Tracker",
|
|
4898
4899
|
"board": "Board",
|
|
4899
4900
|
"pickBoard": "Pick a board",
|
|
4900
|
-
"boardPlaceholder": "Project key
|
|
4901
|
+
"boardPlaceholder": "Project key or team id",
|
|
4902
|
+
"boardFromService": "The repository this service is linked to",
|
|
4903
|
+
"boardNeedsRepo": "This service has no repository linked, so it has no issues to hunt. Link one from the service panel.",
|
|
4901
4904
|
"boardsFailed": "Boards could not be loaded: {reason}",
|
|
4902
4905
|
"issueType": "Issue type",
|
|
4903
4906
|
"issueTypeHelp": "Defaults to bug. Ignored by trackers with no issue types.",
|
|
@@ -4906,6 +4909,8 @@
|
|
|
4906
4909
|
"labelsHelp": "Comma separated. All of them must be present.",
|
|
4907
4910
|
"adoptInto": "Add the picked bug to",
|
|
4908
4911
|
"adoptingInto": "The picked bug lands in {container}",
|
|
4912
|
+
"huntIn": "Service to hunt",
|
|
4913
|
+
"huntingIn": "Hunting the repository linked to {container}; the picked bug lands there too.",
|
|
4909
4914
|
"run": "Hunt",
|
|
4910
4915
|
"running": "Reading the board and rating what it finds…",
|
|
4911
4916
|
"huntFailed": "The hunt failed",
|
|
@@ -4918,6 +4923,7 @@
|
|
|
4918
4923
|
"noCandidates": "No open, unassigned bugs matched on this board.",
|
|
4919
4924
|
"ratings": "Impact {impact}/5, complexity {complexity}/5, {confidence} confidence",
|
|
4920
4925
|
"viaModel": "Rated by {model}.",
|
|
4926
|
+
"scannedBoard": "Scanned {board}.",
|
|
4921
4927
|
"truncated": "Only the first {count} matching bugs were scanned; this board holds more.",
|
|
4922
4928
|
"comments": "{count} comment | {count} comments",
|
|
4923
4929
|
"@comments": {
|
|
@@ -4934,6 +4940,10 @@
|
|
|
4934
4940
|
"failed": "The rating could not be completed, so these are unrated.",
|
|
4935
4941
|
"over_budget": "This workspace is over its spend budget, so these were not rated.",
|
|
4936
4942
|
"empty": "Nothing to rate."
|
|
4943
|
+
},
|
|
4944
|
+
"refusal": {
|
|
4945
|
+
"boardFromService": "This tracker scans the repository the chosen service is linked to, so it has no board of its own.",
|
|
4946
|
+
"missingBoard": "Pick the board to hunt on."
|
|
4937
4947
|
}
|
|
4938
4948
|
},
|
|
4939
4949
|
"pipeline": {
|
package/i18n/locales/es.json
CHANGED
|
@@ -1794,6 +1794,7 @@
|
|
|
1794
1794
|
"dispatch": "Despacho",
|
|
1795
1795
|
"environment": "Entorno",
|
|
1796
1796
|
"evicted": "Desalojada",
|
|
1797
|
+
"harness_shutdown": "Harness detenido",
|
|
1797
1798
|
"timeout": "Tiempo agotado",
|
|
1798
1799
|
"agent": "Agente",
|
|
1799
1800
|
"job_failed": "Trabajo fallido",
|
|
@@ -4739,7 +4740,9 @@
|
|
|
4739
4740
|
"tracker": "Gestor de incidencias",
|
|
4740
4741
|
"board": "Tablero",
|
|
4741
4742
|
"pickBoard": "Elige un tablero",
|
|
4742
|
-
"boardPlaceholder": "Clave de proyecto
|
|
4743
|
+
"boardPlaceholder": "Clave de proyecto o id de equipo",
|
|
4744
|
+
"boardFromService": "El repositorio vinculado a este servicio",
|
|
4745
|
+
"boardNeedsRepo": "Este servicio no tiene ningún repositorio vinculado, así que no hay incidencias que explorar. Vincula uno desde el panel del servicio.",
|
|
4743
4746
|
"boardsFailed": "No se pudieron cargar los tableros: {reason}",
|
|
4744
4747
|
"issueType": "Tipo de incidencia",
|
|
4745
4748
|
"issueTypeHelp": "Por defecto bug. Se ignora en gestores sin tipos de incidencia.",
|
|
@@ -4748,6 +4751,8 @@
|
|
|
4748
4751
|
"labelsHelp": "Separadas por comas. Todas deben estar presentes.",
|
|
4749
4752
|
"adoptInto": "Añadir el error elegido a",
|
|
4750
4753
|
"adoptingInto": "El error elegido se añade a {container}",
|
|
4754
|
+
"huntIn": "Servicio a explorar",
|
|
4755
|
+
"huntingIn": "Se explora el repositorio vinculado a {container}; el error elegido también se añade ahí.",
|
|
4751
4756
|
"run": "Cazar",
|
|
4752
4757
|
"running": "Leyendo el tablero y valorando lo que encuentra…",
|
|
4753
4758
|
"huntFailed": "La caza ha fallado",
|
|
@@ -4760,6 +4765,7 @@
|
|
|
4760
4765
|
"noCandidates": "No hay errores abiertos y sin asignar que coincidan en este tablero.",
|
|
4761
4766
|
"ratings": "Impacto {impact}/5, complejidad {complexity}/5, confianza {confidence}",
|
|
4762
4767
|
"viaModel": "Valorado por {model}.",
|
|
4768
|
+
"scannedBoard": "Explorado {board}.",
|
|
4763
4769
|
"truncated": "Solo se han explorado los primeros {count} errores coincidentes; este tablero contiene más.",
|
|
4764
4770
|
"comments": "{count} comentario | {count} comentarios",
|
|
4765
4771
|
"confidence": {
|
|
@@ -4773,6 +4779,10 @@
|
|
|
4773
4779
|
"failed": "No se ha podido completar la valoración, así que están sin valorar.",
|
|
4774
4780
|
"over_budget": "Este espacio de trabajo ha superado su presupuesto de gasto, así que no se han valorado.",
|
|
4775
4781
|
"empty": "Nada que valorar."
|
|
4782
|
+
},
|
|
4783
|
+
"refusal": {
|
|
4784
|
+
"boardFromService": "Este rastreador analiza el repositorio al que está vinculado el servicio elegido, así que no tiene un tablero propio.",
|
|
4785
|
+
"missingBoard": "Elige el tablero en el que buscar."
|
|
4776
4786
|
}
|
|
4777
4787
|
},
|
|
4778
4788
|
"pipeline": {
|
package/i18n/locales/fr.json
CHANGED
|
@@ -1794,6 +1794,7 @@
|
|
|
1794
1794
|
"dispatch": "Envoi",
|
|
1795
1795
|
"environment": "Environnement",
|
|
1796
1796
|
"evicted": "Évincée",
|
|
1797
|
+
"harness_shutdown": "Harness arrêté",
|
|
1797
1798
|
"timeout": "Délai dépassé",
|
|
1798
1799
|
"agent": "Agent",
|
|
1799
1800
|
"job_failed": "Tâche échouée",
|
|
@@ -4739,7 +4740,9 @@
|
|
|
4739
4740
|
"tracker": "Gestionnaire de tickets",
|
|
4740
4741
|
"board": "Tableau",
|
|
4741
4742
|
"pickBoard": "Choisir un tableau",
|
|
4742
|
-
"boardPlaceholder": "Clé de projet
|
|
4743
|
+
"boardPlaceholder": "Clé de projet ou id d'équipe",
|
|
4744
|
+
"boardFromService": "Le dépôt lié à ce service",
|
|
4745
|
+
"boardNeedsRepo": "Aucun dépôt n'est lié à ce service : il n'y a donc pas de tickets à explorer. Liez-en un depuis le panneau du service.",
|
|
4743
4746
|
"boardsFailed": "Impossible de charger les tableaux : {reason}",
|
|
4744
4747
|
"issueType": "Type de ticket",
|
|
4745
4748
|
"issueTypeHelp": "Par défaut bug. Ignoré par les gestionnaires sans types de tickets.",
|
|
@@ -4748,6 +4751,8 @@
|
|
|
4748
4751
|
"labelsHelp": "Séparées par des virgules. Toutes doivent être présentes.",
|
|
4749
4752
|
"adoptInto": "Ajouter le bug retenu à",
|
|
4750
4753
|
"adoptingInto": "Le bug retenu est ajouté à {container}",
|
|
4754
|
+
"huntIn": "Service à explorer",
|
|
4755
|
+
"huntingIn": "Exploration du dépôt lié à {container} ; le bug retenu y est ajouté.",
|
|
4751
4756
|
"run": "Chasser",
|
|
4752
4757
|
"running": "Lecture du tableau et évaluation des résultats…",
|
|
4753
4758
|
"huntFailed": "La chasse a échoué",
|
|
@@ -4760,6 +4765,7 @@
|
|
|
4760
4765
|
"noCandidates": "Aucun bug ouvert et non assigné ne correspond sur ce tableau.",
|
|
4761
4766
|
"ratings": "Impact {impact}/5, complexité {complexity}/5, confiance {confidence}",
|
|
4762
4767
|
"viaModel": "Évalué par {model}.",
|
|
4768
|
+
"scannedBoard": "{board} exploré.",
|
|
4763
4769
|
"truncated": "Seuls les {count} premiers bugs correspondants ont été parcourus ; ce tableau en contient davantage.",
|
|
4764
4770
|
"comments": "{count} commentaire | {count} commentaires",
|
|
4765
4771
|
"confidence": {
|
|
@@ -4773,6 +4779,10 @@
|
|
|
4773
4779
|
"failed": "L'évaluation n'a pas pu aboutir, ils sont donc non évalués.",
|
|
4774
4780
|
"over_budget": "Cet espace de travail a dépassé son budget de dépenses, ces bogues n'ont donc pas été évalués.",
|
|
4775
4781
|
"empty": "Rien à évaluer."
|
|
4782
|
+
},
|
|
4783
|
+
"refusal": {
|
|
4784
|
+
"boardFromService": "Ce traqueur analyse le dépôt auquel le service choisi est lié : il n'a donc pas de tableau propre.",
|
|
4785
|
+
"missingBoard": "Choisissez le tableau sur lequel chercher."
|
|
4776
4786
|
}
|
|
4777
4787
|
},
|
|
4778
4788
|
"pipeline": {
|
package/i18n/locales/he.json
CHANGED
|
@@ -1794,6 +1794,7 @@
|
|
|
1794
1794
|
"dispatch": "שיגור",
|
|
1795
1795
|
"environment": "סביבה",
|
|
1796
1796
|
"evicted": "פונתה",
|
|
1797
|
+
"harness_shutdown": "כיבוי ה-Harness",
|
|
1797
1798
|
"timeout": "פסק זמן",
|
|
1798
1799
|
"agent": "סוכן",
|
|
1799
1800
|
"job_failed": "המשימה נכשלה",
|
|
@@ -4739,7 +4740,9 @@
|
|
|
4739
4740
|
"tracker": "מערכת מעקב",
|
|
4740
4741
|
"board": "לוח",
|
|
4741
4742
|
"pickBoard": "בחר לוח",
|
|
4742
|
-
"boardPlaceholder": "מפתח
|
|
4743
|
+
"boardPlaceholder": "מפתח פרויקט או מזהה צוות",
|
|
4744
|
+
"boardFromService": "המאגר המקושר לשירות הזה",
|
|
4745
|
+
"boardNeedsRepo": "לשירות הזה לא מקושר מאגר, ולכן אין בו תקלות לסרוק. קשר מאגר מתוך פאנל השירות.",
|
|
4743
4746
|
"boardsFailed": "לא ניתן היה לטעון את הלוחות: {reason}",
|
|
4744
4747
|
"issueType": "סוג הפנייה",
|
|
4745
4748
|
"issueTypeHelp": "ברירת המחדל היא bug. מתעלמים ממנו במערכות ללא סוגי פניות.",
|
|
@@ -4748,6 +4751,8 @@
|
|
|
4748
4751
|
"labelsHelp": "מופרדות בפסיקים. כולן חייבות להופיע.",
|
|
4749
4752
|
"adoptInto": "הוסף את הבאג הנבחר אל",
|
|
4750
4753
|
"adoptingInto": "הבאג הנבחר יתווסף ל-{container}",
|
|
4754
|
+
"huntIn": "השירות לסריקה",
|
|
4755
|
+
"huntingIn": "נסרק המאגר המקושר ל-{container}; הבאג הנבחר יתווסף לשם.",
|
|
4751
4756
|
"run": "צוד",
|
|
4752
4757
|
"running": "קורא את הלוח ומעריך את מה שנמצא…",
|
|
4753
4758
|
"huntFailed": "הציד נכשל",
|
|
@@ -4760,6 +4765,7 @@
|
|
|
4760
4765
|
"noCandidates": "אין בלוח הזה באגים פתוחים ולא משויכים שתואמים.",
|
|
4761
4766
|
"ratings": "השפעה {impact}/5, מורכבות {complexity}/5, רמת ודאות {confidence}",
|
|
4762
4767
|
"viaModel": "הוערך על ידי {model}.",
|
|
4768
|
+
"scannedBoard": "נסרק {board}.",
|
|
4763
4769
|
"truncated": "נסרקו רק {count} הבאגים התואמים הראשונים; בלוח הזה יש עוד.",
|
|
4764
4770
|
"comments": "תגובה אחת | שתי תגובות | {count} תגובות",
|
|
4765
4771
|
"confidence": {
|
|
@@ -4773,6 +4779,10 @@
|
|
|
4773
4779
|
"failed": "לא ניתן היה להשלים את ההערכה, ולכן אלה אינם מדורגים.",
|
|
4774
4780
|
"over_budget": "סביבת העבודה חרגה מתקציב ההוצאות, ולכן הבאגים האלה לא דורגו.",
|
|
4775
4781
|
"empty": "אין מה להעריך."
|
|
4782
|
+
},
|
|
4783
|
+
"refusal": {
|
|
4784
|
+
"boardFromService": "הגששן סורק את המאגר שאליו מקושר השירות שנבחר, ולכן אין לו לוח משלו.",
|
|
4785
|
+
"missingBoard": "בחרו את הלוח שבו לחפש."
|
|
4776
4786
|
}
|
|
4777
4787
|
},
|
|
4778
4788
|
"pipeline": {
|
package/i18n/locales/it.json
CHANGED
|
@@ -3848,6 +3848,7 @@
|
|
|
3848
3848
|
"dispatch": "Invio",
|
|
3849
3849
|
"environment": "Ambiente",
|
|
3850
3850
|
"evicted": "Sfrattata",
|
|
3851
|
+
"harness_shutdown": "Harness arrestato",
|
|
3851
3852
|
"timeout": "Timeout",
|
|
3852
3853
|
"agent": "Agente",
|
|
3853
3854
|
"job_failed": "Job fallito",
|
|
@@ -4296,7 +4297,9 @@
|
|
|
4296
4297
|
"tracker": "Tracker",
|
|
4297
4298
|
"board": "Bacheca",
|
|
4298
4299
|
"pickBoard": "Scegli una bacheca",
|
|
4299
|
-
"boardPlaceholder": "Chiave progetto
|
|
4300
|
+
"boardPlaceholder": "Chiave progetto o id team",
|
|
4301
|
+
"boardFromService": "Il repository collegato a questo servizio",
|
|
4302
|
+
"boardNeedsRepo": "A questo servizio non è collegato alcun repository, quindi non ci sono issue da cercare. Collegane uno dal pannello del servizio.",
|
|
4300
4303
|
"boardsFailed": "Impossibile caricare le board: {reason}",
|
|
4301
4304
|
"issueType": "Tipo di ticket",
|
|
4302
4305
|
"issueTypeHelp": "Per impostazione predefinita bug. Ignorato dai tracker senza tipi di ticket.",
|
|
@@ -4305,6 +4308,8 @@
|
|
|
4305
4308
|
"labelsHelp": "Separate da virgole. Devono essere tutte presenti.",
|
|
4306
4309
|
"adoptInto": "Aggiungi il bug scelto a",
|
|
4307
4310
|
"adoptingInto": "Il bug scelto viene aggiunto a {container}",
|
|
4311
|
+
"huntIn": "Servizio da esaminare",
|
|
4312
|
+
"huntingIn": "Viene esaminato il repository collegato a {container}; il bug scelto viene aggiunto lì.",
|
|
4308
4313
|
"run": "Cerca",
|
|
4309
4314
|
"running": "Lettura della bacheca e valutazione di quanto trovato…",
|
|
4310
4315
|
"huntFailed": "La caccia non è riuscita",
|
|
@@ -4317,6 +4322,7 @@
|
|
|
4317
4322
|
"noCandidates": "Nessun bug aperto e non assegnato corrisponde su questa bacheca.",
|
|
4318
4323
|
"ratings": "Impatto {impact}/5, complessità {complexity}/5, confidenza {confidence}",
|
|
4319
4324
|
"viaModel": "Valutato da {model}.",
|
|
4325
|
+
"scannedBoard": "Esaminato {board}.",
|
|
4320
4326
|
"truncated": "Sono stati esaminati solo i primi {count} bug corrispondenti; questa bacheca ne contiene altri.",
|
|
4321
4327
|
"comments": "{count} commento | {count} commenti",
|
|
4322
4328
|
"confidence": {
|
|
@@ -4330,6 +4336,10 @@
|
|
|
4330
4336
|
"failed": "La valutazione non è stata completata, quindi non sono valutati.",
|
|
4331
4337
|
"over_budget": "Questo spazio di lavoro ha superato il budget di spesa, quindi non sono stati valutati.",
|
|
4332
4338
|
"empty": "Nulla da valutare."
|
|
4339
|
+
},
|
|
4340
|
+
"refusal": {
|
|
4341
|
+
"boardFromService": "Questo tracker analizza il repository a cui è collegato il servizio scelto, quindi non ha una board propria.",
|
|
4342
|
+
"missingBoard": "Scegli la board su cui cercare."
|
|
4333
4343
|
}
|
|
4334
4344
|
},
|
|
4335
4345
|
"pipeline": {
|
package/i18n/locales/ja.json
CHANGED
|
@@ -1794,6 +1794,7 @@
|
|
|
1794
1794
|
"dispatch": "ディスパッチ",
|
|
1795
1795
|
"environment": "環境",
|
|
1796
1796
|
"evicted": "退避",
|
|
1797
|
+
"harness_shutdown": "Harness 停止",
|
|
1797
1798
|
"timeout": "タイムアウト",
|
|
1798
1799
|
"agent": "エージェント",
|
|
1799
1800
|
"job_failed": "ジョブ失敗",
|
|
@@ -4739,7 +4740,9 @@
|
|
|
4739
4740
|
"tracker": "トラッカー",
|
|
4740
4741
|
"board": "ボード",
|
|
4741
4742
|
"pickBoard": "ボードを選択",
|
|
4742
|
-
"boardPlaceholder": "
|
|
4743
|
+
"boardPlaceholder": "プロジェクトキーまたはチーム ID",
|
|
4744
|
+
"boardFromService": "このサービスにリンクされたリポジトリ",
|
|
4745
|
+
"boardNeedsRepo": "このサービスにはリポジトリがリンクされていないため、探索できる課題がありません。サービスパネルからリンクしてください。",
|
|
4743
4746
|
"boardsFailed": "ボードを読み込めませんでした: {reason}",
|
|
4744
4747
|
"issueType": "課題タイプ",
|
|
4745
4748
|
"issueTypeHelp": "既定は bug です。課題タイプを持たないトラッカーでは無視されます。",
|
|
@@ -4748,6 +4751,8 @@
|
|
|
4748
4751
|
"labelsHelp": "カンマ区切り。すべて付いている必要があります。",
|
|
4749
4752
|
"adoptInto": "選んだバグの追加先",
|
|
4750
4753
|
"adoptingInto": "選んだバグは {container} に追加されます",
|
|
4754
|
+
"huntIn": "探索するサービス",
|
|
4755
|
+
"huntingIn": "{container} にリンクされたリポジトリを探索します。選んだバグもそこに追加されます。",
|
|
4751
4756
|
"run": "ハント",
|
|
4752
4757
|
"running": "ボードを読み取り、見つかったものを評価しています…",
|
|
4753
4758
|
"huntFailed": "ハントに失敗しました",
|
|
@@ -4760,6 +4765,7 @@
|
|
|
4760
4765
|
"noCandidates": "このボードには条件に合う未割り当ての未解決バグがありません。",
|
|
4761
4766
|
"ratings": "影響度 {impact}/5、複雑さ {complexity}/5、確信度 {confidence}",
|
|
4762
4767
|
"viaModel": "{model} による評価です。",
|
|
4768
|
+
"scannedBoard": "{board} を走査しました。",
|
|
4763
4769
|
"truncated": "一致したバグのうち最初の {count} 件のみを走査しました。このボードにはさらにあります。",
|
|
4764
4770
|
"comments": "コメント {count} 件 | コメント {count} 件",
|
|
4765
4771
|
"confidence": {
|
|
@@ -4773,6 +4779,10 @@
|
|
|
4773
4779
|
"failed": "評価を完了できなかったため、未評価のまま表示しています。",
|
|
4774
4780
|
"over_budget": "このワークスペースは利用予算を超えているため、評価は行われませんでした。",
|
|
4775
4781
|
"empty": "評価する対象がありません。"
|
|
4782
|
+
},
|
|
4783
|
+
"refusal": {
|
|
4784
|
+
"boardFromService": "このトラッカーは選択したサービスにリンクされたリポジトリを走査するため、独自のボードはありません。",
|
|
4785
|
+
"missingBoard": "調査するボードを選択してください。"
|
|
4776
4786
|
}
|
|
4777
4787
|
},
|
|
4778
4788
|
"pipeline": {
|
package/i18n/locales/pl.json
CHANGED
|
@@ -1794,6 +1794,7 @@
|
|
|
1794
1794
|
"dispatch": "Wysłanie",
|
|
1795
1795
|
"environment": "Środowisko",
|
|
1796
1796
|
"evicted": "Usunięte",
|
|
1797
|
+
"harness_shutdown": "Harness zatrzymany",
|
|
1797
1798
|
"timeout": "Przekroczono czas",
|
|
1798
1799
|
"agent": "Agent",
|
|
1799
1800
|
"job_failed": "Zadanie nieudane",
|
|
@@ -4739,7 +4740,9 @@
|
|
|
4739
4740
|
"tracker": "System zgłoszeń",
|
|
4740
4741
|
"board": "Tablica",
|
|
4741
4742
|
"pickBoard": "Wybierz tablicę",
|
|
4742
|
-
"boardPlaceholder": "Klucz projektu
|
|
4743
|
+
"boardPlaceholder": "Klucz projektu lub identyfikator zespołu",
|
|
4744
|
+
"boardFromService": "Repozytorium powiązane z tą usługą",
|
|
4745
|
+
"boardNeedsRepo": "Ta usługa nie ma powiązanego repozytorium, więc nie ma w niej zgłoszeń do przeszukania. Powiąż je w panelu usługi.",
|
|
4743
4746
|
"boardsFailed": "Nie udało się wczytać tablic: {reason}",
|
|
4744
4747
|
"issueType": "Typ zgłoszenia",
|
|
4745
4748
|
"issueTypeHelp": "Domyślnie bug. Ignorowane przez systemy bez typów zgłoszeń.",
|
|
@@ -4748,6 +4751,8 @@
|
|
|
4748
4751
|
"labelsHelp": "Oddzielone przecinkami. Wszystkie muszą występować.",
|
|
4749
4752
|
"adoptInto": "Dodaj wybrany błąd do",
|
|
4750
4753
|
"adoptingInto": "Wybrany błąd trafi do {container}",
|
|
4754
|
+
"huntIn": "Usługa do przeszukania",
|
|
4755
|
+
"huntingIn": "Przeszukiwane jest repozytorium powiązane z {container}; tam też trafi wybrany błąd.",
|
|
4751
4756
|
"run": "Poluj",
|
|
4752
4757
|
"running": "Odczytywanie tablicy i ocenianie znalezionych zgłoszeń…",
|
|
4753
4758
|
"huntFailed": "Polowanie nie powiodło się",
|
|
@@ -4760,6 +4765,7 @@
|
|
|
4760
4765
|
"noCandidates": "Na tej tablicy nie ma pasujących otwartych, nieprzypisanych błędów.",
|
|
4761
4766
|
"ratings": "Wpływ {impact}/5, złożoność {complexity}/5, pewność {confidence}",
|
|
4762
4767
|
"viaModel": "Ocenione przez {model}.",
|
|
4768
|
+
"scannedBoard": "Przeszukano {board}.",
|
|
4763
4769
|
"truncated": "Przeszukano tylko pierwsze pasujące zgłoszenia w liczbie {count}; ta tablica zawiera ich więcej.",
|
|
4764
4770
|
"comments": "{count} komentarz | {count} komentarze | {count} komentarzy",
|
|
4765
4771
|
"confidence": {
|
|
@@ -4773,6 +4779,10 @@
|
|
|
4773
4779
|
"failed": "Nie udało się dokończyć oceny, więc te zgłoszenia są nieocenione.",
|
|
4774
4780
|
"over_budget": "Ta przestrzeń robocza przekroczyła budżet wydatków, więc nie zostały ocenione.",
|
|
4775
4781
|
"empty": "Nie ma czego oceniać."
|
|
4782
|
+
},
|
|
4783
|
+
"refusal": {
|
|
4784
|
+
"boardFromService": "Ten tracker przeszukuje repozytorium powiązane z wybraną usługą, więc nie ma własnej tablicy.",
|
|
4785
|
+
"missingBoard": "Wybierz tablicę, na której chcesz szukać."
|
|
4776
4786
|
}
|
|
4777
4787
|
},
|
|
4778
4788
|
"pipeline": {
|
package/i18n/locales/tr.json
CHANGED
|
@@ -1794,6 +1794,7 @@
|
|
|
1794
1794
|
"dispatch": "Gönderim",
|
|
1795
1795
|
"environment": "Ortam",
|
|
1796
1796
|
"evicted": "Tahliye edildi",
|
|
1797
|
+
"harness_shutdown": "Harness kapatıldı",
|
|
1797
1798
|
"timeout": "Zaman aşımı",
|
|
1798
1799
|
"agent": "Aracı",
|
|
1799
1800
|
"job_failed": "İş başarısız",
|
|
@@ -4739,7 +4740,9 @@
|
|
|
4739
4740
|
"tracker": "Takip aracı",
|
|
4740
4741
|
"board": "Pano",
|
|
4741
4742
|
"pickBoard": "Bir pano seçin",
|
|
4742
|
-
"boardPlaceholder": "Proje
|
|
4743
|
+
"boardPlaceholder": "Proje anahtarı veya takım kimliği",
|
|
4744
|
+
"boardFromService": "Bu servise bağlı depo",
|
|
4745
|
+
"boardNeedsRepo": "Bu servise bağlı bir depo yok, dolayısıyla taranacak kayıt da yok. Servis panelinden bir depo bağlayın.",
|
|
4743
4746
|
"boardsFailed": "Panolar yüklenemedi: {reason}",
|
|
4744
4747
|
"issueType": "Kayıt türü",
|
|
4745
4748
|
"issueTypeHelp": "Varsayılan olarak bug. Kayıt türü olmayan araçlarda yok sayılır.",
|
|
@@ -4748,6 +4751,8 @@
|
|
|
4748
4751
|
"labelsHelp": "Virgülle ayrılır. Hepsinin bulunması gerekir.",
|
|
4749
4752
|
"adoptInto": "Seçilen hatayı şuraya ekle",
|
|
4750
4753
|
"adoptingInto": "Seçilen hata {container} içine eklenir",
|
|
4754
|
+
"huntIn": "Taranacak servis",
|
|
4755
|
+
"huntingIn": "{container} servisine bağlı depo taranıyor; seçilen hata da oraya eklenir.",
|
|
4751
4756
|
"run": "Avla",
|
|
4752
4757
|
"running": "Pano okunuyor ve bulunanlar değerlendiriliyor…",
|
|
4753
4758
|
"huntFailed": "Av başarısız oldu",
|
|
@@ -4760,6 +4765,7 @@
|
|
|
4760
4765
|
"noCandidates": "Bu panoda eşleşen açık ve atanmamış hata yok.",
|
|
4761
4766
|
"ratings": "Etki {impact}/5, karmaşıklık {complexity}/5, güven {confidence}",
|
|
4762
4767
|
"viaModel": "{model} tarafından değerlendirildi.",
|
|
4768
|
+
"scannedBoard": "{board} tarandı.",
|
|
4763
4769
|
"truncated": "Yalnızca eşleşen ilk {count} hata tarandı; bu panoda daha fazlası var.",
|
|
4764
4770
|
"comments": "{count} yorum | {count} yorum",
|
|
4765
4771
|
"confidence": {
|
|
@@ -4773,6 +4779,10 @@
|
|
|
4773
4779
|
"failed": "Değerlendirme tamamlanamadı, bu yüzden bunlar değerlendirilmedi.",
|
|
4774
4780
|
"over_budget": "Bu çalışma alanı harcama bütçesini aştığı için bunlar değerlendirilmedi.",
|
|
4775
4781
|
"empty": "Değerlendirilecek bir şey yok."
|
|
4782
|
+
},
|
|
4783
|
+
"refusal": {
|
|
4784
|
+
"boardFromService": "Bu izleyici, seçilen hizmete bağlı depoyu tarar; bu yüzden kendine ait bir panosu yoktur.",
|
|
4785
|
+
"missingBoard": "Aranacak panoyu seçin."
|
|
4776
4786
|
}
|
|
4777
4787
|
},
|
|
4778
4788
|
"pipeline": {
|
package/i18n/locales/uk.json
CHANGED
|
@@ -1794,6 +1794,7 @@
|
|
|
1794
1794
|
"dispatch": "Відправлення",
|
|
1795
1795
|
"environment": "Середовище",
|
|
1796
1796
|
"evicted": "Витіснено",
|
|
1797
|
+
"harness_shutdown": "Harness зупинено",
|
|
1797
1798
|
"timeout": "Час вичерпано",
|
|
1798
1799
|
"agent": "Агент",
|
|
1799
1800
|
"job_failed": "Завдання не виконано",
|
|
@@ -4739,7 +4740,9 @@
|
|
|
4739
4740
|
"tracker": "Трекер",
|
|
4740
4741
|
"board": "Дошка",
|
|
4741
4742
|
"pickBoard": "Оберіть дошку",
|
|
4742
|
-
"boardPlaceholder": "Ключ
|
|
4743
|
+
"boardPlaceholder": "Ключ проєкту або ідентифікатор команди",
|
|
4744
|
+
"boardFromService": "Репозиторій, пов'язаний із цим сервісом",
|
|
4745
|
+
"boardNeedsRepo": "Із цим сервісом не пов'язано жодного репозиторію, тож у ньому немає задач для перегляду. Пов'яжіть репозиторій на панелі сервісу.",
|
|
4743
4746
|
"boardsFailed": "Не вдалося завантажити дошки: {reason}",
|
|
4744
4747
|
"issueType": "Тип запиту",
|
|
4745
4748
|
"issueTypeHelp": "Типово bug. Ігнорується трекерами без типів запитів.",
|
|
@@ -4748,6 +4751,8 @@
|
|
|
4748
4751
|
"labelsHelp": "Через кому. Усі мають бути присутні.",
|
|
4749
4752
|
"adoptInto": "Додати обрану помилку до",
|
|
4750
4753
|
"adoptingInto": "Обрана помилка додається до {container}",
|
|
4754
|
+
"huntIn": "Сервіс для перегляду",
|
|
4755
|
+
"huntingIn": "Переглядається репозиторій, пов'язаний із {container}; туди ж потрапить обрана помилка.",
|
|
4751
4756
|
"run": "Полювати",
|
|
4752
4757
|
"running": "Читаємо дошку та оцінюємо знайдене…",
|
|
4753
4758
|
"huntFailed": "Полювання не вдалося",
|
|
@@ -4760,6 +4765,7 @@
|
|
|
4760
4765
|
"noCandidates": "На цій дошці немає відкритих і не призначених помилок, що відповідають умовам.",
|
|
4761
4766
|
"ratings": "Вплив {impact}/5, складність {complexity}/5, впевненість {confidence}",
|
|
4762
4767
|
"viaModel": "Оцінено моделлю {model}.",
|
|
4768
|
+
"scannedBoard": "Переглянуто {board}.",
|
|
4763
4769
|
"truncated": "Переглянуто лише перші відповідні помилки в кількості {count}; на цій дошці їх більше.",
|
|
4764
4770
|
"comments": "{count} коментар | {count} коментарі | {count} коментарів",
|
|
4765
4771
|
"confidence": {
|
|
@@ -4773,6 +4779,10 @@
|
|
|
4773
4779
|
"failed": "Не вдалося завершити оцінювання, тому ці помилки не оцінено.",
|
|
4774
4780
|
"over_budget": "Цей робочий простір перевищив бюджет витрат, тому їх не оцінено.",
|
|
4775
4781
|
"empty": "Нема чого оцінювати."
|
|
4782
|
+
},
|
|
4783
|
+
"refusal": {
|
|
4784
|
+
"boardFromService": "Цей трекер сканує репозиторій, з яким пов’язано вибраний сервіс, тож власної дошки не має.",
|
|
4785
|
+
"missingBoard": "Виберіть дошку, на якій шукати."
|
|
4776
4786
|
}
|
|
4777
4787
|
},
|
|
4778
4788
|
"pipeline": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.273.0",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@modular-vue/vue": "^1.4.1",
|
|
27
27
|
"@nuxt/ui": "^4.10.0",
|
|
28
28
|
"@nuxtjs/i18n": "^10.6.0",
|
|
29
|
-
"@pinia/nuxt": "^1.0.
|
|
29
|
+
"@pinia/nuxt": "^1.0.2",
|
|
30
30
|
"@toad-contracts/core": "0.4.0",
|
|
31
31
|
"@toad-contracts/frontend-http-client": "0.3.2",
|
|
32
32
|
"@toad-contracts/valibot": "0.5.0",
|
|
@@ -35,12 +35,12 @@
|
|
|
35
35
|
"@vue-flow/node-resizer": "^1.5.1",
|
|
36
36
|
"@vueuse/core": "^14.4.0",
|
|
37
37
|
"markdown-it": "^15.0.0",
|
|
38
|
-
"pinia": "^4.0.
|
|
38
|
+
"pinia": "^4.0.3",
|
|
39
39
|
"pinia-plugin-persistedstate": "^4.7.1",
|
|
40
40
|
"valibot": "^1.4.2",
|
|
41
41
|
"vue": "3.5.41",
|
|
42
42
|
"wretch": "^3.0.9",
|
|
43
|
-
"@cat-factory/contracts": "0.
|
|
43
|
+
"@cat-factory/contracts": "0.312.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@toad-contracts/testing": "0.3.2",
|