@cat-factory/app 0.146.3 → 0.147.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/app/components/prReview/PrReviewWindow.vue +332 -59
- package/app/composables/api/prReview.ts +26 -1
- package/app/stores/prReview.ts +46 -1
- package/app/types/execution.ts +1 -0
- package/app/utils/catalog.ts +17 -0
- package/app/utils/prReviewProgress.spec.ts +73 -0
- package/app/utils/prReviewProgress.ts +28 -0
- package/i18n/locales/de.json +32 -3
- package/i18n/locales/en.json +32 -3
- package/i18n/locales/es.json +32 -3
- package/i18n/locales/fr.json +32 -3
- package/i18n/locales/he.json +32 -3
- package/i18n/locales/it.json +32 -3
- package/i18n/locales/ja.json +32 -3
- package/i18n/locales/pl.json +32 -3
- package/i18n/locales/tr.json +32 -3
- package/i18n/locales/uk.json +32 -3
- package/package.json +2 -2
|
@@ -17,9 +17,11 @@ import type {
|
|
|
17
17
|
PrReviewResolution,
|
|
18
18
|
PrReviewSeverity,
|
|
19
19
|
PrReviewStepState,
|
|
20
|
+
StepSubtaskItem,
|
|
20
21
|
StepSubtasks,
|
|
21
22
|
} from '~/types/execution'
|
|
22
23
|
import { subtaskIconClass } from '~/utils/pipelineRender'
|
|
24
|
+
import { activeChunkLabels, chunkReviewPercent, isSlicingChunks } from '~/utils/prReviewProgress'
|
|
23
25
|
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
24
26
|
|
|
25
27
|
const execution = useExecutionStore()
|
|
@@ -46,18 +48,51 @@ const step = computed(() => {
|
|
|
46
48
|
const state = computed<PrReviewStepState | null>(() => step.value?.prReview ?? null)
|
|
47
49
|
const status = computed(() => state.value?.status ?? null)
|
|
48
50
|
const awaiting = computed(() => status.value === 'awaiting_selection')
|
|
51
|
+
// A finding is being re-examined by the Challenge Investigator (the whole review is `challenging`
|
|
52
|
+
// while it runs). The findings stay visible — the challenged one shows a spinner — but the
|
|
53
|
+
// selection controls + per-finding actions are disabled until the verdict lands and it re-parks.
|
|
54
|
+
const challenging = computed(() => status.value === 'challenging')
|
|
49
55
|
// The reviewer's live todo list while it works, streamed onto the step. Its entries are the
|
|
50
|
-
// cohesive slices/chunks the agent grouped the diff into (plus a final "aggregate" step)
|
|
51
|
-
//
|
|
56
|
+
// cohesive slices/chunks the agent grouped the diff into (plus a final "aggregate" step). The
|
|
57
|
+
// derivation of the two `reviewing`-phase sub-states from it lives in `~/utils/prReviewProgress`
|
|
58
|
+
// (pure + unit-tested); see that module for the slicing-vs-reviewing signal.
|
|
52
59
|
const subtasks = computed<StepSubtasks | null>(() => step.value?.subtasks ?? null)
|
|
53
|
-
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Slicing sub-phase: the reviewer is still grouping the diff into chunks (no todo list yet).
|
|
63
|
+
* Once the list exists, slicing is done and we render the per-chunk status list instead.
|
|
64
|
+
*/
|
|
65
|
+
const slicing = computed(() => status.value === 'reviewing' && isSlicingChunks(subtasks.value))
|
|
66
|
+
|
|
67
|
+
/** Chunk-review completion, clamped 0..100 for the progress bar. */
|
|
68
|
+
const chunkPercent = computed(() => chunkReviewPercent(subtasks.value))
|
|
69
|
+
|
|
70
|
+
/** The chunks the reviewer is actively working through right now (their labels), for the callout. */
|
|
71
|
+
const activeChunks = computed<string[]>(() => activeChunkLabels(subtasks.value))
|
|
54
72
|
|
|
55
73
|
/** Icon per todo-item status (matches the pipeline timeline's live subtask breakdown). */
|
|
56
|
-
const ITEM_ICON: Record<
|
|
74
|
+
const ITEM_ICON: Record<StepSubtaskItem['status'], string> = {
|
|
57
75
|
completed: 'i-lucide-check-circle-2',
|
|
58
76
|
in_progress: 'i-lucide-loader-circle',
|
|
59
77
|
pending: 'i-lucide-circle',
|
|
60
78
|
}
|
|
79
|
+
|
|
80
|
+
// Per-chunk status label + chip styling. The key map is an exhaustive Record over the subtask
|
|
81
|
+
// status union, so adding a status without a label fails the typecheck (the sanctioned dynamic
|
|
82
|
+
// enum→key pattern — tier 1 can't see a runtime-built key).
|
|
83
|
+
const CHUNK_STATUS_KEY: Record<StepSubtaskItem['status'], string> = {
|
|
84
|
+
completed: 'prReview.reviewing.chunkStatus.completed',
|
|
85
|
+
in_progress: 'prReview.reviewing.chunkStatus.in_progress',
|
|
86
|
+
pending: 'prReview.reviewing.chunkStatus.pending',
|
|
87
|
+
}
|
|
88
|
+
const CHUNK_STATUS_CLASS: Record<StepSubtaskItem['status'], string> = {
|
|
89
|
+
completed: 'bg-emerald-500/15 text-emerald-300',
|
|
90
|
+
in_progress: 'bg-indigo-500/15 text-indigo-300',
|
|
91
|
+
pending: 'bg-slate-700/60 text-slate-400',
|
|
92
|
+
}
|
|
93
|
+
function chunkStatusLabel(status: StepSubtaskItem['status']): string {
|
|
94
|
+
return t(CHUNK_STATUS_KEY[status])
|
|
95
|
+
}
|
|
61
96
|
// A resolution is executing (the Fixer is committing, or comments are being posted) — show a
|
|
62
97
|
// working state between the human's choice and the run advancing/the stream echoing `done`.
|
|
63
98
|
const working = computed(() => status.value === 'fixing' || status.value === 'posting')
|
|
@@ -101,6 +136,27 @@ const groups = computed(() => {
|
|
|
101
136
|
return out
|
|
102
137
|
})
|
|
103
138
|
|
|
139
|
+
/** A finding the Challenge Investigator RETRACTED — it can no longer be acted on (auto-deselected). */
|
|
140
|
+
function isRetracted(f: PrReviewFinding): boolean {
|
|
141
|
+
return f.challenge?.status === 'retracted'
|
|
142
|
+
}
|
|
143
|
+
/** A finding currently being re-examined by the Challenge Investigator. */
|
|
144
|
+
function isInvestigating(f: PrReviewFinding): boolean {
|
|
145
|
+
return f.challenge?.status === 'investigating'
|
|
146
|
+
}
|
|
147
|
+
/** A finding the investigator UPHELD + strengthened after a challenge (its body actually changed). */
|
|
148
|
+
function isAmended(f: PrReviewFinding): boolean {
|
|
149
|
+
return f.challenge?.status === 'amended'
|
|
150
|
+
}
|
|
151
|
+
/** A finding the investigator UPHELD as written after a challenge (kept, no revision). */
|
|
152
|
+
function isUpheld(f: PrReviewFinding): boolean {
|
|
153
|
+
return f.challenge?.status === 'upheld'
|
|
154
|
+
}
|
|
155
|
+
/** A finding whose challenge investigation FAILED — the finding is kept as-is; re-challenge allowed. */
|
|
156
|
+
function isChallengeFailed(f: PrReviewFinding): boolean {
|
|
157
|
+
return f.challenge?.status === 'failed'
|
|
158
|
+
}
|
|
159
|
+
|
|
104
160
|
// The human's selection — a set of finding ids. Defaults to every finding (the human deselects
|
|
105
161
|
// the noise), so "Finish" without touching anything keeps all. Re-seeded ONLY when the set of
|
|
106
162
|
// finding IDS actually changes — NOT on every execution-stream re-emit (which hands us a fresh
|
|
@@ -111,11 +167,17 @@ const selected = ref<Set<string>>(new Set())
|
|
|
111
167
|
watch(
|
|
112
168
|
findingIdKey,
|
|
113
169
|
() => {
|
|
114
|
-
selected.value = new Set(findings.value.map((f) => f.id))
|
|
170
|
+
selected.value = new Set(findings.value.filter((f) => !isRetracted(f)).map((f) => f.id))
|
|
115
171
|
},
|
|
116
172
|
{ immediate: true },
|
|
117
173
|
)
|
|
118
174
|
|
|
175
|
+
// The effective selection: checked AND not retracted. A retracted finding is never acted on even
|
|
176
|
+
// if its box was checked before the investigator dropped it (it's disabled + unchecked visually).
|
|
177
|
+
const activeSelectedIds = computed(() =>
|
|
178
|
+
findings.value.filter((f) => selected.value.has(f.id) && !isRetracted(f)).map((f) => f.id),
|
|
179
|
+
)
|
|
180
|
+
|
|
119
181
|
function toggle(id: string): void {
|
|
120
182
|
const next = new Set(selected.value)
|
|
121
183
|
if (next.has(id)) next.delete(id)
|
|
@@ -123,7 +185,7 @@ function toggle(id: string): void {
|
|
|
123
185
|
selected.value = next
|
|
124
186
|
}
|
|
125
187
|
function selectAll(): void {
|
|
126
|
-
selected.value = new Set(findings.value.map((f) => f.id))
|
|
188
|
+
selected.value = new Set(findings.value.filter((f) => !isRetracted(f)).map((f) => f.id))
|
|
127
189
|
}
|
|
128
190
|
function clearAll(): void {
|
|
129
191
|
selected.value = new Set()
|
|
@@ -132,13 +194,40 @@ function clearAll(): void {
|
|
|
132
194
|
const canResolve = computed(() => awaiting.value && !prReview.resolving)
|
|
133
195
|
// Fix / Post act on the selection, so they need at least one selected finding; Finish always
|
|
134
196
|
// works (it just records the — possibly empty — curated selection and completes the review).
|
|
135
|
-
const hasSelection = computed(() =>
|
|
197
|
+
const hasSelection = computed(() => activeSelectedIds.value.length > 0)
|
|
136
198
|
|
|
137
199
|
async function onResolve(action: PrReviewResolution): Promise<void> {
|
|
138
200
|
const id = instanceId.value
|
|
139
201
|
if (!id || !canResolve.value) return
|
|
140
202
|
if ((action === 'fix' || action === 'post') && !hasSelection.value) return
|
|
141
|
-
await prReview.resolve(id,
|
|
203
|
+
await prReview.resolve(id, activeSelectedIds.value, action).catch(() => {})
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Per-finding CHALLENGE: the open finding's id (its inline concern box is showing) + the drafted
|
|
207
|
+
// concern text. Dispatching moves the whole review to `challenging` until the verdict lands.
|
|
208
|
+
const challengeForId = ref<string | null>(null)
|
|
209
|
+
const challengeText = ref('')
|
|
210
|
+
function openChallenge(id: string): void {
|
|
211
|
+
challengeForId.value = id
|
|
212
|
+
challengeText.value = ''
|
|
213
|
+
}
|
|
214
|
+
function cancelChallenge(): void {
|
|
215
|
+
challengeForId.value = null
|
|
216
|
+
challengeText.value = ''
|
|
217
|
+
}
|
|
218
|
+
async function submitChallenge(id: string): Promise<void> {
|
|
219
|
+
const inst = instanceId.value
|
|
220
|
+
if (!inst || !canResolve.value) return
|
|
221
|
+
const question = challengeText.value.trim()
|
|
222
|
+
challengeForId.value = null
|
|
223
|
+
challengeText.value = ''
|
|
224
|
+
await prReview.challenge(inst, id, question || undefined).catch(() => {})
|
|
225
|
+
}
|
|
226
|
+
async function onDismiss(id: string): Promise<void> {
|
|
227
|
+
const inst = instanceId.value
|
|
228
|
+
if (!inst || !canResolve.value) return
|
|
229
|
+
if (challengeForId.value === id) cancelChallenge()
|
|
230
|
+
await prReview.dismiss(inst, id).catch(() => {})
|
|
142
231
|
}
|
|
143
232
|
</script>
|
|
144
233
|
|
|
@@ -165,24 +254,39 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
|
|
|
165
254
|
</template>
|
|
166
255
|
|
|
167
256
|
<div class="min-h-0 flex-1 overflow-y-auto px-5 py-4">
|
|
168
|
-
<!-- Reviewing: the read-only reviewer is still working.
|
|
169
|
-
|
|
170
|
-
|
|
257
|
+
<!-- Reviewing: the read-only reviewer is still working. The phase is told apart precisely —
|
|
258
|
+
SLICING (still grouping the diff into chunks, no plan yet) vs REVIEWING (slicing done,
|
|
259
|
+
working through the chunks) — so the copy never claims "reviewing" while it's slicing. -->
|
|
171
260
|
<div
|
|
172
261
|
v-if="status === 'reviewing'"
|
|
173
262
|
data-testid="pr-review-reviewing"
|
|
174
263
|
class="flex h-full flex-col"
|
|
175
264
|
>
|
|
176
|
-
<!--
|
|
177
|
-
<div
|
|
265
|
+
<!-- SLICING: no todo list yet — the reviewer is still grouping the diff into chunks. -->
|
|
266
|
+
<div
|
|
267
|
+
v-if="slicing"
|
|
268
|
+
data-testid="pr-review-slicing"
|
|
269
|
+
class="flex h-full flex-col items-center justify-center gap-2 py-10 text-center text-slate-400"
|
|
270
|
+
>
|
|
271
|
+
<UIcon name="i-lucide-loader-circle" class="h-8 w-8 animate-spin opacity-60" />
|
|
272
|
+
<p class="text-sm text-slate-200">{{ t('prReview.reviewing.slicing.title') }}</p>
|
|
273
|
+
<p class="max-w-sm text-[11px] text-slate-500">
|
|
274
|
+
{{ t('prReview.reviewing.slicing.hint') }}
|
|
275
|
+
</p>
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
<!-- REVIEWING: slicing is done — show every chunk with its status + which are active now. -->
|
|
279
|
+
<div v-else data-testid="pr-review-reviewing-chunks" class="py-2">
|
|
178
280
|
<div class="mb-1 flex items-center gap-2 text-sm text-slate-200">
|
|
179
281
|
<UIcon
|
|
180
282
|
name="i-lucide-loader-circle"
|
|
181
283
|
class="h-4 w-4 shrink-0 animate-spin text-indigo-300"
|
|
182
284
|
/>
|
|
183
|
-
<span>{{ t('prReview.reviewing.title') }}</span>
|
|
285
|
+
<span>{{ t('prReview.reviewing.reviewingChunks.title') }}</span>
|
|
184
286
|
</div>
|
|
185
|
-
<p class="mb-3 text-[11px] text-slate-500">
|
|
287
|
+
<p class="mb-3 text-[11px] text-slate-500">
|
|
288
|
+
{{ t('prReview.reviewing.reviewingChunks.hint') }}
|
|
289
|
+
</p>
|
|
186
290
|
|
|
187
291
|
<div class="flex items-center justify-between text-[11px] text-slate-400">
|
|
188
292
|
<span data-testid="pr-review-chunk-count">
|
|
@@ -200,46 +304,74 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
|
|
|
200
304
|
<div class="mt-1 h-1.5 overflow-hidden rounded-full bg-slate-700/60">
|
|
201
305
|
<div
|
|
202
306
|
class="h-full rounded-full bg-indigo-400 transition-all duration-500"
|
|
203
|
-
:style="{ width: `${
|
|
307
|
+
:style="{ width: `${chunkPercent}%` }"
|
|
204
308
|
/>
|
|
205
309
|
</div>
|
|
206
310
|
|
|
207
|
-
<!-- The
|
|
208
|
-
<
|
|
209
|
-
v-if="
|
|
210
|
-
|
|
211
|
-
|
|
311
|
+
<!-- The chunk(s) being actively reviewed right now, called out on their own. -->
|
|
312
|
+
<div
|
|
313
|
+
v-if="activeChunks.length"
|
|
314
|
+
data-testid="pr-review-active-chunks"
|
|
315
|
+
class="mt-3 rounded-lg border border-indigo-500/30 bg-indigo-500/5 px-2.5 py-2"
|
|
212
316
|
>
|
|
213
|
-
<
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
<span>{{ item.label }}</span>
|
|
231
|
-
</li>
|
|
232
|
-
</ul>
|
|
233
|
-
</div>
|
|
317
|
+
<p class="mb-1 text-[10px] font-semibold uppercase tracking-wide text-indigo-300">
|
|
318
|
+
{{ t('prReview.reviewing.activeHeading') }}
|
|
319
|
+
</p>
|
|
320
|
+
<ul class="space-y-1">
|
|
321
|
+
<li
|
|
322
|
+
v-for="(label, i) in activeChunks"
|
|
323
|
+
:key="i"
|
|
324
|
+
class="flex items-start gap-1.5 text-[12px] text-slate-100"
|
|
325
|
+
>
|
|
326
|
+
<UIcon
|
|
327
|
+
name="i-lucide-loader-circle"
|
|
328
|
+
class="mt-0.5 h-3.5 w-3.5 shrink-0 animate-spin text-indigo-300"
|
|
329
|
+
/>
|
|
330
|
+
<span class="min-w-0">{{ label }}</span>
|
|
331
|
+
</li>
|
|
332
|
+
</ul>
|
|
333
|
+
</div>
|
|
234
334
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
335
|
+
<!-- Every chunk with its explicit status (Reviewed / Reviewing… / Queued). -->
|
|
336
|
+
<template v-if="subtasks!.items?.length">
|
|
337
|
+
<p class="mb-1.5 mt-3 text-[10px] font-semibold uppercase tracking-wide text-slate-400">
|
|
338
|
+
{{ t('prReview.reviewing.chunksHeading') }}
|
|
339
|
+
</p>
|
|
340
|
+
<ul class="space-y-1.5" data-testid="pr-review-chunks">
|
|
341
|
+
<li
|
|
342
|
+
v-for="(item, i) in subtasks!.items"
|
|
343
|
+
:key="i"
|
|
344
|
+
data-testid="pr-review-chunk"
|
|
345
|
+
class="flex items-center gap-1.5 text-[12px]"
|
|
346
|
+
:class="
|
|
347
|
+
item.status === 'completed'
|
|
348
|
+
? 'text-slate-500'
|
|
349
|
+
: item.status === 'in_progress'
|
|
350
|
+
? 'text-slate-100'
|
|
351
|
+
: 'text-slate-400'
|
|
352
|
+
"
|
|
353
|
+
>
|
|
354
|
+
<UIcon
|
|
355
|
+
:name="ITEM_ICON[item.status]"
|
|
356
|
+
class="h-3.5 w-3.5 shrink-0"
|
|
357
|
+
:class="subtaskIconClass(item.status, false)"
|
|
358
|
+
/>
|
|
359
|
+
<span
|
|
360
|
+
class="min-w-0 flex-1 truncate"
|
|
361
|
+
:class="item.status === 'completed' ? 'line-through' : ''"
|
|
362
|
+
>
|
|
363
|
+
{{ item.label }}
|
|
364
|
+
</span>
|
|
365
|
+
<span
|
|
366
|
+
data-testid="pr-review-chunk-status"
|
|
367
|
+
class="shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium uppercase"
|
|
368
|
+
:class="CHUNK_STATUS_CLASS[item.status]"
|
|
369
|
+
>
|
|
370
|
+
{{ chunkStatusLabel(item.status) }}
|
|
371
|
+
</span>
|
|
372
|
+
</li>
|
|
373
|
+
</ul>
|
|
374
|
+
</template>
|
|
243
375
|
</div>
|
|
244
376
|
</div>
|
|
245
377
|
|
|
@@ -335,10 +467,20 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
|
|
|
335
467
|
</div>
|
|
336
468
|
|
|
337
469
|
<template v-else>
|
|
470
|
+
<!-- A challenge is in flight: the Challenge Investigator is re-examining a finding. -->
|
|
471
|
+
<div
|
|
472
|
+
v-if="challenging"
|
|
473
|
+
data-testid="pr-review-challenging"
|
|
474
|
+
class="mb-3 flex items-center gap-2 rounded-md border border-indigo-500/40 bg-indigo-500/10 px-3 py-2 text-[12px] text-indigo-200"
|
|
475
|
+
>
|
|
476
|
+
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 shrink-0 animate-spin" />
|
|
477
|
+
<span>{{ t('prReview.challenge.investigatingBanner') }}</span>
|
|
478
|
+
</div>
|
|
479
|
+
|
|
338
480
|
<!-- Selection toolbar -->
|
|
339
481
|
<div v-if="awaiting" class="mb-2 flex items-center gap-3 text-[11px] text-slate-400">
|
|
340
482
|
<span data-testid="pr-review-selected-count">
|
|
341
|
-
{{ t('prReview.selectedCount', { count:
|
|
483
|
+
{{ t('prReview.selectedCount', { count: activeSelectedIds.length }) }}
|
|
342
484
|
</span>
|
|
343
485
|
<button class="text-indigo-300 hover:underline" @click="selectAll">
|
|
344
486
|
{{ t('prReview.selectAll') }}
|
|
@@ -361,19 +503,21 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
|
|
|
361
503
|
:key="f.id"
|
|
362
504
|
data-testid="pr-review-finding"
|
|
363
505
|
class="mb-1.5 rounded-xl border px-3 py-2 transition"
|
|
364
|
-
:class="
|
|
365
|
-
awaiting && selected.has(f.id)
|
|
506
|
+
:class="[
|
|
507
|
+
awaiting && selected.has(f.id) && !isRetracted(f)
|
|
366
508
|
? 'border-indigo-500/60 bg-indigo-500/5'
|
|
367
|
-
: 'border-slate-800 bg-slate-900/60'
|
|
368
|
-
|
|
509
|
+
: 'border-slate-800 bg-slate-900/60',
|
|
510
|
+
isRetracted(f) ? 'opacity-60' : '',
|
|
511
|
+
]"
|
|
369
512
|
>
|
|
370
513
|
<div class="flex items-start gap-2">
|
|
371
514
|
<input
|
|
372
|
-
v-if="awaiting"
|
|
515
|
+
v-if="awaiting || challenging"
|
|
373
516
|
type="checkbox"
|
|
374
517
|
class="mt-1 accent-indigo-500"
|
|
375
518
|
data-testid="pr-review-finding-toggle"
|
|
376
|
-
:checked="selected.has(f.id)"
|
|
519
|
+
:checked="selected.has(f.id) && !isRetracted(f)"
|
|
520
|
+
:disabled="!awaiting || isRetracted(f)"
|
|
377
521
|
@change="toggle(f.id)"
|
|
378
522
|
/>
|
|
379
523
|
<div class="min-w-0 flex-1">
|
|
@@ -394,7 +538,47 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
|
|
|
394
538
|
>
|
|
395
539
|
{{ t('prReview.postReport.postedBadge') }}
|
|
396
540
|
</span>
|
|
397
|
-
|
|
541
|
+
<!-- Challenge outcome badges -->
|
|
542
|
+
<span
|
|
543
|
+
v-if="isRetracted(f)"
|
|
544
|
+
data-testid="pr-review-finding-retracted"
|
|
545
|
+
class="rounded bg-rose-500/15 px-1.5 py-0.5 text-[10px] font-semibold uppercase text-rose-300 ring-1 ring-rose-500/30"
|
|
546
|
+
>
|
|
547
|
+
{{ t('prReview.challenge.retractedBadge') }}
|
|
548
|
+
</span>
|
|
549
|
+
<span
|
|
550
|
+
v-else-if="isAmended(f)"
|
|
551
|
+
data-testid="pr-review-finding-amended"
|
|
552
|
+
class="rounded bg-sky-500/15 px-1.5 py-0.5 text-[10px] font-semibold uppercase text-sky-300 ring-1 ring-sky-500/30"
|
|
553
|
+
>
|
|
554
|
+
{{ t('prReview.challenge.strengthenedBadge') }}
|
|
555
|
+
</span>
|
|
556
|
+
<span
|
|
557
|
+
v-else-if="isUpheld(f)"
|
|
558
|
+
data-testid="pr-review-finding-upheld"
|
|
559
|
+
class="rounded bg-emerald-500/15 px-1.5 py-0.5 text-[10px] font-semibold uppercase text-emerald-300 ring-1 ring-emerald-500/30"
|
|
560
|
+
>
|
|
561
|
+
{{ t('prReview.challenge.upheldBadge') }}
|
|
562
|
+
</span>
|
|
563
|
+
<span
|
|
564
|
+
v-else-if="isChallengeFailed(f)"
|
|
565
|
+
data-testid="pr-review-finding-challenge-failed"
|
|
566
|
+
class="rounded bg-amber-500/15 px-1.5 py-0.5 text-[10px] font-semibold uppercase text-amber-300 ring-1 ring-amber-500/30"
|
|
567
|
+
>
|
|
568
|
+
{{ t('prReview.challenge.failedBadge') }}
|
|
569
|
+
</span>
|
|
570
|
+
<span
|
|
571
|
+
v-else-if="isInvestigating(f)"
|
|
572
|
+
data-testid="pr-review-finding-investigating"
|
|
573
|
+
class="flex items-center gap-1 rounded bg-indigo-500/15 px-1.5 py-0.5 text-[10px] font-semibold uppercase text-indigo-300 ring-1 ring-indigo-500/30"
|
|
574
|
+
>
|
|
575
|
+
<UIcon name="i-lucide-loader-circle" class="h-3 w-3 animate-spin" />
|
|
576
|
+
{{ t('prReview.challenge.investigatingBadge') }}
|
|
577
|
+
</span>
|
|
578
|
+
<h4
|
|
579
|
+
class="min-w-0 flex-1 text-[13px] font-medium text-slate-100"
|
|
580
|
+
:class="isRetracted(f) ? 'line-through' : ''"
|
|
581
|
+
>
|
|
398
582
|
{{ f.title }}
|
|
399
583
|
</h4>
|
|
400
584
|
</div>
|
|
@@ -404,7 +588,10 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
|
|
|
404
588
|
· {{ t('prReview.line', { line: f.line }) }}</template
|
|
405
589
|
>
|
|
406
590
|
</p>
|
|
407
|
-
<p
|
|
591
|
+
<p
|
|
592
|
+
class="mt-1 whitespace-pre-wrap text-[12px] text-slate-300"
|
|
593
|
+
:class="isRetracted(f) ? 'line-through' : ''"
|
|
594
|
+
>
|
|
408
595
|
{{ f.detail }}
|
|
409
596
|
</p>
|
|
410
597
|
<p
|
|
@@ -414,6 +601,92 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
|
|
|
414
601
|
<span class="text-slate-500">{{ t('prReview.suggestedFix') }}</span>
|
|
415
602
|
{{ f.suggestedFix }}
|
|
416
603
|
</p>
|
|
604
|
+
|
|
605
|
+
<!-- The investigator's justification (why the finding holds up / was retracted),
|
|
606
|
+
or the reason the challenge investigation failed. -->
|
|
607
|
+
<p
|
|
608
|
+
v-if="f.challenge?.justification"
|
|
609
|
+
data-testid="pr-review-finding-justification"
|
|
610
|
+
class="mt-1.5 whitespace-pre-wrap rounded-md px-2 py-1 text-[11px]"
|
|
611
|
+
:class="
|
|
612
|
+
isRetracted(f)
|
|
613
|
+
? 'bg-rose-500/10 text-rose-200'
|
|
614
|
+
: isChallengeFailed(f)
|
|
615
|
+
? 'bg-amber-500/10 text-amber-200'
|
|
616
|
+
: 'bg-sky-500/10 text-sky-200'
|
|
617
|
+
"
|
|
618
|
+
>
|
|
619
|
+
<span class="font-medium">{{
|
|
620
|
+
isChallengeFailed(f)
|
|
621
|
+
? t('prReview.challenge.failedLabel')
|
|
622
|
+
: t('prReview.challenge.verdictLabel')
|
|
623
|
+
}}</span>
|
|
624
|
+
{{ f.challenge.justification }}
|
|
625
|
+
</p>
|
|
626
|
+
|
|
627
|
+
<!-- Per-finding actions: Challenge + Dismiss (only while awaiting a selection). -->
|
|
628
|
+
<div
|
|
629
|
+
v-if="awaiting && !isInvestigating(f)"
|
|
630
|
+
class="mt-1.5 flex items-center gap-3 text-[11px]"
|
|
631
|
+
>
|
|
632
|
+
<button
|
|
633
|
+
v-if="!isRetracted(f)"
|
|
634
|
+
data-testid="pr-review-finding-challenge"
|
|
635
|
+
class="flex items-center gap-1 text-indigo-300 hover:underline disabled:opacity-50"
|
|
636
|
+
:disabled="!canResolve || !access.canExecuteRuns.value"
|
|
637
|
+
@click="openChallenge(f.id)"
|
|
638
|
+
>
|
|
639
|
+
<UIcon name="i-lucide-gavel" class="h-3.5 w-3.5" />
|
|
640
|
+
{{
|
|
641
|
+
f.challenge
|
|
642
|
+
? t('prReview.challenge.reChallenge')
|
|
643
|
+
: t('prReview.challenge.action')
|
|
644
|
+
}}
|
|
645
|
+
</button>
|
|
646
|
+
<button
|
|
647
|
+
data-testid="pr-review-finding-dismiss"
|
|
648
|
+
class="flex items-center gap-1 text-slate-400 hover:text-rose-300 hover:underline disabled:opacity-50"
|
|
649
|
+
:disabled="!canResolve || !access.canExecuteRuns.value"
|
|
650
|
+
@click="onDismiss(f.id)"
|
|
651
|
+
>
|
|
652
|
+
<UIcon name="i-lucide-trash-2" class="h-3.5 w-3.5" />
|
|
653
|
+
{{ t('prReview.challenge.dismiss') }}
|
|
654
|
+
</button>
|
|
655
|
+
</div>
|
|
656
|
+
|
|
657
|
+
<!-- The inline challenge box: an OPTIONAL specific concern for the investigator. -->
|
|
658
|
+
<div
|
|
659
|
+
v-if="challengeForId === f.id"
|
|
660
|
+
data-testid="pr-review-challenge-box"
|
|
661
|
+
class="mt-2 rounded-md border border-indigo-500/40 bg-slate-900/80 p-2"
|
|
662
|
+
>
|
|
663
|
+
<textarea
|
|
664
|
+
v-model="challengeText"
|
|
665
|
+
data-testid="pr-review-challenge-input"
|
|
666
|
+
rows="2"
|
|
667
|
+
:placeholder="t('prReview.challenge.placeholder')"
|
|
668
|
+
class="w-full resize-y rounded border border-slate-700 bg-slate-950/60 px-2 py-1 text-[12px] text-slate-200 outline-none focus:border-indigo-500"
|
|
669
|
+
/>
|
|
670
|
+
<p class="mt-1 text-[10px] text-slate-500">
|
|
671
|
+
{{ t('prReview.challenge.hint') }}
|
|
672
|
+
</p>
|
|
673
|
+
<div class="mt-1.5 flex justify-end gap-2">
|
|
674
|
+
<button
|
|
675
|
+
class="rounded px-2 py-1 text-[11px] text-slate-400 hover:text-slate-200"
|
|
676
|
+
@click="cancelChallenge"
|
|
677
|
+
>
|
|
678
|
+
{{ t('common.cancel') }}
|
|
679
|
+
</button>
|
|
680
|
+
<button
|
|
681
|
+
data-testid="pr-review-challenge-submit"
|
|
682
|
+
class="rounded bg-indigo-500/80 px-2 py-1 text-[11px] font-medium text-white hover:bg-indigo-500 disabled:opacity-50"
|
|
683
|
+
:disabled="!canResolve"
|
|
684
|
+
@click="submitChallenge(f.id)"
|
|
685
|
+
>
|
|
686
|
+
{{ t('prReview.challenge.submit') }}
|
|
687
|
+
</button>
|
|
688
|
+
</div>
|
|
689
|
+
</div>
|
|
417
690
|
</div>
|
|
418
691
|
</div>
|
|
419
692
|
</article>
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
challengePrReviewFindingContract,
|
|
3
|
+
dismissPrReviewFindingContract,
|
|
4
|
+
getPrReviewContract,
|
|
5
|
+
resolvePrReviewContract,
|
|
6
|
+
} from '@cat-factory/contracts'
|
|
2
7
|
import type { ApiContext } from './context'
|
|
3
8
|
|
|
4
9
|
/**
|
|
@@ -26,5 +31,25 @@ export function prReviewApi({ send, ws }: ApiContext) {
|
|
|
26
31
|
pathParams: { executionId },
|
|
27
32
|
body,
|
|
28
33
|
}),
|
|
34
|
+
|
|
35
|
+
// Dismiss a parked finding entirely (drops it + prunes it from the selection).
|
|
36
|
+
dismissPrReviewFinding: (workspaceId: string, executionId: string, findingId: string) =>
|
|
37
|
+
send(dismissPrReviewFindingContract, {
|
|
38
|
+
pathPrefix: ws(workspaceId),
|
|
39
|
+
pathParams: { executionId, findingId },
|
|
40
|
+
}),
|
|
41
|
+
|
|
42
|
+
// Challenge a parked finding — dispatch the Challenge Investigator (optional specific concern).
|
|
43
|
+
challengePrReviewFinding: (
|
|
44
|
+
workspaceId: string,
|
|
45
|
+
executionId: string,
|
|
46
|
+
findingId: string,
|
|
47
|
+
body: { question?: string },
|
|
48
|
+
) =>
|
|
49
|
+
send(challengePrReviewFindingContract, {
|
|
50
|
+
pathPrefix: ws(workspaceId),
|
|
51
|
+
pathParams: { executionId, findingId },
|
|
52
|
+
body,
|
|
53
|
+
}),
|
|
29
54
|
}
|
|
30
55
|
}
|
package/app/stores/prReview.ts
CHANGED
|
@@ -82,5 +82,50 @@ export const usePrReviewStore = defineStore('prReview', () => {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
/** Dismiss a finding entirely: it's removed from the review (and the selection). Stays parked. */
|
|
86
|
+
async function dismiss(executionId: string, findingId: string): Promise<void> {
|
|
87
|
+
error.value = null
|
|
88
|
+
resolving.value = true
|
|
89
|
+
try {
|
|
90
|
+
const state = await api.dismissPrReviewFinding(workspace.requireId(), executionId, findingId)
|
|
91
|
+
reflect(executionId, state as PrReviewStepState)
|
|
92
|
+
} catch (e) {
|
|
93
|
+
error.value = e instanceof Error ? e.message : 'Failed to dismiss finding'
|
|
94
|
+
throw e
|
|
95
|
+
} finally {
|
|
96
|
+
resolving.value = false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Challenge a finding: dispatch the Challenge Investigator with an OPTIONAL specific concern
|
|
102
|
+
* (blank ⇒ the generic "dig deeper + validate" prompt). The review moves to `challenging` while
|
|
103
|
+
* the investigator digs in; its verdict (strengthen / retract the finding) arrives via the stream.
|
|
104
|
+
*/
|
|
105
|
+
async function challenge(
|
|
106
|
+
executionId: string,
|
|
107
|
+
findingId: string,
|
|
108
|
+
question?: string,
|
|
109
|
+
): Promise<void> {
|
|
110
|
+
error.value = null
|
|
111
|
+
resolving.value = true
|
|
112
|
+
try {
|
|
113
|
+
const state = await api.challengePrReviewFinding(
|
|
114
|
+
workspace.requireId(),
|
|
115
|
+
executionId,
|
|
116
|
+
findingId,
|
|
117
|
+
{
|
|
118
|
+
question,
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
reflect(executionId, state as PrReviewStepState)
|
|
122
|
+
} catch (e) {
|
|
123
|
+
error.value = e instanceof Error ? e.message : 'Failed to challenge finding'
|
|
124
|
+
throw e
|
|
125
|
+
} finally {
|
|
126
|
+
resolving.value = false
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { resolving, error, load, resolve, dismiss, challenge }
|
|
86
131
|
})
|
package/app/types/execution.ts
CHANGED
package/app/utils/catalog.ts
CHANGED
|
@@ -412,6 +412,20 @@ export const SYSTEM_AGENT_META: Record<string, AgentArchetype> = {
|
|
|
412
412
|
color: '#22d3ee',
|
|
413
413
|
description: 'Maps the repository into the service → modules blueprint.',
|
|
414
414
|
},
|
|
415
|
+
// The read-only Challenge Investigator: dispatched off a parked `pr-reviewer` step when a human
|
|
416
|
+
// challenges a finding, it re-examines that ONE finding against the full source and upholds
|
|
417
|
+
// (strengthening) or retracts it. Never a palette block; modelled here purely so it is a
|
|
418
|
+
// configurable per-kind default in the Model Defaults panel — a workspace can point it at a
|
|
419
|
+
// different (stronger) model than the reviewer. Its output renders in the pr-review window.
|
|
420
|
+
'challenge-investigator': {
|
|
421
|
+
kind: 'challenge-investigator',
|
|
422
|
+
label: 'Challenge Investigator',
|
|
423
|
+
icon: 'i-lucide-gavel',
|
|
424
|
+
color: '#6366f1',
|
|
425
|
+
description:
|
|
426
|
+
'Re-examines a single challenged PR-review finding against the full source, then upholds ' +
|
|
427
|
+
'(strengthening it) or retracts it with a justification. Configurable separately from the reviewer.',
|
|
428
|
+
},
|
|
415
429
|
// The single environment provisioner: an operational (non-LLM) step that stands up the ephemeral
|
|
416
430
|
// environment the tester / human-test gate run against for a kubernetes/custom service, and is a
|
|
417
431
|
// fast no-op for docker-compose / infraless. Seeded before the first tester/human-test step in the
|
|
@@ -634,6 +648,9 @@ export const MODEL_CONFIGURABLE_SYSTEM_KINDS: AgentArchetype[] = [
|
|
|
634
648
|
'fixer',
|
|
635
649
|
'merger',
|
|
636
650
|
'kaizen',
|
|
651
|
+
// The PR-review Challenge Investigator — pinnable to its own (stronger) model, separately
|
|
652
|
+
// from the reviewer that produced the findings.
|
|
653
|
+
'challenge-investigator',
|
|
637
654
|
].map((kind) => SYSTEM_AGENT_META[kind]!),
|
|
638
655
|
// Companions run LLMs but aren't palette-addable (they're producer toggles), so include
|
|
639
656
|
// them here to keep their per-workspace default model pinnable in the Model Defaults panel.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import type { StepSubtasks } from '~/types/execution'
|
|
3
|
+
import { activeChunkLabels, chunkReviewPercent, isSlicingChunks } from './prReviewProgress'
|
|
4
|
+
|
|
5
|
+
const subtasks = (over: Partial<StepSubtasks>): StepSubtasks => ({
|
|
6
|
+
completed: 0,
|
|
7
|
+
inProgress: 0,
|
|
8
|
+
total: 0,
|
|
9
|
+
...over,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
describe('isSlicingChunks', () => {
|
|
13
|
+
it('is slicing when there is no todo list yet (null/undefined or empty)', () => {
|
|
14
|
+
// No plan committed → the reviewer is still grouping the diff into chunks.
|
|
15
|
+
expect(isSlicingChunks(null)).toBe(true)
|
|
16
|
+
expect(isSlicingChunks(undefined)).toBe(true)
|
|
17
|
+
expect(isSlicingChunks(subtasks({ total: 0 }))).toBe(true)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('is not slicing once the todo list exists (slicing is done)', () => {
|
|
21
|
+
expect(isSlicingChunks(subtasks({ total: 3, completed: 1 }))).toBe(false)
|
|
22
|
+
// A single-chunk plan still counts as sliced — a plan of size 1 is a committed plan.
|
|
23
|
+
expect(isSlicingChunks(subtasks({ total: 1 }))).toBe(false)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('chunkReviewPercent', () => {
|
|
28
|
+
it('is 0 with no plan (avoids 0/0 → NaN)', () => {
|
|
29
|
+
expect(chunkReviewPercent(null)).toBe(0)
|
|
30
|
+
expect(chunkReviewPercent(subtasks({ total: 0, completed: 0 }))).toBe(0)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('rounds completion to an integer percent', () => {
|
|
34
|
+
expect(chunkReviewPercent(subtasks({ total: 4, completed: 1 }))).toBe(25)
|
|
35
|
+
// 1/3 → 33.33… rounds to 33 (the old inline math emitted a fractional width).
|
|
36
|
+
expect(chunkReviewPercent(subtasks({ total: 3, completed: 1 }))).toBe(33)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('clamps to 0..100 even if counts are inconsistent', () => {
|
|
40
|
+
expect(chunkReviewPercent(subtasks({ total: 2, completed: 5 }))).toBe(100)
|
|
41
|
+
expect(chunkReviewPercent(subtasks({ total: 2, completed: -1 }))).toBe(0)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('activeChunkLabels', () => {
|
|
46
|
+
it('returns only the in-progress chunk labels, in order', () => {
|
|
47
|
+
const s = subtasks({
|
|
48
|
+
total: 3,
|
|
49
|
+
completed: 1,
|
|
50
|
+
inProgress: 1,
|
|
51
|
+
items: [
|
|
52
|
+
{ label: 'auth', status: 'completed' },
|
|
53
|
+
{ label: 'db layer', status: 'in_progress' },
|
|
54
|
+
{ label: 'ui', status: 'pending' },
|
|
55
|
+
],
|
|
56
|
+
})
|
|
57
|
+
expect(activeChunkLabels(s)).toEqual(['db layer'])
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('is empty with no items or no in-progress chunk', () => {
|
|
61
|
+
expect(activeChunkLabels(null)).toEqual([])
|
|
62
|
+
expect(activeChunkLabels(subtasks({ total: 2 }))).toEqual([])
|
|
63
|
+
expect(
|
|
64
|
+
activeChunkLabels(
|
|
65
|
+
subtasks({
|
|
66
|
+
total: 1,
|
|
67
|
+
completed: 1,
|
|
68
|
+
items: [{ label: 'only', status: 'completed' }],
|
|
69
|
+
}),
|
|
70
|
+
),
|
|
71
|
+
).toEqual([])
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { StepSubtasks } from '~/types/execution'
|
|
2
|
+
|
|
3
|
+
// Pure derivation of the PR deep-reviewer's `reviewing`-phase progress, factored out of
|
|
4
|
+
// `PrReviewWindow.vue` so it is unit-testable independently of the Vue component.
|
|
5
|
+
//
|
|
6
|
+
// The reviewer maintains a per-slice todo list (`step.subtasks`) once it has grouped the diff
|
|
7
|
+
// into cohesive chunks. The PRESENCE of that list is the signal that slicing is done, so the two
|
|
8
|
+
// `reviewing` sub-phases are told apart by it:
|
|
9
|
+
// - no todo list yet (`total === 0`) → still SLICING the diff into chunks (no plan committed).
|
|
10
|
+
// - todo list present (`total > 0`) → slicing DONE, working through the chunks.
|
|
11
|
+
|
|
12
|
+
/** True while the reviewer is still grouping the diff into chunks (it has not committed a plan). */
|
|
13
|
+
export function isSlicingChunks(subtasks: StepSubtasks | null | undefined): boolean {
|
|
14
|
+
return (subtasks?.total ?? 0) <= 0
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Chunk-review completion as an integer percent, clamped 0..100, for the progress bar. */
|
|
18
|
+
export function chunkReviewPercent(subtasks: StepSubtasks | null | undefined): number {
|
|
19
|
+
const total = subtasks?.total ?? 0
|
|
20
|
+
if (total <= 0) return 0
|
|
21
|
+
const completed = subtasks?.completed ?? 0
|
|
22
|
+
return Math.min(100, Math.max(0, Math.round((completed / total) * 100)))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Labels of the chunk(s) the reviewer is actively working through right now (for the callout). */
|
|
26
|
+
export function activeChunkLabels(subtasks: StepSubtasks | null | undefined): string[] {
|
|
27
|
+
return subtasks?.items?.filter((i) => i.status === 'in_progress').map((i) => i.label) ?? []
|
|
28
|
+
}
|
package/i18n/locales/de.json
CHANGED
|
@@ -4943,10 +4943,39 @@
|
|
|
4943
4943
|
"suggestedFix": "Lösungsvorschlag:",
|
|
4944
4944
|
"line": "Zeile {line}",
|
|
4945
4945
|
"reviewing": {
|
|
4946
|
-
"
|
|
4947
|
-
|
|
4946
|
+
"slicing": {
|
|
4947
|
+
"title": "Diff wird in Abschnitte zerlegt…",
|
|
4948
|
+
"hint": "Die geänderten Dateien werden in zusammenhängende Abschnitte gruppiert, damit jeder einzeln geprüft werden kann. Die Abschnittsliste erscheint hier, sobald die Zerlegung abgeschlossen ist."
|
|
4949
|
+
},
|
|
4950
|
+
"reviewingChunks": {
|
|
4951
|
+
"title": "Abschnitte werden geprüft…",
|
|
4952
|
+
"hint": "Die Zerlegung ist abgeschlossen. Jeder Abschnitt wird nacheinander geprüft und die Ergebnisse werden gesammelt."
|
|
4953
|
+
},
|
|
4948
4954
|
"chunks": "{completed} von {total} Abschnitten geprüft",
|
|
4949
|
-
"inProgress": "{count} in Bearbeitung"
|
|
4955
|
+
"inProgress": "{count} in Bearbeitung",
|
|
4956
|
+
"activeHeading": "Wird jetzt geprüft",
|
|
4957
|
+
"chunksHeading": "Abschnitte",
|
|
4958
|
+
"chunkStatus": {
|
|
4959
|
+
"completed": "Geprüft",
|
|
4960
|
+
"in_progress": "Wird geprüft…",
|
|
4961
|
+
"pending": "In Warteschlange"
|
|
4962
|
+
}
|
|
4963
|
+
},
|
|
4964
|
+
"challenge": {
|
|
4965
|
+
"action": "Anfechten",
|
|
4966
|
+
"reChallenge": "Erneut anfechten",
|
|
4967
|
+
"dismiss": "Verwerfen",
|
|
4968
|
+
"submit": "Untersuchen",
|
|
4969
|
+
"placeholder": "Optional: Was genau ist an diesem Befund falsch oder nicht überzeugend?",
|
|
4970
|
+
"hint": "Leer lassen, damit der Prüfer tiefer gräbt, den Befund begründet und prüft, ob er zutreffend und relevant ist.",
|
|
4971
|
+
"investigatingBanner": "Der Challenge-Investigator prüft einen Befund erneut anhand des Quellcodes…",
|
|
4972
|
+
"investigatingBadge": "Wird untersucht",
|
|
4973
|
+
"strengthenedBadge": "Bestärkt",
|
|
4974
|
+
"upheldBadge": "Bestätigt",
|
|
4975
|
+
"retractedBadge": "Zurückgezogen",
|
|
4976
|
+
"verdictLabel": "Prüfer:",
|
|
4977
|
+
"failedBadge": "Anfechtung fehlgeschlagen",
|
|
4978
|
+
"failedLabel": "Anfechtung fehlgeschlagen:"
|
|
4950
4979
|
},
|
|
4951
4980
|
"fixing": {
|
|
4952
4981
|
"title": "Ausgewählte Befunde werden behoben…",
|
package/i18n/locales/en.json
CHANGED
|
@@ -5072,10 +5072,39 @@
|
|
|
5072
5072
|
"suggestedFix": "Suggested fix:",
|
|
5073
5073
|
"line": "line {line}",
|
|
5074
5074
|
"reviewing": {
|
|
5075
|
-
"
|
|
5076
|
-
|
|
5075
|
+
"slicing": {
|
|
5076
|
+
"title": "Slicing the diff into chunks…",
|
|
5077
|
+
"hint": "Grouping the changed files into cohesive chunks so each can be reviewed on its own. The chunk list appears here once slicing is done."
|
|
5078
|
+
},
|
|
5079
|
+
"reviewingChunks": {
|
|
5080
|
+
"title": "Reviewing the chunks…",
|
|
5081
|
+
"hint": "Slicing is done. Working through each chunk one at a time and collecting findings."
|
|
5082
|
+
},
|
|
5077
5083
|
"chunks": "{completed} of {total} chunks reviewed",
|
|
5078
|
-
"inProgress": "{count} in progress"
|
|
5084
|
+
"inProgress": "{count} in progress",
|
|
5085
|
+
"activeHeading": "Reviewing now",
|
|
5086
|
+
"chunksHeading": "Chunks",
|
|
5087
|
+
"chunkStatus": {
|
|
5088
|
+
"completed": "Reviewed",
|
|
5089
|
+
"in_progress": "Reviewing…",
|
|
5090
|
+
"pending": "Queued"
|
|
5091
|
+
}
|
|
5092
|
+
},
|
|
5093
|
+
"challenge": {
|
|
5094
|
+
"action": "Challenge",
|
|
5095
|
+
"reChallenge": "Challenge again",
|
|
5096
|
+
"dismiss": "Dismiss",
|
|
5097
|
+
"submit": "Investigate",
|
|
5098
|
+
"placeholder": "Optional: what specifically is wrong or unconvincing about this finding?",
|
|
5099
|
+
"hint": "Leave blank to have the investigator dig deeper, justify the finding, and check it's accurate and relevant.",
|
|
5100
|
+
"investigatingBanner": "The Challenge Investigator is re-examining a finding against the source…",
|
|
5101
|
+
"investigatingBadge": "Investigating",
|
|
5102
|
+
"strengthenedBadge": "Strengthened",
|
|
5103
|
+
"upheldBadge": "Upheld",
|
|
5104
|
+
"retractedBadge": "Retracted",
|
|
5105
|
+
"verdictLabel": "Investigator:",
|
|
5106
|
+
"failedBadge": "Challenge failed",
|
|
5107
|
+
"failedLabel": "Challenge failed:"
|
|
5079
5108
|
},
|
|
5080
5109
|
"fixing": {
|
|
5081
5110
|
"title": "Fixing the selected findings…",
|
package/i18n/locales/es.json
CHANGED
|
@@ -4931,10 +4931,39 @@
|
|
|
4931
4931
|
"suggestedFix": "Corrección sugerida:",
|
|
4932
4932
|
"line": "línea {line}",
|
|
4933
4933
|
"reviewing": {
|
|
4934
|
-
"
|
|
4935
|
-
|
|
4934
|
+
"slicing": {
|
|
4935
|
+
"title": "Dividiendo el diff en fragmentos…",
|
|
4936
|
+
"hint": "Agrupando los archivos modificados en fragmentos coherentes para revisar cada uno por separado. La lista de fragmentos aparecerá aquí cuando termine la división."
|
|
4937
|
+
},
|
|
4938
|
+
"reviewingChunks": {
|
|
4939
|
+
"title": "Revisando los fragmentos…",
|
|
4940
|
+
"hint": "La división ha terminado. Revisando cada fragmento de uno en uno y recopilando los hallazgos."
|
|
4941
|
+
},
|
|
4936
4942
|
"chunks": "{completed} de {total} fragmentos revisados",
|
|
4937
|
-
"inProgress": "{count} en curso"
|
|
4943
|
+
"inProgress": "{count} en curso",
|
|
4944
|
+
"activeHeading": "Revisando ahora",
|
|
4945
|
+
"chunksHeading": "Fragmentos",
|
|
4946
|
+
"chunkStatus": {
|
|
4947
|
+
"completed": "Revisado",
|
|
4948
|
+
"in_progress": "Revisando…",
|
|
4949
|
+
"pending": "En cola"
|
|
4950
|
+
}
|
|
4951
|
+
},
|
|
4952
|
+
"challenge": {
|
|
4953
|
+
"action": "Cuestionar",
|
|
4954
|
+
"reChallenge": "Cuestionar de nuevo",
|
|
4955
|
+
"dismiss": "Descartar",
|
|
4956
|
+
"submit": "Investigar",
|
|
4957
|
+
"placeholder": "Opcional: ¿qué es exactamente lo incorrecto o poco convincente de este hallazgo?",
|
|
4958
|
+
"hint": "Déjalo en blanco para que el investigador profundice, justifique el hallazgo y compruebe que es preciso y relevante.",
|
|
4959
|
+
"investigatingBanner": "El Investigador de Objeciones está reexaminando un hallazgo frente al código fuente…",
|
|
4960
|
+
"investigatingBadge": "Investigando",
|
|
4961
|
+
"strengthenedBadge": "Reforzado",
|
|
4962
|
+
"upheldBadge": "Confirmado",
|
|
4963
|
+
"retractedBadge": "Retirado",
|
|
4964
|
+
"verdictLabel": "Investigador:",
|
|
4965
|
+
"failedBadge": "Cuestionamiento fallido",
|
|
4966
|
+
"failedLabel": "Cuestionamiento fallido:"
|
|
4938
4967
|
},
|
|
4939
4968
|
"fixing": {
|
|
4940
4969
|
"title": "Corrigiendo los hallazgos seleccionados…",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -4931,10 +4931,39 @@
|
|
|
4931
4931
|
"suggestedFix": "Correction suggérée :",
|
|
4932
4932
|
"line": "ligne {line}",
|
|
4933
4933
|
"reviewing": {
|
|
4934
|
-
"
|
|
4935
|
-
|
|
4934
|
+
"slicing": {
|
|
4935
|
+
"title": "Découpage du diff en sections…",
|
|
4936
|
+
"hint": "Regroupement des fichiers modifiés en sections cohérentes afin de pouvoir examiner chacune séparément. La liste des sections apparaîtra ici une fois le découpage terminé."
|
|
4937
|
+
},
|
|
4938
|
+
"reviewingChunks": {
|
|
4939
|
+
"title": "Revue des sections…",
|
|
4940
|
+
"hint": "Le découpage est terminé. Chaque section est examinée l'une après l'autre et les remarques sont rassemblées."
|
|
4941
|
+
},
|
|
4936
4942
|
"chunks": "{completed} sur {total} sections examinées",
|
|
4937
|
-
"inProgress": "{count} en cours"
|
|
4943
|
+
"inProgress": "{count} en cours",
|
|
4944
|
+
"activeHeading": "Revue en cours",
|
|
4945
|
+
"chunksHeading": "Sections",
|
|
4946
|
+
"chunkStatus": {
|
|
4947
|
+
"completed": "Examinée",
|
|
4948
|
+
"in_progress": "En cours d'examen…",
|
|
4949
|
+
"pending": "En attente"
|
|
4950
|
+
}
|
|
4951
|
+
},
|
|
4952
|
+
"challenge": {
|
|
4953
|
+
"action": "Contester",
|
|
4954
|
+
"reChallenge": "Contester à nouveau",
|
|
4955
|
+
"dismiss": "Écarter",
|
|
4956
|
+
"submit": "Enquêter",
|
|
4957
|
+
"placeholder": "Facultatif : qu'y a-t-il précisément d'incorrect ou de peu convaincant dans ce constat ?",
|
|
4958
|
+
"hint": "Laissez vide pour que l'enquêteur approfondisse, justifie le constat et vérifie qu'il est exact et pertinent.",
|
|
4959
|
+
"investigatingBanner": "L'enquêteur de contestation réexamine un constat par rapport au code source…",
|
|
4960
|
+
"investigatingBadge": "Enquête en cours",
|
|
4961
|
+
"strengthenedBadge": "Renforcé",
|
|
4962
|
+
"upheldBadge": "Confirmé",
|
|
4963
|
+
"retractedBadge": "Retiré",
|
|
4964
|
+
"verdictLabel": "Enquêteur :",
|
|
4965
|
+
"failedBadge": "Échec de la contestation",
|
|
4966
|
+
"failedLabel": "Échec de la contestation :"
|
|
4938
4967
|
},
|
|
4939
4968
|
"fixing": {
|
|
4940
4969
|
"title": "Correction des points sélectionnés…",
|
package/i18n/locales/he.json
CHANGED
|
@@ -4942,10 +4942,39 @@
|
|
|
4942
4942
|
"suggestedFix": "תיקון מוצע:",
|
|
4943
4943
|
"line": "שורה {line}",
|
|
4944
4944
|
"reviewing": {
|
|
4945
|
-
"
|
|
4946
|
-
|
|
4945
|
+
"slicing": {
|
|
4946
|
+
"title": "מחלק את ההבדלים למקטעים…",
|
|
4947
|
+
"hint": "מקבץ את הקבצים שהשתנו למקטעים לכידים כדי לבדוק כל אחד בנפרד. רשימת המקטעים תופיע כאן לאחר סיום החלוקה."
|
|
4948
|
+
},
|
|
4949
|
+
"reviewingChunks": {
|
|
4950
|
+
"title": "בודק את המקטעים…",
|
|
4951
|
+
"hint": "החלוקה הסתיימה. בודק כל מקטע בנפרד ואוסף את הממצאים."
|
|
4952
|
+
},
|
|
4947
4953
|
"chunks": "{completed} מתוך {total} מקטעים נבדקו",
|
|
4948
|
-
"inProgress": "{count} בתהליך"
|
|
4954
|
+
"inProgress": "{count} בתהליך",
|
|
4955
|
+
"activeHeading": "נבדק כעת",
|
|
4956
|
+
"chunksHeading": "מקטעים",
|
|
4957
|
+
"chunkStatus": {
|
|
4958
|
+
"completed": "נבדק",
|
|
4959
|
+
"in_progress": "בבדיקה…",
|
|
4960
|
+
"pending": "בתור"
|
|
4961
|
+
}
|
|
4962
|
+
},
|
|
4963
|
+
"challenge": {
|
|
4964
|
+
"action": "ערער",
|
|
4965
|
+
"reChallenge": "ערער שוב",
|
|
4966
|
+
"dismiss": "התעלם",
|
|
4967
|
+
"submit": "חקור",
|
|
4968
|
+
"placeholder": "רשות: מה בדיוק שגוי או לא משכנע בממצא הזה?",
|
|
4969
|
+
"hint": "השאירו ריק כדי שהחוקר יעמיק, ינמק את הממצא ויוודא שהוא מדויק ורלוונטי.",
|
|
4970
|
+
"investigatingBanner": "חוקר הערעורים בוחן מחדש ממצא מול קוד המקור…",
|
|
4971
|
+
"investigatingBadge": "בחקירה",
|
|
4972
|
+
"strengthenedBadge": "חוזק",
|
|
4973
|
+
"upheldBadge": "אושר",
|
|
4974
|
+
"retractedBadge": "בוטל",
|
|
4975
|
+
"verdictLabel": "חוקר:",
|
|
4976
|
+
"failedBadge": "הערעור נכשל",
|
|
4977
|
+
"failedLabel": "הערעור נכשל:"
|
|
4949
4978
|
},
|
|
4950
4979
|
"fixing": {
|
|
4951
4980
|
"title": "מתקן את הממצאים שנבחרו…",
|
package/i18n/locales/it.json
CHANGED
|
@@ -4943,10 +4943,39 @@
|
|
|
4943
4943
|
"suggestedFix": "Correzione suggerita:",
|
|
4944
4944
|
"line": "riga {line}",
|
|
4945
4945
|
"reviewing": {
|
|
4946
|
-
"
|
|
4947
|
-
|
|
4946
|
+
"slicing": {
|
|
4947
|
+
"title": "Suddivisione del diff in blocchi…",
|
|
4948
|
+
"hint": "Raggruppamento dei file modificati in blocchi coerenti per esaminare ciascuno separatamente. L'elenco dei blocchi comparirà qui al termine della suddivisione."
|
|
4949
|
+
},
|
|
4950
|
+
"reviewingChunks": {
|
|
4951
|
+
"title": "Revisione dei blocchi…",
|
|
4952
|
+
"hint": "La suddivisione è terminata. Ogni blocco viene esaminato uno alla volta e le osservazioni vengono raccolte."
|
|
4953
|
+
},
|
|
4948
4954
|
"chunks": "{completed} di {total} sezioni esaminate",
|
|
4949
|
-
"inProgress": "{count} in corso"
|
|
4955
|
+
"inProgress": "{count} in corso",
|
|
4956
|
+
"activeHeading": "In revisione ora",
|
|
4957
|
+
"chunksHeading": "Blocchi",
|
|
4958
|
+
"chunkStatus": {
|
|
4959
|
+
"completed": "Esaminato",
|
|
4960
|
+
"in_progress": "In revisione…",
|
|
4961
|
+
"pending": "In coda"
|
|
4962
|
+
}
|
|
4963
|
+
},
|
|
4964
|
+
"challenge": {
|
|
4965
|
+
"action": "Contesta",
|
|
4966
|
+
"reChallenge": "Contesta di nuovo",
|
|
4967
|
+
"dismiss": "Scarta",
|
|
4968
|
+
"submit": "Indaga",
|
|
4969
|
+
"placeholder": "Facoltativo: cosa c'è di preciso di sbagliato o poco convincente in questo rilievo?",
|
|
4970
|
+
"hint": "Lascia vuoto per far approfondire l'investigatore, giustificare il rilievo e verificarne accuratezza e pertinenza.",
|
|
4971
|
+
"investigatingBanner": "L'investigatore delle contestazioni sta riesaminando un rilievo rispetto al codice sorgente…",
|
|
4972
|
+
"investigatingBadge": "In esame",
|
|
4973
|
+
"strengthenedBadge": "Rafforzato",
|
|
4974
|
+
"upheldBadge": "Confermato",
|
|
4975
|
+
"retractedBadge": "Ritirato",
|
|
4976
|
+
"verdictLabel": "Investigatore:",
|
|
4977
|
+
"failedBadge": "Contestazione non riuscita",
|
|
4978
|
+
"failedLabel": "Contestazione non riuscita:"
|
|
4950
4979
|
},
|
|
4951
4980
|
"fixing": {
|
|
4952
4981
|
"title": "Correzione dei rilievi selezionati…",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -4943,10 +4943,39 @@
|
|
|
4943
4943
|
"suggestedFix": "修正案:",
|
|
4944
4944
|
"line": "{line}行目",
|
|
4945
4945
|
"reviewing": {
|
|
4946
|
-
"
|
|
4947
|
-
|
|
4946
|
+
"slicing": {
|
|
4947
|
+
"title": "差分をチャンクに分割中…",
|
|
4948
|
+
"hint": "変更されたファイルをまとまりのあるチャンクにグループ化し、各チャンクを個別にレビューできるようにしています。分割が完了すると、ここにチャンク一覧が表示されます。"
|
|
4949
|
+
},
|
|
4950
|
+
"reviewingChunks": {
|
|
4951
|
+
"title": "チャンクをレビュー中…",
|
|
4952
|
+
"hint": "分割は完了しました。各チャンクを一つずつレビューし、指摘事項を収集しています。"
|
|
4953
|
+
},
|
|
4948
4954
|
"chunks": "{total} 個中 {completed} 個のチャンクをレビュー済み",
|
|
4949
|
-
"inProgress": "{count} 件処理中"
|
|
4955
|
+
"inProgress": "{count} 件処理中",
|
|
4956
|
+
"activeHeading": "レビュー中",
|
|
4957
|
+
"chunksHeading": "チャンク",
|
|
4958
|
+
"chunkStatus": {
|
|
4959
|
+
"completed": "レビュー済み",
|
|
4960
|
+
"in_progress": "レビュー中…",
|
|
4961
|
+
"pending": "待機中"
|
|
4962
|
+
}
|
|
4963
|
+
},
|
|
4964
|
+
"challenge": {
|
|
4965
|
+
"action": "異議を唱える",
|
|
4966
|
+
"reChallenge": "再度異議を唱える",
|
|
4967
|
+
"dismiss": "却下",
|
|
4968
|
+
"submit": "調査する",
|
|
4969
|
+
"placeholder": "任意: この指摘のどこが誤り、または納得できないかを記入してください。",
|
|
4970
|
+
"hint": "空欄のままにすると、調査担当がさらに深く掘り下げ、指摘の根拠を示し、正確性と関連性を検証します。",
|
|
4971
|
+
"investigatingBanner": "異議調査担当がソースコードに照らして指摘を再検証しています…",
|
|
4972
|
+
"investigatingBadge": "調査中",
|
|
4973
|
+
"strengthenedBadge": "強化済み",
|
|
4974
|
+
"upheldBadge": "支持",
|
|
4975
|
+
"retractedBadge": "撤回済み",
|
|
4976
|
+
"verdictLabel": "調査担当:",
|
|
4977
|
+
"failedBadge": "異議調査に失敗",
|
|
4978
|
+
"failedLabel": "異議調査に失敗:"
|
|
4950
4979
|
},
|
|
4951
4980
|
"fixing": {
|
|
4952
4981
|
"title": "選択した指摘を修正中…",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -4931,10 +4931,39 @@
|
|
|
4931
4931
|
"suggestedFix": "Sugerowana poprawka:",
|
|
4932
4932
|
"line": "wiersz {line}",
|
|
4933
4933
|
"reviewing": {
|
|
4934
|
-
"
|
|
4935
|
-
|
|
4934
|
+
"slicing": {
|
|
4935
|
+
"title": "Dzielenie zmian na fragmenty…",
|
|
4936
|
+
"hint": "Grupowanie zmienionych plików w spójne fragmenty, aby każdy można było przejrzeć osobno. Lista fragmentów pojawi się tutaj po zakończeniu podziału."
|
|
4937
|
+
},
|
|
4938
|
+
"reviewingChunks": {
|
|
4939
|
+
"title": "Przeglądanie fragmentów…",
|
|
4940
|
+
"hint": "Podział zakończony. Przeglądanie każdego fragmentu po kolei i zbieranie uwag."
|
|
4941
|
+
},
|
|
4936
4942
|
"chunks": "Sprawdzono {completed} z {total} fragmentów",
|
|
4937
|
-
"inProgress": "{count} w toku"
|
|
4943
|
+
"inProgress": "{count} w toku",
|
|
4944
|
+
"activeHeading": "Przeglądane teraz",
|
|
4945
|
+
"chunksHeading": "Fragmenty",
|
|
4946
|
+
"chunkStatus": {
|
|
4947
|
+
"completed": "Sprawdzony",
|
|
4948
|
+
"in_progress": "Przeglądanie…",
|
|
4949
|
+
"pending": "W kolejce"
|
|
4950
|
+
}
|
|
4951
|
+
},
|
|
4952
|
+
"challenge": {
|
|
4953
|
+
"action": "Zakwestionuj",
|
|
4954
|
+
"reChallenge": "Zakwestionuj ponownie",
|
|
4955
|
+
"dismiss": "Odrzuć",
|
|
4956
|
+
"submit": "Zbadaj",
|
|
4957
|
+
"placeholder": "Opcjonalnie: co dokładnie jest błędne lub nieprzekonujące w tym ustaleniu?",
|
|
4958
|
+
"hint": "Pozostaw puste, aby badacz zgłębił temat, uzasadnił ustalenie i sprawdził, czy jest trafne i istotne.",
|
|
4959
|
+
"investigatingBanner": "Badacz zastrzeżeń ponownie analizuje ustalenie w oparciu o kod źródłowy…",
|
|
4960
|
+
"investigatingBadge": "Badanie",
|
|
4961
|
+
"strengthenedBadge": "Wzmocnione",
|
|
4962
|
+
"upheldBadge": "Podtrzymane",
|
|
4963
|
+
"retractedBadge": "Wycofane",
|
|
4964
|
+
"verdictLabel": "Badacz:",
|
|
4965
|
+
"failedBadge": "Zakwestionowanie nie powiodło się",
|
|
4966
|
+
"failedLabel": "Zakwestionowanie nie powiodło się:"
|
|
4938
4967
|
},
|
|
4939
4968
|
"fixing": {
|
|
4940
4969
|
"title": "Naprawianie wybranych uwag…",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -4943,10 +4943,39 @@
|
|
|
4943
4943
|
"suggestedFix": "Önerilen düzeltme:",
|
|
4944
4944
|
"line": "satır {line}",
|
|
4945
4945
|
"reviewing": {
|
|
4946
|
-
"
|
|
4947
|
-
|
|
4946
|
+
"slicing": {
|
|
4947
|
+
"title": "Fark parçalara bölünüyor…",
|
|
4948
|
+
"hint": "Değiştirilen dosyalar, her biri ayrı ayrı incelenebilsin diye tutarlı parçalara gruplanıyor. Bölme tamamlandığında parça listesi burada görünecek."
|
|
4949
|
+
},
|
|
4950
|
+
"reviewingChunks": {
|
|
4951
|
+
"title": "Parçalar inceleniyor…",
|
|
4952
|
+
"hint": "Bölme tamamlandı. Her parça tek tek inceleniyor ve bulgular toplanıyor."
|
|
4953
|
+
},
|
|
4948
4954
|
"chunks": "{total} bölümden {completed} tanesi incelendi",
|
|
4949
|
-
"inProgress": "{count} devam ediyor"
|
|
4955
|
+
"inProgress": "{count} devam ediyor",
|
|
4956
|
+
"activeHeading": "Şimdi inceleniyor",
|
|
4957
|
+
"chunksHeading": "Parçalar",
|
|
4958
|
+
"chunkStatus": {
|
|
4959
|
+
"completed": "İncelendi",
|
|
4960
|
+
"in_progress": "İnceleniyor…",
|
|
4961
|
+
"pending": "Sırada"
|
|
4962
|
+
}
|
|
4963
|
+
},
|
|
4964
|
+
"challenge": {
|
|
4965
|
+
"action": "İtiraz et",
|
|
4966
|
+
"reChallenge": "Tekrar itiraz et",
|
|
4967
|
+
"dismiss": "Yoksay",
|
|
4968
|
+
"submit": "İncele",
|
|
4969
|
+
"placeholder": "İsteğe bağlı: Bu bulguda tam olarak yanlış veya ikna edici olmayan nedir?",
|
|
4970
|
+
"hint": "Araştırmacının daha derine inmesi, bulguyu gerekçelendirmesi ve doğru ve ilgili olduğunu doğrulaması için boş bırakın.",
|
|
4971
|
+
"investigatingBanner": "İtiraz Araştırmacısı bir bulguyu kaynak koda göre yeniden inceliyor…",
|
|
4972
|
+
"investigatingBadge": "İnceleniyor",
|
|
4973
|
+
"strengthenedBadge": "Güçlendirildi",
|
|
4974
|
+
"upheldBadge": "Onaylandı",
|
|
4975
|
+
"retractedBadge": "Geri çekildi",
|
|
4976
|
+
"verdictLabel": "Araştırmacı:",
|
|
4977
|
+
"failedBadge": "İtiraz başarısız oldu",
|
|
4978
|
+
"failedLabel": "İtiraz başarısız oldu:"
|
|
4950
4979
|
},
|
|
4951
4980
|
"fixing": {
|
|
4952
4981
|
"title": "Seçili bulgular düzeltiliyor…",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -4931,10 +4931,39 @@
|
|
|
4931
4931
|
"suggestedFix": "Пропоноване виправлення:",
|
|
4932
4932
|
"line": "рядок {line}",
|
|
4933
4933
|
"reviewing": {
|
|
4934
|
-
"
|
|
4935
|
-
|
|
4934
|
+
"slicing": {
|
|
4935
|
+
"title": "Поділ змін на фрагменти…",
|
|
4936
|
+
"hint": "Групування змінених файлів у цілісні фрагменти, щоб кожен можна було переглянути окремо. Список фрагментів з'явиться тут після завершення поділу."
|
|
4937
|
+
},
|
|
4938
|
+
"reviewingChunks": {
|
|
4939
|
+
"title": "Огляд фрагментів…",
|
|
4940
|
+
"hint": "Поділ завершено. Кожен фрагмент переглядається по черзі, а зауваження збираються."
|
|
4941
|
+
},
|
|
4936
4942
|
"chunks": "Перевірено {completed} з {total} фрагментів",
|
|
4937
|
-
"inProgress": "{count} у процесі"
|
|
4943
|
+
"inProgress": "{count} у процесі",
|
|
4944
|
+
"activeHeading": "Переглядається зараз",
|
|
4945
|
+
"chunksHeading": "Фрагменти",
|
|
4946
|
+
"chunkStatus": {
|
|
4947
|
+
"completed": "Переглянуто",
|
|
4948
|
+
"in_progress": "Огляд…",
|
|
4949
|
+
"pending": "У черзі"
|
|
4950
|
+
}
|
|
4951
|
+
},
|
|
4952
|
+
"challenge": {
|
|
4953
|
+
"action": "Оскаржити",
|
|
4954
|
+
"reChallenge": "Оскаржити знову",
|
|
4955
|
+
"dismiss": "Відхилити",
|
|
4956
|
+
"submit": "Дослідити",
|
|
4957
|
+
"placeholder": "Необов'язково: що саме неправильне або непереконливе в цьому зауваженні?",
|
|
4958
|
+
"hint": "Залиште порожнім, щоб дослідник копнув глибше, обґрунтував зауваження та перевірив його точність і доречність.",
|
|
4959
|
+
"investigatingBanner": "Дослідник оскаржень повторно перевіряє зауваження за вихідним кодом…",
|
|
4960
|
+
"investigatingBadge": "Дослідження",
|
|
4961
|
+
"strengthenedBadge": "Посилено",
|
|
4962
|
+
"upheldBadge": "Підтверджено",
|
|
4963
|
+
"retractedBadge": "Відкликано",
|
|
4964
|
+
"verdictLabel": "Дослідник:",
|
|
4965
|
+
"failedBadge": "Оскарження не вдалося",
|
|
4966
|
+
"failedLabel": "Оскарження не вдалося:"
|
|
4938
4967
|
},
|
|
4939
4968
|
"fixing": {
|
|
4940
4969
|
"title": "Виправлення вибраних зауважень…",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.147.1",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"valibot": "^1.4.2",
|
|
41
41
|
"vue": "3.5.40",
|
|
42
42
|
"wretch": "^3.0.9",
|
|
43
|
-
"@cat-factory/contracts": "0.
|
|
43
|
+
"@cat-factory/contracts": "0.154.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@toad-contracts/testing": "0.3.2",
|